/* El reset va DENTRO de una capa, y esto no es cosmético: en CSS, una regla sin capa le gana a
   cualquier regla en capa por muy específica que sea. Con el reset fuera, su `padding: 0` del `*`
   ganaba al padding de todas las piezas de `ui-system.css` y las dejaba pegadas (se detectó con
   el segmentado Día|Semana ilegible). El resto de este archivo sigue fuera de capa a propósito:
   así ninguna pieza nueva puede alterar por sorpresa una pantalla vieja todavía sin migrar. */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
}

/* ── Design tokens (single source of truth — Fase 1A) ──
   Color tokens live here (light) and in :root.dark-mode (dark). Non-color
   tokens (container, radius, shadow, font, space) are theme-agnostic and
   declared only once below. No other CSS file declares brand tokens. */
:root {
  /* El modo claro lo declara EXPLÍCITO para que al apagar el oscuro los controles del navegador
     vuelvan; sin esto, la clase se quita y `color-scheme` se queda con el valor heredado. */
  color-scheme: light;
  /* Surfaces / fondos / bordes — paleta cálida (sin blanco puro: evita el brillo del claro) */
  --color-bg:              #f7f4ee;   /* Body   */
  --color-surface:         #fefdfc;   /* Cards  (no #fff a propósito) */
  --color-surface-muted:   #f1ece5;   /* Hover / relleno sutil */
  --color-input:           #fefdfc;
  --color-border:          #e7ddd0;   /* Border */
  --color-border-strong:   #d8cbb8;
  --color-navbar:          #fbf8f3;   /* Navbar — token capturado; cableado a .tenant-nav-bar pendiente (migración A/B) */
  --color-hover:           #f1ece5;   /* nombre canónico del hover; hoy la mayoría usa --color-surface-muted */
  /* Texto (escalera de 5 niveles — cálido para armonizar con las cremas) */
  --color-text-strong:     #2b2620;
  --color-text:            #3d372e;
  --color-text-secondary:  #6a6153;
  --color-text-tertiary:   #8a8071;
  --color-text-muted:      #a89e8d;
  /* Selección / acento */
  --color-accent-bg:       #eff6ff;
  --color-accent-border:   #bfdbfe;
  --color-accent-text:     #1d4ed8;
  --color-tab-active-bg:   #dbeafe;
  /* Capa de plataforma (super-admin / login-as) — acento violeta exclusivo */
  --color-platform:        #7c3aed;
  --color-platform-bg:     #f5f3ff;
  --color-platform-border: #ddd6fe;
  --color-platform-text:   #6d28d9;
  /* Paleta de acción (Opción B — un tono canónico por rol) */
  --color-primary:          #2563eb;
  --color-primary-contrast: #ffffff;
  --color-danger:           #dc2626;
  --color-success:          #16a34a;
  --color-warning:          #f59e0b;
  /* Badges de estado (se mantienen distintos por UX) */
  --color-badge-info-bg:    #dbeafe; --color-badge-info-text:    #1d4ed8;
  --color-badge-success-bg: #dcfce7; --color-badge-success-text: #166534;
  --color-badge-done-bg:    #f0fdf4; --color-badge-done-text:    #15803d;
  --color-badge-warning-bg: #fff7ed; --color-badge-warning-text: #c2410c;
  --color-badge-danger-bg:  #fee2e2; --color-badge-danger-text:  #b91c1c;

  /* ─ Dashboard premium: zonas semánticas por color (claro) ─
     color sólido · fondo suave de tarjeta · borde · texto. El color es lenguaje:
     danger=problema · warning=atención · success=correcto · info=actividad · violet=plataforma. */
  --dash-hero-from:   #eef2ff;  --dash-hero-to:   #faf5ff;  --dash-hero-border: #e0e7ff;
  --dash-danger:   #dc2626;  --dash-danger-soft:  #fef2f2;  --dash-danger-border:  #fecaca;  --dash-danger-text:  #991b1b;
  --dash-warning:  #d97706;  --dash-warning-soft: #fffbeb;  --dash-warning-border: #fde68a;  --dash-warning-text: #92400e;
  --dash-success:  #16a34a;  --dash-success-soft: #f0fdf4;  --dash-success-border: #bbf7d0;  --dash-success-text: #166534;
  --dash-info:     #2563eb;  --dash-info-soft:    #eff6ff;  --dash-info-border:    #bfdbfe;  --dash-info-text:    #1d4ed8;
  --dash-violet:   #7c3aed;  --dash-violet-soft:  #f5f3ff;  --dash-violet-border:  #ddd6fe;  --dash-violet-text:  #6d28d9;
  --dash-card-bg:     #fefdfc;
  --dash-card-border: #e7ddd0;
  --dash-card-shadow: 0 1px 3px rgba(15,23,42,0.05), 0 8px 24px rgba(15,23,42,0.04);
  --dash-glow-strength: 0%;   /* claro: sin halo de color */

  /* Ancho de contenedor canónico (2 anchos) */
  --container-max:  1000px;  /* LEGADO: formularios y lectura. Se retira al migrar la sección */
  --container-wide: 1400px;  /* LEGADO: tablas. Idem */
  --container-full: 2100px;  /* destino: la pantalla (el tope solo entra en ultrapanorámico) */
  /* App-shell (Fase 1D): alto fijo del footer fantasma / zona segura inferior =
     altura de las pestañas colapsadas (desktop). Token de layout (no cambia con el
     tema); reemplaza a --tenant-nav-h, que JS medía. Móvil lo redefine a 2.8125rem. */
  --app-chrome-nav-h: 2.875rem;
  /* Radios */
  --radius-sm:   6px;
  --radius-md:   8px;
  --radius-lg:   10px;
  --radius-xl:   12px;
  --radius-pill: 999px;
  /* Sombras */
  --shadow-sm:    0 2px 8px rgba(37,99,235,0.07);
  --shadow-md:    0 8px 32px rgba(0,0,0,0.15);
  --shadow-focus: 0 0 0 3px rgba(37,99,235,0.1);
  /* Tipografía */
  --font-family-base: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  --font-size-base:   15px;
  /* Escala de espaciado (para código nuevo; no se retro-migra en 1A) */
  --space-1: .25rem;
  --space-2: .5rem;
  --space-3: .75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
}

/* Modo oscuro = SOLO redefinición de tokens de color (nunca layout).
   Familia CÁLIDA (carbón), hermana nocturna del hueso del modo claro: los dos modos son la
   misma marca con dos luces. Antes era un gris azulado (slate) que no emparejaba con el claro.
   La paleta fría anterior está preservada íntegra abajo, en `:root.dark-mode--legacy-slate`,
   por si el dueño decide restaurarla: ver `panel-frontend-guide.md` §5. */
:root.dark-mode {
  /* Los controles que pinta el NAVEGADOR —casillas, radios, el calendario de `input[type=date]`,
     las barras de desplazamiento— no leen tokens: leen esto. Sin declararlo salían blancos sobre
     el carbón, que es el único punto donde el modo oscuro se rompía sin que ningún token pudiera
     arreglarlo. Va aquí y no en cada sección: es una propiedad del modo, no de una pantalla. */
  color-scheme: dark;
  --color-bg:              #141210;
  --color-surface:         #1f1c18;
  --color-surface-muted:   #272320;
  --color-input:           #2b2721;
  --color-border:          #38322b;
  --color-border-strong:   #4d463c;
  --color-navbar:          #1c1915;
  --color-hover:           #272320;
  --color-text-strong:     #f5f1e8;
  --color-text:            #e6e0d4;
  --color-text-secondary:  #a79d8b;
  --color-text-tertiary:   #a79d8b;
  --color-text-muted:      #7d7464;
  --color-accent-bg:       #1e2f4a;
  --color-accent-border:   #2b4a7d;
  --color-accent-text:     #9dc2f7;
  --color-tab-active-bg:   #1e2f4a;
  --color-platform:        #b98cf5;
  --color-platform-bg:     #2a1c3a;
  --color-platform-border: #4c3a7a;
  --color-platform-text:   #cdaefc;
  --color-primary:          #4d8df0;
  --color-primary-contrast: #ffffff;
  --color-danger:           #f2615a;
  --color-success:          #4ec08c;
  --color-warning:          #e0a63c;
  --color-badge-info-bg:    #1e2f4a; --color-badge-info-text:    #9dc2f7;
  --color-badge-success-bg: #1b3227; --color-badge-success-text: #6ed3a4;
  --color-badge-done-bg:    #1d3328; --color-badge-done-text:    #8adcb2;
  --color-badge-warning-bg: #3a2c15; --color-badge-warning-text: #e8a860;
  --color-badge-danger-bg:  #3a1a17; --color-badge-danger-text:  #f0a49c;

  /* ─ Dashboard premium (oscuro): tarjetas profundas + halos de color tipo SaaS ─ */
  --dash-hero-from:   #262034;  --dash-hero-to:   #33261c;  --dash-hero-border: #4a3b30;
  --dash-danger:   #f2615a;  --dash-danger-soft:  rgba(242,97,90,0.10);   --dash-danger-border:  rgba(242,97,90,0.38);   --dash-danger-text:  #f5a49e;
  --dash-warning:  #e0a63c;  --dash-warning-soft: rgba(224,166,60,0.10);  --dash-warning-border: rgba(224,166,60,0.38);  --dash-warning-text: #edc478;
  --dash-success:  #4ec08c;  --dash-success-soft: rgba(78,192,140,0.10);  --dash-success-border: rgba(78,192,140,0.38);  --dash-success-text: #85d6b0;
  --dash-info:     #6da3f2;  --dash-info-soft:    rgba(109,163,242,0.10); --dash-info-border:    rgba(109,163,242,0.38); --dash-info-text:    #a5c8f8;
  --dash-violet:   #bb8cf5;  --dash-violet-soft:  rgba(187,140,245,0.12); --dash-violet-border:  rgba(187,140,245,0.42); --dash-violet-text:  #d3b5fa;
  --dash-card-bg:     #1c1915;
  --dash-card-border: #322c25;
  --dash-card-shadow: 0 1px 3px rgba(0,0,0,0.45), 0 14px 36px rgba(0,0,0,0.35);
  --dash-glow-strength: 22%;  /* oscuro: halo de color visible */
}

/* ── Modo oscuro ANTERIOR (gris azulado / slate). NO se aplica: no hay nada que le ponga esta
   clase al documento. Se conserva para restaurar la paleta previa sin arqueología en git —
   basta con renombrar este selector a `:root.dark-mode` y el de arriba a `--legacy-slate`.
   Decisión y motivo del cambio: `decisiones-ledger.md`. ── */
:root.dark-mode--legacy-slate {
  --color-bg:              #0f172a;
  --color-surface:         #1e293b;
  --color-surface-muted:   #1a2535;
  --color-input:           #263347;
  --color-border:          #334155;
  --color-border-strong:   #475569;
  --color-navbar:          #1a2333;
  --color-hover:           #1a2535;
  --color-text-strong:     #f1f5f9;
  --color-text:            #e2e8f0;
  --color-text-secondary:  #94a3b8;
  --color-text-tertiary:   #94a3b8;
  --color-text-muted:      #64748b;
  --color-accent-bg:       #1e3a5f;
  --color-accent-border:   #1e40af;
  --color-accent-text:     #93c5fd;
  --color-tab-active-bg:   #1e3a5f;
  --color-platform:        #a855f7;
  --color-platform-bg:     #1e0938;
  --color-platform-border: #4c1d95;
  --color-platform-text:   #c084fc;
  --color-primary:          #3b82f6;
  --color-primary-contrast: #ffffff;
  --color-danger:           #ef4444;
  --color-success:          #22c55e;
  --color-warning:          #fbbf24;
  --color-badge-info-bg:    #1e3a5f; --color-badge-info-text:    #93c5fd;
  --color-badge-success-bg: #14331f; --color-badge-success-text: #4ade80;
  --color-badge-done-bg:    #16341d; --color-badge-done-text:    #86efac;
  --color-badge-warning-bg: #3a2310; --color-badge-warning-text: #fb923c;
  --color-badge-danger-bg:  #3a0e0e; --color-badge-danger-text:  #fca5a5;

  --dash-hero-from:   #1b2348;  --dash-hero-to:   #2a1c44;  --dash-hero-border: #3a3a6e;
  --dash-danger:   #f87171;  --dash-danger-soft:  rgba(239,68,68,0.10);   --dash-danger-border:  rgba(239,68,68,0.38);   --dash-danger-text:  #fca5a5;
  --dash-warning:  #fbbf24;  --dash-warning-soft: rgba(245,158,11,0.10);  --dash-warning-border: rgba(245,158,11,0.38);  --dash-warning-text: #fcd34d;
  --dash-success:  #34d399;  --dash-success-soft: rgba(34,197,94,0.10);   --dash-success-border: rgba(34,197,94,0.38);   --dash-success-text: #6ee7b7;
  --dash-info:     #60a5fa;  --dash-info-soft:    rgba(59,130,246,0.10);  --dash-info-border:    rgba(59,130,246,0.38);  --dash-info-text:    #93c5fd;
  --dash-violet:   #c084fc;  --dash-violet-soft:  rgba(168,85,247,0.12);  --dash-violet-border:  rgba(168,85,247,0.42);  --dash-violet-text:  #d8b4fe;
  --dash-card-bg:     #151c2e;
  --dash-card-border: #25304a;
  --dash-card-shadow: 0 1px 3px rgba(0,0,0,0.45), 0 14px 36px rgba(0,0,0,0.35);
  --dash-glow-strength: 22%;
}

body {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  background: var(--color-bg);
  color: var(--color-text);
  line-height: 1.5;
  font-size: 15px;
}

