/* ============================================================
   Inter font (Google Fonts CDN)
   ============================================================ */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

/* ============================================================
   Variables globales — identidad base Sestia (plantilla de marca)
   ------------------------------------------------------------
   Paleta de fábrica (5 colores, misma familia en ambos temas):
     #fbfcfd  casi blanco   → superficie de tarjetas (claro) / texto fuerte (oscuro)
     #eef1f7  gris frío     → lienzo del layout (claro) / texto base (oscuro)
     #e6ebf1  gris azulado  → superficies hundidas / tinte de las tarjetas oscuras
     #572d4b  ciruela       → ACENTO principal de marca
     #9c9ba9  gris medio    → texto secundario / jerarquía baja

   Todo color de la app sale de estos tokens. Un tema del Marketplace (o uno de
   los presets de fábrica, backend/sql/124_sestia_base_themes.sql) solo redefine
   tokens — nunca reglas. Ver docs/frontend/arquitectura/identidad_visual_sestia.md.
   ============================================================ */
:root {
  /* Controles nativos (date picker, flecha de select, scrollbars, botón clear del
     search) siguen este esquema → adaptan a oscuro sin CSS extra. Se invierte en
     html.light-mode. */
  color-scheme: dark;
  /* ── Fondos / superficies (OSCURO = base) ──
     Lienzo = #eef1f7 llevado a carbón frío; las tarjetas son ese mismo gris
     azulado (#e6ebf1) en alfa baja → "flotan" sobre el lienzo sin bordes duros. */
  --bg:                #14161c;
  --bg-2:              #191c24;
  --surface:           rgba(230,235,241,0.05);
  --surface-2:         rgba(230,235,241,0.08);
  --surface-3:         #1b1f28;
  --panel:             rgba(230,235,241,0.06);
  --panel-2:           #232833;
  /* Superficie de overlays flotantes (modales / drawer): casi opaca para que el
     contenido detrás no se transparente y el panel sea legible. NO usar --surface
     (tinte translúcido pensado para cards en flujo). */
  --surface-overlay:   rgba(28,32,41,0.97);
  --control-bg:        rgba(230,235,241,0.07);
  --control-bg-hover:  rgba(230,235,241,0.12);
  --control-border:    rgba(230,235,241,0.14);
  --scrim:             rgba(10,11,15,0.68);
  /* ── Texto ── */
  --text:              #eef1f7;
  --text-strong:       #fbfcfd;
  --muted:             #9c9ba9;
  --muted-2:           #74717f;
  /* ── Bordes ── */
  --border:            rgba(230,235,241,0.10);
  --border-soft:       rgba(230,235,241,0.06);
  /* ── Marca / acento — ciruela #572d4b ──
     En OSCURO la ciruela pura no llega a contraste de texto sobre carbón, así que
     --brand es la ciruela ACLARADA (bordes, iconos, texto de acento) y
     --brand-dark queda en la ciruela pura, que es donde se apoya texto blanco
     (#fff sobre #572d4b ≈ 9.7:1). En CLARO se invierte (ver html.light-mode). */
  --brand:             #a76b91;
  --brand-dark:        #572d4b;
  /* Componentes RGB de la marca — para tints con alfa: rgba(var(--brand-rgb),α).
     Un tema puede overridearlos igual que --brand y así controlar también los
     fondos/anillos translúcidos, no solo los colores sólidos. */
  --brand-rgb:         167, 107, 145;
  --brand-dark-rgb:    87, 45, 75;
  --brand-soft-bg:     rgba(167,107,145,0.18);
  --brand-soft-fg:     #d7abc4;
  --accent:            #14161c;
  --danger:            #ef4444;
  --success:           #10b981;
  /* ── Chrome de navegación (headerbar + sidebar de módulo) ──
     DERIVADOS de la marca a propósito: son `color-mix` sobre --brand-dark, así
     que al activar un preset (que solo redefine --brand*) la barra superior y la
     lateral cambian SOLAS. No pongas aquí un hex fijo — era justo el bug: eran
     azules hardcodeados y se quedaban azules con cualquier tema.
     Cada token se declara DOS veces: el hex plano es el fallback para navegadores
     sin color-mix (que descartan la 2ª declaración y se quedan con la 1ª). */
  --nav-bg:            #271c29;
  --nav-bg:            color-mix(in srgb, var(--brand-dark) 28%, var(--bg));
  --nav-bg-2:          #201a24;
  --nav-bg-2:          color-mix(in srgb, var(--brand-dark) 16%, var(--bg));
  --nav-border:        rgba(230,235,241,0.09);
  /* Velo que se superpone a --nav-bg-2 en la subsidebar: mismo color que la
     sidebar, un escalón más hundido (profundidad sin salirse de la identidad). */
  --nav-shade:         rgba(0,0,0,0.30);
  /* ── Tooltip ── */
  --tooltip-bg:        #262b36;
  --tooltip-fg:        #fbfcfd;
  /* ── Tags / badges (bg + fg) ──
     success/warn/danger/info son SEMÁNTICOS: no siguen la marca (un tema de marca
     roja no debe volver rojo el "todo ok"). El par purple sí sigue la marca. */
  --badge-success-bg:  rgba(16,185,129,0.15);  --badge-success-fg: #34d399;
  --badge-warn-bg:     rgba(245,158,11,0.16);  --badge-warn-fg:    #fbbf24;
  --badge-danger-bg:   rgba(239,68,68,0.15);   --badge-danger-fg:  #f87171;
  --badge-info-bg:     rgba(59,130,246,0.16);  --badge-info-fg:    #60a5fa;
  --badge-neutral-bg:  rgba(156,155,169,0.18); --badge-neutral-fg: #c9c7d2;
  --badge-purple-bg:   rgba(167,107,145,0.18); --badge-purple-fg:  #d7abc4;
  /* ── Sombras (oscuro: más marcadas para que se vean) ── */
  --shadow-sm:  0 1px 2px rgba(0,0,0,0.30);
  --shadow-md:  0 4px 6px rgba(0,0,0,0.35), 0 2px 4px rgba(0,0,0,0.30);
  --shadow-lg:  0 12px 28px rgba(0,0,0,0.50), 0 4px 8px rgba(0,0,0,0.40);
  /* ── Gráficas (ApexCharts) ──
     Los colores de serie NO pueden ser hexes literales en el .rs: ApexCharts
     recibe JSON, no CSS, así que no ve `var(--…)`. Los lee `charts.rs` con
     getComputedStyle y los pasa ya resueltos. Que estén aquí es lo que hace que
     las gráficas sigan al tema del usuario y —sobre todo— a las paletas de
     accesibilidad: antes iban en verde y ámbar fijos, que es justo el par que un
     deuteranope no separa, en el sitio donde más duele.

     `--chart-1..6` es la rampa CATEGÓRICA (series sin significado propio) y va
     ordenada por separabilidad: las dos primeras son las que más se usan y las
     que más lejos están entre sí en cada paleta. Los alias semánticos de abajo
     apuntan a la rampa, así que una paleta solo necesita redefinir los seis. */
  --chart-1:        #60a5fa;
  --chart-2:        #4caf88;
  --chart-3:        #f59e0b;
  --chart-4:        #7c6af7;
  --chart-5:        #ef4444;
  --chart-6:        #94a3b8;
  --chart-ok:       var(--chart-2);
  --chart-warn:     var(--chart-3);
  --chart-bad:      var(--chart-5);
  --chart-info:     var(--chart-1);
  --chart-accent:   var(--chart-4);
  --chart-neutral:  var(--chart-6);
  /* Cromo: rejilla, ejes, leyenda y el texto sobre las porciones del donut. */
  --chart-grid:      #2a2a3e;
  --chart-axis:      #a0a0b0;
  --chart-legend:    #c0c0d0;
  --chart-datalabel: #ffffff;
}

/* ============================================================
   Reset / base
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Transición suave claro⇄oscuro. Solo props de color; NO afecta la carga
   inicial (index.html fija la clase de tema en un script pre-paint, antes del
   primer render → la transición solo dispara en el toggle en runtime). Las
   reglas con transition propia (botones, hover, etc.) ganan por especificidad,
   así que las interacciones siguen igual de rápidas; aquí solo funden las
   superficies estáticas. box-shadow/transform se dejan a sus reglas de hover. */
*, *::before, *::after {
  transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease, fill 0.3s ease;
}

/* scrollbar-gutter: stable → reserva SIEMPRE el ancho de la barra de scroll del
   documento, aunque no haga falta. Evita que al pasar a una tab alta (Bloques)
   aparezca la barra, encoja el viewport y desplace el contenido centrado. */
/* --font-scale (F6.3): escala global de tipografía. Todo lo dimensionado en rem
   reescala con esto; los px literales NO (deuda conocida, riesgo 8 del plan). */
html { font-size: calc(16px * var(--font-scale, 1)); scrollbar-gutter: stable; }

body {
  /* --font-family (F6.5): lo fija el fontpack activo; fallback = Inter de fábrica. */
  font-family: var(--font-family, 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
  background: var(--bg);
  min-height: 100vh;
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

.active {
  background: var(--brand-dark) !important;
  color: #fff !important;
}

/* ============================================================
   Splash / carga inicial
   ============================================================ */
.splash {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #14161c 0%, #2b1f2b 50%, #14161c 100%);
  color: var(--muted);
  font-size: 0.875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ============================================================
   LOGIN PAGE
   ============================================================ */
.login-container {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  /* Fondo estático del login (el dinámico por clima lo pisa .login-container--dynamic).
     Carbón frío → ciruela → carbón: la marca ya presente antes de entrar. */
  background: linear-gradient(135deg, #14161c 0%, #572d4b 45%, #14161c 100%);
  position: relative;
  overflow: hidden;
}

/* capa de estrellas animadas */
.login-container::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image:
    radial-gradient(circle, rgba(255,255,255,0.75) 1px, transparent 1px),
    radial-gradient(circle, rgba(147,197,253,0.60) 1px, transparent 1px),
    radial-gradient(circle, rgba(255,255,255,0.45) 1px, transparent 1px),
    radial-gradient(circle, rgba(96,165,250,0.40) 1px, transparent 1px),
    radial-gradient(circle, rgba(255,255,255,0.30) 1px, transparent 1px);
  background-size: 280px 280px, 190px 190px, 130px 130px, 90px 90px, 60px 60px;
  background-position: 15px 25px, 55px 70px, 90px 15px, 130px 110px, 40px 80px;
  animation: starDrift 50s linear infinite;
  pointer-events: none;
  z-index: 0;
}

/* orbs de brillo pulsantes */
.login-container::after {
  content: '';
  position: fixed;
  inset: 0;
  background:
    radial-gradient(ellipse 650px 650px at 78% 8%,  rgba(var(--brand-rgb),0.14) 0%, transparent 65%),
    radial-gradient(ellipse 450px 450px at 22% 92%, rgba(var(--brand-dark-rgb),0.09)  0%, transparent 65%),
    radial-gradient(ellipse 300px 300px at 55% 50%, rgba(var(--brand-rgb),0.06) 0%, transparent 60%);
  pointer-events: none;
  z-index: 0;
  animation: orbPulse 9s ease-in-out infinite alternate;
}

@keyframes starDrift {
  from { background-position: 15px 25px, 55px 70px, 90px 15px, 130px 110px, 40px 80px; }
  to   { background-position: 295px 305px, 245px 260px, 220px 145px, 220px 200px, 100px 140px; }
}

@keyframes orbPulse {
  from { opacity: 0.6; }
  to   { opacity: 1.0; }
}

/* tarjeta */
.login-card {
  width: 100%;
  max-width: 420px;
  padding: 40px;
  border-radius: 24px;
  background: linear-gradient(145deg, rgba(255,255,255,0.98) 0%, rgba(241,245,249,0.95) 100%);
  border: 1px solid rgba(226,232,240,0.8);
  box-shadow:
    0 25px 60px rgba(0,0,0,0.3),
    0 0 0 1px rgba(255,255,255,0.08),
    inset 0 1px 0 rgba(255,255,255,0.9);
  backdrop-filter: blur(20px);
  animation: fadeInUp 0.45s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  z-index: 1;
  /* GPU: compositor handles transform+opacity animation without layout recalc */
  will-change: transform, opacity;
  contain: layout style paint;
}

@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(28px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0)   scale(1);    }
}

/* header */
.login-header {
  text-align: center;
  margin-bottom: 36px;
}

.login-logo {
  width: 52px;
  height: 52px;
  margin: 0 auto 18px;
  background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 100%);
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8px 20px rgba(var(--brand-dark-rgb),0.4), 0 2px 6px rgba(var(--brand-dark-rgb),0.2);
}

.login-logo svg {
  color: #ffffff;
}

.login-header h1 {
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  background: linear-gradient(135deg, var(--brand-dark) 0%, var(--accent) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  margin-bottom: 6px;
}

.login-subtitle {
  color: var(--muted);
  font-size: 0.875rem;
  font-weight: 500;
}

.login-weather-chip {
  margin-top: 16px;
  padding: 12px 14px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.62);
  border: 1px solid rgba(148, 163, 184, 0.22);
  backdrop-filter: blur(12px);
  display: grid;
  gap: 4px;
}

.login-weather-chip__location {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-strong);
}

.login-weather-chip__country {
  font-size: 0.77rem;
  font-weight: 600;
  color: var(--text-strong);
}

.login-weather-chip__status {
  font-size: 0.76rem;
  color: var(--muted);
  font-weight: 500;
}

.login-container--dynamic {
  background: linear-gradient(135deg, #14161c 0%, #572d4b 45%, #14161c 100%);
  transition: background 1.2s ease;
}

/* ── Paleta de cielo por franja horaria (ortogonal al clima) ─────
   El tema (clima) define las partículas; la fase define el cielo.
   Los climas que ocurren a cualquier hora (nublado/lluvia/nieve)
   construyen su fondo sobre estas variables. ─────────────────── */
.login-container--dynamic.phase-dawn  { --sky-a: #f7b27a; --sky-b: #c98a9b; --sky-c: #5b6aa8; }
.login-container--dynamic.phase-day   { --sky-a: #6fa8dc; --sky-b: #9cc3e6; --sky-c: #3d6aa3; }
.login-container--dynamic.phase-dusk  { --sky-a: #e8915b; --sky-b: #9b5e8e; --sky-c: #2e2f5b; }
.login-container--dynamic.phase-night { --sky-a: #050816; --sky-b: #102347; --sky-c: #040a17; }
/* Fallback si por algún motivo no hay clase de fase. */
.login-container--dynamic { --sky-a: #4b5f79; --sky-b: #6f86a3; --sky-c: #2e3d53; }

.login-container--dynamic.theme-default::before {
  background-image:
    radial-gradient(circle, rgba(255,255,255,0.75) 1px, transparent 1px),
    radial-gradient(circle, rgba(147,197,253,0.60) 1px, transparent 1px),
    radial-gradient(circle, rgba(255,255,255,0.45) 1px, transparent 1px),
    radial-gradient(circle, rgba(96,165,250,0.40) 1px, transparent 1px),
    radial-gradient(circle, rgba(255,255,255,0.30) 1px, transparent 1px);
}

.login-container--dynamic.theme-default::after {
  background:
    radial-gradient(ellipse 650px 650px at 78% 8%,  rgba(var(--brand-rgb),0.14) 0%, transparent 65%),
    radial-gradient(ellipse 450px 450px at 22% 92%, rgba(var(--brand-dark-rgb),0.09)  0%, transparent 65%),
    radial-gradient(ellipse 300px 300px at 55% 50%, rgba(var(--brand-rgb),0.06) 0%, transparent 60%);
}

/* El cielo soleado sigue la hora (amanecer/día/atardecer); el sol cambia de
   posición y color por fase vía variables (usadas en `.theme-sunny .wbg-1`). */
.login-container--dynamic.theme-sunny {
  --sun-x: 83%; --sun-y: 16%;
  --sun-core: rgba(255,255,220,1);
  --sun-glow: rgba(255,228,60,0.38);
  background: linear-gradient(160deg, #8ec5ef 0%, #bfe0f5 45%, #ffe9a8 100%);
}
.login-container--dynamic.theme-sunny.phase-day {
  --sun-x: 83%; --sun-y: 16%;
  --sun-core: rgba(255,255,225,1);
  --sun-glow: rgba(255,228,60,0.38);
  background: linear-gradient(160deg, #8ec5ef 0%, #bfe0f5 45%, #ffe9a8 100%);
}
.login-container--dynamic.theme-sunny.phase-dawn {
  --sun-x: 80%; --sun-y: 62%;
  --sun-core: rgba(255,238,205,1);
  --sun-glow: rgba(255,182,120,0.42);
  background: linear-gradient(160deg, #fde7c7 0%, #f7b98a 46%, #e98a86 100%);
}
.login-container--dynamic.theme-sunny.phase-dusk {
  --sun-x: 22%; --sun-y: 64%;
  --sun-core: rgba(255,226,184,1);
  --sun-glow: rgba(255,140,90,0.44);
  background: linear-gradient(160deg, #ffd29b 0%, #ff9e7a 46%, #b5638f 100%);
}

.login-container--dynamic.theme-sunny::before {
  background:
    radial-gradient(circle at 82% 18%, rgba(255, 252, 227, 0.92) 0 10%, rgba(255, 252, 227, 0.25) 18%, transparent 38%),
    radial-gradient(circle at 12% 22%, rgba(255,255,255,0.24) 0 8%, transparent 24%),
    radial-gradient(circle at 22% 30%, rgba(255,255,255,0.16) 0 7%, transparent 22%);
  animation: orbPulse 8s ease-in-out infinite alternate;
}

.login-container--dynamic.theme-sunny::after {
  background:
    linear-gradient(115deg, rgba(255,255,255,0) 20%, rgba(255,255,255,0.18) 44%, rgba(255,255,255,0) 62%),
    radial-gradient(ellipse 560px 220px at 20% 100%, rgba(116, 185, 129, 0.22) 0%, transparent 62%);
  animation: sunnyGlow 9s ease-in-out infinite alternate;
}

.login-container--dynamic.theme-cloudy {
  /* Velo gris translúcido sobre el cielo de la fase → nublado de día u
     oscuro de noche según las variables --sky-*. */
  background:
    linear-gradient(180deg, rgba(120,134,158,0.42) 0%, rgba(70,84,108,0.34) 100%),
    linear-gradient(135deg, var(--sky-a) 0%, var(--sky-b) 45%, var(--sky-c) 100%);
}

.login-container--dynamic.theme-cloudy::before {
  background:
    radial-gradient(ellipse 360px 135px at 18% 22%, rgba(255,255,255,0.22) 0%, transparent 70%),
    radial-gradient(ellipse 460px 155px at 76% 20%, rgba(255,255,255,0.18) 0%, transparent 72%),
    radial-gradient(ellipse 400px 145px at 48% 74%, rgba(255,255,255,0.10) 0%, transparent 70%);
  filter: blur(6px);
  animation: cloudDrift 24s ease-in-out infinite alternate;
}

.login-container--dynamic.theme-cloudy::after {
  background:
    linear-gradient(180deg, rgba(255,255,255,0.06) 0%, rgba(255,255,255,0) 55%),
    radial-gradient(ellipse 580px 360px at 50% 10%, rgba(255,255,255,0.08) 0%, transparent 70%);
}

.login-container--dynamic.theme-rainy {
  /* Cielo de la fase (azul de día … negro de noche) + velo de tormenta
     cuya opacidad sube con la intensidad (clases .intensity-*). */
  --storm: rgba(8,12,24,0.34);
  background:
    linear-gradient(180deg, var(--storm) 0%, var(--storm) 100%),
    linear-gradient(135deg, var(--sky-a) 0%, var(--sky-b) 46%, var(--sky-c) 100%);
}
.login-container--dynamic.theme-rainy.intensity-1 { --storm: rgba(20,28,44,0.20); }
.login-container--dynamic.theme-rainy.intensity-2 { --storm: rgba(12,18,32,0.36); }
.login-container--dynamic.theme-rainy.intensity-3 { --storm: rgba(4,8,18,0.55); }

.login-container--dynamic.theme-rainy::before {
  background-image:
    repeating-linear-gradient(110deg,
      transparent                    0px    7px,
      rgba(174,214,241, 0.44)        7px    8.5px,
      transparent                    8.5px  24px,
      rgba(174,214,241, 0.26)        24px   25.5px,
      transparent                    25.5px 42px);
  background-size: 42px 88px;
  animation: rainFall 0.48s linear infinite;
  opacity: 1;
}

.login-container--dynamic.theme-rainy::after {
  background:
    radial-gradient(ellipse 620px 280px at 50% 100%, rgba(56, 189, 248, 0.16) 0%, transparent 68%),
    radial-gradient(ellipse 260px 260px at 85% 18%, rgba(14, 165, 233, 0.14) 0%, transparent 70%);
}

.login-container--dynamic.theme-snowy {
  /* Aclarado suave sobre el cielo de la fase: nieve luminosa de día,
     más sobria de noche (las variables --sky-* ya oscurecen). */
  background:
    linear-gradient(180deg, rgba(225,236,246,0.34) 0%, rgba(183,202,223,0.20) 100%),
    linear-gradient(135deg, var(--sky-a) 0%, var(--sky-b) 45%, var(--sky-c) 100%);
}

.login-container--dynamic.theme-snowy::before {
  background-image:
    radial-gradient(circle, rgba(255,255,255,0.92) 1.5px, transparent 1.5px),
    radial-gradient(circle, rgba(255,255,255,0.72) 1px, transparent 1px),
    radial-gradient(circle, rgba(255,255,255,0.54) 1.2px, transparent 1.2px);
  background-size: 180px 180px, 120px 120px, 90px 90px;
  background-position: 10px 10px, 70px 40px, 30px 80px;
  animation: snowFall 12s linear infinite;
}

.login-container--dynamic.theme-snowy::after {
  background:
    linear-gradient(180deg, rgba(255,255,255,0.20) 0%, rgba(255,255,255,0.00) 52%),
    radial-gradient(ellipse 440px 200px at 50% 100%, rgba(255,255,255,0.18) 0%, transparent 70%);
}

.login-container--dynamic.theme-night {
  background: linear-gradient(135deg, #050816 0%, #102347 45%, #040a17 100%);
}

.login-container--dynamic.theme-night::before {
  background-image:
    radial-gradient(circle, rgba(255,255,255,0.86) 1px, transparent 1px),
    radial-gradient(circle, rgba(96,165,250,0.58) 1px, transparent 1px),
    radial-gradient(circle, rgba(255,255,255,0.38) 1px, transparent 1px);
  background-size: 220px 220px, 130px 130px, 80px 80px;
  background-position: 15px 25px, 55px 70px, 90px 15px;
  animation: starDrift 40s linear infinite;
}

.login-container--dynamic.theme-night::after {
  /* Sin disco brillante aquí: la luna (con su fase real) la dibuja el canvas.
     Solo queda el ambiente azulado del cielo nocturno. */
  background:
    radial-gradient(ellipse 460px 460px at 78% 10%, rgba(var(--brand-rgb),0.16) 0%, transparent 68%),
    radial-gradient(ellipse 350px 160px at 50% 100%, rgba(var(--brand-dark-rgb),0.10) 0%, transparent 72%);
}

.login-container--dynamic.theme-christmas {
  background: linear-gradient(135deg, #0f2e24 0%, #7f1d1d 52%, #1f2937 100%);
}

.login-container--dynamic.theme-christmas::before {
  background-image:
    radial-gradient(circle, rgba(248, 250, 252, 0.92) 2px, transparent 2px),
    radial-gradient(circle, rgba(248, 113, 113, 0.72) 2.5px, transparent 2.5px),
    radial-gradient(circle, rgba(74, 222, 128, 0.72) 2.5px, transparent 2.5px),
    radial-gradient(circle, rgba(250, 204, 21, 0.76) 2px, transparent 2px);
  background-size: 170px 170px, 210px 210px, 240px 240px, 130px 130px;
  background-position: 18px 40px, 42px 82px, 110px 22px, 84px 124px;
  animation: festiveBokeh 14s ease-in-out infinite alternate;
}

.login-container--dynamic.theme-christmas::after {
  background:
    radial-gradient(ellipse 540px 220px at 50% 100%, rgba(250, 204, 21, 0.14) 0%, transparent 68%),
    linear-gradient(180deg, rgba(255,255,255,0.10) 0%, rgba(255,255,255,0.00) 48%);
}

@keyframes rainFall {
  from { background-position: 0 0; }
  to   { background-position: -42px 88px; }
}

@keyframes snowFall {
  /* background-position animation: each layer moves exactly one tile height = seamless loop */
  from { background-position: 10px  10px, 70px 40px,  30px 80px; }
  to   { background-position: -10px 190px, 55px 160px, 10px 170px; }
}

@keyframes cloudDrift {
  from { transform: translateX(-14px); opacity: 0.8; }
  to { transform: translateX(16px); opacity: 1; }
}

@keyframes sunnyGlow {
  from { opacity: 0.7; transform: translateX(-12px); }
  to { opacity: 1; transform: translateX(12px); }
}

@keyframes festiveBokeh {
  from { opacity: 0.78; transform: scale(1); }
  to { opacity: 1; transform: scale(1.04); }
}

/* ═══════════════════════════════════════════════════════════════
   Weather background animation layers (.wbg)
   3 fixed divs injected by login.rs, z-index: 0, pointer-events: none
   Each theme drives its own wbg-1/2/3 rules.
   ═══════════════════════════════════════════════════════════════ */

.wbg {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

/* ── Selector de fondos (solo modo test) ──────────────────────── */
.bgsel {
  position: fixed;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: min(92vw, 720px);
  padding: 10px 14px;
  border-radius: 14px;
  background: rgba(15, 23, 42, 0.72);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(148, 163, 184, 0.28);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  color: var(--text);
  font-size: 0.78rem;
}
.bgsel__title {
  font-weight: 700;
  font-size: 0.74rem;
  letter-spacing: 0.02em;
  opacity: 0.85;
  margin-bottom: 2px;
}
.bgsel__row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.bgsel__lbl {
  flex: 0 0 70px;
  font-weight: 600;
  opacity: 0.7;
  font-size: 0.72rem;
}
.bgsel__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}
.bgsel__chip {
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid rgba(148, 163, 184, 0.32);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
  font-size: 0.72rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.bgsel__chip:hover {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(148, 163, 184, 0.55);
}
.bgsel__chip.is-active {
  background: var(--brand);
  border-color: var(--brand-soft-fg);
  color: #fff;
}
@media (max-width: 560px) {
  .bgsel__lbl { flex-basis: 100%; }
  .bgsel__row { flex-wrap: wrap; }
}

/* Canvas cloud — sits above .wbg layers; blur matches HTML example */
canvas.login-cloud-canvas {
  position: fixed;
  inset: 0;
  display: block;
  pointer-events: none;
  z-index: 0;
  filter: blur(3px);
}

/* Canvas rain — sits above .wbg layers, below .login-card (z-index:1) */
canvas.login-rain-canvas {
  position: fixed;
  inset: 0;
  display: block;
  pointer-events: none;
  z-index: 0;
}

/* Canvas moon — luna con fase real (tema noche); sobre las estrellas .wbg */
canvas.login-moon-canvas {
  position: fixed;
  inset: 0;
  display: block;
  pointer-events: none;
  z-index: 0;
}

/* ── CLOUDY: puffy cloud formations scrolling across sky ─────── */
/* wbg-1/2 hidden — canvas handles foreground clouds */
.theme-cloudy .wbg-1,
.theme-cloudy .wbg-2 { display: none; }

.theme-cloudy .wbg-1-UNUSED {
  /* Tile 680×210px: two distinct cloud formations built from
     overlapping circles — fewer blur pixels = crisper puffs. */
  background-image:
    /* Cloud A — left formation */
    radial-gradient(circle 78px  at 17% 72%, rgba(228,240,254,0.96) 0%, transparent 80%),
    radial-gradient(circle 62px  at 23% 56%, rgba(228,240,254,0.94) 0%, transparent 78%),
    radial-gradient(circle 70px  at 32% 58%, rgba(228,240,254,0.96) 0%, transparent 78%),
    radial-gradient(circle 56px  at 40% 67%, rgba(220,234,252,0.90) 0%, transparent 78%),
    radial-gradient(circle 48px  at  8% 76%, rgba(210,228,250,0.82) 0%, transparent 78%),
    /* Cloud B — right formation */
    radial-gradient(circle 62px  at 71% 68%, rgba(220,234,252,0.90) 0%, transparent 78%),
    radial-gradient(circle 50px  at 77% 56%, rgba(220,234,252,0.88) 0%, transparent 76%),
    radial-gradient(circle 56px  at 85% 62%, rgba(220,234,252,0.90) 0%, transparent 76%),
    radial-gradient(circle 42px  at 64% 73%, rgba(210,226,248,0.80) 0%, transparent 78%),
    radial-gradient(circle 36px  at 93% 70%, rgba(210,226,248,0.76) 0%, transparent 76%);
  background-size: 680px 210px;
  background-repeat: repeat-x;
  filter: blur(2.5px);
  opacity: 0.94;
  animation: cloudRoll1 32s linear infinite;
}

.theme-cloudy .wbg-2 {
  /* Larger background clouds, slower + softer */
  background-image:
    radial-gradient(circle 115px at 17% 62%, rgba(195,218,240,0.80) 0%, transparent 78%),
    radial-gradient(circle  98px at 26% 50%, rgba(195,218,240,0.76) 0%, transparent 76%),
    radial-gradient(circle 108px at 36% 55%, rgba(195,218,240,0.78) 0%, transparent 76%),
    radial-gradient(circle  88px at 46% 63%, rgba(185,210,236,0.72) 0%, transparent 76%),
    radial-gradient(circle  92px at 70% 60%, rgba(190,212,238,0.74) 0%, transparent 74%),
    radial-gradient(circle  80px at 79% 50%, rgba(190,212,238,0.70) 0%, transparent 74%),
    radial-gradient(circle  88px at 88% 58%, rgba(190,212,238,0.72) 0%, transparent 74%),
    radial-gradient(circle  72px at 60% 66%, rgba(182,206,234,0.68) 0%, transparent 74%);
  background-size: 900px 280px;
  background-repeat: repeat-x;
  filter: blur(10px);
  opacity: 0.72;
  animation: cloudRoll2 56s linear infinite;
}

.theme-cloudy .wbg-3 {
  background:
    linear-gradient(180deg, rgba(52,72,98,0.62) 0%, rgba(52,72,98,0.18) 26%, transparent 50%),
    linear-gradient(  0deg, rgba(40,58,82,0.32) 0%, transparent 16%);
}

@keyframes cloudRoll1 {
  from { background-position:    0px   70px; }
  to   { background-position:  680px   70px; }
}
@keyframes cloudRoll2 {
  from { background-position: -200px 230px; }
  to   { background-position:  700px 230px; }
}

/* ── RAINY: drizzle + storm clouds + lightning flash ─────────── */

.theme-rainy .wbg-1 {
  /* Very faint background rain — Canvas handles the foreground drops */
  background-image:
    repeating-linear-gradient(107deg,
      transparent            0px   13px,
      rgba(174,214,241,0.09) 13px  14px,
      transparent            14px  42px);
  background-size: 42px 125px;
  animation: rainWbgB 0.72s linear infinite;
  opacity: 0.7;
}

/* Relámpago: solo en lluvia fuerte/tormenta (intensity-3). */
.theme-rainy .wbg-2 { display: none; }
.theme-rainy.intensity-3 .wbg-2 {
  display: block;
  /* Lightning flash: long silence then rapid double-strike (18s cycle). */
  background: linear-gradient(155deg,
    rgba(205,232,255,0.94) 0%,
    rgba(225,244,255,0.65) 40%,
    transparent 78%);
  opacity: 0;
  animation: lightningFlash 18s ease-in 0s infinite;
}

.theme-rainy .wbg-3 {
  /* Oscurecimiento superior que sigue la intensidad (--storm). Las nubes
     volumétricas las dibuja el canvas según la fase (claras de día,
     oscuras de noche), por eso aquí solo damos profundidad al cielo. */
  background:
    linear-gradient(180deg, var(--storm) 0%, transparent 46%);
  opacity: 0.9;
  animation: stormDrift 50s ease-in-out infinite alternate;
}

@keyframes lightningFlash {
  /* Double-strike at 70% of 18s = 12.6s. Total duration ≈ 324ms. */
  0%,   70%,   70.8%, 71%,   71.8%, 100% { opacity: 0; }
  70.3%                                   { opacity: 0.90; }
  70.5%                                   { opacity: 0.28; }
  71.3%                                   { opacity: 0.74; }
  71.6%                                   { opacity: 0.10; }
}

@keyframes rainWbgB {
  from { background-position: 0 0; }
  to   { background-position: -42px 125px; }
}

@keyframes stormDrift {
  from { transform: translateX(-18px); }
  to   { transform: translateX( 18px); }
}

/* ── SUNNY: sun disc + ray corona + wispy clouds ─────────────── */

.theme-sunny .wbg-1 {
  /* Sun disc + inner corona + outer halo + conic ray spikes.
     Posición (--sun-x/--sun-y) y color (--sun-core/--sun-glow) varían por fase. */
  background:
    radial-gradient(circle 52px  at var(--sun-x) var(--sun-y), var(--sun-core) 0%, rgba(255,248,140,0.90) 35%, transparent 58%),
    radial-gradient(circle 118px at var(--sun-x) var(--sun-y), var(--sun-glow)  0%, transparent 62%),
    radial-gradient(circle 245px at var(--sun-x) var(--sun-y), rgba(255,210,20,0.14)  0%, transparent 78%),
    repeating-conic-gradient(from 0deg at var(--sun-x) var(--sun-y),
      rgba(255,240,80,0.28) 0deg 4deg,
      transparent 4deg 22deg);
  animation: sunBeat 5.5s ease-in-out infinite alternate;
}

.theme-sunny .wbg-2 {
  /* Wispy cloud tile 920×130px drifting left to right */
  background-image:
    radial-gradient(ellipse 190px 58px at 22% 66%, rgba(255,255,255,0.64) 0%, transparent 72%),
    radial-gradient(ellipse 148px 44px at 36% 58%, rgba(255,255,255,0.52) 0%, transparent 70%),
    radial-gradient(ellipse 168px 52px at 57% 62%, rgba(255,255,255,0.58) 0%, transparent 70%),
    radial-gradient(ellipse 132px 40px at 72% 58%, rgba(255,255,255,0.50) 0%, transparent 68%),
    radial-gradient(ellipse 155px 48px at 90% 65%, rgba(255,255,255,0.56) 0%, transparent 70%);
  background-size: 920px 130px;
  background-repeat: repeat-x;
  filter: blur(3px);
  opacity: 0.82;
  animation: cloudRollSun 70s linear infinite;
}

.theme-sunny .wbg-3 {
  background:
    linear-gradient(0deg, rgba(255,175,55,0.14) 0%, transparent 32%),
    radial-gradient(ellipse 340px 240px at 92% 12%, rgba(255,255,220,0.20) 0%, transparent 70%);
  animation: sunHaze 9s ease-in-out infinite alternate;
}

@keyframes cloudRollSun {
  from { background-position:    0px 160px; }
  to   { background-position:  920px 160px; }
}
@keyframes sunBeat {
  from { opacity: 0.80; transform: scale(0.97); }
  to   { opacity: 1.00; transform: scale(1.03); }
}
@keyframes sunHaze {
  from { opacity: 0.68; }
  to   { opacity: 1.00; }
}

/* ── SNOWY: layered snowflakes + cloud ceiling ───────────────── */

.theme-snowy .wbg-1 {
  background-image:
    radial-gradient(circle 3.8px at 20% 50%, rgba(255,255,255,0.92) 0%, transparent 100%),
    radial-gradient(circle 2.6px at 68% 50%, rgba(255,255,255,0.82) 0%, transparent 100%);
  background-size: 210px 210px, 140px 140px;
  animation: snowWbgA 18s linear infinite;
}

.theme-snowy .wbg-2 {
  background-image:
    radial-gradient(circle 2.0px at 42% 50%, rgba(255,255,255,0.78) 0%, transparent 100%),
    radial-gradient(circle 1.6px at 74% 50%, rgba(255,255,255,0.66) 0%, transparent 100%);
  background-size: 115px 115px, 78px 78px;
  animation: snowWbgB 10s linear infinite;
  opacity: 0.85;
}

.theme-snowy .wbg-3 {
  /* Soft cloud ceiling at top + ground glow at bottom */
  background:
    radial-gradient(ellipse 460px 130px at 28% 8%,  rgba(222,236,252,0.48) 0%, transparent 72%),
    radial-gradient(ellipse 380px 110px at 74% 5%,  rgba(222,236,252,0.42) 0%, transparent 70%),
    linear-gradient(  0deg, rgba(215,232,250,0.40) 0%, transparent 24%),
    linear-gradient(180deg, rgba(200,222,244,0.22) 0%, transparent 14%);
}

@keyframes snowWbgA {
  from { background-position:  35px   0px,  85px  60px; }
  to   { background-position: -10px 210px,  60px 200px; }
}
@keyframes snowWbgB {
  from { background-position:  58px  22px,  22px  42px; }
  to   { background-position:  20px 137px,  -2px 120px; }
}

/* ── NIGHT: dense starfield + nebula + moon disc ─────────────── */

.theme-night .wbg-1 {
  background-image:
    radial-gradient(circle 1.5px at 50% 50%, rgba(255,255,255,0.90) 0%, transparent 100%),
    radial-gradient(circle 1.0px at 50% 50%, rgba(200,220,255,0.78) 0%, transparent 100%),
    radial-gradient(circle 1.2px at 50% 50%, rgba(255,255,255,0.62) 0%, transparent 100%);
  background-size: 225px 225px, 145px 145px, 88px 88px;
  background-position: 18px 28px, 58px 72px, 40px 14px;
  animation: starTwinkleWbg 5s ease-in-out infinite alternate;
}

.theme-night .wbg-2 {
  background:
    radial-gradient(ellipse 720px 420px at 62% 32%, rgba(22,46,110,0.24) 0%, transparent 74%),
    radial-gradient(ellipse 380px 260px at 16% 74%, rgba(10,22,90,0.15)  0%, transparent 66%);
  animation: nebulaPulse 15s ease-in-out infinite alternate;
}

.theme-night .wbg-3 {
  /* Solo el resplandor azulado de fondo; el disco lunar lo dibuja el canvas
     (login-moon-canvas) con la fase real y su propio halo. */
  background:
    radial-gradient(ellipse 620px 480px at 50% 0%, rgba(var(--brand-rgb),0.12) 0%, transparent 72%);
}

@keyframes starTwinkleWbg {
  from { opacity: 0.60; }
  to   { opacity: 1.00; }
}
@keyframes nebulaPulse {
  from { opacity: 0.48; transform: scale(1.00); }
  to   { opacity: 1.00; transform: scale(1.06); }
}

/* ── CHRISTMAS: snowfall + bokeh + festive corner glow ──────── */

.theme-christmas .wbg-1 {
  background-image:
    radial-gradient(circle 2.8px at 20% 50%, rgba(255,255,255,0.90) 0%, transparent 100%),
    radial-gradient(circle 1.8px at 66% 50%, rgba(255,255,255,0.76) 0%, transparent 100%);
  background-size: 160px 160px, 102px 102px;
  animation: snowWbgA 14s linear infinite;
  opacity: 0.90;
}

.theme-christmas .wbg-2 {
  background-image:
    radial-gradient(circle 16px at 50% 50%, rgba(248,113,113,0.74) 0%, transparent 100%),
    radial-gradient(circle 12px at 50% 50%, rgba(74,222,128,0.70)  0%, transparent 100%),
    radial-gradient(circle 18px at 50% 50%, rgba(250,204,21,0.76)  0%, transparent 100%),
    radial-gradient(circle 11px at 50% 50%, rgba(248,250,252,0.84) 0%, transparent 100%);
  background-size: 225px 225px, 182px 182px, 265px 265px, 138px 138px;
  background-position: 44px 84px, 112px 24px, 86px 126px, 20px 42px;
  filter: blur(2px);
  animation: festiveBokeh 14s ease-in-out infinite alternate;
  opacity: 0.82;
}

.theme-christmas .wbg-3 {
  background:
    radial-gradient(ellipse 620px 295px at 16% 90%, rgba(12,68,34,0.28)  0%, transparent 72%),
    radial-gradient(ellipse 520px 260px at 84% 94%, rgba(110,12,12,0.23) 0%, transparent 68%),
    linear-gradient(180deg, rgba(255,255,255,0.08) 0%, transparent 16%);
}

/* ── DEFAULT: subtle orb depth ──────────────────────────────── */
.theme-default .wbg-2 {
  background:
    radial-gradient(ellipse 500px 350px at 72% 28%, rgba(var(--brand-dark-rgb),0.08)  0%, transparent 68%),
    radial-gradient(ellipse 380px 280px at 28% 80%, rgba(var(--brand-dark-rgb),0.06) 0%, transparent 65%);
  animation: nebulaPulse 18s ease-in-out infinite alternate;
}

/* campos */
.form-field {
  margin-bottom: 20px;
}

.form-field label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 8px;
}

/* La tarjeta de login es SIEMPRE clara (independiente del tema), así que sus
   labels deben ser oscuros en ambos temas para no quedar blancos sobre blanco. */
.login-card .form-field label { color: var(--text-strong); }

/* Selects en tema OSCURO (base): sin `color` explícito el texto del <select> y
   su popup nativo de <option> caen al negro por defecto del navegador → invisibles
   sobre superficie oscura. Damos color legible y un popup casi opaco. En claro,
   los overrides con !important de `html.light-mode select` ganan a esta base. */
select { color: var(--text); }
select option {
  background: var(--surface-overlay);
  color: var(--text);
}

.input-wrapper {
  position: relative;
  display: flex;
  align-items: center;
}

.input-wrapper input {
  width: 100%;
  padding: 14px 46px 14px 14px;
  border-radius: 12px;
  border: 2px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
  font-size: 0.9375rem;
  font-family: inherit;
  font-weight: 400;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: var(--shadow-sm);
}

.input-wrapper input::placeholder {
  color: var(--muted-2);
  font-weight: 400;
}

.input-wrapper input:focus {
  outline: none;
  border-color: var(--brand-dark);
  box-shadow: 0 0 0 3px rgba(var(--brand-dark-rgb),0.1), var(--shadow-sm);
  transform: translateY(-1px);
  background: var(--surface);
}

.input-icon {
  position: absolute;
  right: 14px;
  color: var(--muted-2);
  pointer-events: none;
  transition: color 0.2s ease;
  display: flex;
  align-items: center;
}

.input-wrapper:focus-within .input-icon {
  color: var(--brand);
}

.otp-input-group {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 44px));
  gap: 10px;
  justify-content: start;
}

.pin-box {
  width: 44px;
  height: 52px;
  padding: 0;
  text-align: center;
  border-radius: 12px;
  border: 2px solid var(--border);
  background: var(--surface-2);
  color: var(--text-strong);
  font-size: 1.2rem;
  font-weight: 700;
  font-family: inherit;
  box-shadow: var(--shadow-sm);
  transition:
    transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1),
    border-color 0.2s ease,
    box-shadow 0.2s ease,
    background 0.2s ease;
}

.pin-box::placeholder {
  color: var(--muted-2);
  opacity: 0.7;
}

.pin-box.filled {
  border-color: var(--amber, #d97706);
  background: color-mix(in srgb, var(--surface) 86%, #f59e0b 14%);
}

.pin-box.pop {
  transform: scale(1.14);
}

.pin-box:focus {
  border-color: var(--amber, #d97706);
  outline: none;
  box-shadow: 0 0 0 3px rgba(217, 119, 6, 0.16), var(--shadow-sm);
  background: var(--surface);
}

.password-toggle {
  position: absolute;
  right: 10px;
  background: none;
  border: none;
  color: var(--muted-2);
  cursor: pointer;
  padding: 6px;
  border-radius: 8px;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}

.password-toggle:hover {
  color: var(--text);
  background: rgba(var(--brand-dark-rgb),0.08);
}

/* botón submit */
.btn-login {
  width: 100%;
  padding: 15px 24px;
  border-radius: 12px;
  font-size: 0.9375rem;
  font-weight: 600;
  font-family: inherit;
  letter-spacing: 0.015em;
  background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 100%);
  color: white;
  border: none;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(var(--brand-dark-rgb),0.45), 0 1px 3px rgba(var(--brand-dark-rgb),0.3);
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  margin-top: 12px;
}

/* ── Ticket module layout ───────────────────────────────── */

/* Márgenes del módulo: el contenido no debe pegarse a los bordes de la pantalla. */
.tickets-content { padding: 24px 32px; }

/* ── Toolbar reutilizable de tickets (bandeja + tipos) ─────────────────────── */
.tickets-toolbar {
  position: relative;               /* ancla del popover de filtros */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}
/* Botón de acción (crear/nuevo) DENTRO de la toolbar, pegado a la derecha y
   alineado verticalmente con la barra de búsqueda. margin-left:auto absorbe el
   espacio libre aunque el wrap del popover (invisible, 0px) quede en medio. */
.tickets-toolbar-action { margin-left: auto; flex-shrink: 0; }
.tickets-toolbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* ── TOOLBAR ESTÁNDAR (components/toolbar.rs) ─────────────────────────────────
   Controles compartidos por todos los módulos: segmented de vista, desplegable de
   orden y modo selección. El resto (pastilla de búsqueda, embudo, limpiar,
   refrescar) reutiliza las clases .contacts-* de este mismo bloque. */
.tb-actions { display: inline-flex; gap: 8px; align-items: center; }

.tb-viewseg {
  display: inline-flex; gap: 2px; padding: 3px; margin: 0 4px;
  border: 1px solid var(--control-border); border-radius: 999px;
  background: var(--surface-2);
}
.tb-viewseg-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 26px; padding: 0;
  border: 0; border-radius: 999px; background: transparent; color: var(--muted);
  cursor: pointer; transition: background 0.15s, color 0.15s;
}
.tb-viewseg-btn:hover:not(.is-active) { color: var(--text); }
.tb-viewseg-btn.is-active { background: var(--brand-dark); color: #fff; }

.tb-sort { position: relative; display: inline-flex; }
.tb-menu-pop {
  position: absolute; top: 38px; left: 0; z-index: 60;
  min-width: 210px; padding: 5px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface-overlay); box-shadow: var(--shadow-lg);
}
.tb-menu-item {
  display: block; width: 100%; text-align: left;
  padding: 8px 10px; border: 0; border-radius: 7px;
  background: transparent; color: var(--text);
  font-family: inherit; font-size: 0.82rem; font-weight: 600; cursor: pointer;
}
.tb-menu-item:hover { background: var(--control-bg-hover); }
.tb-menu-item.is-active { color: var(--brand); }

/* Toast de progreso de una acción masiva: barra + "hechos / total". Flota abajo a
   la derecha para no tapar la tabla sobre la que se está actuando. */
.tb-toast {
  position: fixed; right: 22px; bottom: 22px; z-index: 900;
  width: min(320px, calc(100vw - 44px));
  padding: 12px 14px;
  border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface-overlay); box-shadow: var(--shadow-lg);
  animation: tb-toast-in 0.18s ease both;
}
@keyframes tb-toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.tb-toast-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.tb-toast-label { font-size: 0.84rem; font-weight: 700; color: var(--text-strong); }
.tb-toast-count { font-size: 0.8rem; font-weight: 700; color: var(--brand); font-variant-numeric: tabular-nums; }
.tb-toast-track {
  margin: 8px 0 6px; height: 6px; border-radius: 999px;
  background: var(--surface-2); overflow: hidden;
}
.tb-toast-fill {
  height: 100%; border-radius: 999px;
  background: var(--brand-dark);
  transition: width 0.2s ease;
}
.tb-toast.is-done .tb-toast-fill { background: var(--badge-success-fg); }
.tb-toast-foot { font-size: 0.76rem; color: var(--muted); }

/* Popover de filtros (se renderiza siempre, se muestra/oculta por CSS).
   position:absolute lo saca del flujo flex → abrir filtros NO mueve los botones. */
.tickets-filters-wrap { display: none; position: absolute; top: 0; left: 0; }
.tickets-filters-wrap.is-open { display: block; }
/* El wrap (absolute, sin ancho) hacía que el `calc(100% - 64px)` del popover
   colapsara → popover aplastado. Se fija el ancho por viewport, no por el wrap. */
.tickets-toolbar .tickets-filters-popover,
.monitor-toolbar .tickets-filters-popover { top: 52px; left: 0; width: min(420px, calc(100vw - 48px)); }
.monitor-toolbar .tickets-filters-popover { top: 44px; }

/* Botones de la toolbar de tickets al mismo tamaño que Añadir/Exportar (bandeja,
   tipos e interno): Limpiar (btn-sm) y Asignar (btn-secondary) quedaban dispares. */
.tickets-toolbar-right .btn-secondary,
.monitor-toolbar .btn-secondary {
  padding: 8px 16px;
  border-radius: 9px;
  font-size: 0.8125rem;
}

/* Recarga acotada a la tabla: se atenúa mientras llega la nueva página, sin
   desmontar toda la vista (tabs/toolbar quedan). */
.tk-reloading { opacity: 0.55; pointer-events: none; transition: opacity 0.12s ease; }

/* Recarga en curso a nivel de módulo. Se atenúan los HIJOS, no el contenedor:
   `opacity` en el padre tiñe todo el subárbol y ningún hijo puede recuperarse,
   así que la toolbar (`.tb`) y las tabs se apagarían con la tabla — y la toolbar
   es justo lo que tiene que seguir vivo, porque su icono girando es el aviso de
   que algo se está cargando. Con esto, al pulsar refrescar se ve recargarse lo
   que sea que muestre el módulo: tabla, tarjetas o gráficas. */
.is-reloading > *:not(.tb):not(nav):not(.error-banner) {
  opacity: 0.55;
  pointer-events: none;
  transition: opacity 0.12s ease;
}
/* Celdas centradas verticalmente: los badges (SLA/estado) hacían que el texto
   plano (Asunto/Cliente) se viera "subido". */
.tickets-table td { vertical-align: middle; }
/* Fila clickeable → abre el detalle (se elimino la accion "Ver"). */
.tk-row-click { cursor: pointer; }
.tk-row-click:hover > td { background: rgba(255, 255, 255, 0.03); }

/* Scrollbar de los modales/forms: overlay auto-oculta, estilo sidebar (no una
   barra "metida" invasiva). Se revela solo al pasar el puntero por encima. */
.modal-card, .ticket-manage-form, .ticket-panel-scroll {
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
}
.modal-card:hover, .ticket-manage-form:hover, .ticket-panel-scroll:hover {
  scrollbar-color: var(--control-border) transparent;
}
.modal-card::-webkit-scrollbar,
.ticket-manage-form::-webkit-scrollbar,
.ticket-panel-scroll::-webkit-scrollbar { width: 10px; }
.modal-card::-webkit-scrollbar-thumb,
.ticket-manage-form::-webkit-scrollbar-thumb,
.ticket-panel-scroll::-webkit-scrollbar-thumb {
  background: transparent;
  border: 3px solid transparent;
  background-clip: padding-box;
  border-radius: 8px;
  transition: background-color 0.2s ease;
}
.modal-card:hover::-webkit-scrollbar-thumb,
.ticket-manage-form:hover::-webkit-scrollbar-thumb,
.ticket-panel-scroll:hover::-webkit-scrollbar-thumb {
  background: var(--control-border);
  background-clip: padding-box;
}
/* Selects dentro del popover de filtros (compartido contactos/tickets) */
.contacts-filters-popover select {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--control-border);
  border-radius: 9px;
  font: inherit;
  font-size: 0.85rem;
  background: var(--control-bg);
  color: var(--text);
  cursor: pointer;
}
.contacts-filters-popover select:focus { outline: none; border-color: var(--brand); }
.contacts-filters-popover .ticket-daterange { flex-wrap: wrap; }
/* Inputs de texto dentro del popover (Monitor filtra por User ID ahí). */
.contacts-filters-popover input[type="text"] {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--control-border);
  border-radius: 9px;
  font: inherit;
  font-size: 0.85rem;
  background: var(--control-bg);
  color: var(--text);
}
.contacts-filters-popover input[type="text"]:focus { outline: none; border-color: var(--brand); }

/* Dropdown Exportar (CSV / Excel) */
.tickets-export { position: relative; }
.btn-export-main {
  padding: 8px 14px;
  border: 1px solid var(--control-border);
  border-radius: 9px;
  background: var(--control-bg);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s;
}
.btn-export-main:hover:not(:disabled) { background: var(--control-bg-hover); border-color: var(--brand); }
.btn-export-main:disabled { opacity: 0.55; cursor: not-allowed; }
.tickets-export-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 42;
  min-width: 140px;
  display: flex;
  flex-direction: column;
  padding: 5px;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-md);
}
.tickets-export-menu button {
  text-align: left;
  padding: 8px 12px;
  border: none;
  border-radius: 7px;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}
.tickets-export-menu button:hover { background: var(--control-bg-hover); }

/* Cards de resumen clickeables: elevación leve + cursor pointer */
.ticket-summary-card--clickable {
  cursor: pointer;
  transition: transform 0.15s, box-shadow 0.15s;
}
.ticket-summary-card--clickable:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.ticket-summary-card--clickable.is-active { box-shadow: 0 0 0 2px var(--brand); }

/* Modal de confirmación de export */
.export-confirm-body { padding: 18px 20px; color: var(--text); font-size: 0.9rem; }

/* ── Form de nuevo ticket: canal Otro, picker de contacto, uploader ───────── */
.channel-other-input { margin-top: 8px; }

/* Picker de contacto (paginado + búsqueda) */
.ticket-contact-picker { position: relative; display: flex; gap: 8px; align-items: stretch; }
.ticket-contact-userid { flex: 1 1 auto; }
.ticket-contact-search-btn {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
  white-space: nowrap;
}
.ticket-picker-backdrop {
  position: fixed; inset: 0; z-index: 40; background: transparent;
}
.ticket-picker-pop {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 41;
  background: var(--surface-overlay); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; overflow: hidden;
}
.ticket-picker-search {
  margin: 8px; width: calc(100% - 16px);
}
.ticket-picker-list {
  max-height: 240px; overflow-y: auto; display: flex; flex-direction: column;
  padding: 4px; gap: 2px;
  /* barra de scroll estilo sidebar (auto-oculta) */
  scrollbar-width: thin; scrollbar-color: transparent transparent;
}
.ticket-picker-list:hover { scrollbar-color: var(--control-border) transparent; }
.ticket-picker-list::-webkit-scrollbar { width: 8px; }
.ticket-picker-list::-webkit-scrollbar-thumb {
  background: transparent; border: 2px solid transparent; background-clip: padding-box;
  border-radius: 8px;
}
.ticket-picker-list:hover::-webkit-scrollbar-thumb { background: var(--control-border); background-clip: padding-box; }
.ticket-picker-item {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  padding: 8px 10px; border: none; border-radius: 7px; background: transparent;
  color: var(--text); text-align: left; cursor: pointer; width: 100%;
  transition: background 0.12s ease;
}
.ticket-picker-item:hover { background: var(--control-bg-hover); }
.ticket-picker-item-name { font-weight: 600; font-size: 0.88rem; }
.ticket-picker-item-sub { font-size: 0.76rem; color: var(--muted); }
.ticket-picker-empty, .ticket-picker-loading {
  padding: 14px; text-align: center; color: var(--muted); font-size: 0.82rem;
}

/* Uploader de evidencias + chips */
.evidence-uploader { display: flex; gap: 10px; align-items: stretch; flex-wrap: wrap; }
.evidence-upload-btn {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 8px 14px; border-radius: 8px; border: 1px solid var(--control-border);
  background: var(--control-bg); color: var(--text); font-size: 0.85rem;
  white-space: nowrap; transition: background 0.12s ease;
}
.evidence-upload-btn:hover { background: var(--control-bg-hover); }
.evidence-upload-btn.is-busy { opacity: 0.6; pointer-events: none; }
.evidence-url-row { display: flex; gap: 8px; flex: 1 1 240px; }
.evidence-url-input { flex: 1 1 auto; }
.evidence-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.evidence-chip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 4px 6px 4px 10px; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: 0.8rem; color: var(--text);
}
.evidence-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.evidence-chip-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border: none; border-radius: 50%;
  background: transparent; color: var(--muted); cursor: pointer; font-size: 1rem;
  line-height: 1; transition: background 0.12s ease, color 0.12s ease;
}
.evidence-chip-x:hover { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }

/* Limpiar filtros en la toolbar de contactos */
.contacts-toolbar-right { display: flex; align-items: center; gap: 10px; }

.ticket-summary-grid,
.ticket-grid {
  display: grid;
  gap: 14px;
}

.ticket-summary-grid {
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  margin-bottom: 20px;
}

.ticket-grid {
  grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr);
  align-items: start;
}

.ticket-grid-single {
  grid-template-columns: minmax(0, 1fr);
}

/* ── Ticket tabs — pill-group ───────────────────────────── */

/* ── SEGMENTED CONTROL ESTÁNDAR ───────────────────────────────────────────────
   UN solo dialecto de barra de tabs para todo el panel. El de Tickets es el
   canónico; el resto de módulos se cuelga de estas mismas reglas en vez de
   inventar tamaños/colores propios.

   Para sumar una barra nueva: añade su clase de contenedor a las tres listas de
   abajo, a la lista `SEL` del script del pill en index.html, y a los grupos de
   `html.light-mode` (buscar "SEGMENTED"). No dupliques declaraciones.

   El resaltado del tab activo lo pinta el pill deslizante (.slide-tabs::after),
   que anula el `background` del botón activo — el `background` de aquí es solo
   el fallback de si el script no ha corrido todavía. */
.ticket-tabs,
.camp-tabs,
.metrics-tabs,
.metrics-agent-tabs,
.notif-tabs,
.mir-tabs {
  display: flex;
  gap: 3px;
  margin-bottom: 20px;
  background: var(--surface-2);
  border-radius: 999px;                        /* pill moderno (como el buscador) */
  padding: 5px;
  width: fit-content;
  max-width: 100%;
  overflow-x: auto;
}

.ticket-tabs button,
.camp-tab,
.metrics-tab,
.metrics-subtab,
.notif-tabs button,
.mir-tab {
  appearance: none;
  border: 0;
  border-radius: 999px;                        /* pill moderno */
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  padding: 8px 18px;
  white-space: nowrap;
  transition: all 0.15s;
}

.ticket-tabs button:not(.active):hover,
.camp-tab:not(.camp-tab--active):hover,
.metrics-tab:not(.active):hover,
.metrics-subtab:not(.active):hover,
.notif-tabs button:not(.active):hover,
.mir-tab:not(.active):hover {
  background: var(--control-bg-hover);
  color: var(--text);
}

.ticket-tabs button.active,
.camp-tab--active,
.metrics-tab.active,
.metrics-subtab.active,
.notif-tabs button.active,
.mir-tab.active {
  background: var(--brand-dark) !important;
  color: #fff !important;
  box-shadow: 0 1px 4px rgba(0,0,0,0.12);
}

/* ── Toolbar ────────────────────────────────────────────── */

.ticket-toolbar {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 180px));
  gap: 10px;
  align-items: center;
  margin-bottom: 16px;
}

/* Toolbar de la bandeja: nº variable de selects (el de "cerrado" y los desenlaces
   solo existen para supervisor/owner) → flex con wrap, no grid de columnas fijas.
   Los botones van pegados a la derecha; si no caben, envuelven a otra fila. */
.ticket-toolbar-wide {
  display: flex;
  flex-wrap: wrap;
}

.ticket-toolbar-wide select {
  width: auto;
  min-width: 150px;
  flex: 0 1 190px;
}

.ticket-toolbar-wide .ticket-toolbar-actions {
  margin-left: auto;
  flex: 0 0 auto;
}

/* Cada filtro con su título arriba, para que se entienda qué hace. */
.ticket-filter-field {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 0 1 190px;
}
.ticket-filter-field > select {
  width: 100%;
  min-width: 0;
  flex: none;
}
.ticket-filter-label {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.02em;
}

/* Segunda fila de filtros de la bandeja: búsqueda por texto + rango de fechas. */
.ticket-subfilters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin: -4px 0 16px;
}
.ticket-search {
  flex: 1 1 240px;
  min-width: 200px;
  min-height: 38px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-family: inherit;
  font-size: 0.9rem;
  color: var(--text);
  background: var(--control-bg);
}
.ticket-search:focus { outline: none; border-color: var(--brand); }
.ticket-daterange {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-size: 0.85rem;
}
/* datetime-local además de date: Monitor filtra ejecuciones con hora. */
.ticket-daterange input[type="date"],
.ticket-daterange input[type="datetime-local"] {
  min-height: 38px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-family: inherit;
  font-size: 0.85rem;
  color: var(--text);
  background: var(--control-bg);
  min-width: 0;
}
.ticket-daterange input[type="date"]:focus,
.ticket-daterange input[type="datetime-local"]:focus { outline: none; border-color: var(--brand); }
.ticket-clear-dates {
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--muted);
  border-radius: 6px;
  width: 26px;
  height: 26px;
  cursor: pointer;
  line-height: 1;
}
.ticket-clear-dates:hover { background: var(--control-bg-hover); color: var(--text); }
.ticket-created { color: var(--muted); font-size: 0.82rem; white-space: nowrap; }

/* Bandeja: layout fijo para respetar el colgroup y que nada se desborde. */
.tickets-table { table-layout: fixed; width: 100%; }
.tickets-table th,
.tickets-table td {
  padding: 11px 10px;
  vertical-align: middle;
  white-space: normal;
  word-break: normal;
  overflow-wrap: break-word;   /* parte solo si no hay espacio/guion; sin cortes a media palabra */
  overflow: hidden;
  text-overflow: ellipsis;
}
.tickets-table td > .ticket-cell-text,
.tickets-table td > .ticket-cell-clamp {
  display: block;
  max-width: 100%;
  overflow: hidden;
}
.tickets-table td > .ticket-cell-text {
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* REF: código monoespaciado; quiebra limpio en el guion si no cabe */
.tickets-table td.tk-ref {
  font-family: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
  font-size: 0.75rem;
  letter-spacing: -0.02em;
  color: var(--muted-2);
}
/* ASUNTO: máximo 2 líneas con elipsis. El clamp va en un SPAN interno para que el
   <td> siga siendo table-cell y `vertical-align: middle` centre el texto (con el
   clamp en el td, `display:-webkit-box` rompía el centrado → texto "levantado"). */
.tickets-table td.tk-subject { white-space: normal; }
.tickets-table td.tk-subject .tk-subject-txt {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* CREADO: fecha+hora en una sola línea */
.tickets-table td.ticket-created { white-space: nowrap; font-size: 0.8rem; color: var(--muted-2); }
/* Paginación de la bandeja: layout en el bloque canónico (buscar "PAGINACIÓN
   ESTÁNDAR"). Aquí solo la agrupación de los controles. */
.ticket-pagination-controls { display: flex; align-items: center; gap: 12px; }

.ticket-toolbar-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.ticket-toolbar select {
  width: 100%;
  padding: 9px 12px;
  border: 1.5px solid var(--border);
  border-radius: 9px;
  font: inherit;
  font-size: 0.85rem;
  background: var(--control-bg);
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.15s;
}

.ticket-toolbar select:focus {
  outline: none;
  border-color: var(--brand-dark);
}

/* Toolbar de filtros de las pestañas Tipos/Planes: layout flex (no el grid de la
   bandeja), para que la búsqueda se estire y los selects/botón queden alineados. */
.ticket-toolbar--filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.ticket-toolbar--filters > select {
  width: auto;
  min-width: 150px;
  flex: 0 1 190px;
}
.ticket-toolbar--filters > input[type="text"] {
  flex: 1 1 200px;
  min-width: 160px;
  min-height: 38px;
  padding: 9px 12px;
  border: 1.5px solid var(--border);
  border-radius: 9px;
  font: inherit;
  font-size: 0.85rem;
  background: var(--control-bg);
  color: var(--text);
}
.ticket-toolbar--filters > input[type="text"]:focus { outline: none; border-color: var(--brand-dark); }
.ticket-toolbar--filters > button { flex: 0 0 auto; }

/* Reasignar departamento (reclasificar) en el detalle del ticket */
.ticket-reassign-hint { color: var(--muted); font-size: 0.84rem; margin-bottom: 8px; }
.ticket-reassign-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.ticket-reassign-select {
  flex: 1 1 220px;
  min-width: 180px;
  min-height: 38px;
  padding: 9px 12px;
  border: 1.5px solid var(--border);
  border-radius: 9px;
  font: inherit;
  font-size: 0.88rem;
  background: var(--control-bg);
  color: var(--text);
  cursor: pointer;
}
.ticket-reassign-select:focus { outline: none; border-color: var(--brand-dark); }
.ticket-reassign-btn { flex: 0 0 auto; }
.ticket-reassign-error { color: var(--badge-danger-fg); font-size: 0.82rem; margin-top: 8px; }

.ticket-toolbar button,
.ticket-toolbar-actions button {
  padding: 9px 16px;
  border: 1.5px solid var(--border);
  border-radius: 9px;
  background: var(--control-bg);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.15s;
}

.ticket-toolbar button:hover,
.ticket-toolbar-actions button:hover {
  border-color: var(--brand);
  color: var(--brand-dark);
}

.ticket-toolbar-actions .btn-create {
  background: var(--brand-dark);
  color: #fff;
  border-color: var(--brand-dark);
}

.ticket-toolbar-actions .btn-create:hover {
  background: var(--brand-dark);
  border-color: var(--brand-dark);
  color: #fff;
}

.ticket-toolbar-actions .btn-export {
  border-color: rgba(14,116,144,0.22);
  background: linear-gradient(135deg, #ecfeff 0%, #f0fdfa 100%);
  color: #0f766e;
  box-shadow: 0 1px 3px rgba(15,118,110,0.10);
}

.ticket-toolbar-actions .btn-export:hover {
  border-color: #0f766e;
  color: #115e59;
  transform: translateY(-1px);
}

.ticket-toolbar-actions .btn-export--excel {
  border-color: rgba(22,163,74,0.24);
  background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
  color: #15803d;
}

.ticket-toolbar-actions .btn-export--excel:hover {
  border-color: #16a34a;
  color: #166534;
}

.ticket-toolbar-actions .btn-export:disabled,
.ticket-toolbar-actions .btn-export--excel:disabled {
  opacity: 0.58;
  cursor: not-allowed;
  transform: none;
}

/* ── Generic export buttons (CSV / Excel) ───────────────── */

.btn-export {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 600;
  border-radius: 6px;
  border: 1px solid rgba(14,116,144,0.22);
  background: linear-gradient(135deg, #ecfeff 0%, #f0fdfa 100%);
  color: #0f766e;
  cursor: pointer;
  transition: border-color 0.15s, transform 0.1s;
  box-shadow: 0 1px 3px rgba(15,118,110,0.10);
  white-space: nowrap;
}

.btn-export:hover {
  border-color: #0f766e;
  color: #115e59;
  transform: translateY(-1px);
}

.btn-export--excel {
  border-color: rgba(22,163,74,0.24);
  background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
  color: #15803d;
}

.btn-export--excel:hover {
  border-color: #16a34a;
  color: #166534;
}

.btn-export:disabled,
.btn-export--excel:disabled {
  opacity: 0.58;
  cursor: not-allowed;
  transform: none;
}

/* ── Summary cards ──────────────────────────────────────── */

.ticket-summary-card {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0;
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.ticket-summary-card h3 {
  background: linear-gradient(135deg, var(--panel-2) 0%, var(--surface-3) 100%);
  color: var(--text-strong);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 10px 16px;
  margin: 0;
}

.ticket-summary-card p {
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--text);
  padding: 12px 16px 4px;
  margin: 0;
}

.ticket-summary-card small {
  font-size: 0.74rem;
  color: var(--muted);
  padding: 0 16px 14px;
  line-height: 1.5;
}

/* Segunda línea de la tarjeta: desglose del desenlace de los tickets cerrados */
.ticket-summary-card small.ticket-summary-resolutions {
  margin-top: -8px;
  font-size: 0.7rem;
  color: var(--muted-2);
}

/* ── Ticket panels ──────────────────────────────────────── */

.ticket-panel {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 20px 20px 16px;
  box-shadow: var(--shadow-sm);
}

.ticket-panel h3 {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--text-strong);
  margin: 0 0 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.ticket-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}

.ticket-panel-header h3 {
  margin: 0;
  padding-bottom: 0;
  border-bottom: none;
}
/* Botones de acción del header (p. ej. "Órdenes SAE" + "+ Nuevo tipo"). */
.ticket-header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Modal ancho para tablas (catálogo de órdenes SAE, 9 columnas con selects +
   inputs + toggles). Más ancho para que la data respire; en pantallas chicas usa
   casi todo el viewport y la tabla igual hace scroll-x en .ticket-table-wrap. */
.modal-card--wide {
  max-width: min(1280px, 96vw);
  width: 96%;
}

/* Contenedor de scroll para tablas anchas dentro de modales/paneles (p. ej. el
   catálogo de órdenes SAE: 9 columnas). Sin esto la tabla desborda la modal y,
   como .modal-card tiene overflow-x:hidden, las últimas columnas (los toggles de
   acción: "Sube a SAE", "Requiere equipo Fibex", "Activo") quedaban recortadas e
   inaccesibles. Aquí la tabla hace scroll horizontal propio en vez de recortarse. */
.ticket-table-wrap {
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
}
.ticket-table-wrap > table { min-width: max-content; }

/* ── Ticket form ────────────────────────────────────────── */

.ticket-form {
  display: grid;
  gap: 10px;
}

.ticket-form input,
.ticket-form select,
.ticket-form textarea {
  width: 100%;
  padding: 9px 12px;
  border: 1.5px solid var(--border);
  border-radius: 9px;
  font: inherit;
  font-size: 0.87rem;
  background: var(--surface-2);
  color: var(--text);
  transition: border-color 0.15s;
}

.ticket-form input:focus,
.ticket-form select:focus,
.ticket-form textarea:focus {
  outline: none;
  border-color: var(--brand-dark);
  background: var(--surface);
}

.ticket-form textarea {
  min-height: 80px;
  resize: vertical;
}

.ticket-form button[type="submit"] {
  padding: 10px 18px;
  background: var(--brand-dark);
  color: #fff;
  border: 0;
  border-radius: 9px;
  font: inherit;
  font-size: 0.87rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s;
}

.ticket-form button[type="submit"]:hover {
  background: var(--brand-dark);
}

.type-form-actions {
  display: flex;
  gap: 8px;
}

.type-form-actions .btn-cancel {
  padding: 10px 14px;
  background: var(--surface-2);
  color: var(--muted);
  border: 1.5px solid var(--border);
  border-radius: 9px;
  font: inherit;
  font-size: 0.87rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.15s;
}

.type-form-actions .btn-cancel:hover {
  background: var(--control-bg-hover);
}

/* Acciones de fila: ver .pg-actions / .pg-btn (patrón único del panel). */

/* ── Manage modal tabs ──────────────────────────────────── */

.ticket-manage-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  padding: 0 24px 16px;
}

/* Sub-tags del monitor Interno: cambian entre tablas (cada una carga su sección) */
/* Las sub-tags del monitor interno usan el nivel 2 canónico (.cc-subtabs); el
   estilo propio que había aquí se eliminó. */

/* ── Monitor interno: la toolbar es la estándar (.tickets-toolbar); esta clase
   solo queda para ajustar la posición del popover de filtros. ── */
.monitor-toolbar { margin-bottom: 14px; }
.monitor-toolbar .tickets-filters-popover { top: 44px; left: 0; }

/* ── Monitor interno: grilla de cards con minitabla ── */
.interno-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 360px));
  justify-content: start;
  gap: 16px;
  align-items: start;
}
.interno-card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px 12px;
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s;
}
/* OJO: NADA de `transform` en la card. Un transform crea containing block para los
   descendientes `position: fixed` → el backdrop del dropdown de Exportar (fixed, vive
   dentro de la card) dejaba de cubrir el viewport al hacer hover, el cursor salía de la
   card, se quitaba el hover, el backdrop volvía a cubrir todo… bucle infinito de cursor. */
.interno-card:hover {
  border-color: var(--brand);
  box-shadow: var(--shadow-md);
}
/* El backdrop es hijo de la card (que es clicable): no debe heredar el cursor pointer
   ni pintarlo por toda la pantalla mientras el dropdown está abierto. */
.interno-card .lc-filters-backdrop { cursor: default; }
.interno-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}
.interno-card-head h3 { margin: 0; font-size: 0.92rem; color: var(--text-strong); }

/* Minitabla dentro de la card: compacta, sin scroll horizontal. */
.interno-mini { table-layout: fixed; width: 100%; font-size: 0.8rem; }
.interno-mini th {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  padding: 4px 8px;
}
.interno-mini td {
  padding: 5px 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.interno-mini-empty { text-align: center; color: var(--muted); padding: 12px 8px; }
.interno-card-foot {
  margin-top: 10px;
  text-align: right;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--brand);
}

/* Cabecera de la tabla-detalle (al abrir una card): volver · título · Exportar. */
.interno-full-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}
.interno-full-head h3 { margin: 0; font-size: 0.98rem; color: var(--text-strong); flex: 1; }
.interno-back {
  appearance: none;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: 8px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s;
}
.interno-back:hover { background: var(--control-bg-hover); border-color: var(--brand); }

.ticket-manage-tabs button {
  appearance: none;
  border: 0;
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--muted);
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 600;
  font-family: inherit;
  padding: 7px 16px;
  white-space: nowrap;
  transition: all 0.15s;
}

.ticket-manage-tabs button.active {
  background: var(--brand-dark) !important;
  color: #fff !important;
  box-shadow: 0 1px 4px rgba(0,0,0,0.12);
}

.ticket-manage-tabs button:not(.active):hover {
  background: rgba(15,23,42,0.10);
  color: var(--text);
}

.ticket-manage-form {
  padding-bottom: 0;
}

/* Row highlight when editing */
tr.row-editing {
  background: rgba(var(--brand-rgb),0.04);
  outline: 2px solid rgba(var(--brand-rgb),0.25);
  outline-offset: -2px;
}

/* ── SLA badges ─────────────────────────────────────────── */

.sla-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 88px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.77rem;
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid transparent;
}

.sla-green  { background: var(--badge-success-bg); color: var(--badge-success-fg); border-color: var(--badge-success-bg); }
.sla-yellow { background: var(--badge-warn-bg);  color: var(--badge-warn-fg); border-color: var(--badge-warn-bg); }
.sla-red    { background: var(--badge-danger-bg);   color: var(--badge-danger-fg); border-color: var(--badge-danger-bg); }

/* ── Badges de Estado y Prioridad (bandeja de tickets) ── */
.ticket-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 58px;
  max-width: 100%;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 0.74rem;
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid transparent;
}
.tk-status-abierto    { background: var(--badge-info-bg);  color: var(--badge-info-fg); border-color: var(--badge-info-bg); }
.tk-status-tomado     { background: var(--badge-purple-bg); color: var(--badge-purple-fg); border-color: var(--badge-purple-bg); }
.tk-status-en_proceso { background: var(--badge-warn-bg);  color: var(--badge-warn-fg); border-color: var(--badge-warn-bg); }
.tk-status-escalado   { background: var(--badge-danger-bg);   color: var(--badge-danger-fg); border-color: var(--badge-danger-bg); }
.tk-status-resuelto   { background: var(--badge-success-bg);  color: var(--badge-success-fg); border-color: var(--badge-success-bg); }
.tk-status-cerrado    { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); border-color: var(--badge-neutral-bg); }

.tk-prio-baja    { background: var(--badge-success-bg);  color: var(--badge-success-fg); border-color: var(--badge-success-bg); }
.tk-prio-media   { background: var(--badge-info-bg);  color: var(--badge-info-fg); border-color: var(--badge-info-bg); }
.tk-prio-alta    { background: var(--badge-warn-bg);  color: var(--badge-warn-fg); border-color: var(--badge-warn-bg); }
.tk-prio-critica { background: var(--badge-danger-bg);   color: var(--badge-danger-fg); border-color: var(--badge-danger-bg); }

/* ── Resolution status badges ───────────────────────────── */

.res-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid transparent;
}
.res-resuelto    { background: var(--badge-success-bg);  color: var(--badge-success-fg); border-color: var(--badge-success-bg); }
.res-no_resuelto { background: var(--badge-danger-bg);   color: var(--badge-danger-fg); border-color: var(--badge-danger-bg); }
.res-cancelado   { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); border-color: var(--badge-neutral-bg); }
.res-pausado     { background: var(--badge-warn-bg);  color: var(--badge-warn-fg); border-color: var(--badge-warn-bg); }
.res-sin_estado  { background: var(--badge-neutral-bg);  color: var(--badge-neutral-fg); border-color: var(--badge-neutral-bg); }

/* Tinted header cells for resolution outcome columns */
.res-col-resuelto    { color: var(--badge-success-fg); }
.res-col-no_resuelto { color: var(--badge-danger-fg); }
.res-col-cancelado   { color: var(--badge-neutral-fg); }
.res-col-pausado     { color: var(--badge-warn-fg); }

/* Truncated notes cell in trazabilidad */
.res-notes-cell {
  max-width: 220px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.8rem;
  color: var(--muted);
}

/* ── Monitor tab layout ─────────────────────────────────── */

.ticket-monitor-layout {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Allow dense tables (planes, trazabilidad) to scroll horizontally
   instead of squashing columns below readable width              */
.ticket-panel-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.ticket-panel-scroll .contacts-table {
  min-width: 900px;
}

/* ── Responsive ─────────────────────────────────────────── */

@media (max-width: 1024px) {
  .ticket-grid,
  .ticket-toolbar {
    grid-template-columns: 1fr;
  }

  /* La barra de pestañas ya NO envuelve: scrolla de lado. Ver «RESPONSIVE DE
     PÁGINAS» al final del archivo — una píldora partida en varias líneas no
     parece una barra de pestañas, parece un fallo de maquetación. */
}

.btn-login:not(:disabled):hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(var(--brand-dark-rgb),0.55), 0 2px 6px rgba(var(--brand-dark-rgb),0.3);
}

.btn-login:not(:disabled):active {
  transform: translateY(0);
  box-shadow: 0 2px 8px rgba(var(--brand-dark-rgb),0.4);
}

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

/* enlaces debajo del botón */
.login-footer-links {
  text-align: center;
  margin-top: 16px;
}

.link-forgot {
  color: var(--muted);
  font-size: 0.8125rem;
  font-weight: 500;
  text-decoration: none;
  transition: color 0.2s ease;
}

.link-forgot:hover {
  color: var(--brand-dark);
  text-decoration: underline;
}

/* banner de éxito */
.success-banner {
  background: linear-gradient(135deg, rgba(16,185,129,0.08) 0%, rgba(5,150,105,0.05) 100%);
  border: 1px solid rgba(16,185,129,0.25);
  color: #059669;
  padding: 14px 16px;
  border-radius: 12px;
  font-size: 0.875rem;
  font-weight: 500;
  margin-bottom: 20px;
  display: flex;
  align-items: center;
  gap: 10px;
  animation: fadeInUp 0.25s ease;
}

/* banner de error */
.error-banner {
  background: linear-gradient(135deg, rgba(239,68,68,0.08) 0%, rgba(220,38,38,0.05) 100%);
  border: 1px solid rgba(239,68,68,0.25);
  color: #dc2626;
  padding: 14px 16px;
  border-radius: 12px;
  font-size: 0.875rem;
  font-weight: 500;
  margin-bottom: 20px;
  display: flex;
  align-items: center;
  gap: 10px;
  animation: fadeInUp 0.25s ease;
}

/* ============================================================
   HOME PAGE
   ============================================================ */
.home-container {
  min-height: 100vh;
  background: var(--bg);
  display: flex;
  flex-direction: column;
}

/* ---- Header ---- */
.app-header {
  background: var(--nav-bg);
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  border-bottom: 1px solid var(--nav-border);
  padding: 0 28px;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: sticky;
  top: 0;
  z-index: 100;
}

/* Subtle gradient accent line at bottom */
.app-header::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(var(--brand-rgb), 0.35) 25%,
    rgba(var(--brand-rgb), 0.5) 50%,
    rgba(var(--brand-rgb), 0.35) 75%,
    transparent 100%
  );
  pointer-events: none;
}

.app-header-brand {
  display: flex;
  align-items: center;
  gap: 10px;
}

.app-logo-badge {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-dark) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  flex-shrink: 0;
  box-shadow: 0 2px 10px rgba(var(--brand-rgb), 0.45);
}

.app-title {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--text-strong);
  letter-spacing: -0.02em;
}

.header-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* ---- Body layout (sidebar + main) ---- */
.home-layout {
  display: flex;
  flex: 1;
  min-height: 0;
}

/* ---- Sidebar ---- */
.home-sidebar {
  width: 232px;
  flex-shrink: 0;
  background: var(--nav-bg-2);
  border-right: 1px solid var(--nav-border);
  padding: 20px 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
  position: sticky;
  top: 64px;
  height: calc(100vh - 64px);
}

.home-sidebar-user {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.04);
  margin-bottom: 2px;
}

.home-sidebar-avatar {
  width: 36px;
  height: 36px;
  border-radius: 9px;
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-dark) 100%);
  color: #fff;
  font-size: 0.875rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  letter-spacing: 0;
}

.home-sidebar-user-info {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.home-sidebar-name {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.home-sidebar-role {
  font-size: 0.6875rem;
  color: var(--muted-2);
  font-weight: 500;
  white-space: nowrap;
}

.home-sidebar-sep {
  height: 1px;
  background: rgba(255, 255, 255, 0.06);
  margin: 10px 4px;
}

.home-sidebar-label {
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--muted-2);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  padding: 0 10px;
  margin-bottom: 4px;
}

.home-sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.home-sidebar-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 9px;
  border: none;
  background: transparent;
  color: var(--muted-2);
  font-size: 0.8375rem;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
  text-align: left;
  width: 100%;
}

.home-sidebar-item:hover:not(.home-sidebar-item--maint) {
  background: rgba(255, 255, 255, 0.06);
  color: var(--brand-soft-fg);
}

.home-sidebar-item--maint {
  opacity: 0.45;
  cursor: not-allowed;
}

.home-sidebar-item-icon {
  font-size: 1rem;
  flex-shrink: 0;
  line-height: 1;
  width: 20px;
  text-align: center;
}

.home-sidebar-item-name {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.home-sidebar-item-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #f59e0b;
  flex-shrink: 0;
}

.home-sidebar-empty {
  font-size: 0.8rem;
  color: var(--muted-2);
  padding: 8px 10px;
}

/* ---- Main ---- */
.app-main {
  flex: 1;
  padding: 36px 40px;
  min-width: 0;
  overflow-y: auto;
}
/* Centro de Control: el main NO debe scrollear por dentro. Con .home-container
   (min-height:100vh) anidado en el shell (~100vh), el overflow-y:auto del main
   creaba un SEGUNDO scroll vertical junto al del body (doble barra que estorbaba
   al desplazarse). Dejamos que scrollee solo la página. */
.app-main.control-main { overflow: visible; }

.module-main {
  --module-main-max-width: 1440px;
  --module-main-pad-x: clamp(20px, 2.2vw, 36px);
  --module-main-pad-y: clamp(24px, 2vw, 32px);
  width: min(100%, var(--module-main-max-width));
  margin: 0 auto;
  padding: var(--module-main-pad-y) var(--module-main-pad-x) !important;
}

.module-main > * {
  min-width: 0;
}

.module-main--compact {
  --module-main-max-width: 1280px;
}

.module-main--wide {
  --module-main-max-width: 1680px;
}

.module-main--xl {
  --module-main-max-width: 1920px;
}

@media (max-width: 860px) {
  .module-main {
    --module-main-pad-x: 16px;
    --module-main-pad-y: 20px;
  }
}

/* ---- Banner de la portada ----
   Hermano del grid de módulos dentro de `.app-main`, así que ocupa el MISMO
   ancho sin márgenes negativos ni contenedor propio: si mañana cambia el
   padding del main, banner y módulos se mueven juntos.

   El arte de fábrica se dibuja con gradientes sobre los tokens de marca: pesa
   0 bytes, sigue al tema activo (y al modo claro/oscuro) y no obliga a tener
   dos imágenes. La imagen subida es la excepción, no la norma. */
.home-banner {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  margin-bottom: 24px;
  height: var(--hb-h);
  background-color: var(--surface-2);
  border: 1px solid var(--border-soft);
}

/* La altura va por variable, no por `height` directo, porque el modo con la
   cabecera dentro necesita el MISMO valor como mínimo y no como fijo: si el
   saludo y el buscador no caben, el banner crece en vez de recortarlos. */
.hb--h-compacto { --hb-h: clamp(64px, 7vw, 92px); }
.hb--h-normal   { --hb-h: clamp(84px, 9.5vw, 126px); }
.hb--h-alto     { --hb-h: clamp(104px, 12vw, 160px); }

/* ---- Cabecera dentro del banner ---- */
/* La cabecera dentro NO cambia la altura del banner: la altura sigue siendo la
   elegida y el contenido se coloca encima en posición absoluta. Con una rejilla
   de tres filas, las dos vacías sumaban su alto y el banner crecía solo. */
.home-banner--overlay { height: var(--hb-h); }

/* Degradado de apoyo: sobre una foto clara el texto blanco desaparece, y no se
   puede depender del oscurecimiento (es una decisión estética aparte, y puede
   estar en 0). */
.home-banner--overlay::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 25%, rgba(0, 0, 0, 0.55));
  pointer-events: none;
}

/* Las 9 celdas se resuelven con posición absoluta dentro del área útil del
   banner, no con una rejilla: una rejilla de tres filas reserva alto para las
   dos que están vacías y hacía crecer el banner por encima de la altura
   elegida. Aquí el contenido flota y la altura no se toca. */
.hb-content {
  position: absolute;
  inset: clamp(14px, 2vw, 22px) clamp(16px, 2.4vw, 26px);
  z-index: 1;
  pointer-events: none;
}

/* El contenedor deja pasar el ratón (es un marco vacío en su mayor parte); las
   piezas colocadas sí lo reciben, o el buscador no se podría ni pulsar. */
/* Cada pieza se queda dentro de SU tercio. La config ya obliga a que el saludo
   y el buscador estén en columnas distintas, y este tope es lo que convierte esa
   regla en geometría: sin él el saludo mide lo que mida su texto, cruza el
   centro y se come el buscador aunque estén en celdas diferentes. */
.hb-slot {
  position: absolute;
  max-width: calc(33.333% - 10px);
  pointer-events: auto;
}

.hb-slot .home-search { width: 100%; }

/* Dentro del banner el saludo baja de cuerpo: a 30 px no cabe en un tercio y
   saldría cortado siempre. Fuera del banner conserva su tamaño. */
.home-banner--overlay .home-welcome-title {
  font-size: clamp(1.05rem, 1.9vw, 1.45rem);
  margin-bottom: 2px;
}

.home-banner--overlay .home-welcome-sub { font-size: 0.8125rem; }

/* En compacto solo cabe una línea: el subtítulo se retira antes que desbordar. */
.hb--h-compacto.home-banner--overlay .home-welcome-sub { display: none; }

/* El texto que no cabe en su tercio se corta: el banner tiene una altura fija
   elegida por el usuario, así que envolver en dos líneas lo desbordaría. */
.home-banner--overlay .home-welcome-title,
.home-banner--overlay .home-welcome-sub {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hb-at--arriba-izq     { top: 0; left: 0; }
.hb-at--arriba-centro  { top: 0; left: 50%; transform: translateX(-50%); }
.hb-at--arriba-der     { top: 0; right: 0; }
.hb-at--medio-izq      { top: 50%; left: 0; transform: translateY(-50%); }
.hb-at--medio-centro   { top: 50%; left: 50%; transform: translate(-50%, -50%); }
.hb-at--medio-der      { top: 50%; right: 0; transform: translateY(-50%); }
.hb-at--abajo-izq      { bottom: 0; left: 0; }
.hb-at--abajo-centro   { bottom: 0; left: 50%; transform: translateX(-50%); }
.hb-at--abajo-der      { bottom: 0; right: 0; }

/* El texto se alinea con su columna: un saludo a la derecha con el texto
   alineado a la izquierda se lee torcido. */
.hb-slot[class*="-centro"] { text-align: center; }
.hb-slot[class*="-der"]    { text-align: right; }

.home-banner--overlay .home-topline { margin-bottom: 0; }

.home-banner--overlay .home-welcome-title,
.home-banner--overlay .home-welcome-sub {
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

.home-banner--overlay .home-search-ico { color: rgba(255, 255, 255, 0.85); }

.home-banner--overlay .home-search-input {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.35);
  color: #fff;
  backdrop-filter: blur(6px);
}

.home-banner--overlay .home-search-input::placeholder { color: rgba(255, 255, 255, 0.75); }

.home-banner--overlay .home-search-input:focus {
  background: rgba(255, 255, 255, 0.24);
  border-color: #fff;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.18);
}

.home-banner--overlay .home-search-clear { color: rgba(255, 255, 255, 0.85); }
.home-banner--overlay .home-search-clear:hover {
  background: rgba(255, 255, 255, 0.2);
  color: #fff;
}

/* Formas. Arco y cinta son el MISMO radio elíptico invertido: curvan un borde
   largo sin recortar con máscara (una máscara obliga a un SVG externo y rompe
   el borde). */
.hb--recto       { border-radius: 0; }
.hb--redondeado  { border-radius: 20px; }
.hb--pastilla    { border-radius: 999px; }
.hb--arco        { border-radius: 20px 20px 46% 46% / 20px 20px 22% 22%; }
.hb--cinta       { border-radius: 46% 46% 20px 20px / 22% 22% 20px 20px; }

/* `pointer-events:none` + `user-drag:none`: sin esto el navegador arranca SU
   arrastre de imagen (el fantasma semitransparente) en cuanto se pulsa sobre la
   foto, se queda el gesto y el encuadre no se podía mover. La imagen es
   decorativa, así que no pierde nada al no recibir eventos. */
.hb-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* El velo es una capa aparte del fondo: así el mismo control de oscurecimiento
   sirve tanto a los dibujos como a la imagen subida. */
.hb-veil {
  position: absolute;
  inset: 0;
  background: #000;
  opacity: var(--hb-dim, 0);
  pointer-events: none;
}

.hb-art { background-color: var(--brand-dark); }

/* Nota de sintaxis (bug 2026-07-30): estos gradientes llevan SIEMPRE la palabra
   `ellipse`/`circle` delante del tamaño. La forma abreviada `radial-gradient(120%
   140% at …)` es válida en la especificación pero el pipeline de build la
   descarta, y descartar UNA capa invalida la declaración entera: los cuatro
   fondos que la usaban se veían como un plano de color, indistinguibles entre
   sí. `conic-gradient` corrió la misma suerte (no aparece en ningún otro sitio
   del archivo). El resto del CSS ya usaba la forma larga; aquí también. */
.hb-art--aurora {
  background-image:
    radial-gradient(ellipse 75% 130% at 8% -15%, rgba(255, 255, 255, 0.45) 0%, transparent 62%),
    radial-gradient(ellipse 85% 130% at 95% 115%, rgba(var(--brand-rgb), 0.95) 0%, transparent 64%),
    linear-gradient(115deg, var(--brand-dark) 0%, var(--brand) 100%);
}

/* Malla = rejilla romboidal: dos juegos de líneas finas cruzadas. Antes eran
   manchas difusas y no se distinguía de «aurora» — el nombre prometía una
   retícula y el dibujo no la tenía. */
/* Mismo motivo que en «rayos» para el `background-size`: las dos capas repetidas
   se acotan a un periodo. A 45° el paso de 16px se ve como 16·√2 = 22.63px tanto
   en horizontal como en vertical. */
.hb-art--malla {
  background-image:
    repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.17) 0 1px, transparent 1px 16px),
    repeating-linear-gradient(-45deg, rgba(255, 255, 255, 0.17) 0 1px, transparent 1px 16px),
    radial-gradient(ellipse 90% 150% at 84% 6%, rgba(255, 255, 255, 0.3) 0%, transparent 62%),
    linear-gradient(100deg, var(--brand-dark), var(--brand));
  background-size: 22.63px 22.63px, 22.63px 22.63px, cover, cover;
}

/* Trama = semitono: dos rejillas de puntos de distinto calibre y paso, para que
   se lea como un degradado de grano y no como un patrón plano. */
.hb-art--trama {
  background-image:
    radial-gradient(circle, rgba(255, 255, 255, 0.34) 1.6px, transparent 1.8px),
    radial-gradient(circle, rgba(255, 255, 255, 0.13) 3px, transparent 3.2px),
    linear-gradient(120deg, var(--brand-dark), var(--brand));
  background-size: 14px 14px, 42px 42px, cover;
}

.hb-art--olas {
  background-image:
    radial-gradient(ellipse 140% 85% at 50% 168%, rgba(255, 255, 255, 0.34) 0 40%, transparent 41%),
    radial-gradient(ellipse 152% 92% at 50% 186%, rgba(255, 255, 255, 0.26) 0 40%, transparent 41%),
    radial-gradient(ellipse 168% 102% at 50% 206%, rgba(255, 255, 255, 0.18) 0 40%, transparent 41%),
    linear-gradient(160deg, var(--brand), var(--brand-dark));
}

/* Rayos con franjas repetidas en diagonal en vez de un `conic-gradient`: el
   abanico exacto no compensa depender de una función que el build tira. */
/* `background-size` OBLIGATORIO en las capas repetidas, y con estos números
   exactos (bug 2026-07-31: al animarlo salían franjas de anchos distintos y una
   cuña clara a un lado). Sin tamaño, la capa mide lo que mida el banner: al
   correr `background-position` el navegador repite ESE mosaico —del tamaño del
   elemento— y el patrón arranca de nuevo en un punto que no encaja con el
   anterior, así que la costura queda a la vista y además se desplaza.
   Con el mosaico acotado a UN periodo del patrón la repetición es exacta.
   Periodo del dibujo: 74px medidos perpendiculares a las franjas. A 72°,
   74/sin72 = 77.8px en horizontal y 74/cos72 = 239.5px en vertical. */
.hb-art--rayos {
  background-image:
    repeating-linear-gradient(72deg,
      rgba(255, 255, 255, 0.22) 0 9px, transparent 9px 30px,
      rgba(255, 255, 255, 0.12) 30px 37px, transparent 37px 74px),
    radial-gradient(ellipse 95% 150% at 6% 118%, rgba(255, 255, 255, 0.32) 0%, transparent 60%),
    linear-gradient(105deg, var(--brand-dark), var(--brand));
  background-size: 77.8px 239.5px, cover, cover;
}

.hb-art--circuito {
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.14) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.14) 1px, transparent 1px),
    radial-gradient(rgba(255, 255, 255, 0.5) 2px, transparent 2.2px),
    linear-gradient(135deg, var(--brand-dark), var(--brand));
  background-size: 26px 26px, 26px 26px, 78px 78px, cover;
}

/* Topografía = campo de puntos con relieve. Aquí solo va el COLOR; los puntos
   los dibuja un <canvas> (`components/dot_field.rs`), porque cada punto se
   desplaza por su cuenta y eso ningún degradado lo hace. El lienzo pinta en
   blanco con poca alfa, así que el tono sale de estos tokens y el fondo sigue
   al tema sin que el canvas sepa de colores. */
.hb-art--topografia {
  background-image:
    radial-gradient(ellipse 110% 150% at 50% -30%, rgba(255, 255, 255, 0.12) 0%, transparent 62%),
    linear-gradient(165deg, var(--brand-dark) 0%, var(--brand) 135%);
}

/* Constelación: puntos sueltos unidos por una línea cuando se acercan. También
   es lienzo, y por el mismo motivo — cada punto lleva su propia trayectoria. El
   fondo es más frío y plano que el de topografía: las líneas son finas y sobre
   un degradado con mucho contraste se pierden. */
.hb-art--constelacion {
  background-image:
    radial-gradient(ellipse 90% 130% at 88% 8%, rgba(255, 255, 255, 0.10) 0%, transparent 58%),
    linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 150%);
}

/* El lienzo va DEBAJO del velo y del contenido: es el primer hijo del banner y
   no lleva z-index, así que el orden del documento ya lo coloca. */
.hb-dots {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

/* La muestra del desplegable no monta un canvas (habría uno por fila): imita el
   campo con dos rejillas de puntos desfasadas. Es la única regla que dibuja
   puntos en CSS, y por eso está acotada a `.idv-opt-swatch`. */
.idv-opt-swatch.hb-art--topografia {
  background-image:
    radial-gradient(circle, rgba(255, 255, 255, 0.35) 0.9px, transparent 1.1px),
    radial-gradient(circle, rgba(255, 255, 255, 0.18) 0.9px, transparent 1.1px),
    linear-gradient(165deg, var(--brand-dark) 0%, var(--brand) 135%);
  background-size: 5px 4px, 5px 4px, cover;
  background-position: 0 0, 2px 2px, 0 0;
}

/* Misma idea para la constelación: puntos más separados y grandes. Las líneas no
   se imitan —en 44×20 px serían un borrón— así que la muestra dice «puntos
   sueltos», que es la mitad honesta del dibujo. */
.idv-opt-swatch.hb-art--constelacion {
  background-image:
    radial-gradient(circle, rgba(255, 255, 255, 0.5) 1.1px, transparent 1.4px),
    linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 150%);
  background-size: 11px 9px, cover;
}

/* ---- Movimiento (opcional, `animated`) ----
   Se anima SIEMPRE `background-position`, nunca un elemento: el banner puede
   ocupar el ancho de la portada y mover una capa pintada es lo único que el
   compositor resuelve sin repintar la franja entera en cada fotograma.
   Cada arte lleva su propio `@keyframes` porque `background-position` es una
   lista por capa: un desplazamiento genérico movería también el degradado de
   base y el fondo entero se vería «temblar». Los desplazamientos son múltiplos
   exactos del mosaico, así que el bucle no da saltos. */
.hb-anim.hb-art--aurora {
  background-size: 180% 180%, 180% 180%, cover;
  animation: hb-anim-aurora calc(24s / var(--hb-spd, 1)) ease-in-out infinite alternate;
}

@keyframes hb-anim-aurora {
  from { background-position: 0% 50%, 100% 50%, 0 0; }
  to   { background-position: 65% 15%, 35% 85%, 0 0; }
}

.hb-anim.hb-art--malla { animation: hb-anim-malla calc(14s / var(--hb-spd, 1)) linear infinite; }

/* 22.63px = 16 × √2: el paso real del mosaico medido en horizontal cuando las
   líneas van a 45°. Con un número redondo el ciclo saltaría al reiniciarse. */
@keyframes hb-anim-malla {
  from { background-position: 0 0, 0 0, 0 0, 0 0; }
  to   { background-position: 22.63px 0, -22.63px 0, 0 0, 0 0; }
}

.hb-anim.hb-art--trama { animation: hb-anim-trama calc(18s / var(--hb-spd, 1)) linear infinite; }

@keyframes hb-anim-trama {
  from { background-position: 0 0, 0 0, 0 0; }
  to   { background-position: 14px 28px, -42px 42px, 0 0; }
}

.hb-anim.hb-art--olas {
  background-size: 200% 100%, 220% 100%, 240% 100%, cover;
  animation: hb-anim-olas calc(20s / var(--hb-spd, 1)) ease-in-out infinite alternate;
}

@keyframes hb-anim-olas {
  from { background-position: 0% 100%, 0% 100%, 0% 100%, 0 0; }
  to   { background-position: 100% 100%, 100% 100%, 100% 100%, 0 0; }
}

.hb-anim.hb-art--rayos { animation: hb-anim-rayos calc(16s / var(--hb-spd, 1)) linear infinite; }

/* El haz avanza siguiendo su propia dirección (72°), no en horizontal: cruzarlo
   en recto haría que las franjas parecieran deslizarse de lado. */
@keyframes hb-anim-rayos {
  from { background-position: 0 0, 0 0, 0 0; }
  to   { background-position: 70.4px -22.9px, 0 0, 0 0; }
}

.hb-anim.hb-art--circuito { animation: hb-anim-circuito calc(20s / var(--hb-spd, 1)) linear infinite; }

@keyframes hb-anim-circuito {
  from { background-position: 0 0, 0 0, 0 0, 0 0; }
  to   { background-position: 0 26px, 26px 0, 78px 78px, 0 0; }
}

/* Quien pide menos movimiento en el sistema lo pide para todo: el ajuste del
   panel no debe poder pasar por encima. */
@media (prefers-reduced-motion: reduce) {
  .hb-anim { animation: none !important; }
}

/* ---- Saludo + buscador (misma línea) ---- */
.home-topline {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 32px;
}

/* El margen inferior lo pone la fila; si lo dejara también el saludo, el
   buscador quedaría descolgado respecto al título. */
.home-topline .home-welcome { margin-bottom: 0; }

/* Misma forma de píldora y misma lupa a la derecha que el buscador de todos los
   listados (`.contacts-search-input`). Aquí la lupa es un elemento y no una
   `background-image`: así un iconset puede sustituirla (`ui.search`), que en el
   canónico no se puede. */
.home-search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 0 1 380px;
  min-width: 260px;
}

/* `z-index` obligatorio (bug 2026-07-31: la lupa desapareció DENTRO del banner y
   solo ahí). Ahí el input lleva `backdrop-filter`, y un elemento con filtro crea
   contexto de apilamiento y se pinta como si fuera `z-index:0` aunque no esté
   posicionado; como va DESPUÉS en el documento, tapaba a la lupa. Fuera del
   banner no hay filtro, el input no se promociona y la lupa se veía — de ahí que
   pareciera un fallo del icono y no del apilamiento. */
.home-search-ico {
  position: absolute;
  right: 14px;
  z-index: 1;
  display: flex;
  color: var(--muted-2);
  pointer-events: none;
}

/* El tamaño se fija aquí, no solo en el `width`/`height` del SVG de fábrica: un
   iconset del Marketplace puede sustituir `ui.search` por un SVG sin medidas, y
   un SVG sin tamaño dentro de un contenedor absoluto cae al default del
   navegador (300×150 px), que taparía el buscador entero. */
.home-search-ico svg {
  width: 16px;
  height: 16px;
  display: block;
}

/* Con texto escrito, la lupa deja el sitio a la ×: comparten esquina y de otro
   modo se pisarían. */
.home-search.is-filled .home-search-ico { display: none; }

.home-search-input {
  width: 100%;
  padding: 10px 40px 10px 18px;
  border-radius: 999px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 0.875rem;
  outline: none;
  transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
}

.home-search-input::placeholder { color: var(--muted-2); }

.home-search-input:focus {
  background-color: var(--control-bg-hover);
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb), 0.12);
}

/* Mismo `z-index` que la lupa, y por el mismo motivo: el input con
   `backdrop-filter` se pinta encima de lo que va antes en el documento. */
.home-search-clear {
  position: absolute;
  right: 10px;
  z-index: 1;
  border: 0;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 1.05rem;
  line-height: 1;
  padding: 2px 6px;
  border-radius: 6px;
}

.home-search-clear:hover {
  background: var(--control-bg-hover);
  color: var(--text-strong);
}

/* ---- Greeting inline (no box) ---- */
.home-welcome {
  margin-bottom: 32px;
  padding-top: 4px;
}

.home-welcome-title {
  font-size: 1.875rem;
  font-weight: 800;
  color: var(--text-strong);
  letter-spacing: -0.04em;
  margin-bottom: 6px;
  line-height: 1.15;
}

.home-welcome-sub {
  font-size: 0.875rem;
  color: var(--muted);
  font-weight: 400;
}

/* ---- Modules grid ---- */
/* Cabecera de la rejilla: título + controles de orden y vista, con los mismos
   botones-icono (`.contacts-refresh-btn`) y el mismo desplegable (`.tb-menu-pop`)
   que la toolbar estándar de los listados. */
.modules-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
}

.modules-bar .modules-heading { margin-bottom: 0; }

.modules-tools {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* El menú de vistas lleva glifo + etiqueta, así que necesita más ancho que el
   de ordenar, que es solo texto. */
.tb-menu-pop--wide { min-width: 232px; }

/* Estos dos botones viven en el borde DERECHO del contenido, así que su menú
   se ancla por la derecha: anclado por la izquierda (el defecto, pensado para
   toolbars que empiezan a la izquierda) se salía de la pantalla y cortaba las
   etiquetas. */
.modules-tools .tb-menu-pop {
  left: auto;
  right: 0;
}

/* Las filas del menú de vistas llevan glifo delante, así que dejan de ser
   `display:block` para poder alinearlo con la etiqueta. */
.tb-menu-pop--wide .tb-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
}

.tb-menu-ico {
  display: inline-flex;
  align-items: center;
  color: var(--muted);
  flex: none;
}

.tb-menu-item.is-active .tb-menu-ico { color: inherit; }

.modules-heading {
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: 18px;
}

.empty-state {
  color: var(--muted);
  font-size: 0.9375rem;
  padding: 40px;
  text-align: center;
  background: var(--surface);
  border-radius: 16px;
  border: 2px dashed var(--border);
}

.modules-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 14px;
}

/* ---- Vistas de la rejilla (calcadas del explorador de archivos) ----
   Un solo markup de tarjeta para las ocho: lo que cambia es la disposición. Así
   una tarjeta nueva no hay que escribirla ocho veces, y ninguna vista se queda
   sin un dato que las demás sí muestran. */
.mg--xl { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }
.mg--m  { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

.mg--xl .mod-icon { width: 84px; height: 84px; border-radius: 21px; font-size: 2.8rem; }
.mg--xl .mod-icon svg { width: 48px; height: 48px; }
.mg--xl .mod-name { font-size: 0.95rem; }

.mg--m .mod-card { padding: 20px 14px 16px; }
.mg--m .mod-icon { width: 46px; height: 46px; border-radius: 12px; font-size: 1.5rem; }
.mg--m .mod-icon svg { width: 26px; height: 26px; }
.mg--m .mod-name { font-size: 0.78rem; }
/* Medianos: la tarjeta es baja y estrecha, así que el panel de descripción se
   aprieta para que la mayoría de textos entren enteros sin llegar al scroll. */
.mg--m .mod-desc { padding: 10px 12px 12px; font-size: 0.72rem; line-height: 1.38; }

/* Las cuatro vistas en fila comparten esqueleto: icono a la izquierda, texto a
   la derecha, sin el salto de la tarjeta al pasar por encima (en una lista, un
   elemento que se levanta desalinea la fila entera). */
.mg--s .mod-card,
.mg--lista .mod-card,
.mg--detalles .mod-card,
.mg--mosaicos .mod-card,
.mg--contenido .mod-card {
  flex-direction: row;
  align-items: center;
  text-align: left;
  gap: 12px;
  border-radius: 12px;
  padding: 10px 14px;
}

.mg--s .mod-card--link:hover,
.mg--lista .mod-card--link:hover,
.mg--detalles .mod-card--link:hover,
.mg--mosaicos .mod-card--link:hover,
.mg--contenido .mod-card--link:hover {
  transform: none;
}

.mg--s .mod-name,
.mg--lista .mod-name,
.mg--detalles .mod-name,
.mg--mosaicos .mod-name,
.mg--contenido .mod-name {
  text-align: left;
}

.mg--s  { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 8px; }
.mg--s .mod-icon { width: 28px; height: 28px; border-radius: 8px; font-size: 0.95rem; }
.mg--s .mod-icon svg { width: 17px; height: 17px; }

.mg--lista { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 6px; }
.mg--lista .mod-card { padding: 7px 12px; }
.mg--lista .mod-icon { width: 22px; height: 22px; border-radius: 6px; font-size: 0.8rem; }
.mg--lista .mod-icon svg { width: 14px; height: 14px; }

/* Iconos pequeños y Lista solo muestran el nombre: es el punto de esas vistas,
   ver muchos módulos de un vistazo. */
.mg--s .mod-desc,
.mg--lista .mod-desc { display: none; }

/* Detalles, Mosaicos y Contenido sacan la descripción del panel deslizante y la
   ponen en línea: si hay que pasar el ratón para leerla, no es un detalle. */
.mg--detalles .mod-desc,
.mg--mosaicos .mod-desc,
.mg--contenido .mod-desc {
  position: static;
  transform: none;
  background: none;
  border-radius: 0;
  padding: 0;
  /* En línea el texto manda: sin tope de alto ni scroll propio. */
  max-height: none;
  overflow: visible;
  text-align: left;
  color: var(--muted);
  font-size: 0.75rem;
  line-height: 1.45;
}

.mg--detalles { grid-template-columns: 1fr; gap: 4px; }
.mg--detalles .mod-card { padding: 8px 14px; }
.mg--detalles .mod-icon { width: 26px; height: 26px; border-radius: 7px; font-size: 0.9rem; }
.mg--detalles .mod-icon svg { width: 16px; height: 16px; }
/* Nombre en columna fija: las descripciones arrancan alineadas, que es lo que
   hace legible una vista de detalles. */
.mg--detalles .mod-name { flex: 0 0 clamp(140px, 18vw, 230px); }
.mg--detalles .mod-desc { flex: 1 1 auto; }

.mg--mosaicos { grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); }
.mg--mosaicos .mod-card { padding: 12px 14px; align-items: flex-start; }
.mg--mosaicos .mod-icon { width: 44px; height: 44px; border-radius: 12px; font-size: 1.4rem; }
.mg--mosaicos .mod-icon svg { width: 25px; height: 25px; }

.mg--contenido { grid-template-columns: 1fr; gap: 8px; }
.mg--contenido .mod-card { padding: 14px 16px; align-items: flex-start; }
.mg--contenido .mod-icon { width: 52px; height: 52px; border-radius: 14px; font-size: 1.7rem; }
.mg--contenido .mod-icon svg { width: 30px; height: 30px; }
.mg--contenido .mod-name { font-size: 0.95rem; }

/* En Mosaicos y Contenido el nombre y la descripción se apilan a la derecha del
   icono. Se apoya en el orden del markup (icono · nombre · badge · descripción)
   colocando el bloque de texto con grid en vez de envolverlo en otro div, que
   obligaría a un markup distinto por vista. */
.mg--mosaicos .mod-card,
.mg--contenido .mod-card {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  column-gap: 14px;
  row-gap: 2px;
}

.mg--mosaicos .mod-icon,
.mg--contenido .mod-icon { grid-row: 1 / span 2; }

.mg--mosaicos .maint-badge,
.mg--contenido .maint-badge { grid-column: 2; justify-self: start; }

/* ---- Module card ---- */
.mod-card {
  position: relative;
  background: rgba(255, 255, 255, 0.04);
  border-radius: 20px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  padding: 28px 20px 22px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  overflow: hidden;
  transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              border-color 0.22s ease;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), 0 4px 16px rgba(0, 0, 0, 0.04);
}

.mod-card--link {
  cursor: pointer;
}

.mod-card--link:hover {
  transform: translateY(-4px);
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.04),
    0 12px 32px rgba(var(--brand-rgb), 0.12),
    0 4px 12px rgba(0, 0, 0, 0.06);
  border-color: rgba(var(--brand-rgb), 0.3);
}

.mod-card--maint {
  opacity: 0.55;
  cursor: default;
}

.mod-icon {
  font-size: 2rem;
  line-height: 1;
  width: 60px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(145deg, var(--brand) 0%, var(--brand-dark) 100%);
  border-radius: 15px;
  border: 1px solid rgba(var(--brand-rgb), 0.35);
  flex-shrink: 0;
  transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1);
  /* Badge SÓLIDO de marca (sigue el tema en claro y oscuro): un icono de iconset
     (monocromo, currentColor) se pinta blanco legible sobre el badge; los emoji
     multicolor del fallback lo ignoran. */
  color: #fff;
}
/* Un iconset provee un SVG monocromo → tamaño acorde al badge (el emoji se
   dimensiona por font-size). */
.mod-icon svg {
  width: 34px;
  height: 34px;
  display: block;
}

.mod-card--link:hover .mod-icon {
  transform: scale(1.06);
}

.mod-name {
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--text);
  text-align: center;
  line-height: 1.35;
}

.maint-badge {
  font-size: 0.6875rem;
  font-weight: 600;
  color: #b45309;
  background: rgba(245, 158, 11, 0.08);
  border: 1px solid rgba(245, 158, 11, 0.22);
  border-radius: 6px;
  padding: 2px 8px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Description slides up from bottom on hover */
.mod-desc {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 16px 18px 18px;
  /* El panel nunca puede pasar del alto de la tarjeta: `overflow: hidden` de
     .mod-card recortaría el texto contra el borde superior. Se topa al 100% y,
     si la descripción no cabe (tarjetas estrechas, textos largos), scrollea. */
  box-sizing: border-box;
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  background: linear-gradient(160deg, var(--brand-dark) 0%, var(--brand-dark) 100%);
  color: rgba(255, 255, 255, 0.92);
  font-size: 0.775rem;
  font-weight: 400;
  line-height: 1.55;
  text-align: center;
  /* +2px extra para que el borde superior del panel no asome como una línea
     azul en el fondo de la card cuando está oculto (subpixel rounding). */
  transform: translateY(calc(100% + 2px));
  transition: transform 0.26s cubic-bezier(0.4, 0, 0.2, 1);
  border-radius: 0 0 20px 20px;
}

.mod-card--link:hover .mod-desc {
  transform: translateY(0);
}

/* ============================================================
   CONTACTS PAGE (shared page shell)
   ============================================================ */
.page-container {
  min-height: 100vh;
  background: var(--bg);
  display: flex;
  flex-direction: column;
}

.page-header {
  background: linear-gradient(135deg, var(--bg) 0%, var(--panel-2) 100%);
  padding: 0 32px;
  height: 64px;
  display: flex;
  align-items: center;
  gap: 20px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
  position: sticky;
  top: 0;
  z-index: 100;
}

.btn-back {
  padding: 7px 14px;
  border-radius: 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  background: rgba(255,255,255,0.08);
  color: var(--muted);
  border: 1px solid rgba(255,255,255,0.12);
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-back:hover {
  background: rgba(255,255,255,0.15);
  color: var(--text-strong);
}

.page-header h1 {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-strong);
}

.page-content {
  flex: 1;
  padding: 28px 32px;
  max-width: 1200px;
  width: 100%;
  margin: 0 auto;
}

/* ---- Search bar ---- */
.search-form {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

.search-input {
  flex: 1;
  padding: 11px 16px;
  border-radius: 10px;
  border: 1.5px solid var(--border);
  background: rgba(255, 255, 255, 0.06);
  font-size: 0.9375rem;
  font-family: inherit;
  color: var(--text);
  transition: border-color 0.2s, box-shadow 0.2s;
  box-shadow: var(--shadow-sm);
}

.search-input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb),0.12);
}

.btn-search {
  padding: 11px 22px;
  border-radius: 10px;
  font-size: 0.875rem;
  font-weight: 600;
  font-family: inherit;
  background: var(--brand-dark);
  color: white;
  border: none;
  cursor: pointer;
  transition: background 0.2s;
}

.btn-search:hover:not(:disabled) { background: var(--brand-dark); }
.btn-search:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-clear {
  padding: 11px 16px;
  border-radius: 10px;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  background: var(--surface);
  color: var(--muted);
  border: 1.5px solid var(--border);
  cursor: pointer;
  transition: all 0.2s;
}

.btn-clear:hover { background: var(--surface-2); color: var(--danger); border-color: var(--danger); }

/* ---- Result count ---- */
.result-count {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 12px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* ---- Table wrapper ---- */
.table-wrapper {
  background: #161b22;
  border-radius: 16px;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.contacts-table {
  width: 100%;
  border-collapse: collapse;
}

.contacts-table th {
  background: rgba(255,255,255,0.04);
  padding: 12px 18px;
  text-align: left;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.contacts-table td {
  padding: 14px 18px;
  font-size: 0.875rem;
  color: var(--text);
  border-bottom: 1px solid rgba(255,255,255,0.06);
}

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

.contact-row {
  cursor: pointer;
  transition: background 0.15s;
}

.contact-row:hover td { background: rgba(255,255,255,0.04); }

.row-blocked td { color: var(--muted); }
.row-blocked:hover td { background: var(--badge-danger-bg); }

.td-name {
  font-weight: 600;
  color: var(--text);
}

.td-center { text-align: center; }
.td-muted { color: var(--muted); font-size: 0.84rem; }

/* ---- Badges ---- */
.channel-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  background: rgba(var(--brand-rgb),0.1);
  color: var(--brand-dark);
}

.status-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
}

.status-active {
  background: rgba(16,185,129,0.1);
  color: #059669;
}

.status-blocked {
  background: rgba(239,68,68,0.1);
  color: var(--danger);
}

/* ---- States ---- */
.loading-state {
  text-align: center;
  padding: 48px;
  color: var(--muted);
  font-size: 0.9375rem;
  background: var(--surface);
  border-radius: 16px;
  border: 1px solid var(--border);
}

.error-msg {
  color: var(--danger);
  font-size: 0.875rem;
  background: rgba(239,68,68,0.06);
  border: 1px solid rgba(239,68,68,0.2);
  border-radius: 10px;
  padding: 12px 16px;
  margin-bottom: 16px;
}

/* ---- Pagination ---- */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 20px;
}

/* .btn-page / .page-info se retiraron con el pager viejo de Contactos: ahora
   usa el bloque canónico (buscar "PAGINACIÓN ESTÁNDAR"). */

/* ---- Contact detail drawer ---- */
.drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15,23,42,0.45);
  z-index: 199;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.28s ease;
}

.drawer-backdrop.open {
  opacity: 1;
  pointer-events: all;
}

.contact-drawer {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: 380px;
  background: var(--surface-overlay);
  box-shadow: -8px 0 40px rgba(0,0,0,0.14);
  z-index: 200;
  transform: translateX(100%);
  transition: transform 0.3s cubic-bezier(0.4,0,0.2,1);
  overflow-y: auto;
}

.contact-drawer.open { transform: translateX(0); }

.drawer-inner {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.drawer-top {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 28px 24px 24px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}

.drawer-avatar {
  width: 56px;
  height: 56px;
  border-radius: 16px;
  background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 100%);
  color: white;
  font-size: 1.25rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  letter-spacing: 0.02em;
}

.drawer-avatar-pic {
  width: 56px;
  height: 56px;
  border-radius: 16px;
  object-fit: cover;
  flex-shrink: 0;
}

.drawer-identity {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.drawer-name {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.drawer-close {
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 0.9375rem;
  background: rgba(15,23,42,0.06);
  color: var(--muted);
  border: none;
  cursor: pointer;
  transition: all 0.15s;
  flex-shrink: 0;
  align-self: flex-start;
}

.drawer-close:hover { background: rgba(239,68,68,0.1); color: var(--danger); }

.drawer-body {
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.drawer-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 0;
  border-bottom: 1px solid var(--border-soft);
}

.drawer-field:last-child { border-bottom: none; }

.field-label {
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

.field-value {
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--text);
}

.field-id {
  font-family: 'Courier New', monospace;
  font-size: 0.75rem;
  color: var(--muted);
  word-break: break-all;
}

/* ============================================================
   USERS PAGE
   ============================================================ */

/* Cabecera de cada pestaña de Usuarios: solo el título con su tooltip. La
   toolbar de verdad (.tickets-toolbar) va justo debajo, y este margen es el
   aire que las separa — no lo pises con un style inline. */
.users-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 18px;
}

/* Un solo envoltorio con el padding del modal de Departamentos. Sin él, cada
   bloque es hijo directo de .modal-card, hereda `padding: 0 24px` y pierde su
   propio padding vertical → todo pegado a los bordes. */
.dept-modal-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-bottom: 4px;
}

/* Fila "¿Eliminar?" dentro de una celda de acciones. */
.pg-actions-ask { font-size: 0.8rem; color: var(--muted); }
/* Marcador de "sin acciones disponibles" en una fila. */
.pg-actions-empty { color: var(--muted-2); }

/* .users-filterbar / .users-filter-* se retiraron: el módulo usa la toolbar
   estándar y el alta de departamentos vive en su propio modal con .form-field. */

.users-table-wrap {
  background: var(--surface);
  border-radius: 14px;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  overflow-x: auto;
}

.users-table {
  width: 100%;
  border-collapse: collapse;
}

.users-table th {
  background: rgba(255,255,255,0.04);
  padding: 11px 14px;
  text-align: left;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.users-table td {
  padding: 12px 14px;
  font-size: 0.875rem;
  color: var(--text);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  vertical-align: middle;
}

.users-table tr:last-child td { border-bottom: none; }
.users-table tr:hover td { background: rgba(255,255,255,0.04); }
.users-table tr.row-blocked td { opacity: 0.6; }

.col-email { color: var(--muted); font-size: 0.8125rem; }
.col-date  { color: var(--muted); font-size: 0.8125rem; white-space: nowrap; }
.col-actions { white-space: nowrap; display: flex; gap: 5px; align-items: center; }

/* Badges */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
}

.badge-active  { background: rgba(16,185,129,0.12); color: #059669; }
.badge-blocked { background: rgba(239,68,68,0.12);  color: #dc2626; }
.badge-warn    { background: rgba(245,158,11,0.15); color: #b45309; }

.badge-role        { border-radius: 6px; }
.badge-owner       { background: rgba(139,92,246,0.12); color: #7c3aed; }
.badge-supervisor  { background: rgba(59,130,246,0.12);  color: #1d4ed8; }
.badge-operator    { background: rgba(16,185,129,0.12);  color: #059669; }
.badge-marketing   { background: rgba(245,158,11,0.12);  color: #b45309; }
.badge-guest       { background: rgba(148,163,184,0.2);  color: var(--muted); }

/* Action buttons */
.btn-action {
  padding: 8px 16px;
  border-radius: 9px;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  border: none;
  cursor: pointer;
  transition: all 0.2s ease;
}
/* Estado deshabilitado base: sin esto, un `.btn-action` con `disabled` (p. ej. el
   "Siguiente →" de un wizard con validación pendiente) se veía plenamente clicable.
   Los modificadores que ya definen su propio `:disabled` (--send, --danger, …) ganan
   por venir después en la cascada; esto solo cubre los que no lo tenían. */
.btn-action:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-create  { background: var(--brand-dark); color: white; }
.btn-create:hover { background: var(--brand-dark); }

/* .btn-invite se retiró: en la toolbar de Usuarios las acciones secundarias
   usan .btn-secondary, como en Tickets. */

.btn-delete-confirm { background: var(--danger); color: white; }
.btn-delete-confirm:hover { background: #dc2626; }

/* Small action buttons in table rows */
.btn-sm {
  padding: 4px 10px;
  border-radius: 7px;
  font-size: 0.75rem;
  font-weight: 600;
  font-family: inherit;
  border: 1px solid transparent;
  cursor: pointer;
  transition: all 0.15s ease;
}

.btn-edit    { background: var(--control-bg); color: var(--text); border-color: var(--control-border); }
.btn-edit:hover { background: var(--control-bg-hover); }

/* .btn-block / .btn-unblock / .btn-perms se retiraron: las acciones de fila de
   Usuarios usan .pg-btn, neutro salvo lo destructivo. */

.btn-delete  { background: rgba(239,68,68,0.08); color: #dc2626; border-color: rgba(239,68,68,0.2); font-size: 0.85rem; padding: 4px 8px; }
.btn-delete:hover { background: rgba(239,68,68,0.18); }

.btn-cancel  { background: var(--control-bg); color: var(--muted); border-color: var(--control-border); }
.btn-cancel:hover { background: var(--control-bg-hover); color: var(--text); }

.btn-view    { background: rgba(var(--brand-rgb),0.08); color: var(--brand); border-color: rgba(var(--brand-rgb),0.2); }
.btn-view:hover { background: rgba(var(--brand-rgb),0.16); }

/* ── Resultados de encuesta (drill-down canal → contacto → respuestas) ───────── */
/* max-height + overflow:hidden → el scroll vive SOLO en .survey-res-channels,
   no en toda la tarjeta (así el header y el resumen quedan fijos).
   Selector .modal-card.survey-res: gana en especificidad al overflow-y:auto de .modal-card. */
.modal-card.survey-res { max-height: 88vh; display: flex; flex-direction: column; overflow: hidden; padding: 18px 0 6px; }
/* La tarjeta no tiene padding horizontal (0 en los lados) para que el scroll del
   body llegue al borde; cada hijo directo (cabecera y cuerpo) pone su propio
   padding lateral para no chocar con los bordes del modal (#5/#10). */
.modal-card.survey-res > div:not(.modal-header):not(.modal-footer) { padding-left: 22px; padding-right: 22px; }
.survey-res .kn-wizard-head { flex-wrap: wrap; gap: 8px; }

.survey-res-summary {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin: 4px 0 18px;
}
.survey-res-kpi {
  flex: 1 1 120px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 12px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.survey-res-kpi__n { font-size: 1.5rem; font-weight: 800; color: var(--brand); line-height: 1; }
.survey-res-kpi__l { font-size: 0.72rem; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .03em; }

/* El scroll vive en .survey-res-body (hijo directo estático de la tarjeta, flex item
   fiable). channels solo apila; así el header queda fijo y el cuerpo se desplaza. */
.survey-res-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.survey-res-channels { display: flex; flex-direction: column; gap: 10px; }

.survey-res-channel { border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--surface); }
.survey-res-channel__head {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 13px 16px;
  background: var(--brand-soft-bg);
  border: none;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
}
.survey-res-channel__head:hover { background: var(--brand-soft-bg); }
.survey-res-channel__name { font-weight: 700; color: var(--text-strong); font-size: 0.95rem; }
.survey-res-channel__meta { font-size: 0.78rem; color: var(--muted); white-space: nowrap; }

.survey-res-contacts { padding: 6px 10px 10px; display: flex; flex-direction: column; gap: 6px; }

.survey-res-contact { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.survey-res-contact__head {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: var(--surface);
  border: none;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
}
.survey-res-contact__head:hover { background: var(--surface-2); }
.survey-res-contact__name { font-weight: 600; color: var(--text-strong); font-size: 0.88rem; }
.survey-res-contact__pct  { font-size: 0.78rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.survey-res-contact__date { font-size: 0.74rem; color: var(--muted-2); white-space: nowrap; }
.survey-res-contact__sub  { font-size: 0.78rem; color: var(--muted); margin: 0 0 8px; }

.survey-res-pill {
  font-size: 0.7rem;
  font-weight: 700;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--muted);
  white-space: nowrap;
}
.survey-res-pill--ok   { background: rgba(16,185,129,0.12); color: #059669; }
.survey-res-pill--warn { background: rgba(245,158,11,0.14); color: #b45309; }

.survey-res-answers { padding: 4px 14px 14px; background: var(--surface-2); border-top: 1px dashed var(--border); }
.survey-res-answer {
  display: grid;
  grid-template-columns: minmax(140px, 40%) 1fr;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border-soft);
}
.survey-res-answer:last-child { border-bottom: none; }
.survey-res-answer__q { font-size: 0.82rem; color: var(--muted); font-weight: 600; }
.survey-res-answer__a { font-size: 0.86rem; color: var(--text-strong); font-weight: 600; word-break: break-word; }
.survey-res-empty { font-size: 0.82rem; color: var(--muted); margin: 4px 0; font-style: italic; }

/* Responsive: en móvil el modal ocupa casi toda la pantalla y los grids apilan */
@media (max-width: 640px) {
  .modal-card.modal-xl { max-width: 100%; }
  .modal-card.survey-res { max-height: 92vh; padding: 14px 0 6px; }
  .modal-card.survey-res > div:not(.modal-header):not(.modal-footer) { padding: 0 14px; }
  .survey-res-summary { gap: 8px; margin: 4px 0 12px; }
  .survey-res-kpi { flex: 1 1 calc(50% - 8px); padding: 10px 8px; }
  .survey-res-kpi__n { font-size: 1.25rem; }
  /* fila de contacto: nombre|pill arriba, %|fecha en la 2da línea */
  .survey-res-contact__head {
    grid-template-columns: 1fr auto;
    row-gap: 4px;
  }
  .survey-res-answer { grid-template-columns: 1fr; gap: 2px; }
}

/* Header nav */
.header-left { display: flex; align-items: center; gap: 16px; }

.app-header h1 {
  font-size: 1.0625rem;
  font-weight: 700;
  color: var(--text-strong);
  letter-spacing: -0.01em;
}

.header-back {
  color: var(--muted-2);
  font-size: 0.8125rem;
  font-weight: 500;
  text-decoration: none;
  transition: color 0.2s;
  padding: 6px 12px;
  border-radius: 8px;
  background: rgba(255,255,255,0.07);
  border: 1px solid rgba(255,255,255,0.1);
}
.header-back:hover { color: var(--text-strong); background: rgba(255,255,255,0.13); }

/* Modals */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15,23,42,0.55);
  backdrop-filter: blur(4px);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}

.modal-card {
  background: var(--surface-overlay);
  border-radius: 20px;
  box-shadow: 0 25px 60px rgba(0,0,0,0.25);
  width: 100%;
  max-width: 480px;
  max-height: 90vh;
  overflow-y: auto;
  overflow-x: hidden;   /* nunca scroll horizontal: el contenido se ajusta en Y */
  animation: modalSlideIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes modalSlideIn {
  from { opacity: 0; transform: translateY(18px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0)    scale(1);    }
}

/* ── Modal de error crítico (sesión expirada, fallo al iniciar, etc.) ── */
.crit-overlay {
  position: fixed;
  inset: 0;
  z-index: 3000;                 /* por encima de cualquier modal/drawer */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: var(--scrim);
  backdrop-filter: blur(4px);
}
.crit-modal {
  width: min(420px, 94vw);
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 18px;
  box-shadow: var(--shadow-lg);
  padding: 28px 26px 24px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  animation: modalSlideIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.crit-img { width: 160px; max-width: 60%; height: auto; margin-bottom: 4px; }
.crit-title { font-size: 1.25rem; font-weight: 700; color: var(--text-strong); margin: 0; }
.crit-msg { font-size: 0.92rem; color: var(--muted); margin: 0; line-height: 1.5; white-space: pre-line; }
.crit-btn {
  margin-top: 8px;
  padding: 11px 22px;
  border: none;
  border-radius: 10px;
  background: var(--brand);
  color: #fff;
  font-size: 0.92rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s;
}
.crit-btn:hover { background: var(--brand-dark); }

/* ── Ruta inexistente (fallback del router: /docs y cualquier URL desconocida) ── */
.route-notfound {
  min-height: 70vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 12px;
  padding: 32px 20px;
}
.route-notfound-img { width: 160px; max-width: 55%; height: auto; margin-bottom: 4px; }
.route-notfound-title { font-size: 1.4rem; font-weight: 700; color: var(--text-strong); margin: 0; }
.route-notfound-msg { font-size: 0.95rem; color: var(--muted); margin: 0; max-width: 460px; line-height: 1.5; }
.route-notfound-btn {
  margin-top: 8px;
  padding: 11px 22px;
  border-radius: 10px;
  background: var(--brand);
  color: #fff;
  font-size: 0.92rem;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.15s;
}
.route-notfound-btn:hover { background: var(--brand-dark); }

.modal-card.modal-wide { max-width: 640px; }
.modal-card.permissions-modal { max-width: min(1080px, 95vw); }
.modal-card.modal-sm   { max-width: 380px; padding: 28px; }

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px 0;
  margin-bottom: 20px;
}

.modal-header h3 {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
}

.modal-close {
  background: none;
  border: none;
  font-size: 1.1rem;
  color: var(--muted);
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
  transition: all 0.15s;
}
.modal-close:hover { background: var(--control-bg-hover); color: var(--text); }

.modal-card form,
.modal-card > div:not(.modal-header):not(.modal-footer) {
  padding: 0 24px;
}

.modal-card .form-field { margin-bottom: 16px; }

.modal-card .form-field input:not([type="checkbox"]):not([type="radio"]),
.modal-card .form-field textarea,
.modal-card .form-field select {
  width: 100%;
  max-width: 100%;
  padding: 10px 14px;
  border-radius: 10px;
  border: 2px solid var(--border);
  font-size: 0.9rem;
  font-family: inherit;
  background: var(--surface-2);
  color: var(--text);
  transition: border-color 0.2s;
}
.modal-card .form-field input:not([type="checkbox"]):not([type="radio"]):focus,
.modal-card .form-field select:focus {
  outline: none;
  border-color: var(--brand-dark);
  background: var(--surface);
}

.modal-card .form-field label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 6px;
}

.form-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }

.form-check { margin: 4px 0 8px; }
.check-label {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 0.82rem;
  color: var(--text);
  cursor: pointer;
  line-height: 1.4;
}
.check-label input[type="checkbox"] { margin-top: 2px; flex-shrink: 0; }

.modal-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 20px 24px;
  border-top: 1px solid var(--border);
  margin-top: 20px;
}

/* Permissions panel */
/* .perms-filterbar / .perms-filter-* se retiraron: las pestañas de permisos y el
   modal de overrides usan la toolbar estándar (ver `perms_toolbar`). */

.perms-scroll {
  max-height: 52vh;
  overflow-x: auto;
  overflow-y: auto;
  padding: 0 24px;
}

.perms-table {
  width: 100%;
  min-width: 920px;
  border-collapse: collapse;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}

.perms-table th {
  background: rgba(255,255,255,0.04);
  border-bottom: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 9px 10px;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}

.perms-table td {
  border-bottom: 1px solid rgba(255,255,255,0.06);
  color: var(--text);
  font-size: 0.78rem;
  padding: 9px 10px;
  vertical-align: middle;
}

.perms-table tr:last-child td { border-bottom: none; }
.perms-table tr:hover td { background: rgba(255,255,255,0.04); }
.perms-table tr.perm-granted td { background: rgba(16,185,129,0.035); }
/* Permiso restringido por whitelist de departamento y el depto seleccionado NO está */
.perms-table tr.perm-dept-filtered td { background: rgba(239,68,68,0.05); }
.perm-dept-lock { color: #b45309; font-size: 0.72rem; font-weight: 600; white-space: nowrap; }

.perm-module-cell {
  color: var(--muted);
  font-weight: 700;
  min-width: 180px;
}

.perm-module-chip {
  border-radius: 999px;
  display: inline-flex;
  font-size: 0.72rem;
  font-weight: 800;
  line-height: 1;
  padding: 6px 9px;
  white-space: nowrap;
}

.module-contacts { background: rgba(20,184,166,0.14); color: #0f766e; }
.module-users { background: rgba(var(--brand-rgb),0.14); color: var(--brand-soft-fg); }
.module-campaigns { background: rgba(245,158,11,0.16); color: #b45309; }
.module-support { background: rgba(14,165,233,0.14); color: #0369a1; }
.module-control { background: rgba(139,92,246,0.14); color: #6d28d9; }
.module-analytics { background: rgba(16,185,129,0.14); color: #047857; }
.module-security { background: rgba(239,68,68,0.13); color: #b91c1c; }
.module-billing { background: rgba(217,119,6,0.14); color: #92400e; }
.module-other { background: var(--bg); color: var(--muted); }

.perm-action-badge.action-view { background: rgba(59,130,246,0.12); color: #1d4ed8; }
.perm-action-badge.action-create { background: rgba(16,185,129,0.14); color: #047857; }
.perm-action-badge.action-edit { background: rgba(245,158,11,0.15); color: #b45309; }
.perm-action-badge.action-delete { background: rgba(239,68,68,0.12); color: #dc2626; }
.perm-action-badge.action-export { background: rgba(14,165,233,0.14); color: #0369a1; }
.perm-action-badge.action-manage { background: rgba(139,92,246,0.14); color: #6d28d9; }
.perm-action-badge.action-other { background: var(--surface-2); color: var(--text); }

.perm-action-badge {
  background: var(--surface-2);
  border-radius: 999px;
  color: var(--text);
  display: inline-flex;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
  padding: 6px 9px;
  white-space: nowrap;
}

.perm-id-cell {
  color: var(--text-strong);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.74rem !important;
  min-width: 230px;
  word-break: break-word;
}

.perm-description-cell {
  color: var(--muted) !important;
  max-width: 280px;
  min-width: 220px;
}

.perm-status {
  display: inline-flex;
  font-size: 0.75rem;
  font-weight: 700;
  white-space: nowrap;
}
.status-granted { color: #059669; }
.status-denied  { color: var(--muted); }

.perm-override-badge {
  display: inline-flex;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 999px;
  white-space: nowrap;
}

.override-yes {
  background: rgba(245,158,11,0.12);
  color: #b45309;
}

.override-no {
  background: rgba(239,68,68,0.10);
  color: #dc2626;
}

.perm-override-empty {
  color: var(--muted-2);
  font-size: 0.74rem;
  font-weight: 600;
}

.perm-action-cell { white-space: nowrap; }

/* ============================================================
   CONTROL CENTER
   ============================================================ */

/* El segmented NO envuelve: si las pestañas superan el ancho, se hace scroll
   horizontal dentro de la barra (rueda con Shift o arrastre táctil, pero también
   con la propia barra de scroll — no todo el mundo conoce Shift+rueda). La barra
   es no invasiva estilo sidebar: carril propio abajo, transparente hasta hover. */
.control-tabs {
  display: flex;
  flex-wrap: nowrap;
  gap: 3px;
  margin-bottom: 28px;
  overflow-x: auto;
  overflow-y: hidden;
  background: var(--surface-2);
  border-radius: 999px;                        /* pill moderno */
  padding: 5px;
  width: 100%;
  max-width: 100%;
  /* Scrollbar OCULTO: un carril de scroll dentro de un pill segmentado redondeado
     se ve raro y reservaba un espacio fantasma arriba aunque el thumb estuviera
     invisible. La barra sigue siendo scrolleable con rueda/trackpad/drag; sin
     carril reservado = look limpio del pill en todo momento. */
  scrollbar-width: none;                       /* Firefox */
  -ms-overflow-style: none;                     /* IE/Edge legacy */
}
.control-tabs::-webkit-scrollbar { display: none; height: 0; }              /* WebKit */
/* Ya no se voltea el contenedor (antes rotateX(180deg) subía el scrollbar): sin
   scrollbar visible, el flip solo complicaba y no aporta. Se retira el
   contra-flip de las tabs (regla `.control-tabs > .control-tab`) más abajo. */

.control-tab {
  appearance: none;
  border: 0;
  border-radius: 999px;                        /* pill moderno */
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  padding: 8px 18px;
  white-space: nowrap;
  flex-shrink: 0;                              /* no se comprimen: fuerzan el scroll */
  transition: background 0.15s, color 0.15s;
}
/* (El contra-flip de .control-tabs > .control-tab se retiró junto con el flip del
   contenedor: ya no hay rotateX en la barra, así que las tabs se ven normales sin
   necesidad de contra-voltearlas.) */

.control-tab:not(.active):hover {
  background: var(--control-bg-hover);
  color: var(--text);
}

/* Estado activo autosuficiente (no depende de la global .active): sólido de marca
   + texto blanco en oscuro; el override light-mode lo suaviza. Tokens sólidos que
   siempre resuelven (evita el fallback a violeta-sólido con texto invisible). */
.control-tab.active {
  background: var(--brand-dark) !important;
  color: #fff !important;
  box-shadow: 0 1px 4px rgba(0,0,0,0.12) !important;
}

.control-tab:disabled {
  cursor: not-allowed;
  opacity: 0.4;
}

/* ── Centro de Control: tabs en 2 niveles (#10) ──────────────────────────────
   Nivel 1 = GRUPOS (segmented fuerte, centrado, solo 3 → fit-content). */
.cc-group-tabs {
  width: fit-content; max-width: 100%;
  margin-left: 0; margin-right: auto; margin-bottom: 18px;
}
/* Nivel 2 = SUBTABS del grupo activo: texto plano con SUBRAYADO en la activa,
   alineadas a la IZQUIERDA y "con menos fuerza" para que resalte el segmented
   de arriba. Separadas del nivel-1 (margin del grupo) y con línea base tipo pestañas.
   Nota: el global `.active { background:!important; color:!important }` (línea 98)
   le ganaba en especificidad → la activa salía como pill sólido azul. Por eso
   background/color van con !important aquí. */
.cc-subtabs {
  display: flex; justify-content: flex-start; flex-wrap: wrap;
  gap: 2px; margin: 0 0 24px;
  border-bottom: 1px solid var(--border-soft);
}
.cc-subtab {
  appearance: none; border: 0; border-radius: 0; background: transparent !important;
  cursor: pointer; font-family: inherit; font-size: 0.8rem; font-weight: 500;
  color: var(--muted) !important;
  padding: 7px 13px; border-bottom: 2px solid transparent; margin-bottom: -1px;
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  box-shadow: none !important;
  transition: color 0.15s, border-color 0.15s;
}
.cc-subtab:hover:not(.active):not(:disabled) { color: var(--text) !important; }
.cc-subtab.active {
  color: var(--brand) !important; border-bottom-color: var(--brand); font-weight: 600;
}
.cc-subtab:disabled { opacity: 0.4; cursor: not-allowed; }
/* El tooltip de las subtabs se pinta flotante (script .cc-tab-tip) igual que en las
   tabs; se oculta el texto inline por defecto. */
.cc-subtabs .info-tooltip-text { display: none; }

.control-table .col-code {
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.78rem;
}

.control-table .col-outputs {
  color: var(--muted);
  max-width: 320px;
}

.control-table .col-token {
  min-width: 160px;
  vertical-align: top;
}
.control-table .col-token .key-status-info {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  margin-bottom: 6px;
}

/* ── Badge de token pressable + popover (columna Token de Canales) ──────────────
   El badge dice solo el estado (Validado / Inválido / Sin verificar) y al pulsarlo
   abre un popover con la fecha de la última verificación y un botón para re-verificar
   (el resultado se avisa por toast, no en la celda). */
/* ── Compartir canal: enlace público + QR ─────────────────────────────────── */
.cc-share-gen { margin-top: 12px; width: 100%; }
.cc-share-result { margin-top: 16px; display: flex; flex-direction: column; gap: 10px; }
/* El QR necesita fondo claro para escanearse en cualquier tema — excepción
   intencional al tema, igual que .totp-qr-container. */
.cc-share-qr {
  display: flex; justify-content: center;
  padding: 14px; background: #fff; border-radius: 12px;
  border: 1px solid var(--border);
}
.cc-share-qr img { width: 200px; height: 200px; display: block; }
.cc-share-linkrow { display: flex; gap: 8px; align-items: center; }
.cc-share-linkrow .form-input { flex: 1 1 auto; min-width: 0; }
.cc-share-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.cc-share-actions .btn-action,
.cc-share-actions .btn-secondary { text-decoration: none; text-align: center; }

/* ── Identidad Visual › Códigos QR ──────────────────────────────────────────── */
.qr-editor-grid {
  display: grid;
  grid-template-columns: minmax(260px, 1fr) minmax(220px, 280px);
  gap: 24px; align-items: start; margin-top: 12px;
}
.qr-editor-controls { display: flex; flex-direction: column; gap: 10px; }
.qr-editor-colors { display: flex; gap: 16px; }
.qr-editor-color { display: flex; flex-direction: column; gap: 4px; }
.qr-color-input {
  width: 56px; height: 34px; padding: 2px; cursor: pointer;
  border: 1px solid var(--control-border); border-radius: 8px; background: var(--control-bg);
}
.qr-range { width: 100%; accent-color: var(--brand); }
.qr-editor-logo-upload { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.qr-editor-save { display: flex; align-items: center; gap: 12px; margin-top: 8px; }
.qr-saved { font-size: .8125rem; color: var(--badge-success-fg); }
.qr-editor-preview { display: flex; flex-direction: column; gap: 8px; }
.qr-preview-box {
  display: flex; align-items: center; justify-content: center;
  min-height: 220px; padding: 16px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px;
}
.qr-preview-box img { width: 100%; max-width: 240px; height: auto; display: block; }
@media (max-width: 720px) { .qr-editor-grid { grid-template-columns: 1fr; } }

.cc-token-wrap { position: relative; display: inline-block; }
.cc-token-badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 0.72rem; font-weight: 600; font-family: inherit;
  border: 1px solid transparent; cursor: default;   /* estado, NO botón (#5) */
  transition: filter 0.15s ease, box-shadow 0.15s ease;
}
.cc-token-wrap:hover .cc-token-badge { filter: brightness(1.05); box-shadow: 0 0 0 3px rgba(var(--brand-rgb),0.10); }
.cc-token-badge:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.cc-token-caret { opacity: 0.7; flex-shrink: 0; }
.cc-token-badge--ok      { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.cc-token-badge--bad     { background: var(--badge-danger-bg);  color: var(--badge-danger-fg); }
.cc-token-badge--unknown { background: var(--surface-2);        color: var(--muted); }

/* position: fixed → el popover escapa el overflow del .users-table-wrap (que recorta
   por su overflow-x:auto). El wrap no tiene transform, así que fixed sale de la tabla
   y va por encima (#7). Un script (index.html) fija top/left desde el rect del badge;
   el elemento NO se mueve en el DOM, así el botón "Verificar" conserva su listener.
   Se REVELA al HOVER del wrap (#5): oculto por defecto, visible al pasar el cursor o
   al enfocar por teclado (focus-within). El script lo ancla pegado al badge (gap 0)
   para que el cursor pueda entrar al popover y pulsar "Verificar" sin perder el hover. */
.cc-token-pop {
  position: fixed; z-index: 1000;
  min-width: 220px; display: none; flex-direction: column; gap: 10px;
  padding: 12px; background: var(--surface-overlay);
  border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-md);
}
.cc-token-wrap:hover .cc-token-pop,
.cc-token-wrap:focus-within .cc-token-pop { display: flex; }
.cc-token-pop-row { display: flex; flex-direction: column; gap: 2px; }
.cc-token-pop-label {
  font-size: 0.68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted);
}
.cc-token-pop-val { font-size: 0.82rem; color: var(--text); }
.cc-token-pop-err {
  font-size: 0.74rem; color: var(--badge-danger-fg); background: var(--badge-danger-bg);
  padding: 6px 8px; border-radius: 6px; word-break: break-word;
}
.cc-token-verify { align-self: flex-start; }

/* Popover informativo al HOVER (#9): usado en columnas de la Base de conocimiento
   (Canales / Estado / Actualizado). position:fixed para escapar el overflow de la
   tabla; un script (index.html) lo ancla al rect del disparador. */
.hovpop { position: relative; display: inline-flex; cursor: default; }
.hovpop__panel {
  position: fixed; z-index: 1000;
  visibility: hidden; opacity: 0;
  min-width: 160px; max-width: 300px;
  display: flex; flex-direction: column; gap: 4px;
  padding: 10px 12px; background: var(--surface-overlay);
  border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-md);
  transition: opacity 0.15s ease;
}
.hovpop:hover .hovpop__panel,
.hovpop:focus-within .hovpop__panel { visibility: visible; opacity: 1; }
.hovpop__row { font-size: 0.8rem; color: var(--text); word-break: break-word; }
.hovpop__row--label { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }

/* Fila de la tabla de system prompts: clickeable → abre el modal de detalle/edición.
   La celda de acciones corta la propagación (sus botones no abren el modal). */
.control-row-click { cursor: pointer; }
.control-row-click:hover > td { background: var(--control-bg-hover); }

/* Botón de acción deshabilitado dentro de .tb-icon-wrap: sin captura de ratón, así
   el envoltorio recibe el hover y muestra su tooltip + cursor de bloqueo. */
.tb-icon-wrap.is-disabled .pg-btn:disabled { pointer-events: none; }

.badge-status {
  border-radius: 999px;
}

.badge-status-live {
  background: rgba(16,185,129,0.12);
  color: #059669;
}

.badge-status-test {
  background: rgba(245,158,11,0.14);
  color: #b45309;
}

.badge-status-off {
  background: rgba(148,163,184,0.18);
  color: var(--muted);
}

.output-section {
  margin-bottom: 4px;
}

.form-section-label {
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  margin: 0 0 8px;
  text-transform: uppercase;
}

.output-grid {
  display: grid;
  /* min 140px: la etiqueta más larga ("Documentos") necesita aire para no rozar
     el borde de su toggle en el modal angosto. */
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 8px;
  margin: 0 0 20px;
}

.output-toggle {
  align-items: center;
  background: var(--surface-2);
  border: 1.5px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  cursor: pointer;
  display: flex;
  font-size: 0.84rem;
  font-weight: 500;
  gap: 8px;
  min-height: 44px;
  padding: 8px 12px;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s, color 0.15s;
  user-select: none;
}

.output-toggle:hover {
  background: var(--surface-2);
  border-color: var(--muted-2);
}

.output-toggle:has(input:checked) {
  background: var(--brand-soft-bg);
  border-color: var(--brand);
  color: var(--brand-dark);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb),0.1);
}

.output-toggle input[type="checkbox"] {
  accent-color: var(--brand);
  cursor: pointer;
  flex-shrink: 0;
  height: 15px;
  width: 15px !important;
}

.output-toggle-icon {
  font-size: 1rem;
  line-height: 1;
  flex-shrink: 0;
}

.output-toggle-label {
  flex: 1;
  min-width: 0;
}

.readonly-line {
  align-items: center;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
  padding: 10px 14px;
}

.readonly-info-pair {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.readonly-label {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.readonly-line strong {
  color: var(--text);
  font-size: 0.84rem;
  font-weight: 600;
}

/* ID del canal: empujado a la derecha y en gris tenue → dato de referencia, no
   protagonista. */
.readonly-info-pair--id { margin-left: auto; text-align: right; }
.readonly-info-pair--id strong { color: var(--muted-2); font-weight: 500; }

.control-form-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow-sm);
  padding: 20px 24px 0;
}

.form-section {
  margin-bottom: 24px;
}

.form-section-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
  margin: 0 0 16px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

/* Checkbox con etiqueta dentro de .form-field: `.form-field label` (block)
   pisa a `.check-label` (flex) por especificidad y el checkbox quedaba
   flotando en su propia línea. Restaurar el layout en línea. */
.form-field label.check-label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
  font-weight: 600;
}

/* Límites → Bloqueo de loop concurrente: lista de estado por canal */
.ll-channel-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 620px;
  margin-top: 8px;
}
.ll-channel-row {
  display: grid;
  grid-template-columns: minmax(180px, 1fr) 170px auto;
  gap: 12px;
  align-items: center;
  padding: 8px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
}
.ll-channel-name {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.ll-channel-name > span:first-child {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ll-channel-id {
  font-size: 0.72rem;
  color: var(--muted);
}
.control-form-panel .form-field select.ll-channel-select {
  width: 170px;
  padding: 7px 10px;
}
.ll-override-tag {
  font-size: 0.7rem;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--badge-info-bg);
  color: var(--badge-info-fg);
  white-space: nowrap;
}
@media (max-width: 560px) {
  .ll-channel-row { grid-template-columns: 1fr; gap: 6px; }
  .control-form-panel .form-field select.ll-channel-select { width: 100%; }
}

.control-form-panel .form-field input:not([type="checkbox"]):not([type="radio"]),
.control-form-panel .form-field textarea,
.control-form-panel .form-field select {
  width: 100%;
  padding: 10px 14px;
  border-radius: 10px;
  border: 2px solid var(--border);
  font-size: 0.9rem;
  font-family: inherit;
  background: var(--surface-2);
  color: var(--text);
  transition: border-color 0.2s;
}

.control-form-panel .form-field select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 16px;
  padding-right: 36px;
  cursor: pointer;
}

.control-form-panel .form-field input:not([type="checkbox"]):not([type="radio"]):focus,
.control-form-panel .form-field textarea:focus,
.control-form-panel .form-field select:focus {
  outline: none;
  border-color: var(--brand-dark);
  background: var(--surface);
}

.control-form-panel .form-field input:not([type="checkbox"]):not([type="radio"]):disabled,
.control-form-panel .form-field textarea:disabled,
.control-form-panel .form-field select:disabled {
  color: var(--muted);
  cursor: not-allowed;
}

.control-textarea {
  min-height: 150px;
  resize: vertical;
}

.control-textarea-tall {
  min-height: 220px;
}

.control-form-footer {
  margin-left: -24px;
  margin-right: -24px;
}

.control-save-state {
  color: #059669;
  font-size: 0.8rem;
  font-weight: 700;
}

/* ============================================================
   LLM panel
   ============================================================ */

.llm-key-field {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 16px;
}

.key-input-row {
  display: flex;
  gap: 8px;
  align-items: center;
}

.key-input-row input {
  flex: 1;
}

.key-set-badge {
  color: #059669;
  font-weight: 600;
  font-size: 0.85rem;
}

.key-unset-badge {
  color: var(--muted-2);
  font-weight: 500;
  font-size: 0.85rem;
}

.form-hint {
  display: block;
  margin-top: 4px;
  font-size: 0.75rem;
  color: var(--muted-2);
  line-height: 1.4;
}

.evidencia-tipos {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 8px;
  background: var(--panel-2);
  border-radius: 6px;
  flex-wrap: wrap;
}
.evidencia-label {
  font-size: 0.75rem;
  color: var(--muted-2);
  white-space: nowrap;
}

input[type="range"] {
  width: 100%;
  accent-color: var(--brand);
  cursor: pointer;
}

input[type="range"]:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.range-labels {
  display: flex;
  justify-content: space-between;
  font-size: 0.72rem;
  color: var(--muted-2);
  margin-top: 2px;
}

.range-val {
  font-weight: 700;
  color: var(--brand);
  font-variant-numeric: tabular-nums;
}

/* ── LLM panel extras ───────────────────────────────────── */

.fallback-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

/* Fallback chips — hide native checkbox, use pill style */
.fallback-checks .check-label {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 13px;
  border-radius: 20px;
  border: 1.5px solid var(--border);
  background: var(--surface-2);
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
  transition: all 0.15s;
  line-height: 1;
}

.fallback-checks .check-label input[type="checkbox"] {
  display: none;
}

.fallback-checks .check-label::before {
  content: '○';
  font-size: 0.75rem;
  opacity: 0.5;
  transition: all 0.15s;
}

.fallback-checks .check-label:has(input:checked) {
  background: rgba(var(--brand-rgb),0.08);
  border-color: var(--brand);
  color: var(--brand-dark);
  font-weight: 600;
}

.fallback-checks .check-label:has(input:checked)::before {
  content: '✓';
  opacity: 1;
}

.fallback-checks .check-label:hover:not(:has(input:disabled)) {
  border-color: var(--muted-2);
  color: var(--text);
}

.timeout-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-top: 6px;
}

.timeout-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.timeout-label {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
}

.provider-model-tabs {
  display: flex;
  gap: 3px;
  margin-bottom: 14px;
  background: var(--surface-2);
  border-radius: 999px;                        /* pill moderno */
  padding: 4px;
  width: fit-content;
  max-width: 100%;
  flex-wrap: wrap;
  overflow-x: auto;
}

.model-fields-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 16px;
}

/* Campos de modelos apilados verticalmente (#11): un campo encima de otro,
   alineados a la izquierda (no lado a lado como el grid anterior). */
.llm-model-fields { display: flex; flex-direction: column; gap: 14px; }
/* Campos de modelo con tamaño fijo — no se estiran a lo ancho del panel (#11 fix). */
.llm-model-fields .model-input-wrap { width: 100%; max-width: 460px; }
.control-form-panel .form-field input.control-timeout-input { max-width: 220px; }

/* Proveedor activo como selector principal: centrado y separado del resto (#11 fix). */
.llm-provider-field { align-items: center; text-align: center; }
.provider-model-tabs--main {
  margin: 6px auto 22px;
  padding: 5px;
  gap: 4px;
}
.provider-model-tabs--main .control-tab { padding: 9px 22px; font-size: 0.9rem; }

/* Botón "catálogo de modelos" (icono libro) DENTRO del campo, a la derecha (#11b).
   Sin caja; una línea divisoria lo separa del texto; con hover. Fuera de un
   .model-input-wrap (p. ej. Multimedia aún sin reestructurar) queda como icono inline. */
.model-catalog-btn {
  background: transparent; border: 0; cursor: pointer; color: var(--muted);
  padding: 4px 6px; display: inline-flex; align-items: center;
  transition: color 0.15s ease;
}
.model-catalog-btn:hover { color: var(--brand); }
.model-input-wrap { position: relative; display: block; }
.model-input-wrap .model-catalog-btn {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  padding: 4px 8px; border-left: 1px solid var(--control-border); border-radius: 0;
  height: 60%;
}
/* Press leve: conservar el centrado vertical, solo un pequeño scale (antes el
   :active global lo empujaba hacia abajo perdiendo el translateY). */
.model-input-wrap .model-catalog-btn:active { transform: translateY(-50%) scale(0.94); }
.control-form-panel .form-field input.model-input-with-btn { padding-right: 46px; }

.llm-keys-section {
  margin-top: 24px;
  padding: 16px 24px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
}
/* API Keys colapsable (#11d). */
.llm-keys-head {
  display: flex; align-items: center; justify-content: space-between;
  cursor: pointer; user-select: none;
}
.llm-keys-caret { color: var(--muted); transition: transform 0.2s ease; }
.llm-keys-caret.is-open { transform: rotate(180deg); }
.llm-keys-body { margin-top: 12px; }
/* Colapso suave (grid-template-rows 0fr→1fr anima a altura auto sin JS). */
.llm-keys-collapse { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.22s ease; }
.llm-keys-collapse.is-open { grid-template-rows: 1fr; }
.llm-keys-collapse-inner { overflow: hidden; }

/* Bloque de contenido grande dentro del modal de detalle (Base de conocimiento):
   contenedor interno propio, no una fila más de .detail-row. */
.detail-content-block { margin-top: 16px; }
.detail-content-title {
  display: block; font-size: 0.78rem; font-weight: 600; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 8px;
}
.detail-content-box {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
  padding: 14px 16px; max-height: 340px; overflow-y: auto;
  white-space: pre-wrap; word-break: break-word; line-height: 1.55;
  color: var(--text); font-size: 0.9rem;
}
.detail-content-box--muted { color: var(--muted); font-style: italic; }
.detail-content-box.is-empty { color: var(--muted); font-style: italic; }

/* Modal de detalle de Personalidad (#3): KPIs (puntos clave) arriba en grid + los
   contenedores con el contenido completo (personalidad / conocimiento) debajo. */
.pers-kpi-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px; margin-bottom: 20px;
}
.pers-kpi {
  display: flex; flex-direction: column; gap: 3px;
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: 10px; padding: 10px 12px;
}
.pers-kpi-label {
  font-size: 0.66rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted);
}
.pers-kpi-val { font-size: 0.92rem; font-weight: 600; color: var(--text-strong); word-break: break-word; }
.pers-sections { display: flex; flex-direction: column; gap: 16px; }
.pers-section { display: flex; flex-direction: column; gap: 8px; }
.pers-section-title {
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--muted);
}

/* Modal de detalle de Encuesta (#7): acciones rápidas (participación/resultados) +
   apartado dedicado a las preguntas. */
.survey-detail-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 16px 0; }
.survey-detail-actions .pg-btn { display: inline-flex; align-items: center; gap: 6px; }
.survey-q-section { margin-top: 8px; }
.survey-q-list {
  list-style: none; counter-reset: sq; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 6px;
}
.survey-q-item {
  counter-increment: sq;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: 8px; padding: 10px 12px;
}
.survey-q-item::before {
  content: counter(sq); flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 999px;
  background: var(--brand-soft-bg); color: var(--brand-soft-fg);
  font-size: 0.72rem; font-weight: 700;
}
.survey-q-label { flex: 1 1 auto; color: var(--text); font-size: 0.9rem; word-break: break-word; }
.survey-q-meta { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; }
.survey-q-type {
  font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--muted); background: var(--surface-3); border-radius: 6px; padding: 2px 7px;
}
.survey-q-req {
  font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--badge-warn-fg); background: var(--badge-warn-bg); border-radius: 6px; padding: 2px 7px;
}

/* Tabs internos del modal de detalle (Base de conocimiento: Detalle | Historial).
   Segmented pill simple, sin el flip/scroll de .control-tabs. Borde/artefacto
   eliminados (#6): sin border ni box-shadow heredados; el pill activo lo pinta
   .control-tab.active (fade de marca) sin sombra dura. */
/* Underline limpio (mismo lenguaje que los subtabs #10 y los ticket-monitor-subtabs):
   texto plano con subrayado de marca en la activa. Antes era un pill segmentado sobre
   una banda surface-2 → la inactiva mostraba la banda y parecían dos botones iguales
   (bug reportado). background/color con !important para ganarle al global
   `.active{background!important}`. */
.detail-modal-tabs {
  display: flex; gap: 2px; padding: 0; margin: 0 0 16px;
  background: transparent; border: 0; border-bottom: 1px solid var(--border-soft);
  border-radius: 0; width: 100%;
}
.detail-modal-tabs .control-tab {
  border: 0; border-radius: 0; box-shadow: none !important;
  background: transparent !important; color: var(--muted) !important;
  padding: 8px 16px; border-bottom: 2px solid transparent; margin-bottom: -1px; font-weight: 500;
}
.detail-modal-tabs .control-tab:hover:not(.active) { color: var(--text) !important; }
.detail-modal-tabs .control-tab.active {
  background: transparent !important; color: var(--brand) !important;
  border-bottom-color: var(--brand); font-weight: 600;
}

/* Modal de detalle de la Base de conocimiento con TAMAÑO FIJO (#6): cambiar de tab
   (Detalle ↔ Historial) no reajusta el modal; el cuerpo scrollea internamente. */
.kn-detail-modal { display: flex; flex-direction: column; height: 80vh; max-height: 740px; overflow: hidden; }
.kn-detail-modal .modal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* Cabecera del contenedor de contenido: título + botón «expandir en grande». */
.detail-content-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.detail-content-head .detail-content-title { margin-bottom: 0; }

/* Contenido del índice EXPANDIDO en un modal apilado POR ENCIMA del de detalle (#6). */
.modal-backdrop--stacked { z-index: 200; }
.kn-content-modal { display: flex; flex-direction: column; height: 80vh; max-height: 760px; overflow: hidden; }
.kn-content-modal .modal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.kn-content-box-lg { max-height: none; height: 100%; box-sizing: border-box; }

/* Historial como TABLA (antes filas <details> desplegables) (#6). */
.kn-hist-table-wrap { margin-top: 4px; }
.kn-hist-table .kn-hist-changes { font-size: 0.82rem; }

/* Cambiar key (#11d.2): botón siempre visible; el input+Guardar/Cancelar van en un
   popover flotante (position:fixed, anclado por JS) para escapar el overflow del
   colapsable. Verificar/Cambiar nunca mutan a Guardar/Cancelar. */
.key-change-anchor { position: relative; display: inline-block; }
.btn-edit.is-active { background: var(--brand-soft-bg); color: var(--brand-soft-fg); }
.key-change-pop {
  position: fixed; z-index: 1200; top: 0; left: 0;
  width: 260px; box-sizing: border-box;
  background: var(--surface-overlay); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: var(--shadow-lg); padding: 12px;
  display: flex; flex-direction: column; gap: 8px;
}
/* Multimedia (#11f): toggle con título arriba + tooltip al lado, toggle debajo;
   al apagar, el cuerpo de config se atenúa y bloquea. */
.mm-toggle-field { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; margin-bottom: 20px; }
.mm-toggle-title { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; color: var(--text-strong); }
/* Toggle multimedia: DEBAJO del proveedor, SIN contenedor visible y alineado a
   la IZQUIERDA. Título arriba e interruptor debajo, como el resto de ajustes de
   sí/no del panel: con los dos en la misma línea era el único que se leía de
   derecha a izquierda. */
.mm-toggle-inline {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  margin: 18px 0 24px;
}
.mm-config-body { transition: opacity 0.2s ease; }
.mm-config-body.is-locked { opacity: 0.45; pointer-events: none; user-select: none; }
.mm-free-section { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }

.key-change-label { font-size: 0.78rem; font-weight: 600; color: var(--muted); }
.key-change-input {
  width: 100%; box-sizing: border-box; padding: 8px 10px;
  background: var(--control-bg); border: 1px solid var(--control-border);
  border-radius: 6px; color: var(--text); font-size: 0.88rem;
}
.key-change-input:focus { outline: none; border-color: var(--brand); }
.key-change-actions { display: flex; justify-content: flex-end; gap: 8px; }

.llm-keys-title {
  margin: 0 0 4px;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--text-strong);
}

.llm-keys-grid {
  display: flex;
  flex-direction: column;
}

.llm-key-row .timeout-label {
  min-width: 148px;
  flex-shrink: 0;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-strong);
}

/* ============================================================
   LLM Key Status badges
   ============================================================ */
.key-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 10px;
  border-radius: 12px;
  font-size: 0.72rem;
  font-weight: 600;
  white-space: nowrap;
}
.key-badge.valid       { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.key-badge.low-credits { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.key-badge.invalid     { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.key-badge.no-key      { background: var(--surface-2); color: var(--muted); }
.key-badge.error       { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.key-badge.unknown     { background: var(--surface-2); color: var(--muted-2); }

.key-credits-info {
  font-size: 0.7rem;
  color: var(--muted);
  margin-left: 2px;
}
.key-error-hint {
  font-size: 0.68rem;
  color: #991b1b;
}
.key-checked-at {
  font-size: 0.65rem;
  color: var(--muted);
}

.btn-verify {
  padding: 5px 12px;
  font-size: 0.75rem;
  font-weight: 600;
  font-family: inherit;
  border-radius: 7px;
  border: 1.5px solid var(--border);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  transition: all 0.15s;
  white-space: nowrap;
}
.btn-verify:hover:not(:disabled) {
  border-color: var(--brand);
  color: var(--brand);
  background: rgba(var(--brand-rgb),0.05);
}
.btn-verify:disabled { opacity: 0.45; cursor: not-allowed; }

/* Notification tabs (Plantillas | Historial) */
.notif-tab-nav {
  display: flex;
  gap: 4px;
  margin-bottom: 20px;
  border-bottom: 2px solid var(--border);
  padding-bottom: 0;
}
.notif-tab {
  padding: 8px 18px;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
  border-radius: 6px 6px 0 0;
}
.notif-tab:hover { color: var(--brand); background: rgba(var(--brand-rgb),0.05); }
.notif-tab--active {
  color: var(--brand);
  border-bottom-color: var(--brand);
  background: rgba(var(--brand-rgb),0.07);
}

/* Notification log — filter bar */
.notif-log-filters {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 16px;
  padding: 14px 16px;
  background: var(--panel-1);
  border: 1px solid var(--border);
  border-radius: 10px;
}

/* Notification log — pagination */
.notif-log-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 0 4px;
}

/* Variable chips — email template editor */
.var-chips-container {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  margin-bottom: 14px;
}
.var-chips-label {
  display: block;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 8px;
}
.var-chips-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.var-chip {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: 20px;
  font-size: 0.72rem;
  font-family: 'JetBrains Mono', Consolas, monospace;
  font-weight: 600;
  background: rgba(var(--brand-rgb),0.08);
  color: var(--brand);
  border: 1.5px solid rgba(var(--brand-rgb),0.25);
  cursor: pointer;
  transition: all 0.12s;
  white-space: nowrap;
  line-height: 1;
}
.var-chip:hover {
  background: rgba(var(--brand-rgb),0.18);
  border-color: var(--brand);
  transform: translateY(-1px);
}
/* form-label utility used in tpl create form */
.form-label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-1);
  margin-bottom: 6px;
}

/* Input base GLOBAL — con tokens (tema claro/oscuro). Antes solo existía
   `.kn-wizard .form-input`, así que cualquier `.form-input` fuera del wizard
   (p. ej. los modales de edición dedicados) quedaba sin estilo → se veía roto /
   sin tema oscuro. `.kn-wizard .form-input` (más específico) sigue ganando donde
   aplica, así que no hay regresión en los wizards. */
.form-input {
  display: block;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  padding: 10px 14px;
  border-radius: 10px;
  border: 2px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
  font-size: 0.9rem;
  font-family: inherit;
  outline: none;
  transition: border-color 0.18s, box-shadow 0.18s, background 0.18s;
}
.form-input:focus {
  border-color: var(--brand-dark);
  background: var(--surface);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb), .12);
}
.form-input::placeholder { color: var(--muted-2); }

.key-status-row {
  display: flex;
  align-items: center;
  gap: 16px;
  flex: 1;
  min-width: 0;
  padding: 11px 0;
  border-bottom: 1px solid var(--border-soft);
}

.key-status-info {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  flex-wrap: wrap;
}

.key-status-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
  align-items: center;
}

.llm-key-row {
  display: flex;
  align-items: center;
  gap: 0;
}

.llm-key-row:last-child .key-status-row { border-bottom: none; }

/* ============================================================
   Auditoría
   ============================================================ */
/* La barra de filtros propia de Auditoría (.audit-filters / .audit-filter-*) se
   retiró: el módulo usa la toolbar estándar (.tickets-toolbar + embudo). */

.audit-table-wrap {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: auto;
  box-shadow: var(--shadow-sm);
  margin-bottom: 18px;
}
.audit-loading {
  padding: 40px;
  text-align: center;
  color: var(--muted);
  font-size: 0.9rem;
}
.audit-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.83rem;
}
.audit-table thead tr {
  background: var(--panel-2);
  border-bottom: 1px solid var(--border);
}
.audit-table th {
  padding: 10px 14px;
  font-weight: 600;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  text-align: left;
  white-space: nowrap;
}
.audit-table td { padding: 9px 14px; border-bottom: 1px solid var(--border); vertical-align: top; }
.audit-table tbody tr:last-child td { border-bottom: none; }
.audit-table tbody tr:hover td { background: rgba(var(--brand-rgb),0.03); }

.audit-cell--dt   { white-space: nowrap; color: var(--muted); font-size: 0.8rem; }
.audit-cell--user { min-width: 160px; }
.audit-cell--action { max-width: 380px; font-size: 0.84rem; }
.audit-cell--resource { color: var(--muted); font-size: 0.8rem; white-space: nowrap; }
.audit-cell--ip   { color: var(--muted); font-size: 0.78rem; white-space: nowrap; }
.audit-cell--result { white-space: nowrap; }
.audit-cell--actions { white-space: nowrap; }

.audit-user-name  { display: block; font-weight: 500; }
.audit-user-email { display: block; font-size: 0.76rem; color: var(--muted); }

/* Celda de acción en tres líneas: qué se hizo · sobre qué objeto · qué cambió.
   El objetivo y el resumen son el snapshot legible que graba core/audit_diff.py;
   las filas anteriores a esa función simplemente no los pintan. */
.audit-action-name    { display: block; font-weight: 500; }
.audit-action-target  { display: block; font-size: 0.78rem; color: var(--text); }
.audit-change-summary {
  display: block;
  font-size: 0.76rem;
  color: var(--muted);
  line-height: 1.35;
  margin-top: 2px;
}

.audit-action {
  font-family: 'Fira Code', 'Cascadia Code', monospace;
  font-size: 0.78rem;
  background: var(--panel-2);
  border-radius: 4px;
  padding: 1px 6px;
  color: var(--brand-dark);
  word-break: break-all;
}

.audit-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 10px;
  border-radius: 12px;
  font-size: 0.72rem;
  font-weight: 600;
}
.audit-badge--ok     { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.audit-badge--denied { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.audit-badge--error  { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
/* La columna Módulo usa el mismo badge que Resultado, en morado neutro. */
.audit-badge--module { background: var(--badge-purple-bg); color: var(--badge-purple-fg); }

.audit-error-detail {
  display: block;
  font-size: 0.72rem;
  color: var(--danger);
  margin-top: 2px;
  cursor: help;
}

/* El detalle ya no expande la fila: se abre en modal (.audit-detail-modal), así
   que .audit-toggle-btn, .audit-row--expanded, .audit-row-detail y
   .audit-module-tag (que además fijaba colores a mano) quedaron sin uso. */
.audit-cell--module { white-space: nowrap; }

/* ── Modal de detalle del registro ── */
.audit-detail-modal { max-width: 640px; width: 100%; }

.audit-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px 18px;
  margin-bottom: 14px;
}

.audit-module-tag {
  display: inline-block;
  font-family: 'Fira Code', 'Cascadia Code', monospace;
  font-size: 0.72rem;
  background: var(--badge-purple-bg);
  color: var(--badge-purple-fg);
  border: 1px solid rgba(var(--brand-rgb), 0.30);
  border-radius: 4px;
  padding: 2px 7px;
  white-space: nowrap;
}

.audit-detail-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  word-break: break-word;
}

.audit-detail-section {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.audit-detail-label {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.audit-detail-label--error { color: var(--badge-danger-fg); }

/* Separa cada bloque de texto largo de lo que tiene encima dentro del modal. */
.audit-detail-section + .audit-detail-section { margin-top: 12px; }

.audit-changes-pre {
  font-size: 0.78rem;
  font-family: 'Fira Code', 'Cascadia Code', monospace;
  white-space: pre-wrap;
  word-break: break-all;
  color: var(--text);
  margin: 0;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  max-height: 300px;
  overflow-y: auto;
}

.audit-error-pre {
  background: var(--badge-danger-bg);
  border-color: var(--badge-danger-bg);
  color: var(--badge-danger-fg);
}

/* Layout del pager en el bloque canónico (buscar "PAGINACIÓN ESTÁNDAR"). */

/* ============================================================
   Metrics module
   ============================================================ */

.metrics-toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  flex-wrap: wrap;
  padding: 16px 40px 0;
}

.metrics-header-controls {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.metrics-period-select {
  appearance: none;
  -webkit-appearance: none;
  background: rgba(255,255,255,0.1);
  color: var(--text-strong);
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: 8px;
  padding: 7px 30px 7px 12px;
  font-size: 0.85rem;
  font-family: inherit;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23f1f5f9' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 12px;
  transition: border-color 0.2s;
}
.metrics-period-select:focus { outline: none; border-color: rgba(255,255,255,0.5); }
.metrics-period-select option { background: var(--panel-2); color: var(--text-strong); }

.metrics-refresh-btn {
  padding: 7px 12px;
  font-size: 1rem;
  min-width: 36px;
  background: rgba(255,255,255,0.1);
  border: 1px solid rgba(255,255,255,0.2);
  color: var(--text-strong);
  border-radius: 8px;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.15s;
}
.metrics-refresh-btn:hover { background: rgba(255,255,255,0.18); }

/* ── Main tab nav (pill-group) ── */
/* SEGMENTED: estilo en el bloque canónico. Aquí solo su posición en la cabecera. */
/* La barra vive DENTRO del main (como en Centro de Control), así que no lleva
   gutter propio: los 40px de antes la desalineaban del contenido de abajo. */
.metrics-tabs { margin: 0 0 20px; }

/* ── KPI grid ── */
.metrics-kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 12px;
  margin-bottom: 24px;
}

.metrics-kpi-card {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border);
  border-left: 3px solid var(--kpi-accent, var(--brand));
  border-radius: 10px;
  padding: 18px 16px;
  box-shadow: var(--shadow-sm);
}

.metrics-kpi-value {
  font-size: 1.7rem;
  font-weight: 700;
  color: var(--text);
  line-height: 1;
  margin-bottom: 6px;
}

.metrics-kpi-label {
  font-size: 0.75rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 600;
}

/* ── Charts grid ── */
.metrics-charts-grid {
  display: grid;
  /* auto-fit (no auto-fill): colapsa las columnas vacías y estira los gráficos
     presentes para llenar todo el ancho, proporcional a la tabla de abajo. */
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 16px;
  margin-bottom: 24px;
}

.metrics-charts-grid--2col {
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
}

.metrics-chart-card {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 18px 16px;
  box-shadow: var(--shadow-sm);
}

.metrics-chart-card--full { grid-column: 1 / -1; }
.metrics-chart-card--sm   { min-width: 200px; }

.metrics-chart-title {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 14px;
}

.metrics-chart-container { min-height: 200px; }

.metrics-section-title {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-strong);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin: 16px 0 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-soft);
}

.metrics-loading {
  text-align: center;
  padding: 48px 24px;
  color: var(--muted);
  font-size: 0.95rem;
}

.metrics-empty {
  color: var(--muted);
  font-size: 0.88rem;
  padding: 32px 16px;
  text-align: center;
}

/* ── Barras de cobertura de guías (Uso del Panel › Guías) ── */
.metrics-cov-list { display: flex; flex-direction: column; gap: 14px; padding: 6px 2px; }
.metrics-cov-row { display: flex; flex-direction: column; gap: 6px; }
.metrics-cov-head { display: flex; justify-content: space-between; align-items: baseline; font-size: 13px; }
.metrics-cov-label { font-weight: 600; color: var(--text-strong); }
.metrics-cov-val { font-variant-numeric: tabular-nums; color: var(--muted); font-weight: 600; }
.metrics-cov-track { height: 12px; background: var(--surface-2); border-radius: 999px; overflow: hidden; }
.metrics-cov-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--brand), var(--brand-soft-fg));
  border-radius: 999px;
  transition: width 0.4s ease;
  min-width: 2px;
}

/* ── Agent sub-tabs (pill-group) ── */
/* SEGMENTED: estilo en el bloque canónico. */
.metrics-agent-tabs { border-bottom: none; }

/* ── Light controls for Investment Tab ── */
.metrics-select-light {
  appearance: none;
  -webkit-appearance: none;
  background-color: var(--control-bg);
  color: var(--text);
  border: 1px solid var(--control-border);
  border-radius: 8px;
  padding: 7px 32px 7px 12px;
  font-size: 0.85rem;
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 12px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  box-shadow: var(--shadow-sm);
}
.metrics-select-light:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb), 0.15);
}
.metrics-select-light option {
  background: var(--surface);
  color: var(--text);
}

.metrics-input-light {
  background: var(--control-bg);
  color: var(--text);
  border: 1px solid var(--control-border);
  border-radius: 8px;
  padding: 6px 12px;
  font-size: 0.85rem;
  font-family: inherit;
  font-weight: 500;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.metrics-input-light:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb), 0.15);
}


/* ============================================================
   LIVECHAT MODULE
   ============================================================ */

/* Badge variants (--modifier on base .badge) */
.badge--warn    { background: rgba(245,158,11,0.15);  color: #b45309; border: 1px solid rgba(245,158,11,0.3); }
.badge--success { background: rgba(16,185,129,0.12);  color: #059669; border: 1px solid rgba(16,185,129,0.25); }
.badge--danger  { background: rgba(239,68,68,0.12);   color: #dc2626; border: 1px solid rgba(239,68,68,0.25); }
.badge--info    { background: rgba(var(--brand-rgb),0.12);  color: var(--brand-soft-fg); border: 1px solid rgba(var(--brand-rgb),0.25); }
.badge--locked  { background: rgba(239,68,68,0.10);   color: #b91c1c; border: 1px solid rgba(239,68,68,0.22); }
.badge--ticket-active { background: rgba(var(--brand-rgb),0.10); color: var(--brand-soft-fg); border: 1px solid rgba(var(--brand-rgb),0.22); }

/* Page header meta (conversation count) */
.page-header-meta {
  font-size: 0.8125rem;
  color: var(--muted);
  font-weight: 500;
  margin-left: auto;
}

/* ── 3-column layout ─────────────────────────────────────── */
.livechat-page {
  height: 100vh;
  overflow: hidden;
}

.livechat-page .page-header {
  flex-shrink: 0;
}

.livechat-layout {
  display: flex;
  align-items: stretch;
  height: calc(100vh - 60px);
  overflow: hidden;
  background: var(--bg);
}

/* ── Bandeja (izquierda) — ancho por estilo inline (redimensionable) ─────── */
.livechat-sidebar {
  position: relative;               /* ancla del popover de filtros */
  flex: 0 0 auto;
  width: 320px;                     /* fallback; el inline manda */
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-right: 1px solid var(--border-soft);
  overflow: hidden;
  transition: width 0.22s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Arrastrando el resizer el ancho debe seguir al puntero al instante. */
.livechat-sidebar.is-resizing { transition: none; }
.livechat-sidebar--collapsed { width: 48px !important; }

/* Barra superior de la bandeja: búsqueda + embudo + colapsar */
.lc-topbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 12px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  flex-shrink: 0;
}
.lc-topbar .lc-search { flex: 1 1 auto; min-width: 0; }
/* Toolbar estándar dentro de la bandeja: la pastilla manda el ancho y los
   iconos van pegados (mismo espaciado que .contacts-search-form). */
.lc-topbar { gap: 2px; }
.lc-topbar .contacts-search-input { flex: 1 1 auto; min-width: 0; }
.lc-topbar--collapsed { justify-content: center; padding: 12px 4px; }

/* Riel de la bandeja colapsada: los avatares siguen visibles y clicables. */
.lc-rail-list {
  list-style: none; margin: 0; padding: 6px 0;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  overflow-y: auto; flex: 1 1 auto; min-height: 0;
  scrollbar-width: thin; scrollbar-color: transparent transparent;
}
.lc-rail-list:hover { scrollbar-color: rgba(255,255,255,0.20) transparent; }
.lc-rail-list::-webkit-scrollbar { width: 6px; }
.lc-rail-list::-webkit-scrollbar-track { background: transparent; }
.lc-rail-list::-webkit-scrollbar-thumb { background: transparent; border-radius: 6px; }
.lc-rail-list:hover::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.20); }
.lc-rail-item {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0;
  border: none; border-radius: 10px; background: transparent;
  cursor: pointer; transition: background 0.12s ease;
}
.lc-rail-item:hover { background: var(--control-bg-hover); }
.lc-rail-item.is-selected { background: var(--brand-soft-bg); box-shadow: inset 0 0 0 1px var(--brand); }
.lc-rail-dot {
  position: absolute; top: 3px; right: 3px;
  width: 7px; height: 7px; border-radius: 50%; background: #f59e0b;
}

/* Botones cuadrados de la barra (embudo, colapsar) */
.lc-filter-btn,
.lc-collapse-btn {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px; height: 34px;
  border: 1px solid var(--control-border);
  border-radius: 9px;
  background: var(--control-bg);
  color: var(--text);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.lc-filter-btn:hover,
.lc-collapse-btn:hover { background: var(--control-bg-hover); }
.lc-filter-btn.is-active { border-color: var(--brand); color: var(--brand); }
.lc-filter-count {
  position: absolute; top: -5px; right: -5px;
  min-width: 15px; height: 15px; padding: 0 3px;
  background: var(--brand); color: #fff;
  border-radius: 8px; font-size: 0.6rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center; line-height: 1;
}

/* Popover de filtros (empaquetados tras el embudo) */
.lc-filters-backdrop {
  position: fixed; inset: 0; z-index: 40;
  background: transparent;
}
.lc-filters-popover {
  position: absolute; top: 58px; left: 8px; right: 8px; z-index: 41;
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-md);
}

/* Wrapper scrollable de la lista (se oculta al colapsar) */
.lc-list-wrap {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
.livechat-sidebar--collapsed .lc-list-wrap { display: none; }

/* ── Resizer entre paneles ─────────────────────────────────── */
.lc-resizer {
  flex: 0 0 auto;
  width: 5px;
  cursor: col-resize;
  background: transparent;
  transition: background 0.15s;
}
.lc-resizer:hover { background: var(--brand); }

.livechat-filters {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  flex-shrink: 0;
}

.filter-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

/* Búsqueda de conversaciones (nombre / user_id) */
.lc-search {
  position: relative;
  display: flex;
  align-items: center;
}
.lc-search-icon {
  position: absolute;
  left: 10px;
  font-size: 0.8rem;
  opacity: 0.7;
  pointer-events: none;
}
.lc-search-input {
  width: 100%;
  padding: 8px 30px 8px 30px;
  border: 1px solid var(--control-border);
  border-radius: 8px;
  background: var(--control-bg);
  color: var(--text);
  font-size: 0.85rem;
  outline: none;
}
.lc-search-input::placeholder { color: var(--muted-2); }
.lc-search-input:focus {
  border-color: var(--brand);
  background: var(--control-bg-hover);
}
.lc-search-clear {
  position: absolute;
  right: 8px;
  border: none;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.8rem;
  line-height: 1;
  padding: 4px;
  border-radius: 6px;
}
.lc-search-clear:hover { color: var(--text); background: var(--control-bg-hover); }

.livechat-filters select,
.lc-filters-popover select {
  width: 100%;
  padding: 9px 30px 9px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,0.12);
  background: rgba(255,255,255,0.07);
  color: var(--muted);
  font-size: 0.78rem;
  font-family: inherit;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  outline: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 10px;
}

.livechat-filters select:focus,
.lc-filters-popover select:focus {
  border-color: rgba(99,179,237,0.5);
  background-color: rgba(255,255,255,0.1);
}

.livechat-filters select option,
.lc-filters-popover select option { background: var(--panel-2); }

.filter-channel-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") !important;
}

.filter-pills {
  display: flex;
  gap: 6px;
}

.filter-pill {
  flex: 1;
  padding: 8px 10px;
  border-radius: 20px;
  border: 1px solid rgba(255,255,255,0.12);
  background: rgba(255,255,255,0.05);
  color: var(--muted-2);
  font-size: 0.75rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: all 0.15s;
  text-align: center;
}

.filter-pill:hover {
  background: rgba(255,255,255,0.1);
  color: var(--text);
}

.filter-pill--active {
  background: rgba(var(--brand-rgb),0.2) !important;
  border-color: rgba(var(--brand-rgb),0.4) !important;
  color: var(--brand-soft-fg) !important;
}

.conversation-list {
  list-style: none;
  overflow-y: auto;
  flex: 1;
  padding: 8px;
}

.conversation-list::-webkit-scrollbar { width: 4px; }
.conversation-list::-webkit-scrollbar-track { background: transparent; }
.conversation-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 2px; }

.conv-item {
  padding: 12px 14px;
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.15s ease;
  margin-bottom: 4px;
}

.conv-item:hover {
  background: rgba(255,255,255,0.06);
}

.conv-item--selected {
  background: rgba(var(--brand-rgb),0.18) !important;
  border: 1px solid rgba(var(--brand-rgb),0.3);
}

.conv-item-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}

.conv-channel-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: #475569;
  flex-shrink: 0;
  overflow: hidden;
}

.conv-channel-icon {
  width: 30px;
  height: 30px;
  display: block;
  object-fit: cover;
}

.conv-contact-pic {
  width: 30px;
  height: 30px;
  display: block;
  object-fit: cover;
  border-radius: 50%;
}

/* Per-channel brand colors (fallback when img fails) */
.conv-channel-badge--wa   { background: #25D366; }
.conv-channel-badge--wapi { background: #1DAA61; }
.conv-channel-badge--ig   { background: linear-gradient(135deg, #833AB4, #E1306C, #F77737); }
/* Comentarios de IG: mismo degradado pero con anillo punteado — distingue a simple
   vista un comentario público de un DM privado en la lista de conversaciones. */
.conv-channel-badge--igc  {
  background: linear-gradient(135deg, #833AB4, #E1306C, #F77737);
  outline: 2px dashed var(--badge-warn-fg);
  outline-offset: 1px;
}
.conv-channel-badge--tg   { background: #229ED9; }
.conv-channel-badge--fb   { background: #0084FF; }
.conv-channel-badge--tt   { background: #111; }
.conv-channel-badge--web  { background: #3b5bdb; }
.conv-channel-badge--other { background: #475569; }

/* Channel type cell in config table */
.ch-type-cell {
  align-items: center;
  display: flex;
  gap: 8px;
}

.ch-type-label {
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 500;
  white-space: nowrap;
}

.conv-title {
  flex: 1;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.conv-item-meta {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 6px;
}

.conv-last-msg {
  font-size: 0.75rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
  max-width: 180px;
}

.conv-time {
  font-size: 0.7rem;
  color: var(--muted);
  white-space: nowrap;
  flex-shrink: 0;
}

.conv-assigned {
  font-size: 0.7rem;
  color: #22c55e;
  margin-top: 2px;
  font-weight: 500;
}

/* Sidebar pagination */
.livechat-sidebar .pagination {
  padding: 8px 12px;
  border-top: 1px solid rgba(255,255,255,0.06);
  flex-shrink: 0;
  justify-content: space-between;
}

.livechat-sidebar .pagination button {
  padding: 5px 10px;
  border-radius: 7px;
  font-size: 0.75rem;
  font-weight: 600;
  font-family: inherit;
  background: rgba(255,255,255,0.07);
  color: var(--muted-2);
  border: 1px solid rgba(255,255,255,0.1);
  cursor: pointer;
  transition: all 0.15s;
}

.livechat-sidebar .pagination button:hover:not(:disabled) {
  background: rgba(255,255,255,0.13);
  color: var(--text);
}

.livechat-sidebar .pagination button:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.livechat-sidebar .pagination span {
  font-size: 0.75rem;
  color: var(--muted);
}

.livechat-sidebar .loading-row,
.livechat-sidebar .error-banner {
  margin: 6px;
  padding: 8px 12px;
  font-size: 0.8rem;
  border-radius: 8px;
}

.livechat-sidebar .loading-row {
  color: var(--muted);
  text-align: center;
  padding: 20px;
}

/* ── Historial de mensajes (centro) ──────────────────────── */
.livechat-messages {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface-3);
  border-right: 1px solid var(--border);
  overflow: hidden;
}

/* Nombre del contacto en la cabecera del chat → abre la ficha derecha */
.mh-contact-name {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 260px;
  padding: 5px 8px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: var(--text-strong);
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.mh-contact-name:hover { background: var(--control-bg); border-color: var(--control-border); }
.mh-contact-name-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mh-contact-name-caret { flex-shrink: 0; opacity: 0.6; }

.livechat-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 0.9375rem;
  font-weight: 500;
  background: var(--bg);
}

.messages-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-3);
  flex-shrink: 0;
  gap: 12px;
  flex-wrap: wrap;
}

.messages-header-id {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.messages-header-id strong {
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.action-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.action-error {
  font-size: 0.78rem;
  color: var(--danger);
  font-weight: 500;
  max-width: 200px;
}

/* Livechat action buttons */
.btn-action--pause {
  padding: 6px 14px;
  border-radius: 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  border: none;
  cursor: pointer;
  transition: all 0.15s ease;
  background: rgba(245,158,11,0.12);
  color: #b45309;
  border: 1px solid rgba(245,158,11,0.3);
}
.btn-action--pause:hover:not(:disabled) { background: rgba(245,158,11,0.2); }

.btn-action--resume {
  padding: 6px 14px;
  border-radius: 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: all 0.15s ease;
  background: rgba(16,185,129,0.12);
  color: #059669;
  border: 1px solid rgba(16,185,129,0.3);
}
.btn-action--resume:hover:not(:disabled) { background: rgba(16,185,129,0.2); }

.btn-action--assign {
  padding: 6px 14px;
  border-radius: 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: all 0.15s ease;
  background: rgba(var(--brand-rgb),0.1);
  color: var(--brand-dark);
  border: 1px solid rgba(var(--brand-rgb),0.25);
}
.btn-action--assign:hover:not(:disabled) { background: rgba(var(--brand-rgb),0.18); }

.btn-action--unassign {
  padding: 6px 14px;
  border-radius: 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: all 0.15s ease;
  background: var(--surface-2);
  color: var(--muted);
  border: 1px solid var(--border);
}
.btn-action--unassign:hover:not(:disabled) { background: var(--control-bg-hover); color: var(--text); }

.btn-action--resolve {
  padding: 6px 14px;
  border-radius: 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: all 0.15s ease;
  background: rgba(139,92,246,0.1);
  color: #7c3aed;
  border: 1px solid rgba(139,92,246,0.25);
}
.btn-action--resolve:hover:not(:disabled) { background: rgba(139,92,246,0.18); }

.btn-action--send {
  padding: 6px 18px;
  border-radius: 8px;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.15s ease;
  background: var(--brand-dark);
  color: #fff;
  border: none;
  white-space: nowrap;
  align-self: flex-end;
}
.btn-action--send:hover:not(:disabled) { background: var(--brand-dark); }

.btn-action--pause:disabled,
.btn-action--resume:disabled,
.btn-action--assign:disabled,
.btn-action--unassign:disabled,
.btn-action--resolve:disabled,
.btn-action--send:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Reply box */
.reply-box {
  flex-shrink: 0;
  border-top: 1px solid var(--border);
  background: var(--surface-3);
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.reply-input-row {
  display: flex;
  gap: 10px;
  align-items: flex-end;
}

.reply-textarea {
  flex: 1;
  min-height: 72px;
  max-height: 160px;
  resize: vertical;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 10px;
  padding: 10px 14px;
  font-size: 0.85rem;
  font-family: inherit;
  line-height: 1.5;
  color: var(--text);
  background: rgba(255,255,255,0.07);
  outline: none;
  transition: border-color 0.15s, background 0.15s;
}
.reply-textarea:focus { border-color: rgba(99,179,237,0.5); background: rgba(255,255,255,0.1); }
.reply-textarea::placeholder { color: var(--muted); }

.reply-error {
  margin: 0;
  font-size: 0.8rem;
}

.reply-ia-notice {
  flex-shrink: 0;
  border-top: 1px solid var(--border);
  background: var(--surface)beb;
  color: #92400e;
  font-size: 0.85rem;
  font-weight: 500;
  padding: 12px 16px;
  text-align: center;
}
.reply-ia-notice--warn {
  background: var(--badge-danger-bg);
  color: #991b1b;
  border-top-color: #fecaca;
}

/* Messages list */
.messages-list {
  flex: 1;
  overflow-y: auto;
  padding: 20px 28px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: var(--surface-2);
}

.messages-list::-webkit-scrollbar { width: 5px; }
.messages-list::-webkit-scrollbar-track { background: transparent; }
.messages-list::-webkit-scrollbar-thumb { background: var(--muted-2); border-radius: 3px; }

/* Bubbles */
.msg-bubble {
  max-width: 72%;
  padding: 12px 16px;
  border-radius: 16px;
  word-break: break-word;
}

.msg-bubble--user {
  align-self: flex-end;
  background: var(--badge-info-bg);
  border: 1px solid rgba(var(--brand-rgb),0.25);
  border-bottom-right-radius: 4px;
}

.msg-bubble--agent {
  align-self: flex-start;
  background: var(--surface);
  border: 1px solid var(--border);
  border-bottom-left-radius: 4px;
  box-shadow: var(--shadow-sm);
}

.msg-bubble--operator {
  align-self: flex-start;
  background: var(--badge-success-bg);
  border: 1px solid rgba(34,197,94,0.25);
  border-bottom-left-radius: 4px;
}

.msg-bubble--system {
  align-self: center;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 5px 14px;
  max-width: 90%;
}

.msg-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

.msg-sender {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--muted);
}

.msg-bubble--user .msg-sender { color: var(--badge-info-fg); }
.msg-bubble--operator .msg-sender { color: var(--badge-success-fg); }
.msg-bubble--system .msg-sender { display: none; }

.msg-time {
  font-size: 0.68rem;
  color: var(--muted-2);
  margin-left: auto;
}

.msg-text {
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-wrap;
}

.msg-bubble--system .msg-text {
  font-size: 0.78rem;
  color: var(--muted);
  text-align: center;
}

.msg-empty {
  color: var(--muted-2) !important;
  font-style: italic;
}

.msg-media-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--brand-dark);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 500;
}
.msg-media-link:hover { text-decoration: underline; }

/* ── Rich media bubbles ─────────────────────────────────────── */
.msg-media-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.msg-media-img {
  max-width: 280px;
  max-height: 280px;
  border-radius: 10px;
  object-fit: cover;
  display: block;
  cursor: pointer;
  transition: opacity 0.15s;
}
.msg-media-img:hover { opacity: 0.88; }

.msg-media-sticker {
  max-width: 140px;
  max-height: 140px;
  object-fit: contain;
  display: block;
}

.msg-media-video {
  max-width: 300px;
  border-radius: 10px;
  display: block;
  background: #000;
}

.msg-media-audio {
  width: 260px;
  max-width: 100%;
  height: 36px;
  accent-color: var(--brand);
}

.msg-media-doc {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 8px;
  background: rgba(0,0,0,0.05);
  color: var(--brand-dark);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 500;
  max-width: 260px;
}
.msg-media-doc:hover { background: rgba(0,0,0,0.1); }
.msg-media-doc-icon { font-size: 1.2rem; flex-shrink: 0; }
.msg-media-doc-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.msg-media-caption {
  margin-top: 2px;
  font-size: 0.825rem;
  color: var(--text);
}

.msg-location {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.msg-location-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--brand-dark);
  font-size: 0.875rem;
  font-weight: 600;
  text-decoration: none;
  background: rgba(var(--brand-rgb),0.1);
  padding: 5px 10px;
  border-radius: 6px;
}
.msg-location-link:hover { background: rgba(var(--brand-rgb),0.18); }
.msg-location-addr {
  font-size: 0.78rem;
  color: var(--muted);
}

/* ── Ficha del contacto (derecha) ───────────────────────── */
.livechat-contact-panel {
  flex: 0 0 auto;
  width: 320px;                     /* fallback; el inline manda */
  display: flex;
  flex-direction: column;
  background: var(--surface-3);
  overflow-y: auto;
  padding: 20px;
}
/* Barra superior de la ficha (título + X para cerrar) */
.lc-contact-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 12px;
}
.lc-contact-topbar-title {
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-2);
}
.lc-contact-close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.lc-contact-close:hover { background: var(--control-bg); border-color: var(--control-border); color: var(--text); }

/* Tabs de la ficha del contacto — scroll horizontal si el panel es angosto.
   La barra de scroll va ARRIBA del strip (truco de flip vertical) y usa el mismo
   estilo overlay auto-oculto de la sidebar. */
.lc-contact-tabs-wrap { display: flex; flex-direction: column; gap: 10px; }
.lc-tabs {
  display: flex;
  gap: 3px;
  padding: 4px;
  background: var(--surface-2);
  border-radius: 10px;
  overflow-x: auto;
  transform: scaleY(-1);            /* voltea → la barra (abajo) queda arriba */
  scrollbar-width: thin;            /* Firefox */
  scrollbar-color: transparent transparent;
}
.lc-tabs > * { transform: scaleY(-1); }   /* restaura el contenido */
.lc-tabs:hover { scrollbar-color: rgba(255, 255, 255, 0.20) transparent; }
.lc-tabs::-webkit-scrollbar { height: 8px; }
.lc-tabs::-webkit-scrollbar-track { background: transparent; }
.lc-tabs::-webkit-scrollbar-thumb {
  background: transparent;
  border: 2px solid transparent;
  background-clip: padding-box;
  border-radius: 8px;
  transition: background-color 0.2s ease;
}
.lc-tabs:hover::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.20); background-clip: padding-box; }
.lc-tabs:hover::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.34); background-clip: padding-box; }
html.light-mode .lc-tabs:hover { scrollbar-color: rgba(0, 0, 0, 0.22) transparent; }
html.light-mode .lc-tabs:hover::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.18); background-clip: padding-box; }
html.light-mode .lc-tabs:hover::-webkit-scrollbar-thumb:hover { background: rgba(0, 0, 0, 0.30); background-clip: padding-box; }
.lc-tab {
  flex: 1 0 auto;
  white-space: nowrap;              /* no romper el texto → desborda y hace scroll */
  padding: 7px 10px;
  border: none;
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
  font-family: inherit;
  font-size: 0.74rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.lc-tab:hover { color: var(--text); }
.lc-tab.is-active { background: var(--brand-dark); color: #fff; }
.lc-tab-panel { display: flex; flex-direction: column; gap: 12px; }
.lc-tab-panel.is-hidden { display: none; }

.livechat-contact-panel::-webkit-scrollbar { width: 4px; }
.livechat-contact-panel::-webkit-scrollbar-track { background: transparent; }
.livechat-contact-panel::-webkit-scrollbar-thumb { background: var(--muted-2); border-radius: 2px; }

.contact-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 0.875rem;
  font-weight: 500;
  text-align: center;
  padding: 24px;
}

.contact-card {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 20px;
  box-shadow: var(--shadow-sm);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.contact-name {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 4px;
}

.contact-field {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.contact-label {
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--muted-2);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.contact-value {
  font-size: 0.8375rem;
  color: var(--text);
  font-weight: 500;
  word-break: break-all;
}

.contact-card .badge {
  align-self: flex-start;
}

.contact-profile-pic {
  width: 76px;
  height: 76px;
  border-radius: 50%;
  object-fit: cover;
  align-self: center;
  border: 2px solid var(--border);
}

.contact-card-header {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 6px;
}

.contact-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.contact-section-title {
  font-size: 0.65rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  padding: 10px 0 4px;
  border-bottom: 1px solid var(--border);
  margin-top: 6px;
}

.contact-empty { color: var(--text-muted, #9ca3af); font-style: italic; font-size: 0.8rem; }
.contact-yes   { color: #16a34a; font-weight: 600; }
.contact-no    { color: var(--text); }

/* ── Drawer full-detail extra classes ───────────────────────── */

.drawer-section-title {
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  padding: 10px 0 2px;
  border-bottom: 1px solid var(--border);
  margin-top: 4px;
}

.field-empty { color: var(--text-muted, #9ca3af); font-style: italic; }
.field-yes   { color: #16a34a; font-weight: 600; }
.field-no    { color: var(--text); }

.drawer-loading {
  padding: 12px 0;
  color: var(--text-muted, #9ca3af);
  font-size: 0.875rem;
}

.drawer-error {
  padding: 8px;
  color: #dc2626;
  background: var(--badge-danger-bg);
  border-radius: 6px;
  font-size: 0.8rem;
  margin: 8px 0;
}

/* ── Agent JSON response rendering ──────────────────────────── */
.agent-response {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.agent-thought {
  background: rgba(0,0,0,0.04);
  border: 1px solid rgba(0,0,0,0.08);
  border-radius: 8px;
  padding: 6px 10px;
  font-size: 0.82rem;
  color: var(--muted);
}

.agent-thought summary.agent-thought-label {
  cursor: pointer;
  font-weight: 600;
  font-size: 0.78rem;
  color: var(--muted-2);
  user-select: none;
  list-style: none;
  padding: 2px 0;
}

.agent-thought summary.agent-thought-label::-webkit-details-marker {
  display: none;
}

.agent-thought[open] summary.agent-thought-label {
  margin-bottom: 6px;
  color: var(--muted);
}

.agent-thought .msg-text {
  font-size: 0.8rem;
  color: var(--muted);
  font-style: italic;
  margin: 2px 0;
}

.agent-user-parts {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.agent-user-parts .msg-text {
  margin: 0;
}

/* Metadatos del agente por mensaje: tarea + etapa embudo */
.msg-agent-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 2px;
}

.msg-flag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 10px;
  white-space: nowrap;
}

.msg-flag--task {
  background: rgba(245, 158, 11, 0.12);
  color: #b45309;
  border: 1px solid rgba(245, 158, 11, 0.3);
}

.msg-flag--done {
  background: rgba(34, 197, 94, 0.12);
  color: #15803d;
  border: 1px solid rgba(34, 197, 94, 0.3);
}

.msg-flag--embudo {
  background: rgba(var(--brand-rgb), 0.10);
  color: var(--brand);
  border: 1px solid rgba(var(--brand-rgb), 0.25);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: default;
}

/* ============================================================
   Base de Conocimiento (Centro de Control)
   ============================================================ */

.kn-toolbar { flex-wrap: wrap; gap: 10px; }
.kn-filters { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.kn-search {
  padding: 7px 10px; border: 1px solid var(--control-border); border-radius: 8px;
  font-size: 0.85rem; min-width: 180px;
}
.kn-select {
  padding: 7px 10px; border: 1px solid var(--control-border); border-radius: 8px;
  font-size: 0.85rem; background: var(--surface); color: var(--text);
}

/* Badges de tipo */
.kn-tipo-badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 0.74rem; font-weight: 600; padding: 3px 9px;
  border-radius: 10px; white-space: nowrap;
}
.kn-tipo-informacion_basica      { background: rgba(59,130,246,.12); color:#1d4ed8; border:1px solid rgba(59,130,246,.3); }
.kn-tipo-instrucciones           { background: rgba(14,165,233,.12); color:#0369a1; border:1px solid rgba(14,165,233,.3); }
.kn-tipo-promociones             { background: rgba(236,72,153,.12); color:#be185d; border:1px solid rgba(236,72,153,.3); }
.kn-tipo-informacion_corporativa { background: rgba(100,116,139,.12); color: var(--text-strong); border:1px solid rgba(100,116,139,.3); }
.kn-tipo-reglas                  { background: rgba(34,197,94,.12); color:#15803d; border:1px solid rgba(34,197,94,.3); }
.kn-tipo-restricciones           { background: rgba(239,68,68,.12); color:#b91c1c; border:1px solid rgba(239,68,68,.3); }

/* Chips de canal */
.kn-chip {
  display: inline-block; font-size: 0.72rem; padding: 2px 8px; margin: 1px 2px;
  border-radius: 10px; background: rgba(var(--brand-rgb),.1); color:var(--brand);
  border: 1px solid rgba(var(--brand-rgb),.25);
}
.kn-chip-all { background: rgba(34,197,94,.1); color:#15803d; border-color: rgba(34,197,94,.25); }
.kn-td-chips { max-width: 200px; }
.kn-td-tema { font-weight: 600; max-width: 260px; }
.kn-td-updated { font-size: 0.78rem; color: var(--muted); }

.kn-row-expired td { opacity: 0.75; }
.kn-badge-expired {
  display:inline-block; margin-left:5px; font-size:0.7rem; font-weight:700;
  padding:2px 7px; border-radius:8px; background:rgba(239,68,68,.12);
  color:#b91c1c; border:1px solid rgba(239,68,68,.3);
}
.kn-badge-hidden {
  display:inline-block; margin-left:5px; font-size:0.7rem; font-weight:600;
  padding:2px 7px; border-radius:8px; background:rgba(245,158,11,.12);
  color:#b45309; border:1px solid rgba(245,158,11,.3);
}
.btn-danger { color:#b91c1c; border-color: rgba(239,68,68,.4); }

/* ── Wizard ── */
.kn-wizard {
  max-width: 720px; width: 95%; max-height: 92vh;
  overflow-y: auto; padding: 24px 28px;
}
.kn-wizard-head { display:flex; justify-content:space-between; align-items:center; margin-bottom:14px; }
.kn-wizard-body { min-height: 280px; }

/* Campos del wizard */
.kn-form-step { display:flex; flex-direction:column; gap:0; width:100%; }
.kn-form-group { display:flex; flex-direction:column; gap:6px; margin-bottom:20px; }
.kn-form-group:last-child { margin-bottom:4px; }

/* Campo multimedia: lista de archivos (varios) + subir/añadir */
.kn-media { display:flex; flex-direction:column; gap:10px; }
.kn-media-row { display:flex; gap:8px; flex-wrap:wrap; align-items:center; }
.kn-media-list { display:flex; flex-direction:column; gap:10px; }
.kn-media-item {
  display:flex; flex-direction:column; gap:6px;
  padding:10px; border:1px solid var(--border-soft); border-radius:8px;
  background:var(--surface-2);
}
.kn-media-item-row { display:flex; gap:6px; align-items:center; }
.kn-media-item-row .form-input { flex:1; }
.kn-media-del { color:var(--badge-danger-fg, #ef4444); }
.kn-media-newurl { flex:1; min-width:160px; }
.kn-media-upload { cursor:pointer; display:inline-flex; align-items:center; }
.kn-media-upload.is-busy { opacity:0.6; pointer-events:none; }
.kn-media-required {
  display:flex; align-items:center; gap:6px;
  font-size:0.8rem; color:var(--muted); cursor:pointer; user-select:none;
}
.kn-media-required input { width:15px; height:15px; cursor:pointer; }
.kn-media-msg { font-size:0.8rem; color:var(--muted); }
.kn-media-ok  { font-size:0.82rem; color:var(--badge-success-fg, #22c55e); font-weight:600; }
.kn-media-err { font-size:0.82rem; color:var(--badge-danger-fg, #ef4444); font-weight:600; }

/* Stickers + galería base del system prompt (SystemPromptModal). Rejilla de tarjetas:
   miniatura + campos (nombre/descripción/URL) + botón de quitar. */
.sp-media-grid {
  display:grid;
  grid-template-columns:repeat(auto-fill, minmax(260px, 1fr));
  gap:12px; margin-bottom:12px;
}
.sp-media-card {
  display:flex; align-items:flex-start; gap:10px; position:relative;
  padding:10px; border:1px solid var(--border-soft); border-radius:10px;
  background:var(--surface-2);
}
.sp-media-thumb {
  flex:0 0 52px; width:52px; height:52px; border-radius:8px; overflow:hidden;
  background:var(--control-bg); display:flex; align-items:center; justify-content:center;
}
.sp-media-thumb img { width:100%; height:100%; object-fit:cover; }
.sp-media-kind { font-size:10px; font-weight:700; letter-spacing:0.06em; color:var(--muted); }
.sp-media-fields { flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:6px; }
.sp-media-fields .form-input { width:100%; box-sizing:border-box; }
.sp-media-del {
  flex:0 0 auto; border:none; background:transparent; cursor:pointer;
  color:var(--muted); font-size:14px; line-height:1; padding:2px 5px; border-radius:6px;
}
.sp-media-del:hover { color:var(--badge-danger-fg, #ef4444); background:var(--badge-danger-bg); }
.sp-media-add { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.sp-media-newurl { flex:1 1 240px; min-width:180px; }
.sp-media-upload { cursor:pointer; display:inline-flex; align-items:center; }
.sp-media-upload.is-busy { opacity:0.6; pointer-events:none; }
.sp-media-msg { margin-top:8px; font-size:0.8rem; color:var(--muted); }

.kn-wizard .form-label {
  display:flex; align-items:center; gap:4px;
  font-size:0.83rem; font-weight:600; color: var(--text-strong);
}
.kn-wizard .form-input {
  display:block; width:100%; padding:10px 14px;
  border-radius:10px; border:2px solid var(--border);
  background: var(--surface-2); font-size:0.9rem; font-family:inherit;
  color: var(--text-strong); transition: border-color 0.18s, box-shadow 0.18s, background 0.18s;
  box-sizing:border-box; outline:none;
}
.kn-wizard .form-input:focus {
  border-color:var(--brand); background: var(--surface);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb),.12);
}
.kn-wizard .form-input::placeholder { color: var(--muted-2); font-size:0.86rem; }

.kn-textarea-sm { min-height: 90px; resize: vertical; line-height:1.55; }
.kn-textarea-md { min-height: 120px; resize: vertical; line-height:1.55; }
.kn-textarea-lg { min-height: 260px; resize: vertical; font-family:inherit; line-height:1.6; }

.kn-stepper {
  display:flex; gap: 4px; margin-bottom: 20px; flex-wrap: wrap;
}
.kn-step {
  display:flex; align-items:center; gap:6px; padding:5px 10px;
  border-radius: 14px; font-size: 0.76rem; color: var(--muted-2);
}
.kn-step-dot {
  display:inline-flex; align-items:center; justify-content:center;
  width: 20px; height: 20px; border-radius:50%;
  background: var(--control-bg-hover); color: var(--muted); font-size:0.72rem; font-weight:700;
  flex-shrink:0;
}
.kn-step.active { color:var(--brand); font-weight:600; background: rgba(var(--brand-rgb),.08); }
.kn-step.active .kn-step-dot { background:var(--brand-dark); color:#fff; }
.kn-step.done .kn-step-dot { background:#22c55e; color:#fff; }
.kn-step.done { color:#15803d; }

.kn-step-question { font-weight:600; color: var(--text); margin: 4px 0 14px; }

/* Cards de tipo (paso 1) */
.kn-tipo-cards {
  display:grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 10px;
}
.kn-tipo-card {
  display:flex; flex-direction:column; align-items:flex-start; gap:4px;
  padding: 13px 14px; border: 2px solid var(--border); border-radius: 12px;
  background: rgba(255,255,255,0.04); cursor:pointer; text-align:left; transition: border-color .15s, box-shadow .15s;
  width:100%; box-sizing:border-box;
}
.kn-tipo-card:hover { border-color:var(--brand); background: rgba(255,255,255,0.08); }
.kn-tipo-card.selected { border-color:var(--brand); background: rgba(var(--brand-rgb),0.12); box-shadow: 0 0 0 3px rgba(var(--brand-rgb),.15); }
.kn-tipo-card-icon { font-size: 1.5rem; }

/* Canal chips — selector de canales en wizard de tareas */
.kn-channel-chips { display:flex; flex-wrap:wrap; gap:8px; margin-top:6px; }
.kn-channel-chip {
  padding: 5px 14px; border-radius: 20px;
  border: 1.5px solid var(--border); cursor: pointer;
  font-size: 0.82rem; color: var(--muted); background: var(--surface-2);
  font-weight: 500; transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.kn-channel-chip:hover { border-color: var(--brand); background: var(--surface-2); color: var(--brand); }
.kn-channel-chip.active {
  border-color: var(--brand); background: rgba(var(--brand-rgb),.08);
  color: var(--brand); font-weight: 700;
  box-shadow: 0 0 0 2px rgba(var(--brand-rgb),.15);
}
.kn-tipo-card-label { font-weight:700; color: var(--text-strong); font-size:0.88rem; }
.kn-tipo-card-desc { font-size:0.74rem; color: var(--muted); line-height:1.35; }

/* Tooltips de ayuda */
.kn-help {
  display:inline-flex; align-items:center; justify-content:center;
  width:17px; height:17px; border-radius:50%;
  background: var(--control-bg-hover); color: var(--muted); font-size:0.68rem; font-weight:700;
  cursor: help; flex-shrink:0;
}
.kn-hint-warn { margin-top:10px; padding:8px 12px; border-radius:8px; font-size:0.8rem; color:#92400e; background:rgba(245,158,11,.08); border:1px solid rgba(245,158,11,.25); }

/* ── Asistente de canal Telegram ─────────────────────────────────────────── */
.tg-help { display:block; margin-top:6px; font-size:0.78rem; color: var(--muted); line-height:1.4; }
.kn-warn { margin-top:10px; padding:8px 12px; border-radius:8px; font-size:0.8rem; color:#92400e; background:rgba(245,158,11,.10); border:1px solid rgba(245,158,11,.30); }
.kn-ok { margin-top:10px; padding:8px 12px; border-radius:8px; font-size:0.8rem; color:#065f46; background:rgba(16,185,129,.10); border:1px solid rgba(16,185,129,.30); }

/* Reemplazo de credenciales (solo owner) en el modal de edición de canal */
.cc-creds {
  margin-top: 18px;
  padding: 16px;
  border: 1px solid rgba(239, 68, 68, 0.25);
  border-radius: 12px;
  background: rgba(239, 68, 68, 0.04);
}
.cc-creds-head { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.cc-creds-title { font-weight: 700; color: var(--text); font-size: 0.92rem; }
.cc-creds-sub { color: var(--muted); font-size: 0.8rem; line-height: 1.4; }
.cc-masked { color: var(--muted); letter-spacing: 1px; }
.cc-creds-msg {
  margin-top: 10px;
  padding: 8px 12px;
  border-radius: 8px;
  font-size: 0.82rem;
  color: var(--brand-dark);
  background: rgba(var(--brand-rgb), 0.08);
  border: 1px solid rgba(var(--brand-rgb), 0.25);
}
.cc-creds-actions { margin-top: 12px; display: flex; justify-content: flex-end; }
/* margin-bottom 24px: el CTA es lo último visible del modal Crear (el bloque no
   guiado va oculto), así que sin este aire chocaría con el borde inferior. */
.tg-cta { margin:4px 0 24px; padding:18px; border:1px dashed #38bdf8; border-radius:12px; background:rgba(56,189,248,.06); text-align:center; }
.tg-cta-title { display:inline-flex; align-items:center; justify-content:center; gap:6px; margin:0 0 14px; font-weight:700; color:#0369a1; }
.tg-url { display:block; margin-top:6px; padding:8px 10px; border-radius:8px; background:#0f172a; color:#7dd3fc; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:0.78rem; word-break:break-all; }
.tg-secret-row { display:flex; gap:8px; align-items:center; }
.tg-secret-row input { flex:1; }
.tg-review { list-style:none; margin:8px 0; padding:0; }
.tg-review li { padding:6px 0; border-bottom:1px solid var(--border); font-size:0.85rem; color: var(--text-strong); }
.tg-review li:last-child { border-bottom:none; }
.tg-result .tg-review li { border-bottom:1px solid var(--border); }
.kn-required { color:#b91c1c; font-size:0.76rem; font-weight:600; }
.kn-optional { color: var(--muted-2); font-size:0.76rem; }

/* Duplicados */
.kn-dup-error { margin-top:6px; font-size:0.8rem; color:#b91c1c; font-weight:600; }
.kn-dup-warn {
  margin-top:8px; padding:8px 12px; border-radius:8px; font-size:0.8rem;
  background: rgba(245,158,11,.08); border:1px solid rgba(245,158,11,.3); color:#92400e;
}
.kn-dup-warn ul { margin: 4px 0 0 18px; }

/* Canales (paso 4) */
.kn-check { display:flex; align-items:center; gap:6px; padding:5px 0; font-size:0.85rem; color: var(--text-strong); cursor:pointer; }
.kn-channel-list { margin-left: 18px; border-left: 2px solid var(--border); padding-left: 12px; }
.kn-channel-type { font-size:0.7rem; color: var(--muted-2); margin-left:6px; }
.kn-fecha-row { display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
.kn-date { max-width: 170px; }
.kn-fecha-chips { display:flex; gap:6px; flex-wrap:wrap; }
.kn-chip-btn {
  padding: 4px 10px; font-size:0.76rem; border-radius: 12px;
  border:1px solid var(--control-border); background: var(--surface-2); color: var(--text-strong); cursor:pointer;
}
.kn-chip-btn:hover { background: var(--brand-soft-bg); border-color:var(--brand); }

/* Revisión (paso 5) */
.kn-preview-index {
  padding: 12px 14px; border:1px solid var(--border); border-radius:10px;
  background: var(--surface-2); margin-bottom:10px;
}
.kn-preview-index strong { display:block; margin-top:6px; color: var(--text-strong); }
.kn-preview-desc { margin:4px 0 0; font-size:0.82rem; color: var(--muted); }
.kn-preview-detail { margin: 8px 0; font-size: 0.82rem; }
.kn-preview-detail summary { cursor:pointer; color:var(--brand); font-weight:600; padding:4px 0; }
.kn-preview-content {
  white-space: pre-wrap; background: var(--surface-2); padding:10px 12px;
  border-radius:8px; font-size:0.78rem; max-height: 220px; overflow-y:auto;
}
.kn-human-summary {
  margin-top:10px; padding:10px 14px; border-radius:10px; font-size:0.85rem;
  background: rgba(34,197,94,.07); border:1px solid rgba(34,197,94,.25); color:#14532d;
}
.kn-edit-toggles { margin-top:12px; display:flex; gap:18px; }

/* Conflictos */
.kn-conflict {
  padding: 14px 16px; border-radius:10px; margin-bottom: 10px;
  background: rgba(245,158,11,.07); border:1px solid rgba(245,158,11,.35);
}
.kn-conflict-title { margin: 0 0 8px; color:#92400e; }
.kn-conflict-list { margin: 6px 0 10px 16px; font-size:0.84rem; }
.kn-similarity { color: var(--muted-2); font-size:0.76rem; }
.kn-llm-explain {
  padding:8px 12px; background: var(--surface); border-radius:8px;
  border:1px solid rgba(245,158,11,.3); font-size:0.84rem; color: var(--text-strong);
}

/* Historial */
.kn-history { max-width: 640px; width: 95%; max-height: 85vh; overflow-y:auto; padding: 22px 26px; }
.kn-hist-list { display:flex; flex-direction:column; gap:6px; }
.kn-hist-item {
  border:1px solid var(--border); border-radius:10px; padding:8px 12px; font-size:0.84rem;
}
.kn-hist-item summary { cursor:pointer; }
.kn-hist-action { font-weight:700; color:var(--brand); }
.kn-hist-date { float:right; color: var(--muted-2); font-size:0.76rem; }
.kn-hist-field { margin: 8px 0 0 8px; }
.kn-hist-fieldname { font-weight:600; font-size:0.78rem; color: var(--text-strong); }
.kn-hist-diff { display:flex; gap:8px; align-items:flex-start; margin-top:2px; flex-wrap:wrap; }
.kn-hist-old {
  background: rgba(239,68,68,.08); color:#991b1b; padding:2px 8px;
  border-radius:6px; font-size:0.76rem; max-width:45%; overflow-wrap:anywhere;
}
.kn-hist-new {
  background: rgba(34,197,94,.08); color:#166534; padding:2px 8px;
  border-radius:6px; font-size:0.76rem; max-width:45%; overflow-wrap:anywhere;
}
.kn-hist-arrow { color: var(--muted-2); }
.kn-hist-note { font-size:0.8rem; color: var(--muted); margin:6px 0 0; }

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 480px) {
  .login-card {
    padding: 28px 24px;
  }

  .app-header, .page-header {
    padding: 0 16px;
  }

  .app-main, .page-content {
    padding: 20px 16px;
  }

  .modules-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  /* En móvil el buscador baja bajo el saludo y ocupa el ancho: a 300px de base
     compartiría línea con el título y ninguno de los dos se leería. */
  .home-search {
    flex: 1 1 100%;
  }

  .greeting-title {
    font-size: 1.5rem;
  }

  .home-greeting {
    padding: 28px 24px;
  }
}

/* ============================================================
   CAMPAÑAS MASIVAS
   ============================================================ */

.campaigns-page {
  padding: 28px 32px;
}

.campaigns-header {
  margin-bottom: 20px;
}

.campaigns-title {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 4px;
}

.campaigns-subtitle {
  color: var(--muted);
  font-size: 0.88rem;
  margin: 0;
}

/* Los avisos del módulo salen por el toast global (ver .toast-host). */

/* ── Tabs: SEGMENTED estándar, estilo en el bloque canónico ── */
/* Icono (iconset) o emoji de la pestaña, alineado con el texto del label. */
.camp-tab svg { vertical-align: -0.15em; }

/* ── Content ── */
.camp-content {
  background: var(--surface);
  border-radius: 12px;
  border: 1px solid var(--border);
  padding: 24px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.04);
}

.camp-empty {
  text-align: center;
  color: var(--muted);
  font-size: 0.9rem;
  padding: 40px 0;
}

/* ── Detalle de campaña: rejilla de metadatos ── */
.camp-detail-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px 16px;
  margin-bottom: 1rem;
}
.camp-detail-toolbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px;
}
.camp-detail-counts { display: flex; gap: 6px; flex-wrap: wrap; }
.camp-detail-export { margin-left: auto; display: flex; gap: 6px; }
.camp-cell-err {
  max-width: 240px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--muted); font-size: 0.8rem;
}
.camp-detail-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: 8px;
}
.camp-detail-k {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
}
.camp-detail-v {
  font-size: 0.9rem;
  color: var(--text-strong);
  font-weight: 600;
  word-break: break-word;
}

/* ── Badges ── */
.badge {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 20px;
  font-size: 0.76rem;
  font-weight: 600;
  white-space: nowrap;
}

.badge-green  { background: rgba(16,185,129,0.15); color: #34d399; }
.badge-yellow { background: rgba(245,158,11,0.16);  color: #fbbf24; }
.badge-red    { background: rgba(239,68,68,0.15);   color: #f87171; }
.badge-blue   { background: rgba(59,130,246,0.16);  color: #60a5fa; }
.badge-gray   { background: rgba(148,163,184,0.18); color: var(--muted); }
.badge-type   { background: var(--badge-purple-bg);  color: var(--badge-purple-fg); }

/* ── Table ── */
.camp-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.camp-table th {
  text-align: left;
  padding: 10px 12px;
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
}

.camp-table td {
  padding: 12px 12px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  vertical-align: middle;
}

.camp-table tr:last-child td { border-bottom: none; }
.camp-table tr:hover td      { background: rgba(255,255,255,0.04); }

.camp-cell-title { font-weight: 600; color: var(--text); margin-bottom: 2px; }
.camp-cell-sub   { font-size: 0.78rem; color: var(--muted); }

.camp-date { font-size: 0.8rem; color: var(--muted); white-space: nowrap; }

/* ── Progress bar ── */
.camp-progress {
  height: 6px;
  background: var(--control-bg-hover);
  border-radius: 3px;
  overflow: hidden;
  margin-bottom: 4px;
  min-width: 80px;
}

.camp-progress-bar {
  height: 100%;
  background: linear-gradient(90deg, var(--brand), var(--brand-dark));
  border-radius: 3px;
  transition: width 0.4s ease;
  min-width: 2px;
}

.camp-progress-label {
  font-size: 0.78rem;
  color: var(--muted);
  white-space: nowrap;
}

.camp-failed-count { color: #dc2626; }

.camp-spinner {
  display: inline-block;
  animation: campPulse 1s ease-in-out infinite;
  color: var(--brand);
}

@keyframes campPulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.3; }
}

/* ── Action buttons ── */
.camp-actions { white-space: nowrap; }

.camp-btn {
  padding: 5px 12px;
  border-radius: 6px;
  border: 1px solid transparent;
  font-size: 0.82rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s;
  margin-right: 4px;
}

.camp-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.camp-btn--primary { background: var(--brand); color: #fff; border-color: var(--brand); }
.camp-btn--primary:hover:not(:disabled) { background: var(--brand-dark); }

.camp-btn--warn    { background: #f59e0b; color: #fff; border-color: #f59e0b; }
.camp-btn--warn:hover:not(:disabled)    { background: #d97706; }

.camp-btn--danger  { background: var(--control-bg); color: var(--badge-danger-fg); border-color: rgba(239,68,68,0.45); }
.camp-btn--danger:hover:not(:disabled)  { background: var(--badge-danger-bg); }

.camp-btn--secondary { background: var(--control-bg); color: var(--text); border-color: var(--control-border); }
.camp-btn--secondary:hover:not(:disabled) { background: var(--control-bg-hover); border-color: var(--muted); color: var(--text-strong); }

.camp-btn--sm      { padding: 4px 10px; font-size: 0.78rem; }

.camp-btn--lg      { padding: 10px 24px; font-size: 0.95rem; }

/* ── Form ── */
.camp-form {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.camp-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin-bottom: 20px;
}

.camp-field        { display: flex; flex-direction: column; gap: 6px; }
.camp-field--full  { grid-column: 1 / -1; }

.camp-label {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
}

.camp-hint-inline {
  font-weight: 400;
  color: var(--muted);
  font-size: 0.78rem;
  margin-left: 6px;
}

.camp-hint {
  font-size: 0.78rem;
  color: var(--muted);
  margin: 2px 0 0;
}

.camp-input,
.camp-select,
.camp-textarea {
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.88rem;
  color: var(--text);
  background: var(--surface);
  transition: border-color 0.15s;
  font-family: inherit;
  width: 100%;
  box-sizing: border-box;
}

.camp-input:focus,
.camp-select:focus,
.camp-textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb),0.15);
}

.camp-input:disabled,
.camp-select:disabled,
.camp-textarea:disabled { background: var(--surface-2); color: var(--muted); }

.camp-textarea     { resize: vertical; min-height: 80px; }
.camp-textarea--csv { font-family: 'Courier New', monospace; font-size: 0.8rem; }

.camp-csv-count {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--brand);
  margin-top: 4px;
}

.camp-form-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.camp-checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.88rem;
  color: var(--text);
  cursor: pointer;
  user-select: none;
}

/* ── Templates tab ── */
.camp-templates { }

.camp-templates-toolbar {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-bottom: 20px;
}

.camp-templates-toolbar .camp-select { max-width: 280px; }

.camp-template-name { font-weight: 500; font-family: monospace; font-size: 0.85rem; }
.camp-vars-count    { text-align: center; font-weight: 600; color: #7c3aed; }
.camp-sync-time     { font-size: 0.78rem; color: var(--muted); white-space: nowrap; }

/* ── Config tab ── */
.camp-config { }

.camp-section-title {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

.camp-channels-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 28px;
}

.camp-channel-card {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2);
}

.camp-channel-info {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  flex: 1;
}

.camp-channel-name   { font-weight: 700; color: var(--text); }
.camp-channel-nameid { font-family: monospace; font-size: 0.8rem; color: var(--muted); }
.camp-channel-checked { font-size: 0.78rem; color: var(--muted); width: 100%; }
.camp-channel-err    { color: #dc2626; }

.camp-channel-creds {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-shrink: 0;
}

.camp-creds-masked {
  font-size: 0.78rem;
  color: var(--muted);
  font-family: monospace;
}

.camp-channel-actions { flex-shrink: 0; }

.camp-channel-form { margin-top: 8px; }


.camp-edit-form {
  flex-basis: 100%;
  width: 100%;
  margin-top: 12px;
  padding: 14px;
  background: var(--surface);
  border: 1px dashed var(--primary);
  border-radius: 8px;
}

.camp-edit-title {
  font-size: 0.82rem;
  color: var(--muted);
  margin: 0 0 10px;
}

.camp-edit-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

/* ── Utility: toggle switch ── */
.switch-field {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  max-width: 100%;
  cursor: pointer;
  color: var(--text);
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.35;
}

.form-field label.switch-field {
  display: inline-flex;
  margin-bottom: 6px;
}

.switch-field__text {
  display: inline-block;
}

/* Variante compacta: filas de toggles pequeños (multimedia, obligatorio en
   gestión de tickets). Menos peso/tamaño para que no dominen el modal. */
.switch-field--sm {
  gap: 8px;
  font-size: 0.82rem;
  font-weight: 500;
}

.toggle-switch {
  --switch-width: 46px;
  --switch-height: 24px;
  --switch-knob-size: 18px;
  --switch-knob-offset: 3px;
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  width: var(--switch-width);
  height: var(--switch-height);
  cursor: pointer;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--muted-2) 40%, transparent);
  border: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
  border-radius: 999px;
  transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}
.toggle-slider::before {
  content: "";
  position: absolute;
  width: var(--switch-knob-size);
  height: var(--switch-knob-size);
  border-radius: 50%;
  background: #fff;
  top: var(--switch-knob-offset);
  left: var(--switch-knob-offset);
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.22);
  transition:
    transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
    background 0.3s ease;
}
.toggle-switch input:focus-visible + .toggle-slider {
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb), 0.18);
}
.toggle-switch input:checked + .toggle-slider {
  background: color-mix(in srgb, var(--brand) 72%, #ffffff 8%);
  border-color: color-mix(in srgb, var(--brand) 82%, #ffffff 6%);
}
.toggle-switch input:checked + .toggle-slider::before {
  transform: translateX(22px);
}
.toggle-switch input:disabled + .toggle-slider {
  opacity: 0.62;
  cursor: not-allowed;
}
.toggle-switch--sm {
  --switch-width: 32px;
  --switch-height: 18px;
  --switch-knob-size: 12px;
  --switch-knob-offset: 3px;
}
.toggle-switch--sm input:checked + .toggle-slider::before { transform: translateX(14px); }
.toggle-switch--lg {
  --switch-width: 60px;
  --switch-height: 32px;
  --switch-knob-size: 24px;
  --switch-knob-offset: 4px;
}
.toggle-switch--lg input:checked + .toggle-slider::before { transform: translateX(28px); }

/* ── Utility: small buttons ── */
.btn-xs {
  font-size: 0.75rem; padding: 3px 10px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--panel-2);
  color: var(--text); cursor: pointer; white-space: nowrap;
}
.btn-xs:hover { background: var(--border); }
.btn-link-xs {
  font-size: 0.75rem; padding: 0 4px; border: none; background: none;
  color: var(--brand); cursor: pointer; text-decoration: underline;
}
.btn-link-xs:hover { color: var(--brand-dark); }

/* ── Utility: status chips ── */
.status-chip {
  font-size: 0.7rem; padding: 2px 8px; border-radius: 999px;
  font-weight: 600; white-space: nowrap;
}
.chip-active { background: rgba(16,185,129,0.15); color: #34d399; }
.chip-off    { background: rgba(239,68,68,0.15);  color: #f87171; }

/* ── Herramientas panel ── */
.tools-panel { padding: 24px; display: flex; flex-direction: column; gap: 12px; }

/* Header con botón agregar */
.tools-list-header { display: flex; justify-content: flex-end; padding-bottom: 4px; }

/* Botón peligro pequeño en tarjeta */
.btn-danger-xs {
  padding: 3px 10px; font-size: 0.75rem; border-radius: 6px; border: none; cursor: pointer;
  background: var(--badge-danger-bg); color: var(--badge-danger-fg); font-weight: 600; transition: background 0.15s;
}
.btn-danger-xs:hover { background: rgba(239,68,68,0.3); }

/* Formulario crear MCP */
/* padding horizontal = el del resto de modales estándar (no toca los bordes). */
.tools-create-form { display: flex; flex-direction: column; gap: 14px; padding: 4px 24px 8px; }
.tools-create-field { display: flex; flex-direction: column; gap: 4px; }
.tools-create-field label { font-size: 0.78rem; font-weight: 600; color: var(--muted); }
.tools-create-field .form-hint { font-size: 0.72rem; color: var(--muted-2); }
.tools-create-row { display: flex; gap: 16px; }
.tools-create-row .tools-create-field { flex: 1; }
/* Toggle + etiqueta en una fila (autenticación / habilitado del modal MCP). */
.tools-inline-toggle {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  font-size: 0.82rem; font-weight: 600; color: var(--text-strong);
}
.tools-inline-toggle > span { display: inline-flex; align-items: center; gap: 6px; }

/* ── Métodos de pago: lista compacta de campos + editor por chips ─────────── */
.pm-field-list { display: flex; flex-direction: column; gap: 8px; }
.pm-field-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface-2); cursor: pointer;
}
.pm-field-row:hover { border-color: var(--brand); }
.pm-field-name { font-weight: 600; color: var(--text-strong); flex: 1 1 auto; }
.pm-field-del { margin-left: auto; }
/* Chips de opciones (tag input: escribir + Enter) */
.tag-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.tag-chips:empty { display: none; }
.tag-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 6px 3px 10px; border-radius: 999px;
  background: var(--brand-soft-bg); color: var(--brand-soft-fg);
  font-size: 0.8rem; font-weight: 600;
}
.tag-chip-x {
  border: 0; background: transparent; cursor: pointer; color: inherit;
  font-size: 1rem; line-height: 1; padding: 0 2px;
}
.tag-chip-x:hover { color: var(--badge-danger-fg); }
/* Checkboxes con estilo de la app (checkbox + texto sin chocar) */
.pm-check-grid { display: flex; flex-wrap: wrap; gap: 10px 20px; }
.pm-toggle-group { display: flex; flex-direction: column; gap: 10px; }
.pm-check {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  font-size: 0.86rem; color: var(--text);
}
/* Checkbox custom con estilo de la app (caja + check de marca), no el nativo. */
.pm-check input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  width: 18px; height: 18px; margin: 0; flex-shrink: 0;
  border: 1.5px solid var(--control-border); border-radius: 5px;
  background: var(--control-bg); cursor: pointer;
  display: inline-grid; place-content: center;
  transition: background 0.15s, border-color 0.15s;
}
.pm-check input[type="checkbox"]::before {
  content: ""; width: 11px; height: 11px; transform: scale(0);
  transition: transform 0.12s ease; background: #fff;
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
.pm-check input[type="checkbox"]:checked { background: var(--brand); border-color: var(--brand); }
.pm-check input[type="checkbox"]:checked::before { transform: scale(1); }
.pm-check input[type="checkbox"]:hover:not(:checked) { border-color: var(--brand); }
.pm-check > span { display: inline-flex; align-items: center; gap: 6px; }

/* ── Correo & Alertas: editor + vista previa lado a lado, divisor arrastrable ── */
/* El modal debe ser ancho: el global `.modal-card{max-width:480px}` ganaba al width
   inline (max-width pisa a width) → se quedaba angosto y la preview no cabía. */
.modal-card.mail-modal {
  width: min(1180px, 96vw); max-width: min(1180px, 96vw); max-height: 94vh; overflow-y: auto;
}
.mail-split { display: flex; align-items: stretch; gap: 0; }
/* Editor: ancho controlado por --mail-ew (el divisor JS lo ajusta). SIN overflow
   propio → el dropdown de autocompletado no se recorta. */
.mail-editor-pane { flex: 0 0 var(--mail-ew, 480px); min-width: 300px; }
/* Divisor arrastrable (col-resize); el JS de index.html ajusta --mail-ew. */
.mail-divider {
  flex: 0 0 6px; align-self: stretch; margin: 0 10px;
  background: var(--border); border-radius: 3px; cursor: col-resize;
  transition: background 0.15s;
}
.mail-divider:hover { background: var(--brand); }
.mail-preview-pane { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; }
.mail-preview-head {
  font-size: 0.8125rem; font-weight: 600; color: var(--text-strong);
  margin-bottom: 10px; padding-bottom: 8px; border-bottom: 1px solid var(--border);
}
.mail-preview-subject {
  background: var(--surface-2); padding: 8px 12px; border-radius: 6px;
  margin-bottom: 10px; font-size: 0.8125rem; word-break: break-word; border: 1px solid var(--border);
}
.mail-preview-subject strong { color: var(--muted); font-size: 0.72rem; text-transform: uppercase; letter-spacing: .04em; }
.mail-preview-frame { width: 100%; flex: 1 1 auto; min-height: 460px; border: 1px solid var(--border); border-radius: 8px; background: #fff; }
.mail-preview-note { margin: 6px 0 0; font-size: 0.72rem; color: var(--muted); text-align: center; }
.mail-preview-note code { background: var(--surface-2); padding: 1px 3px; border-radius: 3px; }
/* Autocompletado de {{variables}} */
.mail-ac-field { position: relative; }
.mail-ac-menu {
  position: absolute; z-index: 60; left: 0; right: 0; top: 100%;
  margin-top: 2px; max-height: 240px; overflow-y: auto;
  background: var(--surface-overlay); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: var(--shadow-lg); padding: 4px;
}
.mail-ac-item {
  display: flex; flex-direction: column; gap: 1px; width: 100%; text-align: left;
  background: transparent; border: 0; border-radius: 7px; padding: 6px 10px; cursor: pointer;
}
.mail-ac-item:hover { background: var(--control-bg-hover); }
.mail-ac-name { font-family: ui-monospace, Consolas, monospace; font-size: 0.8rem; color: var(--brand); font-weight: 600; }
.mail-ac-desc { font-size: 0.72rem; color: var(--muted); }
@media (max-width: 860px) {
  .mail-split { flex-direction: column; }
  .mail-editor-pane { flex: 1 1 auto; min-width: 0; padding-bottom: 14px; }
  .mail-divider { display: none; }
  .mail-preview-pane { padding-top: 14px; border-top: 1px solid var(--border); }
}

/* ── Límites: cada opción = título + tooltip arriba, control debajo ───────── */
.limit-opt { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.limit-opt-title {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 600; color: var(--text-strong); font-size: 0.95rem;
}

.tools-feedback {
  padding: 8px 14px; background: var(--badge-success-bg); color: var(--badge-success-fg);
  border-radius: 8px; font-size: 0.85rem; font-weight: 500;
}

.tools-list { display: flex; flex-direction: column; gap: 12px; }

.tools-card {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 12px; overflow: hidden;
  transition: opacity 0.2s;
}
.tools-card--off { opacity: 0.6; }

.tools-card-head {
  display: flex; align-items: center; justify-content: flex-start;
  padding: 14px 18px; gap: 12px; flex-wrap: wrap;
}
.tools-card-head.is-clickable { cursor: pointer; }
.tools-card-head.is-clickable:hover { background: var(--surface-2); }
/* Chevron a la derecha (sin caja); gira 180° al expandir. */
.tools-card-chevron {
  display: inline-flex; align-items: center; color: var(--muted);
  transition: transform 0.18s ease; flex-shrink: 0;
}
.tools-card-chevron.is-open { transform: rotate(180deg); }
.tools-card-title { flex: 1 1 auto; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tools-mcp-id    { font-weight: 700; font-size: 0.95rem; }
.tools-mcp-type  { font-size: 0.78rem; color: var(--muted); }

.health-badge {
  font-size: 0.7rem; font-weight: 600; padding: 2px 10px;
  border-radius: 999px; white-space: nowrap;
}
.health-badge--ok       { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.health-badge--error    { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.health-badge--timeout  { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.health-badge--internal { background: var(--badge-purple-bg); color: var(--badge-purple-fg); }
.health-badge--unknown  { background: var(--panel-2); color: var(--muted); }
.health-ms { font-weight: 400; font-size: 0.68rem; }

.tools-card-btns { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.tools-card-meta {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 8px 18px 14px; border-top: 1px solid var(--border);
  font-size: 0.82rem; color: var(--muted);
}
.tools-meta-ch    { display: flex; align-items: center; gap: 4px; }
.tools-meta-check { font-size: 0.78rem; }

.tools-tools-grid {
  display: flex; flex-direction: column; gap: 0;
  border-top: 1px solid var(--border);
}
.tools-tool-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 18px; border-bottom: 1px solid var(--border);
  font-size: 0.85rem; flex-wrap: wrap;
}
.tools-tool-row:last-child { border-bottom: none; }
.tools-tool-row--off { opacity: 0.55; }
.tools-tool-name { font-weight: 600; min-width: 140px; }
.tools-tool-ch   { color: var(--muted); flex: 1; font-size: 0.78rem; }
.tools-tool-acts { display: flex; align-items: center; gap: 8px; margin-left: auto; }

.tools-key-note {
  margin: 12px 0 8px; font-size: 0.82rem; color: var(--muted);
}

.tools-channels-body { padding: 8px 24px 12px; display: flex; flex-direction: column; gap: 8px; }
.tools-channels-opts { display: flex; flex-direction: column; gap: 6px; padding-left: 8px; }
.tools-channel-opt {
  display: flex; align-items: center; gap: 8px;
  font-size: 0.85rem; cursor: pointer;
}
.tools-channel-opt input { cursor: pointer; }

/* ── Responsive ── */
@media (max-width: 768px) {
  .campaigns-page { padding: 20px 16px; }
  .camp-form-grid { grid-template-columns: 1fr; }
  .camp-field--full { grid-column: 1; }
  .camp-table th, .camp-table td { padding: 8px 8px; }
  .camp-channel-card { flex-direction: column; }
  .camp-channel-creds { flex-wrap: wrap; }
  /* `.camp-tabs` ya no envuelve: scrolla (ver «RESPONSIVE DE PÁGINAS» al final). */
  .camp-template-actions .camp-btn { flex: 1 1 180px; }
}

/* ═══════════════════════════════════════════════════════════════
   Contacts Page — redesign
   ═══════════════════════════════════════════════════════════════ */

.contacts-page {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  background: var(--bg);
}

/* ── Header card ── */
.contacts-hdr {
  background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand-dark) 65%, var(--brand-dark) 100%);
  padding: 24px 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.contacts-hdr-left {
  display: flex;
  align-items: center;
  gap: 16px;
}

.contacts-hdr-icon {
  width: 48px;
  height: 48px;
  background: rgba(255,255,255,0.15);
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  flex-shrink: 0;
}

.contacts-hdr-title {
  color: white;
  font-size: 1.25rem;
  font-weight: 700;
  margin: 0;
  line-height: 1.3;
}

.contacts-hdr-sub {
  color: rgba(255,255,255,0.65);
  font-size: 0.78rem;
  margin: 3px 0 0;
}

.btn-back-contacts {
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.22);
  border-radius: 8px;
  color: white;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 7px 14px;
  transition: background 0.15s;
  white-space: nowrap;
  flex-shrink: 0;
}
.btn-back-contacts:hover { background: rgba(255,255,255,0.22); }

/* La toolbar estándar dentro de Contactos conserva el respiro y el sangrado que
   tenía la vieja .contacts-toolbar (si no, queda pegada al header y desalineada
   respecto a la tabla). */
.page-container > .tickets-toolbar { margin: 16px 32px 0; }

.contacts-toolbar {
  position: relative;               /* ancla del popover de filtros */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  /* Padding horizontal = el de .contacts-content (32px) para que la toolbar
     quede alineada con la tabla de abajo. Sin padding inferior: la separación
     con la tabla la pone .contacts-content y así queda en los 16px del resto
     de módulos (antes se sumaban los dos y daba 40px). */
  padding: 16px 32px 0;
}

/* ── TOOLBAR ESTÁNDAR ─────────────────────────────────────────────────────────
   Un solo dialecto para todos los módulos: barra de búsqueda redondeada con la
   lupa decorativa dentro, y a su derecha los iconos de acción SIN contenedor
   (filtros, limpiar filtros, refrescar). El hover levanta el icono, no pinta
   una caja detrás. Añadir un módulo = reusar estas clases, no crear otras. */
.contacts-search-form {
  display: flex;
  align-items: center;
  gap: 2px;
  width: 100%;
  max-width: 520px;
}

/* Botón de filtros (embudo) — ancla del contador de filtros activos */
.contacts-filter-btn { position: relative; }
/* Estado ENCENDIDO de cualquier icono-interruptor de la toolbar (embudo con
   filtros activos, modo selección). Estaba solo bajo `.ec-toolbar`, así que el
   botón de modo selección de la toolbar estándar no se iluminaba en ningún
   módulo salvo Ecommerce. Va sobre `.contacts-refresh-btn`, que es la clase
   base de todos los iconos de la barra. */
.contacts-refresh-btn.is-active,
.contacts-filter-btn.is-active { color: var(--brand); }
.contacts-refresh-btn.is-active svg,
.contacts-filter-btn.is-active svg { transform: translateY(-2px); }
/* Tinte de fondo: un interruptor encendido tiene que leerse de un vistazo, no
   solo por el color del trazo. El botón es 34×34 sin borde, así que basta con
   redondearlo. */
.contacts-refresh-btn.is-active {
  background: var(--brand-soft-bg);
  border-radius: 8px;
}
/* Refresco en curso: el icono gira. Es el único acuse de recibo posible cuando
   la recarga devuelve exactamente los mismos datos — sin él, el botón parece
   roto aunque la petición haya salido y vuelto. */
.contacts-refresh-btn.is-spinning svg {
  animation: tb-refresh-spin 0.7s linear infinite;
  transform-origin: 50% 50%;
}

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

@media (prefers-reduced-motion: reduce) {
  .contacts-refresh-btn.is-spinning svg { animation: none; opacity: 0.5; }
}

.contacts-filter-count {
  position: absolute; top: 0; right: 0;
  min-width: 15px; height: 15px; padding: 0 3px;
  background: var(--brand); color: #fff;
  border-radius: 8px; font-size: 0.6rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center; line-height: 1;
}

/* Envoltorio del botón de limpiar: recibe el hover (y con él el tooltip y el
   cursor) cuando el botón está deshabilitado y no genera eventos propios. */
.tb-icon-wrap { display: inline-flex; }
.tb-icon-wrap.is-disabled { cursor: not-allowed; }

/* Popover de filtros (canal + estado) */
.contacts-filters-popover {
  position: absolute; top: 62px; left: 32px; z-index: 41;
  width: min(420px, calc(100% - 64px));
  display: flex; flex-direction: column; gap: 14px;
  padding: 14px;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-md);
}
.contacts-fpop-section { display: flex; flex-direction: column; gap: 8px; }
.contacts-fpop-title {
  font-size: 0.68rem; font-weight: 800; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted-2);
}
/* Chips dentro del popover: sin banda propia, permiten envolver */
.contacts-filters-popover .contacts-channel-bar {
  background: transparent; border: none; padding: 0;
  flex-wrap: wrap; overflow: visible;
}
.contacts-filters-popover .contacts-sc-wrap { flex-wrap: wrap; }

/* Barra de búsqueda: pastilla redondeada. La lupa es decorativa y va DENTRO,
   pegada a la derecha, como background-image — así no hay que tocar el marcado
   de cada módulo. Es gris neutro a propósito: se lee igual en ambos temas. */
.contacts-search-input {
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  border-radius: 999px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.875rem;
  outline: none;
  flex: 1;
  min-width: 0;
  padding: 10px 40px 10px 18px;
  transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.5-3.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
}
.contacts-search-input::placeholder { color: var(--muted-2); }
.contacts-search-input:focus {
  background-color: var(--control-bg-hover);
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb),0.12);
}

/* Iconos de acción de la toolbar: sin caja. Solo el icono, y el hover lo
   levanta y lo tiñe de marca. Sirve para filtros, limpiar y refrescar. */
.contacts-refresh-btn {
  align-items: center;
  background: none;
  border: none;
  padding: 0;
  color: var(--muted);
  cursor: pointer;
  display: flex;
  height: 34px;
  width: 34px;
  justify-content: center;
  flex-shrink: 0;
}
.contacts-refresh-btn svg { transition: transform 0.15s ease, color 0.15s ease; }
.contacts-refresh-btn:hover:not(:disabled) { color: var(--brand); }
.contacts-refresh-btn:hover:not(:disabled) svg { transform: translateY(-2px); }
.contacts-refresh-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 8px; }
/* Deshabilitado: no captura el ratón, así el envoltorio muestra el tooltip. */
.contacts-refresh-btn:disabled { opacity: 0.35; cursor: not-allowed; pointer-events: none; }

/* ── Channel chips bar ── */
.contacts-channel-bar {
  background: var(--surface-3);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 8px;
  overflow-x: auto;
  padding: 12px 24px;
  scrollbar-width: none;
}
.contacts-channel-bar::-webkit-scrollbar { display: none; }

.ch-chip {
  align-items: center;
  background: rgba(255, 255, 255, 0.06);
  border: 1.5px solid var(--border);
  border-radius: 999px;
  color: var(--text);
  cursor: pointer;
  display: inline-flex;
  flex-shrink: 0;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  gap: 6px;
  padding: 6px 14px;
  transition: border-color 0.15s, background 0.15s, color 0.15s;
  white-space: nowrap;
}
.ch-chip:hover { background: rgba(255, 255, 255, 0.1); border-color: var(--muted); }
.ch-chip--on {
  background: var(--brand-soft-bg);
  border-color: var(--brand);
  color: var(--brand-dark);
  font-weight: 600;
}
.ch-chip-icon {
  flex-shrink: 0;
  height: 16px;
  object-fit: contain;
  width: 16px;
}
.ch-chip-count {
  background: rgba(0,0,0,0.07);
  border-radius: 999px;
  color: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  padding: 1px 7px;
}
.ch-chip--on .ch-chip-count { background: rgba(var(--brand-rgb),0.15); }

/* ── Status sub-bar ── */
.contacts-subbar {
  background: var(--surface-3);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 24px;
}

.contacts-sc-wrap {
  display: flex;
  gap: 6px;
}

.sc-chip {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 0.8rem;
  font-weight: 500;
  padding: 5px 14px;
  transition: all 0.15s;
}
.sc-chip:hover { border-color: var(--muted-2); color: var(--text); }
.sc-chip--on {
  background: var(--brand-dark);
  border-color: var(--brand-dark);
  color: white;
  font-weight: 600;
}

.contacts-count-label {
  color: var(--muted);
  font-size: 0.8125rem;
  font-weight: 500;
}

/* ── Content wrapper ── */
.contacts-content {
  padding: 16px 32px 24px;
  flex: 1;
}

/* ── Table — contact cell ── */
.contact-cell {
  align-items: center;
  display: flex;
  gap: 12px;
}

.contact-avatar {
  align-items: center;
  border-radius: 50%;
  color: white;
  display: flex;
  flex-shrink: 0;
  font-size: 0.875rem;
  font-weight: 700;
  height: 38px;
  justify-content: center;
  letter-spacing: 0.02em;
  width: 38px;
}

.contact-avatar-pic {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

/* Avatar color variants */
.av-indigo  { background: #4f46e5; }
.av-emerald { background: #059669; }
.av-violet  { background: #7c3aed; }
.av-amber   { background: #d97706; }
.av-rose    { background: #e11d48; }
.av-sky     { background: #0284c7; }

.contact-cell-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.contact-cell-name {
  color: var(--text);
  font-size: 0.875rem;
  font-weight: 600;
}

.contact-cell-phone {
  color: var(--muted);
  font-size: 0.75rem;
}

/* ── Vista TARJETAS de contactos ──────────────────────────────────────────────
   Secundaria: la tabla sigue siendo la vista de trabajo. La tarjeta existe para
   RECONOCER a alguien (cara, nombre, canal), así que lleva menos datos a
   propósito. `auto-fit` —no `auto-fill`— para que las tarjetas estiren y llenen
   la fila en vez de dejar columnas fantasma (misma razón que en Métricas). */
.contact-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 12px;
}
/* Las dos vistas se montan a la vez y se alterna cuál se muestra: cambiar de
   vista no vuelve a pedir datos ni pierde la selección en curso. */
.contact-cards.is-hidden,
.table-wrapper.is-hidden,
.users-table-wrap.is-hidden { display: none; }

.contact-card {
  display: flex; flex-direction: column; gap: 12px;
  padding: 14px;
  border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.contact-card:hover { border-color: var(--brand); box-shadow: var(--shadow-sm); }
/* El bloqueado se marca con una barra lateral, no tiñendo la tarjeta entera:
   el tinte compite con el de selección y se vuelven indistinguibles. */
.contact-card.row-blocked { box-shadow: inset 3px 0 0 var(--badge-danger-fg); }

.contact-card-head { display: flex; align-items: center; gap: 12px; min-width: 0; }
.contact-avatar--lg { width: 46px; height: 46px; font-size: 1rem; }
.contact-card-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.contact-card-name {
  color: var(--text-strong); font-size: 0.9rem; font-weight: 700;
  /* Sin esto un nombre largo ensancha la celda del grid y descuadra la fila. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.contact-card-phone { color: var(--muted); font-size: 0.75rem; }

.contact-card-meta {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; flex-wrap: wrap;
}

/* Las acciones se separan abajo y quedan siempre a la misma altura, aunque una
   tarjeta tenga teléfono y la de al lado no. En modo selección se ocultan solas
   (`.row-selectmode .pg-actions`), igual que en la tabla. */
.contact-card .pg-actions {
  display: flex; gap: 6px; margin-top: auto;
  padding-top: 10px; border-top: 1px solid var(--border-soft);
}

/* Selección: en la tabla el fondo lo pinta la CELDA, aquí lo pinta la tarjeta.
   Misma lectura visual —tinte de marca + barra a la izquierda— para que el modo
   selección se reconozca igual en las dos vistas. */
.contact-card.row-selected {
  background: var(--brand-soft-bg);
  border-color: var(--brand);
  box-shadow: inset 3px 0 0 var(--brand);
}

/* ── Status badges (contacts) ── */
.contact-sbadge {
  border-radius: 999px;
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 3px 10px;
  white-space: nowrap;
}
.contact-sbadge--blocked  { background: rgba(239,68,68,0.1);  color: #dc2626; }
.contact-sbadge--verified { background: rgba(16,185,129,0.1); color: #059669; }
.contact-sbadge--pending  { background: var(--surface-2); color: var(--muted); }
.contact-sbadge--paused   { background: rgba(245,158,11,0.12); color: #d97706; }

.contact-estado-cell { display: flex; flex-direction: column; gap: 3px; }

/* ── Contracts / friendship / actions ── */
.contracts-count {
  color: var(--text);
  font-size: 0.84rem;
  font-weight: 500;
}

.friendship-val {
  align-items: center;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  display: inline-flex;
  font-size: 0.84rem;
  font-weight: 600;
  justify-content: center;
  min-width: 32px;
  padding: 3px 8px;
}

/* Acciones de fila de Contactos: ver .pg-actions / .pg-btn. */

/* ── Campañas: plantilla row + vista previa ────────────────────────────────── */

.camp-template-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.camp-template-row .camp-select { flex: 1; }

.camp-btn--outline {
  background: transparent;
  border: 1.5px solid var(--brand);
  color: var(--brand);
  border-radius: 7px;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  padding: 0 14px;
  white-space: nowrap;
  transition: background 0.15s, color 0.15s;
}
.camp-btn--outline:hover:not(:disabled) {
  background: var(--brand);
  color: #fff;
}
.camp-btn--outline:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.camp-preview-label {
  font-size: 0.8rem;
  color: var(--muted);
  margin-bottom: 10px;
}
.camp-preview-label code {
  background: var(--surface-2);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 0.78rem;
  color: #7c3aed;
}

.camp-preview-phone {
  display: inline-flex;
  flex-direction: column;
  gap: 0;
  max-width: 380px;
}

.camp-preview-bubble {
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--border);
  border-radius: 12px 12px 12px 2px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
  overflow: hidden;
  font-size: 0.88rem;
  color: var(--text);
}

.camp-preview-media {
  background: #d1fae5;
  color: #065f46;
  font-size: 0.8rem;
  font-weight: 600;
  padding: 10px 14px;
  border-bottom: 1px solid #a7f3d0;
}

.camp-preview-header {
  background: var(--badge-success-bg);
  color: #14532d;
  font-weight: 700;
  font-size: 0.92rem;
  padding: 10px 14px;
  border-bottom: 1px solid #bbf7d0;
}

.camp-preview-body {
  padding: 12px 14px;
  white-space: pre-wrap;
  line-height: 1.55;
  color: var(--text-strong);
}

.camp-preview-footer {
  padding: 6px 14px 10px;
  font-size: 0.77rem;
  color: var(--muted-2);
  border-top: 1px solid var(--border-soft);
}

.camp-preview-btns {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-top: 2px;
}

/* ── Header multimedia: estado + subida ── */
.camp-hdr-media {
  margin-top: 14px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.camp-hdr-media-head { font-size: 0.85rem; color: var(--text); }
.camp-hdr-media-ok   { font-size: 0.82rem; color: var(--badge-success-fg); font-weight: 600; }
.camp-hdr-media-warn { font-size: 0.82rem; color: var(--badge-warn-fg); font-weight: 600; }
.camp-hdr-media-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.camp-hdr-media-msg  { font-size: 0.82rem; color: var(--muted); }

/* Mapeo de variables de plantilla → campo del CSV */
.camp-varmap { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; background: var(--surface-2); }
.camp-varmap-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; }
.camp-varmap-token {
  font-family: monospace; font-size: 0.82rem; font-weight: 700;
  color: var(--brand); background: var(--brand-soft-bg);
  padding: 3px 8px; border-radius: 6px; min-width: 72px; text-align: center;
}
.camp-varmap-arrow { color: var(--muted); }
.camp-varmap-select {
  flex: 1 1 220px; min-width: 180px; min-height: 36px;
  padding: 8px 10px; border: 1px solid var(--control-border); border-radius: 8px;
  background: var(--control-bg); color: var(--text); font-size: 0.86rem; font-family: inherit;
}
.camp-varmap-select:focus { outline: none; border-color: var(--brand-dark); }

/* Picker de archivo del storage para el header media de la campaña */
.camp-hdr-storage { margin-top: 10px; border-top: 1px dashed var(--border); padding-top: 8px; }
.camp-hdr-storage-title { font-size: 0.8rem; color: var(--muted); margin-bottom: 6px; text-align: center; }
.camp-hdr-storage-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.camp-hdr-storage-input {
  flex: 1 1 140px; min-width: 120px; min-height: 34px; padding: 7px 10px;
  border: 1px solid var(--control-border); border-radius: 8px;
  background: var(--control-bg); color: var(--text); font-size: 0.84rem; font-family: inherit;
}
.camp-hdr-storage-list { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; max-height: 180px; overflow-y: auto; }
.camp-hdr-storage-item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-size: 0.84rem; padding: 4px 8px; border: 1px solid var(--border); border-radius: 6px;
}
.camp-hdr-storage-item--off { opacity: 0.55; }
.camp-hdr-storage-use { padding: 2px 10px; }
.camp-hdr-storage-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin: 6px 0; font-size: 0.82rem; }
.camp-crumb { background: none; border: none; color: var(--brand); cursor: pointer; padding: 0 2px; font: inherit; }
.camp-crumb:hover { text-decoration: underline; }
.camp-crumb-sep { color: var(--muted); }
.camp-hdr-storage-folder { cursor: pointer; }
.camp-hdr-storage-folder:hover { background: var(--control-bg-hover); }

.camp-preview-btn {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--brand-dark);
  cursor: default;
  font-size: 0.82rem;
  font-weight: 600;
  padding: 9px 14px;
  text-align: center;
}

.camp-preview-empty {
  color: var(--muted);
  font-size: 0.82rem;
  padding: 8px 0;
}

/* ── Campañas: carga de archivo CSV / Excel ────────────────────────────────── */

.camp-file-zone {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 2px dashed var(--control-border);
  border-radius: 8px;
  padding: 14px 18px;
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--muted);
  transition: border-color 0.2s, background 0.2s;
  background: var(--surface-2);
  user-select: none;
}
.camp-file-zone:hover {
  border-color: var(--brand);
  background: var(--brand-soft-bg);
  color: var(--brand);
}
.camp-file-zone--has-file {
  border-color: #10b981;
  background: var(--badge-success-bg);
  color: #065f46;
  font-weight: 600;
}
.camp-file-icon {
  font-size: 1.1rem;
}

.camp-template-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 8px;
}

.camp-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 10px 0;
  color: var(--muted-2);
  font-size: 0.75rem;
}
.camp-divider::before,
.camp-divider::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--border);
}

/* ── Canal de correo en Campañas ─────────────────────────────────────────────
   Solo tokens semánticos: el conmutador y las tarjetas heredan el tema claro y
   el oscuro sin una sola regla `html.light-mode` propia. */

/* Conmutador WhatsApp / Correo del formulario de nueva campaña. */
.camp-kind-switch {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
}
.camp-kind-opt {
  appearance: none;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 8px 20px;
  transition: background 0.15s, color 0.15s;
}
.camp-kind-opt:hover:not(.camp-kind-opt--active) {
  background: var(--control-bg-hover);
  color: var(--text);
}
/* `--brand-dark` es SIEMPRE la variante sobre la que se apoya texto blanco. */
.camp-kind-opt--active {
  background: var(--brand-dark);
  color: #fff;
}
.camp-kind-opt:disabled { opacity: 0.55; cursor: default; }

/* Cabecera de las pestañas de configuración de correo. */
.camp-config-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.camp-config-head .camp-hint { max-width: 62ch; }

/* Tarjeta contenedora de los formularios de canal y plantilla. */
.camp-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 18px;
  margin-bottom: 18px;
  box-shadow: var(--shadow-sm);
}

/* Pie de las tarjetas KPI del panel de resultados de correo. */
.metrics-kpi-sub {
  color: var(--muted);
  font-size: 0.75rem;
  line-height: 1.4;
}

/* ── Sub-pestañas de canal (Plantillas, Configuración, Resultados) ───────────
   Deliberadamente MÁS DISCRETAS que `.camp-tab`: subrayado en vez de píldora
   rellena. Si se vieran igual, la barra parecería dos filas de pestañas del mismo
   nivel y se perdería la jerarquía «qué gestiono» → «de qué canal». */
.camp-subtabs {
  display: flex;
  gap: 4px;
  margin: -6px 0 18px;
  border-bottom: 1px solid var(--border);
}
.camp-subtab {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 9px 16px;
  margin-bottom: -1px;      /* el borde activo se apoya en el del contenedor */
  transition: color 0.15s, border-color 0.15s;
}
.camp-subtab:hover:not(.camp-subtab--active) {
  color: var(--text);
}
.camp-subtab--active {
  color: var(--brand);
  border-bottom-color: var(--brand);
}

/* Cadena de reenganche: los saltos de una campaña y sus hijas, en fila.
   Scroll horizontal propio para que una cadena larga no ensanche la página. */
.camp-chain {
  display: flex;
  gap: 10px;
  align-items: stretch;
  overflow-x: auto;
  padding: 10px 0 4px;
}
.camp-chain-step {
  flex: 0 0 auto;
  min-width: 220px;
  padding: 12px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  position: relative;
}
/* La flecha que une un salto con el siguiente. */
.camp-chain-step + .camp-chain-step::before {
  content: "→";
  position: absolute;
  left: -16px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--muted-2);
}
.camp-chain-step--active {
  border-color: var(--brand);
  box-shadow: inset 3px 0 0 var(--brand);
}
.camp-chain-depth {
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-2);
  margin-bottom: 4px;
}

/* ── Contact edit button (livechat panel) ── */
.btn-edit-contact {
  background: var(--surface-2);
  border: 1px solid var(--control-border);
  border-radius: 7px;
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  margin-top: 6px;
  padding: 4px 12px;
  transition: background 0.15s;
}
.btn-edit-contact:hover { background: var(--control-bg-hover); }

/* ── Contact edit form (livechat panel) ── */
.contact-edit-form {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 10px 0;
  padding: 14px;
}
.edit-field-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.edit-field-row label {
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 600;
}
.edit-field-row input[type="text"],
.edit-field-row input[type="email"] {
  background: var(--surface);
  border: 1px solid var(--control-border);
  border-radius: 6px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.84rem;
  outline: none;
  padding: 6px 10px;
  transition: border-color 0.15s;
}
.edit-field-row input[type="text"]:focus,
.edit-field-row input[type="email"]:focus { border-color: var(--brand); }

.edit-field-row--check label {
  align-items: center;
  color: var(--text);
  display: flex;
  font-size: 0.84rem;
  font-weight: 500;
  gap: 6px;
}
.edit-actions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}
.btn-save-contact {
  background: var(--brand-dark);
  border: none;
  border-radius: 7px;
  color: #fff;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.84rem;
  font-weight: 600;
  padding: 6px 18px;
  transition: background 0.15s;
}
.btn-save-contact:hover:not(:disabled) { background: var(--brand); }
.btn-save-contact:disabled { opacity: 0.6; cursor: not-allowed; }

.btn-cancel-edit {
  background: var(--surface-2);
  border: 1px solid var(--control-border);
  border-radius: 7px;
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 0.84rem;
  font-weight: 600;
  padding: 6px 14px;
  transition: background 0.15s;
}
.btn-cancel-edit:hover { background: var(--control-bg-hover); }

/* ── Badge paused in livechat contact panel ── */
.badge--paused { background: rgba(245,158,11,0.15); color: #b45309; }

/* ============================================================
   Ticket picker modal (livechat — Asignarme)
   ============================================================ */
.ticket-picker-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ticket-picker-modal {
  background: var(--surface-overlay);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  width: 480px;
  max-width: 95vw;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.ticket-picker-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.ticket-picker-header h3 {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-strong);
  margin: 0;
}

.btn-close-picker {
  background: none;
  border: none;
  font-size: 1.4rem;
  color: var(--muted-2);
  cursor: pointer;
  line-height: 1;
  padding: 0 4px;
  transition: color 0.15s;
}
.btn-close-picker:hover { color: var(--text-strong); }

.ticket-picker-list {
  overflow-y: auto;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.ticket-picker-item {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.ticket-picker-item:hover {
  background: var(--brand-soft-bg);
  border-color: var(--brand);
}

.ticket-picker-top {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

.ticket-picker-ref {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--brand);
  background: rgba(var(--brand-rgb),0.08);
  padding: 2px 6px;
  border-radius: 4px;
  font-family: monospace;
}

.ticket-picker-type {
  font-size: 0.72rem;
  color: var(--muted);
  background: var(--surface-2);
  padding: 2px 6px;
  border-radius: 4px;
}

.ticket-picker-title {
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--text-strong);
  margin-bottom: 6px;
  line-height: 1.3;
}

.ticket-picker-meta {
  display: flex;
  align-items: center;
  gap: 8px;
}

.ticket-picker-status {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 10px;
  background: var(--surface-2);
  color: var(--muted);
}
.tps--abierto  { background: rgba(59,130,246,0.1);  color: #2563eb; }
.tps--tomado   { background: rgba(245,158,11,0.1);  color: #d97706; }
.tps--en_proceso { background: rgba(139,92,246,0.1); color: #7c3aed; }

.ticket-picker-taken {
  font-size: 0.75rem;
  color: var(--muted);
}

.ticket-picker-empty {
  padding: 24px;
  text-align: center;
  color: var(--muted-2);
  font-size: 0.875rem;
}

/* ============================================================
   Ticket detail inline panel (livechat — badge click)
   ============================================================ */
.badge--ticket-active {
  cursor: pointer;
  border: none;
  font-size: inherit;
  padding: 3px 10px;
  border-radius: 9999px;
  transition: background 0.15s;
}
.badge--ticket-active:hover { background: rgba(var(--brand-rgb),0.20); }
.badge--ticket-open { background: rgba(var(--brand-rgb),0.22); outline: 2px solid rgba(var(--brand-rgb),0.35); }

/* ── Modal cierre de ticket (livechat) ───────────────────────────────── */
.lc-close-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(3px);
  z-index: 800;
}
.lc-close-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(460px, 94vw);
  background: var(--surface-overlay);
  border-radius: 14px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2), 0 4px 16px rgba(0, 0, 0, 0.08);
  z-index: 801;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: lcModalIn 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes lcModalIn {
  from { opacity: 0; transform: translate(-50%, -52%) scale(0.97); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.lc-close-modal-header {
  padding: 18px 46px 14px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  position: relative;
}
.lc-close-modal-ticket-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin-bottom: 6px;
}
.lc-close-modal-ticket-ref {
  font-family: 'SFMono-Regular', Consolas, monospace;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--brand);
}
.lc-close-modal-ticket-domain {
  font-size: 0.71rem;
  color: var(--muted);
  background: var(--control-bg-hover);
  padding: 2px 7px;
  border-radius: 4px;
  text-transform: uppercase;
  letter-spacing: .04em;
}
.lc-close-modal-ticket-title {
  font-size: 0.94rem;
  font-weight: 600;
  color: var(--text-strong);
  line-height: 1.35;
}
.lc-close-modal-x {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 30px;
  height: 30px;
  border: none;
  background: var(--control-bg-hover);
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.9rem;
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s, color 0.12s;
  line-height: 1;
}
.lc-close-modal-x:hover { background: var(--muted-2); color: var(--text-strong); }

.lc-close-modal-body {
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.lc-close-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.lc-close-label {
  font-size: 0.71rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .05em;
}
.lc-close-select {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--control-border);
  border-radius: 7px;
  font-family: inherit;
  font-size: 0.88rem;
  color: var(--text);
  background: var(--surface);
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
  cursor: pointer;
}
.lc-close-select:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb),0.12);
}
.lc-close-textarea {
  width: 100%;
  min-height: 82px;
  padding: 9px 10px;
  border: 1px solid var(--control-border);
  border-radius: 7px;
  font-family: inherit;
  font-size: 0.88rem;
  resize: vertical;
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
  box-sizing: border-box;
  color: var(--text);
}
.lc-close-textarea:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb),0.12);
}
.lc-close-modal-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}

.btn-action--close-ticket {
  background: #dc2626;
  color: #fff;
  border: none;
  border-radius: 7px;
  padding: 8px 18px;
  font-size: 0.84rem;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.15s;
}
.btn-action--close-ticket:hover:not(:disabled) { background: #b91c1c; }
.btn-action--close-ticket:disabled { opacity: 0.55; cursor: not-allowed; }

/* ============================================================
   Ticket detail modal
   ============================================================ */
.modal-card.modal-xl { max-width: 860px; }

.ticket-detail-modal .modal-header { align-items: flex-start; gap: 12px; }

.ticket-detail-title-block { flex: 1; min-width: 0; }

.ticket-detail-badges {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}

.ticket-ref-badge {
  font-family: 'Roboto Mono', ui-monospace, monospace;
  font-size: 0.78rem;
  font-weight: 600;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--muted);
  padding: 2px 8px;
  border-radius: 6px;
  letter-spacing: 0.04em;
}

.ticket-detail-title {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
  margin: 0;
  word-break: break-word;
}

.ticket-detail-body {
  padding: 0 24px;
  overflow-y: auto;
  max-height: 62vh;
  display: flex;
  flex-direction: column;
}

.ticket-detail-section {
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
}

.ticket-detail-section:last-child { border-bottom: none; }

.ticket-detail-section-title {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  margin-bottom: 10px;
}

.ticket-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 10px 16px;
}

.ticket-detail-field { display: flex; flex-direction: column; gap: 3px; }

.tdl {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--muted-2);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.ticket-detail-text {
  font-size: 0.875rem;
  color: var(--text);
  line-height: 1.65;
  white-space: pre-wrap;
  background: var(--panel-2);
  border-radius: 8px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  margin: 0;
}

/* Detalle simple clave→valor (modales de detalle: tickets, tareas, canales,
   personalidad, encuestas…). Genérico para no repetir el layout por módulo. */
.detail-row {
  display: grid;
  grid-template-columns: 170px 1fr;
  gap: 14px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
  align-items: start;
}
.detail-row:last-child { border-bottom: none; }
.detail-label {
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.detail-value { font-size: 0.88rem; color: var(--text); line-height: 1.5; word-break: break-word; }
.detail-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.detail-chip {
  display: inline-flex;
  align-items: center;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 0.78rem;
  color: var(--text);
}

/* Evidence */
.ticket-evidence-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}

.evidence-item {
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--panel-2);
  display: block;
  text-decoration: none;
  transition: box-shadow 0.15s;
}

.evidence-item:hover { box-shadow: var(--shadow-md); }

.evidence-img {
  width: 100%;
  height: 150px;
  object-fit: cover;
  display: block;
  transition: opacity 0.15s;
}

.evidence-img:hover { opacity: 0.88; }

.evidence-video {
  width: 100%;
  height: 150px;
  display: block;
  background: #000;
}

.evidence-item--audio {
  grid-column: 1 / -1;
  padding: 12px;
}

.evidence-item--audio audio { width: 100%; }

.evidence-item--file {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 80px;
  color: var(--brand);
  font-size: 0.85rem;
  font-weight: 600;
}

.evidence-item--file:hover { background: var(--brand-soft-bg); }

.evidence-item--invalid {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 80px;
  padding: 10px 12px;
  border: 1px dashed #fca5a5;
  background: var(--badge-danger-bg);
  color: #b91c1c;
  font-size: 0.8rem;
  font-weight: 600;
  text-align: center;
}
.evidence-invalid-icon { font-size: 1rem; line-height: 1; }

/* Ticket status badges (bandeja + detail) */
.status-abierto    { background: rgba(59,130,246,0.1);  color: #2563eb; }
.status-tomado     { background: rgba(139,92,246,0.1);  color: #7c3aed; }
.status-en_proceso { background: rgba(245,158,11,0.1);  color: #d97706; }
.status-escalado   { background: rgba(239,68,68,0.1);   color: #dc2626; }
.status-resuelto   { background: rgba(16,185,129,0.1);  color: #059669; }
.status-cerrado    { background: rgba(100,116,139,0.1); color: var(--muted); }

/* Priority badge */
.priority-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
}

.priority-baja    { background: rgba(100,116,139,0.1); color: var(--muted); }
.priority-media   { background: rgba(59,130,246,0.1);  color: #2563eb; }
.priority-alta    { background: rgba(245,158,11,0.1);  color: #d97706; }
.priority-critica { background: rgba(239,68,68,0.1);   color: #dc2626; }

/* Ver button in ticket row */
.btn-detail { background: var(--brand-soft-bg); color: var(--brand-soft-fg); border-color: rgba(var(--brand-rgb), 0.35); }
.btn-detail:hover { background: rgba(var(--brand-rgb), 0.18); }

/* ============================================================
   BOTONES GLOBALES (btn-primary / btn-secondary / btn-danger)
   ============================================================ */
.btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 20px;
  border-radius: 10px;
  font-size: 0.875rem;
  font-weight: 600;
  font-family: inherit;
  border: none;
  cursor: pointer;
  transition: all 0.2s ease;
  background: var(--brand-dark);
  color: #fff;
}
.btn-primary:hover:not(:disabled) { background: var(--brand-dark); transform: translateY(-1px); box-shadow: 0 4px 12px rgba(var(--brand-dark-rgb),0.35); }
.btn-primary:active:not(:disabled) { transform: translateY(0); }
.btn-primary:disabled { opacity: 0.55; cursor: not-allowed; }

.btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 20px;
  border-radius: 10px;
  font-size: 0.875rem;
  font-weight: 600;
  font-family: inherit;
  border: 1px solid var(--border);
  cursor: pointer;
  transition: all 0.2s ease;
  background: var(--control-bg-hover);
  color: var(--text-strong);
}
/* Hover en oscuro: aclarar levemente SIN perder el texto (antes #cbd5e1 dejaba
   texto claro sobre fondo claro = ilegible). En claro lo pisa el override de abajo. */
.btn-secondary:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.2);
  color: var(--text-strong);
}
.btn-secondary:disabled { opacity: 0.55; cursor: not-allowed; }

.btn-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 20px;
  border-radius: 10px;
  font-size: 0.875rem;
  font-weight: 600;
  font-family: inherit;
  border: none;
  cursor: pointer;
  transition: all 0.2s ease;
  background: rgba(239,68,68,0.1);
  color: #dc2626;
  border: 1px solid rgba(239,68,68,0.25);
}
.btn-danger:hover:not(:disabled) { background: #ef4444; color: #fff; }
.btn-danger:disabled { opacity: 0.55; cursor: not-allowed; }

/* ============================================================
   PROFILE PAGE
   ============================================================ */

/* ── Layout ── */
.profile-page {
  min-height: 100vh;
  background: var(--bg);
  display: flex;
  flex-direction: column;
}

.profile-main {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 28px 32px;
  max-width: 860px;
  width: 100%;
  margin: 0 auto;
  box-sizing: border-box;
}

/* ── Tab nav ──
   Dos niveles, como el Centro de Control: el nivel 1 usa .control-tabs /
   .control-tab y el nivel 2 .cc-subtabs / .cc-subtab (estilos en el bloque de
   Centro de Control). Aquí solo queda la alineación a la izquierda. */
.profile-main .cc-group-tabs { margin-bottom: 14px; }
.profile-tabs { justify-content: flex-start; }

/* ── Content area ── */
.profile-content {
  animation: fadeInUp 0.2s ease;
}

/* ── Card / form ── */
.profile-form {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 24px 28px 28px;
  box-shadow: var(--shadow-md);
}

.profile-section-title {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 20px;
  padding-bottom: 12px;
  border-bottom: 2px solid var(--border);
}

/* ── Inputs scoped to profile ── */
.profile-form .form-field input,
.profile-form .form-field select,
.profile-form .form-field textarea {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 14px;
  border-radius: 9px;
  border: 1.5px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
  font-size: 0.9rem;
  font-family: inherit;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.profile-form .form-field input:focus,
.profile-form .form-field select:focus,
.profile-form .form-field textarea:focus {
  outline: none;
  border-color: var(--brand-dark);
  box-shadow: 0 0 0 3px rgba(var(--brand-dark-rgb),0.1);
  background: var(--surface);
}
.profile-form .form-field input:disabled,
.profile-form .form-field select:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  background: var(--surface-2);
}

/* ── Helpers ── */
.profile-hint {
  font-size: 0.875rem;
  color: var(--muted);
  margin: 0 0 18px;
  line-height: 1.5;
}
.profile-hint strong { color: var(--text); }

.profile-row {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-top: 20px;
  flex-wrap: wrap;
}

/* ── Info banner ── */
.info-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border-radius: 10px;
  font-size: 0.875rem;
  font-weight: 500;
  border: 1px solid rgba(var(--brand-rgb),0.3);
  background: rgba(var(--brand-rgb),0.07);
  color: var(--brand-dark);
  margin-bottom: 18px;
}

/* ── MFA ── */
.mfa-status {
  font-size: 0.9rem;
  color: var(--muted);
  margin-bottom: 20px;
}
.mfa-status strong { color: var(--text); }

.mfa-options {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.mfa-buttons {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

/* ── TOTP ── */
.totp-qr-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: 16px 0;
  padding: 20px;
  background: #fff; /* EXCEPCIÓN intencional: el QR necesita fondo claro para escanearse en ambos temas */
  border: 1.5px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow-md);
}

.totp-qr-img {
  width: 200px;
  height: 200px;
  border-radius: 8px;
  display: block;
}

.totp-secret {
  display: block;
  font-family: 'Courier New', Courier, monospace;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 14px;
  word-break: break-all;
  color: var(--text);
  user-select: all;
}

/* ── Security questions ── */
.security-question-block {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 18px;
  margin-bottom: 14px;
}

/* ── Responsive ── */
@media (max-width: 640px) {
  .profile-main { padding: 16px; }
  .profile-tabs { width: 100%; overflow-x: auto; }
  .profile-form { padding: 18px 16px 22px; }
  .mfa-buttons { flex-direction: column; }
  .profile-row { flex-direction: column; align-items: stretch; }
  .profile-row .btn-primary,
  .profile-row .btn-secondary { width: 100%; justify-content: center; }
}

/* ── Monitor health cards ── */
.monitor-health-grid {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}

.monitor-health-card {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 16px;
  min-width: 110px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  box-shadow: 0 1px 3px rgba(0,0,0,.05);
  transition: box-shadow .15s;
}

.monitor-health-card:hover {
  box-shadow: 0 3px 8px rgba(0,0,0,.08);
}

.monitor-health-card-top {
  display: flex;
  align-items: center;
  gap: 7px;
}

.monitor-health-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.monitor-health-dot--ok   { background: #22c55e; }
.monitor-health-dot--warn { background: #f59e0b; animation: campPulse 1.5s infinite; }
.monitor-health-dot--err  { background: #ef4444; }
.monitor-health-dot--conn { background: #94a3b8; animation: campPulse 1s infinite; }

.monitor-health-name {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-strong);
}

.monitor-health-status {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
}

.monitor-health-latency {
  font-size: 0.68rem;
  color: var(--muted-2);
  font-family: 'JetBrains Mono', monospace;
}

/* Las barras de filtros propias de Monitor (.monitor-filters /
   .monitor-filter-field) se retiraron: las cuatro pestañas usan la toolbar
   estándar (.tickets-toolbar + embudo). */

.monitor-filter-check {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 38px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--text);
  font-size: 0.84rem;
  font-weight: 500;
  cursor: pointer;
}

.monitor-filter-check input {
  margin: 0;
  width: 16px;
  height: 16px;
  accent-color: var(--brand-dark);
  cursor: pointer;
}

.monitor-filter-check span {
  line-height: 1;
  white-space: nowrap;
}

.monitor-click-row {
  cursor: pointer;
}

.monitor-detail-row td {
  background: var(--surface-2);
}

.monitor-detail-panel {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 18px;
  margin: 6px 0;
}

.monitor-detail-section {
  margin-bottom: 12px;
}

.monitor-detail-label {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 5px;
}

.monitor-detail-text {
  display: block;
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  color: var(--text-strong);
  font-size: 0.84rem;
}

.monitor-terminal {
  background: var(--surface-3);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px;
  min-height: 320px;
  max-height: 560px;
  overflow: auto;
}

.monitor-log-pre {
  margin: 0 0 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid rgba(148, 163, 184, 0.18);
  font-size: 0.78rem;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Log entry lines — colored by level */
.monitor-log-entry {
  padding: 2px 0;
  font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
  font-size: 0.78rem;
  white-space: pre-wrap;
  word-break: break-all;
  line-height: 1.55;
  border-bottom: 1px solid rgba(148, 163, 184, 0.06);
}
.monitor-log-entry--debug   { color: var(--muted); }
.monitor-log-entry--info    { color: var(--muted-2); }
.monitor-log-entry--warning { color: #fbbf24; }
.monitor-log-entry--error   { color: #f87171; }
.monitor-log-entry--critical { color: #f43f5e; font-weight: 700; }

/* Conversaciones — chat bubbles */
.monitor-context-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 8px 0;
  max-height: 620px;
  overflow-y: auto;
}

.monitor-msg {
  display: flex;
  flex-direction: column;
  max-width: 78%;
}
.monitor-msg--user  { align-self: flex-end;  align-items: flex-end; }
.monitor-msg--agent { align-self: flex-start; align-items: flex-start; }
.monitor-msg-role {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: 4px;
  text-transform: capitalize;
}
.monitor-msg-content {
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  font-size: 0.85rem;
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.55;
  color: var(--text-strong);
  background: var(--surface);
}
.monitor-msg--user  .monitor-msg-content {
  background: var(--brand-soft-bg);
  border-color: rgba(var(--brand-rgb), 0.35);
  color: var(--brand-soft-fg);
  border-radius: 12px 12px 4px 12px;
}
.monitor-msg--agent .monitor-msg-content {
  border-radius: 12px 12px 12px 4px;
}

.monitor-context-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
}

.monitor-context-item pre {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── PAGINACIÓN ESTÁNDAR ──────────────────────────────────────────────────────
   Anterior / info / Siguiente centrados bajo la tabla. El de Pagos es el
   canónico; Monitor, Tickets, Contactos y Marketplace se cuelgan de aquí.
   Añadir un módulo = sumar su clase a estas listas, no crear otro pager.
   Centrado (no pegado a la derecha) para que el bloque quede simétrico con la
   tabla, que ocupa el ancho completo. */
.monitor-pagination,
.pg-pager,
.ticket-pagination,
.audit-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-top: 14px;
  flex-wrap: wrap;
}

.monitor-page-info,
.pg-pager > span,
.ticket-pagination-info,
.ticket-pagination-page,
.audit-page-info {
  color: var(--muted);
  font-size: 0.82rem;
}

/* Los extremos se ven bloqueados, sin texto que lo explique. */
.monitor-pagination button:disabled,
.pg-pager button:disabled,
.ticket-pagination button:disabled,
.audit-pagination button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.monitor-breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.monitor-cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 14px;
}

.monitor-channel-card {
  text-align: left;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px;
  cursor: pointer;
  box-shadow: 0 10px 30px rgba(0,0,0,0.12);
  transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease, background .15s ease;
}

.monitor-channel-card:hover {
  transform: translateY(-2px);
  border-color: var(--brand);
  box-shadow: 0 14px 34px rgba(var(--brand-dark-rgb),0.18);
}

.monitor-channel-title {
  font-size: 1rem;
  font-weight: 700;
  margin-bottom: 12px;
}

.monitor-channel-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.monitor-channel-stats div {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.monitor-channel-stats span {
  font-size: 0.7rem;
  color: var(--muted-2);
  text-transform: uppercase;
}

.monitor-channel-stats strong {
  font-size: 0.95rem;
  color: var(--text-strong);
}

.monitor-channel-footer {
  margin-top: 12px;
  font-size: 0.76rem;
  color: var(--muted-2);
}

.monitor-exec-table td,
.monitor-exec-table th {
  white-space: nowrap;
}

.monitor-provider-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
}

.monitor-provider-openrouter { background: var(--badge-info-bg); color: var(--badge-info-fg); }
.monitor-provider-gemini { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.monitor-provider-kimi { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.monitor-provider-deepseek { background: var(--badge-purple-bg); color: var(--badge-purple-fg); }
.monitor-provider-other { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }

.monitor-inline-flag {
  margin-left: 6px;
}

.monitor-row-error td { background: var(--badge-danger-bg); }
.monitor-row-timeout td { background: var(--surface)7ed; }
.monitor-row-running td { background: var(--brand-soft-bg); }
.monitor-row-tool-warn td { background: var(--surface)beb; }

/* ── Monitor detail v2: header bar ────────────────────────────────── */
.monitor-detail-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 18px;
  padding: 10px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  margin-bottom: 12px;
}
.monitor-detail-meta-item {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.monitor-detail-meta-key {
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--muted-2);
  letter-spacing: .04em;
}
.monitor-detail-meta-val {
  font-size: 0.82rem;
  color: var(--text-strong);
}
.monitor-detail-meta-val--bold { font-weight: 700; }

/* ── Token grid ────────────────────────────────────────────────────── */
.monitor-token-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 8px;
  margin-top: 6px;
}
.monitor-token-grid--sm {
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
}
.monitor-token-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  text-align: center;
  gap: 4px;
  min-width: 0;
}
.monitor-token-cell--total {
  border-color: var(--brand);
  background: var(--brand-soft-bg);
}
.monitor-token-cell--prompt { border-color: var(--brand-soft-fg); background: var(--brand-soft-bg); }
.monitor-token-cell--compl  { border-color: #86efac; background: var(--badge-success-bg); }
.monitor-token-cell--think  { border-color: #d8b4fe; background: var(--badge-purple-bg); }
.monitor-token-cell-label {
  font-size: 0.64rem;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--muted);
  letter-spacing: .04em;
}
.monitor-token-cell-val {
  font-size: 1.1rem;
  font-weight: 800;
  color: var(--text-strong);
}
.monitor-token-cell--total  .monitor-token-cell-val { color: var(--brand-dark); }
.monitor-token-cell--prompt .monitor-token-cell-val { color: var(--brand-dark); }
.monitor-token-cell--compl  .monitor-token-cell-val { color: #15803d; }
.monitor-token-cell--think  .monitor-token-cell-val { color: #7c3aed; }

/* ── Media analysis input block ───────────────────────────────────── */
.monitor-media-block {
  margin-top: 10px;
  padding: 12px 14px;
  background: var(--badge-warn-bg);
  border: 1px solid #fde68a;
  border-radius: 8px;
}
.monitor-media-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  font-size: 0.83rem;
  color: #78350f;
  margin: 8px 0 0;
}
.monitor-media-meta span {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  background: rgba(253,230,138,0.5);
  border-radius: 5px;
}

/* ── Round accordion ──────────────────────────────────────────────── */
.monitor-round-accordion {
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface);
}
.monitor-round-accordion-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  cursor: pointer;
  user-select: none;
  background: var(--surface-2);
  transition: background 0.12s;
}
.monitor-round-accordion-header:hover { background: var(--surface-2); }
.monitor-round-toggle {
  font-size: 0.82rem;
  color: var(--muted);
  width: 14px;
  flex-shrink: 0;
}
.monitor-round-accordion-title {
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--text-strong);
}
.monitor-round-accordion-meta {
  font-size: 0.78rem;
  color: var(--muted);
  margin-left: auto;
}
.monitor-round-accordion-body {
  padding: 12px 14px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ── Thinking block ───────────────────────────────────────────────── */
.monitor-thinking-block {
  background: var(--badge-purple-bg);
  border: 1px solid #e9d5ff;
  border-radius: 8px;
  padding: 10px 12px;
}

/* ── Detail panel: prevent horizontal overflow inside scrollable table ── */
.monitor-detail-panel {
  min-width: 0;
  overflow-x: hidden;
}
pre.monitor-detail-text {
  overflow-x: auto;
  max-height: 260px;
}

/* ── Tool card header + I/O two-column ───────────────────────────── */
.monitor-tool-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px 10px;
  margin-bottom: 10px;
}
.monitor-tool-name {
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--text-strong);
}
.monitor-tool-mcp {
  font-size: 0.76rem;
  color: var(--brand);
  background: var(--brand-soft-bg);
  padding: 2px 7px;
  border-radius: 5px;
  font-weight: 600;
}
.monitor-tool-ronda,
.monitor-tool-dur {
  font-size: 0.76rem;
  color: var(--muted);
}
.monitor-tool-io {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

/* ── Agent output error state ────────────────────────────────────── */
.monitor-output-error {
  background: var(--badge-danger-bg);
  border: 1px solid #fecaca;
  border-radius: 8px;
  padding: 12px 14px;
  color: #991b1b;
  font-size: 0.88rem;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── Media output blocks ─────────────────────────────────────────── */
.monitor-media-out-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}
.monitor-media-out-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.84rem;
}
.monitor-media-out-type {
  font-weight: 700;
  font-size: 0.68rem;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--control-bg-hover);
  padding: 2px 6px;
  border-radius: 4px;
}

/* ── Input preview in executions table ───────────────────────────── */
.monitor-exec-input-preview {
  color: var(--muted);
  font-size: 0.79rem;
  font-style: italic;
  max-width: 220px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
}

@media (max-width: 900px) {
  .monitor-token-grid        { grid-template-columns: repeat(2, 1fr); }
  .monitor-token-grid--sm    { grid-template-columns: 1fr; }
  .monitor-tool-io           { grid-template-columns: 1fr; }
  .monitor-drawer            { width: 100vw; }
}

/* ── Drawer de detalle de ejecución ─────────────────────────────────── */
.monitor-drawer-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.38);
  backdrop-filter: blur(2px);
  z-index: 900;
}
.monitor-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(700px, 92vw);
  background: var(--surface-overlay);
  z-index: 901;
  display: flex;
  flex-direction: column;
  box-shadow: -6px 0 32px rgba(0, 0, 0, 0.14);
  animation: drawerSlideIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes drawerSlideIn {
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
}
.monitor-drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  flex-shrink: 0;
  gap: 12px;
}
.monitor-drawer-title {
  font-size: 0.97rem;
  font-weight: 700;
  color: var(--text-strong);
}
.monitor-drawer-subtitle {
  font-size: 0.76rem;
  color: var(--muted);
  margin-top: 2px;
  font-family: 'SFMono-Regular', Consolas, monospace;
  letter-spacing: .03em;
}
.monitor-drawer-close {
  width: 34px;
  height: 34px;
  border: none;
  background: var(--control-bg-hover);
  border-radius: 7px;
  cursor: pointer;
  font-size: 1rem;
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background 0.12s, color 0.12s;
  line-height: 1;
}
.monitor-drawer-close:hover {
  background: var(--muted-2);
  color: var(--text-strong);
}
.monitor-drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
}

.monitor-detail-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}

.monitor-detail-section--full {
  grid-column: 1 / -1;
}

.monitor-rounds-list,
.monitor-tools-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.monitor-round-card,
.monitor-tool-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px;
}

.monitor-round-title,
.monitor-tool-title {
  font-weight: 700;
  color: var(--text-strong);
  margin-bottom: 8px;
}

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

  .monitor-detail-section--full {
    grid-column: auto;
  }
}

/* ============================================================
   Workflow Visualizer (logs en vivo — vista workflow)
   ============================================================ */

.wf-view-toggle {
  display: flex;
  gap: 6px;
  margin-bottom: 12px;
}

.wf-toggle-btn {
  padding: 5px 14px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s;
}

.wf-toggle-btn.active {
  background: var(--panel-2) !important;
  color: var(--text) !important;
  border-color: var(--text-strong);
}

/* Container */
.wf-container {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-height: 72vh;
  overflow-y: auto;
  padding-right: 4px;
}

.wf-container::-webkit-scrollbar { width: 4px; }
.wf-container::-webkit-scrollbar-thumb { background: var(--muted-2); border-radius: 2px; }

/* Empty state */
.wf-empty-state {
  text-align: center;
  padding: 48px 20px;
  color: var(--muted);
  background: var(--surface-3);
  border-radius: 10px;
}

.wf-empty-icon { font-size: 36px; margin-bottom: 10px; }
.wf-empty-state p { font-size: 0.85rem; }

/* Execution card */
.wf-card {
  background: #1a1d27;
  border: 1px solid #2e3348;
  border-radius: 10px;
  overflow: hidden;
  transition: border-color 0.2s;
  flex-shrink: 0;
}

.wf-card--running { border-color: #4f6ef7; box-shadow: 0 0 0 1px rgba(79,110,247,0.15); }
.wf-card--done    { border-color: #22c55e; }
.wf-card--error   { border-color: #ef4444; }

/* Card header */
.wf-card-header {
  padding: 9px 14px;
  display: flex;
  align-items: center;
  gap: 10px;
  background: #22263a;
  border-bottom: 1px solid #2e3348;
  flex-wrap: wrap;
}

.wf-req    { font-family: monospace; font-size: 10px; color: var(--muted); flex-shrink: 0; }
.wf-user   { font-size: 11px; color: #06b6d4; }
.wf-canal  { font-size: 11px; color: #a855f7; }
.wf-time   { font-size: 11px; color: var(--muted); margin-left: auto; }

.wf-badge  { padding: 2px 7px; border-radius: 4px; font-size: 10px; font-weight: 700; flex-shrink: 0; }
.wf-badge--running { background: rgba(79,110,247,0.2);  color: #6c8af7; }
.wf-badge--done    { background: rgba(34,197,94,0.15);  color: #22c55e; }
.wf-badge--error   { background: rgba(239,68,68,0.15);  color: #ef4444; }

/* Pipeline flow */
.wf-pipeline-flow {
  padding: 16px 14px 12px;
  display: flex;
  align-items: center;
  overflow-x: auto;
}

.wf-pipeline-flow::-webkit-scrollbar { height: 3px; }
.wf-pipeline-flow::-webkit-scrollbar-thumb { background: #2e3348; border-radius: 2px; }

/* Node */
.wf-node { flex-shrink: 0; }

.wf-node-box {
  width: 100px;
  background: #0f1117;
  border: 1.5px solid #2e3348;
  border-radius: 10px;
  padding: 9px 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  transition: all 0.25s;
  position: relative;
}

.wf-node-box.pending { opacity: 0.32; }

.wf-node-box.active {
  border-color: #4f6ef7;
  box-shadow: 0 0 0 3px rgba(79,110,247,0.25);
  background: rgba(79,110,247,0.04);
}

.wf-node-box.active::after {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: 10px;
  border: 1.5px solid #4f6ef7;
  animation: wf-pulse-border 1.5s ease-out infinite;
  pointer-events: none;
}

@keyframes wf-pulse-border {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.07); }
}

.wf-node-box.done {
  border-color: #22c55e;
  box-shadow: 0 0 10px rgba(34,197,94,0.3);
  background: rgba(34,197,94,0.04);
}

.wf-node-box.error {
  border-color: #ef4444;
  background: rgba(239,68,68,0.05);
}

.wf-node-box.active .wf-node-label { color: #6c8af7; }
.wf-node-box.done   .wf-node-label { color: #22c55e; }
.wf-node-box.error  .wf-node-label { color: #ef4444; }

.wf-node-icon   { font-size: 18px; line-height: 1; }
.wf-node-label  { font-size: 10px; font-weight: 600; color: var(--muted-2); text-align: center; }
.wf-node-detail { font-size: 8px; color: var(--muted); text-align: center; max-width: 90px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Round dots inside agent node */
.wf-rounds-row { display: flex; gap: 3px; flex-wrap: wrap; justify-content: center; margin-top: 3px; }
.wf-round-dot  { width: 6px; height: 6px; border-radius: 50%; background: #2e3348; }
.wf-round-dot.tool  { background: #f97316; box-shadow: 0 0 4px rgba(249,115,22,0.5); }
.wf-round-dot.final { background: #22c55e; }

/* Edge connector */
.wf-edge { flex-shrink: 0; display: flex; align-items: center; width: 28px; }

.wf-edge-line {
  height: 2px;
  width: 100%;
  background: #2e3348;
  border-radius: 1px;
  transition: background 0.3s;
}

.wf-edge-line.lit {
  background: #22c55e;
  box-shadow: 0 0 6px rgba(34,197,94,0.4);
}

/* Tool cluster */
.wf-tool-cluster {
  padding: 8px 14px 4px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  border-top: 1px solid #2e3348;
}

.wf-mcp-bubble {
  background: #0f1117;
  border: 1px solid #2e3348;
  border-radius: 8px;
  padding: 6px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 70px;
}

.wf-mcp-label   { font-size: 8px; color: var(--muted); font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; }
.wf-tool-pills  { display: flex; flex-wrap: wrap; gap: 3px; justify-content: center; }

.wf-tool-pill {
  border-radius: 20px;
  padding: 2px 6px;
  font-size: 9px;
  border: 1px solid #2e3348;
  color: var(--muted-2);
  background: #22263a;
}

.wf-tool-pill.ok   { border-color: rgba(34,197,94,0.4);  color: #22c55e; }
.wf-tool-pill.fail { border-color: rgba(239,68,68,0.4);  color: #ef4444; }

/* Metrics bar */
.wf-metrics-bar {
  padding: 7px 14px 10px;
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  border-top: 1px solid #2e3348;
}

.wf-metric       { display: flex; flex-direction: column; gap: 2px; }
.wf-metric-key   { font-size: 8px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
.wf-metric-val   { font-size: 11px; font-weight: 600; color: var(--text); font-family: monospace; }
.wf-metric-val.blue   { color: #06b6d4; }
.wf-metric-val.purple { color: #a855f7; }
.wf-metric-val.green  { color: #22c55e; }
.wf-metric-val.orange { color: #f97316; }

/* Log trail */
.wf-log-trail { padding: 0 14px 10px; border-top: 1px solid #2e3348; }

.wf-log-trail-toggle {
  font-size: 10px;
  color: var(--muted);
  cursor: pointer;
  padding: 6px 0 2px;
  user-select: none;
  transition: color 0.15s;
}

.wf-log-trail-toggle:hover { color: var(--muted-2); }

.wf-log-list {
  margin-top: 4px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  max-height: 140px;
  overflow-y: auto;
}

.wf-log-list::-webkit-scrollbar { width: 3px; }
.wf-log-list::-webkit-scrollbar-thumb { background: #2e3348; }

.wf-log-entry {
  font-family: monospace;
  font-size: 9px;
  color: var(--muted);
  padding: 2px 4px;
  border-radius: 3px;
  line-height: 1.5;
}

.wf-log-entry:hover    { background: #22263a; color: var(--muted-2); }
.wf-log-entry.level-warn  { color: #eab308; }
.wf-log-entry.level-error { color: #ef4444; }

@media (max-width: 900px) {
  .wf-pipeline-flow { gap: 0; }
  .wf-node-box { width: 80px; }
  .wf-node-label { font-size: 9px; }
  .wf-edge { width: 18px; }
}

/* ============================================================
   Módulo Multimedia
   ============================================================ */
.mm-page { padding: 28px 32px; }
.mm-header { margin-bottom: 20px; }
.mm-title { font-size: 1.5rem; font-weight: 700; color: var(--text); margin: 10px 0 4px; }
.mm-subtitle { color: var(--muted); font-size: 0.88rem; margin: 0; }
.mm-banner {
  margin-bottom: 16px;
  padding: 11px 14px;
  border-radius: 10px;
  border: 1px solid rgba(var(--brand-rgb), 0.30);
  background: rgba(var(--brand-rgb), 0.08);
  color: var(--brand-dark);
  font-size: 0.86rem;
}
.mm-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
  margin-bottom: 16px;
}
.mm-row,
.mm-toolbar {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
}
.mm-toolbar { margin-top: 14px; }
.mm-section-title { font-size: 1rem; font-weight: 700; color: var(--text); margin-bottom: 4px; }
.mm-helper { color: var(--muted); font-size: 0.82rem; }
/* Cabecera del apartado (barra 1): plegado + título + tooltip + conteo. El conteo
   se ve siempre, también con el apartado colapsado. */
.mm-sec-head { display: flex; align-items: center; gap: 8px; }
.mm-sec-head .mm-section-title { margin: 0; display: inline-flex; align-items: center; gap: 6px; }
.mm-sec-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  border: 1px solid var(--control-border); border-radius: 8px;
  background: var(--control-bg); color: var(--text);
  cursor: pointer;
}
.mm-sec-toggle:hover { background: var(--control-bg-hover); }
.mm-caret { transition: transform 0.15s ease; }
.mm-caret.is-open { transform: rotate(90deg); }
.mm-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 7px;
  border-radius: 999px;
  background: var(--brand-soft-bg); color: var(--brand-soft-fg);
  font-size: 0.75rem; font-weight: 700;
}
.mm-buckets {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 12px;
  margin-top: 16px;
}
/* Menú ⋮ de una tarjeta (disco, carpeta o archivo): SIEMPRE visible, no solo en
   hover — es el único acceso a "Eliminar". */
.mm-menu { position: absolute; top: 8px; right: 8px; }
.mm-dots {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  border: 1px solid transparent; border-radius: 8px;
  background: transparent; color: var(--muted);
  cursor: pointer;
}
.mm-dots:hover { background: var(--control-bg-hover); color: var(--text); border-color: var(--control-border); }
.mm-menu-pop {
  position: absolute; top: 30px; right: 0; z-index: 60;
  min-width: 168px; padding: 5px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface-overlay);
  box-shadow: var(--shadow-lg);
}
.mm-menu-item {
  display: block; width: 100%; text-align: left;
  padding: 8px 10px; border: 0; border-radius: 7px;
  background: transparent; color: var(--text);
  font-family: inherit; font-size: 0.82rem; font-weight: 600;
  cursor: pointer;
}
.mm-menu-item:hover { background: var(--control-bg-hover); }
.mm-menu-item.is-danger { color: #ef4444; }
.mm-menu-item.is-danger:hover { background: rgba(239, 68, 68, 0.12); }

/* El menú de orden y el segmented de vista los aporta la toolbar estándar
   (.tb-sort / .tb-viewseg en components/toolbar.rs). Aquí solo queda el estado
   activo del menú ⋮ y la separación de la toolbar dentro de la tarjeta. */
.mm-menu-item.is-active { color: var(--brand); }
.mm-card .tb { margin-top: 14px; }

/* ── Discos colapsados: tira horizontal de píldoras ─────────────
   Permite seleccionar/borrar sin desplegar el apartado. Scroll en X cuando no
   caben; el nombre se trunca con ellipsis (completo en el `title`). */
.mm-chips {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding-bottom: 4px;
  overflow-x: auto;
  scrollbar-width: thin;
}
.mm-chips-empty { color: var(--muted); font-size: 0.82rem; }
.mm-chip {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  max-width: 190px;
  padding: 6px 4px 6px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  cursor: pointer;
  transition: box-shadow 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
/* Sin `transform` en hover: un ancestro transformado ancla los `position: fixed`
   y el desplegable ⋮ volvería a recortarse contra el scroll de la tira. */
.mm-chip:hover { border-color: var(--brand); box-shadow: var(--shadow-md); }
.mm-chip--active {
  border-color: var(--brand);
  background: rgba(var(--brand-rgb), 0.06);
  box-shadow: 0 0 0 1px var(--brand) inset;
}
.mm-chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
}
/* El ⋮ va en el flujo de la píldora (no absoluto como en la tarjeta). Su
   desplegable escapa del recorte del scroll con `position: fixed`, pero las
   coordenadas las calcula `menu_pop_style` (CardMenu fixed=true) desde el rect
   del botón: sin ellas caería al final del flujo, lejos del ⋮. */
.mm-chips .mm-menu { position: static; }
.mm-chips .mm-dots { width: 22px; height: 22px; }

/* Colapso animado del apartado de Discos: el contenido NO se desmonta (si lo
   hiciera no habría transición de salida); se anima su altura con grid. */
.mm-collapse {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows 0.22s ease, opacity 0.18s ease;
}
.mm-collapse.is-open { grid-template-rows: 1fr; opacity: 1; }
.mm-collapse-inner { overflow: hidden; min-height: 0; }
/* Ya abierto no recorta: si no, el desplegable ⋮ de la última fila se corta contra
   el borde del contenedor. Al cerrar vuelve a `hidden` y la altura sigue animando. */
.mm-collapse.is-open .mm-collapse-inner { overflow: visible; }

/* Entrada del listado al navegar entre carpetas. */
@keyframes mm-fade-up {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.mm-objects-anim { animation: mm-fade-up 0.18s ease both; }

/* Esqueletos mientras carga el directorio. */
.mm-skeleton {
  height: 108px; border-radius: 12px;
  border: 1px solid var(--border);
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--control-bg-hover) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: mm-shimmer 1.2s ease-in-out infinite;
}
@keyframes mm-shimmer {
  from { background-position: 100% 50%; }
  to   { background-position: 0 50%; }
}
.mm-explorer-status {
  display: flex; align-items: center; gap: 12px;
  margin: 12px 0 4px; font-size: 0.8rem; color: var(--muted);
}
.mm-loading { display: inline-flex; align-items: center; gap: 7px; }
.mm-spinner {
  width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--control-border); border-top-color: var(--brand);
  animation: mm-spin 0.7s linear infinite;
}
@keyframes mm-spin { to { transform: rotate(360deg); } }

/* Zona de arrastre: la tarjeta entera del explorador acepta archivos. */
.mm-card.mm-dropzone {
  border-color: var(--brand);
  box-shadow: 0 0 0 2px rgba(var(--brand-rgb), 0.18) inset;
}
.mm-file-hidden { display: none; }
/* Modal de detalles: pares etiqueta/valor, el valor puede ser una ruta larga. */
.mm-details { margin: 4px 0 6px; padding: 0; display: grid; gap: 2px; }
.mm-details-row {
  display: grid;
  grid-template-columns: 116px minmax(0, 1fr);
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.84rem;
}
.mm-details-row:last-child { border-bottom: 0; }
.mm-details dt { color: var(--muted); }
.mm-details dd { margin: 0; color: var(--text); overflow-wrap: break-word; }
.mm-details-path {
  margin: 0 0 10px;
  font-size: 0.8rem;
  color: var(--muted);
  overflow-wrap: break-word;
}

.mm-upload-list {
  margin: 0 0 4px; padding: 10px 12px 10px 26px;
  max-height: 190px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface-2);
  font-size: 0.82rem; color: var(--text);
}

/* ── Vista de lista: FILAS de verdad, tipo explorador de archivos ──────────────
   No son tarjetas estiradas: sin caja propia, sin relleno de miniatura, icono
   pequeño a la izquierda, nombre que ocupa el ancho y metadatos a la derecha.
   Solo las separa una línea y el hover pinta la fila entera. */
.mm-buckets.is-list,
.mm-objects.is-list {
  display: block;
  border: 1px solid var(--border);
  border-radius: 10px;
  /* `overflow: hidden` recortaría el desplegable ⋮ de las últimas filas; el
     redondeo se consigue en la primera y la última fila. */
  overflow: visible;
  background: var(--surface);
}
.mm-buckets.is-list .mm-bucket:first-child,
.mm-objects.is-list .mm-object:first-child { border-radius: 9px 9px 0 0; }
.mm-buckets.is-list .mm-bucket:last-child,
.mm-objects.is-list .mm-object:last-child { border-radius: 0 0 9px 9px; }
.mm-buckets.is-list .mm-bucket,
.mm-objects.is-list .mm-object {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 12px;
  border: 0;
  border-bottom: 1px solid var(--border-soft);
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  text-align: left;
  /* Nada de elevación/escala en filas: desalinea la lista. */
  transform: none;
}
.mm-buckets.is-list .mm-bucket:last-child,
.mm-objects.is-list .mm-object:last-child { border-bottom: 0; }
.mm-buckets.is-list .mm-bucket:hover,
.mm-objects.is-list .mm-object:hover {
  transform: none;
  box-shadow: none;
  border-color: var(--border-soft);
  background: var(--control-bg-hover);
}
.mm-buckets.is-list .mm-bucket--active {
  background: var(--brand-soft-bg);
  box-shadow: inset 3px 0 0 var(--brand);
}

/* Icono/miniatura de la fila: cuadrado pequeño, sin fondo de tarjeta. */
.mm-objects.is-list .mm-fileicon,
.mm-objects.is-list .mm-thumb {
  flex: 0 0 26px;
  width: 26px;
  height: 26px;
  min-height: 0;
  border-radius: 5px;
  font-size: 0.58rem;
  letter-spacing: 0.02em;
  background: transparent;
  object-fit: cover;
}
.mm-objects.is-list .mm-thumb { background: var(--panel-2); }
.mm-objects.is-list .mm-foldericon { font-size: 1.05rem; background: transparent; }

/* Nombre a la izquierda ocupando el hueco, metadatos alineados a la derecha. */
.mm-objects.is-list .mm-object-info {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 0;
}
.mm-objects.is-list .mm-object-key { flex: 1 1 auto; padding-right: 0; }
.mm-objects.is-list .mm-item-desc {
  flex: 0 0 auto;
  text-align: right;
  font-size: 0.78rem;
  white-space: nowrap;
}

/* Discos en fila: nombre + uso + etiquetas en una sola línea. */
.mm-buckets.is-list .mm-bucket-name { flex: 1 1 auto; min-width: 0; padding-right: 0; }
.mm-buckets.is-list .mm-usage {
  flex: 0 0 auto;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  margin: 0;
  width: 240px;
}
.mm-buckets.is-list .mm-usage-track { width: 90px; flex: 0 0 90px; }
.mm-buckets.is-list .mm-usage-text { white-space: nowrap; }
.mm-buckets.is-list .mm-bucket-tags { flex: 0 0 auto; margin: 0; }

/* El ⋮ deja de flotar en la esquina y cierra la fila. */
.mm-buckets.is-list .mm-menu,
.mm-objects.is-list .mm-menu { position: relative; top: auto; right: auto; flex: 0 0 auto; order: 9; }
.mm-buckets.is-list .mm-menu .mm-menu-pop,
.mm-objects.is-list .mm-menu .mm-menu-pop { top: 30px; right: 0; }

/* Esqueletos de carga en modo lista: barras, no tarjetas. */
.mm-objects.is-list.mm-skeletons { padding: 0; }
.mm-objects.is-list .mm-skeleton { height: 40px; border: 0; border-radius: 0; border-bottom: 1px solid var(--border-soft); }

.mm-bucket {
  position: relative;                          /* ancla del menú ⋮ */
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  padding: 14px;
  text-align: left;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
  display: grid;
  gap: 6px;
}
.mm-bucket:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
  border-color: var(--brand);
}
.mm-bucket--active {
  border-color: var(--brand);
  background: rgba(var(--brand-rgb), 0.06);
  box-shadow: 0 0 0 1px var(--brand) inset;
}
.mm-bucket span { color: var(--text); }
.mm-bucket small { color: var(--muted); font-size: 0.75rem; }
.mm-input,
.mm-select,
.mm-file {
  min-height: 40px;
  border: 1px solid var(--control-border);
  border-radius: 8px;
  padding: 9px 12px;
  background: var(--control-bg);
  color: var(--text);
  font-size: 0.88rem;
  font-family: inherit;
}
.mm-input:focus,
.mm-select:focus,
.mm-file:focus { outline: none; border-color: var(--brand); }
.mm-input { min-width: 240px; flex: 1 1 240px; }
.mm-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 38px;
  padding: 0 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 600;
  font-family: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.mm-btn:hover { background: var(--panel-2); }
.mm-btn--sm { min-height: 32px; padding: 0 10px; font-size: 0.78rem; font-weight: 500; }
.mm-btn--ghost { background: transparent; border-color: transparent; color: var(--muted); }
.mm-btn--ghost:hover { background: var(--panel-2); color: var(--text); }
.mm-btn--primary {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.mm-btn--primary:hover { background: var(--brand-dark); border-color: var(--brand-dark); }
.mm-btn--danger {
  color: var(--danger);
  border-color: rgba(239, 68, 68, 0.35);
  background: var(--surface);
}
.mm-btn--danger:hover { background: rgba(239, 68, 68, 0.08); border-color: var(--danger); }
.mm-objects {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 14px;
  margin-top: 16px;
}
/* Realce al pasar el ratón: leve elevación + escala, como las tarjetas de disco.
   `position: relative` ancla el menú ⋮ de la tarjeta. */
.mm-object:hover { transform: translateY(-2px) scale(1.02); box-shadow: var(--shadow-md); border-color: var(--brand); }
.mm-object .mm-object-key { padding-right: 26px; }
.mm-object {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: visible;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.mm-thumb {
  display: block;
  width: 100%;
  height: 170px;
  object-fit: cover;
  background: var(--panel-2);
}
.mm-fileicon {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 170px;
  background: linear-gradient(135deg, var(--panel-2) 0%, var(--surface-3) 100%);
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.mm-object-info { padding: 14px; display: grid; gap: 8px; }
.mm-object-key {
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mm-item-desc { color: var(--muted); font-size: 0.8rem; }
.mm-object-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.mm-empty {
  margin-top: 14px;
  padding: 20px;
  border: 1px dashed var(--border);
  border-radius: 10px;
  color: var(--muted);
  background: var(--panel-2);
  font-size: 0.86rem;
  text-align: center;
}

/* ── Discos: cabecera de fila con acciones ──────────────────── */
.mm-row-actions { display: flex; gap: 8px; align-items: center; }

/* ── Tarjeta de disco: nombre, barra de uso y etiquetas ─────── */
/* padding-right: deja libre la esquina del menú ⋮ (nombres largos no se le meten debajo). */
.mm-bucket-name { font-weight: 700; color: var(--text); padding-right: 30px; }
.mm-usage { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.mm-usage-track {
  width: 100%;
  height: 6px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
}
.mm-usage-bar {
  height: 100%;
  border-radius: 999px;
  background: var(--brand);
  transition: width 0.3s ease;
}
.mm-usage-bar.full { background: var(--danger); }
.mm-usage-text { color: var(--muted); font-size: 0.78rem; }
.mm-bucket-tags { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.mm-tag {
  font-size: 0.72rem;
  font-weight: 600;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--panel-2);
  color: var(--muted);
  border: 1px solid var(--border);
}

/* ── Breadcrumbs del explorador ─────────────────────────────── */
.mm-crumbs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px;
  margin: 12px 0;
  padding: 8px 12px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.mm-crumb-item { display: inline-flex; align-items: center; gap: 2px; }
.mm-crumb {
  background: none;
  border: none;
  color: var(--brand-dark);
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 500;
  font-family: inherit;
  padding: 2px 6px;
  border-radius: 6px;
}
.mm-crumb:hover { background: rgba(var(--brand-rgb), 0.10); }
.mm-crumb-sep { color: var(--muted); padding: 0 1px; }

/* ── Barra de subida en el directorio actual ────────────────── */
.mm-uploadbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

/* ── Barra de búsqueda y filtros del explorador ─────────────── */
.mm-filters {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 14px;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.mm-filter-search {
  flex: 1 1 220px;
  min-width: 180px;
}
/* Los selects del explorador necesitan más ancho que los del modal "Crear disco". */
.mm-filters .mm-select {
  max-width: none;
  flex: 0 0 auto;
  min-width: 140px;
}
.mm-filter-date {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.82rem;
  color: var(--muted);
  white-space: nowrap;
}
.mm-filters .mm-input--narrow {
  flex: 0 0 auto;
  width: auto;
  min-width: 130px;
  max-width: 150px;
}
.mm-filter-count {
  margin-left: auto;
  font-size: 0.82rem;
  color: var(--muted);
  white-space: nowrap;
}
@media (max-width: 720px) {
  .mm-filter-count { margin-left: 0; }
  .mm-filters .mm-select,
  .mm-filter-search { flex: 1 1 100%; }
}

/* ── Carpeta (tarjeta clicable) ─────────────────────────────── */
.mm-folder { cursor: pointer; }
.mm-folder:hover { border-color: var(--brand); }
.mm-foldericon {
  background: linear-gradient(135deg, rgba(var(--brand-rgb), 0.14), rgba(var(--brand-rgb), 0.05));
  color: var(--brand-dark);
  font-size: 2.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ── Modal "Crear disco" ────────────────────────────────────── */
.mm-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 16px;
}
.mm-modal {
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 24px;
  width: 100%;
  max-width: 460px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
}
.mm-modal .mm-section-title { margin-bottom: 2px; }
.mm-field-label {
  display: block;
  margin: 14px 0 6px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text);
}
/* El modal apila un campo por línea; la única fila de dos columnas es número +
   unidad, y la unidad no se estira. */
.mm-modal { overflow-x: hidden; }
.mm-modal .mm-input, .mm-modal .mm-select { width: 100%; min-width: 0; box-sizing: border-box; }
.mm-field-row--unit { display: flex; gap: 8px; align-items: center; flex-wrap: nowrap; }
.mm-field-row--unit .mm-input { flex: 1 1 auto; min-width: 0; }
.mm-field-row--unit .mm-unit { flex: 0 0 88px; width: 88px; }
.mm-modal .switch-field { margin: 2px 0 4px; }

.mm-field-row { display: flex; gap: 8px; align-items: center; }
.mm-field-inline { display: flex; gap: 6px; align-items: center; color: var(--text); font-size: 0.88rem; }
.mm-select { max-width: 90px; cursor: pointer; }
.mm-input--narrow { max-width: 80px; min-width: 0; flex: 0 0 80px; }
.mm-check {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  font-size: 0.88rem;
  color: var(--text);
  cursor: pointer;
}
.mm-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 22px;
}

@media (max-width: 900px) {
  .mm-page { padding: 22px 16px; }
  .mm-objects,
  .mm-buckets { grid-template-columns: 1fr; }
}

/* ============================================================
   Módulo QA — homogéneo con el resto del panel (mismos tokens)
   ============================================================ */
.qa-page { padding: 28px 32px; }
/* El PageHeader rompe el padding del root para quedar a ancho completo (igual
   que en .page-container), no constreñido por el contenido del módulo. */
.qa-page > .ph,
.mm-page > .ph,
.campaigns-page > .ph {
  margin: -28px -32px 18px;
}
.qa-header { margin-bottom: 20px; }
.qa-header-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; margin-top: 10px; }
.qa-title { font-size: 1.5rem; font-weight: 700; color: var(--text); margin: 0 0 4px; }
.qa-subtitle { color: var(--muted); font-size: 0.88rem; margin: 0; }
.qa-actions { display: flex; gap: 8px; flex-shrink: 0; }

.qa-card { background: rgba(255,255,255,0.05); border: 1px solid var(--border); border-radius: 12px; padding: 20px; box-shadow: 0 1px 4px rgba(0,0,0,0.1); margin-bottom: 16px; }
.qa-section-title { font-size: 1rem; font-weight: 700; color: var(--text); margin: 0 0 12px; }

.qa-btn { padding: 7px 14px; border-radius: 8px; border: 1px solid var(--border); background: rgba(255,255,255,0.06); color: var(--text); font-size: 0.85rem; font-weight: 600; font-family: inherit; cursor: pointer; transition: all 0.15s; }
.qa-btn:hover:not(:disabled) { background: rgba(255,255,255,0.1); }
.qa-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.qa-btn--primary { background: var(--brand); color: #fff; border-color: var(--brand); }
.qa-btn--primary:hover:not(:disabled) { background: var(--brand-dark); }
.qa-btn--danger { color: #dc2626; border-color: #fca5a5; }
.qa-btn--danger:hover:not(:disabled) { background: var(--badge-danger-bg); }
.qa-btn--sm { padding: 4px 10px; font-size: 0.78rem; font-weight: 500; }

.qa-label { display: block; font-size: 0.8rem; font-weight: 600; color: var(--muted); margin: 10px 0 4px; }
.qa-input, .qa-textarea { width: 100%; padding: 9px 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 0.88rem; font-family: inherit; box-sizing: border-box; color: var(--text); }
.qa-input:focus, .qa-textarea:focus { outline: none; border-color: var(--brand); }
.qa-textarea { resize: vertical; min-height: 90px; }
.qa-form-actions { display: flex; gap: 8px; margin-top: 14px; }

.qa-item { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 12px 14px; border: 1px solid var(--border); border-radius: 10px; margin-bottom: 8px; }
.qa-item-name { font-weight: 600; color: var(--text); }
.qa-item-id { color: var(--muted); font-size: 0.82rem; }
.qa-item-desc { color: var(--muted); font-size: 0.82rem; margin-top: 2px; }
.qa-item-actions { display: flex; gap: 6px; flex-shrink: 0; }

.qa-run { display: flex; justify-content: space-between; align-items: center; padding: 10px 14px; border: 1px solid var(--border); border-radius: 8px; margin-bottom: 6px; cursor: pointer; transition: background 0.15s; }
.qa-run:hover { background: var(--surface-2); }

.qa-banner { padding: 10px 16px; border-radius: 8px; font-size: 0.88rem; margin-bottom: 16px; background: var(--brand-soft-bg); color: var(--brand-soft-fg); border: 1px solid rgba(var(--brand-rgb), 0.35); }
.qa-empty { color: var(--muted); font-size: 0.88rem; padding: 8px 0; }

.qa-result { padding: 8px 0; border-bottom: 1px solid var(--border); }
.qa-tag { font-weight: 700; }
.qa-tag--pass { color: #16a34a; }
.qa-tag--fail { color: #dc2626; }

.qa-help { background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px; color: var(--muted); font-size: 0.82rem; margin-bottom: 16px; line-height: 1.5; }
.qa-assert { font-size: 0.82rem; margin: 3px 0; color: var(--text); }
.qa-assert-ev { color: var(--muted); }
.qa-transcript { margin-top: 8px; }
.qa-transcript summary { cursor: pointer; color: var(--muted); font-size: 0.8rem; }
.qa-msg { font-size: 0.82rem; padding: 4px 0; border-bottom: 1px solid var(--border); }
.qa-msg-role { font-weight: 600; color: var(--muted); margin-right: 6px; }

/* Impresión / PDF: aísla el detalle de la corrida */
@media print {
  body * { visibility: hidden; }
  .qa-print-area, .qa-print-area * { visibility: visible; }
  .qa-print-area { position: absolute; left: 0; top: 0; width: 100%; box-shadow: none !important; border: none !important; }
  .qa-noprint { display: none !important; }
  .qa-transcript[open] summary { display: none; }
}

/* ── Colored action pill buttons (dark theme) ──────────────────────── */
.btn-detail  { background: rgba(var(--brand-dark-rgb),0.12);  color: var(--brand-soft-fg);  border-color: rgba(var(--brand-rgb),0.28); }
.btn-detail:hover:not(:disabled)  { background: rgba(var(--brand-dark-rgb),0.22);  border-color: rgba(var(--brand-rgb),0.45); }

/* Ticket missing utilities */

.btn-action--danger {
  background: rgba(239,68,68,0.12);
  color: #f87171;
  border: 1px solid rgba(239,68,68,0.25);
}
.btn-action--danger:hover:not(:disabled) {
  background: rgba(239,68,68,0.22);
  border-color: rgba(239,68,68,0.4);
}
.btn-action--danger:disabled { opacity: 0.45; cursor: not-allowed; }

.ticket-detail-field span:last-child {
  font-size: 0.875rem;
  color: var(--muted);
}

.form-field-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

.input-req-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 4px;
}

.input-req-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.input-req-row select,
.input-req-row input[type="text"] {
  flex: 1;
  min-width: 100px;
  padding: 7px 10px;
  border: 1px solid rgba(255,255,255,0.09);
  border-radius: 8px;
  background: rgba(255,255,255,0.05);
  color: var(--text);
  font: inherit;
  font-size: 0.82rem;
}

.input-req-row select option { background: var(--panel-2); color: var(--text); }

.input-req-row select:focus,
.input-req-row input[type="text"]:focus {
  outline: none;
  border-color: rgba(var(--brand-rgb),0.5);
  background: rgba(255,255,255,0.08);
}

.input-req-row .btn-danger {
  background: rgba(239,68,68,0.12);
  color: #f87171;
  border-color: rgba(239,68,68,0.25);
}
.input-req-row .btn-danger:hover { background: rgba(239,68,68,0.2); }

/* ============================================================
   PAGE HEADER COMPONENT (.ph) — dark theme base styles
   ============================================================ */

.ph {
  background: var(--nav-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--nav-border);
  /* padding-left alineado con la columna de iconos de la sidebar
     (sidebar 10px + item 12px = 22px al borde del icono). */
  padding: 0 24px 0 22px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  position: sticky;
  top: 0;
  z-index: 100;
}

/* ── Left side ── */
.ph-left {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  flex: 1;
}

/* Marca (izquierda del header en /home): logo + nombre del panel. */
.ph-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.ph-back {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(255, 255, 255, 0.04);
  color: rgba(148, 163, 184, 0.85);
  cursor: pointer;
  transition: background 0.18s, border-color 0.18s, color 0.18s;
  flex-shrink: 0;
}

.ph-back:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.15);
  color: var(--text-strong);
}

.ph-divider {
  width: 1px;
  height: 18px;
  background: rgba(255, 255, 255, 0.1);
  flex-shrink: 0;
}

.ph-icon {
  font-size: 1.1875rem;
  flex-shrink: 0;
  line-height: 1;
}
/* Un iconset provee un SVG monocromo → tamaño acorde al emoji del header
   (el emoji se dimensiona por font-size; hereda color via currentColor). */
.ph-icon svg { width: 1.1875rem; height: 1.1875rem; display: block; }

.ph-title-wrap {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.ph-title {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--text-strong);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ph-meta {
  font-size: 0.725rem;
  color: rgba(148, 163, 184, 0.65);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Right side ── */
.ph-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* Module switcher */
.ph-switcher {
  position: relative;
}

.ph-switcher-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  background: rgba(255, 255, 255, 0.05);
  color: rgba(203, 213, 225, 0.9);
  font-size: 0.8rem;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.18s, border-color 0.18s, color 0.18s;
  white-space: nowrap;
}

.ph-switcher-btn:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.16);
  color: var(--text-strong);
}

.ph-chevron {
  transition: transform 0.2s ease;
  flex-shrink: 0;
}

.ph-chevron--open {
  transform: rotate(180deg);
}

.ph-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
}

.ph-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 210px;
  background: #1a2234;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  padding: 8px;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.25);
  z-index: 60;
  animation: phDropIn 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes phDropIn {
  from { opacity: 0; transform: translateY(-6px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.ph-dropdown-label {
  font-size: 0.6875rem;
  font-weight: 700;
  color: rgba(100, 116, 139, 0.75);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  padding: 4px 8px 8px;
  display: block;
}

.ph-dropdown-grid {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ph-mod-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 9px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted-2);
  font-size: 0.8125rem;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  text-align: left;
  width: 100%;
}

.ph-mod-item:hover:not(.ph-mod-item--maint) {
  background: rgba(var(--brand-rgb), 0.1);
  border-color: rgba(var(--brand-rgb), 0.18);
  color: var(--brand-soft-fg);
}

.ph-mod-item--maint {
  opacity: 0.4;
  cursor: not-allowed;
}

.ph-mod-icon {
  font-size: 0.9375rem;
  flex-shrink: 0;
  line-height: 1;
}

.ph-mod-name {
  flex: 1;
}

.ph-mod-maint-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #f59e0b;
  flex-shrink: 0;
}

/* User chip */
.ph-user-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px 4px 4px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.07);
  background: rgba(255, 255, 255, 0.04);
  /* Clickeable → Mi Perfil. Reset de botón + mismo hover que los vecinos. */
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  transition: background 0.18s, border-color 0.18s, transform 0.15s, box-shadow 0.18s;
}

.ph-user-avatar {
  width: 28px;
  height: 28px;
  border-radius: 7px;
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-dark) 100%);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  letter-spacing: 0;
}

.ph-user-info {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.ph-user-name {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-strong);
  white-space: nowrap;
}

.ph-user-role {
  font-size: 0.6875rem;
  color: rgba(148, 163, 184, 0.65);
  font-weight: 500;
  white-space: nowrap;
}

/* Logout button */
.ph-logout {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: 1px solid rgba(239, 68, 68, 0.2);
  background: rgba(239, 68, 68, 0.06);
  color: rgba(252, 165, 165, 0.65);
  cursor: pointer;
  transition: background 0.18s, border-color 0.18s, color 0.18s, transform 0.15s, box-shadow 0.18s;
  flex-shrink: 0;
}

.ph-logout:hover {
  background: rgba(239, 68, 68, 0.16);
  border-color: rgba(239, 68, 68, 0.32);
  color: #f87171;
}

/* THEME TOGGLE BUTTON */

/* Cuadrado, coherente con la campana de notificaciones (misma forma). */
/* El botón de guía comparte estilo con el de tema: son hermanos en la fila de
   controles del header y deben leerse como el mismo tipo de control. */
.ph-theme-btn,
.ph-guide-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.12);
  background: rgba(255,255,255,0.06);
  color: rgba(224,231,255,0.85);
  font-size: 1rem;
  cursor: pointer;
  transition: background 0.18s, border-color 0.18s, transform 0.15s, box-shadow 0.18s;
  flex-shrink: 0;
}
.ph-theme-btn:hover,
.ph-guide-btn:hover {
  background: rgba(255,255,255,0.12);
  border-color: rgba(255,255,255,0.22);
}

/* El de ayuda va un punto más marcado que el de tema. No es capricho: el de tema
   se busca cuando se quiere, mientras que este tiene que ENCONTRARLO alguien que
   está perdido — y quien está perdido no rastrea una fila de iconos tenues.
   Sigue por debajo de la campana, que avisa de cosas que sí urgen. */
.ph-guide-btn {
  color: #fff;
  border-color: rgba(255,255,255,0.28);
  background: rgba(255,255,255,0.14);
}
.ph-guide-btn:hover {
  background: rgba(255,255,255,0.24);
  border-color: rgba(255,255,255,0.42);
}
.ph-guide-btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* Hover unificado de los botones de la derecha del header (tema, campana, chip de
   usuario, salir): aumento de escala + leve iluminación. */
.ph-theme-btn:hover,
.ph-notif-btn:hover,
.ph-user-chip:hover,
.ph-logout:hover {
  transform: scale(1.08);
  box-shadow: 0 0 0 1px rgba(255,255,255,0.12), 0 3px 12px rgba(0,0,0,0.28);
}
.ph-user-chip:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.14);
}

/* ── Header con color del tema (switch de identidad visual, owner) ──────────
   La barra adopta el brand en vez de la superficie neutra; el contenido pasa a
   texto claro de alto contraste sobre la marca. */
.ph--themed {
  background: var(--brand-dark);
  border-bottom-color: color-mix(in srgb, var(--brand-dark) 70%, black 20%);
}
.ph--themed .ph-title,
.ph--themed .ph-user-name,
.ph--themed .app-title,
.ph--themed .ph-icon { color: #fff; }
.ph--themed .ph-meta,
.ph--themed .ph-user-role { color: rgba(255, 255, 255, 0.75); }
.ph--themed .ph-back,
.ph--themed .ph-theme-btn,
.ph--themed .ph-user-chip {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.22);
  color: #fff;
}
.ph--themed .ph-divider { background: rgba(255, 255, 255, 0.28); }
.ph--themed .ph-back:hover,
.ph--themed .ph-theme-btn:hover,
.ph--themed .ph-user-chip:hover { background: rgba(255, 255, 255, 0.22); }

/* LIGHT MODE OVERRIDES  (html.light-mode) */

/* ── Swap GLOBAL de tokens (CLARO). Global, NO scoped a .page-container, para
   que el login y CUALQUIER raíz adapten. Único lugar de la verdad del tema:
   migrar colores hardcodeados a estos tokens (ver tabla en el plan). ── */
html.light-mode {
  color-scheme: light;
  --bg:                #eef1f7;
  --bg-2:              #e6ebf1;
  --surface:           #fbfcfd;
  --surface-2:         #eef1f7;
  --surface-3:         #e6ebf1;
  --surface-overlay:   rgba(251,252,253,0.98);
  --panel:             rgba(251,252,253,0.96);
  --panel-2:           rgba(238,241,247,0.92);
  --control-bg:        #fbfcfd;
  --control-bg-hover:  #e6ebf1;
  --control-border:    rgba(87,45,75,0.16);
  --scrim:             rgba(32,24,35,0.45);
  --text:              #332c3a;
  --text-strong:       #201823;
  --muted:             #6b6575;
  --muted-2:           #9c9ba9;
  --border:            rgba(87,45,75,0.12);
  --border-soft:       rgba(87,45,75,0.07);
  /* Marca en CLARO: la ciruela pura es el acento (contraste 10.5:1 sobre #fbfcfd)
     y --brand-dark es su versión más profunda para hover/estados activos. Es la
     inversión exacta del par de :root (ver comentario allí). */
  --brand:             #572d4b;
  --brand-dark:        #3f2036;
  --brand-rgb:         87, 45, 75;
  --brand-dark-rgb:    63, 32, 54;
  --brand-soft-bg:     rgba(87,45,75,0.10);
  --brand-soft-fg:     #572d4b;
  --accent:            #201823;
  /* Chrome de navegación en CLARO: mismo mecanismo que en :root, pero el tinte de
     marca va sobre la superficie clara y en dosis mínima (la barra debe leerse
     como blanca "de la marca", no como una banda de color). */
  --nav-bg:            #f4f2f5;
  --nav-bg:            color-mix(in srgb, var(--brand) 4%, var(--surface));
  --nav-bg-2:          #f0edf2;
  --nav-bg-2:          color-mix(in srgb, var(--brand) 7%, var(--surface));
  --nav-border:        rgba(87,45,75,0.12);
  /* En claro el velo es mucho más suave: sobre superficie clara, un 30% de negro
     pintaría un panel gris sucio en vez de un escalón de profundidad. */
  --nav-shade:         rgba(0,0,0,0.055);
  --tooltip-bg:        #2b1f27;
  --tooltip-fg:        #fbfcfd;
  --badge-success-bg:  #dcfce7; --badge-success-fg: #15803d;
  --badge-warn-bg:     #fef9c3; --badge-warn-fg:    #a16207;
  --badge-danger-bg:   #fef2f2; --badge-danger-fg:  #b91c1c;
  --badge-info-bg:     #dbeafe; --badge-info-fg:    #1d4ed8;
  --badge-neutral-bg:  #e6ebf1; --badge-neutral-fg: #6b6575;
  --badge-purple-bg:   #f3e8ef; --badge-purple-fg:  #572d4b;
  --shadow-sm:  0 1px 2px rgba(32,24,35,0.05);
  --shadow-md:  0 4px 6px rgba(32,24,35,0.07), 0 2px 4px rgba(32,24,35,0.06);
  --shadow-lg:  0 10px 25px rgba(32,24,35,0.12), 0 4px 8px rgba(32,24,35,0.06);
  /* Gráficas · claro. Los mismos tonos, bajados de claridad: los del modo oscuro
     están pensados para brillar sobre carbón y sobre blanco se lavan. */
  --chart-1:        #2563eb;
  --chart-2:        #0e8a63;
  --chart-3:        #b45309;
  --chart-4:        #6d4aff;
  --chart-5:        #dc2626;
  --chart-6:        #64748b;
  --chart-grid:      #e2e6ed;
  --chart-axis:      #5a6270;
  --chart-legend:    #332c3a;
  --chart-datalabel: #ffffff;
}

html.light-mode body { background: var(--bg); }

/* La tarjeta de login es SIEMPRE clara (independiente del tema): fija un
   contexto de tokens claro para que su contenido sea legible en ambos temas. */
.login-card {
  /* La tarjeta es clara aun en tema oscuro → sus controles nativos (autofill,
     date/time, flecha de select) también deben renderizar en claro. */
  color-scheme: light;
  --text:           #332c3a;
  --text-strong:    #201823;
  --muted:          #6b6575;
  --muted-2:        #9c9ba9;
  --border:         rgba(87,45,75,0.12);
  --border-soft:    rgba(87,45,75,0.07);
  --surface:        #fbfcfd;
  --surface-2:      #eef1f7;
  --control-bg:     #fbfcfd;
  --control-bg-hover: #e6ebf1;
  --control-border: rgba(87,45,75,0.16);
  /* La tarjeta es clara en ambos temas → su marca debe ser la variante CLARA
     (ciruela pura), no la aclarada de :root, que sobre blanco no contrasta. */
  --brand:          #572d4b;
  --brand-dark:     #3f2036;
  --brand-rgb:      87, 45, 75;
  --brand-dark-rgb: 63, 32, 54;
  --brand-soft-bg:  rgba(87,45,75,0.10);
  --brand-soft-fg:  #572d4b;
}

html.light-mode .page-container { background: var(--bg); }

/* .app-header / .ph / .module-sidebar / .home-sidebar NO se overridean aquí:
   sus tokens --nav-* ya tienen valor propio por tema (ver bloque html.light-mode). */
html.light-mode .app-header {
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
}

html.light-mode .app-header::after {
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(var(--brand-rgb), 0.2) 25%,
    rgba(var(--brand-rgb), 0.28) 50%,
    rgba(var(--brand-rgb), 0.2) 75%,
    transparent 100%
  );
}

html.light-mode .app-title {
  color: var(--text-strong);
}

html.light-mode .home-container {
  background: var(--bg);
}


html.light-mode .home-sidebar-user {
  background: rgba(0, 0, 0, 0.03);
}

html.light-mode .home-sidebar-name {
  color: var(--text-strong);
}

html.light-mode .home-sidebar-role {
  color: var(--muted-2);
}

html.light-mode .home-sidebar-sep {
  background: rgba(0, 0, 0, 0.07);
}

html.light-mode .home-sidebar-item {
  color: var(--muted);
}

html.light-mode .home-sidebar-item:hover:not(.home-sidebar-item--maint) {
  background: rgba(var(--brand-rgb), 0.07);
  color: var(--brand-dark);
}

html.light-mode .home-welcome-title {
  color: var(--text-strong);
}

html.light-mode .home-welcome-sub {
  color: var(--muted);
}

/* Dentro del banner el saludo va SOBRE una imagen o un degradado de marca, no
   sobre el fondo del panel: sigue siendo blanco también en tema claro. Hay que
   repetirlo aquí con el prefijo `html.light-mode` porque las dos reglas de
   arriba son más específicas que `.home-banner--overlay .home-welcome-title` y
   pintaban el texto oscuro sobre el banner oscuro. */
html.light-mode .home-banner--overlay .home-welcome-title,
html.light-mode .home-banner--overlay .home-welcome-sub {
  color: #fff;
}

html.light-mode .mod-card {
  background: var(--surface);
  border-color: rgba(226, 232, 240, 0.8);
  box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 16px rgba(0,0,0,0.04);
}
html.light-mode .mod-name { color: var(--text-strong); }

html.light-mode .ph {
  backdrop-filter: blur(16px);
}
html.light-mode .ph-title  { color: var(--text-strong); }
html.light-mode .ph-meta   { color: var(--muted); }
html.light-mode .ph-icon   { color: var(--brand); }
html.light-mode .ph-divider { background: rgba(0,0,0,0.1); }

html.light-mode .ph-back {
  color: var(--muted);
  border-color: rgba(0,0,0,0.08);
  background: rgba(0,0,0,0.03);
}
html.light-mode .ph-back:hover {
  color: var(--text-strong);
  background: rgba(0,0,0,0.07);
}

/* Tema y chip de usuario: misma superficie que la campana (--control-bg = #ffffff
   en claro). Antes usaban un negro translúcido que sobre el header se leía sucio. */
html.light-mode .ph-theme-btn,
html.light-mode .ph-user-chip {
  background: var(--control-bg);
  border-color: var(--control-border);
  color: var(--text);
}
html.light-mode .ph-theme-btn:hover,
html.light-mode .ph-user-chip:hover {
  background: var(--control-bg-hover);
  border-color: var(--control-border);
}

/* Salir sigue siendo rojo en claro (igual que en oscuro), solo se oscurece el
   tinte para que contraste sobre fondo blanco. */
html.light-mode .ph-logout {
  background: rgba(239, 68, 68, 0.08);
  border-color: rgba(239, 68, 68, 0.28);
  color: #dc2626;
}
html.light-mode .ph-logout:hover {
  background: rgba(239, 68, 68, 0.16);
  border-color: rgba(239, 68, 68, 0.42);
  color: #b91c1c;
}

html.light-mode .ph-switcher-btn {
  background: rgba(0,0,0,0.04);
  color: var(--text-strong);
  border-color: rgba(0,0,0,0.1);
}
html.light-mode .ph-switcher-btn:hover {
  background: rgba(0,0,0,0.07);
}
html.light-mode .ph-dropdown {
  background: var(--surface);
  border: 1px solid rgba(0,0,0,0.1);
  box-shadow: 0 8px 32px rgba(0,0,0,0.12);
}
html.light-mode .ph-dropdown-label { color: var(--muted); }
html.light-mode .ph-mod-item {
  background: rgba(0,0,0,0.02);
  color: var(--text-strong);
  border-color: rgba(0,0,0,0.06);
}
html.light-mode .ph-mod-item:hover:not(.ph-mod-item--maint) {
  background: rgba(var(--brand-rgb),0.08);
  color: var(--brand);
}

/* (la superficie del chip se define arriba, junto a la del botón de tema) */
html.light-mode .ph-user-name  { color: var(--text-strong); }
html.light-mode .ph-user-role  { color: var(--muted); }
html.light-mode .ph-user-avatar {
  background: rgba(var(--brand-rgb),0.15);
  color: var(--brand);
}

html.light-mode .ticket-panel,
html.light-mode .camp-content,
html.light-mode .users-table-wrap,
html.light-mode .perms-table,
html.light-mode .control-form-panel,
html.light-mode .tools-card,
html.light-mode .table-wrapper,
html.light-mode .contact-detail-panel {
  background: var(--panel) !important;
  border-color: rgba(0,0,0,0.08) !important;
  box-shadow: 0 2px 16px rgba(0,0,0,0.06);
}

/* SEGMENTED en tema claro: misma bandeja para todas las barras de tabs. Si añades
   una barra nueva al bloque canónico, súmala también aquí. */
html.light-mode .ticket-tabs,
html.light-mode .camp-tabs,
html.light-mode .metrics-tabs,
html.light-mode .metrics-agent-tabs,
html.light-mode .notif-tabs,
html.light-mode .mir-tabs,
html.light-mode .mir-dtabs,
html.light-mode .control-tabs {
  background: var(--panel);
  border-color: rgba(0,0,0,0.08);
}
html.light-mode .ticket-tabs button,
html.light-mode .camp-tab,
html.light-mode .metrics-tab,
html.light-mode .metrics-subtab,
html.light-mode .notif-tabs button,
html.light-mode .mir-tab,
html.light-mode .mir-dtab,
html.light-mode .control-tab { color: var(--muted); }

/* :not(...-active) obligatorio: sin él, pasar el ratón por el tab ACTIVO le pone
   texto oscuro encima del pill sólido de marca → texto invisible en claro. */
html.light-mode .ticket-tabs button:hover:not(.active),
html.light-mode .camp-tab:hover:not(.camp-tab--active),
html.light-mode .metrics-tab:hover:not(.active),
html.light-mode .metrics-subtab:hover:not(.active),
html.light-mode .notif-tabs button:hover:not(.active),
html.light-mode .control-tab:hover:not(.active) {
  background: rgba(0,0,0,0.04);
  color: var(--text-strong);
}
/* La pill activa va SÓLIDA de marca también en claro (antes usaba el tinte
   translúcido --brand-soft-bg y se leía como "sin seleccionar"). Mismo aspecto
   que el resto de barras segmented del panel. */
html.light-mode .control-tab.active {
  background: var(--brand-dark) !important;
  color: #fff !important;
}

html.light-mode .contacts-table th {
  background: rgba(0,0,0,0.04);
  color: var(--muted);
  border-bottom-color: rgba(0,0,0,0.09);
}
html.light-mode .contacts-table td {
  color: var(--text-strong);
  border-bottom-color: rgba(0,0,0,0.05);
}
html.light-mode .contacts-table tr:hover td {
  background: rgba(var(--brand-rgb),0.04);
}
html.light-mode .contact-row:hover td {
  background: var(--brand-soft-bg);
}

html.light-mode .users-table th {
  background: rgba(0,0,0,0.04);
  color: var(--muted);
  border-bottom-color: rgba(0,0,0,0.09);
}
html.light-mode .users-table td {
  color: var(--text-strong);
  border-bottom-color: rgba(0,0,0,0.05);
}
html.light-mode .users-table tr:hover td {
  background: rgba(var(--brand-rgb),0.04);
}

html.light-mode .camp-table td {
  border-bottom-color: rgba(0,0,0,0.05);
}
html.light-mode .camp-table tr:hover td {
  background: rgba(var(--brand-rgb),0.04);
}

html.light-mode .perms-table th {
  background: rgba(0,0,0,0.04);
  color: var(--muted);
}
html.light-mode .perms-table td {
  border-bottom-color: rgba(0,0,0,0.05);
}
html.light-mode .perms-table tr:hover td {
  background: rgba(var(--brand-rgb),0.04);
}

html.light-mode input[type="text"],
html.light-mode input[type="email"],
html.light-mode input[type="password"],
html.light-mode input[type="number"],
html.light-mode input[type="search"],
html.light-mode input[type="tel"],
html.light-mode input[type="url"],
html.light-mode select,
html.light-mode textarea {
  background: var(--panel) !important;
  border-color: rgba(0,0,0,0.12) !important;
  color: var(--text-strong) !important;
}
html.light-mode select option {
  background: var(--surface);
  color: var(--text-strong);
}
html.light-mode input:focus,
html.light-mode select:focus,
html.light-mode textarea:focus {
  border-color: rgba(var(--brand-rgb),0.5) !important;
  background: var(--surface) !important;
}
html.light-mode input::placeholder,
html.light-mode textarea::placeholder {
  color: rgba(71,85,105,0.5) !important;
}

html.light-mode .filter-bar,
html.light-mode .contacts-filter-bar {
  background: var(--panel);
  border-color: rgba(0,0,0,0.08);
}

html.light-mode .section-title,
html.light-mode .contacts-title,
html.light-mode .users-title,
html.light-mode .control-title,
html.light-mode .panel-title,
html.light-mode .ticket-section-title { color: var(--text-strong); }

html.light-mode label               { color: var(--muted) !important; }
html.light-mode .form-section-title { color: var(--text-strong) !important; }

html.light-mode .home-card,
html.light-mode .mod-card {
  background: var(--panel);
  border-color: rgba(0,0,0,0.08);
  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
}
html.light-mode .home-card:hover,
html.light-mode .mod-card:hover {
  background: var(--surface);
  box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
html.light-mode .mod-card-title,
html.light-mode .home-card-title { color: var(--text-strong); }
html.light-mode .mod-card-desc,
html.light-mode .home-card-desc  { color: var(--muted); }

html.light-mode ::-webkit-scrollbar-track { background: rgba(0,0,0,0.04); }
html.light-mode ::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); }
html.light-mode ::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.25); }

/* ── Light-mode overrides for newly darkened module elements ── */
html.light-mode .page-container         { background: var(--surface-2); }
html.light-mode .livechat-layout        { background: var(--surface-2); }
html.light-mode .livechat-messages      { background: var(--surface); }
html.light-mode .livechat-empty         { background: var(--surface-2); }
html.light-mode .messages-header        { background: var(--surface); }
html.light-mode .livechat-contact-panel { background: var(--surface-2); }
html.light-mode .reply-box              { background: var(--surface); border-top-color: var(--text); }
html.light-mode .reply-textarea         { background: var(--surface-2); border-color: var(--control-border); color: var(--text-strong); }
html.light-mode .reply-textarea::placeholder { color: var(--muted-2); }
html.light-mode .reply-textarea:focus   { background: var(--surface); border-color: var(--brand-dark); }
html.light-mode .messages-list          { background: var(--surface-2); }
html.light-mode .livechat-sidebar       { background: var(--surface); border-right-color: var(--text); }
html.light-mode .livechat-filters       { border-bottom-color: var(--text); }
html.light-mode .lc-topbar              { border-bottom-color: var(--text); }
html.light-mode .livechat-filters select,
html.light-mode .lc-filters-popover select { background: var(--surface); border-color: var(--border); color: var(--text); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); }
html.light-mode .livechat-filters select option,
html.light-mode .lc-filters-popover select option { background: var(--surface); }
html.light-mode .livechat-filters select:focus,
html.light-mode .lc-filters-popover select:focus  { border-color: var(--brand); background-color: var(--surface); }
html.light-mode .filter-pill            { background: var(--surface-2); border-color: var(--border); color: var(--muted); }
html.light-mode .filter-pill:hover      { background: var(--surface-2); color: var(--text); }
html.light-mode .filter-pill--active    { background: rgba(var(--brand-rgb),0.1) !important; border-color: rgba(var(--brand-rgb),0.35) !important; color: var(--brand-dark) !important; }
html.light-mode .conv-item:hover        { background: var(--surface-2); }
html.light-mode .conv-item--selected    { background: rgba(var(--brand-rgb),0.08) !important; border-color: rgba(var(--brand-rgb),0.25); }
html.light-mode .conv-title             { color: var(--text-strong); }
html.light-mode .conv-last-msg          { color: var(--muted); }
html.light-mode .conv-time              { color: var(--muted-2); }
html.light-mode .ticket-summary-card    { background: var(--surface); }
html.light-mode .contacts-channel-bar   { background: var(--surface); }
html.light-mode .contacts-subbar        { background: var(--surface); }
html.light-mode .contacts-filters-popover .contacts-channel-bar { background: transparent; }
html.light-mode .ch-chip                { background: var(--surface-2); }
html.light-mode .ch-chip:hover          { background: var(--surface-2); border-color: var(--muted-2); }
html.light-mode .metrics-kpi-card       { background: var(--surface); }
html.light-mode .metrics-kpi-value      { color: var(--text-strong); }
html.light-mode .metrics-chart-card     { background: var(--surface); }
html.light-mode .metrics-chart-title    { color: var(--text-strong); }
html.light-mode .contact-card           { background: var(--surface); }
html.light-mode .monitor-health-card    { background: var(--surface); }
html.light-mode .qa-card                { background: var(--surface); box-shadow: 0 1px 4px rgba(0,0,0,0.04); }
html.light-mode .qa-btn                 { background: var(--surface); }
html.light-mode .qa-btn:hover:not(:disabled) { background: var(--surface-2); }
html.light-mode .profile-page           { background: var(--surface-2); }
html.light-mode .search-input           { background: var(--surface); }

/* ── Phase-2: theme-adaptive overrides (tabs, ticket panel, pastel tags) ── */
/* .metrics-tabs / .metrics-tab: ver el grupo SEGMENTED de tema claro más arriba.
   Estaban duplicados aquí y, al ir después, se comían la bandeja unificada. */

html.light-mode .ticket-panel h3        { color: var(--text-strong); border-bottom-color: rgba(0,0,0,0.07); }
html.light-mode .ticket-panel-header    { border-bottom-color: rgba(0,0,0,0.07); }
html.light-mode .tickets-table td.tk-ref      { color: var(--muted); }
html.light-mode .tickets-table td.ticket-created { color: var(--muted); }
html.light-mode .ticket-toolbar button,
html.light-mode .ticket-toolbar-actions button { background: var(--surface); color: var(--text-strong); }

html.light-mode .badge-green  { background: var(--badge-success-bg); color: var(--badge-success-fg); }
html.light-mode .badge-yellow { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
html.light-mode .badge-red    { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
html.light-mode .badge-blue   { background: var(--badge-info-bg); color: var(--badge-info-fg); }
html.light-mode .badge-gray   { background: var(--surface-2); color: var(--muted); }
html.light-mode .badge-type   { background: var(--badge-purple-bg); color: var(--badge-purple-fg); }
html.light-mode .chip-active  { background: var(--badge-success-bg); color: var(--badge-success-fg); }
html.light-mode .chip-off     { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }

/* Controles translúcidos (diseñados sobre oscuro) que se camuflan en claro */
html.light-mode .btn-secondary {
  background: var(--surface);
  border-color: rgba(0,0,0,0.12);
  color: var(--text-strong);
}
html.light-mode .btn-secondary:hover:not(:disabled) { background: var(--surface-2); }
html.light-mode .metrics-refresh-btn {
  background: var(--surface);
  border-color: rgba(0,0,0,0.12);
  color: var(--text-strong);
}
html.light-mode .metrics-refresh-btn:hover { background: var(--surface-2); }
/* .contacts-refresh-btn y .contacts-search-input ya no necesitan excepción: la
   toolbar estándar se define con tokens. Y un `background` en corto aquí
   borraría la lupa del buscador, que va como background-image. */
html.light-mode .metrics-period-select {
  background-color: var(--surface);
  border-color: rgba(0,0,0,0.12);
  color: var(--text-strong);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%231e293b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
html.light-mode .kn-tipo-card           { background: var(--surface); border-color: var(--border); }
html.light-mode .kn-tipo-card:hover     { background: var(--surface-2); border-color: var(--brand); }
html.light-mode .kn-tipo-card.selected  { background: var(--surface-2); border-color: var(--brand); }
html.light-mode .camp-preview-bubble    { background: var(--surface); color: var(--text-strong); border-color: var(--border); }

/* ── InfoTooltip component ───────────────────────────────────────────────── */
.info-tooltip-wrapper {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 4px;
  vertical-align: middle;
  overflow: visible;
  outline: none;
}

.info-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  /* Neutro: hereda el color del contexto (texto de la tab). Sobre la tab activa
     (fondo de marca, texto blanco) el ícono se vuelve blanco → visible; sobre las
     inactivas usa el color atenuado. Adapta a claro/oscuro sin color de marca fijo. */
  background: color-mix(in srgb, currentColor 16%, transparent);
  color: inherit;
  opacity: 0.85;
  font-size: 10px;
  font-style: normal;
  cursor: default;
  user-select: none;
  line-height: 1;
}

.info-tooltip-text {
  visibility: hidden;
  opacity: 0;
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--tooltip-bg);
  color: var(--tooltip-fg);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.45;
  /* El globo es una FRASE, no una etiqueta: hereda del título donde se ancla, y
     esos títulos van en versalitas espaciadas (`.idv-field-label` y compañía).
     Sin este reseteo el texto de ayuda salía en mayúsculas: parecía un grito. */
  text-transform: none;
  letter-spacing: normal;
  padding: 9px 11px;
  border-radius: 8px;
  width: max-content;
  max-width: min(280px, calc(100vw - 32px));
  white-space: normal;
  z-index: 1200;
  pointer-events: none;
  transition: opacity 0.15s ease;
  box-shadow: var(--shadow-md);
}

.info-tooltip-text::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translateX(-50%);
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-bottom: 6px solid var(--tooltip-bg);
}

.info-tooltip-wrapper:hover .info-tooltip-text,
.info-tooltip-wrapper:focus .info-tooltip-text,
.info-tooltip-wrapper:focus-within .info-tooltip-text {
  visibility: visible;
  opacity: 1;
}

/* Tooltips de las tabs del Centro de Control (#8): la barra tiene scroll horizontal
   (recorta) y está volteada por transform (#1) → un tooltip descendiente NUNCA puede
   escapar por CSS. Se oculta el tooltip in-DOM y un script (index.html) pinta uno
   flotante `.cc-tab-tip` colgado de <body> con posición fixed, inmune a ambos. */
.control-tabs .info-tooltip-text { display: none; }

.cc-tab-tip {
  position: fixed;
  z-index: 2000;
  background: var(--tooltip-bg);
  color: var(--tooltip-fg);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.45;
  padding: 9px 11px;
  border-radius: 8px;
  max-width: min(280px, calc(100vw - 32px));
  white-space: normal;
  box-shadow: var(--shadow-md);
  pointer-events: none;
  opacity: 0;
  transform: translateX(-50%);
  transition: opacity 0.15s ease;
}
.cc-tab-tip.is-visible { opacity: 1; }
.cc-tab-tip::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translateX(-50%);
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-bottom: 6px solid var(--tooltip-bg);
}

/* ── Módulo Gestión de Pagos ───────────────────────────────────────────────── */
.pagos-page .page-content { background: var(--bg); max-width: 1440px; }
.pg-header { margin-bottom: 18px; }
.pg-title { font-size: 1.4rem; font-weight: 700; color: var(--text); margin: 10px 0 2px; }
.pg-subtitle { color: var(--muted); font-size: 0.9rem; margin: 0; }
.pg-banner { background: var(--badge-info-bg); border: 1px solid var(--badge-info-fg); color: var(--badge-info-fg); padding: 10px 14px; border-radius: 8px; margin-bottom: 14px; font-size: 0.88rem; }

.pg-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 12px; margin-bottom: 18px; }
.pg-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px 18px; box-shadow: var(--shadow-sm); }
.pg-card-n { font-size: 1.6rem; font-weight: 700; color: var(--text); line-height: 1.1; }
.pg-card-l { font-size: 0.78rem; color: var(--muted); margin-top: 4px; }
.pg-card--ok { border-color: rgba(16,185,129,0.45); background: var(--badge-success-bg); }

/* Pagos: KPIs de estado (clicables, filtran la tabla) separados por una divisoria
   del KPI de monto validado, que es solo lectura y va a la derecha. */
.pagos-page .pg-kpis { display: flex; align-items: stretch; gap: 14px; margin-bottom: 18px; flex-wrap: wrap; }
.pagos-page .pg-kpis > .pg-cards { flex: 1 1 480px; margin-bottom: 0; }
.pagos-page .pg-kpi-sep { flex: 0 0 1px; align-self: stretch; background: var(--border); }
.pagos-page .pg-kpi-total { flex: 0 0 auto; min-width: 190px; display: flex; flex-direction: column; justify-content: center; }
.pagos-page .pg-kpi-total-n { font-size: 1rem; }
/* Al envolver, la divisoria quedaría suelta como una línea de 1px: se oculta. */
@media (max-width: 720px) { .pagos-page .pg-kpi-sep { display: none; } }

.pg-input { padding: 8px 10px; border: 1px solid var(--control-border); border-radius: 8px; font-family: inherit; font-size: 0.85rem; background: var(--control-bg); color: var(--text); min-width: 140px; flex: 1 1 auto; }
.pg-input:focus { outline: 2px solid var(--brand-soft-fg); outline-offset: -1px; }

.pg-btn { padding: 7px 13px; border-radius: 8px; border: 1px solid var(--control-border); background: var(--control-bg); color: var(--text); font-size: 0.83rem; font-weight: 600; font-family: inherit; cursor: pointer; transition: all 0.15s; }
.pg-btn:hover:not(:disabled) { background: var(--control-bg-hover); }
/* Deshabilitado: se ve bloqueado sin necesidad de texto que lo explique. */
.pg-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.pg-btn--primary { background: var(--brand); color: #fff; border-color: var(--brand); }
.pg-btn--primary:hover:not(:disabled) { background: var(--brand-dark); }
.pg-btn--danger { color: var(--badge-danger-fg); border-color: rgba(239,68,68,0.45); }
.pg-btn--danger:hover:not(:disabled) { background: var(--badge-danger-bg); }
.pg-btn--sm { padding: 4px 9px; font-size: 0.76rem; }

/* overflow-x:auto → en pantallas angostas la tabla (grid de ancho fijo) hace scroll
   horizontal DENTRO de su tarjeta en vez de recortar la columna de acciones. */
.pg-table { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; overflow-x: auto; overflow-y: hidden; box-shadow: var(--shadow-sm); }
.pg-row { display: grid; grid-template-columns: 92px 1.2fr 110px 1.4fr 95px 1fr 110px 56px 272px; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--border); font-size: 0.82rem; color: var(--text); align-items: center; min-width: 900px; }
.pg-row:last-child { border-bottom: none; }
.pg-row > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pg-row--head { font-weight: 700; background: var(--surface-2); color: var(--muted); text-transform: uppercase; font-size: 0.68rem; letter-spacing: 0.03em; }
/* Pagos: 8 columnas (sin la de SAE) y celda de acciones para 2 botones. */
.pagos-page .pg-row { grid-template-columns: 92px 1.2fr 110px 1.4fr 95px 1fr 110px 180px; min-width: 820px; }
/* El modo selección ya NO añade columna: se marca haciendo clic en la fila, como
   en Contactos y Campañas. La columna de casillas era de este módulo solo. */
/* La fila entera abre el detalle (la celda de acciones corta la propagación). */
.pagos-page .pg-row--click { cursor: pointer; }
.pagos-page .pg-row--click:hover { background: var(--control-bg-hover); }
/* E-commerce: tablas con columnas parejas (nº de columnas varía por tab, así que
   no se usa la grid fija de .pg-row global). Checkbox inline en el toolbar. */
.ecommerce-page .pg-row { display: flex; grid-template-columns: none; min-width: 720px; }
.ecommerce-page .pg-row > span { flex: 1 1 0; }
/* Preview de imagen dentro del drawer de producto (pane Imagen). */
.ec-img-preview { margin-top: 8px; max-width: 180px; max-height: 140px; border-radius: 8px; border: 1px solid var(--border); object-fit: cover; }

/* Título de un toggle (mir-switch-row) con su tooltip a la derecha. */
.ec-switch-title { display: inline-flex; align-items: center; gap: 6px; }

/* Bloque de imagen al inicio de la tab Datos: etiqueta + dropzone + preview apilados. */
.ec-img-block { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
/* Dropzone de imagen: borde punteado, hover con color de marca y leve escala. */
.ec-dropzone { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 6px; padding: 26px 18px; text-align: center; cursor: pointer;
    border: 2px dashed var(--border); border-radius: 12px; background: var(--surface-2);
    color: var(--muted); transition: border-color .15s ease, background .15s ease, transform .15s ease, box-shadow .15s ease; }
.ec-dropzone:hover { border-color: var(--brand); color: var(--text); background: var(--brand-soft-bg);
    transform: scale(1.02); box-shadow: var(--shadow-sm); }
.ec-dropzone.is-drag { border-color: var(--brand); background: var(--brand-soft-bg); color: var(--text); transform: scale(1.03); }
.ec-dropzone-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.ec-dropzone-ic { width: 34px; height: 34px; color: var(--brand); }
.ec-dropzone-text { font-weight: 600; font-size: 0.9rem; }
.ec-dropzone-hint { font-size: 0.78rem; color: var(--muted-2); }

/* Miniatura en la celda de producto (config mostrar_miniaturas). */
.ec-prod-cell { display: inline-flex; align-items: center; gap: 8px; }
.ec-thumb { width: 30px; height: 30px; border-radius: 6px; object-fit: cover; border: 1px solid var(--border); flex: 0 0 auto; }
/* Hueco invisible del mismo tamaño que la miniatura: reserva el espacio para que
   todas las filas midan igual, pero SIN dibujar un cuadro (sin fondo ni borde). */
.ec-thumb--empty { background: transparent; border-color: transparent; }
/* Integración MIR (EM1): badge de producto externo, hint y card del enlace. */
.ec-badge { display: inline-flex; align-items: center; padding: 1px 7px; border-radius: 999px;
    font-size: 11px; font-weight: 600; line-height: 1.6; }
.ec-badge--ext { background: var(--badge-purple-bg); color: var(--badge-purple-fg); }
.ec-badge--ok  { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.ec-badge--warn { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.ec-badge--danger { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.ec-hint { color: var(--muted); font-size: 12px; margin: 6px 0; }
.ec-link-card { display: flex; flex-direction: column; gap: 6px; padding: 12px;
    border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2);
    grid-column: 1 / -1; }
.ec-link-card .pg-btn { align-self: flex-start; }
/* Tarjeta de estado de integración (Config › Integraciones, handshake §5.4). */
.ec-integ-card .ec-integ-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ec-resv-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }

/* Editor de items al crear un pedido. */
.ec-items-editor { margin-top: 12px; }
.ec-items-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; gap: 8px; }
.ec-item-del { color: var(--badge-danger-fg); }

/* Acción destructiva (botón de borrado en tarjetas y filas). */
.ec-danger { color: var(--badge-danger-fg); }
/* Detalle de pedido (drawer): tabla de items, totales, acciones y datos read-only. */
.ec-items { margin-bottom: 14px; }
/* E8-pesaje: input de cantidad real por item + acciones. */
/* `min-width: 0` es obligatorio: `.pg-input` trae `min-width: 140px`, más de lo que le
   toca a la celda en la fila, así que el campo se salía por encima de «Subtotal». */
.ec-real-input {
  width: 100%; min-width: 0; max-width: 100%; flex: 1 1 auto;
  box-sizing: border-box; padding: 2px 6px; font-size: 0.82rem;
}
/* La celda del peso necesita algo más de sitio que las de texto (lleva campo + unidad). */
.ecommerce-page .ec-items .pg-row > span:has(.ec-real-input) { flex: 1.3 1 0; min-width: 0; }
/* El input y su unidad, en línea: el peso se teclea sabiendo si son kilos o gramos
   (el placeholder que lo decía desaparece justo al empezar a escribir). */
.ec-weigh-cell { display: flex; align-items: center; gap: 5px; min-width: 0; width: 100%; }
.ec-unit-suffix { font-size: 0.75rem; color: var(--muted); white-space: nowrap; flex: 0 0 auto; }
/* Celda de dos líneas: el valor y, debajo, de dónde sale (la conversión estimada). */
.ec-cell-2l { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ec-cell-note { font-size: 0.7rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Semáforo de pesaje: SOLO el campo «Real» se tiñe (no la fila entera). Rojo =
   falta pesar, amarillo = escrito pero sin guardar. Si ya tiene peso y no hay
   cambios, sin marca (estado normal). El color acompaña al propio valor del campo. */
.ec-items .ec-weigh-cell.ec-weigh-pend  .ec-real-input {
    border-color: var(--badge-danger-fg); background: var(--badge-danger-bg);
}
.ec-items .ec-weigh-cell.ec-weigh-dirty .ec-real-input {
    border-color: var(--badge-warn-fg); background: var(--badge-warn-bg);
}

/* Botón con cambios pendientes: se tiñe de aviso para que "hay algo sin guardar"
   se vea sin leer. Apagado = no hay diferencias respecto a lo guardado. */
.ec-btn-dirty {
  background: var(--badge-warn-bg) !important;
  border-color: var(--badge-warn-fg) !important;
  color: var(--badge-warn-fg) !important;
  font-weight: 600;
}
/* Acción destructiva/irreversible (cerrar como pagado, cancelar pedido). */
.ec-btn-danger {
  background: var(--danger); border-color: var(--danger); color: #fff; font-weight: 600;
}
.ec-btn-danger:hover:not(:disabled) { filter: brightness(1.08); }
/* Acción de avance (listo para cobrar). */
.ec-btn-ok {
  background: var(--success); border-color: var(--success); color: #fff; font-weight: 600;
}
.ec-btn-ok:hover:not(:disabled) { filter: brightness(1.08); }
/* Iconos de acción coloreados en la fila (listo=verde, cancelar=rojo). Solo tiñen el
   icono; el fondo lo deja el .ec-icon-btn. Deshabilitados quedan en gris por :disabled. */
.ec-ic-ok:not(:disabled)     { color: var(--badge-success-fg); }
.ec-ic-danger:not(:disabled) { color: var(--badge-danger-fg); }

/* Cifras con signo: verde si sube, rojo si baja. */
.ec-num-up   { color: var(--badge-success-fg); }
.ec-num-down { color: var(--badge-danger-fg); }

/* Historial: fecha y hora en columnas propias y estrechas (el resto se reparte). */
.ec-hist .pg-row > span:nth-child(1) { flex: 0 0 104px; }
.ec-hist .pg-row > span:nth-child(2) { flex: 0 0 64px; }

/* Capa de cierre de popovers que viven DENTRO de un modal: `mir-pop-dismiss` (19)
   queda por debajo del backdrop del modal (100) y nunca recibía el clic. */
.ec-pop-dismiss { position: fixed; inset: 0; z-index: 2099; background: transparent; border: none; padding: 0; }

/* Confirmación sobre el detalle: no reemplaza al modal, se apila encima. */
.ec-confirm-backdrop {
  position: fixed; inset: 0; z-index: 2200; display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(15,23,42,0.55); backdrop-filter: blur(3px);
}
.ec-confirm-card {
  width: min(440px, 100%); max-height: calc(100vh - 32px); overflow-y: auto; box-sizing: border-box;
  display: flex; flex-direction: column; gap: 12px; padding: 20px;
  background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: var(--shadow-lg);
}
.ec-confirm-title { margin: 0; font-size: 1rem; color: var(--text-strong); }
.ec-confirm-card .ec-totals { margin: 0; }
.ec-confirm-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* Caja de mensaje al cliente dentro del modal de confirmación. */
.ec-confirm-msg-label { display: flex; flex-direction: column; gap: 6px; font-size: 0.8rem; color: var(--muted); }
.ec-confirm-msg-label textarea { resize: vertical; }

/* Footer del detalle con las acciones de flujo: resumen de asignados a la izquierda,
   botones a la derecha. Envuelve en pantallas chicas. */
.ec-order-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ec-actions-spacer { flex: 1 1 auto; }
.ec-assignees { font-size: 0.8rem; color: var(--muted); font-weight: 600; white-space: nowrap; }
/* Tab Responsables (detalle de pedido): lista de operadores que tomaron el pedido. */
.ec-resp-pane { display: flex; flex-direction: column; gap: 8px; padding-top: 6px; }
.ec-resp-list { display: flex; flex-direction: column; gap: 8px; }
.ec-resp-item { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px; }
.ec-resp-name { font-weight: 700; color: var(--text-strong); }
.ec-resp-when { font-size: 0.76rem; color: var(--muted); }
/* Shell del modal de detalle mientras carga (aparece al instante, sin esperar el fetch). */
.ec-detail-loading { display: flex; align-items: center; justify-content: center;
    min-height: 200px; color: var(--muted); font-size: 0.9rem; }
/* Card del detalle YA cargado: no re-anima al reemplazar al shell (evita el efecto de
   "cerrar y abrir otro modal"). El shell sí anima la apertura inicial. */
.ecommerce-page .modal-card.ec-detail-loaded { animation: none; }
/* Aviso de que las notas van al cliente (bajo el textarea correspondiente). */
.ec-cli-note-hint { font-size: 0.72rem; color: var(--brand); margin-top: 2px; }
/* Bloque de pesaje: cabecera con botón "Guardar pesos" a la derecha (coherente con notas). */
.ec-weigh-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
/* Cierre forzado: aparece solo si la diferencia supera la tolerancia (aviso + botón). */
.ec-settle-force { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  margin-top: 8px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); }
.ec-totals { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 16px; padding: 12px 14px; background: var(--surface-2); border-radius: 10px; }
.ec-totals > div { display: flex; justify-content: space-between; font-size: 0.85rem; color: var(--text); }
.ec-totals span { color: var(--muted); }
/* Detalle de impuesto por producto (tab Pedido): tag "IVA X%" / "Exento" junto al subtotal. */
.ec-sub-cell { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ec-tax-tag {
  font-size: 0.68rem; font-weight: 600; line-height: 1; padding: 2px 6px; border-radius: 999px;
  background: var(--badge-info-bg); color: var(--badge-info-fg); white-space: nowrap;
}
.ec-tax-tag.ec-tax-exento { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }
/* Marcador "+imp" en la columna Monto de la tabla de pedidos: el total ya trae impuesto;
   el hover (title) muestra el desglose. Discreto para no competir con la cifra. */
.ec-monto-tax { cursor: help; }
.ec-tax-mark {
  font-size: 0.6rem; font-weight: 600; color: var(--badge-info-fg);
  background: var(--badge-info-bg); border-radius: 4px; padding: 0 3px;
  margin-left: 3px; vertical-align: super; white-space: nowrap;
}
/* Montos (tab Pedido) v2: desglose chico arriba, TOTAL destacado, nota al pie. */
.ec-totals2 { margin: 4px 0 16px; padding: 12px 14px; background: var(--surface-2); border-radius: 10px; }
.ec-totals-break { display: flex; flex-direction: column; gap: 5px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
.ec-totals-break > div { display: flex; justify-content: space-between; font-size: 0.82rem; }
.ec-totals-break span { color: var(--muted); }
.ec-totals-break b { color: var(--text); font-weight: 600; }
/* Total a cobrar: el número protagonista. */
.ec-total-hero { margin-top: 8px; }
.ec-total-line { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.ec-total-line span { color: var(--muted); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }
.ec-total-line strong { color: var(--text-strong); font-size: 1.6rem; font-weight: 800; line-height: 1.1; }
.ec-total-bs { text-align: right; color: var(--muted); font-size: 0.85rem; margin-top: 2px; }
/* Nota al pie: estimado + diferencia (contexto del pesaje). Color heredado del contenedor
   para que `.ec-num-up/.ec-num-down` gane en la diferencia (no hay selector `span`). */
.ec-totals-foot {
  display: flex; gap: 6px; flex-wrap: wrap; align-items: center;
  border-top: 1px dashed var(--border); margin-top: 8px; padding-top: 6px;
  font-size: 0.78rem; color: var(--muted);
}
.ec-totals-foot .ec-foot-sep { opacity: 0.5; }
/* Equivalente en Bs (E8a): tasa de rates_service, separado de los totales base. */
.ec-bs-row { border-top: 1px dashed var(--border); margin-top: 2px; padding-top: 6px; }
/* `--accent` solo está definido en `:root` (tema oscuro) y vale #111827 — casi negro.
   Como no se redefine en `html.light-mode`, en OSCURO el equivalente en Bs quedaba
   negro sobre fondo oscuro, ilegible. Se usa el token de texto fuerte, que sí
   cambia con el tema. */
.ec-bs-row strong { color: var(--text-strong); }
.ec-bs { display: block; font-size: 0.78rem; color: var(--muted); margin-top: 2px; }
/* Cobro (E8b): cotización + pagos + cierre en el detalle del pedido. */
.ec-cobro { display: flex; flex-direction: column; gap: 12px; padding: 4px 0; }
.ec-cobro-head { display: flex; gap: 16px; flex-wrap: wrap; padding: 10px 12px; background: var(--surface-2); border-radius: 10px; }
.ec-cobro-head > div { display: flex; flex-direction: column; }
.ec-cobro-head span { color: var(--muted); font-size: 0.78rem; }
.ec-cobro-cov { font-size: 0.9rem; color: var(--text); }
.ec-cobro-note { font-size: 0.75rem; color: var(--muted); margin-top: -2px; }
/* Misma razón que `.ec-bs-row strong`: `--accent` es casi negro y no cambia con el
   tema. Aquí además el par vigente/vencido se lee mejor como verde/rojo. */
.ec-credit { color: var(--badge-success-fg); font-weight: 600; }
.ec-loss { color: var(--badge-danger-fg); font-weight: 600; }
.ec-muted { color: var(--muted); }
/* Columna "#" angosta en la tabla de cotizaciones; número legible por pedido. */
.ec-pays-quotes .pg-row > span:first-child { flex: 0 0 44px; }
.ec-quote-seq { font-weight: 700; color: var(--text-strong); }
/* Referencia de cotización aplicada a un pago (columna Cotización). */
.ec-quote-ref { color: var(--brand); font-weight: 600; }
/* Cabecera de sub-sección de cobro (Pagos / Cotizaciones) con acciones a la derecha. */
.ec-pays-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; margin: 10px 0 4px; }
.ec-pays-actions { display: flex; gap: 8px; flex-shrink: 0; }
.ec-inline-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px; }
/* Notas del operador (detalle pedido): botón Guardar en la esquina superior derecha. */
.ec-notes-block { display: flex; flex-direction: column; gap: 6px; }
.ec-notes-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
/* Notas del operador (ancho completo) y, debajo, el cambio de estado. */
.ec-pedido-edit {
  display: flex; flex-direction: column; gap: 14px; margin-top: 4px;
}
.ec-estado-block { display: flex; flex-direction: column; gap: 8px; }
/* El título "Cambiar estado" va encima del desplegable, no a su lado. */
.ec-estado-block > label { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.ec-estado-block > label .pg-input { width: 100%; max-width: 320px; }
/* Bloque de notas con cambios sin guardar: el contenedor entero se tiñe (no solo el botón)
   para dar coherencia con el estado "amarillo = pendiente de guardar". */
.ec-notes-block.ec-notes-dirty {
  background: var(--badge-warn-bg); border: 1px solid var(--badge-warn-fg);
  border-radius: 10px; padding: 10px; margin: -1px;
}
/* Popover de cambio de estado rápido (tabla de pedidos): en Portal, position:fixed
   (escapa del overflow de la tabla). Se ancla a la derecha del botón vía `left` + transform. */
.ec-quick-pop {
  position: fixed; z-index: 2100; min-width: 190px; width: 210px; max-width: calc(100vw - 16px);
  max-height: calc(100vh - 16px); overflow-y: auto; overflow-x: hidden; box-sizing: border-box;
  background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow-lg); padding: 12px; display: flex; flex-direction: column; gap: 10px;
}
.ec-quick-pop label { display: flex; flex-direction: column; gap: 4px; font-size: 0.8rem; color: var(--muted); }
/* Calculadora del margen de pesaje (Config › Pedidos). La pantalla es una cuenta:
   estimado + margen = tope. Todo lo demás (rótulos, unidades, atajos) acompaña a esa
   línea; nada compite con ella. */
.ec-calc-pop { width: 340px; }
/* `.pg-input` trae `min-width: 140px` y `flex: 1 1 auto` (pensados para formularios
   anchos): tres campos en fila pedían 420 px y la ecuación se salía del popover. */
.ec-calc-pop .pg-input { min-width: 0; max-width: 100%; box-sizing: border-box; }
.ec-calc-title { font-size: 0.85rem; font-weight: 700; color: var(--text-strong); }
.ec-calc-hint { font-size: 0.76rem; color: var(--muted); line-height: 1.35; text-align: center; }
/* Clave-valor en una línea: la unidad se elige una vez y no forma parte de la cuenta. */
.ec-calc-kv { display: flex; align-items: center; gap: 8px; }
.ec-calc-kv-k { font-size: 0.78rem; color: var(--muted); white-space: nowrap; }
.ec-calc-kv-v { flex: 1; min-width: 0; padding: 5px 8px; font-size: 0.8rem; }
/* La ecuación. Los campos comparten ancho; los operadores solo ocupan lo suyo. */
.ec-calc-eq { display: flex; align-items: flex-start; gap: 5px; }
.ec-calc-field { display: flex; flex-direction: column; align-items: center; gap: 3px; flex: 1; min-width: 0; }
.ec-calc-cap { font-size: 0.68rem; color: var(--muted); white-space: nowrap; }
.ec-calc-box { position: relative; display: block; width: 100%; }
.ec-calc-box .pg-input { width: 100%; flex: none; padding: 6px 8px; text-align: center; font-size: 0.85rem; }
.ec-calc-pct { padding-right: 18px !important; }
.ec-calc-sfx {
  position: absolute; right: 7px; top: 50%; transform: translateY(-50%);
  pointer-events: none; font-size: 0.7rem; color: var(--muted);
}
/* La unidad va DEBAJO del campo, no dentro: dentro competía con el número. El alto
   mínimo mantiene alineadas las tres columnas aunque la del medio (%) no lleve nada. */
.ec-calc-u { font-size: 0.7rem; color: var(--muted); min-height: 0.9rem; }
.ec-calc-op { font-size: 0.9rem; font-weight: 700; color: var(--muted); flex: 0 0 auto; padding-top: 22px; }
/* Atajos: los escenarios típicos, ya resueltos. Texto, no botonera: son una ayuda para
   decidir, no la acción principal. Pulsar uno aplica ese margen. */
.ec-calc-ex { display: flex; justify-content: space-between; gap: 6px; }
.ec-calc-exbtn {
  flex: 1; padding: 3px 2px; border: none; background: none; cursor: pointer;
  font-family: inherit; font-size: 0.72rem; color: var(--muted); border-radius: 6px;
  white-space: nowrap;
}
.ec-calc-exbtn:hover { background: var(--control-bg-hover); color: var(--text); }
.ec-calc-exbtn.is-active { color: var(--brand); font-weight: 700; }
.ec-calc-foot { display: flex; justify-content: center; }
/* Campo de margen + botón «Calcular» en la misma línea. */
.ec-margen-row { display: flex; gap: 6px; align-items: center; }
.ec-margen-row .pg-input { flex: 1; min-width: 0; }

/* Detalle (modal) solo-lectura: grid de campos etiqueta/valor. */
.ec-view-img { display: block; width: 100%; max-width: 260px; max-height: 220px; object-fit: cover; border-radius: 12px; border: 1px solid var(--border); margin: 4px auto 16px; }
.ec-view-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 18px; }
.ec-view-grid > div { min-width: 0; }
.ec-view-grid span { display: block; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); font-weight: 700; }
.ec-view-grid p { margin: 2px 0 0; color: var(--text); font-size: 0.9rem; word-break: break-word; }
.ec-view-full { grid-column: 1 / -1; }

/* Typeahead de cliente (buscar escribiendo). */
.ec-typeahead { position: relative; display: flex; flex-direction: column; }
.ec-typeahead-list { position: absolute; top: 100%; left: 0; right: 0; z-index: 70; margin: 4px 0 0; padding: 4px;
  list-style: none; max-height: 240px; overflow-y: auto; background: var(--surface-overlay);
  border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-md); }
.ec-typeahead-list li { padding: 8px 10px; border-radius: 7px; font-size: 0.86rem; color: var(--text); cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ec-typeahead-list li:hover { background: var(--control-bg-hover); }

/* Items del pedido: filas compactas (sin scroll lateral, todo en el ancho del drawer). */
.ec-items-empty { padding: 14px; text-align: center; color: var(--muted); font-size: 0.84rem; border: 1px dashed var(--border); border-radius: 10px; }
.ec-oi-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; margin-bottom: 8px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px; }
.ec-oi-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ec-oi-name { font-weight: 600; font-size: 0.88rem; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ec-oi-meta { font-size: 0.78rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ec-oi-actions { display: flex; gap: 4px; flex: 0 0 auto; }

/* Pista de conversión en el modal de item. */
.ec-conv-hint { font-size: 0.82rem; color: var(--muted); background: var(--surface-2); border-radius: 8px; padding: 8px 10px; }
.ec-conv-hint strong { color: var(--text); }

/* ── Config del módulo: agrupación por contenedor ──────────────────────────────
   Cada bloque de ajustes va en su propia tarjeta con título. El "para qué sirve"
   vive SIEMPRE en el tooltip del título, nunca como párrafo suelto. Aplica a
   todas las secciones (general, productos, pedidos, integraciones). */
.ec-cfg-group { background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
                padding: 18px 22px; display: flex; flex-direction: column; gap: 16px; }
.ec-cfg-group-title { font-weight: 700; font-size: 0.92rem; color: var(--text-strong);
                      display: inline-flex; align-items: center; gap: 6px; }
/* Las secciones con contenedores necesitan más ancho que el .mir-config-section
   base (460px), sobre todo la de avisos (editor + vista previa lado a lado).
   Se cualifica con `.mir-config-section` A PROPÓSITO: esa regla se define MÁS
   ABAJO en este archivo, así que con un solo selector de clase ganaba por orden
   y el ancho extra no se aplicaba nunca. Subir la especificidad lo hace inmune a
   dónde quede cada bloque. */
.mir-config-section.ec-cfg-wide { max-width: 1180px; }

/* Config de e-commerce: aunque el contenedor (grupo) sea ancho, los CAMPOS no se
   estiran a todo el ancho — los inputs de texto y las filas de toggle se limitan a un
   ancho cómodo y quedan a la izquierda. Antes el toggle era un <label> a todo el ancho,
   así que clicar a la derecha del switch igual lo activaba/desactivaba; al acotar el
   ancho, el área clicable queda solo sobre el campo. Aplica a todas las secciones de
   config (General, Productos, Pedidos, Integraciones). No toca ColsManager ni los
   editores de avisos (no son hijos directos de .mir-form-grid). */
.ecommerce-page .mir-config-section .mir-form-grid > label,
.ecommerce-page .mir-config-section .mir-form-grid > .mir-switch-row {
  max-width: 460px;
}

/* ── Plantillas de avisos al cliente ───────────────────────────────────────── */
/* Botones de inserción de comodines: separados, no pegados. */
.ec-tpl-vars { display: flex; flex-wrap: wrap; gap: 8px; }
/* Editor a la izquierda, flecha al centro, vista previa a la derecha; apilados en
   pantallas chicas. La columna del medio es `auto`: solo ocupa lo que mide el icono. */
.ec-tpl-split { display: grid; grid-template-columns: minmax(0,1fr) auto minmax(0,1fr);
                gap: 18px; align-items: stretch; }
/* Apilado: la flecha pasa a apuntar hacia abajo, que es como fluye el contenido. */
@media (max-width: 760px) {
    .ec-tpl-split { grid-template-columns: 1fr; gap: 10px; }
    .ec-tpl-arrow { transform: rotate(90deg); padding: 0; }
}
.ec-tpl-pane { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ec-tpl-label { font-size: 12px; color: var(--muted); }
/* Se alinea con las cajas, no con las etiquetas: de ahí el padding superior, que
   compensa la altura de la fila de `.ec-tpl-label`. */
.ec-tpl-arrow { display: flex; align-items: center; justify-content: center;
                color: var(--muted-2); padding-top: 22px; }
.ec-tpl-preview { flex: 1; font-size: 0.85rem; color: var(--text); background: var(--surface-2);
                  border: 1px solid var(--border-soft); border-radius: 8px; padding: 12px 14px;
                  white-space: pre-wrap; overflow-wrap: break-word; min-height: 96px;
                  line-height: 1.5; }
.ec-tpl-preview--default { color: var(--muted); font-style: italic; }
/* Comodín mal escrito: rojo, para que no pase desapercibido. */
.ec-tpl-warn { font-size: 0.82rem; color: var(--badge-danger-fg); background: var(--badge-danger-bg);
               border: 1px solid var(--badge-danger-bg); border-radius: 8px; padding: 8px 10px; }
/* Autocompletado mientras se escribe `{...`. Panel flotante → --surface-overlay. */
.ec-tpl-ac-wrap { position: relative; display: flex; flex-direction: column; gap: 6px; }
.ec-tpl-ac { position: absolute; top: 100%; left: 0; right: 0; z-index: 40; margin-top: 4px;
             background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 10px;
             box-shadow: var(--shadow-lg); max-height: 190px; overflow-y: auto; padding: 4px; }
.ec-tpl-ac-item { display: block; width: 100%; text-align: left; padding: 7px 9px; border: 0;
                  border-radius: 7px; background: transparent; color: var(--text); cursor: pointer;
                  font-size: 0.85rem; }
.ec-tpl-ac-item:hover { background: var(--control-bg-hover); }
.ec-tpl-ac-key { font-weight: 700; color: var(--text-strong); }
.ec-tpl-ac-label { color: var(--muted); margin-left: 6px; }

.pg-empty { padding: 26px; text-align: center; color: var(--muted); }
/* Celda de acciones común a Pagos, Contactos y Tickets: botones inline, un solo
   clic, mismo tamaño en los tres módulos. flex-wrap evita que se desborden en
   columnas estrechas. */
.pg-actions { display: flex; gap: 4px; flex-wrap: wrap; white-space: nowrap; }

/* Acciones como iconos (Ver/Editar/Eliminar/Activar) en las tablas del ecommerce. */
.ec-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0; }
.ec-ic { width: 15px; height: 15px; }

/* Botón de acción SOLO-icono compartido (estandarización): mismo look que
   .ec-icon-btn pero con nombre genérico para reusar en Contactos/Tickets/Pagos/
   Centro de Control. Se combina con .pg-btn .pg-btn--sm. El tooltip va en title=.
   .icon-btn-danger tiñe el icono en rojo (hover incluido). .icon-ic dimensiona SVG. */
.icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0; }
.icon-ic { width: 15px; height: 15px; }
.icon-btn-danger { color: var(--badge-danger-fg); }
.icon-btn-danger:hover:not(:disabled) { color: var(--badge-danger-fg); border-color: var(--badge-danger-fg); }

/* Tabs siempre montadas: la inactiva se oculta (no se desmonta → sin re-fetch/flicker). */
.ec-hidden { display: none; }

/* Toast minimalista fijo (reemplaza el banner que empujaba el layout). */
.ec-toast {
  position: fixed; right: 20px; bottom: 20px; z-index: 2000;
  display: flex; align-items: center; gap: 10px;
  max-width: min(360px, 90vw); padding: 11px 14px;
  background: var(--surface-overlay); color: var(--text);
  border: 1px solid var(--border); border-left: 3px solid var(--brand);
  border-radius: 10px; box-shadow: var(--shadow-lg); font-size: 0.85rem;
  animation: ecToastIn 0.25s ease;
}
.ec-toast-x { border: 0; background: transparent; color: var(--muted); cursor: pointer;
  font-size: 0.9rem; line-height: 1; padding: 2px 4px; border-radius: 6px; flex: 0 0 auto; }
.ec-toast-x:hover { background: var(--control-bg-hover); color: var(--text); }
@keyframes ecToastIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

/* Modales del ecommerce: tamaño fijo grande + scroll interno. No cambian de
   tamaño según el contenido ni al cambiar de tab; responsivos en móvil. */
.ecommerce-page .modal-card {
  width: 100%;
  max-width: min(920px, 95vw);
  height: min(85vh, 720px);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.ecommerce-page .modal-card > .modal-header,
.ecommerce-page .modal-card > .modal-footer { flex: 0 0 auto; }
.ecommerce-page .modal-card > div:not(.modal-header):not(.modal-footer) {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* Modales de FORMULARIO de e-commerce (antes drawers de crear/editar): más angostos
   que el modal de detalle (que es ancho por sus tablas) y altura al contenido. Los
   campos van apilados (uno debajo de otro) — `.mir-form-grid` ya es de una columna. */
.ecommerce-page .modal-card.ec-modal-form { max-width: 620px; height: auto; }
.ecommerce-page .modal-card.ec-modal-form > .ec-modal-body { padding-top: 12px; padding-bottom: 12px; }
@media (max-width: 640px) {
  .ecommerce-page .modal-card { height: 90vh; max-width: 100%; }
}
/* Excepción: modales pequeños (confirmaciones) vuelven a altura automática. */
.ecommerce-page .modal-card.ec-modal-auto { height: auto; max-height: 90vh; }
.ec-confirm-msg { color: var(--text); font-size: 0.9rem; line-height: 1.5; margin: 0; }
/* Fila clicable (pedidos del cliente → abre en la tab Pedidos). */

/* Columnas configurables (en Config): orden ↑/↓ + on/off. */
.ec-cols-config { margin-top: 18px; border-top: 1px solid var(--border); padding-top: 14px; max-width: 460px; }
.ec-cols-title { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); }
.ec-cols-row { display: flex; align-items: center; gap: 10px; padding: 5px 0; transition: opacity 0.15s ease; }
.ec-cols-row.is-off { opacity: 0.42; }
.ec-cols-ord { display: inline-flex; gap: 2px; }
.ec-cols-ord .ec-icon-btn { padding: 2px 7px; line-height: 1; }
.ec-cols-name { flex: 1 1 auto; font-size: 0.88rem; color: var(--text); }
.ec-cols-check { display: inline-flex; align-items: center; }
.ec-cols-check input[type="checkbox"] { width: auto; }
.ec-cols-check[title]:not([title=""]) { cursor: help; }

/* Columnas personalizadas (G2). */
.ec-cols-actions { display: inline-flex; gap: 2px; margin-left: 4px; }
.ec-cols-actions .ec-icon-btn { padding: 2px 7px; line-height: 1; }
.ec-col-badge { display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 999px;
  font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  background: var(--brand-soft-bg); color: var(--brand); vertical-align: middle; }
/* Fila "agregar columna": bordes punteados con hover de escala + color. */
.ec-cols-addrow { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 8px;
  padding: 9px 0; border: 1.5px dashed var(--border); border-radius: 10px; color: var(--muted);
  cursor: pointer; user-select: none; font-size: 0.86rem;
  transition: transform 0.12s ease, border-color 0.12s ease, color 0.12s ease, background 0.12s ease; }
.ec-cols-addrow:hover, .ec-cols-addrow:focus-visible { transform: scale(1.02); border-color: var(--brand);
  color: var(--brand); background: var(--brand-soft-bg); outline: none; }
.ec-cols-addrow-plus { font-size: 1.05rem; font-weight: 700; line-height: 1; }
/* Popover crear/editar columna adicional (en Portal, fixed). */
.ec-col-pop { position: fixed; z-index: 2100; min-width: 230px; width: 250px; max-width: calc(100vw - 16px);
  max-height: calc(100vh - 16px); overflow-y: auto; overflow-x: hidden; box-sizing: border-box;
  background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow-lg); padding: 14px; display: flex; flex-direction: column; gap: 10px; }
/* Dentro del popover el label del checkbox debe envolver (no nowrap → sin scroll horizontal). */
.ec-col-pop .ec-extra-req-chk, .ec-quick-pop .ec-extra-req-chk { white-space: normal; align-items: flex-start; }
.ec-col-pop .pg-input, .ec-quick-pop .pg-input { max-width: 100%; box-sizing: border-box; }
/* Descripción viva del rol de flujo seleccionado (bajo el selector en el popover). */
.ec-col-pop-title { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); }
.ec-col-pop-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.8rem; color: var(--muted); }
.ec-col-pop-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 2px; }
.ec-extra-req-chk { display: inline-flex; align-items: center; gap: 5px; font-size: 0.82rem; color: var(--text); white-space: nowrap; }
.ec-extra-req-chk input[type="checkbox"] { width: auto; }
/* Bloque de campos personalizados en los formularios de crear/editar. */
.ec-extra-fields { margin-top: 6px; border-top: 1px dashed var(--border); padding-top: 10px; }
/* Aire entre el título de la sección y el primer campo (antes casi se tocaban). */
.ec-extra-fields .ec-cols-title { margin-bottom: 12px; }
.ec-req { color: var(--danger, #d33); font-weight: 700; }
/* Validación inline de campos obligatorios: borde rojo + aviso bajo el campo. */
.ec-field-err, .ec-field-err:focus { border-color: var(--danger) !important; }
.ec-req-msg { display: block; color: var(--danger); font-size: 0.72rem; margin-top: 3px; }

/* `.ec-toolbar` desapareció: Productos y Pedidos ya usan la toolbar estándar
   (`.tickets-toolbar`, con su propio margen). Con ella se fue también la regla del
   `.mir-icon` del modo selección, que era del icono propio del módulo. */

/* Fila clicable → abre el detalle (se eliminó la acción "Ver"). */
.pg-row.ec-row-click { cursor: pointer; }
.pg-row.ec-row-click:hover { background: var(--control-bg-hover); }

/* ── Vista CATÁLOGO de Productos (tarjetas) ────────────────────────────────────
   Secundaria: la tabla sigue siendo la vista de trabajo. Aquí manda la foto, que
   es lo que el dueño de la tienda reconoce. `auto-fit` para que las tarjetas
   estiren y llenen la fila (con `auto-fill` quedarían columnas fantasma). */
/* Tarjeta de ANCHO ACOTADO, no elástica, y `auto-fill` en vez de `auto-fit`.
   Es lo contrario de la regla de las gráficas de Métricas, por un motivo
   concreto: la tarjeta lleva foto. `auto-fit` colapsa las columnas vacías y
   reparte el ancho entre las que hay —tres productos en una pantalla ancha =
   tarjetas de ~400px— y como la foto es 1:1, la tarjeta crece otro tanto a lo
   alto. El `1fr` tampoco vale: aunque `auto-fill` conserve las columnas, un `1fr`
   les reparte el sobrante y siguen creciendo. Con el tope en 200px la tarjeta
   mide lo mismo con 3 productos que con 300; el mínimo de 160px es para que en
   una columna estrecha encoja en vez de desbordar.
   `justify-content: start` deja el hueco sobrante a la derecha en vez de repartir
   separaciones distintas entre tarjeta y tarjeta. */
.ec-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 200px));
  justify-content: start;
  gap: 14px;
}
.ec-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface); overflow: hidden; cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.ec-card:hover { border-color: var(--brand); box-shadow: var(--shadow-sm); }

/* Relación fija 1:1 y `object-fit: cover`: las fotos de producto llegan en
   cualquier proporción y sin esto cada tarjeta tendría una altura distinta.
   El `max-height` es el tope duro: en una rejilla estrecha la columna puede pasar
   de 240px de ancho, y sin él la foto se llevaría media pantalla de alto. Con
   `cover` recorta, no deforma. */
.ec-card-img {
  position: relative;
  aspect-ratio: 1 / 1;
  max-height: 200px;
  background: var(--surface-2);
}
.ec-card-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ec-card-img--empty { display: block; width: 100%; height: 100%; }
/* El estado va sobre la foto, así que necesita su propia superficie: el color del
   estado lo elige el usuario en la configuración y puede coincidir con cualquier
   cosa que haya en la imagen (un «Activo» verde sobre una camisa clara
   desaparecía). La píldora usa `--surface-overlay` —la superficie casi opaca de
   los elementos flotantes— y deja el color para el texto, que es donde significa
   algo. */
.ec-card-estado {
  position: absolute;
  top: 8px;
  left: 8px;
  max-width: calc(100% - 16px);
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  font-size: 0.7rem;
  line-height: 1.5;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ec-card-body { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px 12px; }
.ec-card-name {
  color: var(--text-strong); font-size: 0.85rem; font-weight: 700;
  /* Dos líneas como máximo: un nombre largo no debe descuadrar la rejilla. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.ec-card-sku { color: var(--muted-2); font-size: 0.7rem; font-family: ui-monospace, monospace; }
.ec-card-foot {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px; margin-top: 4px;
}
.ec-card-precio { color: var(--text-strong); font-size: 0.95rem; font-weight: 700; }
.ec-card-stock { color: var(--muted); font-size: 0.75rem; }
/* Interruptor bloqueado por configuración (hoy: «aplica impuesto» sin impuesto configurado).
   El candado y el `not-allowed` van en la FILA, no en el input: el input deshabilitado no
   dispara hover, así que el tooltip del `title` no llegaría a verse nunca. */
.ec-switch-locked { cursor: not-allowed; }
.ec-switch-locked .ec-switch-title { color: var(--muted); }
.ec-switch-locked input[type="checkbox"] { pointer-events: none; opacity: 0.55; }
.ec-card .pg-actions {
  display: flex; gap: 6px; margin-top: 10px;
  padding-top: 10px; border-top: 1px solid var(--border-soft);
}

/* La selección se pinta sobre la tarjeta entera, con el mismo tinte y barra que
   la fila, para que el modo selección se lea igual en las dos vistas. */
.ec-card.ec-selectmode { user-select: none; }
.ec-card.ec-row-sel {
  background: var(--brand-soft-bg);
  border-color: var(--brand);
  box-shadow: inset 3px 0 0 var(--brand);
}

/* Selección múltiple (estilo MIR): fila clicable en modo selección + marcada. */
.pg-row.ec-selectmode { cursor: pointer; user-select: none; }
.pg-row.ec-selectmode:hover { background: var(--control-bg-hover); }
.pg-row.ec-row-sel,
.row-selected { background: var(--brand-soft-bg); box-shadow: inset 3px 0 0 var(--brand); }
.pg-row.ec-row-sel:hover,
.row-selected:hover { background: var(--brand-soft-bg); }
/* En modo selección la fila marca/desmarca: se ocultan sus acciones inline para
   que un clic no dispare otra cosa (mismo criterio que .ec-hidden del Ecommerce). */
.row-selectmode .pg-actions { display: none; }
/* `user-select:none` es obligatorio: Shift+clic para marcar un rango dispara
   ADEMÁS la selección de texto nativa del navegador y todo el rango queda pintado
   de azul. Se apaga solo en modo selección; fuera de él el texto se copia normal. */
/* En las tablas <table> reales el fondo lo pinta la CELDA (`… tr:hover td`), así
   que un background en la fila no se ve: la selección se repite sobre los td y
   la barra de marca va en la primera celda. */
.users-table tr.row-selected td,
.audit-table tr.row-selected td,
.camp-table tr.row-selected td { background: var(--brand-soft-bg); }
.users-table tr.row-selected td:first-child,
.audit-table tr.row-selected td:first-child,
.camp-table tr.row-selected td:first-child { box-shadow: inset 3px 0 0 var(--brand); }

.row-selectmode,
tr.row-selectmode,
.pg-row.row-selectmode {
    cursor: pointer;
    -webkit-user-select: none;
    user-select: none;
}
/* Estilos de celda cuando .pg-table se usa sobre un <table> REAL (catálogo de
   órdenes SAE). Las demás .pg-table son grids de divs (.pg-row) → estas reglas,
   al apuntar a thead/th/td, NO las afectan. Sin esto la tabla SAE salía sin
   formato (HTML plano, celdas apretadas). */
/* width:auto + min-width:100% → la tabla llena el modal como mínimo pero CRECE con
   su contenido (celdas nowrap) en vez de comprimir 9 columnas en el ancho del modal.
   overflow:visible anula el overflow-x del .pg-table base: el scroll horizontal lo
   maneja SOLO .ticket-table-wrap (evita doble scroll y el apretado). */
table.pg-table { border-collapse: separate; border-spacing: 0; width: auto; min-width: 100%; overflow: visible; }
.pg-table thead th {
  text-align: left; padding: 11px 12px; white-space: nowrap;
  font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--muted); background: var(--surface-2);
  border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 1;
}
.pg-table tbody td {
  padding: 9px 12px; font-size: 0.84rem; color: var(--text);
  border-bottom: 1px solid var(--border-soft); vertical-align: middle; white-space: nowrap;
}
.pg-table tbody tr:last-child td { border-bottom: none; }
.pg-table tbody tr:hover td { background: var(--surface-2); }
.pg-table th.pg-cell--center, .pg-table td.pg-cell--center { text-align: center; }
/* Anchos mínimos por columna para que la data no quede apretada (celdas nowrap ya
   evitan el corte; esto asegura columnas legibles y toggles con aire). */
.pg-table thead th, .pg-table tbody td { min-width: 5.5rem; }
.pg-table thead th:first-child, .pg-table tbody td:first-child { min-width: 12rem; }   /* Detalle */
.pg-table thead th:nth-child(2),  .pg-table tbody td:nth-child(2)  { min-width: 10rem; }   /* Tipo / grupo */
.pg-table th.pg-cell--center, .pg-table td.pg-cell--center { min-width: 6.5rem; }          /* toggles con aire */
/* Los controles de fila (select/input) heredan el ancho justo, no todo el td. */
.pg-table tbody td .pg-input { min-width: 8rem; }

.pg-tag { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 0.72rem; font-weight: 600; }
.pg-tag--registrado { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }
.pg-tag--en_revision { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.pg-tag--validado { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.pg-tag--rechazado { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.pg-tag--cerrado { background: var(--badge-purple-bg); color: var(--badge-purple-fg); }

/* Layout en el bloque canónico (buscar "PAGINACIÓN ESTÁNDAR"). */

/* En móvil, los filtros de estos módulos ocupan el ancho completo (una columna)
   en vez de quedar apretados o desbordar. Las tablas anchas siguen con scroll-x
   dentro de su contenedor. */
@media (max-width: 640px) {
  .users-filter,
  .users-filter--text,
  .perms-filter { flex: 1 1 100%; min-width: 0; }
  .pg-input { flex: 1 1 100%; }
}

/* ── Modal de detalle del pago ─────────────────────────────────────────────── */
.pg-modal-overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: var(--scrim);
  backdrop-filter: blur(2px);
  display: flex; align-items: center; justify-content: center;
  padding: clamp(12px, 4vw, 24px);
}
.pg-modal {
  background: var(--surface-overlay);
  border-radius: 14px;
  width: min(680px, 100%);
  max-height: 88vh;
  overflow: hidden;
  display: flex; flex-direction: column;
  box-shadow: var(--shadow-lg);
}
.pg-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.pg-modal-head h3 { margin: 0; font-size: 1.05rem; color: var(--text); }
.pg-modal-close {
  border: none; background: transparent; cursor: pointer;
  font-size: 1.1rem; color: var(--muted); line-height: 1; padding: 4px 8px; border-radius: 8px;
}
.pg-modal-close:hover { background: var(--control-bg-hover); color: var(--text); }
.pg-modal-body { padding: 18px 20px; overflow-y: auto; }
.pg-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px 18px;
}
.pg-detail-item { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pg-detail-k { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); font-weight: 600; }
.pg-detail-item > span:last-child { font-size: 0.9rem; color: var(--text); word-break: break-word; }
.pg-detail-section { margin-top: 16px; }
.pg-detail-notes {
  margin-top: 6px; padding: 10px 12px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 8px;
  font-size: 0.85rem; color: var(--text); white-space: pre-wrap; word-break: break-word;
}
.pg-proof { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.pg-proof-img {
  max-width: 100%; max-height: 360px; border-radius: 10px;
  border: 1px solid var(--border); object-fit: contain; background: var(--surface-2);
}
.pg-proof-link { font-size: 0.82rem; color: var(--brand); text-decoration: none; }
.pg-proof-link:hover { text-decoration: underline; }

/* ═══════════════════════════════════════════════════════════════
   App shell + sidebar global de módulos (navegación unificada)
   Reemplaza el sidebar duplicado de home y el dropdown "Módulos".
   ═══════════════════════════════════════════════════════════════ */
/* Shell en COLUMNA: header global arriba a lo ancho, debajo la fila
   [sidebar | contenido]. El header (.ph) es sticky top:0 dentro del shell. */
.app-shell { display: flex; flex-direction: column; min-height: 100vh; }
.app-body { display: flex; align-items: stretch; flex: 1 1 auto; min-height: 0; }
.app-content { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.module-sidebar {
  flex: 0 0 auto;
  width: 252px;                                /* cabe "Gestión de Tickets" sin cortar */
  /* bg/borde tokenizados (identidad visual): sidebar theme-aware en claro/oscuro. */
  background: var(--nav-bg-2);
  border-right: 1px solid var(--nav-border);
  padding: 14px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: sticky;
  /* La sidebar ahora vive BAJO el header (60px): pega justo debajo. */
  top: 60px;
  align-self: flex-start;
  height: calc(100vh - 60px);
  overflow-y: auto;
  transition: width 0.18s ease;
  z-index: 30;
  /* Scrollbar overlay: reserva su propio carril (no pisa iconos) y se oculta
     hasta que el puntero entra a la sidebar. */
  scrollbar-gutter: stable;
  scrollbar-width: thin;                       /* Firefox */
  scrollbar-color: transparent transparent;
}
.module-sidebar:hover { scrollbar-color: rgba(255, 255, 255, 0.20) transparent; }
.module-sidebar::-webkit-scrollbar { width: 8px; }
.module-sidebar::-webkit-scrollbar-track { background: transparent; }
.module-sidebar::-webkit-scrollbar-thumb {
  background: transparent;
  border: 2px solid transparent;               /* inset → carril propio, no toca iconos */
  background-clip: padding-box;
  border-radius: 8px;
  transition: background-color 0.2s ease;
}
.module-sidebar:hover::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.20); background-clip: padding-box; }
.module-sidebar:hover::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.34); background-clip: padding-box; }
.module-sidebar--collapsed { width: 64px; }
/* Colapso provocado por navegación: instantáneo, no se ve cerrarse (module_sidebar.rs). */
.module-sidebar--no-anim { transition: none; }

.module-sidebar-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 12px;
  margin-bottom: 8px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 10px;
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-weight: 700;
  font-size: 0.8rem;
  cursor: pointer;
}
.module-sidebar-toggle:hover { background: rgba(255, 255, 255, 0.06); }
.module-sidebar-toggle-icon { font-size: 1rem; line-height: 1; width: 18px; text-align: center; }
.module-sidebar--collapsed .module-sidebar-toggle { justify-content: center; }
.module-sidebar--collapsed .module-sidebar-toggle-label { display: none; }

.module-sidebar-nav { display: flex; flex-direction: column; gap: 2px; }

.module-sidebar-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 10px 12px;
  border: none;
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  font-family: inherit;
  font-size: 0.86rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.12s ease, color 0.12s ease;
}
.module-sidebar-item:hover { background: rgba(255, 255, 255, 0.07); color: #fff; }
.module-sidebar-item.is-active { background: var(--brand-dark); color: #fff; }
.module-sidebar-item.is-maint { opacity: 0.45; cursor: not-allowed; }
.module-sidebar-icon { font-size: 1.1rem; width: 22px; text-align: center; flex-shrink: 0; }
.module-sidebar-label { overflow: hidden; text-overflow: ellipsis; }
.module-sidebar-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #f59e0b; margin-left: auto; flex-shrink: 0;
}
/* `position: relative` para que el punto de «sección nueva» se sitúe respecto
   al botón (ver .module-sidebar-new-dot). */
.module-sidebar--collapsed .module-sidebar-item { justify-content: center; padding: 10px 0; position: relative; }
.module-sidebar--collapsed .module-sidebar-label,
.module-sidebar--collapsed .module-sidebar-dot { display: none; }

/* Botón ☰ del header (toggle de la sidebar) */
.ph-menu-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px; height: 34px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: transparent;
  color: var(--text);
  cursor: pointer;
  flex-shrink: 0;
}
.ph-menu-toggle:hover { background: rgba(255, 255, 255, 0.06); }

/* El comportamiento en tablet/móvil de la sidebar vive en la sección
   «RESPONSIVE DEL SHELL» del FINAL de este archivo, no aquí.

   Aquí hubo un `@media (max-width: 860px)` que la pasaba a `position: fixed`
   dejando el riel de 64px visible. Sacaba la barra del flujo sin compensar el
   contenido, así que el riel se dibujaba ENCIMA de los primeros 64px de cada
   página; y al anclarla en `top: 0` metía su propio botón «Módulos» debajo del
   header (z-100 contra z-30), justo lo que aquel comentario decía que había que
   evitar. Se sustituyó por un cajón de verdad. */

/* ── Light mode: sidebar global ── */
html.light-mode .module-sidebar:hover { scrollbar-color: rgba(0, 0, 0, 0.22) transparent; }
html.light-mode .module-sidebar:hover::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.18); background-clip: padding-box; }
html.light-mode .module-sidebar:hover::-webkit-scrollbar-thumb:hover { background: rgba(0, 0, 0, 0.30); background-clip: padding-box; }
html.light-mode .module-sidebar-toggle { color: var(--text-strong); border-color: var(--border); }
html.light-mode .module-sidebar-toggle:hover { background: var(--surface-2); }
html.light-mode .module-sidebar-item { color: var(--muted); }
html.light-mode .module-sidebar-item:hover { background: var(--surface-2); color: var(--text-strong); }
html.light-mode .module-sidebar-item.is-active { background: var(--brand-dark); color: #fff; }
html.light-mode .ph-menu-toggle { color: var(--text-strong); border-color: var(--border); }
html.light-mode .ph-menu-toggle:hover { background: var(--surface-2); }

/* ═══════════════════════════════════════════════════════════════
   Subsidebar de tabs (hover sobre una fila de la sidebar)
   Mismo lenguaje que .module-sidebar (por eso repite sus hex: es una
   extensión de la misma superficie, no una card en flujo), un tono más
   oscura y pegada a su borde derecho. Solo aparece en hover.
   ═══════════════════════════════════════════════════════════════ */
.module-subsidebar {
  position: fixed;
  left: 252px;                                 /* = ancho de .module-sidebar */
  top: 60px;                                   /* bajo el header global */
  bottom: 0;
  width: 244px;
  /* MISMO color que la sidebar (--nav-bg-2, tokenizado por la identidad visual)
     con una capa de oscurecimiento encima para la profundidad. Antes era un hex
     fijo (#0e1420) y quedaba fuera del tema al cambiar los tokens de nav. */
  background-color: var(--nav-bg-2);
  background-image: linear-gradient(var(--nav-shade), var(--nav-shade));
  border-right: 1px solid var(--nav-border);
  padding: 14px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
  z-index: 31;                                 /* sobre el contenido, bajo el header */
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.20) transparent;
  /* Entrada/salida suave (140 ms). El panel no se desmonta: `is-open` lo muestra,
     y `visibility` se retrasa hasta el final del fundido para poder animar la salida. */
  opacity: 0;
  visibility: hidden;
  transform: translateX(-8px);
  pointer-events: none;
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0s linear 0.14s;
}
.module-subsidebar.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0s;
}
.module-subsidebar--rail { left: 64px; }       /* sidebar colapsada */

/* Flecha "hay más": SIEMPRE visible en las filas con tabs — es el indicador de
   que ese módulo despliega subsidebar. El hover solo la refuerza. */
.module-sidebar-caret {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  color: currentColor;
  opacity: 0.45;
  transition: opacity 0.12s ease, transform 0.12s ease;
}
.module-sidebar-item:hover .module-sidebar-caret { opacity: 0.9; transform: translateX(2px); }
.module-sidebar-item.is-active .module-sidebar-caret { opacity: 0.9; }
.module-sidebar-dot + .module-sidebar-caret { margin-left: 6px; }
.module-sidebar--collapsed .module-sidebar-caret { display: none; }

.module-subsidebar-title {
  padding: 4px 12px 8px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.module-subsidebar-nav { display: flex; flex-direction: column; gap: 2px; }

.module-subsidebar-item {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 9px 12px;
  border: none;
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 0.12s ease, color 0.12s ease;
}
.module-subsidebar-item:hover { background: rgba(255, 255, 255, 0.07); color: #fff; }
.module-subsidebar-item.is-active { background: var(--brand-dark); color: #fff; }

/* En tablet/móvil la subsidebar deja de ser un segundo panel a la derecha y pasa
   a superponerse al cajón (navegación en profundidad). Ver «RESPONSIVE DEL
   SHELL» al final del archivo. */

/* ── Light mode: subsidebar ── */
/* El color base y el velo ya son tokens (--nav-bg-2 / --nav-shade), así que en
   claro solo queda el borde de separación. */
html.light-mode .module-subsidebar {
  box-shadow: 1px 0 0 rgba(0, 0, 0, 0.03);
}
html.light-mode .module-subsidebar-title { color: var(--muted); }
html.light-mode .module-subsidebar-item { color: var(--muted); }
html.light-mode .module-subsidebar-item:hover { background: rgba(0, 0, 0, 0.05); color: var(--text-strong); }
html.light-mode .module-subsidebar-item.is-active { background: var(--brand-dark); color: #fff; }

/* ── Módulos MIR (Reservas) y Cinema (Cine) ──────────────────────────────── */
/* Tabs superiores estilo píldora (igual que módulo Tickets). */
/* .mir-tabs / .mir-tab (Reservas, Cine y Ecommerce) están registradas en el
   bloque canónico «SEGMENTED CONTROL ESTÁNDAR»: repetían sus reglas y se
   quedaron fuera del grupo de tema claro (bandeja invisible). */

.mir-toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.mir-toolbar .pg-input { min-width: 200px; }

.mir-form { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.mir-form label { display: flex; flex-direction: column; gap: 4px; font-size: 0.82rem; color: var(--muted); }
.mir-form .mir-check { flex-direction: row; align-items: center; gap: 8px; color: var(--text); }
.mir-form .mir-check input { width: auto; }

.pg-modal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 14px 20px;
  border-top: 1px solid var(--border); }

.mir-config-editor { width: 100%; min-height: 220px; font-family: ui-monospace, Menlo, monospace;
  font-size: 0.82rem; padding: 12px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface-2); color: var(--text); box-sizing: border-box; margin-bottom: 12px; }

/* Config con selector lateral de secciones (estilo panel). */
.mir-config { display: flex; gap: 18px; align-items: flex-start; }
.mir-config-nav { flex: 0 0 190px; display: flex; flex-direction: column; gap: 4px;
  border-right: 1px solid var(--border-soft); padding-right: 12px; }
.mir-config-navitem { text-align: left; padding: 9px 12px; border: 1px solid transparent; border-radius: 9px;
  background: transparent; color: var(--text-1); font-size: 0.88rem; font-weight: 600; cursor: pointer; }
.mir-config-navitem:hover { background: var(--surface-2); }
.mir-config-navitem.is-active { background: var(--brand-soft-bg); color: var(--brand-dark); border-color: var(--border-soft); }
.mir-config-content { flex: 1; min-width: 0; }
.mir-config-section { display: flex; flex-direction: column; gap: 18px; max-width: 460px; }
.mir-config-hint { color: var(--muted); font-size: 12px; margin: 0; }
.mir-config-row { display: flex; flex-direction: column; gap: 6px; }
.mir-config-rowhead { display: inline-flex; align-items: center; gap: 6px; }
.mir-config-rowtitle { font-weight: 700; font-size: 0.92rem; color: var(--text-strong); }
.mir-config-input { max-width: 200px; }
.mir-config-actions { display: flex; }

.mir-capacity { margin-bottom: 16px; }

/* Insignias por estado de reserva */
.mir-st--hold      { background: rgba(180,83,9,0.12);  color: #b45309; border: 1px solid rgba(180,83,9,0.25); }
.mir-st--confirmed { background: rgba(29,78,216,0.12); color: #1d4ed8; border: 1px solid rgba(29,78,216,0.25); }
.mir-st--completed { background: rgba(4,120,87,0.12);  color: #047857; border: 1px solid rgba(4,120,87,0.25); }
.mir-st--cancelled { background: rgba(185,28,28,0.12); color: #b91c1c; border: 1px solid rgba(185,28,28,0.25); }
.mir-st--expired   { background: rgba(71,85,105,0.12); color: #475569; border: 1px solid rgba(71,85,105,0.25); }

/* Cinema: salas */
.cinema-rooms-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin-bottom: 18px; }
.cinema-room-card { background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
  padding: 14px; cursor: pointer; box-shadow: var(--shadow-sm); transition: box-shadow 0.15s; }
.cinema-room-card:hover { box-shadow: var(--shadow-md); border-color: var(--brand); }
.cinema-room-title { font-weight: 700; color: var(--text); margin-bottom: 6px; }
.cinema-room-meta { font-size: 0.8rem; color: var(--muted); }
.cinema-room-next { font-size: 0.8rem; color: var(--brand-dark); margin-top: 6px; }
.cinema-day { margin-top: 18px; }
.cinema-day h3 { font-size: 1rem; color: var(--text); margin: 0 0 10px; }

/* Cinema: mapa de asientos */
.cinema-seatmap { display: flex; flex-wrap: wrap; gap: 6px; }
.cinema-seat { display: inline-flex; align-items: center; justify-content: center; min-width: 38px;
  height: 32px; padding: 0 6px; border-radius: 6px; font-size: 0.72rem; font-weight: 600; border: 1px solid var(--border); }
.cinema-seat--free      { background: var(--control-bg); color: var(--muted); }
.cinema-seat--hold      { background: rgba(180,83,9,0.18);  color: #b45309; }
.cinema-seat--confirmed { background: rgba(29,78,216,0.18); color: #1d4ed8; }
.cinema-seat--completed { background: rgba(4,120,87,0.18);  color: #047857; }
.cinema-general { padding: 16px; background: var(--panel-2); border-radius: 8px; font-size: 0.95rem; color: var(--text); }
.cinema-uuid { font-family: ui-monospace, Menlo, monospace; font-size: 0.72rem; color: var(--muted); }

/* ── MIR: modales de formulario (antes drawers laterales) ────────────────────
   Misma convención que Ecommerce: crear/editar viven en un modal centrado, no en
   un drawer. Los `.mir-drawer*` se retiraron al no quedar ningún uso. */
.mir-page .modal-card {
  width: 100%;
  max-width: min(920px, 95vw);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.mir-page .modal-card > .modal-header,
.mir-page .modal-card > .modal-footer { flex: 0 0 auto; }
.mir-page .modal-card > div:not(.modal-header):not(.modal-footer) {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* Formularios: más angostos que un modal de detalle y de ALTURA FIJA — no
   siguen a su contenido. Con altura automática la caja crece y encoge al
   cambiar de tab o al desplegar un campo condicional, y las tabs (que están
   arriba) se mueven bajo el cursor. El scroll vive en el cuerpo. */
.mir-page .modal-card.mir-modal-form { max-width: 620px; height: min(80vh, 660px); }
.mir-page .modal-card.mir-modal-form > .mir-modal-body { padding-top: 12px; padding-bottom: 12px; }
/* Sub-modal (definir un campo del agente): se abre ENCIMA del formulario. */
.mir-submodal-backdrop { z-index: 120; }
.mir-page .modal-card.mir-modal-sub { max-width: 520px; height: min(72vh, 580px); }
/* Tooltips dentro del modal: abren HACIA ABAJO para no cortarse contra la
   cabecera, y por encima del resto del contenido. */
.mir-modal-form .mir-tip-bubble { top: calc(100% + 10px); bottom: auto; transform: translateX(-50%) translateY(-4px); z-index: 130; }
.mir-modal-form .mir-tip:hover .mir-tip-bubble,
.mir-modal-form .mir-tip:focus-visible .mir-tip-bubble { transform: translateX(-50%) translateY(0); }

/* Integración MIR→E-commerce: lo que el recurso ya tiene a la venta + acciones.
   Reusa a propósito los primitivos `.ec-seg` / `.ec-res-*` del picker inverso: es
   el mismo control, no un dialecto nuevo por módulo. */
.mir-integ-links { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-2); }
.mir-integ-links-title { font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.03em; color: var(--muted); }
.mir-integ-link { display: flex; align-items: center; gap: 8px; }
.mir-integ-link-name { font-weight: 600; color: var(--text); flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mir-integ-link-sku { font-size: 0.72rem; color: var(--muted); font-family: ui-monospace, Menlo, monospace; }
.mir-integ-link-price { font-size: 0.8rem; color: var(--text-strong); font-weight: 700; }
.mir-integ-empty-acts { display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.mir-integ-cancel { margin-top: 12px; }

/* Tabs internos de los modales de formulario: mismo lenguaje visual que las tabs principales. */
.mir-dtabs { display: inline-flex; gap: 3px; margin-bottom: 18px; padding: 5px;
  background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: 12px; flex-wrap: wrap; }
.mir-dtab { appearance: none; border: 0; border-radius: 8px; padding: 8px 16px; background: transparent;
  cursor: pointer; font-family: inherit; font-size: 0.8125rem; font-weight: 600; color: var(--muted);
  white-space: nowrap; transition: all 0.15s; }
.mir-dtab:hover { background: var(--control-bg-hover); color: var(--text); }
.mir-dtab.active { background: var(--brand-dark); color: #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.12); }

/* Campos del drawer — apilados uno encima de otro (como el front viejo), no lado a lado */
.mir-form-grid { display: grid; grid-template-columns: 1fr; gap: 12px; margin-bottom: 12px; }
.mir-form-grid label, .mir-form-full label { display: flex; flex-direction: column; gap: 4px;
  font-size: 0.82rem; color: var(--muted); }
.mir-form-full { display: block; margin-bottom: 12px; }
.mir-form-full > label { width: 100%; }
/* Toggle switch apilado: etiqueta arriba, switch debajo (coherente con los campos de texto). */
.mir-switch-row { display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  font-size: 0.85rem; color: var(--text); padding: 6px 0; margin-bottom: 4px; }
.mir-switch-row > span { display: inline-flex; }
/* El fondo del interruptor se fuerza con !important: ningún estado (focus/active/hover)
   ni pintado nativo del navegador puede blanquearlo. Solo depende de checked. */
.mir-switch-row input[type="checkbox"] { appearance: none !important; -webkit-appearance: none !important; position: relative;
  width: 42px; height: 24px; min-width: 42px; border-radius: 999px; background: var(--switch-off, var(--muted-2)) !important;
  border: 1px solid var(--border); cursor: pointer; transition: background 0.15s ease; flex: 0 0 auto; margin: 0; box-shadow: none; }
.mir-switch-row input[type="checkbox"]::after { content: ""; position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.25);
  transition: transform 0.15s ease; }
.mir-switch-row input[type="checkbox"]:checked { background: var(--brand) !important; }
.mir-switch-row input[type="checkbox"]:checked::after { transform: translateX(18px); }
/* Focus/active: nunca cambian el fondo; solo un anillo accesible en teclado. */
.mir-switch-row input[type="checkbox"]:focus,
.mir-switch-row input[type="checkbox"]:active,
.mir-switch-row input[type="checkbox"]:hover { box-shadow: none; outline: none; }
.mir-switch-row input[type="checkbox"]:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.mir-help-text { font-size: 0.8rem; color: var(--muted); margin: 0 0 12px; }
.mir-dpane-grid-full { grid-column: 1 / -1; }
.mir-inline-note { padding: 10px 12px; border-radius: 10px; background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border-soft); font-size: 0.82rem; }

/* ── Chips de días de la semana (tab Repetición del bloque de tiempo) ── */
.mir-wd-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.mir-wd-chip { display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px;
  border: 1px solid var(--control-border); border-radius: 999px; background: var(--control-bg);
  cursor: pointer; font-size: 0.8rem; user-select: none; }
.mir-wd-chip:hover { background: var(--control-bg-hover); }
.mir-wd-chip input { accent-color: var(--brand); }

/* ── Tarjeta de integración compartida (colapsable, MIR + E-commerce) ── */
.integ-card { border: 1px solid var(--border-soft); border-radius: 12px; background: var(--surface);
  overflow: hidden; }
.integ-card-head { display: flex; align-items: center; gap: 10px; width: 100%; padding: 12px 14px;
  background: var(--surface-2); border: 0; cursor: pointer; text-align: left; color: var(--text); }
.integ-card-head:hover { background: var(--control-bg-hover); }
.integ-card-title { font-weight: 700; color: var(--text-strong); flex: 1; }
.integ-card-chevron { color: var(--muted); font-size: 0.9rem; transition: transform 0.25s ease; }
.integ-card.is-open .integ-card-chevron { transform: rotate(180deg); }
/* Animación suave: grid-template-rows 0fr→1fr (no salta como max-height). */
.integ-card-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.25s ease; }
.integ-card-body.is-open { grid-template-rows: 1fr; }
.integ-card-body-inner { overflow: hidden; display: flex; flex-direction: column; gap: 10px;
  padding: 0 14px; }
.integ-card-body.is-open .integ-card-body-inner { padding: 12px 14px; }
.integ-card-status { display: flex; flex-wrap: wrap; gap: 6px; }

.ec-integ-cell { display: inline-flex; align-items: center; gap: 6px; }
.ec-integ-mod { font-size: 0.75rem; color: var(--muted); }
.ec-integ-note { padding: 10px 12px; border-radius: 10px; background: var(--brand-soft-bg);
  color: var(--brand-soft-fg); font-size: 0.82rem; margin-bottom: 10px; }

/* ── Picker de slots de reserva (item de pedido, Fase 4) ── */
.ec-slots { display: flex; flex-wrap: wrap; gap: 6px; }
.ec-slot { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 6px 10px; border: 1px solid var(--control-border); border-radius: 8px;
  background: var(--control-bg); cursor: pointer; color: var(--text); font-variant-numeric: tabular-nums; }
.ec-slot:hover:not(:disabled) { background: var(--control-bg-hover); border-color: var(--brand); }
.ec-slot.is-sel { border-color: var(--brand); background: var(--brand-soft-bg); color: var(--brand-soft-fg); }
.ec-slot:disabled { opacity: 0.45; cursor: not-allowed; }
.ec-slot-rem { font-size: 0.68rem; color: var(--muted); }

/* ── Crear recurso nuevo inline (par producto↔recurso, Fase 5) ── */

.mir-field-label { display: inline-flex; align-items: center; gap: 6px; color: var(--text); font-weight: 600; }
/* Validación de campos obligatorios (marca en rojo, sin usar el banner superior). */
.mir-req-star { color: var(--danger); font-weight: 700; margin-left: 2px; }
/* Spinner en línea para botones (Creando…/Guardando…). */
.mir-spinner { display: inline-block; width: 13px; height: 13px; vertical-align: -2px;
  border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;
  animation: mir-spin 0.7s linear infinite; }
@keyframes mir-spin { to { transform: rotate(360deg); } }
.mir-field-err { display: block; color: var(--danger); font-size: 0.75rem; margin-top: 4px; }
/* Botón extra-pequeño (acciones inline: cambiar contacto, enlazar pago). */
.pg-btn--xs { padding: 3px 10px; font-size: 0.75rem; line-height: 1.2; }
/* Picker de titular (contacto obligatorio) en el form de reserva. */
.mir-contact-search { position: relative; }
.mir-contact-results { margin-top: 4px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); box-shadow: var(--shadow-md); max-height: 220px; overflow-y: auto; }
.mir-contact-result { display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  width: 100%; text-align: left; padding: 8px 12px; background: none; border: none; cursor: pointer;
  border-bottom: 1px solid var(--border-soft); color: var(--text-strong); }
.mir-contact-result:last-child { border-bottom: none; }
.mir-contact-result:hover { background: var(--control-bg-hover); }
.mir-contact-chip { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); }
.mir-contact-chip-info { display: flex; flex-direction: column; gap: 2px; }
.mir-contact-chip-sub { color: var(--muted); font-size: 0.78rem; }
/* Sección Pago en el detalle de reserva. */
.mir-pago-section { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.mir-pago-title { margin: 0 0 6px; font-size: 0.9rem; color: var(--text-strong); }
.mir-pago-empty { display: flex; flex-direction: column; gap: 8px; padding: 8px 0; }
.mir-pago-warn { color: var(--badge-warn-fg); font-size: 0.82rem; }
.mir-pago-link { display: flex; flex-direction: column; gap: 8px; }
.mir-pago-cands { display: flex; flex-direction: column; gap: 4px; }
.mir-pago-cands-label { color: var(--muted); font-size: 0.78rem; font-weight: 600; }
.mir-pago-cand { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  font-size: 0.82rem; padding: 4px 0; }
.mir-pago-manual { display: flex; gap: 8px; align-items: center; }
.mir-pago-manual .pg-input { flex: 1; }
/* Aviso suave (única fuente de cupos por rama). */
.mir-inline-warn { border-left: 3px solid var(--danger); background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
/* Modales de detalle (recurso/bloque/reserva): filas etiqueta→valor. */
.mir-detail-modal { max-width: 460px; width: 100%; }
.mir-detail-row { display: flex; justify-content: space-between; gap: 16px; padding: 8px 0; border-bottom: 1px solid var(--border-soft); font-size: 0.88rem; }
.mir-detail-row:last-child { border-bottom: none; }
.mir-detail-k { color: var(--muted); font-weight: 600; flex: 0 0 auto; }
.mir-detail-v { color: var(--text-strong); text-align: right; word-break: break-word; }
/* Ficha del recurso con pestañas (Datos · Bloques): la de Bloques trae el
   calendario completo dentro, así que necesita el ancho de un mes y scroll
   propio en lugar de crecer sin límite. */
.mir-detail-modal--tabs { max-width: min(860px, 96vw); display: flex; flex-direction: column;
  height: min(86vh, 740px); max-height: 88vh; }
.mir-detail-modal--tabs > .pg-modal-body { overflow-y: auto; }
.mir-detail-cal { display: flex; flex-direction: column; gap: 10px; }
/* Cabecera de mes sin menú de salto (aquí el calendario es de solo lectura). */
.mir-cal-title--static { font-weight: 700; color: var(--text-strong); padding: 6px 10px; cursor: default; }
.mir-cal-title--static:hover { background: transparent; border-color: transparent; }
/* Bloque de solo lectura: mismo aspecto que el clicable, sin afordancia de clic. */
.mir-day-block--ro { cursor: default; }
/* Tarjeta de reserva: cabecera con título + botón de detalle. */
.mir-kcard-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 6px; }
.mir-kcard-top .mir-kcard-title { flex: 1 1 auto; }
/* Bloque en vista día: el contenedor entero abre DETALLES; editar es un botón
   segmentado DENTRO del mismo bloque (separador a la izquierda y hover propio).
   Antes el botón de detalle colgaba fuera del bloque y el clic abría el editor:
   lo contrario de lo que espera cualquiera al pulsar una tarjeta. */
.mir-day-bedit {
  appearance: none; position: relative; flex: 0 0 auto;
  margin-left: 7px; width: 24px; height: 24px; border-radius: 7px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; background: transparent; color: var(--muted-2);
  cursor: pointer;
  transition: color 0.14s ease, background 0.14s ease, border-color 0.14s ease,
              transform 0.14s ease, box-shadow 0.14s ease;
}
/* Filete que lo separa del contenido del bloque: "dentro, pero es otra acción". */
.mir-day-bedit::before {
  content: ""; position: absolute; left: -4px; top: 3px; bottom: 3px;
  width: 1px; background: var(--border-soft);
}
/* En reposo se apaga; al pasar por el bloque se insinúa (hay algo pulsable). */
.mir-day-block:hover .mir-day-bedit { color: var(--muted); }
/* Hover propio: el par --brand-soft-bg/-fg es el que garantiza contraste en los
   DOS temas (con --brand-dark el lápiz quedaba ciruela sobre ciruela y no se
   leía en oscuro). Se levanta 1px, se remarca con borde de marca y sombra corta,
   y el lápiz se inclina como al cogerlo. */
.mir-day-block .mir-day-bedit:hover {
  color: var(--brand-soft-fg);
  background: var(--brand-soft-bg);
  border-color: var(--brand);
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}
.mir-day-bedit .mir-icon { transition: transform 0.14s ease; }
.mir-day-bedit:hover .mir-icon { transform: rotate(-10deg); }
.mir-day-bedit:active { transform: translateY(0); box-shadow: none; }
.mir-day-bedit:focus-visible {
  outline: none; color: var(--brand-soft-fg); background: var(--brand-soft-bg);
  border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-soft-bg);
}
.pg-input.is-err { border-color: var(--danger); box-shadow: 0 0 0 1px var(--danger) inset; }
.mir-dtp-err .mir-dtp-btn { border-color: var(--danger); box-shadow: 0 0 0 1px var(--danger) inset; }
.mir-icon { width: 16px; height: 16px; display: inline-block; flex: 0 0 auto; }
.mir-icon--xs { width: 14px; height: 14px; }
.mir-icon--lg { width: 26px; height: 26px; }
.mir-icon-placeholder { width: 16px; height: 16px; display: inline-block; }
.mir-tip { position: relative; display: inline-flex; align-items: center; color: var(--muted); }
.mir-tip:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 999px; }
.mir-tip-ic { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px;
  border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--muted); }
.mir-tip-bubble { position: absolute; left: 50%; bottom: calc(100% + 10px); transform: translateX(-50%) translateY(4px);
  min-width: 180px; max-width: 260px; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--surface); color: var(--text); box-shadow: var(--shadow-lg); font-size: 0.78rem; line-height: 1.4;
  opacity: 0; pointer-events: none; transition: opacity 0.16s ease, transform 0.16s ease; z-index: 30; font-weight: 500; }
.mir-tip:hover .mir-tip-bubble, .mir-tip:focus-visible .mir-tip-bubble { opacity: 1; transform: translateX(-50%) translateY(0); }
/* Burbuja flotante fixed (nueva): sale de cualquier contenedor, aparece al instante. */
.mir-tip-pop { position: fixed; z-index: 9999; min-width: 160px; max-width: 260px; padding: 9px 11px;
  border-radius: 10px; border: 1px solid var(--border); background: var(--surface-overlay); color: var(--text);
  box-shadow: var(--shadow-lg); font-size: 0.78rem; line-height: 1.4; font-weight: 500;
  pointer-events: none; white-space: normal; }

.mir-modal-shell { animation: mir-modal-in 0.18s ease forwards; }
.mir-modal-shell.is-closing { animation: mir-modal-out 0.18s ease forwards; }
/* Sin `forwards`: al terminar no conserva transform → los tooltips fixed escapan del modal. */
.mir-modal-shell .pg-modal { animation: mir-modal-panel-in 0.18s ease; transform-origin: center top; }
.mir-modal-shell.is-closing .pg-modal { animation: mir-modal-panel-out 0.18s ease forwards; }
@keyframes mir-modal-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes mir-modal-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes mir-modal-panel-in { from { opacity: 0; transform: translateY(8px) scale(0.97); } to { opacity: 1; transform: translateY(0) scale(1); } }
@keyframes mir-modal-panel-out { from { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 0; transform: translateY(8px) scale(0.97); } }
.mir-modal-context { margin-bottom: 14px; padding: 10px 12px; border-radius: 10px; background: var(--surface-2); color: var(--text); }
.mir-modal-headerline { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.mir-modal-headerline-title { font-weight: 700; color: var(--text); }

/* ── MIR: modal chooser "¿qué crear?" ────────────────────────────────────── */
.mir-chooser { max-width: 560px; }
/* El cuerpo del modal permite scroll SOLO vertical (nunca lateral) si hay muchas cards. */
.mir-chooser .pg-modal-body { overflow-x: hidden; }
.mir-chooser-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 8px; }
.mir-chooser-grid--triple { grid-template-columns: repeat(3, 1fr); }
/* Cards = cuadrados perfectos (aspect-ratio 1/1), contenido centrado. */
.mir-chooser-card { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; text-align: center; aspect-ratio: 1 / 1; padding: 18px;
  border: 1px solid var(--border); border-radius: 16px; background: var(--surface-2);
  cursor: pointer; transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s; font-family: inherit; }
.mir-chooser-card:hover { border-color: var(--brand); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.mir-chooser-card.is-disabled, .mir-chooser-card:disabled { opacity: 0.6; cursor: not-allowed; transform: none; box-shadow: none; }
/* Icono dentro de una pastilla, como en el front original. */
.mir-chooser-card .mir-icon--lg { box-sizing: border-box; width: 52px; height: 52px; padding: 13px;
  border-radius: 14px; background: var(--brand-soft-bg); color: var(--brand); }
.mir-chooser-tip { position: absolute; top: 10px; right: 10px; }
/* La burbuja del tooltip abre HACIA ABAJO y anclada a la derecha para no cortarse
   contra el borde de la card ni forzar scroll lateral (fix imagen 3). */
.mir-chooser-tip .mir-tip-bubble { bottom: auto; top: calc(100% + 8px); left: auto; right: 0;
  transform: translateY(-4px); }
.mir-chooser-tip .mir-tip:hover .mir-tip-bubble,
.mir-chooser-tip .mir-tip:focus-visible .mir-tip-bubble { transform: translateY(0); }
.mir-chooser-title { font-weight: 700; color: var(--text-strong); font-size: 1rem; }
@media (max-width: 680px) { .mir-chooser-grid--triple { grid-template-columns: 1fr; } .mir-chooser-card { aspect-ratio: auto; } }
@media (max-width: 520px) { .mir-chooser-grid { grid-template-columns: 1fr; } .mir-chooser-card { aspect-ratio: auto; } }

/* ── MIR: schema fields del agente + sub-drawer ──────────────────────────── */
.mir-sf-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin: 16px 0 6px; }
.mir-sf-head h4 { margin: 0; font-size: 0.9rem; color: var(--text-strong); }
.mir-sf-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.mir-sf-row { display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); }
.mir-sf-grp { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--brand-dark); background: var(--brand-soft-bg); padding: 2px 7px; border-radius: 999px; }
.mir-sf-lbl { font-weight: 600; color: var(--text); flex: 1; }
.mir-sf-type { font-size: 0.78rem; color: var(--muted); }
.mir-sf-req { font-size: 0.7rem; color: var(--badge-warn-fg); background: var(--badge-warn-bg);
  padding: 2px 7px; border-radius: 999px; }
.mir-sf-acts { display: flex; gap: 4px; }
/* Responsive: en móvil los modales ocupan casi toda la pantalla. */
@media (max-width: 560px) {
  .mir-page .modal-card,
  .mir-page .modal-card.mir-modal-form,
  .mir-page .modal-card.mir-modal-sub { max-width: 100%; height: 88vh; }
}

/* ── MIR: árbol de recursos ──────────────────────────────────────────────── */
.mir-tree { display: flex; flex-direction: column; gap: 3px; overflow: hidden; }
.mir-tree-row { display: flex; align-items: stretch; gap: 8px; padding: 8px 10px;
  border: 1px solid var(--border-soft); border-radius: 8px; background: var(--surface);
  animation: mir-tree-row-in 0.28s cubic-bezier(0.22, 0.9, 0.22, 1) both;
  animation-delay: calc(var(--mir-depth, 0) * 22ms); transform-origin: top center;
  transition: background-color 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease; }
.mir-tree-row:hover { background: var(--surface-2); }
@keyframes mir-tree-row-in {
  from { opacity: 0; transform: translateY(-6px) scaleY(0.94); }
  60% { opacity: 1; transform: translateY(1px) scaleY(1.01); }
  to { opacity: 1; transform: none; }
}
.mir-tree-twirl { display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: none; cursor: pointer; color: var(--muted);
  width: 22px; min-width: 22px; height: auto; padding: 0; border-radius: 6px; align-self: stretch; }
.mir-tree-twirl:hover { color: var(--text); background: var(--surface-2); }
.mir-tree-twirl .mir-icon { width: 15px; height: 15px; transition: transform 0.22s ease; margin: auto; }
.mir-tree-twirl.is-open .mir-icon { transform: rotate(90deg); }
.mir-tree-twirl--empty { cursor: default; }
/* Región clicable de la fila: al pulsarla se expande/colapsa (como el front viejo). */
.mir-tree-main { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; min-height: 40px; padding: 8px 4px; border-radius: 6px;
  transition: background-color 0.18s ease, transform 0.18s ease; }
.mir-tree-main.is-expandable { cursor: pointer; }
.mir-tree-main.is-expandable:hover { background: var(--surface-2); transform: translateX(1px); }
.mir-tree-icon { font-size: 1rem; flex: 0 0 auto; }
.mir-tree-label { font-weight: 600; color: var(--text); flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Indicador sutil de que la fila tiene hijos: nº de recursos hijos directos. */
.mir-tree-count { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 3px;
  font-size: 0.7rem; font-weight: 600; color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px;
  padding: 1px 8px; white-space: nowrap; }
.mir-tree-inv { font-size: 0.74rem; color: var(--muted); white-space: nowrap; }
.mir-tree-actions { display: flex; gap: 6px; margin-left: auto; align-items: center; }
/* Codo ↳ de las filas hijas: la sangría sola no basta cuando el padre queda
   fuera de pantalla o hay varias ramas con el mismo nombre de hoja. */
.mir-tree-branch { flex: 0 0 auto; display: inline-flex; align-items: center; align-self: center;
  color: var(--muted-2); }
.mir-tree-branch .mir-icon { width: 15px; height: 15px; }
.mir-tree-row.is-child:hover .mir-tree-branch { color: var(--brand); }
/* Selección múltiple: barra lateral izquierda + tinte de marca, SIN casillas
   (mismo lenguaje que Ecommerce/Contactos: la fila entera es el control). */
.mir-tree-row.is-selected { background: var(--brand-soft-bg); border-color: var(--brand);
  box-shadow: inset 3px 0 0 var(--brand); }
/* Modo selección: toda la fila es seleccionable. */
.mir-tree-row.is-selectmode { cursor: pointer; user-select: none; }
.mir-tree-row.is-selectmode:hover { background: var(--control-bg-hover); }
.mir-tree-row.is-selectmode.is-selected:hover { background: var(--brand-soft-bg); }
.mir-tree-pub-btn { min-height: 36px; display: inline-flex; align-items: center; justify-content: center; }
.pg-input--sm { padding: 4px 8px; font-size: 0.8rem; }

/* Barra flotante de selección múltiple (`SelectionBar`, común a todos los
   módulos; MIR era el último con una propia y ya la usa). */
.tb-selbar { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 800;
  display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-radius: 14px; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow-lg); max-width: min(96vw, 1120px); }
.tb-selbar-count { font-size: 0.85rem; color: var(--text); white-space: nowrap; font-weight: 600; }
.tb-selbar-sep { width: 1px; height: 22px; background: var(--border); }

/* Modal de publicación / despublicación en cascada */
.mir-pub-box { width: min(620px, 100%); }
.mir-pub-intro { margin: 0 0 12px; font-size: 0.88rem; color: var(--muted); line-height: 1.45; }
.mir-pub-summary { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.mir-pub-chip { padding: 3px 10px; border-radius: 99px; font-size: 0.74rem; font-weight: 700; border: 1px solid transparent; }
/* Colores por estado con tokens del panel (se adaptan a claro/oscuro). */
.mir-pub-chip.is-ready { color: var(--badge-success-fg); background: var(--badge-success-bg); border-color: var(--badge-success-bg); }
.mir-pub-chip.is-err { color: var(--badge-danger-fg); background: var(--badge-danger-bg); border-color: var(--badge-danger-bg); }
.mir-pub-chip.is-live { color: var(--badge-neutral-fg); background: var(--badge-neutral-bg); border-color: var(--badge-neutral-bg); }
.mir-pub-chip.is-warn { color: var(--badge-warn-fg); background: var(--badge-warn-bg); border-color: var(--badge-warn-bg); }
.mir-pub-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; max-height: 46vh; overflow-y: auto; }
.mir-pub-row { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface); }
.mir-pub-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; background: var(--muted); }
.mir-pub-row.is-ready .mir-pub-dot { background: var(--badge-success-fg); }
.mir-pub-row.is-live .mir-pub-dot { background: var(--badge-neutral-fg); }
.mir-pub-row.is-err .mir-pub-dot { background: var(--badge-danger-fg); }
.mir-pub-row.is-warn .mir-pub-dot { background: var(--badge-warn-fg); }
.mir-pub-row.is-off .mir-pub-dot { background: var(--muted); }
.mir-pub-row.is-ready { border-color: var(--badge-success-bg); background: var(--badge-success-bg); }
.mir-pub-row.is-err { border-color: var(--badge-danger-bg); background: var(--badge-danger-bg); }
.mir-pub-row.is-warn { border-color: var(--badge-warn-bg); background: var(--badge-warn-bg); }
.mir-pub-main { flex: 1; min-width: 0; }
.mir-pub-name { font-size: 0.9rem; color: var(--text); font-weight: 500; }
.mir-pub-state { font-size: 0.76rem; font-weight: 700; flex-shrink: 0; color: var(--muted); }
.mir-pub-row.is-ready .mir-pub-state { color: var(--badge-success-fg); }
.mir-pub-row.is-err .mir-pub-state { color: var(--badge-danger-fg); }
.mir-pub-row.is-warn .mir-pub-state { color: var(--badge-warn-fg); }
.mir-pub-progress { flex: 1; display: flex; align-items: center; gap: 12px; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: 10px; background: var(--surface-2); color: var(--text); font-size: 0.85rem; font-weight: 600; min-width: 0; }
.mir-progress-track { position: relative; flex: 1; min-width: 120px; height: 10px; border-radius: 999px; overflow: hidden;
  background: var(--control-bg); border: 1px solid var(--border-soft); }
.mir-progress-fill { position: absolute; inset: 0 auto 0 0; width: 0; border-radius: inherit;
  background: linear-gradient(90deg, var(--brand), var(--brand-dark)); transition: width 0.18s ease; }
.mir-progress-label { white-space: nowrap; }
.mir-rstatus { font-size: 0.7rem; padding: 2px 8px; border-radius: 999px; font-weight: 600; white-space: nowrap; }
.mir-rstatus--draft { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }
.mir-rstatus--ready { background: var(--badge-info-bg); color: var(--badge-info-fg); }
.mir-rstatus--live { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.mir-rstatus--paused { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.mir-rstatus--archived { background: var(--badge-neutral-bg); color: var(--muted); }

/* ── MIR: popovers de toolbar (filtros / ordenar) ────────────────────────── */
/* Ancho FIJO (no flex-grow) para que la barra de búsqueda sea idéntica en las 4
   tabs; antes flex:1 la hacía crecer distinto según los botones/spacer vecinos. */
.mir-toolbar-search { flex: 0 0 auto; width: 360px; max-width: 100%; }
.mir-pop-wrap { position: relative; display: inline-block; }
.mir-pop-dismiss { position: fixed; inset: 0; z-index: 19; background: transparent; border: none; padding: 0; }
.mir-pop { position: absolute; top: calc(100% + 4px); left: 0; z-index: 20; min-width: 220px;
  background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow-lg); padding: 12px; display: flex; flex-direction: column; gap: 10px; }
.mir-pop label { display: flex; flex-direction: column; gap: 4px; font-size: 0.8rem; color: var(--muted); }
.mir-pop--sort { min-width: 200px; padding: 6px; }
.mir-sort-opt { text-align: left; padding: 7px 10px; background: transparent; border: none;
  border-radius: 6px; cursor: pointer; font-family: inherit; font-size: 0.85rem; color: var(--text); }
.mir-sort-opt:hover { background: var(--surface-2); }
.mir-sort-opt.is-active { background: var(--brand-soft-bg); color: var(--brand-dark); font-weight: 600; }
.pg-btn.is-active { border-color: var(--brand); color: var(--brand-dark); }
.mir-toolbar .pg-btn { display: inline-flex; align-items: center; gap: 6px; }
/* Empuja los botones de acción (limpiar / carga / nuevo) hacia la derecha. */
.mir-toolbar-spacer { flex: 1 1 auto; }
/* Botones solo-icono del módulo: conservar comportamiento general. */
.mir-icon-btn { position: relative; }
/* Descendiente (no `>`): el <Icon> envuelve el svg en spans display:contents. */
.mir-icon-btn .mir-icon { margin: 0; }
/* Acciones de fila en recursos: los iconos ESTÁNDAR del panel (`.icon-btn`), un
   pelo más grandes porque la fila del árbol es más alta que la de una tabla. */
.mir-tree-actions .icon-btn { width: 34px; min-width: 34px; height: 34px; }
/* Contador de filtros activos sobre el botón solo-icono. */
.mir-btn-badge { position: absolute; top: -6px; right: -6px; min-width: 16px; height: 16px;
  padding: 0 4px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.65rem; font-weight: 700; line-height: 1; color: #fff; background: var(--brand);
  border-radius: 999px; }

/* ── MIR: bloques (vista lista + agenda/calendario) ──────────────────────── */
.mir-viewswitch { display: inline-flex; gap: 2px; }
.mir-row--off { opacity: 0.55; }
.mir-agenda { display: flex; flex-direction: column; gap: 16px; }
.mir-agenda-date { font-weight: 700; color: var(--text-strong); font-size: 0.9rem;
  margin-bottom: 6px; padding-bottom: 4px; border-bottom: 1px solid var(--border); }
.mir-agenda-item { display: flex; align-items: center; gap: 12px; padding: 8px 10px;
  border: 1px solid var(--border-soft); border-radius: 8px; background: var(--surface);
  cursor: pointer; margin-bottom: 4px; }
.mir-agenda-item:hover { background: var(--surface-2); }
.mir-agenda-time { font-weight: 600; color: var(--brand-dark); min-width: 48px; }
.mir-agenda-name { font-weight: 600; color: var(--text); flex: 1; }
.mir-agenda-res { font-size: 0.8rem; color: var(--muted); }

/* ── MIR: bloques — calendario por mes ───────────────────────────────────── */
/* Barra de mes: ‹ Junio 2026 › + Hoy */
.mir-cal-bar { display: flex; align-items: center; gap: 6px; margin-bottom: 12px; }
.mir-cal-nav { border: 1px solid var(--border); background: var(--surface); color: var(--text);
  width: 30px; height: 30px; border-radius: 8px; cursor: pointer; font-size: 1.05rem; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.mir-cal-nav:hover { background: var(--control-bg-hover); }
.mir-cal-title { border: 1px solid transparent; background: transparent; cursor: pointer;
  font-weight: 700; color: var(--text-strong); font-size: 1.05rem; padding: 4px 10px; border-radius: 8px;
  min-width: 150px; text-align: center; }
.mir-cal-title:hover { background: var(--control-bg-hover); border-color: var(--border); }
.mir-cal-today { margin-left: 4px; }
/* Dropdown de mes/año */
.mir-cal-menu { min-width: 240px; padding: 10px; }
.mir-cal-menu-year { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.mir-cal-menu-year > span { font-weight: 700; color: var(--text-strong); }
.mir-cal-menu-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.mir-cal-menu-m { border: 1px solid var(--border); background: var(--surface); color: var(--text);
  border-radius: 8px; padding: 8px 4px; cursor: pointer; text-transform: capitalize; font-size: 0.82rem; }
.mir-cal-menu-m:hover { background: var(--control-bg-hover); }
.mir-cal-menu-m.is-sel { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 700; }
/* Cabecera de días de la semana */
.mir-cal-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; margin-bottom: 8px; }
.mir-cal-dow > span { text-align: center; font-size: 0.72rem; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.04em; }
/* Rejilla de días */
.mir-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; }
.mir-cal-cell { position: relative; min-height: 92px; border: 1px solid var(--border);
  border-radius: 12px; background: var(--surface); padding: 6px 8px; cursor: pointer;
  transition: border-color 0.12s ease, box-shadow 0.12s ease, transform 0.12s ease; display: flex; flex-direction: column; }
.mir-cal-cell:hover { border-color: var(--brand); box-shadow: var(--shadow-md); transform: translateY(-1px); }
.mir-cal-cell--empty { background: transparent; border: none; cursor: default; min-height: 0; }
.mir-cal-cell--empty:hover { box-shadow: none; transform: none; }
.mir-cal-cell.is-today { border-color: var(--brand); box-shadow: inset 0 0 0 1px var(--brand); }
.mir-cal-cell-top { display: flex; align-items: center; justify-content: space-between; }
.mir-cal-daynum { font-weight: 700; color: var(--text); font-size: 0.9rem; }
.mir-cal-cell.is-today .mir-cal-daynum { color: var(--brand-dark); }
.mir-cal-add { opacity: 0; border: 1px solid var(--border); background: var(--surface); color: var(--brand-dark);
  width: 22px; height: 22px; border-radius: 6px; cursor: pointer; line-height: 1; font-size: 1rem;
  display: inline-flex; align-items: center; justify-content: center; transition: opacity 0.12s ease; }
.mir-cal-cell:hover .mir-cal-add { opacity: 1; }
.mir-cal-add:hover { background: var(--brand); border-color: var(--brand); color: #fff; }
/* Señal sutil de "hay bloques" (no se pintan los bloques enteros) */
.mir-cal-hint { margin-top: auto; display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.72rem; color: var(--muted); font-weight: 600; }
.mir-cal-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--brand); flex: 0 0 auto; }
@media (max-width: 640px) {
  .mir-cal-cell { min-height: 64px; padding: 4px 6px; }
  .mir-cal-cnt { display: none; }
  .mir-cal-add { opacity: 1; }
}

/* ── MIR: bloques — vista día (drill) con efecto hacia delante ────────────── */
.mir-day { animation: mir-day-in 0.22s cubic-bezier(0.2, 0.7, 0.2, 1); }
@keyframes mir-day-in { from { opacity: 0; transform: scale(0.96) translateX(14px); } to { opacity: 1; transform: none; } }
/* Salida: inversa a la entrada (se aleja hacia la derecha y desvanece). */
.mir-day.is-closing { animation: mir-day-out 0.2s cubic-bezier(0.4, 0, 1, 1) forwards; }
@keyframes mir-day-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: scale(0.96) translateX(14px); } }
.mir-day-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.mir-day-title { margin: 0; font-size: 1.02rem; color: var(--text-strong); flex: 1; text-transform: capitalize; }
/* Lista de horas contenida y con scroll (no se extiende indefinidamente).
   Barra estilo sidebar: fina, sin carril reservado y solo visible en hover. */
.mir-day-hours { display: flex; flex-direction: column; max-height: calc(100vh - 300px); min-height: 200px;
  overflow-y: auto; border: 1px solid var(--border-soft); border-radius: 10px;
  scrollbar-width: thin; scrollbar-color: transparent transparent; }
.mir-day-hours:hover { scrollbar-color: var(--control-border) transparent; }
.mir-day-hours::-webkit-scrollbar { width: 8px; }
.mir-day-hours::-webkit-scrollbar-track { background: transparent; }
.mir-day-hours::-webkit-scrollbar-thumb {
  background: transparent; border: 2px solid transparent; background-clip: padding-box;
  border-radius: 8px; transition: background-color 0.2s ease;
}
.mir-day-hours:hover::-webkit-scrollbar-thumb { background: var(--control-border); background-clip: padding-box; }
.mir-day-hours:hover::-webkit-scrollbar-thumb:hover { background: var(--muted-2); background-clip: padding-box; }
.mir-day-hour { display: flex; align-items: stretch; gap: 10px; border-top: 1px solid var(--border-soft);
  min-height: 44px; padding: 4px 0; }
.mir-day-hour:last-child { border-bottom: 1px solid var(--border-soft); }
.mir-day-hlabel { flex: 0 0 48px; font-size: 0.74rem; color: var(--muted); font-weight: 600; padding-top: 4px; }
.mir-day-hbody { flex: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.mir-day-block { display: inline-flex; align-items: center; gap: 8px; padding: 6px 10px;
  border: 1px solid var(--border); border-left: 3px solid var(--brand); border-radius: 8px;
  background: var(--surface); cursor: pointer; font-family: inherit; text-align: left; }
.mir-day-block:hover { background: var(--surface-2); }
.mir-day-btime { font-weight: 700; color: var(--brand-dark); font-size: 0.8rem; }
.mir-day-bname { font-weight: 600; color: var(--text); font-size: 0.84rem; }
.mir-day-bres { font-size: 0.76rem; color: var(--muted); }
.mir-day-hadd { opacity: 0; border: 1px dashed var(--border); background: transparent; color: var(--muted);
  width: 26px; height: 26px; border-radius: 6px; cursor: pointer; line-height: 1; font-size: 1rem;
  display: inline-flex; align-items: center; justify-content: center; transition: opacity 0.12s ease; }
.mir-day-hour:hover .mir-day-hadd { opacity: 1; }
.mir-day-hadd:hover { border-style: solid; border-color: var(--brand); color: var(--brand-dark); background: var(--surface); }

/* ── MIR: selector fecha+hora propio (MirDateTime) ───────────────────────── */
.mir-dtp { position: relative; }
.mir-dtp-btn { width: 100%; text-align: left; border: 1px solid var(--border); background: var(--surface);
  color: var(--text); border-radius: 8px; padding: 8px 10px; cursor: pointer; font-family: inherit; font-size: 0.86rem; }
.mir-dtp-btn:hover { border-color: var(--brand); }
.mir-dtp-btn.is-empty { color: var(--muted); }
.mir-dtp-pop { position: absolute; top: calc(100% + 4px); left: 0; z-index: 20; width: 260px;
  background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow-lg); padding: 10px; }
.mir-dtp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.mir-dtp-title { font-weight: 700; color: var(--text-strong); font-size: 0.86rem; }
.mir-dtp-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-bottom: 4px; }
.mir-dtp-dow > span { text-align: center; font-size: 0.66rem; font-weight: 700; color: var(--muted); }
.mir-dtp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.mir-dtp-day { border: none; background: transparent; color: var(--text); border-radius: 7px;
  aspect-ratio: 1 / 1; cursor: pointer; font-size: 0.8rem; display: inline-flex; align-items: center; justify-content: center; }
.mir-dtp-day:hover { background: var(--control-bg-hover); }
.mir-dtp-day--empty { background: transparent; cursor: default; }
.mir-dtp-day--empty:hover { background: transparent; }
.mir-dtp-day.is-sel { background: var(--brand); color: #fff; font-weight: 700; }
.mir-dtp-foot { display: flex; align-items: center; gap: 6px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border-soft); }
.mir-dtp-foot-lbl { font-size: 0.78rem; color: var(--muted); font-weight: 600; margin-right: 2px; }
.mir-dtp-sel { padding: 5px 6px; min-width: 62px; }
.mir-dtp-colon { color: var(--muted); font-weight: 700; }

/* ── Reservas: Tablero (kanban) ── */
/* Scroll horizontal con barra estilo sidebar (fina, sin carril reservado, solo
   en hover). Se retiró el doble-flip vertical que la subía arriba: existía
   porque la barra era gruesa y molestaba abajo; con la barra auto-oculta el
   truco solo complicaba (invertía el eje de todo el tablero). */
.mir-kanban-wrap {
  overflow-x: auto; overflow-y: hidden; padding: 2px 0;
  scrollbar-width: thin; scrollbar-color: transparent transparent;
}
.mir-kanban-wrap:hover { scrollbar-color: var(--control-border) transparent; }
.mir-kanban-wrap::-webkit-scrollbar { height: 8px; }
.mir-kanban-wrap::-webkit-scrollbar-track { background: transparent; }
.mir-kanban-wrap::-webkit-scrollbar-thumb {
  background: transparent; border: 2px solid transparent; background-clip: padding-box;
  border-radius: 8px; transition: background-color 0.2s ease;
}
.mir-kanban-wrap:hover::-webkit-scrollbar-thumb { background: var(--control-border); background-clip: padding-box; }
.mir-kanban-wrap:hover::-webkit-scrollbar-thumb:hover { background: var(--muted-2); background-clip: padding-box; }
.mir-kanban { display: flex; gap: 12px; align-items: stretch; }
/* Altura fija responsiva: cubre el alto de la pantalla actual; el cuerpo hace scroll interno. */
.mir-kcol { flex: 0 0 240px; min-width: 240px; background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: 12px; display: flex; flex-direction: column;
  height: calc(100vh - 250px); min-height: 420px; }
.mir-kcol.is-drop { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-soft-bg) inset; }
.mir-kcol-head { display: flex; align-items: center; gap: 8px; padding: 10px 12px;
  border-bottom: 1px solid var(--border-soft); border-radius: 12px 12px 0 0; }
.mir-kcol-name { font-weight: 700; font-size: 0.85rem; color: var(--text); flex: 1; }
/* Cabecera pintada del color de su estado. */
.mir-kcol-head[data-status="HOLD"]      { background: var(--badge-warn-bg); }
.mir-kcol-head[data-status="HOLD"] .mir-kcol-name      { color: var(--badge-warn-fg); }
.mir-kcol-head[data-status="CONFIRMED"] { background: var(--badge-info-bg); }
.mir-kcol-head[data-status="CONFIRMED"] .mir-kcol-name { color: var(--badge-info-fg); }
.mir-kcol-head[data-status="COMPLETED"] { background: var(--badge-success-bg); }
.mir-kcol-head[data-status="COMPLETED"] .mir-kcol-name { color: var(--badge-success-fg); }
.mir-kcol-head[data-status="CANCELLED"] { background: var(--badge-danger-bg); }
.mir-kcol-head[data-status="CANCELLED"] .mir-kcol-name { color: var(--badge-danger-fg); }
.mir-kcol-head[data-status="EXPIRED"]   { background: var(--badge-neutral-bg); }
.mir-kcol-head[data-status="EXPIRED"] .mir-kcol-name   { color: var(--badge-neutral-fg); }
.mir-kcol-count { font-size: 0.75rem; font-weight: 700; background: var(--control-bg); color: var(--muted);
  border-radius: 999px; padding: 1px 8px; min-width: 22px; text-align: center; }
.mir-kcol-body { padding: 8px; display: flex; flex-direction: column; gap: 8px; overflow-y: auto; flex: 1;
  scrollbar-width: thin; scrollbar-color: transparent transparent; }
.mir-kcol-body:hover { scrollbar-color: var(--control-border) transparent; }
.mir-kcol-body::-webkit-scrollbar { width: 8px; }
.mir-kcol-body::-webkit-scrollbar-track { background: transparent; }
.mir-kcol-body::-webkit-scrollbar-thumb {
  background: transparent; border: 2px solid transparent; background-clip: padding-box;
  border-radius: 8px; transition: background-color 0.2s ease;
}
.mir-kcol-body:hover::-webkit-scrollbar-thumb { background: var(--control-border); background-clip: padding-box; }
.mir-kcol-body:hover::-webkit-scrollbar-thumb:hover { background: var(--muted-2); background-clip: padding-box; }
.mir-kcolempty { font-size: 0.78rem; color: var(--muted); text-align: center; padding: 16px 4px; }
.mir-kcard { background: var(--surface); border: 1px solid var(--border-soft); border-radius: 10px;
  padding: 10px; cursor: pointer; box-shadow: var(--shadow-sm); user-select: none;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease; }
.mir-kcard:hover { border-color: var(--brand); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.mir-kcard-title { font-weight: 600; font-size: 0.85rem; color: var(--text); }
.mir-kcard-holder { font-size: 0.8rem; color: var(--text-1); margin-top: 2px; }
.mir-kcard-meta { font-size: 0.75rem; color: var(--muted); margin-top: 2px; }
.mir-kcard-actions { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }

/* ── Reservas / MIR: Tab Exportar ── */
.mir-export { padding: 4px 0; }
.mir-export-hint { font-size: 0.85rem; color: var(--muted); margin: 0 0 14px; }
.mir-export-cards { display: flex; flex-wrap: wrap; gap: 14px; }
.mir-export-card { flex: 0 0 240px; background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: 12px; padding: 16px; display: flex; flex-direction: column; gap: 8px; box-shadow: var(--shadow-sm); }
.mir-export-ico { font-size: 1.8rem; }
.mir-export-name { font-weight: 700; font-size: 0.95rem; color: var(--text); flex: 1;
  display: inline-flex; align-items: center; gap: 6px; }
.mir-export-card .pg-btn { margin-top: 6px; }

/* ── MIR: Carga masiva (importar CSV) ── */
.mir-bulk-modal { width: min(760px, 100%); }
.mir-bulk-actions { display: flex; justify-content: flex-end; margin-bottom: 12px; }
.mir-bulk-filerow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.mir-bulk-filerow .mir-bulk-file { flex: 1 1 200px; margin-bottom: 0; }
.mir-bulk-file { display: flex; align-items: center; gap: 8px; border: 1.5px dashed var(--control-border);
  border-radius: 10px; padding: 12px; cursor: pointer; background: var(--control-bg); margin-bottom: 12px;
  transition: transform 0.15s ease, border-color 0.15s ease, background 0.15s ease; }
.mir-bulk-file:hover { transform: scale(1.015); border-color: var(--brand); background: var(--control-bg-hover); }
.mir-bulk-file.is-drag { transform: scale(1.03); border-color: var(--brand); border-style: solid;
  background: var(--brand-soft-bg); box-shadow: 0 0 0 3px var(--brand-soft-bg); }
.mir-bulk-file input[type="file"] { display: none; }
.mir-bulk-file span { font-size: 0.85rem; color: var(--text-1); }
.mir-bulk-empty { font-size: 0.82rem; color: var(--muted); padding: 6px 0; }
.mir-bulk-legend { border: 1px solid var(--border-soft); border-radius: 10px; background: var(--surface-2);
  padding: 10px 12px; margin-bottom: 12px; }
.mir-bulk-legend-title { font-weight: 700; font-size: 0.82rem; color: var(--text); margin-bottom: 6px; }
.mir-bulk-legend ul { margin: 0; padding-left: 16px; display: flex; flex-direction: column; gap: 3px; }
.mir-bulk-legend li { font-size: 0.78rem; color: var(--muted); line-height: 1.4; }
.mir-bulk-legend strong { color: var(--text); }
.mir-bulk-legend em { color: var(--badge-warn-fg); font-style: normal; font-weight: 600; }
.mir-bulk-summary { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.mir-bulk-chip { font-size: 0.78rem; font-weight: 700; border-radius: 999px; padding: 3px 10px;
  background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }
.mir-bulk-chip--new { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.mir-bulk-chip--edit { background: var(--badge-info-bg); color: var(--badge-info-fg); }
.mir-bulk-chip--del { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.mir-bulk-warns { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
.mir-bulk-warn { font-size: 0.78rem; color: var(--badge-warn-fg); background: var(--badge-warn-bg);
  border-radius: 8px; padding: 6px 10px; }
.mir-bulk-errors { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px;
  border: 1px solid var(--badge-danger-fg); border-radius: 10px; padding: 8px 10px; background: var(--badge-danger-bg); }
.mir-bulk-errors-title { font-size: 0.8rem; font-weight: 700; color: var(--badge-danger-fg); }
.mir-bulk-error { font-size: 0.78rem; color: var(--badge-danger-fg); }
.mir-bulk-lists { display: flex; flex-direction: column; gap: 12px; margin-top: 10px; }
.mir-bulk-group h4 { margin: 0 0 6px; font-size: 0.82rem; color: var(--muted); }
.mir-bulk-item { font-size: 0.82rem; color: var(--text); padding: 4px 8px; border-radius: 6px;
  background: var(--surface-2); margin-bottom: 3px; display: flex; align-items: baseline; gap: 8px;
  justify-content: space-between; }
.mir-bulk-item-name { font-weight: 600; }
.mir-bulk-item-meta { font-size: 0.75rem; color: var(--muted); text-align: right; }
.mir-bulk-item--del { color: var(--badge-danger-fg); text-decoration: line-through; }

.mir-delete-modal { width: min(620px, 100%); }
.mir-delete-lead { margin: 0 0 12px; color: var(--text); }
.mir-delete-summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 14px; }
.mir-delete-stat { padding: 12px; border-radius: 12px; background: var(--surface-2); border: 1px solid var(--border); color: var(--text); font-size: 0.84rem; }
.mir-delete-list { margin-top: 12px; }
.mir-delete-list h4 { margin: 0 0 8px; font-size: 0.82rem; color: var(--muted); }
.mir-delete-item { padding: 7px 10px; border-radius: 8px; background: var(--surface-2); color: var(--text); margin-bottom: 6px; font-size: 0.82rem; }
.mir-delete-loading { margin-top: 12px; color: var(--muted); font-size: 0.82rem; }
.mir-delete-arm { margin-top: 12px; padding: 12px; border-radius: 12px; background: rgba(220,38,38,0.08); color: var(--danger); border: 1px solid rgba(220,38,38,0.35); font-size: 0.84rem; }
.mir-res-actions { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 10px; margin-top: 6px; border-top: 1px solid var(--border-soft); }
@media (max-width: 640px) { .mir-delete-summary { grid-template-columns: 1fr; } }

/* ═══════════════════════════════════════════════════════════════════════════
   MARKETPLACE (pages/marketplace.rs) — tokens only, ver reglas de theming
   ═══════════════════════════════════════════════════════════════════════════ */

.mk-page { min-height: 100vh; background: var(--bg); }
.mk-body { padding: 20px 24px; max-width: 1100px; margin: 0 auto; }

.mk-install-card { display: flex; justify-content: space-between; align-items: center; gap: 16px;
  flex-wrap: wrap; background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 16px 18px; margin-bottom: 14px; box-shadow: var(--shadow-sm); }
.mk-install-title { margin: 0 0 4px; font-size: 0.95rem; color: var(--text-strong); }
.mk-install-hint { margin: 0; font-size: 0.8rem; color: var(--muted); max-width: 560px; }
.mk-install-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.mk-force { display: flex; align-items: center; gap: 6px; font-size: 0.78rem; color: var(--muted);
  cursor: pointer; user-select: none; }
.mk-upload-btn { display: inline-flex; align-items: center; cursor: pointer; }
.mk-upload-btn.is-busy { opacity: 0.7; cursor: wait; }

.mk-banner { padding: 10px 14px; border-radius: 10px; font-size: 0.84rem; margin-bottom: 12px; }
.mk-banner--error { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.mk-banner--ok { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.mk-banner--warn { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.mk-banner--info { background: var(--badge-info-bg); color: var(--badge-info-fg); }

/* Módulos activables (eje activo/catalogable) */
.mk-modules { max-width: 720px; }
.mk-mod-hint { color: var(--muted); font-size: 0.85rem; margin: 0 0 14px; line-height: 1.45; }
.mk-mod-list { display: flex; flex-direction: column; gap: 8px; }
.mk-mod-row { display: flex; align-items: center; gap: 12px; padding: 10px 14px;
    background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
.mk-mod-icon { font-size: 1.1rem; width: 1.4rem; text-align: center; }
.mk-mod-name { flex: 1; color: var(--text); font-weight: 500; }
.mk-mod-btn { min-width: 104px; }

/* Funcionalidades (SMTP) */
.mk-feat-block { margin-bottom: 22px; }
.mk-feat-title { font-size: 0.9rem; color: var(--text-strong); margin: 0 0 8px; }
.mk-feat-main { flex: 1; display: flex; flex-direction: column; gap: 2px; }
.mk-feat-sub { font-size: 0.78rem; color: var(--muted); }
.mk-feat-input { background: var(--control-bg); border: 1px solid var(--control-border);
    color: var(--text); border-radius: 8px; padding: 6px 10px; font-size: 0.84rem; width: 200px; }
.mk-feat-hint { color: var(--muted); font-size: 0.8rem; margin: 8px 0 0; }

.mk-table { margin-bottom: 10px; }
.mk-row { display: grid; grid-template-columns: 1.6fr 110px 90px 1fr 130px 210px; gap: 8px;
  padding: 10px 14px; border-bottom: 1px solid var(--border); font-size: 0.83rem;
  color: var(--text); align-items: center; }
.mk-row:last-child { border-bottom: none; }
.mk-row > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mk-row--head { font-weight: 700; background: var(--surface-2); color: var(--muted);
  text-transform: uppercase; font-size: 0.68rem; letter-spacing: 0.03em; }
.mk-name { font-weight: 600; color: var(--text-strong); }
.mk-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.78rem; }
.mk-tag--theme { background: var(--badge-purple-bg); color: var(--badge-purple-fg); }
.mk-tag--module { background: var(--badge-info-bg); color: var(--badge-info-fg); }
.mk-footnote { font-size: 0.75rem; color: var(--muted-2); margin-top: 6px; }

/* ── Marketplace F4: tabs + catálogo + modal de publicación ─────────────── */
.mk-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border);
    margin-bottom: 18px; }
.mk-tab { appearance: none; border: none; background: transparent; cursor: pointer;
    padding: 10px 16px; font-size: 0.85rem; color: var(--muted); font-weight: 600;
    border-bottom: 2px solid transparent; margin-bottom: -1px; }
.mk-tab:hover { color: var(--text); }
.mk-tab.active { color: var(--brand); border-bottom-color: var(--brand); }

.mk-catalog-bar { display: flex; justify-content: space-between; align-items: center;
    gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }

.mk-catalog-grid { display: grid; gap: 16px;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }

.mk-card { display: flex; flex-direction: column; background: var(--surface);
    border: 1px solid var(--border); border-radius: 14px; overflow: hidden; }
.mk-card-logo { height: 120px; display: flex; align-items: center; justify-content: center;
    background: var(--surface-2); border-bottom: 1px solid var(--border); }
.mk-card-logo img { max-width: 100%; max-height: 100%; object-fit: contain; }
.mk-card-logo-ph { font-size: 2.4rem; opacity: 0.7; }
.mk-card-body { padding: 14px 16px; flex: 1; }
.mk-card-head { display: flex; align-items: center; justify-content: space-between;
    gap: 8px; margin-bottom: 6px; }
.mk-card-title { margin: 0; font-size: 0.95rem; color: var(--text-strong);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mk-card-summary { margin: 0 0 10px; font-size: 0.82rem; color: var(--muted);
    line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; }
.mk-card-meta { display: flex; align-items: center; justify-content: space-between;
    gap: 8px; font-size: 0.76rem; color: var(--muted-2); }
.mk-card-pub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mk-card-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.mk-chip { font-size: 0.7rem; padding: 2px 8px; border-radius: 999px;
    background: var(--surface-2); color: var(--muted); border: 1px solid var(--border); }
.mk-card-actions { padding: 12px 16px; border-top: 1px solid var(--border);
    display: flex; justify-content: flex-end; }

.mk-modal-backdrop { position: fixed; inset: 0; background: var(--scrim);
    display: flex; align-items: flex-start; justify-content: center; z-index: 1000;
    padding: 5vh 20px; overflow-y: auto; }
.mk-modal { background: var(--surface-overlay); border: 1px solid var(--border);
    border-radius: 16px; box-shadow: var(--shadow-lg); width: 100%; max-width: 520px;
    max-height: 90vh; overflow-y: auto; padding: 22px 24px; margin: auto; }
/* Modal de formulario: sin recorte para que las tooltips salgan fuera del modal
   (el scroll pasa al backdrop). */
.mk-modal--form { overflow: visible; max-height: none; }
.mk-modal-head { display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 4px; }
.mk-modal-head h3 { margin: 0; font-size: 1.02rem; color: var(--text-strong); }
.mk-modal-x { appearance: none; border: none; background: transparent; cursor: pointer;
    font-size: 1rem; color: var(--muted); padding: 4px 8px; border-radius: 8px; }
.mk-modal-x:hover { background: var(--control-bg-hover); color: var(--text); }
.mk-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.mk-field > span { font-size: 0.78rem; color: var(--muted); font-weight: 600; }
.mk-modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 8px;
    flex-wrap: wrap; align-items: center; }
/* Una acción NUNCA parte su etiqueta en dos renglones: un botón a media palabra
   se lee como un fallo de maqueta. Si no caben, envuelve la fila (arriba). */
.mk-modal-actions .pg-btn, .mk-modal-actions .mk-linkbtn { white-space: nowrap; }
/* El enlace-acción se pinta como botón: sin el subrayado global de los <a>. */
.mk-modal-actions a.pg-btn { text-decoration: none; display: inline-flex;
    align-items: center; }
.mk-modal--sm { max-width: 420px; }
/* Modal de confirmación con TRES acciones (aviso de tools): a 420 px la tercera
   se salía y las etiquetas se partían. */
.mk-modal--acts3 { max-width: 540px; }
.mk-confirm-msg { margin: 4px 0 18px; font-size: 0.88rem; color: var(--muted); line-height: 1.5; }
/* Secciones plegables de la ficha (Herramientas, Versiones). `<details>` nativo:
   sin JS, accesible con teclado y recordando su estado mientras la ficha vive.
   Cerradas por defecto — son detalle, no lo primero que se mira. */
.mk-fold { border-top: 1px solid var(--border); padding: 14px 0 0; margin-top: 18px; }
.mk-fold-head { display: flex; align-items: center; gap: 10px; cursor: pointer;
    list-style: none; padding: 2px 0; user-select: none; }
.mk-fold-head::-webkit-details-marker { display: none; }   /* triángulo nativo */
.mk-fold-head h3 { margin: 0; font-size: 0.94rem; color: var(--text-strong); }
.mk-fold-head:hover h3 { color: var(--brand); }
.mk-fold-arrow { display: inline-block; font-size: 1.1rem; line-height: 1;
    color: var(--muted); transition: transform 0.16s ease; }
.mk-fold[open] .mk-fold-arrow { transform: rotate(90deg); }
.mk-fold-badge { min-width: 20px; padding: 1px 7px; border-radius: 999px;
    background: var(--brand-soft-bg); color: var(--brand-soft-fg);
    font-size: 0.72rem; font-weight: 600; text-align: center; }
.mk-fold-body { padding-top: 12px; }

/* Apertura/cierre animados. Dos capas, y hacen falta las dos:
   · `::details-content` + `interpolate-size` anima la altura REAL en los dos
     sentidos (abrir y cerrar), pero solo existe en navegadores recientes;
   · el keyframe de respaldo cubre al resto — solo puede animar la apertura,
     porque al cerrar el contenido desaparece del árbol antes de poder animarlo.
   Van separados por `@supports` para que no se animen a la vez y salga doble. */
@keyframes mk-fold-in {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: translateY(0); }
}
.mk-fold[open] > .mk-fold-body { animation: mk-fold-in 0.18s ease-out; }

@supports selector(details::details-content) {
    .mk-fold[open] > .mk-fold-body { animation: none; }
    .mk-fold { interpolate-size: allow-keywords; }
    .mk-fold::details-content {
        block-size: 0;
        overflow: hidden;
        opacity: 0;
        transition: block-size 0.22s ease, content-visibility 0.22s allow-discrete,
                    opacity 0.18s ease;
    }
    .mk-fold[open]::details-content { block-size: auto; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .mk-fold[open] > .mk-fold-body { animation: none; }
    .mk-fold::details-content { transition: none; }
}

/* Tools del agente en la ficha del módulo. */
.mk-tools { display: flex; flex-direction: column; gap: 10px; }
.mk-tool-row { background: var(--surface-2); border: 1px solid var(--border-soft);
    border-radius: 10px; padding: 10px 12px; }
.mk-tool-head { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.mk-tool-desc { margin: 0; font-size: 0.84rem; color: var(--muted); line-height: 1.45; }
.mk-chip--on { background: var(--badge-success-bg); color: var(--badge-success-fg); }

/* Lista de tools del agente que trae un módulo recién instalado (aviso).
   Viñetas, una por línea: de corrido y separadas por puntos no se leen como
   una lista de cosas que el agente podrá hacer, que es lo que son. */
.mk-tools-ul { margin: 0 0 18px; padding: 10px 12px 10px 28px; list-style: disc;
    background: var(--surface-2); border: 1px solid var(--border-soft);
    border-radius: 8px; font-size: 0.82rem; color: var(--text); }
.mk-tools-ul li { margin: 2px 0; word-break: break-word; }
/* Check de purga al desinstalar un módulo. */
.mk-purge { display: flex; gap: 10px; align-items: flex-start; margin: 0 0 18px;
    padding: 10px 12px; background: var(--surface-2);
    border: 1px solid var(--border-soft); border-radius: 10px; cursor: pointer; }
.mk-purge input { margin-top: 2px; flex: 0 0 auto; }
.mk-purge span { display: flex; flex-direction: column; gap: 3px; }
.mk-purge strong { font-size: 0.84rem; font-weight: 600; color: var(--text-strong); }
.mk-purge em { font-style: normal; font-size: 0.78rem; color: var(--muted); line-height: 1.45; }

/* Salida neutra de un modal: texto, sin caja. No compite con las acciones. */
/* Sin subrayado en reposo: junto a dos botones reales, un texto subrayado se leía
   como un enlace roto. Aparece al pasar por encima, que es cuando hace falta la
   señal de "esto se pulsa". */
.mk-linkbtn { background: none; border: none; padding: 6px 4px; margin-right: auto;
    font: inherit; font-size: 0.84rem; color: var(--muted); cursor: pointer;
    text-decoration: none; text-underline-offset: 3px; }
.mk-linkbtn:hover:not(:disabled) { color: var(--text); text-decoration: underline; }
.mk-linkbtn:disabled { opacity: 0.5; cursor: default; }

/* ── Catálogo v2: cards compactas expandibles ──────────────────────────── */
.mk-cards { display: grid; gap: 14px; align-items: start;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.mk-card2 { display: flex; background: var(--surface); border: 1px solid var(--border);
    border-radius: 14px; overflow: hidden; cursor: pointer;
    transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease; }
.mk-card2:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg);
    border-color: var(--brand); }
.mk-card2-main { flex: 1 1 auto; min-width: 0; padding: 14px 16px;
    display: flex; flex-direction: column; gap: 8px; }
.mk-card2-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.mk-card2-title { font-weight: 600; color: var(--text-strong);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mk-card2-sub { display: flex; align-items: center; gap: 8px; font-size: 0.78rem; color: var(--muted); }
.mk-card2-actions { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
/* Acciones de un paquete (instalar / actualizar / desinstalar). Cuáles salen
   depende del estado, así que NO pueden tener posición fija: con `margin-left:auto`
   un solo botón se pegaba al extremo dejando un hueco muerto, y con dos la fila
   quedaba descentrada. Ahora reparten el ancho entre los que haya: 1 botón ocupa
   todo, 2 van a mitades, 3 a tercios. */
.mk-act { flex: 1 1 0; min-width: 0; text-align: center; justify-content: center; }
/* En la ficha de detalle NO se estiran: ahí conviven con el título y estirarlos
   daría botones enormes. Solo se agrupan a la izquierda, sin hueco delante. */
.mk-detail-actions .mk-act { flex: 0 0 auto; }
.mk-push { margin-left: auto; }
.mk-update { background: var(--brand-soft-bg); color: var(--brand-soft-fg); }
.mk-update:hover { background: var(--brand-soft-bg); filter: brightness(1.08); }
/* Panel lateral que aparece al expandir la card hacia la derecha. */
.mk-card2-more { flex: 0 0 0; width: 0; overflow: hidden; opacity: 0;
    background: var(--surface-2); border-left: 0 solid var(--border);
    transition: width 0.2s ease, opacity 0.2s ease, padding 0.2s ease; }
.mk-card2.is-expanded { grid-column: span 2; border-color: var(--brand); }
.mk-card2.is-expanded .mk-card2-more { flex: 0 0 250px; width: 250px; opacity: 1;
    padding: 14px 16px; border-left: 1px solid var(--border); }
.mk-card2-logo { max-width: 100%; max-height: 90px; object-fit: contain; border-radius: 8px;
    margin-bottom: 10px; display: block; }
.mk-card2-summary { margin: 0 0 10px; font-size: 0.82rem; color: var(--text); line-height: 1.45; }
.mk-card2-kv { display: flex; justify-content: space-between; gap: 8px;
    font-size: 0.76rem; color: var(--muted-2); margin-bottom: 8px; }
.mk-card2-kv > span:last-child { color: var(--muted); }

/* ── Tab Subida: tabla técnica ─────────────────────────────────────────── */
.mk-up-table { margin-bottom: 10px; }
.mk-up-row { display: grid; grid-template-columns: 1.3fr 1.4fr 80px 90px 90px 190px; gap: 8px;
    align-items: center; padding: 10px 12px; border-bottom: 1px solid var(--border); }
.mk-up-row:last-child { border-bottom: none; }
.mk-up-row > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mk-up-row--head { font-weight: 700; background: var(--surface-2); color: var(--muted);
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; }
.mk-up-tags { display: flex; flex-wrap: wrap; gap: 4px; white-space: normal; }
.mk-up-link { color: var(--brand); cursor: pointer; }
.mk-up-link:hover { text-decoration: underline; }
.mk-up-row .pg-actions { display: flex; gap: 6px; white-space: normal; }

/* ── Rejilla de CATEGORÍAS (primera vista del catálogo) ────────────────────── */
.mk-cat-grid { display: grid; gap: 18px; align-items: start;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.mk-cat { appearance: none; cursor: pointer; text-align: left; display: flex;
    flex-direction: column; gap: 8px; padding: 22px 20px; border-radius: 16px;
    border: 1px solid var(--border);
    background: linear-gradient(150deg, var(--brand-soft-bg), var(--surface) 78%);
    transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease; }
.mk-cat:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); border-color: var(--brand); }
/* En «Paquetes instalados» las categorías FILTRAN la lista de abajo en vez de
   entrar a otra pantalla, así que van más bajas: comparten sitio con la toolbar
   y la rejilla, y a tamaño completo se comerían la vista. */
.mk-cat-grid--sm { gap: 12px; margin-bottom: 16px;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
.mk-cat-grid--sm .mk-cat { padding: 14px 16px; gap: 4px; border-radius: 12px; }
.mk-cat-grid--sm .mk-cat-emoji { font-size: 1.5rem; }
.mk-cat-grid--sm .mk-cat-emoji svg { width: 1.5rem; height: 1.5rem; }
.mk-cat-grid--sm .mk-cat-label { font-size: 0.94rem; }
.mk-cat-grid--sm .mk-cat-count { font-size: 0.74rem; }
/* Filtro aplicado: se marca la tarjeta, que es el control que lo activó. */
.mk-cat.is-active { border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand) inset; }
.mk-cat-emoji { font-size: 2.4rem; line-height: 1; color: var(--brand); display: inline-flex; }
/* Icono del iconset en la card de categoría (si hay iconset activo que lo cubra);
   si no, cae al emoji de fallback (texto) — de ahí el font-size de arriba. */
.mk-cat-emoji svg { width: 2.4rem; height: 2.4rem; display: block; }
.mk-cat-label { font-size: 1.15rem; font-weight: 700; color: var(--text-strong); }
.mk-cat-count { font-size: 0.8rem; color: var(--muted); }

/* ── Catálogo v3: cards CUADRADAS (imagen arriba) ──────────────────────────── */
.mk-grid { display: grid; gap: 16px; align-items: start; justify-content: start;
    grid-template-columns: repeat(auto-fill, minmax(180px, 200px)); }
/* Los módulos del código y los paquetes del registry se pintan en dos bloques
   distintos del `view!`, pero deben compartir UNA rejilla: si cada bloque abre su
   propia `.mk-grid`, el primero cierra su última fila a medias y el segundo empieza
   otra, y las tarjetas dejan de cuadrar entre secciones. `display: contents` hace
   que las tarjetas de dentro sean hijas directas de la rejilla de fuera. */
.mk-grid-part { display: contents; }

/* ── Funcionalidades del panel (SMTP y futuras) ────────────────────────────
   No son módulos con pantalla propia, así que no van en la rejilla de tarjetas:
   se listan como filas con su descripción y su botón de encendido. */
.mk-feats { margin-top: 28px; }
.mk-feat { display: flex; gap: 16px; align-items: flex-start; justify-content: space-between;
    background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
    padding: 16px 18px; }
.mk-feat-main { flex: 1 1 auto; min-width: 0; }
.mk-feat-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.mk-feat-name { font-weight: 600; color: var(--text-strong); }
.mk-feat-actions { flex: 0 0 auto; }
@media (max-width: 640px) {
    .mk-feat { flex-direction: column; align-items: stretch; }
}
/* Avisos y estados vacíos ocupan la fila entera; si no, la rejilla los encajaría
   en una columna de 200px. */
.mk-grid > .mk-banner, .mk-grid > .pg-empty { grid-column: 1 / -1; }
/* Cuadro PERFECTO: la card entera es 1:1; la imagen ocupa el espacio restante
   sobre el cuerpo (nombre/versión/acciones). */
.mk-sq { display: flex; flex-direction: column; aspect-ratio: 1 / 1; background: var(--surface);
    border: 1px solid var(--border); border-radius: 14px; overflow: hidden; cursor: pointer;
    transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease; }
.mk-sq:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); border-color: var(--brand); }
.mk-sq-thumb { position: relative; flex: 1 1 auto; min-height: 0; background: var(--surface-2);
    display: flex; align-items: center; justify-content: center; overflow: hidden; }
.mk-sq-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mk-sq-ph { font-size: 3rem; font-weight: 800; color: var(--muted-2); text-transform: uppercase; }
.mk-sq-ph--lg { font-size: 4.5rem; }
.mk-sq-type { position: absolute; top: 8px; left: 8px; }
.mk-sq-body { flex: 0 0 auto; padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; }
.mk-sq-name { font-weight: 600; color: var(--text-strong);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mk-sq-sub { display: flex; align-items: center; gap: 8px; font-size: 0.78rem; color: var(--muted); }
.mk-sq-actions { display: flex; align-items: stretch; gap: 8px; margin-top: 2px; }
.mk-sq-actions .mk-push { margin-left: 0; }

/* ── Pantalla de detalle (tipo store) ──────────────────────────────────────── */
.mk-detail { display: flex; flex-direction: column; gap: 18px; }
.mk-back { align-self: flex-start; appearance: none; border: 1px solid var(--border);
    background: var(--control-bg); color: var(--text); cursor: pointer; font-weight: 600;
    padding: 7px 14px; border-radius: 999px; display: inline-flex; align-items: center; gap: 4px;
    margin-bottom: 14px;
    transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease; }
.mk-back:hover { background: var(--control-bg-hover); border-color: var(--brand); transform: translateX(-2px); }
.mk-back-arrow { font-size: 1.2rem; line-height: 1; }
.mk-detail-hero { display: flex; gap: 20px; align-items: center; }
.mk-detail-cover { flex: 0 0 140px; width: 140px; height: 140px; border-radius: 18px;
    overflow: hidden; background: var(--surface-2); display: flex; align-items: center; justify-content: center; }
.mk-detail-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mk-detail-headinfo { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.mk-detail-title { margin: 0; font-size: 1.5rem; color: var(--text-strong); }
.mk-detail-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 0.85rem; color: var(--muted); }
.mk-detail-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.mk-muted { color: var(--muted); }
.mk-detail-section { display: flex; flex-direction: column; gap: 10px; }
.mk-detail-section h3 { margin: 0; font-size: 0.95rem; color: var(--text-strong); }
.mk-detail-desc { margin: 0; font-size: 0.9rem; color: var(--text); line-height: 1.6; white-space: pre-wrap; }
.mk-vers { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.mk-ver-row { display: grid; grid-template-columns: 100px 1fr auto; gap: 10px; align-items: center;
    padding: 8px 12px; border-bottom: 1px solid var(--border); font-size: 0.82rem; }
.mk-ver-row:last-child { border-bottom: none; }

/* Carrusel de imágenes de vista previa (scroll lateral). */
.mk-shots { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 6px; scroll-snap-type: x mandatory; }
.mk-shot { flex: 0 0 auto; appearance: none; border: 1px solid var(--border); background: var(--surface-2);
    border-radius: 12px; overflow: hidden; cursor: pointer; padding: 0; scroll-snap-align: start;
    transition: border-color 0.15s ease, transform 0.15s ease; }
.mk-shot:hover { border-color: var(--brand); transform: translateY(-2px); }
.mk-shot img { height: 200px; width: auto; max-width: 360px; object-fit: cover; display: block; }

/* Lightbox (imagen ampliada en modal). */
.mk-lightbox { position: fixed; inset: 0; z-index: 1100; background: var(--scrim, rgba(0,0,0,0.8));
    display: flex; align-items: center; justify-content: center; padding: 32px; cursor: zoom-out; }
.mk-lightbox img { max-width: 92vw; max-height: 88vh; object-fit: contain; border-radius: 10px;
    box-shadow: var(--shadow-lg); cursor: default; }
.mk-lightbox-x { position: absolute; top: 18px; right: 22px; appearance: none; border: none;
    background: rgba(0,0,0,0.5); color: #fff; font-size: 1.1rem; width: 40px; height: 40px;
    border-radius: 50%; cursor: pointer; }
.mk-lightbox-x:hover { background: rgba(0,0,0,0.75); }

/* ── Form de subida: tooltips, imagen centrada, chips de preview ───────────── */
.mk-flabel { display: inline-flex; align-items: center; gap: 6px; font-size: 0.78rem;
    color: var(--muted); font-weight: 600; }
.mk-field--center { align-items: center; text-align: center; }
.mk-imgpick { position: relative; display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
    width: 120px; height: 120px; border: 2px dashed var(--border); border-radius: 16px; overflow: hidden;
    background: var(--surface-2); color: var(--muted); font-size: 0.78rem; text-align: center;
    padding: 8px; transition: border-color 0.15s ease, color 0.15s ease; }
.mk-imgpick:hover { border-color: var(--brand); color: var(--text); }
.mk-imgpick-preview { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* Miniaturas de imágenes de vista previa en el form. */
.mk-promo-grid { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.mk-promo-thumb { position: relative; width: 84px; height: 84px; border-radius: 10px; overflow: hidden;
    border: 1px solid var(--border); background: var(--surface-2); }
.mk-promo-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mk-promo-thumb .mk-promo-x { position: absolute; top: 3px; right: 3px; width: 20px; height: 20px;
    border-radius: 50%; background: rgba(0,0,0,0.6); color: #fff; border: none; cursor: pointer;
    font-size: 0.7rem; display: flex; align-items: center; justify-content: center; padding: 0; }
.mk-promo-thumb .mk-promo-x:hover { background: rgba(0,0,0,0.85); }

/* Modal ancho de tamaño FIJO (vista previa de la página del paquete): grande,
   responsivo y con scroll interno — no se adapta al contenido. */
.mk-modal--wide { width: min(920px, 94vw); max-width: none; height: 84vh; max-height: 84vh;
    overflow: hidden; display: flex; flex-direction: column; margin: 0 auto; }
.mk-modal-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
    margin-top: 12px; }
.mk-ph-tag { opacity: 0.7; }
.mk-ph-note { font-size: 0.72rem; color: var(--muted); align-self: center; }
.mk-back:disabled { cursor: default; opacity: 0.6; }
.mk-back:disabled:hover { transform: none; border-color: var(--border); background: var(--control-bg); }
.mk-tip { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px;
    border-radius: 50%; background: var(--control-bg); color: var(--muted); border: 1px solid var(--border);
    font-size: 0.68rem; font-weight: 700; cursor: help; position: relative; }
.mk-tip:hover { color: var(--text); border-color: var(--brand); }
.mk-tip::after { content: attr(data-tip); position: absolute; bottom: 130%; left: 50%;
    transform: translateX(-50%); width: max-content; max-width: 240px; white-space: normal;
    background: var(--tooltip-bg); color: var(--tooltip-fg); font-size: 0.72rem; font-weight: 500;
    line-height: 1.4; padding: 7px 10px; border-radius: 8px; box-shadow: var(--shadow-md);
    opacity: 0; pointer-events: none; transition: opacity 0.15s ease; z-index: 50; }
.mk-tip:hover::after { opacity: 1; }
.mk-promo-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.mk-promo-chip { display: inline-flex; align-items: center; gap: 6px; font-size: 0.74rem;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; padding: 3px 6px 3px 10px; }
.mk-promo-x { appearance: none; border: none; background: transparent; color: var(--muted);
    cursor: pointer; font-size: 0.72rem; padding: 2px 4px; border-radius: 50%; }
.mk-promo-x:hover { background: var(--control-bg-hover); color: var(--text); }

@media (max-width: 640px) {
    .mk-detail-hero { flex-direction: column; align-items: flex-start; }
}

/* ── Identidad Visual (F6.1) — selección de tema base ─────────────────────── */
.identity-theme-list { display: flex; flex-direction: column; gap: 8px; max-width: 520px; }
.identity-theme-option { display: flex; align-items: center; gap: 12px; cursor: pointer;
    padding: 12px 14px; border: 1px solid var(--border); border-radius: 10px;
    background: var(--control-bg); transition: border-color .15s, background .15s; }
.identity-theme-option:hover { border-color: var(--brand); background: var(--control-bg-hover); }
.identity-theme-option.is-active { border-color: var(--brand); background: var(--brand-soft-bg); }
.identity-theme-option input[type="radio"] { accent-color: var(--brand); margin: 0; cursor: pointer; }
.identity-theme-option input:disabled { cursor: not-allowed; }
.identity-theme-name { font-weight: 600; color: var(--text-strong); }
.identity-theme-ver { margin-left: auto; font-size: 0.78rem; color: var(--muted); }

/* Escala de fuente (F6.3): botonera de valores discretos. */
.identity-scale-btns { display: flex; flex-wrap: wrap; gap: 8px; }
.identity-scale-btn { padding: 8px 16px; border: 1px solid var(--border); border-radius: 8px;
    background: var(--control-bg); color: var(--text); font-weight: 600; cursor: pointer;
    transition: border-color .15s, background .15s, color .15s; }
.identity-scale-btn:hover:not(:disabled) { border-color: var(--brand); background: var(--control-bg-hover); }
.identity-scale-btn.is-active { border-color: var(--brand); background: var(--brand-soft-bg); color: var(--brand-soft-fg); }
.identity-scale-btn:disabled { opacity: 0.55; cursor: not-allowed; }
.profile-subsection-title { margin: 0 0 12px; font-size: 0.95rem; font-weight: 700; color: var(--text-strong); }

/* Iconos de iconset (F6.4): el <span> contiene un <svg> reconstruido por el
   componente Icon. Se dimensiona al hueco y hereda el color del texto. */
.module-sidebar-icon svg { width: 1.25em; height: 1.25em; display: block; }

/* ═══════════════════════════════════════════════════════════════════════════
   MOTOR SDUI (src/sdui/) — tokens only; clases sdui-* para módulos instalados
   ═══════════════════════════════════════════════════════════════════════════ */

.sdui-page { min-height: 100vh; background: var(--bg); }
.sdui-page-body { padding: 20px 24px; max-width: 1200px; margin: 0 auto; }
.sdui-page-msg { padding: 40px; text-align: center; color: var(--muted); }
.sdui-view-title { font-size: 1.15rem; color: var(--text-strong); margin: 0 0 14px; }

.sdui-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  padding: 16px; box-shadow: var(--shadow-sm); }
.sdui-placeholder { border: 1px dashed var(--border); border-radius: 8px; padding: 10px 12px;
  color: var(--muted-2); font-size: 0.8rem; background: var(--surface-2); }
.sdui-placeholder code { color: var(--muted); }

.sdui-heading { color: var(--text-strong); margin: 0; }
.sdui-text { color: var(--text); margin: 0; font-size: 0.88rem; }
.sdui-text--muted { color: var(--muted); }

.sdui-badge { display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 0.72rem; font-weight: 600; }
.sdui-badge--primary { background: var(--brand-soft-bg); color: var(--brand-soft-fg); }
.sdui-badge--info    { background: var(--badge-info-bg);    color: var(--badge-info-fg); }
.sdui-badge--success { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.sdui-badge--warning { background: var(--badge-warn-bg);    color: var(--badge-warn-fg); }
.sdui-badge--danger  { background: var(--badge-danger-bg);  color: var(--badge-danger-fg); }
.sdui-badge--gray    { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }

.sdui-alert { display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px;
  border-radius: 10px; border: 1px solid var(--border-soft); font-size: 0.85rem; }
.sdui-alert--info    { background: var(--badge-info-bg);    color: var(--badge-info-fg); }
.sdui-alert--success { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.sdui-alert--warning { background: var(--badge-warn-bg);    color: var(--badge-warn-fg); }
.sdui-alert--danger  { background: var(--badge-danger-bg);  color: var(--badge-danger-fg); }
.sdui-alert-title { display: block; margin-bottom: 2px; }
.sdui-alert-body { flex: 1; }
.sdui-alert-close { background: none; border: none; cursor: pointer; color: inherit; font-size: 0.9rem; }

.sdui-divider { border: none; border-top: 1px solid var(--border); margin: 10px 0; }
.sdui-divider--labeled { display: flex; align-items: center; gap: 10px; color: var(--muted-2);
  font-size: 0.75rem; margin: 10px 0; }
.sdui-divider--labeled::before, .sdui-divider--labeled::after {
  content: ""; flex: 1; border-top: 1px solid var(--border); }

.sdui-empty { text-align: center; padding: 34px 16px; color: var(--muted); }
.sdui-empty-icon { font-size: 2rem; margin-bottom: 6px; }
.sdui-empty-title { font-weight: 600; color: var(--text-strong); margin-bottom: 4px; }
.sdui-empty-text { font-size: 0.83rem; margin-bottom: 12px; }

.sdui-kv { margin: 0; display: grid; gap: 6px; }
.sdui-kv-row { display: flex; gap: 12px; font-size: 0.85rem; }
.sdui-kv-row dt { color: var(--muted); min-width: 140px; }
.sdui-kv-row dd { margin: 0; color: var(--text); }

.sdui-tabs-header { display: flex; gap: 4px; border-bottom: 1px solid var(--border);
  margin-bottom: 14px; flex-wrap: wrap; }
.sdui-tab { padding: 8px 14px; border: none; background: none; cursor: pointer;
  color: var(--muted); font-size: 0.86rem; font-weight: 600; font-family: inherit;
  border-bottom: 2px solid transparent; }
.sdui-tab.is-active { color: var(--brand); border-bottom-color: var(--brand); }

.sdui-modal-overlay { position: fixed; inset: 0; z-index: 1000; background: var(--scrim);
  backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center; }
.sdui-modal { background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  width: min(480px, 92vw); max-height: 86vh; overflow: auto; box-shadow: var(--shadow-lg); }
.sdui-modal--lg { width: min(760px, 94vw); }
.sdui-modal-head { display: flex; justify-content: space-between; align-items: center;
  padding: 14px 18px; border-bottom: 1px solid var(--border); }
.sdui-modal-head h3 { margin: 0; font-size: 0.95rem; color: var(--text-strong); }
.sdui-modal-close { background: none; border: none; cursor: pointer; color: var(--muted); }
.sdui-modal-body { padding: 16px 18px; }
.sdui-modal-footer { padding: 12px 18px; border-top: 1px solid var(--border);
  display: flex; gap: 8px; justify-content: flex-end; }

.sdui-stat { background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  padding: 14px 16px; box-shadow: var(--shadow-sm); }
.sdui-stat-head { display: flex; gap: 6px; align-items: center; color: var(--muted);
  font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; }
.sdui-stat-value { font-size: 1.5rem; font-weight: 700; color: var(--text-strong); margin: 4px 0 2px; }
.sdui-stat-sub { font-size: 0.76rem; color: var(--muted-2); }
.sdui-stat-sub--good { color: var(--badge-success-fg); }
.sdui-stat-sub--warn { color: var(--badge-warn-fg); }

.sdui-btn { padding: 7px 13px; border-radius: 8px; border: 1px solid var(--control-border);
  background: var(--control-bg); color: var(--text); font-size: 0.83rem; font-weight: 600;
  font-family: inherit; cursor: pointer; transition: all 0.15s; }
.sdui-btn:hover:not(:disabled) { background: var(--control-bg-hover); }
.sdui-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.sdui-btn--primary { background: var(--brand); color: #fff; border-color: var(--brand); }
.sdui-btn--primary:hover:not(:disabled) { background: var(--brand-dark); }
.sdui-btn--danger { color: var(--badge-danger-fg); border-color: rgba(239,68,68,0.45); }
.sdui-btn--danger:hover:not(:disabled) { background: var(--badge-danger-bg); }
.sdui-btn--ghost { background: transparent; }

.sdui-field { display: flex; flex-direction: column; gap: 4px; }
.sdui-label { font-size: 0.76rem; color: var(--muted); font-weight: 600; }
.sdui-input { padding: 8px 10px; border: 1px solid var(--control-border); border-radius: 8px;
  font-family: inherit; font-size: 0.85rem; background: var(--control-bg); color: var(--text); }
.sdui-input:focus { outline: 2px solid var(--brand); outline-offset: -1px; }

.sdui-toggle { display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  font-size: 0.84rem; color: var(--text); user-select: none; }
.sdui-toggle input { display: none; }
.sdui-toggle-track { width: 34px; height: 19px; border-radius: 999px; background: var(--control-border);
  position: relative; transition: background 0.15s; flex-shrink: 0; }
.sdui-toggle input:checked + .sdui-toggle-track { background: var(--brand); }
/* knob blanco fijo en ambos temas (excepción intencional, igual que los toggles del panel) */
.sdui-toggle-knob { position: absolute; top: 2px; left: 2px; width: 15px; height: 15px;
  border-radius: 50%; background: #fff; transition: left 0.15s; }
.sdui-toggle input:checked + .sdui-toggle-track .sdui-toggle-knob { left: 17px; }

.sdui-form { display: flex; flex-direction: column; gap: 10px; max-width: 480px; }

.sdui-table-wrap { background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  overflow-x: auto; box-shadow: var(--shadow-sm); }
.sdui-table { width: 100%; border-collapse: collapse; font-size: 0.83rem; }
.sdui-table th { padding: 10px 14px; background: var(--surface-2); color: var(--muted);
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.03em; text-align: left; }
.sdui-table td { padding: 9px 14px; border-top: 1px solid var(--border); color: var(--text); }
.sdui-row--clickable { cursor: pointer; }
.sdui-row--clickable:hover td { background: var(--surface-2); }
.sdui-table-msg { text-align: center; color: var(--muted); padding: 24px !important; }
.sdui-table-msg--error { color: var(--badge-danger-fg); }
.sdui-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.78rem; }
.sdui-nowrap { white-space: nowrap; }

.sdui-spinner-wrap { display: flex; gap: 10px; align-items: center; justify-content: center; padding: 14px; }
.sdui-spinner { border: 3px solid var(--border); border-top-color: var(--brand); border-radius: 50%;
  animation: sdui-spin 0.8s linear infinite; }
.sdui-spinner--sm { width: 14px; height: 14px; }
.sdui-spinner--md { width: 22px; height: 22px; }
.sdui-spinner--lg { width: 34px; height: 34px; }
.sdui-spinner-label { color: var(--muted); font-size: 0.83rem; }
@keyframes sdui-spin { to { transform: rotate(360deg); } }

.sdui-pagination { display: flex; gap: 12px; align-items: center; justify-content: center; }
.sdui-pagination-info { color: var(--muted); font-size: 0.83rem; }

.sdui-toast { position: fixed; bottom: 24px; right: 24px; z-index: 1100; padding: 12px 18px;
  border-radius: 10px; font-size: 0.86rem; font-weight: 600; box-shadow: var(--shadow-lg);
  background: var(--tooltip-bg); color: var(--tooltip-fg); }
.sdui-toast--success { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.sdui-toast--warning { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.sdui-toast--danger  { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }

.sdui-chart { width: 100%; }

/* ── SDUI · kanban (tablero por columnas con drag & drop) ── */
.sdui-kanban { width: 100%; }
.sdui-kanban-msg { text-align: center; color: var(--muted); padding: 24px; }
.sdui-kanban-msg--error { color: var(--badge-danger-fg); }
.sdui-kanban-cols { display: flex; gap: 14px; overflow-x: auto; padding-bottom: 8px;
  align-items: flex-start; }
.sdui-kanban-col { flex: 0 0 288px; width: 288px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 12px; display: flex; flex-direction: column;
  max-height: 100%; }
.sdui-kanban-col-head { display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 10px 12px; border-top: 3px solid var(--brand);
  border-top-left-radius: 12px; border-top-right-radius: 12px; }
.sdui-kanban-col-label { font-weight: 700; font-size: 0.84rem; color: var(--text-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sdui-kanban-col-count { flex: 0 0 auto; min-width: 20px; text-align: center; padding: 1px 7px;
  border-radius: 999px; font-size: 0.7rem; font-weight: 700; color: var(--muted);
  background: var(--control-bg); border: 1px solid var(--border-soft); }
.sdui-kanban-col-body { display: flex; flex-direction: column; gap: 8px; padding: 10px;
  overflow-y: auto; min-height: 48px; }
.sdui-kanban-col-body.sdui-kanban-dragover { background: var(--brand-soft-bg); }
.sdui-kanban-col-empty { text-align: center; color: var(--muted-2); font-size: 0.78rem;
  padding: 14px 6px; }
.sdui-kanban-card { background: var(--surface); border: 1px solid var(--border);
  border-radius: 10px; padding: 10px 12px; cursor: grab; box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column; gap: 4px; }
.sdui-kanban-card:hover { border-color: var(--brand); }
.sdui-kanban-card:active { cursor: grabbing; }
.sdui-kanban-card[draggable="false"] { cursor: default; }
.sdui-kanban-card-title { font-weight: 600; font-size: 0.84rem; color: var(--text-strong); }
.sdui-kanban-card-sub { font-size: 0.78rem; color: var(--muted); }
.sdui-kanban-card-badges { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.sdui-kanban-card-meta { font-size: 0.72rem; color: var(--muted-2); margin-top: 2px; }

/* ── SDUI · calendar (calendario mensual de eventos) ── */
.sdui-cal { width: 100%; }
.sdui-cal-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.sdui-cal-title { font-weight: 700; font-size: 0.95rem; color: var(--text-strong);
  min-width: 150px; }
.sdui-cal-nav { width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--control-bg); color: var(--text); cursor: pointer; font-size: 1rem; }
.sdui-cal-nav:hover { background: var(--control-bg-hover); }
.sdui-cal-today { padding: 5px 12px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--control-bg); color: var(--text); cursor: pointer; font-size: 0.8rem; }
.sdui-cal-today:hover { background: var(--control-bg-hover); }
.sdui-cal-loading, .sdui-cal-error { font-size: 0.78rem; color: var(--muted); }
.sdui-cal-error { color: var(--badge-danger-fg); }
.sdui-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.sdui-cal-dow { text-align: center; font-size: 0.7rem; text-transform: uppercase;
  letter-spacing: 0.03em; color: var(--muted); padding: 4px 0; font-weight: 600; }
.sdui-cal-cell { min-height: 92px; background: var(--surface); border: 1px solid var(--border);
  border-radius: 8px; padding: 4px; display: flex; flex-direction: column; gap: 3px; overflow: hidden; }
.sdui-cal-empty { background: transparent; border: 1px dashed var(--border-soft); }
.sdui-cal-today { }
.sdui-cal-cell.sdui-cal-today { border-color: var(--brand); box-shadow: inset 0 0 0 1px var(--brand); }
.sdui-cal-daynum { font-size: 0.72rem; font-weight: 700; color: var(--muted); text-align: right;
  padding: 0 2px; }
.sdui-cal-events { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; }
.sdui-cal-ev { font-size: 0.68rem; padding: 2px 5px; border-radius: 5px; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-left: 3px solid transparent; }
.sdui-cal-ev--vencido { background: var(--badge-danger-bg); color: var(--badge-danger-fg);
  border-left-color: var(--badge-danger-fg); }
.sdui-cal-ev--hoy { background: var(--badge-warn-bg); color: var(--badge-warn-fg);
  border-left-color: var(--badge-warn-fg); }
.sdui-cal-ev--proximo { background: var(--badge-info-bg); color: var(--badge-info-fg);
  border-left-color: var(--badge-info-fg); }
.sdui-cal-ev--done { background: var(--surface-2); color: var(--muted-2);
  border-left-color: var(--border); text-decoration: line-through; }

/* ── E-commerce · integración producto↔recurso (modal 2 cards + tab crear/enlazar) ── */
.ec-integ-tab { display: flex; flex-direction: column; gap: 12px; }
.ec-seg { display: inline-flex; padding: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--border-soft); width: fit-content; }
.ec-seg-btn { appearance: none; border: 0; background: transparent; color: var(--muted); cursor: pointer;
  padding: 7px 16px; border-radius: 8px; font-size: 0.85rem; font-weight: 600; transition: background .15s, color .15s; }
.ec-seg-btn.active { background: var(--surface-overlay); color: var(--text-strong); box-shadow: var(--shadow-sm); }
.ec-integ-field { display: flex; flex-direction: column; gap: 6px; }
.ec-integ-unlink { align-self: flex-start; }

/* Dropdown de recurso: buscable + paginado (mismo patrón que el selector de cliente) */
.ec-res-picker { position: relative; }
.ec-res-picker-btn { width: 100%; text-align: left; cursor: pointer; }
/* Capa invisible que captura los clics fuera del dropdown de recursos para cerrarlo.
   z-index bajo el drop (40) y sobre el resto del contenido del drawer. */
.ec-res-dismiss { position: fixed; inset: 0; z-index: 39; background: transparent; border: none; padding: 0; cursor: default; }
.ec-res-drop { position: absolute; z-index: 40; top: calc(100% + 4px); left: 0; right: 0;
  background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow-lg); padding: 8px; }
.ec-res-search { width: 100%; margin-bottom: 6px; }
.ec-res-list { display: flex; flex-direction: column; max-height: 240px; overflow-y: auto; }
.ec-res-opt { appearance: none; border: 0; background: transparent; color: var(--text); cursor: pointer;
  text-align: left; padding: 8px 10px; border-radius: 8px; font-size: 0.88rem; }
.ec-res-opt:hover { background: var(--control-bg-hover); }
.ec-res-more { appearance: none; border: 0; background: transparent; color: var(--brand); cursor: pointer;
  padding: 8px 10px; font-size: 0.82rem; font-weight: 600; }

/* Modal de integración (pre-form): el dropdown de recurso debe ESCAPAR del modal (mismo criterio
   que el chooser: sin overflow que lo recorte; su único límite es la pantalla). El modal no se
   aplasta con poco contenido (min-height), y el segmented Crear/Enlazar va en pill redondo. */
.ec-integ-modal.pg-modal, .ec-integ-modal .pg-modal-body { overflow: visible; }
.ec-integ-modal.pg-modal { min-height: 320px; }
.ec-integ-modal .ec-seg { border-radius: 999px; }
.ec-integ-modal .ec-seg-btn { border-radius: 999px; }
/* El dropdown de recurso escapa hacia abajo → el modal se fija ARRIBA (no centrado) para dejarle
   toda la pantalla debajo, y la lista se limita al alto del viewport para no pasar del borde. */
.ec-integ-overlay { align-items: flex-start; }
.ec-integ-overlay .pg-modal { margin-top: 7vh; }
.ec-integ-modal .ec-res-list { max-height: min(240px, 42vh); }

/* Chips de día seleccionados (repetición de bloque L-V…) */
.mir-wd-chip.is-on { background: var(--brand-soft-bg); border-color: var(--brand); color: var(--brand-soft-fg); font-weight: 600; }

/* ── Popover tipo tooltip saliendo de una card (chooser) ──────────────────────── */
/* La celda envuelve la card y ancla el popover; la card llena la celda. */
.chooser-cell { position: relative; display: flex; }
.chooser-cell > .mir-chooser-card { width: 100%; }
.card-pop { position: absolute; z-index: 60; left: 50%; bottom: calc(100% + 12px); transform: translateX(-50%);
  min-width: 210px; background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: var(--shadow-lg); padding: 8px; display: flex; flex-direction: column; gap: 6px; }
.card-pop::after { content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 7px solid transparent; border-top-color: var(--surface-overlay); }
.card-pop-title { font-size: 0.76rem; color: var(--muted); padding: 2px 6px 4px; }
.card-pop-opt { appearance: none; border: 1px solid var(--control-border); background: var(--control-bg); color: var(--text);
  cursor: pointer; text-align: left; padding: 8px 10px; border-radius: 8px; font-size: 0.86rem; font-weight: 600; }
.card-pop-opt:hover { background: var(--control-bg-hover); border-color: var(--brand); }

/* Segmento de días — chips (reutilizado del bloque L-V) */
.mir-seg { display: inline-flex; padding: 3px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--border-soft); width: fit-content; margin-bottom: 8px; }
.mir-seg-btn { appearance: none; border: 0; background: transparent; color: var(--muted); cursor: pointer; padding: 7px 16px; border-radius: 8px; font-size: 0.85rem; font-weight: 600; }
.mir-seg-btn.is-on { background: var(--surface-overlay); color: var(--text-strong); box-shadow: var(--shadow-sm); }

/* El popover-tooltip del chooser debe poder salirse del cuerpo del modal (no recortar). */
.mir-chooser, .mir-chooser .pg-modal-body { overflow: visible; }

/* ── MIR: selector con buscador + paginado (recursos / bloques) ───────────────
   Un `<select>` nativo con miles de asientos es inusable y no se puede buscar.
   Mismo patrón que `ec-res-picker` (integración E-commerce) e `IdvSelect`. */
.mir-pick { position: relative; }
.mir-pick-field {
  width: 100%; display: flex; align-items: center; gap: 8px;
  text-align: left; cursor: pointer;
}
.mir-pick-value { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mir-pick-value.is-empty { color: var(--muted); }
.mir-pick-caret { flex: 0 0 auto; color: var(--muted); font-size: 0.8rem; }
/* Capa a pantalla completa: cierra el desplegable al pulsar fuera. Va por debajo
   del propio desplegable, no de los tooltips. */
.mir-pick-dismiss { position: fixed; inset: 0; z-index: 199; background: transparent; border: none; padding: 0; cursor: default; }
/* `fixed` + Portal: el desplegable se monta fuera del modal y su posición la
   calcula `pick_drop_style_for` (left/top/width/max-height en el atributo
   style). Ningún contenedor con overflow puede recortarlo. */
.mir-pick-drop {
  position: fixed; z-index: 200;
  display: flex; flex-direction: column; min-height: 0;
  background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow-lg); padding: 8px;
}
.mir-pick-search { width: 100%; margin-bottom: 6px; flex: 0 0 auto; }
.mir-pick-list {
  /* El alto lo manda el max-height del desplegable; la lista rellena lo que quede. */
  display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: transparent transparent;
}
.mir-pick-list:hover { scrollbar-color: var(--control-border) transparent; }
.mir-pick-list::-webkit-scrollbar { width: 8px; }
.mir-pick-list::-webkit-scrollbar-track { background: transparent; }
.mir-pick-list::-webkit-scrollbar-thumb {
  background: transparent; border: 2px solid transparent; background-clip: padding-box;
  border-radius: 8px; transition: background-color 0.2s ease;
}
.mir-pick-list:hover::-webkit-scrollbar-thumb { background: var(--control-border); background-clip: padding-box; }
.mir-pick-opt {
  appearance: none; border: 0; background: transparent; color: var(--text); cursor: pointer;
  text-align: left; padding: 7px 10px; border-radius: 8px;
  display: flex; flex-direction: column; gap: 1px;
}
.mir-pick-opt:hover { background: var(--control-bg-hover); }
.mir-pick-opt.is-active { background: var(--brand-soft-bg); color: var(--brand-soft-fg); }
.mir-pick-opt.is-disabled { opacity: 0.55; cursor: not-allowed; }
.mir-pick-opt.is-disabled:hover { background: transparent; }
.mir-pick-opt-main { display: flex; align-items: baseline; gap: 6px; }
.mir-pick-opt-label { font-size: 0.86rem; font-weight: 600; }
.mir-pick-opt-hint { font-size: 0.72rem; color: var(--muted); }
/* Rama de ancestros: sin ella, dos «Asiento A1» de salas distintas se leen igual. */
.mir-pick-opt-sub { font-size: 0.73rem; color: var(--muted); }
.mir-pick-more {
  appearance: none; border: 0; background: transparent; color: var(--brand); cursor: pointer;
  padding: 8px 10px; font-size: 0.8rem; font-weight: 600; text-align: center;
}
.mir-pick-more:hover { background: var(--control-bg-hover); border-radius: 8px; }
.mir-pick-empty { padding: 12px 10px; font-size: 0.8rem; color: var(--muted); text-align: center; }

/* ── MIR: velo de carga sobre el contenido de una tab ─────────────────────────
   El giro del icono de «Refrescar» dice que se está recargando; esto dice QUÉ.
   Se pinta aunque los datos vuelvan idénticos, con un suelo de visibilidad en
   el código (si no, una respuesta de 40 ms sería un parpadeo ilegible). */
.mir-loadwrap { position: relative; }
/* El velo hace TODO el trabajo (no se toca la opacidad de los hijos: dependía
   de que las filas fueran hijas directas y en el árbol de Recursos no se
   apagaban). Difumina y atenúa el fondo, bloquea el clic y deja un solo punto
   de atención: la píldora con el spinner. */
.mir-loadveil {
  position: absolute; inset: 0; z-index: 6;
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 52px;
  border-radius: inherit;
  background: var(--scrim);
  backdrop-filter: blur(3px) saturate(0.55);
  -webkit-backdrop-filter: blur(3px) saturate(0.55);
  pointer-events: auto; cursor: progress;
  animation: mir-veil-in 0.16s ease-out;
}
@keyframes mir-veil-in { from { opacity: 0; } to { opacity: 1; } }
.mir-loadcard {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 10px 16px; border-radius: 999px;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  color: var(--text-strong); font-size: 0.85rem; font-weight: 600;
}
.mir-spinner {
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--control-border); border-top-color: var(--brand);
  animation: mir-spin 0.7s linear infinite;
}
@keyframes mir-spin { to { transform: rotate(360deg); } }

/* Tarjeta de reserva arrastrable desde el primer píxel (sin retardo de "armado"). */
.mir-kcard { cursor: grab; }
.mir-kcard:active { cursor: grabbing; }

/* ============================================================================
   crm.css — Estilos del módulo CRM (Kanban de embudos + constructor + modales)

   Extraído de frontend/style.css del panel original. Al portar:
   · Opción A: pega este bloque al final de tu style.css.
   · Opción B: si tu bundler soporta @import, déjalo como archivo aparte.

   DEPENDE de los tokens semánticos de tema del panel (var(--brand), var(--surface),
   var(--text), var(--border), var(--badge-*), etc.). Si el proyecto destino NO tiene
   esos tokens, define equivalentes en :root o reemplaza los var(--token) por colores
   fijos. Ver la sección "Theming (tokens)" del CLAUDE.md del panel original.
   ============================================================================ */

/* ── CRM · Kanban de embudos ─────────────────────────────────────────────── */
.crm-wrap { padding: 16px 24px 32px; display: flex; flex-direction: column; gap: 16px; }
.crm-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.crm-embudo-select {
  background: var(--control-bg); color: var(--text);
  border: 1px solid var(--control-border); border-radius: 10px;
  padding: 8px 12px; font-size: 14px; min-width: 220px;
}
.crm-embudo-select:hover { background: var(--control-bg-hover); }
.crm-run-btn {
  background: var(--brand); color: #fff; border: none; border-radius: 10px;
  padding: 8px 16px; font-size: 14px; font-weight: 600; cursor: pointer;
  box-shadow: var(--shadow-sm); transition: background .15s ease;
}
.crm-run-btn:hover { background: var(--brand-dark); }
.crm-loading-chip {
  font-size: 12px; color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 999px; padding: 4px 10px;
}

.crm-board {
  display: flex; gap: 16px; align-items: flex-start;
  overflow-x: auto; padding-bottom: 12px;
}
.crm-col {
  flex: 0 0 288px; min-width: 288px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 12px; box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column; gap: 8px;
}
.crm-col-head { display: flex; align-items: center; justify-content: space-between; }
.crm-col-title { display: flex; align-items: center; gap: 8px; }
.crm-col-name { font-weight: 700; color: var(--text-strong); font-size: 15px; }
.crm-col-flag {
  font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px;
  background: var(--badge-success-bg); color: var(--badge-success-fg);
}
.crm-col-count {
  font-size: 12px; font-weight: 600; color: var(--muted);
  background: var(--surface-2); border-radius: 999px; padding: 2px 9px; min-width: 24px; text-align: center;
}
.crm-col-obj { font-size: 12px; color: var(--muted); line-height: 1.35; min-height: 16px; }
.crm-col-body { display: flex; flex-direction: column; gap: 8px; }

.crm-card {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 10px; padding: 10px; display: flex; flex-direction: column; gap: 6px;
}
.crm-card-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.crm-card-name { font-weight: 600; color: var(--text); font-size: 14px; }
.crm-card-lock { font-size: 12px; }
.crm-card-sub { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--muted); flex-wrap: wrap; }
.crm-card-chan {
  font-size: 11px; padding: 1px 7px; border-radius: 999px;
  background: var(--badge-info-bg); color: var(--badge-info-fg);
}
.crm-move-select {
  margin-top: 2px; width: 100%; font-size: 12px;
  background: var(--control-bg); color: var(--text);
  border: 1px solid var(--control-border); border-radius: 8px; padding: 5px 8px;
}
.crm-move-select:hover { background: var(--control-bg-hover); }
.crm-col-empty { font-size: 12px; color: var(--muted-2); text-align: center; padding: 10px 0; font-style: italic; }

/* ── CRM · Constructor + tabs + modales ──────────────────────────────────── */
.crm-viewtabs { display: inline-flex; background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px; padding: 3px; gap: 2px; }
.crm-tab { background: transparent; color: var(--muted); border: none; border-radius: 8px; padding: 6px 14px; font-size: 13px; font-weight: 600; cursor: pointer; }
.crm-tab.active { background: var(--brand); color: #fff; }
.crm-cons-actions { display: inline-flex; gap: 8px; }
.crm-btn-sec { background: var(--control-bg); color: var(--text); border: 1px solid var(--control-border); border-radius: 9px; padding: 7px 12px; font-size: 13px; cursor: pointer; }
.crm-btn-sec:hover { background: var(--control-bg-hover); }
.crm-btn-danger { color: var(--danger, #ef4444); }

.crm-constructor { display: flex; flex-direction: column; gap: 12px; max-width: 860px; }

/* Canvas / flujo horizontal del constructor */
.crm-canvas-viewport {
  position: relative; overflow: auto; border: 1px solid var(--border); border-radius: 16px;
  background:
    linear-gradient(90deg, var(--surface-2) 1px, transparent 1px) 0 0 / 28px 28px,
    linear-gradient(var(--surface-2) 1px, transparent 1px) 0 0 / 28px 28px,
    var(--bg-2, var(--bg));
  padding: 28px; min-height: 340px;
}
.crm-canvas-zoom {
  position: sticky; top: 10px; left: 10px; z-index: 5; display: inline-flex; align-items: center; gap: 4px;
  background: var(--surface-overlay, var(--panel-2)); border: 1px solid var(--border); border-radius: 10px;
  padding: 4px 6px; box-shadow: var(--shadow-sm); width: fit-content;
}
.crm-canvas-zoom-label { font-size: 12px; color: var(--muted); min-width: 38px; text-align: center; }
.crm-flow {
  display: flex; align-items: flex-start; gap: 0; width: max-content; margin-top: 10px;
  transition: transform .12s ease;
}
.crm-flow-item { display: flex; align-items: flex-start; flex: 0 0 auto; }
.crm-flow-node { flex: 0 0 auto; width: 268px; }
.crm-flow-arrow {
  flex: 0 0 auto; align-self: center; color: var(--muted-2); font-size: 20px; padding: 0 6px;
  transform: translateY(-2px);
}
.crm-flow-entry {
  display: flex; align-items: center; gap: 10px; background: var(--brand-soft-bg, var(--surface-2));
  border: 1px solid var(--border); border-radius: 14px; padding: 14px 16px; box-shadow: var(--shadow-sm);
}
.crm-flow-entry-ico { font-size: 18px; color: var(--brand); }
.crm-flow-entry strong { display: block; color: var(--text-strong); font-size: 14px; }
.crm-flow-entry span { display: block; color: var(--muted); font-size: 12px; }

.crm-fase-card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 14px 16px; box-shadow: var(--shadow-sm); }
.crm-fase-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.crm-fase-title { display: flex; align-items: center; gap: 9px; color: var(--text-strong); font-size: 15px; }
.crm-fase-orden { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 7px; background: var(--brand-soft-bg, var(--surface-2)); color: var(--brand-soft-fg, var(--brand)); font-size: 12px; font-weight: 700; }
.crm-fase-tools { display: inline-flex; gap: 4px; }
.crm-icon-btn { background: var(--control-bg); border: 1px solid var(--control-border); border-radius: 7px; width: 28px; height: 28px; color: var(--text); cursor: pointer; font-size: 13px; line-height: 1; }
.crm-icon-btn:hover:not(:disabled) { background: var(--control-bg-hover); }
.crm-icon-btn:disabled { opacity: .4; cursor: not-allowed; }
.crm-fase-obj { color: var(--muted); font-size: 12.5px; margin: 6px 0 10px; }
.crm-fase-sec { display: flex; align-items: center; justify-content: space-between; margin-top: 10px; }
.crm-fase-sec-title { font-size: 12px; font-weight: 600; color: var(--muted-2); text-transform: uppercase; letter-spacing: .03em; }
.crm-mini-btn { background: transparent; border: 1px dashed var(--border); border-radius: 8px; color: var(--brand); font-size: 12px; padding: 3px 10px; cursor: pointer; }
.crm-mini-btn:hover { background: var(--surface-2); }
.crm-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.crm-chip { display: inline-flex; align-items: center; gap: 6px; background: var(--badge-info-bg); color: var(--badge-info-fg); border-radius: 999px; padding: 3px 10px; font-size: 12px; }
.crm-chip-action { background: var(--badge-purple-bg); color: var(--badge-purple-fg); }
.crm-chip-x { background: transparent; border: none; color: inherit; cursor: pointer; font-size: 14px; line-height: 1; opacity: .7; padding: 0; }
.crm-chip-x:hover { opacity: 1; }
.crm-soft-empty { font-size: 12px; color: var(--muted-2); font-style: italic; }
.crm-entry-mode { font-size: 12.5px; font-weight: 600; color: var(--brand); background: var(--brand-soft-bg, var(--surface-2)); border: 1px solid var(--border-soft); border-radius: 8px; padding: 5px 10px; margin-top: 4px; }
.crm-add-fase {
  align-self: center; background: transparent; border: 1.5px dashed var(--brand); color: var(--brand);
  border-radius: 14px; padding: 18px 14px; font-size: 13px; font-weight: 600; cursor: pointer;
  min-height: 64px;
}
.crm-add-fase:hover { background: var(--brand-soft-bg, var(--surface-2)); }

.crm-modal-backdrop { position: fixed; inset: 0; background: var(--scrim, rgba(0,0,0,.5)); display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 20px; }
.crm-modal { background: var(--surface-overlay, var(--panel-2)); border: 1px solid var(--border); border-radius: 16px; box-shadow: var(--shadow-lg); width: 100%; max-width: 460px; max-height: 90vh; overflow-y: auto; }
.crm-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px; border-bottom: 1px solid var(--border); color: var(--text-strong); font-size: 16px; }
.crm-modal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 14px 18px; border-top: 1px solid var(--border); }
.crm-form { display: flex; flex-direction: column; gap: 6px; padding: 16px 18px; }
.crm-form label { font-size: 12.5px; font-weight: 600; color: var(--muted); margin-top: 6px; }
.crm-input { background: var(--control-bg); color: var(--text); border: 1px solid var(--control-border); border-radius: 9px; padding: 8px 11px; font-size: 14px; width: 100%; box-sizing: border-box; }
.crm-textarea { min-height: 70px; resize: vertical; font-family: inherit; }
.crm-hint { font-size: 11.5px; color: var(--muted-2); margin-top: 4px; }

/* ── CRM · Detalle del lead (notas + historial) ──────────────────────────── */
.crm-card-name-link { cursor: pointer; text-decoration: underline; text-decoration-color: transparent; transition: text-decoration-color .12s ease; }
.crm-card-name-link:hover { text-decoration-color: var(--brand); color: var(--brand); }

.crm-modal-lead { max-width: 520px; }
.crm-lead-toolbar { display: flex; align-items: center; justify-content: space-between; padding: 12px 18px 0; gap: 10px; }
.crm-lead-body { padding: 14px 18px 18px; }
.crm-lead-notas { display: flex; flex-direction: column; gap: 12px; }
.crm-note-form { display: flex; flex-direction: column; gap: 8px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px; padding: 12px; }
.crm-note-tipo { width: auto; align-self: flex-start; }
.crm-lead-list { display: flex; flex-direction: column; gap: 8px; max-height: 320px; overflow-y: auto; }
.crm-note-item { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 9px 12px; }
.crm-note-item-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
.crm-note-tag { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--brand); }
.crm-note-date { font-size: 11px; color: var(--muted-2); }
.crm-note-text { font-size: 13.5px; color: var(--text); line-height: 1.4; }
.crm-hist-item { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
.crm-hist-evento { font-size: 13px; color: var(--text); font-weight: 600; }

/* ── CRM · Ficha del cliente (contacto + actividades + cronología) ────────── */
.crm-lead-contact { display: flex; flex-wrap: wrap; gap: 8px; padding: 12px 18px 0; }
.crm-contact-chip {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px;
  background: var(--surface-2); color: var(--text); border: 1px solid var(--border);
  border-radius: 999px; padding: 4px 12px; text-decoration: none;
}
.crm-contact-chip:hover { border-color: var(--brand); color: var(--brand); }
.crm-note-form-row { display: flex; gap: 8px; align-items: center; }
.crm-note-due { width: auto; flex: 0 0 auto; }
.crm-lead-section-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin-top: 2px; }

/* Actividad pendiente (nota con vencimiento) */
.crm-activity-item {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
  background: var(--surface); border: 1px solid var(--border); border-left-width: 3px;
  border-radius: 10px; padding: 9px 12px;
}
.crm-activity-item.crm-due-overdue { border-left-color: var(--badge-danger-fg); }
.crm-activity-item.crm-due-today   { border-left-color: var(--badge-warn-fg); }
.crm-activity-item.crm-due-soon    { border-left-color: var(--badge-success-fg); }
.crm-activity-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.crm-activity-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.crm-activity-actions { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.crm-due-badge { font-size: 11px; font-weight: 700; border-radius: 999px; padding: 2px 9px; white-space: nowrap; }
.crm-due-badge.crm-due-overdue { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.crm-due-badge.crm-due-today   { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.crm-due-badge.crm-due-soon    { background: var(--badge-success-bg); color: var(--badge-success-fg); }

/* Cronología unificada */
.crm-timeline { position: relative; }
.crm-timeline-item { display: flex; gap: 10px; padding: 8px 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
.crm-timeline-icon { font-size: 15px; flex: 0 0 auto; line-height: 1.5; }
.crm-timeline-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 auto; }
.crm-timeline-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }

/* ════════════════════════════════════════════════════════════════════════════
   CRM · Rediseño estilo Odoo (panel de control + vistas + tarjetas grandes)
   Estas reglas van DESPUÉS de las base → ganan por cascada (mismo specificity).
   ════════════════════════════════════════════════════════════════════════════ */

/* Más aire general */
.crm-wrap { padding: 0 0 32px; gap: 0; }

/* ── Panel de control (barra superior tipo Odoo) ─────────────────────────── */
.crm-cpanel {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; padding: 12px 24px; margin-bottom: 18px;
  background: var(--surface); border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 20;
}
.crm-cpanel-left { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.crm-cpanel-right { display: flex; align-items: center; gap: 10px; }
.crm-embudo-select { min-width: 200px; }

/* Conmutador de vistas (iconos, derecha) */
.crm-viewswitch { display: inline-flex; background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px; padding: 3px; gap: 2px; }
.crm-viewbtn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; height: 30px; padding: 0 8px; font-size: 15px; line-height: 1;
  background: transparent; color: var(--muted); border: none; border-radius: 8px; cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.crm-viewbtn:hover { background: var(--control-bg-hover); color: var(--text); }
.crm-viewbtn.active { background: var(--brand); color: #fff; box-shadow: var(--shadow-sm); }
/* El botón Constructor (⚙) va suelto a la derecha del grupo */
.crm-cpanel-right > .crm-viewbtn { border: 1px solid var(--border); background: var(--surface-2); }
.crm-cpanel-right > .crm-viewbtn.active { border-color: var(--brand); background: var(--brand); }

/* Buscador (centro del panel) */
.crm-cpanel-search { flex: 1 1 240px; max-width: 480px; position: relative; display: flex; align-items: center; }
.crm-search-ico { position: absolute; left: 13px; font-size: 13px; opacity: .55; pointer-events: none; }
.crm-search-input {
  width: 100%; background: var(--control-bg); color: var(--text);
  border: 1px solid var(--control-border); border-radius: 999px;
  padding: 9px 34px 9px 36px; font-size: 13.5px; box-sizing: border-box;
}
.crm-search-input:focus { border-color: var(--brand); outline: none; box-shadow: 0 0 0 3px var(--brand-soft-bg, transparent); }
.crm-search-clear {
  position: absolute; right: 8px; width: 22px; height: 22px; border: none; background: transparent;
  color: var(--muted); cursor: pointer; font-size: 17px; line-height: 1; border-radius: 50%;
}
.crm-search-clear:hover { background: var(--surface-2); color: var(--text); }

/* Botón "+" de alta manual por fase */
.crm-col-head-actions { display: flex; align-items: center; gap: 6px; }
.crm-col-add {
  width: 24px; height: 24px; border-radius: 7px; border: 1px solid var(--border);
  background: var(--surface); color: var(--brand); font-size: 17px; line-height: 1;
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.crm-col-add:hover { background: var(--brand); color: #fff; border-color: var(--brand); }

/* Board sin scrollbars internos por columna: la página entera scrollea */
.crm-col { max-height: none; }
.crm-col-body { overflow-y: visible; }
.crm-board { padding-bottom: 24px; }

/* ── Ficha completa del lead (/crm/lead/:embudo/:user) ───────────────────── */
.crm-lead-page { padding: 18px 24px 48px; display: flex; flex-direction: column; gap: 18px; max-width: 1100px; margin: 0 auto; }
.crm-lp-top { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.crm-lp-title { display: flex; flex-direction: column; gap: 2px; }
.crm-lp-title h2 { margin: 0; font-size: 22px; font-weight: 800; color: var(--text-strong); }
.crm-lp-id { font-size: 11.5px; color: var(--muted-2); font-family: ui-monospace, monospace; }
.crm-lp-stepper { display: flex; flex-wrap: wrap; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--surface-2); }
.crm-lp-step { flex: 1 1 auto; min-width: 96px; padding: 11px 14px; border: none; border-right: 1px solid var(--border); background: transparent; color: var(--muted); font-size: 13.5px; font-weight: 700; cursor: pointer; transition: background .12s ease, color .12s ease; }
.crm-lp-step:last-child { border-right: none; }
.crm-lp-step:hover:not(:disabled) { background: var(--surface-3, var(--surface)); color: var(--text); }
.crm-lp-step.active { background: var(--brand); color: #fff; }
.crm-lp-step:disabled { cursor: default; opacity: .9; }
.crm-lp-body { display: grid; grid-template-columns: minmax(0, 340px) minmax(0, 1fr); gap: 18px; align-items: start; }
.crm-lp-main { display: flex; flex-direction: column; gap: 14px; }
.crm-lp-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 16px; box-shadow: var(--shadow-sm); }
.crm-lp-card-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin-bottom: 10px; }
.crm-lp-card-title--sep { margin-top: 14px; }
.crm-lp-field { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--text); padding: 5px 0; }
.crm-lp-field > span:first-child { width: 18px; text-align: center; flex: 0 0 auto; }
.crm-lp-field a { color: var(--brand); text-decoration: none; }
.crm-lp-field a:hover { text-decoration: underline; }
.crm-lp-chatter { display: flex; flex-direction: column; gap: 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 16px; box-shadow: var(--shadow-sm); }
@media (max-width: 860px) { .crm-lp-body { grid-template-columns: 1fr; } }

/* ── Ficha del lead v2 — split-screen alta densidad (estilo Odoo clásico) ─── */
.crm-lp2-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 20px; background: var(--surface-2); border-bottom: 1px solid var(--border); }
.crm-lp2-btn { padding: 7px 14px; font-size: 13px; font-weight: 600; border: 1px solid var(--border); background: var(--surface); color: var(--text); border-radius: 4px; cursor: pointer; transition: background .12s ease, border-color .12s ease; }
.crm-lp2-btn:hover:not(:disabled) { background: var(--surface-3, var(--surface-2)); border-color: var(--brand); }
.crm-lp2-btn--won { background: var(--brand); color: #fff; border-color: var(--brand); }
.crm-lp2-btn--won:hover:not(:disabled) { background: var(--brand-dark, var(--brand)); }
.crm-lp2-btn:disabled { opacity: .5; cursor: default; }

.crm-lp2 { display: flex; align-items: stretch; }
.crm-lp2-left { flex: 1 1 60%; min-width: 0; padding: 20px 26px; border-right: 1px solid var(--border); }
.crm-lp2-right { flex: 1 1 40%; min-width: 0; padding: 16px 20px; background: var(--bg-2, var(--surface-2)); }

.crm-lp2-headrow { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; }
.crm-lp2-titlewrap { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.crm-lp2-title { margin: 0; font-size: 26px; font-weight: 800; color: var(--text-strong); line-height: 1.1; }

/* Breadcrumb con chevrons (arriba-derecha) */
.crm-lp2-breadcrumb { display: inline-flex; }
.crm-lp2-step {
  border: none; background: var(--surface-2); color: var(--muted); font-size: 12px; font-weight: 700;
  padding: 8px 14px 8px 22px; cursor: pointer; white-space: nowrap;
  clip-path: polygon(0 0, calc(100% - 11px) 0, 100% 50%, calc(100% - 11px) 100%, 0 100%, 11px 50%);
  margin-left: -10px;
}
.crm-lp2-step:first-child { margin-left: 0; padding-left: 14px; clip-path: polygon(0 0, calc(100% - 11px) 0, 100% 50%, calc(100% - 11px) 100%, 0 100%); }
.crm-lp2-step:hover:not(:disabled):not(.active) { background: var(--surface-3, var(--surface)); color: var(--text); }
.crm-lp2-step.active { background: var(--brand); color: #fff; }
.crm-lp2-step:disabled { cursor: default; }

/* Grid etiqueta-valor en DOS columnas de pares (aprovecha el ancho, con aire) */
.crm-lp2-grid { display: grid; grid-template-columns: 118px minmax(0, 1fr) 118px minmax(0, 1fr); gap: 14px 24px; align-items: baseline; font-size: 13.5px; }
.crm-lp2-lbl { color: var(--muted); font-weight: 600; padding: 1px 0; }
.crm-lp2-val { color: var(--text); padding: 1px 0; min-width: 0; word-break: break-word; }
.crm-lp2-val a { color: var(--brand); text-decoration: none; }
.crm-lp2-val a:hover { text-decoration: underline; }
.crm-lp2-money { font-size: 20px; font-weight: 800; color: var(--text-strong); }
.crm-lp2-empty { color: var(--muted-2); }
.crm-lp2-grid--info { margin-top: 2px; grid-template-columns: 118px minmax(0, 1fr); gap: 12px 20px; }
@media (max-width: 620px) { .crm-lp2-grid { grid-template-columns: 110px minmax(0, 1fr); } }

/* Tabs inferiores izquierda */
.crm-lp2-subtabs { display: flex; border-bottom: 1px solid var(--border); margin-top: 22px; }
.crm-lp2-subtab { padding: 9px 16px; border: none; background: transparent; color: var(--muted); font-size: 13.5px; font-weight: 600; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.crm-lp2-subtab:hover { color: var(--text); }
.crm-lp2-subtab.active { color: var(--brand); border-bottom-color: var(--brand); }
.crm-lp2-subtabbody { padding: 14px 0; }
.crm-lp2-notelist { display: flex; flex-direction: column; gap: 10px; }
.crm-lp2-noteitem { border-bottom: 1px solid var(--border-soft, var(--border)); padding-bottom: 8px; }
.crm-lp2-noteitem:last-child { border-bottom: none; }
.crm-lp2-noteitem-head { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 3px; }

/* Chatter derecha */
.crm-lp2-chattertabs { display: flex; gap: 6px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
.crm-lp2-ctab { padding: 8px 14px; border: none; background: transparent; color: var(--muted); font-size: 13.5px; font-weight: 700; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.crm-lp2-ctab:hover { color: var(--text); }
.crm-lp2-ctab.active { color: var(--brand); border-bottom-color: var(--brand); }
.crm-lp2-chatterform { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.crm-lp2-tl-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 16px 0 8px; }
.crm-lp2-act { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; padding: 8px 0 8px 10px; border-left: 3px solid var(--border); margin-bottom: 4px; }
.crm-lp2-act.crm-due-overdue { border-left-color: var(--badge-danger-fg); }
.crm-lp2-act.crm-due-today { border-left-color: var(--badge-warn-fg); }
.crm-lp2-act.crm-due-soon { border-left-color: var(--badge-success-fg); }
.crm-lp2-act-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1 1 auto; }
.crm-lp2-timeline { display: flex; flex-direction: column; }
.crm-lp2-tl-item { display: flex; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--border-soft, var(--border)); }
.crm-lp2-tl-item:last-child { border-bottom: none; }
.crm-lp2-tl-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; margin-top: 6px; background: var(--muted-2); }
.crm-tl-move { background: var(--brand); }
.crm-tl-note { background: var(--badge-success-fg); }
.crm-tl-system { background: var(--muted-2); }
.crm-lp2-tl-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.crm-lp2-tl-line1 { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.crm-lp2-tl-who { font-weight: 700; color: var(--text-strong); font-size: 13px; }
.crm-lp2-tl-what { font-size: 12.5px; color: var(--muted); }
.crm-lp2-tl-detail { font-size: 13px; color: var(--text); margin-top: 3px; line-height: 1.4; }

@media (max-width: 900px) {
  .crm-lp2 { flex-direction: column; }
  .crm-lp2-left { border-right: none; border-bottom: 1px solid var(--border); flex-basis: auto; }
  .crm-lp2-headrow { flex-direction: column; }
}

/* ── Ficha del lead v2 — fuentes más grandes + modales (etiquetas / actividad) ─ */
.crm-lp2-title { font-size: 28px; }
.crm-lp2-grid { font-size: 15px; }
.crm-lp2-lbl { font-size: 13.5px; }
.crm-lp2-val { font-size: 15px; }
.crm-lp2-money { font-size: 22px; }
.crm-lp2-left .crm-note-text, .crm-lp2-right .crm-note-text { font-size: 14px; }
.crm-lp2-tl-who { font-size: 14.5px; }
.crm-lp2-tl-what { font-size: 13.5px; }
.crm-lp2-tl-detail { font-size: 14px; }
.crm-lp2-tl-title { font-size: 12px; }
.crm-lp2-noteitem-head .crm-note-tag, .crm-lp2-act .crm-note-tag { font-size: 12.5px; }

/* Modal actividad — selector de tipo */
.crm-actmodal-types { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
.crm-actmodal-type { padding: 8px 13px; border: 1px solid var(--border); background: var(--surface-2); color: var(--text); border-radius: 6px; font-size: 13px; font-weight: 600; cursor: pointer; }
.crm-actmodal-type:hover { border-color: var(--brand); }
.crm-actmodal-type.active { background: var(--brand); color: #fff; border-color: var(--brand); }

/* Modal etiquetas — catálogo con color */
.crm-tagmodal-list { display: flex; flex-wrap: wrap; gap: 8px; min-height: 20px; }
.crm-tagchip { display: inline-flex; align-items: center; gap: 2px; border: 1.5px solid; border-radius: 999px; padding: 3px 4px 3px 11px; font-size: 13px; font-weight: 600; background: transparent; }
.crm-tagchip.on { box-shadow: 0 0 0 2px rgba(0,0,0,.06) inset; }
.crm-tagchip-lbl { cursor: pointer; }
.crm-tagchip-del { border: none; background: transparent; color: inherit; cursor: pointer; font-size: 15px; line-height: 1; opacity: .55; padding: 0 3px; }
.crm-tagchip-del:hover { opacity: 1; }
.crm-tagmodal-create { display: flex; gap: 8px; align-items: center; }
.crm-tagmodal-create .crm-input { flex: 1 1 auto; }
.crm-tagmodal-color { width: 44px; height: 38px; padding: 2px; border: 1px solid var(--control-border); border-radius: 8px; background: var(--control-bg); cursor: pointer; flex: 0 0 auto; }

/* Vista Actividades — barra de filtros + estado hecho + quién */
.crm-acts-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.crm-act-who { font-size: 12px; color: var(--muted-2); margin-top: 3px; }
.crm-act-done { opacity: .72; }
.crm-act-done.crm-act-item { border-left-color: var(--muted-2); }
.crm-act-done .crm-note-text { text-decoration: line-through; }
.crm-act-group-title:not(.crm-due-overdue):not(.crm-due-today):not(.crm-due-soon) { color: var(--muted); }

/* Constructor — form amigable de condición/regla + drag de fases */
.crm-cond-fields { display: flex; flex-wrap: wrap; gap: 8px 16px; padding: 4px 0 2px; }
.crm-check { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; color: var(--text); cursor: pointer; }
.crm-check input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; accent-color: var(--brand); }
.crm-fase-card[draggable="true"] { cursor: grab; }
.crm-fase-card[draggable="true"]:active { cursor: grabbing; }

/* ── Kanban más grande, vibrante y aireado (estilo Odoo, colores de marca) ── */
.crm-board { gap: 16px; padding: 0 24px 18px; }
.crm-col {
  flex: 0 0 340px; min-width: 340px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 14px; padding: 0; box-shadow: none; gap: 0;
  max-height: calc(100vh - 180px);
  transition: box-shadow .12s ease, border-color .12s ease, background .12s ease;
}
.crm-col-head {
  padding: 14px 16px 10px; border-top: 4px solid var(--brand); border-radius: 14px 14px 0 0;
  align-items: flex-start;
  background: linear-gradient(var(--brand-soft-bg, transparent), transparent);
}
.crm-col-name { font-size: 15px; font-weight: 800; letter-spacing: -.01em; }
.crm-col-count { font-size: 12.5px; }
.crm-col-obj { padding: 0 16px; }
.crm-col-body { padding: 12px 14px 16px; gap: 12px; overflow-y: auto; min-height: 60px; }
/* Resaltado al arrastrar sobre la columna */
.crm-col-drop { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand) inset; background: var(--brand-soft-bg, var(--surface-3)); }
.crm-col-drophint { border: 1.5px dashed var(--border); border-radius: 10px; }

.crm-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 14px 15px; gap: 10px; box-shadow: var(--shadow-sm);
  cursor: grab; transition: box-shadow .12s ease, transform .12s ease, border-color .12s ease;
}
.crm-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); border-color: var(--brand); }
.crm-card:active { cursor: grabbing; }
.crm-card-name { font-size: 15.5px; font-weight: 700; color: var(--text-strong); }
.crm-card-sub { font-size: 12.5px; }

/* Etiquetas (tags) */
.crm-card-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.crm-tag {
  display: inline-flex; align-items: center; font-size: 11px; font-weight: 600;
  background: var(--badge-info-bg); color: var(--badge-info-fg);
  border-radius: 6px; padding: 2px 8px;
}
.crm-tag-add { background: transparent; color: var(--muted); border: 1px dashed var(--border); cursor: pointer; }
.crm-tag-add:hover { color: var(--brand); border-color: var(--brand); }

.crm-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 2px; }
/* Estrellas de prioridad (0-3) */
.crm-card-stars { display: inline-flex; gap: 1px; }
.crm-star { font-size: 16px; line-height: 1; color: var(--border); cursor: pointer; transition: color .1s ease, transform .1s ease; }
.crm-star:hover { transform: scale(1.2); }
.crm-star.on { color: #f5b301; }
.crm-card-activity {
  display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700;
  border-radius: 999px; padding: 3px 10px; white-space: nowrap;
}
.crm-card-activity.crm-due-overdue { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.crm-card-activity.crm-due-today   { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.crm-card-activity.crm-due-soon    { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.crm-card-avatar {
  margin-left: auto; flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 800;
  background: var(--brand); color: #fff;
}

/* ── Vista Lista ─────────────────────────────────────────────────────────── */
.crm-list-wrap { margin: 0 24px; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--surface); }
.crm-list-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.crm-list-table thead th {
  text-align: left; padding: 11px 14px; font-size: 12px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .03em; background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.crm-list-table td { padding: 11px 14px; border-bottom: 1px solid var(--border-soft, var(--border)); color: var(--text); }
.crm-list-row { cursor: pointer; transition: background .1s ease; }
.crm-list-row:hover { background: var(--surface-2); }
.crm-list-row:last-child td { border-bottom: none; }
.crm-list-name { font-weight: 600; color: var(--text-strong); }
.crm-pill {
  display: inline-flex; align-items: center; font-size: 12px; font-weight: 600;
  background: var(--surface-2); color: var(--text); border: 1px solid var(--border);
  border-radius: 999px; padding: 2px 10px;
}
.crm-estado-activo { background: var(--badge-success-bg); color: var(--badge-success-fg); border-color: transparent; }
.crm-estado-pausado { background: var(--badge-warn-bg); color: var(--badge-warn-fg); border-color: transparent; }
.crm-estado-completado { background: var(--badge-info-bg); color: var(--badge-info-fg); border-color: transparent; }

/* ── Vista Actividades ───────────────────────────────────────────────────── */
.crm-acts { display: flex; flex-direction: column; gap: 18px; padding: 0 24px; max-width: 900px; }
.crm-act-group { display: flex; flex-direction: column; gap: 8px; }
.crm-act-group-title { font-size: 13px; font-weight: 700; padding: 4px 2px; }
.crm-act-group-title.crm-due-overdue { color: var(--badge-danger-fg); }
.crm-act-group-title.crm-due-today   { color: var(--badge-warn-fg); }
.crm-act-group-title.crm-due-soon    { color: var(--badge-success-fg); }
.crm-act-item {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  background: var(--surface); border: 1px solid var(--border); border-left-width: 3px;
  border-radius: 10px; padding: 11px 14px;
}
.crm-act-item.crm-due-overdue { border-left-color: var(--badge-danger-fg); }
.crm-act-item.crm-due-today   { border-left-color: var(--badge-warn-fg); }
.crm-act-item.crm-due-soon    { border-left-color: var(--badge-success-fg); }
.crm-act-main { display: flex; flex-direction: column; gap: 5px; cursor: pointer; min-width: 0; flex: 1 1 auto; }
.crm-act-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* ── Vista Calendario ────────────────────────────────────────────────────── */
.crm-cal { padding: 0 24px; }
.crm-cal-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.crm-cal-title { font-size: 17px; font-weight: 700; color: var(--text-strong); min-width: 160px; }
.crm-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.crm-cal-dow { text-align: center; font-size: 11px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; padding: 4px 0; }
.crm-cal-cell {
  min-height: 108px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
  padding: 6px; display: flex; flex-direction: column; gap: 4px;
}
.crm-cal-empty { background: transparent; border-color: transparent; }
.crm-cal-today { border-color: var(--brand); box-shadow: inset 0 0 0 1px var(--brand); }
.crm-cal-daynum { font-size: 12px; font-weight: 700; color: var(--muted); align-self: flex-end; }
.crm-cal-today .crm-cal-daynum { color: var(--brand); }
.crm-cal-events { display: flex; flex-direction: column; gap: 3px; overflow: hidden; }
.crm-cal-ev {
  font-size: 11px; font-weight: 600; border-radius: 6px; padding: 2px 6px; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.crm-cal-ev.crm-due-overdue { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.crm-cal-ev.crm-due-today   { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.crm-cal-ev.crm-due-soon    { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.crm-cal-ev.crm-cal-done    { background: var(--surface-2); color: var(--muted-2); text-decoration: line-through; }

@media (max-width: 720px) {
  .crm-cal-cell { min-height: 74px; }
  .crm-cpanel { padding: 10px 14px; }
  .crm-board, .crm-acts, .crm-cal, .crm-list-wrap { padding-left: 14px; padding-right: 14px; }
  .crm-list-wrap { margin: 0 14px; }
}

/* ── CRM · Entrada del flujo (con reglas de inscripción) ─────────────────── */
.crm-flow-entry-card { background: var(--brand-soft-bg, var(--surface-2)); border: 1px solid var(--border); border-radius: 14px; padding: 14px 16px; box-shadow: var(--shadow-sm); }
.crm-flow-entry-ico { font-size: 16px; color: var(--brand); }

/* ── CRM · Plantillas / Import / Export ──────────────────────────────────── */
.crm-modal-wide { max-width: 640px; }
.crm-template-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px; padding: 16px 18px; }
.crm-template-card {
  display: flex; flex-direction: column; gap: 6px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 12px; padding: 14px; cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.crm-template-card:hover { border-color: var(--brand); background: var(--brand-soft-bg, var(--surface)); }
.crm-template-icon { font-size: 22px; }
.crm-template-card strong { color: var(--text-strong); font-size: 14px; }
.crm-template-resumen { color: var(--muted); font-size: 12px; line-height: 1.4; }
.crm-textarea-lg { min-height: 220px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }

/* ── Pill deslizante en barras de tabs (segmented controls) ──────────────────
   El resaltado del tab activo (fondo brand-dark) se DESLIZA entre pestañas en vez
   de saltar. Es 100% global: un listener en index.html marca cada contenedor de
   tabs con `.slide-tabs` y escribe las variables `--pill-*` a partir del botón
   activo (detectado por su clase: .active / .is-active / *--active). No hay que
   tocar la lógica de cada tab. Los tabs de SUBRAYADO (ticket-monitor-subtabs,
   notif) NO se incluyen (usan border-bottom, no un fondo). La lista de
   contenedores vive en el JS de index.html. Ver docs/frontend/guias/tabs_pill_deslizante.md */
.slide-tabs { position: relative; }
.slide-tabs > button { position: relative; z-index: 1; }
.slide-tabs::after {
  content: "";
  position: absolute;
  left: 0; top: 0;
  width: var(--pill-w, 0);
  height: var(--pill-h, 0);
  transform: translate(var(--pill-x, 0), var(--pill-y, 0));
  background: var(--brand-dark);
  border-radius: 999px;                        /* pill moderno */
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.14);
  opacity: var(--pill-o, 0);
  z-index: 0;
  pointer-events: none;
  transition: transform 0.4s cubic-bezier(0.65, 0, 0.35, 1),
              width     0.4s cubic-bezier(0.65, 0, 0.35, 1),
              height    0.4s cubic-bezier(0.65, 0, 0.35, 1);
}
/* Primer posicionamiento sin animación (evita que el pill "crezca" desde 0,0). */
.slide-tabs.slide-tabs--no-anim::after { transition: none; }

/* El botón activo deja de pintar su propio fondo: el pill es el único resaltado.
   (Gana en especificidad al `.active { background: !important }` global y a los
   `*--active` de cada módulo.) El color del texto lo mantiene cada módulo. */
.slide-tabs > .active,
.slide-tabs > .is-active,
.slide-tabs > [class*="--active"] {
  background: transparent !important;
  box-shadow: none !important;
}

/* TEMA CLARO: los overrides `html.light-mode .<modulo>-tab.active` (0,3,1) le ganaban a
   la regla de arriba (0,2,0) y repintaban un fondo propio translúcido + texto de MARCA.
   Como el pill de detrás es un sólido de marca, quedaba texto de marca sobre fondo de
   marca = invisible (bug: tabs del centro de control y de tickets en claro). El pill es
   el único resaltado, así que sobre él el texto va blanco en AMBOS temas, igual que en
   oscuro. La clase se repite (.slide-tabs.slide-tabs) para mantener la especificidad
   en 0,4,1 y ganarle a esos overrides sin depender del orden del archivo.
   Ya no hay excepciones: camp-tabs tenía un pill claro propio y se unificó con el
   resto al estandarizar el segmented control. */
html.light-mode .slide-tabs.slide-tabs > .active,
html.light-mode .slide-tabs.slide-tabs > .is-active,
html.light-mode .slide-tabs.slide-tabs > [class*="--active"] {
  background: transparent !important;
  box-shadow: none !important;
  color: #fff !important;
}

/* camp-tabs usa un pill claro (var(--surface)) en vez de brand-dark. */
.slide-tabs.camp-tabs::after {
  background: var(--surface);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}

@media (prefers-reduced-motion: reduce) {
  .slide-tabs::after { transition: none; }
}

/* ── Medidor de fortaleza de contraseña ──────────────────────────────────────
   Segmentos que se llenan según el score 0..4 (util::password::password_score),
   alineado con la política de contraseñas del backend. Colores por tokens. */
.pw-meter { margin-top: 8px; display: flex; flex-direction: column; gap: 5px; }
.pw-meter--hidden { display: none; }

.meter { display: flex; gap: 5px; }
.meter span {
  flex: 1; height: 5px; border-radius: 3px;
  background: var(--control-bg);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.2s ease;
}
.meter[data-score="1"] span:nth-child(-n+1),
.meter[data-score="2"] span:nth-child(-n+2),
.meter[data-score="3"] span:nth-child(-n+3),
.meter[data-score="4"] span { transform: scaleX(1); }

.meter[data-score="1"] span { background: var(--badge-danger-fg); }
.meter[data-score="2"] span { background: var(--badge-warn-fg); }
.meter[data-score="3"] span,
.meter[data-score="4"] span { background: var(--badge-success-fg); }

.pw-meter__row { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.pw-meter__label { font-size: 12px; font-weight: 600; white-space: nowrap; }
.pw-meter__label[data-score="1"] { color: var(--badge-danger-fg); }
.pw-meter__label[data-score="2"] { color: var(--badge-warn-fg); }
.pw-meter__label[data-score="3"],
.pw-meter__label[data-score="4"] { color: var(--badge-success-fg); }
.pw-meter__hint { font-size: 11.5px; color: var(--muted); text-align: right; }

/* ── Feedback al presionar un botón ──────────────────────────────────────────
   Indica visualmente que el click se registró. Aplica a TODOS los botones (micro
   escala al presionar); el ripple va en los botones de acción principales.
   `:disabled` no reacciona (los botones en curso ya se bloquean — anti doble envío). */
button { transition: transform 0.16s cubic-bezier(0.34, 1.56, 0.64, 1); }
button:active:not(:disabled) {
  transform: scale(0.96);
  transition: transform 0.07s ease-out;
}

/* Ripple: onda desde el punto de click en los CTA principales. El <span.ripple> lo
   inyecta un listener delegado en index.html. Requiere overflow:hidden en el host. */
.btn-primary, .btn-login, .btn-danger, .btn-create, .camp-btn {
  position: relative;
  overflow: hidden;
}
.ripple {
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.4);
  transform: scale(0);
  animation: ripple-out 0.6s ease-out;
  pointer-events: none;
}
@keyframes ripple-out {
  to { transform: scale(3); opacity: 0; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Notificaciones internas — campana del header + página + preferencias
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Campana en el header ── */
.ph-notif { position: relative; display: flex; align-items: center; }
.ph-notif-btn {
  position: relative;
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  border-radius: 10px;
  width: 38px; height: 38px;
  font-size: 1.05rem;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: var(--text);
  transition: background .15s, transform 0.15s, box-shadow 0.18s;
}
.ph-notif-btn:hover { background: var(--control-bg-hover); }
.ph-notif-badge {
  position: absolute; top: -4px; right: -4px;
  min-width: 17px; height: 17px; padding: 0 4px;
  background: #ff8a00; color: #fff;
  border-radius: 9px; font-size: 0.66rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  line-height: 1;
  z-index: 1;
}
/* Halo pulsante detrás del badge mientras haya no-leídas (solo se renderiza el
   badge cuando unread > 0, así que la animación solo corre cuando corresponde). */
.ph-notif-badge::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  background: #ff8a00;
  z-index: -1;
  animation: notif-pulse 1.8s ease-out infinite;
}
@keyframes notif-pulse {
  0%        { transform: scale(1);   opacity: 0.7; }
  70%, 100% { transform: scale(2.6); opacity: 0; }
}
/* El menú no se desmonta: `is-open` anima despliegue y colapso (140 ms). El cierre
   al pulsar fuera lo hace un listener de window, no un backdrop `fixed` — el header
   lleva `backdrop-filter` y eso vuelve al header containing block de sus hijos
   `fixed`, así que el backdrop solo cubría los 60px de la cabecera. */
.ph-notif-menu {
  position: absolute; top: 46px; right: 0; z-index: 41;
  width: 340px; max-width: 90vw;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  transform-origin: top right;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px) scale(0.97);
  pointer-events: none;
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0s linear 0.14s;
}
.ph-notif-menu.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0s;
}
.ph-notif-menu-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border-soft);
  font-weight: 600; color: var(--text-strong);
  font-size: 0.9rem;
}
.ph-notif-all {
  background: none; border: none; cursor: pointer;
  color: var(--brand); font-size: 0.8rem; font-weight: 600;
}
/* Scrollbar estilo sidebar: fina, sin carril gris reservado y solo visible
   cuando el puntero está dentro del menú. */
.ph-notif-menu-list {
  max-height: 380px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
}
.ph-notif-menu-list:hover { scrollbar-color: rgba(255, 255, 255, 0.20) transparent; }
.ph-notif-menu-list::-webkit-scrollbar { width: 8px; }
.ph-notif-menu-list::-webkit-scrollbar-track { background: transparent; }
.ph-notif-menu-list::-webkit-scrollbar-thumb {
  background: transparent;
  border: 2px solid transparent;               /* carril propio, no pisa el texto */
  background-clip: padding-box;
  border-radius: 8px;
  transition: background-color 0.2s ease;
}
.ph-notif-menu-list:hover::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.20); background-clip: padding-box; }
.ph-notif-menu-list:hover::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.34); background-clip: padding-box; }
html.light-mode .ph-notif-menu-list:hover { scrollbar-color: rgba(0, 0, 0, 0.22) transparent; }
html.light-mode .ph-notif-menu-list:hover::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.18); background-clip: padding-box; }
html.light-mode .ph-notif-menu-list:hover::-webkit-scrollbar-thumb:hover { background: rgba(0, 0, 0, 0.30); background-clip: padding-box; }
.ph-notif-empty { padding: 24px; text-align: center; color: var(--muted); font-size: 0.85rem; }
/* Fila = <button>: hay que anular el chrome del navegador (borde claro y fondo
   propio), si no se ve un contorno blanco por fila. */
.ph-notif-row {
  display: block;
  width: 100%;
  appearance: none;
  border: none;
  border-bottom: 1px solid var(--border-soft);
  background: transparent;
  color: inherit;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  padding: 10px 14px;
  transition: background 0.12s ease;
}
.ph-notif-row:last-child { border-bottom: none; }
.ph-notif-row:hover { background: var(--control-bg-hover); }
.ph-notif-row--unread { background: var(--brand-soft-bg); }
.ph-notif-row--unread:hover { background: var(--control-bg-hover); }

/* "Ver más (+N)": carga la siguiente tanda sin salir del dropdown. */
.ph-notif-more {
  display: block;
  width: 100%;
  appearance: none;
  border: none;
  border-top: 1px solid var(--border-soft);
  background: transparent;
  color: var(--brand);
  font-family: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  padding: 10px 14px;
  transition: background 0.12s ease;
}
.ph-notif-more:hover { background: var(--control-bg-hover); }
.ph-notif-more:disabled { color: var(--muted); cursor: default; }
.ph-notif-row-title { font-size: 0.85rem; font-weight: 600; color: var(--text-strong); }
.ph-notif-row-body {
  font-size: 0.78rem; color: var(--muted); margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ══════════════════════════════════════════════════════════════════════════
   Esqueletos de carga (shimmer) — LiveChat: lista, chat y ficha de contacto
   ══════════════════════════════════════════════════════════════════════════ */
/* Tokens: :root = tema oscuro (base); html.light-mode = claro. */
:root { --skel-base: #1e2129; --skel-hi: #2a2e38; }
html.light-mode { --skel-base: #e0e4ec; --skel-hi: #f2f4f9; }

.skel {
  background: linear-gradient(90deg, var(--skel-base) 25%, var(--skel-hi) 50%, var(--skel-base) 75%);
  background-size: 200% 100%;
  animation: shimmer-sweep 1.4s ease-in-out infinite;
  border-radius: 6px;
}
@keyframes shimmer-sweep {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) { .skel { animation: none; } }

/* formas base */
.skel-avatar    { width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0; }
.skel-avatar-lg { width: 60px; height: 60px; border-radius: 50%; margin: 0 auto 6px; }
.skel-bar       { height: 11px; }
.skel-line      { height: 12px; }
.skel-bubble    { height: 34px; border-radius: 14px; }

/* lista de conversaciones */
.conv-skel-list { list-style: none; margin: 0; padding: 0; }
.conv-skel {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border-bottom: 1px solid var(--border-soft);
}
.conv-skel-lines { flex: 1; display: flex; flex-direction: column; gap: 8px; }

/* chat */
.chat-skel { display: flex; flex-direction: column; gap: 12px; padding: 18px 16px; }
.msg-skel { display: flex; }
.msg-skel--in  { justify-content: flex-start; }
.msg-skel--out { justify-content: flex-end; }
.msg-skel .skel-bubble { min-width: 120px; }

/* ficha de contacto */
.contact-skel { display: flex; flex-direction: column; gap: 12px; padding: 16px; }
.contact-skel-field { display: flex; flex-direction: column; gap: 6px; }

/* ── Toasts (notificaciones emergentes) ── */
.toast-host {
  position: fixed; top: 18px; left: 50%; transform: translateX(-50%);
  z-index: 9999; display: flex; flex-direction: column; gap: 10px;
  align-items: center; pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: 11px;
  min-width: 280px; max-width: 92vw;
  padding: 12px 18px;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-left: 4px solid var(--brand);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  color: var(--text-strong); font-size: 0.9rem; font-weight: 600;
  animation: toast-in 0.55s cubic-bezier(0.18, 1.25, 0.4, 1) both;
}
.toast--info    { border-left-color: var(--badge-info-fg, #3b82f6); }
.toast--ok      { border-left-color: var(--badge-success-fg, #22c55e); }
.toast--warn    { border-left-color: #ff8a00; }
.toast--danger  { border-left-color: var(--badge-danger-fg, #ef4444); }
.toast-dot {
  width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0;
  background: currentColor;
}
.toast--info   .toast-dot { color: var(--badge-info-fg, #3b82f6); }
.toast--ok     .toast-dot { color: var(--badge-success-fg, #22c55e); }
.toast--warn   .toast-dot { color: #ff8a00; }
.toast--danger .toast-dot { color: var(--badge-danger-fg, #ef4444); }
.toast-title { line-height: 1.3; }
@keyframes toast-in {
  from { transform: translateY(-160%); opacity: 0; }
  to   { transform: translateY(0);     opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
}

/* ── Página de notificaciones ── */
/* .notif-tabs / .notif-tab: SEGMENTED estándar, estilo en el bloque canónico
   (buscar "SEGMENTED CONTROL ESTÁNDAR"). Aquí solo el contenido del módulo. */

/* El módulo respira: mismo ancho de columna en las 3 pestañas y separado de los
   bordes de la pantalla (antes las filas quedaban pegadas a la izquierda). */
.notif-main {
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding: 20px 24px 32px;
}
.notif-main .notif-tabs { margin: 0 0 18px; }   /* alineadas a la izquierda */

.notif-empty { text-align: center; color: var(--muted); padding: 36px 0; font-size: 0.9rem; }

.notif-inbox-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
}
.notif-check { font-size: 0.85rem; color: var(--muted); display: flex; align-items: center; gap: 6px; }

.notif-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.notif-item {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 12px 14px;
  background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: 10px;
}
.notif-item--unread { border-left: 3px solid var(--brand); background: var(--surface-2); }
/* Hover de fila: el mismo del popover de la campana (marca dónde está el ratón). */
.notif-item { transition: background 0.12s ease; }
.notif-item:hover,
.notif-item--unread:hover { background: var(--control-bg-hover); }
.notif-item-btn { cursor: pointer; }
.notif-item-main { min-width: 0; flex: 1; }
.notif-item-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.notif-item-title { font-weight: 600; color: var(--text-strong); font-size: 0.9rem; }
.notif-item-body {
  font-size: 0.83rem; color: var(--muted); margin-top: 4px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.notif-item-meta { font-size: 0.74rem; color: var(--muted-2); margin-top: 6px; }
.notif-item-actions { display: flex; gap: 6px; flex-shrink: 0; align-items: center; }

/* fila completa clicable (abre el detalle) */
.notif-item-btn {
  flex: 1; min-width: 0; width: 100%;
  background: none; border: none; margin: 0; padding: 0;
  text-align: left; cursor: pointer; color: inherit; font: inherit;
  display: flex; flex-direction: column; gap: 4px;
}
.notif-item-btn:hover .notif-item-title { color: var(--brand); }
.notif-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--brand); flex-shrink: 0;
}

/* modal de detalle */
.notif-detail-backdrop {
  position: fixed; inset: 0; z-index: 60; background: var(--scrim, rgba(0,0,0,0.5));
}
.notif-detail {
  position: fixed; z-index: 61; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 780px; max-width: 94vw; max-height: 88vh; overflow-y: auto;
  background: var(--surface-overlay); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: var(--shadow-lg); padding: 24px 28px;
}
.notif-detail-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.notif-detail-close {
  background: none; border: none; cursor: pointer; font-size: 1.05rem;
  color: var(--muted); line-height: 1; padding: 4px;
}
.notif-detail-title { margin: 0 0 10px; font-size: 1.1rem; color: var(--text-strong); text-wrap: balance; }
.notif-detail-body { margin: 0 0 16px; font-size: 0.92rem; color: var(--text); line-height: 1.6; white-space: pre-wrap; }
.notif-detail-meta { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.notif-detail-meta > div { display: flex; gap: 10px; font-size: 0.85rem; color: var(--text); }
.notif-detail-k {
  min-width: 52px; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.03em; font-size: 0.72rem; padding-top: 2px;
}
.notif-detail-go { display: inline-block; text-decoration: none; }
.notif-detail-foot { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 4px; }
.notif-detail-foot .camp-btn { text-decoration: none; }
.notif-detail-hist {
  background: none; border: none; cursor: pointer; padding: 0;
  color: var(--brand); font: inherit; font-size: 0.85rem; font-weight: 600;
}

/* enlace con subrayado animado (hover) */
.underline-link {
  position: relative;
  text-decoration: none;
}
.underline-link::after {
  content: '';
  position: absolute;
  left: 0; bottom: -4px;
  width: 100%; height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s cubic-bezier(0.65, 0, 0.35, 1);
}
.underline-link:hover::after { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
  .underline-link::after { transition: none; }
}

/* cuerpo markdown en el detalle */
.notif-md { line-height: 1.6; }
.notif-md > *:first-child { margin-top: 0; }
.notif-md > *:last-child { margin-bottom: 0; }
.notif-md p { margin: 0 0 10px; }
.notif-md h1, .notif-md h2, .notif-md h3, .notif-md h4 {
  margin: 14px 0 8px; color: var(--text-strong); line-height: 1.3;
}
.notif-md h1 { font-size: 1.15rem; }
.notif-md h2 { font-size: 1.05rem; }
.notif-md h3 { font-size: 0.98rem; }
.notif-md ul, .notif-md ol { margin: 0 0 10px; padding-left: 22px; }
.notif-md li { margin: 3px 0; }
.notif-md a { color: var(--brand); text-decoration: underline; }
.notif-md code {
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: 5px; padding: 1px 5px; font-size: 0.86em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.notif-md pre {
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: 8px; padding: 12px 14px; overflow-x: auto; margin: 0 0 10px;
}
.notif-md pre code { background: none; border: none; padding: 0; }
.notif-md blockquote {
  margin: 0 0 10px; padding: 4px 14px; border-left: 3px solid var(--border);
  color: var(--muted);
}
.notif-md table { border-collapse: collapse; width: 100%; margin: 0 0 10px; font-size: 0.86rem; }
.notif-md th, .notif-md td { border: 1px solid var(--border-soft); padding: 6px 9px; text-align: left; }
.notif-md hr { border: none; border-top: 1px solid var(--border-soft); margin: 12px 0; }

/* ── Historial de enviados ──
   Filtros y paginación usan los bloques canónicos: TOOLBAR ESTÁNDAR
   (.tickets-toolbar + .contacts-search-form + embudo) y PAGINACIÓN ESTÁNDAR
   (.pg-pager). Aquí solo queda el scroll de la tabla. */
.notif-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.notif-table-scroll .notif-table { min-width: 520px; }

/* ── Responsive: pantallas pequeñas ── */
@media (max-width: 640px) {
  .notif-page { padding-left: 12px; padding-right: 12px; }
  .notif-main { padding: 14px 12px 24px; }
  .notif-item { flex-direction: column; align-items: stretch; }
  .notif-detail { width: 96vw; max-height: 90vh; padding: 16px 16px; }
  .notif-detail-foot { flex-direction: column; align-items: stretch; }
  .notif-inbox-toolbar { flex-direction: column; align-items: stretch; gap: 8px; }
  .ph-notif-menu { width: 94vw; right: -6px; }
}

/* ── Compositor (modal) ── */
/* El ancho lo pone .notif-main; las pestañas ya no fijan el suyo (antes la lista
   quedaba anclada a la izquierda con 720px sueltos). */
.notif-compose-card { width: min(640px, 94vw); }
/* Detalle de una enviada: modal de solo lectura con sus destinatarios. */
.notif-detail-card { width: min(820px, 94vw); }
.notif-detail-card .notif-table-scroll { max-height: 60vh; overflow-y: auto; padding: 0 18px; }
.notif-compose-card .notif-compose { padding: 4px 18px 0; max-height: 66vh; overflow-y: auto; }
.notif-field { margin-bottom: 14px; display: flex; flex-direction: column; gap: 6px; }
.notif-field > label { font-size: 0.8rem; font-weight: 600; color: var(--text-strong); }
.notif-field input[type=text], .notif-field textarea, .notif-field select {
  background: var(--control-bg); border: 1px solid var(--control-border);
  border-radius: 8px; padding: 9px 11px; color: var(--text); font-size: 0.88rem;
  width: 100%;
}
.notif-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: -6px 0 14px; }
.notif-chip {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 20px;
  background: var(--control-bg); border: 1px solid var(--control-border);
  font-size: 0.83rem; color: var(--text); cursor: pointer;
}
.notif-banner { padding: 10px 14px; border-radius: 8px; margin-bottom: 14px; font-size: 0.86rem; }
.notif-banner--ok  { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.notif-banner--err { background: var(--badge-danger-bg);  color: var(--badge-danger-fg); }

/* ── Preferencias / ajustes (tablas de checkboxes) ── */
.notif-master { display: flex; align-items: center; gap: 8px; font-size: 0.9rem; color: var(--text-strong); font-weight: 600; margin-bottom: 8px; }
.notif-prefs-hint { font-size: 0.8rem; color: var(--muted); margin: 0 0 14px; }
.notif-prefs-table th:not(:first-child), .cc-notif-settings .camp-table th:not(:first-child) { width: 90px; text-align: center; }
.notif-prefs-table td:not(:first-child), .cc-notif-settings .camp-table td:not(:first-child) { text-align: center; }
.notif-lock { font-size: 0.75rem; }
.notif-recipients { margin-top: 18px; border-top: 1px solid var(--border-soft); padding-top: 14px; }
.notif-recipients-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.notif-recipients-head h4 { margin: 0; font-size: 0.9rem; color: var(--text-strong); }

/* ============================================================
   Identidad Visual / Personalización de perfil — filas + dropdown
   con búsqueda + barra Guardar fija. Sin líneas separadoras.
   ============================================================ */
/* El panel es más ancho que la columna de 560px que tenía: la previa del banner
   vive aquí dentro y con una columna estrecha mentía sobre las proporciones
   reales (un tercio de 560 px no se parece a un tercio del ancho de la portada).
   Los controles siguen acotados aparte para que un select no se estire. */
.idv-panel {
  display: flex;
  flex-direction: column;
  gap: 22px;
  max-width: 920px;
  padding-bottom: 88px; /* deja aire para la barra fija de Guardar */
}

/* Ajustes agrupados por tipo, con el mismo contenedor que la config de
   e-commerce (`.ec-cfg-group`): son muchos y en una columna plana no se
   distinguía dónde acababa el color y empezaba el banner. */
.idv-group {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 18px 22px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.idv-group-title {
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--text-strong);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.idv-row { display: flex; flex-direction: column; gap: 8px; }
.idv-row-head { display: flex; align-items: center; gap: 6px; }
.idv-row-title { font-size: 0.9rem; font-weight: 600; color: var(--text-strong); }
/* Los controles no crecen con el panel: un desplegable de 900 px de ancho para
   elegir «Fábrica» es ilegible. Quien sí usa todo el ancho es la previa del
   banner, que está fuera de esta clase. */
.idv-row-control { max-width: 420px; display: flex; flex-direction: column; gap: 10px; }
/* Toggle pegado a la izquierda de su fila (sin texto al lado: lo explica el
   tooltip del título). */
.idv-row-switch { align-self: flex-start; justify-content: flex-start; }
/* Fila que rompe el ancho del panel: el editor de paleta necesita más aire que un
   select, si no los 6 campos quedan en una columna larguísima. */
.idv-row--stack { width: 100%; }

/* ============================================================
   Editor de paleta (.tep — theme editor palette)
   Compartido por Identidad Visual y Perfil → Apariencia.
   ============================================================ */

/* Conmutador «Tema predefinido» / «Personalizado». Son excluyentes (el custom
   REEMPLAZA al preset, no se apila), así que se pintan como un segmented control
   y no como dos casillas independientes. */
.tep-source {
  display: inline-flex;
  gap: 3px;
  padding: 5px;
  /* Pastilla completa, como el resto de segmented controls del panel. */
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  width: fit-content;
  /* Aire propio: pegaba con el título de la fila y con el select de abajo. */
  margin: 4px 0 6px;
}
.tep-source-btn {
  border: none;
  background: transparent;
  color: var(--muted);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  padding: 8px 18px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.tep-source-btn:hover:not(:disabled):not(.is-active) { color: var(--text-strong); }
.tep-source-btn.is-active { background: var(--brand-dark); color: #fff; }
.tep-source-btn:disabled { opacity: 0.55; cursor: not-allowed; }

.tep {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 18px;
  border-radius: 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
}

/* Aviso de «estás editando el modo X»: el editor solo toca un tema a la vez. */
.tep-mode {
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--brand-soft-fg);
  background: var(--brand-soft-bg);
  border: 1px solid rgba(var(--brand-rgb), 0.28);
  border-radius: 10px;
  padding: 10px 12px;
}

.tep-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
}

.tep-field { display: flex; flex-direction: column; gap: 6px; }
.tep-field-head { display: flex; flex-direction: column; gap: 1px; }
.tep-field-label { font-size: 0.82rem; font-weight: 600; color: var(--text-strong); }
.tep-field-hint { font-size: 0.72rem; color: var(--muted); }
.tep-field-control { display: flex; align-items: center; gap: 8px; }

/* El swatch nativo: se le quita el chrome del navegador para que sea un cuadro de
   color limpio. Los pseudo-elementos son específicos de cada motor. */
.tep-swatch {
  width: 40px;
  height: 34px;
  flex-shrink: 0;
  padding: 0;
  border: 1px solid var(--control-border);
  border-radius: 8px;
  background: none;
  cursor: pointer;
}
.tep-swatch::-webkit-color-swatch-wrapper { padding: 3px; }
.tep-swatch::-webkit-color-swatch { border: none; border-radius: 5px; }
.tep-swatch::-moz-color-swatch { border: none; border-radius: 5px; }
.tep-swatch:disabled { opacity: 0.5; cursor: not-allowed; }

.tep-hex {
  width: 100%;
  min-width: 0;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--text);
  font-family: 'Fira Code', 'Cascadia Code', ui-monospace, monospace;
  font-size: 0.8rem;
  text-transform: lowercase;
}
.tep-hex:focus { outline: none; border-color: var(--brand); }

.tep-auto {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.8rem;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
}

.tep-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding-top: 4px;
  border-top: 1px solid var(--border-soft);
}
.tep-count { font-size: 0.75rem; color: var(--muted-2); }
.tep-reset { font-size: 0.8rem; padding: 7px 14px; }
.idv-name-input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 14px;
  border-radius: 10px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--text);
  font-size: 0.875rem;
  font-family: inherit;
}
.idv-name-input:focus { outline: none; border-color: var(--brand); }
.idv-name-input:disabled { opacity: 0.55; cursor: not-allowed; }

/* ---- Banner de Inicio (Identidad Visual) ----
   La vista previa reutiliza `.home-banner` y `.hb-*` de la portada: si un día
   cambia una forma, cambia en los dos sitios a la vez. Aquí solo se acota la
   altura y se añade el marcador de encuadre. */
.hb-preview { margin-bottom: 4px; }

/* Solo con imagen: sin ella no hay nada que colocar. `touch-action: none` es lo
   que impide que el navegador se quede el gesto para hacer scroll en móvil. */
.hb-preview.is-draggable {
  cursor: grab;
  touch-action: none;
  user-select: none;
}

.hb-preview.is-draggable:active { cursor: grabbing; }

/* Saludo y buscador de muestra dentro de la previa. Usan las clases reales
   (`hb-slot`, `home-welcome-title`, `home-search`) para que la colocación sea
   la de verdad y no una aproximación dibujada aparte; lo único propio es que
   nada de esto recibe el ratón: encima está el gesto de encuadre de la imagen.
   El buscador es un `<span>`, no un `<input>`: un campo de texto dentro de un
   panel de ajustes invita a escribir en él. */
.hb-ghost, .hb-ghost * { pointer-events: none; }

.hb-ghost .home-search-input {
  display: block;
  color: inherit;
  opacity: 0.75;
}

.hb-focus {
  position: absolute;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.45), inset 0 0 0 2px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}

.idv-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  max-width: 420px; /* mismo motivo que `.idv-row-control` */
  margin-top: 12px;
}

/* Los ajustes de sí/no del banner NO tienen variante en fila: el interruptor va
   debajo de su etiqueta como en el resto del panel. Ponerlo al lado dejaba una
   sola config leyéndose distinto de todas las demás. */

/* Botón de subida: se muestra solo cuando el fondo elegido es «Imagen propia». */
.idv-upload {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px dashed var(--control-border);
  background: var(--control-bg);
  color: var(--text);
  font-size: 0.8125rem;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.idv-upload:hover {
  background: var(--control-bg-hover);
  border-color: var(--brand);
}

.idv-field-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  align-self: flex-start;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.idv-hint {
  font-size: 0.75rem;
  color: var(--muted-2);
  margin: 0;
}

/* Campo de texto del saludo. Comparte medidas con `.idv-select` para que la
   columna de controles del panel quede pareja. */
.idv-input {
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 0.875rem;
  outline: none;
}

.idv-input:focus {
  border-color: var(--brand);
  background: var(--control-bg-hover);
}

.idv-input:disabled { opacity: 0.6; cursor: not-allowed; }

/* Comodines insertables. Se pintan como fichas y no como texto de ayuda porque
   además de documentar, SE PULSAN: escribirlos a mano es donde se cuelan las
   erratas que dejan un `{nombe}` literal en la portada. */
.idv-vars {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.idv-var {
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--muted);
  border-radius: 999px;
  padding: 2px 10px;
  font-family: inherit;
  font-size: 0.72rem;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.idv-var:hover:not(:disabled) {
  background: var(--brand-soft-bg);
  color: var(--brand-soft-fg);
  border-color: var(--brand);
}

.idv-var:disabled { opacity: 0.5; cursor: not-allowed; }

/* Muestra del dibujo en el desplegable de fondo (campo cerrado y cada fila):
   el mismo `.hb-art--*` de la portada, en miniatura. «Aurora» o «Trama» no
   dicen nada hasta que se ven. Rectangular y no cuadrada porque en 22×22 una
   trama cabe en menos de un mosaico y todos los fondos se parecen. */
.idv-opt-swatch {
  width: 44px;
  height: 20px;
  border-radius: 5px;
  flex: none;
}

/* «Imagen propia» no tiene dibujo: se marca como hueco a rellenar. */
.hb-art--custom {
  background-color: var(--control-bg);
  background-image:
    repeating-linear-gradient(45deg,
      rgba(var(--brand-rgb), 0.28) 0 5px, transparent 5px 10px);
  box-shadow: inset 0 0 0 1px var(--control-border);
}

.idv-range {
  width: 100%;
  max-width: 320px;
  accent-color: var(--brand);
}

/* Selector de celda del banner: un mapa 3×3 en miniatura. Elegir «arriba a la
   derecha» en un desplegable obliga a traducir mentalmente; en el mapa se
   señala. La celda ocupada por el otro elemento va deshabilitada. */
/* Los dos selectores van UNO DEBAJO DEL OTRO, no en fila. En fila, el segundo
   se iba al extremo derecho del panel (la fila es ancha y las rejillas miden
   ~100 px), y dos controles idénticos separados por medio panel no se leen como
   pareja. Apilados comparten borde izquierdo con el resto de los campos. */
.idv-slotrow {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
}

.idv-slots {
  display: grid;
  grid-template-columns: repeat(3, 30px);
  grid-template-rows: repeat(3, 22px);
  gap: 4px;
}

.idv-slot {
  border: 1px solid var(--control-border);
  border-radius: 5px;
  background: var(--control-bg);
  cursor: pointer;
  padding: 0;
  transition: background 0.15s, border-color 0.15s;
}

.idv-slot:hover:not(:disabled) {
  background: var(--control-bg-hover);
  border-color: var(--brand);
}

.idv-slot.is-active {
  background: var(--brand);
  border-color: var(--brand);
}

/* Ocupada por el otro elemento: se ve que existe pero no se puede elegir. */
.idv-slot:disabled {
  cursor: not-allowed;
  opacity: 0.4;
  border-style: dashed;
}

/* Dropdown con búsqueda (componente IdvSelect) */
.idv-select { position: relative; width: 100%; }
.idv-select-field {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 14px;
  border-radius: 10px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--text);
  font-size: 0.875rem;
  font-family: inherit;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.idv-select-field:hover:not(:disabled) { background: var(--control-bg-hover); }
.idv-select-field:disabled { opacity: 0.55; cursor: not-allowed; }
.idv-select.is-open .idv-select-field { border-color: var(--brand); }
.idv-select-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.idv-select-caret { color: var(--muted); font-size: 0.7rem; flex-shrink: 0; }

.idv-select-backdrop { position: fixed; inset: 0; z-index: 60; }
.idv-select-panel {
  position: absolute;
  z-index: 61;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.idv-select-search {
  width: 100%;
  box-sizing: border-box;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--text);
  font-size: 0.85rem;
  font-family: inherit;
}
.idv-select-search:focus { outline: 2px solid var(--brand); outline-offset: -1px; }
.idv-select-list {
  max-height: 240px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.idv-select-opt {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  padding: 8px 12px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--text);
  font-size: 0.85rem;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.12s;
}
.idv-select-opt:hover { background: var(--control-bg-hover); }
.idv-select-opt.is-active {
  background: var(--brand-soft-bg);
  color: var(--brand-soft-fg);
  font-weight: 600;
}
.idv-opt-label { flex: 1; }
.idv-opt-sub { font-size: 0.75rem; color: var(--muted); }
.idv-select-opt.is-active .idv-opt-sub { color: var(--brand-soft-fg); }
.idv-select-empty { padding: 10px 12px; font-size: 0.8rem; color: var(--muted); text-align: center; }

/* Barra Guardar — fija en la esquina inferior derecha (siempre visible al hacer scroll) */
.idv-savebar {
  position: fixed;
  right: 32px;
  bottom: 24px;
  z-index: 90;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
}
.idv-saved { font-size: 0.82rem; color: var(--badge-success-fg); font-weight: 600; }
.idv-save-btn { white-space: nowrap; }

/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS DE TRABAJO — sistema propio `flx-*` (tokens, claro + oscuro).
   Flujo de trabajo del constructor: (izq) lista de flujos configurados →
   (der) ① disparadores → ② embudo de fases (drag & drop) → ③ revisión.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Layout de 2 columnas: sidebar de flujos + canvas del flujo seleccionado. */
.flx-layout {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: 18px;
  align-items: start;
}
@media (max-width: 900px) { .flx-layout { grid-template-columns: 1fr; } }

/* ── Sidebar: lista de flujos configurados ─────────────────────────────────── */
.flx-sidebar {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 14px;
  position: sticky;
  top: 14px;
  box-shadow: var(--shadow-sm);
}
.flx-sidebar-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 12px;
}
.flx-sidebar-title {
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted-2);
}
.flx-flow-list { display: flex; flex-direction: column; gap: 8px; }
.flx-flow-card {
  display: flex; flex-direction: column; gap: 6px;
  width: 100%; text-align: left; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--border);
  border-left: 3px solid transparent;
  border-radius: 12px; padding: 11px 13px;
  transition: border-color .15s ease, background .15s ease, transform .05s ease;
}
.flx-flow-card:hover { background: var(--control-bg-hover); border-color: var(--border-soft); }
.flx-flow-card:active { transform: translateY(1px); }
.flx-flow-card.active {
  border-color: var(--brand); border-left-color: var(--brand);
  background: var(--brand-soft-bg, var(--surface-2));
}
.flx-flow-card-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.flx-flow-card-name { font-size: 14px; font-weight: 600; color: var(--text-strong); line-height: 1.25; }
.flx-flow-card-meta { font-size: 11.5px; color: var(--muted); }
.flx-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: var(--muted-2); }
.flx-dot.on { background: var(--badge-success-fg, #10b981); box-shadow: 0 0 0 3px var(--badge-success-bg); }

/* ── Canvas del flujo ──────────────────────────────────────────────────────── */
.flx-canvas { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

.flx-flow-header {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 14px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 16px; padding: 16px 18px; box-shadow: var(--shadow-sm);
}
.flx-flow-title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.flx-flow-title { font-size: 18px; font-weight: 700; color: var(--text-strong); margin: 0; }
.flx-flow-obj { font-size: 13px; color: var(--muted); margin: 6px 0 0; line-height: 1.45; max-width: 60ch; }
.flx-flow-header-actions { display: inline-flex; gap: 6px; flex: 0 0 auto; }

.flx-status-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 3px 9px; border-radius: 999px;
}
.flx-status-badge.on { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.flx-status-badge.off { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }

/* Botones del canvas */
.flx-btn {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--control-bg); color: var(--text);
  border: 1px solid var(--control-border); border-radius: 9px;
  padding: 7px 12px; font-size: 13px; cursor: pointer;
  transition: background .12s ease, border-color .12s ease;
}
.flx-btn:hover { background: var(--control-bg-hover); }
.flx-btn-primary { background: var(--brand); border-color: var(--brand); color: #fff; }
.flx-btn-primary:hover { background: var(--brand-dark, var(--brand)); }
.flx-btn-danger { color: var(--danger, #ef4444); border-color: var(--control-border); }
.flx-btn-danger:hover { background: var(--badge-danger-bg); border-color: var(--badge-danger-fg); }
.flx-btn-add {
  background: transparent; border: 1px dashed var(--brand); color: var(--brand);
  border-radius: 9px; padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.flx-btn-add:hover { background: var(--brand-soft-bg, var(--surface-2)); }
.flx-icon-btn {
  background: var(--control-bg); border: 1px solid var(--control-border); border-radius: 8px;
  width: 30px; height: 30px; color: var(--text); cursor: pointer; font-size: 14px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.flx-icon-btn:hover:not(:disabled) { background: var(--control-bg-hover); }
.flx-icon-btn:disabled { opacity: .35; cursor: not-allowed; }

/* ── Barra de etapas (①→②→③) ──────────────────────────────────────────────── */
.flx-stagebar { display: flex; align-items: center; gap: 6px; padding: 2px 2px 0; flex-wrap: wrap; }
.flx-stagebar-item {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12.5px; color: var(--muted); font-weight: 600;
  padding: 5px 12px; border-radius: 999px; background: var(--surface-2);
  border: 1px solid var(--border);
}
.flx-stagebar-item .flx-stage-badge {
  width: 20px; height: 20px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-size: 11px; font-weight: 700;
  background: var(--control-bg); color: var(--muted-2);
}
.flx-stagebar-item.done { color: var(--brand); border-color: var(--brand); }
.flx-stagebar-item.done .flx-stage-badge { background: var(--brand); color: #fff; }
.flx-stagebar-arrow { color: var(--muted-2); font-size: 14px; }

/* ── Secciones del canvas ──────────────────────────────────────────────────── */
.flx-section {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 16px; padding: 16px 18px; box-shadow: var(--shadow-sm);
}
.flx-section-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-bottom: 12px;
}
.flx-section-title {
  display: flex; align-items: center; gap: 9px;
  font-size: 14px; font-weight: 700; color: var(--text-strong);
}
.flx-section-title .flx-num {
  width: 22px; height: 22px; border-radius: 7px; display: inline-flex;
  align-items: center; justify-content: center; font-size: 12px; font-weight: 700;
  background: var(--brand-soft-bg, var(--surface-2)); color: var(--brand-soft-fg, var(--brand));
}
.flx-section-hint { font-size: 12px; color: var(--muted-2); margin: -6px 0 12px; }

/* ── Chips (disparadores / condiciones) ────────────────────────────────────── */
.flx-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.flx-chip {
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--badge-info-bg); color: var(--badge-info-fg);
  border-radius: 999px; padding: 4px 11px; font-size: 12.5px; font-weight: 500;
}
.flx-chip.purple { background: var(--badge-purple-bg); color: var(--badge-purple-fg); }
.flx-chip-x {
  background: transparent; border: none; color: inherit; cursor: pointer;
  font-size: 15px; line-height: 1; opacity: .6; padding: 0;
}
.flx-chip-x:hover { opacity: 1; }
.flx-empty { font-size: 12.5px; color: var(--muted-2); font-style: italic; }

/* ── Embudo de fases (drag & drop) ─────────────────────────────────────────── */
.flx-funnel { display: flex; flex-direction: column; align-items: center; gap: 0; }
.flx-stage {
  width: 100%;
  display: flex; align-items: stretch; gap: 0;
  transition: transform .08s ease, opacity .12s ease;
}
.flx-stage.dragging { opacity: .45; }
.flx-stage.over .flx-stage-body { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-soft-bg, var(--surface-2)) inset; }

/* Riel numerado + conector vertical (metáfora de embudo). */
.flx-stage-rail { display: flex; flex-direction: column; align-items: center; flex: 0 0 44px; }
.flx-stage-num {
  width: 30px; height: 30px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; flex: 0 0 auto;
  background: var(--brand); color: #fff; box-shadow: 0 0 0 4px var(--brand-soft-bg, var(--surface-2));
}
.flx-stage-line { width: 2px; flex: 1 1 auto; min-height: 18px; background: var(--border); margin: 4px 0; }
.flx-stage.terminal .flx-stage-num { background: var(--muted-2); }

.flx-stage-body {
  flex: 1 1 auto; min-width: 0;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 14px; padding: 13px 15px; margin-bottom: 14px;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.flx-stage-head { display: flex; align-items: center; gap: 9px; }
.flx-drag-handle {
  cursor: grab; color: var(--muted-2); font-size: 15px; user-select: none;
  padding: 2px 4px; border-radius: 6px; flex: 0 0 auto;
}
.flx-drag-handle:hover { color: var(--text); background: var(--control-bg); }
.flx-drag-handle:active { cursor: grabbing; }
.flx-stage-name { font-size: 14.5px; font-weight: 600; color: var(--text-strong); flex: 1 1 auto; min-width: 0; }
.flx-stage-tags { display: inline-flex; gap: 5px; flex-wrap: wrap; }
.flx-tag {
  font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .02em;
  padding: 2px 7px; border-radius: 6px; background: var(--badge-neutral-bg); color: var(--badge-neutral-fg);
}
.flx-tag.mode { background: var(--badge-info-bg); color: var(--badge-info-fg); }
.flx-tag.warn { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.flx-stage-actions { display: inline-flex; gap: 4px; flex: 0 0 auto; }
.flx-stage-obj { font-size: 12.5px; color: var(--muted); margin: 8px 0 0; line-height: 1.4; }
.flx-stage-rules {
  font-size: 12px; color: var(--muted-2); margin: 8px 0 0; padding-left: 14px;
  display: flex; flex-direction: column; gap: 3px;
}
.flx-stage-rules li { list-style: disc; }
.flx-stage-cond { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border); }
.flx-stage-cond-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--muted-2); margin-bottom: 6px; }

.flx-add-stage {
  width: 100%; background: transparent; border: 1.5px dashed var(--brand); color: var(--brand);
  border-radius: 14px; padding: 14px; font-size: 13px; font-weight: 600; cursor: pointer;
  transition: background .12s ease;
}
.flx-add-stage:hover { background: var(--brand-soft-bg, var(--surface-2)); }

/* ── Revisión de cambios ───────────────────────────────────────────────────── */
.flx-review-grid { display: flex; flex-direction: column; gap: 9px; }
.flx-check {
  display: flex; align-items: center; gap: 10px;
  font-size: 13px; color: var(--text); padding: 9px 12px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px;
}
.flx-check-ico { width: 20px; height: 20px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; font-size: 13px; border-radius: 50%; }
.flx-check.ok .flx-check-ico { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.flx-check.no .flx-check-ico { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.flx-check.warn .flx-check-ico { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.flx-review-actions { display: flex; align-items: center; gap: 10px; margin-top: 14px; flex-wrap: wrap; }
.flx-review-note { font-size: 12px; color: var(--muted-2); }

/* ── Estimador de tokens de contexto (revisión del constructor) ────────────── */
.flx-token-card {
  margin-top: 14px; padding: 14px 16px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px;
}
.flx-token-head { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600;
  color: var(--text-strong); margin-bottom: 12px; }
.flx-token-ico { font-size: 15px; }
.flx-token-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.flx-token-item { text-align: center; padding: 10px 6px; border-radius: 10px;
  background: var(--surface); border: 1px solid var(--border-soft); }
.flx-token-item--total { background: var(--brand-soft-bg); border-color: var(--brand); }
.flx-token-val { font-size: 20px; font-weight: 700; color: var(--text-strong); line-height: 1.1; }
.flx-token-item--total .flx-token-val { color: var(--brand-soft-fg); }
.flx-token-lbl { font-size: 11px; color: var(--muted); margin-top: 4px; }
.flx-token-note { font-size: 11.5px; color: var(--muted-2); line-height: 1.45; margin: 10px 0 0; }

/* ── Toolbar de export de reportes (auditoría) ─────────────────────────────── */
.flx-rep-sep { display: inline-block; width: 1px; height: 22px; background: var(--border); margin: 0 4px; }
.flx-rep-dash { color: var(--muted-2); font-size: 13px; }
.flx-rep-note { font-size: 12px; color: var(--muted-2); line-height: 1.45; margin: 8px 0 0; }

/* ── Objetivos: editor (wizard) + display (tarjeta de fase) ────────────────── */
.flx-wizard-hint { font-size: 12px; color: var(--muted); line-height: 1.45; margin: 0 0 12px; }
.flx-obj-editor { display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px; }
.flx-obj-row { display: flex; align-items: flex-start; gap: 8px; }
.flx-obj-fields { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px; padding: 10px; }
.flx-obj-desc-input { min-height: 54px; resize: vertical; font-family: inherit; }

.flx-stage-objectives { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border); }
.flx-obj-list { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.flx-obj-item { display: flex; align-items: baseline; gap: 7px; font-size: 12.5px; flex-wrap: wrap; }
.flx-obj-check { color: var(--brand); font-size: 11px; flex: 0 0 auto; }
.flx-obj-name { font-weight: 600; color: var(--text-strong); }
.flx-obj-desc { color: var(--muted); }
.flx-obj-desc::before { content: "— "; color: var(--muted-2); }

/* Empty state grande cuando no hay flujo seleccionado */
.flx-blank {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; text-align: center; padding: 60px 24px;
  background: var(--surface); border: 1px dashed var(--border); border-radius: 16px;
  color: var(--muted);
}
.flx-blank-ico { font-size: 40px; opacity: .5; }
/* ── Módulo Mapa Interactivo (owner-only) ─────────────────────────────────── */
.mapa-page { display: flex; flex-direction: column; gap: 14px; padding: 18px; height: 100%; box-sizing: border-box; }
/* El PageHeader rompe el padding del root para quedar a ancho completo (igual que
   en los demás módulos). Sin esto la barra superior queda embutida y se ve mal. */
.mapa-page > .ph { margin: -18px -18px 0; }
/* Tabs DENTRO del panel izquierdo. Fuera del panel dejaban una franja vacía a lo ancho
   de la página, justo encima del mapa. Los avisos del módulo salen por el toast global
   (`.toast-host`), no por un banner aquí: el banner empujaba el lienzo y el mapa
   saltaba en cada guardado.

   El colapsar es una PESTAÑA en la costura entre el panel y el mapa. Dentro de la fila
   de tabs les quitaba el ancho justo que necesitan para caber las cuatro (y salía una
   barra de scroll horizontal); en una fila propia encima, se veía suelto. */
/* La pestaña cuelga de `.mapa-body` y se coloca con un `left` EXPLÍCITO, calculado desde
   el ancho del panel (330 px, o 62 px colapsado — los mismos números que usa el aside).
   Dos intentos anteriores la dejaron invisible: dentro del aside la recortaba su
   `overflow-y: auto` (que en CSS vuelve `auto` también el eje X), y en un envoltorio flex
   con `left: 100%` tampoco aparecía. Anclarla al contenedor que YA define las dos
   columnas quita de en medio los dos misterios. */
/* `left` = ancho EXACTO del panel: la pestaña arranca donde el panel termina y sale
   entera hacia el hueco. Antes empezaba 12 px antes y quedaba medio montada sobre la
   esquina del panel, que se leía como un botón encima y no como una pestaña. */
.mapa-collapse-tab { position: absolute; top: 18px; left: 330px; z-index: 20;
    width: 24px; height: 44px; display: flex; align-items: center; justify-content: center;
    padding: 0; cursor: pointer; color: var(--text);
    background: var(--surface-overlay); border: 1px solid var(--border); border-left: 0;
    border-radius: 0 10px 10px 0; box-shadow: 3px 0 8px rgba(0,0,0,0.22);
    transition: left .18s ease, color .15s ease, background .15s ease; }
.mapa-body--collapsed .mapa-collapse-tab { left: 62px; }
.mapa-collapse-tab:hover { color: var(--brand); background: var(--control-bg-hover); }
/* Las 4 pestañas se reparten el ancho del panel y no desbordan. La barra de scroll
   queda por si el idioma alarga las etiquetas, pero SIN ocupar sitio ni verse: reservada
   siempre, partía el segmented en dos y se leía como un control roto. */
.mapa-tabs { width: 100%; margin-bottom: 0; padding: 4px;
    scrollbar-width: none; -ms-overflow-style: none; }
.mapa-tabs::-webkit-scrollbar { width: 0; height: 0; display: none; }
.mapa-tabs .mir-tab { flex: 1 1 0; min-width: 0; padding-left: 6px; padding-right: 6px;
    text-align: center; white-space: nowrap; }
/* El buscador ocupa su fila entero. Cuando la pestaña no tiene nada que buscar la fila
   no se pinta y el contenido sube: un hueco donde debería haber un buscador se lee como
   que algo no cargó. */
.mapa-search-row .contacts-search-input { flex: 1 1 auto; min-width: 0; }

/* Altura FIJA basada en viewport → el mapa no crece con la lista; ambas columnas
   tienen scroll interno propio. Se descuenta solo la cabecera de la página: la fila
   de tabs y la de avisos ya no están fuera del panel (antes eran 60 px más). No se
   usa `flex:1` porque `.mapa-page` hereda `height:100%` de un padre sin altura
   definida, y entonces el lienzo colapsaría a 0. */
/* `position: relative` para que la pestaña de colapsar se ancle AQUÍ: este es el
   contenedor que define las dos columnas, así que sus coordenadas son las únicas fiables
   para colocarla en la costura. */
/* El `gap` es la COSTURA donde vive la pestaña de colapsar: con los 14 px de antes no
   cabía (mide 24) y quedaba pisando el borde del mapa, medio escondida. */
.mapa-body { display: flex; gap: 32px; height: calc(100dvh - 150px); min-height: 460px;
    position: relative; }
.mapa-controls { width: 330px; flex-shrink: 0; height: 100%; overflow-y: auto; display: flex; flex-direction: column; gap: 14px;
    background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px; }
.mapa-control-block { display: flex; flex-direction: column; }
.mapa-label { font-size: 0.78rem; color: var(--muted); margin-bottom: 4px; font-weight: 600; }
.mapa-select, .mapa-input { width: 100%; box-sizing: border-box; padding: 8px 10px; border-radius: 8px;
    border: 1px solid var(--control-border); background: var(--control-bg); color: var(--text); font-size: 0.88rem; }
.mapa-input { margin-bottom: 8px; }

.mapa-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 14px; }
.mapa-stat { background: var(--surface-2); border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; }
.mapa-stat span { font-size: 0.72rem; color: var(--muted); }
.mapa-stat b { font-size: 1.15rem; color: var(--text-strong); }

.mapa-offices-actions { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.mapa-btn { padding: 8px 12px; border-radius: 8px; border: 1px solid var(--control-border);
    background: var(--control-bg); color: var(--text); cursor: pointer; font-size: 0.85rem; font-weight: 600; }
.mapa-btn:hover { background: var(--control-bg-hover); }
.mapa-btn--primary { background: var(--brand); border-color: var(--brand); color: #fff; }
.mapa-btn--danger { background: var(--badge-danger-bg); color: var(--badge-danger-fg); border-color: transparent; }
.mapa-btn:disabled { opacity: 0.55; cursor: not-allowed; }

.mapa-editor { background: var(--surface-2); border-radius: 10px; padding: 12px; margin-bottom: 12px; }
.mapa-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: -2px 0 10px; padding-bottom: 8px; border-bottom: 1px solid var(--border-soft); }
.mapa-editor-title { font-weight: 600; color: var(--text-strong); font-size: 0.95rem; }
.mapa-editor-close { background: var(--control-bg); border: 1px solid var(--control-border); color: var(--muted); border-radius: 8px; width: 28px; height: 28px; line-height: 1; cursor: pointer; font-size: 0.9rem; display: flex; align-items: center; justify-content: center; }
.mapa-editor-close:hover { background: var(--control-bg-hover); color: var(--text-strong); }
.mapa-editor-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 6px; }
.mapa-coord-row { display: flex; gap: 8px; }
.mapa-coord-row .mapa-input { flex: 1; min-width: 0; }

.mapa-office-list { display: flex; flex-direction: column; gap: 12px; }
.mapa-office-group { display: flex; flex-direction: column; }
/* SIN `text-transform: uppercase`: el nombre del estado se muestra tal como lo escribió
   quien creó la sede. Forzar mayúsculas convertía "Distrito Capital" en un grito y, peor,
   hacía indistinguibles dos grafías distintas del mismo sitio en la lista. */
.mapa-office-estado { font-size: 0.8rem; font-weight: 700; color: var(--text-strong);
    letter-spacing: 0.2px; margin-bottom: 4px; display: flex; align-items: center; gap: 6px; width: 100%;
    background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: 8px; padding: 7px 10px;
    cursor: pointer; text-align: left; }
.mapa-office-estado:hover { background: var(--control-bg-hover); }
.mapa-acc-chevron { display: inline-block; transition: transform 0.15s; color: var(--muted); font-size: 0.75rem; }
.mapa-acc-chevron.is-open { transform: rotate(90deg); }
.mapa-office-estado .mapa-office-count { margin-left: auto; }
.mapa-office-group-body { display: flex; flex-direction: column; padding: 4px 0 2px; }
.mapa-office-count { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); border-radius: 999px;
    padding: 0 7px; font-size: 0.7rem; font-weight: 700; }
/* La fila es ahora un contenedor en columna: arriba el grueso (nombre + flags) como
   botón que selecciona la sede, más el "⋮"; debajo, en flujo, su desplegable de
   opciones. En flujo —no flotante— nunca lo recorta el scroll del panel. */
.mapa-office-row { display: flex; flex-direction: column; width: 100%;
    border-radius: 8px; border: 1px solid transparent; }
.mapa-office-row:hover, .mapa-office-row.is-open { background: var(--control-bg-hover); border-color: var(--border-soft); }
.mapa-office-row-top { display: flex; align-items: center; gap: 4px; }
.mapa-office-main { flex: 1; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 7px 4px 7px 10px; background: transparent; border: none; color: var(--text);
    cursor: pointer; text-align: left; font-size: 0.86rem; }
.mapa-office-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mapa-office-flags { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }

/* Botón "⋮" y su desplegable de opciones (accordion en flujo). */
.mapa-office-menu-btn { flex-shrink: 0; width: 26px; height: 26px; margin-right: 4px; border-radius: 6px;
    border: 1px solid transparent; background: transparent; color: var(--muted); cursor: pointer;
    font-size: 1rem; line-height: 1; display: flex; align-items: center; justify-content: center; }
.mapa-office-menu-btn:hover, .mapa-office-row.is-open .mapa-office-menu-btn {
    background: var(--control-bg); border-color: var(--control-border); color: var(--text); }
.mapa-office-menu { display: flex; flex-direction: column; gap: 2px; margin: 0 6px 6px; padding: 4px;
    border-radius: 8px; background: var(--surface-2); border: 1px solid var(--border-soft); }
.mapa-office-menu-item { display: block; width: 100%; text-align: left; padding: 8px 10px; border-radius: 6px;
    background: transparent; border: none; color: var(--text); cursor: pointer; font-size: 0.82rem;
    text-decoration: none; }
.mapa-office-menu-item:hover { background: var(--control-bg-hover); }
.mapa-office-menu-item[disabled] { opacity: 0.6; cursor: default; }
.mapa-office-menu-empty { padding: 8px 10px; font-size: 0.78rem; color: var(--muted); }

/* Modal del QR de una sede: QR centrado sobre fondo blanco fijo (ambos temas). */
.mapa-qr-modal { max-width: 340px; }
.mapa-qr-modal-body { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
.mapa-qr-modal-body .mapa-qr-img { width: 240px; max-width: 100%; }
.mapa-badge { border-radius: 999px; padding: 1px 8px; font-size: 0.68rem; font-weight: 700; }
.mapa-badge--warn { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.mapa-badge--ok { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.mapa-pin--miss { color: var(--badge-warn-fg); }

/* ── Zonas de cobertura y probador de ubicación ─────────────────────────────── */
/* El punto de color es la única forma de reconocer una zona en la lista sin abrirla:
   el mismo color con el que se pinta su polígono en el mapa. */
.mapa-zone-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0;
    border: 1px solid var(--border); }
.mapa-zone-vertices { color: var(--muted); font-size: 0.7rem; }
.mapa-check { display: flex; align-items: center; gap: 7px; margin-top: 10px;
    font-size: 0.82rem; color: var(--text); cursor: pointer; }
.mapa-draw-status { display: flex; align-items: center; justify-content: space-between; gap: 8px;
    margin-top: 10px; padding: 7px 10px; border-radius: 8px; font-size: 0.78rem;
    background: var(--brand-soft-bg); color: var(--brand-soft-fg); }
.mapa-probe-result { margin-top: 10px; padding: 10px; border-radius: 10px;
    background: var(--surface-2); border: 1px solid var(--border-soft);
    display: flex; flex-direction: column; gap: 6px; }
.mapa-probe-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
    font-size: 0.8rem; }
.mapa-probe-row span { color: var(--muted); }
.mapa-probe-row b { text-align: right; }
.mapa-hint { margin: 2px 0 0; font-size: 0.74rem; color: var(--muted); line-height: 1.45; }
/* Asa invisible de arrastre sobre cada vértice: el círculo visible es un
   circleMarker (que no arrastra), así que el marker de encima solo aporta el área
   de agarre. Sin cursor propio no se adivina que se puede mover. */
.mapa-vertex-handle { cursor: grab; }
.mapa-vertex-handle:active { cursor: grabbing; }

/* ── Panel izquierdo: cabecera + toolbar + colapso ──────────────────────────── */
/* Mismo cromo que el panel de Flujos de Trabajo (`.flx-sidebar-head`, `.flx-btn-new`,
   `.lc-topbar`): son las mismas piezas, así que se reutilizan sus clases y aquí solo
   va lo propio de este módulo. */
.mapa-head-actions { display: flex; align-items: center; gap: 6px; }
/* El aside ya separa sus hijos con `gap: 14px`; el margen propio de la cabecera se
   sumaba y abría un hueco doble entre el título y el buscador. */
.mapa-controls .flx-sidebar-head { margin-bottom: 0; }
/* El popover de filtros se ancla al panel, no a la página. */
.mapa-controls { position: relative; }
.mapa-controls .lc-topbar { margin: 0; padding: 0; border-bottom: 0; }
.mapa-controls .lc-filters-popover { top: 92px; }
/* Zona scrolleable: la cabecera y la toolbar se quedan fijas arriba. */
.mapa-panel-body { flex: 1 1 auto; min-height: 0; overflow-y: auto;
    display: flex; flex-direction: column; gap: 14px;
    scrollbar-width: thin; scrollbar-color: transparent transparent; }
.mapa-panel-body:hover { scrollbar-color: var(--control-border) transparent; }
.mapa-panel-body::-webkit-scrollbar { width: 6px; }
.mapa-panel-body::-webkit-scrollbar-thumb { background: transparent; border-radius: 6px; }
.mapa-panel-body:hover::-webkit-scrollbar-thumb { background: var(--control-border); }
/* Colapso: el panel se reduce a un riel y el mapa se queda con el ancho. La
   transición va sobre el ancho del aside (el body es flex, no grid como Flujos). */
.mapa-controls { transition: width .18s ease, padding .18s ease; }
.mapa-controls--collapsed { width: 62px; padding: 10px 8px; overflow: hidden; }

/* ── Controles sobre el propio mapa ─────────────────────────────────────────── */
/* z-index 500: por encima de los panes de Leaflet (≤400) y por debajo de sus
   popups (700). */
.mapa-style-picker { position: absolute; top: 12px; right: 12px; z-index: 500; }
.mapa-style-picker .mapa-select { min-width: 150px; background: var(--surface-overlay);
    box-shadow: var(--shadow-md); }
/* Barra de dibujo: mientras se traza una zona el formulario está escondido, así que
   sus controles tienen que estar aquí, sobre el mapa. */
.mapa-draw-bar { position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%);
    z-index: 500; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    max-width: calc(100% - 24px); padding: 8px 12px; border-radius: 12px;
    background: var(--surface-overlay); border: 1px solid var(--border);
    box-shadow: var(--shadow-lg); }
.mapa-draw-count { font-weight: 700; font-size: 0.82rem; color: var(--text-strong); }
.mapa-draw-bar .mapa-hint { margin: 0; }
.mapa-btn--sm { padding: 5px 10px; font-size: 0.78rem; }
.mapa-place-hint .mapa-btn--sm { background: rgba(255,255,255,0.18); border-color: rgba(255,255,255,0.5);
    color: #fff; margin-left: 10px; }

/* ── Modales de sede y zona ─────────────────────────────────────────────────── */
.mapa-modal { width: min(560px, 94vw); }
.mapa-modal-body { display: flex; flex-direction: column; padding: 0 22px; }
.mapa-modal-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Empuja las acciones normales a la derecha y deja "Eliminar" solo a la izquierda:
   el botón destructivo no debe quedar pegado a "Guardar". */
.mapa-modal-spacer { flex: 1 1 auto; }

/* ── Combo de Estado/Región ─────────────────────────────────────────────────── */
/* No es un <select>: hay que poder escribir uno nuevo Y elegir uno ya usado. El
   desplegable lista los existentes con su propio buscador y paginado, que es lo que
   evita acabar con "Carabobo", "carabobo" y "Edo. Carabobo" como tres grupos. */
.mapa-combo { position: relative; }
/* Backdrop propio para cerrar al pulsar fuera. Vive DENTRO de `.modal-card`, que ya
   frena la propagación del clic, así que cerrarlo no cierra el modal.
   El input se sube POR ENCIMA del backdrop (`z-index` mayor): si no, el backdrop se come
   los clics del campo y se puede abrir el desplegable pero no seguir escribiendo. */
.mapa-combo-backdrop { position: fixed; inset: 0; z-index: 10; }
.mapa-combo.is-open .mapa-input { position: relative; z-index: 11; }
.mapa-combo-pop { position: absolute; top: 38px; left: 0; right: 0; z-index: 12;
    background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: var(--shadow-lg); padding: 8px; }
.mapa-combo-list { display: flex; flex-direction: column; gap: 2px; }
.mapa-combo-item { text-align: left; width: 100%; padding: 6px 8px; border-radius: 7px;
    border: 1px solid transparent; background: transparent; color: var(--text);
    font-size: 0.85rem; cursor: pointer; }
.mapa-combo-item:hover { background: var(--control-bg-hover); border-color: var(--border-soft); }
.mapa-combo-pager { display: flex; align-items: center; justify-content: center; gap: 10px;
    margin-top: 6px; font-size: 0.76rem; color: var(--muted); }

/* ── Selector de CÓMO se indica la ubicación ────────────────────────────────── */
/* Se pregunta la forma antes de enseñar campos: las tres maneras de decir lo mismo
   puestas a la vez es lo que hacía tedioso dar de alta una sede. Arranca sin nada
   seleccionado a propósito. */
/* Usa el segmented ESTÁNDAR (`.mir-tabs`/`.mir-tab`, bloque canónico de arriba); aquí
   solo se le dice que ocupe el ancho y reparta los tres botones. Antes era una copia con
   sus propios tamaños y colores, justo lo que ese bloque existe para evitar. */
.mapa-seg { width: 100%; margin-bottom: 8px; padding: 4px;
    scrollbar-width: none; -ms-overflow-style: none; }
.mapa-seg::-webkit-scrollbar { width: 0; height: 0; display: none; }
.mapa-seg .mir-tab { flex: 1 1 0; min-width: 0; padding-left: 6px; padding-right: 6px;
    text-align: center; white-space: nowrap; }
.mapa-coord-pick, .mapa-link-pick { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.mapa-coord-pick { flex-direction: row; align-items: center; }
.mapa-coord-echo { font-size: 0.8rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.mapa-link-pick .mapa-input { margin-bottom: 0; }
.mapa-link-pick .mapa-hint { margin: 0; }

/* ── Área de la zona: campo obligatorio, no acción suelta ───────────────────── */
/* Sin área el backend rechaza la zona. El aviso es un punto rojo junto a la etiqueta y
   el borde de los botones — nada más. Un párrafo explicando lo que ya dice el color es
   texto que nadie lee, y encima aleja el control de su etiqueta. */
.mapa-label--req { display: flex; align-items: center; gap: 6px; margin-top: 12px; }
.mapa-req-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--badge-danger-fg); }
.mapa-area-ok { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.mapa-area-pts { font-size: 0.8rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.mapa-area-actions { display: flex; gap: 8px; flex-wrap: wrap; }
/* Botón de un campo obligatorio todavía vacío: se distingue del resto sin ser un
   botón destructivo (que es lo que significa el rojo sólido en este panel). */
.mapa-btn--need { border-color: var(--badge-danger-fg); color: var(--badge-danger-fg); }
.mapa-btn--need:hover { background: var(--badge-danger-bg); }

/* ── Importar límite administrativo (OpenStreetMap) ─────────────────────────── */
/* Estado vacío: icono y una línea, centrado en el hueco de la lista. La lista vacía no
   es el sitio para explicar el módulo — quien llega ahí solo necesita ver que no hay
   nada, no leerse un párrafo. */
.mapa-empty { flex: 1 1 auto; min-height: 160px; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 10px; color: var(--muted-2); }
.mapa-empty svg { opacity: .55; }
.mapa-empty span { font-size: 0.82rem; }
/* La lista tiene que ocupar el alto disponible o el vacío no queda centrado en nada.
   (La regla base con `gap` está más arriba; aquí solo se le añade el crecimiento.) */
.mapa-office-list { flex: 1 1 auto; min-height: 0; }

.mapa-import { margin-top: 10px; padding: 10px; border-radius: 10px;
    background: var(--surface-2); border: 1px solid var(--border-soft);
    display: flex; flex-direction: column; gap: 8px; }
.mapa-import .mapa-hint { margin: 0; }
.mapa-import-row { display: flex; gap: 8px; align-items: center; }
.mapa-import-row .mapa-input { margin-bottom: 0; flex: 1 1 auto; min-width: 0; }
.mapa-import-hit { display: flex; flex-direction: column; gap: 2px; text-align: left; width: 100%;
    padding: 7px 9px; border-radius: 8px; border: 1px solid var(--border-soft);
    background: var(--control-bg); color: var(--text); cursor: pointer; }
.mapa-import-hit:hover { background: var(--control-bg-hover); }
.mapa-import-name { font-size: 0.82rem; line-height: 1.35; }
.mapa-import-kind { font-size: 0.7rem; color: var(--muted); text-transform: capitalize; }

/* ── Contenido de la burbuja de Leaflet ─────────────────────────────────────── */
/* La ficha de una sede/zona es LA BURBUJA ANCLADA, no una tarjeta en una esquina: va
   pegada al punto y se mueve con el mapa. Llegó a haber las dos a la vez —la del bridge
   y una propia— mostrando lo mismo dos veces. */
/* Los tokens de `.leaflet-popup-*` de más abajo NO se aplicaban: en index.html el
   `style.css` se enlaza ANTES que el CSS de Leaflet, así que a igual especificidad
   ganaba el de Leaflet y la burbuja salía blanca sobre el panel oscuro. Se recupera
   añadiendo el contenedor del mapa al selector (0,2,0 contra 0,1,0). */
.mapa-canvas .leaflet-popup-content-wrapper,
.mapa-canvas .leaflet-popup-tip { background: var(--surface-overlay); color: var(--text);
    box-shadow: var(--shadow-lg); }
.mapa-canvas .leaflet-popup-content { color: var(--text); margin: 12px 14px; }
.mapa-canvas .leaflet-popup-content strong { color: var(--text-strong); }
.mapa-canvas .leaflet-popup-content small { color: var(--muted); }

/* La ✕ es NUESTRA y la de Leaflet se oculta: Leaflet la coloca absoluta en la esquina
   del wrapper, fuera de nuestro marcado, así que poner el ⋮ "a su lado" era adivinar su
   tamaño y su margen — y las dos veces acabaron encimados. En la misma barra flex, la
   geometría la decide un solo contenedor. */
.mapa-canvas .leaflet-popup-close-button { display: none; }
.mapa-pop { position: relative; min-width: 200px; padding-right: 52px; }
.mapa-pop-tools { position: absolute; top: -3px; right: -4px;
    display: flex; align-items: center; gap: 2px; }
.mapa-pop-menu-btn, .mapa-pop-close-btn { width: 24px; height: 24px; flex: none;
    display: flex; align-items: center; justify-content: center;
    padding: 0; border: 0; border-radius: 6px; background: transparent;
    color: var(--muted); cursor: pointer; line-height: 1; }
.mapa-pop-menu-btn { font-size: 1.05rem; }
.mapa-pop-close-btn { font-size: 0.8rem; }
.mapa-pop-menu-btn:hover, .mapa-pop-close-btn:hover {
    background: var(--control-bg-hover); color: var(--text-strong); }
/* El menú nace oculto y lo abre el bridge con `.is-open`: dentro de un popup de Leaflet
   el DOM se recrea en cada apertura, así que su estado no puede vivir en Leptos. */
.mapa-pop-menu { display: none; position: absolute; top: 26px; right: 0; z-index: 3;
    min-width: 128px; flex-direction: column; padding: 4px; border-radius: 9px;
    background: var(--surface-overlay); border: 1px solid var(--border); box-shadow: var(--shadow-lg); }
.mapa-pop-menu.is-open { display: flex; }
.mapa-pop-menu button { text-align: left; padding: 7px 9px; border: 0; border-radius: 6px;
    background: transparent; color: var(--text); font-size: 0.83rem; cursor: pointer; }
.mapa-pop-menu button:hover { background: var(--control-bg-hover); }
.mapa-pop-menu button.is-danger { color: var(--badge-danger-fg); }
.mapa-pop a { color: var(--brand-dark); }

/* Centrar en mi ubicación: misma columna que el selector de estilo, justo debajo. */
.mapa-me-btn { position: absolute; top: 56px; right: 12px; z-index: 500;
    width: 34px; height: 34px; display: flex; align-items: center; justify-content: center;
    padding: 0; cursor: pointer; color: var(--text);
    background: var(--surface-overlay); border: 1px solid var(--border);
    border-radius: 9px; box-shadow: var(--shadow-md); }
.mapa-me-btn:hover { color: var(--brand); }

/* ── Probador de ubicación: control del mapa, no pestaña ────────────────────── */
/* Ocupaba una de las cuatro pestañas del panel izquierdo, y para consultar un punto había
   que abandonar la lista de sedes o zonas. Probar una ubicación es mirar el mapa, así que
   vive sobre el mapa: mismo cromo y misma columna que "mi ubicación", justo debajo. */
.mapa-probe-btn { position: absolute; top: 98px; right: 12px; z-index: 500;
    width: 34px; height: 34px; display: flex; align-items: center; justify-content: center;
    padding: 0; cursor: pointer; color: var(--text);
    background: var(--surface-overlay); border: 1px solid var(--border);
    border-radius: 9px; box-shadow: var(--shadow-md); }
.mapa-probe-btn:hover { color: var(--brand); }
.mapa-probe-btn.is-active { color: var(--brand); border-color: var(--brand); }
/* Anclado al botón que lo abre, hacia la izquierda: a la derecha no cabe (está pegado al
   borde) y hacia abajo taparía justo la parte del mapa que se está consultando.
   `max-height` + scroll propio porque el resultado crece con la sede más cercana y su
   enlace, y sin tope se sale del lienzo en pantallas bajas. */
.mapa-probe-pop { position: absolute; top: 98px; right: 54px; z-index: 501;
    width: min(320px, calc(100% - 70px)); max-height: calc(100% - 120px); overflow-y: auto;
    display: flex; flex-direction: column;
    padding: 12px; border-radius: 12px;
    background: var(--surface-overlay); border: 1px solid var(--border);
    box-shadow: var(--shadow-lg); }
.mapa-probe-head { display: flex; align-items: center; justify-content: space-between;
    gap: 10px; margin-bottom: 10px; }
.mapa-probe-head span { font-size: 0.82rem; font-weight: 700; color: var(--text-strong); }
.mapa-probe-close { padding: 0 4px; background: transparent; border: 0; cursor: pointer;
    color: var(--muted); font-size: 0.9rem; line-height: 1; }
.mapa-probe-close:hover { color: var(--text-strong); }

.mapa-confirm { width: min(420px, 94vw); }
.mapa-confirm-text { margin: 0; font-size: 0.88rem; line-height: 1.5; color: var(--text); }

/* Enlace de Maps del probador: es EL MISMO que recibe el agente, y verlo aquí es la
   forma de comprobar que apunta donde debe antes de que llegue a un cliente. */
.mapa-probe-link { display: inline-block; margin-top: 6px; font-size: 0.8rem;
    color: var(--brand); text-decoration: underline; }

/* Enlace + botón "Generar QR" en la misma fila del resultado del probador. */
.mapa-probe-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
.mapa-probe-actions .mapa-probe-link { margin-top: 0; }

/* Caja del QR generado. Fondo blanco fijo (el QR se dibuja sobre blanco en ambos
   temas); centrado y acotado para no romper el ancho del popover del probador. */
.mapa-qr-box { margin-top: 10px; padding: 10px; border-radius: 10px;
    border: 1px solid var(--border); background: var(--surface-2);
    display: flex; flex-direction: column; align-items: center; gap: 8px; }
.mapa-qr-img { width: 180px; max-width: 100%; height: auto; display: block;
    background: #fff; border-radius: 8px; padding: 6px; }
.mapa-qr-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.mapa-qr-box .mapa-hint { text-align: center; }

/* z-index:0 aísla el stacking context de Leaflet (sus panes/controles usan
   z-index hasta ~1000). Sin esto el mapa se pinta sobre overlays de la barra
   superior (p. ej. el desplegable de notificaciones, z-index 41). */
.mapa-canvas-wrap { flex: 1; position: relative; z-index: 0; min-width: 0; height: 100%; border-radius: 12px; overflow: hidden;
    border: 1px solid var(--border); }
.mapa-canvas { width: 100%; height: 100%; }
/* Toggle de vista de cobertura (calor / puntos / zonas) */
.mapa-view-toggle { display: flex; gap: 4px; }
.mapa-view-btn { flex: 1; padding: 6px 4px; font-size: 0.75rem; border: 1px solid var(--control-border);
    background: var(--control-bg); color: var(--muted); border-radius: 8px; cursor: pointer; white-space: nowrap; }
.mapa-view-btn:hover { background: var(--control-bg-hover); }
.mapa-view-btn.is-active { background: var(--brand-soft-bg); color: var(--brand-soft-fg); border-color: var(--brand); font-weight: 600; }
/* divIcons de cobertura (Leaflet inyecta estos className) */
.mapa-pin-ico { background: none; border: none; font-size: 20px; line-height: 1; text-align: center;
    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.4)); }
.mapa-cluster-label { background: none; border: none; display: flex; align-items: center; justify-content: center;
    font-weight: 700; color: #fff; font-size: 0.82rem; text-shadow: 0 1px 3px rgba(0,0,0,0.7); pointer-events: none; }
.mapa-place-hint { position: absolute; top: 12px; left: 50%; transform: translateX(-50%); z-index: 500;
    background: var(--brand); color: #fff; padding: 8px 16px; border-radius: 999px; font-size: 0.85rem;
    font-weight: 600; box-shadow: var(--shadow-md); }

/* Estilo "midnight": tiñe de azul los tiles oscuros (CARTO dark_all no es azul
   nativo; sin API key no hay raster midnight, así que se logra por filtro CSS
   solo sobre el pane de tiles → no afecta marcadores/heatmap ni popups). */
.mapa-midnight .leaflet-tile-pane {
    filter: sepia(0.7) hue-rotate(178deg) saturate(2.2) brightness(0.78) contrast(1.05);
}

/* Leaflet popups en tema oscuro/claro */
.leaflet-popup-content-wrapper, .leaflet-popup-tip { background: var(--surface-overlay); color: var(--text); }
.leaflet-popup-content { font-size: 0.85rem; }
.leaflet-container { background: var(--surface-2); font: inherit; }

@media (max-width: 860px) {
    .mapa-body { flex-direction: column; }
    /* Apilado, el panel ocupa todo el ancho: la pestaña caería en mitad de la lista y
       colapsar no aporta nada (no hay columna que ensanchar). */
    .mapa-collapse-tab { display: none; }
    .mapa-controls { width: 100%; max-height: 340px; }
    .mapa-canvas-wrap { min-height: 420px; }
}

/* ── Buscador de modelos OpenRouter (modal LLM/Multimedia) ─────────────────── */
.model-picker-modal {
  max-width: min(1180px, 96vw);
  width: 96%;
  display: flex;
  flex-direction: column;
  max-height: 90vh;
  overflow: hidden; /* solo el área de scroll interna scrollea (no doble scroll) */
  padding: 18px 22px 14px;                     /* #11c: que el contenido no toque los bordes */
}
.model-picker-loading { font-size: 0.85rem; color: var(--muted); align-self: center; }

/* Toolbar canónica del catálogo de modelos: búsqueda redonda + embudo (filtros
   empaquetados en popover) + limpiar. Mismo patrón que Contactos/Auditoría. */
.model-picker-toolbar {
  position: relative;
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  justify-content: flex-start; margin-bottom: 12px;
}
/* Mismo ancho que en Contactos (pastilla 520px + iconos pegados): sin esto la
   forma crecía a todo el ancho y separaba los iconos del buscador (hueco raro). */
.model-picker-toolbar .contacts-search-form { flex: 0 1 520px; max-width: 520px; width: 100%; position: static; }
/* El popover del embudo se ancla a la toolbar (position:relative de arriba). */
.model-picker-fpop { min-width: 260px; }
.model-picker-fpop .contacts-fpop-section { display: flex; flex-direction: column; gap: 6px; }
.model-picker-fpop-ctrl {
  width: 100%; box-sizing: border-box; padding: 8px 12px; border-radius: 8px;
  border: 1px solid var(--control-border); background: var(--control-bg);
  color: var(--text); font-size: 0.88rem;
}

.model-picker-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-bottom: 12px;
}
.model-picker-filters select,
.model-picker-filters input {
  padding: 9px 12px;
  border-radius: 8px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--text);
  font-size: 0.9rem;
}
.model-picker-filters .model-picker-search { flex: 1 1 240px; min-width: 200px; }
.model-picker-filters input[type="number"] { width: 200px; }
.model-picker-scroll {
  overflow: auto;
  flex: 1 1 auto;
  min-height: 0; /* imprescindible para que un hijo flex pueda scrollear */
  border: 1px solid var(--border);
  border-radius: 12px;
}
.model-picker-table {
  width: 100%;
  min-width: 760px;
  border-collapse: collapse;
  font-size: 0.9rem;
}
.model-picker-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  /* OPACO (no --surface-2, que es translúcido): si no, las filas se ven a través del
     header sticky y parece que se solapan (bug del catálogo, #8). */
  background: var(--surface-overlay);
  color: var(--text-strong);
  font-weight: 600;
  text-align: left;
  padding: 10px 14px;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}
.model-picker-table thead th.model-picker-action-col,
.model-picker-table td.model-picker-action { text-align: center; white-space: nowrap; }
.model-picker-table td {
  padding: 10px 14px;
  border-bottom: 1px solid var(--border-soft);
  vertical-align: top;
  white-space: nowrap;
}
.model-picker-table tbody tr:hover { background: var(--surface-2); }
.model-picker-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.model-picker-name { white-space: normal; min-width: 240px; max-width: 380px; }
.model-picker-name-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.model-picker-model { font-weight: 600; color: var(--text-strong); }
.model-picker-name code { font-size: 0.72rem; color: var(--muted); word-break: break-all; }
.model-picker-caps { font-size: 0.78rem; color: var(--muted); white-space: normal; max-width: 170px; }
.model-picker-free {
  font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em;
  padding: 1px 7px; border-radius: 999px;
  background: var(--badge-success-bg); color: var(--badge-success-fg);
}
.model-picker-warn {
  display: flex; gap: 12px; align-items: center; justify-content: space-between;
  background: var(--badge-warn-bg); color: var(--badge-warn-fg);
  border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 14px; margin-bottom: 12px; font-size: 0.85rem; line-height: 1.4;
}
.model-picker-warn button { flex: 0 0 auto; }
@media (max-width: 640px) {
  .model-picker-filters .model-picker-search,
  .model-picker-filters input[type="number"] { flex: 1 1 100%; width: 100%; }
}

/* ── Step-up de desactivar 2FA: tabs contraseña / código ────────────────────── */
.mfa-stepup { margin-top: 4px; }
.mfa-tabs { display: flex; gap: 8px; margin-bottom: 12px; }
.mfa-tab {
  flex: 1;
  padding: 9px 12px;
  border-radius: 8px;
  cursor: pointer;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--muted);
  font-size: 0.85rem;
  font-weight: 600;
  font-family: inherit;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.mfa-tab:hover { background: var(--control-bg-hover); }
.mfa-tab.active {
  background: var(--brand-soft-bg);
  color: var(--brand-soft-fg);
  border-color: var(--brand);
}

/* Auditor del system prompt (Personalidad > Auditar) */
.prompt-audit-legend { display:flex; flex-wrap:wrap; gap:8px 14px; align-items:center; padding:10px 12px; margin-bottom:12px; background:var(--surface-2); border:1px solid var(--border); border-radius:10px; }
.prompt-audit-total { display:flex; flex-direction:column; margin-right:6px; padding-right:14px; border-right:1px solid var(--border); }
.prompt-audit-total-num { font-size:1.25rem; font-weight:700; color:var(--text-strong); font-variant-numeric:tabular-nums; line-height:1.1; }
.prompt-audit-total-lbl { font-size:0.72rem; color:var(--muted); }
.prompt-audit-chip { display:flex; align-items:center; gap:6px; font-size:0.78rem; }
.prompt-audit-dot { width:10px; height:10px; border-radius:3px; flex:0 0 auto; }
.prompt-audit-chip-lbl { color:var(--muted); }
.prompt-audit-chip-tok { color:var(--text-strong); font-variant-numeric:tabular-nums; font-weight:600; }
.prompt-audit-scroll { overflow:auto; flex:1 1 auto; min-height:0; display:flex; flex-direction:column; gap:10px; padding-right:4px; }
.prompt-audit-seg { border-left:4px solid #94a3b8; background:var(--surface-2); border-radius:0 8px 8px 0; padding:10px 14px; }
.prompt-audit-seg-head { display:flex; align-items:center; gap:10px; margin-bottom:6px; flex-wrap:wrap; }
.prompt-audit-seg-title { font-weight:700; color:var(--text-strong); font-size:0.9rem; }
.prompt-audit-seg-src { font-size:0.72rem; font-weight:600; text-transform:uppercase; letter-spacing:0.02em; }
.prompt-audit-seg-tok { margin-left:auto; font-size:0.75rem; color:var(--muted); font-variant-numeric:tabular-nums; }
.prompt-audit-seg-text { margin:0; white-space:pre-wrap; word-break:break-word; font-family:ui-monospace,Menlo,Consolas,monospace; font-size:0.8rem; line-height:1.5; color:var(--text); background:var(--bg); border:1px solid var(--border-soft); border-radius:6px; padding:8px 10px; max-height:300px; overflow:auto; }
.prompt-audit-tools-head { display:flex; align-items:center; gap:10px; margin:16px 0 2px; padding-top:12px; border-top:1px solid var(--border); }
.prompt-audit-tools-head .prompt-audit-seg-tok { margin-left:auto; }
.prompt-audit-tool { padding:6px 0; border-top:1px solid var(--border-soft); }
.prompt-audit-tool:first-child { border-top:none; }
.prompt-audit-tool-head { display:flex; align-items:center; gap:8px; }
.prompt-audit-tool-head .prompt-audit-seg-tok { margin-left:auto; }
.prompt-audit-tool-name { font-weight:600; color:var(--text-strong); font-size:0.82rem; }
.prompt-audit-tool-desc { font-size:0.78rem; color:var(--muted); white-space:pre-wrap; margin-top:2px; line-height:1.45; }

/* ═══════════════════════════════════════════════════════════════════════════
   Centro de Comunicaciones — asistente de líneas
   Solo tokens semánticos (tema claro/oscuro). El modal flota → --surface-overlay.
   ═══════════════════════════════════════════════════════════════════════════ */

.comms-page { padding: 0 4px; }

.comms-alert {
  padding: 12px 16px; border-radius: 10px; margin: 12px 0;
  font-size: 0.9rem; border: 1px solid var(--border);
}
.comms-alert--error { background: var(--badge-danger-bg); color: var(--badge-danger-fg); border-color: transparent; }
.comms-alert--info  { background: var(--badge-info-bg);   color: var(--badge-info-fg);   border-color: transparent; }

.comms-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin: 16px 0 20px; flex-wrap: wrap;
}
.comms-h2 { margin: 0; font-size: 1.15rem; color: var(--text-strong); }
.comms-sub { margin: 4px 0 0; font-size: 0.85rem; color: var(--muted); }
.comms-hint { margin: 6px 0 0; font-size: 0.78rem; color: var(--muted-2); line-height: 1.45; }

.comms-empty {
  text-align: center; padding: 48px 24px; color: var(--muted);
  background: var(--surface-2); border-radius: 14px; border: 1px dashed var(--border);
}
.comms-empty__icon { font-size: 2.4rem; margin-bottom: 8px; }

/* ── Lista de líneas ── */
.comms-lines { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; }
.comms-line-card {
  display: flex; align-items: center; gap: 14px; padding: 16px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
}
.comms-line-card__main { flex: 1; min-width: 0; }
.comms-line-card__side { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.comms-line-phone { font-weight: 700; font-size: 1.05rem; color: var(--text-strong); }
.comms-line-name { font-size: 0.85rem; color: var(--muted); margin-top: 2px; }
.comms-line-stage { font-size: 0.75rem; color: var(--muted-2); }

/* ── Badges que faltaban (solo tokens) ── */
.badge-success { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.badge-warn    { background: var(--badge-warn-bg);    color: var(--badge-warn-fg); }
.badge-info    { background: var(--badge-info-bg);    color: var(--badge-info-fg); }
.badge-neutral { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }

/* ── Botón fantasma / select (faltaban) ── */
.btn-ghost {
  background: transparent; color: var(--muted); border: 1px solid var(--border);
  border-radius: 8px; padding: 6px 12px; cursor: pointer; font-size: 0.85rem;
}
.btn-ghost:hover:not(:disabled) { background: var(--control-bg-hover); color: var(--text); }
.form-select {
  width: 100%; padding: 10px 12px; border-radius: 8px;
  background: var(--control-bg); color: var(--text); border: 1px solid var(--control-border);
  font-size: 0.9rem;
}
.form-select:focus { outline: none; border-color: var(--brand); }

/* ── Modal del asistente (flota → --surface-overlay) ── */
.comms-modal-backdrop {
  position: fixed; inset: 0; background: var(--scrim);
  display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 20px;
}
.comms-modal {
  background: var(--surface-overlay); border: 1px solid var(--border);
  border-radius: 16px; width: 100%; max-width: 560px; max-height: 90vh;
  overflow-y: auto; box-shadow: var(--shadow-lg);
}
.comms-modal__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 22px; border-bottom: 1px solid var(--border);
}
.comms-modal__head h3 { margin: 0; font-size: 1.05rem; color: var(--text-strong); }
.comms-modal__close {
  background: none; border: none; font-size: 1.2rem; color: var(--muted);
  cursor: pointer; padding: 4px 8px; border-radius: 6px;
}
.comms-modal__close:hover { background: var(--control-bg-hover); color: var(--text); }
.comms-modal__body { padding: 24px 22px; }

/* ── Pasos ── */
.comms-step { text-align: center; }
.comms-step__num {
  width: 34px; height: 34px; border-radius: 50%; margin: 0 auto 12px;
  background: var(--brand-soft-bg); color: var(--brand-soft-fg);
  display: flex; align-items: center; justify-content: center; font-weight: 700;
}
.comms-step h4 { margin: 0 0 6px; color: var(--text-strong); font-size: 1.05rem; }
.comms-field { text-align: left; margin: 18px 0; }
.comms-field--muted { padding: 14px; background: var(--surface-2); border-radius: 10px; }

/* ── Selección de número ── */
.comms-numeros { display: flex; flex-direction: column; gap: 8px; margin: 16px 0; }
.comms-numero-opt {
  display: flex; align-items: center; gap: 10px; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: 10px; cursor: pointer;
  background: var(--surface); transition: border-color 0.15s;
}
.comms-numero-opt:hover { border-color: var(--brand); }
.comms-numero-phone { font-weight: 600; color: var(--text-strong); }
.comms-numero-costo { margin-left: auto; font-size: 0.8rem; color: var(--muted); }

/* ── Progreso en vivo ── */
.comms-progreso { text-align: center; padding: 12px 0; }
.comms-progreso h4 { margin: 14px 0 6px; color: var(--text-strong); }
.comms-progress-bar {
  height: 8px; background: var(--surface-3); border-radius: 999px; overflow: hidden; margin-bottom: 20px;
}
.comms-progress-fill {
  height: 100%; background: var(--brand); border-radius: 999px; transition: width 0.6s ease;
}
.comms-spinner {
  width: 40px; height: 40px; margin: 0 auto; border-radius: 50%;
  border: 4px solid var(--surface-3); border-top-color: var(--brand);
  animation: comms-spin 0.8s linear infinite;
}
@keyframes comms-spin { to { transform: rotate(360deg); } }
.comms-check {
  width: 64px; height: 64px; margin: 0 auto 8px; border-radius: 50%;
  background: var(--badge-success-bg); color: var(--badge-success-fg);
  display: flex; align-items: center; justify-content: center; font-size: 2rem; font-weight: 700;
}
.comms-warn-icon {
  width: 56px; height: 56px; margin: 0 auto 8px; border-radius: 50%;
  background: var(--badge-warn-bg); color: var(--badge-warn-fg);
  display: flex; align-items: center; justify-content: center; font-size: 1.6rem;
}
.comms-progreso--ok .comms-line-phone { margin: 10px 0 18px; font-size: 1.3rem; }
.comms-error-msg { color: var(--muted); font-size: 0.9rem; margin: 8px 0 16px; line-height: 1.5; }
.comms-recover { display: flex; flex-direction: column; gap: 12px; align-items: center; }

/* ═══════════════════════════════════════════════════════════════════════════
   AGENTE-GUÍA ILUSTRADO  ·  .ga-* (personaje)  ·  .gt-* (escenario del tour)
   ---------------------------------------------------------------------------
   Sustituye al tour anterior (.tut-*), que se borró al migrar las 15 páginas.

   COLOR: solo tokens. Los locales del personaje se declaran una vez en `.ga` y
   ahí se retematiza todo. No hay overrides por tema para la silueta: --brand,
   --brand-dark y --bg ya se invierten solos (:root ~20 ↔ html.light-mode ~10951).

   MOVIMIENTO: en bucle infinito SOLO respira el personaje (~1 % de escala). Todo
   lo demás es de un disparo o finito (≤3 iteraciones). Es deliberado: con un
   público de atención corta, el movimiento tiene que llamar y soltar. El tour
   anterior latía para siempre y encima animaba un box-shadow de 9999px: un
   repintado de pantalla completa a 60 fps que no paraba nunca.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Capas del escenario ────────────────────────────────────────────────────
   2400-2404: por encima del tour viejo (2000-2002) y por DEBAJO del modal de
   error crítico (.crit-overlay, 3000) y de los toasts — un fallo de sesión debe
   poder tapar la guía. */

.gt-blocker {
  position: fixed;
  inset: 0;
  z-index: 2400;
  background: transparent;
}
/* Sin objetivo que resaltar, el velo lo pone el bloqueador. Con objetivo lo pone
   la sombra gigante del agujero, que además recorta el hueco. */
.gt-blocker--dim { background: var(--scrim); }

/* El foco. Es una caja VACÍA del overlay colocada encima del objetivo, cuya
   sombra gigante oscurece todo lo demás.
   Frente a la técnica anterior (poner la clase sobre el elemento de la página):
     · no toca el DOM ajeno — adiós al position/z-index/pointer-events !important
       y al caso especial que el motor anterior necesitaba para el header;
     · al ser fixed no lo recorta ningún overflow:hidden, así que por fin se puede
       resaltar una fila de tabla o algo dentro de un panel con scroll;
     · se puede ANIMAR: el foco se desliza de un elemento al siguiente.
   Contrapartida: el elemento resaltado queda por debajo del velo y no es
   clicable. Los pasos que piden pulsar el elemento real no pintan el bloqueador
   (Advance::ClickTarget). */
.gt-hole {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 2401;
  pointer-events: none;
  border-radius: 12px;
  box-shadow: 0 0 0 100vmax var(--scrim);
  transition:
    transform     .34s cubic-bezier(.22, 1, .36, 1),
    width         .34s cubic-bezier(.22, 1, .36, 1),
    height        .34s cubic-bezier(.22, 1, .36, 1),
    border-radius .2s ease;
}
/* Anillo de marca en un pseudo-elemento aparte para que el pulso sea
   transform+opacity (compositor) y no un box-shadow de pantalla completa. */
.gt-hole::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: inherit;
  border: 3px solid rgba(var(--brand-rgb), .95);
  animation: gt-ring 1.8s ease-out 3;
}
@keyframes gt-ring {
  0%   { transform: scale(1);    opacity: 1; }
  70%  { transform: scale(1.08); opacity: 0; }
  100% { transform: scale(1.08); opacity: 0; }
}

/* Flecha. Sin viewBox a propósito: 1 unidad = 1 px CSS, así no hay que
   remapear coordenadas al redimensionar. */
.gt-link {
  position: fixed;
  inset: 0;
  z-index: 2402;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
  overflow: visible;
}
.gt-link-line,
.gt-link-tip {
  fill: none;
  stroke: var(--brand);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* El trazo avanza DEL personaje AL objetivo: la animación lleva dirección, que
   es justo el vector de atención que se busca. Un disparo. */
.gt-link-line {
  stroke-dasharray: var(--gt-len, 400);
  stroke-dashoffset: var(--gt-len, 400);
  animation: gt-draw .5s ease-out forwards;
}
.gt-link-tip { opacity: 0; animation: gt-tip .2s ease-out .42s forwards; }
@keyframes gt-draw { to { stroke-dashoffset: 0; } }
@keyframes gt-tip  { to { opacity: 1; } }

/* Colocación del personaje. Este div SOLO posiciona y funde; la respiración va
   en el <svg> de dentro — si estuvieran en el mismo elemento se pisarían la
   propiedad `transform`. */
.gt-avatar {
  position: fixed;
  z-index: 2403;
  pointer-events: none;
  transition: left .32s cubic-bezier(.22, 1, .36, 1),
              top  .32s cubic-bezier(.22, 1, .36, 1),
              opacity .15s ease;
}
/* Mientras se recoloca se desvanece: arrastrar al personaje media pantalla hace
   que el usuario lo persiga a él en vez de mirar el destino. */
.gt-avatar--moving { opacity: 0; }

/* ── Tarjeta ────────────────────────────────────────────────────────────────
   --surface-overlay (casi opaca) porque es un panel FLOTANTE. Con --surface, que
   es translúcida, sobre el velo se vuelve ilegible en oscuro. */
.gt-card {
  position: fixed;
  z-index: 2404;
  width: min(380px, calc(100vw - 24px));
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 20px 22px 16px;
  box-shadow: var(--shadow-lg);
  animation: gt-card-in .26s ease-out;
}
/* Sin objetivo (o sin hueco en ningún lado): centrada abajo, como el tour
   clásico. Es una posición que el usuario ya reconoce. */
.gt-card--centered {
  left: 50%;
  bottom: 40px;
  top: auto;
  transform: translateX(-50%);
}
.gt-card:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
@keyframes gt-card-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* La variante centrada ya usa `transform` para centrarse: su entrada no puede
   pisarlo o saltaría media tarjeta a la derecha. */
.gt-card--centered { animation: gt-card-in-centered .26s ease-out; }
@keyframes gt-card-in-centered {
  from { opacity: 0; transform: translateX(-50%) translateY(10px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

.gt-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}
.gt-eyebrow {
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--brand);
}
.gt-counter { font-size: .75rem; font-weight: 600; color: var(--muted-2); }
/* Guía de novedades: la cabecera se distingue de la guía normal para que quien
   ya conoce la pantalla entienda de un vistazo por qué le vuelve a salir. */
.gt-eyebrow--news {
  background: var(--brand-soft-bg);
  color: var(--brand-soft-fg);
  padding: 2px 8px;
  border-radius: 999px;
}

/* Progreso explícito, no cinco puntitos: "voy por 2 de 5" es información, y para
   quien se pierde con facilidad es la diferencia entre seguir y abandonar. */
.gt-progress {
  height: 4px;
  border-radius: 2px;
  background: var(--control-bg-hover);
  overflow: hidden;
  margin-bottom: 14px;
}
.gt-progress-fill {
  display: block;
  height: 100%;
  border-radius: 2px;
  background: var(--brand);
  transition: width .3s cubic-bezier(.22, 1, .36, 1);
}

.gt-title {
  font-size: 1.0625rem;
  font-weight: 700;
  color: var(--text-strong);
  letter-spacing: -.01em;
  margin: 0 0 8px;
}
.gt-body {
  font-size: .9rem;
  line-height: 1.55;
  color: var(--muted);
  margin: 0 0 18px;
}

/* ── Ejemplo de llenado ─────────────────────────────────────────────────────
   Maqueta INERTE de un formulario: campo → valor de ejemplo. Solo aparece en
   modo «con detalle». Deliberadamente NO parece un formulario editable (sin
   inputs, sin bordes de campo) para que nadie intente escribir en él. */
.gt-example {
  margin: 0 0 18px;
  padding: 12px 14px;
  border: 1px dashed var(--border);
  border-radius: 12px;
  background: var(--surface-2);
}
.gt-example-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}
.gt-example-title {
  font-size: .8125rem;
  font-weight: 700;
  color: var(--text-strong);
}
.gt-example-badge {
  font-size: .625rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--badge-info-fg);
  background: var(--badge-info-bg);
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
}
.gt-example-fields {
  display: grid;
  grid-template-columns: minmax(0, auto) 1fr;
  gap: 4px 12px;
  margin: 0;
}
.gt-example-row {
  display: contents;
}
.gt-example-label {
  font-size: .8125rem;
  font-weight: 600;
  color: var(--muted-2);
  white-space: nowrap;
}
.gt-example-value {
  margin: 0;
  font-size: .8125rem;
  color: var(--text);
  /* Se ve como "dato ya escrito", no como campo por rellenar. */
  font-variant-numeric: tabular-nums;
  word-break: break-word;
}
.gt-example-note {
  margin: 10px 0 0;
  font-size: .75rem;
  line-height: 1.5;
  color: var(--muted);
}

/* ── Botón "¿Qué hace cada botón?" ──────────────────────────────────────────
   Va en la cabecera de un modal/apartado con muchos botones y abre su guía
   efímera. Pastilla discreta: ayuda a mano sin competir con las acciones. */
.gt-modal-help {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px 5px 8px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--control-bg);
  color: var(--muted);
  font: inherit;
  font-size: .8125rem;
  font-weight: 600;
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}
.gt-modal-help:hover {
  background: var(--control-bg-hover);
  color: var(--text);
  border-color: var(--brand);
}
.gt-modal-help-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--brand-dark);
  color: #fff;
  font-size: .75rem;
  font-weight: 800;
  line-height: 1;
}

.gt-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.gt-btn-group { display: flex; align-items: center; gap: 8px; }
.gt-btn-skip {
  font: inherit;
  font-size: .8125rem;
  font-weight: 500;
  color: var(--muted-2);
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px 2px;
  transition: color .15s;
}
.gt-btn-skip:hover { color: var(--muted); }
.gt-btn-prev,
.gt-btn-next {
  padding: 8px 16px;
  border-radius: 10px;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: .875rem;
  font-weight: 600;
  transition: background .15s;
}
.gt-btn-prev { background: var(--surface-2); color: var(--muted); }
.gt-btn-prev:hover { background: var(--control-bg-hover); }
/* --brand-dark es la variante que aguanta texto blanco en AMBOS temas. */
.gt-btn-next { background: var(--brand-dark); color: #fff; }
.gt-btn-next:hover { background: var(--brand); }
.gt-hint {
  font-size: .8125rem;
  font-weight: 600;
  color: var(--brand);
}

/* ── Personaje ──────────────────────────────────────────────────────────────
   --ga-body  = --brand      : variante "sobre el fondo del tema" → legible en
                               claro y en oscuro sin duplicar reglas.
   --ga-shade = --brand-dark : más profunda en ambos temas → el brazo en reposo
                               recede sin inventar un color.
   --ga-ink   = --bg         : ojos y boca del color del lienzo → contraste
                               automático al cambiar de tema.
   --ga-halo  = --bg         : contorno tipo pegatina; en claro despega la
                               silueta del velo, en oscuro es invisible. */
.ga {
  --ga-body:  var(--brand);
  --ga-shade: var(--brand-dark);
  --ga-ink:   var(--bg);
  --ga-halo:  var(--bg);
  --ga-swing: 420ms;
  --ga-ease:  cubic-bezier(.34, 1.28, .48, 1);

  display: block;
  height: var(--ga-size, 124px);
  width: auto;
  transform-origin: 50% 91%;
  animation: ga-breathe 4.6s ease-in-out infinite;
  /* Único bucle permanente del sistema → la capa de composición se justifica.
     No copiar esto en el resto de clases: una capa GPU eterna para una animación
     de entrada de 0,3 s es memoria de vídeo tirada. */
  will-change: transform;
}

.ga-solid {
  fill: var(--ga-body);
  stroke: var(--ga-halo);
  stroke-width: 4;
  stroke-linejoin: round;
  paint-order: stroke fill;
}
.ga-shadow { fill: rgba(var(--brand-dark-rgb), .18); }
.ga-leg    { fill: var(--ga-shade); }
.ga-neck   { fill: var(--ga-shade); }
.ga-eye    { fill: var(--ga-ink); }
.ga-mouth  { fill: none; stroke: var(--ga-ink); stroke-width: 2; stroke-linecap: round; }

/* Lentes: el avatar de las guías «con detalle». Ocultos por defecto; los enciende
   `.ga--glasses` en el `<svg>`. Mismo `--ga-ink` que ojos y boca → legible sobre la
   cabeza en claro y en oscuro sin una regla por tema. Lentes sin relleno para que
   los ojos se vean a través. */
.ga-glasses { display: none; }
.ga--glasses .ga-glasses { display: inline; }
.ga-glass-lens {
  fill: none;
  stroke: var(--ga-ink);
  stroke-width: 1.8;
}
.ga-glass-bar {
  fill: none;
  stroke: var(--ga-ink);
  stroke-width: 1.8;
  stroke-linecap: round;
}

/* Micro-desplazamiento de la mirada hacia donde señala: dos reglas que refuerzan
   mucho el "mira allí". */
.ga-face { transition: transform var(--ga-swing) var(--ga-ease); }
.ga--face-left  .ga-face { transform: translateX(-2px); }
.ga--face-right .ga-face { transform: translateX(2px); }

/* Brazos. `transform-box: view-box` hace que transform-origin use unidades del
   viewBox.
   OJO: esta `transition` abreviada SUSTITUYE a la regla global de style.css:109
   en estos elementos, por eso stroke/fill se repiten aquí — si no, el personaje
   dejaría de fundir su color al cambiar de tema. Y `stroke` NO está en la lista
   global, así que los brazos (dibujados con stroke) hay que declararlos a mano
   en cualquier caso. */
.ga-arm,
.ga-arm-think {
  transform-box: view-box;
  transition: transform var(--ga-swing) var(--ga-ease),
              stroke .3s ease, fill .3s ease, opacity .2s ease;
}
.ga-arm--r, .ga-arm-think { transform-origin: 99px 62px; }
.ga-arm--l                { transform-origin: 61px 62px; }

.ga-limb-halo { fill: none; stroke: var(--ga-halo);  stroke-width: 16;   stroke-linecap: round; }
.ga-limb      { fill: none; stroke: var(--ga-shade); stroke-width: 10.5; stroke-linecap: round; }
.ga-hand, .ga-fist { fill: var(--ga-shade); }

/* El brazo que señala se adelanta de color: el gesto se ilumina. */
.ga--point-right .ga-arm--r .ga-limb, .ga--point-right .ga-arm--r .ga-hand,
.ga--point-left  .ga-arm--l .ga-limb, .ga--point-left  .ga-arm--l .ga-hand,
.ga--point-up    .ga-arm--r .ga-limb, .ga--point-up    .ga-arm--r .ga-hand,
.ga--point-down  .ga-arm--r .ga-limb, .ga--point-down  .ga-arm--r .ga-hand {
  stroke: var(--ga-body);
  fill: var(--ga-body);
}

/* Ángulos. En SVG rotate() positivo es horario y el brazo en reposo cuelga hacia
   abajo (90° de pantalla): -88° ≈ horizontal derecha, -168° ≈ arriba, -35° ≈
   abajo-derecha. */
.ga--point-right .ga-arm--r { transform: rotate(-88deg); }
.ga--point-left  .ga-arm--l { transform: rotate(88deg); }
.ga--point-up    .ga-arm--r { transform: rotate(-168deg); }
.ga--point-down  .ga-arm--r { transform: rotate(-35deg); }
/* Mirando a la izquierda, arriba/abajo usan el brazo de ese lado. */
.ga--face-left.ga--point-up   .ga-arm--r { transform: none; }
.ga--face-left.ga--point-up   .ga-arm--l { transform: rotate(168deg); }
.ga--face-left.ga--point-down .ga-arm--r { transform: none; }
.ga--face-left.ga--point-down .ga-arm--l { transform: rotate(35deg); }
.ga--face-left.ga--point-up   .ga-arm--l .ga-limb,
.ga--face-left.ga--point-down .ga-arm--l .ga-limb { stroke: var(--ga-body); }

/* Pensar: la mano va a la barbilla, no es una rotación → brazo aparte. */
.ga-arm-think { opacity: 0; visibility: hidden; }
.ga--think .ga-arm-think { opacity: 1; visibility: visible; }
.ga--think .ga-arm--r { opacity: 0; }
.ga-bubble { opacity: 0; visibility: hidden; fill: var(--ga-body); }
.ga--think .ga-bubble { opacity: 1; visibility: visible; }
.ga--think .ga-bubble circle { animation: ga-dots 1.8s ease-in-out 3; }
.ga--think .ga-bubble circle:nth-child(2) { animation-delay: .18s; }
.ga--think .ga-bubble circle:nth-child(3) { animation-delay: .36s; }

/* Saludar y celebrar: finitos. Los keyframes INCLUYEN el ángulo base porque una
   `animation` sobre transform sustituye por completo al de la `transition`; sin
   él, el brazo daría un salto al empezar. */
.ga--wave .ga-arm--r { animation: ga-wave-r .9s ease-in-out 2; }
.ga--face-left.ga--wave .ga-arm--r { animation: none; }
.ga--face-left.ga--wave .ga-arm--l { animation: ga-wave-l .9s ease-in-out 2; }
.ga--celebrate .ga-arm--r { animation: ga-cheer-r .7s ease-in-out 2; }
.ga--celebrate .ga-arm--l { animation: ga-cheer-l .7s ease-in-out 2; }
/* Sustituye a ga-breathe mientras dura: dos animaciones sobre la misma
   propiedad no se componen, gana la última. */
.ga--celebrate { animation: ga-hop .5s ease-in-out 3; }
.ga-sparks { opacity: 0; fill: var(--brand); }
.ga--celebrate .ga-sparks { opacity: 1; }
.ga--celebrate .ga-spark {
  animation: ga-spark 1.1s ease-out 2;
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
.ga--celebrate .ga-spark:nth-child(2) { animation-delay: .2s; }
.ga--celebrate .ga-spark:nth-child(3) { animation-delay: .4s; }

/* Respiración: ~0,9 px y 1,2 % de escala en 4,6 s. Por debajo del umbral que
   captura la atención — se lee como "está vivo", no como "algo se mueve". */
@keyframes ga-breathe {
  0%, 100% { transform: translateY(0)      scaleY(1); }
  50%      { transform: translateY(-1.2px) scaleY(1.012); }
}
@keyframes ga-wave-r  { 0%,100% { transform: rotate(-142deg); } 50% { transform: rotate(-166deg); } }
@keyframes ga-wave-l  { 0%,100% { transform: rotate(142deg);  } 50% { transform: rotate(166deg);  } }
@keyframes ga-cheer-r { 0%,100% { transform: rotate(-145deg); } 50% { transform: rotate(-128deg); } }
@keyframes ga-cheer-l { 0%,100% { transform: rotate(145deg);  } 50% { transform: rotate(128deg);  } }
@keyframes ga-hop     { 0%,100% { transform: translateY(0);   } 45% { transform: translateY(-7px); } }
@keyframes ga-dots    { 0%,100% { opacity: .25; } 50% { opacity: 1; } }
@keyframes ga-spark {
  0%   { opacity: 0; transform: scale(.4); }
  40%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.25); }
}

/* En claro el --scrim del proyecto tiene α .45 y deja poco foco para un tour. La
   misma sombra se declara dos veces: al componerse sobre sí misma el alfa se
   acumula (≈.70) sin hardcodear ningún color nuevo. Si el velo claro se quedara
   corto en TODOS los modales, lo correcto sería subir --scrim en
   html.light-mode (~10964), no parchear aquí. */
html.light-mode .gt-hole {
  box-shadow: 0 0 0 100vmax var(--scrim), 0 0 0 100vmax var(--scrim);
}

/* ── Tarjeta "Primeros pasos" del Inicio ────────────────────────────────────
   Un solo ítem destacado a la vez: el primero que falta. Los demás quedan en
   gris y sin explicación. Seis botones iguales son seis decisiones. */

.fs-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 20px 22px 16px;
  /* Separación arriba Y abajo. La tarjeta va detrás de la rejilla de módulos del
     Inicio, y esa sección no lleva `margin-bottom`: el ritmo vertical de la página
     lo dan los bloques de arriba empujando hacia abajo (banner 24px, cabecera 32px,
     barra de módulos 18px). Como la tarjeta es el ÚLTIMO bloque, su margen inferior
     no separaba de nada y arriba quedaba pegada a las tarjetas de módulos.
     El margen va aquí y no en la sección para que, cuando la tarjeta no se muestra
     (descartada, o el usuario no es owner/supervisor), no quede un hueco suelto. */
  margin-top: 28px;
  margin-bottom: 28px;
}
.fs-card--done {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 14px 20px;
}
.fs-done-text { color: var(--text-strong); font-weight: 600; font-size: .9375rem; }

.fs-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 10px;
}
.fs-title { font-size: 1.0625rem; font-weight: 700; color: var(--text-strong); margin: 0; }
.fs-count { font-size: .8125rem; font-weight: 600; color: var(--muted-2); }

.fs-progress {
  height: 5px;
  border-radius: 3px;
  background: var(--control-bg-hover);
  overflow: hidden;
  margin-bottom: 16px;
}
.fs-progress-fill {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--brand);
  transition: width .35s cubic-bezier(.22, 1, .36, 1);
}

.fs-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.fs-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 10px;
  border-radius: 10px;
}
/* El pendiente en curso: el único con fondo, explicación y botón fuerte. */
.fs-item--next { background: var(--brand-soft-bg); }
.fs-item--done .fs-item-label { color: var(--muted-2); text-decoration: line-through; }

.fs-check {
  flex: 0 0 20px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .75rem;
  font-weight: 700;
  color: #fff;
}
.fs-item--done .fs-check { background: var(--brand-dark); border-color: var(--brand-dark); }

.fs-item-text { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }
.fs-item-label { font-size: .9375rem; font-weight: 600; color: var(--text); }
.fs-item-why { font-size: .8125rem; color: var(--muted); }

.fs-go {
  flex: 0 0 auto;
  padding: 6px 14px;
  border-radius: 9px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--muted);
  font: inherit;
  font-size: .8125rem;
  font-weight: 600;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.fs-go:hover { background: var(--control-bg-hover); color: var(--text); }
.fs-go--primary {
  background: var(--brand-dark);
  border-color: transparent;
  color: #fff;
  padding: 8px 20px;
  font-size: .875rem;
}
.fs-go--primary:hover { background: var(--brand); color: #fff; }

.fs-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 14px;
  margin-top: 14px;
}
.fs-tour {
  background: none;
  border: none;
  padding: 4px 2px;
  font: inherit;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--brand);
  cursor: pointer;
}
.fs-tour:hover { text-decoration: underline; }
.fs-dismiss {
  background: none;
  border: none;
  padding: 4px 2px;
  font: inherit;
  font-size: .8125rem;
  color: var(--muted-2);
  cursor: pointer;
}
.fs-dismiss:hover { color: var(--muted); }

/* ── Elección de cuánto explicar ────────────────────────────────────────────
   Se pregunta una sola vez en la vida del usuario. Dos opciones grandes y una
   salida: tres decisiones son ya demasiadas para una pregunta que nadie pidió. */

.gt-card--ask { width: min(440px, calc(100vw - 24px)); }
.gt-avatar--ask { left: 50%; bottom: 300px; transform: translateX(-50%); }

.gt-modes { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.gt-mode {
  display: flex;
  flex-direction: column;
  gap: 3px;
  align-items: flex-start;
  padding: 12px 16px;
  border-radius: 12px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  cursor: pointer;
  font: inherit;
  text-align: left;
  transition: background .15s, border-color .15s;
}
.gt-mode:hover { background: var(--control-bg-hover); border-color: var(--brand); }
.gt-mode-name { font-size: .9375rem; font-weight: 700; color: var(--text-strong); }
.gt-mode-desc { font-size: .8125rem; color: var(--muted); }

/* Cambiar de nivel de detalle sin salir de la guía. Va junto a "Salir": es donde
   mira quien está a punto de abandonar porque no se entera. */
.gt-left-actions { display: flex; align-items: center; gap: 12px; }
.gt-btn-mode {
  background: none;
  border: none;
  padding: 6px 2px;
  font: inherit;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--brand);
  cursor: pointer;
}
.gt-btn-mode:hover { text-decoration: underline; }

/* ── Perfil › Guías ─────────────────────────────────────────────────────────*/

.gp-modes { display: flex; gap: 6px; }
.gp-mode {
  padding: 7px 14px;
  border-radius: 9px;
  border: 1px solid var(--control-border);
  background: var(--control-bg);
  color: var(--muted);
  font: inherit;
  font-size: .8125rem;
  font-weight: 600;
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}
.gp-mode:hover { background: var(--control-bg-hover); color: var(--text); }
.gp-mode.is-on {
  background: var(--brand-dark);
  border-color: transparent;
  color: #fff;
}

.gp-list {
  list-style: none;
  margin: 12px 0 0;
  padding: 8px;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2);
}
.gp-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 10px;
  border-radius: 8px;
}
.gp-item:hover { background: var(--control-bg); }
.gp-item-name { font-size: .875rem; color: var(--text); }
.gp-item-btn {
  background: none;
  border: none;
  padding: 2px 4px;
  font: inherit;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--brand);
  cursor: pointer;
}
.gp-item-btn:hover { text-decoration: underline; }
.gp-item-pending { font-size: .75rem; color: var(--muted-2); }

/* ── Boceto de un módulo del código, en el Catálogo ─────────────────────────
   Esquema, no captura: responde a «¿a qué se parece esto?» sin necesitar 13
   imágenes que mantener y que envejecen mal. Todo con tokens, así que sirve en
   claro y en oscuro sin duplicar reglas. */

.mb {
  display: block;
  width: 100%;
  height: auto;
  background: var(--surface-2);
}
/* Dentro de la miniatura cuadrada del catálogo, el boceto la llena igual que
   haría el logo de un paquete: si no, la tarjeta de un módulo se vería con un
   hueco donde las demás tienen imagen. */
.mk-sq-thumb .mb { width: 100%; height: 100%; object-fit: contain; padding: 10px; }
.mk-detail-cover--boceto { padding: 16px; background: var(--surface-2); }
.mk-detail-cover--boceto .mb { background: none; }
.mb-marco { fill: none; stroke: var(--border); stroke-width: 1.5; }
/* Los bloques normales insinúan contenido: han de leerse como «hay algo aquí»,
   no competir con el nombre del módulo que va debajo. */
.mb rect:not(.mb-marco) { fill: var(--control-bg-hover); }
.mb .mb-fuerte { fill: var(--brand); opacity: .75; }
.mb .mb-suave { fill: var(--control-bg-hover); stroke: none; }
.mb .mb-linea {
  fill: none;
  stroke: var(--brand);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .75;
}
.mb circle { fill: var(--brand); opacity: .75; }

/* Marca de los módulos que vienen con el panel: no se instalan ni tienen versión
   que elegir, así que se distinguen de los paquetes del Marketplace. */
.mk-incluido {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--badge-info-bg);
  color: var(--badge-info-fg);
  font-size: .6875rem;
  font-weight: 700;
}
.mk-siempre-al-dia { font-size: .75rem; color: var(--muted-2); }

/* El correo saliente no tiene boceto de módulo —no es una pantalla— así que su
   miniatura es el icono, centrado y a media altura para que no compita con los
   bocetos vecinos, que sí dibujan una interfaz. */
.mk-sq-thumb--feat svg {
  width: 46%;
  height: 46%;
  color: var(--brand);
  opacity: .85;
}
.mk-detail-cover--boceto > svg {
  width: 100%;
  height: 100%;
  color: var(--brand);
  opacity: .85;
}

/* Nota bajo la rejilla cuando en Módulos solo hay lo que trae el panel. Va
   fuera del grid: dentro ocuparía una celda y rompería la cuadrícula. */
.mk-nota-terceros {
  margin: 14px 2px 0;
  font-size: .8125rem;
  color: var(--muted-2);
}

.mk-feat-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
  flex-wrap: wrap;
}
.mk-feat-row .pg-input { flex: 1 1 240px; max-width: 380px; }

/* ── Centro de Control › Guías: apariencia del agente ───────────────────────
   Una tarjeta por gesto. La vista previa muestra la imagen al tamaño y con el
   recorte con que se verá de verdad en la guía, no una miniatura genérica. */

.gi-intro, .gi-foot { max-width: 70ch; }
.gi-foot { margin-top: 18px; }

/* Segmented que elige el modo de guía cuyas imágenes se editan («al grano» /
   «con detalle»). Cada modo tiene su propio juego de avatares. */
.gi-modo {
  display: inline-flex;
  gap: 4px;
  margin-top: 16px;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2);
}
.gi-modo-btn {
  border: none;
  background: none;
  font: inherit;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--muted);
  padding: 6px 16px;
  border-radius: 999px;
  cursor: pointer;
}
.gi-modo-btn:hover { color: var(--text-strong); }
/* Activo = pastilla de marca con texto blanco (--brand-dark es siempre la variante
   sobre la que se apoya texto blanco, en ambos temas). */
.gi-modo-btn--active { background: var(--brand-dark); color: #fff; }
.gi-modo-btn--active:hover { color: #fff; }

.gi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 14px;
  margin-top: 18px;
}

.gi-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface-2);
}

/* Alto fijo para que las tarjetas queden alineadas aunque las imágenes que
   suban tengan proporciones distintas. */
.gi-preview {
  flex: 0 0 96px;
  height: 104px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.gi-img { max-height: 104px; max-width: 96px; width: auto; object-fit: contain; }

.gi-meta { flex: 1 1 auto; min-width: 0; }
.gi-name { font-size: .9375rem; font-weight: 600; color: var(--text-strong); }
.gi-help { font-size: .8125rem; color: var(--muted); margin-top: 2px; }
.gi-state { font-size: .75rem; color: var(--muted-2); margin-top: 6px; }

.gi-actions { display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
/* El input real se oculta y el <label> hace de botón: un input de archivo nativo
   no se puede estilar y rompe la coherencia de la pantalla. */
.gi-file { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.gi-btn { cursor: pointer; text-align: center; font-size: .8125rem; padding: 7px 14px; }
.gi-clear {
  background: none;
  border: none;
  padding: 2px;
  font: inherit;
  font-size: .75rem;
  color: var(--muted-2);
  cursor: pointer;
}
.gi-clear:hover { color: var(--badge-danger-fg); }
.gi-busy { font-size: .75rem; color: var(--muted-2); text-align: center; }

/* Marca "Nuevo" en la barra lateral, hasta la primera visita al módulo.
   Dos formas para los dos estados de la barra:
     · desplegada → etiqueta, que dice de qué avisa;
     · contraída  → punto, porque ahí solo hay sitio para el icono y la etiqueta
       desbordaba la columna descuadrando la lista entera.
   Misma gramática que el indicador de la campana: un punto que late significa
   «hay algo sin ver», y eso ya se entiende sin leer nada. */
.module-sidebar-new {
  margin-left: auto;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--brand-dark);
  color: #fff;
  font-size: .625rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.module-sidebar-new-dot { display: none; }

.module-sidebar--collapsed .module-sidebar-new { display: none; }
.module-sidebar--collapsed .module-sidebar-new-dot {
  display: block;
  position: absolute;
  top: 6px;
  right: 8px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--brand);
  /* El halo va en el pseudo-elemento para que lo anime el compositor: el punto
     en sí no se mueve, así que no arrastra al resto de la fila. */
}
.module-sidebar--collapsed .module-sidebar-new-dot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--brand);
  animation: notif-pulse 1.8s ease-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .module-sidebar--collapsed .module-sidebar-new-dot::after { animation: none; }
}

@media (prefers-reduced-motion: reduce) {
  /* Se apaga el MOVIMIENTO, no el mensaje: la pose es un transform estático, así
     que el personaje sigue señalando el elemento correcto. */
  .fs-progress-fill { transition: none; }
  .ga { animation: none; }
  .ga-arm, .ga-arm-think, .ga-face { transition-duration: .01ms; }
  .ga--wave .ga-arm--r, .ga--wave .ga-arm--l,
  .ga--celebrate .ga-arm--r, .ga--celebrate .ga-arm--l { animation: none; }
  /* Sin animación los keyframes ya no aplican el ángulo: hay que fijarlo. */
  .ga--wave .ga-arm--r { transform: rotate(-142deg); }
  .ga--face-left.ga--wave .ga-arm--l { transform: rotate(142deg); }
  .ga--celebrate .ga-arm--r { transform: rotate(-145deg); }
  .ga--celebrate .ga-arm--l { transform: rotate(145deg); }
  .ga--think .ga-bubble circle,
  .ga--celebrate .ga-spark { animation: none; opacity: 1; }
  .ga--celebrate .ga-spark { transform: none; }

  .gt-avatar { transition: opacity .01ms; }
  .gt-hole { transition: none; }
  .gt-hole::after { animation: none; }   /* el anillo se queda fijo y visible */
  .gt-link-line { animation: none; stroke-dashoffset: 0; }
  .gt-link-tip  { animation: none; opacity: 1; }
  .gt-card, .gt-card--centered { animation: none; }
  .gt-progress-fill { transition: none; }
}
/* Casilla de confirmación dentro de un formulario del Marketplace (p. ej. aceptar
   un downgrade al subir una versión anterior a la publicada). */
.mk-check-row {
  flex-direction: row; align-items: center; gap: 8px;
  font-size: 0.82rem; color: var(--text);
}
.mk-check-row input[type="checkbox"] { flex: 0 0 auto; margin: 0; }


/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — rediseño del Constructor (2026-08-03)
   Izquierda: toolbar de bandeja (buscador + filtros + colapsar) y paginado.
   Derecha: 3 etapas como tarjetas navegables con drill animado.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Panel izquierdo ──────────────────────────────────────────────────────── */

/* Botón "Nuevo": relleno sólido de marca (antes era punteado/fantasma). El
   hover sube levemente y refuerza la sombra para que se sienta presionable. */
.flx-btn-new {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px;
  border: 1px solid var(--brand-dark);
  border-radius: 9px;
  background: var(--brand-dark);
  color: #fff;
  font-size: 12.5px; font-weight: 600; font-family: inherit;
  cursor: pointer; white-space: nowrap;
  box-shadow: var(--shadow-sm);
  transition: transform .12s ease, box-shadow .12s ease, filter .12s ease;
}
.flx-btn-new:hover { filter: brightness(1.1); transform: translateY(-1px); box-shadow: var(--shadow-md); }
.flx-btn-new:active { transform: translateY(0); box-shadow: var(--shadow-sm); filter: brightness(0.96); }
.flx-btn-new:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Ramas (bifurcaciones): sub-sección en la etapa de fases. Misma identidad que las
   tarjetas de disparador (borde, radio, superficie) y los botones-icono de acción. */
.flx-ramas {
  border: 1px solid var(--border-soft); border-radius: 12px;
  background: var(--surface); padding: 12px 14px; margin-bottom: 14px;
}
.flx-ramas-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.flx-ramas-title {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12.5px; font-weight: 700; color: var(--text-strong);
}
.flx-ramas-head .flx-btn-new { margin-left: auto; }
.flx-ramas-list { display: flex; flex-direction: column; gap: 8px; }
.flx-rama-card {
  display: flex; align-items: center; gap: 10px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface-2); padding: 9px 12px;
}
.flx-rama-card.is-off { opacity: .6; border-style: dashed; }
.flx-rama-card-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.flx-rama-card-name {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12.5px; font-weight: 600; color: var(--text-strong);
}
.flx-rama-card-crit {
  font-size: 11.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 52ch;
}
.flx-rama-tt { display: flex; gap: 8px; flex-wrap: wrap; }
.flx-rama-tt .form-input { flex: 1 1 160px; min-width: 130px; }

/* La toolbar va DEBAJO del título + botón. `.lc-topbar` ya trae la pastilla de
   búsqueda y los iconos sin caja; aquí solo se ajusta el espaciado. */
.flx-sidebar .lc-topbar { margin: 0 0 10px; padding: 0; }
.flx-sidebar .lc-filters-popover { top: 92px; }

/* Colapso: la rejilla pasa a un riel angosto. La transición es del ancho de la
   columna, así que va en el grid, no en el aside. */
.flx-layout { transition: grid-template-columns .18s ease; }
.flx-layout--collapsed { grid-template-columns: 62px minmax(0, 1fr); }
.flx-sidebar--collapsed { padding: 10px 8px; align-items: center; }
.flx-sidebar--collapsed .lc-topbar--collapsed { justify-content: center; margin-bottom: 8px; }
.flx-rail-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; align-items: center; }
.flx-rail-item {
  position: relative;
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--control-border);
  border-radius: 10px;
  background: var(--control-bg);
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.flx-rail-item:hover { background: var(--control-bg-hover); }
.flx-rail-item.is-selected { border-color: var(--brand); background: var(--brand-soft-bg); color: var(--brand-soft-fg); }
.flx-rail-ini { font-size: 13px; font-weight: 700; }
.flx-rail-item .flx-dot { position: absolute; top: 4px; right: 4px; width: 6px; height: 6px; box-shadow: none; }

/* "Ver más": tanda siguiente de la lista. */
.flx-more-btn {
  margin-top: 4px;
  padding: 8px;
  border: 1px dashed var(--control-border);
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  font-size: 12px; font-family: inherit;
  cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.flx-more-btn:hover { background: var(--control-bg); color: var(--text); border-color: var(--border-soft); }

/* ── Panel derecho: estado vacío ──────────────────────────────────────────── */
.flx-blank-title { font-size: 15px; font-weight: 600; color: var(--text-strong); }
.flx-blank-sub { font-size: 13px; color: var(--muted); max-width: 46ch; line-height: 1.5; text-align: center; }

/* Cierra el menú ⋮ al pulsar fuera sin oscurecer la página. */
.flx-menu-backdrop { position: fixed; inset: 0; z-index: 59; }
.flx-flow-header-actions.mm-menu { position: relative; }

/* ── Etapas como tarjetas navegables ──────────────────────────────────────── */
.flx-stagebar { gap: 10px; }
.flx-stagecard {
  flex: 1 1 0;
  min-width: 150px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: transform .12s ease, border-color .12s, box-shadow .12s, background .12s;
}
.flx-stagecard:hover { transform: translateY(-1px); border-color: var(--border-soft); box-shadow: var(--shadow-sm); }
.flx-stagecard.done { border-color: var(--brand); }
.flx-stagecard.done .flx-stage-badge { background: var(--brand-dark); color: #fff; }
.flx-stagecard.is-open {
  background: var(--brand-soft-bg);
  border-color: var(--brand);
  box-shadow: 0 0 0 1px var(--brand) inset;
}
.flx-stagecard .flx-stage-badge {
  width: 20px; height: 20px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 11px; font-weight: 700;
}
.flx-stagecard-label { font-size: 13.5px; font-weight: 600; color: var(--text-strong); }
.flx-stagecard-meta { font-size: 11.5px; color: var(--muted); }
.flx-stage-hint { font-size: 12.5px; color: var(--muted-2); font-style: italic; margin: 2px 0 0; }

/* Drill de etapa — misma entrada/salida que la vista día de MIR. */
.flx-drill { animation: flx-drill-in .22s cubic-bezier(0.2, 0.7, 0.2, 1); }
@keyframes flx-drill-in {
  from { opacity: 0; transform: scale(0.96) translateX(14px); }
  to   { opacity: 1; transform: none; }
}
.flx-drill.is-closing { animation: flx-drill-out .2s cubic-bezier(0.4, 0, 1, 1) forwards; }
@keyframes flx-drill-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: scale(0.96) translateX(14px); }
}

/* El botón de añadir vive en la esquina superior derecha de la etapa. */
.flx-section-actions { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* Conmutador de vista de las fases (Detalle | Flujo). */
.flx-viewswitch {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  border-radius: 999px;
  background: var(--surface-2);
}
.flx-viewswitch-btn {
  padding: 5px 11px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-size: 11.5px; font-weight: 600; font-family: inherit;
  cursor: pointer;
  transition: background .12s, color .12s;
}
.flx-viewswitch-btn:hover { color: var(--text); }
.flx-viewswitch-btn.is-on { background: var(--surface); color: var(--text-strong); box-shadow: var(--shadow-sm); }

/* ── (1) Disparadores: una tarjeta por disparador, un badge por palabra ──── */
.flx-disp-list { display: flex; flex-direction: column; gap: 10px; }
.flx-disp-card {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2);
  padding: 11px 13px;
}
.flx-disp-head { display: flex; align-items: center; gap: 9px; margin-bottom: 9px; }
.flx-disp-label { font-size: 12.5px; font-weight: 600; color: var(--text-strong); }
.flx-disp-mode {
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--badge-neutral-bg);
  color: var(--badge-neutral-fg);
  font-size: 10.5px; font-weight: 600;
}
.flx-disp-head .flx-icon-btn { margin-left: auto; }
.flx-kw-list { display: flex; flex-wrap: wrap; gap: 6px; }
.flx-kw {
  padding: 4px 10px;
  border: 1px solid var(--brand);
  border-radius: 999px;
  background: var(--brand-soft-bg);
  color: var(--brand-soft-fg);
  font-size: 11.5px; font-weight: 600;
}

/* ── (2) Vista "Flujo": cajas resumidas + conectores ─────────────────────── */
.flx-canvasflow { display: flex; flex-direction: column; align-items: center; gap: 0; padding: 4px 0; }
.flx-node-wrap { display: flex; flex-direction: column; align-items: center; width: 100%; max-width: 460px; }
.flx-node {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface);
  padding: 11px 14px;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform .12s ease, border-color .12s, box-shadow .12s;
}
.flx-node:hover { transform: translateY(-1px); border-color: var(--border-soft); box-shadow: var(--shadow-md); }
.flx-node.is-open { border-color: var(--brand); }
.flx-node.dragging { opacity: .45; }
.flx-node.over { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-soft-bg) inset; }
.flx-node.terminal { border-style: dashed; }
.flx-node-head { display: flex; align-items: center; gap: 9px; }
.flx-node-num {
  width: 22px; height: 22px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--brand-dark);
  color: #fff;
  font-size: 11px; font-weight: 700;
}
.flx-node-name { font-size: 13.5px; font-weight: 600; color: var(--text-strong); }
.flx-node-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }
.flx-node-tags:empty { margin-top: 0; }
.flx-node-detail { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.flx-node-facts { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }
.flx-connector { color: var(--muted-2); font-size: 17px; line-height: 1; padding: 5px 0; }

@media (max-width: 900px) {
  .flx-layout--collapsed { grid-template-columns: 1fr; }
  .flx-stagecard { min-width: 0; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — ajustes del Constructor (2026-08-03, 2ª pasada)
   ═══════════════════════════════════════════════════════════════════════════ */

/* (1) La toolbar heredaba el `border-bottom` de `.lc-topbar` (bandeja de
   Livechat) y, al quitarle el padding, la línea quedaba pegada al buscador.
   Aquí la separación la da el margen, no una regla. */
.flx-sidebar .lc-topbar { border-bottom: 0; margin: 0 0 10px; padding: 0; }

/* (2) Las dos columnas miden lo mismo: el grid estira la izquierda hasta el
   alto del canvas de la derecha y la lista scrollea por dentro. Por eso el
   sidebar deja de ser `sticky` — con altura completa no tiene nada que seguir. */
.flx-layout { align-items: stretch; }
.flx-sidebar {
  position: static;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.flx-sidebar .flx-flow-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
}
.flx-sidebar .flx-flow-list:hover { scrollbar-color: var(--control-border) transparent; }
.flx-sidebar .flx-flow-list::-webkit-scrollbar { width: 6px; }
.flx-sidebar .flx-flow-list::-webkit-scrollbar-thumb { background: transparent; border-radius: 6px; }
.flx-sidebar .flx-flow-list:hover::-webkit-scrollbar-thumb { background: var(--control-border); }
.flx-sidebar--collapsed .flx-rail-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* (3) Vista "Flujo": lienzo libre estilo n8n ─────────────────────────────── */
.flx-flow-help {
  font-size: 12px; color: var(--muted-2); font-style: italic;
  margin: 0 0 10px; line-height: 1.5;
}
.flx-flowcanvas {
  position: relative;
  width: 100%;
  overflow: auto;
  border: 1px dashed var(--border);
  border-radius: 14px;
  background:
    radial-gradient(circle, var(--border) 1px, transparent 1px) 0 0 / 22px 22px;
  /* `touch-action: none` es lo que permite arrastrar con el dedo sin que el
     navegador se quede el gesto para hacer scroll. */
  touch-action: none;
}
/* Las líneas van DEBAJO de las cajas y no interceptan el puntero. */
.flx-flowlinks {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
}
.flx-flowlinks line {
  stroke: var(--brand);
  stroke-width: 2;
  stroke-linecap: round;
  opacity: 0.55;
}
/* Dos clases de arista, y la diferencia importa: `seq` es lo que pasa SIEMPRE
   —el paso siguiente— y `gate`/`pick` es lo que pasa SI se cumple algo. Va
   punteado porque una línea sólida hacia una rama se lee como «y después esto»,
   que es justo lo que no es. */
.flx-flowlinks line.is-gate,
.flx-flowlinks line.is-pick {
  stroke: var(--chart-3);
  stroke-dasharray: 6 5;
  opacity: 0.8;
}
/* El color base de la punta vive más abajo, junto al resto del lienzo. */
.flx-flowarrow.is-gate, .flx-flowarrow.is-pick { fill: var(--chart-3); opacity: 0.8; }

/* Leyenda del lienzo. Sin ella el punteado y el rombo son decoración: nadie
   adivina que una línea discontinua significa «solo si se cumple la regla». */
.flx-flow-leyenda {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin: -4px 0 10px;
  font-size: .68rem;
  color: var(--muted-2);
}
.flx-leg { display: inline-flex; align-items: center; gap: 6px; }
.flx-leg-line { width: 22px; height: 0; border-top: 2px solid var(--brand); opacity: .7; }
.flx-leg-line.is-gate { border-top-style: dashed; border-top-color: var(--chart-3); opacity: 1; }
.flx-leg-box { width: 13px; height: 13px; border-radius: 3px; border: 1px solid var(--border); }
.flx-leg-box.is-gate { border-color: var(--chart-3); border-left-width: 3px; }
.flx-leg-box.is-fin { border-style: dashed; border-color: var(--badge-success-fg); }
.flx-node--free {
  position: absolute;
  z-index: 1;
  cursor: grab;
  user-select: none;
  transform: none;              /* el hover no debe desplazar una caja posicionada */
}
.flx-node--free:hover { transform: none; }

/* ── Caja de BIFURCACIÓN ─────────────────────────────────────────────────────
   Una fase que parte el recorrido y una que no se pintaban EXACTAMENTE igual:
   la compuerta no aparecía por ninguna parte del lienzo. El borde izquierdo
   grueso es lo que las separa de un vistazo; el contenido de abajo dice según
   qué se parte y hacia dónde. */
.flx-node.is-gate {
  border-left: 3px solid var(--chart-3);
}
.flx-node-gate-ico {
  margin-left: auto;
  font-size: 15px;
  line-height: 1;
  color: var(--chart-3);
}
.flx-node-gate {
  margin-top: 8px;
  padding-top: 7px;
  border-top: 1px dashed var(--border);
}
.flx-node-gate-tit {
  font-size: .7rem;
  font-weight: 600;
  color: var(--chart-3);
}
.flx-node-gate-sub { font-size: .66rem; color: var(--muted-2); margin-top: 1px; }
.flx-node-gate-outs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
.flx-node-out {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--control-bg);
  font-size: .65rem;
  font-weight: 600;
  color: var(--text);
}

/* Cinta de rama: dice QUIÉN recorre la caja. En el lienzo, una fase de rama y
   una del tronco eran indistinguibles, y eso es lo primero que hay que saber. */
.flx-node-rama {
  display: inline-block;
  margin-bottom: 5px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--control-bg);
  font-size: .63rem;
  font-weight: 600;
  color: var(--muted);
}
/* El color va en el borde y NUNCA solo: la caja lleva siempre el nombre de la
   rama escrito, para que el mapa se siga leyendo sin distinguir colores. */
.flx-node.is-rama { border-top-width: 3px; }
.flx-node.is-rama.r0 { border-top-color: var(--chart-1); }
.flx-node.is-rama.r1 { border-top-color: var(--chart-2); }
.flx-node.is-rama.r2 { border-top-color: var(--chart-3); }
.flx-node.is-rama.r3 { border-top-color: var(--chart-4); }
.flx-node.is-rama.r4 { border-top-color: var(--chart-5); }
.flx-node.is-rama.r5 { border-top-color: var(--chart-6); }

.flx-tag.is-fin { background: var(--badge-success-bg); color: var(--badge-success-fg); }

.flx-node--free.dragging {
  cursor: grabbing;
  opacity: 1;
  z-index: 3;
  border-color: var(--brand);
  box-shadow: var(--shadow-lg);
}
.flx-node--free.is-open { z-index: 2; }


/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — palabras clave como badges y grupos (2026-08-03, 3ª pasada)
   ═══════════════════════════════════════════════════════════════════════════ */

/* Caja de etiquetas del formulario: las palabras ya encerradas y, detrás de
   ellas, el input donde se escribe la siguiente. Parece un solo campo. */
.flx-kwbox {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 8px 10px;
  border: 1px solid var(--control-border);
  border-radius: 10px;
  background: var(--control-bg);
  cursor: text;
  transition: border-color .12s, box-shadow .12s;
}
.flx-kwbox:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 2px var(--brand-soft-bg);
}
/* El input no lleva caja propia: la caja es `.flx-kwbox`. */
.flx-kw-input {
  flex: 1 1 90px;
  min-width: 90px;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--text);
  font-size: 12.5px;
  font-family: inherit;
  padding: 3px 2px;
}
.flx-kw-input::placeholder { color: var(--muted-2); }

/* × de cada badge dentro del formulario. */
.flx-kw-x {
  margin-left: 6px;
  border: 0;
  background: transparent;
  color: inherit;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  opacity: .6;
  padding: 0;
  font-family: inherit;
}
.flx-kw-x:hover { opacity: 1; }

/* Botón secundario: crear un grupo extra es la excepción, no la acción
   principal, así que va en fantasma y no compite con "+ Palabra clave". */
.flx-btn-ghost {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px;
  border: 1px solid var(--control-border);
  border-radius: 9px;
  background: transparent;
  color: var(--muted);
  font-size: 12px; font-weight: 600; font-family: inherit;
  cursor: pointer; white-space: nowrap;
  transition: background .12s, color .12s, border-color .12s;
}
.flx-btn-ghost:hover { background: var(--control-bg); color: var(--text); border-color: var(--border-soft); }
.flx-btn-ghost:disabled { opacity: .55; cursor: not-allowed; }

/* ── Documento JSON del flujo (importar/exportar todas las fases+ramas) ── */
.flx-doc-modal { max-width: 760px; width: 92vw; }
.flx-doc-help { margin: 0 0 12px; color: var(--muted); font-size: 12.5px; line-height: 1.5; }
.flx-doc-textarea {
  width: 100%; min-height: 260px; resize: vertical;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12.5px; line-height: 1.5; tab-size: 2; white-space: pre;
}
.flx-doc-parse-err {
  margin-top: 10px; padding: 8px 12px; border-radius: 9px;
  background: var(--badge-danger-bg); color: var(--badge-danger-fg);
  border: 1px solid var(--badge-danger-fg); font-size: 12.5px;
}
.flx-doc-plan {
  margin-top: 14px; padding: 12px 14px; border-radius: 12px;
  background: var(--surface-2); border: 1px solid var(--border);
}
.flx-doc-plan-head { font-size: 12.5px; color: var(--muted); margin-bottom: 10px; }
.flx-doc-plan-head b { color: var(--text-strong); }
.flx-doc-resumen { display: flex; flex-wrap: wrap; gap: 8px; }
.flx-doc-chip {
  display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 600;
  background: var(--badge-neutral-bg); color: var(--badge-neutral-fg);
}
.flx-doc-chip.is-crear   { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.flx-doc-chip.is-upd     { background: var(--badge-info-bg);    color: var(--badge-info-fg); }
.flx-doc-chip.is-ret     { background: var(--badge-danger-bg);  color: var(--badge-danger-fg); }
.flx-doc-chip.is-desact  { background: var(--badge-warn-bg);    color: var(--badge-warn-fg); }
.flx-doc-chip.is-reord   { background: var(--badge-purple-bg);  color: var(--badge-purple-fg); }
.flx-doc-block { margin-top: 12px; }
.flx-doc-block-title {
  font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted-2); margin-bottom: 6px;
}
.flx-doc-block ul { margin: 0; padding-left: 0; list-style: none; }
.flx-doc-block li { font-size: 12.5px; color: var(--text); line-height: 1.6; }
.flx-doc-errores li { color: var(--badge-danger-fg); }
.flx-doc-errores code, .flx-doc-acciones code {
  font-size: 11.5px; padding: 1px 5px; border-radius: 5px;
  background: var(--control-bg); color: var(--muted);
}
.flx-doc-avisos li { color: var(--badge-warn-fg); }
.flx-doc-acciones li { display: flex; align-items: center; gap: 8px; }
.flx-doc-acc-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: var(--muted-2); }
.flx-doc-acc-dot.is-crear      { background: var(--badge-success-fg); }
.flx-doc-acc-dot.is-actualizar { background: var(--badge-info-fg); }
.flx-doc-acc-dot.is-retirar    { background: var(--badge-danger-fg); }
.flx-doc-acc-dot.is-desactivar { background: var(--badge-warn-fg); }
.flx-doc-acc-tipo { font-weight: 600; text-transform: capitalize; }
.flx-doc-footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.flx-doc-footer-right { display: flex; align-items: center; gap: 10px; }

/* ── Drawer del asistente IA (chat del constructor de Flujos) ── */
.flx-btn-ia { color: var(--brand); border-color: var(--brand-soft-bg, var(--control-border)); }
.flx-btn-ia:hover { background: var(--brand-soft-bg); color: var(--brand-dark, var(--brand)); }

.flx-chat-backdrop { position: fixed; inset: 0; background: var(--scrim); z-index: 1400; }
.flx-chat-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 1401;
  width: min(440px, 96vw); display: flex; flex-direction: column;
  background: var(--surface-overlay); border-left: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
}
.flx-chat-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px; border-bottom: 1px solid var(--border); flex: 0 0 auto;
}
.flx-chat-title { font-weight: 700; color: var(--text-strong); }
.flx-chat-head-actions { display: flex; align-items: center; gap: 8px; }
.flx-chat-nuevo { padding: 4px 10px; font-size: 11.5px; }
.flx-chat-unavailable {
  margin: 12px 16px 0; padding: 10px 12px; border-radius: 10px; font-size: 12.5px;
  background: var(--badge-warn-bg); color: var(--badge-warn-fg);
  border: 1px solid var(--badge-warn-fg);
}
/* Aviso de apertura: ventana flotante CENTRADA sobre el chat, no una franja de
   lado a lado. El `box-shadow` gigante hace de velo — el split tiene
   `overflow:hidden`, así que se recorta a la caja del chat. */
.flx-chat-notice {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 3;
  width: min(420px, calc(100% - 48px)); max-height: calc(100% - 48px); overflow-y: auto;
  padding: 18px 20px; border-radius: 14px;
  background: var(--surface-overlay); border: 1px solid var(--border);
  box-shadow: var(--shadow-lg), 0 0 0 9999px var(--scrim);
}
.flx-chat-notice-title { font-weight: 700; color: var(--text-strong); margin-bottom: 8px; }
.flx-chat-notice-list { margin: 0 0 12px; padding-left: 18px; display: flex; flex-direction: column; gap: 7px; }
.flx-chat-notice-list li { font-size: 12.5px; line-height: 1.5; color: var(--text); }
.flx-chat-notice-ok { width: 100%; }
.flx-chat-body { flex: 1 1 auto; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.flx-chat-empty { color: var(--muted); font-size: 13px; line-height: 1.6; text-align: center; margin: auto 8px; }

.flx-chat-msg { display: flex; flex-direction: column; gap: 6px; max-width: 90%; }
.flx-chat-msg.is-user { align-self: flex-end; align-items: flex-end; }
.flx-chat-msg.is-bot { align-self: flex-start; align-items: flex-start; }
.flx-chat-msg.is-sys { align-self: center; align-items: center; max-width: 96%; }
.flx-chat-bubble {
  padding: 9px 13px; border-radius: 14px; font-size: 13.5px; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word;
}
.is-user .flx-chat-bubble { background: var(--brand); color: #fff; border-bottom-right-radius: 4px; }
.is-bot .flx-chat-bubble { background: var(--surface-2); color: var(--text); border: 1px solid var(--border); border-bottom-left-radius: 4px; }
.is-sys .flx-chat-bubble { background: transparent; color: var(--muted); font-size: 12px; font-style: italic; text-align: center; }
.flx-chat-tools { font-size: 11px; color: var(--muted-2); font-style: italic; }
.flx-chat-thinking { padding: 9px 13px; border-radius: 14px; background: var(--surface-2); color: var(--muted); font-size: 13px; }

.flx-chat-widget-btns { display: flex; gap: 8px; flex-wrap: wrap; }
.flx-chat-opciones { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.flx-chat-opcion {
  text-align: left; padding: 9px 12px; border-radius: 10px; cursor: pointer;
  background: var(--control-bg); border: 1px solid var(--control-border); color: var(--text);
  display: flex; flex-direction: column; gap: 2px; font-family: inherit; transition: background .12s, border-color .12s;
}
.flx-chat-opcion:hover { background: var(--control-bg-hover); border-color: var(--brand); }
.flx-chat-opcion-t { font-weight: 600; font-size: 13px; }
.flx-chat-opcion-d { font-size: 12px; color: var(--muted); }

.flx-chat-preview {
  padding: 11px 13px; border-radius: 12px; background: var(--surface-2);
  border: 1px solid var(--border); border-left: 3px solid var(--brand); width: 100%;
}
.flx-chat-preview-head { font-weight: 600; color: var(--text-strong); font-size: 13.5px; display: flex; align-items: center; gap: 8px; }
.flx-chat-preview-obj { font-size: 12.5px; color: var(--text); margin-top: 4px; line-height: 1.5; }
.flx-chat-preview-meta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; font-size: 11.5px; color: var(--muted); }

.flx-chat-plan { width: 100%; margin-top: 4px; }
.flx-chat-plan-actions { display: flex; gap: 8px; margin-top: 12px; }

.flx-chat-input { flex: 0 0 auto; display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--border); align-items: flex-end; }
.flx-chat-textarea { flex: 1 1 auto; resize: none; font-size: 13.5px; }
.flx-chat-send { flex: 0 0 auto; }

/* Grupo de palabras clave: acciones a la derecha y estado apagado. */
.flx-disp-actions { display: inline-flex; gap: 4px; margin-left: auto; }
.flx-disp-card.is-off { opacity: .6; border-style: dashed; }
.flx-disp-mode.is-off { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }

/* Los tooltips dentro de un `label`/`h3` no deben empujar el texto. */
.form-label .info-tooltip-wrapper,
.kn-wizard-head h3 .info-tooltip-wrapper,
.kn-check .info-tooltip-wrapper { margin-left: 6px; vertical-align: middle; }


/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — el lienzo como espacio de trabajo (2026-08-03, 4ª pasada)
   ═══════════════════════════════════════════════════════════════════════════ */

/* El alto lo fija el componente (crece con las cajas y con la que esté
   desplegada); aquí solo el suelo, para que nunca se sienta una tira. */
.flx-flowcanvas {
  min-height: 560px;
  resize: vertical;             /* el usuario puede darse más alto si quiere */
  background-size: 24px 24px;
}
/* Barras de scroll discretas: el lienzo se desplaza cuando se arrastra una
   caja fuera de la vista, no como forma normal de navegarlo. */
.flx-flowcanvas { scrollbar-width: thin; scrollbar-color: transparent transparent; }
.flx-flowcanvas:hover { scrollbar-color: var(--control-border) transparent; }
.flx-flowcanvas::-webkit-scrollbar { width: 8px; height: 8px; }
.flx-flowcanvas::-webkit-scrollbar-thumb { background: transparent; border-radius: 8px; }
.flx-flowcanvas:hover::-webkit-scrollbar-thumb { background: var(--control-border); }

/* Punta de flecha del conector: mismo color que la línea, sin borde. */
.flx-flowarrow { fill: var(--brand); opacity: 0.55; }

/* El detalle desplegado no puede crecer sin fin: pasado un límite scrollea
   dentro de la caja en vez de empujar el lienzo entero. */
.flx-node--free .flx-node-detail {
  max-height: 220px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--control-border) transparent;
}


/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — señales del arrastre en el lienzo (2026-08-03, 5ª pasada)
   Antes no había NADA: soltabas a ciegas y solo lo notabas por el resultado.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Caja de destino: anillo de marca + fondo teñido + un salto de escala. */
.flx-node--free.drop-target {
  border-color: var(--brand);
  background: var(--brand-soft-bg);
  box-shadow: 0 0 0 3px var(--brand-soft-bg), var(--shadow-md);
  transform: scale(1.03);
  transition: transform .12s ease, box-shadow .12s ease, background .12s ease;
}
/* La que se arrastra, cuando hay destino bajo el puntero: se inclina un poco
   para que se lea que las dos están emparejadas. */
.flx-node--free.will-swap {
  border-color: var(--brand);
  transform: rotate(-1.5deg);
}

/* Etiqueta flotante sobre la caja de destino. */
.flx-swap-hint {
  position: absolute;
  top: -11px;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--brand-dark);
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .02em;
  box-shadow: var(--shadow-sm);
  pointer-events: none;
  z-index: 4;
}

/* Mientras se arrastra, el cursor de agarre manda en todo el lienzo: si solo
   estuviera en la caja, al adelantarse el puntero volvía a la flecha. */
.flx-flowcanvas:has(.flx-node--free.dragging) { cursor: grabbing; }
.flx-flowcanvas:has(.flx-node--free.dragging) .flx-node--free { cursor: grabbing; }


/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — formularios de una pantalla con ajustes avanzados plegados
   ═══════════════════════════════════════════════════════════════════════════ */

/* Disclosure de "Ajustes avanzados": no es un botón de acción, así que no
   compite con Guardar. Deja claro que hay algo debajo sin llamar la atención. */
.flx-adv-toggle {
  align-self: flex-start;
  margin-top: 4px;
  padding: 6px 2px;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: color .12s;
}
.flx-adv-toggle:hover { color: var(--brand); }

.flx-adv-box {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2);
}
/* Subtítulo dentro de avanzados (agrupa los topes de abandono). */
.flx-adv-sub {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted-2);
  padding-top: 4px;
  border-top: 1px solid var(--border);
}
.flx-adv-box .kn-form-group:first-child { margin-top: 0; }

/* La caja de palabras clave dentro del formulario de proceso hereda el mismo
   aspecto que la del formulario de grupos: es el mismo gesto. */
.kn-form-group > .flx-kwbox { margin-top: 2px; }


/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — lienzo con envoltorio, menú ⋮ por caja y acuse del intercambio
   (2026-08-03, 6ª pasada)
   ═══════════════════════════════════════════════════════════════════════════ */

/* El scroll y el marco pasan al ENVOLTORIO. Antes scrolleaba el mismo elemento
   que pintaba la cuadrícula, y el fondo de un contenedor con scroll se queda
   en la parte visible: al desplazarse a la derecha las cajas aparecían fuera
   del espacio de trabajo, sobre el fondo del panel. */
.flx-flowwrap {
  position: relative;
  width: 100%;
  overflow: auto;
  min-height: 560px;
  resize: vertical;             /* el usuario puede darse más alto si quiere */
  border: 1px dashed var(--border);
  border-radius: 14px;
  /* El arrastre con el dedo es del lienzo, no del scroll del navegador. */
  touch-action: none;
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
}
.flx-flowwrap:hover { scrollbar-color: var(--control-border) transparent; }
.flx-flowwrap::-webkit-scrollbar { width: 8px; height: 8px; }
.flx-flowwrap::-webkit-scrollbar-thumb { background: transparent; border-radius: 8px; }
.flx-flowwrap:hover::-webkit-scrollbar-thumb { background: var(--control-border); }

/* El lienzo ya solo dibuja: crece con las cajas (ancho/alto en línea desde el
   componente) y nunca es más estrecho que su envoltorio. */
.flx-flowcanvas {
  overflow: visible;
  border: 0;
  border-radius: 0;
  resize: none;
  min-height: 0;
  min-width: 100%;
}
.flx-flowwrap:has(.flx-node--free.dragging) { cursor: grabbing; }

/* ── Menú ⋮ dentro de una caja del lienzo ───────────────────────────────── */
.flx-node-menu {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 5;
}
/* Deja sitio al ⋮ para que no se le monte encima al nombre de la fase. */
.flx-node--free .flx-node-head { padding-right: 26px; }
/* Discreto en reposo; se afirma al pasar por encima de la caja. */
.flx-node-menu .mm-dots { opacity: .45; cursor: pointer; }
.flx-node--free:hover .flx-node-menu .mm-dots,
.flx-node-menu .mm-dots:hover { opacity: 1; }
/* El menú tiene que quedar por encima de SU PROPIO backdrop (`.flx-menu-backdrop`,
   z-index 59), que es el que cierra el desplegable al pulsar fuera. Con un valor
   por debajo, el backdrop —transparente y a pantalla completa— se queda delante
   de las opciones: el menú se ve, pero los clics de "Editar" y "Eliminar" caen en
   el fondo y solo lo cierran. El valor base de `.mm-menu-pop` (60) ya lo resuelve;
   aquí solo hace falta no bajarlo. */
.flx-node-menu .mm-menu-pop { z-index: 61; }

/* Además hay que levantar la CAJA. `.flx-node--free` tiene z-index propio, así que
   crea contexto de apilado: sin esto, el menú de una caja se dibuja por debajo de
   las cajas vecinas y de la que esté desplegada (z-index 2), y las de al lado
   tapan las opciones aunque el clic sí llegue. */
.flx-node--free:has(.mm-menu-pop) { z-index: 8; }

/* ── Acuse del intercambio ──────────────────────────────────────────────────
   Al soltar, las dos cajas cambian de contenido y se separan un poco. Sin este
   destello el gesto terminaba en una escena idéntica a la de antes y no había
   forma de saber que había pasado algo. */
.flx-node--free.just-swapped {
  /* El desplazamiento se anima para que se vea el "se apartan", no un salto. */
  transition: left .28s cubic-bezier(.22,.61,.36,1),
              top  .28s cubic-bezier(.22,.61,.36,1);
  animation: flx-swap-flash .9s ease-out 1;
}
@keyframes flx-swap-flash {
  0%   { border-color: var(--brand); box-shadow: 0 0 0 4px var(--brand-soft-bg), var(--shadow-md); }
  60%  { border-color: var(--brand); box-shadow: 0 0 0 4px var(--brand-soft-bg), var(--shadow-md); }
  100% { border-color: var(--border); box-shadow: none; }
}
/* Respeta a quien pide menos movimiento: el color sigue avisando, el
   desplazamiento no se anima. */
@media (prefers-reduced-motion: reduce) {
  .flx-node--free.just-swapped { transition: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — Auditoría con toolbar estándar y subtabs (2026-08-03, 7ª pasada)
   ═══════════════════════════════════════════════════════════════════════════ */

/* Las subtabs usan el cromo compartido `.cc-subtabs` de Centro de Control; aquí
   solo hace falta separarlas de la toolbar que va justo debajo. */
.metrics-content > .cc-subtabs { margin-bottom: 12px; }

/* Las dos fechas del popover de filtros, en una línea con su flecha. */
.flx-daterange {
  display: flex;
  align-items: center;
  gap: 8px;
}
.flx-daterange .crm-input { flex: 1 1 0; min-width: 0; }

/* Etiqueta pequeña pegada a un dato dentro de una celda (fase desactivada,
   recordatorios con error). No es un badge de estado de la fila. */
.flx-tag-inline {
  margin-left: 6px;
  font-size: 10px;
  vertical-align: middle;
}

/* ── Bitácora ──────────────────────────────────────────────────────────────
   Es la tabla más densa del módulo: muchas filas cortas. La fecha en tabular
   para que las columnas no bailen, y el detalle JSON acotado para que una
   entrada larga no estire la tabla entera. */
.flx-log-when {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: 12px;
}
.flx-log-detail {
  max-width: 340px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px;
  color: var(--muted);
}

/* La nota de alcance va pegada a la toolbar: dice qué flujo y qué periodo se
   está mirando, y eso manda sobre TODOS los bloques de la pestaña. */
.metrics-content > .flx-rep-note { margin: 10px 0 16px; }

/* El botón de exportar de Tickets dentro de la toolbar estándar: el ancla del
   desplegable tiene que ser el contenedor, no la barra entera. */
.tb-actions .tickets-export { position: relative; }


/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — Personas: elección de vista, y paginación de las tablas largas
   (2026-08-03, 8ª pasada)
   ═══════════════════════════════════════════════════════════════════════════ */

/* Dos tarjetas grandes: "quién se quedó por el camino" y "todos los contactos".
   Son preguntas distintas y una necesita elegir flujo, así que se preguntan
   antes de traer nada en vez de apilar las dos tablas. */
/* Flex centrado, NO grid de 1fr: con `1fr` las dos tarjetas se estiran hasta
   el ancho del panel y quedan como dos franjas. Aquí el ancho lo fija la
   tarjeta y el hueco sobrante se reparte a los lados. */
.flx-pick-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
  margin-top: 24px;
}
.flx-pick-card {
  flex: 0 0 auto;
  width: 300px;
  max-width: 100%;
  min-height: 132px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding: 22px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface);
  text-align: left;
  font-family: inherit;
  cursor: pointer;
  transition: border-color .14s, box-shadow .14s, transform .14s;
}
.flx-pick-card:hover {
  border-color: var(--brand);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
/* El detalle largo vive en el tooltip del título: la tarjeta es una elección
   entre dos, no un sitio donde leer dos párrafos. */
.flx-pick-title {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 15px;
  font-weight: 700;
  color: var(--text-strong);
}
/* Condición de uso de la tarjeta (p. ej. "hay que elegir un flujo"): se lee
   ANTES de pulsar, no después en forma de pantalla vacía. */
.flx-pick-meta {
  margin-top: auto;
  padding-top: 6px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted-2);
}

/* Elegir flujo antes del listado. Caja centrada y estrecha: es una decisión
   de una sola cosa, no una pantalla que deba ocupar todo el ancho. */
.flx-choose-flow {
  width: 100%;
  max-width: 420px;
  margin: 24px auto 0;
  padding: 20px;
  border: 1px dashed var(--border);
  border-radius: 14px;
  background: var(--surface-2);
}
.flx-choose-flow-title {
  margin: 0 0 12px;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--text-strong);
}
/* Buscador propio, dentro de la caja: filtra la lista de flujos por nombre.
   No es el buscador de la toolbar (ese busca filas de una tabla que aquí
   todavía no existe). */
.flx-choose-flow-search { width: 100%; margin-bottom: 10px; }
/* Lista vertical: los flujos se leen en columna, uno debajo de otro. Antes
   era una fila de píldoras que envolvía, y con varios flujos había que
   rastrear el nombre en zigzag. */
.flx-choose-flow-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 320px;
  overflow-y: auto;
}
.flx-choose-flow-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--control-border);
  border-radius: 10px;
  background: var(--control-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: border-color .12s, background .12s;
}
.flx-choose-flow-item:hover { border-color: var(--brand); background: var(--control-bg-hover); }

/* La caja pasó de ser el paso previo de UNA tabla a ser la puerta de entrada de
   toda la pestaña Auditoría, así que se ensancha y explica POR QUÉ hay que
   elegir: sin el motivo, la pantalla se lee como una pared. */
.flx-choose-flow { max-width: 520px; text-align: center; }
.flx-choose-flow-ico { font-size: 2rem; line-height: 1; margin-bottom: 8px; }
.flx-choose-flow-sub {
  margin: 0 0 14px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted);
}
/* El buscador y la lista vuelven a alinearse a la izquierda: centrar nombres de
   longitud distinta obliga a buscar el principio de cada uno en un sitio. */
.flx-choose-flow-search, .flx-choose-flow-list { text-align: left; }

/* ── Barra de ALCANCE de Auditoría ───────────────────────────────────────────
   Flujo y periodo mandan sobre las seis subpestañas, así que van a la vista y
   no dentro del desplegable de filtros de la toolbar, donde quedaban al mismo
   nivel que un filtro que solo afecta a una tabla. */
.flx-aud-scope {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px 18px;
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  background: var(--surface-2);
}
.flx-aud-scope-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.flx-aud-scope-lbl {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted-2);
}
/* El selector de flujo es lo que más se usa de la barra y el nombre de un
   proceso es largo: se le da sitio antes que a las fechas, que son fijas. */
.flx-aud-scope-item:first-child .crm-input { min-width: 240px; }
/* La casilla de «Alcance» tiene que alinearse con la base de los <select> de al
   lado, no con la etiqueta: sin esto sube y la fila se ve escalonada. */
.flx-aud-scope-check {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  margin: 0;
  font-size: 12.5px;
  white-space: nowrap;
}

/* Filtro de persona de la Bitácora. Pastilla y no <select>: se llega a ella
   desde el listado de Personas, y un desplegable con todos los contactos del
   proceso sería una lista larguísima para algo que casi nunca se elige aquí. */
.flx-aud-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  padding: 0 6px 0 12px;
  border-radius: 999px;
  background: var(--brand-soft-bg);
  color: var(--brand-soft-fg);
  font-size: 12.5px;
  font-weight: 600;
}
.flx-aud-pill-x {
  border: 0;
  background: transparent;
  color: inherit;
  font-size: 15px;
  line-height: 1;
  padding: 2px 6px;
  border-radius: 999px;
  cursor: pointer;
  opacity: .7;
}
.flx-aud-pill-x:hover { opacity: 1; background: var(--control-bg-hover); }

/* «Ver ›» de cada fila del listado: el único salto entre subpestañas. Discreto
   —es una columna más, no una acción destacada— pero con afordancia de botón. */
.flx-ver-bitacora {
  padding: 3px 10px;
  border: 1px solid var(--control-border);
  border-radius: 999px;
  background: var(--control-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color .12s, background .12s;
}
.flx-ver-bitacora:hover { border-color: var(--brand); background: var(--control-bg-hover); }

/* ── Paginador de las tablas largas ─────────────────────────────────────────
   El rango y el total salen de la BASE (`COUNT(*) OVER ()`), así que dice
   "51–100 de 4.312" de verdad, no "de las que traje". */
.flx-pager {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 2px 0;
}
.flx-pager-btn {
  padding: 7px 16px;
  border: 1px solid var(--control-border);
  border-radius: 999px;
  background: var(--control-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: border-color .12s, background .12s;
}
.flx-pager-btn:hover:not(:disabled) { border-color: var(--brand); background: var(--control-bg-hover); }
/* Deshabilitado sin desaparecer: los extremos del recorrido deben seguir
   ocupando su sitio para que la barra no salte al llegar a la última página. */
.flx-pager-btn:disabled { opacity: .4; cursor: default; }
.flx-pager-info {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Aviso de que el buscador acota la tabla pero no las cifras agregadas. */
.flx-scope-warn {
  margin: -8px 0 14px;
  font-size: 12px;
  color: var(--badge-warn-fg);
  background: var(--badge-warn-bg);
  padding: 7px 12px;
  border-radius: 9px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   FLUJOS — casilla destacada del formulario de paso (2026-08-04, 9ª pasada)
   ═══════════════════════════════════════════════════════════════════════════ */

/* "Con este paso el proceso termina" vive FUERA de «Ajustes avanzados»: no es
   un matiz, es lo que hace que el flujo pueda cerrarse. Se le da caja propia
   para que no se lea como una nota al pie del bloque de objetivos. */
.kn-check.flx-check-main {
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2);
  font-weight: 600;
  color: var(--text-strong);
}


/* ══════════════════════════════════════════════════════════════════════════
   E-commerce · Categorías y carga masiva del catálogo
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Selector de categoría (components/category_select.rs) ────────────────
   El CAMPO hereda el chasis de `.idv-select`; el PANEL no puede: vive dentro
   del drawer de producto, que recorta con overflow y tiene su propio z-index,
   así que se monta en un Portal colgado del <body> con coordenadas fijas
   calculadas desde el campo. */
.ec-cat-panel {
  position: fixed;
  z-index: 2501;                 /* por encima de CUALQUIER modal o drawer del módulo */
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;                  /* mismo respiro que `.idv-select-panel` */
  box-sizing: border-box;
  max-height: 320px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-overlay);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.ec-cat-panel .idv-select-list { overflow-y: auto; }
/* Capa de descarte del panel: justo POR DEBAJO de él y por encima del backdrop del
   modal de producto (2000), que era quien se comía el clic. */
.ec-cat-dismiss {
  position: fixed; inset: 0; z-index: 2500;
  background: transparent; border: none; padding: 0;
}
.ec-cat-opt--raiz .idv-opt-label { font-weight: 600; color: var(--text-strong); }
.ec-cat-opt--hija { padding-left: 26px; }
.ec-cat-opt--hija .idv-opt-label::before {
  content: "└ ";
  color: var(--muted-2);
}
/* Categoría padre mostrada solo para dar contexto a una subcategoría que sí
   coincidió con la búsqueda: se ve, pero no se puede elegir. */
.ec-cat-panel .idv-select-opt.is-context {
  opacity: 0.6;
  cursor: default;
  background: transparent;
}
.ec-cat-panel .idv-select-opt.is-context:hover { background: transparent; }
.ec-cat-empty { color: var(--muted); font-style: italic; }
.ec-cat-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 6px;
  border-top: 1px solid var(--border-soft);
}
.ec-cat-pager-info { font-size: 0.78rem; color: var(--muted); }

/* ── Celda «Categoría» de la tabla de productos ───────────────────────────── */
.ec-cat-cell { display: inline-flex; align-items: center; gap: 4px; }
.ec-cat-raiz { color: var(--muted); }
.ec-cat-sep { color: var(--muted-2); }

/* ── Gestor del árbol (Configuración › Productos) ─────────────────────────── */
.ec-cat-manager { display: flex; flex-direction: column; gap: 10px; }
/* Buscador pegado a la izquierda y botón a la derecha. El buscador NO se estira:
   el árbol no necesita una barra de ancho completo. */
.ec-cat-bar { display: flex; align-items: center; gap: 8px; }
.ec-cat-search { width: 240px; max-width: 100%; flex: 0 0 auto; }
.ec-cat-bar > .pg-btn { margin-left: auto; }
.ec-cat-tree {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2);
  overflow: hidden;
}
.ec-cat-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
}
.ec-cat-row + .ec-cat-row { border-top: 1px solid var(--border-soft); }
.ec-cat-row--hija { padding-left: 28px; }
/* Fila de contexto: la categoría padre de una subcategoría encontrada al buscar. */
.ec-cat-row--ctx { opacity: 0.65; }
.ec-cat-bullet { color: var(--muted-2); font-size: 0.75rem; flex-shrink: 0; }
.ec-cat-name { color: var(--text-strong); font-weight: 500; }
.ec-cat-row--hija .ec-cat-name { font-weight: 400; }
/* Número de productos de la fila: empuja las acciones al extremo derecho. */
.ec-cat-count {
  margin-left: auto;
  min-width: 26px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--badge-neutral-bg);
  color: var(--badge-neutral-fg);
  font-size: 0.72rem;
  text-align: center;
}
.ec-cat-actions { display: flex; gap: 4px; flex-shrink: 0; }
/* Popover de alta/renombrado (Portal). Una fila nueva NO aparece en el árbol
   hasta que la categoría existe de verdad en la base. */
.ec-cat-pop { width: 260px; }
.ec-cat-pop-title { font-weight: 600; color: var(--text-strong); font-size: 0.85rem; }

/* ── Carga masiva ─────────────────────────────────────────────────────────── */
.ec-bulk-card { width: min(760px, 94vw); }
.ec-bulk-body {
  padding: 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-height: 68vh;
  overflow-y: auto;
}
.ec-bulk-steps { display: flex; flex-direction: column; gap: 16px; }
.ec-bulk-step { display: flex; flex-direction: column; gap: 8px; }
.ec-bulk-step-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  color: var(--text-strong);
}
/* Número del paso en pastilla: sustituye a la lista numerada y deja el título
   alineado con su tooltip. */
.ec-bulk-step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  background: var(--brand-dark);
  color: #fff;
  font-size: 0.72rem;
  flex-shrink: 0;
}
.ec-bulk-dl { display: flex; gap: 8px; }
.ec-bulk-summary {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border-soft);
}
.ec-bulk-delete {
  padding: 8px 10px;
  border: 1px solid var(--badge-danger-fg);
  border-radius: 10px;
  background: var(--badge-danger-bg);
}

/* Pestañas del resumen. Llevan el color del tipo y su propio contador: una fila
   de badges encima repetía exactamente la misma información. */
.ec-bulk-tabs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ec-bulk-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--control-bg);
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 500;
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.ec-bulk-tab:hover { background: var(--control-bg-hover); }
.ec-bulk-tab-n {
  min-width: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--surface-3);
  color: var(--text-strong);
  font-size: 0.76rem;
  font-weight: 700;
  text-align: center;
}
/* Activa: el color del tipo pasa al fondo y el contador se invierte para que
   siga leyéndose (es el dato importante de la pestaña). */
.ec-bulk-tab--new.is-active { background: var(--badge-success-bg); color: var(--badge-success-fg); border-color: var(--badge-success-fg); }
.ec-bulk-tab--upd.is-active { background: var(--badge-info-bg);    color: var(--badge-info-fg);    border-color: var(--badge-info-fg); }
.ec-bulk-tab--del.is-active { background: var(--badge-danger-bg);  color: var(--badge-danger-fg);  border-color: var(--badge-danger-fg); }
.ec-bulk-tab--err.is-active { background: var(--badge-warn-bg);    color: var(--badge-warn-fg);    border-color: var(--badge-warn-fg); }
.ec-bulk-tab--new .ec-bulk-tab-n { color: var(--badge-success-fg); }
.ec-bulk-tab--upd .ec-bulk-tab-n { color: var(--badge-info-fg); }
.ec-bulk-tab--del .ec-bulk-tab-n { color: var(--badge-danger-fg); }
.ec-bulk-tab--err .ec-bulk-tab-n { color: var(--badge-warn-fg); }
.ec-bulk-tab.is-active .ec-bulk-tab-n { background: var(--surface-overlay); }
.ec-bulk-nochange { margin-left: auto; font-size: 0.78rem; color: var(--muted-2); }

.ec-bulk-list {
  max-height: 240px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2);
  padding: 8px 10px;
}
.ec-bulk-list ul { margin: 0; padding-left: 16px; display: flex; flex-direction: column; gap: 6px; }
.ec-bulk-list li { font-size: 0.84rem; }
.ec-bulk-diff { color: var(--muted); font-size: 0.78rem; margin-top: 2px; }

/* Aviso inline de consecuencia (borrados, filas con error). */
.ec-warn { color: var(--badge-danger-fg); font-weight: 500; }

/* ── Centro de Control → Panel → Asistente (§4) ─────────────────────────────── */
.assistant-cards { display: flex; flex-direction: column; gap: 20px; }
.assistant-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 20px 22px;
}
.assistant-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.assistant-card-title { margin: 0 0 4px; font-size: 1.15rem; color: var(--text-strong); }
.assistant-card-desc { margin: 0; color: var(--muted); font-size: 0.9rem; max-width: 62ch; }
.assistant-motivo {
  margin: 10px 0 0; padding: 8px 12px; border-radius: 8px; font-size: 0.85rem;
  background: var(--badge-warn-bg); color: var(--badge-warn-fg);
}
.assistant-section { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--border-soft); }
.assistant-section-label {
  display: block; font-size: 0.8rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.03em; color: var(--muted-2); margin-bottom: 8px;
}
.assistant-scope { color: var(--text); font-size: 0.9rem; }
.assistant-scope strong { color: var(--text-strong); }

.assistant-caps { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 18px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--border-soft); }
.assistant-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.assistant-list li { font-size: 0.88rem; color: var(--text); }
.assistant-list-ok li::marker { content: "✓  "; color: var(--badge-success-fg); }
.assistant-list-no li::marker { content: "✕  "; color: var(--badge-warn-fg); }

.assistant-model-editor { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.assistant-provider-tag {
  font-size: 0.8rem; padding: 3px 10px; border-radius: 999px;
  background: var(--brand-soft-bg); color: var(--brand-soft-fg); font-weight: 600;
}
.assistant-model-row { display: flex; align-items: center; gap: 10px; }
.assistant-model-current { display: flex; flex-direction: column; }
.assistant-model-name {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9rem;
  color: var(--text-strong); background: var(--surface-2); padding: 4px 10px; border-radius: 6px;
}
.assistant-model-editor .form-input { max-width: 380px; }

.assistant-key-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.assistant-key-provider { max-width: 170px; }
.assistant-key-row .form-input[type="password"] { flex: 1; min-width: 220px; }

.assistant-tools-table { width: 100%; }
.assistant-tool-desc { color: var(--muted); font-size: 0.85rem; max-width: 40ch; }
.assistant-tools-table code {
  font-size: 0.82rem; background: var(--surface-2); padding: 2px 6px; border-radius: 5px; color: var(--text-strong);
}

.assistant-prompt-details { margin-top: 4px; }
.assistant-prompt-details summary { cursor: pointer; color: var(--brand); font-size: 0.88rem; }
.assistant-prompt-meta { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--muted); }
.assistant-prompt {
  margin: 10px 0 0; padding: 14px 16px; border-radius: 10px; max-height: 340px; overflow: auto;
  background: var(--surface-2); border: 1px solid var(--border-soft);
  color: var(--text); font-size: 0.82rem; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
}

.assistant-consumo { display: flex; gap: 26px; flex-wrap: wrap; }
.assistant-kpi { display: flex; flex-direction: column; }
.assistant-kpi-num { font-size: 1.5rem; font-weight: 700; color: var(--text-strong); }
.assistant-kpi-lbl { font-size: 0.8rem; color: var(--muted); }

/* ── Asistentes: LISTADO (tarjetas / lista) ──────────────────────────────────
   La vista de entrada ya no es un asistente sino el conjunto: una tarjeta por
   asistente con lo mínimo para elegir (nombre, descripción, módulo, estado).
   `auto-fill` a propósito: con un solo asistente la tarjeta conserva su ancho
   de tarjeta en vez de estirarse a todo el módulo. */
.asst-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 290px));
  gap: 14px; margin-top: 16px;
}
/* Tarjeta compacta: nombre arriba, módulo y estado abajo. Nada más — para
   elegir un asistente no hace falta su descripción, que vive en su detalle.
   Sin `aspect-ratio`: la altura la marca el contenido, así no queda un hueco
   muerto en el centro; el ancho lo acota la rejilla. */
.asst-mini {
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  padding: 14px; cursor: pointer;
  display: flex; flex-direction: column; gap: 12px;
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}
.asst-mini:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--brand);
}
.asst-mini-title {
  margin: 0; font-size: 0.92rem; color: var(--text-strong); font-weight: 650; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
/* Las dos etiquetas en UNA fila, ancladas abajo a la izquierda: nunca una
   debajo de otra. Si el nombre del módulo es largo, se recorta el chip —
   nunca se parte la fila. */
.asst-mini-foot { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; min-width: 0; }
.asst-mini-foot .asst-chip { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.asst-mini-foot .badge { flex: 0 0 auto; font-size: 0.7rem; padding: 2px 8px; }
.asst-chip {
  font-size: 0.72rem; padding: 3px 9px; border-radius: 999px;
  background: var(--surface-2); color: var(--muted); border: 1px solid var(--border-soft);
  white-space: nowrap;
}

.asst-list { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.asst-row {
  display: flex; align-items: center; gap: 14px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
  padding: 12px 16px;
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}
.asst-row:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); border-color: var(--brand); }
.asst-row-name { font-weight: 600; color: var(--text-strong); flex: 0 0 auto; }
.asst-row-desc {
  flex: 1; min-width: 0; font-size: 0.85rem; color: var(--muted);
  white-space: nowrap; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 48px), transparent 100%);
          mask-image: linear-gradient(90deg, #000 calc(100% - 48px), transparent 100%);
}

/* Acción de módulo bloqueada (crear asistentes aún no existe). El `title` y el
   cursor viven en el envoltorio: un <button disabled> no dispara ninguno. */
.asst-locked-wrap { display: inline-flex; cursor: not-allowed; }
.asst-btn-locked { pointer-events: none; opacity: 0.45; filter: grayscale(1); }

/* ── Asistentes: DETALLE ──────────────────────────────────────────────────
   Jerarquía en tres escalones y nada más: (1) volver, (2) identidad —quién es
   este asistente—, (3) cuatro secciones cerradas con su resumen. Al entrar no
   hay ningún cuerpo abierto: se elige qué mirar en vez de recibirlo todo. */
.asst-detail { display: block; }
.asst-back { margin-bottom: 18px; }

.asst-id { padding-bottom: 18px; border-bottom: 1px solid var(--border); margin-bottom: 22px; }
.asst-id-name {
  margin: 0 0 10px; font-size: 1.45rem; line-height: 1.25; font-weight: 700;
  color: var(--text-strong); letter-spacing: -0.01em;
}
.asst-id-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Cada sección es una caja propia con aire alrededor: separadas, no apiladas
   contra una línea. */
.asst-acc {
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  margin-bottom: 12px; overflow: hidden;
}
.asst-acc-head {
  width: 100%; display: flex; align-items: center; gap: 10px;
  background: none; border: 0; padding: 16px 20px; cursor: pointer; text-align: left;
  color: var(--text-strong); font: inherit; transition: background-color 0.14s ease;
}
.asst-acc-head:hover { background: var(--surface-2); }
.asst-acc.is-open > .asst-acc-head { border-bottom: 1px solid var(--border-soft); }
.asst-acc-title { font-size: 1rem; font-weight: 650; flex: 0 0 auto; }
.asst-acc-chevron { color: var(--muted); font-size: 0.8rem; transition: transform 0.15s ease; }
.asst-acc-chevron.is-open { transform: rotate(90deg); }
/* Resumen: un dato corto, nunca un texto que haya que recortar. Sin máscara —
   degradaba el final aunque el texto cupiera entero y se leía como cortado. */
.asst-acc-sum {
  margin-left: auto; min-width: 0; font-size: 0.82rem; color: var(--muted);
  font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.asst-acc-body { display: none; padding: 20px; }
.asst-acc.is-open > .asst-acc-body { display: block; }

/* Dentro de una sección: etiqueta pequeña arriba, valor debajo. Dos columnas
   como máximo — el ancho de la pantalla no es el ancho de lectura. */
.asst-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px 28px; }
.asst-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.asst-field--wide { grid-column: 1 / -1; }
.asst-field-label {
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--muted-2);
}
.asst-field-val { font-size: 0.92rem; color: var(--text); }
.asst-prose { max-width: 78ch; line-height: 1.6; color: var(--muted); }

.asst-prompt-block { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--border-soft); }
.asst-prompt-head { display: flex; align-items: center; gap: 10px; }
.asst-prompt-head .assistant-prompt-meta { font-size: 0.78rem; }
.asst-prompt-block .assistant-prompt { margin-top: 12px; max-height: 260px; }
.asst-prompt-expand {
  margin-left: auto; background: var(--control-bg); border: 1px solid var(--control-border);
  color: var(--text); border-radius: 8px; width: 28px; height: 28px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; font-size: 0.9rem;
}
.asst-prompt-expand:hover { background: var(--control-bg-hover); color: var(--brand); }
/* Segmented archivo/vista previa: mismo control que el de la toolbar, en pequeño. */
.asst-seg { margin-left: 4px; }
.asst-seg .tb-viewseg-btn { width: 26px; height: 26px; }

/* Vista previa del system prompt: el Markdown ya aplicado dentro de la MISMA caja
   que el archivo, así alternar no mueve nada de sitio. */
.asst-md {
  white-space: normal; font-family: inherit; font-size: 0.88rem; line-height: 1.6;
}
.asst-md > :first-child { margin-top: 0; }
.asst-md > :last-child { margin-bottom: 0; }
.asst-md h1, .asst-md h2, .asst-md h3, .asst-md h4 {
  color: var(--text-strong); line-height: 1.3; margin: 18px 0 8px; font-weight: 650;
}
.asst-md h1 { font-size: 1.15rem; }
.asst-md h2 { font-size: 1.05rem; }
.asst-md h3 { font-size: 0.96rem; }
.asst-md h4 { font-size: 0.9rem; color: var(--text); }
.asst-md p { margin: 0 0 10px; }
.asst-md ul, .asst-md ol { margin: 0 0 10px; padding-left: 22px; }
.asst-md li { margin-bottom: 4px; }
.asst-md code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.82em;
  background: var(--surface-3, var(--control-bg)); padding: 1px 5px; border-radius: 5px;
}
.asst-md pre {
  background: var(--control-bg); border: 1px solid var(--border-soft); border-radius: 8px;
  padding: 10px 12px; overflow-x: auto; margin: 0 0 12px;
}
.asst-md pre code { background: none; padding: 0; }
.asst-md blockquote {
  margin: 0 0 12px; padding: 2px 0 2px 12px;
  border-left: 3px solid var(--border); color: var(--muted);
}
.asst-md a { color: var(--brand); }
.asst-md hr { border: 0; border-top: 1px solid var(--border-soft); margin: 16px 0; }
.asst-md table { border-collapse: collapse; margin: 0 0 12px; font-size: 0.85em; }
.asst-md th, .asst-md td { border: 1px solid var(--border-soft); padding: 5px 9px; text-align: left; }
.asst-md th { background: var(--surface-2); color: var(--text-strong); }

.asst-prompt-modal { display: flex; flex-direction: column; }
.asst-prompt-full { max-height: 72vh; margin: 0 22px 20px; }

/* Barra de scroll estilo sidebar: fina, sin carril reservado (no empuja el
   contenido) y visible solo al pasar el puntero. */
.asst-scroll { scrollbar-width: thin; scrollbar-color: transparent transparent; }
.asst-scroll:hover { scrollbar-color: var(--control-border) transparent; }
.asst-scroll::-webkit-scrollbar { width: 8px; }
.asst-scroll::-webkit-scrollbar-track { background: transparent; }
.asst-scroll::-webkit-scrollbar-thumb {
  background: transparent; border: 2px solid transparent; background-clip: padding-box;
  border-radius: 8px; transition: background-color 0.2s ease;
}
.asst-scroll:hover::-webkit-scrollbar-thumb { background: var(--control-border); background-clip: padding-box; }

/* ── Chat del asistente de Flujos: vista PARTIDA (estado en vivo + chat, §operaciones) ── */
.flx-chat-split {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1401;
  width: min(1180px, 96vw); height: min(88vh, 900px);
  display: flex; overflow: hidden;
  background: var(--surface-overlay); border: 1px solid var(--border);
  border-radius: 16px; box-shadow: var(--shadow-lg);
}
/* El chat es el protagonista: ocupa el ancho entero del split. El panel de fases
   entra por la derecha solo cuando se pide (cerrado por defecto). */
.flx-chat-drawer {
  position: relative; top: auto; right: auto; bottom: auto; z-index: auto;
  width: auto; box-shadow: none; border-left: none;
  flex: 1 1 auto; min-width: 0;
}
/* Colapsa animando `flex-basis`: el panel va siempre montado y solo cambia de
   ancho, así hay algo que animar (montarlo/desmontarlo no se puede transicionar).
   `visibility` corta el foco y el lector de pantalla cuando está cerrado. */
.flx-chat-live {
  flex: 0 0 0; width: 0; min-width: 0; overflow: hidden; visibility: hidden; opacity: 0;
  display: flex; flex-direction: column;
  background: var(--surface-2); border-left: 1px solid transparent;
  transition: flex-basis 0.24s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.18s ease, visibility 0s linear 0.24s, border-left-color 0.24s ease;
}
.flx-chat-live.is-open {
  flex-basis: 360px; visibility: visible; opacity: 1;
  border-left-color: var(--border);
  transition: flex-basis 0.24s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.18s ease 0.06s, visibility 0s, border-left-color 0.24s ease;
}
/* El contenido no se comprime mientras el panel se abre: se mantiene a su ancho
   final y el recorte del panel lo va revelando. */
.flx-chat-live > * { flex: 0 0 auto; width: 360px; }
.flx-chat-live > .flx-live-body { flex: 1 1 auto; }
.flx-live-toggle { padding: 4px 10px; font-size: 11.5px; }
.flx-live-toggle.is-active { background: var(--brand-soft-bg); color: var(--brand-soft-fg); border-color: transparent; }
.flx-live-head {
  flex: 0 0 auto; padding: 14px 16px; border-bottom: 1px solid var(--border);
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
}
.flx-live-titlewrap { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.flx-live-title { font-weight: 700; color: var(--text-strong); font-size: 15px; }
.flx-live-badge { font-size: 11px; padding: 2px 8px; border-radius: 999px; font-weight: 600; }
.flx-live-badge.is-on { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.flx-live-badge.is-draft { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }
.flx-live-body { flex: 1 1 auto; overflow-y: auto; padding: 14px 16px; }
.flx-live-empty { color: var(--muted); font-size: 13px; text-align: center; margin-top: 24px; line-height: 1.6; }
.flx-live-muted { color: var(--muted); font-size: 12.5px; }
.flx-live-sub { margin-top: 8px; font-weight: 700; color: var(--text-strong); font-size: 12.5px; }
.flx-live-list { margin: 4px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 3px; }
.flx-live-list li { font-size: 12.5px; color: var(--text); }
.flx-live-tag { font-size: 10.5px; padding: 1px 7px; border-radius: 999px; background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }
.flx-live-fase-obj { margin-top: 6px; font-size: 12.5px; color: var(--text); line-height: 1.45; }

/* Tarjeta de operación propuesta por el asistente (crear/editar/eliminar/reordenar). */
.flx-chat-op { background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px; padding: 12px; margin-top: 4px; }
.flx-chat-op-head { font-weight: 700; color: var(--text-strong); font-size: 13px; }
.flx-chat-op-resumen { margin-top: 4px; font-size: 12.5px; color: var(--text); line-height: 1.45; }
.flx-chat-op-detalle { margin: 8px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 3px; }
.flx-chat-op-detalle li { font-size: 12px; color: var(--muted); }
.flx-chat-op-actions { display: flex; gap: 8px; margin-top: 10px; }
.flx-chat-op-done { margin-top: 8px; font-size: 12.5px; font-weight: 600; color: var(--badge-success-fg); }

@media (max-width: 860px) {
  .flx-chat-split { flex-direction: column; width: 96vw; height: 92vh; }
  /* En vertical el panel va DEBAJO del chat: colapsa en alto, no en ancho. */
  .flx-chat-live { width: auto; border-left: none; border-top: 1px solid transparent; }
  .flx-chat-live > * { width: auto; }
  .flx-chat-live.is-open { flex-basis: 45%; border-left-color: transparent; border-top-color: var(--border); }
  .flx-chat-drawer { flex: 1 1 auto; }
}

/* Vista "Flujo" del panel en vivo del chat: cadena vertical de nodos con conectores. */
.flx-live-flow { display: flex; flex-direction: column; align-items: stretch; }
.flx-live-node {
  background: var(--surface); border: 1px solid var(--brand); border-radius: 10px;
  padding: 10px 12px; box-shadow: var(--shadow-sm);
}
/* La tarjeta entera abre la fase. */
.flx-live-node { cursor: pointer; transition: border-color 0.16s ease, box-shadow 0.16s ease; }
.flx-live-node:hover { border-color: var(--brand); }
.flx-live-node-head { display: flex; align-items: center; gap: 8px; width: 100%; }
.flx-live-node-chevron { margin-left: auto; color: var(--muted); font-size: 11px; transition: transform 0.15s ease; }
.flx-live-node-chevron.is-open { transform: rotate(90deg); }
.flx-live-node-detail { display: none; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border-soft); }
.flx-live-node.is-open > .flx-live-node-detail { display: block; }
.flx-live-node.is-open { border-color: var(--brand); box-shadow: var(--shadow-md); }
.flx-live-node-num {
  width: 22px; height: 22px; border-radius: 6px; flex: 0 0 auto;
  background: var(--brand-soft-bg); color: var(--brand-soft-fg); font-weight: 700; font-size: 12px;
  display: inline-flex; align-items: center; justify-content: center;
}
.flx-live-node-name { font-weight: 600; color: var(--text-strong); font-size: 13px; }
.flx-live-node-tags { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.flx-live-node-modo { font-size: 10.5px; padding: 1px 7px; border-radius: 999px; background: var(--surface-2); color: var(--muted); }
.flx-live-tag.is-term { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.flx-live-node-obj { font-size: 10.5px; color: var(--muted); }
.flx-live-arrow { text-align: center; color: var(--brand); font-size: 16px; line-height: 1.2; margin: 3px 0; }

/* Lista de operaciones propuestas (varias tarjetas) + «Aplicar todo». */
.flx-chat-ops { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.flx-chat-ops-all { display: flex; justify-content: flex-end; }
.flx-chat-op-dismiss { margin-top: 8px; font-size: 12.5px; color: var(--muted); font-style: italic; text-decoration: line-through; }

/* ══════════════════════════════════════════════════════════════════════════
   Bitácora de Flujos — línea de tiempo legible
   ══════════════════════════════════════════════════════════════════════════
   Rejilla de tres columnas (cuándo · llave · texto) para que la del medio se lea
   como una línea de tiempo vertical.

   La LLAVE es lo que hace el trabajo: los eventos que el agente marcó en una
   misma acción caen en el mismo segundo, y presentarlos como cuatro filas
   sueltas hace pensar que dudó cuatro veces. Agrupados bajo una llave con
   «N a la vez», se leen como UN movimiento.

   Todo con tokens: nada de hex literales (ver CLAUDE.md, «Theming»). */

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

.flx-bit-dia {
    margin: 18px 0 6px;
    font-size: .78rem;
    font-weight: 600;
    color: var(--muted);
    letter-spacing: .04em;
}
.flx-bit-dia:first-child { margin-top: 4px; }

.flx-bit-grupo {
    display: grid;
    grid-template-columns: 92px 14px 1fr;
    align-items: stretch;
    padding: 2px 0;
}

.flx-bit-cuando {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    padding-right: 10px;
    gap: 2px;
}
.flx-bit-hora {
    font-variant-numeric: tabular-nums;   /* las horas se alinean en columna */
    font-size: .82rem;
    color: var(--muted);
}
/* Solo aparece cuando hay más de un evento: en un grupo de uno no aporta nada
   y añadiría una línea a cada fila. */
.flx-bit-juntos {
    font-size: .68rem;
    color: var(--brand);
    white-space: nowrap;
}

/* La llave se dibuja SOLO en los grupos de más de uno (`is-multi`). En un grupo
   suelto una línea vertical sugeriría una agrupación que no existe. */
.flx-bit-llave { position: relative; }
.flx-bit-grupo.is-multi .flx-bit-llave::before {
    content: "";
    position: absolute;
    left: 5px;
    top: 10px;
    bottom: 10px;
    width: 2px;
    border-radius: 1px;
    background: var(--brand);
    opacity: .45;
}

.flx-bit-eventos { display: flex; flex-direction: column; gap: 6px; padding: 4px 0; }

.flx-bit-ev { display: flex; align-items: flex-start; gap: 8px; }

/* El icono va coloreado por TONO para ver de un vistazo si la conversación fue
   bien sin leer nada. */
.flx-bit-icono {
    flex: 0 0 auto;
    width: 20px; height: 20px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    font-size: .72rem;
    line-height: 1;
}
.flx-bit-icono.is-ok   { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.flx-bit-icono.is-warn { background: var(--badge-warn-bg);    color: var(--badge-warn-fg); }
.flx-bit-icono.is-bad  { background: var(--badge-danger-bg);  color: var(--badge-danger-fg); }
.flx-bit-icono.is-info { background: var(--badge-info-bg);    color: var(--badge-info-fg); }

.flx-bit-txt { min-width: 0; }
.flx-bit-frase {
    color: var(--text-strong);
    font-size: .88rem;
    line-height: 1.35;
}
.flx-bit-quien { color: var(--muted); font-size: .76rem; }
/* De quién es el hecho. Solo sale SIN filtro de persona: ahí la línea de tiempo
   mezcla a todo el mundo y sin el nombre es una lista de cosas que le pasaron a
   alguien. Con la persona ya elegida se repetiría en cada línea sin añadir nada. */
.flx-bit-contacto { color: var(--text-strong); font-weight: 600; }
.flx-bit-fase { color: var(--muted-2); }

.flx-bit-toggle { margin-left: auto; }

@media (max-width: 640px) {
    /* Por debajo de este ancho la columna de la hora se come el texto. Se apila y
       la llave desaparece: el rótulo «N a la vez» sigue diciendo lo mismo. */
    .flx-bit-grupo { grid-template-columns: 1fr; }
    .flx-bit-cuando { flex-direction: row; align-items: baseline; justify-content: flex-start; gap: 8px; }
    .flx-bit-llave { display: none; }
}

/* ── Constructor de Flujos: capacidades por fase ────────────────────────────
   Ayudas y selectores añadidos con las herramientas por fase, la compuerta de
   enrutamiento, los canales y la derivación entre procesos. Solo tokens. */

/* Párrafo de ayuda bajo un control. Existe porque varias de estas opciones
   NO hacen lo que su etiqueta sugiere a primera vista: «Activable por el
   agente» parecía una preferencia de presentación y en realidad el motor
   rechaza el arranque; una casilla sin esta explicación se marca al revés. */
.flx-hint {
    margin: 6px 0 0;
    font-size: .78rem;
    line-height: 1.45;
    color: var(--muted);
}

.flx-canales {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    padding: 8px 10px;
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    background: var(--surface-2);
}

/* Lista de herramientas del agente con sus dos casillas. Se hace scrollable en
   vez de crecer: el catálogo pasa de treinta tools y empujaría el pie del modal
   fuera de la pantalla, dejando el botón de guardar inalcanzable. */
.flx-tools-grid {
    display: flex;
    flex-direction: column;
    max-height: 260px;
    overflow-y: auto;
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    background: var(--surface-2);
}

.flx-tool-row {
    display: grid;
    grid-template-columns: 1fr auto auto auto;
    align-items: center;
    gap: 10px;
    padding: 6px 10px;
    border-bottom: 1px solid var(--border-soft);
}
.flx-tool-row:last-child { border-bottom: none; }

.flx-tool-name {
    font-size: .82rem;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.flx-tool-mcp {
    font-size: .7rem;
    color: var(--muted-2);
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--control-bg);
}
/* Las del propio módulo de Flujos: se puede sugerir pero NO esconder. Se dice
   en vez de dejar el hueco vacío, que se lee como un fallo de render. */
.flx-tool-lock {
    font-size: .7rem;
    color: var(--muted-2);
    font-style: italic;
}

@media (max-width: 560px) {
    .flx-tool-row { grid-template-columns: 1fr; gap: 2px; }
}

/* Peldaño del temporizador: los minutos delante y el mensaje al lado, porque se
   lee «a los 30 minutos, esto». Apilados se pierde de vista a qué umbral
   corresponde cada texto en cuanto hay tres. */
.flx-seg-fields {
    display: grid;
    grid-template-columns: 90px auto 1fr;
    align-items: start;
    gap: 6px;
    flex: 1;
    min-width: 0;
}
.flx-seg-unidad {
    padding-top: 7px;
    font-size: .75rem;
    color: var(--muted);
    white-space: nowrap;
}

@media (max-width: 560px) {
    .flx-seg-fields { grid-template-columns: 90px auto; }
    .flx-seg-fields > .flx-obj-desc-input { grid-column: 1 / -1; }
}

/* ── Compuerta de enrutamiento ──────────────────────────────────────────────
   Una regla se lee como una frase: «Si <campo> <operador> <valor> → <rama>».
   Por eso va en una sola línea de grid y no en un formulario de cuatro campos
   apilados: el orden de lectura ES el significado. */
.flx-enr-reglas {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 6px;
}

.flx-enr-regla {
    display: grid;
    grid-template-columns: auto minmax(120px, 1.4fr) minmax(110px, 1fr) minmax(90px, 1fr) auto minmax(110px, 1fr) auto;
    align-items: center;
    gap: 6px;
    padding: 6px 8px;
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    background: var(--surface-2);
}

.flx-enr-si,
.flx-enr-then {
    font-size: .78rem;
    color: var(--muted);
    white-space: nowrap;
}

/* Los inputs de la fila son estrechos a propósito: la fila entera tiene que
   caber sin scroll horizontal dentro del modal. */
.flx-enr-regla .form-input {
    min-width: 0;
    padding: 5px 7px;
    font-size: .78rem;
}

.flx-enr-probar {
    margin-top: 12px;
    padding: 10px;
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    background: var(--surface-2);
}

.flx-enr-probar-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 6px;
    font-size: .82rem;
    color: var(--text-strong);
}

.flx-btn-link {
    background: none;
    border: none;
    padding: 0;
    font-size: .76rem;
    color: var(--brand);
    cursor: pointer;
    text-decoration: underline;
}

.flx-mono {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .76rem;
}

/* Resultado de la simulación. Sin rama NO es un error —el contacto sigue el
   tronco, que es un desenlace legítimo—, así que el estado neutro es el
   normal y el verde solo aparece cuando de verdad enruta. */
.flx-enr-res {
    margin-top: 8px;
    padding: 8px 10px;
    border-radius: 8px;
    border: 1px solid var(--border-soft);
    background: var(--surface-3);
}
.flx-enr-res.is-ok {
    border-color: var(--badge-success-fg);
    background: var(--badge-success-bg);
}
.flx-enr-res-titular {
    font-size: .82rem;
    color: var(--text-strong);
    margin-bottom: 4px;
}
.flx-enr-res-lista {
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: .76rem;
}
.flx-enr-res-lista li {
    display: flex;
    gap: 6px;
    align-items: baseline;
    padding: 1px 0;
    color: var(--muted);
}
.flx-enr-res-si { color: var(--text); }
.flx-enr-res-no { color: var(--muted-2); }
.flx-enr-res-marca { font-weight: 700; }
.flx-enr-res-si .flx-enr-res-marca { color: var(--badge-success-fg); }
.flx-enr-res-no .flx-enr-res-marca { color: var(--muted-2); }

.flx-hint-warn { color: var(--badge-warn-fg); }
.flx-hint-error { color: var(--badge-danger-fg); }

/* ── Mapa de recorridos: el flujograma ──────────────────────────────────────
   Sustituye a las columnas de antes (`.flx-mapa-col*`, borradas). Aquellas
   contaban qué pasos tiene cada recorrido pero no DÓNDE se decide entre ellos:
   la compuerta era un renglón dentro de una caja y el punto de bifurcación —lo
   único que hay que entender de un flujo con ramas— no se veía.

   El scroll horizontal vive en `.flx-fc-ramas` y NO en la página: un flujo con
   seis ramas no debe empujar el ancho del panel entero. */
.flx-fc {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 6px 2px 14px;
}

/* Píldora de entrada: da un punto de partida al ojo. Sin ella el flujograma
   empieza en el aire y el primer paso se lee como si fuera opcional. */
.flx-fc-inicio {
    padding: 5px 16px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-2);
    font-size: .76rem;
    color: var(--muted);
}

/* Conector vertical con punta. Es un elemento propio y no un ::after de la caja
   porque tiene que poder ir también DESPUÉS de un rombo, que no es una caja. */
.flx-fc-conector {
    position: relative;
    width: 1px;
    height: 22px;
    background: var(--border);
    flex: 0 0 auto;
}
.flx-fc-conector::after {
    content: "";
    position: absolute;
    bottom: -1px;
    left: 50%;
    transform: translateX(-50%);
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 6px solid var(--border);
}

.flx-fc-espina {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
}

.flx-fc-paso {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    max-width: 320px;
}
.flx-fc-paso .flx-mapa-caja { width: 100%; }

/* La compuerta: rombo a la izquierda, salidas a la derecha. En fila para que la
   condición quede pegada a su destino — apiladas, con tres o cuatro reglas ya no
   se sabe cuál lleva a dónde. */
.flx-fc-gate {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 10px;
    width: 100%;
}

/* Rombo por `clip-path` y no por `rotate(45deg)`: rotar obliga a contrarrotar el
   texto y a fijar el tamaño a mano, y el rótulo de la compuerta es de largo
   variable. El padding lateral generoso es lo que mantiene el texto dentro de la
   parte ancha de la figura. */
.flx-fc-rombo {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    min-width: 132px;
    padding: 20px 34px;
    clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
    background: var(--badge-info-bg);
    border: 0;
    text-align: center;
}
.flx-fc-rombo.is-ancho {
    min-width: 300px;
    max-width: 460px;
    padding: 22px 56px;
    background: var(--brand-soft-bg);
}
.flx-fc-rombo-txt {
    font-size: .72rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--badge-info-fg);
    word-break: break-word;
}
.flx-fc-rombo.is-ancho .flx-fc-rombo-txt { color: var(--brand); }

/* Salvedad del rombo de criterio: se dibuja en un punto, pero el motor no lo ata
   a ninguno. Sin esta línea el flujograma promete un orden que no existe. */
.flx-fc-nota {
    max-width: 62ch;
    margin: 8px 0 0;
    font-size: .68rem;
    line-height: 1.45;
    color: var(--muted-2);
    text-align: center;
    font-style: italic;
}

.flx-fc-salidas {
    display: flex;
    flex-direction: column;
    gap: 5px;
    min-width: 0;
    flex: 1 1 auto;
}
.flx-fc-salida {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    font-size: .7rem;
}
.flx-fc-cond {
    padding: 2px 7px;
    border-radius: 6px;
    background: var(--control-bg);
    color: var(--muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .66rem;
}
/* La salida por defecto no es una regla: es lo que pasa cuando NINGUNA casa.
   Punteada para que no se lea como una condición más de la lista. */
.flx-fc-cond.is-defecto {
    background: transparent;
    border: 1px dashed var(--border);
    font-family: inherit;
    font-style: italic;
}
.flx-fc-flecha { color: var(--muted-2); }
.flx-fc-destino {
    padding: 2px 8px;
    border-radius: 999px;
    font-weight: 600;
    background: var(--control-bg);
    color: var(--text);
}
.flx-fc-aviso {
    font-size: .7rem;
    color: var(--badge-warn-fg);
}

/* Ramas, una columna cada una. */
.flx-fc-ramas {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    gap: 14px;
    margin-top: 18px;
    width: 100%;
    overflow-x: auto;
    padding-bottom: 6px;
}
.flx-fc-rama {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 0 0 auto;
    min-width: 236px;
    max-width: 300px;
    padding: 10px;
    border: 1px solid var(--border-soft);
    border-top-width: 3px;
    border-radius: 10px;
    background: var(--surface-2);
}
.flx-fc-rama-head { width: 100%; }
.flx-fc-rama-nom {
    font-size: .84rem;
    font-weight: 600;
    color: var(--text-strong);
}
/* Quién decide la rama es una diferencia de COMPORTAMIENTO, no de presentación:
   una que decide la compuerta se toma en un punto exacto; una que elige el
   agente puede tomarla desde el primer turno. */
.flx-fc-rama-quien {
    display: inline-block;
    margin-top: 3px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: .64rem;
    background: var(--control-bg);
    color: var(--muted-2);
}
.flx-fc-rama-quien.is-gate {
    background: var(--badge-info-bg);
    color: var(--badge-info-fg);
}
.flx-fc-rama-crit {
    width: 100%;
    margin: 6px 0 10px;
    font-size: .7rem;
    line-height: 1.4;
    color: var(--muted);
}
.flx-fc-rama-crit.is-warn { color: var(--badge-warn-fg); }
.flx-fc-derivacion {
    width: 100%;
    margin-top: 8px;
    padding-top: 6px;
    border-top: 1px solid var(--border-soft);
    font-size: .7rem;
    color: var(--brand);
}

/* Color por rama. Sale de la rampa de gráficas para que las ramas sigan al tema
   y, sobre todo, a las paletas de accesibilidad: si el único distintivo de una
   rama fuera un color fijo, un daltónico no las separaría. Por eso el color
   NUNCA va solo — la rama siempre lleva además su nombre escrito. */
.flx-fc-rama.r0 { border-top-color: var(--chart-1); }
.flx-fc-rama.r1 { border-top-color: var(--chart-2); }
.flx-fc-rama.r2 { border-top-color: var(--chart-3); }
.flx-fc-rama.r3 { border-top-color: var(--chart-4); }
.flx-fc-rama.r4 { border-top-color: var(--chart-5); }
.flx-fc-rama.r5 { border-top-color: var(--chart-6); }
.flx-fc-destino.r0, .flx-node-out.r0 { color: var(--chart-1); }
.flx-fc-destino.r1, .flx-node-out.r1 { color: var(--chart-2); }
.flx-fc-destino.r2, .flx-node-out.r2 { color: var(--chart-3); }
.flx-fc-destino.r3, .flx-node-out.r3 { color: var(--chart-4); }
.flx-fc-destino.r4, .flx-node-out.r4 { color: var(--chart-5); }
.flx-fc-destino.r5, .flx-node-out.r5 { color: var(--chart-6); }
/* r9 = la regla apunta a una rama que no existe: un callejón sin salida. */
.flx-fc-destino.r9, .flx-node-out.r9 {
    background: var(--badge-danger-bg);
    color: var(--badge-danger-fg);
}

.flx-mapa-caja {
    position: relative;
    padding: 7px 9px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    cursor: pointer;
    transition: border-color .12s, box-shadow .12s;
}
.flx-mapa-caja.is-fin { border-color: var(--badge-success-fg); }

/* Afordancia del doble clic. Sin ella el gesto es invisible: nada en una caja
   plana dice que responde. El `:focus-visible` es la misma señal para quien
   navega con el tabulador, que es como se llega aquí sin ratón.

   Cada figura la señala como puede: la caja tiene borde, la cabecera de rama y
   el rombo NO (el rombo se recorta con `clip-path`, que además se come cualquier
   `outline`), así que esos dos cambian de fondo. */
.flx-mapa-caja:hover { border-color: var(--brand); }
.flx-mapa-caja:focus-visible,
.flx-fc-rama-head:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
}
.flx-fc-rama-head {
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 6px;
    transition: background .12s;
}
.flx-fc-rama-head:hover { background: var(--control-bg-hover); }
.flx-fc-rombo.is-clic { cursor: pointer; transition: background .12s; }
.flx-fc-rombo.is-clic:hover { background: var(--brand-soft-bg); }
.flx-fc-rombo.is-clic:hover .flx-fc-rombo-txt { color: var(--brand); }

/* ── Ficha del mapa ───────────────────────────────────────────────────────
   Solo lectura; el botón «Editar» del pie aparece únicamente con permiso y
   abre el wizard de siempre. */
.flx-ficha { max-width: 620px; width: 100%; }
.flx-ficha-nombre {
    margin-left: 8px;
    font-weight: 500;
    color: var(--muted);
}
.flx-ficha-body {
    max-height: 62vh;
    overflow-y: auto;
    padding: 4px 2px 8px;
}
.flx-ficha-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-bottom: 10px;
}
/* Rejilla de dos columnas para los datos cortos: en una sola columna la ficha
   se alarga y obliga a desplazar para llegar a lo que sí es texto. */
.flx-ficha-datos {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 4px 16px;
    margin-bottom: 12px;
}
.flx-ficha-fila {
    display: flex;
    gap: 8px;
    font-size: .76rem;
}
.flx-ficha-lbl { color: var(--muted-2); flex: 0 0 auto; }
.flx-ficha-val { color: var(--text); word-break: break-word; }

.flx-ficha-sec {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--border-soft);
}
.flx-ficha-sec-tit {
    margin-bottom: 6px;
    font-size: .74rem;
    font-weight: 600;
    color: var(--text-strong);
}
.flx-ficha-texto {
    margin: 0;
    font-size: .78rem;
    line-height: 1.5;
    color: var(--text);
}
.flx-ficha-off { color: var(--muted-2); font-style: italic; }
.flx-ficha-lista,
.flx-ficha-salidas {
    margin: 0;
    padding-left: 18px;
    font-size: .76rem;
    line-height: 1.6;
    color: var(--text);
}
.flx-ficha-lista li { margin-bottom: 3px; }
.flx-ficha-url {
    display: block;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .68rem;
    color: var(--muted-2);
    word-break: break-all;
}
.flx-chip-bloq {
    background: var(--badge-danger-bg);
    color: var(--badge-danger-fg);
}

.flx-mapa-caja-nombre {
    font-size: .8rem;
    color: var(--text);
}
.flx-mapa-caja-num {
    display: inline-block;
    min-width: 17px;
    margin-right: 5px;
    padding: 0 4px;
    border-radius: 5px;
    background: var(--control-bg);
    color: var(--muted-2);
    font-size: .66rem;
    text-align: center;
}
.flx-mapa-caja-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 8px;
    margin-top: 3px;
    font-size: .68rem;
    color: var(--muted-2);
}
.flx-mapa-tag {
    padding: 0 6px;
    border-radius: 999px;
    background: var(--control-bg);
}
.flx-mapa-tag.is-fin {
    background: var(--badge-success-bg);
    color: var(--badge-success-fg);
}

.flx-mapa-vacia {
    font-size: .72rem;
    color: var(--muted-2);
    font-style: italic;
}

.flx-mapa-huerfanas {
    margin-top: 12px;
    padding: 10px;
    border: 1px dashed var(--badge-warn-fg);
    border-radius: 10px;
    background: var(--surface-2);
}
/* Las huérfanas NO se encadenan: no forman un recorrido, son pasos sueltos que
   el motor no le enseña a nadie. */
.flx-mapa-col-cuerpo {
    display: flex;
    flex-direction: column;
    padding: 10px 0 0;
    gap: 8px;
}
.flx-mapa-col-titulo {
    font-size: .84rem;
    font-weight: 600;
    color: var(--text-strong);
}

/* Compuerta que se evalúa y nunca reparte: el fallo no da error en ninguna parte,
   así que se señala en la propia fila o no se ve. */
.flx-badge-warn {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: .68rem;
    background: var(--badge-warn-bg);
    color: var(--badge-warn-fg);
}

@media (max-width: 720px) {
    .flx-enr-regla { grid-template-columns: 1fr; gap: 4px; }
    .flx-enr-si, .flx-enr-then { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   ACCESIBILIDAD — paletas alternativas
   ══════════════════════════════════════════════════════════════════════════════

   Se activan con una clase en <html> (`a11y-cvd`, `a11y-mono`, `a11y-acro`,
   `a11y-broma`) que pone el botón de accesibilidad del header y, antes del
   primer paint, el script de `index.html`. El daltonismo lleva ADEMÁS una
   clase de tipo (`a11y-cvd-deuteranopia`…): la base pone las superficies y el
   tipo, los colores de estado.

   ## Por qué CSS estático y no tokens inyectados desde Rust

   El resto de capas de color (tema del Marketplace, tema del usuario, preview)
   se inyectan como `<style>` desde `theme.rs`, y su espejo pre-paint en
   `index.html` repite la lógica. Para las paletas de accesibilidad eso obligaría
   a duplicar TRES tablas de color en JavaScript, con la garantía de que se
   separarían. Con una clase, el pre-paint son dos líneas y el color vive en un
   solo sitio: aquí.

   ## Especificidad

   Ganan a TODO, incluida la previsualización del editor de identidad:

     :root                                     0,1,0   panel · oscuro
     html.light-mode                           0,1,1   panel · claro
     html:root:root                            0,2,1   usuario · oscuro
     html.light-mode:root:root                 0,3,1   usuario · claro
     html:root:root:root:root                  0,4,1   preview · oscuro
     html.light-mode:root:root:root:root       0,5,1   preview · claro
     html.a11y-X:root:root:root:root:root      0,6,1   ← accesibilidad · oscuro
     html.a11y-X.light-mode:root:…:root        0,7,1   ← accesibilidad · claro

   Una necesidad de acceso no la puede pisar el tema que eligió el operador ni
   una previsualización a medio guardar. Por eso van arriba del todo.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── (a) Daltonismo — base común ─────────────────────────────────────────────────────────────
   Paleta de Okabe–Ito: colores distinguibles en deuteranopía, protanopía y
   tritanopía. El cambio que de verdad importa NO es que los colores sean más
   bonitos: es que «éxito» deja de ser verde y «error» rojo —el par que un
   deuteranope ve igual— y pasan a verde-azulado (#009E73) y bermellón
   (#D55E00), que se separan por tono Y por luminosidad.
   Referencia: Okabe & Ito, «Color Universal Design» (2008).                   */
html.a11y-cvd:root:root:root:root:root {
    --bg:               #12131a !important;
    --bg-2:             #171922 !important;
    --surface:          #1c1f2a !important;
    --surface-2:        #22252f !important;
    --surface-3:        #282c37 !important;
    --surface-overlay:  #1c1f2a !important;
    --panel:            #1c1f2a !important;
    --panel-2:          #22252f !important;
    --nav-bg:           #12131a !important;
    --nav-bg-2:         #171922 !important;
    --nav-border:       #3a3f4d !important;
    --nav-shade:        #0d0e14 !important;

    --control-bg:       #262a35 !important;
    --control-bg-hover: #30353f !important;
    --control-border:   #4a505f !important;
    --scrim:            rgba(0,0,0,0.66) !important;

    --text:             #eceff4 !important;
    --text-strong:      #ffffff !important;
    --muted:            #b6bcc9 !important;
    --muted-2:          #8f96a5 !important;

    --border:           #3a3f4d !important;
    --border-soft:      #2b2f3a !important;

    /* Azul Okabe–Ito aclarado: es el acento y necesita contraste sobre oscuro. */
    --brand:            #56b4e9 !important;
    --brand-dark:       #0072b2 !important;
    --brand-rgb:        86,180,233 !important;
    --brand-dark-rgb:   0,114,178 !important;
    --brand-soft-bg:    rgba(86,180,233,0.16) !important;
    --brand-soft-fg:    #8fcdf1 !important;
    --accent:           #56b4e9 !important;

    /* Verde-azulado y bermellón, NO verde y rojo. */
    --success:          #009e73 !important;
    --danger:           #d55e00 !important;
    --badge-success-bg: rgba(0,158,115,0.20) !important;
    --badge-success-fg: #4ecfa8 !important;
    --badge-warn-bg:    rgba(230,159,0,0.20) !important;
    --badge-warn-fg:    #f0b74a !important;
    --badge-danger-bg:  rgba(213,94,0,0.22) !important;
    --badge-danger-fg:  #f08a4b !important;
    --badge-info-bg:    rgba(0,114,178,0.22) !important;
    --badge-info-fg:    #6fbde8 !important;
    --badge-neutral-bg: rgba(150,156,168,0.20) !important;
    --badge-neutral-fg: #c3c8d2 !important;
    --badge-purple-bg:  rgba(204,121,167,0.20) !important;
    --badge-purple-fg:  #dfa3c4 !important;

    --tooltip-bg:       #f2f4f8 !important;
    --tooltip-fg:       #14161c !important;

    --chart-grid:        #2b2f3a !important;
    --chart-axis:        #b6bcc9 !important;
    --chart-legend:      #eceff4 !important;
    --chart-datalabel:   #ffffff !important;
}

html.a11y-cvd.light-mode:root:root:root:root:root {
    --bg:               #ffffff !important;
    --bg-2:             #f4f6f9 !important;
    --surface:          #ffffff !important;
    --surface-2:        #f1f4f8 !important;
    --surface-3:        #e7ebf1 !important;
    --surface-overlay:  #ffffff !important;
    --panel:            #ffffff !important;
    --panel-2:          #f1f4f8 !important;
    --nav-bg:           #ffffff !important;
    --nav-bg-2:         #f4f6f9 !important;
    --nav-border:       #c8cfd9 !important;
    --nav-shade:        #e7ebf1 !important;

    --control-bg:       #f1f4f8 !important;
    --control-bg-hover: #e4e9f0 !important;
    --control-border:   #b3bcc9 !important;
    --scrim:            rgba(16,20,28,0.42) !important;

    --text:             #14181f !important;
    --text-strong:      #000000 !important;
    --muted:            #4a5260 !important;
    --muted-2:          #656d7c !important;

    --border:           #c8cfd9 !important;
    --border-soft:      #dfe4ea !important;

    --brand:            #0072b2 !important;
    --brand-dark:       #004e7c !important;
    --brand-rgb:        0,114,178 !important;
    --brand-dark-rgb:   0,78,124 !important;
    --brand-soft-bg:    rgba(0,114,178,0.12) !important;
    --brand-soft-fg:    #004e7c !important;
    --accent:           #0072b2 !important;

    --success:          #006f51 !important;
    --danger:           #a34700 !important;
    --badge-success-bg: rgba(0,158,115,0.16) !important;
    --badge-success-fg: #00614a !important;
    --badge-warn-bg:    rgba(230,159,0,0.20) !important;
    --badge-warn-fg:    #7a5300 !important;
    --badge-danger-bg:  rgba(213,94,0,0.16) !important;
    --badge-danger-fg:  #8f3f00 !important;
    --badge-info-bg:    rgba(0,114,178,0.14) !important;
    --badge-info-fg:    #00456e !important;
    --badge-neutral-bg: rgba(90,98,112,0.14) !important;
    --badge-neutral-fg: #3c4351 !important;
    --badge-purple-bg:  rgba(204,121,167,0.16) !important;
    --badge-purple-fg:  #7d3d5e !important;

    --tooltip-bg:       #14181f !important;
    --tooltip-fg:       #ffffff !important;

    --chart-grid:        #dfe4ea !important;
    --chart-axis:        #4a5260 !important;
    --chart-legend:      #14181f !important;
    --chart-datalabel:   #ffffff !important;
}


/* ── Tipos de daltonismo ────────────────────────────────────────────────────────
   Bloques GENERADOS (tabla de color + contraste medido). Retocar un hex aquí es
   correcto; si se rehace la tabla entera, hay que rehacer también la comprobación
   de contraste que se describe abajo.

   La clase base `a11y-cvd` pone las SUPERFICIES (grises fríos, sin croma) y una
   clase de tipo `a11y-cvd-<tipo>` pone solo los ~21 tokens que codifican ESTADO.
   Separarlo evita repetir 46 tokens seis veces, que es donde se cuelan las
   erratas.

   Especificidad: la base va en (0,6,1)/(0,7,1) como el resto de accesibilidad y
   el tipo un escalón por encima —(0,7,1)/(0,8,1)—, para que gane sin depender del
   orden en la hoja, que es la regla de todo el sistema de temas.

   ## Qué eje usa cada familia, y por qué NO vale una sola paleta

   · protan / deutan → falla el eje ROJO-VERDE. Eje seguro: azul vs naranja.
     Protan además percibe el rojo MÁS OSCURO de lo que es: un rojo oscuro le
     llega casi negro, así que su «peligro» es naranja brillante y nunca burdeos.
   · tritan → falla el eje AZUL-AMARILLO. Eje seguro: rojo vs cian. Aquí la
     paleta Okabe-Ito (que se apoya en azul y naranja) es justo la MALA, y por eso
     no se reutiliza: el amarillo se sustituye por rosa.
   · «-anomalía» = el cono existe pero desplazado: conserva algo de tono, así que
     admite la paleta rica (verde-azulado, naranja, azul).
   · «-opia» = el cono NO existe: dentro del eje perdido el tono no separa nada,
     así que los estados se apoyan en DOS tonos del eje que sobrevive por DOS
     niveles de claridad.

   ## El texto del badge NO es el color del estado

   Se aclara (oscuro) u oscurece (claro) hasta 4.5:1 contra el fondo REAL del
   badge, que es el color del estado con alfa COMPUESTO sobre la superficie — no
   la superficie a secas. Sin componer, el cálculo promete un contraste que la
   pantalla no tiene: `#0072b2` como texto sobre `#1c1f2a` da 2.97:1 y se lee mal,
   que es justo lo que esta paleta viene a arreglar.
   ─────────────────────────────────────────────────────────────────────────────── */

/* deuteranomalia */
html.a11y-cvd-deuteranomalia:root:root:root:root:root:root {
    --brand:             #56b4e9 !important;
    --brand-dark:        #0072b2 !important;
    --brand-rgb:         86,180,233 !important;
    --brand-dark-rgb:    0,114,178 !important;
    --brand-soft-bg:     rgba(86,180,233,0.16) !important;
    --brand-soft-fg:     #56b4e9 !important;
    --accent:            #56b4e9 !important;
    --success:           #009e73 !important;
    --danger:            #d55e00 !important;
    --badge-success-bg:  rgba(0,158,115,0.22) !important;
    --badge-success-fg:  #33b18f !important;
    --badge-warn-bg:     rgba(230,159,0,0.22) !important;
    --badge-warn-fg:     #e69f00 !important;
    --badge-danger-bg:   rgba(213,94,0,0.22) !important;
    --badge-danger-fg:   #e08640 !important;
    --badge-info-bg:     rgba(0,114,178,0.22) !important;
    --badge-info-fg:     #59a3cd !important;
    --badge-neutral-bg:  rgba(150,156,168,0.22) !important;
    --badge-neutral-fg:  #a0a6b1 !important;
    --badge-purple-bg:   rgba(204,121,167,0.22) !important;
    --badge-purple-fg:   #d48db4 !important;

    --chart-1:           #56b4e9 !important;
    --chart-2:           #009e73 !important;
    --chart-3:           #f0b02a !important;
    --chart-4:           #cc79a7 !important;
    --chart-5:           #d55e00 !important;
    --chart-6:           #c0c5cd !important;
}
html.a11y-cvd-deuteranomalia.light-mode:root:root:root:root:root:root {
    --brand:             #0072b2 !important;
    --brand-dark:        #004e7c !important;
    --brand-rgb:         0,114,178 !important;
    --brand-dark-rgb:    0,78,124 !important;
    --brand-soft-bg:     rgba(0,114,178,0.12) !important;
    --brand-soft-fg:     #004e7c !important;
    --accent:            #0072b2 !important;
    --success:           #006f51 !important;
    --danger:            #a34700 !important;
    --badge-success-bg:  rgba(0,111,81,0.15) !important;
    --badge-success-fg:  #006f51 !important;
    --badge-warn-bg:     rgba(122,83,0,0.15) !important;
    --badge-warn-fg:     #7a5300 !important;
    --badge-danger-bg:   rgba(163,71,0,0.15) !important;
    --badge-danger-fg:   #a34700 !important;
    --badge-info-bg:     rgba(0,69,110,0.15) !important;
    --badge-info-fg:     #00456e !important;
    --badge-neutral-bg:  rgba(90,98,112,0.15) !important;
    --badge-neutral-fg:  #5a6270 !important;
    --badge-purple-bg:   rgba(125,61,94,0.15) !important;
    --badge-purple-fg:   #7d3d5e !important;

    --chart-1:           #0072b2 !important;
    --chart-2:           #00795c !important;
    --chart-3:           #c98600 !important;
    --chart-4:           #a3567f !important;
    --chart-5:           #8f2d00 !important;
    --chart-6:           #8e949c !important;
}

/* protanomalia */
html.a11y-cvd-protanomalia:root:root:root:root:root:root {
    --brand:             #56b4e9 !important;
    --brand-dark:        #0072b2 !important;
    --brand-rgb:         86,180,233 !important;
    --brand-dark-rgb:    0,114,178 !important;
    --brand-soft-bg:     rgba(86,180,233,0.16) !important;
    --brand-soft-fg:     #56b4e9 !important;
    --accent:            #56b4e9 !important;
    --success:           #009e73 !important;
    --danger:            #f07818 !important;
    --badge-success-bg:  rgba(0,158,115,0.22) !important;
    --badge-success-fg:  #33b18f !important;
    --badge-warn-bg:     rgba(240,194,68,0.22) !important;
    --badge-warn-fg:     #f0c244 !important;
    --badge-danger-bg:   rgba(240,120,24,0.22) !important;
    --badge-danger-fg:   #f2862f !important;
    --badge-info-bg:     rgba(0,114,178,0.22) !important;
    --badge-info-fg:     #59a3cd !important;
    --badge-neutral-bg:  rgba(150,156,168,0.22) !important;
    --badge-neutral-fg:  #a0a6b1 !important;
    --badge-purple-bg:   rgba(204,121,167,0.22) !important;
    --badge-purple-fg:   #d48db4 !important;

    --chart-1:           #56b4e9 !important;
    --chart-2:           #009e73 !important;
    --chart-3:           #f0c244 !important;
    --chart-4:           #cc79a7 !important;
    --chart-5:           #f07818 !important;
    --chart-6:           #c0c5cd !important;
}
html.a11y-cvd-protanomalia.light-mode:root:root:root:root:root:root {
    --brand:             #0072b2 !important;
    --brand-dark:        #004e7c !important;
    --brand-rgb:         0,114,178 !important;
    --brand-dark-rgb:    0,78,124 !important;
    --brand-soft-bg:     rgba(0,114,178,0.12) !important;
    --brand-soft-fg:     #004e7c !important;
    --accent:            #0072b2 !important;
    --success:           #006f51 !important;
    --danger:            #a85300 !important;
    --badge-success-bg:  rgba(0,111,81,0.15) !important;
    --badge-success-fg:  #006f51 !important;
    --badge-warn-bg:     rgba(107,74,0,0.15) !important;
    --badge-warn-fg:     #6b4a00 !important;
    --badge-danger-bg:   rgba(168,83,0,0.15) !important;
    --badge-danger-fg:   #a04f00 !important;
    --badge-info-bg:     rgba(0,69,110,0.15) !important;
    --badge-info-fg:     #00456e !important;
    --badge-neutral-bg:  rgba(90,98,112,0.15) !important;
    --badge-neutral-fg:  #5a6270 !important;
    --badge-purple-bg:   rgba(125,61,94,0.15) !important;
    --badge-purple-fg:   #7d3d5e !important;

    --chart-1:           #0072b2 !important;
    --chart-2:           #00795c !important;
    --chart-3:           #d4a100 !important;
    --chart-4:           #a3567f !important;
    --chart-5:           #9c3800 !important;
    --chart-6:           #8e949c !important;
}

/* deuteranopia */
html.a11y-cvd-deuteranopia:root:root:root:root:root:root {
    --brand:             #56b4e9 !important;
    --brand-dark:        #0072b2 !important;
    --brand-rgb:         86,180,233 !important;
    --brand-dark-rgb:    0,114,178 !important;
    --brand-soft-bg:     rgba(86,180,233,0.16) !important;
    --brand-soft-fg:     #56b4e9 !important;
    --accent:            #56b4e9 !important;
    --success:           #8fd3f4 !important;
    --danger:            #e07b18 !important;
    --badge-success-bg:  rgba(143,211,244,0.22) !important;
    --badge-success-fg:  #8fd3f4 !important;
    --badge-warn-bg:     rgba(245,215,110,0.22) !important;
    --badge-warn-fg:     #f5d76e !important;
    --badge-danger-bg:   rgba(224,123,24,0.22) !important;
    --badge-danger-fg:   #e58f3b !important;
    --badge-info-bg:     rgba(0,114,178,0.22) !important;
    --badge-info-fg:     #59a3cd !important;
    --badge-neutral-bg:  rgba(150,156,168,0.22) !important;
    --badge-neutral-fg:  #a0a6b1 !important;
    --badge-purple-bg:   rgba(154,167,184,0.22) !important;
    --badge-purple-fg:   #9fabbc !important;

    --chart-1:           #4a9fd4 !important;
    --chart-2:           #c2e8fb !important;
    --chart-3:           #ffe9a8 !important;
    --chart-4:           #12587f !important;
    --chart-5:           #e07b18 !important;
    --chart-6:           #8a8f99 !important;
}
html.a11y-cvd-deuteranopia.light-mode:root:root:root:root:root:root {
    --brand:             #0072b2 !important;
    --brand-dark:        #004e7c !important;
    --brand-rgb:         0,114,178 !important;
    --brand-dark-rgb:    0,78,124 !important;
    --brand-soft-bg:     rgba(0,114,178,0.12) !important;
    --brand-soft-fg:     #004e7c !important;
    --accent:            #0072b2 !important;
    --success:           #004e7c !important;
    --danger:            #a34700 !important;
    --badge-success-bg:  rgba(0,78,124,0.15) !important;
    --badge-success-fg:  #004e7c !important;
    --badge-warn-bg:     rgba(107,74,0,0.15) !important;
    --badge-warn-fg:     #6b4a00 !important;
    --badge-danger-bg:   rgba(163,71,0,0.15) !important;
    --badge-danger-fg:   #a34700 !important;
    --badge-info-bg:     rgba(0,45,79,0.15) !important;
    --badge-info-fg:     #002d4f !important;
    --badge-neutral-bg:  rgba(90,98,112,0.15) !important;
    --badge-neutral-fg:  #5a6270 !important;
    --badge-purple-bg:   rgba(76,86,102,0.15) !important;
    --badge-purple-fg:   #4c5666 !important;

    --chart-1:           #0f7fbe !important;
    --chart-2:           #7cc4ec !important;
    --chart-3:           #d9b300 !important;
    --chart-4:           #003a5c !important;
    --chart-5:           #8a3600 !important;
    --chart-6:           #8a9099 !important;
}

/* protanopia */
html.a11y-cvd-protanopia:root:root:root:root:root:root {
    --brand:             #56b4e9 !important;
    --brand-dark:        #0072b2 !important;
    --brand-rgb:         86,180,233 !important;
    --brand-dark-rgb:    0,114,178 !important;
    --brand-soft-bg:     rgba(86,180,233,0.16) !important;
    --brand-soft-fg:     #56b4e9 !important;
    --accent:            #56b4e9 !important;
    --success:           #8fd3f4 !important;
    --danger:            #f59237 !important;
    --badge-success-bg:  rgba(143,211,244,0.22) !important;
    --badge-success-fg:  #8fd3f4 !important;
    --badge-warn-bg:     rgba(245,215,110,0.22) !important;
    --badge-warn-fg:     #f5d76e !important;
    --badge-danger-bg:   rgba(245,146,55,0.22) !important;
    --badge-danger-fg:   #f59237 !important;
    --badge-info-bg:     rgba(0,114,178,0.22) !important;
    --badge-info-fg:     #59a3cd !important;
    --badge-neutral-bg:  rgba(150,156,168,0.22) !important;
    --badge-neutral-fg:  #a0a6b1 !important;
    --badge-purple-bg:   rgba(154,167,184,0.22) !important;
    --badge-purple-fg:   #9fabbc !important;

    --chart-1:           #4a9fd4 !important;
    --chart-2:           #c2e8fb !important;
    --chart-3:           #ffe9a8 !important;
    --chart-4:           #12587f !important;
    --chart-5:           #ef8118 !important;
    --chart-6:           #8a8f99 !important;
}
html.a11y-cvd-protanopia.light-mode:root:root:root:root:root:root {
    --brand:             #0072b2 !important;
    --brand-dark:        #004e7c !important;
    --brand-rgb:         0,114,178 !important;
    --brand-dark-rgb:    0,78,124 !important;
    --brand-soft-bg:     rgba(0,114,178,0.12) !important;
    --brand-soft-fg:     #004e7c !important;
    --accent:            #0072b2 !important;
    --success:           #004e7c !important;
    --danger:            #b35a00 !important;
    --badge-success-bg:  rgba(0,78,124,0.15) !important;
    --badge-success-fg:  #004e7c !important;
    --badge-warn-bg:     rgba(107,74,0,0.15) !important;
    --badge-warn-fg:     #6b4a00 !important;
    --badge-danger-bg:   rgba(179,90,0,0.15) !important;
    --badge-danger-fg:   #a15100 !important;
    --badge-info-bg:     rgba(0,45,79,0.15) !important;
    --badge-info-fg:     #002d4f !important;
    --badge-neutral-bg:  rgba(90,98,112,0.15) !important;
    --badge-neutral-fg:  #5a6270 !important;
    --badge-purple-bg:   rgba(76,86,102,0.15) !important;
    --badge-purple-fg:   #4c5666 !important;

    --chart-1:           #0f7fbe !important;
    --chart-2:           #7cc4ec !important;
    --chart-3:           #d9b300 !important;
    --chart-4:           #003a5c !important;
    --chart-5:           #a04200 !important;
    --chart-6:           #8a9099 !important;
}

/* tritanomalia */
html.a11y-cvd-tritanomalia:root:root:root:root:root:root {
    --brand:             #00b3bd !important;
    --brand-dark:        #007d85 !important;
    --brand-rgb:         0,179,189 !important;
    --brand-dark-rgb:    0,125,133 !important;
    --brand-soft-bg:     rgba(0,179,189,0.16) !important;
    --brand-soft-fg:     #00b3bd !important;
    --accent:            #00b3bd !important;
    --success:           #00a37a !important;
    --danger:            #e8455f !important;
    --badge-success-bg:  rgba(0,163,122,0.22) !important;
    --badge-success-fg:  #33b595 !important;
    --badge-warn-bg:     rgba(255,133,179,0.22) !important;
    --badge-warn-fg:     #ff85b3 !important;
    --badge-danger-bg:   rgba(232,69,95,0.22) !important;
    --badge-danger-fg:   #ee7487 !important;
    --badge-info-bg:     rgba(0,160,168,0.22) !important;
    --badge-info-fg:     #33b3b9 !important;
    --badge-neutral-bg:  rgba(150,156,168,0.22) !important;
    --badge-neutral-fg:  #a0a6b1 !important;
    --badge-purple-bg:   rgba(176,110,204,0.22) !important;
    --badge-purple-fg:   #c08bd6 !important;

    --chart-1:           #1c7f9c !important;
    --chart-2:           #00c2a0 !important;
    --chart-3:           #ffb08c !important;
    --chart-4:           #ff85b3 !important;
    --chart-5:           #e8455f !important;
    --chart-6:           #9aa3ad !important;
}
html.a11y-cvd-tritanomalia.light-mode:root:root:root:root:root:root {
    --brand:             #00747a !important;
    --brand-dark:        #00494d !important;
    --brand-rgb:         0,116,122 !important;
    --brand-dark-rgb:    0,73,77 !important;
    --brand-soft-bg:     rgba(0,116,122,0.12) !important;
    --brand-soft-fg:     #00494d !important;
    --accent:            #00747a !important;
    --success:           #00614a !important;
    --danger:            #a3122c !important;
    --badge-success-bg:  rgba(0,97,74,0.15) !important;
    --badge-success-fg:  #00614a !important;
    --badge-warn-bg:     rgba(161,31,92,0.15) !important;
    --badge-warn-fg:     #a11f5c !important;
    --badge-danger-bg:   rgba(163,18,44,0.15) !important;
    --badge-danger-fg:   #a3122c !important;
    --badge-info-bg:     rgba(0,95,102,0.15) !important;
    --badge-info-fg:     #005f66 !important;
    --badge-neutral-bg:  rgba(90,98,112,0.15) !important;
    --badge-neutral-fg:  #5a6270 !important;
    --badge-purple-bg:   rgba(107,45,133,0.15) !important;
    --badge-purple-fg:   #6b2d85 !important;

    --chart-1:           #046b8c !important;
    --chart-2:           #3aab90 !important;
    --chart-3:           #e08a4a !important;
    --chart-4:           #c2185b !important;
    --chart-5:           #a8102f !important;
    --chart-6:           #5f6672 !important;
}

/* tritanopia */
html.a11y-cvd-tritanopia:root:root:root:root:root:root {
    --brand:             #00c3cf !important;
    --brand-dark:        #00838c !important;
    --brand-rgb:         0,195,207 !important;
    --brand-dark-rgb:    0,131,140 !important;
    --brand-soft-bg:     rgba(0,195,207,0.16) !important;
    --brand-soft-fg:     #00c3cf !important;
    --accent:            #00c3cf !important;
    --success:           #7fe3d0 !important;
    --danger:            #d92040 !important;
    --badge-success-bg:  rgba(127,227,208,0.22) !important;
    --badge-success-fg:  #7fe3d0 !important;
    --badge-warn-bg:     rgba(255,158,196,0.22) !important;
    --badge-warn-fg:     #ff9ec4 !important;
    --badge-danger-bg:   rgba(217,32,64,0.22) !important;
    --badge-danger-fg:   #e66e83 !important;
    --badge-info-bg:     rgba(0,160,168,0.22) !important;
    --badge-info-fg:     #33b3b9 !important;
    --badge-neutral-bg:  rgba(150,156,168,0.22) !important;
    --badge-neutral-fg:  #a0a6b1 !important;
    --badge-purple-bg:   rgba(201,143,224,0.22) !important;
    --badge-purple-fg:   #cc95e2 !important;

    --chart-1:           #2f7f95 !important;
    --chart-2:           #7fe3d0 !important;
    --chart-3:           #ffb08c !important;
    --chart-4:           #c76a9a !important;
    --chart-5:           #d92040 !important;
    --chart-6:           #9aa3ad !important;
}
html.a11y-cvd-tritanopia.light-mode:root:root:root:root:root:root {
    --brand:             #00696e !important;
    --brand-dark:        #00424a !important;
    --brand-rgb:         0,105,110 !important;
    --brand-dark-rgb:    0,66,74 !important;
    --brand-soft-bg:     rgba(0,105,110,0.12) !important;
    --brand-soft-fg:     #00424a !important;
    --accent:            #00696e !important;
    --success:           #00524a !important;
    --danger:            #8f0f24 !important;
    --badge-success-bg:  rgba(0,82,74,0.15) !important;
    --badge-success-fg:  #00524a !important;
    --badge-warn-bg:     rgba(143,26,82,0.15) !important;
    --badge-warn-fg:     #8f1a52 !important;
    --badge-danger-bg:   rgba(143,15,36,0.15) !important;
    --badge-danger-fg:   #8f0f24 !important;
    --badge-info-bg:     rgba(0,74,82,0.15) !important;
    --badge-info-fg:     #004a52 !important;
    --badge-neutral-bg:  rgba(90,98,112,0.15) !important;
    --badge-neutral-fg:  #5a6270 !important;
    --badge-purple-bg:   rgba(92,36,122,0.15) !important;
    --badge-purple-fg:   #5c247a !important;

    --chart-1:           #05566e !important;
    --chart-2:           #4cc0a4 !important;
    --chart-3:           #f0a06a !important;
    --chart-4:           #a8306e !important;
    --chart-5:           #b00d33 !important;
    --chart-6:           #7b828c !important;
}

/* ── (b) Blanco y negro ─────────────────────────────────────────────────────────
   Sin tono: TODO se separa por luminosidad. Es la comprobación más dura que
   tiene el panel — lo que aquí deja de distinguirse es que dependía solo del
   color, y eso falla también para quien ve bien (una impresión en blanco y
   negro, una pantalla con poco brillo, sol directo sobre el móvil).
   Los seis estados se reparten la escala de grises en escalones amplios; si dos
   badges se ven iguales aquí, hay que separar sus grises, no volver al color.  */
html.a11y-mono:root:root:root:root:root {
    --bg:               #000000 !important;
    --bg-2:             #0a0a0a !important;
    --surface:          #141414 !important;
    --surface-2:        #1e1e1e !important;
    --surface-3:        #282828 !important;
    --surface-overlay:  #141414 !important;
    --panel:            #141414 !important;
    --panel-2:          #1e1e1e !important;
    --nav-bg:           #000000 !important;
    --nav-bg-2:         #0a0a0a !important;
    --nav-border:       #4d4d4d !important;
    --nav-shade:        #000000 !important;

    --control-bg:       #232323 !important;
    --control-bg-hover: #333333 !important;
    --control-border:   #666666 !important;
    --scrim:            rgba(0,0,0,0.78) !important;

    --text:             #f2f2f2 !important;
    --text-strong:      #ffffff !important;
    --muted:            #b8b8b8 !important;
    --muted-2:          #8f8f8f !important;

    --border:           #4d4d4d !important;
    --border-soft:      #333333 !important;

    --brand:            #ffffff !important;
    --brand-dark:       #4d4d4d !important;
    --brand-rgb:        255,255,255 !important;
    --brand-dark-rgb:   77,77,77 !important;
    --brand-soft-bg:    rgba(255,255,255,0.14) !important;
    --brand-soft-fg:    #ffffff !important;
    --accent:           #ffffff !important;

    --success:          #e0e0e0 !important;
    --danger:           #9c9c9c !important;
    --badge-success-bg: rgba(255,255,255,0.26) !important;
    --badge-success-fg: #ffffff !important;
    --badge-warn-bg:    rgba(255,255,255,0.20) !important;
    --badge-warn-fg:    #dcdcdc !important;
    --badge-danger-bg:  rgba(255,255,255,0.34) !important;
    --badge-danger-fg:  #000000 !important;
    --badge-info-bg:    rgba(255,255,255,0.14) !important;
    --badge-info-fg:    #c4c4c4 !important;
    --badge-neutral-bg: rgba(255,255,255,0.09) !important;
    --badge-neutral-fg: #a0a0a0 !important;
    --badge-purple-bg:  rgba(255,255,255,0.17) !important;
    --badge-purple-fg:  #cfcfcf !important;

    --tooltip-bg:       #ffffff !important;
    --tooltip-fg:       #000000 !important;

    --chart-1:           #d4d4d4 !important;
    --chart-2:           #a8a8a8 !important;
    --chart-3:           #eeeeee !important;
    --chart-4:           #7d7d7d !important;
    --chart-5:           #ffffff !important;
    --chart-6:           #5c5c5c !important;
    --chart-grid:        #333333 !important;
    --chart-axis:        #c9c9c9 !important;
    --chart-legend:      #ffffff !important;
    --chart-datalabel:   #000000 !important;
}

html.a11y-mono.light-mode:root:root:root:root:root {
    --bg:               #ffffff !important;
    --bg-2:             #f5f5f5 !important;
    --surface:          #ffffff !important;
    --surface-2:        #f0f0f0 !important;
    --surface-3:        #e4e4e4 !important;
    --surface-overlay:  #ffffff !important;
    --panel:            #ffffff !important;
    --panel-2:          #f0f0f0 !important;
    --nav-bg:           #ffffff !important;
    --nav-bg-2:         #f5f5f5 !important;
    --nav-border:       #999999 !important;
    --nav-shade:        #e4e4e4 !important;

    --control-bg:       #f0f0f0 !important;
    --control-bg-hover: #e0e0e0 !important;
    --control-border:   #8a8a8a !important;
    --scrim:            rgba(0,0,0,0.45) !important;

    --text:             #000000 !important;
    --text-strong:      #000000 !important;
    --muted:            #3d3d3d !important;
    --muted-2:          #5c5c5c !important;

    --border:           #999999 !important;
    --border-soft:      #d4d4d4 !important;

    --brand:            #000000 !important;
    --brand-dark:       #000000 !important;
    --brand-rgb:        0,0,0 !important;
    --brand-dark-rgb:   0,0,0 !important;
    --brand-soft-bg:    rgba(0,0,0,0.10) !important;
    --brand-soft-fg:    #000000 !important;
    --accent:           #000000 !important;

    --success:          #2b2b2b !important;
    --danger:           #000000 !important;
    --badge-success-bg: rgba(0,0,0,0.20) !important;
    --badge-success-fg: #000000 !important;
    --badge-warn-bg:    rgba(0,0,0,0.12) !important;
    --badge-warn-fg:    #2b2b2b !important;
    --badge-danger-bg:  #000000 !important;
    --badge-danger-fg:  #ffffff !important;
    --badge-info-bg:    rgba(0,0,0,0.07) !important;
    --badge-info-fg:    #4a4a4a !important;
    --badge-neutral-bg: rgba(0,0,0,0.05) !important;
    --badge-neutral-fg: #5c5c5c !important;
    --badge-purple-bg:  rgba(0,0,0,0.15) !important;
    --badge-purple-fg:  #1f1f1f !important;

    --tooltip-bg:       #000000 !important;
    --tooltip-fg:       #ffffff !important;

    /* Las sombras del tema claro llevan un tinte ciruela — `rgba(32,24,35,…)`—
       que a un 5-12 % de alfa no se ve, pero en una paleta que se llama «blanco
       y negro» no pinta nada. Las otras paletas SÍ las heredan a propósito: son
       neutras y no codifican ningún estado. */
    --shadow-sm:  0 1px 2px rgba(0,0,0,0.10) !important;
    --shadow-md:  0 4px 6px rgba(0,0,0,0.12), 0 2px 4px rgba(0,0,0,0.10) !important;
    --shadow-lg:  0 10px 25px rgba(0,0,0,0.18), 0 4px 8px rgba(0,0,0,0.10) !important;

    --chart-1:           #5e5e5e !important;
    --chart-2:           #8a8a8a !important;
    --chart-3:           #3b3b3b !important;
    --chart-4:           #a8a8a8 !important;
    --chart-5:           #111111 !important;
    --chart-6:           #c2c2c2 !important;
    --chart-grid:        #dcdcdc !important;
    --chart-axis:        #3d3d3d !important;
    --chart-legend:      #111111 !important;
    --chart-datalabel:   #ffffff !important;
}


/* ── Acromatopsia (daltonismo total) ────────────────────────────────────────────
   NO es la paleta «blanco y negro» con otro nombre, y por eso no la reutiliza.

   La acromatopsia congénita casi siempre viene con FOTOFOBIA: el mismo defecto
   que quita el color deja la visión dependiendo de los bastones, que se saturan
   con luz que a otra persona le parece normal. Un panel blanco brillante —o
   negro puro con texto blanco puro, que es el par de máximo deslumbramiento— no
   ayuda, molesta.

   Por eso:
   · Fondo gris muy oscuro (#161616), NO negro puro.
   · Texto gris claro (#d8d8d8), NO blanco puro. El par da ~11:1, de sobra para
     leer, sin el brillo del 21:1.
   · Ignora el modo claro a propósito: el mismo bloque sirve para los dos. Ofrecer
     una variante clara sería ofrecer justo lo que hay que evitar.
   · Los seis estados se separan por CLARIDAD en escalones amplios, como en la
     paleta monocroma — con el color no queda nada más de donde tirar.
   ─────────────────────────────────────────────────────────────────────────────── */
html.a11y-acro:root:root:root:root:root,
html.a11y-acro.light-mode:root:root:root:root:root {
    --bg:               #161616 !important;
    --bg-2:             #1b1b1b !important;
    --surface:          #202020 !important;
    --surface-2:        #262626 !important;
    --surface-3:        #2d2d2d !important;
    --surface-overlay:  #202020 !important;
    --panel:            #202020 !important;
    --panel-2:          #262626 !important;
    --nav-bg:           #161616 !important;
    --nav-bg-2:         #1b1b1b !important;
    --nav-border:       #454545 !important;
    --nav-shade:        #121212 !important;

    --control-bg:       #2a2a2a !important;
    --control-bg-hover: #343434 !important;
    --control-border:   #565656 !important;
    --scrim:            rgba(0,0,0,0.72) !important;

    /* Gris claro, no blanco: el blanco puro sobre gris muy oscuro es el par de
       máximo deslumbramiento, y es justo lo que hay que evitar aquí. */
    --text:             #d8d8d8 !important;
    --text-strong:      #ececec !important;
    --muted:            #a5a5a5 !important;
    --muted-2:          #838383 !important;

    --border:           #454545 !important;
    --border-soft:      #313131 !important;

    --brand:            #cfcfcf !important;
    --brand-dark:       #555555 !important;
    --brand-rgb:        207,207,207 !important;
    --brand-dark-rgb:   85,85,85 !important;
    --brand-soft-bg:    rgba(207,207,207,0.14) !important;
    --brand-soft-fg:    #dcdcdc !important;
    --accent:           #cfcfcf !important;

    --success:          #cfcfcf !important;
    --danger:           #8e8e8e !important;
    /* Seis estados repartidos por claridad, con el fondo del badge marcando el
       escalón grueso y el texto el fino. */
    --badge-success-bg: rgba(255,255,255,0.24) !important;
    --badge-success-fg: #ededed !important;
    --badge-warn-bg:    rgba(255,255,255,0.17) !important;
    --badge-warn-fg:    #cccccc !important;
    --badge-danger-bg:  rgba(255,255,255,0.32) !important;
    --badge-danger-fg:  #1a1a1a !important;
    --badge-info-bg:    rgba(255,255,255,0.11) !important;
    --badge-info-fg:    #b0b0b0 !important;
    --badge-neutral-bg: rgba(255,255,255,0.07) !important;
    --badge-neutral-fg: #949494 !important;
    --badge-purple-bg:  rgba(255,255,255,0.14) !important;
    --badge-purple-fg:  #c0c0c0 !important;

    --tooltip-bg:       #ececec !important;
    --tooltip-fg:       #161616 !important;

    --shadow-sm:  0 1px 2px rgba(0,0,0,0.40) !important;
    --shadow-md:  0 4px 6px rgba(0,0,0,0.45), 0 2px 4px rgba(0,0,0,0.40) !important;
    --shadow-lg:  0 12px 28px rgba(0,0,0,0.55), 0 4px 8px rgba(0,0,0,0.45) !important;

    --chart-1:           #c7c7c7 !important;
    --chart-2:           #a0a0a0 !important;
    --chart-3:           #dcdcdc !important;
    --chart-4:           #7c7c7c !important;
    --chart-5:           #ededed !important;
    --chart-6:           #5a5a5a !important;
    --chart-grid:        #3a3a3a !important;
    --chart-axis:        #c0c0c0 !important;
    --chart-legend:      #ededed !important;
    --chart-datalabel:   #1a1a1a !important;
}

/* ── (c) «Anti-daltónicos» — BROMA ──────────────────────────────────────────────
   Es un chiste y está etiquetado como tal en el menú. Hace justo lo contrario de
   (a): apoya todos los estados en el par rojo/verde que un deuteranope ve
   idéntico, y con luminosidades casi iguales para que tampoco el contraste los
   separe.
   Va en su propia sección del menú, bajo su rótulo de broma y separada de las
   opciones reales, para que nadie que necesite (a) la pulse por error.
   El mismo bloque sirve para claro y oscuro: la gracia es que no cambie nada.  */
html.a11y-broma:root:root:root:root:root,
html.a11y-broma.light-mode:root:root:root:root:root {
    --bg:               #2a2f1e !important;
    --bg-2:             #313620 !important;
    --surface:          #363b22 !important;
    --surface-2:        #3b4026 !important;
    --surface-3:        #414629 !important;
    --surface-overlay:  #363b22 !important;
    --panel:            #363b22 !important;
    --panel-2:          #3b4026 !important;
    --nav-bg:           #2a2f1e !important;
    --nav-bg-2:         #313620 !important;
    --nav-border:       #6d7a3a !important;
    --nav-shade:        #232817 !important;

    --control-bg:       #46512a !important;
    --control-bg-hover: #4f5a2f !important;
    --control-border:   #7a6b3a !important;
    --scrim:            rgba(40,45,25,0.7) !important;

    /* Oliva sobre oliva: el contraste justo para que se lea… poco. */
    --text:             #b9a94e !important;
    --text-strong:      #c9b84f !important;
    --muted:            #8f8a45 !important;
    --muted-2:          #7d7a3f !important;

    --border:           #6d7a3a !important;
    --border-soft:      #5b6531 !important;

    --brand:            #9a8f3d !important;
    --brand-dark:       #6f6a2c !important;
    --brand-rgb:        154,143,61 !important;
    --brand-dark-rgb:   111,106,44 !important;
    --brand-soft-bg:    rgba(154,143,61,0.25) !important;
    --brand-soft-fg:    #b9a94e !important;
    --accent:           #9a8f3d !important;

    /* Los seis estados, en rojo y verde de la MISMA luminosidad. */
    --success:          #6f9c3d !important;
    --danger:           #9c6f3d !important;
    --badge-success-bg: rgba(111,156,61,0.28) !important;
    --badge-success-fg: #7fa84a !important;
    --badge-warn-bg:    rgba(140,140,55,0.28) !important;
    --badge-warn-fg:    #8f8f42 !important;
    --badge-danger-bg:  rgba(156,111,61,0.28) !important;
    --badge-danger-fg:  #a8804a !important;
    --badge-info-bg:    rgba(125,140,60,0.28) !important;
    --badge-info-fg:    #869145 !important;
    --badge-neutral-bg: rgba(130,130,70,0.24) !important;
    --badge-neutral-fg: #8a8a4c !important;
    --badge-purple-bg:  rgba(150,120,60,0.26) !important;
    --badge-purple-fg:  #9a854a !important;

    --tooltip-bg:       #46512a !important;
    --tooltip-fg:       #b9a94e !important;

    --chart-1:           #6f9c3d !important;
    --chart-2:           #7d9440 !important;
    --chart-3:           #8f8f42 !important;
    --chart-4:           #87883f !important;
    --chart-5:           #9c6f3d !important;
    --chart-6:           #94773d !important;
}

/* ── Botón y panel de accesibilidad ────────────────────────────────────────────
   El botón vive en el header, junto al de guías. El panel es un popover anclado
   a él. Usa `--surface-overlay` y no `--surface`: flota sobre el contenido y con
   la superficie translúcida se veía casi invisible en oscuro.                  */
.ph-a11y-wrap { position: relative; display: inline-flex; }

.ph-a11y-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 9px;
    border: 1px solid var(--control-border);
    background: var(--control-bg);
    color: var(--text);
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease;
}
.ph-a11y-btn:hover { background: var(--control-bg-hover); }
.ph-a11y-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* Encendido = hay una paleta de accesibilidad activa. Sin esta marca, el único
   indicio de que el panel está en otra paleta es el color — que es justo lo que
   estas opciones cambian. */
.ph-a11y-btn.is-on {
    background: var(--brand-soft-bg);
    border-color: var(--brand);
    color: var(--brand-soft-fg);
}

.ph-a11y-panel {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 1200;
    width: 292px;
    padding: 12px;
    border-radius: 12px;
    border: 1px solid var(--border);
    background: var(--surface-overlay);
    box-shadow: var(--shadow-lg);
    /* Con los seis tipos de daltonismo desplegados el panel pasa de la altura de
       una pantalla baja. Scrollea por dentro en vez de crecer: si crece, la
       escala de letra y el botón de reiniciar quedan fuera de la vista, y son
       justo lo que busca quien abre esto. */
    max-height: min(76vh, 620px);
    overflow-y: auto;
    overscroll-behavior: contain;
}

.ph-a11y-title {
    font-size: .74rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0 0 6px;
}
.ph-a11y-title + .ph-a11y-title { margin-top: 14px; }

.ph-a11y-opt {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    width: 100%;
    padding: 7px 8px;
    border: 1px solid transparent;
    border-radius: 9px;
    background: none;
    color: var(--text);
    text-align: left;
    cursor: pointer;
}
.ph-a11y-opt:hover { background: var(--control-bg); }
.ph-a11y-opt:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.ph-a11y-opt.is-on {
    background: var(--brand-soft-bg);
    border-color: var(--brand);
}

/* Muestra de la paleta: tres cuadros con los colores que de verdad cambian.
   Un nombre solo no dice nada — «daltonismo» no explica qué va a pasar. */
.ph-a11y-swatches { display: flex; gap: 3px; flex: 0 0 auto; margin-top: 2px; }
.ph-a11y-sw {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    border: 1px solid var(--border-soft);
}

/* Tipos de daltonismo: cuelgan de «Daltonismo», así que se sangran y llevan una
   guía a la izquierda. Sin la sangría se leen como cuatro paletas más al mismo
   nivel, y son una elección DENTRO de una. */
.ph-a11y-opt.is-sub {
    margin-left: 10px;
    padding-left: 10px;
    border-left: 2px solid var(--border-soft);
    border-radius: 0 9px 9px 0;
}
.ph-a11y-opt.is-sub.is-on { border-left-color: var(--brand); }

/* Familia del tipo (rojo-verde / azul-amarillo). Es el dato que decide la
   paleta —qué eje de color se pierde—, así que encabeza su grupo. */
.ph-a11y-fam {
    margin: 8px 0 3px 20px;
    font-size: .68rem;
    color: var(--muted-2);
}

/* Leyenda de las muestras. Alineada con la columna de cuadritos (10px de padding
   de la fila) para que se lea como su cabecera y no como un texto suelto. */
.ph-a11y-hint {
    margin: -2px 0 6px 10px;
    font-size: .64rem;
    letter-spacing: .02em;
    color: var(--muted-2);
}

.ph-a11y-opt-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ph-a11y-opt-name { font-size: .82rem; }
.ph-a11y-opt-desc { font-size: .7rem; color: var(--muted); line-height: 1.35; }

/* Escala de letra. El valor va en el medio y los botones a los lados: es un
   control de «más/menos», no una lista de tamaños. */
.ph-a11y-scale {
    display: grid;
    grid-template-columns: 38px 1fr 38px;
    align-items: center;
    gap: 8px;
}
.ph-a11y-scale-btn {
    height: 34px;
    border-radius: 9px;
    border: 1px solid var(--control-border);
    background: var(--control-bg);
    color: var(--text);
    cursor: pointer;
    font-size: .95rem;
}
.ph-a11y-scale-btn:hover:not(:disabled) { background: var(--control-bg-hover); }
.ph-a11y-scale-btn:disabled { opacity: .4; cursor: default; }
.ph-a11y-scale-val {
    text-align: center;
    font-size: .84rem;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
}

.ph-a11y-reset {
    width: 100%;
    margin-top: 12px;
    padding: 7px;
    border-radius: 9px;
    border: 1px solid var(--control-border);
    background: none;
    color: var(--muted);
    font-size: .76rem;
    cursor: pointer;
}
.ph-a11y-reset:hover { background: var(--control-bg); color: var(--text); }

/* Backdrop invisible: cierra al pulsar fuera sin robar el foco ni oscurecer. */
.ph-a11y-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1199;
    background: transparent;
    border: none;
    cursor: default;
}

/* ── Constructor de plantillas de Meta (Campañas → Plantillas) ──────────────────
   Formulario con tokens del tema; la burbuja imita WhatsApp con sus colores fijos
   (excepción intencional, como la tarjeta de login), para que la previsualización
   se lea igual que en la app en los dos temas. */
.tpl-builder { max-width: 960px; }
.tpl-builder-grid {
    display: grid;
    grid-template-columns: 1.2fr 0.8fr;
    gap: 18px;
    padding: 4px;
}
@media (max-width: 760px) { .tpl-builder-grid { grid-template-columns: 1fr; } }

.tpl-form { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.tpl-lbl {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-strong);
    margin-top: 8px;
}
.tpl-hint { font-size: 0.72rem; color: var(--muted); margin: 2px 0 4px; }
.tpl-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.tpl-body { font-family: inherit; resize: vertical; }
.tpl-ex { margin-top: 4px; }
.tpl-examples { display: flex; flex-direction: column; gap: 4px; }
.tpl-addvar { align-self: flex-start; font-size: 0.76rem; margin-top: 2px; }

.tpl-btns-head {
    display: flex; align-items: center; justify-content: space-between;
    margin-top: 8px;
}
.tpl-btn-row { display: flex; gap: 6px; align-items: center; margin-top: 4px; }
.tpl-btn-type { flex: 0 0 130px; }
.tpl-btn-del { flex: 0 0 auto; }
.tpl-row-actions { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; }
.tpl-reject { color: var(--badge-danger-fg); font-size: 0.72rem; }

.tpl-builder-foot {
    display: flex; justify-content: flex-end; gap: 8px;
    margin-top: 14px; padding: 10px 4px 2px;
    border-top: 1px solid var(--border);
}

/* Previsualización — imita el chat de WhatsApp (colores fijos, a propósito). */
.tpl-preview { display: flex; flex-direction: column; gap: 6px; }
.tpl-phone {
    background: #e5ddd5;                 /* fondo de chat de WhatsApp */
    border-radius: 12px;
    padding: 16px 12px;
    min-height: 180px;
}
.tpl-bubble {
    background: #ffffff;                 /* burbuja entrante (plantilla), blanca */
    color: #111b21;
    border-radius: 10px;
    padding: 10px 12px;
    max-width: 100%;
    box-shadow: 0 1px 1px rgba(0,0,0,0.15);
    word-break: break-word;
}
.tpl-bubble-header { font-weight: 700; margin-bottom: 6px; white-space: pre-wrap; }
.tpl-bubble-body { white-space: pre-wrap; line-height: 1.4; }
.tpl-bubble-empty { color: #667781; font-style: italic; }
.tpl-bubble-footer { font-size: 0.78rem; color: #667781; margin-top: 8px; white-space: pre-wrap; }
.tpl-bubble-btns { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.tpl-bubble-btn {
    background: #ffffff;
    color: #027eb5;                      /* azul de acción de WhatsApp */
    text-align: center;
    border-radius: 8px;
    padding: 7px;
    font-size: 0.86rem;
    font-weight: 500;
    box-shadow: 0 1px 1px rgba(0,0,0,0.15);
}

/* ── Instagram · publicaciones, comentarios e insights (pages/instagram.rs) ──
   Solo tokens de tema. Los únicos colores fijos son los del degradado sobre
   la foto (texto blanco sobre sombra negra): van sobre la imagen, no sobre
   una superficie del tema, así que no cambian entre claro y oscuro. */
.ig-page { padding: 28px 32px; display: flex; flex-direction: column; gap: 16px; }
.ig-error { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ig-topbar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.ig-select, .ig-search {
  background: var(--control-bg); border: 1px solid var(--control-border); color: var(--text);
  border-radius: 10px; padding: 8px 12px; font-size: 14px;
}
.ig-select:hover, .ig-search:hover { background: var(--control-bg-hover); }
.ig-search { min-width: 260px; flex: 1; }
.ig-account-card {
  display: flex; align-items: center; gap: 18px; flex: 1; min-width: 320px; padding: 16px 20px;
  border: 1px solid var(--border); border-radius: 16px; background: var(--surface); box-shadow: var(--shadow-sm);
}
.ig-avatar { width: 64px; height: 64px; border-radius: 50%; object-fit: cover; background: var(--panel-2); flex: none; }
.ig-avatar-ph { display: flex; align-items: center; justify-content: center; font-size: 28px; }
.ig-account-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.ig-username { font-weight: 700; font-size: 18px; color: var(--text-strong); }
.ig-name { color: var(--muted); font-size: 13px; }
.ig-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.ig-chip {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px 9px; border-radius: 999px;
  font-size: 11.5px; font-weight: 600; background: var(--badge-neutral-bg); color: var(--badge-neutral-fg);
}
.ig-chip--ok { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.ig-chip--warn { background: var(--badge-warn-bg); color: var(--badge-warn-fg); }
.ig-chip--off { background: var(--badge-danger-bg); color: var(--badge-danger-fg); }
.ig-stats { display: flex; gap: 22px; }
.ig-stat { text-align: center; }
.ig-stat-n { font-size: 20px; font-weight: 700; color: var(--text-strong); }
.ig-stat-l { font-size: 11.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.ig-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); }
.ig-tab {
  background: none; border: none; border-bottom: 2px solid transparent; padding: 10px 14px;
  color: var(--muted); font-weight: 600; cursor: pointer; font-size: 14px;
}
.ig-tab:hover { color: var(--text); }
.ig-tab.active { color: var(--brand); border-bottom-color: var(--brand); }
.ig-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ig-check { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: 13px; cursor: pointer; }
.ig-refresh { margin-left: auto; }
.ig-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
.ig-card {
  border: 1px solid var(--border); border-radius: 14px; background: var(--surface); overflow: hidden;
  cursor: pointer; transition: transform .15s ease, box-shadow .15s ease; display: flex; flex-direction: column;
}
.ig-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.ig-thumb-wrap { position: relative; aspect-ratio: 1 / 1; background: var(--panel-2); }
.ig-thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
.ig-thumb-ph { display: flex; align-items: center; justify-content: center; font-size: 40px; color: var(--muted); }
.ig-type-badge {
  position: absolute; top: 8px; left: 8px; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 600;
  background: var(--surface-overlay); color: var(--text); box-shadow: var(--shadow-sm);
}
.ig-type-badge--inline { position: static; }
.ig-agent-badge {
  position: absolute; top: 8px; right: 8px; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 700;
  background: var(--brand-dark); color: #fff;
}
.ig-card-overlay {
  position: absolute; left: 0; right: 0; bottom: 0; display: flex; gap: 12px; padding: 6px 10px;
  font-size: 12px; font-weight: 600; color: #fff; background: linear-gradient(transparent, rgba(0, 0, 0, .55));
}
.ig-card-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 4px; }
.ig-caption { font-size: 13px; color: var(--text); line-height: 1.35; }
.ig-meta { font-size: 12px; color: var(--muted); }
.ig-more { display: flex; justify-content: center; padding: 8px 0 4px; }
.ig-empty {
  padding: 36px 16px; text-align: center; color: var(--muted); border: 1px dashed var(--border);
  border-radius: 14px; background: var(--surface);
}
.ig-empty--hero { padding: 56px 16px; }
.ig-empty-icon { font-size: 40px; margin-bottom: 8px; }
.ig-empty h3 { color: var(--text-strong); margin: 0 0 6px; }
.ig-empty p { margin: 0; }
.ig-skeleton {
  min-height: 280px; border-color: transparent;
  background: linear-gradient(90deg, var(--skel-base), var(--skel-hi), var(--skel-base));
  background-size: 200% 100%; animation: ig-shimmer 1.2s infinite;
}
.ig-skeleton-card { min-height: 96px; }
@keyframes ig-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

.ig-modal-backdrop {
  position: fixed; inset: 0; background: var(--scrim); display: flex; align-items: center; justify-content: center;
  z-index: 120; padding: 20px;
}
.ig-modal {
  position: relative; width: min(1100px, 100%); max-height: 92vh; display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(320px, .9fr); grid-template-rows: minmax(0, 1fr);
  background: var(--surface-overlay); border: 1px solid var(--border); border-radius: 18px;
  box-shadow: var(--shadow-lg); overflow: hidden;
}
.ig-modal--loading { display: block; width: min(420px, 100%); padding: 20px; }
.ig-modal-close {
  position: absolute; top: 10px; right: 10px; z-index: 2; border: none; background: var(--control-bg);
  color: var(--text); width: 32px; height: 32px; border-radius: 50%; cursor: pointer;
}
.ig-modal-close:hover { background: var(--control-bg-hover); }
.ig-modal-media { background: var(--panel-2); display: flex; flex-direction: column; min-height: 0; }
.ig-modal-img, .ig-modal-video { width: 100%; flex: 1; max-height: 78vh; object-fit: contain; background: var(--bg-2); }
.ig-modal-ph { flex: 1; font-size: 64px; }
.ig-carousel-strip { display: flex; gap: 6px; padding: 8px; overflow-x: auto; background: var(--surface-2); }
.ig-carousel-dot {
  width: 56px; height: 56px; flex: none; border-radius: 8px; overflow: hidden; border: 2px solid transparent;
  background: var(--panel-2); padding: 0; cursor: pointer;
}
.ig-carousel-dot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ig-carousel-dot.active { border-color: var(--brand); }
.ig-carousel-vid { display: flex; align-items: center; justify-content: center; height: 100%; }
.ig-modal-side { padding: 18px 20px; min-height: 0; max-height: 92vh; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }
.ig-modal-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding-right: 36px; }
.ig-modal-caption { margin: 0; white-space: pre-wrap; font-size: 14px; color: var(--text); line-height: 1.45; }
.ig-link-btn {
  background: none; border: none; padding: 0; color: var(--brand); font-weight: 600; cursor: pointer;
  text-decoration: none; font-size: 13px;
}
.ig-link-btn:hover { text-decoration: underline; }
.ig-insights, .ig-totals { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px; }
.ig-insight { padding: 10px 12px; border-radius: 12px; background: var(--surface-2); border: 1px solid var(--border-soft); }
.ig-insight-n { font-size: 18px; font-weight: 700; color: var(--text-strong); }
.ig-insight-l { font-size: 11.5px; color: var(--muted); }
.ig-note { font-size: 12.5px; color: var(--muted); padding: 8px 10px; border-radius: 10px; background: var(--surface-2); }
.ig-section-title {
  margin: 6px 0 0; font-size: 13px; font-weight: 700; color: var(--text-strong); text-transform: uppercase;
  letter-spacing: .05em; display: flex; align-items: center; gap: 8px;
}
.ig-count { font-weight: 600; color: var(--muted); }
.ig-comments, .ig-agent-list { display: flex; flex-direction: column; gap: 8px; }
.ig-comment, .ig-agent-item {
  padding: 10px 12px; border-radius: 12px; background: var(--surface-2); border: 1px solid var(--border-soft);
  display: flex; flex-direction: column; gap: 4px;
}
.ig-comment--own { border-color: rgba(var(--brand-rgb), .45); background: var(--brand-soft-bg); }
.ig-comment-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ig-comment-user { font-weight: 700; font-size: 13px; color: var(--text-strong); }
.ig-comment-text { font-size: 13.5px; color: var(--text); white-space: pre-wrap; }
.ig-replies { margin-top: 6px; padding-left: 12px; border-left: 2px solid var(--border); display: flex; flex-direction: column; gap: 6px; }
.ig-agent-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.ig-more-comments { align-self: flex-start; }
.ig-metrics { display: flex; flex-direction: column; gap: 14px; }
.ig-chart-card { padding: 14px 16px; border: 1px solid var(--border); border-radius: 14px; background: var(--surface); }
.ig-chart-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.ig-bars { display: flex; align-items: flex-end; gap: 3px; height: 140px; }
.ig-bar { flex: 1; height: 100%; display: flex; align-items: flex-end; }
.ig-bar-fill { width: 100%; border-radius: 4px 4px 0 0; background: var(--chart-1); min-height: 2px; transition: height .2s ease; }
.ig-bar:hover .ig-bar-fill { background: var(--chart-accent); }
@media (max-width: 860px) {
  .ig-modal { grid-template-columns: 1fr; grid-template-rows: auto auto; overflow-y: auto; }
  .ig-modal-side { max-height: none; overflow: visible; }
  .ig-modal-img, .ig-modal-video { max-height: 50vh; }
  .ig-stats { width: 100%; justify-content: space-around; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE DEL SHELL — header global + sidebar de módulos
   ═══════════════════════════════════════════════════════════════════════════

   Va al FINAL del archivo a propósito: al empatar en especificidad con las
   reglas de escritorio, gana por orden de fuente. Así ninguna regla de aquí
   necesita `!important`, que es lo que convierte un CSS en intocable.

   TODO lo responsive del shell vive en este bloque. Si mañana hay que revisar
   cómo se comporta el panel en un teléfono, se lee esto y nada más.

   ── Tramos ────────────────────────────────────────────────────────────────
   | Tramo              | Ancho      | Qué cambia                            |
   |--------------------|------------|---------------------------------------|
   | Escritorio         | >= 1024px  | NADA. Idéntico a como estaba          |
   | Tablet             | <= 1023px  | Sidebar = cajón; chip solo avatar     |
   | Teléfono           | <=  639px  | Controles secundarios al menú «...»   |
   | Teléfono pequeño   | <=  399px  | Sin meta ni icono de módulo           |

   1024 es la frontera porque es donde deja de haber ratón de forma fiable: un
   iPad vertical (820) y uno horizontal (1024) se manejan con el dedo, y el
   riel de iconos sin etiqueta —que es lo que había— incumple la regla 5 de
   `docs/frontend/guias/reglas_ui_ux.md`: iconos sin texto de apoyo.

   639 y 399 salen de los anchos reales: 430 (iPhone Pro Max), 390 (iPhone 14),
   360 (Android base) y 320 (iPhone SE 1a gen, el peor caso que hay que cubrir).

   ── Por qué un cajón y no un riel ─────────────────────────────────────────
   Un riel de 64px es el 18% de una pantalla de 360px, y son 64px que el
   contenido pierde en TODAS las pantallas para una navegación que se usa una
   vez cada tantas. El cajón no cuesta nada mientras está cerrado.

   ── El estado del cajón NO es nuevo ───────────────────────────────────────
   Se reutiliza `SidebarCollapsed` (`module_sidebar.rs`), que ya existe:
       colapsada  ==  cajón cerrado
       expandida  ==  cajón abierto
   Esto no es un atajo, es lo que evita dos fuentes de verdad. Y de regalo, el
   `Effect` que ya colapsa la barra al navegar cierra el cajón al navegar, que
   es justo lo que se espera de un cajón en un teléfono.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Alto del header en un solo sitio. El cajón y la subsidebar cuelgan de él; sin
   esta variable, cambiar el header obliga a acordarse de otros tres `top: 60px`
   repartidos por el archivo (que es como se quedaron desalineados antes). */
:root { --ph-h: 60px; }

/* Hamburguesa, menú «...» y el «volver» del segundo nivel: solo existen por
   debajo de escritorio. Se declaran ocultos aquí y cada tramo los enciende,
   para que añadirlos no cambie ni un píxel de la vista de escritorio. */
.ph-menu-toggle,
.ph-overflow-wrap,
.module-subsidebar-back { display: none; }

/* Backdrop del cajón. Es un <button> y no un <div> para que también se cierre
   con teclado sin un handler global — mismo criterio que `.ph-a11y-backdrop`. */
.module-drawer-backdrop { display: none; }


/* ══════════════════════ TABLET Y POR DEBAJO — <= 1023px ════════════════════ */
@media (max-width: 1023px) {

  /* ── Header ────────────────────────────────────────────────────────────── */
  .ph {
    /* El 22px de la izquierda alineaba con la columna de iconos de la sidebar.
       Sin sidebar en el flujo, ese anclaje ya no existe. */
    padding: 0 12px;
    gap: 8px;
  }

  .ph-menu-toggle { display: inline-flex; }

  /* Objetivos táctiles del header. Medidos, iban de 30x30 (el chip de usuario)
     a 38x38: por debajo del suelo con el que un dedo acierta sin mirar, y aquí
     están todos pegados unos a otros en una sola fila.

     40px es el mínimo, no el tamaño del dibujo: el icono sigue midiendo lo que
     medía y lo que crece es la caja que lo rodea. En una barra de 60px deja 10px
     arriba y abajo, así que no aprieta. */
  .ph-back,
  .ph-menu-toggle,
  .ph-guide-btn,
  .ph-a11y-btn,
  .ph-notif-btn,
  .ph-theme-btn,
  .ph-logout,
  .ph-user-chip {
    width: 40px;
    height: 40px;
    min-width: 40px;
    justify-content: center;
  }

  /* El chip pierde el texto y se queda en avatar: nombre y rol son lo primero
     que sobra cuando hay que elegir, porque quien usa el panel ya sabe quién
     es. Sigue llevando a Mi Perfil. */
  .ph-user-info { display: none; }
  .ph-user-chip {
    padding: 0;
    border-radius: 50%;
    /* Sin borde ni fondo el avatar ya se lee solo, y así no parece un botón
       cortado a la mitad. El área de toque siguen siendo los 40px de arriba. */
    background: transparent;
    border-color: transparent;
  }

  /* ── Sidebar = cajón ───────────────────────────────────────────────────── */
  .module-sidebar {
    position: fixed;
    top: var(--ph-h);
    left: 0;
    bottom: 0;
    /* `height: auto` mata el `calc(100vh - 60px)` de escritorio: con `top` y
       `bottom` fijados el alto ya está resuelto, y el calc sobraba 60px por
       debajo (el cajón terminaba fuera de la pantalla). */
    height: auto;
    /* 82vw deja ver una franja del contenido detrás: es lo que dice «esto está
       encima, se puede cerrar» sin necesidad de explicarlo. */
    width: min(284px, 82vw);
    /* `env()` para que en un iPhone la última fila no quede bajo la barra de
       gestos. Sin soporte, el fallback es 0 y queda como estaba. */
    padding: 12px 10px calc(12px + env(safe-area-inset-bottom, 0px));
    /* Bajo el header (z-100), sobre el contenido. */
    z-index: 90;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    /* Que el scroll del cajón no arrastre la página de detrás al llegar al
       final — en táctil es el efecto de goma que descoloca la lectura. */
    overscroll-behavior: contain;
  }

  /* Cerrado. Mantiene el ancho para que la salida se anime igual que la
     entrada; si encogiera a 0 el cajón se desinflaría en vez de deslizarse. */
  .module-sidebar--collapsed {
    width: min(284px, 82vw);
    transform: translateX(-100%);
  }

  /* Abierto. Va DESPUÉS del cerrado a propósito: las dos reglas empatan en
     especificidad (0,2,0 contra 0,1,0 -> gana esta) y el orden decide. */
  .module-sidebar:not(.module-sidebar--collapsed) {
    transform: none;
    box-shadow: var(--shadow-lg);
  }

  /* El colapso instantáneo al navegar es de escritorio (evitar ver cerrarse el
     riel). En un cajón hay que VER que se cierra: es lo que confirma que el
     toque hizo algo. */
  .module-sidebar--no-anim { transition: transform 0.2s ease; }

  /* Dentro del cajón nunca se aplica el modo riel: aunque el estado diga
     «colapsada», lo que está es cerrada, y al abrirse debe verse entera.
     Importa aunque esté fuera de pantalla — se ve durante los 200 ms en que el
     cajón se desliza al cerrarse, y ahí encogerse a iconos se lee como un fallo. */
  .module-sidebar--collapsed .module-sidebar-toggle { justify-content: flex-start; }
  .module-sidebar--collapsed .module-sidebar-toggle-label { display: inline; }
  .module-sidebar--collapsed .module-sidebar-item { justify-content: flex-start; padding: 10px 12px; }
  .module-sidebar--collapsed .module-sidebar-label,
  .module-sidebar--collapsed .module-sidebar-dot { display: revert; }
  /* El caret va explícito y NO con `revert`: `revert` devuelve al valor del
     navegador (`inline` para un span), no al `inline-flex` de su regla base — y
     en `inline` se ignoran el ancho, el alto y el centrado de su zona táctil. */
  .module-sidebar--collapsed .module-sidebar-caret { display: inline-flex; }

  /* El botón «Módulos» del cajón es el de cerrar: mismo sitio, misma ☰, y no
     hay que enseñar un gesto nuevo. */
  .module-sidebar-toggle { margin-bottom: 10px; min-height: 44px; }

  /* Objetivos táctiles: 44px es el mínimo con el que un dedo acierta sin
     mirar. Las filas venían de 38px (10px + 18px de icono + 10px). */
  .module-sidebar-item { min-height: 44px; }

  /* Zona de toque del caret: el icono sigue midiendo 14px, pero lo que se
     pulsa son 44px. Es lo que permite que la fila navegue al módulo y el
     caret abra las tabs sin que se roben el toque. */
  .module-sidebar-caret {
    width: 44px;
    height: 44px;
    margin: -10px -12px -10px auto;
    justify-content: center;
    opacity: 0.6;
    border-radius: 10px;
  }
  .module-sidebar-caret:active { background: var(--scrim); }

  /* ── Backdrop ──────────────────────────────────────────────────────────── */
  .module-drawer-backdrop {
    display: block;
    position: fixed;
    /* Desde debajo del header: el header sigue siendo utilizable con el cajón
       abierto (la campana, el tema y el perfil no se apagan por navegar). */
    inset: var(--ph-h) 0 0 0;
    z-index: 89;
    background: var(--scrim);
    border: none;
    padding: 0;
    cursor: pointer;
    /* Un backdrop que aparece de golpe se lee como un fallo de pintado. */
    animation: drawer-scrim 0.2s ease;
  }

  /* ── Subsidebar = segundo nivel SOBRE el cajón ─────────────────────────── */
  /* En escritorio sale a la derecha de la barra. Aquí no hay sitio para dos
     paneles (244 + 64 = 308px de una pantalla de 360), así que se superpone:
     el patrón de profundidad de siempre en móvil. */
  .module-subsidebar {
    top: var(--ph-h);
    left: 0;
    width: min(284px, 82vw);
    padding: 12px 10px calc(12px + env(safe-area-inset-bottom, 0px));
    /* Sobre el cajón (90), bajo el header (100). */
    z-index: 91;
    transform: translateX(-100%);
    box-shadow: var(--shadow-lg);
    overscroll-behavior: contain;
  }
  /* Mata el `left: 64px` de escritorio (el riel ya no existe en este tramo). */
  .module-subsidebar--rail { left: 0; }
  .module-subsidebar.is-open { transform: none; }

  .module-subsidebar-item { min-height: 44px; }

  /* Volver al cajón desde el segundo nivel. Sin esto se entra a las tabs de un
     módulo y no hay forma de retroceder sin cerrar y volver a abrir. */
  .module-subsidebar-back {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-height: 44px;
    padding: 9px 12px;
    margin-bottom: 6px;
    border: none;
    border-radius: 10px;
    background: transparent;
    color: var(--muted);
    font-family: inherit;
    font-size: 0.82rem;
    font-weight: 700;
    text-align: left;
    cursor: pointer;
  }
  .module-subsidebar-back:active { background: var(--scrim); }

  /* ── Contenido ─────────────────────────────────────────────────────────── */
  /* El cajón está fuera del flujo, así que el contenido ocupa el ancho entero.
     Lo que antes lo tapaba eran 64px de riel sin compensar. */
  .app-content { width: 100%; }
}

/* El backdrop solo existe por debajo de escritorio, pero la animación debe
   declararse fuera del media query o no se resuelve. */
@keyframes drawer-scrim { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .module-drawer-backdrop { animation: none; }
}


/* ═════════════════════════ TELÉFONO — <= 639px ════════════════════════════ */
@media (max-width: 639px) {

  /* Seis controles no caben en 360px: a partir de aquí el header desbordaba y
     el título se quedaba en 0px de ancho (medido, no estimado).

     Al menú «...» se van los tres que NO se usan a diario: guía, tema y cerrar
     sesión. Ninguno se pierde, solo cambian de sitio.

     Accesibilidad se queda FUERA del menú, con su botón propio. Es el único de
     los cuatro que alguien puede necesitar para poder leer la pantalla en la
     que está: esconderlo un toque más adentro es justo lo contrario de para lo
     que existe. Y es también el único que señaliza estado (`is-on` cuando hay
     una paleta activa), señal que un menú cerrado se tragaría. */
  .ph-guide-btn,
  .ph-theme-btn,
  .ph-logout { display: none; }

  .ph-overflow-wrap { display: inline-flex; position: relative; }

  .ph-overflow-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: transparent;
    color: var(--text);
    cursor: pointer;
    flex-shrink: 0;
  }
  .ph-overflow-btn:hover { background: var(--control-bg-hover); }

  /* Mismo criterio que `.ph-a11y-backdrop`: <button> para que cierre también
     con teclado, sin colgar un handler global de la ventana. */
  .ph-overflow-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1199;
    background: transparent;
    border: none;
    cursor: default;
  }

  .ph-overflow-menu {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 1200;
    min-width: 208px;
    padding: 6px;
    border-radius: 12px;
    border: 1px solid var(--border);
    /* `--surface-overlay` y no `--surface`: flota sobre el contenido, y el
       tinte translúcido de `--surface` se vuelve casi invisible en oscuro. */
    background: var(--surface-overlay);
    box-shadow: var(--shadow-lg);
  }

  .ph-overflow-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 44px;
    padding: 10px 12px;
    border: none;
    border-radius: 9px;
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: 0.88rem;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
  }
  .ph-overflow-item:hover { background: var(--control-bg-hover); }
  .ph-overflow-item svg { flex-shrink: 0; }

  /* «Mi perfil» solo aparece en el menú cuando el avatar se ha ido del header,
     es decir a ≤399px. Entre 400 y 639 el avatar sigue en la barra y tenerlo
     además aquí sería la misma acción dos veces en la misma pantalla. */
  .ph-overflow-item--profile { display: none; }
  .ph-overflow-avatar {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    background: var(--brand-dark);
    color: #fff;
    font-size: 0.72rem;
    font-weight: 700;
  }

  /* Cerrar sesión es el único destructivo del menú: se separa con una línea y
     se tiñe, para que no se pulse por inercia al ir a por el de arriba. */
  .ph-overflow-item--danger {
    color: var(--badge-danger-fg);
    margin-top: 4px;
    border-top: 1px solid var(--border-soft);
    border-radius: 0 0 9px 9px;
  }

  .ph { padding: 0 8px; gap: 6px; }
  .ph-left { gap: 8px; }
  .ph-right { gap: 4px; }

  /* La divisoria separaba «volver» del módulo. Con la hamburguesa delante ya
     hay tres elementos antes del título; una raya más es ruido. */
  .ph-divider { display: none; }

  .ph-title { font-size: 0.9rem; }
}


/* ════════════════════ TELÉFONO PEQUEÑO — <= 399px ═════════════════════════ */
@media (max-width: 399px) {

  /* A 320px lo único que queda por recortar es lo que no es el nombre del
     sitio donde estás. El icono del módulo repite lo que ya dice el título, y
     la meta («24 abiertos · 3 sin asignar») es un dato que también está dentro
     de la página. El título NO se toca: es lo que dice dónde estás. */
  .ph-icon,
  .ph-meta { display: none; }

  /* Aun así no bastaba: con siete controles en la fila, el título se quedaba en
     15px de ancho a 320px (medido) — o sea, en nada. El avatar es lo que menos
     cuesta mover: es un destino de navegación, no una señal viva como el
     contador de la campana ni una ayuda para poder leer como accesibilidad. Se
     va al menú «...» como «Mi perfil», que es a donde llevaba.
     El título pasa de 15px a ~80px. */
  .ph-user-chip { display: none; }
  .ph-overflow-item--profile { display: flex; }

  .ph-title { font-size: 0.875rem; }

  /* El cajón puede ocupar casi todo: a este ancho, una franja del 18% no
     enseña nada útil del contenido de detrás. */
  .module-sidebar,
  .module-sidebar--collapsed,
  .module-subsidebar { width: min(284px, 88vw); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE DE PÁGINAS — patrones compartidos
   ═══════════════════════════════════════════════════════════════════════════

   Sigue el contrato de tramos de «RESPONSIVE DEL SHELL» (1024 / 639 / 399), que
   está documentado en `docs/frontend/arquitectura/responsive.md`.

   Aquí van los arreglos que NO son de una página: los comparten muchas porque
   comparten el componente. Arreglarlos una vez arregla 18 módulos; arreglarlos
   en cada página sería copiar el mismo parche 18 veces y que se desincronicen.

   Lo específico de una página va en el bloque de esa página, no aquí.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 1. Barras de herramientas ────────────────────────────────────────────────
   `.tickets-toolbar` la usan DIECIOCHO páginas (`components/toolbar.rs`). Es
   `display:flex` sin `flex-wrap`, con un buscador que pide `width:100%` y un
   botón de acción con `flex-shrink:0`: por debajo de ~700px la suma no cabe y el
   contenido del módulo acaba scrollando de lado.

   Envuelve en vez de scrollar, al revés que las pestañas, y por un motivo: una
   barra de herramientas no tiene orden que recorrer —son acciones sueltas— así
   que apilarlas se lee bien. Una barra de pestañas sí es una secuencia, y
   partirla en varias líneas dentro de una píldora la rompe visualmente. */
@media (max-width: 1023px) {
  .tickets-toolbar,
  .monitor-toolbar,
  .mir-toolbar,
  .ec-toolbar,
  .mk-toolbar {
    flex-wrap: wrap;
    gap: 10px;
  }

  /* El buscador pasa a ocupar la fila entera: es el control que más se usa y el
     que peor funciona estrecho. `min-width:0` en el input es obligatorio — sin
     él, el ancho intrínseco del texto impide que el flex lo encoja, y es lo que
     dejaba el placeholder cortado en «Buscar po». */
  .contacts-search-form {
    flex: 1 1 100%;
    max-width: none;
    min-width: 0;
  }
  .contacts-search-input { min-width: 0; }

  /* El bloque de acciones deja de empujarse a la derecha con `margin-left:auto`:
     con la fila envuelta, ese auto lo mandaba solo a la esquina de una línea
     vacía.

     `flex: 1 1 100%` y no `0 0 auto`: con `0 0 auto` no encoge y conserva el
     ancho de su contenido — medido, 487px en Reservas › Recursos, empujando la
     página 128px. Ocupando la fila entera, sus botones envuelven dentro. */
  .tickets-toolbar-action {
    margin-left: 0;
    flex: 1 1 100%;
    min-width: 0;
  }
  /* Pero sus BOTONES no encogen: con `min-width: 0` en el contenedor, los hijos
     pasaban a comprimirse por debajo de su etiqueta y la recortaban (medido:
     48px de texto perdido en los botones de Reservas). Que envuelvan a la línea
     siguiente, que es lo que se pretendía. */
  .tickets-toolbar-action > * { flex: 0 0 auto; max-width: 100%; }
}


/* ── 2. Barras de pestañas: scrollan, no envuelven ────────────────────────────
   El bloque canónico «SEGMENTED CONTROL ESTÁNDAR» ya trae `max-width:100%` y
   `overflow-x:auto`, que es lo correcto. Pero dos reglas viejas lo anulaban con
   `flex-wrap: wrap` (`.ticket-tabs` a ≤1024px y `.camp-tabs` a ≤768px), y una
   píldora partida en tres líneas no parece una barra de pestañas: parece un
   error de maquetación. Se anulan aquí y se refuerza el scroll. */
@media (max-width: 1023px) {
  .ticket-tabs,
  .camp-tabs,
  .metrics-tabs,
  .metrics-agent-tabs,
  .notif-tabs,
  .mir-tabs,
  .control-tabs {
    flex-wrap: nowrap;
    width: 100%;                       /* la píldora ocupa la fila: se ve que es una tira */
    justify-content: flex-start;
    /* Arrastre suave y con parada en cada pestaña, como una galería. */
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    /* La barra de scroll nativa sobre una píldora de 5px de padding se ve fatal
       y roba alto. El indicio de que hay más va por el degradado de abajo. */
    scrollbar-width: none;
  }
  .ticket-tabs::-webkit-scrollbar,
  .camp-tabs::-webkit-scrollbar,
  .metrics-tabs::-webkit-scrollbar,
  .metrics-agent-tabs::-webkit-scrollbar,
  .notif-tabs::-webkit-scrollbar,
  .mir-tabs::-webkit-scrollbar,
  .control-tabs::-webkit-scrollbar { display: none; }

  .ticket-tabs button,
  .camp-tab,
  .metrics-tab,
  .notif-tabs button,
  .mir-tab {
    flex: 0 0 auto;                    /* no se comprimen hasta ser ilegibles */
    scroll-snap-align: center;
  }

  /* Las sub-pestañas de segundo nivel (`.cc-subtabs`) SÍ envuelven: no son una
     píldora, son texto subrayado sobre una línea, y ahí varias filas se leen
     bien. Solo necesitan aire entre ellas para el dedo. */
  .cc-subtabs { gap: 2px 6px; }
  .cc-subtab { padding: 9px 12px; }
}

/* Aviso de que la tira sigue: un degradado al borde derecho mientras quede algo
   por ver. `mask` con `scroll-timeline` no tiene soporte suficiente, así que se
   deja fijo — es un indicio, no un indicador exacto de posición. */
@media (max-width: 1023px) {
  .ticket-tabs,
  .camp-tabs,
  .metrics-tabs,
  .notif-tabs,
  .mir-tabs {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  }
}


/* ── 3. Tablas anchas: contexto mientras se arrastra ──────────────────────────
   Las tablas del panel ya scrollan dentro de su tarjeta (`.pg-table` con
   `overflow-x:auto` y `.pg-row` con `min-width`), así que no se pierde nada.
   Lo que se pierde es el HILO: al arrastrar hasta la columna de acciones ya no
   se ve de qué fila son, y hay que volver a la izquierda para comprobarlo.

   La primera columna se queda fija. Es la que identifica la fila (fecha, nombre,
   referencia) y es lo que convierte el arrastre en algo usable.

   `background` explícito y no `inherit`: la celda viaja por encima del resto de
   la fila, y sin fondo propio se leería el texto de debajo a través. Las filas
   de cabecera y las resaltadas llevan el suyo. */
@media (max-width: 1023px) {
  .pg-row > :first-child,
  .audit-table thead th:first-child,
  .audit-table tbody td:first-child,
  .users-table thead th:first-child,
  .users-table tbody td:first-child,
  .contacts-table thead th:first-child,
  .contacts-table tbody td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--surface);
  }
  .pg-row--head > :first-child,
  .audit-table thead th:first-child,
  .users-table thead th:first-child,
  .contacts-table thead th:first-child {
    background: var(--surface-2);
    z-index: 2;
  }
  /* Borde de corte: sin él, la columna fija y la que pasa por debajo se funden
     y parece que el texto se solapa. */
  .pg-row > :first-child::after,
  .audit-table td:first-child::after,
  .users-table td:first-child::after,
  .contacts-table td:first-child::after {
    content: "";
    position: absolute;
    top: 0; right: 0; bottom: 0;
    width: 1px;
    background: var(--border);
  }
  .pg-row > :first-child,
  .audit-table td:first-child,
  .users-table td:first-child,
  .contacts-table td:first-child { position: sticky; }
}


/* ── 4. Rejillas de formulario y de detalle ───────────────────────────────────
   Dos y tres columnas en una pantalla de 360px dejan campos de 150px: un select
   de fechas o un correo no caben, y el formulario deja de poder rellenarse.
   Todas pasan a una columna.

   Se listan una a una en vez de con un selector genérico a propósito: hay
   rejillas que NO deben colapsar (un calendario de 7 columnas sigue siendo un
   calendario, y la rejilla de 6 casillas del OTP cabe de sobra). */
@media (max-width: 639px) {
  .camp-form-grid,
  .mir-form,
  .model-fields-grid,
  .filter-row,
  .monitor-channel-stats,
  .monitor-tool-io,
  .monitor-token-grid,
  .monitor-detail-grid,
  .ec-view-grid,
  .timeout-grid,
  .mir-chooser-grid,
  .mir-chooser-grid--triple,
  .mir-cal-menu-grid,
  .qr-editor-grid,
  .tpl-builder-grid,
  .crm-lp-body {
    grid-template-columns: 1fr;
  }

  /* Filas etiqueta/valor: la etiqueta deja de tener columna fija y se pone
     encima. Con 170px reservados para la etiqueta, al valor le quedaban 150. */
  .detail-row,
  .mm-details-row,
  .survey-res-answer,
  .ll-channel-row {
    grid-template-columns: 1fr;
    gap: 2px;
  }

  /* El campo que ocupaba dos columnas ya no tiene dos que ocupar. */
  .camp-field--full { grid-column: 1; }
}


/* ── 5. Paneles flotantes con ancho fijo ──────────────────────────────────────
   Un panel de 380px en una pantalla de 320 se sale por el lado y arrastra la
   página con él. El patrón del repo para esto ya existe —`width: min(Xpx, 94vw)`,
   lo usan los modales de Notificaciones— y aquí se aplica a los dos que
   quedaban con el ancho en píxeles pelados. */
@media (max-width: 1023px) {
  .contact-drawer      { width: min(380px, 92vw); }
  .ticket-picker-modal { width: min(480px, 92vw); }
}


/* ── 6. Objetivos táctiles de las páginas ─────────────────────────────────────
   Mismo criterio que en el shell: 40px es el suelo con el que un dedo acierta
   sin mirar. Se agranda la CAJA, no el dibujo ni la letra.

   `.pg-btn--sm` y los iconos de la toolbar son los que más se repiten en las
   tablas (editar, borrar, verificar) y los que peor se aciertan: van en una
   columna estrecha, pegados unos a otros y al borde de la pantalla. */
@media (max-width: 1023px) {
  .pg-btn,
  .pg-btn--sm,
  .contacts-refresh-btn,
  .contacts-filter-btn,
  .tb-viewseg-btn {
    min-height: 40px;
  }
  .pg-btn--sm { padding: 4px 12px; }
  .tb-viewseg-btn { width: 40px; height: 36px; }

  /* Los botones de una celda de acciones dejan de pegarse entre sí. */
  .pg-row .pg-btn + .pg-btn { margin-left: 4px; }
}


/* ── 7. El resto de objetivos táctiles ────────────────────────────────────────
   Medidos en las 29 páginas, estos son los que más se repiten por debajo de
   32px. No es una lista arbitraria: sale de contar cuántas veces aparece cada
   uno demasiado pequeño en la auditoría (`camp-btn` 867 veces, `mir-cal-add`
   720, `cc-subtab` 324…).

   Crece la CAJA, nunca la letra ni el icono: agrandar el texto descolocaría
   tablas y barras que ya están ajustadas, y el problema no era leerlos — era
   acertarles. */
@media (max-width: 1023px) {
  .camp-btn,
  .qa-btn,
  .crm-mini-btn,
  .btn-xs,
  .btn-export,
  .mk-btn,
  .ig-link-btn {
    min-height: 40px;
    padding-top: 0;
    padding-bottom: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* Botones cuadrados de icono: pasan a 40×40 sin tocar el glifo de dentro. */
  .crm-viewbtn,
  .crm-icon-btn,
  .crm-col-add,
  .crm-chip-x,
  .mir-cal-nav,
  .mir-cal-add {
    min-width: 40px;
    min-height: 40px;
  }
  /* La «x» de un chip dentro de su píldora no puede medir 40 o rompe el chip:
     ahí se agranda solo el area de toque, con el dibujo en su sitio. */
  .crm-chip-x {
    min-width: 0;
    min-height: 0;
    padding: 10px;
    margin: -10px -6px -10px 0;
  }

  /* `.mir-cal-add` vive en la celda de un día del calendario, que mide lo que
     mide: ahí 40px lo desbordaría. Se queda con el ancho de su celda. */
  .mir-cal-cell .mir-cal-add { min-width: 0; width: 100%; }

  .cc-subtab { min-height: 40px; }
}


/* ── 8. Texto por debajo de 11px ──────────────────────────────────────────────
   112 combinaciones tenían texto de 10px o menos. En un escritorio a 60cm es
   letra pequeña; en un móvil a 30cm y en movimiento, no se lee.

   Se sube el SUELO, no todos los tamaños: las etiquetas de 0.82rem se quedan
   como están. Solo las que caen por debajo de 11px suben a 11px, que es donde
   empieza a leerse sin acercarse el teléfono a la cara.

   `rem` y no `px`: la escala de letra de accesibilidad (Perfil › Apariencia)
   multiplica el tamaño raíz, y en px se quedaría fuera de ese ajuste — es decir,
   quien más lo necesita no lo notaría. */
@media (max-width: 1023px) {
  .pg-row--head,
  .camp-table th,
  .audit-table th,
  .users-table th,
  .contacts-table th,
  .control-table th,
  .btn-export,
  .tb-toast-foot,
  .crm-mini-btn,
  .health-ms,
  .tools-meta-check,
  .module-subsidebar-title,
  .ph-user-role { font-size: 0.6875rem; }   /* 11px con la raíz por defecto */
}


/* ── 9. Gráficas ──────────────────────────────────────────────────────────────
   ApexCharts dibuja un `<svg>` con el ancho EN DURO que tenía su contenedor al
   renderizarse. Si se renderizó a 1440px y la pantalla es de 390, el svg sigue
   midiendo lo de antes y arrastra la página de lado. Fue el culpable número uno
   del scroll lateral (56 veces `svg`, 34 `metrics-chart-card`).

   La tarjeta recorta y el svg se escala con ella. No se toca `charts.rs`: el
   reglamento de gráficas de `CLAUDE.md` manda que el alto se pase explícito en
   px al helper, y esto no lo cambia — solo impide que el ancho reviente la
   página mientras Apex no ha vuelto a medir. */
@media (max-width: 1023px) {
  /* `max-width` SIN `overflow-x: hidden`: recortar una gráfica es peor que
     dejarla desbordar — se pierde media serie y no se ve que falte nada. Lo que
     hay que impedir es que el svg fije un ancho mayor que su tarjeta, no taparlo. */
  .metrics-chart-card,
  .metrics-chart-container,
  .ig-chart-card,
  .monitor-chart-card {
    max-width: 100%;
    min-width: 0;
  }
  .metrics-chart-container .apexcharts-canvas,
  .ig-chart-card .apexcharts-canvas,
  .monitor-chart-card .apexcharts-canvas {
    max-width: 100% !important;   /* Apex escribe el ancho inline; no hay otra vía */
  }
  .apexcharts-canvas > svg,
  .apexcharts-svg {
    max-width: 100%;
    height: auto;
  }

  /* El grid de gráficas baja su mínimo. El reglamento de Métricas de `CLAUDE.md`
     exige `auto-fit` (nunca `auto-fill`) y se respeta; lo único que cambia es el
     mínimo de columna, que en 340px no cabe en un móvil.

     OJO con `minmax(0, 1fr)`: con `auto-fit` y mínimo CERO caben infinitas
     columnas, y el grid mete una por gráfica. Probado: dejaba tarjetas de 162px
     con el título y el botón de exportar encima, sin sitio para la gráfica. El
     mínimo tiene que ser un ancho real. */
  .metrics-charts-grid,
  .metrics-charts-grid--2col {
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  }
}

/* En teléfono, una gráfica por fila y punto: dos de 150px no se leen. */
@media (max-width: 639px) {
  .metrics-charts-grid,
  .metrics-charts-grid--2col,
  .metrics-kpi-grid {
    grid-template-columns: 1fr;
  }
}


/* ── 10. Lo que solo existe al pasar el ratón ─────────────────────────────────
   Mismo fallo que tenía el caret de la barra lateral, y del mismo calibre: en
   una pantalla táctil no hay «pasar el ratón», así que un control con
   `opacity: 0` que solo aparece en `:hover` sencillamente NO EXISTE.

   `.mir-cal-add` es el botón de añadir reserva a un día del calendario: la
   acción principal del módulo Reservas, invisible en un móvil. */
@media (max-width: 1023px) {
  .mir-cal-add,
  .mir-cal-cell .mir-cal-add {
    opacity: 1;
    /* Atenuado, no oculto: sigue siendo secundario frente a lo que hay en el
       día, pero se ve que está y se puede pulsar. */
    filter: none;
  }
  .mir-cal-cell:hover .mir-cal-add { opacity: 1; }
}


/* ── 11. Calendarios: siete columnas son siete columnas ───────────────────────
   Un mes NO se puede colapsar a una columna sin dejar de ser un calendario. A
   320px, siete columnas dejan celdas de 38px: ni cabe el número del día ni se
   acierta con el dedo.

   La rejilla conserva sus siete columnas y se le pone un ancho mínimo usable;
   el contenedor scrolla de lado. Se arrastra el mes, que es un gesto que ya se
   conoce de cualquier app de calendario, en vez de leer una cuadrícula ilegible.

   La fila de nombres de día scrolla SINCRONIZADA por estar dentro del mismo
   contenedor: si se dejara fuera, las columnas se desalinearían al arrastrar. */
@media (max-width: 767px) {
  /* `.mir-cal-scroll` se añade en `pages/mir.rs`; `.crm-cal` ya existía y hace
     de contenedor en CRM. El `min-width` SIN un contenedor que scrolle empuja la
     página entera — probado: dejaba Reservas arrastrándose 186px. */
  .mir-cal-scroll,
  .crm-cal {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* El `min-width` va SIEMPRE descendiendo del contenedor que scrolla, nunca
     suelto. Así, si algún día el envoltorio desaparece del markup, la regla deja
     de aplicarse sola en vez de empujar la página entera de lado. */
  .mir-cal-scroll .mir-cal-grid,
  .mir-cal-scroll .mir-cal-dow,
  .crm-cal .crm-cal-grid {
    min-width: 560px;          /* 7 columnas de 72px + huecos: el día se lee y se pulsa */
  }
  .mir-cal-cell { min-height: 76px; }
}


/* ── 12. Tablas que aún recortaban o arrastraban la página ────────────────────
   `.crm-list-wrap` llevaba `overflow: hidden` —puesto para redondear las
   esquinas de la tabla— y eso RECORTA: 352px de columnas desaparecían sin que
   se viera que faltaba nada. `overflow-x: auto` sigue redondeando igual y deja
   llegar al contenido. */
@media (max-width: 1023px) {
  .crm-list-wrap {
    overflow-x: auto;
    overflow-y: hidden;
  }
  .crm-list-wrap,
  .crm-cal,
  .mir-cal { margin-left: 0; margin-right: 0; padding-left: 0; padding-right: 0; }
}

/* Envoltorio de la tabla de campañas (`pages/campaigns.rs`). Mismo papel que
   `.users-table-wrap` y `.audit-table-wrap`: la tarjeta que scrolla. */
.camp-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}


/* ── 13. Los objetivos táctiles que faltaban ──────────────────────────────────
   Los que siguen saliendo por debajo de 32px tras el grupo 7. Las casillas de
   verificación merecen mención aparte: un `<input type=checkbox>` mide 13px por
   defecto y es de lo que más se falla con el dedo; se agranda con `transform`
   para no descolocar la fila en la que vive. */
@media (max-width: 1023px) {
  .ec-toast-x,
  .wf-toggle-btn,
  .gt-modal-help,
  .flx-btn-new,
  .crm-btn-sec,
  .mm-sec-toggle,
  .mir-cal-title { min-height: 40px; min-width: 40px; }

  .camp-checkbox-label input[type="checkbox"],
  .camp-label input[type="checkbox"],
  .mir-switch-row input[type="checkbox"] {
    transform: scale(1.35);
    transform-origin: left center;
    margin-right: 6px;
  }

  /* Los controles de Leaflet son del mapa, no del panel: se agrandan sin tocar
     su maquetación interna. */
  .leaflet-control-zoom-in,
  .leaflet-control-zoom-out { width: 40px !important; height: 40px !important; line-height: 40px !important; }
}


/* ── 14. Texto pequeño que quedaba ────────────────────────────────────────────
   Mismo criterio que el punto 8: suelo de 11px, en `rem` para que la escala de
   letra de accesibilidad lo siga multiplicando. */
@media (max-width: 1023px) {
  .crm-list-table,
  .crm-list-table thead th,
  .crm-cal-dow,
  .cc-help,
  .cc-hint,
  .camp-cell-sub,
  .mm-meta,
  .flx-aud-meta,
  .prof-hint { font-size: 0.6875rem; }
  .crm-list-table { font-size: 0.8125rem; }
}


/* ── 15. Filas de acciones que no envolvían ───────────────────────────────────
   El mismo patrón repetido por todo el panel: una fila de botones o de campos
   con `display: flex` y sin `flex-wrap`. En escritorio sobra sitio y nadie lo
   nota; en un móvil la fila empuja la página entera de lado.

   Medido: `.tb-actions` 487px en Reservas › Recursos, `.qa-item-actions` 214px
   en QA, `.mm-collapse-inner` 495px en Multimedia. Todas envuelven. */
@media (max-width: 1023px) {
  .tb-actions,
  .qa-item-actions,
  .qa-run,
  .camp-template-actions,
  .mm-row-actions,
  .ec-row-actions,
  .mir-day-head,
  .crm-card-actions,
  .flx-aud-scope,
  .tools-card-btns,
  .pg-modal-foot {
    flex-wrap: wrap;
  }

  /* `.qa-item-actions` y compañía llevan `flex-shrink: 0` para no encogerse
     frente al texto de al lado; con la fila envuelta eso ya no hace falta y es
     justo lo que les impedía bajar de línea. */
  .qa-item-actions,
  .mm-row-actions { flex-shrink: 1; }

  /* Campos de formulario dentro de filas flex: sin `min-width: 0` el ancho
     intrínseco del contenido impide que encojan, y da igual lo que diga el flex. */
  .pg-input,
  .mir-config-input,
  .contacts-search-input,
  .camp-input,
  .ec-input { min-width: 0; max-width: 100%; }

  /* Multimedia: el cuerpo desplegable de una sección. Su contenido es una fila
     de miniaturas con ancho propio. */
  .mm-collapse-inner { max-width: 100%; }
  .mm-collapse-inner > * { max-width: 100%; }
}


/* ── 16. Cuadros de diálogo a lo ancho de la pantalla ─────────────────────────
   Un modal con padding de 24px a cada lado en una pantalla de 320 deja 272px de
   contenido. Se aprieta el padding y se deja que el modal use el ancho, que es
   lo que hay. El patrón `min(Xpx, 94vw)` del repo ya cubre el ancho; esto es
   solo el aire de dentro. */
@media (max-width: 639px) {
  .modal-card,
  .pg-modal,
  .mir-modal,
  .camp-modal,
  .ec-modal,
  .mk-modal { width: min(560px, 94vw); }

  .pg-modal-body,
  .modal-body,
  .mir-modal-body { padding-left: 14px; padding-right: 14px; }

  /* El pie del modal apila los botones: dos botones de 120px y un gap no caben
     en 272px, y el de confirmar es el que se queda fuera. */
  .pg-modal-foot,
  .modal-footer {
    flex-direction: column-reverse;   /* la acción principal, arriba y al alcance del pulgar */
    align-items: stretch;
  }
  .pg-modal-foot > *,
  .modal-footer > * { width: 100%; }
}


/* ── 17. Livechat: maestro-detalle en vez de tres columnas ────────────────────
   Livechat son tres paneles en fila: bandeja (320px) · conversación · ficha del
   contacto (320px). En una pantalla de 390px eso deja 70px para la conversación,
   que es donde está todo el trabajo del módulo.

   Tres columnas no caben. En un teléfono se ve UNA cosa a la vez:

     sin conversación elegida  ->  la bandeja, a pantalla completa
     con conversación elegida  ->  la conversación, y la bandeja en su riel de
                                   48px (que ya existe y sigue siendo clicable,
                                   así que cambiar de chat es un toque)

   Cómo se sabe cuál de los dos estados es, sin tocar Rust: la conversación vacía
   pinta `.livechat-empty` («Selecciona una conversación»). `:has()` lee eso. Es
   una técnica que el archivo ya usa en otros nueve sitios.

   Si algún día ese texto cambia de clase, esto deja de aplicarse y Livechat
   vuelve a las tres columnas — se degrada a lo de antes, no se rompe. */
@media (max-width: 1023px) {
  /* Sin conversación: solo la bandeja. */
  .livechat-layout:has(.livechat-empty) .livechat-sidebar {
    flex: 1 1 100%;
    width: 100% !important;           /* el ancho lo fija un `style=` inline */
  }
  .livechat-layout:has(.livechat-empty) .livechat-messages { display: none; }

  /* Con conversación: la bandeja expandida se superpone en vez de robarle sitio
     a la conversación. Se abre con el botón «Expandir bandeja» que ya está en el
     riel, y al elegir un chat se vuelve a plegar. */
  .livechat-layout { position: relative; }
  .livechat-layout:not(:has(.livechat-empty)) .livechat-sidebar:not(.livechat-sidebar--collapsed) {
    position: absolute;
    top: 0; bottom: 0; left: 0;
    z-index: 20;
    width: min(320px, 86vw) !important;
    box-shadow: var(--shadow-lg);
  }

  /* La ficha del contacto: PANEL DESLIZANTE, no capa flotante a medias.
     Antes la dejé en `position: absolute` dentro del layout y con el fondo que
     tenía cuando iba en la fila —pensado para estar AL LADO, no encima—: se leía
     translúcida y se montaba sobre el nombre de la conversación.

     Ahora es un panel propio: `fixed` bajo el header, opaco con la superficie
     que el repo reserva para lo que flota (`--surface-overlay`, no `--surface`),
     y por encima de todo lo demás del módulo. Se cierra con su ✕, que ya existe. */
  .livechat-contact-panel {
    position: fixed;
    top: var(--ph-h);
    right: 0;
    bottom: 0;
    z-index: 95;
    width: min(380px, 100vw) !important;
    background: var(--surface-overlay);
    border-left: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /* Su ✕ se queda a la vista aunque se baje por la ficha. */
  .lc-contact-topbar {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--surface-overlay);
    padding-top: 4px;
    padding-bottom: 8px;
  }
  .lc-contact-close { min-width: 40px; min-height: 40px; }

  /* El alto fijo `calc(100vh - 60px)` no cuenta con que en un móvil la barra del
     navegador aparece y desaparece. `dvh` sigue a la barra; donde no exista, se
     queda con el `vh` de la línea anterior. */
  .livechat-layout { height: calc(100vh - var(--ph-h)); }
  .livechat-layout { height: calc(100dvh - var(--ph-h)); }
}


/* ── 18. Sueltos medidos uno a uno ────────────────────────────────────────────
   Lo que quedaba tras los patrones compartidos. Cada uno con su medición. */
@media (max-width: 1023px) {
  /* Multimedia: `.mm-collapse` es un grid y sus hijos NO encogen por debajo de
     su ancho de contenido — `min-width` de un item de grid vale `auto`, no 0.
     Medido: 495px de contenido arrastrando la página 142px. */
  .mm-collapse-inner { min-width: 0; }
  .mm-collapse { min-width: 0; }

  /* Pagos: la fila de KPIs es flex, y un item de flex no encoge por debajo de su
     contenido salvo que se le diga (`min-width` vale `auto` por defecto). El que
     se salía era la tarjeta del total: su contenido medía 343 y con los 18px de
     padding a cada lado pedía 381, sobre 343 disponibles. */
  .pagos-page .pg-kpis > * { min-width: 0; }
  .pagos-page .pg-kpis > .pg-cards,
  .pagos-page .pg-kpi-total { flex: 1 1 100%; }

  /* Campañas › Plantillas: el botón recorta su PROPIA etiqueta (77px de caja
     para 83 de texto). No es que no quepa en la fila: es que tiene un ancho que
     su texto no respeta. Se le deja crecer con su contenido. */
  .camp-templates-toolbar { flex-wrap: wrap; }
  .camp-templates-toolbar .camp-btn { width: auto; flex: 0 0 auto; }
}


/* ── 19. El globo de ayuda, oculto, empujaba la página ────────────────────────
   `.info-tooltip-text` está `visibility: hidden` hasta que se enfoca. Pero
   `visibility: hidden` SIGUE OCUPANDO SITIO: el globo mide 280px, está anclado
   al centro del icono con `translateX(-50%)`, y si el icono cae cerca del borde
   derecho, esos píxeles invisibles hacen scrollar el contenido.

   Medido: 76px de arrastre en Usuarios › Permisos de Rol a 360px, causados por
   un globo que nadie estaba viendo. Y salía en varias páginas a la vez, porque
   el componente lo usan siete.

   Se pasa a `display: none`, que no ocupa nada. El repo ya lo hace así en las
   pestañas del Centro de Control. Se pierde el fundido de 0,15s en móvil; a
   cambio la página deja de moverse sola.

   Y cuando SÍ se muestra, se ancla por la derecha en vez de centrado: centrado
   sobre un icono que está a 20px del borde, la mitad del globo cae fuera. */
@media (max-width: 1023px) {
  .info-tooltip-text {
    display: none;
    left: auto;
    right: 0;
    transform: none;
    max-width: min(260px, calc(100vw - 40px));
  }
  .info-tooltip-wrapper:hover .info-tooltip-text,
  .info-tooltip-wrapper:focus .info-tooltip-text,
  .info-tooltip-wrapper:focus-within .info-tooltip-text {
    display: block;
  }
  /* La flecha del globo apuntaba al centro; con el globo anclado a la derecha,
     apunta al icono, que es donde nace. */
  .info-tooltip-text::before { left: auto; right: 10px; transform: none; }
}


/* ── 20. Configuraciones con navegación lateral ───────────────────────────────
   `.mir-config` es una columna de secciones de 190px fijos más el contenido. En
   343px de ancho útil, al contenido le quedaban 135 y sus campos pedían 318.
   Lo usan Reservas, Cine y Ecommerce en su pestaña de Configuración.

   Se apila: la lista de secciones arriba (en fila, envolviendo) y el contenido
   debajo, a todo el ancho. La separación pasa de un borde a la derecha a uno
   debajo, que es lo que corresponde cuando algo deja de estar al lado. */
@media (max-width: 767px) {
  .mir-config { flex-direction: column; gap: 12px; }
  .mir-config-nav {
    flex: 1 1 auto;
    width: 100%;
    flex-direction: row;
    flex-wrap: wrap;
    border-right: none;
    border-bottom: 1px solid var(--border-soft);
    padding-right: 0;
    padding-bottom: 8px;
  }
  .mir-config-navitem { flex: 0 1 auto; min-height: 40px; }
  .mir-config-content { width: 100%; }
  .mir-config-section { max-width: none; }
  .mir-config-input { max-width: 100%; }
}


/* ── 21. Últimos objetivos táctiles ───────────────────────────────────────────
   `.mir-cal-add` seguía saliendo pequeño entre 768 y 1023px: ahí el calendario
   no lleva el ancho mínimo (es de ≤767px) y la regla que le daba ancho completo
   dentro de la celda lo dejaba en 22px. Se le pone alto y ancho mínimos de
   verdad y se acabó la excepción. */
@media (max-width: 1023px) {
  .mir-cal-cell .mir-cal-add { min-width: 32px; width: auto; }

  /* Casillas de verificación sueltas en formularios: 13px por defecto. */
  .camp-checkbox-label input[type="checkbox"],
  .camp-label input[type="checkbox"],
  input[type="checkbox"].camp-check,
  .ec-check input[type="checkbox"],
  .flx-check input[type="checkbox"] {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
  }
  /* El area de toque la da la etiqueta entera, que es lo que se pulsa de verdad. */
  .camp-checkbox-label,
  .camp-label,
  .ec-check,
  .flx-check { min-height: 40px; display: inline-flex; align-items: center; gap: 8px; }
}


/* ── 22. Texto de los ejes de las gráficas ────────────────────────────────────
   ApexCharts escribe sus etiquetas a 10px. No se puede subir desde CSS sin
   pelearse con su `style` inline, así que se sube con la única propiedad que sí
   hereda al `<text>` del svg. Afecta a ejes y leyendas, no a los datos. */
@media (max-width: 1023px) {
  .apexcharts-text,
  .apexcharts-text tspan,
  .apexcharts-legend-text { font-size: 11px !important; }
}


/* ── 23. La pastilla de búsqueda a 320px ──────────────────────────────────────
   La barra de búsqueda lleva dentro el input y entre tres y cinco iconos (vista,
   embudo, limpiar, refrescar, orden). A 320px el input ya ocupa la fila entera y
   los iconos se salen: medido, 15-36px de arrastre en Contactos, Multimedia,
   Marketplace y Usuarios — el mismo componente en todos.

   Los iconos bajan a una segunda línea. Es lo que ya hace el resto de la toolbar
   desde el grupo 1; a esta le faltaba porque es un `<form>` y no se tocó. */
@media (max-width: 399px) {
  .contacts-search-form {
    flex-wrap: wrap;
    row-gap: 6px;
  }
  /* El input se queda con la primera línea entera y los iconos con la segunda. */
  .contacts-search-form > .contacts-search-input,
  .contacts-search-form > .contacts-searchbox { flex: 1 1 100%; }
}


/* ── 24. Rejillas dentro de tarjetas de configuración ─────────────────────────
   `.mir-form-grid` vive dentro de `.ec-cfg-group`, que es flex. Un item de flex
   no encoge por debajo de su contenido salvo que se le diga, así que la rejilla
   conservaba 295px dentro de 227 disponibles. Con `min-width: 0` encoge y sus
   campos, que ya son de una columna, encogen con ella. */
@media (max-width: 1023px) {
  .mir-form-grid,
  .ec-cfg-group,
  .mir-form-full { min-width: 0; }
  .mir-form-grid > label,
  .mir-form-full > label { min-width: 0; max-width: 100%; }
}


/* ── 25. Red de seguridad de los 320px ────────────────────────────────────────
   Último tramo y el más apretado. Estas dos reglas no arreglan nada concreto:
   evitan que un ancho intrínseco cualquiera —una palabra larga, un `<pre>`, una
   URL sin espacios— arrastre la página entera. No tocan la maquetación de nada
   que ya quepa.

   Lo que NO se hace aquí es `overflow-x: hidden` en el `body`: eso oculta el
   síntoma y de paso recorta lo que se salga, que es justo lo que se ha estado
   arreglando durante todo este trabajo. */
@media (max-width: 399px) {
  .app-content pre,
  .app-content code,
  .app-content .mono { max-width: 100%; overflow-x: auto; }

  /* Palabras sin espacios (correos, URLs, identificadores) dentro de texto. */
  .app-content p,
  .app-content .pg-card-l,
  .app-content .camp-cell-sub,
  .app-content .mod-desc { overflow-wrap: break-word; }
}


/* ── 26. Los dos ultimos, a 320px ─────────────────────────────────────────────
   Tarjeta de modulo en Inicio: a 320px la rejilla deja tarjetas de 128px, que
   con 20px de padding a cada lado son 88 de contenido. «Comunicaciones» mide
   ~100 en una sola palabra, asi que no hay donde partir y se sale. Se aprieta el
   padding y se permite partir palabra: un nombre de modulo cortado es peor que
   un guion.

   Pie del formulario de Campañas: `flex-wrap: nowrap` comprimia el boton a 80px
   para una etiqueta de 95 y le recortaba «Crear campa…». Que envuelva. */
@media (max-width: 399px) {
  .mod-card { padding-left: 10px; padding-right: 10px; }
  .mod-name { overflow-wrap: break-word; hyphens: auto; }

  .camp-form-footer { flex-wrap: wrap; gap: 8px; }
  .camp-form-footer > * { flex: 0 0 auto; max-width: 100%; }

  /* Hay botones de Campañas colgando de contenedores flex SIN CLASE (una fila
     suelta en el `view!`), así que no se pueden nombrar: se alcanzan por el hijo.
     `+ Nueva Campaña Automática` se quedaba en 77px para 83 de etiqueta. */
  .campaigns-page div:has(> .camp-btn) { flex-wrap: wrap; gap: 8px; }
  .camp-btn { flex: 0 0 auto; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   27. TABLAS COMO TARJETAS EN TELÉFONO Y TABLET
   ═══════════════════════════════════════════════════════════════════════════

   Una tabla que se arrastra de lado «funciona», pero con nueve columnas leer una
   fila entera obliga a recorrerla a ciegas perdiendo de vista de quién era, y a
   volver atrás para comprobarlo. Eso es soportable, no usable.

   Y NO es solo cosa de teléfonos: medido, la tabla de Contactos pide 855px y en
   una tablet de 768 su contenedor tiene 687 — «Método pago», «Amistad» y
   «Acciones» quedaban fuera. Por eso el corte es 1024px y no 640: por debajo de
   ahí no se puede dar por hecho que una tabla ancha quepa.

   En tablet las tarjetas van en DOS COLUMNAS: a 768px una sola columna
   desperdiciaría media pantalla y obligaría a un scroll vertical larguísimo.

   Cada fila pasa a ser una TARJETA con sus campos apilados y
   etiquetados. El rótulo de cada campo sale de la cabecera de su columna, que un
   script de `index.html` copia a `data-etiqueta`; aquí se pinta con `::before`.

   Por qué el rótulo no se escribe en el HTML: son 63 tablas en 20 archivos. Una
   etiqueta a mano en cada celda se desincronizaría de su cabecera al primer
   cambio de columna. Leyéndola de la cabecera, no puede mentir.

   Si el script no corriera, no hay `data-etiqueta`, el `::before` sale vacío y
   la tarjeta se ve sin rótulos —legible, solo que más pobre—. No se rompe.

   Qué NO se convierte:
   - Los calendarios: un mes es una cuadrícula, apilarlo lo destruye.
   - El tablero kanban: sus columnas SON el significado.
   - `.perms-table` y `.model-picker-table`: matrices de casillas, donde el valor
     está en el cruce fila×columna y una tarjeta por fila no dice nada.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 1023px) {

  /* ── Forma 1: <table> ──────────────────────────────────────────────────── */
  .users-table,
  .audit-table,
  .contacts-table,
  .camp-table,
  .crm-list-table,
  .pg-table:not(.pg-table--matriz) {
    display: block;
    width: 100%;
    border-collapse: separate;
  }

  .users-table thead,
  .audit-table thead,
  .contacts-table thead,
  .camp-table thead,
  .crm-list-table thead { display: none; }   /* los rótulos van ahora en cada celda */

  .users-table tbody,
  .audit-table tbody,
  .contacts-table tbody,
  .camp-table tbody,
  .crm-list-table tbody { display: block; }

  .users-table tbody tr,
  .audit-table tbody tr,
  .contacts-table tbody tr,
  .camp-table tbody tr,
  .crm-list-table tbody tr {
    display: block;
    margin: 0 0 10px;
    padding: 4px 12px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
  }

  .users-table tbody td,
  .audit-table tbody td,
  .contacts-table tbody td,
  .camp-table tbody td,
  .crm-list-table tbody td {
    display: grid;
    /* Rótulo a la izquierda, valor a la derecha. `40%` y no un ancho fijo para
       que un rótulo largo («Última conexión») no aplaste su valor. */
    grid-template-columns: minmax(84px, 38%) minmax(0, 1fr);
    gap: 10px;
    align-items: baseline;
    width: auto;
    padding: 9px 0;
    border: none;
    border-bottom: 1px solid var(--border-soft);
    /* El truncado con puntos suspensivos era para una celda de ancho fijo. En la
       tarjeta hay sitio: el dato se lee entero. */
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    overflow-wrap: break-word;
  }
  .users-table tbody tr td:last-child,
  .audit-table tbody tr td:last-child,
  .contacts-table tbody tr td:last-child,
  .camp-table tbody tr td:last-child,
  .crm-list-table tbody tr td:last-child { border-bottom: none; }

  .users-table tbody td::before,
  .audit-table tbody td::before,
  .contacts-table tbody td::before,
  .camp-table tbody td::before,
  .crm-list-table tbody td::before {
    content: attr(data-etiqueta);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--muted);
  }
  /* Celda sin rótulo (la de acciones): ocupa la fila entera y sus botones
     quedan alineados a la izquierda, no colgando de una columna vacía. */
  .users-table tbody td:not([data-etiqueta]),
  .audit-table tbody td:not([data-etiqueta]),
  .contacts-table tbody td:not([data-etiqueta]),
  .camp-table tbody td:not([data-etiqueta]),
  .crm-list-table tbody td:not([data-etiqueta]) {
    grid-template-columns: 1fr;
    padding-top: 12px;
  }

  /* ── Forma 2: rejilla `.pg-row` ────────────────────────────────────────── */
  .pg-table { overflow-x: visible; }
  .pg-row--head { display: none; }
  .pg-row,
  .pagos-page .pg-row,
  .ecommerce-page .pg-row {
    display: block;
    grid-template-columns: none;
    min-width: 0;
    margin: 0 10px 10px;
    padding: 4px 12px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
  }
  .pg-table > .pg-row:first-of-type { margin-top: 10px; }

  .pg-row > span,
  .ecommerce-page .pg-row > span {
    display: grid;
    grid-template-columns: minmax(84px, 38%) minmax(0, 1fr);
    gap: 10px;
    align-items: baseline;
    flex: none;
    padding: 9px 0;
    border-bottom: 1px solid var(--border-soft);
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    overflow-wrap: break-word;
  }
  .pg-row > span:last-child { border-bottom: none; }
  .pg-row > span::before {
    content: attr(data-etiqueta);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .pg-row > span:not([data-etiqueta]) {
    grid-template-columns: 1fr;
    padding-top: 12px;
  }

  /* Celda con VARIOS hijos —la de acciones, típicamente dos o cuatro botones—.
     Con la rejilla de dos columnas, el rótulo ocupaba la primera, el primer
     botón la segunda y el resto caía a filas nuevas, descolgados bajo el rótulo.
     Pasa a fila que envuelve: rótulo y botones seguidos, en orden. */
  .users-table tbody td:has(> :nth-child(2)),
  .audit-table tbody td:has(> :nth-child(2)),
  .contacts-table tbody td:has(> :nth-child(2)),
  .camp-table tbody td:has(> :nth-child(2)),
  .crm-list-table tbody td:has(> :nth-child(2)),
  .pg-row > span:has(> :nth-child(2)) {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
  }
  .users-table tbody td:has(> :nth-child(2))::before,
  .audit-table tbody td:has(> :nth-child(2))::before,
  .contacts-table tbody td:has(> :nth-child(2))::before,
  .camp-table tbody td:has(> :nth-child(2))::before,
  .crm-list-table tbody td:has(> :nth-child(2))::before,
  .pg-row > span:has(> :nth-child(2))::before {
    flex: 0 0 100%;             /* el rótulo, en su propia línea sobre los botones */
    margin-bottom: 2px;
  }

  /* Lo que era una píldora sigue siendo una píldora: una insignia estirada a
     todo el ancho de su columna deja de leerse como insignia y parece una barra
     de estado. Se ciñe a su texto, alineada al principio del valor. */
  .users-table tbody td > .badge,
  .audit-table tbody td > .badge,
  .contacts-table tbody td > .badge,
  .camp-table tbody td > .badge,
  .crm-list-table tbody td > .badge,
  .pg-row > span > .badge,
  .users-table tbody td > [class*="-badge"],
  .pg-row > span > [class*="-badge"],
  .pg-row > span > [class*="pg-st"],
  .users-table tbody td > .pg-actions,
  .pg-row > span > .pg-actions {
    justify-self: start;
    width: auto;
    max-width: 100%;
  }

  /* La primera columna fija (punto 3) sobra aquí: en una tarjeta no hay de qué
     perder el hilo, y `sticky` sobre un bloque apilado descoloca el borde. */
  .pg-row > :first-child,
  .audit-table tbody td:first-child,
  .users-table tbody td:first-child,
  .contacts-table tbody td:first-child {
    position: static;
    background: transparent;
  }
  .pg-row > :first-child::after,
  .audit-table td:first-child::after,
  .users-table td:first-child::after,
  .contacts-table td:first-child::after { display: none; }
}


/* ── 28. Tarjetas en DOS COLUMNAS en tablet ───────────────────────────────────
   Entre 640 y 1023px sobra ancho para dos tarjetas. Una sola columna a 768px
   desperdicia media pantalla y convierte diez filas en un scroll larguísimo.

   `auto-fit` con un mínimo real (300px): a 640 entran dos justas, a 1023 entran
   tres. Nunca una tarjeta de 150px, que es lo que pasa con `minmax(0, 1fr)`. */
@media (min-width: 640px) and (max-width: 1023px) {
  .users-table tbody,
  .audit-table tbody,
  .contacts-table tbody,
  .camp-table tbody,
  .crm-list-table tbody {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 10px;
    align-items: start;
  }
  .users-table tbody tr,
  .audit-table tbody tr,
  .contacts-table tbody tr,
  .camp-table tbody tr,
  .crm-list-table tbody tr { margin: 0; }

  /* La rejilla `.pg-row` no tiene `tbody`: la cuadrícula va en su tarjeta. */
  .pg-table {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 10px;
    align-items: start;
    padding: 10px;
    background: transparent;
    border: none;
    box-shadow: none;
  }
  .pg-row { margin: 0; }
}

/* ── 29. El texto se parte por PALABRAS, no por letras ────────────────────────
   `overflow-wrap: anywhere` corta donde sea, y un correo o un nombre acaban
   partidos a mitad de sílaba. `break-word` solo parte cuando una palabra no cabe
   entera de ninguna manera, que es el caso que había que cubrir.

   Los identificadores largos sin espacios (UUID, referencias, URL) sí se
   parten donde toque, pero en `<code>` y celdas de identificador, no en prosa. */
@media (max-width: 1023px) {
  .app-content { overflow-wrap: break-word; }
  .app-content code,
  .app-content .mono,
  .app-content [class*="-uuid"],
  .app-content [class*="-id"] { overflow-wrap: anywhere; }
}


/* ── 30. El valor de una tarjeta se ciñe a su contenido ───────────────────────
   En la rejilla de la celda, el valor ocupaba TODA la columna: una insignia de
   «Verificado» salía como una barra de 184px de lado a lado, y dejaba de leerse
   como insignia. `justify-items: start` la devuelve a su tamaño.

   Lo que SÍ debe ocupar el ancho —barras de progreso, campos de formulario,
   selectores— se exceptúa a continuación: son controles, no valores. */
@media (max-width: 1023px) {
  .users-table tbody td,
  .audit-table tbody td,
  .contacts-table tbody td,
  .camp-table tbody td,
  .crm-list-table tbody td,
  .pg-row > span { justify-items: start; }

  .users-table tbody td > input,
  .users-table tbody td > select,
  .contacts-table tbody td > input,
  .contacts-table tbody td > select,
  .contacts-table tbody td > [class*="bar"],
  .contacts-table tbody td > [class*="meter"],
  .contacts-table tbody td > [class*="progress"],
  .camp-table tbody td > [class*="bar"],
  .camp-table tbody td > [class*="progress"],
  .pg-row > span > input,
  .pg-row > span > select,
  .pg-row > span > [class*="bar"],
  .pg-row > span > [class*="progress"] { justify-self: stretch; width: 100%; }

  /* Los envoltorios flex de una celda (chips de canal, estado, ficha del
     contacto) dejan de estirar a sus hijos. */
  .contact-cell,
  .ch-type-cell,
  .contact-estado-cell { align-items: flex-start; }
}


/* ── 31. Constructores de Flujos y de CRM en táctil ───────────────────────────
   Un lienzo de nodos conectados no se puede apilar: las conexiones SON la
   información. Pero sí se puede dejar usable, y lo que lo impedía no era el
   lienzo: era que la paleta de nodos se llevaba 300px fijos de los 313
   disponibles y el lienzo se quedaba sin sitio.

   En táctil la paleta deja de ser una columna y pasa a ser una TIRA horizontal
   arriba, que se desplaza. El lienzo se queda con el ancho entero y se arrastra
   en las dos direcciones, que es el gesto que ya se conoce de cualquier mapa.

   `touch-action: pan-x pan-y` es lo que permite arrastrarlo con el dedo sin que
   el navegador se quede el gesto para hacer scroll de la página. */
@media (max-width: 1023px) {
  .flx-layout {
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
  }
  .flx-layout > * { min-width: 0; }

  /* `position: static`: un `sticky` en una barra que ya no es columna lateral no
     tiene sentido. El resto —la tira horizontal— NO va aquí: ponerlo en la barra
     entera colocaba su cabecera, su buscador y su lista uno al lado del otro.
     Solo la lista es una tira; ver «33.3» al final del archivo. */
  .flx-sidebar { position: static; }

  /* El lienzo: ancho completo y arrastrable. */
  .flx-canvas,
  .crm-flow-wrap,
  .crm-board {
    min-width: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-x pan-y;
    overscroll-behavior-x: contain;   /* arrastrar el lienzo no navega atrás */
  }

  /* Auditoría de Flujos: el detalle de un ámbito es una columna estrecha que no
     encogía. */
  .flx-aud-scope-item { min-width: 0; }
  .flx-aud-scope-item > * { max-width: 100%; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   32. CORRECCIONES TRAS PROBAR EN DISPOSITIVO
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 32.1 Livechat: fondos SÓLIDOS ────────────────────────────────────────────
   Los paneles se seguían viendo a través, y la causa está en los tokens:

     --surface          oscuro: rgba(230,235,241,0.05)   ← 95% TRANSPARENTE
     --surface-overlay  oscuro: rgba(28,32,41,0.97)      ← deja pasar un 3%

   `--surface` está pensado para una tarjeta EN FLUJO, donde debajo solo hay
   fondo; sobre contenido es casi invisible. Y `--surface-overlay` es «casi
   opaca» a propósito, que basta para un menú pequeño pero no para un panel que
   cubre media pantalla con texto debajo.

   Aquí hace falta opacidad TOTAL, así que se usan los dos únicos tokens de
   superficie sólidos en ambos temas: `--surface-3` (#1b1f28 / #e6ebf1) y
   `--bg-2` (#191c24 / #e6ebf1). */
@media (max-width: 1023px) {
  .livechat-contact-panel { background: var(--surface-3); }

  /* La bandeja también: cuando se superpone a la conversación llevaba
     `--surface`, es decir, nada. */
  .livechat-layout:not(:has(.livechat-empty)) .livechat-sidebar:not(.livechat-sidebar--collapsed) {
    background: var(--bg-2);
  }
}


/* ── 32.2 Livechat: sin conversación, la bandeja no se pliega ─────────────────
   Plegar la bandeja cuando no hay ningún chat abierto deja un riel de iconos
   centrado en una pantalla vacía: no lleva a ninguna parte y no hay a qué
   volver. El botón de plegar solo tiene sentido cuando hay una conversación
   detrás que mirar.

   Se oculta el botón y se fuerza la bandeja a ancho completo aunque el estado
   diga «plegada» (se puede llegar plegada desde una conversación anterior). */
@media (max-width: 1023px) {
  /* Los DOS botones: el de plegar (barra desplegada) y el de expandir (barra ya
     plegada). Se localiza el de plegar por su `title`, que es lo único que lo
     distingue de los otros iconos de la barra — comparten clase. */
  .livechat-layout:has(.livechat-empty) .lc-topbar--collapsed { display: none; }
  .livechat-layout:has(.livechat-empty) .lc-topbar [title="Colapsar bandeja"] { display: none; }
  .livechat-layout:has(.livechat-empty) .livechat-sidebar,
  .livechat-layout:has(.livechat-empty) .livechat-sidebar--collapsed {
    width: 100% !important;
    flex: 1 1 100%;
  }
  .livechat-layout:has(.livechat-empty) .livechat-sidebar--collapsed .lc-list-wrap {
    display: block;                 /* la lista vuelve, que es lo único que hay */
  }
}


/* ── 32.3 Las pestañas se reacomodan, NUNCA se ocultan ────────────────────────
   El degradado del borde derecho difuminaba las últimas pestañas para indicar
   que la tira seguía. Pero una pestaña medio borrada se lee como deshabilitada,
   y eso estorba justo a lo que sirve la barra: navegar.

   Se quita el degradado y las pestañas ENVUELVEN: todas visibles, siempre. La
   píldora deja de ser una cápsula de una sola línea y pasa a ser un bloque
   redondeado que crece hacia abajo — que es lo que de verdad es cuando hay
   ocho pestañas y 360px. */
@media (max-width: 1023px) {
  .ticket-tabs,
  .camp-tabs,
  .metrics-tabs,
  .metrics-agent-tabs,
  .notif-tabs,
  .mir-tabs,
  .control-tabs {
    -webkit-mask-image: none;
            mask-image: none;
    flex-wrap: wrap;
    row-gap: 4px;
    overflow-x: visible;
    border-radius: 14px;            /* cápsula -> bloque: con varias filas, 999px deforma */
    scroll-snap-type: none;
  }
  .ticket-tabs button,
  .camp-tab,
  .metrics-tab,
  .notif-tabs button,
  .mir-tab {
    flex: 0 1 auto;
    scroll-snap-align: none;
  }
}


/* ── 32.4 Tarjetas: la cabecera de la tabla ya no se pinta ────────────────────
   En Pagos seguía saliendo la fila de cabecera entera («FechaMétodoMonto…»)
   encima de las tarjetas. Sobra: cada tarjeta ya lleva el rótulo de cada campo
   a su izquierda.

   Estaba oculta con `.pg-row--head { display: none }`, pero la regla de al lado
   —`.pagos-page .pg-row`— tiene MÁS especificidad (0,2,0 contra 0,1,0), también
   casa con la cabecera (lleva las dos clases) y le devolvía el `display: block`.
   Se excluye explícitamente. */
@media (max-width: 1023px) {
  .pg-row--head,
  .pagos-page .pg-row--head,
  .ecommerce-page .pg-row--head { display: none !important; }
}


/* ── 32.5 Tarjetas: todo alineado igual y con iconos que se ven ───────────────
   Unas tablas centraban su contenido y otras no, y apiladas en tarjetas esa
   mezcla se nota mucho más que en una tabla. Todo al principio de la línea: es
   donde empieza a leerse y donde ya está el rótulo.

   Y los iconos de acción salían de 30px de ancho por 40 de alto — altos pero
   estrechos, casi invisibles. Pasan a 40×40 con su dibujo más grande. */
@media (max-width: 1023px) {
  .users-table tbody td,
  .audit-table tbody td,
  .contacts-table tbody td,
  .camp-table tbody td,
  .crm-list-table tbody td,
  .pg-row > span {
    text-align: left;
  }
  .users-table tbody td > *,
  .audit-table tbody td > *,
  .contacts-table tbody td > *,
  .camp-table tbody td > *,
  .crm-list-table tbody td > *,
  .pg-row > span > * { text-align: left; }

  /* Iconos de acción: caja cuadrada de verdad y glifo legible. */
  .pg-row .pg-btn,
  .pg-row button,
  .users-table tbody td button,
  .audit-table tbody td button,
  .contacts-table tbody td button,
  .camp-table tbody td button,
  .crm-list-table tbody td button {
    min-width: 40px;
    min-height: 40px;
    padding-left: 10px;
    padding-right: 10px;
  }
  .pg-row .pg-btn svg,
  .pg-row button svg,
  .users-table tbody td button svg,
  .audit-table tbody td button svg,
  .contacts-table tbody td button svg,
  .camp-table tbody td button svg,
  .crm-list-table tbody td button svg {
    width: 18px;
    height: 18px;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   33. SEGUNDA RONDA DE CORRECCIONES TRAS PROBAR EN DISPOSITIVO
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 33.1 El texto largo SIN puntos de corte reventaba las tarjetas ───────────
   Este es el mismo fallo en Usuarios y en Permisos, y la causa es una sutileza
   de CSS que conviene dejar escrita:

     `overflow-wrap: break-word`  parte una palabra para que no DESBORDE, pero
                                  NO reduce el tamaño mínimo intrínseco.
     `overflow-wrap: anywhere`    sí lo reduce.

   Las celdas de una tarjeta son rejillas. Una rejilla dimensiona sus columnas
   por el contenido mínimo, y con `break-word` ese mínimo sigue siendo la palabra
   entera. Resultado medido: `dpimentel@smartautomatai.com` (sin guiones ni
   puntos donde partir) dejaba la celda pidiendo 310px sobre 245 disponibles, y
   un UUID de 36 caracteres estiraba la tabla de Permisos a **920px dentro de un
   contenedor de 539** — de ahí el scroll lateral y los rótulos cortados
   («BRE» en vez de «NOMBRE»).

   Se cambió a `break-word` en la ronda anterior justamente para evitar que la
   prosa se partiera letra a letra. Con `anywhere` eso NO vuelve: el algoritmo de
   línea usa primero los espacios y solo parte dentro de una palabra cuando no
   hay ninguna otra opción. Lo que cambia es que ahora un correo o un UUID sí
   tienen esa opción, en vez de empujar la tabla entera. */
@media (max-width: 1023px) {
  .users-table tbody td,
  .audit-table tbody td,
  .contacts-table tbody td,
  .camp-table tbody td,
  .crm-list-table tbody td,
  .mk-up-row > span,
  .pg-row > span {
    overflow-wrap: anywhere;
    min-width: 0;
  }

  /* Y la tabla no puede pasarse de su contenedor pase lo que pase dentro.
     `min-width` PRIMERO: en CSS el mínimo gana al máximo, así que un
     `min-width: 920px` deja `max-width: 100%` en papel mojado — que es
     exactamente lo que pasaba en Permisos, clavada en 920px dentro de un
     contenedor de 539. Estos mínimos existen para que la tabla NO se aplaste
     mientras es una tabla; convertida en tarjetas ya no aplican. */
  .perms-table,
  .ticket-panel-scroll .contacts-table,
  .notif-table-scroll .notif-table,
  .model-picker-table,
  .pg-row,
  .pagos-page .pg-row,
  .ecommerce-page .pg-row { min-width: 0; }

  .users-table,
  .audit-table,
  .contacts-table,
  .camp-table,
  .crm-list-table,
  .perms-table,
  .notif-table,
  .model-picker-table,
  .pg-table { max-width: 100%; }

  /* El rótulo deja de ser un porcentaje: con la tabla estirada, el 38% se
     convertía en 339px de etiqueta para «Módulo». Un tope en píxeles mantiene la
     proporción legible sea cual sea el ancho de la tarjeta. */
  .users-table tbody td,
  .audit-table tbody td,
  .contacts-table tbody td,
  .camp-table tbody td,
  .crm-list-table tbody td,
  .pg-row > span {
    grid-template-columns: minmax(80px, 34%) minmax(0, 1fr);
  }
}


/* ── 33.2 Marketplace › Subida también es una tabla ───────────────────────────
   `.mk-up-table` es una rejilla de SEIS columnas (`.mk-up-row`) que no estaba en
   el tratamiento de tarjetas, así que seguía siendo una tabla y perdía las
   columnas de la derecha — ID y NOTAS desaparecían.

   Lleva `.pg-table` en su clase, así que hereda casi todo; lo suyo es la rejilla
   de columnas propia, que hay que deshacer igual que la de `.pg-row`. */
@media (max-width: 1023px) {
  .mk-up-table .mk-up-row--head,
  .mk-up-row--head { display: none !important; }

  .mk-up-row {
    display: block;
    grid-template-columns: none;
    min-width: 0;
    margin: 0 10px 10px;
    padding: 4px 12px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
  }
  .mk-up-row > span {
    display: grid;
    grid-template-columns: minmax(80px, 34%) minmax(0, 1fr);
    gap: 10px;
    align-items: baseline;
    justify-items: start;
    padding: 9px 0;
    border-bottom: 1px solid var(--border-soft);
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    text-align: left;
  }
  .mk-up-row > span:last-child { border-bottom: none; }
  .mk-up-row > span::before {
    content: attr(data-etiqueta);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .mk-up-row > span:not([data-etiqueta]) { grid-template-columns: 1fr; padding-top: 12px; }
  .mk-up-row > span:has(> :nth-child(2)) {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  }
  .mk-up-row > span:has(> :nth-child(2))::before { flex: 0 0 100%; margin-bottom: 2px; }
}


/* ── 33.3 Flujos: solo la LISTA es una tira, no la barra entera ───────────────
   La barra lateral del constructor tiene tres partes apiladas: la cabecera con
   el título y «+ Nuevo», el buscador, y la lista de flujos. Ponerla entera en
   `flex-direction: row` las colocó las tres UNA AL LADO DE OTRA: el título y el
   buscador aplastados a la izquierda y las tarjetas de flujo convertidas en
   columnas de 30px. Eso es lo que se veía roto.

   La barra vuelve a ser una columna; lo único que se convierte en tira
   horizontal es la lista. */
@media (max-width: 1023px) {
  .flx-sidebar {
    flex-direction: column;
    overflow-x: visible;
    padding: 14px;
  }
  .flx-sidebar > * { flex: 0 0 auto; }

  .flx-sidebar .flx-flow-list {
    flex-direction: row;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overflow-y: hidden;
    padding-bottom: 6px;
    max-height: none;
    scrollbar-width: none;
  }
  .flx-sidebar .flx-flow-list::-webkit-scrollbar { display: none; }
  /* Cada tarjeta con ancho propio: en una tira, `1fr` las dejaría en nada. */
  .flx-sidebar .flx-flow-card {
    flex: 0 0 auto;
    width: min(230px, 72vw);
    scroll-snap-align: start;
  }
  .flx-sidebar .flx-flow-list { scroll-snap-type: x proximity; }
}


/* ── 33.4 Mapa: la barra plegada deja de robarle sitio ────────────────────────
   El mapa ya era responsive; lo que sobraba era el hueco. Plegada, la barra
   seguía ocupando 62px más los 32 de separación: 94px de nada en una pantalla
   donde el mapa es todo.

   Plegada pasa a ocupar CERO, y la pestaña para volver a abrirla se pega al
   borde. Ese `left` es explícito a propósito (ver el comentario de
   `.mapa-collapse-tab`): la pestaña se ancla al contenedor, así que hay que
   moverla con el panel o se queda flotando en medio del mapa. */
.mapa-body--collapsed { gap: 0; }
.mapa-controls--collapsed {
  width: 0;
  min-width: 0;
  padding: 0;
  border-width: 0;
  overflow: hidden;
}
.mapa-body--collapsed .mapa-collapse-tab { left: 0; }


/* ═══════════════════════════════════════════════════════════════════════════
   34. TERCERA RONDA — HUECO INFERIOR, PORTADA Y BUSCADOR
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 34.1 Los 60px muertos del final de la página ─────────────────────────────
   Varias páginas dejaban una franja vacía abajo por la que había que scrollear
   sin encontrar nada. La causa es exacta y sale de medir: el shell es
   `min-height: 100vh` y CONTIENE el header de 60px; dentro, el contenedor de la
   página vuelve a pedir `min-height: 100vh`. Total: 100vh + 60px, o sea 60px de
   scroll que no lleva a ningún sitio.

   Medido en Perfil: shell de 960px en una ventana de 900, con 60px de scroll
   vertical. Y pasaba en TODOS los anchos, escritorio incluido — no era un fallo
   de móvil, solo se nota más ahí.

   `dvh` va después de `vh` a propósito: donde exista, la altura sigue a la barra
   del navegador (que aparece y desaparece al scrollear en un móvil); donde no,
   se queda con la línea anterior. */
.home-container,
.page-container,
.profile-page,
.mk-page,
.sdui-page {
  min-height: calc(100vh - var(--ph-h));
  min-height: calc(100dvh - var(--ph-h));
}
.livechat-page {
  height: calc(100vh - var(--ph-h));
  height: calc(100dvh - var(--ph-h));
}


/* ── 34.2 Portada: una columna por fila en el teléfono ────────────────────────
   Una regla a ≤480px forzaba `repeat(2, 1fr)`. En las vistas de LISTA —mosaicos,
   contenido, lista y detalles, donde la tarjeta es icono + texto en horizontal—
   dos columnas dejan ~165px por tarjeta: el nombre del módulo se parte en tres
   líneas y el icono se come la mitad del ancho.

   Esas cuatro pasan a una sola columna. Las vistas de MOSAICO cuadrado (xl, m,
   s, l) se quedan en dos: ahí la tarjeta es un icono centrado con su nombre
   debajo, y dos por fila se leen bien y se recorren más rápido. */
@media (max-width: 639px) {
  .modules-grid.mg--mosaicos,
  .modules-grid.mg--contenido,
  .modules-grid.mg--lista,
  .modules-grid.mg--detalles {
    grid-template-columns: 1fr;
  }
}


/* ── 34.3 La cabecera de Inicio, apilada por debajo de 900px ──────────────────
   Por debajo de 900px el saludo y el buscador dejan de compartir fila: arriba el
   nombre con todo el ancho que pueda, y debajo el buscador a ancho completo.

   **Los 900px son un tramo propio, fuera del contrato de 1024/639/399**
   (`docs/frontend/arquitectura/responsive.md`). Está pedido así y tiene sentido
   medido: el problema no es el ancho de la pantalla sino que el saludo y el
   buscador se reparten una fila, y a 900 el saludo ya baja de 292px a 173.

   La cabecera se pinta de DOS maneras distintas según Identidad Visual, y hay
   que tratar las dos o el arreglo solo se ve en una de ellas:

     `overlay: false` (de fábrica)  el saludo y el buscador van en
                                    `.home-topline`, DEBAJO del banner, en una
                                    fila flex.
     `overlay: true`                van DENTRO del banner, cada uno en una celda
                                    de una rejilla 3×3 que se resuelve con
                                    `position: absolute` y `max-width: 33.333%`.

   Ese tope de un tercio es lo que recorta el nombre: medido con `overlay`
   activo, el saludo cae a 160px a 640, a 104 a 390 y a 80 a 320 — «Hola, Di…».
   Y no basta con sacar del absoluto a `.hb-content`: quienes están posicionados
   son los `.hb-slot`, así que sin tocarlos siguen anclados a `.home-banner`
   —que es `position: relative`— y no se apila nada. Es justo lo que le faltaba a
   la versión anterior de este bloque, que por eso no se notó.

   El degradado de apoyo se queda: la cabecera sigue DENTRO del banner, sobre el
   dibujo o la foto, así que el texto blanco lo necesita igual. */
@media (max-width: 900px) {

  /* ── Cabecera debajo del banner (de fábrica) ── */
  .home-topline {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    margin-bottom: 22px;
  }
  .home-topline .home-welcome { width: 100%; min-width: 0; }

  /* ── Cabecera dentro del banner ── */
  /* El banner deja de tener altura fija: crece con lo que ahora contiene. */
  .home-banner--overlay {
    height: auto;
    min-height: var(--hb-h);
  }
  .home-banner--overlay .hb-content {
    position: static;
    inset: auto;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: clamp(14px, 2vw, 22px) clamp(16px, 2.4vw, 26px);
    pointer-events: auto;      /* en absoluto no los recibía; ahora el input se usa */
  }
  /* Los dos salen de la rejilla 3×3 y pasan a ser dos filas. El `transform` se
     anula porque las celdas centradas lo usan para centrarse (`translateX`), y
     el `text-align` porque una celda «-der» alinea su texto a la derecha: en una
     fila a ancho completo eso dejaría el saludo pegado al borde contrario. */
  .home-banner--overlay .hb-slot {
    position: static;
    transform: none;
    max-width: 100%;
    width: 100%;
    text-align: left;
  }
  /* El saludo va SIEMPRE arriba y el buscador debajo, elija el owner la celda
     que elija: el orden lo da el marcado (`home.rs` pinta saludo y luego
     buscador), que aquí es el orden de las filas. */

  /* Dentro del banner el título se corta con puntos suspensivos porque la altura
     era fija. Ahora el banner crece, así que puede envolver y leerse entero. */
  .home-banner--overlay .home-welcome-title,
  .home-banner--overlay .home-welcome-sub {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }

  /* ── Las dos disposiciones ── */
  .home-search {
    flex: 1 1 auto;
    width: 100%;
    /* El suelo de 260px del buscador desborda dentro del banner a 320px: ahí la
       fila útil son 239px una vez descontado el relleno. */
    min-width: 0;
  }
  .home-welcome { min-width: 0; }
  .home-welcome-title { overflow-wrap: break-word; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   35. LIVECHAT EN TÁCTIL — LA BANDEJA SALE DE LA FILA
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 35.1 La bandeja deja de ser una columna y pasa a ser un cajón ────────────
   Hasta aquí, con una conversación abierta en el teléfono, la bandeja plegada
   seguía siendo un riel de 48px de avatares. Medido en 390px: 48 de riel + 1 de
   borde = 49px que se le restan a la conversación, un 12,5% de la pantalla, para
   mostrar avatares recortados que además hay que desplegar para leer.

   La bandeja sale del todo de la fila y se convierte en un cajón que se desliza
   sobre la conversación. Lo abre un botón nuevo en la cabecera del chat
   (`.mh-chats-btn`), que es donde el pulgar ya está.

   Dos detalles que hacen que funcione y no son obvios:

   · El cajón está en `position: fixed` en LOS DOS estados y lo que cambia es el
     `transform`. Si el plegado se hiciera con `width: 0` y el desplegado con
     `fixed`, no habría animación —no se interpola entre dos modos de
     posicionamiento— y el panel aparecería de golpe.
   · Al elegir una conversación el cajón se cierra solo. Eso NO se puede hacer
     desde aquí: lo hace `elegir_chat` en `livechat.rs`, que mira el ancho en el
     momento del toque. Sin eso, elegir un chat lo abriría detrás del cajón. */
.lc-chats-backdrop { display: none; }

@media (max-width: 1023px) {
  /* Sin conversación elegida la bandeja sigue siendo la página entera (regla
     17): este bloque solo habla del caso «hay un chat abierto».

     Los dos estados se enumeran con `:not(--collapsed)` y `.--collapsed` en vez
     de escribir una sola regla sobre `.livechat-sidebar`, y no es por gusto: la
     regla 17 ya define el estado desplegado con
     `…:not(:has(.livechat-empty)) .livechat-sidebar:not(.livechat-sidebar--collapsed)`,
     que son CUATRO clases. Una regla de tres perdería contra ella por
     especificidad aunque vaya después —medido: la bandeja se quedaba en
     `position: absolute; z-index: 20; width: 320px`, o sea POR DEBAJO del telón
     (z 95)—. Empatando a cuatro, gana la de más abajo, que es esta. */
  .livechat-layout:not(:has(.livechat-empty)) .livechat-sidebar:not(.livechat-sidebar--collapsed),
  .livechat-layout:not(:has(.livechat-empty)) .livechat-sidebar.livechat-sidebar--collapsed {
    position: fixed;
    top: var(--ph-h);
    left: 0;
    bottom: 0;
    z-index: 96;
    width: min(400px, 88vw) !important;   /* gana al `width` del `style=` inline */
    max-width: 100vw;
    background: var(--bg-2);              /* sólido: flota sobre el chat */
    border-right: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    transition: transform 0.24s cubic-bezier(0.4, 0, 0.2, 1);
  }
  .livechat-layout:not(:has(.livechat-empty)) .livechat-sidebar.livechat-sidebar--collapsed {
    transform: translateX(-100%);
  }
  .livechat-layout:not(:has(.livechat-empty)) .livechat-sidebar:not(.livechat-sidebar--collapsed) {
    transform: none;
  }
  /* El riel de avatares era el sustituto de la bandeja cuando había sitio para
     48px. Ahora el sustituto es el botón de la cabecera, así que sobra. */
  .livechat-layout:not(:has(.livechat-empty)) .lc-rail-list,
  .livechat-layout:not(:has(.livechat-empty)) .lc-topbar--collapsed { display: none; }

  /* Y el tirador de redimensionar no tiene sentido sobre un cajón. */
  .lc-resizer { display: none; }

  /* Telón: oscurece el chat y se cierra al tocarlo (el `on:click` está en el
     componente). Va justo por debajo del cajón y por encima de todo lo demás
     del módulo. */
  .lc-chats-backdrop {
    display: block;
    position: fixed;
    inset: var(--ph-h) 0 0 0;
    z-index: 95;
    background: var(--scrim);
  }
  /* Salvo cuando no hay conversación: ahí la bandeja ES la página y un telón
     solo oscurecería la propia lista que se está mirando. */
  .livechat-layout:has(.livechat-empty) .lc-chats-backdrop { display: none; }

  /* «← Ant» y «Sig →» de la bandeja medían 24px de alto: por debajo del suelo
     táctil de 40. Se veía ahora y no antes porque hasta este bloque la lista de
     conversaciones nunca llegó a tener más de una página en el banco. */
  .livechat-sidebar .pagination button {
    min-height: 40px;
    padding: 5px 14px;
  }
}


/* ── 35.2 El botón que abre el cajón ──────────────────────────────────────────
   Vive en la cabecera de la conversación, pegado al nombre del contacto. En
   escritorio no se pinta: allí la bandeja está siempre a la vista y un botón
   para «ver las conversaciones» no significaría nada. */
.mh-chats-btn { display: none; }

@media (max-width: 1023px) {
  .mh-chats-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 1px solid var(--control-border);
    border-radius: 10px;
    background: var(--control-bg);
    color: var(--text);
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
  .mh-chats-btn:hover,
  .mh-chats-btn:active { background: var(--control-bg-hover); }

  /* Con el botón delante, el nombre se queda con lo que sobre — y ese resto ya
     no es fijo, así que el tope de 260px deja de servir. */
  .messages-header-id {
    flex: 1 1 auto;
    min-width: 0;
  }
  .mh-contact-name { max-width: 100%; min-width: 0; }
}

/* Las etiquetas «IA pausada» y «Resuelta» comparten línea con el nombre, y en
   320px se lo comían ENTERO: quedaba el botón, el chevron y la etiqueta, sin
   saber con quién se está hablando. Medido a 320px: nombre 0px.

   Se deja envolver el bloque y se le da al nombre el resto de la primera línea;
   la etiqueta baja sola a la segunda cuando no cabe, y solo cuando no cabe —a
   639px sigue al lado, porque ahí hay sitio. Ninguna de las dos se oculta: el
   estado de la conversación es justo lo que hay que ver antes de escribir.

   Las dos declaraciones del nombre hacen cada una un trabajo distinto, y con una
   sola esto no funciona (las dos versiones están medidas):

     `flex: 1 1 0`   la base es CERO, así que el nombre nunca es el que provoca
                     el salto de línea. Con `auto`, su ancho natural (~190px) no
                     cabía junto al botón y bajaba ÉL, dejando la cabecera en
                     tres filas de 119px.
     `min-width`     es lo que empuja a la etiqueta abajo: sin un suelo, los tres
                     caben siempre en una línea y el nombre se queda con lo que
                     sobre — 0px a 320px, que es el fallo original. Con 150 de
                     suelo, la suma se pasa del ancho y la que salta es la
                     etiqueta, que es la que sobra. El 132 sale de medir: con
                     150 el que no cabía en la primera línea era el nombre y la
                     cabecera se iba a TRES filas (119px) en un teléfono de 320
                     —la barra de scroll se lleva 15px que no estaban en la
                     cuenta—.

   El `min()` es el seguro: `min-width` gana a `max-width`, así que un suelo en
   píxeles a secas desbordaría en una pantalla más estrecha que el suelo. */
@media (max-width: 639px) {
  .messages-header-id {
    flex-wrap: wrap;
    row-gap: 4px;
  }
  .mh-contact-name {
    flex: 1 1 0;
    min-width: min(132px, 100%);
  }
  .messages-header-id .badge { flex: 0 0 auto; }
}


/* ── 35.3 Las acciones de la cabecera, en iconos ──────────────────────────────
   «Reanudar IA», «Pausar IA» y «Asignarme» ocupaban cuatro palabras en una
   cabecera de 390px: envolvían a una segunda y a veces tercera línea y empujaban
   el nombre del contacto fuera de la vista.

   Ahora cada botón lleva su icono delante y su texto detrás. En escritorio se
   ven los dos; en táctil solo el icono, con el nombre en `title` y en
   `aria-label` para el lector de pantalla — que es el motivo de que la etiqueta
   se oculte con `display: none` en un `<span>` aparte y no borrando el texto. */
.btn-action--resume,
.btn-action--pause,
.btn-action--assign,
.btn-action--unassign {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}
/* El icono se referencia por ELEMENTO, no por su clase. Los seis van dentro de
   `<crate::icons::Icon>`, que resuelve iconset → base → el svg escrito aquí: si
   hay un iconset instalado, el svg que llega es OTRO y no lleva `.ba-ico`. Los
   envoltorios del componente son `display: contents`, así que el hijo flex del
   botón es el `<svg>` en los tres casos. */
.mh-chats-btn svg,
.btn-action--resume svg,
.btn-action--pause svg,
.btn-action--assign svg,
.btn-action--unassign svg,
.reply-send svg { flex: 0 0 auto; }

@media (max-width: 1023px) {
  .action-bar .ba-label { display: none; }
  .action-bar .btn-action--resume,
  .action-bar .btn-action--pause,
  .action-bar .btn-action--assign,
  .action-bar .btn-action--unassign {
    width: 40px;
    min-width: 40px;
    height: 40px;
    padding: 0;
    gap: 0;
    border-radius: 50%;
  }
  /* La cabecera ya no envuelve: nombre a la izquierda, acciones a la derecha. */
  .messages-header {
    padding: 10px 12px;
    gap: 8px;
    flex-wrap: nowrap;
  }
  .action-bar { flex-wrap: nowrap; flex: 0 0 auto; }
  /* Las ramas «no toca mostrar este botón» devuelven un `<span></span>` vacío
     (livechat.rs). No pintan nada, pero SÍ cuentan como hijo del flex y cobran
     su `gap`: 8px por cada una. Medido a 320px, esos 8px eran justo los que
     hacían que el nombre del contacto no cupiera al lado de su botón y la
     cabecera se fuera a tres filas. */
  .action-bar > span:empty { display: none; }
  /* El error de una acción sí puede envolver, pero debajo, no entre los botones. */
  .action-error { flex: 1 1 100%; order: 9; max-width: 100%; }
}


/* ── 35.4 La barra de enviar mensajes ─────────────────────────────────────────
   Era un recuadro de 72px de alto siempre —vacío o no— con un botón cuadrado de
   texto al lado. Pasa a ser lo que se espera de un mensajero: una píldora que
   contiene el cuadro de texto y el botón de enviar, con el icono de avión.

   El cuadro arranca en una línea y CRECE al escribir hasta 140px (lo sube el
   script de `index.html` y lo devuelve a una línea `livechat.rs` al enviarse:
   vaciarlo por código no dispara ningún evento `input`). Por eso `resize: none`
   —la esquinita de arrastrar pelea con el alto automático— y por eso el `rows=1`
   del marcado: es el suelo del que parte.

   Los selectores llevan tres clases a propósito. `html.light-mode
   .reply-textarea` (0,2,1) le repinta fondo y borde al cuadro; para que dentro
   de la píldora sea transparente hay que ganarle, y `.reply-box
   .reply-input-row .reply-textarea` (0,3,0) lo hace sin recurrir a
   `!important`. */
.reply-box { padding: 12px 20px 14px; gap: 6px; }

.reply-input-row {
  align-items: flex-end;
  gap: 8px;
  padding: 5px 5px 5px 6px;
  border: 1px solid var(--control-border);
  border-radius: 24px;
  background: var(--control-bg);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.reply-input-row:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft-bg);
}

.reply-box .reply-input-row .reply-textarea {
  min-height: 40px;
  max-height: 140px;
  resize: none;
  border: 1px solid transparent;
  border-radius: 18px;
  background: transparent;
  padding: 10px 10px;
  font-size: 0.9rem;
  scrollbar-width: thin;
}
.reply-box .reply-input-row .reply-textarea:focus {
  border-color: transparent;
  background: transparent;
}

.reply-send {
  align-self: flex-end;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  flex: 0 0 auto;
  height: 38px;
  padding: 0 16px 0 14px;
  border-radius: 19px;
  transition: filter 0.15s, transform 0.1s, opacity 0.15s;
}
.reply-send svg { transform: translateX(-1px); }
.reply-send:hover:not(:disabled) { filter: brightness(1.14); }
.reply-send:active:not(:disabled) { transform: scale(0.95); }

/* Enviando: el avión late en vez de cambiar el texto, que en el teléfono ni se
   ve porque la etiqueta está oculta. */
.reply-send--loading svg { animation: lc-send-pulse 0.9s ease-in-out infinite; }
@keyframes lc-send-pulse {
  0%, 100% { opacity: 1; transform: translateX(-1px); }
  50%      { opacity: 0.35; transform: translateX(4px); }
}
@media (prefers-reduced-motion: reduce) {
  .reply-send--loading svg { animation: none; opacity: 0.5; }
  .livechat-layout:not(:has(.livechat-empty)) .livechat-sidebar:not(.livechat-sidebar--collapsed),
  .livechat-layout:not(:has(.livechat-empty)) .livechat-sidebar.livechat-sidebar--collapsed { transition: none; }
}

@media (max-width: 639px) {
  /* En el teléfono el botón es solo el avión, redondo: la palabra «Enviar» al
     lado se come 56px del cuadro de escritura. */
  .reply-send-label { display: none; }
  .reply-send {
    width: 40px;
    height: 40px;
    padding: 0;
    gap: 0;
    border-radius: 50%;
  }
  .reply-box { padding: 10px 12px 12px; }
  /* Por debajo de 16px iOS hace zoom al enfocar el cuadro y deja la página
     desencuadrada al salir. */
  .reply-box .reply-input-row .reply-textarea { font-size: 16px; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   36. LA PASTILLA DEL MÓDULO EN PERMISOS
   ═══════════════════════════════════════════════════════════════════════════ */


/* La pastilla con el nombre del módulo lleva `white-space: nowrap`: en una
   tabla con columnas anchas está bien —una pastilla partida en dos líneas se lee
   mal—, pero en modo tarjeta la celda es una rejilla de dos columnas y el nombre
   largo de un módulo no cabe en la segunda.

   Medido a 320px en Permisos por rol y por departamento: la pastilla pide 179px
   sobre una columna de 150, y esos 29px suben por la tabla hasta sacar 15px de
   scroll a la página. Es lo único que quedaba desbordando en todo el panel a ese
   ancho.

   Por debajo de 1024px la pastilla envuelve. Sigue siendo una pastilla —el radio
   de 999px y el fondo se mantienen—, solo que a dos líneas cuando hace falta. */
@media (max-width: 1023px) {
  .perm-module-chip {
    white-space: normal;
    overflow-wrap: anywhere;
    min-width: 0;
    line-height: 1.25;
    text-align: left;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   37. ADJUNTAR ARCHIVOS EN EL LIVECHAT
   ═══════════════════════════════════════════════════════════════════════════ */


/* El clip entra DENTRO de la píldora del bloque 35, a la izquierda del cuadro de
   escritura, que es donde se busca en cualquier mensajería. La píldora ya era
   `flex` con `align-items: flex-end`, así que el clip se apoya en la misma línea
   base que el botón de enviar aunque el cuadro crezca a varias líneas. */
.reply-input-row { padding-left: 4px; }

/* El selector de archivos no se puede maquetar —cada navegador pinta su propio
   botón— así que se oculta y lo abre el clip por código. */
.reply-file-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.reply-attach {
  align-self: flex-end;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.reply-attach svg { flex: 0 0 auto; }
.reply-attach:hover:not(:disabled) { background: var(--control-bg-hover); color: var(--text); }
.reply-attach:active:not(:disabled) { transform: scale(0.94); }
.reply-attach:disabled { opacity: 0.45; cursor: not-allowed; }

/* Canal que no admite archivos: el clip se apaga a la vista pero SIGUE pulsable,
   porque al pulsarlo es cuando dice por qué. Un botón deshabilitado no muestra su
   `title`, y en una pantalla táctil no hay `title` que valga. */
.reply-attach--off { opacity: 0.4; }


/* ── La ficha del archivo elegido ─────────────────────────────────────────────
   Va ENCIMA de la píldora y no dentro: dentro competiría por el ancho con el
   cuadro de escritura, y en un teléfono no caben los dos. Arriba ocupa su propia
   línea, se lee el nombre entero y el cuadro no pierde sitio. */
.reply-adjunto {
  display: flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  max-width: 100%;
  padding: 6px 6px 6px 10px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2);
  font-size: 0.8125rem;
}
.reply-adjunto-ico {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--brand);
}
.reply-adjunto-nombre {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-strong);
  font-weight: 600;
}
.reply-adjunto-peso {
  flex: 0 0 auto;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.reply-adjunto-quitar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.reply-adjunto-quitar:hover:not(:disabled) { background: var(--control-bg-hover); color: var(--text); }
.reply-adjunto-quitar:disabled { opacity: 0.4; cursor: not-allowed; }


/* ── Táctil ───────────────────────────────────────────────────────────────────
   El suelo de 40px del contrato de tramos vale también aquí: el clip y la ✕ son
   dos objetivos pequeños y juntos, y fallarlos significa abrir el selector de
   archivos sin querer o borrar el que ya se eligió. */
@media (max-width: 1023px) {
  .reply-attach { width: 40px; height: 40px; }
  .reply-adjunto-quitar { width: 40px; height: 40px; }
  .reply-adjunto { padding: 4px 4px 4px 10px; }
}

@media (max-width: 639px) {
  /* Con el clip dentro, el cuadro de escritura se queda con lo que sobre; el
     nombre del archivo se recorta antes que empujar el peso fuera de la ficha. */
  .reply-adjunto { font-size: 0.78rem; gap: 6px; }
  .reply-adjunto-nombre { flex: 1 1 auto; }
  .reply-input-row { padding-left: 2px; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   38. LA MEDIA DEL CHAT EN PANTALLAS ESTRECHAS
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 38.1 Nada se sale de su burbuja ──────────────────────────────────────────
   Las burbujas miden `max-width: 72%`, pero su contenido multimedia llevaba
   topes en PÍXELES: imagen 280, vídeo 300, documento 260. Un tope en píxeles no
   sabe nada del 72%, así que en cuanto la pantalla es estrecha gana él.

   Medido a 340px: la burbuja da 221px de ancho y la imagen se plantaba en 280,
   saliéndose **104px** por la derecha y estirando la burbuja hasta el borde de
   la pantalla. `overflow: hidden` de la columna de mensajes la recortaba, así
   que además de salirse se veía cortada.

   `min(px, 100%)` conserva el tope de siempre donde hay sitio y cede al ancho de
   la burbuja donde no lo hay. Es lo que debió ser desde el principio: el número
   en píxeles está para que una foto no ocupe media pantalla en un monitor, no
   para forzarla en un teléfono. */
.msg-media-img    { max-width: min(280px, 100%); }
.msg-media-video  { max-width: min(300px, 100%); }
.msg-media-doc    { max-width: min(260px, 100%); }
.msg-media-sticker { max-width: min(140px, 100%); }

/* Con el ancho ya limitado, la altura sigue a la proporción de la imagen en vez
   de recortarla: `object-fit: cover` sobre una caja sin alto fijo no hacía nada
   salvo confundir a quien lo lea. */
.msg-media-img { height: auto; object-fit: contain; }

/* En un teléfono el 72% deja 193px útiles dentro de la burbuja, y ahí no entra
   ni una frase de tres palabras sin partirse. El 88% es lo que usa cualquier
   mensajería en ese ancho: sigue distinguiendo quién escribe —la burbuja no
   llega al borde contrario— y deja leer. */
@media (max-width: 639px) {
  .msg-bubble { max-width: 88%; }
}


/* ── 38.2 El documento adjunto tiene que verse ────────────────────────────────
   `background: rgba(0,0,0,0.05)` es un negro al 5%: sobre la burbuja verde de un
   operador no se distingue, y en tema oscuro es MÁS oscuro que la burbuja, así
   que desaparece del todo. El texto encima iba en `--brand-dark`, que en oscuro
   es la ciruela pura: morado oscuro sobre verde oscuro.

   Este chip no está sobre el fondo de la página sino sobre una burbuja de color
   que cambia con el remitente y con el tema, así que no vale una superficie
   translúcida — hay cuatro fondos distintos debajo. Se usan los dos tokens de
   superficie SÓLIDOS en ambos temas (`--surface-3`) más un borde, que es lo que
   lo convierte en un botón reconocible sobre cualquiera de ellos. */
.msg-media-doc {
  background: var(--surface-3);
  border: 1px solid var(--border);
  color: var(--text-strong);
  font-weight: 600;
  transition: border-color 0.15s, background 0.15s;
}
.msg-media-doc:hover {
  background: var(--surface-3);
  border-color: var(--brand);
}
/* El icono sí lleva el acento: es lo que dice de un vistazo que hay un archivo. */
.msg-media-doc-icon { color: var(--brand); }

/* Objetivo táctil: el chip es un enlace para abrir el archivo y medía 40px justos
   de alto solo por su relleno. Se fija, que es lo que pide el contrato de tramos. */
@media (max-width: 1023px) {
  .msg-media-doc { min-height: 44px; }
}


/* ── 38.3 El botón de enviar cruzaba el borde de la píldora ───────────────────
   Es geometría de DOS CÍRCULOS, y equivocarse de comparación da un resultado
   convincente y falso — pasó en la primera versión de este bloque.

   La esquina de la píldora es un arco de radio `R` centrado en `Ca`. El botón es
   un círculo de radio `rb` centrado en `Cb`. El botón cabe si:

       distancia(Ca, Cb) + rb  ≤  R

   Lo que NO vale es comprobar la base del botón contra el borde a esa altura: el
   punto más salido de un círculo respecto de una esquina no es su base, es su
   diagonal. Con esa comprobación equivocada salía que hacían falta 8px de
   relleno, y 8px es justo lo que lo sacaba: al alejar el botón del centro del
   arco, `distancia(Ca, Cb)` crece y la desigualdad deja de cumplirse. Medido,
   quedaba en **−0.12px** — se salía.

   El óptimo es que los dos centros COINCIDAN, y eso pasa cuando
   `relleno + borde + rb = R`. Con el botón de 40 (rb 20), el borde de 1 y un
   relleno de 5, sale `R = 26`: los centros quedan a √2 px y el margen es de
   4.6px por los dos lados. Vale igual para el clip de la izquierda, que es del
   mismo tamaño, y no depende del alto de la fila: al crecer el cuadro de texto
   el botón sigue anclado abajo y la distancia al centro del arco no cambia.

   Lo comprueba `bench/encaje.js`, que lee los valores calculados del navegador y
   devuelve el margen en píxeles. */
.reply-input-row {
  padding: 5px;
  border-radius: 26px;
}

/* En el teléfono el cuadro de texto va con menos aire por dentro: lo pone ya la
   píldora, y cada píxel ahí es texto que se lee. */
@media (max-width: 639px) {
  .reply-box .reply-input-row .reply-textarea { padding: 10px 8px; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   39. MÉTRICAS → META → COSTO WHATSAPP
   ═══════════════════════════════════════════════════════════════════════════ */


/* La pestaña reutiliza lo que ya existe —`.metrics-kpi-grid`,
   `.metrics-charts-grid`, `.metrics-chart-card`, `.audit-table`, `.btn-export`,
   `.tickets-export`— y solo añade lo suyo: la barra de filtros propios, los
   avisos y los pies de tabla. */

.wa-cost-bar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.wa-cost-filtros {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  min-width: 0;
}

.wa-cost-campo {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.wa-cost-campo > span {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
}
.wa-cost-campo select { min-width: 170px; max-width: 100%; }

/* El rango que se está mirando, en texto: el selector de período vive en la
   barra de Métricas, arriba, y desde aquí no se ve cuál está puesto. */
.wa-cost-rango {
  align-self: center;
  padding-bottom: 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}

.wa-cost-acciones {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

/* Separador del menú de exportación: distingue el resumen del detalle por
   cliente, que son dos archivos muy distintos y se piden por error. */
.wa-cost-menu-sep {
  padding: 8px 10px 4px;
  margin-top: 4px;
  border-top: 1px solid var(--border-soft);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}


/* ── Avisos ───────────────────────────────────────────────────────────────────
   No son decoración: cambian cómo se lee la cifra de al lado. Un costo de 0 es
   correcto antes del cobro y es un fallo después, y sin el aviso las dos cosas
   se ven igual. */
.wa-cost-avisos {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}
.wa-cost-aviso {
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 0.8125rem;
  line-height: 1.5;
}
.wa-cost-aviso strong { color: var(--text-strong); }
.wa-cost-aviso--warn {
  background: var(--badge-warn-bg);
  border-color: transparent;
  color: var(--badge-warn-fg);
}
.wa-cost-aviso--warn strong { color: var(--badge-warn-fg); }


/* La ventana de la proyección, debajo de su cifra: son los últimos 30 días mire
   lo que mire el filtro, y sin decirlo parece contradecir al costo del período. */
.wa-cost-kpi-nota {
  margin-top: 4px;
  font-size: 0.6875rem;
  line-height: 1.35;
  color: var(--muted-2);
}


/* ── Cabecera de cada tabla ───────────────────────────────────────────────── */
.wa-cost-cab {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.wa-cost-cab-acciones { display: flex; gap: 6px; flex: 0 0 auto; }
.wa-cost-pista { font-size: 0.75rem; color: var(--muted-2); }
.wa-cost-pie {
  margin-top: 10px;
  font-size: 0.75rem;
  color: var(--muted);
}
.wa-cost .metrics-chart-card { margin-top: 16px; }


/* ── Táctil ───────────────────────────────────────────────────────────────────
   Las tablas ya se convierten en tarjetas por debajo de 1024px (bloque 27), así
   que aquí solo hay que ocuparse de la barra y de una cosa propia: el detalle
   por cliente tiene DIECISÉIS columnas, y dieciséis campos rotulados por tarjeta
   son cuatro pantallas de teléfono por cliente.

   En teléfono se dejan las ocho que contestan «quién, cuánto y cuándo»; las
   otras ocho siguen en la exportación, que es de donde se sacan de verdad. Se
   ocultan la celda Y su cabecera: el script que rotula las tarjetas empareja por
   POSICIÓN, así que descuadrar una de las dos rotularía todo corrido. */
@media (max-width: 1023px) {
  .wa-cost-bar { align-items: stretch; }
  .wa-cost-filtros { width: 100%; }
  .wa-cost-campo { flex: 1 1 160px; }
  .wa-cost-campo select { min-width: 0; width: 100%; }
  .wa-cost-acciones { width: 100%; justify-content: flex-end; }
  .wa-cost-rango { align-self: flex-start; padding-bottom: 0; }
}

@media (max-width: 639px) {
  .wa-cost-clientes th:nth-child(4),   /* Mercado */
  .wa-cost-clientes td:nth-child(4),
  .wa-cost-clientes th:nth-child(7),   /* Fallidos */
  .wa-cost-clientes td:nth-child(7),
  .wa-cost-clientes th:nth-child(8),   /* Costo real */
  .wa-cost-clientes td:nth-child(8),
  .wa-cost-clientes th:nth-child(10),  /* USD por mensaje */
  .wa-cost-clientes td:nth-child(10),
  .wa-cost-clientes th:nth-child(11),  /* Desde */
  .wa-cost-clientes td:nth-child(11),
  .wa-cost-clientes th:nth-child(12),  /* Hasta */
  .wa-cost-clientes td:nth-child(12),
  .wa-cost-clientes th:nth-child(15),  /* Fecha de entrada */
  .wa-cost-clientes td:nth-child(15) {
    display: none;
  }

  .wa-cost-rango { font-size: 0.75rem; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   40. EL COMPOSITOR NO PUEDE EMPUJAR AL BOTÓN FUERA
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 40.1 El cuadro de texto tiene que poder encogerse ─────────────────────────
   El bloque 38 arregló la GEOMETRÍA de la esquina, y eso era real, pero no era
   todo: el botón puede salirse por una razón distinta y anterior, que es que no
   le quede sitio.

   Un elemento flex arranca con `min-width: auto`, y eso significa «no me encojas
   por debajo de mi contenido mínimo». El contenido mínimo de un `<textarea>` lo
   fija su atributo `cols` —20 por defecto— multiplicado por el ancho de carácter
   de LA TIPOGRAFÍA QUE SE ESTÉ USANDO. Cuando ese mínimo es mayor que el hueco
   libre, el cuadro se niega a ceder y quien sale despedido es el botón, que es
   el último de la fila.

   Eso explica por qué se veía en un teléfono y no aquí: Chromium en el banco lo
   deja encogerse igualmente, y Safari y varios navegadores de Android respetan el
   mínimo al pie de la letra. El ancho exacto al que se rompe depende entonces de
   la fuente cargada, que es justo lo que hacía que apareciera a 338px y no a 340.

   `min-width: 0` quita ese suelo: el cuadro absorbe TODO el estrechamiento y el
   botón y el clip, que son `flex: 0 0 auto`, no se mueven nunca. Deja de depender
   del navegador y de la tipografía. */
.reply-box .reply-input-row .reply-textarea {
  min-width: 0;
  flex: 1 1 0;
}
.reply-attach,
.reply-send {
  flex: 0 0 auto;
}


/* ── 40.2 Menos alto y menos peso en el teléfono ───────────────────────────────
   El clip baja de 40 a 34 y el aire de alrededor se recorta: la barra pasa de
   ~80px a ~70, que en una pantalla de 568 son diez píxeles de conversación.

   **34 y no 20.** El contrato de tramos fija 40px como objetivo táctil mínimo por
   debajo de 1024, y 20 dejaría el clip en la mitad de eso — se falla y se abre el
   selector de archivos sin querer, o peor, se pulsa enviar. 34 es lo más que se
   puede recortar sin bajar del tamaño en que un pulgar acierta, y ya libera
   suficiente sitio. El botón de enviar se queda en 40: es el que manda el
   mensaje y es el que no se puede fallar. */
@media (max-width: 639px) {
  .reply-box { padding: 8px 10px 10px; gap: 5px; }
  .reply-attach { width: 34px; height: 34px; }
  /* Centrado con el cuadro de texto, no con su base: con el clip más bajo que la
     fila, `flex-end` lo dejaba descolgado contra el borde inferior. */
  .reply-attach { align-self: center; }
  .reply-box .reply-input-row .reply-textarea { padding: 8px; }
}


/* ── 40.3 Los campos de fecha de Costo WhatsApp ───────────────────────────────
   Las fechas son de la pestaña y no de la barra de Métricas, que solo se pinta
   para «Resumen Meta»: heredarla dejaba esta pantalla sin ningún control de
   fechas, con el filtro viajando en la petición y sin forma de tocarlo. */
.wa-cost-campo--fecha input[type="date"] {
  min-width: 150px;
  max-width: 100%;
}

/* El menú de exportación dice con qué sale, porque no es obvio que use lo de
   arriba y exportar el período equivocado no se nota hasta que alguien abre el
   archivo. */
.wa-cost-menu-nota {
  padding: 8px 10px;
  max-width: 220px;
  border-bottom: 1px solid var(--border-soft);
  margin-bottom: 4px;
  font-size: 0.6875rem;
  line-height: 1.4;
  color: var(--muted);
}
.wa-cost-menu { min-width: 200px; }

@media (max-width: 1023px) {
  .wa-cost-campo--fecha { flex: 1 1 140px; }
  .wa-cost-campo--fecha input[type="date"] { min-width: 0; width: 100%; }
}

/* En el teléfono «Desde» y «Hasta» comparten fila: son una sola pregunta y una
   encima de otra la barra de filtros se comía 235px antes de enseñar un dato. */
@media (max-width: 639px) {
  .wa-cost-campo--fecha { flex: 1 1 calc(50% - 5px); min-width: 0; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   41. NOTIFICACIONES EN PANTALLA ESTRECHA
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 41.1 El desplegable de la campana se salía por la IZQUIERDA ──────────────
   La regla de móvil era `width: 94vw; right: -6px`, y esas dos cosas juntas no
   pueden funcionar: el panel se ancla al BOTÓN, que está pegado al borde
   derecho, y desde ahí se le pide el 94% del ancho de la pantalla. El resultado
   solo puede salirse por el otro lado.

   Medido: 39px fuera por la izquierda a 518px, 20px a 320. El texto quedaba
   cortado en la primera letra de cada línea.

   No es cuestión de ajustar el ancho: mientras el ancla sea el botón, cualquier
   panel más ancho que la distancia del botón al borde izquierdo se sale. Así que
   por debajo de 640px el panel deja de colgar del botón y se ancla a la VENTANA,
   con un margen a cada lado. Es lo que hace cualquier aplicación en un teléfono,
   y además deja de depender de dónde esté la campana en la cabecera. */
@media (max-width: 640px) {
  .ph-notif-menu {
    position: fixed;
    top: calc(var(--ph-h) + 6px);
    left: 10px;
    right: 10px;
    width: auto;
    max-width: none;
    transform-origin: top center;
    /* Y que no se salga por abajo: la lista de dentro ya tiene su propio
       scroll, así que lo que sobra se recorre en vez de desbordar. */
    max-height: calc(100vh - var(--ph-h) - 28px);
    max-height: calc(100dvh - var(--ph-h) - 28px);
    display: flex;
    flex-direction: column;
  }
  .ph-notif-menu-list { flex: 1 1 auto; min-height: 0; }
}


/* ── 41.2 Las etiquetas de tipo desbordaban la tarjeta ────────────────────────
   En modo tarjeta cada celda es una rejilla de dos columnas, y la segunda es
   `minmax(0, 1fr)`: puede encogerse. Lo que no encoge es la etiqueta que va
   dentro, porque hereda `white-space: nowrap` de la celda de la tabla.

   Y ahí está el detalle que cuesta ver: **no hay ninguna regla que se lo ponga a
   la etiqueta**. `white-space` se hereda, así que buscarla por su selector no
   encuentra nada — se comprobó recorriendo todas las hojas y ninguna regla que
   case con el elemento declara la propiedad. Viene de arriba.

   Medido en Notificaciones → Histórico con «Inicio de sesión en nuevo
   dispositivo»: la fila se pasaba 34px a 376 y 72px a 320.

   Se arregla en las etiquetas de TODAS las tablas que se vuelven tarjetas, no
   solo en esta: el mismo texto largo en una etiqueta aparece en Auditoría, en
   Campañas y en Usuarios, y arreglarlo una por una es garantizar que la próxima
   vuelva a aparecer. */
@media (max-width: 1023px) {
  .contacts-table tbody td .badge,
  .users-table tbody td .badge,
  .audit-table tbody td .badge,
  .camp-table tbody td .badge,
  .crm-list-table tbody td .badge,
  .pg-row > span .badge,
  .mk-up-row > span .badge {
    white-space: normal;
    overflow-wrap: anywhere;
    min-width: 0;
    max-width: 100%;
    text-align: left;
    line-height: 1.35;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   42. LOS FILTROS DE COSTO WHATSAPP, CON ASPECTO DE CONTROL
   ═══════════════════════════════════════════════════════════════════════════ */


/* El bloque 39 le dio a estos campos MEDIDAS pero no PINTURA: `.wa-cost-campo
   select` solo declaraba `min-width`, y el `input[type="date"]` lo mismo. Sin
   fondo, ni borde, ni radio, ni relleno, el navegador los pintaba con su
   aspecto por defecto — gris de sistema, esquinas rectas, una flecha distinta
   en cada equipo — justo al lado del botón de exportar, que sí está cuidado.
   Se veían como un formulario a medio hacer.

   La referencia no es inventada: es `.btn-export-main`, que vive en ESTA misma
   barra. Mismos tokens (`--control-bg` / `--control-border`), mismo radio,
   misma tipografía y mismo hover. Así los tres controles de la barra leen como
   una sola pieza en vez de tres cosas distintas pegadas.

   Los tokens `--control-*` están definidos en los dos temas y en los de
   accesibilidad (alto contraste, etc.), así que esto adapta solo. Por eso se
   usan tokens y no colores: un `#fbfcfd` aquí sería invisible en oscuro.

   AVISO PARA QUIEN TOQUE ESTO: aquí NO se declara `min-width`, `max-width`,
   `width` ni `flex` a propósito. El responsive de esta barra vive en los
   bloques 39 y 40, MÁS ARRIBA en el archivo; como las media queries no suben
   la especificidad, manda el orden del archivo y cualquier medida escrita aquí
   pisaría en silencio la de móvil. Pintura sí, tamaño no. */

.wa-cost-campo select,
.wa-cost-campo--fecha input[type="date"] {
  box-sizing: border-box;
  height: 35px;                  /* medida del botón de exportar: los tres cuadran */
  padding: 8px 12px;
  border: 1px solid var(--control-border);
  border-radius: 9px;
  background: var(--control-bg);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
}

.wa-cost-campo select:hover,
.wa-cost-campo--fecha input[type="date"]:hover {
  background: var(--control-bg-hover);
  border-color: var(--brand);
}

/* El anillo de foco es el del resto del panel, no el del navegador: el `outline`
   nativo se dibuja pegado al borde y con el radio de 9px queda como una costura. */
.wa-cost-campo select:focus,
.wa-cost-campo select:focus-visible,
.wa-cost-campo--fecha input[type="date"]:focus,
.wa-cost-campo--fecha input[type="date"]:focus-visible {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(var(--brand-rgb), 0.12);
}

/* ── La flecha del select ─────────────────────────────────────────────────────
   La nativa es distinta en cada sistema —y en Windows es un triángulo gris
   sobre un botón con su propio fondo, que es lo que más chirriaba— así que se
   quita y se dibuja una.

   Se dibuja con bordes en un `::after` del `<label>` y color `currentColor`
   heredado del token, NO con una imagen de fondo: una flecha en `data:` lleva
   el color cosido dentro y habría que repetirla por cada tema, incluidos los de
   alto contraste. Así hay una sola regla y se adapta sola.

   El `:not(--fecha)` evita ponérsela a los campos de fecha, que ya traen su
   propio icono de calendario. */
.wa-cost-campo select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 30px;           /* hueco para la flecha */
}

.wa-cost-campo:not(.wa-cost-campo--fecha) { position: relative; }

.wa-cost-campo:not(.wa-cost-campo--fecha)::after {
  content: "";
  position: absolute;
  right: 12px;
  /* Anclada ABAJO, no centrada: el `<label>` es una columna con la etiqueta
     encima, así que su borde inferior coincide con el del select y esta es la
     única referencia que no se mueve cuando la barra se reordena en móvil. */
  bottom: 14px;
  width: 6px;
  height: 6px;
  border-right: 1.6px solid var(--muted);
  border-bottom: 1.6px solid var(--muted);
  transform: rotate(45deg);
  pointer-events: none;
  transition: border-color 0.15s;
}

.wa-cost-campo:not(.wa-cost-campo--fecha):hover::after,
.wa-cost-campo:not(.wa-cost-campo--fecha):focus-within::after {
  border-color: var(--text);
}

/* Firefox deja un punteado propio dentro del select al enfocarlo, encima del
   anillo de arriba. */
.wa-cost-campo select:-moz-focusring {
  color: transparent;
  text-shadow: 0 0 0 var(--text);
}

/* El desplegable abierto lo pinta el sistema, no la hoja: sin esto en Windows
   sale blanco sobre blanco en tema oscuro. `--surface` no vale aquí porque es
   translúcido; hace falta un fondo SÓLIDO. */
.wa-cost-campo select option {
  background: var(--bg-2);
  color: var(--text);
}

/* La etiqueta de encima: un punto menos de gris y algo más de aire, que con el
   control ya dibujado quedaba pegada a él. */
.wa-cost-campo > span {
  margin-bottom: 1px;
  color: var(--muted);
}


/* ── Tema claro: hay que ganarle a una regla global ───────────────────────────
   `html.light-mode select` (L12539) fuerza con `!important` el fondo y el borde
   de TODOS los selects del panel. Resultado medido aquí: el select salía con
   borde `rgba(0,0,0,0.12)` mientras el campo de fecha y el botón de exportar,
   a su lado, llevaban el `rgba(87,45,75,0.16)` del panel. Tres controles
   pegados con dos bordes distintos: exactamente lo que se veía mal.

   A un `!important` solo se le gana con otro, y la especificidad decide cuál:
   `html.light-mode select` es (0,1,2) y esto es (0,2,1) —dos clases pesan más
   que dos elementos—, así que manda este. Va acotado a `.wa-cost-bar`: no
   cambia ningún otro select del panel.

   En oscuro no hace falta nada: ahí la regla global no aplica y ya coinciden
   los tres (comprobado). */
html.light-mode .wa-cost-bar .wa-cost-campo select {
  background: var(--control-bg) !important;
  border-color: var(--control-border) !important;
  color: var(--text) !important;
}
html.light-mode .wa-cost-bar .wa-cost-campo select:hover {
  background: var(--control-bg-hover) !important;
  border-color: var(--brand) !important;
}
html.light-mode .wa-cost-bar .wa-cost-campo select:focus {
  background: var(--control-bg) !important;
  border-color: var(--brand) !important;
}


/* ═══════════════════════════════════════════════════════════════════════════
   43. EL MENÚ DE EXPORTAR SE SALÍA POR LA IZQUIERDA
   ═══════════════════════════════════════════════════════════════════════════ */


/* `.tickets-export-menu` ancla su borde DERECHO al del botón (`right: 0`) y
   crece hacia la izquierda con `min-width: 140px`. Mientras el botón está a la
   derecha de la pantalla eso es lo correcto. Pero cuando la barra se reordena
   en pantalla estrecha, el botón se va al borde IZQUIERDO, y desde ahí no caben
   140px hacia la izquierda: el menú se sale.

   Medido en `/audit`: el botón acaba en x=119 y el menú ocupa de −21 a 119.
   21px fuera, con la primera letra de «CSV» y «Excel» cortada. Igual en
   `/tickets`. A 640px y por encima aún cabía de milagro (el botón acaba en 143,
   tres píxeles de margen), lo que explica que no se viera antes.

   Lo que NO vale es voltearlo para todos. Medido a 360px:

       /audit                 botón en x16→119   a la izq NO cabe, a la der sí
       /tickets               botón en x32→135   a la izq NO cabe, a la der sí
       Costo WhatsApp         botón en x226→329  a la izq SÍ cabe, a la der NO

   En Costo WhatsApp la barra alinea sus acciones a la derecha, así que ahí el
   anclaje actual es el bueno y voltearlo rompería ESA pantalla por el otro
   lado.

   Lo que separa un caso del otro no es la página sino el contenedor: las que
   fallan montan el botón dentro del `Toolbar`, que envuelve sus acciones en
   `.tb-actions` y las alinea a la izquierda al reordenarse. Costo WhatsApp usa
   su propia `.wa-cost-acciones`, que alinea a la derecha. Por eso la regla se
   acota a `.tb-actions`: arregla las que se rompen, no toca la que funciona, y
   cualquier pantalla futura que use el Toolbar queda cubierta sin tocar nada.

   `@media (max-width: 1023px)` es donde el Toolbar reordena sus acciones a una
   fila propia; por encima de ahí el botón sigue a la derecha y `right: 0` es lo
   correcto. */
@media (max-width: 1023px) {
  .tb-actions .tickets-export-menu {
    left: 0;
    right: auto;
    /* Y si aun abriendo hacia la derecha no cupiera —un menú con textos largos
       en una pantalla muy estrecha— que encoja en vez de salirse. */
    max-width: calc(100vw - 24px);
  }
  .tb-actions .tickets-export-menu button {
    white-space: normal;
    overflow-wrap: anywhere;
    text-align: left;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   44. EL TECLADO DEL MÓVIL Y EL ALTO REAL DE LA VENTANA
   ═══════════════════════════════════════════════════════════════════════════ */


/* Al escribir en el Livechat desde el móvil, debajo del compositor quedaba un
   hueco negro del tamaño del teclado.

   Primero se intentó con `interactive-widget=resizes-content` en el `meta
   viewport`, que es la forma estándar de decirle al navegador que encoja la
   página al abrirse el teclado. Medido en el banco, el diseño responde
   perfectamente a eso: encogiendo la ventana de 780 a 360px el compositor
   queda pegado abajo con 0px de hueco, y al volver a 780 se recoloca. O sea,
   la maquetación nunca estuvo mal.

   El problema es que ese `meta` NO lo honran todos los navegadores, y en el que
   se reportó el fallo no surtió efecto. Sin él, la ventana de MAQUETACIÓN sigue
   midiendo la pantalla entera mientras el teclado tapa la mitad de abajo; como
   `dvh` mide esa ventana y no lo que se ve, `100dvh` seguía dando la pantalla
   completa y lo que sobraba quedaba debajo del compositor.

   Así que se deja de depender de que el navegador colabore: `--vvh` lo publica
   un script de `index.html` leyendo `visualViewport.height`, que es lo que se ve
   de verdad y existe en todos los navegadores desde 2018. Donde había `100dvh`
   ahora va `var(--vvh, 100dvh)`.

   Las tres líneas de cada regla no son redundancia: un navegador sin `dvh`
   descarta las dos últimas y se queda con `vh`; uno con `dvh` pero sin el
   script (o con `visualViewport` ausente) se queda con `dvh`. Escribirlo en una
   sola línea dejaría `min-height: auto` en los viejos, que es justo el fallo
   que este bloque arregla. */


/* El `body` también, y esto es la mitad del arreglo: si el documento sigue
   midiendo la pantalla entera, el navegador PUEDE desplazarlo para enseñar el
   campo enfocado, y ese desplazamiento reabre el hueco por abajo aunque el
   Livechat ya mida bien. Midiendo `--vvh` no sobra nada que desplazar. */
body {
  min-height: 100vh;
  min-height: 100dvh;
  min-height: var(--vvh, 100dvh);
}

/* ── Y `.app-shell`, que era LA causa ─────────────────────────────────────────
   Tenía una sola regla, `min-height: 100vh`, que nunca se actualizó a `dvh`.

   En un móvil `100vh` NO es la ventana: es la ventana GRANDE, la de cuando la
   barra del navegador está oculta. Medido en el teléfono donde se reportó el
   fallo: `vh` daba 768 mientras la ventana real era 664. O sea, el documento
   medía SIEMPRE 104px más que la pantalla y la página se podía desplazar.

   Ahí estaba el fallo entero. Al abrirse el teclado el navegador desplaza la
   página para enseñar el campo enfocado, y al cerrarse NO lo devuelve. El
   contenido se queda subido y debajo aparece un hueco del tamaño exacto del
   desplazamiento. Medido en las cuatro fases:

       teclado abierto    doc 461  ventana 405  scrollY   0   hueco   0px
       teclado cerrado    doc 768  ventana 664  scrollY 104   hueco 104px
       abierto otra vez   doc 461  ventana 405  scrollY  56   hueco  56px

   El hueco ES el desplazamiento, y el desplazamiento es `documento − ventana`:
   768−664 = 104 y 461−405 = 56. Cuadra al píxel.

   Por eso los arreglos anteriores no lo tocaron: `.livechat-page` y
   `.livechat-layout` medían bien —y siguen midiendo bien en las cuatro fases—,
   pero su ANTEPASADO seguía estirando el documento. Y por eso no se veía en el
   banco: ahí `vh` y la ventana valen lo mismo, y la diferencia solo existe en un
   navegador móvil de verdad.

   Es `min-height`, no `height`: una página con contenido largo sigue creciendo y
   desplazándose con normalidad. Lo que se quita es el desplazamiento FANTASMA de
   una página que cabe entera. */
.app-shell {
  min-height: 100vh;
  min-height: 100dvh;
  min-height: var(--vvh, 100dvh);
}


/* ── Y el cierre: que en el Livechat el documento NO pueda desplazarse ────────
   Arreglar `.app-shell` quitó una de las causas —medido en el teléfono, pasó de
   768 a 664, el valor correcto— pero el documento seguía midiendo 768. Queda
   algún otro elemento estirándolo: la hoja tiene 35 declaraciones con `100vh` y
   perseguirlas de una en una es garantizar otra ronda de fallo.

   Así que se ataca el mecanismo en vez del síntoma. El Livechat es una pantalla
   de aplicación: ocupa exactamente la ventana y lo único que se desplaza es la
   lista de mensajes, que tiene su propio scroll. El documento NUNCA debería
   desplazarse aquí. Fijándolo a la ventana y prohibiendo el desbordamiento, el
   navegador no tiene dónde desplazar la página al abrirse el teclado, y sin
   desplazamiento no hay hueco — da igual qué descendiente mida de más.

   `:has()` acota la regla a la pantalla del Livechat sin tocar el resto del
   panel. Donde no exista, la regla entera se descarta y queda como estaba: no
   puede empeorar nada. */
@media (max-width: 1023px) {
  html:has(.livechat-page),
  body:has(.livechat-page) {
    height: 100vh;
    height: 100dvh;
    height: var(--vvh, 100dvh);
    overflow: hidden;
  }
}

/* Mismo ámbito que las reglas originales (bloques 17 y 34), que viven las dos
   dentro de este `@media`: en escritorio no hay teclado que descontar y no hay
   por qué tocar nada. */
@media (max-width: 1023px) {
  /* El elemento lleva DOS clases, `page-container livechat-page`, y
     `.page-container` arrastra un `min-height` del bloque 34. Un `min-height`
     manda sobre el `height`: sin tocarlo, esto se quedaba midiendo la pantalla
     entera aunque el `height` dijera otra cosa —medido: `height` 320px y alto
     real 720px—, el documento seguía siendo más alto que lo visible y el
     navegador podía desplazarlo, que es como reaparecía el hueco. */
  .livechat-page {
    height: calc(100vh - var(--ph-h));
    height: calc(100dvh - var(--ph-h));
    height: calc(var(--vvh, 100dvh) - var(--ph-h));
    min-height: calc(100vh - var(--ph-h));
    min-height: calc(100dvh - var(--ph-h));
    min-height: calc(var(--vvh, 100dvh) - var(--ph-h));
  }

  .livechat-layout {
    height: calc(100vh - var(--ph-h));
    height: calc(100dvh - var(--ph-h));
    height: calc(var(--vvh, 100dvh) - var(--ph-h));
  }
}
