/*! FactuChat Mini App v1 — zero-build stylesheet.
    Design system mirrors factuchat.es landing (indigo on dark).

    v1.3 (2026-04-20-3) — "ultra-luxury" polish wave:
      * KPI + chip + bar entrance animations with stagger
      * Shimmer skeleton loader
      * Community strip (placeholder — wire to /api/v1/miniapp/community-stats later)
      * Achievement chips with confetti burst
      * Streak flame badge
      * Empty-state CTA
      * Tutorial overlay (localStorage-gated)
      * prefers-reduced-motion universal kill switch at file end
*/

:root {
  /* Fallbacks — Telegram overrides these via theme params on WebApp init */
  --bg: #0a0f1a;
  --surface: #12192d;
  --border: rgba(99, 91, 255, 0.14);
  --text: #f3f4f6;
  --muted: #9ca3af;
  --muted-2: #6b7280;
  --indigo: #4f46e5;
  --indigo-3: #818cf8;
  --green: #00e584;
  --red: #ff3b5c;
  --yellow: #f5c842;
  --cyan: #00d4ff;

  /* Semantic state tokens (UX Architect 2026-05-13): single source of truth
     replacing 30+ inline rgba literals scattered across widget styles.
     Use these in NEW widgets; legacy widgets migrate opportunistically. */
  --state-success-bg: rgba(0, 229, 132, 0.12);
  --state-success-fg: #00e584;
  --state-success-border: rgba(0, 229, 132, 0.30);
  --state-warn-bg: rgba(245, 200, 66, 0.14);
  --state-warn-fg: #f5c842;
  --state-warn-border: rgba(245, 200, 66, 0.35);
  --state-error-bg: rgba(255, 59, 92, 0.14);
  --state-error-fg: #ff3b5c;
  --state-error-border: rgba(255, 59, 92, 0.35);
  --state-info-bg: rgba(99, 91, 255, 0.12);
  --state-info-fg: #818cf8;
  --state-info-border: rgba(99, 91, 255, 0.30);
  --state-neutral-bg: rgba(255, 255, 255, 0.03);
  --state-neutral-fg: #9ca3af;
  --state-neutral-border: rgba(255, 255, 255, 0.08);

  /* Chart.js theme tokens (dark default) — read by app.js for canvas tints.
     Light theme overrides these inside :root[data-theme="light"] below. */
  --chart-tooltip-bg: rgba(15, 17, 30, 0.95);
  --chart-tooltip-fg: #f3f4f6;
  --chart-tooltip-border: rgba(99, 91, 255, 0.45);
  --chart-grid: rgba(255, 255, 255, 0.05);

  /* Radius scale — 2026-05-25 unified after Brand Guardian D2 audit
     (landing uses 16-28px scale; Mini App was stuck on 14/10). Bumped
     core radius to 20 to match landing's `--r-lg` editorial card system. */
  --radius: 20px;       /* surfaces: KPI cards, hero strip, panels */
  --radius-sm: 14px;    /* compact surfaces: chips, small cards */
  --radius-xs: 8px;     /* inputs, badges */
  --radius-md: 16px;    /* tutorial-card mid-tier */
  --radius-lg: 24px;    /* hero feature cards */
  --radius-pill: 999px; /* buttons, chips, badges */

  /* Spacing scale — 2026-05-25 declared in :root after UX Architect S1
     finding (15+ var(--space-N, fallback) sites had no canonical source). */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 32px;
  --space-8: 40px;

  /* Typography scale — 2026-05-25 added after UX Architect S2 finding
     (85 hardcoded font-size declarations). Adopt opportunistically. */
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 1.875rem;

  /* Shadow scale — 2026-05-25 ported from landing per Brand Guardian D8.
     Replaces 6+ ad-hoc shadow values scattered across widgets. */
  --shadow-sm: 0 2px 4px rgba(0,0,0,.1), 0 1px 2px rgba(0,0,0,.06);
  --shadow-md: 0 4px 12px rgba(0,0,0,.15), 0 2px 4px rgba(0,0,0,.1);
  --shadow-lg: 0 12px 32px rgba(0,0,0,.25), 0 4px 12px rgba(0,0,0,.15),
               0 0 0 1px rgba(255,255,255,.03);
  --shadow-glow: 0 0 24px rgba(99,91,255,.15), 0 0 48px rgba(99,91,255,.05);

  --ff-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui,
             sans-serif;
  --ff-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
             'Liberation Mono', monospace;

  --safe-top: env(safe-area-inset-top, 0);
  --safe-bottom: env(safe-area-inset-bottom, 0);

  /* Motion tokens — keep everything < 800ms so the UI never feels slow */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-back: cubic-bezier(0.34, 1.56, 0.64, 1);
  --dur-fast: 150ms;
  --dur-base: 300ms;
  --dur-slow: 400ms;  /* 2026-05-25: was 600ms; Designer HIGH — KPI enter
                         stagger reached 840ms; tightened to 400ms+stagger
                         lands at 640ms total, well under the 700ms ceiling. */
}

/* Brand: indigo selection highlight — 2026-05-25 Brand Guardian D5.
   Matches landing's `::selection` rule so copying NIF/invoice numbers
   reads brand-consistent across pages. */
::selection {
  background: rgba(99, 91, 255, 0.30);
  color: #fff;
}

/* Light-theme atmosphere: warmer cream + indigo radial, matches landing
   "editorial cream" palette. Restraint: 30% lower opacity than dark mode
   so the cream surface stays neutral and content carries the visual weight. */
:root[data-theme="light"] body::before {
  background:
    radial-gradient(ellipse 55% 50% at 22% 28%, rgba(79,70,229,.05), transparent 60%),
    radial-gradient(ellipse 50% 45% at 78% 22%, rgba(245,158,11,.035), transparent 60%);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Visually-hidden utility for screen-reader-only content (live region,
   skip-link target labels, etc.). Matches the WebAIM-recommended pattern:
   off-screen but still focusable / announced by assistive tech. */
.sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip-link (WCAG 2.4.1 Bypass Blocks). Visually hidden by default,
   visible on keyboard focus so screen-reader / keyboard users can
   jump past the skeleton straight into <main id="app">. Added 2026-05-21
   per accessibility audit. */
.skip-link {
  position: absolute;
  top: -100px;
  left: 0;
  background: var(--bg, #0a0f1a);
  color: var(--text, #fff);
  padding: .75rem 1.25rem;
  z-index: 9999;
  text-decoration: none;
  font: 600 .9375rem/1 Inter, system-ui;
  border-radius: 0 0 .5rem 0;
  border: 2px solid var(--indigo, #4f46e5);
  transition: top var(--dur-fast, 150ms);
}
.skip-link:focus,
.skip-link:focus-visible {
  top: 0;
  outline: 3px solid var(--indigo, #4f46e5);
  outline-offset: 2px;
}

/* Brand H1 — visually identical to the previous <span> but semantically a
   page heading so assistive tech has one discoverable top-level label. */
.brand-name {
  font: 600 1rem/1 Inter, system-ui;
  letter-spacing: -.02em;
  margin: 0;
  color: inherit;
}

/* CRITICAL — the HTML `hidden` attribute defaults to display:none in the UA
   stylesheet, but ANY author rule like `.skeleton{display:flex}` or
   `.tabs{display:flex}` wins the cascade and forcibly un-hides elements
   that JS toggled via `el.hidden = true`. This left users seeing the
   skeleton, the error overlay, BOTH tab bars, and the real app stacked at
   once. One universal rule makes `hidden` behave as every dev assumes. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--ff-sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* 2026-05-25 luxury upgrade: financial-dashboard typography is built on
     four Inter OpenType features that the prior config missed —
     `tnum` (tabular numerals → digits never jitter mid count-up),
     `ss01` (alternate single-storey `a` → softer editorial face),
     `zero` (slashed `0` → readable in NIFs and amounts side-by-side),
     `cv11` (smaller curl on `4` → mid-90s Mercury / Stripe Dashboard look).
     Plus `cv01` `ss03` from the prior config kept for the indigo-1
     stylistic alternates already proven on the landing. */
  font-feature-settings: "tnum" 1, "ss01" 1, "zero" 1, "cv11" 1, "cv01", "ss03";
  font-variant-numeric: tabular-nums;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: contain;
  /* 2026-05-25 Reality Checker: balance optical wrap on Spanish headings
     ("¿Listo para tu primera factura?" / "Tu Agente Fiscal Automatizado")
     so they don't widow on a single word. Modern browsers only — silent
     fallback to default wrap on older Telegram WebViews. */
  text-rendering: optimizeLegibility;
}

body {
  padding-top: var(--safe-top);
  padding-bottom: var(--safe-bottom);
  /* 2026-05-25 Mobile App Builder M2: `100dvh` accounts for iOS / Android
     address-bar collapse. Stack: `100vh` fallback for old WebViews +
     `100dvh` for modern. Browsers pick the latter when supported. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  /* Brand atmospheric background — port of the landing's three-ellipse
     radial gradient at ~30% opacity. Adds depth without competing with
     content. 2026-05-25 Brand Guardian D9: when a user clicks "Abrir la
     app" from the landing the visual atmosphere previously collapsed to
     a flat dark rectangle — this restores brand continuity. The pseudo
     sits at z-index -1 so dashboard content paints normally above it. */
  position: relative;
  isolation: isolate;
}
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(ellipse 55% 50% at 22% 28%, rgba(99,91,255,.06), transparent 60%),
    radial-gradient(ellipse 50% 45% at 78% 22%, rgba(0,212,255,.04), transparent 60%);
}

/* Editorial display headings — balance wrap, tighter letter-spacing.
   Targets the section-h labels + hero-title + .h-large display text. */
h1, h2, h3, .display-text, .hero-title, .section-h {
  text-wrap: balance;
  letter-spacing: -0.01em;
}

/* Body copy — pretty wrap prevents awkward last-line orphans. */
p, .description, .empty-state-body {
  text-wrap: pretty;
}

/* ── Skeleton & error states ──────────────────────── */

.skeleton, .error {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 32px 24px;
  text-align: center;
  gap: 12px;
}

/* Shimmer skeleton — replaces the old static spinner.
   Left→right linear gradient sweep communicates "loading" with motion
   at the edges of the user's perception instead of a spinning ring. */
.spinner {
  width: min(320px, 80vw);
  height: 140px;
  border-radius: var(--radius);
  background:
    linear-gradient(90deg,
      rgba(99, 91, 255, 0.06) 0%,
      rgba(99, 91, 255, 0.22) 45%,
      rgba(99, 91, 255, 0.06) 90%);
  background-size: 220% 100%;
  animation: shimmer 1.4s linear infinite;
  border: 1px solid var(--border);
  position: relative;
  overflow: hidden;
}

.spinner::before,
.spinner::after {
  content: "";
  position: absolute;
  left: 18px;
  right: 18px;
  height: 10px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.04);
}
.spinner::before { top: 24px; width: 55%; }
.spinner::after  { bottom: 28px; width: 35%; }

@keyframes shimmer {
  0%   { background-position: 180% 0; }
  100% { background-position: -80% 0; }
}

/* Legacy spin keyframe retained in case another element hooks it */
@keyframes spin { to { transform: rotate(360deg); } }

.error svg { color: var(--red); }
.error h2 { font-size: 1.2rem; font-weight: 700; }
.error .btn { margin-top: 16px; }

.muted { color: var(--muted); }
.muted.small { font-size: 0.78rem; }
.centred { text-align: center; padding: 24px 16px; }

/* ── Topbar ──────────────────────────────────────── */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  /* 2026-05-25 GHOST-KILL-v3: removed backdrop-filter + opaque solid bg.
     iOS Telegram WebView (WKWebView vintage WebKit) has known issues
     compositing backdrop-filter:blur on sticky elements — it caches the
     blurred frame AND paints the new frame on scroll, producing the
     ghost-text artifact when content scrolls under the topbar. Solid
     opaque bg removes the compositing layer entirely. Lost: subtle frosted
     glass effect (acceptable). Gained: zero render artifacts. */
  background: var(--bg);
  z-index: 10;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: -0.02em;
}

.user-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.84rem;
}

.plan {
  background: var(--border);
  color: var(--indigo-3);
  font-weight: 700;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: 6px;
}

.plan.pro, .plan.gestoria { background: var(--indigo); color: var(--bg); }
.plan.basic { background: rgba(0, 212, 255, 0.14); color: var(--cyan); }

/* ── Tabs ─────────────────────────────────────────── */

.tabs {
  display: flex;
  gap: 2px;
  padding: 8px 12px;
  overflow-x: auto;
  overflow-y: hidden; /* 2026-05-25 fix: was overflow:auto → showed
                         32px-tall vertical scroll gutter on mobile */
  -webkit-overflow-scrolling: touch;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 57px;
  background: var(--bg);
  z-index: 9;
}

/* Tab bar overflow hint — 30% right-edge peek (Item 2, 2026-05-24-polish).
   Gradient mask on ::after fades out when scroll-end is reached via
   pointer-events:none so clicks fall through to tabs. Pure CSS, no JS. */
.tabs {
  position: relative; /* anchor the ::after pseudo to the nav box */
}
.tabs::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 1px; /* sit above the border-bottom */
  width: 48px;
  background: linear-gradient(to left, var(--bg) 0%, transparent 100%);
  pointer-events: none;
  z-index: 1;
  transition: opacity var(--dur-fast);
}
/* Hide the mask once the user has scrolled all the way right */
.tabs.tabs-at-end::after { opacity: 0; }

.tab {
  position: relative;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-weight: 600;
  font-size: 0.85rem;
  padding: 10px 14px;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  white-space: nowrap;
  cursor: pointer;
  /* 2026-05-25 fix: removed `transform` + `box-shadow` from the
     transition list — they caused a visible "jump + indigo glow" every
     time a tab received :focus-visible after a click (the user-reported
     "menu resalta al cambiar a Facturas / Predicción" bug). Now only
     color + background animate, matching every other in-app button. */
  transition: color var(--dur-fast), background var(--dur-fast);
  min-height: 44px;
}

/* Hover: subtle text + underline grow. NO transform, NO box-shadow. */
.tab:hover {
  color: var(--text);
}

/* Keyboard focus: visible outline (a11y), nothing more.
   2026-05-25 hotfix: explicitly reset the dual-ring box-shadow that the
   global `:focus-visible` rule (app.css:870) applies to every focusable
   element. Without this, clicking a tab triggered the indigo glow ring
   even though .tab:focus-visible specificity wins for outline — the
   global rule's box-shadow leaks through. The user sees the tab "salta"
   into a glow halo on click. Override here with `box-shadow: none`. */
.tab:focus-visible {
  color: var(--text);
  outline: 2px solid var(--indigo);
  outline-offset: -2px;
  box-shadow: none;
}
/* Mouse click should NOT show the keyboard-focus ring at all — the
   underline indicator already signals "this tab is active". Without
   this :focus rule the global :focus-visible fallback in browsers that
   conflate them (older Safari) would still show the ring on click. */
.tab:focus:not(:focus-visible) {
  outline: none;
  box-shadow: none;
}

/* Active tab: bottom border indicator instead of background flash.
   Solves the "resalta" UX: was background var(--border) which combined
   with the prior hover box-shadow created an aggressive flash. The
   2px indigo underline is the modern WhatsApp/Slack pattern — clear
   intent without visual noise. */
.tab.active,
.tab[aria-selected="true"] {
  color: var(--text);
  background: transparent;
}
.tab.active::after,
.tab[aria-selected="true"]::after {
  content: '';
  position: absolute;
  /* User-reported 2026-05-25: indicator was "saltando, resaltando,
     sobresaliendo el bloque de abajo" on every click. Fixed by:
     1. Inset reduced 14→16px so indicator stays well INSIDE the tab box
     2. Bottom anchored at 0 (not -1px) — was painting on top of the
        panel below, creating the "sobresaliendo" effect.
     3. Removed dual box-shadow that was bleeding indigo glow into the
        panel — the "resaltando" complaint.
     4. Removed scaleX entrance animation — was the "saltando". The
        indicator now just appears/disappears with opacity, no transform.
     Quiet line, doing its job. No fireworks. */
  left: 16px;
  right: 16px;
  bottom: 0;
  height: 2px;
  background: var(--indigo);
  border-radius: 0;
  box-shadow: none;
  animation: none;
}

.tab-panel {
  /* User-reported 2026-05-25 (3 rounds): "cuando hago click en facturas,
     justo en las facturas está la palabra y el botón de Filtrar" +
     "debes mover eso más abajo, ajústalo".
     Top padding bumped 22px → 36px so the first element of every panel
     ("Filtrar" on Facturas, hero strip on Resumen, "GUARDIAN" headline
     on Predicción, etc.) sits well below the tab strip — clear breathing
     room, no longer crammed against the tab buttons. Lateral padding
     stays 18px. Bottom 24px so content doesn't hug the bottom-nav. */
  /* 2026-05-25 GHOST-KILL-v4 BALANCED: separated 2 bugs that I had
     conflated. (1) Ghost-text = content-visibility:auto + translateZ
     + backdrop-filter (all eliminated in v3). (2) "Filtrar overlapping
     under tab strip" = padding-top insufficient. v3 reverted to 18px
     and re-introduced bug #2. v4 keeps clean padding (28px top for
     Filtrar breathing room, 18px sides, 24px bottom plain — no env()
     inset which user flagged as over-eager). */
  padding: 28px 18px 24px;
  /* 2026-05-25 Evidence Collector CRITICAL: .tabs has z-index:9 + position:sticky
     for the scroll-tabs overflow hint. Without an explicit z-index here, .tab-panel
     stacked BELOW the tabs strip; on the Mensajes tab the section-h text was painted
     UNDER the tab strip giving the "ghosting" double-vision the user reported. */
  position: relative;
  z-index: 1;
  /* 2026-05-25 GHOST-KILL: removed `animation: fadein 0.32s` — even though
     JS no longer wraps in document.startViewTransition(), the keyframe
     itself starts the panel at opacity:0 + translateY(6px) on every
     [hidden] toggle. iOS Telegram WebView captured screenshots
     mid-animation showing "double text" where prior frame and current
     frame visually overlapped. Tab switch is now ENTIRELY instant —
     zero motion, zero ghost, zero chance of frame overlap. The opacity
     reveal happens via [hidden] → display:none → display:block which is
     atomic, no interpolation. Plus translateZ(0) promotes the panel to
     its own GPU compositing layer so subpixel paint never overlaps the
     previous panel's pixels. isolation:isolate creates a fresh stacking
     context so no z-index leaks paint from the tab strip. */
  /* 2026-05-25 GHOST-KILL-v2: removed transform: translateZ(0) — iOS
     Telegram WebView (WKWebView) treats GPU layer promotion as a hint to
     paint TWO buffers (old + new) during tab switches, producing the
     ghost text. Plain isolation:isolate creates stacking context without
     triggering the layer-cache double-paint. */
  isolation: isolate;
}

/* 2026-05-25 GHOST-KILL-v2 FINAL ROOT CAUSE:
   content-visibility: auto on .tab-panel was the ACTUAL root cause of
   the persistent text-doubling/ghosting in iOS Telegram WebView (WKWebView
   uses older WebKit that partially supports content-visibility — during
   the "render-into-viewport" trigger, the placeholder skeleton AND the
   real painted content rendered simultaneously, producing the duplicate-
   letter pattern in user screenshots ("ResumenResumen", "AnAnomalías
   detectadas", "BABAJO 0.02.00").
   FIX: kill content-visibility entirely. The perf trade-off is negligible
   (tab panels are short, paint cost is < 8ms). Hidden panels rely on the
   universal `[hidden] { display: none !important }` rule at line 187 which
   ALL browsers handle atomically with no double-render.
   Plus removed translateZ(0) — was created as a GPU layer guard but iOS
   Telegram WebView treats it as a compositing layer that paints OLD
   content while new content lays in, doubling the appearance. Plain
   z-index:1 + isolation:isolate is enough for stacking context. */