.container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 1rem;
}
/* Páginas de tablas anchas: ancho canónico amplio (§7). */
.container--wide { max-width: var(--container-wide); }

/* ── El ancho de destino: la pantalla ────────────────────────────────────────────────────────
   Los dos de arriba son LEGADO y se van retirando sección por sección. Nacieron sin una regla
   común y el resultado es el que se ve hoy: Catálogo ancho, Citas angosta, y el cliente
   sintiendo que cambia de aplicación al pasar de una a otra. Ese salto es el motivo del cambio,
   no la estética de una pantalla suelta.

   Cómo se decide el ancho a partir de ahora: NO lo decide la página, lo decide el contenido.
   El marco ocupa la pantalla —igual que Lightning, HubSpot o Jira— y quien se acota es cada
   bloque de dentro: un formulario se pone a dos columnas dentro de su tarjeta, un párrafo lleva
   su propio tope de lectura. Estirar la página sin esa pasada de contenido deja huecos y sale
   PEOR que hoy: por eso se migra sección por sección, ancho y contenido juntos, y no de un
   golpe para todo el panel.

   El tope de 2100px no contradice lo anterior. En cualquier monitor corriente (1366, 1440,
   1600, 1920) no llega a aplicarse nunca: ahí el marco es literalmente la pantalla. Solo entra
   en un ultrapanorámico, donde sin él una columna de nombres se iría a más de mil píxeles de
   ancho. Si algún día estorba, es un número. */
.container--full {
  max-width: var(--container-full);
  padding-inline: 1.5rem;   /* a pantalla completa, 1rem deja el contenido pegado al borde */
}

/* ---- Header ---- */
.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 0 1.25rem;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: 1.25rem;
  gap: 1rem;
  flex-wrap: wrap;
}

.header h1 {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--color-text-strong);
}

/* Tenant name + current section, stacked — lets nav.js fill the section name without
   touching #tenant-name's own textContent (a separate element, not a child of the h1). */
.header-title-group { display: flex; flex-direction: column; min-width: 0; gap: 0.1rem; }
.header-section-title {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.8rem;
  color: var(--color-text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.header-section-title:empty { display: none; }
/* El icono de la sección: misma familia de trazo que el menú y el header. */
.header-section-title .icon { display: inline-flex; flex: 0 0 auto; }
.header-section-title .icon svg {
  width: 15px; height: 15px; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.header-work-section .icon svg { width: 18px; height: 18px; }

.header-nav { display: flex; gap: 0.5rem; align-items: center; }

/* ---- Acciones del header: iconos de trazo, sin recuadro ----
   Todos comparten caja (36px), grosor de línea y color, así leen como una familia. Antes eran
   emojis dentro de `.btn.btn-ghost`: cuatro recuadros seguidos convertían el header en una
   barra de herramientas y saturaban el ancho del teléfono. */
.hdr-ico {
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 8px;
  color: var(--color-text-tertiary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  flex-shrink: 0;
  text-decoration: none;
  transition: background 0.15s, color 0.15s;
}
.hdr-ico:hover { background: var(--color-surface-muted); color: var(--color-text-strong); }
.hdr-ico:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.hdr-ico svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  display: block;
}
/* El contador del radar se posa sobre el icono (ya no ocupa sitio a su lado). */
.hdr-ico .tenant-tab-badge {
  position: absolute;
  top: 1px;
  right: 1px;
  margin: 0;
  border: 2px solid var(--color-navbar);
}

/* ---- Segunda franja del header (solo teléfono) ----
   Fija igual que la primera (decisión del dueño: al desplazar no se pierde ninguna de las
   dos). Lleva el botón del menú, la sección actual en grande y el radar. */
/* En escritorio la caja de identidad no existe para el layout: sus hijos se comportan como
   hijos directos del header (la fila única de siempre). En teléfono sí es una fila. */
.header-id { display: contents; }
.header-work { display: none; }
.header-work-section {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.95rem;
  font-weight: 650;
  color: var(--color-text-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- Header logo — platform logo (super-admin, global) + tenant logo (per-tenant),
   resolved per request by renderHeaderLogos(). Supports 0/1/2 logos: with two, a thin
   token-coloured separator sits between them; the row never pushes the page on mobile. ---- */
.header-logo { display: inline-flex; align-items: center; gap: 0.5rem; flex-shrink: 0; min-width: 0; }
.header-logo-img { height: 1.75rem; width: auto; max-width: 8rem; object-fit: contain; display: block; }
.header-logo-sep { width: 1px; height: 1.25rem; background: var(--color-border); flex-shrink: 0; }
@media (max-width: 480px) {
  .header-logo-img { height: 1.5rem; max-width: 5rem; }
}

/* ---- Impersonation banner (Fase 1B; relocated from nav.js inline style) ----
   Fixed-amber status indicator: the background follows the --color-warning token, while the
   text/button stay a fixed near-black so the warning reads identically in light AND dark mode
   (documented status-color exception — there is no always-dark token to use here). */
.impersonation-banner {
  background: var(--color-warning);
  color: #1c1917;
  padding: 8px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 13px;
  font-weight: 500;
  position: sticky;
  top: 0;
  z-index: 1000;
}
.impersonation-banner button {
  background: #1c1917;
  color: var(--color-primary-contrast);
  border: none;
  padding: 4px 12px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 12px;
}

/* ---- Buttons ---- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0.9rem;
  border-radius: 6px;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  border: none;
  text-decoration: none;
  transition: opacity 0.15s;
}
.btn:hover { opacity: 0.85; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-primary   { background: var(--color-primary); color: var(--color-primary-contrast); }
.btn-outline   { background: transparent; border: 1px solid var(--color-primary); color: var(--color-primary); }
.btn-danger    { background: var(--color-danger); color: var(--color-primary-contrast); }
.btn-ghost     { background: transparent; color: var(--color-text-tertiary); border: 1px solid var(--color-border); }
.btn-sm        { padding: 0.25rem 0.6rem; font-size: 0.8rem; }
.btn-logout    { color: var(--color-text-muted); border-color: var(--color-border); }
.btn-logout:hover { color: var(--color-danger); border-color: #fca5a5; }

/* ---- Tabs ---- */
.tabs {
  display: flex;
  gap: 0.25rem;
  border-bottom: 3px solid var(--color-border);
  margin-bottom: 1rem;
}

.tab {
  padding: 0.5rem 1rem;
  background: none;
  border: none;
  border-bottom: 3px solid transparent;
  margin-bottom: -3px;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--color-text-tertiary);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.tab:hover { color: var(--color-text); }
.tab.active { color: var(--color-accent-text); background: var(--color-tab-active-bg); border-bottom-color: var(--color-primary); font-weight: 600; }

/* Separador visual entre el grupo "operación" (datos del negocio) y el grupo
   "ajustes" (comportamiento del bot) dentro de una barra de tabs. No es un .tab:
   no recibe el listener de cambio de pestaña. */
.tab-group-sep {
  flex: 0 0 auto;
  align-self: stretch;
  width: 1px;
  margin: 0.25rem 0.5rem;
  background: var(--color-border);
}

/* ── Sub-tabs (secondary navigation within a tab) ── */
.subtabs {
  display: flex;
  gap: 0.25rem;
  border-bottom: 3px solid var(--color-border);
  margin-bottom: 1.25rem;
}
.subtab {
  padding: 0.4rem 0.9rem;
  background: none;
  border: none;
  border-bottom: 3px solid transparent;
  margin-bottom: -3px;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--color-text-tertiary);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.subtab.active { color: var(--color-accent-text); background: var(--color-tab-active-bg); border-bottom-color: var(--color-primary); font-weight: 600; }
.subtab:hover:not(.active) { color: var(--color-text); }

/* ---- Cards ---- */
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 1rem;
  margin-bottom: 0.75rem;
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s;
  text-decoration: none;
  display: block;
  color: inherit;
}
.card:hover { border-color: #93c5fd; box-shadow: 0 2px 8px rgba(37,99,235,0.07); }
/* Tarjeta de AVISO: la que resume algo y se pliega, no la que muestra contenido.
   Menos padding y menos margen — un plegable cerrado no merece el mismo peso
   visual que una tarjeta de datos, y dos seguidas se comían media pantalla. */
.card--notice {
  padding: 0.6rem 0.75rem;
  margin-bottom: 0.5rem;
  cursor: default;
}

.card-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 0.5rem;
}

.card-name {
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--color-text-strong);
}

.card-preview {
  font-size: 0.85rem;
  color: var(--color-text-tertiary);
  margin-top: 0.2rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 500px;
}

.card-meta {
  font-size: 0.78rem;
  color: var(--color-text-muted);
  white-space: nowrap;
  flex-shrink: 0;
  text-align: right;
}

/* ---- Badges ---- */
.badge {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.badge-active    { background: #dcfce7; color: #166534; }
.badge-escalated { background: #fff7ed; color: #9a3412; }
.badge-closed    { background: var(--color-surface-muted); color: var(--color-text-tertiary); }

/* ---- Empty state ---- */
.empty {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--color-text-muted);
  font-size: 0.9rem;
}

.loading {
  text-align: center;
  padding: 2rem 1rem;
  color: var(--color-text-muted);
  font-size: 0.9rem;
}
/* La rueda va en el ::before del texto y no en el marcado: así los ~40 sitios que
   ya escriben <div class="loading">Cargando…</div> la ganan sin tocar ni una línea
   de JS, y ninguno se queda con un texto quieto que parece la app colgada. */
.loading::before {
  content: '';
  display: block;
  width: 22px;
  height: 22px;
  margin: 0 auto 0.5rem;
  border: 2px solid var(--color-border);
  border-top-color: var(--color-primary);
  border-radius: 50%;
  animation: loader-spin 0.7s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .loading::before { animation-duration: 2.4s; }
}

/* ---- Loader (spinner) + Skeleton — componente único de carga ----
   Markup canónico:
   - Bloque centrado:  <div class="loader-box"><div class="loader"></div></div>
   - En línea (botón):  <span class="loader loader--sm"></span>Enviando…
   - Esqueleto:         <div class="skeleton" style="height:…"></div>
   Colores = tokens de marca → se ve bien en claro/oscuro automáticamente. */
.loader {
  display: inline-block;
  width: 28px;
  height: 28px;
  border: 3px solid var(--color-border);
  border-top-color: var(--color-primary);
  border-radius: 50%;
  animation: loader-spin 0.7s linear infinite;
  vertical-align: middle;
}
.loader--sm {
  width: 14px;
  height: 14px;
  border-width: 2px;
  border-color: transparent;
  border-top-color: currentColor;
  margin-right: 0.3rem;
}
.loader-box {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3rem 1rem;
}
@keyframes loader-spin {
  to { transform: rotate(360deg); }
}

.skeleton {
  background: linear-gradient(90deg,
    var(--color-border) 25%, var(--color-surface) 37%, var(--color-border) 63%);
  background-size: 400% 100%;
  border-radius: 6px;
  animation: skeleton-shimmer 1.4s ease infinite;
}
@keyframes skeleton-shimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

/* ---- Alert banner ---- */
.alert {
  padding: 0.75rem 1rem;
  border-radius: 8px;
  margin-bottom: 1rem;
  font-size: 0.875rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.alert-warning { background: #fff7ed; border: 1px solid #fed7aa; color: #9a3412; }
.alert-success { background: #f0fdf4; border: 1px solid #bbf7d0; color: #166534; }

/* ---- Back link ---- */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  color: var(--color-text-tertiary);
  text-decoration: none;
  font-size: 0.875rem;
  margin-bottom: 1rem;
}
.back-link:hover { color: var(--color-primary); }

/* ---- Chat messages ---- */
.messages-wrapper {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1rem 0;
}

.message {
  max-width: 72%;
  display: flex;
  flex-direction: column;
}
.message-user      { align-self: flex-start; align-items: flex-start; }
.message-assistant { align-self: flex-end; align-items: flex-end; }

.bubble {
  padding: 0.6rem 0.9rem;
  border-radius: 14px;
  font-size: 0.9rem;
  line-height: 1.5;
  word-break: break-word;
}
.message-user .bubble      { background: var(--color-surface-muted); color: var(--color-text); border-bottom-left-radius: 4px; }
.message-assistant .bubble { background: var(--color-primary); color: #fff; border-bottom-right-radius: 4px; }

.message-time {
  font-size: 0.72rem;
  color: var(--color-text-muted);
  margin-top: 0.2rem;
  padding: 0 0.1rem;
}

/* ---- FAQ list ---- */
.faq-item {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 1rem;
  margin-bottom: 0.75rem;
}
.faq-item.inactive { opacity: 0.55; }

.faq-question { font-weight: 600; color: var(--color-text-strong); margin-bottom: 0.35rem; }
.faq-answer   { font-size: 0.875rem; color: var(--color-text-secondary); margin-bottom: 0.5rem; }
.faq-keywords { font-size: 0.78rem; color: var(--color-text-muted); }
.faq-actions  { display: flex; gap: 0.4rem; margin-top: 0.75rem; }

/* ---- Form ---- */
.form-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 1.25rem;
  margin-bottom: 1.5rem;
}
.form-card h2 { font-size: 1rem; font-weight: 600; margin-bottom: 1rem; color: var(--color-text-strong); }

.form-group { margin-bottom: 0.85rem; }
.form-group label {
  display: block;
  font-size: 0.825rem;
  font-weight: 500;
  color: var(--color-text-secondary);
  margin-bottom: 0.3rem;
}
.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--color-border-strong);
  border-radius: 6px;
  font-size: 0.9rem;
  font-family: inherit;
  color: var(--color-text);
  background: var(--color-input);
  transition: border-color 0.15s;
  resize: vertical;
}
.form-group input:focus,
.form-group textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(37,99,235,0.1);
}
/* Casillas y radios: NO heredan el width:100% de arriba (esa regla es para campos de
   texto). Un checkbox estirado a todo el ancho empuja su etiqueta a la línea de abajo
   y se lee como un campo roto — sobre todo en móvil, donde el ancho es el del modal. */
.form-group input[type="checkbox"],
.form-group input[type="radio"] {
  width: auto;
  flex: 0 0 auto;
  margin: 0;
  padding: 0;
}

/* Fila casilla + texto: casilla a la izquierda, etiqueta al lado y alineada arriba
   (así una etiqueta de dos líneas no descentra la casilla). Se pone en el <label>
   que envuelve al checkbox dentro de un .form-group. */
.form-group label.form-check {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin-bottom: 0;
  cursor: pointer;
  line-height: 1.35;
}
.form-group label.form-check input[type="checkbox"],
.form-group label.form-check input[type="radio"] {
  margin-top: 0.15rem;   /* alinea la casilla con la primera línea del texto */
}

.form-hint { font-size: 0.78rem; color: var(--color-text-muted); margin-top: 0.25rem; }

.form-actions { display: flex; gap: 0.5rem; margin-top: 1rem; flex-shrink: 0; }

/* ---- Responsive ---- */
@media (max-width: 600px) {
  .card-preview { max-width: 200px; }
  .message { max-width: 88%; }
  /* El header YA NO se apila en columna aquí (regla legacy, anterior al sidebar): con el
     botón "Menú" al frente, apilar producía un header de 3 filas que se comía la pantalla.
     Ahora es una sola fila — lo que cede es el logo de plataforma y el texto del logout
     (ver el bloque ≤768px más abajo), no la altura. */
}

/* ---- Metrics page ---- */
.section-title {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin: 1.5rem 0 0.75rem;
}

.metrics-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}
.metrics-grid-3 { grid-template-columns: repeat(3, 1fr); }

.metric-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 1.25rem 1rem;
  text-align: center;
}

.metric-value {
  font-size: 2rem;
  font-weight: 700;
  color: var(--color-text-strong);
  line-height: 1;
  margin-bottom: 0.4rem;
}

.metric-label {
  font-size: 0.78rem;
  color: var(--color-text-tertiary);
  font-weight: 500;
}

/* ---- Breakdown bars ---- */
.breakdown-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 1.25rem;
}

.breakdown-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.85rem;
}
.breakdown-row:last-child { margin-bottom: 0; }

.breakdown-label {
  width: 100px;
  font-size: 0.85rem;
  color: var(--color-text-secondary);
  flex-shrink: 0;
}

.breakdown-bar-wrap {
  flex: 1;
  background: var(--color-surface-muted);
  border-radius: 999px;
  height: 12px;
  overflow: hidden;
  min-width: 0;
}

.breakdown-bar {
  height: 100%;
  border-radius: 999px;
  transition: width 0.4s ease;
  min-width: 2px;
}
.breakdown-bar-faq    { background: var(--color-success); }
.breakdown-bar-claude { background: var(--color-primary); }
.breakdown-bar-esc    { background: var(--color-warning); }

.breakdown-pct {
  width: 70px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text-strong);
  text-align: right;
  flex-shrink: 0;
}
.breakdown-count { font-weight: 400; color: var(--color-text-muted); }

/* ---- Top FAQ list ---- */
.top-faq-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  overflow: hidden;
}

.top-faq-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--color-surface-muted);
  font-size: 0.875rem;
}
.top-faq-row:last-child { border-bottom: none; }

.top-faq-rank {
  width: 22px;
  height: 22px;
  background: var(--color-surface-muted);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-text-tertiary);
  flex-shrink: 0;
}

.top-faq-question { flex: 1; color: var(--color-text); }
.top-faq-count { font-weight: 600; color: var(--color-primary); flex-shrink: 0; }

/* ---- Scheduling — Appointment status badges ---- */
.badge-scheduled { background: var(--color-badge-info-bg); color: var(--color-badge-info-text); }
.badge-confirmed  { background: var(--color-badge-success-bg);   color: var(--color-badge-success-text); }
.badge-cancelled  { background: var(--color-surface-muted);     color: var(--color-text-tertiary); }
.badge-completed  { background: var(--color-badge-done-bg); color: var(--color-badge-done-text); }
.badge-noshow     { background: var(--color-badge-warning-bg); color: var(--color-badge-warning-text); }

/* ---- Scheduling — No-show risk (Capa 2) ---- */
.badge-risk-med   { background: var(--color-badge-warning-bg); color: var(--color-badge-warning-text); }
.badge-risk-high  { background: var(--color-badge-danger-bg);  color: var(--color-badge-danger-text); }
.appt-overbook-hint {
  margin-top: 0.5rem;
  padding: 0.4rem 0.6rem;
  border-radius: 0.5rem;
  font-size: 0.8rem;
  background: var(--color-badge-danger-bg);
  color: var(--color-badge-danger-text);
}

/* ---- Scheduling — Modal ---- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 1rem;
}

.modal-box {
  background: var(--color-surface);
  border-radius: 12px;
  padding: 1.5rem;
  width: 100%;
  max-width: 440px;
  max-height: 90vh;
  max-height: 90dvh;   /* móvil: viewport dinámico (el body app-shell quita el scroll de respaldo) */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}

.modal-body {
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}

.modal-box h2 {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text-strong);
  margin-bottom: 1.25rem;
}

/* ---- Invitación a instalar la app (install-invite.js) ---- */
/* Reusa .modal-overlay/.modal-box: es el mismo objeto visual del panel, solo más angosto y con
   las acciones en columna, que es como se toca con el pulgar. */
.install-invite__box  { max-width: 380px; }
.install-invite__text { margin-bottom: 1.25rem; color: var(--color-text-secondary); }

.install-invite__actions {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.install-invite__actions .btn { text-align: center; }

/* ---- Scheduling — Toggle switch ---- */
.sched-toggle {
  position: relative;
  display: inline-block;
  width: 40px;
  height: 22px;
  cursor: pointer;
  flex-shrink: 0;
}

.sched-toggle input { opacity: 0; width: 0; height: 0; }

.sched-toggle-slider {
  position: absolute;
  inset: 0;
  background: var(--color-border-strong);
  border-radius: 999px;
  transition: background 0.2s;
}

.sched-toggle-slider::before {
  content: '';
  position: absolute;
  width: 16px;
  height: 16px;
  left: 3px;
  top: 3px;
  background: var(--color-surface);
  border-radius: 50%;
  transition: transform 0.2s;
}

.sched-toggle input:checked + .sched-toggle-slider { background: var(--color-primary); }
.sched-toggle input:checked + .sched-toggle-slider::before { transform: translateX(18px); }

/* ---- GCal integration section (provider modal) ---- */
.gcal-integration-section { margin-bottom: 0.5rem; }
.gcal-section-divider { border-top: 1px solid var(--color-border); margin: 1rem 0 0.75rem; }
.gcal-section-title { font-size: 0.9rem; font-weight: 600; color: var(--color-text-strong); margin: 0 0 0.6rem; }
.gcal-status { display: flex; align-items: center; gap: 0.6rem; min-height: 2rem; }
.gcal-status-label { font-size: 0.875rem; color: var(--color-text); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.status-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
.status-dot.connected    { background: var(--color-success); }
.status-dot.disconnected { background: var(--color-text-muted); }
.gcal-info-text { font-size: 0.78rem; color: var(--color-text-muted); margin: 0.4rem 0 0; }

/* ---- Scheduling — Day navigation (agenda) ---- */
.sched-day-nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1rem;
  padding: 0.5rem 0;
}

.sched-day-nav span {
  font-weight: 600;
  color: var(--color-text-strong);
  text-align: center;
  flex: 1;
  font-size: 0.95rem;
  text-transform: capitalize;
  min-width: 180px;
}

.agenda-date-picker {
  border: 1px solid var(--color-border);
  border-radius: 6px;
  padding: 0.3rem 0.5rem;
  font-size: 0.85rem;
  color: var(--color-text);
  background: var(--color-surface);
  cursor: pointer;
  height: 32px;
}

/* ---- Scheduling — Section header with action button ---- */
.sched-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}

/* ---- Scheduling — Week table (horarios) ---- */
.sched-week-table { display: flex; flex-direction: column; gap: 0.5rem; }

.sched-day-row {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 0.75rem 1rem;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.sched-day-name {
  width: 90px;
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--color-text-strong);
  flex-shrink: 0;
}

.sched-slots {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  min-width: 0;
}

.sched-slot {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: var(--color-accent-bg);
  border: 1px solid var(--color-accent-border);
  color: var(--color-accent-text);
  border-radius: 6px;
  padding: 0.2rem 0.5rem;
  font-size: 0.8rem;
  font-weight: 500;
}

.sched-slot-delete {
  background: none;
  border: none;
  color: var(--color-text-muted);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0;
}
.sched-slot-delete:hover { color: var(--color-danger); }

.sched-empty-day {
  font-size: 0.825rem;
  color: var(--color-text-muted);
  font-style: italic;
}

/* Responsive for metrics */
@media (max-width: 600px) {
  .metrics-grid     { grid-template-columns: repeat(2, 1fr); }
  .metrics-grid-3   { grid-template-columns: repeat(3, 1fr); }
  .breakdown-label  { width: 70px; font-size: 0.78rem; }
}

/* === RESPONSIVE / MOBILE === */

/* ---- Hamburger button (hidden on desktop) ----
   Opción B (revisión del dueño): las 3 barras dibujadas en CSS + la palabra "Menú" dentro de
   una cápsula, primer elemento del header (mismo lado por el que entra el drawer). La palabra
   existe porque el cliente no es técnico: un icono suelto no se descubre. Bajo 380px la
   palabra se oculta y queda el icono solo — a ese ancho la fila logo+nombre+acciones ya no
   admite el texto sin partirse en dos (verificado con scripts/ui-harness/nav-sidebar/
   mobile-header.html a 320/360/390/430px). El icono NO se sustituye por un "✕" de texto al
   abrir: las barras se transforman en X vía `body.nav-open` (ver abajo). */
.hamburger-btn {
  display: none;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  color: var(--color-text);
  line-height: 1;
  border-radius: 999px;
  min-height: 36px;
  padding: 0.35rem 0.65rem 0.35rem 0.5rem;
  gap: 0.4rem;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background 0.15s;
}
.hamburger-btn:hover { background: var(--color-surface-muted); }
.hamburger-bars { display: flex; flex-direction: column; gap: 3px; flex-shrink: 0; }
.hamburger-bars i {
  display: block;
  width: 15px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.18s ease, opacity 0.18s ease;
}
body.nav-open .hamburger-bars i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
body.nav-open .hamburger-bars i:nth-child(2) { opacity: 0; }
body.nav-open .hamburger-bars i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) {
  .hamburger-bars i { transition: none; }
}
@media (max-width: 600px) {
  /* La franja de identidad lleva logo + nombre + 3 iconos (108px). Con dos logos no cabría un
     nombre legible, así que el de la plataforma (global, siempre el mismo) cede ante el del
     negocio, que es el que dice de quién es el panel. */
  .header-logo-platform, .header-logo-sep { display: none; }
  .header-logo-img { height: 1.4rem; max-width: 4.5rem; }
  .header-title-group { min-width: 4rem; }
}

/* ---- Nav backdrop overlay ---- */
.nav-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.45);
  z-index: 199;
}
.nav-open .nav-overlay { display: block; }

/* ---- Table scroll wrapper ---- */
.table-wrapper {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* Toda tabla envuelta desliza en móvil: sin min-width, la tabla `width:100%` se encoge
   para caber en el wrapper y el `overflow-x` nunca llega a desbordar → no aparece barra.
   En escritorio (cuando max-content < ancho disponible) `width:100%` gana y no cambia nada. */
.table-wrapper > table { min-width: max-content; }

/* ---- Conversation cost panel (F6, super-admin / impersonation) ---- */
.cost-panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  padding: 0.75rem 1rem;
  margin-bottom: 0.75rem;
  font-size: 0.82rem;
  color: var(--color-text);
}
.cost-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
.cost-title { font-weight: 700; color: var(--color-text-strong); }
.cost-total { color: var(--color-text-secondary); }
.cost-toggle { display: flex; gap: 0.4rem; margin-bottom: 0.6rem; flex-wrap: wrap; }
.cost-table { width: 100%; border-collapse: collapse; }
.cost-table th, .cost-table td {
  text-align: left;
  padding: 0.35rem 0.5rem;
  border-bottom: 1px solid var(--color-border);
}
.cost-table th { color: var(--color-text-tertiary); font-weight: 600; }
.cost-table .cost-num { text-align: right; font-variant-numeric: tabular-nums; }
.cost-turn { margin-bottom: 0.75rem; }
.cost-turn-head { color: var(--color-text-secondary); margin-bottom: 0.25rem; font-family: monospace; font-size: 0.76rem; }
.cost-note { color: var(--color-text-muted); font-size: 0.74rem; margin-top: 0.25rem; }

/* ---- Back-row nav (faq.html, conversation.html) ---- */
.back-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}
.back-row-links {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
}