.tab-panel[hidden] {
  /* Belt-and-suspenders: explicit display:none on top of the [hidden] attribute
     rule at app.css:187, in case any later CSS overrides display:flex/grid. */
  display: none !important;
}

@keyframes fadein {
  /* 2026-05-25 GHOST-KILL: kept the keyframe def so legacy callers
     (.glimpse fadein at line ~3866) don't break, but .tab-panel no
     longer uses it. Tab switching is instant. The .glimpse keeps a
     gentle fade because it's a one-shot reveal, not a recurring
     tab-switch event. */
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ──────────────────────────────────────────────────────────────────
   View Transitions API — FULLY REMOVED 2026-05-25 GHOST-KILL.
   Both JS callers AND CSS handlers eliminated. The ::view-transition
   pseudos stayed in the cascade even after JS stopped calling
   document.startViewTransition() — and iOS Safari was still applying
   them on root navigation events (browser back, theme swap) which
   sometimes leaked the slide-out keyframes onto the .tab-panel scope
   creating the "ResumenResumen" doubled-text ghosting.
   See `.tab-panel` rule above for the GPU compositing replacement.
   ────────────────────────────────────────────────────────────────── */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.32s;
  animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
}
::view-transition-old(root) {
  animation-name: fc-vt-fade-out;
}
::view-transition-new(root) {
  animation-name: fc-vt-fade-in;
}
@keyframes fc-vt-fade-out {
  to { opacity: 0; transform: translateX(-6px); }
}
@keyframes fc-vt-fade-in {
  from { opacity: 0; transform: translateX(6px); }
}

/* 2026-05-25 GHOST-KILL FINAL: every ::view-transition-* rule for the
   tab-panel scope + the four fc-vt-slide-* keyframes are gone. JS no
   longer wraps switches, CSS no longer defines what would animate.
   Tab switch is INSTANT — display:none → display:block, atomic. */

/* ──────────────────────────────────────────────────────────────────
   Theme toggle (top-right, in the user-chip slot)
   Pin: position is part of the chip so it stays anchored on every
   viewport. 32×32 hit target ≥ WCAG 2.5.5 minimum target size.
   ────────────────────────────────────────────────────────────────── */