@media (max-width: 768px) {
  /* Container padding */
  .container { padding: 0.75rem; }

  /* ── Header de teléfono: DOS franjas, ambas fijas ──
     Franja 1 (identidad): logo + nombre del negocio + configuración/tema/salir.
     Franja 2 (operación): menú + sección actual + radar.
     Es la misma caja `.header` envolviendo a dos filas, así que sigue siendo un único hijo
     del app-shell → las dos quedan fuera del área que desplaza, nunca se pierden de vista. */
  .header {
    flex-wrap: wrap;
    align-items: center;
    padding: 0.4rem 0 0;
    gap: 0.4rem 0.5rem;
  }
  /* Franja 1 como fila propia: ocupa todo el ancho y NO se parte — lo que cede es el ancho
     del nombre del negocio (puntos suspensivos), nunca la altura del header. */
  .header-id {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex: 0 0 100%;
    min-width: 0;
    flex-wrap: nowrap;
  }
  .header h1 {
    font-size: 0.9375rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1;
  }
  /* Franja 1: el nombre es lo único elástico; logo y acciones conservan su tamaño. */
  .header-title-group { flex: 1 1 auto; min-width: 0; }
  .header-title-group .header-section-title { display: none; }   /* vive abajo, en grande */
  .header-nav { flex-shrink: 0; gap: 0.1rem; }

  /* Franja 2: ocupa el ancho completo → cae sola a la segunda línea del flex. */
  .header-work {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex: 0 0 100%;
    order: 3;
    min-height: 44px;
    border-top: 1px solid var(--color-border);
  }

  /* Hamburger only shown on pages with their own sidebar (e.g. coupons.html) */
  /* nav.js pages hide it via JS and use .tenant-nav-bar instead */
  .hamburger-btn { display: inline-flex; }

  /* Tabs: wrap on mobile */
  .tabs { flex-wrap: wrap; }
  .tab  { font-size: 0.8rem; padding: 0.4rem 0.65rem; }

  /* La sección que ya tiene el selector de teléfono (`ui-navpicker`, que le pone esta marca a la
     barra) suelta sus pestañas aquí: envueltas en tres renglones se llevaban el 39 % de la
     pantalla. La marca la pone el JS del selector y NO a mano, para que una sección sin selector
     jamás se quede sin ninguna de las dos formas de navegar. Encima de 768 px no cambia nada:
     las pestañas de siempre y el selector escondido. */
  .tabs--has-picker { display: none; }
  .tabs--has-picker + .config-banners { margin-top: 0; }

  /* Y el conmutador de grupos de una sección tampoco se pinta aquí: sus opciones ya se listan
     DENTRO de la hoja del selector, y pintarlas además fuera devolvería el segundo nivel que este
     control existe para quitar. Se esconde con CSS y no quitando el marcado, porque el selector
     LEE estas opciones para armar su lista: si desaparecieran del documento, no habría nada que
     listar. Va AQUÍ y no en `ui-system.css` porque las clases que esconde (`.subtabs`,
     `.inv-subtabs`…) viven fuera de las capas de cascada, y una regla sin capa
     le gana siempre a una que está dentro. Si el conmutador va envuelto en una barra propia, la
     sección esconde también el envoltorio — solo ella sabe si lo tiene. */
  [data-nav-sub-for] { display: none; }

  /* Cards */
  .card-preview { max-width: 100% !important; }

  /* Modals: full-width on mobile */
  .modal-box {
    width: 95vw;
    max-width: 95vw;
    padding: 1.25rem 1rem;
  }

  /* Form actions: wrap */
  .form-actions { flex-wrap: wrap; }

  /* KPI and metrics grids: 2 columns */
  .metrics-grid   { grid-template-columns: repeat(2, 1fr); }
  .metrics-grid-3 { grid-template-columns: repeat(2, 1fr); }
  .kpi-grid       { grid-template-columns: repeat(2, 1fr); }
  .stock-cols     { grid-template-columns: repeat(2, 1fr); }

  /* Dashboard period selector */
  .period-selector { flex-wrap: wrap; }
  .period-btn      { min-width: 90px; text-align: center; }

  /* Dashboard rates row */
  .rates-row { flex-direction: column; }

  /* Contacts table — horizontal scroll */
  #contacts-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

  /* Login box margin on small screens */
  .login-box { margin: 1rem; }

  /* Back-row nav on mobile */
  .back-row { flex-wrap: wrap; gap: 0.4rem; }
}

@media (max-width: 480px) {
  .metrics-grid-3 { grid-template-columns: repeat(2, 1fr); }
  .metric-value   { font-size: 1.5rem; }
  .kpi-value      { font-size: 1.5rem; }
}

/* ── Tenant panel: horizontal tab bar (replaces hamburger sidebar) ── */
.tenant-nav-bar {
  display: flex;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
  padding: 0 1rem;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* Reserve the COLLAPSED height so the server-emitted hole never lets the content jump
     before nav.js paints — zero layout shift even in degradation (Fase 1C, Hallazgo #3).
     Desktop = one row of .tenant-tab-link: padding 0.75rem×2 (1.5rem) + a 0.875rem line at
     line-height 1.5 (≈1.31rem) + 2px bottom border ≈ 2.875rem. The mobile toggle differs
     slightly → its own min-height in the max-width:768px block below. */
  min-height: 2.875rem;
}
.tenant-nav-bar::-webkit-scrollbar { display: none; }

.tenant-tab-link {
  padding: 0.75rem 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--color-text-tertiary);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  flex-shrink: 0;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.tenant-tab-link:hover { color: var(--color-text-strong); }
.tenant-tab-link.active { color: var(--color-accent-text); background: var(--color-tab-active-bg); border-bottom-color: var(--color-primary); font-weight: 600; }
/* Impersonación (secciones-vendibles Fase 3, §8.3): sección no contratada, visible solo para el
   superadmin — atenuada + candado, pero clickeable (el backend deja pasar al superadmin, D6). */
.tenant-tab-link--locked, .tenant-tab-link.tenant-tab--locked { opacity: 0.5; font-style: italic; }
.tenant-tab-link.tenant-tab--locked:hover { opacity: 0.75; }

/* Group divider between nav zones. On desktop the label text is hidden (font-size:0)
   and the element renders as a thin vertical rule; the first group has no leading rule. */
.tenant-nav-group-head {
  align-self: center;
  flex-shrink: 0;
  font-size: 0;
  border-left: 1px solid var(--color-border);
  height: 1.25rem;
  margin: 0 0.4rem;
}
.tenant-nav-group-head.is-first { border-left: none; margin: 0; }

/* Mobile hamburger toggle (hidden on desktop) */
.tenant-nav-toggle {
  display: none;
}

@media (max-width: 768px) {
  /* On mobile: collapse tab bar into a hamburger dropdown */
  .tenant-nav-bar {
    flex-direction: column;
    overflow-x: visible;
    overflow-y: visible;
    padding: 0;
    /* Collapsed = just the .tenant-nav-toggle: padding 0.75rem×2 (1.5rem) + a 0.875rem line
       at line-height 1.5 (≈1.31rem), no border ≈ 2.8125rem. Per-breakpoint so degradation
       shows zero jump on BOTH mobile and desktop (Fase 1C, Hallazgo #3). */
    min-height: 2.8125rem;
  }

  /* Show the toggle button */
  .tenant-nav-toggle {
    display: flex;
    width: 100%;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1rem;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text);
    font-family: inherit;
    text-align: left;
  }
  .tenant-nav-toggle:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }

  .tenant-nav-active-label { flex: 1; }
  .tenant-nav-chevron { color: var(--color-text-muted); font-size: 0.75rem; flex-shrink: 0; }

  /* Hide all tab links by default on mobile */
  .tenant-tab-link {
    display: none;
    padding: 0.7rem 1.25rem;
    border-bottom: 1px solid var(--color-border);
    border-right: none;
    margin-bottom: 0;
    white-space: normal;
  }

  /* Show links when nav is open */
  .tenant-nav-bar.nav-open .tenant-tab-link {
    display: flex;
    align-items: center;
  }

  /* Group header: hidden when collapsed; an uppercase label row when nav is open */
  .tenant-nav-group-head { display: none; }
  .tenant-nav-bar.nav-open .tenant-nav-group-head {
    display: block;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    background: var(--color-surface-muted);
    padding: 0.5rem 1.25rem 0.25rem;
    border-left: none;
    height: auto;
    margin: 0;
  }

  /* Active link: left accent bar instead of bottom border */
  .tenant-tab-link.active {
    border-bottom-color: var(--color-border);
    border-left: 3px solid var(--color-primary);
    padding-left: calc(1.25rem - 3px);
    background: var(--color-tab-active-bg);
    color: var(--color-accent-text);
  }
}

/* ── Sidebar nav (vertical, accordion groups — Fase 1 sidebar-nav-groups) ──────────────────
   New component, not yet wired into the 24 live pages (chasis swap pending — see nav.js
   renderSidebarNav()). Verify visually via scripts/ui-harness/nav-sidebar/. Desktop: docked
   aside, collapsible to icons-only. ≤768px: becomes a fixed overlay drawer with backdrop,
   triggered by #hamburger-btn (wireSidebarMobileToggle in nav.js), closed by tapping a link
   or the backdrop. */
.sidebar-nav {
  width: 230px;
  flex: 0 0 230px;
  background: var(--color-surface);
  border-right: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  padding: 0.75rem 0.5rem;
  overflow-y: auto;
  transition: width 0.15s;
}
.sidebar-nav.sidebar-collapsed { width: 64px; flex-basis: 64px; }

/* Top bar: collapse chevron (left) + expand/collapse-all groups (right) — collapse chevron
   is duplicated at the bottom too (owner feedback: a single button far at the bottom is easy
   to miss on a long list). Both icon-only, no text label. */
.sidebar-topbar { display: flex; align-items: center; justify-content: space-between; padding: 0 0.15rem 0.4rem; gap: 0.4rem; }
/* Controles de utilidad SIN recuadro: con borde eran tres cajas que abrían la columna como si
   fueran una barra de herramientas, cuando lo que importa empieza debajo. */
.sidebar-utilbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  background: none;
  border: 0;
  padding: 0.2rem 0.3rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: inherit;
  font-size: 0.68rem;
  color: var(--color-text-muted);
}
.sidebar-utilbtn:hover { background: var(--color-surface-muted); color: var(--color-text-strong); }
.sidebar-utilbtn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.sidebar-collapse-btn { color: var(--color-text-tertiary); }
.sidebar-collapse-btn svg {
  width: 17px; height: 17px; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}

.sidebar-groups { display: flex; flex-direction: column; gap: 0.1rem; }

/* El grupo es un RÓTULO, no un botón gordo: antes pesaba más (15px/700) que los destinos que
   se navegan (13px, gris claro), o sea que lo más visible era lo que nunca se toca. */
.sidebar-navgroup-head {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  color: var(--color-text-muted);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.42rem 0.4rem;
  border-radius: var(--radius-md);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  font-family: inherit;
}
.sidebar-navgroup-head:hover { background: var(--color-surface-muted); color: var(--color-text-strong); }
.sidebar-navgroup-head .icon { display: inline-flex; flex: 0 0 auto; color: var(--color-text-tertiary); }
.sidebar-navgroup-head .icon svg,
.sidebar-navitem .icon svg {
  width: 18px; height: 18px; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
/* El conteo informa de lo que NO se ve: con el grupo abierto sobra, la lista ya está delante. */
.sidebar-navgroup-head .count { margin-left: auto; color: var(--color-text-muted); font-weight: 600; font-size: 0.66rem; letter-spacing: 0; }
.sidebar-navgroup.open .sidebar-navgroup-head .count { display: none; }
.sidebar-navgroup-head .chev { display: inline-flex; margin-left: auto; color: var(--color-text-muted); transition: transform 0.12s; }
.sidebar-navgroup-head .count + .chev { margin-left: 0.3rem; }
.sidebar-navgroup-head .chev svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; display: block; }
.sidebar-navgroup.open .sidebar-navgroup-head .chev { transform: rotate(90deg); }

/* Los hijos cuelgan de un carril (el ::before): sin él, con dos grupos abiertos no se ve dónde
   acaba uno y empieza el otro. */
.sidebar-navgroup-children {
  display: none;
  position: relative;
  padding: 0.1rem 0 0.35rem 0.72rem;
}
.sidebar-navgroup.open .sidebar-navgroup-children { display: block; }
.sidebar-navgroup-children::before {
  content: '';
  position: absolute;
  left: 0.95rem; top: 0.15rem; bottom: 0.4rem;
  width: 1px;
  background: var(--color-border);
}

.sidebar-navitem {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  position: relative;
  padding: 0.4rem 0.45rem 0.4rem 0.75rem;
  margin-left: 0.5rem;
  border-radius: var(--radius-md);
  font-size: 0.82rem;
  color: var(--color-text-secondary);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
}
.sidebar-navitem .icon { display: inline-flex; flex: 0 0 auto; color: var(--color-text-tertiary); }
.sidebar-navitem:hover { background: var(--color-surface-muted); color: var(--color-text-strong); }
.sidebar-navitem.active { background: var(--color-accent-bg); color: var(--color-accent-text); font-weight: 600; }
.sidebar-navitem.active .icon { color: var(--color-primary); }
/* La barra ancla el activo al carril: el fondo solo no basta cuando la lista es larga. */
.sidebar-navitem.active::before {
  content: '';
  position: absolute;
  left: -0.5rem; top: 0.35rem; bottom: 0.35rem;
  width: 2px;
  border-radius: var(--radius-pill);
  background: var(--color-primary);
}
.sidebar-navitem--locked, .sidebar-navitem.sidebar-navitem--locked { opacity: 0.5; font-style: italic; }
.sidebar-navitem--locked:hover { opacity: 0.75; }
.sidebar-navitem .label { overflow: hidden; text-overflow: ellipsis; }
.sidebar-navitem .lock { flex: 0 0 auto; font-size: 0.7rem; }
.sidebar-navitem .tenant-tab-badge { margin-left: auto; }

.sidebar-spacer { flex: 1; }
/* El de abajo es hijo directo de la columna flex: sin esto se estira a todo el ancho y se lee
   como un campo de texto vacío al pie del menú. */
.sidebar-nav > .sidebar-collapse-btn { align-self: flex-start; margin-left: 0.15rem; }


/* Estrechada: SOLO los 5 iconos de grupo. Antes se apilaban aquí los 20 destinos, y 20 iconos
   sin texto ni cabían en pantalla ni se distinguían entre sí. Las secciones de cada grupo salen
   ahora en `.sidebar-flyout` al posar el cursor (nav.js showSidebarFlyout). El botón de
   expandir/plegar todo se esconde: no queda acordeón que gobernar. */
.sidebar-nav.sidebar-collapsed .sidebar-toggle-all-btn,
.sidebar-nav.sidebar-collapsed .sidebar-navgroup-head .label,
.sidebar-nav.sidebar-collapsed .sidebar-navgroup-head .count,
.sidebar-nav.sidebar-collapsed .sidebar-navgroup-head .chev,
.sidebar-nav.sidebar-collapsed .sidebar-navgroup-children {
  display: none;
}
.sidebar-nav.sidebar-collapsed .sidebar-topbar { justify-content: center; }
.sidebar-nav.sidebar-collapsed .sidebar-navgroup-head {
  justify-content: center;
  padding: 0.5rem 0.4rem;
}
/* El resumen de avisos del grupo: solo existe estrechada (ensanchada se ven los de cada
   sección, y dos cifras para lo mismo confunden). Se posa sobre el icono, no ocupa ancho. */
/* `:not(.sidebar-collapsed)` y no un `display:none` suelto: `.tenant-tab-badge` se declara
   después en este archivo y, a igual especificidad, ganaría ella y el resumen se colaría también
   en la barra ancha (donde ya se ve el aviso de cada sección — dos cifras para lo mismo). */
.sidebar-nav:not(.sidebar-collapsed) .sidebar-group-badge { display: none; }
.sidebar-nav.sidebar-collapsed .sidebar-navgroup-head { position: relative; }
.sidebar-nav.sidebar-collapsed .sidebar-group-badge {
  display: block;
  position: absolute;
  top: 0.1rem;
  right: 0.5rem;
  margin: 0;
  border: 2px solid var(--color-surface);
}
.sidebar-nav.sidebar-collapsed .sidebar-group-badge.is-empty { display: none; }

/* Con los grupos pegados uno a otro hace falta la línea: si no, los 5 iconos leen como una
   lista sola de 5 secciones. */
.sidebar-nav.sidebar-collapsed .sidebar-navgroup + .sidebar-navgroup {
  margin-top: 0.3rem;
  padding-top: 0.3rem;
  border-top: 1px solid var(--color-border);
}

/* La tarjeta del grupo en modo estrecho: mismos ítems, fuera del carril (nav.js la cuelga de
   <body> por el recorte de overflow, igual que el globo). */
.sidebar-flyout {
  position: fixed;
  z-index: 1200;
  width: 200px;
  padding: 0.4rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
}
.sidebar-flyout-head {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  padding: 0.2rem 0.5rem 0.4rem;
}
/* Dentro de la tarjeta no hay carril del que colgar: los ítems clonados pierden la sangría. */
.sidebar-flyout .sidebar-navitem { margin-left: 0; padding-left: 0.5rem; }
.sidebar-flyout .sidebar-navitem.active::before { display: none; }

/* Collapsed tooltip (nav.js shows/hides this, see showSidebarTooltip). `position:fixed` +
   appended straight to <body> — NOT a CSS ::after on the item itself: .sidebar-nav has
   overflow-y:auto, and per the CSS Overflow spec a browser FORCES overflow-x to `auto` too
   whenever the other axis isn't `visible` (you cannot mix visible+auto on the two axes) — so
   any ::after escaping to the right of a 64px column was silently clipped no matter what
   overflow-x was declared as. A `position:fixed` element outside the scroll container entirely
   sidesteps that. Appears the instant the cursor lands (JS mouseenter, no CSS transition) —
   unlike the browser's native `title` tooltip (OS-level delay, ~500ms+, felt too slow).
   `aria-label` replaces `title` on the items so the native one never fires alongside this. */
.sidebar-tooltip {
  position: fixed;
  transform: translateY(-50%);
  background: var(--color-text-strong);
  color: var(--color-bg);
  padding: 0.3rem 0.6rem;
  border-radius: 6px;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
  z-index: 3000;
  pointer-events: none;
  box-shadow: var(--shadow-md, 0 2px 8px rgba(0, 0, 0, 0.2));
}

/* Mobile (≤768px): the sidebar becomes a fixed overlay drawer, hidden until #hamburger-btn
   opens it. Keyed off `body.nav-open` — the EXISTING generic mechanism in api.js's
   initHamburger() (icon toggle, ESC, backdrop-click-to-close), not a bespoke one: that button
   used to be forced hidden on every page (dead in practice, no page ever opted out via
   `keepHamburger`), so the sidebar reuses it instead of adding a second parallel mechanism.
   The backdrop is the EXISTING `.nav-overlay` (styles.css, appended to <body> by
   initHamburger) — z-index 199, below the sidebar's 1000. The icons-only collapse still
   works INSIDE the open drawer (§ owner call, Fase 1). */
@media (max-width: 768px) {
  .sidebar-nav {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 1000;
    transform: translateX(-100%);
    transition: transform 0.18s ease;
    box-shadow: 2px 0 12px rgba(0, 0, 0, 0.25);
  }
  body.nav-open .sidebar-nav { transform: translateX(0); }
  /* Solo-iconos es EXCLUSIVO de escritorio (decisión del dueño): en un teléfono 64px de
     iconos pelados se ven diminutos. Sin el colapso, los dos chevrones no tienen función. */
  .sidebar-collapse-btn { display: none; }
  .sidebar-topbar { justify-content: flex-start; }
  /* En el cajón manda el pulgar, no el ratón: fila de ~44px. En escritorio esa altura sería
     aire desperdiciado.
     El ancho se queda en 250px (~64% de un teléfono de 390): lo justo para que "Reportes
     programados" quepa entero, y ni un pixel más — el cajón tapa el contenido mientras está
     abierto, así que lo que sobre de ancho es pantalla que el usuario deja de ver. */
  .sidebar-nav { width: 250px; flex-basis: 250px; }
  .sidebar-navitem { padding-top: 0.62rem; padding-bottom: 0.62rem; font-size: 0.9rem; }
  .sidebar-navgroup-head { padding-top: 0.55rem; padding-bottom: 0.55rem; }
  .sidebar-utilbtn { font-size: 0.75rem; padding: 0.35rem 0.4rem; }
}

.tenant-tab-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-danger);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  border-radius: 999px;
  min-width: 1.1rem;
  height: 1.1rem;
  padding: 0 0.25rem;
  margin-left: 0.25rem;
  vertical-align: middle;
  line-height: 1;
}
/* Reserved-but-empty slot: keeps its width so the async count's arrival doesn't shift
   the navbar (inbox/pqrs paint this placeholder at boot, before the count resolves). */
.tenant-tab-badge.is-empty { visibility: hidden; }