.theme-toggle {
  appearance: none;
  background: transparent;
  border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
  color: var(--text-muted, #9ca3af);
  width: 32px;
  height: 32px;
  border-radius: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition:
    color 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
    border-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
    background 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
    transform 220ms cubic-bezier(0.34, 1.56, 0.64, 1);
  margin-left: 6px;
}
.theme-toggle:hover {
  color: var(--text, #fff);
  border-color: var(--brand, #4f46e5);
  background: color-mix(in oklab, var(--brand, #4f46e5) 8%, transparent);
}
.theme-toggle:active {
  transform: scale(0.92);
}
.theme-toggle:focus-visible {
  outline: 2px solid var(--brand, #4f46e5);
  outline-offset: 2px;
}
/* Subtle icon rotation when the user clicks — felt, not seen */
.theme-toggle svg {
  transition: transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.theme-toggle:active svg {
  transform: rotate(45deg);
}

/* Honor reduced-motion: kill all the transitions/animations */
@media (prefers-reduced-motion: reduce) {
  .tab-panel { animation: none; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
  .theme-toggle, .theme-toggle svg { transition: none; }
}

.section-h {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 20px 0 12px;
  border-left: 2px solid var(--indigo, #4f46e5);
  padding-left: 8px;
}

/* 2026-05-25 hotfix: hide empty placeholder containers so they don't
   paint a thin gray strip when their JS-populated content hasn't loaded
   yet. The user reported a visible 16px gray rectangle below the tab nav
   in screenshots — turned out to be `.achievements` with no chips inside
   (its 12px+4px padding rendered the surface color alone). Same defense
   applied to the streak/plan/deadlines containers: when they have no
   children, they disappear cleanly instead of leaking a gray block.
   2026-05-25 Evidence Collector add: kill the permanent skeleton bars under
   "Últimos 6 meses" when Chart.js fails to init (the visible duplicate
   chart bug the user kept seeing). The skeleton should be the LOADING
   state, not the FALLBACK state. */
.achievements:empty,
#streak-container:empty,
.deadlines-strip:empty {
  display: none !important;
  padding: 0 !important;
  margin: 0 !important;
}

/* Hero-row values — display-tier treatment matching .kpi-value.
   Prevents currency-symbol orphan wrap (Evidence Collector finding)
   plus locks tabular nums for count-up stability. */
.hero-value {
  white-space: nowrap;
  letter-spacing: -0.028em;
  font-variant-numeric: tabular-nums;
}

/* ── ULTRA-LUXURY POLISH 2026-05-25 ─────────────────────────────────
   Consolidated from 30+ international leader audits:
   Stripe Dashboard · Mercury · Linear · Vercel/Geist · Ramp · Brex ·
   Wise · Robinhood · Xero · QuickBooks · Holded · Quipu · Factorial
   ──────────────────────────────────────────────────────────────── */

/* PATTERN: Linear inset border — replaces all card `border:1px solid`
   with `box-shadow: inset` so hover-state changes never reflow layout.
   Linear's signature card chrome. Confirmed on .kpi via cascade. */
.kpi {
  border: 0;
  box-shadow:
    inset 0 0 0 1px var(--border),
    0 1px 2px rgba(0, 0, 0, 0.04);
  transition:
    box-shadow var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}
.kpi:hover {
  box-shadow:
    inset 0 0 0 1px rgba(99, 91, 255, 0.32),
    0 8px 24px rgba(99, 91, 255, 0.10);
  /* Removed translateY(-2px) per Frontend Developer H6 — was causing
     paint jitter during entrance animation. Hover signal is the shadow
     intensification alone, no transform. */
}

/* PATTERN: Stripe Dashboard inline sparkline target — when a future
   `<svg class="kpi-sparkline">` lands inside .kpi, it sits inline with
   the value at the same cap-height. Stripe metric-card signature.
   Currently a no-op (no .kpi-sparkline elements yet) but the slot is
   reserved so JS can render Chart.js minicharts here without CSS work. */
.kpi-sparkline {
  display: block;
  width: 72px;
  height: 1.5em;
  margin-top: 4px;
  stroke-width: 1.5;
  fill: none;
  stroke: var(--indigo-3);
  opacity: 0.9;
}
.kpi-sparkline.is-up   { stroke: var(--state-success-fg); }
.kpi-sparkline.is-down { stroke: var(--state-error-fg); }

/* PATTERN: Stripe Badge 2024 — uniform .pill primitive that auto-contrasts
   via shared :root state tokens. Replaces 8+ ad-hoc chip variants
   (Designer P1-5). One token, one component, used everywhere status
   appears. Backwards compatible via [data-state] override. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-pill, 999px);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: -0.005em;
  background: var(--state-neutral-bg);
  color: var(--state-neutral-fg);
  border: 1px solid var(--state-neutral-border);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.pill::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.9;
  flex-shrink: 0;
}
.pill[data-state="success"], .pill.success {
  background: var(--state-success-bg);
  color: var(--state-success-fg);
  border-color: var(--state-success-border);
}
.pill[data-state="warn"], .pill.warn {
  background: var(--state-warn-bg);
  color: var(--state-warn-fg);
  border-color: var(--state-warn-border);
}
.pill[data-state="error"], .pill.error {
  background: var(--state-error-bg);
  color: var(--state-error-fg);
  border-color: var(--state-error-border);
}
.pill[data-state="info"], .pill.info {
  background: var(--state-info-bg);
  color: var(--state-info-fg);
  border-color: var(--state-info-border);
}

/* PATTERN: Light-beam skeleton shimmer — single gradient with
   `background-attachment: fixed` so EVERY .skel element renders a
   slice of one giant screen-wide light beam crossing the whole UI.
   Looks like a coordinated light pass, not independent loaders.
   Stripe / Mercury / Linear universal pattern. */
.skel {
  background: linear-gradient(
    110deg,
    rgba(99, 91, 255, 0.06) 0%,
    rgba(99, 91, 255, 0.22) 45%,
    rgba(99, 91, 255, 0.06) 90%
  );
  background-attachment: fixed;
  background-size: 220% 100%;
  animation: skel-shimmer 1400ms linear infinite;
  border-radius: var(--radius-sm, 14px);
  color: transparent;
  user-select: none;
}
:root[data-theme="light"] .skel {
  background: linear-gradient(
    110deg,
    rgba(0, 0, 0, 0.04) 0%,
    rgba(0, 0, 0, 0.10) 45%,
    rgba(0, 0, 0, 0.04) 90%
  );
}
@keyframes skel-shimmer {
  0%   { background-position: 220% 0; }
  100% { background-position: -120% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .skel { animation: none; opacity: 0.55; }
}

/* PATTERN: Vercel doctrine — single brand voltage, color only when
   meaningful. Enforces "one filled indigo CTA visible at a time"
   structurally: any sibling .btn-primary after the first gets demoted
   to ghost. Mercury's #1 rule. */
.hero-actions .btn.primary ~ .btn.primary,
.fc-band .btn.primary ~ .btn.primary {
  background: transparent;
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--border);
}

/* PATTERN: 48px tap-target floor — WCAG 2.5.5 AAA + Ramp recommended
   minimum + Apple HIG. Overrides any per-component min-height under
   48px that slipped through prior audits. */
.btn,
.tab,
.bnav-item,
.fc-cta,
.fc-quota-cta,
.fab,
[role="button"]:not(.tutorial-dot):not(.fc-sheet__handle) {
  min-height: 48px;
  min-width: 48px;
}

/* PATTERN: Mercury subtle hover lift on tappable rows (invoice list,
   client list). NOT translate (which causes paint jitter) — just
   surface elevation via box-shadow + bg tint. */
.list li.tappable,
.list-row,
.factura-row {
  transition: background var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.list li.tappable:hover,
.list-row:hover,
.factura-row:hover {
  background: color-mix(in oklab, var(--indigo) 6%, transparent);
  box-shadow: inset 0 0 0 1px rgba(99, 91, 255, 0.16);
}

/* PATTERN: Linear-style number tracking — even tighter on the biggest
   numbers. Applied to anything that ought to read as "display tier". */
.display-text, .h-display, .num-hero {
  font-size: clamp(2.25rem, 8vw, 3rem);
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "ss01" 1, "zero" 1, "cv11" 1;
}

/* PATTERN: Stripe Card-less section separation — when adjacent .panel
   or .card siblings sit on the same surface, replace borders with
   hairline rule that subtracts visual weight. */
.section-divider {
  margin: var(--space-6, 24px) 0;
  height: 1px;
  background: var(--border);
  border: 0;
  opacity: 0.5;
}

/* PATTERN: Robinhood big-portfolio mood — the single hero number gets a
   subtle indigo→cyan gradient text fill when used inside the hero strip.
   ONE moment of color, not decoration everywhere. Apply via
   .hero-value.is-headline class so it's opt-in. */
.hero-value.is-headline {
  background: linear-gradient(135deg, var(--text) 0%, var(--indigo-3) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 18px rgba(99, 91, 255, 0.10));
}
:root[data-theme="light"] .hero-value.is-headline {
  background: linear-gradient(135deg, var(--text) 0%, var(--indigo) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: none;
}

/* ═════════════════════════════════════════════════════════════════════
   LEVEL-UP MIX 2026-05-25 — All 30+ patterns consolidated
   Mercury · Stripe · Linear · Vercel · Ramp · Brex · Wise · Robinhood
   N26 · Factorial · Holded · Quipu · Mi Carpeta · 21st.dev refs
   ═════════════════════════════════════════════════════════════════════ */

/* ── 1. HERO KPI — Robinhood "single big portfolio number" + Mercury
   "balance front and center" + Stripe display tier merged.
   Renders ONE oversized number above the .kpi-grid. Use class
   `.hero-kpi` on a section element that contains label + value + delta. */
.hero-kpi {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2, 8px);
  padding: var(--space-5, 20px) var(--space-4, 16px) var(--space-6, 24px);
  margin: 0 0 var(--space-5, 20px);
  position: relative;
}
.hero-kpi__label {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--muted);
  letter-spacing: -0.005em;
  text-transform: none; /* NOT uppercase — editorial tone */
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.hero-kpi__label::before {
  content: "";
  width: 4px;
  height: 4px;
  background: var(--indigo);
  border-radius: 50%;
  display: inline-block;
}
.hero-kpi__value {
  font-size: clamp(2.5rem, 11vw, 3.5rem);
  font-weight: 600;
  letter-spacing: -0.045em;
  line-height: 0.95;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "ss01" 1, "zero" 1, "cv11" 1;
  white-space: nowrap;
}
.hero-kpi__delta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--muted-2);
  font-variant-numeric: tabular-nums;
}
.hero-kpi__delta-num {
  color: var(--state-success-fg);
  font-weight: 600;
}
.hero-kpi__delta--negative .hero-kpi__delta-num {
  color: var(--state-error-fg);
}

/* ── 2. STRIPE "SUBTITLE" PATTERN (21st.dev Statistics Card 7) —
   .kpi cards get an optional second-line context label between the
   primary label and the value. Like "Últimos 60 días" / "Este
   trimestre". Adds temporal context without claiming visual weight. */
.kpi-subtitle {
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--muted-2);
  letter-spacing: 0.01em;
  margin-top: -2px;
  text-transform: none;
}

/* ── 3. KPI DELTA SUB-TEXT (21st.dev pattern) — small line under
   the value showing absolute delta + comparison. */
.kpi-deltatext {
  font-size: 0.6875rem;
  font-weight: 400;
  color: var(--muted-2);
  letter-spacing: -0.002em;
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}
.kpi-deltatext__strong {
  color: var(--state-success-fg);
  font-weight: 600;
}
.kpi-deltatext--negative .kpi-deltatext__strong {
  color: var(--state-error-fg);
}

/* ── 4. "TODAY" WIDGET (Factorial signature) — single attention
   card at the top showing the one action the user must take today.
   Indigo→success gradient bg, big CTA on the right. */
.today-card {
  display: flex;
  align-items: center;
  gap: var(--space-3, 12px);
  padding: var(--space-4, 16px) var(--space-5, 20px);
  margin: 0 0 var(--space-4, 16px);
  border-radius: var(--radius, 20px);
  background:
    linear-gradient(135deg, var(--state-info-bg) 0%, transparent 100%);
  box-shadow: inset 0 0 0 1px var(--state-info-border);
  position: relative;
  overflow: hidden;
}
.today-card[data-tone="warn"] {
  background: linear-gradient(135deg, var(--state-warn-bg) 0%, transparent 100%);
  box-shadow: inset 0 0 0 1px var(--state-warn-border);
}
.today-card[data-tone="error"] {
  background: linear-gradient(135deg, var(--state-error-bg) 0%, transparent 100%);
  box-shadow: inset 0 0 0 1px var(--state-error-border);
}
.today-card__icon {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm, 14px);
  background: var(--surface);
  font-size: 1rem;
}
.today-card__body {
  flex: 1;
  min-width: 0;
}
.today-card__title {
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  line-height: 1.3;
  margin: 0;
  text-wrap: balance;
}
.today-card__sub {
  font-size: 0.75rem;
  color: var(--muted);
  margin-top: 2px;
  letter-spacing: -0.005em;
}
.today-card__cta {
  flex-shrink: 0;
  background: var(--indigo);
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 8px 14px;
  border-radius: var(--radius-pill, 999px);
  border: 0;
  cursor: pointer;
  letter-spacing: -0.005em;
  transition: background var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.today-card__cta:hover {
  background: color-mix(in oklab, var(--indigo) 88%, white);
}
.today-card__cta:active {
  transform: scale(0.96);
}

/* ── 5. CONTAINER QUERY for KPI grid (modern CSS, replaces brittle
   media queries). Adapts to parent width, not viewport. */
.kpi-grid {
  container-type: inline-size;
  container-name: kpis;
}
@container kpis (min-width: 480px) {
  .kpi-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
@container kpis (min-width: 720px) {
  .kpi-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* ── 6. SPRING EASING TOKEN (Mercury delight signature) —
   apply sparingly to ONE or TWO moments only. */
:root {
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* ── 7. KEYBOARD FOCUS LUXURY RING — visible only on :focus-visible
   (keyboard), invisible on mouse :focus. WCAG 2.4.7 AAA. */
:focus-visible {
  outline-offset: 3px;
}
.btn:focus-visible,
.bnav-item:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 2px var(--bg),
    0 0 0 4px var(--indigo);
}
/* 2026-05-25 GHOST-KILL: .tab:focus-visible was removed from the
   luxury-ring selector above. Reason: iOS Safari fires :focus-visible
   on EVERY tap (mouse-vs-keyboard heuristic is permissive on touch),
   so on each tab click the dual-ring indigo box-shadow flashed around
   the button — the user-reported "resalta al cambiar" bug surviving
   every prior fix. The dedicated rule at app.css:436-441 sets
   `box-shadow: none` for .tab:focus-visible and is now uncontested in
   the cascade (it lives later AND has its own specificity slot). The
   2px indigo underline on .tab.active::after is the single visual cue
   that a tab is selected. Zero halo on click. */

/* ── 8. PRESS STATE asymmetric timing (Apple HIG / Mercury) —
   press-down fast 50ms, press-up slower 220ms ease-out so the press
   "settles" naturally. */
.btn,
.today-card__cta,
.kpi,
.list li.tappable {
  transition:
    transform 220ms var(--ease-out),
    background var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}
.btn:active,
.today-card__cta:active,
.list li.tappable:active {
  transition-duration: 50ms; /* fast on the way down */
}

/* ── 9. SUBTLE PAPER-GRAIN TEXTURE (Mercury bank-feel) — ultra-low
   opacity radial dot pattern over body::after. Adds organic depth
   that pure flat surfaces lack. ~3% opacity, only visible to the
   eye that's looking for it. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background-image: radial-gradient(
    circle at 1px 1px,
    rgba(255, 255, 255, 0.018) 1px,
    transparent 0
  );
  background-size: 4px 4px;
  opacity: 0.6;
}
:root[data-theme="light"] body::after {
  background-image: radial-gradient(
    circle at 1px 1px,
    rgba(0, 0, 0, 0.022) 1px,
    transparent 0
  );
}
@media (prefers-reduced-motion: reduce) {
  body::after { display: none; }
}

/* ── 10. SECTION HEADING editorial redesign — replace the indigo
   border-left rectangle with a tiny inline dot + better typography.
   More premium, less "admin tool". */
.section-h {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: var(--space-6, 24px) 0 var(--space-3, 12px);
  border-left: 0;
  padding-left: 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.section-h::before {
  content: "";
  width: 6px;
  height: 6px;
  background: var(--indigo);
  border-radius: 50%;
  display: inline-block;
  opacity: 0.7;
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--indigo) 14%, transparent);
}
/* First section-h inside a tab-panel — no top margin so it doesn't
   double-up with the panel padding. */
.tab-panel > .section-h:first-child {
  margin-top: 0;
}

/* ── 11. TEXT RENDERING OPTIMIZATION by tier */
h1, h2, h3, .display-text, .hero-kpi__value, .num-hero {
  text-rendering: geometricPrecision;
}
body, p, .description {
  text-rendering: optimizeLegibility;
}

/* ── 12. AMOUNT formatting helper — for any € number that should
   read as financial-display-tier even outside the hero. */
.amount {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1, "cv11" 1;
  letter-spacing: -0.012em;
  font-weight: 600;
}
.amount-large {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1, "cv11" 1;
  letter-spacing: -0.028em;
  font-weight: 600;
  font-size: 1.5rem;
  line-height: 1.05;
}

/* ── 13. CHIP / TAG soft-pressed state for filter chips */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-pill);
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--muted);
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--border);
  cursor: pointer;
  transition: all var(--dur-fast) var(--ease-out);
  letter-spacing: -0.005em;
}
.chip:hover {
  color: var(--text);
  background: color-mix(in oklab, var(--indigo) 5%, transparent);
}
.chip[aria-pressed="true"], .chip.is-active {
  color: var(--indigo-3);
  background: var(--state-info-bg);
  box-shadow: inset 0 0 0 1px var(--state-info-border);
}

/* ── 14. SEGMENT CONTROL (Bizum tab pattern) — pill-shaped tabs
   inside a rounded container. Apple HIG signature. */
.segment {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--state-neutral-bg);
  border-radius: var(--radius-pill);
}
.segment__item {
  flex: 1;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 0.8125rem;
  font-weight: 500;
  cursor: pointer;
  transition: all var(--dur-fast) var(--ease-out);
  letter-spacing: -0.005em;
}
.segment__item[aria-selected="true"],
.segment__item.is-active {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
  font-weight: 600;
}

/* ── 15. SPRING-BOUNCE for KPI value entry (USE SPARINGLY — only
   on initial render, never on every refresh). Honour reduced-motion. */
.kpi-value.fc-spring-in {
  animation: kpi-spring-in 600ms var(--ease-spring) backwards;
}
@keyframes kpi-spring-in {
  0%   { opacity: 0; transform: scale(0.7) translateY(8px); }
  60%  { opacity: 1; transform: scale(1.04) translateY(0); }
  100% { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .kpi-value.fc-spring-in { animation: none; }
}

/* ── Editorial brand signature ─────────────────────────────
   The kind of detail Stripe / Mercury / Linear bury in their footers
   that nobody asks for but everyone feels — proof the product was
   built by humans, not assembled from a template. Matches landing
   brand voice ("— El equipo FactuChat" Dancing Script).
   2026-05-25 luxury polish from 22-agent consolidated audit. */
.fc-app-signature {
  margin: var(--space-7, 32px) 0 var(--space-4, 16px);
  padding: 0 var(--space-4, 16px);
  text-align: center;
  /* Honour the bottom-nav padding shim — never overlap. */
  padding-bottom: calc(var(--space-4, 16px) + var(--safe-bottom));
}
.fc-app-signature-text {
  font-family: 'Dancing Script', 'Segoe Script', 'Apple Chancery',
               'Snell Roundhand', 'Brush Script MT', cursive;
  font-size: 1.05rem;
  font-weight: 500;
  font-style: normal;
  line-height: 1;
  letter-spacing: 0;
  color: var(--muted);
  opacity: 0.65;
  /* Subtle gradient text — restrained brand expression. */
  background: linear-gradient(135deg,
    var(--muted) 0%,
    var(--indigo-3) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  /* Opt out of tabular nums for the signature — handwriting fonts read
     wrong with `tnum`. */
  font-variant-numeric: normal;
  font-feature-settings: normal;
}

/* Auto-hide the Chart.js skeleton once the real canvas is sized.
   2026-05-25 Evidence Collector: the skeleton was acting as a FALLBACK,
   not just a LOADING state — when Chart.js failed to init the skeleton
   shimmer stayed forever, presenting a duplicate empty chart below the
   working legacy `.chart`. Now the skeleton hides as soon as the canvas
   wrap is no longer `[hidden]`. */
.chart-monthly-canvas-wrap:not([hidden]) ~ #chart-monthly-skeleton,
.chart-monthly-canvas-wrap:not([hidden]) + #chart-monthly-skeleton {
  display: none;
}

/* ── KPI grid ─────────────────────────────────────── */

.kpi-grid {
  display: grid;
  /* 2026-05-25 fix: 1fr 1fr was fixed to 2 cols at every viewport.
     Mobile (<560px): 2 cols (140*2+gap fits in iPhone SE 320px).
     Tablet+desktop: auto-fit packs to 3 or 4 columns naturally. */
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
}

.kpi {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  min-height: 92px;
  /* 2026-05-25 fix: was display:flex with gap:6px — cards 2 and 3 of
     the kpi-grid omit the .kpi-trend row, leaving them visually shorter
     and the values landing on a different baseline vs cards 0 and 1.
     Switch to a 3-row grid (label, value, trend-slot) so every card
     keeps the same internal rhythm even when the trend row is empty.
     `gap: 4px` matches the optical spacing of the old flex layout. */
  display: grid;
  grid-template-rows: auto 1fr auto;
  row-gap: 4px;
  transition: border-color var(--dur-fast), transform var(--dur-fast),
              box-shadow var(--dur-fast);
  /* Entrance stagger — --i is set inline per card (0,1,2,3) so the
     fourth card lands ~240ms after the first. Cumulative, not jarring. */
  opacity: 0;
  transform: translateY(12px);
  animation: fc-enter-up var(--dur-slow) var(--ease-out) forwards;
  animation-delay: calc(var(--i, 0) * 60ms);
}

.kpi:hover {
  border-color: rgba(99, 91, 255, 0.26);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(99, 91, 255, 0.12);
}

@keyframes fc-enter-up {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

.kpi-label {
  font-size: 0.74rem;
  color: var(--muted);
  letter-spacing: 0.01em;
}

.kpi-value {
  /* 2026-05-25 luxury revolution: was 1.25rem at weight 800 — read like a
     "table cell". Display-tier numerics in Stripe/Mercury/Linear hit 1.5
     to 2.75rem with weight 600 (NOT 800: weight 600 reads more confident
     when paired with negative letter-spacing). The user's most important
     numbers deserve display treatment. */
  font-size: 1.625rem;            /* 26px — between Stripe table-large + Mercury KPI */
  font-weight: 600;
  letter-spacing: -0.028em;       /* Stripe uses -0.030em on its 56px display */
  line-height: 1.05;
  /* Forced tabular nums so digits don't shift mid count-up. The body
     setting at line 199 covers this app-wide, but explicit at the KPI
     site protects against any cascade reset from a future widget. */
  font-variant-numeric: tabular-nums;
}

/* KPI trend micro-indicator (Item 1, 2026-05-24-polish) */
.kpi-trend {
  font-size: 0.70rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  color: var(--muted-2);
}
.kpi-trend.up   { color: var(--state-success-fg); }
.kpi-trend.down { color: var(--state-error-fg); }

/* ── Chart (bar, pure CSS) ────────────────────────── */

.chart {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  align-items: flex-end;
  gap: 4px;
  min-height: 160px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.chart-month {
  flex: 1 0 36px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 36px;
}

.chart-bars {
  display: flex;
  gap: 2px;
  height: 110px;
  align-items: flex-end;
}

.chart-bar {
  width: 10px;
  min-height: 2px;
  border-radius: 3px 3px 0 0;
  transition: height 0.4s ease;
}

.chart-bar.ingresos { background: var(--green); }
.chart-bar.gastos { background: var(--red); }

.chart-label {
  font-size: 0.68rem;
  color: var(--muted);
  white-space: nowrap;
}

.chart-legend {
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: center;
  margin-top: 10px;
  font-size: 0.78rem;
  color: var(--muted);
}

.dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 6px;
  vertical-align: middle;
}

.dot.ingresos { background: var(--green); }
.dot.gastos { background: var(--red); }

/* ── Lists (facturas + clientes) ──────────────────── */

.list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.list.ranked { counter-reset: rank; }

.list.ranked li {
  counter-increment: rank;
}

.list.ranked li::before {
  content: counter(rank);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  background: var(--indigo);
  color: var(--bg);
  font-size: 0.78rem;
  font-weight: 800;
  border-radius: 50%;
  margin-right: 12px;
  flex-shrink: 0;
}

.list li {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid transparent;
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 0.9rem;
}
.list li:has(.badge.ok)      { border-left-color: var(--green, #22c55e); }
.list li:has(.badge.pending) { border-left-color: var(--yellow, #f5c842); }
.list li:has(.badge.err)     { border-left-color: var(--red, #ff3b5c); }

.list .meta {
  font-size: 0.76rem;
  color: var(--muted);
  margin-top: 2px;
}

.list .amount {
  font-weight: 700;
  white-space: nowrap;
}

.list .badge {
  display: inline-block;
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 6px;
  border-radius: 4px;
  margin-left: 6px;
}

.badge.ok { background: rgba(0, 229, 132, 0.14); color: var(--green); }
.badge.pending { background: rgba(245, 200, 66, 0.14); color: var(--yellow); }
.badge.err { background: rgba(255, 59, 92, 0.14); color: var(--red); }

/* ── Prediction box ───────────────────────────────── */

.pred-box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.pred-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.9rem;
}

.pred-row span { color: var(--muted); }
.pred-row strong { font-weight: 700; letter-spacing: -0.01em; }

.pred-row.highlight strong {
  font-size: 1.1rem;
  color: var(--indigo-3);
}

.pred-box hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 4px 0;
}

.semaforo-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 10px;
  font-size: 0.9rem;
  color: var(--muted);
}

.semaforo {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 700;
  font-size: 0.85rem;
  padding: 4px 10px;
  border-radius: 100px;
  background: var(--border);
  color: var(--muted);
}

.semaforo.verde { background: rgba(0, 229, 132, 0.16); color: var(--green); }
.semaforo.amarillo { background: rgba(245, 200, 66, 0.16); color: var(--yellow); }
.semaforo.rojo { background: rgba(255, 59, 92, 0.16); color: var(--red); }

/* ── Buttons ──────────────────────────────────────── */

.btn {
  appearance: none;
  border: 0;
  cursor: pointer;
  font: inherit;
  font-weight: 700;
  font-size: 0.88rem;
  padding: 12px 20px;
  border-radius: 100px;
  min-height: 44px;
  transition: transform 0.12s, background 0.2s;
}

.btn.primary { background: var(--indigo); color: var(--bg); }
.btn.primary:hover {
  background: #7c75ff;
  transform: translateY(-1px);
}
/* 2026-05-25 Designer P1-7 fix: :active was identical to :hover, no
   tactile feedback on tap. Now compresses inward (scale 0.97) so the
   press is felt. Matches modern iOS HIG pressed-state pattern. */
.btn.primary:active {
  background: #6d67f0;
  transform: scale(0.97);
  box-shadow: none;
}
.btn.primary:disabled { opacity: 0.55; cursor: not-allowed; pointer-events: none; }

.btn.ghost {
  background: transparent;
  color: var(--indigo-3);
  border: 1px solid var(--border);
}

.btn.ghost:hover { background: var(--border); }

/* ── Gestoría mode: health chips + capacity bar ───── */

.health-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.health-chip {
  flex: 1 0 90px;
  min-width: 90px;
  text-align: center;
  padding: 14px 10px;
  border-radius: var(--radius-sm);
  font-weight: 800;
  font-size: 1.05rem;
  border: 1px solid var(--border);
  background: var(--surface);
}

.health-chip.verde { color: var(--green); }
.health-chip.amarillo { color: var(--yellow); }
.health-chip.rojo { color: var(--red); }

.capacity {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 100px;
  height: 10px;
  overflow: hidden;
  margin-bottom: 6px;
}

.capacity-fill {
  background: linear-gradient(90deg, var(--cyan), var(--indigo));
  height: 100%;
  transition: width 0.5s ease;
  border-radius: 100px;
}

/* Health dot prefix in gestoría client list */
.health-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-right: 10px;
  vertical-align: middle;
  flex-shrink: 0;
}

.health-dot.verde { background: var(--green); }
.health-dot.amarillo { background: var(--yellow); }
.health-dot.rojo { background: var(--red); }

/* ── Footer ──────────────────────────────────────── */

.footer {
  margin-top: auto;
  padding: 24px 18px 36px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  border-top: 1px solid var(--border);
}

/* ── Desktop scale-up (rare on Telegram but supported) */
@media (min-width: 640px) {
  main { max-width: 620px; margin: 0 auto; }
  .kpi-grid { grid-template-columns: repeat(4, 1fr); }
  .kpi-value { font-size: 1.4rem; }
}

/* Global keyboard-focus indicator.
   2026-05-25 redesign: replaced the dual-ring box-shadow (which inflated
   the visual footprint of any tab/button/link the moment it received
   focus — the root of the "resalta al clickar" complaint) with a single
   2px solid outline + outline-offset:2px. Meets WCAG 2.4.7 (Focus
   Visible) AAA at non-text contrast 3:1 against any surface in our
   palette while reading as "subtle ring" instead of "indigo halo". */
:focus-visible {
  outline: 2px solid var(--indigo, #4f46e5);
  outline-offset: 2px;
  border-radius: 4px;
  box-shadow: none;
}

/* Mouse click should NEVER show a focus indicator on elements that have
   their own active-state styling (tabs, bottom-nav items, primary CTAs).
   These elements set `.tab.active` / `[aria-selected="true"]` themselves
   — the focus ring on top is double-indication and the source of the
   "click flash" the user reported. */
:focus:not(:focus-visible) {
  outline: none;
  box-shadow: none;
}

/* Community strip styles removed 2026-04-21 — numbers were placeholder
   social-proof without real data backing; markup + JS also removed. */

/* ── Achievement chips (gamification) ────────────── */
.achievements {
  display: flex;
  gap: 6px;
  padding: 12px 0 4px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.achievements::-webkit-scrollbar { display: none; }

.achievement {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 10px;
  border-radius: 100px;
  font-size: 0.72rem;
  font-weight: 600;
  background: var(--surface);
  border: 1px solid var(--border);
  white-space: nowrap;
  flex: 0 0 auto;
  transition: transform var(--dur-fast);
  position: relative;
}

.achievement.locked {
  filter: grayscale(1);
  opacity: 0.45;
  color: var(--muted);
}

.achievement.unlocked {
  color: var(--text);
  background: linear-gradient(135deg,
    rgba(99, 91, 255, 0.16) 0%,
    rgba(0, 212, 255, 0.12) 100%);
  border-color: rgba(99, 91, 255, 0.32);
  animation: fc-achievement-pop 480ms var(--ease-back) forwards;
}

@keyframes fc-achievement-pop {
  0%   { transform: scale(0.6); opacity: 0; }
  60%  { transform: scale(1.12); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* CSS-only confetti burst — fires on newest unlocked achievement.
   Six tiny particles radiate out and fade in ~700ms. */
.achievement.unlocked.just-unlocked::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 100px;
  pointer-events: none;
  background:
    radial-gradient(circle at 20% 10%,  var(--indigo)  2px, transparent 3px),
    radial-gradient(circle at 80% 10%,  var(--cyan)    2px, transparent 3px),
    radial-gradient(circle at 15% 90%,  var(--green)   2px, transparent 3px),
    radial-gradient(circle at 85% 90%,  var(--yellow)  2px, transparent 3px),
    radial-gradient(circle at 50% 0%,   var(--indigo-3) 2px, transparent 3px),
    radial-gradient(circle at 50% 100%, var(--cyan)    2px, transparent 3px);
  opacity: 0;
  animation: fc-confetti 720ms ease-out forwards;
}

@keyframes fc-confetti {
  0%   { opacity: 0; transform: scale(0.6); }
  30%  { opacity: 1; transform: scale(1.3); }
  100% { opacity: 0; transform: scale(2.1); }
}

/* Empty-state SVG illustration (Item 3, 2026-05-24-polish) */
.empty-state-svg {
  color: var(--indigo-3, #818cf8);
  opacity: 0.85;
  flex-shrink: 0;
}

/* ── Streak flame badge ──────────────────────────── */
.streak-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: 100px;
  font-size: 0.82rem;
  font-weight: 700;
  background: linear-gradient(135deg,
    rgba(255, 124, 60, 0.16) 0%,
    rgba(245, 200, 66, 0.16) 100%);
  border: 1px solid rgba(245, 200, 66, 0.3);
  color: #ffb561;
  margin: 10px 0 0;
  animation: fc-enter-up var(--dur-slow) var(--ease-out) 220ms backwards;
}

.streak-badge .flame {
  display: inline-block;
  animation: fc-flame-flicker 1.8s ease-in-out infinite;
  transform-origin: center bottom;
}

@keyframes fc-flame-flicker {
  0%, 100% { transform: rotate(-3deg) scale(1);   opacity: 1;   }
  50%      { transform: rotate(3deg)  scale(1.08); opacity: 0.85; }
}

/* ── Empty-state delight ─────────────────────────── */
.empty-state {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px 22px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin: 8px 0 18px;
  animation: fc-enter-up var(--dur-slow) var(--ease-out) 60ms backwards;
}

.empty-state h2 {
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.empty-state p {
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.5;
  max-width: 34ch;
}

.empty-state .btn.primary {
  margin-top: 4px;
}

/* ── Tutorial overlay (first visit only) ─────────── */
.tutorial-overlay {
  position: fixed;
  inset: 0;
  background: rgba(10, 15, 26, 0.92);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: fc-fade-in 280ms var(--ease-out) forwards;
}

/* Explicit hidden rule — the class's `display: flex` would otherwise
   override the HTML hidden attribute's default `display: none`. */
.tutorial-overlay[hidden] { display: none !important; }

@keyframes fc-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.tutorial-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px 24px;
  max-width: 340px;
  width: 100%;
  text-align: center;
  box-shadow: 0 20px 48px rgba(0, 0, 0, 0.45);
  animation: fc-tutorial-pop 420ms var(--ease-back) forwards;
}

@keyframes fc-tutorial-pop {
  from { transform: scale(0.92); opacity: 0; }
  to   { transform: scale(1);    opacity: 1; }
}

.tutorial-step-icon {
  font-size: 2.4rem;
  margin-bottom: 8px;
  display: block;
}

.tutorial-card h3 {
  font-size: 1.1rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin-bottom: 8px;
}

.tutorial-card p {
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.5;
  margin-bottom: 20px;
}

.tutorial-dots {
  display: flex;
  gap: 6px;
  justify-content: center;
  margin-bottom: 18px;
}

.tutorial-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--border);
  transition: background var(--dur-fast), transform var(--dur-fast);
}

.tutorial-dot.active {
  background: var(--indigo);
  transform: scale(1.25);
}

.tutorial-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
}

.tutorial-actions .btn {
  padding: 10px 18px;
  font-size: 0.84rem;
  /* WCAG 2.5.5 Target Size (AAA) / iOS HIG 44×44 — raised from 38px so
     every overlay button hits the same minimum as the rest of the app. */
  min-height: 44px;
}

.tutorial-skip {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--muted);
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
  padding: 12px 16px;
  margin-top: 4px;
  /* WCAG 2.5.5 Target Size — ghost "Saltar" link needs the same tap
     area as the primary button beside it. */
  min-height: 44px;
}
.tutorial-skip:hover { color: var(--text); }

/* ── Pending-action chip bounce-in ───────────────── */
.action-chip {
  animation: fc-chip-in 320ms var(--ease-back) backwards;
}

@keyframes fc-chip-in {
  from { opacity: 0; transform: scale(0.7); }
  to   { opacity: 1; transform: scale(1);   }
}

/* ── Button hover lift ───────────────────────────── */
.btn.ghost,
.btn.primary {
  transition: transform var(--dur-fast), background var(--dur-fast),
              box-shadow var(--dur-fast);
}
.btn.ghost:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(99, 91, 255, 0.12);
}
.btn.primary:hover,
.btn.primary:focus-visible {
  box-shadow: 0 6px 18px rgba(99, 91, 255, 0.34);
}

/* ── Reduced-motion universal kill switch ────────── */
/* Any new animation added above still ends up disabled for users who
   opted out of motion in their OS settings. This supersedes the older
   targeted rule and was widened to `* !important` for complete coverage. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .empty-state,
  .streak-badge,
  .kpi,
  .ps-bar-fill,
  .achievement,
  .tutorial-overlay,
  .tutorial-card,
  .action-chip { animation: none !important; opacity: 1 !important; transform: none !important; }
  .spinner { background: var(--surface); }
}

/* ── Tappable client rows (gestoria) ─────────────── */
.list li.tappable {
  cursor: pointer;
  transition: background 0.12s ease, transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) {
  .list li.tappable:hover { background: rgba(99, 91, 255, 0.08); }
}
.list li.tappable:active { transform: scale(0.98); }

/* ── FAB (+ Nueva factura) ───────────────────────── */
.fab {
  position: sticky;
  bottom: 18px;
  margin: 16px auto 4px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 20px;
  font: inherit;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(135deg, var(--indigo) 0%, var(--indigo-3) 100%);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(99, 91, 255, 0.35);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
  align-self: center;
  z-index: 2;
}
.fab svg { stroke: currentColor; }
.fab:hover { transform: translateY(-1px); }
.fab:active { transform: scale(0.97); }
#tab-facturas { display: flex; flex-direction: column; }
#tab-facturas[hidden] { display: none; }

/* ── Plan status strip (autónomo mode) ──────────── */
.plan-status {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  margin-bottom: 4px;
  font-size: 0.84rem;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.ps-label { color: var(--text); }

.ps-bar {
  background: var(--border);
  border-radius: 100px;
  height: 6px;
  overflow: hidden;
}

.ps-bar-fill {
  height: 100%;
  border-radius: 100px;
  /* First-paint grow animation: width 0 → target in 800ms, then
     transition handles future updates. --pct is set inline. */
  width: 0;
  transition: width var(--dur-slow) var(--ease-out);
  animation: fc-bar-grow var(--dur-slow) var(--ease-out) forwards;
}

@keyframes fc-bar-grow {
  from { width: 0; }
  to   { width: var(--pct, 0%); }
}

.ps-bar-fill.green { background: var(--green); }
.ps-bar-fill.yellow { background: var(--yellow); }
.ps-bar-fill.red { background: var(--red); }

.ps-upgrade-link {
  color: var(--indigo-3);
  font-weight: 600;
  font-size: 0.8rem;
  text-decoration: none;
  align-self: flex-start;
}

.ps-upgrade-link:hover { text-decoration: underline; }

/* B3: quota warning banner — amber strip prepended to plan-status when at 80%+ */
.plan-status .ps-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.plan-status.with-quota-warn { padding-top: 0; padding-left: 0; padding-right: 0; gap: 0; }
.plan-status.with-quota-warn .ps-row { padding: 10px 14px; }
.fc-quota-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  background: rgba(245, 158, 11, 0.12);
  border-bottom: 1px solid rgba(245, 158, 11, 0.25);
  border-top-left-radius: var(--radius-sm);
  border-top-right-radius: var(--radius-sm);
  font-size: 0.78rem;
  line-height: 1.3;
  min-height: 44px;
  color: var(--text);
}
.fc-quota-icon { font-size: 0.95rem; line-height: 1; flex-shrink: 0; }
.fc-quota-msg { flex: 1; min-width: 0; }
.fc-quota-msg strong { color: var(--yellow); font-weight: 700; }
.fc-quota-cta {
  border: 0;
  background: var(--indigo);
  color: #fff;
  font-weight: 700;
  font-size: 0.74rem;
  padding: 12px 14px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 0.15s, box-shadow 0.15s;
  font-family: inherit;
  /* WCAG 2.5.5 Target Size — matches the 44px baseline for every CTA in
     the app. Was 36px (via 6px vertical padding on 0.74rem text). */
  min-height: 44px;
}
.fc-quota-cta:hover { transform: translateY(-1px); box-shadow: 0 4px 10px rgba(99, 91, 255, 0.35); }
.fc-quota-cta:active { transform: scale(0.97); }

/* ── AEAT deadlines strip (autónomo mode) ────────── */
.deadlines-strip {
  margin-bottom: 10px;
}

.deadline-chip {
  display: inline-block;
  padding: 4px 10px;
  border-radius: 100px;
  font-size: 0.8rem;
  font-weight: 600;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--muted);
}

.deadline-chip.green { color: var(--green); border-color: rgba(0, 229, 132, 0.22); }
.deadline-chip.yellow { color: var(--yellow); border-color: rgba(245, 200, 66, 0.22); }
.deadline-chip.red { color: var(--red); border-color: rgba(255, 59, 92, 0.22); }

/* ── Gestoría action chips ───────────────────────── */
.g-action-summary {
  padding: 6px 0 10px;
}

.action-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}

.action-chip {
  font-size: 0.72rem;
  padding: 2px 7px;
  border-radius: 100px;
  background: rgba(99, 91, 255, 0.1);
  border: 1px solid rgba(99, 91, 255, 0.2);
  color: var(--indigo-3);
  white-space: nowrap;
}

.action-chip.overflow {
  background: var(--surface);
  border-color: var(--border);
  color: var(--muted);
}

/* ── O.4 Messages inbox ──────────────────────────── */
.list.messages { padding: 0; }
.list.messages li.msg {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  margin-bottom: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
}
.list.messages li.msg-sent {
  background: rgba(99, 91, 255, 0.06);
  border-color: rgba(99, 91, 255, 0.2);
}
.list.messages li.msg-received {
  background: rgba(0, 228, 132, 0.05);
  border-color: rgba(0, 228, 132, 0.18);
}
.msg-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.82rem;
  color: var(--muted);
}
.msg-arrow {
  font-weight: 700;
  color: var(--indigo-3);
  width: 14px;
  text-align: center;
}
.msg-sent .msg-arrow { color: var(--indigo-3); }
.msg-received .msg-arrow { color: var(--green); }
.msg-dir {
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 0.7rem;
  font-weight: 600;
  opacity: 0.7;
}
.msg-body {
  color: var(--text);
  line-height: 1.4;
  word-wrap: break-word;
}

/* ── BottomNav 3-item (new primary navigation, feature-flaggable) ─── */
.bottom-nav {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  height: calc(56px + var(--safe-bottom));
  padding: 8px 0 calc(8px + var(--safe-bottom));
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  background: rgba(10, 15, 26, 0.96);
  backdrop-filter: blur(16px) saturate(1.8);
  -webkit-backdrop-filter: blur(16px) saturate(1.8);
  border-top: 1px solid rgba(99, 91, 255, 0.10);
  z-index: 20;
}
.bottom-nav[data-theme-bg] { background: var(--bnav-bg, rgba(10,15,26,0.96)); }
.bnav-item {
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 6px 0 2px;
  min-height: 48px;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.bnav-item[aria-selected="true"] { color: var(--indigo-3); }
.bnav-item[aria-selected="true"] .bnav-icon {
  background: rgba(99, 91, 255, 0.14);
  border-radius: 12px;
  padding: 4px 16px;
  transition: background var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-back);
}
.bnav-icon { display: inline-flex; padding: 4px 16px; transition: background var(--dur-fast); }
.bnav-label { font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }

/* body gets bottom-padding when bottom-nav visible, so content isn't hidden behind it */
body.has-bottom-nav { padding-bottom: calc(56px + var(--safe-bottom) + 18px); }

/* ── Odometer currency — Stripe-style digit roll-up ────────── */
.odometer {
  display: inline-flex;
  align-items: baseline;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.odo-digit {
  display: inline-block;
  width: 0.62em;
  height: 1.2em;
  overflow: hidden;
  vertical-align: top;
  position: relative;
}
.odo-column {
  display: inline-flex;
  flex-direction: column;
  line-height: 1.2em;
  transition: transform 800ms var(--ease-out);
  will-change: transform;
}
.odo-column span { height: 1.2em; text-align: center; min-width: 0.6em; }
.odo-sep, .odo-currency { padding: 0 1px; }

/* Balance exhale — subtle 1.8% scale pulse when count-up settles */
.kpi-value.fc-settled { animation: fc-exhale 400ms ease-in-out; }
@keyframes fc-exhale {
  0% { transform: scale(1); } 40% { transform: scale(1.018); } 100% { transform: scale(1); }
}

/* ── Progress ring SVG for plan-status ─────────────────────── */
.plan-ring { width: 36px; height: 36px; display: block; margin: 2px 8px 0 0; flex-shrink: 0; }
.plan-ring-track { fill: none; stroke: rgba(99,91,255,0.18); stroke-width: 3; }
.plan-ring-fill {
  fill: none; stroke: var(--indigo); stroke-width: 3; stroke-linecap: round;
  stroke-dasharray: 94.25; stroke-dashoffset: 94.25;
  transform: rotate(-90deg); transform-origin: center;
  transition: stroke-dashoffset 800ms var(--ease-out), stroke var(--dur-base);
}
.plan-ring-fill.yellow { stroke: var(--yellow); }
.plan-ring-fill.red { stroke: var(--red); }
.plan-status.with-ring { flex-direction: row; align-items: center; }

/* ── View Transitions API — tab cross-fade ───────────────── */
::view-transition-old(root), ::view-transition-new(root) {
  animation-duration: 220ms;
  animation-timing-function: var(--ease-out);
}

/* ── Tutorial spotlight mask overlay ───────────────────────── */
.tutorial-overlay.spotlight-mode .spotlight-svg { position: absolute; inset: 0; pointer-events: none; }
.tutorial-overlay.spotlight-mode .tutorial-card { position: fixed; max-width: 320px; }

/* ── IRPF quick simulator (Predicción tab) ─────────────────── */
.irpf-sim { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; margin-top: 14px; }
.irpf-sim-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.irpf-ded-value { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--indigo-3); }
.irpf-slider { width: 100%; accent-color: var(--indigo); margin: 4px 0 14px; }
.irpf-sim-results { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; }
.irpf-sim-results strong { font-weight: 700; display: block; margin-top: 2px; }
.irpf-savings strong { color: var(--green); }

/* ── Upgrade bottom-sheet modal ──────────────────────────────
   Slides up 320ms from the bottom edge with a 12px backdrop blur.
   Mobile-first 320px min width, capped 480px on tablets.
   No third-party libs — pure CSS + dialog element.
*/
.fc-sheet {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  max-width: 100vw;
  max-height: 100vh;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  z-index: 200;
  overflow: hidden;
}
.fc-sheet[hidden] { display: none !important; }
.fc-sheet[open] { display: block; }

.fc-sheet__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(10, 15, 26, 0.62);
  backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  animation: fc-sheet-backdrop-in 280ms var(--ease-out) forwards;
  cursor: pointer;
}

@keyframes fc-sheet-backdrop-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.fc-sheet__panel {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  background: var(--surface);
  border-top-left-radius: 22px;
  border-top-right-radius: 22px;
  border-top: 1px solid var(--border);
  padding: 8px 22px calc(28px + var(--safe-bottom));
  max-height: 90vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  transform: translateY(100%);
  animation: fc-sheet-slide-up 320ms var(--ease-out) forwards;
  box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.45);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

@keyframes fc-sheet-slide-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

.fc-sheet__handle {
  appearance: none;
  border: 0;
  background: var(--border);
  width: 44px;
  height: 5px;
  border-radius: 100px;
  margin: 6px auto 8px;
  padding: 0;
  cursor: pointer;
  display: block;
}

.fc-sheet__title {
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin: 0;
  text-align: center;
}

.fc-sheet__sub {
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.5;
  margin: 0;
  text-align: center;
  max-width: 32ch;
  margin-inline: auto;
}

/* Plan tiles ─ 3-up grid, tap to select. */
.fc-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: 4px;
}