/* ── Dark mode: overrides for inline <style> blocks in HTML pages ── */
/* dashboard.html */
html.dark-mode .kpi-card      { background: var(--color-surface); border-color: var(--color-border); }
html.dark-mode .kpi-value     { color: var(--color-text); }
html.dark-mode .kpi-label     { color: var(--color-text-tertiary); }
html.dark-mode .ratio-card    { background: var(--color-surface); border-color: var(--color-border); }
html.dark-mode .ratio-card h3 { color: var(--color-text-secondary); }
html.dark-mode .chart-card    { background: var(--color-surface); border-color: var(--color-border); }
html.dark-mode .chart-card h3 { color: var(--color-text-secondary); }
html.dark-mode .ratio-bar-wrap { background: var(--color-border); }
html.dark-mode .ratio-row     { color: var(--color-text-secondary); }
html.dark-mode .ratio-labels  { color: var(--color-text-tertiary); }
html.dark-mode .period-btn    { background: var(--color-surface); border-color: var(--color-border-strong); color: var(--color-text-secondary); }
html.dark-mode .period-btn.active { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }
html.dark-mode .dashboard-section h2 { color: var(--color-text); border-bottom-color: var(--color-border); }
html.dark-mode .table-card    { background: var(--color-surface); border-color: var(--color-border); }
html.dark-mode .data-table th { color: var(--color-text-tertiary); border-bottom-color: var(--color-border); }
html.dark-mode .data-table td { color: var(--color-text-secondary); border-bottom-color: var(--color-border); }
html.dark-mode .rate-card     { background: var(--color-surface); border-color: var(--color-border); }
html.dark-mode .rate-card .rate-title { color: var(--color-text-tertiary); }
html.dark-mode .rate-card .rate-val   { color: var(--color-text); }
html.dark-mode .ai-summary-card { background: var(--color-surface); border-color: var(--color-border); }
html.dark-mode .ai-summary-card .ai-card-label { color: var(--color-text-tertiary); }
html.dark-mode .ai-summary-card .ai-card-value { color: var(--color-text-strong); }
html.dark-mode .ai-summary-card .ai-card-sub   { color: var(--color-text-muted); }
html.dark-mode .endpoint-item { background: var(--color-surface-muted); }
html.dark-mode .endpoint-label { color: var(--color-text-secondary); }
html.dark-mode .endpoint-calls { color: var(--color-text-tertiary); }
html.dark-mode .endpoint-cost  { color: var(--color-text); }
html.dark-mode .top-conv-table th   { color: var(--color-text-tertiary); border-bottom-color: var(--color-border); }
html.dark-mode .top-conv-table td   { color: var(--color-text-secondary); border-bottom-color: var(--color-border); }
html.dark-mode .top-conv-table .mono { color: var(--color-text-tertiary); }
html.dark-mode .stock-col-card.red    { background: #3b1010; border-color: #7f1d1d; }
html.dark-mode .stock-col-card.yellow { background: #2d2000; border-color: #713f12; }
html.dark-mode .stock-col-card.green  { background: #0f2d1a; border-color: #14532d; }

/* Warning/notice amber blocks */
.notice-yellow { background: #fffbeb; border: 1px solid #fde68a; color: #92400e; }

/* Warning/notice amber blocks — soften in dark mode */
html.dark-mode .window-notice,
html.dark-mode .notice-yellow,
html.dark-mode .save-warning  { background: #2d1f00; border-color: #78450a; color: #fbbf24; }
html.dark-mode .message-internal { background: #2d1f00; border-left-color: var(--color-warning); color: #fbbf24; }
html.dark-mode .message-internal.level-error { background: #2d0a0f; border-color: #f43f5e; color: #fda4af; }

/* ── Tab content: scroller del tab activo (catalog/scheduling). Fase 1D Vía A: el
   alto lo da el flex del app-shell (body.app-shell--fixed .tab-content), ya no se
   mide por JS; este bloque base solo fija padding + overflow. ── */
.tab-content {
  padding: 1rem 0;
  overflow-y: auto;
  /* `overflow-x` DEBE declararse. Regla de CSS poco conocida y causa real de la
     barra horizontal "de dos píxeles" del panel en teléfono: si un eje es distinto
     de `visible`, el navegador computa el otro como `auto` — declarar solo
     `overflow-y: auto` activa TAMBIÉN el scroll lateral, y cualquier hijo que se
     pase por un pelo lo hace visible. `clip` y no `hidden` porque `hidden` crearía
     un segundo contexto de scroll y rompería las cabeceras pegajosas de dentro.
     Lo ancho de verdad (tablas) vive en `.table-wrapper`, que tiene su propio
     scroll y no depende de esto. */
  overflow-x: clip;
}
@media (max-width: 600px) {
  .tab-content { padding: 0.75rem 0; }
}
/* Scrollbars en modo oscuro — unificado para TODAS las zonas scrolleables.
   No des-oculta barras con display:none/scrollbar-width:none (p.ej. tenant-nav-bar). */
html.dark-mode { scrollbar-color: var(--color-border-strong) var(--color-bg); }
html.dark-mode ::-webkit-scrollbar             { width: 8px; height: 8px; }
html.dark-mode ::-webkit-scrollbar-track       { background: var(--color-bg); }
html.dark-mode ::-webkit-scrollbar-thumb       { background: var(--color-border-strong); border-radius: 4px; }
html.dark-mode ::-webkit-scrollbar-thumb:hover { background: var(--color-text-muted); }

html.dark-mode .tab-content                              { scrollbar-color: var(--color-border-strong) var(--color-surface); }
html.dark-mode .tab-content::-webkit-scrollbar           { width: 6px; }
html.dark-mode .tab-content::-webkit-scrollbar-track     { background: var(--color-surface); }
html.dark-mode .tab-content::-webkit-scrollbar-thumb     { background: var(--color-border-strong); border-radius: 3px; }
html.dark-mode .tab-content::-webkit-scrollbar-thumb:hover { background: var(--color-text-muted); }
html.dark-mode .modal-body                              { scrollbar-color: var(--color-border-strong) var(--color-surface); }
html.dark-mode .modal-body::-webkit-scrollbar           { width: 6px; }
html.dark-mode .modal-body::-webkit-scrollbar-track     { background: var(--color-surface); }
html.dark-mode .modal-body::-webkit-scrollbar-thumb     { background: var(--color-border-strong); border-radius: 3px; }
html.dark-mode .modal-body::-webkit-scrollbar-thumb:hover { background: var(--color-text-muted); }

/* ============================================================ */
/* Scheduling — Week View (vista semanal de la agenda)          */
/* ============================================================ */

/* grilla — ancho completo en escritorio, scroll horizontal en mobile (.table-wrapper) */
#week-grid { border-collapse: collapse; width: 100%; min-width: 820px; table-layout: fixed; font-size: 0.9rem; }
#week-grid th, #week-grid td { border: 1px solid var(--color-border); padding: 0.45rem 0.5rem; white-space: nowrap; text-align: center; }
#week-grid th { background: var(--color-surface-muted); color: var(--color-text-strong); font-weight: 600; }
#week-grid td { height: 38px; }
#week-grid td.week-time { width: 64px; font-weight: 600; background: var(--color-surface-muted); color: var(--color-text-secondary); }

/* celdas — colores base (modo claro) */
.slot-closed  { background: var(--color-surface-muted); color: var(--color-text-muted); }
/* celdas en el pasado (hora de fin ya pasó): atenuadas, no seleccionables */
td.slot-past       { background: var(--color-surface-muted); color: var(--color-text-muted); cursor: default; }
td.slot-past:hover { background: var(--color-surface-muted); }
.slot-free    { background: #338554; cursor: pointer; }
.slot-free:hover { background: #2c7349; }
.slot-busy    { background: #dbeafe; color: var(--color-text); cursor: pointer; font-size: 0.82rem; overflow: hidden; text-overflow: ellipsis; }
.slot-busy:hover { background: #bfdbfe; }
.slot-busy.status-cancelled { background: #fee2e2; }

/* celdas — overrides para modo oscuro (tonos apagados, sin brillo) */
html.dark-mode .slot-free:hover      { background: #3d9a63; }
html.dark-mode .slot-busy            { background: #1e3a5f; }
html.dark-mode .slot-busy:hover      { background: #2a4d73; }
html.dark-mode .slot-busy.status-cancelled { background: #4c1d1d; }

/* Hora de inicio dentro de la celda libre — oculta en escritorio (ya está la
   columna "Hora"), visible en mobile donde el scroll horizontal la esconde. */
.slot-time { display: none; color: #fff; font-size: 0.7rem; font-weight: 600; }

/* Header fijo (sticky) de la grilla — escritorio Y mobile. El ÚNICO scroll
   vertical es el del body de la tabla: en vista semana el .tab-content no
   scrollea (week-active) y el wrapper toma el alto restante vía flex. Así no
   hay doble scrollbar. box-shadow simula el borde inferior (con border-collapse
   el borde no sigue al th sticky durante el scroll). */
#tab-agenda .tab-content.week-active           { overflow: hidden; display: flex; flex-direction: column; }
#tab-agenda .tab-content.week-active #week-view-container { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
#tab-agenda .tab-content.week-active #week-toolbar        { flex: 0 0 auto; }
#tab-agenda .tab-content.week-active #week-summary        { flex: 0 0 auto; }
#tab-agenda .tab-content.week-active #week-board         { flex: 1 1 auto; min-height: 0; }
/* La tabla ya no se ve en pantalla (solo se imprime), pero la regla se conserva por si un
   día vuelve a mostrarse: sin ella, en vista semana no tomaría el alto restante. */
#tab-agenda .tab-content.week-active .table-wrapper       { flex: 1 1 auto; min-height: 0; overflow: auto; }
#week-grid thead th { position: sticky; top: 0; z-index: 2; box-shadow: inset 0 -1px 0 var(--color-border); }

@media (max-width: 768px) {
  .slot-time { display: inline; }
}

/* toggle Día / Semana */
.view-toggle { display: flex; gap: 0.25rem; margin-bottom: 0.75rem; }
.toggle-btn { padding: 0.3rem 0.85rem; border: 1px solid var(--color-border-strong); border-radius: 6px; background: var(--color-surface); color: var(--color-text); cursor: pointer; font-size: 0.88rem; }
.toggle-btn.active { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }

/* Controles de semana: la barra es la pieza `ui-toolbar` (ui-system.css), igual que la del día;
   aquí solo queda el rango de fechas, que va dentro del bloque de contexto de esa pieza. */
#week-label { white-space: nowrap; }

/* Modal de booking — bloque fijo (cabecera + paciente) vs. bloque scrollable
   (solo los campos de abajo del paciente). El .modal-box ya es flex column. */
#modal-week-booking .wb-fixed  { flex: 0 0 auto; }
#modal-week-booking .wb-scroll { flex: 0 1 auto; min-height: 0; overflow-y: auto; }

/* Scrollbar del área scrollable — respeta el modo oscuro. */
html.dark-mode #modal-week-booking .wb-scroll { scrollbar-color: var(--color-border-strong) var(--color-surface); }
html.dark-mode #modal-week-booking .wb-scroll::-webkit-scrollbar { width: 6px; }
html.dark-mode #modal-week-booking .wb-scroll::-webkit-scrollbar-track { background: var(--color-surface); }
html.dark-mode #modal-week-booking .wb-scroll::-webkit-scrollbar-thumb { background: var(--color-border-strong); border-radius: 3px; }
html.dark-mode #modal-week-booking .wb-scroll::-webkit-scrollbar-thumb:hover { background: var(--color-text-muted); }

/* Mobile: ancla el modal arriba y deja una franja inferior LIBRE = alto del nav
   bar (token fijo --app-chrome-nav-h, Fase 1D Vía A), como footer invisible. */
@media (max-width: 768px) {
  /* Ancla arriba y reserva abajo 2× el alto del nav bar (franja inferior libre).
     100dvh respeta el viewport visible real del navegador móvil (con fallback a vh). */
  #modal-week-booking { align-items: flex-start; padding: 1rem 1rem calc(2 * var(--app-chrome-nav-h) + 1rem); }
  #modal-week-booking .modal-box {
    max-height: calc(100vh - 2 * var(--app-chrome-nav-h) - 2rem);
    max-height: calc(100dvh - 2 * var(--app-chrome-nav-h) - 2rem);
  }
}


/* ===== PQRS chat media — Conversación e Inbox (media-lightbox.js) ===== */
.chat-media-img {
  display: block;
  max-width: 160px;
  max-height: 160px;
  object-fit: cover;
  border-radius: 8px;
  margin-bottom: 4px;
  cursor: zoom-in;
  border: 1px solid var(--color-border);
}
.chat-media-file {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 4px;
  padding: 0.4rem 0.65rem;
  border-radius: 8px;
  background: var(--color-surface-muted);
  color: var(--color-text);
  text-decoration: none;
  font-size: 0.82rem;
  border: 1px solid var(--color-border);
}
.chat-media-file:hover { border-color: var(--color-border-strong); }

/* Lightbox overlay (auto-instalado en <body>) */
.media-lightbox {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
}
.media-lightbox-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.82); }
.media-lightbox-box {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  max-width: 96vw;
  max-height: 96vh;
}
.media-lightbox-img {
  max-width: 92vw;
  max-height: 82vh;
  object-fit: contain;
  border-radius: 8px;
  background: var(--color-surface);
  cursor: zoom-in;
}
.media-lightbox-img.zoomed { max-width: none; max-height: none; width: auto; cursor: zoom-out; }
.media-lightbox-bar { position: relative; display: flex; gap: 0.5rem; }

/* ════════════════════════════════════════════════════════════════════════════
   App-shell (Fase 1D Vía A, header fijo — Fase 1 sidebar-nav-groups actualizó la nav)
   El header (logo/nombre + tema/cerrar sesión) queda FIJO a todo el ancho arriba; debajo,
   un sidebar vertical (izquierda, con sus 5 grupos-acordeón, ver nav.js renderSidebarNav)
   y el contenido (dentro de .app-content-main > .container, centrado) van lado a lado —
   cada uno scrollea POR SU CUENTA. El flexbox calcula las alturas → las 6 variables
   medidas por JS (--inbox-, --conv-, --tab-content-max-h, --tenant-nav-h) se eliminan.
   Opt-in por página vía body.app-shell.
   DOM: <body.app-shell> > [banner] + header + <main.app-content> (fila: <aside.sidebar-nav>
   + <div.app-content-main> con el .container centrado dentro) + footer.
   Reemplaza el navbar horizontal (`.tenant-nav-wrap`/`.tenant-nav-bar`/`fillNavBar` en
   nav.js): ese código queda SIN USAR tras el swap — ver pendientes.md § limpieza.
   ════════════════════════════════════════════════════════════════════════════ */
body.app-shell {
  height: 100vh;            /* fallback */
  height: 100dvh;           /* viewport dinámico: respeta teclado/barras del móvil */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* Chrome fijo a lo ancho (hijos directos del shell) */
body.app-shell > .impersonation-banner,
body.app-shell > .header,
body.app-shell > .tenant-nav-wrap,
body.app-shell > .tenant-nav-bar,           /* degradación legacy: navbar sin wrapper */
body.app-shell > .app-footer { flex-shrink: 0; }
body.app-shell > .impersonation-banner { position: static; }   /* ya no sticky: es flex-item del shell */

/* Header a todo el ancho, contenido con padding lateral (estilo admin); pegado al
   navbar (margin-bottom:0). Conserva su border-bottom como separador (como el admin). */
body.app-shell > .header { padding: 1rem 1.5rem; margin-bottom: 0; }

/* Navbar a todo el ancho + indicador de scroll (degradado) en el borde derecho,
   visible solo cuando hay más pestañas de las que caben (nav.js marca .is-scrollable). */
body.app-shell > .tenant-nav-wrap { position: relative; }
.tenant-nav-wrap > .tenant-nav-bar { width: 100%; }
.tenant-nav-wrap::after {
  content: ''; position: absolute; top: 0; right: 0; bottom: 0; width: 2rem;
  background: linear-gradient(to right, transparent, var(--color-surface));
  pointer-events: none; opacity: 0; transition: opacity 0.15s;
}
.tenant-nav-wrap.is-scrollable::after { opacity: 1; }

/* .app-content ahora es una FILA: <aside.sidebar-nav> (ancho fijo, scroll propio) +
   <div.app-content-main> (el resto — .container centrado adentro, conserva max-width +
   margin auto). overflow:hidden aquí a propósito: cada hijo scrollea POR SU CUENTA
   (.sidebar-nav ya trae su propio overflow-y:auto; .app-content-main hereda abajo el
   overflow-y:auto que antes tenía .app-content) — si el padre también scrolleara, el
   sidebar se movería junto con el contenido en vez de quedar fijo en pantalla. */
body.app-shell > .app-content {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  overflow: hidden;
}
.app-content-main {
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
  padding-bottom: var(--app-chrome-nav-h);  /* zona segura para el botón flotante del tutor */
}
.app-footer {
  flex-shrink: 0;
  height: var(--app-chrome-nav-h);          /* footer fantasma transparente */
}

/* Modo app-fixed (catalog/scheduling/inbox/conversation): el main NO scrollea; el
   .container interno fila (flex) y su hijo (tabla/tab-content/chat) scrollea solo.
   Se ancla a .app-content-main (no a .app-content, que ahora SIEMPRE es la fila del
   sidebar) — .app-content-main es quien reemplaza el rol que .app-content tenía antes
   de que el sidebar se insertara como su hermano. */
body.app-shell--fixed .app-content-main { overflow: hidden; display: flex; flex-direction: column; padding: 0; }
body.app-shell--fixed .app-content-main > .container { flex: 1 1 auto; min-height: 0; min-width: 0; width: 100%; display: flex; flex-direction: column; }
/* Footer fantasma (Fase 1E, decisión del dueño 2026-06-20): presente por defecto también
   en --fixed (catalog/scheduling/páginas tabla-lista). Se apaga SOLO donde la barra inferior
   propia es el composer del chat (inbox/conversation) vía .app-shell--no-footer. */
body.app-shell--no-footer .app-footer { display: none; }

/* Modo B (catalog/scheduling/purchasing/finance): la barra de pestañas (.tabs) queda
   fija y solo el .tab-content activo scrollea. Cadena flex: container (molde) →
   wrapper de pestañas (#catalog-content, #purchasing-content, #finance-content; el
   propio container en scheduling) → panel activo (#tab-*) → .tab-content. Reemplaza
   la medición JS de --tab-content-max-h. El thead sticky + tbody scroll del
   week-grid/data-scroll quedan intactos (siguen anclados a su scroller).
   CADA wrapper nuevo con este patrón (container > #wrapper-content > .tabs + #tab-*)
   DEBE sumarse aquí a las dos listas — si no, el ancho no se acota y la tabla queda
   guillotinada sin scroll horizontal en móvil (ver nota debajo y panel-frontend-guide §7).
   :not(.u-hidden) en #purchasing-content (gate de catalog_enabled, igual que
   #catalog-content) y en los [id^="tab-"] de ambos (toggle de pestaña activa): sin el
   guard, el display:flex de esta regla le gana en especificidad a `.u-hidden { display:
   none }` (id > clase) y muestra lo que debía seguir oculto — mismo patrón ya usado en
   support.css para #support-content. */
body.app-shell--fixed #catalog-content,
body.app-shell--fixed #purchasing-content:not(.u-hidden),
body.app-shell--fixed #finance-content { flex: 1 1 auto; min-height: 0; min-width: 0; max-width: 100%; display: flex; flex-direction: column; }
body.app-shell--fixed .tabs { flex-shrink: 0; }
body.app-shell--fixed #catalog-content > [id^="tab-"],
body.app-shell--fixed #purchasing-content > [id^="tab-"]:not(.u-hidden),
body.app-shell--fixed #finance-content > [id^="tab-"]:not(.u-hidden),
body.app-shell--fixed .app-content-main > .container > [id^="tab-"] { flex: 1 1 auto; min-height: 0; min-width: 0; max-width: 100%; display: flex; flex-direction: column; }
/* min-width:0 + acotado de ancho en TODA la cadena flex-column. Sin esto, una tabla
   más ancha que el viewport (que lleva min-width:max-content para poder deslizar) NO
   empuja al .container a encogerse: el ancho se PROPAGA hacia arriba por la cadena hasta
   el .container (que crece a su contenido) y el `main.app-content` (overflow:hidden) lo
   GUILLOTINA sin barra de scroll → la tabla se ve cortada y no desliza. El min-width:0 da
   permiso de encoger pero no obliga; el ancho fijo sí: el .container lleva width:100%
   (acota a la pantalla y CONSERVA su max-width de diseño 1000/1400 → escritorio intacto),
   y cada eslabón interno lleva max-width:100% (no exceder al padre). Así el desbordamiento
   muere DENTRO del wrapper de la tabla (.inv-tbl-wrap / .table-wrapper / .survey-table-wrap,
   que sí tienen overflow-x:auto) en vez de subir hasta app-content. Diagnóstico vía DevTools:
   en iPhone (390px) el .container medía 1097px (no se acotaba). Ver panel-frontend-guide §7. */
body.app-shell--fixed .tab-content { max-height: none; flex: 1 1 auto; min-height: 0; min-width: 0; max-width: 100%; }

/* Corrección #1 (ataque W6.1): menú móvil con scroll interno — nunca empuja/corta el centro */
@media (max-width: 768px) {
  :root { --app-chrome-nav-h: 2.8125rem; }   /* casa con el min-height de las pestañas colapsadas móviles */
  body.app-shell > .header { padding-left: 1rem; padding-right: 1rem; }
  .tenant-nav-wrap::after { display: none; }  /* en móvil el navbar colapsa a toggle (sin scroll horizontal) */
  .tenant-nav-bar.nav-open { max-height: 70vh; overflow-y: auto; }
}

/* Corrección #3 (ataque W7.5) + pantallas muy bajas (W6.1/zoom): liberar el alto fijo */
@media (max-height: 460px) {
  body.app-shell, body.app-shell > .app-content { height: auto; overflow: visible; }
}
@media print {
  body.app-shell, body.app-shell > .app-content { height: auto; overflow: visible; display: block; }
  .app-footer { display: none; }
  .sidebar-nav { display: none; }  /* la nav no tiene nada que aportar en papel */
}

/* ════════════════════════════════════════════════════════════════════════════
   Estándar de tablas/listas (Fase 1E) — encabezado fijo + scroll interno
   Una sola fuente de verdad para el patrón que catalog/scheduling resolvían a
   mano: thead/filtros FIJOS y solo el cuerpo scrollea. Se apoya en el app-shell
   --fixed (1D): cadena flex body.app-shell--fixed .app-content-main > .container
   (fill) → .data-view (fill) → .data-scroll (scrollea). Tokens, cero hex nuevo.
   ════════════════════════════════════════════════════════════════════════════ */
/* Columna flex que llena el alto disponible dentro del .container fijo (--fixed). */
.data-view    { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
/* Región superior fija: ENVUELVE título + .filters-row + botones existentes. No scrollea. */
.data-toolbar { flex-shrink: 0; margin-bottom: 0.75rem; }
/* EL único scroller: cuerpo de la tabla/lista. Ambos ejes (subsume .table-wrapper). */
.data-scroll  { flex: 1 1 auto; min-height: 0; overflow: auto; padding-bottom: 0.5rem; }
/* Región inferior fija: paginación / acciones. No scrollea. */
.data-footer  { flex-shrink: 0; }
/* Encabezado de columnas pegado: fondo OPACO por token (evita bleed en claro/oscuro). */
.data-scroll table thead th { position: sticky; top: 0; z-index: 2; background: var(--color-surface); }

/* ════════════════════════════════════════════════════════════════════════════
   UTILIDADES (Fase 5 — erradicar inline). Declaradas AL FINAL a propósito:
   styles.css carga de último en cada página → estas clases ganan los empates de
   especificidad contra reglas de página de igual peso. TODO sale de tokens 1A:
   cero hex literal. Catálogo y rationale: plan/panel-ui-standardization/
   05-fase-5-DISENO-ENDURECIDO.md §3.
   ════════════════════════════════════════════════════════════════════════════ */

/* --- Visibilidad --- */
/* Solo para buckets SEGUROS: estático o mostrado por JS con display='block/flex/inline'
   (el write inline gana a la clase). NO usar donde el JS muestra con display='' (vuelve
   a la clase y queda oculto): ahí se cambia el JS a classList o se conserva el inline. */
.u-hidden { display: none; }

/* --- Flex --- */
.u-flex         { display: flex; align-items: center; }
.u-flex-between { display: flex; align-items: center; justify-content: space-between; }
.u-flex-end     { display: flex; justify-content: flex-end; }
.u-flex-col     { display: flex; flex-direction: column; }
.u-flex-wrap    { flex-wrap: wrap; }
.u-gap-1 { gap: var(--space-1); }
.u-gap-2 { gap: var(--space-2); }
.u-gap-3 { gap: var(--space-3); }

/* --- Espaciado (desde la escala de tokens) --- */
.u-m0   { margin: 0; }
.u-mt-2 { margin-top: var(--space-2); }
.u-mt-3 { margin-top: var(--space-3); }
.u-mt-4 { margin-top: var(--space-4); }
.u-mb-2 { margin-bottom: var(--space-2); }
.u-mb-3 { margin-bottom: var(--space-3); }

/* --- Ancho --- */
.u-w-full { width: 100%; }
.u-w-auto { width: auto; }

/* --- Texto (color desde tokens) --- */
.text-strong    { color: var(--color-text-strong); }
.text-secondary { color: var(--color-text-secondary); }
.text-tertiary  { color: var(--color-text-tertiary); }
.text-muted     { color: var(--color-text-muted); }
.text-danger    { color: var(--color-danger); }
.text-success   { color: var(--color-success); }
.u-fs-sm { font-size: 0.85rem; }
.u-fs-xs { font-size: 0.75rem; }

/* --- Micro-componentes (patrones repetidos enteros) --- */
/* Etiqueta de campo: fs pequeño + gris secundario + bloque + margen inferior. */
.u-field-label {
  display: block;
  font-size: 0.8rem;
  color: var(--color-text-secondary);
  margin-bottom: var(--space-1);
}

/* Mensaje de feedback bajo un control = composición de atómicas (NO un componente con
   display:none horneado, que rompería los que el JS muestra con display=''):
   ok  → class="u-hidden u-fs-sm text-success"
   err → class="u-hidden u-fs-sm text-danger" */

/* Cajita/panel sutil: superficie tenue + borde + radio + padding. */
.u-panel {
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 1.25rem;
}

/* Input/select autónomo (no hay estilo base de input en styles.css). */
.u-input {
  width: 100%;
  box-sizing: border-box;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  font-size: 0.85rem;
  background: var(--color-input);
  color: var(--color-text);
  font-family: inherit;
}

/* ── Bandeja de oportunidades (copiloto-oportunidades F1) ───────────────────────────────────
   Cargado en TODAS las páginas opt-in vía content-end.html (script común, mismo punto que
   panel-record-tracker.js) — por eso vive en el CSS global, no en un .css de sección.
   z-index: por debajo de .modal-overlay (100, styles.css:665) a propósito — el modal de
   confirmación (que reusa esa clase) debe quedar SIEMPRE por encima del drawer; por debajo del
   tutor (9000+, help-overlay.css). */

.opp-bell-btn { position: relative; }

.opp-drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.35);
  z-index: 89;
}

.opp-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(400px, 100vw);
  background: var(--color-surface);
  box-shadow: -8px 0 32px rgba(0, 0, 0, 0.2);
  z-index: 90;
  display: flex;
  flex-direction: column;
}

/* .opp-drawer declara su propio display:flex — necesita mayor especificidad que .u-hidden
   (misma especificidad + declarado después perdería la cascada, ver aviso en §"Visibilidad"). */
.opp-drawer.u-hidden { display: none; }

.opp-drawer-header {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--color-border);
}

.opp-drawer-header h2 {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text-strong);
  margin: 0;
}

.opp-drawer-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0.75rem;
}

/* Subtítulos de grupo dentro del radar: separan "Oportunidades" (accionables) de "Resúmenes"
   (informes, no accionables). Se pintan solo si el grupo tiene tarjetas (appendOppGroup). */
.opp-group-title {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
  padding: 0.9rem 0.25rem 0.45rem;
}
.opp-group-title:first-child { padding-top: 0.25rem; }

/* Variante embebida arriba del chat de Copiloto (§9, último bullet) — mismo feed, montado
   directo (sin el flex/scroll del drawer): alto acotado con scroller propio para no empujar el
   composer fuera de pantalla si hay varias tarjetas; colapsa a padding chico en el estado vacío. */
.opp-feed-inline {
  max-height: 220px;
  overflow-y: auto;
  padding: 0.75rem 0.75rem 0;
  flex-shrink: 0;
}
.opp-feed-inline .opp-empty-state { padding: 0.5rem 0.25rem 0.75rem; font-size: 0.78rem; }
.opp-feed-inline .opp-card:last-child { margin-bottom: 0.75rem; }

.opp-empty-state {
  text-align: center;
  color: var(--color-text-secondary);
  font-size: 0.85rem;
  padding: 2rem 1.25rem;
  line-height: 1.5;
}

.opp-card {
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 0.85rem 1rem;
  margin-bottom: 0.75rem;
}

/* Identidad propia de la tarjeta digest (F2 §10) — resumen/informe redactado, distinta de una
   alerta o una acción: acento a la izquierda con el token de acento ya usado en el resto del panel. */
.opp-card--digest {
  border-left: 3px solid var(--color-accent-border);
}

/* Candidato "recopilando datos" (warming-up.ts) — informativa, sin acción: apagada, mismo
   criterio que .faq-item.inactive (opacity, sin tocar el token de color). */
.opp-card--warming {
  opacity: 0.6;
}

.opp-card-title {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-text-strong);
  margin-bottom: 0.25rem;
}

.opp-card-body {
  font-size: 0.82rem;
  color: var(--color-text-secondary);
  margin-bottom: 0.6rem;
  line-height: 1.4;
}

.opp-card-detail {
  font-size: 0.8rem;
  color: var(--color-text-tertiary);
  margin: 0 0 0.6rem;
}

.opp-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.opp-confirm-title {
  font-size: 0.95rem;
  color: var(--color-text-strong);
  margin-bottom: 0.75rem;
  line-height: 1.4;
}

.opp-confirm-message {
  min-height: 4.5rem;
  resize: vertical;
  margin-bottom: 0.75rem;
}

.opp-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}

/* Selector de reacomodo de agenda (copiloto F6 §7) — solo tokens, 0 hex a mano. */
.opp-rs-box {
  max-width: 34rem;
  width: 100%;
  display: flex;
  flex-direction: column;
  max-height: 85vh;
}
.opp-rs-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}
.opp-rs-head .opp-confirm-title { margin-bottom: 0; }
.opp-rs-body {
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.opp-rs-hint { font-size: 0.85rem; color: var(--color-text-secondary); margin: 0; }
.opp-rs-msg { font-size: 0.9rem; color: var(--color-text); line-height: 1.5; }
.opp-rs-day {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.opp-rs-day-head {
  background: var(--color-surface-muted);
  color: var(--color-text-secondary);
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0.4rem 0.6rem;
}
.opp-rs-appt {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  width: 100%;
  text-align: left;
  padding: 0.5rem 0.6rem;
  background: var(--color-surface);
  border: none;
  border-top: 1px solid var(--color-border);
  cursor: pointer;
  color: var(--color-text);
}
.opp-rs-appt:hover:not(.is-disabled) { background: var(--color-surface-muted); }
.opp-rs-appt.is-disabled { cursor: default; opacity: 0.6; }
.opp-rs-appt-main { font-size: 0.9rem; color: var(--color-text-strong); }
.opp-rs-appt-sub { font-size: 0.78rem; color: var(--color-text-tertiary); }
.opp-rs-summary {
  font-size: 0.9rem;
  color: var(--color-text-strong);
  background: var(--color-accent-bg);
  border: 1px solid var(--color-accent-border);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.6rem;
}
.opp-rs-slots { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.3rem; }
.opp-rs-slot {
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: 0.82rem;
  cursor: pointer;
}
.opp-rs-slot:hover { background: var(--color-surface-muted); }
.opp-rs-slot.is-selected {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  border-color: var(--color-primary);
}
.opp-rs-send { align-self: flex-start; margin-top: 0.3rem; }

/* Reagendar (unificacion-reagendamiento Fase 3): picklist de fecha (toda la ventana) + huecos de la
   fecha elegida cargados bajo demanda. Reusa .opp-rs-slots/.opp-rs-slot para los botones de hora. */
.reschedule-field-label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--color-text-secondary);
  margin: 0.6rem 0 0.3rem;
}
.reschedule-date-select {
  width: 100%;
  box-sizing: border-box;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  font-size: 0.9rem;
  background: var(--color-input);
  color: var(--color-text);
  font-family: inherit;
}
.reschedule-slots-for-date { margin-top: 0.2rem; }

/* ════════════════════════════════════════════════════════════════════════════
   Utilidad compartida de impresión (print-utils.js) — imprimir una sección del
   panel como documento. printSection() clona el objetivo en #print-root y marca
   <body class="printing">. Aquí: mostrar SOLO #print-root, cabecera del documento
   y forzar tema CLARO en papel (redeclarando los tokens neutros en el subtree,
   pase lo que pase con el modo oscuro del tenant). La orientación @page la fija
   el CSS de cada página (landscape en scheduling.css / catalog.css).
   ════════════════════════════════════════════════════════════════════════════ */

/* Contenedor de impresión: invisible en pantalla; solo existe durante window.print(). */
#print-root { display: none; }
/* Opt-out / opt-in genéricos para cualquier sección imprimible. */
.print-only { display: none; }

@media print {
  /* Papel blanco aunque el tenant tenga modo oscuro (evita el fondo oscuro del
     app-shell si el usuario activa "gráficos de fondo" al imprimir). */
  html, body.printing { background: #ffffff !important; }
  /* Solo el documento clonado se imprime; el chrome y el resto del app-shell fuera. */
  body.printing > *:not(#print-root) { display: none !important; }
  body.printing #print-root { display: block !important; }
  .no-print { display: none !important; }
  .print-only { display: block !important; }

  /* Papel blanco + tema CLARO forzado en el subtree (redeclara tokens neutros:
     las custom properties heredan, así que ganan a html.dark-mode dentro de #print-root). */
  #print-root {
    background: #ffffff;
    color: #0f172a;
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
    --color-bg: #ffffff;
    --color-surface: #ffffff;
    --color-surface-muted: #f1f5f9;
    --color-border: #cbd5e1;
    --color-border-strong: #94a3b8;
    --color-text-strong: #0f172a;
    --color-text: #1e293b;
    --color-text-secondary: #334155;
    --color-text-tertiary: #475569;
    --color-text-muted: #64748b;
  }

  /* Cabecera del documento impreso. */
  .print-doc-header { margin-bottom: 8mm; color: #0f172a; }
  .print-doc-header .pdh-business { font-size: 15pt; font-weight: 700; }
  .print-doc-header .pdh-title    { font-size: 13pt; font-weight: 600; margin-top: 1mm; }
  .print-doc-header .pdh-range    { font-size: 11pt; margin-top: 1mm; }
  .print-doc-header .pdh-printed  { font-size: 9pt; color: #64748b; margin-top: 2mm; }

  /* Tablas dentro del documento: header repetido por página, filas sin cortarse. */
  #print-root table { width: 100%; min-width: 0 !important; border-collapse: collapse; }
  #print-root thead { display: table-header-group; }
  #print-root thead th { position: static !important; box-shadow: none !important; background: #f1f5f9 !important; color: #0f172a !important; }
  #print-root th, #print-root td { border: 1px solid #cbd5e1 !important; }
  #print-root tr, #print-root td { break-inside: avoid; page-break-inside: avoid; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   LA CAMPANA DE AVISOS (plan app-avisos-campana, Fase 2)
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Lo que el negocio recibe —agendó, canceló, compró, está molesto— dejaba de existir en cuanto
   el aviso del celular se tocaba. Esto es su bandeja.

   POR QUÉ VIVE AQUÍ y no en una hoja propia: es CHASIS. Aparece en todas las páginas del panel,
   igual que el radar de oportunidades, y una hoja aparte obligaría a cargarla en las veinte
   páginas o a que faltara en alguna.

   FORMA: en el teléfono ocupa TODO el contenido —el dueño lo decidió al ver el boceto: una hoja
   a media altura deja una franja muerta arriba y obliga a leer quince avisos por una ranura—.
   Desde 769 px se recoge a un panel anclado a la derecha, como el radar. Es UNA media query, no
   una segunda implementación.

   Las formas de dentro salen del sistema de piezas (ui-btn, ui-segmented, ui-badge) y la tarjeta
   calca .app-order, que a su vez calcó .am-card. Aquí no se estrena lenguaje visual. */

.nb-bell-btn { position: relative; }

/* El velo. Solo se ve desde 769 px, y ahora de verdad: la intención estaba escrita aquí desde el
   principio ("en el teléfono el panel tapa el contenido entero") pero no había regla que la
   cumpliera, y el panel del teléfono NO tapa el contenido entero — respeta la barra del negocio,
   que es chasis. Resultado en un teléfono real: lo único que el velo llegaba a oscurecer era esa
   franja, así que al abrir Avisos el nombre del negocio y sus iconos se veían bajo una sombra
   sucia, como si estuvieran deshabilitados. Y tampoco hacía falta para cerrar: ahí no queda nada
   fuera del panel donde se pueda tocar. */
.nb-backdrop {
  position: fixed;
  inset: 0;
  z-index: 89;
  background: rgb(15 23 42 / 35%);
  display: none;
}
@media (min-width: 769px) {
  .nb-backdrop { display: block; }
}
.nb-backdrop.u-hidden { display: none; }

.nb-panel {
  position: fixed;
  /* Debajo de la barra del negocio: el nombre, la campana y salir son chasis y no se tapan
     (decisión del dueño). El alto lo publica el chasis en --app-header-h; sin ese dato (páginas
     aún no migradas) el respaldo son 56 px, el alto real de la franja. */
  top: var(--app-header-h, 56px);
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
}
.nb-panel.u-hidden { display: none; }

@media (min-width: 769px) {
  .nb-panel {
    top: 0;
    left: auto;
    width: min(420px, 100vw);
    background: var(--color-surface);
    box-shadow: -8px 0 32px rgb(0 0 0 / 20%);
  }
}

.nb-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 0.75rem 0.5rem;
}
.nb-title {
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--color-text-strong);
}
.nb-sub {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-text-muted);
}
.nb-x { margin-left: auto; }

/* Píldoras a la izquierda y los tres puntos ANCLADOS a la derecha, a su mismo nivel. Colgados
   junto al título "se ven raros" (dueño, 2026-08-05). Las píldoras se arrastran de lado si no
   caben; el ⋮ nunca se va con ellas. */
.nb-tools {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0 0.75rem 0.5rem;
  border-bottom: 1px solid var(--color-border);
}
.nb-tools .ui-segmented {
  flex: 1 1 auto;
  min-width: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}
.nb-tools .ui-segmented::-webkit-scrollbar { display: none; }
.nb-kebab { flex: 0 0 auto; }

/* Segunda fila, solo en la lista de reportes: Todos · Día · Semana. Filtra la lista; no navega. */
.nb-subtools {
  flex: 0 0 auto;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--color-border);
}
.nb-subtools.u-hidden { display: none; }

/* Lo ÚNICO que se desplaza. */
.nb-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 0.75rem 1rem;
}

/* Encabezado de grupo — mismo molde que .app-group, pegajoso dentro del cuerpo. */
.nb-group {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0.5rem -0.75rem 0.5rem;
  padding: 0.4375rem 0.75rem;
  background: var(--color-surface-muted);
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}
.nb-group__n {
  margin-left: auto;
  font-weight: 800;
  color: var(--color-text);
}

/* La tarjeta. El filo izquierdo dice DE QUÉ SE TRATA, con la gramática de color que el panel ya
   usa: rojo = exige a una persona, azul = movimiento, verde = entró plata, violeta = lectura. */
.nb-card {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-primary);
  border-radius: 10px;
  padding: 0.6rem 0.7rem;
  margin-bottom: 0.5rem;
  cursor: pointer;
}
.nb-card--urgent { border-left-color: var(--color-danger); }
.nb-card--report { border-left-color: var(--dash-violet); }
.nb-card--money  { border-left-color: var(--color-success); }
/* Leídas: se ATENÚAN, nunca se recolorean. Un gris inventado dejaría de significar "leído" en el
   resto del producto (mismo criterio que ui-card--off). */
.nb-card--read { opacity: 0.62; }

.nb-card__top {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}
.nb-card__title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--color-text-strong);
}
.nb-card__when {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.72rem;
  color: var(--color-text-muted);
  white-space: nowrap;
}
.nb-card__dot {
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--color-primary);
}
.nb-card__body {
  margin-top: 0.2rem;
  font-size: 0.83rem;
  color: var(--color-text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Abierta: el texto completo. Es el primer toque que pidió el dueño; el segundo paso es un botón
   explícito, no otro toque a ciegas sobre la tarjeta.
   `pre-line` porque el texto largo de un escalado viene en PÁRRAFOS —lo que pasó, el resumen, el
   último mensaje— y sin él los tres se pegaban en un solo bloque corrido donde no se distingue
   dónde acaba uno. Solo abierta: en reposo son dos líneas recortadas y ahí los saltos solo
   gastarían el recorte. */
.nb-card--open .nb-card__body {
  display: block;
  overflow: visible;
  white-space: pre-line;
}
.nb-card__n { flex: 0 0 auto; margin-left: 0.25rem; }
.nb-card__cta { margin-top: 0.55rem; }

/* Vacío. En una lista de pendientes, estar vacío es una BUENA noticia y se dice así. */
.nb-empty {
  padding: 3rem 1rem;
  text-align: center;
  color: var(--color-text-muted);
}
.nb-empty__icon { font-size: 2rem; }
.nb-empty__title {
  margin-top: 0.5rem;
  font-weight: 700;
  color: var(--color-text);
}
.nb-empty__sub { font-size: 0.85rem; }
.nb-empty__cta { margin-top: 0.9rem; }

.nb-menu {
  position: absolute;
  top: 5.4rem;
  right: 0.75rem;
  z-index: 3;
  min-width: 12.5rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
.nb-menu.u-hidden { display: none; }
.nb-menu__opt {
  display: block;
  width: 100%;
  padding: 0.65rem 0.85rem;
  background: none;
  border: 0;
  text-align: left;
  font-size: 0.875rem;
  color: var(--color-text);
  cursor: pointer;
}
.nb-menu__opt + .nb-menu__opt { border-top: 1px solid var(--color-border); }
.nb-menu__opt--danger { color: var(--color-danger); font-weight: 600; }
.nb-menu__opt[disabled] { opacity: 0.45; cursor: default; }

/* Confirmación EN EL SITIO, no el diálogo del navegador (regla de la guía de la app). Dice
   cuántos se van, que también se van los no leídos y cuánto dura la marcha atrás: sin ese último
   dato "borrar" da miedo y nadie lo usa. */
.nb-confirm {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
  background: rgb(0 0 0 / 45%);
}
.nb-confirm.u-hidden { display: none; }
.nb-confirm__box {
  width: 100%;
  max-width: 20rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  padding: 1rem;
  box-shadow: var(--shadow-md);
}
.nb-confirm__title {
  font-size: 1rem;
  font-weight: 800;
  color: var(--color-text-strong);
}
.nb-confirm__text {
  margin: 0.4rem 0 0.9rem;
  font-size: 0.85rem;
  color: var(--color-text-secondary);
}
.nb-confirm__actions {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* Aviso de "se borraron, se pueden recuperar". */
.nb-undo {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0.75rem 0;
  padding: 0.6rem 0.7rem;
  background: var(--color-accent-bg);
  border: 1px solid var(--color-accent-border);
  border-radius: 10px;
  font-size: 0.82rem;
  color: var(--color-accent-text);
}
.nb-undo__btn { flex: 0 0 auto; margin-left: auto; }

/* "Ver los anteriores" al pie de la lista de reportes. */
.nb-more { margin-top: 0.75rem; }

/* ── LOS REPORTES DENTRO DEL PANEL (app-avisos-campana F4) ───────────────────────────────────
   El otro lado de la campana: la lista de resúmenes guardados y la pantalla de uno.
   Se ve feo en WhatsApp por el canal —allá solo cabe texto plano con barritas de guiones—, no
   por el contenido: los datos ya venían por bloques, así que esto es presentación.
   Ni un color propio: todo sale de los tokens, como el resto del panel. */
.rp-head { padding: 0.75rem 0 0.5rem; }
.rp-title {
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--color-text-strong);
}
.rp-when {
  font-size: 0.78rem;
  color: var(--color-text-muted);
}

/* La cifra grande: lo primero que se mira, y lo único que se recuerda del día. */
.rp-hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  margin: 0.75rem 0;
}
/* Con una sola cifra, la rejilla de dos dejaría media pantalla vacía: parecería un dato perdido. */
.rp-hero--one { grid-template-columns: 1fr; }
.rp-hero__box {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 0.7rem;
}
.rp-hero__lab {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.rp-hero__n {
  /* La cifra se ENCOGE en pantallas estrechas en vez de partirse: a 320 px, "$1.240.000" partido
     en dos líneas se lee como un número cortado a la mitad. */
  font-size: clamp(1.1rem, 5.6vw, 1.5rem);
  font-weight: 800;
  line-height: 1.15;
  color: var(--color-text-strong);
  white-space: nowrap;
}
.rp-hero__n--good { color: var(--color-success); }
.rp-hero__n--bad  { color: var(--color-danger); }
.rp-hero__delta {
  font-size: 0.72rem;
  color: var(--color-text-muted);
}

/* Un bloque del reporte. Mismo molde de tarjeta que todo lo demás del panel. */
.rp-block {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 0.7rem;
  margin-bottom: 0.5rem;
}
.rp-block__h {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 0.45rem;
  font-size: 0.88rem;
  font-weight: 800;
  color: var(--color-text-strong);
}
.rp-block__n {
  margin-left: auto;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--color-text-muted);
}
.rp-row {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.3rem 0;
  border-top: 1px solid var(--color-border);
  font-size: 0.85rem;
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}
.rp-row:first-of-type { border-top: 0; }
.rp-row__v {
  margin-left: auto;
  font-weight: 700;
  white-space: nowrap;
  color: var(--color-text);
}
/* La prosa del asistente. Se lee como texto, no como una fila de dato. */
.rp-text {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--color-text-secondary);
  white-space: pre-wrap;
}
/* La barra de proporción: la misma idea que las barritas del WhatsApp, pero dibujada. */
.rp-bar {
  height: 0.35rem;
  margin-top: 0.35rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface-muted);
  overflow: hidden;
}
.rp-bar__fill {
  height: 100%;
  background: var(--color-primary);
}