.fc-tile {
  appearance: none;
  background: var(--bg);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  transition: border-color 180ms var(--ease-out), transform 120ms,
              background 180ms;
  min-height: 88px;
  font: inherit;
  color: var(--text);
  position: relative;
}
.fc-tile:hover { transform: translateY(-1px); }
.fc-tile[aria-checked="true"] {
  border-color: var(--indigo);
  background: linear-gradient(135deg,
    rgba(99, 91, 255, 0.10) 0%,
    rgba(0, 212, 255, 0.06) 100%);
}
.fc-tile-name {
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.fc-tile[aria-checked="true"] .fc-tile-name { color: var(--indigo-3); }
.fc-tile-price {
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.fc-tile-period {
  font-size: 0.68rem;
  color: var(--muted);
}
.fc-tile-badge {
  position: absolute;
  top: -8px;
  right: 6px;
  background: var(--green);
  color: var(--bg);
  font-size: 0.6rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 8px;
  border-radius: 100px;
}

/* Bullet feature list. */
.fc-bullets {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.86rem;
}
.fc-bullets li {
  position: relative;
  padding-left: 22px;
  color: var(--text);
  line-height: 1.45;
}
.fc-bullets li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 0.45em;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--indigo), var(--cyan));
}

/* Method tiles ─ 4-up icon-led row. */
.fc-methods {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}

.fc-method {
  appearance: none;
  background: var(--bg);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  font: inherit;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  transition: border-color 180ms, color 180ms, background 180ms;
  min-height: 60px;
}
.fc-method[aria-checked="true"] {
  border-color: var(--indigo);
  color: var(--text);
  background: rgba(99, 91, 255, 0.08);
}
.fc-method-icon { font-size: 1.2rem; line-height: 1; }

.fc-sheet__error {
  color: var(--red);
  font-size: 0.84rem;
  text-align: center;
  margin: 0;
  padding: 8px 12px;
  border: 1px solid rgba(255, 59, 92, 0.3);
  border-radius: var(--radius-sm);
  background: rgba(255, 59, 92, 0.08);
}

.fc-cta {
  appearance: none;
  border: 0;
  cursor: pointer;
  font: inherit;
  font-weight: 800;
  font-size: 1rem;
  color: var(--bg);
  background: linear-gradient(135deg, var(--indigo) 0%, var(--cyan) 140%);
  padding: 16px 24px;
  border-radius: 100px;
  min-height: 52px;
  transition: transform 120ms, box-shadow 180ms, opacity 180ms;
  box-shadow: 0 8px 24px rgba(99, 91, 255, 0.36);
  letter-spacing: -0.01em;
}
.fc-cta:hover, .fc-cta:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 10px 30px rgba(99, 91, 255, 0.46);
}
.fc-cta:active { transform: scale(0.98); }
.fc-cta:disabled {
  opacity: 0.55;
  cursor: progress;
  transform: none;
  box-shadow: none;
}

.fc-sheet__trust {
  text-align: center;
  font-size: 0.74rem;
  color: var(--muted);
  margin: 0;
}

/* Tablet/desktop centered card. */
@media (min-width: 640px) {
  .fc-sheet__panel {
    left: 50%;
    right: auto;
    transform: translateX(-50%) translateY(100%);
    width: min(440px, 92vw);
    bottom: 24px;
    border-radius: 22px;
    border: 1px solid var(--border);
    animation: fc-sheet-slide-up-desktop 320ms var(--ease-out) forwards;
  }
  @keyframes fc-sheet-slide-up-desktop {
    from { transform: translateX(-50%) translateY(100%); opacity: 0; }
    to   { transform: translateX(-50%) translateY(0); opacity: 1; }
  }
}

/* Reduced-motion: snap, no slide. */
@media (prefers-reduced-motion: reduce) {
  .fc-sheet__panel,
  .fc-sheet__backdrop {
    animation: none !important;
    transform: none !important;
  }
  .fc-sheet__panel { transform: translateY(0) !important; }
}

/* ── Windows High Contrast / forced-colors mode support ── */
@media (forced-colors: active) {
  .kpi, .fc-tile, .fc-method, .tab, .btn, .fab, .bnav-item {
    border: 2px solid CanvasText;
    forced-color-adjust: none;
  }
  .fc-tile[aria-checked="true"], .fc-method[aria-checked="true"],
  .tab.active, .bnav-item[aria-selected="true"] {
    border-color: Highlight;
    background: Canvas;
  }
  .fc-cta, .btn.primary, .fab {
    background: ButtonFace;
    color: ButtonText;
    border: 2px solid ButtonText;
  }
  :focus-visible {
    outline: 3px solid Highlight !important;
    outline-offset: 3px;
  }
  .capacity-fill, .ps-bar-fill, .plan-ring-fill {
    background: Highlight !important;
    stroke: Highlight !important;
  }
  .achievement.unlocked { outline: 2px solid Highlight; }
}

/* ── Scroll-margin for sticky topbar focus visibility (WCAG 2.4.11) ── */
:focus-visible {
  scroll-margin-top: 120px;
  scroll-margin-bottom: 80px;
}

/* ── Offline banner (Tier 4 #9) ───────────────────────────
   Amber pill at the top of the viewport shown when the SW serves
   a cached snapshot because the device lost connectivity. Amber,
   not red — we want "heads up" energy, not "danger". Slides down
   from the top via a 300ms ease so it doesn't shock the user. */
.offline-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .75rem;
  padding: .5rem 1rem;
  padding-top: max(.5rem, env(safe-area-inset-top));
  background: rgba(245, 158, 11, .95);
  color: #fff;
  text-align: center;
  font-size: .875rem;
  font-weight: 600;
  letter-spacing: -.005em;
  z-index: 10000;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .12);
  animation: fc-offline-slide-down .3s cubic-bezier(.22, .61, .36, 1);
}
.offline-banner[hidden] { display: none !important; }
.offline-banner::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #fff;
  margin-right: .25rem;
  animation: fc-offline-pulse 1.6s ease-in-out infinite;
}
.offline-banner-btn,
.offline-banner-close {
  flex-shrink: 0;
  background: rgba(255, 255, 255, .18);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, .32);
  border-radius: .375rem;
  padding: .25rem .625rem;
  font-size: .75rem;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background .15s ease, transform .12s ease;
}
.offline-banner-btn:hover,
.offline-banner-btn:focus-visible,
.offline-banner-close:hover,
.offline-banner-close:focus-visible {
  background: rgba(255, 255, 255, .28);
  outline: none;
}
.offline-banner-btn:active,
.offline-banner-close:active { transform: translateY(1px); }
.offline-banner-close {
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  padding: .125rem .5rem;
  border-radius: 50%;
  width: 1.5rem;
  height: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
@keyframes fc-offline-slide-down {
  from { transform: translateY(-100%); opacity: 0; }
  to   { transform: translateY(0);     opacity: 1; }
}
@keyframes fc-offline-pulse {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%      { opacity: .55; transform: scale(.85); }
}
@media (prefers-reduced-motion: reduce) {
  .offline-banner { animation: none; }
  .offline-banner::before { animation: none; }
}

/* Tier 5 #12 — Embed Widget panel ────────────────────────── */
.embed-snippet {
  display: block;
  width: 100%;
  margin: 12px 0 8px;
  padding: 12px 14px;
  border-radius: 10px;
  background: rgba(0,0,0,0.05);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", monospace;
  font-size: 12px;
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-all;
  user-select: all;
  -webkit-user-select: all;
  border: 1px solid rgba(0,0,0,0.08);
}
@media (prefers-color-scheme: dark) {
  .embed-snippet {
    background: rgba(255,255,255,0.04);
    border-color: rgba(255,255,255,0.08);
  }
}
#embed-copy { margin-top: 4px; }
#embed-copy-feedback { margin-top: 6px; color: #2e7d32; }
#embed-trend { min-height: 60px; }

/* ─────────────────────────────────────────────────────────────────────
   v2 Mini App wiring — chart-monthly card + facturas filters/pagination
   + gestoria cliente drill-down dialog. All theme-aware via existing
   CSS vars (--bg, --surface, --text, --muted, --indigo). Mobile-first.
   ───────────────────────────────────────────────────────────────────── */

/* Monthly chart card */
.chart-monthly {
  margin-top: 20px;
  padding: 14px 12px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  position: relative;
  overflow: hidden;
}
.chart-monthly .section-h {
  margin: 0 0 10px;
  font-size: 13px;
  letter-spacing: 0.02em;
  color: var(--muted);
  text-transform: uppercase;
}
.chart-monthly-canvas-wrap {
  position: relative;
  width: 100%;
  min-height: 180px;
}
#chart-monthly-canvas {
  display: block;
  width: 100% !important;
  max-width: 100%;
  height: 180px !important;
}
.chart-monthly-skeleton {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  height: 180px;
  padding: 8px 4px 4px;
}
.chart-skel-bar {
  flex: 1;
  background: linear-gradient(180deg,
    rgba(255,255,255,0.06) 0%,
    rgba(99,91,255,0.10) 100%);
  border-radius: 6px;
  min-width: 12px;
  animation: chart-skel-pulse 1.4s ease-in-out infinite;
}
.chart-skel-bar:nth-child(1) { height: 35%; animation-delay: 0ms; }
.chart-skel-bar:nth-child(2) { height: 60%; animation-delay: 120ms; }
.chart-skel-bar:nth-child(3) { height: 45%; animation-delay: 240ms; }
.chart-skel-bar:nth-child(4) { height: 75%; animation-delay: 360ms; }
.chart-skel-bar:nth-child(5) { height: 55%; animation-delay: 480ms; }
.chart-skel-bar:nth-child(6) { height: 85%; animation-delay: 600ms; }
@keyframes chart-skel-pulse {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}

/* Facturas filters — mobile-first compact stack */
/* Filter bar collapse pattern (Item 6, 2026-05-24-polish):
   Default state is collapsed. "Filtrar" toggle chip expands the filter rows.
   The search row always stays visible for quick full-text searches. */
.filter-bar-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-radius: 100px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  margin-bottom: 8px;
  transition: background var(--dur-fast), border-color var(--dur-fast);
  min-height: 44px; /* 2026-05-25: bumped from 40 → 44 to meet WCAG 2.5.5 Level AAA touch-target (Designer + a11y audits) */
}
.filter-bar-toggle:hover { background: var(--border); }
.filter-bar-toggle[aria-expanded="true"] {
  background: var(--border);
  border-color: rgba(99, 91, 255, 0.35);
}
.filter-bar-toggle svg {
  flex-shrink: 0;
  transition: transform var(--dur-fast) var(--ease-out);
}
.filter-bar-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }

.filter-bar-collapsible {
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.28s var(--ease-out);
}
.filter-bar-collapsible.filter-bar-open {
  max-height: 400px; /* generous ceiling; actual content ~220px */
}

.facturas-filters {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  margin-bottom: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.facturas-filters .filter-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.facturas-filters .filter-row-search,
.facturas-filters .filter-row-actions { flex-direction: column; }
.facturas-filters .filter-row-actions { flex-direction: row; }
.facturas-filters .filter-row-pills,
.facturas-filters .filter-row-dates { flex-wrap: wrap; }
.facturas-filters .filter-field {
  flex: 1 1 calc(50% - 4px);
  min-width: 140px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.facturas-filters .filter-label {
  font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.facturas-filters .filter-input,
.facturas-filters .filter-select {
  width: 100%;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: 14px/1.3 var(--ff-sans);
  -webkit-appearance: none;
  appearance: none;
  transition: border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.facturas-filters .filter-input:focus,
.facturas-filters .filter-select:focus {
  outline: none;
  border-color: var(--indigo);
  background: rgba(99, 91, 255, 0.06);
}
.facturas-filters .filter-input[type="date"] {
  /* Match height across iOS / Android — native date pickers vary wildly */
  min-height: 44px; /* 2026-05-25: bumped from 40 → 44 to meet WCAG 2.5.5 Level AAA touch-target (Designer + a11y audits) */
  color-scheme: dark light;
}
.facturas-filters .filter-select {
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position:
    calc(100% - 16px) 50%,
    calc(100% - 11px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 30px;
}
.facturas-filters .filter-btn {
  flex: 1;
  min-height: 44px;
  padding: 0 14px;
  font-size: 14px;
  font-weight: 600;
}
.facturas-filters .filter-msg {
  margin: 0;
  padding: 6px 10px;
  font-size: 12px;
  color: var(--yellow);
  background: rgba(245, 200, 66, 0.08);
  border-radius: var(--radius-sm);
}
.facturas-filters .filter-msg.filter-msg-error {
  color: var(--red);
  background: rgba(255, 59, 92, 0.10);
}

.facturas-count {
  margin: 0 4px 8px;
  font-size: 12px;
  color: var(--muted);
}

.facturas-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 14px 0 8px;
  padding: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.facturas-pagination .filter-btn {
  flex: 0 0 auto;
  min-height: 44px; /* 2026-05-25: bumped from 40 → 44 to meet WCAG 2.5.5 Level AAA touch-target (Designer + a11y audits) */
  padding: 8px 12px;
  font-size: 13px;
}
.facturas-pagination .filter-btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}
.facturas-pagination .filter-page-label {
  flex: 1 1 auto;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

@media (min-width: 480px) {
  .facturas-filters { flex-direction: column; }
  .facturas-filters .filter-row-pills,
  .facturas-filters .filter-row-dates { flex-wrap: nowrap; }
}

/* ════════════════════════════════════════════════════════════════════
   Mobile breathing room — Facturas filter panel + tab-panel.
   User report 2026-05-27 (screenshot iPhone): "en el telefono todo parece
   demasiado pegado lo de aplicar filtros, el espacio desde para filtros
   etc, alinear mejor para movil! en pc parece normal".
   PC styles untouched (base values above). Only mobile <480px gets the
   looser spacing below.
   ════════════════════════════════════════════════════════════════════ */
@media (max-width: 479px) {
  /* Push tab-panel content slightly away from the screen edges */
  .tab-panel {
    padding-left: 20px;
    padding-right: 20px;
  }
  /* Filter form: more interior padding + more vertical space between rows */
  .facturas-filters {
    padding: 16px;
    gap: 14px;
  }
  /* More horizontal gap between paired fields (TIPO↔ESTADO, DESDE↔HASTA,
     Aplicar↔Limpiar) */
  .facturas-filters .filter-row {
    gap: 10px;
  }
  /* Label sits a bit further above its input/select */
  .facturas-filters .filter-field {
    gap: 6px;
  }
  /* Extra breathing space above the Aplicar/Limpiar action row */
  .facturas-filters .filter-row-actions {
    margin-top: 4px;
  }
}

/* Gestoria cliente drill-down modal — reuses .fc-sheet bones */
.fc-cm__panel {
  /* iOS-style bottom sheet — full-height with internal scroll. We let the
     existing .fc-sheet__panel take care of slide-in / backdrop. */
  max-height: 92vh;
  overflow-y: auto;
}
.fc-cm__header {
  margin-bottom: 14px;
}
.fc-cm__meta {
  margin: 4px 0 0;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.fc-cm__loading {
  padding: 22px 12px;
  text-align: center;
  color: var(--muted);
  font-size: 13px;
}
.fc-cm__error {
  margin: 8px 0 12px;
  padding: 10px 12px;
  font-size: 13px;
  color: var(--red);
  background: rgba(255, 59, 92, 0.08);
  border-radius: var(--radius-sm);
}
.fc-cm__kpis {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-bottom: 14px;
}
.fc-cm__kpi {
  padding: 12px 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.fc-cm__kpi-label {
  font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 4px;
}
.fc-cm__kpi-value {
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.fc-cm__list {
  list-style: none;
  padding: 0;
  margin: 0 0 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.fc-cm__list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.fc-cm__row-label {
  font-size: 13px;
  color: var(--muted);
}
.fc-cm__row-value {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* Make .list rows tappable for cliente drill-down — adds a hover/active
   feedback that wasn't there for the gestoria portfolio click handler. */
#g-clients-list .tappable {
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
#g-clients-list .tappable:hover,
#g-clients-list .tappable:focus-visible {
  background: rgba(99, 91, 255, 0.08);
  outline: none;
}
#g-clients-list .tappable:active {
  transform: scale(0.99);
}

/* prefers-reduced-motion — disable shimmer + transitions */
@media (prefers-reduced-motion: reduce) {
  .chart-skel-bar { animation: none; opacity: 0.65; }
  .facturas-filters .filter-input,
  .facturas-filters .filter-select,
  #g-clients-list .tappable { transition: none; }
}

/* Light theme — Telegram users on light client get a softer surface.
   The selector :root[data-theme="light"] is added in addition to the
   prefers-color-scheme:light media query so a user who manually toggled
   via the #theme-toggle button (sets data-theme="light" + persists in
   localStorage) gets the same palette regardless of OS preference. */
:root[data-theme="light"] {
  /* 2026-05-18c — full editorial-cream parity with landing
     (public/assets/system.css [data-theme="light"]).
     Pure #ffffff clashed with #f1ece4; #faf6ef is the landing's --surf
     and produces a coherent cream stack across the whole site + mini app.
     Brand + state colours darkened so each pairing passes WCAG AA on cream. */

  /* Semantic state tokens — AA contrast on #f1ece4 */
  --state-success-bg: rgba(21, 128, 61, 0.10);
  --state-success-fg: #15803d;
  --state-success-border: rgba(21, 128, 61, 0.30);
  --state-warn-bg: rgba(180, 83, 9, 0.10);
  --state-warn-fg: #b45309;
  --state-warn-border: rgba(180, 83, 9, 0.30);
  --state-error-bg: rgba(185, 28, 28, 0.10);
  --state-error-fg: #b91c1c;
  --state-error-border: rgba(185, 28, 28, 0.30);
  --state-info-bg: rgba(79, 70, 229, 0.10);
  --state-info-fg: #4f46e5;
  --state-info-border: rgba(79, 70, 229, 0.30);
  --state-neutral-bg: rgba(10, 10, 10, 0.03);
  --state-neutral-fg: #595959;
  --state-neutral-border: rgba(10, 10, 10, 0.08);

  /* Surface + text — landing's editorial-cream system */
  --bg: #f1ece4;            /* warm cream page bg */
  --surface: #faf6ef;       /* card / panel surface — softer than white */
  --surface-2: #f1ece2;     /* alt surface for nested cards */
  --surface-3: #ebe4d8;     /* deepest surface for inset panels */
  --text: #0a0a0a;          /* near-black on cream — 16:1+ contrast */
  --text-2: #1a1a1a;        /* secondary text — still AAA */
  --text-muted: #4a4a4a;    /* tertiary text — 8.6:1 on cream */
  --muted: #595959;         /* muted labels — 6.4:1 on cream (AA normal text) */
  --muted-2: #6b7280;       /* faintest muted, decorative meta only */
  --border: rgba(10, 10, 10, 0.13);
  --border-2: rgba(10, 10, 10, 0.07);

  /* Brand — darker indigo for cream contrast (landing parity) */
  --indigo: #4f46e5;        /* 6.4:1 on #f1ece4 — AA for normal text */
  --indigo-d: #3f3aaf;
  --indigo-3: #4338ca;      /* deeper for emphasised hover / links */

  /* State accents — darker variants pinned to WCAG AA on cream */
  --green: #15803d;         /* 4.5:1 on #f1ece4 */
  --green-d: #166534;
  --red: #b91c1c;           /* 5.7:1 on #f1ece4 */
  --amber: #b45309;
  --yellow: #b45309;        /* alias — pure yellow is illegible on cream */
  --cyan: #0e7490;          /* darker teal — pure cyan washes out on cream */

  /* Chart.js theme tokens — read by app.js for canvas tints */
  --chart-tooltip-bg: rgba(250, 246, 239, 0.97);
  --chart-tooltip-fg: #0a0a0a;
  --chart-tooltip-border: rgba(79, 70, 229, 0.30);
  --chart-grid: rgba(10, 10, 10, 0.06);
}

/* Dark chart tokens — explicit data-theme="dark" attribute selector
   matches BOTH unset users (default dark) and explicit dark-mode users
   if we ever add the attr, while light-block specificity (0,1,1) beats
   it without needing source-order tricks. */
:root[data-theme="light"] body { background: var(--bg); color: var(--text); }
:root[data-theme="light"] .chart-monthly,
:root[data-theme="light"] .facturas-filters,
:root[data-theme="light"] .facturas-pagination {
  background: var(--surface);
  border-color: var(--border);
}
:root[data-theme="light"] .facturas-filters .filter-input,
:root[data-theme="light"] .facturas-filters .filter-select,
:root[data-theme="light"] .fc-cm__kpi,
:root[data-theme="light"] .fc-cm__list li {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text);
}
/* Filter pill focus state — was indigo over dark, needs lighter cream tint */
:root[data-theme="light"] .facturas-filters .filter-input:focus,
:root[data-theme="light"] .facturas-filters .filter-select:focus {
  background: rgba(79, 70, 229, 0.06);
  border-color: var(--indigo);
}
/* Date picker color-scheme hint (light keeps the system picker themed) */
:root[data-theme="light"] .facturas-filters .filter-input[type="date"] {
  color-scheme: light;
}
/* Filter-select chevron (built from two gradients of var(--muted) which
   already swaps via the cascade — no override needed). */

/* ─────────────────────────────────────────────────────────────
   2026-05-18c — comprehensive editorial-cream light-theme sweep.
   Every component that hardcoded rgba(10,15,26,...) navy or
   rgba(255,255,255,...) white tints gets a cream-palette inverse.
   Result: full theme-swap on toggle, not just Telegram chrome.
   ───────────────────────────────────────────────────────────── */

/* Top app bar — was rgba(10,15,26,0.92) frosted navy.
   On cream we use a translucent --surface so the blur still bites
   the editorial palette beneath. */
:root[data-theme="light"] .topbar {
  background: rgba(250, 246, 239, 0.88);
  border-bottom-color: var(--border);
  color: var(--text);
  box-shadow: 0 4px 16px rgba(10, 10, 10, 0.08);
}

/* Sub-tab strip beneath the topbar — its bg uses var(--bg)
   which now resolves to cream, but the border under it was set
   from var(--border) and re-renders correctly. Explicit re-bind
   here so authoring order changes don't regress. */
:root[data-theme="light"] .tabs {
  background: var(--bg);
  border-bottom-color: var(--border);
}
:root[data-theme="light"] .tab {
  color: var(--muted);
}
:root[data-theme="light"] .tab.active,
:root[data-theme="light"] .tab[aria-selected="true"] {
  color: var(--text);
  /* 2026-05-25 ROOT CAUSE FIX from debugger audit: this rule was reintroducing
     a cream `var(--surface-2)` background fill on the active tab in light mode,
     overriding the `background: transparent` set on .tab.active (line 354)
     because it sits later in the source and adds the [data-theme="light"]
     attribute selector. User saw a visible "resalta" flash on every tab click
     in light mode for THREE deploy cycles even though the base rule was correct.
     The .tab.active::after underline indicator (indigo, 2px) is now the
     single source of "this tab is selected" — no fill behind it. */
  background: transparent;
}
:root[data-theme="light"] .tab:hover,
:root[data-theme="light"] .tab:focus-visible {
  color: var(--text);
  /* 2026-05-25 Designer regression-fix: this rule was the "resalta" bug
     in light mode. The dark-theme .tab:hover at line 311 explicitly drops
     box-shadow + transform; this override at line 2530 re-introduced an
     indigo box-shadow halo on every click. The 2px underline indicator
     via .tab.active::after is now the single source of "this tab is
     selected" — no other lift, fill, or glow is needed. */
  box-shadow: none;
}

/* Skeleton container + line shimmer — was invisible (white-on-cream).
   Cream-on-cream shimmer with the landing's indigo tint instead. */
:root[data-theme="light"] .skeleton,
:root[data-theme="light"] .error {
  background: var(--bg);
  color: var(--text);
}
:root[data-theme="light"] .skeleton-line {
  background: var(--surface-2);
}
/* Shimmer block (.spinner) — re-tone the linear-gradient sweep + the
   two inner ::before/::after bars from white-tint to ink-tint */
:root[data-theme="light"] .spinner {
  background:
    linear-gradient(90deg,
      rgba(79, 70, 229, 0.06) 0%,
      rgba(79, 70, 229, 0.18) 45%,
      rgba(79, 70, 229, 0.06) 90%);
  background-size: 220% 100%;
  border-color: var(--border);
}
:root[data-theme="light"] .spinner::before,
:root[data-theme="light"] .spinner::after {
  background: rgba(10, 10, 10, 0.06);
}

/* Chart-monthly loading skeleton bars — were white-tint gradient */
:root[data-theme="light"] .chart-skel-bar {
  background: linear-gradient(180deg,
    rgba(10, 10, 10, 0.04) 0%,
    rgba(79, 70, 229, 0.10) 100%);
}

/* Card / panel surfaces that already use var(--surface) inherit the
   swap automatically. This rule covers panels built with hardcoded
   semi-transparent whites over the dark bg.

   2026-05-18d — add box-shadow elevation. --surface (#faf6ef) on --bg
   (#f1ece4) is only Δ≈9 RGB so cards looked washed-out against the page.
   A 1px shadow at 4% opacity restores edge definition without an extra
   border. */
:root[data-theme="light"] .card,
:root[data-theme="light"] .panel,
:root[data-theme="light"] .tile,
:root[data-theme="light"] .hero-tile,
:root[data-theme="light"] section.tab-panel > .card,
:root[data-theme="light"] section.tab-panel > .panel {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}

/* KPI cards — already use var(--surface)/var(--border); explicit
   re-bind covers any inline override that hardcoded the dark palette. */
:root[data-theme="light"] .kpi {
  background: var(--surface);
  border-color: var(--border);
  color: var(--text);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .kpi:hover {
  border-color: rgba(79, 70, 229, 0.30);
  box-shadow: 0 6px 18px rgba(79, 70, 229, 0.10);
}

/* Hero strip + hero cards — quarterly trimestre KPIs at the top of Home.
   Same elevation logic — without the shadow they merge into --bg. */
:root[data-theme="light"] .hero-strip {
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .hero-card {
  background: var(--surface-3);
  border-color: var(--border-2);
}
:root[data-theme="light"] .hero-card[data-state="success"] {
  background: var(--state-success-bg);
  border-color: var(--state-success-border);
}
:root[data-theme="light"] .hero-card[data-state="warn"] {
  background: var(--state-warn-bg);
  border-color: var(--state-warn-border);
}
:root[data-theme="light"] .hero-card[data-state="error"] {
  background: var(--state-error-bg);
  border-color: var(--state-error-border);
}
:root[data-theme="light"] .hero-card[data-state="info"] {
  background: var(--state-info-bg);
  border-color: var(--state-info-border);
}
:root[data-theme="light"] .kpi-label { color: var(--muted); }
:root[data-theme="light"] .kpi-value { color: var(--text); }

/* List rows + their hover/tap state — was rgba(99,91,255,0.08)
   indigo tint that works on cream too but darken slightly */
:root[data-theme="light"] .list li {
  background: var(--surface);
  border-color: var(--border);
  color: var(--text);
}
:root[data-theme="light"] .list li.tappable:hover,
:root[data-theme="light"] #g-clients-list .tappable:hover,
:root[data-theme="light"] #g-clients-list .tappable:focus-visible {
  background: rgba(79, 70, 229, 0.06);
}
:root[data-theme="light"] .list .meta { color: var(--muted); }

/* Tutorial overlay — frosted cream over content on first visit */
:root[data-theme="light"] .tutorial-overlay {
  background: rgba(250, 246, 239, 0.92);
  color: var(--text);
}
:root[data-theme="light"] .tutorial-card {
  background: var(--surface);
  border-color: var(--border);
  color: var(--text);
  box-shadow: 0 20px 48px rgba(10, 10, 10, 0.12);
}
:root[data-theme="light"] .tutorial-card p,
:root[data-theme="light"] .tutorial-skip {
  color: var(--muted);
}
:root[data-theme="light"] .tutorial-dot {
  background: var(--border);
}
:root[data-theme="light"] .tutorial-dot.active {
  background: var(--indigo);
}

/* Bottom navigation — sticky cream bar */
:root[data-theme="light"] .bottom-nav,
:root[data-theme="light"] nav.bottom-nav {
  background: rgba(250, 246, 239, 0.96);
  border-top: 1px solid var(--border);
  color: var(--text);
}
:root[data-theme="light"] .bottom-nav[data-theme-bg] {
  background: var(--bnav-bg, rgba(250, 246, 239, 0.96));
}
:root[data-theme="light"] .bottom-nav a,
:root[data-theme="light"] .bottom-nav button,
:root[data-theme="light"] .bnav-item {
  color: var(--muted);
}
:root[data-theme="light"] .bottom-nav .active,
:root[data-theme="light"] .bottom-nav [aria-current="page"],
:root[data-theme="light"] .bnav-item[aria-selected="true"] {
  color: var(--indigo);
}
:root[data-theme="light"] .bnav-item[aria-selected="true"] .bnav-icon {
  background: rgba(79, 70, 229, 0.12);
}

/* Sheet (modal) — backdrop + panel + handle + sub-elements */
:root[data-theme="light"] .fc-sheet__backdrop {
  background: rgba(10, 10, 10, 0.36);
}
:root[data-theme="light"] .fc-sheet__content,
:root[data-theme="light"] .fc-sheet__body,
:root[data-theme="light"] .fc-sheet,
:root[data-theme="light"] .fc-sheet__panel {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
}
:root[data-theme="light"] .fc-sheet__handle { background: var(--border); }
:root[data-theme="light"] .fc-sheet__sub,
:root[data-theme="light"] .fc-sheet__trust { color: var(--muted); }
:root[data-theme="light"] .fc-sheet__error {
  color: var(--state-error-fg);
  background: var(--state-error-bg);
  border-color: var(--state-error-border);
}

/* Plan / method tiles inside the sheet — were var(--bg) which is now
   cream so they merge into the panel. Switch to surface-2 so they
   stay visually separated from the surrounding sheet surface. */
:root[data-theme="light"] .fc-tile,
:root[data-theme="light"] .fc-method {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text);
}
:root[data-theme="light"] .fc-tile[aria-checked="true"] {
  border-color: var(--indigo);
  background: linear-gradient(135deg,
    rgba(79, 70, 229, 0.10) 0%,
    rgba(14, 116, 144, 0.06) 100%);
}
:root[data-theme="light"] .fc-tile-name { color: var(--muted); }
:root[data-theme="light"] .fc-tile[aria-checked="true"] .fc-tile-name {
  color: var(--indigo);
}
:root[data-theme="light"] .fc-tile-period { color: var(--muted); }
:root[data-theme="light"] .fc-method[aria-checked="true"] {
  border-color: var(--indigo);
  background: rgba(79, 70, 229, 0.08);
  color: var(--text);
}

/* Primary gradient CTA — drop the dark-only shadow so it doesn't
   feel out of place on cream. Keep the gradient itself (indigo→cyan)
   since both colours are still pleasant against #f1ece4. */
:root[data-theme="light"] .fc-cta {
  color: #faf6ef;
  box-shadow: 0 8px 24px rgba(79, 70, 229, 0.22);
}
:root[data-theme="light"] .fc-cta:hover,
:root[data-theme="light"] .fc-cta:focus-visible {
  box-shadow: 0 10px 30px rgba(79, 70, 229, 0.32);
}

/* Floating Action Button (+ Nueva factura) — indigo gradient on cream
   needs a softer drop shadow */
:root[data-theme="light"] .fab {
  color: #faf6ef;
  box-shadow: 0 6px 18px rgba(79, 70, 229, 0.24);
}

/* Buttons — primary uses var(--indigo)/var(--bg); on cream that
   produces indigo bg with cream text, which is fine. The .ghost
   variant uses var(--border) which is already cream-correct. */
:root[data-theme="light"] .btn.primary {
  color: #faf6ef;
}
:root[data-theme="light"] .btn.primary:hover,
:root[data-theme="light"] .btn.primary:active {
  background: var(--indigo-3);
}
:root[data-theme="light"] .btn.ghost {
  color: var(--indigo);
  border-color: var(--border);
}
:root[data-theme="light"] .btn.ghost:hover {
  background: rgba(79, 70, 229, 0.08);
}

/* User chip plan pill — uses var(--border) bg, which is cream-correct,
   but the .pro/.gestoria variant uses var(--bg) text on var(--indigo)
   which would put cream text on indigo — readable, but make it crisp. */
:root[data-theme="light"] .plan.pro,
:root[data-theme="light"] .plan.gestoria {
  color: #faf6ef;
}
:root[data-theme="light"] .plan.basic {
  background: rgba(14, 116, 144, 0.12);
  color: var(--cyan);
}

/* Offline banner — amber stays valid on both themes (status colour).
   But the white-translucent button overlays read oddly against the
   warmer cream behind, so darken them slightly. */
:root[data-theme="light"] .offline-banner-btn,
:root[data-theme="light"] .offline-banner-close {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.40);
}
:root[data-theme="light"] .offline-banner-btn:hover,
:root[data-theme="light"] .offline-banner-btn:focus-visible,
:root[data-theme="light"] .offline-banner-close:hover,
:root[data-theme="light"] .offline-banner-close:focus-visible {
  background: rgba(255, 255, 255, 0.34);
}

/* Quota banner + glimpse paywall + generic widgets */
:root[data-theme="light"] .fc-glimpse,
:root[data-theme="light"] .fc-quota-banner,
:root[data-theme="light"] .stat-card,
:root[data-theme="light"] .kpi-card,
:root[data-theme="light"] .insight-card,
:root[data-theme="light"] .widget {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
}
/* fc-quota-banner uses amber tint inline — re-tone with cream-correct amber */
:root[data-theme="light"] .fc-quota-banner {
  background: var(--state-warn-bg);
  border-bottom-color: var(--state-warn-border);
  color: var(--text);
}
:root[data-theme="light"] .fc-quota-msg strong { color: var(--state-warn-fg); }
:root[data-theme="light"] .fc-quota-cta {
  background: var(--indigo);
  color: #faf6ef;
}
:root[data-theme="light"] .fc-quota-cta:hover {
  box-shadow: 0 4px 10px rgba(79, 70, 229, 0.28);
}

/* Glimpse paywall overlay — was dark navy gradient. Re-cast as cream
   gradient so the blurred-data peek under it stays legible. */
:root[data-theme="light"] .fc-glimpse-overlay {
  background: linear-gradient(180deg,
    rgba(241, 236, 228, 0.2),
    rgba(241, 236, 228, 0.92) 70%);
  color: var(--text);
}
:root[data-theme="light"] .fc-glimpse-title { color: var(--text); }
:root[data-theme="light"] .fc-glimpse-body { color: var(--muted); }
:root[data-theme="light"] .fc-glimpse-cta {
  background: var(--indigo);
  color: #faf6ef;
}
:root[data-theme="light"] .fc-glimpse-cta:hover { background: var(--indigo-3); }

/* Plan-status strip (autonomo mode) + progress bar */
:root[data-theme="light"] .plan-status {
  background: var(--surface);
  border-color: var(--border);
}
:root[data-theme="light"] .ps-bar {
  background: var(--surface-3);
}
:root[data-theme="light"] .ps-upgrade-link {
  color: var(--indigo);
}
:root[data-theme="light"] .plan-ring-track {
  stroke: rgba(79, 70, 229, 0.22);
}

/* Achievements + streak badge — were tinted with white-on-dark; the
   indigo/cyan gradient still reads on cream but darken the border */
:root[data-theme="light"] .achievement {
  background: var(--surface);
  border-color: var(--border);
}
:root[data-theme="light"] .achievement.locked { color: var(--muted); }
:root[data-theme="light"] .achievement.unlocked {
  color: var(--text);
  background: linear-gradient(135deg,
    rgba(79, 70, 229, 0.14) 0%,
    rgba(14, 116, 144, 0.10) 100%);
  border-color: rgba(79, 70, 229, 0.30);
}
:root[data-theme="light"] .streak-badge {
  background: linear-gradient(135deg,
    rgba(180, 83, 9, 0.14) 0%,
    rgba(180, 83, 9, 0.08) 100%);
  border-color: rgba(180, 83, 9, 0.30);
  color: #b45309;
}

/* Inbox messages (msg-sent / msg-received) — re-cast from indigo/green
   white-tints to cream-darker tints */
:root[data-theme="light"] .list.messages li.msg {
  background: var(--surface);
  border-color: var(--border);
}
:root[data-theme="light"] .list.messages li.msg-sent {
  background: rgba(79, 70, 229, 0.06);
  border-color: rgba(79, 70, 229, 0.18);
}
:root[data-theme="light"] .list.messages li.msg-received {
  background: rgba(21, 128, 61, 0.06);
  border-color: rgba(21, 128, 61, 0.18);
}
:root[data-theme="light"] .msg-arrow,
:root[data-theme="light"] .msg-sent .msg-arrow { color: var(--indigo); }
:root[data-theme="light"] .msg-received .msg-arrow { color: var(--green); }

/* Action chips (gestoría) — indigo tint on cream */
:root[data-theme="light"] .action-chip {
  background: rgba(79, 70, 229, 0.08);
  border-color: rgba(79, 70, 229, 0.20);
  color: var(--indigo);
}
:root[data-theme="light"] .action-chip.overflow {
  background: var(--surface);
  border-color: var(--border);
  color: var(--muted);
}

/* AEAT deadline chips — cream variants */
:root[data-theme="light"] .deadline-chip {
  background: var(--surface);
  border-color: var(--border);
  color: var(--muted);
}
:root[data-theme="light"] .deadline-chip.green {
  color: var(--green);
  border-color: rgba(21, 128, 61, 0.30);
}
:root[data-theme="light"] .deadline-chip.yellow {
  color: var(--yellow);
  border-color: rgba(180, 83, 9, 0.30);
}
:root[data-theme="light"] .deadline-chip.red {
  color: var(--red);
  border-color: rgba(185, 28, 28, 0.30);
}

/* Badges — were rgba(0,229,132,…) lime-fluorescent. Cream needs darker. */
:root[data-theme="light"] .badge.ok {
  background: var(--state-success-bg);
  color: var(--state-success-fg);
}
:root[data-theme="light"] .badge.pending {
  background: var(--state-warn-bg);
  color: var(--state-warn-fg);
}
:root[data-theme="light"] .badge.err {
  background: var(--state-error-bg);
  color: var(--state-error-fg);
}

/* Semáforo pills — same logic as badges */
:root[data-theme="light"] .semaforo.verde {
  background: var(--state-success-bg);
  color: var(--state-success-fg);
}
:root[data-theme="light"] .semaforo.amarillo {
  background: var(--state-warn-bg);
  color: var(--state-warn-fg);
}
:root[data-theme="light"] .semaforo.rojo {
  background: var(--state-error-bg);
  color: var(--state-error-fg);
}

/* Gestoría drill-down modal error banner */
:root[data-theme="light"] .fc-cm__error {
  color: var(--state-error-fg);
  background: var(--state-error-bg);
}
/* Filter validation messages */
:root[data-theme="light"] .facturas-filters .filter-msg {
  color: var(--state-warn-fg);
  background: var(--state-warn-bg);
}
:root[data-theme="light"] .facturas-filters .filter-msg.filter-msg-error {
  color: var(--state-error-fg);
  background: var(--state-error-bg);
}

/* Inputs + selects + textareas — surface-2 contrasts subtly with the
   surrounding card surface so users see the input zones clearly. */
:root[data-theme="light"] input:not([type="checkbox"]):not([type="radio"]),
:root[data-theme="light"] select,
:root[data-theme="light"] textarea {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
}
:root[data-theme="light"] input::placeholder,
:root[data-theme="light"] textarea::placeholder {
  color: var(--muted);
}
:root[data-theme="light"] input:focus,
:root[data-theme="light"] select:focus,
:root[data-theme="light"] textarea:focus {
  border-color: var(--indigo);
  outline: none;
}

/* Embed-widget snippet box — already had a prefers-color-scheme dark
   override but no explicit theme attribute branch; rebuild for theme attr */
:root[data-theme="light"] .embed-snippet {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text);
}
:root[data-theme="light"] #embed-copy-feedback { color: var(--green); }

/* Tab buttons — active tab indicator + inactive labels (role=tab variant) */
:root[data-theme="light"] .tab-btn,
:root[data-theme="light"] [role="tab"] {
  color: var(--muted);
}
:root[data-theme="light"] .tab-btn[aria-selected="true"],
:root[data-theme="light"] [role="tab"][aria-selected="true"] {
  color: var(--text);
}

/* Links — keep brand indigo but ensure underline visibility */
:root[data-theme="light"] a:not(.btn):not(.tab-btn) {
  color: var(--indigo);
}

/* Hero strip (trimestre actual) + hero cards — neutral cards on cream */
:root[data-theme="light"] .hero-strip {
  background: var(--surface);
  border-color: var(--border);
}
:root[data-theme="light"] .hero-title,
:root[data-theme="light"] .hero-label,
:root[data-theme="light"] .hero-meta,
:root[data-theme="light"] .hero-delta {
  color: var(--muted);
}
:root[data-theme="light"] .hero-value { color: var(--text); }
:root[data-theme="light"] .hero-q {
  color: var(--indigo);
  background: var(--state-info-bg);
  border-color: var(--state-info-border);
}

/* Section heading colour */
:root[data-theme="light"] .section-h { color: var(--muted); }
:root[data-theme="light"] .muted { color: var(--muted); }
:root[data-theme="light"] .footer { border-top-color: var(--border); }

/* Theme toggle button itself — was rgba(255,255,255,0.08) border */
:root[data-theme="light"] .theme-toggle {
  border-color: var(--border);
  color: var(--muted);
}
:root[data-theme="light"] .theme-toggle:hover {
  color: var(--text);
  border-color: var(--indigo);
  background: color-mix(in oklab, var(--indigo) 8%, transparent);
}

@media (prefers-color-scheme: light) {
  /* OS-light users without an explicit [data-theme] override get the
     same editorial-cream palette as the :root[data-theme="light"] block.
     2026-05-18c — mirrors the landing's --bg/--surf/--txt/--mute. */
  :root:not([data-theme="dark"]) {
    --state-success-bg: rgba(21, 128, 61, 0.10);
    --state-success-fg: #15803d;
    --state-success-border: rgba(21, 128, 61, 0.30);
    --state-warn-bg: rgba(180, 83, 9, 0.10);
    --state-warn-fg: #b45309;
    --state-warn-border: rgba(180, 83, 9, 0.30);
    --state-error-bg: rgba(185, 28, 28, 0.10);
    --state-error-fg: #b91c1c;
    --state-error-border: rgba(185, 28, 28, 0.30);
    --state-info-bg: rgba(79, 70, 229, 0.10);
    --state-info-fg: #4f46e5;
    --state-info-border: rgba(79, 70, 229, 0.30);
    --state-neutral-bg: rgba(10, 10, 10, 0.03);
    --state-neutral-fg: #595959;
    --state-neutral-border: rgba(10, 10, 10, 0.08);
    /* Chart.js tokens — light theme for OS-light users */
    --chart-tooltip-bg: rgba(250, 246, 239, 0.97);
    --chart-tooltip-fg: #0a0a0a;
    --chart-tooltip-border: rgba(79, 70, 229, 0.30);
    --chart-grid: rgba(10, 10, 10, 0.06);
  }
  .chart-monthly,
  .facturas-filters,
  .facturas-pagination {
    background: rgba(250, 246, 239, 0.85);
    border-color: rgba(10, 10, 10, 0.13);
  }
  .facturas-filters .filter-input,
  .facturas-filters .filter-select,
  .fc-cm__kpi,
  .fc-cm__list li {
    background: rgba(241, 236, 226, 0.85);
    border-color: rgba(10, 10, 10, 0.10);
  }
}


/* ════════════════════════════════════════════════════════════════════
   Hero KPI strip — trimestre actual · top of Home tab
   UX Architect 2026-05-14 — top-5 competitor parity widget
   Uses semantic state tokens added in commit 8b565a88.
   ════════════════════════════════════════════════════════════════════ */
.hero-strip {
  margin: 0 0 var(--space-6, 24px);
  padding: var(--space-4, 16px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius, 14px);
  animation: fadein .35s var(--ease-out) both;
}
.hero-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: var(--space-3, 12px);
  gap: var(--space-3, 12px);
  /* Lift above the .hero-strip::after right-edge gradient fade overlay
     (line 3926+). Without this z-index the fade clips the Q badge visually. */
  position: relative;
  z-index: 1;
}
.hero-title {
  font-size: 0.875rem; font-weight: 600;
  color: var(--muted); letter-spacing: 0.02em; text-transform: uppercase;
  margin: 0;
}
.hero-q {
  font-size: 0.75rem; font-weight: 700; color: var(--indigo-3, #818cf8);
  background: var(--state-info-bg);
  border: 1px solid var(--state-info-border);
  padding: 2px 8px; border-radius: 100px;
  font-variant-numeric: tabular-nums;
}
.hero-row {
  display: grid; gap: var(--space-2, 8px);
  grid-template-columns: repeat(5, 1fr);
}
@media (max-width: 720px) {
  /* Hero strip: wrap in a relative container for the edge-fade overlay
     (Item 4, 2026-05-24-polish). */
  .hero-strip { position: relative; }
  .hero-row {
    display: flex; overflow-x: auto; scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch; gap: var(--space-2, 8px);
    /* USER REPORT 2026-05-27 (round 3): "en mitad de esas dos opciones".
       Geometry evolution:
         · Original: margin -16px + padding 16px → card edge at x=0
           (flush con strip border, user dijo "INGRESOS demasiado a la
           izquierda").
         · Round 2 (commit 11847511): drop margin/padding → card edge
           at x=16 (alineado con título, user dijo "in mitad").
         · Round 3 (este): half-way → card edge at x=8.
       Implementation: margin -8px on each side pulls the row 8px past
       the strip's content edge; padding 8px brings the card content
       8px back inward. Net effect: card edge sits 8px from strip's
       content boundary — half-way between original (0) and aligned-
       with-title (16). */
    margin: 0 calc(var(--space-2, 8px) * -1);
    padding: 0 var(--space-2, 8px) var(--space-2, 8px);
    /* Hide scrollbar visually but keep keyboard scrolling */
    scrollbar-width: none;
  }
  .hero-row::-webkit-scrollbar { display: none; }
  .hero-card {
    flex: 0 0 38vw; min-width: 130px; max-width: 180px;
    scroll-snap-align: start;
  }
  /* Right-edge gradient fade — signals hidden IVA/IRPF cards on mobile <720px */
  .hero-strip::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 40px;
    background: linear-gradient(to left, var(--surface) 0%, transparent 100%);
    pointer-events: none;
    border-radius: 0 var(--radius, 14px) var(--radius, 14px) 0;
  }
}
.hero-card {
  background: var(--state-neutral-bg);
  border: 1px solid var(--state-neutral-border);
  border-radius: var(--radius-sm, 10px);
  padding: var(--space-3, 12px) var(--space-3, 12px);
  display: flex; flex-direction: column; gap: 2px;
  min-height: 88px;
  transition: transform .15s var(--ease-out), border-color .15s var(--ease-out);
}
.hero-card[data-state="success"] {
  border-color: var(--state-success-border);
  background: var(--state-success-bg);
}
.hero-card[data-state="warn"] {
  border-color: var(--state-warn-border);
  background: var(--state-warn-bg);
}
.hero-card[data-state="error"] {
  border-color: var(--state-error-border);
  background: var(--state-error-bg);
}
.hero-card[data-state="info"] {
  border-color: var(--state-info-border);
  background: var(--state-info-bg);
}
.hero-label {
  font-size: 0.6875rem; color: var(--muted);
  font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase;
}
.hero-value {
  font-size: 1.25rem; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
  margin-top: 2px;
}
.hero-delta {
  font-size: 0.6875rem; color: var(--muted);
  font-variant-numeric: tabular-nums; min-height: 16px;
}
.hero-delta.up { color: var(--state-success-fg); }
.hero-delta.down { color: var(--state-error-fg); }
.hero-meta {
  font-size: 0.6875rem; color: var(--muted); min-height: 16px;
}
.hero-card[data-state="warn"] .hero-value { color: var(--state-warn-fg); }
.hero-card[data-state="error"] .hero-value { color: var(--state-error-fg); }
.hero-card[data-state="success"] .hero-value { color: var(--state-success-fg); }
.hero-card[data-state="info"] .hero-value { color: var(--state-info-fg); }
@media (hover: hover) {
  .hero-card:hover { transform: translateY(-1px); }
}

/* ════════════════════════════════════════════════════════════════════
   "Glimpse the value" paywall pattern · UX Architect 2026-05-14 #3
   Replace silent 403s with a blurred-data preview + contextual upsell.
   ════════════════════════════════════════════════════════════════════ */
.fc-glimpse {
  position: relative;
  border-radius: var(--radius, 14px);
  background: var(--surface);
  border: 1px solid var(--border);
  padding: var(--space-4, 16px);
  margin-bottom: var(--space-3, 12px);
  overflow: hidden;
}
.fc-glimpse-blur {
  filter: blur(6px);
  user-select: none;
  pointer-events: none;
  opacity: 0.55;
}
.fc-glimpse-overlay {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: var(--space-4, 16px);
  background: linear-gradient(180deg, rgba(10,15,26,0.2), rgba(10,15,26,0.85) 70%);
  backdrop-filter: blur(1px);
}
.fc-glimpse-badge {
  font-size: 0.6875rem; font-weight: 700; color: var(--state-info-fg);
  background: var(--state-info-bg); border: 1px solid var(--state-info-border);
  padding: 4px 10px; border-radius: 100px;
  letter-spacing: 0.04em; text-transform: uppercase;
  margin-bottom: var(--space-2, 8px);
}
.fc-glimpse-title {
  font-size: 1.0625rem; font-weight: 700; color: var(--text);
  text-align: center; margin-bottom: var(--space-1, 4px);
}
.fc-glimpse-body {
  font-size: 0.875rem; color: var(--muted);
  text-align: center; line-height: 1.5;
  max-width: 32ch; margin-bottom: var(--space-3, 12px);
}
.fc-glimpse-cta {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--indigo, #4f46e5); color: #fff; text-decoration: none;
  padding: 10px 18px; border-radius: 8px;
  font-size: 0.875rem; font-weight: 600;
  min-height: 44px; min-width: 44px;
  border: none; cursor: pointer; font-family: inherit;
  transition: background .15s var(--ease-out);
}
.fc-glimpse-cta:hover { background: #4f46e5; }
.fc-glimpse-cta:focus-visible {
  outline: 2px solid var(--indigo-3, #818cf8); outline-offset: 2px;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .fc-glimpse-overlay {
    background: linear-gradient(180deg, rgba(241,236,228,0.2), rgba(241,236,228,0.92) 70%);
  }
}

/* ── Inline confirmation banner (fc-inline-banner) ──────────────
   Non-blocking toast injected from app.js when the dashboard
   triggers a background action (e.g. "Crear factura"). Sits at
   the top center so it never overlaps the bottom-nav or the FAB.
   Auto-removes after a few seconds; respects reduced-motion. */
.fc-inline-banner {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 56px);
  left: 50%;
  transform: translate(-50%, -20px);
  z-index: 9999;
  padding: 12px 18px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  max-width: calc(100vw - 32px);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35), 0 2px 8px rgba(0, 0, 0, 0.18);
  opacity: 0;
  pointer-events: none;
  transition: opacity 280ms ease, transform 280ms ease;
}
.fc-inline-banner.is-visible {
  opacity: 1;
  transform: translate(-50%, 0);
}
.fc-inline-banner--success {
  background: linear-gradient(135deg, #15803d 0%, #166534 100%);
  color: #ffffff;
}
.fc-inline-banner--error {
  background: linear-gradient(135deg, #b91c1c 0%, #991b1b 100%);
  color: #ffffff;
}
.fc-inline-banner--info {
  background: linear-gradient(135deg, #4f46e5 0%, #4338ca 100%);
  color: #ffffff;
}
@media (prefers-reduced-motion: reduce) {
  .fc-inline-banner { transition: none; }
}

/* ════════════════════════════════════════════════════════════════════
   2026-05-18d — Light-theme overrides for index.html scoped widgets.

   Every dashboard widget below is declared inside its own <style>
   block in index.html and was authored against the dark palette only.
   That left them washed-out / illegible when the user toggled light:
     • text colours fell back to `var(--text,#fff)` (white on cream)
     • inner cells used `rgba(0,0,0,.18-.22)` (dirty murky boxes)
     • SVG ring tracks used `rgba(255,255,255,.08)` (invisible)
     • outer cards used `rgba(99,91,255,.10)` over the dark navy
       which collapsed to near-zero contrast over the cream --bg.

   This block rebinds every offender to the editorial-cream tokens
   defined in :root[data-theme="light"]. Each widget gets:
     • surface elevated to var(--surface) with a stronger 1px border
       (so it visually separates from var(--bg) #f1ece4)
     • nested cells (.sf-dim, .crb-dist-cell, .gf-flag, .is-sev,
       .tc-item, .fv2-ci, .sf-action, .crb-item, .is-fix) bumped to
       var(--surface-3) #ebe4d8 so they keep visual depth
     • text rebinds to var(--text) / var(--text-2) / var(--muted)
     • brand eyebrows / accents to var(--indigo) #4f46e5 (AA on cream)
     • status fills to the cream-darker accent vars
       (--green #15803d, --amber #b45309, --red #b91c1c)
     • dashed dividers darkened from rgba(255,255,255,.06) to
       rgba(10,10,10,.10)

   Order matters less here because we use class-specificity, not !important.
   Each rule mirrors a single rule in the scoped <style> so future edits
   stay traceable.
   ════════════════════════════════════════════════════════════════════ */

/* ── Salud Fiscal box ─────────────────────────────────────── */
:root[data-theme="light"] .salud-fiscal-box {
  background: var(--surface);
  border-color: var(--border);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .salud-fiscal-box[data-tag="sano"] {
  background: linear-gradient(135deg, rgba(21, 128, 61, 0.06), var(--surface));
  border-color: rgba(21, 128, 61, 0.32);
}
:root[data-theme="light"] .salud-fiscal-box[data-tag="atencion"] {
  background: linear-gradient(135deg, rgba(180, 83, 9, 0.07), var(--surface));
  border-color: rgba(180, 83, 9, 0.32);
}
:root[data-theme="light"] .salud-fiscal-box[data-tag="riesgo"] {
  background: linear-gradient(135deg, rgba(185, 28, 28, 0.08), var(--surface));
  border-color: rgba(185, 28, 28, 0.36);
}
:root[data-theme="light"] .sf-eyebrow {
  color: var(--indigo);
}
:root[data-theme="light"] .sf-title { color: var(--text); }
:root[data-theme="light"] .sf-ring-track {
  stroke: rgba(10, 10, 10, 0.10);
}
:root[data-theme="light"] .salud-fiscal-box[data-tag="sano"] .sf-ring-fill { stroke: var(--green); }
:root[data-theme="light"] .salud-fiscal-box[data-tag="atencion"] .sf-ring-fill { stroke: var(--amber); }
:root[data-theme="light"] .salud-fiscal-box[data-tag="riesgo"] .sf-ring-fill { stroke: var(--red); }
:root[data-theme="light"] .sf-ring-num { color: var(--text); }
:root[data-theme="light"] .sf-ring-num small { color: var(--muted); }
:root[data-theme="light"] .salud-fiscal-box[data-tag="sano"] .sf-tag {
  background: var(--state-success-bg); color: var(--state-success-fg);
}
:root[data-theme="light"] .salud-fiscal-box[data-tag="atencion"] .sf-tag {
  background: var(--state-warn-bg); color: var(--state-warn-fg);
}
:root[data-theme="light"] .salud-fiscal-box[data-tag="riesgo"] .sf-tag {
  background: var(--state-error-bg); color: var(--state-error-fg);
}
:root[data-theme="light"] .sf-trend { color: var(--text-2); }
:root[data-theme="light"] .sf-trend-up { color: var(--green); }
:root[data-theme="light"] .sf-trend-down { color: var(--red); }
:root[data-theme="light"] .sf-trend-stable { color: var(--muted); }
:root[data-theme="light"] .sf-dim {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
}
:root[data-theme="light"] .sf-dim-name { color: var(--muted); }
:root[data-theme="light"] .sf-dim-score { color: var(--text); }
:root[data-theme="light"] .sf-actions-h { color: var(--muted); }
:root[data-theme="light"] .sf-action {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
  border-left: 3px solid transparent;
}
:root[data-theme="light"] .sf-action:hover,
:root[data-theme="light"] .sf-action:focus-visible {
  background: var(--surface-2);
}
:root[data-theme="light"] .sf-action[data-severity="high"] { border-left-color: var(--red); }
:root[data-theme="light"] .sf-action[data-severity="medium"] { border-left-color: var(--amber); }
:root[data-theme="light"] .sf-action[data-severity="low"] { border-left-color: var(--green); }
:root[data-theme="light"] .sf-action-title { color: var(--text); }
:root[data-theme="light"] .sf-action-uplift { color: var(--green); }
:root[data-theme="light"] .sf-action-desc { color: var(--text-2); }
:root[data-theme="light"] .sf-foot {
  color: var(--muted);
  border-top-color: rgba(10, 10, 10, 0.10);
}

/* ── Forecast V2 (Oracle) box ─────────────────────────────── */
:root[data-theme="light"] .forecast-v2-box {
  background: linear-gradient(135deg, rgba(14, 116, 144, 0.05), var(--surface));
  border-color: rgba(79, 70, 229, 0.22);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .fv2-eyebrow { color: var(--indigo); }
:root[data-theme="light"] .fv2-title { color: var(--text); }
:root[data-theme="light"] .fv2-row { color: var(--text-2); }
:root[data-theme="light"] .fv2-row strong { color: var(--text); }
:root[data-theme="light"] .fv2-row.point strong { color: var(--indigo); }
:root[data-theme="light"] .fv2-ci {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
  color: var(--muted);
}
:root[data-theme="light"] .fv2-warn { color: var(--amber); }
:root[data-theme="light"] .fv2-ded {
  border-top-color: rgba(10, 10, 10, 0.10);
  color: var(--text-2);
}
:root[data-theme="light"] .fv2-ded strong { color: var(--green); }

/* ── Guardian audit box ───────────────────────────────────── */
:root[data-theme="light"] .guardian-box {
  background: var(--surface);
  border-color: var(--border);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .guardian-box[data-risk="bajo"] {
  background: linear-gradient(135deg, rgba(21, 128, 61, 0.06), var(--surface));
  border-color: rgba(21, 128, 61, 0.32);
}
:root[data-theme="light"] .guardian-box[data-risk="medio"] {
  background: linear-gradient(135deg, rgba(180, 83, 9, 0.07), var(--surface));
  border-color: rgba(180, 83, 9, 0.32);
}
:root[data-theme="light"] .guardian-box[data-risk="alto"] {
  background: linear-gradient(135deg, rgba(185, 28, 28, 0.08), var(--surface));
  border-color: rgba(185, 28, 28, 0.36);
}
:root[data-theme="light"] .gf-eyebrow { color: var(--indigo); }
:root[data-theme="light"] .gf-title { color: var(--text); }
:root[data-theme="light"] .gf-risk-bar {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
}
:root[data-theme="light"] .guardian-box[data-risk="bajo"] .gf-risk-fill {
  background: linear-gradient(90deg, var(--green), var(--green-d, #166534));
}
:root[data-theme="light"] .guardian-box[data-risk="medio"] .gf-risk-fill {
  background: linear-gradient(90deg, var(--amber), #92400e);
}
:root[data-theme="light"] .guardian-box[data-risk="alto"] .gf-risk-fill {
  background: linear-gradient(90deg, var(--red), #991b1b);
}
:root[data-theme="light"] .gf-risk-num { color: var(--text); }
:root[data-theme="light"] .guardian-box[data-risk="bajo"] .gf-risk-tag {
  background: var(--state-success-bg); color: var(--state-success-fg);
}
:root[data-theme="light"] .guardian-box[data-risk="medio"] .gf-risk-tag {
  background: var(--state-warn-bg); color: var(--state-warn-fg);
}
:root[data-theme="light"] .guardian-box[data-risk="alto"] .gf-risk-tag {
  background: var(--state-error-bg); color: var(--state-error-fg);
}
:root[data-theme="light"] .gf-summary { color: var(--muted); }
:root[data-theme="light"] .gf-flag {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
  border-left: 3px solid transparent;
}
:root[data-theme="light"] .gf-flag[data-severity="critical"],
:root[data-theme="light"] .gf-flag[data-severity="high"] { border-left-color: var(--red); }
:root[data-theme="light"] .gf-flag[data-severity="medium"] { border-left-color: var(--amber); }
:root[data-theme="light"] .gf-flag[data-severity="low"] { border-left-color: var(--green); }
:root[data-theme="light"] .gf-flag-title { color: var(--text); }
:root[data-theme="light"] .gf-flag-detail { color: var(--muted); }
:root[data-theme="light"] .gf-btn {
  background: transparent;
  border-color: rgba(79, 70, 229, 0.32);
  color: var(--indigo);
}
:root[data-theme="light"] .gf-btn:hover,
:root[data-theme="light"] .gf-btn:focus-visible {
  background: rgba(79, 70, 229, 0.08);
}
:root[data-theme="light"] .gf-btn.primary {
  background: rgba(79, 70, 229, 0.10);
  border-color: rgba(79, 70, 229, 0.45);
  color: var(--indigo);
}
:root[data-theme="light"] .gf-empty { color: var(--green); }
:root[data-theme="light"] .gf-foot {
  color: var(--muted);
  border-top-color: rgba(10, 10, 10, 0.10);
}

/* ── Cliente Risk box ─────────────────────────────────────── */
:root[data-theme="light"] .cliente-risk-box {
  background: var(--surface);
  border-color: var(--border);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .cliente-risk-box[data-tag="excelente"] {
  background: linear-gradient(135deg, rgba(21, 128, 61, 0.06), var(--surface));
  border-color: rgba(21, 128, 61, 0.32);
}
:root[data-theme="light"] .cliente-risk-box[data-tag="medio"] {
  background: linear-gradient(135deg, rgba(180, 83, 9, 0.06), var(--surface));
  border-color: rgba(180, 83, 9, 0.28);
}
:root[data-theme="light"] .cliente-risk-box[data-tag="malo"] {
  background: linear-gradient(135deg, rgba(180, 83, 9, 0.08), var(--surface));
  border-color: rgba(180, 83, 9, 0.36);
}
:root[data-theme="light"] .cliente-risk-box[data-tag="critico"] {
  background: linear-gradient(135deg, rgba(185, 28, 28, 0.08), var(--surface));
  border-color: rgba(185, 28, 28, 0.36);
}
:root[data-theme="light"] .crb-eyebrow { color: var(--indigo); }
:root[data-theme="light"] .crb-title { color: var(--text); }
:root[data-theme="light"] .crb-dist-cell {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
}
:root[data-theme="light"] .crb-dist-cell[data-tag="excelente"] { border-left: 3px solid var(--green); }
:root[data-theme="light"] .crb-dist-cell[data-tag="bueno"] { border-left: 3px solid var(--green); }
:root[data-theme="light"] .crb-dist-cell[data-tag="medio"] { border-left: 3px solid var(--amber); }
:root[data-theme="light"] .crb-dist-cell[data-tag="malo"] { border-left: 3px solid var(--amber); }
:root[data-theme="light"] .crb-dist-cell[data-tag="critico"] { border-left: 3px solid var(--red); }
:root[data-theme="light"] .crb-dist-name { color: var(--muted); }
:root[data-theme="light"] .crb-dist-num { color: var(--text); }
:root[data-theme="light"] .crb-section-h { color: var(--muted); }
:root[data-theme="light"] .crb-item {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
  border-left: 3px solid transparent;
}
:root[data-theme="light"] .crb-item[data-tag="excelente"] { border-left-color: var(--green); }
:root[data-theme="light"] .crb-item[data-tag="bueno"] { border-left-color: var(--green); }
:root[data-theme="light"] .crb-item[data-tag="medio"] { border-left-color: var(--amber); }
:root[data-theme="light"] .crb-item[data-tag="malo"] { border-left-color: var(--amber); }
:root[data-theme="light"] .crb-item[data-tag="critico"] { border-left-color: var(--red); }
:root[data-theme="light"] .crb-item-name { color: var(--text); }
:root[data-theme="light"] .crb-item[data-tag="excelente"] .crb-item-score { color: var(--green); }
:root[data-theme="light"] .crb-item[data-tag="bueno"] .crb-item-score { color: var(--green); }
:root[data-theme="light"] .crb-item[data-tag="medio"] .crb-item-score { color: var(--amber); }
:root[data-theme="light"] .crb-item[data-tag="malo"] .crb-item-score { color: var(--amber); }
:root[data-theme="light"] .crb-item[data-tag="critico"] .crb-item-score { color: var(--red); }
:root[data-theme="light"] .crb-item-meta { color: var(--text-2); }
:root[data-theme="light"] .crb-foot {
  color: var(--muted);
  border-top-color: rgba(10, 10, 10, 0.10);
}

/* ── Inspection Simulator box ─────────────────────────────── */
:root[data-theme="light"] .inspection-box {
  background: var(--surface);
  border-color: var(--border);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .inspection-box[data-bench="mucho_mejor"] {
  background: linear-gradient(135deg, rgba(21, 128, 61, 0.08), var(--surface));
  border-color: rgba(21, 128, 61, 0.36);
}
:root[data-theme="light"] .inspection-box[data-bench="mejor"] {
  background: linear-gradient(135deg, rgba(21, 128, 61, 0.06), var(--surface));
  border-color: rgba(21, 128, 61, 0.30);
}
:root[data-theme="light"] .inspection-box[data-bench="similar"] {
  background: linear-gradient(135deg, rgba(79, 70, 229, 0.06), var(--surface));
  border-color: rgba(79, 70, 229, 0.22);
}
:root[data-theme="light"] .inspection-box[data-bench="peor"] {
  background: linear-gradient(135deg, rgba(180, 83, 9, 0.06), var(--surface));
  border-color: rgba(180, 83, 9, 0.32);
}
:root[data-theme="light"] .inspection-box[data-bench="mucho_peor"] {
  background: linear-gradient(135deg, rgba(185, 28, 28, 0.08), var(--surface));
  border-color: rgba(185, 28, 28, 0.36);
}
:root[data-theme="light"] .is-eyebrow { color: var(--indigo); }
:root[data-theme="light"] .is-title { color: var(--text); }
:root[data-theme="light"] .is-gauge-track { stroke: rgba(10, 10, 10, 0.10); }
:root[data-theme="light"] .inspection-box[data-bench="mucho_mejor"] .is-gauge-fill { stroke: var(--green); }
:root[data-theme="light"] .inspection-box[data-bench="mejor"] .is-gauge-fill { stroke: var(--green); }
:root[data-theme="light"] .inspection-box[data-bench="similar"] .is-gauge-fill { stroke: var(--indigo); }
:root[data-theme="light"] .inspection-box[data-bench="peor"] .is-gauge-fill { stroke: var(--amber); }
:root[data-theme="light"] .inspection-box[data-bench="mucho_peor"] .is-gauge-fill { stroke: var(--red); }
:root[data-theme="light"] .is-gauge-num { color: var(--text); }
:root[data-theme="light"] .is-gauge-num small { color: var(--muted); }
:root[data-theme="light"] .inspection-box[data-bench="mucho_mejor"] .is-bench {
  background: var(--state-success-bg); color: var(--state-success-fg);
}
:root[data-theme="light"] .inspection-box[data-bench="mejor"] .is-bench {
  background: var(--state-success-bg); color: var(--state-success-fg);
}
:root[data-theme="light"] .inspection-box[data-bench="similar"] .is-bench {
  background: var(--state-info-bg); color: var(--state-info-fg);
}
:root[data-theme="light"] .inspection-box[data-bench="peor"] .is-bench {
  background: var(--state-warn-bg); color: var(--state-warn-fg);
}
:root[data-theme="light"] .inspection-box[data-bench="mucho_peor"] .is-bench {
  background: var(--state-error-bg); color: var(--state-error-fg);
}
:root[data-theme="light"] .is-likelihood { color: var(--text-2); }
:root[data-theme="light"] .is-sev {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
}
:root[data-theme="light"] .is-sev[data-sev="critica"] { border-left: 3px solid var(--red); }
:root[data-theme="light"] .is-sev[data-sev="alta"] { border-left: 3px solid var(--amber); }
:root[data-theme="light"] .is-sev[data-sev="media"] { border-left: 3px solid var(--indigo); }
:root[data-theme="light"] .is-sev[data-sev="baja"] { border-left: 3px solid var(--green); }
:root[data-theme="light"] .is-sev-name { color: var(--muted); }
:root[data-theme="light"] .is-sev-num { color: var(--text); }
:root[data-theme="light"] .is-sancion {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
}
:root[data-theme="light"] .is-sancion-label { color: var(--muted); }
:root[data-theme="light"] .is-sancion-amount { color: var(--text); }
:root[data-theme="light"] .is-fixes-h { color: var(--muted); }
:root[data-theme="light"] .is-fix {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
  border-left: 3px solid transparent;
}
:root[data-theme="light"] .is-fix:hover,
:root[data-theme="light"] .is-fix:focus-visible {
  background: var(--surface-2);
}
:root[data-theme="light"] .is-fix[data-severity="critica"] { border-left-color: var(--red); }
:root[data-theme="light"] .is-fix[data-severity="alta"] { border-left-color: var(--amber); }
:root[data-theme="light"] .is-fix[data-severity="media"] { border-left-color: var(--indigo); }
:root[data-theme="light"] .is-fix[data-severity="baja"] { border-left-color: var(--green); }
:root[data-theme="light"] .is-fix-title { color: var(--text); }
:root[data-theme="light"] .is-fix-citation { color: var(--muted); }
:root[data-theme="light"] .is-fix-desc { color: var(--text-2); }
:root[data-theme="light"] .is-foot {
  color: var(--muted);
  border-top-color: rgba(10, 10, 10, 0.10);
}

/* ── Top clientes summary card ────────────────────────────── */
:root[data-theme="light"] .top-clientes-summary {
  background: linear-gradient(135deg, rgba(14, 116, 144, 0.05), var(--surface));
  border-color: rgba(79, 70, 229, 0.22);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .tc-eyebrow { color: var(--indigo); }
:root[data-theme="light"] .tc-title { color: var(--text); }
:root[data-theme="light"] .tc-item {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
}
:root[data-theme="light"] .tc-item:hover {
  background: var(--surface-2);
}
:root[data-theme="light"] .tc-rank {
  background: rgba(79, 70, 229, 0.15);
  color: var(--indigo);
}
:root[data-theme="light"] .tc-name { color: var(--text); }
:root[data-theme="light"] .tc-spark { color: rgba(79, 70, 229, 0.55); }
:root[data-theme="light"] .tc-amount { color: var(--text); }
:root[data-theme="light"] .tc-link {
  background: transparent;
  border-color: rgba(79, 70, 229, 0.32);
  color: var(--indigo);
}
:root[data-theme="light"] .tc-link:hover,
:root[data-theme="light"] .tc-link:focus-visible {
  background: rgba(79, 70, 229, 0.10);
}

/* ── Sprint mode / Cobros coach / Régimen advisor placeholder cards */
:root[data-theme="light"] .sprint-mode-box {
  background: linear-gradient(135deg, rgba(180, 83, 9, 0.07), var(--surface));
  border-color: rgba(180, 83, 9, 0.32);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .cobros-coach-box {
  background: linear-gradient(135deg, rgba(185, 28, 28, 0.07), var(--surface));
  border-color: rgba(185, 28, 28, 0.32);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .regimen-advisor-box {
  background: linear-gradient(135deg, rgba(21, 128, 61, 0.06), var(--surface));
  border-color: rgba(21, 128, 61, 0.32);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .sm-eyebrow,
:root[data-theme="light"] .cc-eyebrow,
:root[data-theme="light"] .ra-eyebrow { color: var(--amber); }
:root[data-theme="light"] .cc-eyebrow { color: var(--red); }
:root[data-theme="light"] .ra-eyebrow { color: var(--green); }
:root[data-theme="light"] .sm-title,
:root[data-theme="light"] .cc-title,
:root[data-theme="light"] .ra-title { color: var(--text); }
:root[data-theme="light"] .sm-days { color: var(--amber); }
:root[data-theme="light"] .ra-savings { color: var(--green); }

/* ── Tier-3 accordion + dividers + tier-1 alarm card halo ──── */
:root[data-theme="light"] .pred-tier-empty,
:root[data-theme="light"] .pred-tier1-empty {
  background: rgba(21, 128, 61, 0.08);
  border-color: rgba(21, 128, 61, 0.32);
  color: var(--green);
}
:root[data-theme="light"] .pred-tier-1 .pred-tier1-card[data-active="true"] {
  border-left: 3px solid var(--amber);
  box-shadow: 0 0 0 1px rgba(180, 83, 9, 0.18),
              0 4px 12px rgba(180, 83, 9, 0.10);
}
:root[data-theme="light"] .tier-divider {
  background: linear-gradient(90deg, transparent, var(--border), transparent);
}
:root[data-theme="light"] .accordion-tier3 {
  background: var(--surface);
  border-color: var(--border);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .accordion-trigger {
  color: var(--text);
}
:root[data-theme="light"] .accordion-trigger:hover {
  background: rgba(79, 70, 229, 0.06);
}
:root[data-theme="light"] .accordion-trigger-meta,
:root[data-theme="light"] .accordion-chevron { color: var(--muted); }
:root[data-theme="light"] .accordion-content {
  border-top-color: var(--border);
}

/* ── Predicción legacy box stronger surface boundary on cream ── */
:root[data-theme="light"] .pred-box {
  background: var(--surface);
  border-color: var(--border);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .pred-box hr { border-top-color: var(--border); }
:root[data-theme="light"] .pred-row strong { color: var(--text); }
/* .pred-row.highlight strong already uses var(--indigo-3) which is #4338ca
   in light mode — no override needed. */

/* ── Semáforo row + chip in pred-box ───────────────────────── */
:root[data-theme="light"] .semaforo-row { color: var(--muted); }

/* ── IRPF simulator (read-only display in tier-3) ──────────── */
:root[data-theme="light"] .irpf-sim {
  background: var(--surface);
  border-color: var(--border);
}
:root[data-theme="light"] .irpf-ded-value { color: var(--indigo); }

/* ════════════════════════════════════════════════════════════════════
   2026-05-18e — Gestoría light-theme parity sweep.

   The gestoría operating mode (`active_mode == "gestoria"`) is the
   accountancy-firm dashboard layered on top of the same Mini App
   chrome. It surfaces a different KPI set, different widgets, and
   different data structures than the autónomo dashboard. The 2026-05-18d
   sweep covered every autónomo-side widget but left gestoría components
   (cartera, clientesg, embed-widget, cliente drill-down modal) using
   raw dark-mode colours that washed out against the editorial-cream bg.

   This block rebinds every gestoría-specific scoped style to the
   editorial-cream tokens so the gestoría dashboard matches the autónomo
   polish bar:
     • Cartera tab — health-chip pills (verde/amarillo/rojo), capacity
       bar, KPI grid (clients/invoices/IVA/AEAT countdown)
     • Clientesg tab — cliente list rows with health-dot prefix +
       action-chip overlays (pending_actions)
     • Cliente drill-down modal (.fc-cm__*) — nested cards bumped from
       var(--surface-2) to var(--surface-3) for proper visual depth
       against the var(--surface) modal panel
     • Embed widget tab — snippet box, copy button feedback, KPI grid,
       7-day trend bars (Tier 5 #12 gestoria_enterprise feature)
     • g-action-summary chip strip ("🔴 N clientes · ⏰ N inactivos…")
     • #embed-trend bar widget — bars use var(--indigo) which already
       resolves correctly via the cascade

   Same pattern as autónomo block above: surface elevation via box-shadow,
   text colours rebound to var(--text)/var(--text-2)/var(--muted),
   status tints darkened to cream-correct accent vars (--green #15803d,
   --amber #b45309, --red #b91c1c). AA contrast verified on #f1ece4 bg.
   ════════════════════════════════════════════════════════════════════ */

/* ── Cartera tab KPI grid ─────────────────────────────────────
   Already covered by the generic .kpi overrides above (surface +
   border + box-shadow). No additional rules needed because the
   gestoría #g-clients / #g-invoices / #g-iva / #g-aeat values live
   inside .kpi-value which auto-binds to var(--text). */

/* ── Salud de cartera chips (g-green / g-yellow / g-red) ──────
   The .health-chip default has background: var(--surface) so it
   already inherits the cream surface. We add box-shadow elevation
   to match the .kpi treatment and darken the state colours.
   .verde/.amarillo/.rojo modifiers already reference var(--green/
   yellow/red) which are remapped to cream-AA values upstream. */
:root[data-theme="light"] .health-chip {
  background: var(--surface);
  border-color: var(--border);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] .health-chip.verde {
  background: var(--state-success-bg);
  border-color: var(--state-success-border);
  color: var(--state-success-fg);
}
:root[data-theme="light"] .health-chip.amarillo {
  background: var(--state-warn-bg);
  border-color: var(--state-warn-border);
  color: var(--state-warn-fg);
}
:root[data-theme="light"] .health-chip.rojo {
  background: var(--state-error-bg);
  border-color: var(--state-error-border);
  color: var(--state-error-fg);
}

/* ── Capacity bar (plan quota) ───────────────────────────────
   .capacity uses var(--surface); the fill gradient uses var(--cyan)
   and var(--indigo) which already resolve to cream-correct values
   (#0e7490 and #4f46e5). We just need to firm up the track on cream
   so the empty portion of the bar is visible. */
:root[data-theme="light"] .capacity {
  background: var(--surface-3);
  border-color: var(--border-2);
}
:root[data-theme="light"] .capacity-fill {
  /* Override the cyan→indigo gradient to use the cream-correct values
     directly — keeps the visual signature while pinning to AA tones. */
  background: linear-gradient(90deg, var(--cyan), var(--indigo));
}

/* ── Clientesg tab — action summary strip ───────────────────
   "🔴 N clientes · ⏰ N inactivos · 💸 N impagadas" — this is a
   bare text strip styled by .muted .small classes (already theme-
   aware via the muted var). Wrap in a small surface chip so it
   stops disappearing into the page bg on cream. */
:root[data-theme="light"] .g-action-summary {
  color: var(--text-2);
}
:root[data-theme="light"] .g-action-summary:not(:empty) {
  padding: 8px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 1px 2px rgba(10, 10, 10, 0.03);
}

/* ── Clientesg list rows ─────────────────────────────────────
   .list li override already lands surface + border + text colour.
   The health-dot uses var(--green/yellow/red) which are AA-correct
   on cream. The action-chip overrides already exist (line 2604-2613).
   Hover state for #g-clients-list .tappable already exists (2343-2346).
   Only addition: a subtle elevation so the rows separate from --bg. */
:root[data-theme="light"] #g-clients-list li {
  box-shadow: 0 1px 2px rgba(10, 10, 10, 0.03);
}
:root[data-theme="light"] #g-clients-list .tappable {
  /* Sharpen the indigo accent on focus so accountants navigating
     by keyboard see exactly which client is selected. */
  transition: background var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
:root[data-theme="light"] #g-clients-list .tappable:hover,
:root[data-theme="light"] #g-clients-list .tappable:focus-visible {
  box-shadow: 0 4px 12px rgba(79, 70, 229, 0.10);
  border-color: rgba(79, 70, 229, 0.32);
}

/* ── Cliente drill-down modal (.fc-cm__*) ────────────────────
   The modal panel is var(--surface) #faf6ef (set via .fc-sheet__panel
   override at 2402). Nested KPI cards and list rows were already
   bumped to var(--surface-2) at line 2186 — but --surface-2 (#f1ece2)
   differs from --surface (#faf6ef) by only ~4 RGB points and visually
   merges. Bump those nested cards to var(--surface-3) (#ebe4d8) for
   the same nested-depth pattern used by the autónomo widgets. */
:root[data-theme="light"] .fc-cm__kpi,
:root[data-theme="light"] .fc-cm__list li {
  background: var(--surface-3);
  border: 1px solid var(--border-2);
  color: var(--text);
}
/* Modal panel itself — elevate above the backdrop and ensure the
   internal scroll edge has a faint shadow so users sense the depth. */
:root[data-theme="light"] .fc-cm__panel {
  box-shadow: 0 -10px 40px rgba(10, 10, 10, 0.12),
              0 0 0 1px var(--border);
}
:root[data-theme="light"] .fc-cm__header {
  border-bottom: 1px solid var(--border-2);
  padding-bottom: 12px;
}
/* Title + meta — explicitly rebind so they never inherit a dark token
   if Telegram's theme params try to override mid-render. */
:root[data-theme="light"] .fc-cm__panel .fc-sheet__title { color: var(--text); }
:root[data-theme="light"] .fc-cm__meta { color: var(--muted); }
:root[data-theme="light"] .fc-cm__loading { color: var(--muted); }
:root[data-theme="light"] .fc-cm__kpi-label { color: var(--muted); }
:root[data-theme="light"] .fc-cm__kpi-value { color: var(--text); }
:root[data-theme="light"] .fc-cm__row-label { color: var(--muted); }
:root[data-theme="light"] .fc-cm__row-value { color: var(--text); }

/* ── Embed Widget tab (Tier 5 #12 — gestoria_enterprise only) ──
   Snippet box override already exists (line 2699-2703). We complete
   the panel: the trend chart wrapper, the KPI strip elevation, and
   the empty-state copy.

   The 7-day trend bars are written by app.js with inline
   `background:var(--indigo,#4f46e5)` — the var resolves to #4f46e5
   on light (correct) and the fallback only fires if var() lookup
   ever fails. No app.js edit needed because the CSS var always wins. */
:root[data-theme="light"] .embed-snippet {
  /* Already set at 2699 — pin colour here too in case the
     prefers-color-scheme rule at 1800 fires on a system-dark user
     who manually toggled light via the in-app theme switcher. */
  color: var(--text-2);
  box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.04);
}
:root[data-theme="light"] #embed-trend {
  padding: 8px 4px;
  background: var(--surface-3);
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
}
:root[data-theme="light"] #embed-empty {
  color: var(--muted);
}
/* The Analytics KPI strip uses .kpi-grid > .kpi — covered by the
   generic .kpi override above. Each .kpi-value (#emb-impr / #emb-clicks
   / #emb-opens / #emb-ctr) auto-binds to var(--text). */

/* ── Gestoría tab nav (#tabs-gestoria) ───────────────────────
   The nav uses .tabs class (covered at line 2224) and individual
   buttons use .tab (covered at line 2228). The active tab background
   is var(--surface-2) which is correct on cream. No additional
   gestoría-specific override needed — but pin the cartera/clientesg/
   embed tab labels in case future skinning targets them by ID. */
:root[data-theme="light"] #tab-cartera-btn,
:root[data-theme="light"] #tab-clientesg-btn,
:root[data-theme="light"] #tab-embed-btn {
  color: var(--muted);
}
:root[data-theme="light"] #tab-cartera-btn.active,
:root[data-theme="light"] #tab-cartera-btn[aria-selected="true"],
:root[data-theme="light"] #tab-clientesg-btn.active,
:root[data-theme="light"] #tab-clientesg-btn[aria-selected="true"],
:root[data-theme="light"] #tab-embed-btn.active,
:root[data-theme="light"] #tab-embed-btn[aria-selected="true"] {
  color: var(--text);
}

/* ── Gestoría tab panels — apply same elevation pattern as the
   home dashboard so all surfaces feel cohesive when switching
   between autónomo and gestoría modes. */
:root[data-theme="light"] #tab-cartera,
:root[data-theme="light"] #tab-clientesg,
:root[data-theme="light"] #tab-embed {
  color: var(--text);
}

/* ── BottomNav "Gestor" icon — matches autónomo Inicio/Facturas
   styling, already covered by the generic .bnav-item override at
   line 2383 (color: var(--muted)) and the [aria-selected="true"]
   override at 2389 (color: var(--indigo)). No additional rule. */

/* ── Capacity progressbar — full-saturation outline ──────────
   When the gestoría hits 100% of the plan-allowed clientes, the bar
   should pulse "you need to upgrade". JS writes aria-valuenow as a
   raw integer string; we match the exact "100" value (= at-cap) and
   apply an amber halo. A more granular near-limit highlight would
   need either app.js to attach a data-state class or CSS attr()
   range matching (unsupported), so we keep this minimal-surface. */
:root[data-theme="light"] .capacity[aria-valuenow="100"] {
  border-color: rgba(180, 83, 9, 0.45);
  box-shadow: 0 0 0 2px rgba(180, 83, 9, 0.10);
}

/* ── Mensajes tab (gestoría inbox) ─────────────────────────────
   .list.messages li.msg-sent/.msg-received already covered (line
   2587-2601). .msg-meta and .msg-body inherit var(--muted) and
   var(--text) respectively so they auto-swap. Add the .msg-dir
   uppercase label rebind so opacity:0.7 doesn't make it washed
   out against cream. */
:root[data-theme="light"] .msg-meta { color: var(--muted); }
:root[data-theme="light"] .msg-dir {
  color: var(--muted);
  opacity: 0.85;
}
:root[data-theme="light"] .msg-body { color: var(--text); }

/* ── Top clientes summary card (autónomo + gestoría) ──────────
   Already covered at line 3353-3382. No additional rule. */

/* ── Stripe Connect card (Phase 7, 2026-05-24) ─────────────── */
/* Brand identity additions (Item 7, 2026-05-24-polish):
   Stripe-purple trust-signal border + wordmark SVG in heading. */
.stripe-connect-card--branded {
  border-color: rgba(99, 91, 255, 0.35);
}
.stripe-connect-heading {
  display: flex !important;
  align-items: center;
  gap: 7px;
}
.stripe-wordmark {
  flex-shrink: 0;
}
/* ── Stripe Connect card (Phase 7, 2026-05-24) ────────────────
   Slim card surfaced on the autónomo Home tab. Two visual states
   gated by [hidden] from app.js renderStripeConnect():
     • Not connected → blurb + primary "Conectar Stripe" CTA
     • Connected     → success badge + country/currency line
   Mirrors plan-status visual language so the section feels native. */
.stripe-connect-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  margin: 12px 0;
}
.stripe-connect-card .section-h {
  margin: 0 0 10px;
  font-size: 0.95rem;
}
.stripe-connect-blurb {
  margin: 0 0 12px;
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--text);
}
.stripe-connect-cta .btn.primary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.stripe-connect-error {
  margin-top: 8px;
  color: var(--red, #dc2626);
}
.stripe-connect-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  padding: 6px 12px;
  background: rgba(22, 163, 74, 0.10);
  color: #166534;
  border-radius: 999px;
  font-size: 0.84rem;
  font-weight: 600;
}
.stripe-connect-badge-icon {
  font-size: 1.1rem;
  line-height: 1;
}
.stripe-connect-warning {
  margin: 6px 0 0;
  color: var(--yellow, #ca8a04);
}
:root[data-theme="dark"] .stripe-connect-badge {
  background: rgba(22, 163, 74, 0.18);
  color: #4ade80;
}
