/* ════════════════════════════════════════════════════════════════
   D3.1 — TIPOGRAFÍA PROPIA (Plus Jakarta Sans, self-hosted)
   ════════════════════════════════════════════════════════════════
   Variable font (eje wght 200-800), subset latin, un solo WOFF2 de
   ~27KB en /lib/fonts (cache immutable en vercel.json). Cubre los 4
   pesos que usa el CSS (400/500/600/700). font-display: swap → texto
   visible con fuente de sistema hasta que carga. Italic no se incluye
   (9 usos menores): el browser lo sintetiza desde la upright.
   Licencia: SIL OFL 1.1 (/lib/fonts/OFL.txt).
   ════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Plus Jakarta Sans';
  src: url('/lib/fonts/plus-jakarta-sans-latin-wght.woff2') format('woff2');
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

/* ════════════════════════════════════════════════════════════════
   D1.1 — TOKENS BASE
   ════════════════════════════════════════════════════════════════
   Implementación del sistema definido en docs/design-tokens.md.
   Sin cambio visual percibido vs producción pre-D1.1: los selectores
   existentes siguen referenciando las mismas vars con los mismos
   valores. Los tokens nuevos (capa 1 primitivas, capa 2 semánticos,
   capa 3 componente) se agregan en paralelo. Las vars heredadas
   (--bg, --text, --green, etc.) se preservan como aliases con su
   valor literal exacto de producción. Componentes migrarán a los
   tokens semánticos entre D1.2 y D1.5; los aliases se removerán
   recién en D1.5.
   ════════════════════════════════════════════════════════════════ */

:root {
  /* ──── CAPA 1 — PRIMITIVAS ─────────────────────────────────── */

  /* Neutrals — D1.5: --color-neutral-50 alineado a #F5F5F3 (valor exacto
     de prod --bg-secondary legacy) para zero cambio visual al migrar body
     a --bg-canvas. */
  --color-neutral-0:   #FFFFFF;
  --color-neutral-50:  #F5F5F3;
  --color-neutral-100: #F0F0EE;
  --color-neutral-200: #E4E4E1;
  --color-neutral-300: #C8C8C4;
  --color-neutral-400: #9B9B97;
  --color-neutral-500: #6B6B68;
  --color-neutral-600: #4D4D4A;
  --color-neutral-700: #353532;
  --color-neutral-800: #242422;
  --color-neutral-900: #1A1A18;

  /* Blue */
  --color-blue-50:  #E6F1FB;
  --color-blue-100: #C5DDF4;
  --color-blue-200: #9BC4ED;
  --color-blue-500: #378ADD;
  --color-blue-600: #2A6FB8;
  --color-blue-700: #1F578F;
  --color-blue-800: #0C447C;
  --color-blue-900: #082F58;

  /* Green */
  --color-green-50:  #D1FAE5;
  --color-green-100: #A7F3D0;
  --color-green-500: #1D9E75;
  --color-green-600: #157A5A;
  --color-green-700: #0F5B43;
  --color-green-800: #065F46;

  /* Amber */
  --color-amber-50:  #FEF9C3;
  --color-amber-100: #FEF3C7;
  --color-amber-200: #FDE68A;
  --color-amber-500: #EF9F27;
  --color-amber-600: #BA7517;
  --color-amber-700: #92400E;
  --color-amber-800: #633806;

  /* Red */
  --color-red-50:  #FFEBEE;
  --color-red-100: #FEE2E2;
  --color-red-500: #D85A30;
  --color-red-600: #C62828;
  --color-red-700: #991B1B;
  --color-red-800: #7F1D1D;
  --color-red-900: #450A0A;

  /* Burgundy */
  --color-burgundy-500: #A04545;
  --color-burgundy-600: #823737;
  --color-burgundy-700: #642A2A;

  /* Purple */
  --color-purple-50:  #E8E6FA;
  --color-purple-100: #D6D1F5;
  --color-purple-200: #B5AFE8;
  --color-purple-500: #7F77DD;
  --color-purple-600: #4A3FB3;
  --color-purple-700: #2E2576;

  /* Lime */
  --color-lime-400: #8AC23E;
  --color-lime-500: #639922;
  --color-lime-600: #4D7619;

  /* ──── CAPA 2 — SEMÁNTICOS (light) ─────────────────────────── */

  /* Superficies */
  --bg-canvas:   var(--color-neutral-50);
  --bg-surface:  var(--color-neutral-0);
  --bg-elevated: var(--color-neutral-0);
  --bg-overlay:  rgba(0, 0, 0, 0.5);

  /* Texto — coincide con legacy --text-secondary/-tertiary en light */
  --text-primary:   var(--color-neutral-900);
  --text-secondary: var(--color-neutral-500);
  --text-tertiary:  var(--color-neutral-400);
  --text-on-brand:  var(--color-neutral-0);

  /* Bordes */
  --border-subtle:  rgba(0, 0, 0, 0.08);
  --border-default: rgba(0, 0, 0, 0.10);
  --border-strong:  rgba(0, 0, 0, 0.18);
  --border-focus:   var(--color-blue-500);

  /* Intents */
  --intent-success: var(--color-green-500);
  --intent-warning: var(--color-amber-600);
  --intent-danger:  var(--color-red-500);
  --intent-info:    var(--color-blue-500);
  --intent-neutral: var(--color-neutral-500);

  /* Buckets financieros — anclaje protegido (ver §3.4 del doc) */
  --bucket-liquidez:        var(--color-green-500);
  --bucket-liquidez-rapida: var(--color-lime-500);
  --bucket-colchon:         var(--color-blue-500);
  --bucket-inversion:       var(--color-amber-500);
  --bucket-patrimonio:      var(--color-purple-500);
  --bucket-deuda:           var(--color-burgundy-500);

  /* Money flow */
  --money-positive: var(--color-green-500);
  --money-negative: var(--color-red-500);
  --money-neutral:  var(--color-neutral-500);

  /* Money delta (YoY, comparativas) */
  --money-delta-up:     var(--color-amber-600);
  --money-delta-down:   var(--color-green-500);
  --money-delta-flat:   var(--color-neutral-500);
  --money-delta-nodata: var(--color-neutral-400);

  /* Confidence (H.1 / H.2 — detección IA) */
  --confidence-high:   var(--color-green-500);
  --confidence-medium: var(--color-amber-600);
  --confidence-low:    var(--color-neutral-500);

  /* Estados UI */
  --state-hover:    rgba(0, 0, 0, 0.04);
  --state-active:   rgba(0, 0, 0, 0.08);
  --state-selected: rgba(55, 138, 221, 0.08);
  --state-disabled: rgba(0, 0, 0, 0.40);

  /* ──── CAPA 3 — COMPONENTE / SISTEMA ───────────────────────── */

  /* Tipografía — pre-WOFF2 stack del sistema.
     Cambia a Plus Jakarta Sans / Fraunces / JetBrains Mono cuando
     los archivos WOFF2 lleguen a /lib/fonts/. */
  /* D3.1: Plus Jakarta Sans self-hosted primero; stack de sistema como fallback. */
  --font-sans:  'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', sans-serif;
  --font-serif: Georgia, serif;
  --font-mono:  'SFMono-Regular', Menlo, Consolas, monospace;

  /* Escala tipográfica */
  --font-size-2xs:  10px;
  --font-size-xs:   11px;
  --font-size-sm:   12px;
  --font-size-base: 14px;
  --font-size-md:   16px;
  --font-size-lg:   18px;
  --font-size-xl:   22px;
  --font-size-2xl:  28px;
  --font-size-3xl:  36px;
  /* D2.1 — tamaño "display" para el balance protagonista del Dashboard.
     Iteración 1: 32px se sintió demasiado fuerte respecto al resto
     del hero (Ingresos/Gastos quedaban subordinados). Ajustado a
     26px — diferencia sutil pero clara contra cur-val default
     (16px tras la subida de D2.1 iteración 2). */
  --font-size-display: 26px;

  /* Pesos */
  --font-weight-regular:  400;
  --font-weight-medium:   500;
  --font-weight-semibold: 600;

  /* Line heights */
  --leading-tight:   1.2;
  --leading-snug:    1.35;
  --leading-normal:  1.5;
  --leading-relaxed: 1.65;

  /* Spacing (escala 4px) */
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  --space-8:  32px;
  --space-10: 40px;
  --space-12: 48px;

  /* Radios — --radius-sm coincide con el legacy y se declara solo acá */
  --radius-xs:   4px;
  --radius-sm:   8px;
  --radius-md:   12px;
  --radius-lg:   16px;
  --radius-full: 9999px;

  /* Sombras */
  --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.04);
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.10);
  --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.18);

  /* Motion */
  --duration-fast:   100ms;
  --duration-normal: 150ms;
  --duration-slow:   250ms;
  --ease-out:        cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out:     cubic-bezier(0.4, 0, 0.2, 1);

  /* Breakpoints (referencia documental — no se usan en var() de CSS) */
  --bp-sm: 480px;
  --bp-md: 640px;
  --bp-lg: 960px;
  --bp-xl: 1280px;

  /* D1.2.2 — Component token: separación vertical de .btn-primary y
     .btn-secondary cuando siguen a un campo de form. Valor literal
     6px (no usa --space-2 que es 8px) para preservar exactamente la
     densidad actual. La eliminación del margin-top global se difiere
     a D1.5, cuando se puedan agregar wrappers/gaps en el markup. */
  --btn-stack-gap: 6px;

  /* D2.1 — Tokens de jerarquía visual del Dashboard.
     - --metric-hero-padding: padding interno del tile de métrica
       principal (lead) del hero. Más generoso que metrics secundarios
       para dar peso visual al balance protagonista.
     - --card-header-gap: gap entre título y controles del .card-header.
       Reemplaza el implícito de flex-wrap mobile.
     - --safe-bottom: padding inferior reservado para safe-area en
       iOS/Android (bottom nav futura, gestos sistema). Por ahora
       resuelve a env(safe-area-inset-bottom) con fallback 0. */
  /* D2.1 iter 2: el padding extra de --space-4 (16px) sumado al
     font-size-display generaba un tile que parecía un componente
     distinto en el grid. Reducido a --space-3 (igual al default
     de .hero-metric) — la jerarquía la carga solo la tipografía. */
  --metric-hero-padding: var(--space-3);
  --card-header-gap:     var(--space-2);
  --safe-bottom:         env(safe-area-inset-bottom, 0px);
  /* D2.1 — letter-spacing para labels micro-mayúsculas (.sec, .hero-label,
     eyebrows). Valor 0.06em estaba duplicado literalmente en varios lugares. */
  --letter-spacing-label: 0.06em;

  /* ──── D1.3 — CALLOUT TOKENS ────────────────────────────────── */
  /* 5 variantes (info/success/warning/danger/neutral) con bg + border
     + text. Valores literales preservan la identidad visual exacta
     de los banners legacy en producción (legacy-banner, preview-context,
     legacy-banner-error). El consumo es vía .app-callout--<variant>
     que setea --callout-bg/--callout-border/--callout-text locales. */

  --callout-info-bg:     var(--color-blue-50);     /* #E6F1FB */
  --callout-info-border: rgba(55, 138, 221, 0.25);
  --callout-info-text:   var(--color-blue-800);    /* #0C447C */

  --callout-success-bg:     var(--color-green-50);  /* #D1FAE5 */
  --callout-success-border: rgba(29, 158, 117, 0.25);
  --callout-success-text:   var(--color-green-800); /* #065F46 */

  /* Warning bright — variante "legacy-banner": tinte amber cálido */
  --callout-warning-bg:     #FFF8E1;
  --callout-warning-border: #F9A825;
  --callout-warning-text:   #5D4037;

  /* Warning soft — variante "tx-pago-tarjeta": amber-50 más pálido */
  --callout-warning-soft-bg:     var(--color-amber-50);  /* #FEF9C3 */
  --callout-warning-soft-border: var(--color-amber-200); /* #FDE68A */
  --callout-warning-soft-text:   var(--color-amber-700); /* #92400E */

  --callout-danger-bg:     var(--color-red-50);     /* #FFEBEE */
  --callout-danger-border: var(--color-red-600);    /* #C62828 */
  --callout-danger-text:   var(--color-red-800);    /* #7F1D1D */

  --callout-neutral-bg:     var(--bg-canvas);
  --callout-neutral-border: var(--border-default);
  --callout-neutral-text:   var(--text-primary);

  /* ──── D1.7 — SKELETON LOADING TOKENS ────────────────────── */
  /* Tokens del sistema de skeletons. bg = color base del placeholder,
     highlight = brillo que pasa en la animación shimmer. */
  --skeleton-bg:        rgba(0, 0, 0, 0.06);
  --skeleton-highlight: rgba(0, 0, 0, 0.10);

  /* ──── ALIASES HEREDADOS ───────────────────────────────────── */
  /* Aliases legacy remanentes — D1.5/D1.8: aliases con 0 consumidores
     ya fueron removidos. Los siguientes se conservan porque migrarlos
     cambiaría el dark mode:
     - --blue/--red no shiftean en dark; --intent-info/--intent-danger
       SÍ shiftean (más brillantes). Migración diferida a D2 o aceptación
       explícita del shift.
     - --bg-tertiary no tiene token semántico directo equivalente
       (tono entre canvas y surface). Diferido.
     D1.8: confirmado --amber/--green/--purple sin consumidores (catColor
     usa hex literal del modelo de categoría, no var()) → removidos. */

  --bg-tertiary:  #ebebea;
  --red:          #D85A30;
  --blue:         #378ADD;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* ──── CAPA 2 — SEMÁNTICOS (dark overrides) ─────────────── */

    /* Superficies — D1.5 Opción A: swap canvas/surface respecto al doc
       original. Producción tiene patrón "recessed" en dark (cards más
       OSCURAS que la página). Restaurado para preservar feel pre-D1.2.1.
       Light mode mantiene patrón convencional (surface más clara que
       canvas). */
    --bg-canvas:   #242422;
    --bg-surface:  #1C1C1A;
    --bg-elevated: #2E2E2B;
    --bg-overlay:  rgba(0, 0, 0, 0.6);

    /* Texto — coincide con legacy --text-secondary/-tertiary en dark */
    --text-primary:   #E8E8E4;
    --text-secondary: #9B9B97;
    --text-tertiary:  #6B6B68;
    --text-on-brand:  #FFFFFF;

    /* Bordes */
    --border-subtle:  rgba(255, 255, 255, 0.06);
    --border-default: rgba(255, 255, 255, 0.08);
    --border-strong:  rgba(255, 255, 255, 0.15);
    --border-focus:   #5BA5E8;

    /* Intents */
    --intent-success: #3FBD93;
    --intent-warning: #D89238;
    --intent-danger:  #E07854;
    --intent-info:    #5BA5E8;
    --intent-neutral: #9B9B97;

    /* Buckets */
    --bucket-liquidez:        #3FBD93;
    --bucket-liquidez-rapida: #8AC23E;
    --bucket-colchon:         #5BA5E8;
    --bucket-inversion:       #F5B549;
    --bucket-patrimonio:      #9D96E6;
    --bucket-deuda:           #C16363;

    /* Money flow */
    --money-positive: #3FBD93;
    --money-negative: #E07854;
    --money-neutral:  #9B9B97;

    /* Money delta */
    --money-delta-up:     #D89238;
    --money-delta-down:   #3FBD93;
    --money-delta-flat:   #9B9B97;
    --money-delta-nodata: #6B6B68;

    /* Confidence */
    --confidence-high:   #3FBD93;
    --confidence-medium: #D89238;
    --confidence-low:    #9B9B97;

    /* Estados UI */
    --state-hover:    rgba(255, 255, 255, 0.04);
    --state-active:   rgba(255, 255, 255, 0.08);
    --state-selected: rgba(91, 165, 232, 0.12);
    --state-disabled: rgba(255, 255, 255, 0.30);

    /* D1.7 — Skeleton tokens (dark) */
    --skeleton-bg:        rgba(255, 255, 255, 0.06);
    --skeleton-highlight: rgba(255, 255, 255, 0.10);

    /* ──── CALLOUT TOKENS (dark overrides) ───────────────────── */
    /* Valores literales preservan la identidad visual exacta de los
       banners legacy en dark mode (legacy-banner dark, etc.). */

    --callout-info-bg:     rgba(55, 138, 221, 0.15);
    --callout-info-border: rgba(91, 165, 232, 0.35);
    --callout-info-text:   #9bc4ed;

    --callout-success-bg:     rgba(29, 158, 117, 0.15);
    --callout-success-border: rgba(63, 189, 147, 0.35);
    --callout-success-text:   #A7F3D0;

    --callout-warning-bg:     #2C2200;
    --callout-warning-border: #7A5F00;
    --callout-warning-text:   #FFD54F;

    --callout-warning-soft-bg:     #451A03;
    --callout-warning-soft-border: #78350F;
    --callout-warning-soft-text:   var(--color-amber-200); /* #FDE68A */

    --callout-danger-bg:     #3B0F12;
    --callout-danger-border: #B71C1C;
    --callout-danger-text:   #FFCDD2;

    --callout-neutral-bg:     var(--bg-canvas);
    --callout-neutral-border: var(--border-default);
    --callout-neutral-text:   var(--text-primary);

    /* ──── ALIASES HEREDADOS (dark overrides) ───────────────── */
    /* D1.5: aliases con 0 consumidores ya fueron removidos.
       --bg-tertiary sigue siendo redeclarado en dark porque shiftea
       en producción (light #ebebea → dark #2e2e2b). */

    --bg-tertiary: #2e2e2b;
  }
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font-sans);
  background: var(--bg-canvas);
  color: var(--text-primary);
  font-size: var(--font-size-base);
  line-height: 1.55;
  min-height: 100vh;
}

.screen { display: none; }
.screen.active { display: block; }
.tab { display: none; }
.tab.active { display: block; }

#screen-login,
#screen-reset,
#screen-level-selector {
  min-height: 100vh;
  align-items: center;
  justify-content: center;
  padding: 24px;
  /* D2.5b — Backdrop editorial en capas de las pantallas de entrada.
     Glow verde de marca arriba-izquierda + un segundo glow muy tenue
     abajo-derecha, sobre base canvas → profundidad diagonal sobria.
     Estático (sin animación), sin grilla, sin ilustración. Scope ESTRICTO
     a las 3 pantallas de entrada: NO se aplica a #screen-app.
     --login-glow / --login-glow-2 son aliases component-scoped (NO tokens
     de paleta): verde del mark de marca (favicon #1D9E75) a baja alpha.
     Color literal porque el codebase no usa color-mix y los tokens no
     exponen canales rgba; queda acotado a este bloque. */
  --login-glow:   rgba(29, 158, 117, 0.10);
  --login-glow-2: rgba(29, 158, 117, 0.06);
  background:
    radial-gradient(90% 60% at 12% -5%, var(--login-glow) 0%, transparent 55%),
    radial-gradient(75% 55% at 105% 108%, var(--login-glow-2) 0%, transparent 55%),
    var(--bg-canvas);
}
#screen-login.active,
#screen-reset.active,
#screen-level-selector.active {
  display: flex;
}

/* D2.5b — Glows del backdrop en dark. Verde dark de marca (#3FBD93 =
   rgb(63,189,147)) a baja alpha, levemente más presentes para leerse sobre
   el canvas oscuro (#242422). */
@media (prefers-color-scheme: dark) {
  #screen-login,
  #screen-reset,
  #screen-level-selector {
    --login-glow:   rgba(63, 189, 147, 0.13);
    --login-glow-2: rgba(63, 189, 147, 0.07);
  }
}

/* D2.5c — Backdrop desktop más presente. Glows más amplios y un poco más
   visibles + viñeta muy sutil para enfocar el centro (que la card no quede
   flotando perdida en pantallas grandes). Solo desktop (≥720px): mobile
   mantiene el backdrop más liviano de arriba. Sin grilla, sin ilustración.
   --login-vignette: alias component-scoped (NO token de paleta), borde de la
   viñeta a baja alpha. */
@media (min-width: 720px) {
  #screen-login,
  #screen-reset,
  #screen-level-selector {
    /* D2.5d — glows un toque más presentes y un wash cálido verde (--login-tint)
       sobre el canvas para sacarle el "blanco vacío" al light desktop. La
       viñeta queda igual. --login-tint es alias component-scoped (no token). */
    --login-glow:     rgba(29, 158, 117, 0.18);
    --login-glow-2:   rgba(29, 158, 117, 0.11);
    --login-vignette: rgba(0, 0, 0, 0.05);
    --login-tint:     rgba(29, 158, 117, 0.06);
    background:
      radial-gradient(120% 95% at 6% -12%, var(--login-glow) 0%, transparent 62%),
      radial-gradient(105% 85% at 110% 114%, var(--login-glow-2) 0%, transparent 62%),
      radial-gradient(130% 130% at 50% 42%, transparent 56%, var(--login-vignette) 100%),
      linear-gradient(160deg, var(--login-tint) 0%, transparent 60%),
      var(--bg-canvas);
  }
}
@media (min-width: 720px) and (prefers-color-scheme: dark) {
  #screen-login,
  #screen-reset,
  #screen-level-selector {
    /* D2.5d — dark se mantiene como D2.5c: glows iguales y el wash cálido se
       neutraliza (--login-tint transparent) para no alterar el look oscuro. */
    --login-glow:     rgba(63, 189, 147, 0.17);
    --login-glow-2:   rgba(63, 189, 147, 0.10);
    --login-vignette: rgba(0, 0, 0, 0.24);
    --login-tint:     transparent;
  }
  /* D2.5c — Glow localizado del panel de marca, verde dark, algo más presente. */
  .login-brand {
    background: radial-gradient(95% 80% at 12% 6%, rgba(63, 189, 147, 0.10) 0%, transparent 60%);
  }
}

/* Card base de entrada (single-column): reset y selector. */
.login-wrap {
  width: 100%;
  max-width: 400px;
  background: var(--bg-surface);
  /* D2.5 — Card elevada sobre el backdrop: radius mayor, sombra calma y
     borde sutil. Reusa tokens existentes. */
  border-radius: var(--radius-lg);
  border: 0.5px solid var(--border-default);
  box-shadow: var(--shadow-md);
  padding: var(--space-8) var(--space-8);
}
.login-wrap h1 { font-size: 22px; font-weight: 500; margin-bottom: 4px; }
.login-wrap .subtitle { font-size: 13px; color: var(--text-secondary); margin-bottom: 24px; }

/* D2.5b — Login en dos zonas (Dirección C). La card aloja una zona de
   marca/mensaje y una zona de formulario en superficie propia. Mobile-first:
   apilado (1 col). Desktop (≥720px): split lado a lado. La variante .login-split
   resetea el padding del .login-wrap (cada zona maneja el suyo) y clipea las
   esquinas del panel de formulario al radius de la card. */
.login-split {
  /* D2.5c — Ancho desktop más generoso y flexible (ideal ~920–960px) para que
     el módulo tenga presencia sin quedar perdido en pantallas grandes. En
     mobile gana el width:100% (clamp no llega al mínimo). */
  max-width: clamp(900px, 94vw, 960px);
  padding: 0;
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr;
  /* D2.5c — Anclaje: sombra presente y borde definido. La sombra premium en
     capas se aplica solo en desktop (abajo) para no tocar mobile. */
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
}
.login-brand {
  /* --brand-mark vive acá (no en .brand-lockup) para que badge y el acento de
     micro-beneficios compartan el verde de marca. Alias component-scoped. */
  --brand-mark: #1D9E75;
  padding: var(--space-8);
}
.login-form {
  /* Superficie propia del formulario, diferenciada de la card (bg-surface):
     en light queda gris suave sobre blanco; en dark queda más clara que la
     card (patrón recessed) → separación clara en ambos modos. */
  background: var(--bg-canvas);
  border-top: 0.5px solid var(--border-default);
  padding: var(--space-8);
}
@media (min-width: 720px) {
  /* D2.5c — Proporción asimétrica ~58/42 (marca/mensaje más ancho, form cómodo). */
  /* D2.5d — Sombra premium en capas (ambient tight + soft amplia), solo desktop,
     para más sensación premium sin tocar mobile. Literales component-scoped (no
     tokens). En dark las sombras negras quedan casi invisibles → dark no cambia. */
  .login-split {
    grid-template-columns: 1.35fr 1fr;
    align-items: stretch;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05), 0 18px 48px rgba(0, 0, 0, 0.13);
  }
  .login-brand {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: var(--space-10);
    /* D2.5c/d — Glow localizado en el panel de marca: atmósfera propia + lo
       diferencia del panel de formulario (--bg-canvas). D2.5d sube un toque la
       presencia en light (0.07 → 0.10) para que la brand zone se sienta más
       editorial sin recargar. */
    background: radial-gradient(95% 80% at 12% 6%, rgba(29, 158, 117, 0.10) 0%, transparent 62%);
  }
  .login-form {
    border-top: none;
    border-left: 0.5px solid var(--border-default);
    padding: var(--space-10);
  }
}
@media (max-width: 390px) {
  .login-brand, .login-form { padding: var(--space-6); }
}

/* Headline héroe de la zona de marca: el elemento de mayor peso visual,
   por encima del wordmark. */
.login-brand h1 { font-size: 25px; font-weight: 600; line-height: 1.2; margin-bottom: 6px; }
/* D2.5c — Subtítulo con un poco más de presencia (14px) en la columna de marca. */
.login-brand .subtitle { font-size: 14px; line-height: 1.5; margin-bottom: var(--space-3); }
@media (max-width: 390px) { .login-brand h1 { font-size: 22px; } }

/* D2.5c — Micro-beneficios con más presencia y un acento sutil de marca:
   pequeña barra verde antes de la línea (detalle reservado al verde, coherente
   con CTA tinta). Sube de text-tertiary a text-secondary y de xs a sm. */
.login-benefits {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  margin: 0;
}
.login-benefits::before {
  content: "";
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--brand-mark, #1D9E75);
  flex: 0 0 auto;
}

/* D2.5b — Lockup de marca (Badge V1 contenido + wordmark + micro-bajada).
   Más protagonista que en D2.5a, pero el headline (25px) sigue siendo el foco.
   --brand-mark es un alias component-scoped (NO token de paleta): verde exacto
   del mark de marca (favicon.svg #1D9E75), para coherencia con el ícono. */
.brand-lockup {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  /* D2.5c — Divisor sutil bajo el lockup: separa el bloque de identidad del
     bloque de mensaje (headline+subtítulo), dando jerarquía a la columna. */
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-5);
  border-bottom: 0.5px solid var(--border-default);
}
.brand-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--brand-mark);
  color: #fff;
  font-weight: 700;
  font-size: 20px;
  line-height: 1;
  flex: 0 0 auto;
  box-shadow: 0 2px 8px rgba(29, 158, 117, 0.30);
}
.brand-id { display: flex; flex-direction: column; line-height: 1.25; }
.brand-wordmark {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text-primary);
}
.brand-tagline {
  font-size: 12px;
  color: var(--text-secondary);
}

/* D2.3.2 — Login footer brand. Discreta línea al pie del card de login,
   sin links a páginas inexistentes (Privacidad/Términos diferidos hasta
   que las páginas existan). */
.login-footer {
  font-size: var(--font-size-xs);
  color: var(--text-tertiary);
  text-align: center;
  margin-top: var(--space-4);
  letter-spacing: 0.04em;
}

/* D2.3.2 — Selector de experiencia (M2). Reutiliza .login-wrap para
   alinear visualmente con el screen-login (mismo card, mismo padding).
   Las cards de nivel son labels clickeables que envuelven el radio.

   Hotfix D2.3.2 iter 3 (post-validación visual): layout reescrito con
   box-sizing border-box, anchos explícitos, radio tamaño fijo y
   contenido en columna interna. El layout anterior dejaba el radio
   flotando y el texto desbordando. */
.level-wrap {
  width: 100%;
  max-width: 440px;
  box-sizing: border-box;
}
.level-wrap h1 { margin-bottom: 4px; }
.level-wrap .subtitle { margin-bottom: var(--space-4); }

.level-card {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  cursor: pointer;
  margin-bottom: var(--space-2);
  transition: border-color .15s ease, background .15s ease;
  box-sizing: border-box;
  width: 100%;
  background: transparent;
}
.level-card:hover { border-color: var(--border-strong); }
.level-card:has(.level-card-radio:checked) {
  border-color: var(--text-primary);
  background: var(--bg-canvas);
}
.level-card--recommended { border-color: var(--border-strong); }

/* Radio nativo: tamaño fijo y no shrink. appearance:auto fuerza el
   render nativo en navegadores con resets agresivos. accent-color
   pinta el dot interno cuando está :checked. */
.level-card-radio {
  appearance: auto;
  -webkit-appearance: radio;
  width: 16px;
  height: 16px;
  margin: 3px 0 0 0;
  flex: 0 0 16px;
  accent-color: var(--text-primary);
  cursor: pointer;
}

/* Contenido en columna: label arriba, descripción abajo. min-width:0
   evita que el flex item se estire más allá del contenedor cuando el
   texto es largo. */
.level-card-content {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.level-card-label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--text-primary);
  line-height: 1.3;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.level-card-desc {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  line-height: 1.45;
  overflow-wrap: break-word;
  word-wrap: break-word;
}

.level-card-badge {
  display: inline-block;
  font-size: 10px;
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 9999px;
  background: var(--bg-canvas);
  color: var(--text-secondary);
  border: 0.5px solid var(--border-default);
  white-space: nowrap;
}

/* Separación entre la última card y los CTAs del selector. */
.level-wrap .level-card:last-of-type { margin-bottom: var(--space-4); }

/* Mobile: que el wrap llene el viewport disponible (con padding del
   .login-wrap heredado). El padding lateral del .login-wrap es 28px
   en mobile — el wrap es width:100% y eso ya respeta el viewport. */
@media (max-width: 480px) {
  .level-wrap { max-width: 100%; }
  .level-card { padding: var(--space-3); }
}

/* D2.3.2 + D2.3.3 — Pill de nivel en topbar. Discreta, mono, 10px,
   border line. Render dinámico por JS — se mantiene oculto (hidden)
   hasta que renderLevelPill() la rellene con el label correcto.
   D2.3.3: ahora es <button> clickeable que navega a Más → Experiencia.
   appearance reset + cursor pointer + hover sutil. */
.level-pill {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10px;
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  background: transparent;
  border: 1px solid var(--border-default);
  border-radius: 9999px;
  padding: 2px 8px;
  white-space: nowrap;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  font-family: var(--font-mono, ui-monospace, monospace);
  transition: border-color .15s ease, color .15s ease;
}
.level-pill:hover {
  border-color: var(--border-strong);
  color: var(--text-primary);
}
.level-pill:focus-visible {
  outline: 2px solid var(--text-primary);
  outline-offset: 2px;
}

/* D2.4.3: la pill toma el acento del nivel (mismo mapeo que la card
   Experiencia) en el color de texto, atando topbar ↔ card sin recargar el
   topbar con bordes fuertes. Tokens semánticos existentes (shiftean en
   dark). Especificidad por encima de :hover a propósito: el acento se
   mantiene en hover, el feedback de hover queda en el borde. */
body[data-level="basic"]        .level-pill { color: var(--intent-info); }
body[data-level="intermediate"] .level-pill { color: var(--money-positive); }
body[data-level="advanced"]     .level-pill { color: var(--bucket-patrimonio); }

/* D2.3.3 polish iter 3 — Label dual para la pill de nivel.
   En desktop se ve el label completo (ej. "· todas mis finanzas");
   en mobile <640px se acorta (ej. "· completo") para no empujar el
   bloque FX del topbar. basic/intermediate tienen el mismo texto en
   ambos modos — el toggle aplica igual sin afectar layout. */
.level-pill-label-short { display: none; }

@media (max-width: 640px) {
  .level-pill-label-full { display: none; }
  .level-pill-label-short { display: inline; }
}

/* D2.3.3 — Filtrado de nav por nivel. Cada .nb declara `data-levels` con
   los niveles que la ven (espacios como separador). El filtrado lo hace
   CSS: si el body tiene un data-level definido, ocultamos las .nb que NO
   lo incluyen. Si el body no tiene data-level todavía (edge case boot
   temprano pre-D2.3.1), todas las .nb quedan visibles — comportamiento
   legacy pre-D2.3, defensivo. */
body[data-level="basic"]        .nav .nb:not([data-levels~="basic"]),
body[data-level="intermediate"] .nav .nb:not([data-levels~="intermediate"]),
body[data-level="advanced"]     .nav .nb:not([data-levels~="advanced"]) {
  display: none;
}

/* D2.3.3 — Pantalla Más. Hub de overflow + ajustes en 4 secciones.
   Lista plana con items: label + chevron. Sin tiles coloridos
   (decisión visual del handoff §M5). */
#tab-mas { padding-top: var(--space-2); }

.mas-section {
  margin-bottom: var(--space-6);
}
.mas-section:last-child { margin-bottom: 0; }
.mas-section-title {
  margin-bottom: var(--space-2);
}
.mas-list {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-surface);
}
.mas-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: none;
  border-bottom: 0.5px solid var(--border-default);
  background: transparent;
  color: var(--text-primary);
  font-size: var(--font-size-base);
  text-align: left;
  cursor: pointer;
  transition: background .15s ease;
  width: 100%;
  box-sizing: border-box;
  appearance: none;
  -webkit-appearance: none;
}
.mas-item:last-child { border-bottom: none; }
.mas-item:hover { background: var(--bg-canvas); }
.mas-item:focus-visible {
  outline: 2px solid var(--text-primary);
  outline-offset: -2px;
}
.mas-item-label {
  font-weight: var(--font-weight-medium);
}
.mas-item-chevron {
  color: var(--text-tertiary);
  font-size: var(--font-size-lg);
  line-height: 1;
  flex-shrink: 0;
}
.mas-item--danger .mas-item-label { color: var(--money-negative, #D85A30); }

/* D2.4.1 — Fila Tipo de cambio USD/UYU en Más → Herramientas.
   Reusa la estructura visual de .mas-item (padding, hairline) pero
   reemplaza el chevron por un input numérico editable. Layout:
   [label + descripción a la izquierda]   [input a la derecha].
   No es <button> — es <div> porque el área clickeable no es la fila
   entera sino solo el input. */
.mas-item--fx {
  cursor: default;
}
.mas-item--fx:hover { background: transparent; }

.mas-item-fx-info {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.mas-item-fx-desc {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  line-height: 1.4;
}

.mas-fx-input {
  width: 72px;
  padding: 6px 10px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm, 8px);
  background: var(--bg-canvas);
  color: var(--text-primary);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  text-align: center;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
  appearance: textfield;
  -moz-appearance: textfield;
}
.mas-fx-input::-webkit-outer-spin-button,
.mas-fx-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.mas-fx-input:focus {
  outline: 2px solid var(--text-primary);
  outline-offset: 1px;
}

/* D2.3.3 — Cambiar de experiencia (3 botones inline en la sección
   Experiencia de Más). Layout flex con wrap para mobile. El botón del
   nivel actual queda disabled con visual destacado pero no clickeable. */
.mas-level-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.mas-level-btn {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
  white-space: nowrap;
  appearance: none;
  -webkit-appearance: none;
}
.mas-level-btn:hover:not(:disabled) {
  border-color: var(--border-strong);
}
.mas-level-btn--current {
  border-color: var(--text-primary);
  background: var(--bg-canvas);
  cursor: default;
}
.mas-level-btn:disabled { opacity: 0.85; }
.mas-level-btn:focus-visible {
  outline: 2px solid var(--text-primary);
  outline-offset: 2px;
}

/* ─── D2.4.2 — Más como centro de control ─────────────────────────
   Reorden + iconografía SVG mono + card Experiencia protagonista +
   grupo "En vista completa" para basic + sección Cuenta con identidad.
   Cero tokens de color nuevos (G-02 del handoff D2.4). */

/* Header de sección con ícono + título + conteo. El ícono mono usa
   currentColor (hereda del título) y vive en un span pequeño. */
.mas-section-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.mas-section-icon {
  display: inline-flex;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  color: var(--text-secondary);
}
.mas-section-icon svg { width: 100%; height: 100%; }
.mas-section-title-text { flex: 1 1 auto; min-width: 0; }
.mas-section-count { color: var(--text-tertiary); font-weight: var(--font-weight-medium); }

/* Ícono por fila en .mas-list. Cuadrado redondeado con bg sutil que
   contiene el SVG mono. Centrado. flex-shrink:0 para que no se achique
   cuando hay textos largos al lado. */
.more-icon {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-sm, 8px);
  background: var(--bg-canvas);
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.more-icon svg { width: 18px; height: 18px; }

/* Variante "info" — fila sin chevron, no clickeable (identidad de
   Cuenta). No cambia cursor a pointer, no hover. */
.mas-item--info {
  cursor: default;
}
.mas-item--info:hover { background: transparent; }

/* Avatar de identidad en Cuenta. Reusa .more-icon como base pero con
   bg distinto (border-default sobre canvas) para destacar las iniciales. */
.mas-account-avatar {
  background: var(--bg-canvas);
  color: var(--text-primary);
  font-weight: var(--font-weight-medium);
  font-size: var(--font-size-sm);
  font-family: var(--font-mono, ui-monospace, monospace);
  letter-spacing: 0.02em;
}

/* Card Experiencia protagonista (.xp-card). Fondo neutro de superficie
   con acento sutil border-left. Evita contrastes oscuros que rompen dark
   mode (decisión Sebas D2.4.2).
   D2.4.3: acento por nivel via --xp-accent (alias component-scoped, NO
   token de paleta nuevo) que mapea a tokens semánticos existentes — los
   tres shiftean correctamente en dark. El mapeo refuerza la escalera:
   basic → info (azul, entrada); estándar → money-positive (verde, default
   histórico); advanced → bucket-patrimonio (violeta, que ES el color de
   patrimonio en el sistema → coherente con "todas mis finanzas"). */
.mas-section--xp { margin-bottom: var(--space-6); }
.xp-card {
  --xp-accent: var(--money-positive);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-left: 3px solid var(--xp-accent);
  box-sizing: border-box;
}
.xp-card[data-level="basic"]    { --xp-accent: var(--intent-info); }
.xp-card[data-level="advanced"] { --xp-accent: var(--bucket-patrimonio); }
.xp-card__eyebrow {
  font-size: var(--font-size-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-tertiary);
  font-weight: var(--font-weight-medium);
  margin: 0 0 4px 0;
}
.xp-card__title {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-medium);
  color: var(--text-primary);
  margin: 0 0 var(--space-2) 0;
  text-transform: capitalize;
}
.xp-card__tagline {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.5;
  margin: 0 0 var(--space-3) 0;
}

/* D2.4.3: mini-listado "qué incluye" en la card Experiencia. Lista de
   capas del nivel actual, con tick. Reusa tokens existentes. El color del
   tick recibe el acento por nivel en el bloque [data-level] más abajo. */
.xp-card__includes {
  list-style: none;
  margin: 0 0 var(--space-4) 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.xp-card__includes-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.35;
}
.xp-card__includes-tick {
  flex: 0 0 auto;
  display: inline-flex;
  width: 16px;
  height: 16px;
  color: var(--xp-accent);
}
.xp-card__includes-tick svg { width: 100%; height: 100%; }

/* Segmented dentro de la card Experiencia. 3 botones flex, wrap en
   mobile. El actual queda con border del acento del nivel (--xp-accent)
   como señal de "estás acá". */
.xp-card__seg {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.xp-card__seg-btn {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm, 8px);
  background: var(--bg-canvas);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
  white-space: nowrap;
  appearance: none;
  -webkit-appearance: none;
}
.xp-card__seg-btn:hover:not(:disabled) { border-color: var(--border-strong); }
.xp-card__seg-btn--current {
  border-color: var(--xp-accent);
  background: var(--bg-surface);
  cursor: default;
}
.xp-card__seg-btn:disabled { opacity: 1; }
.xp-card__seg-btn:focus-visible {
  outline: 2px solid var(--text-primary);
  outline-offset: 2px;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px var(--space-4);
  background: var(--bg-surface);
  border-bottom: 0.5px solid var(--border-default);
  position: sticky;
  top: 0;
  z-index: 20;
}
.topbar-left { display: flex; align-items: center; gap: 8px; }
.topbar-right { display: flex; align-items: center; gap: 6px; }
.topbar-right input[type=number] { width: 54px; text-align: center; }

/* D2.3.3 polish — Labels FX duales y botón Salir responsive.
   Desktop: label largo "1 USD =" + input + "UYU" + Salir.
   D2.4.1: en mobile el `.topbar-right` entero se oculta (FX, labels, Salir);
   el FX se edita ahora desde Más → Herramientas. Las clases `.fx-label-*`
   y `.topbar-logout` quedan como artefactos (cleanup futuro D2.4.4) pero
   ya no son necesarias en el path mobile. */
.fx-label-short { display: none; }

@media (max-width: 640px) {
  .fx-label-long { display: none; }
  .fx-label-short { display: inline-flex; }
  .topbar-logout { display: none; }

  /* D2.4.1 — Topbar mobile limpio: marca · pill · avatar.
     - `.topbar-right` (FX + Salir) se oculta entero. El FX vive en
        Más → Herramientas → Tipo de cambio USD/UYU.
     - `#top-email` se oculta. El email queda visible solo en Más → Cuenta
        cuando esa sección se construya (PV-07). Por ahora desaparece del
        topbar y reaparecerá en D2.4.2. */
  .topbar-right { display: none; }
  #top-email { display: none; }
}

/* D2.3.3 polish — Remover spinners del input FX (sin tocar otros
   number inputs). En mobile acortan el campo visualmente y no suman;
   en desktop tampoco aportan (el usuario tipea el valor). El input
   sigue siendo type=number y editable; las flechas del teclado
   incrementan/decrementan igual. Scoped a #fx-rate solo para evitar
   efectos colaterales en otros inputs numéricos del proyecto.
   Necesario global (no en media query) porque Firefox aplica el
   spinner siempre y WebKit lo renderea en cualquier viewport. */
#fx-rate::-webkit-outer-spin-button,
#fx-rate::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
#fx-rate {
  -moz-appearance: textfield;
  appearance: textfield;
}
.fx-label { font-size: 12px; color: var(--text-secondary); }

/* UX-2a — El FX sale de la topbar en TODOS los viewports (en mobile ya
   estaba oculto por D2.4.1 via `.topbar-right { display:none }`; esta
   regla cubre desktop). El tipo de cambio se ve y edita en Más →
   Herramientas (#fx-rate-mas, sincronizado por FX_INPUT_IDS/saveFX).
   ⚠️ Solo CSS a propósito: #fx-rate NO se elimina del DOM porque
   loadProfile (app.js) lo referencia sin guard y el refresh BCU le
   escribe el valor — sobre un elemento oculto ambos siguen funcionando.
   El botón Salir queda visible en desktop. */
.topbar-right .fx-label,
.topbar-right #fx-rate { display: none; }

.nav {
  display: flex;
  overflow-x: auto;
  padding: 0 var(--space-4);
  background: var(--bg-surface);
  border-bottom: 0.5px solid var(--border-default);
  scrollbar-width: none;
}
.nav::-webkit-scrollbar { display: none; }

/* UX-1: indicador de scroll del nav en mobile — "scroll shadows" CSS puro.
   Dos covers del color de superficie (attachment local: viajan con el
   contenido y tapan la sombra cuando ese extremo está alcanzado) + dos
   sombras suaves fijas a los bordes (attachment scroll). Sin JS ni markup:
   la sombra derecha indica que hay más tabs y desaparece al final del scroll. */
@media (max-width: 640px) {
  .nav {
    background:
      linear-gradient(90deg, var(--bg-surface) 30%, transparent) left,
      linear-gradient(270deg, var(--bg-surface) 30%, transparent) right,
      radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.22), transparent) left,
      radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.22), transparent) right;
    background-color: var(--bg-surface);
    background-repeat: no-repeat;
    background-size: 48px 100%, 48px 100%, 16px 100%, 16px 100%;
    background-attachment: local, local, scroll, scroll;
  }
}

.nb {
  padding: 10px 13px;
  font-size: 12px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  color: var(--text-secondary);
  white-space: nowrap;
  margin-bottom: -0.5px;
  font-family: inherit;
  transition: color .15s;
}
.nb:hover { color: var(--text-primary); }
.nb.active { color: var(--text-primary); border-bottom-color: var(--text-primary); font-weight: 500; }

/* D2.1 — padding-bottom incluye safe-area inset para preparar futura
   bottom nav y respetar gestos del sistema en iOS/Android.
   var(--safe-bottom) resuelve a env(safe-area-inset-bottom, 0px) —
   0 en navegadores que no la soportan, sin impacto visual. */
.page {
  padding: 16px 16px calc(16px + var(--safe-bottom));
  max-width: 960px;
  margin: 0 auto;
}

/* UX-2a — FAB "Agregar movimiento". Solo mobile (≤640px): en desktop el
   form manual está a un click y el FAB solo taparía contenido. Fixed
   abajo-derecha respetando --safe-bottom (gestos iOS/Android). 56px =
   tap target cómodo (>44px). z-index 30: sobre el contenido y la topbar
   sticky (20), bajo los modales (1000) — un modal abierto lo cubre.
   Verde del mark de marca, coherente con favicon/iconos PWA. */
.fab {
  display: none;
  position: fixed;
  right: 16px;
  bottom: calc(16px + var(--safe-bottom));
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: none;
  background: var(--brand-mark, #1D9E75);
  color: #fff;
  font-size: 26px;
  font-weight: 500;
  line-height: 1;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28);
  z-index: 30;
  align-items: center;
  justify-content: center;
  transition: transform var(--duration-normal), box-shadow var(--duration-normal);
}
.fab:active { transform: scale(0.94); }
@media (max-width: 640px) {
  .fab { display: flex; }
}

.card {
  background: var(--bg-surface);
  border: 0.5px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-3);
}
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-3); }
.card-header .sec { margin: 0; }

/* D1.2 — Modifiers de card disponibles para componentes nuevos.
   No se aplican a markup existente todavía. Habilitan elevación y
   feedback hover en cards interactivas (ej. import-type-card en D1.5). */
.card--elevated {
  box-shadow: var(--shadow-sm);
}
.card--interactive {
  cursor: pointer;
  transition: background var(--duration-normal) var(--ease-in-out),
              border-color var(--duration-normal) var(--ease-in-out);
}
.card--interactive:hover {
  background: var(--bg-canvas);
}

.sec {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  color: var(--text-secondary);
  letter-spacing: var(--letter-spacing-label);
  text-transform: uppercase;
  margin-bottom: 10px;
}
.muted { color: var(--text-secondary); }
.small { font-size: 12px; }

.metric-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  gap: 8px;
  margin-bottom: 12px;
}
.metric {
  background: var(--bg-surface);
  border: 0.5px solid var(--border-default);
  border-radius: var(--radius-sm);
  /* Padding asimétrico mantenido literal: la breathing room horizontal
     (14px) está calibrada para la métrica grande — el token --space-4
     (16px) se siente apretado en mobile. Migrar en D1.6 con mobile review. */
  padding: 12px 14px;
}
.metric-label { font-size: var(--font-size-xs); color: var(--text-secondary); margin-bottom: 5px; }
.metric-value { font-size: 20px; font-weight: var(--font-weight-medium); line-height: var(--leading-tight); }
.metric-sub { font-size: var(--font-size-xs); color: var(--text-tertiary); margin-top: 3px; }

.field { margin-bottom: 11px; }
.field label { display: block; font-size: 11px; color: var(--text-secondary); margin-bottom: 4px; }
.field-optional { font-weight: 400; opacity: .6; }

input, select, textarea {
  width: 100%;
  padding: 7px 10px;
  font-size: 13px;
  border: 0.5px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text-primary);
  font-family: inherit;
  transition: border-color .15s;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(55,138,221,0.1);
}

/* Override: checkboxes no toman el width/padding global. */
input[type=checkbox] {
  width: auto;
  padding: 0;
  box-shadow: none;
  cursor: pointer;
}
textarea { resize: vertical; min-height: 52px; line-height: 1.4; }

/* UX-2a — Inputs a 16px en mobile: bajo ese tamaño iOS Safari hace
   auto-zoom al enfocar el control (rompe el layout y desorienta).
   Solo mobile para no perder densidad en desktop. `.mas-fx-input`
   necesita regla propia: su font-size por clase (--font-size-base)
   le gana al selector de elemento. Quedan FUERA a propósito los
   controles densos (`select.inline-small` 11px y los inputs inline
   12px de Categorías/preview) — follow-up solo si el QA mobile
   muestra zoom molesto ahí (riesgo de desborde en layouts densos). */
@media (max-width: 640px) {
  input, select, textarea { font-size: 16px; }
  .mas-fx-input { font-size: 16px; }
}

.form-row { display: flex; gap: 10px; }
.form-row .field { flex: 1; min-width: 0; }

button {
  font-family: inherit;
  cursor: pointer;
  font-size: 13px; /* tamaño legacy, no migra a --font-size-sm (12px) ni base (14px) */
  border-radius: var(--radius-sm);
  transition: opacity var(--duration-normal), background var(--duration-normal);
}

/* D1.2.2 — Focus-visible ring para botones interactivos.
   Solo se muestra con navegación por teclado (Tab), no con click.
   Mejora accesibilidad sin agregar ruido visual al uso con mouse. */
.btn-primary:focus-visible,
.btn-secondary:focus-visible,
.btn-link:focus-visible,
.btn-link-small:focus-visible,
.btn-small:focus-visible,
.btn-danger:focus-visible,
.btn-edit:focus-visible,
.seg-btn:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}

.btn-primary {
  display: block; width: 100%;
  padding: 9px var(--space-4);
  /* Color via aliases legacy (--text/--bg) para preservar la inversión
     light↔dark de producción. Migran a tokens dedicados en D1.5. */
  background: var(--text-primary); color: var(--bg-surface);
  border: none;
  font-weight: var(--font-weight-medium);
  margin-top: var(--btn-stack-gap);
}
.btn-primary:hover { opacity: 0.82; }
.btn-primary:disabled, .btn-primary[disabled] { opacity: 0.5; cursor: not-allowed; pointer-events: none; }

.btn-secondary {
  display: block; width: 100%;
  padding: var(--space-2) var(--space-4);
  background: transparent;
  color: var(--text-primary);
  border: 0.5px solid var(--border-strong);
  margin-top: var(--btn-stack-gap);
}
.btn-secondary:hover { background: var(--bg-canvas); }

.btn-link {
  display: block; width: 100%;
  padding: 6px 0;
  margin-top: var(--space-1);
  background: transparent; border: none;
  color: var(--blue); /* alias preserva #378ADD en ambos modos pre-D1.5 */
  font-size: 0.85rem;
  cursor: pointer; text-align: center;
}
.btn-link:hover { text-decoration: underline; }

.seg-control {
  display: flex;
  border: 0.5px solid var(--border-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.seg-btn {
  flex: 1;
  padding: 7px 6px; /* legacy literal — calibrado, touch ataca D1.6 */
  background: transparent;
  border: none;
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  border-radius: 0;
  font-family: inherit;
  transition: background var(--duration-normal), color var(--duration-normal);
}
.seg-btn + .seg-btn { border-left: 0.5px solid var(--border-strong); }
/* Color via aliases (--text/--bg) para preservar inversión light↔dark. */
.seg-btn.active { background: var(--text-primary); color: var(--bg-surface); font-weight: var(--font-weight-medium); }

.btn-small {
  padding: 5px 11px; /* tamaño legacy, no migra — touch target ataca D1.6 */
  background: transparent;
  border: 0.5px solid var(--border-strong);
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
}
.btn-small:hover { background: var(--bg-canvas); }

.btn-danger {
  /* D1.2: padding 2×7 → 3×8 — visibilidad sin requerir hover. */
  font-size: var(--font-size-xs);
  padding: 3px var(--space-2);
  background: transparent;
  border: 0.5px solid transparent;
  color: var(--red); /* alias preserva color en ambos modos pre-D1.5 */
  border-radius: 6px; /* legacy literal — no en escala (xs=4 / sm=8) */
}
.btn-danger:hover { border-color: var(--red); background: rgba(216,90,48,0.06); }

.btn-row { display: flex; gap: var(--space-2); }
.btn-row .btn-primary, .btn-row .btn-secondary { margin-top: 0; }

.avatar {
  width: 30px; height: 30px; border-radius: 50%;
  background: #E6F1FB; color: #0C447C;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 500; flex-shrink: 0;
}

.tx-row {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 0;
  border-bottom: 0.5px solid var(--border-default);
}
.tx-row:last-child { border-bottom: none; }
.dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.tx-info { flex: 1; min-width: 0; }
.tx-desc { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tx-meta { font-size: 11px; color: var(--text-secondary); margin-top: 1px; }
.tx-note { font-size: 11px; color: var(--text-secondary); margin-top: 2px; font-style: italic; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tx-amount { font-size: 13px; font-weight: 500; white-space: nowrap; }
.tx-actions { display: flex; align-items: center; gap: 5px; flex-shrink: 0; }

select.inline-small { font-size: 11px; padding: 3px 5px; width: auto; height: auto; border-radius: 6px; }

.cat-header-labels {
  display: flex; justify-content: flex-end; gap: 8px;
  font-size: 10px; color: var(--text-tertiary); margin-bottom: 9px;
}
.cat-header-labels span:first-child { width: 66px; text-align: right; }
.cat-header-labels span:last-child  { width: 38px; text-align: right; }

.cat-bar-row { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; }
.cat-bar-row:last-child { margin-bottom: 0; }
.cat-bar-label { display: flex; align-items: center; gap: 6px; width: 108px; flex-shrink: 0; }
.cat-bar-label span { font-size: 12px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cat-bar-bg { flex: 1; height: 7px; background: var(--bg-tertiary); border-radius: 4px; overflow: hidden; }
.cat-bar-fill { height: 100%; border-radius: 4px; transition: width .45s ease; }
.cat-bar-amt { font-size: 11px; color: var(--text-secondary); width: 66px; text-align: right; flex-shrink: 0; }
.cat-bar-pct { font-size: 11px; font-weight: 500; width: 38px; text-align: right; flex-shrink: 0; }

/* D2.2.6 — Fix de contraste del porcentaje de Top categorías en dark mode.
   El JS aplica `style="color:${catColor(c)}"` inline al .cat-bar-pct para
   reforzar la asociación con la categoría (ej. verde Transferencia). En
   dark mode varios de esos colores (verde brillante, ámbar) bajan de
   contraste sobre el fondo oscuro y el "58%" queda poco legible.
   Override scoped: SOLO en #top-cat-bars (Dashboard), SOLO en dark mode,
   movemos el % a --text-primary para legibilidad. El dot y la barra
   mantienen catColor — la asociación visual no se pierde. !important es
   necesario para ganarle al style inline. Light mode intacto. */
@media (prefers-color-scheme: dark) {
  #top-cat-bars .cat-bar-pct { color: var(--text-primary) !important; }
}

.liq-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.liq-card { padding: 11px 13px; border-radius: var(--radius-sm); border: 0.5px solid var(--border-default); }
.liq-name  { font-size: 11px; font-weight: 500; margin-bottom: 3px; }
.liq-value { font-size: 18px; font-weight: 500; line-height: 1.2; }
.liq-pct   { font-size: 11px; color: var(--text-secondary); margin-top: 2px; }
/* D2.2.5 — Estado del colchón en la card de Liquidez. Mismo tamaño que
   .liq-pct pero peso medium para que el mensaje "Te faltan / Cubierto /
   Por encima" tenga un poco más de presencia sin gritarle al usuario. */
.liq-status { font-weight: var(--font-weight-medium); color: var(--text-primary); }

/* D2.2.5 — Polish de Transacciones del período en el Dashboard.
   Aplicado SOLO al #recent-tx (Dashboard), no afecta la pestaña
   Transacciones que mantiene su densidad de tabla. Más respiración
   vertical, descripción con peso medium, fecha en color tertiary
   atenuado, monto un punto más prominente. */
#recent-tx .tx-row {
  padding: var(--space-3) 0;
  gap: var(--space-3);
}
#recent-tx .tx-desc {
  font-weight: var(--font-weight-medium);
  font-size: var(--font-size-base);
}
#recent-tx .tx-meta {
  font-size: var(--font-size-xs);
  color: var(--text-tertiary);
  margin-top: 2px;
}
#recent-tx .tx-amount {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
}

/* Barra de progreso del Colchón objetivo (G4) */
.pat-progress {
  height: 6px;
  background: var(--bg-canvas);
  border-radius: 3px;
  overflow: hidden;
  margin: 6px 0 4px;
}
.pat-progress-fill {
  height: 100%;
  border-radius: 3px;
  transition: width .25s ease;
}

.inv-row { display: flex; align-items: flex-start; gap: 10px; padding: 10px 0; border-bottom: 0.5px solid var(--border-default); }
.inv-row:last-child { border-bottom: none; }

/* D1.2.4 — Chip base. Inline-flex con padding y radius compactos.
   Padding 2×8 y radius 10px se mantienen literales (fuera de escala);
   son la densidad calibrada de chips inline en .tx-row / .acc-card. */
.chip {
  display: inline-flex;
  align-items: center;
  font-size: var(--font-size-2xs);
  padding: 2px 8px;
  border-radius: 10px;
  font-weight: var(--font-weight-medium);
}

/* D1.2.4 — Family de badges. Misma escala que la familia .chip:
   font-size --font-size-xs (11px), padding 2×9, radius 10px.
   Hex hardcoded migrados a primitivas de la paleta donde matchean.
   Tints customizados (#FAEEDA, #FFFBEB) se mantienen literales y
   quedan como candidatos a refinamiento del sistema en D1.3. */
.badge-warning {
  font-size: var(--font-size-xs);
  padding: 2px 9px;
  border-radius: 10px;
  background: #FAEEDA; /* tinte amber custom, fuera de la escala primitiva */
  color: var(--color-amber-800);
  font-weight: var(--font-weight-medium);
}
.badge-local {
  font-size: var(--font-size-xs);
  padding: 2px 9px;
  border-radius: 10px;
  background: var(--color-green-50);
  color: var(--color-green-800);
  font-weight: var(--font-weight-medium);
}
/* H.2.1: badge para commitments reconciliados (status='paid'). Púrpura matching dot de commitments. */
.badge-paid {
  font-size: var(--font-size-xs);
  padding: 2px 9px;
  border-radius: 10px;
  background: var(--color-purple-50);
  color: var(--color-purple-600);
  font-weight: var(--font-weight-medium);
}
/* Badge especial — marker inline dentro de .tx-row. Más chico (1×6, radius 4)
   y con border. Misma familia de color (amber) que badge-warning pero más sutil. */
.badge-dupe {
  font-size: var(--font-size-2xs);
  padding: 1px 6px;
  border-radius: var(--radius-xs);
  background: #FEF3C722; /* amber-100 con alpha — tinte custom */
  color: #B45309;        /* amber custom, fuera de paleta */
  font-weight: var(--font-weight-semibold);
  border: 1px solid #FCD34D; /* amber-300 equivalente, no en paleta */
}
.tx-warn { background: #FFFBEB; /* amber-50 más claro — tinte custom */ }

.upload-zone {
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius-md);
  padding: 28px 20px; text-align: center; cursor: pointer;
  transition: background .15s; color: var(--text-secondary); margin-bottom: 10px;
}
.upload-zone:hover, .upload-zone.drag { background: var(--bg-canvas); border-color: var(--blue); }
.upload-zone svg { margin: 0 auto 10px; display: block; color: var(--text-tertiary); }
.upload-zone p { font-size: 13px; }

.ai-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--font-size-xs);
  padding: 3px 10px;
  border-radius: 10px;
  background: var(--color-blue-50);
  color: var(--color-blue-800);
  margin-bottom: 10px;
  font-weight: var(--font-weight-medium);
}

.ai-progress-row { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; font-size: 12px; color: var(--text-secondary); }

.spinner {
  width: 14px; height: 14px;
  border: 2px solid var(--border-strong); border-top-color: var(--blue);
  border-radius: 50%; animation: spin .7s linear infinite; flex-shrink: 0;
}
@keyframes spin { to { transform: rotate(360deg); } }

.progress-bar { height: 4px; background: var(--bg-tertiary); border-radius: 2px; overflow: hidden; }
.progress-fill { height: 100%; background: var(--blue); border-radius: 2px; transition: width .3s; }

.filters { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
.filters select { flex: 1; min-width: 110px; }

.divider { height: 0.5px; background: var(--border-default); margin: 14px 0; border: none; }
.error-msg { color: var(--red); font-size: 12px; margin-top: 8px; min-height: 18px; }

#chart-evol { max-height: 180px; }

@media (max-width: 480px) {
  /* D2.1: mantiene padding mobile compacto + safe-area inferior para
     respetar gestos sistema (iOS home indicator, Android nav). */
  .page { padding: 12px 12px calc(12px + var(--safe-bottom)); }
  .metric-value { font-size: 17px; }
  .liq-value { font-size: 15px; }
  .form-row { flex-direction: column; gap: 0; }
  .topbar-right { gap: 4px; }
}

/* ════════════════════════════════════════════════════════════════
   D1.6 — MOBILE LAYOUT FIXES
   ════════════════════════════════════════════════════════════════
   Objetivo: que en 360-414px viewport las cards (especialmente
   Dashboard · Evolución mensual) no rompan layout. El problema
   principal era el .card-header con flex justify-between teniendo:
   (a) un título uppercase con letter-spacing,
   (b) un .seg-control con inline style="width:168px" o 210px,
   sumando >ancho-interno disponible → overflow visual.

   Fix: wrap del card-header + seg-controls full-width en mobile.
   Sin tocar JS, datasets ni configuración de Chart.js.
   ════════════════════════════════════════════════════════════════ */
@media (max-width: 480px) {
  /* .card-header pasa de single-row a wrap: título arriba, controles
     debajo en su propia línea cuando el ancho lo requiere.
     D2.1: tokenizado con var(--card-header-gap) (= var(--space-2) hoy). */
  .card-header {
    flex-wrap: wrap;
    gap: var(--card-header-gap);
  }
  /* Seg-controls dentro de card-headers: ocupan todo el ancho disponible.
     !important porque el markup tiene inline style="width:168px" / 210px
     que no podemos tocar sin migrar markup. */
  .card-header .seg-control {
    width: 100% !important;
  }
  /* Canvas/SVG dentro de cards no debe forzar overflow horizontal.
     Chart.js con responsive:true respeta el contenedor; este guard
     asegura que ningún canvas legacy se desborde. */
  .card > canvas,
  .card > svg {
    max-width: 100%;
  }
  /* Card de Evolución: altura del canvas ajustada para mobile.
     Bajamos el min-height de 180 → 160 para reducir el card alto
     cuando ya hay seg-control wrappeado abajo del título. */
  .dash-card-evol > canvas {
    min-height: 160px;
  }
  /* Defensa contra overflow horizontal en cards (cualquier hijo que
     se exceda queda contenido). */
  .card {
    overflow-x: hidden;
  }

  /* ──── Tabs nav + touch targets ─────────────────────────────── */
  /* Tabs horizontales: padding más generoso → touch confortable +
     altura efectiva ≥44px contando el border-bottom. Font ligeramente
     mayor para legibilidad en pantallas chicas. */
  .nb {
    padding: 12px 14px;
    font-size: 13px;
  }
  /* CTAs principales en mobile alcanzan min-height 40px (cerca del
     objetivo Apple HIG 44px sin inflar layouts). */
  .btn-primary,
  .btn-secondary {
    min-height: 40px;
  }
  /* Seg-controls: altura efectiva mínima 36px (bumpeado desde ~30 del
     padding 7px). Suficiente para tap confortable en 3-4 botones. */
  .seg-btn {
    min-height: 36px;
  }
  /* Botones secundarios chicos en toolbars (no en row dense): tap
     confortable. Aplicado solo a .bulk-toolbar y .filters para no
     inflar .btn-edit/.btn-danger dentro de .tx-row. */
  .bulk-toolbar-actions .btn-small,
  .bulk-toolbar-actions .btn-danger,
  .filters .btn-small {
    min-height: 36px;
  }

  /* ──── Card & metric spacing en mobile ──────────────────────── */
  /* Padding reducido a --space-3 (12px) para aprovechar el ancho
     limitado del viewport mobile sin amontonar contenido.
     Decisión aprobada en D1.0, implementación diferida a D1.6. */
  .card {
    padding: var(--space-3);
  }
  /* Modal sigue con padding ligeramente mayor para respiración
     en formularios — bajamos sólo levemente. */
  .modal-card {
    padding: var(--space-4);
  }
  /* Cards de métricas: padding asimétrico calibrado a mobile.
     Reducimos horizontal de 14px a 12px para igualar al vertical. */
  .metric {
    padding: var(--space-3);
  }
  /* Grid de metric-grid: gap más chico en mobile */
  .metric-grid {
    gap: var(--space-2);
  }
  /* Card-header margin-bottom más chico → menos aire vertical */
  .card-header {
    margin-bottom: var(--space-2);
  }
}

/* ─── MODALES (edición de transacción / inversión) ──────── */
.modal {
  display: none;
  position: fixed;
  inset: 0;
  background: var(--bg-overlay);
  z-index: 1000;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
}
.modal.open { display: flex; }
.modal-card {
  background: var(--bg-elevated);
  border-radius: var(--radius-md);
  /* D1.2: border 1px → 0.5px para consistencia con .card */
  border: 0.5px solid var(--border-default);
  padding: var(--space-5);
  width: 100%;
  max-width: 480px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
}
.modal-card .sec { margin-bottom: 14px; }
.modal-card .btn-row { margin-top: var(--space-4); gap: var(--space-2); }
.btn-edit {
  background: transparent;
  color: var(--text-secondary);
  /* D1.2: border 1px → 0.5px (consistencia con .btn-secondary/.btn-small) */
  border: 0.5px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2);
  font-size: 13px; /* legacy literal */
  cursor: pointer;
  line-height: 1;
}
.btn-edit:hover { color: var(--text-primary); border-color: var(--text-secondary); }

/* ─── DASHBOARD FASE E ──────────────────────────────────── */

/* Héroe: navegación de período */
.dash-hero-card { padding: var(--space-4); }
.dash-period {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}
.dash-period-label {
  flex: 1;
  font-size: 14px;
  font-weight: 500;
  /* Sin text-transform: fmtPeriodLabel() ya capitaliza solo la primera letra */
}

/* Grid 3 columnas para las 3 métricas principales */
.hero-metrics-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
}

/* Tarjeta individual de métrica */
.hero-metric {
  background: var(--bg-canvas);
  border-radius: var(--radius-sm);
  padding: 12px;
}
.hero-label {
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-label);
  margin-bottom: var(--space-2);
}

/* D2.1 — Tile lead: la métrica protagonista del hero (Ahorro/balance).
   Padding más generoso + valor tipográfico "display" para crear peso
   visual sin alterar el layout 3-col del grid. */
.hero-metric--lead {
  padding: var(--metric-hero-padding);
}
.hero-metric--lead .cur-val {
  font-size: var(--font-size-display);
  font-weight: var(--font-weight-semibold);
  line-height: var(--leading-tight);
}
.hero-metric--lead .cur-tag {
  font-size: var(--font-size-xs);
}
@media (max-width: 480px) {
  /* D2.1 iter 2: en mobile bajamos a --font-size-xl (22px) — sigue
     siendo notablemente mayor que el .cur-val default mobile (17px
     ya configurado más abajo) sin dominar la columna estrecha. */
  .hero-metric--lead .cur-val {
    font-size: var(--font-size-xl);
  }
}

/* Fila moneda + valor */
.cur-row {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin-bottom: 3px;
}
.cur-tag {
  font-size: 10px;
  color: var(--text-tertiary);
  font-weight: 500;
  min-width: 26px;
  flex-shrink: 0;
}
.cur-val {
  /* D2.1 iter 2: bumpeado 15 → 16 (--font-size-md) para acompañar
     la baja del display de 32→26. Mantiene jerarquía sin extremar
     el contraste entre lead y métricas secundarias. */
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-medium);
  line-height: var(--leading-tight);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Badges de comparativa LM / LY */
.delta-row {
  margin-top: 7px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.delta-item { font-size: 11px; line-height: 1.4; }
.delta-label { color: var(--text-tertiary); font-size: 10px; }

/* Grid 2 columnas para el cuerpo del dashboard.
   Sin align-items: start → las cards de la misma fila igualan altura (stretch). */
.dash-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
/* Una card que ocupa toda la fila del grid (ej: Transacciones del período en G4) */
.dash-2col .card-full { grid-column: 1 / -1; }

/* Card de Evolución: flex-column para que el canvas crezca hasta igualar
   el alto del card de Patrimonio en desktop. Mantiene min-height en mobile. */
.dash-card-evol {
  display: flex;
  flex-direction: column;
}
.dash-card-evol > canvas {
  flex: 1;
  min-height: 180px;
}

/* Wrapper con altura fija para que Chart.js respete maintainAspectRatio:false */
.analisis-chart-wrap {
  position: relative;
  height: 220px;
}

@media (max-width: 640px) {
  .dash-2col          { grid-template-columns: 1fr; }
  .hero-metrics-grid  { grid-template-columns: 1fr; }
  .cur-val            { font-size: 17px; }
  .analisis-chart-wrap { height: 180px; }
}

/* ─── FASE G — MIGRACIÓN ASISTIDA DE CUENTAS ────────────── */

/* Banner informativo en tab Cuentas cuando hay cuentas legacy.
   D1.3: refactorizado para usar callout-warning tokens. Markup conserva
   las clases .legacy-banner / -text / -btn / -close existentes para
   no tocar HTML. La identidad visual es idéntica vía tokens. */
.legacy-banner {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--callout-warning-bg);
  border: 1px solid var(--callout-warning-border);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  margin-bottom: var(--space-3);
  font-size: 13px;
  color: var(--callout-warning-text);
}
.legacy-banner-text { flex: 1; }
.legacy-banner-btn {
  background: none;
  border: none;
  color: #E65100; /* contraste especial del CTA dentro de warning bright */
  font-weight: var(--font-weight-semibold);
  cursor: pointer;
  padding: 0;
  font-size: 13px;
  white-space: nowrap;
  text-decoration: underline;
  text-underline-offset: 2px;
  font-family: inherit;
}
.legacy-banner-btn:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  border-radius: 2px;
}
@media (prefers-color-scheme: dark) {
  .legacy-banner-btn { color: #FFB300; }
}
.legacy-banner-close {
  background: none;
  border: none;
  color: var(--text-tertiary);
  cursor: pointer;
  font-size: var(--font-size-lg);
  line-height: 1;
  padding: 0 2px;
  flex-shrink: 0;
  font-family: inherit;
}
.legacy-banner-close:hover { color: var(--text-secondary); }
.legacy-banner-close:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}

/* L.7c: variante rojo/error para advertencias bloqueantes (mismatch de tipo de archivo).
   D1.3: refactorizado a callout-danger tokens. */
.legacy-banner-error {
  background: var(--callout-danger-bg);
  border-color: var(--callout-danger-border);
  color: var(--callout-danger-text);
}

/* Chip de advertencia "revisar" en cuentas legacy */
.chip-warn {
  display: inline-flex;
  align-items: center;
  font-size: var(--font-size-2xs);
  padding: 2px 7px;
  border-radius: 10px;
  font-weight: var(--font-weight-semibold);
  /* Tints amber custom — candidatos a migración a sistema de callouts en D1.3 */
  background: #FFF3E0;
  color: #E65100;
  border: 1px solid #FFCC80;
}
@media (prefers-color-scheme: dark) {
  .chip-warn { background: #2C1500; color: #FFB300; border-color: #7A3E00; }
}

/* Filas del modal de migración */
.migration-row {
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 12px;
  margin-bottom: 10px;
}
.migration-row:last-child { margin-bottom: 0; }
.migration-acc-name {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

/* ─── Banda Compromisos del Dashboard (H.0d) ─────────────── */
/* Compacta, entre el hero mensual y el grid 2x2. */
.comp-bar {
  background: var(--bg-canvas);
  border: 0.5px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  margin-bottom: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.comp-bar-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}
.comp-bar-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
  margin: 0;
  font-weight: 500;
}
.comp-bar-data {
  font-size: 13px;
  font-weight: 500;
  margin: 0;
  line-height: 1.4;
}
/* Empty state: borde dashed, sin fondo, texto atenuado */
.comp-bar-empty {
  background: transparent;
  border-style: dashed;
  justify-content: center;
}
.comp-bar-empty .comp-bar-data {
  font-weight: 400;
  font-style: italic;
}

/* ─── H.1b — Compromisos sugeridos en import preview ─────── */
.comm-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 0;
  border-bottom: 0.5px solid var(--border-default);
}
.comm-row:last-child { border-bottom: 0; }
.comm-checkbox {
  margin-top: 3px;
  flex-shrink: 0;
}
.comm-body { flex: 1; min-width: 0; }
.comm-title {
  font-size: 13px;
  font-weight: 500;
  margin: 0 0 4px;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.comm-sub {
  font-size: 11px;
  color: var(--text-secondary);
  margin: 0;
  line-height: 1.5;
}
.comm-actions {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-shrink: 0;
}
.comm-details {
  margin-top: 8px;
  padding: 8px 10px;
  background: var(--bg-canvas);
  border-radius: var(--radius-sm);
  font-size: 11px;
  color: var(--text-secondary);
}
.comm-detail-item {
  display: flex;
  justify-content: space-between;
  padding: 3px 0;
  font-variant-numeric: tabular-nums;
}
.comm-conf {
  display: inline-block;
  font-size: 9px;
  padding: 1px 6px;
  border-radius: 8px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.comm-conf-high   { background: rgba(29,158,117,0.12);  color: #1D9E75; }
.comm-conf-medium { background: rgba(186,117,23,0.12);  color: #BA7517; }
.comm-conf-low    { background: rgba(136,134,128,0.12); color: #888780; }
/* Cuando un candidate está rechazado (selected=false), atenuar */
.comm-row.is-unselected .comm-title,
.comm-row.is-unselected .comm-sub {
  opacity: 0.55;
}

/* ─── H.2c — Reconciliaciones sugeridas en import preview ── */
.recon-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 0;
  border-bottom: 0.5px solid var(--border-default);
}
.recon-row:last-child { border-bottom: 0; }
.recon-checkbox {
  margin-top: 3px;
  flex-shrink: 0;
}
.recon-body { flex: 1; min-width: 0; }
.recon-title {
  font-size: 13px;
  font-weight: 500;
  margin: 0 0 4px;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.recon-sub {
  font-size: 11px;
  color: var(--text-secondary);
  margin: 0;
  line-height: 1.5;
}
.recon-arrow {
  display: inline-block;
  margin: 0 6px;
  color: var(--text-secondary);
  font-weight: 600;
}
.recon-conf {
  display: inline-block;
  font-size: 9px;
  padding: 1px 6px;
  border-radius: 8px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.recon-conf-medium { background: rgba(186,117,23,0.12); color: #BA7517; }
/* Cuando una row está seleccionada, sutilmente destacarla */
.recon-row.is-selected .recon-title { color: var(--text-primary); }

/* ─── Análisis Sub-fase A — Delta YoY en top categorías ──── */
/* Wrap por categoría: la barra arriba + el meta YoY abajo.
   Reemplaza el margin-bottom 9px de .cat-bar-row por uno propio que
   abarca a los dos elementos. Solo se aplica en Top categorías de Análisis. */
.cat-item-yoy {
  margin-bottom: 12px;
}
.cat-item-yoy:last-child { margin-bottom: 0; }
/* A2: drill-down — cursor y feedback visual al pasar el mouse */
.cat-item-drilldown { cursor: pointer; border-radius: 6px; padding: 4px 6px; margin-left: -6px; transition: background .15s; }
.cat-item-drilldown:hover { background: var(--bg-canvas); }
.cat-item-drilldown.cat-item-active { background: var(--bg-canvas); outline: 1.5px solid var(--border-default); }
/* A2: chip de categoría activa + lista de transacciones */
.analisis-cat-chip { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; font-size: 12px; color: var(--text-secondary); padding: 6px 10px; background: var(--bg-canvas); border-radius: 8px; }
.analisis-cat-tx-list { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
/* A2.1: alineación arriba para que mes + día no deformen filas con nota */
.analisis-cat-tx-row { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; padding: 6px 0; border-bottom: 1px solid var(--border-default); }
.analisis-cat-tx-row:last-child { border-bottom: none; }
/* A2.1: fecha en dos líneas: mes (normal) + día (atenuado, más pequeño) */
.analisis-cat-tx-date { display: flex; flex-direction: column; gap: 1px; color: var(--text-secondary); flex-shrink: 0; min-width: 58px; padding-top: 1px; }
.analisis-cat-tx-day  { font-size: 10px; opacity: .6; }
/* A2.1: descripción (línea principal) + nota (línea secundaria) */
.analisis-cat-tx-desc { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.analisis-cat-tx-desc-main { color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.analisis-cat-tx-note { color: var(--text-secondary); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.analisis-cat-tx-acc { color: var(--text-secondary); font-size: 11px; flex-shrink: 0; max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-top: 2px; }
.analisis-cat-tx-amt { font-variant-numeric: tabular-nums; color: var(--text-primary); flex-shrink: 0; text-align: right; min-width: 70px; padding-top: 1px; }
.cat-item-yoy .cat-bar-row {
  margin-bottom: 3px;
}
.cat-yoy-meta {
  font-size: 10.5px;
  padding-left: 18px; /* alinea con el texto bajo el dot del label */
  font-weight: 500;
  letter-spacing: 0.01em;
}
.cat-yoy-up      { color: #BA7517; }
.cat-yoy-down    { color: #1D9E75; }
.cat-yoy-similar {
  color: var(--text-secondary);
  font-style: italic;
  font-weight: 400;
}
.cat-yoy-nodata {
  color: var(--text-secondary);
  font-style: italic;
  font-weight: 400;
  opacity: 0.7;
}

/* ─── L.0 — Bulk delete de transacciones ─────────────── */
/* Toolbar arriba del listado cuando bulkMode está activo */
.bulk-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 14px;
  margin-bottom: 10px;
  background: rgba(216,90,48,0.08);
  border: 1px solid rgba(216,90,48,0.25);
  border-radius: 8px;
}
.bulk-toolbar-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.bulk-count {
  font-weight: 600;
  color: var(--text-primary);
}
.bulk-totals {
  font-size: 12px;
  color: var(--text-secondary);
}
.bulk-toolbar-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

/* Toggle activo cuando bulkMode = true */
#btn-bulk-toggle.active {
  background: rgba(216,90,48,0.18);
  border-color: rgba(216,90,48,0.45);
  color: #D85A30;
}

/* Checkbox y fila en bulkMode */
.tx-checkbox {
  margin-right: 8px;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  cursor: pointer;
  accent-color: #D85A30;
}
.tx-row.bulk-mode {
  /* Espacio para el checkbox al inicio */
  padding-left: 4px;
}
/* Durante bulk mode ocultamos las acciones individuales — la toolbar las reemplaza.
   El × clásico y ✎ / ⧉ reaparecen al salir del modo. */
.tx-row.bulk-mode .tx-actions .btn-edit,
.tx-row.bulk-mode .tx-actions .btn-danger {
  display: none;
}
.tx-row.bulk-mode.selected {
  background: rgba(216,90,48,0.08);
}

/* ─── L.2 — Cuentas: tablero por secciones + UX0 ──────── */
/* D1.5: clase legacy .cuentas-empty-state y sus 4 sub-clases removidas.
   El empty state principal de Cuentas se renderiza con el sistema
   .empty-state.empty-state--initial (D1.4) — ver index.html línea 405. */

/* Cada sección del tablero: 🏦 Bancos / 💵 Efectivo / 📈 Inversiones / 📦 Otros */
.acc-section {
  margin-bottom: 18px;
}
.acc-section:last-child { margin-bottom: 0; }

.acc-section-header {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 6px 0 4px;
  border-bottom: 0.5px solid var(--border-default);
}
.acc-section-icon {
  font-size: 16px;
  line-height: 1;
}
.acc-section-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary);
}
.acc-section-count {
  font-size: 11px;
  color: var(--text-tertiary);
}
.acc-section-total {
  margin-left: auto;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
}
.acc-section-desc {
  margin: 4px 0 8px;
  font-size: 11.5px;
  color: var(--text-secondary);
}
.acc-section-empty {
  padding: 8px 0;
  font-style: italic;
}

/* Card individual: extiende tx-row con metadata extra (institución, subtipo, last4) */
.acc-card-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}
.acc-card-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 3px;
  font-size: 11.5px;
  color: var(--text-secondary);
}
.acc-card-institution {
  font-weight: 500;
  color: var(--text-secondary);
}

/* Chips nuevos de subtipo y último 4 — más sutiles que el chip de tipo */
.chip-subtype {
  background: var(--bg-tertiary);
  color: var(--text-secondary);
  font-size: 10.5px; /* literal — entre --font-size-2xs (10) y -xs (11) */
  padding: 1px 7px;
  border-radius: var(--radius-sm);
}
.chip-last4 {
  background: var(--bg-tertiary);
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 10.5px;
  padding: 1px 7px;
  border-radius: var(--radius-sm);
  letter-spacing: 0.02em;
}

/* ─── L.2.5 — Tarjetas como entidad ─────────────────── */
/* Total de la sección 💳 cuando hay deuda: color rojo apagado (burgundy)
   para diferenciar visualmente de los activos, sin alarmismo. */
.acc-section-total-debt {
  color: #A04545;
}
/* Microcopy condicional bajo el campo Saldo cuando type=tarjeta */
.acc-saldo-hint {
  color: var(--text-secondary);
  font-style: italic;
  font-size: 11.5px;
  line-height: 1.4;
}
/* v1.11.2 — Microcopy condicional bajo el campo Moneda cuando type=tarjeta */
.acc-moneda-hint {
  color: var(--text-secondary);
  font-style: italic;
  font-size: 11.5px;
  line-height: 1.4;
}

/* ─── L.3a — Wizard de importación (tipo → cuenta → archivo) ─── */

/* Stepper superior — pills sutiles con estado activo/done/upcoming. */
.import-stepper {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 16px;
  font-size: 12px;
  color: var(--text-tertiary);
}
.import-step-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 14px;
  background: var(--bg-tertiary);
  color: var(--text-secondary);
  font-weight: 500;
}
.import-step-pill .import-step-num {
  display: inline-flex;
  width: 18px; height: 18px;
  align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--bg-canvas);
  color: var(--text-tertiary);
  font-size: 11px;
  font-weight: 600;
}
.import-step-pill.active {
  background: #E6F1FB;
  color: #0C447C;
}
.import-step-pill.active .import-step-num {
  background: var(--blue);
  color: #fff;
}
.import-step-pill.done {
  color: var(--text-secondary);
}
.import-step-pill.done .import-step-num {
  background: #1D9E75;
  color: #fff;
}
.import-step-pill.upcoming {
  opacity: 0.7;
}
.import-step-sep {
  color: var(--text-tertiary);
  font-size: 14px;
}

/* Paso 1 — grid de 2 cards (tipo de extracto). */
.import-type-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}
.import-type-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 18px 16px;
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-canvas);
  cursor: pointer;
  text-align: left;
  transition: background .15s, border-color .15s, transform .1s;
  font-family: inherit;
  color: var(--text-primary);
  width: 100%;
}
.import-type-card:hover {
  background: var(--bg-tertiary);
  border-color: var(--blue);
}
.import-type-card:active {
  transform: scale(0.99);
}
.import-type-icon { font-size: 28px; line-height: 1; }
.import-type-title { font-size: 14px; font-weight: 600; }
.import-type-desc { line-height: 1.4; }

@media (max-width: 480px) {
  .import-type-grid { grid-template-columns: 1fr; }
}

/* Paso 2 — selector de cuenta/tarjeta compatible. */
.import-account-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 12px 14px;
  border: 0.5px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-canvas);
  cursor: pointer;
  text-align: left;
  transition: background .15s, border-color .15s;
  font-family: inherit;
  color: var(--text-primary);
  width: 100%;
  margin-bottom: 8px;
}
.import-account-card:hover {
  background: var(--bg-tertiary);
  border-color: var(--blue);
}
.import-account-card:last-child { margin-bottom: 0; }
.import-account-name { font-size: 13.5px; font-weight: 500; }
.import-account-meta { line-height: 1.35; }

/* D2.6 (G1): el empty state de "sin cuentas compatibles" migró al sistema
   canónico .empty-state (--inline). La clase .empty-state-card se removió
   por no tener más usos. */

/* Header de contexto reutilizado en paso 2, 3 y preview. */
.import-context-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  margin-bottom: 12px;
  background: var(--bg-canvas);
  border-radius: var(--radius-sm);
  font-size: 12px;
}
.import-context-label {
  color: var(--text-secondary);
  font-weight: 500;
}

.btn-link-small {
  background: transparent;
  border: none;
  color: var(--blue); /* alias preserva color en ambos modos pre-D1.5 */
  cursor: pointer;
  font-family: inherit;
  font-size: var(--font-size-sm);
  padding: 2px 6px; /* legacy literal — fuera de escala */
  border-radius: var(--radius-xs);
}
.btn-link-small:hover { text-decoration: underline; }

/* U1a: mensaje persistente de resultado de carga arriba del preview. */
.import-result-msg {
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  margin-bottom: 12px;
  padding: 10px 12px;
  background: var(--callout-success-bg, var(--bg-canvas));
  border: 0.5px solid var(--callout-success-border, var(--border-default));
  border-radius: var(--radius-sm);
  line-height: 1.45;
}

/* Preview — chip de contexto arriba del card-header.
   D1.3: refactorizado a callout-info tokens. Markup intacto. */
.preview-context {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  margin-bottom: 10px;
  background: var(--callout-info-bg);
  color: var(--callout-info-text);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
}
.preview-context-label {
  font-weight: var(--font-weight-medium);
  opacity: 0.8;
}
@media (prefers-color-scheme: dark) {
  /* .preview-context dark heredado de --callout-info-* dark overrides */
  .import-step-pill.active {
    background: rgba(55, 138, 221, 0.2);
    color: #9bc4ed;
  }
}

/* ─── L.3b — Meta del extracto en preview ─────────────── */

.preview-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  padding: 10px 12px;
  margin-bottom: 10px;
  background: var(--bg-canvas);
  border: 0.5px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.4;
}
.preview-meta-item { white-space: nowrap; }
.preview-meta-key {
  color: var(--text-tertiary);
  font-weight: 500;
  margin-right: 3px;
}
.preview-meta-warn {
  color: #B45309;
  font-weight: 500;
}
.preview-meta-hint {
  display: block;
  margin-top: 2px;
}
@media (prefers-color-scheme: dark) {
  .preview-meta-warn { color: #EAB308; }
}

/* ─── L.3c — Importaciones recientes ──────────────────── */
.recent-import-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0;
  border-bottom: 1px solid var(--border, #e5e7eb);
  gap: 12px;
}
.recent-import-row:last-child { border-bottom: none; }
.recent-import-row.reversed { opacity: 0.55; }

.recent-import-main {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.recent-import-date {
  font-size: 0.78rem;
  color: var(--muted, #888);
  white-space: nowrap;
}
.recent-import-name {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text, #111);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
}
.recent-import-type {
  font-size: 0.78rem;
  color: var(--muted, #888);
  white-space: nowrap;
}

.recent-import-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.recent-import-count {
  font-size: 0.82rem;
  color: var(--muted, #888);
  white-space: nowrap;
}
.recent-import-status {
  font-size: 0.72rem;
  background: #fee2e2;
  color: #991b1b;
  border-radius: 4px;
  padding: 1px 6px;
  white-space: nowrap;
}
@media (prefers-color-scheme: dark) {
  .recent-import-status { background: #450a0a; color: #fca5a5; }
}
/* H.2.2: sub-línea con count de reconciliaciones del batch.
   flex-basis: 100% fuerza nueva línea dentro de .recent-import-main (que tiene flex-wrap: wrap). */
.recent-import-recon {
  flex-basis: 100%;
  font-size: 0.75rem;
  color: #4A3FB3;
  line-height: 1.4;
  margin-top: 2px;
}
@media (prefers-color-scheme: dark) {
  .recent-import-recon { color: #B5AFE8; }
}
/* H.2.3: sub-línea convertida en botón clickeable. Reset de estilos default
   del <button> + cursor pointer + hover sutil. Mantiene el layout flex-basis:100%
   heredado de .recent-import-recon arriba. */
.recent-import-recon-link {
  background: none;
  border: none;
  padding: 0;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  /* hereda font-size, color, line-height, margin-top de .recent-import-recon */
}
.recent-import-recon-link:hover { color: #2E2576; text-decoration: underline; }
.recent-import-recon-link:focus { outline: none; text-decoration: underline; }
@media (prefers-color-scheme: dark) {
  .recent-import-recon-link:hover { color: #D6D1F5; }
}

/* H.2.3: chip de filtro activo en Compromisos · Pagadas.
   D1.3: hex migrados a primitivas purple (matching .badge-paid).
   Es un callout domain-specific — no encaja en intents genéricos,
   por eso usa primitives directamente en vez de --callout-* tokens. */
.pagadas-filter-chip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--color-purple-50);
  color: var(--color-purple-600);
  border-radius: 6px;
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
  font-size: 0.82rem;
  line-height: var(--leading-snug);
}
.pagadas-filter-chip-text { flex: 1; }
.pagadas-filter-chip-close {
  background: none;
  border: none;
  color: var(--color-purple-600);
  font-size: var(--font-size-lg);
  line-height: 1;
  padding: 0 4px;
  cursor: pointer;
  flex-shrink: 0;
  font-family: inherit;
}
.pagadas-filter-chip-close:hover { color: var(--color-purple-700); }
.pagadas-filter-chip-close:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}
@media (prefers-color-scheme: dark) {
  .pagadas-filter-chip {
    background: rgba(127, 119, 221, 0.18);
    color: var(--color-purple-100);
  }
  .pagadas-filter-chip-close { color: var(--color-purple-100); }
  .pagadas-filter-chip-close:hover { color: #FFFFFF; }
}

/* ─── L.3d / U1b.1 — Chips de subtype en Movimientos ──────────────────── */
/* Chip inline en pestaña Transacciones.
   Tinted backgrounds y border con alpha — candidatos a sistema de
   chip variants tinted en D1.3. !important defensivo conservado para
   asegurar prioridad sobre eventual inline style. */
.chip-pago-tarjeta {
  background: #FEF9C322 !important; /* amber-50 con alpha 13% */
  color: var(--color-amber-700) !important;
  border: 1px solid #FDE68A44; /* amber-200 con alpha 27% */
}
@media (prefers-color-scheme: dark) {
  .chip-pago-tarjeta {
    background: #451A0344 !important;
    color: var(--color-amber-200) !important;
    border-color: #78350F66;
  }
}

/* U1b.1 — chips por subtype de transferencia (ahorro / inversión / otro). */
.chip-ahorro {
  background: #7F77DD22 !important;
  color: #5B54B8 !important;
  border: 1px solid #7F77DD44;
}
.chip-inversion {
  background: #EF9F2722 !important;
  color: #B45309 !important;
  border: 1px solid #EF9F2744;
}
.chip-transferencia {
  background: #88878018 !important;
  color: #6B6A66 !important;
  border: 1px solid #88878040;
}
@media (prefers-color-scheme: dark) {
  .chip-ahorro        { background: #2A2752 !important; color: #B8B2F0 !important; border-color: #4A4490; }
  .chip-inversion     { background: #3A2A08 !important; color: #F0C674 !important; border-color: #7A5A1A; }
  .chip-transferencia { background: #2A2A28 !important; color: #B0AFA8 !important; border-color: #4A4A46; }
}

/* U1-IMPORT.1a.5 — botón-chip de acción en el preview de importación.
   Acción disponible (no estado): neutro a propósito — los chips de estado
   (chip-pago-tarjeta / chip-transferencia) quedan para filas ya marcadas.
   Reusa las métricas de .chip; el microcopy completo vive en title/aria-label. */
.chip-action {
  background: transparent;
  border: 1px solid var(--border-default);
  color: var(--text-secondary);
  cursor: pointer;
  font-family: inherit;
}
.chip-action:hover { border-color: var(--border-focus); color: var(--text-primary); }
.chip-action:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
/* Mobile: tap target un poco más generoso sin agrandar el chip en desktop. */
@media (max-width: 640px) {
  .chip-action { padding: 4px 10px; min-height: 28px; }
}

/* U1b.1 — acciones de clasificación en el editor de transacción. */
.ed-clasif {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}
.ed-clasif-note {
  font-size: 0.8rem;
  color: var(--secondary-text, #6B7280);
  margin: 0;
}

/* U1b.1 — aviso no bloqueante en Movimientos (ej. revisar categoría). */
.tx-msg {
  font-size: 0.82rem;
  color: var(--callout-warning-soft-text, #92400E);
  background: var(--callout-warning-soft-bg, #FEF9C3);
  border: 1px solid var(--callout-warning-soft-border, #FDE68A);
  border-radius: var(--radius-xs, 6px);
  padding: 6px var(--space-3, 12px);
  margin: var(--space-2, 8px) 0;
}
.tx-msg[hidden] { display: none; }

/* U1b.2 — resumen de totales por filtros en Movimientos. Compacto, flex-wrap,
   separado por moneda. Se oculta en bulk mode (lo maneja el JS). */
.tx-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px var(--space-3, 12px);
  padding: 8px var(--space-3, 12px);
  margin: var(--space-2, 8px) 0;
  font-size: 0.82rem;
  line-height: 1.5;
  background: var(--surface-2, #F6F6F4);
  border: 1px solid var(--border-color, #E5E3DE);
  border-radius: var(--radius-xs, 6px);
}
.tx-summary[hidden] { display: none; }
.tx-sum-acc { font-weight: 600; padding-right: var(--space-2, 8px); border-right: 1px solid var(--border-color, #E5E3DE); }
.tx-sum-count { font-weight: 600; }
.tx-sum-cur {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 8px;
}
.tx-sum-cur-label { font-weight: 600; opacity: 0.65; }
.tx-sum-in  { color: #1D9E75; }
.tx-sum-out { color: #D85A30; }
.tx-sum-net { font-weight: 600; }
.tx-sum-transfers { opacity: 0.7; font-style: italic; }
/* U1b.6b: bloque "Top categorías con estos filtros" (reusa .cat-bar-* y .dot).
   width:100% lo baja a su propia línea dentro del flex-wrap de .tx-summary. */
.tx-sum-bycat { width: 100%; margin-top: 6px; padding-top: 8px; border-top: 0.5px solid var(--border-default); }
.tx-sum-bycat-title { margin: 0 0 6px; font-weight: 600; }
.tx-sum-bycat-note { margin: 0 0 8px; font-style: italic; opacity: 0.75; font-size: 0.76rem; }
.tx-sum-bycat-empty { margin: 0; opacity: 0.7; font-size: 0.78rem; }
.tx-sum-bycat-cur { margin-bottom: 8px; }
.tx-sum-bycat-cur:last-child { margin-bottom: 0; }
.tx-sum-bycat-cur .tx-sum-cur-label { display: block; margin-bottom: 4px; }
@media (prefers-color-scheme: dark) {
  .tx-summary { background: #23262B; border-color: #333; }
}
@media (max-width: 480px) {
  .tx-summary { font-size: 0.78rem; gap: 2px 8px; }
}

/* U1b.4 — paginación de Movimientos. Controles debajo del listado. */
.tx-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2, 8px) var(--space-3, 12px);
  margin: var(--space-3, 12px) 0 0;
  font-size: 0.82rem;
}
.tx-pager[hidden] { display: none; }
.tx-pager-controls {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2, 8px);
}
.tx-pager-status { font-weight: 600; }
.tx-pager-range { opacity: 0.7; }
.tx-pager-btn[disabled] { opacity: 0.4; cursor: default; }
@media (max-width: 480px) {
  .tx-pager { justify-content: center; font-size: 0.78rem; }
  .tx-pager-range { width: 100%; text-align: center; }
}

@media (max-width: 480px) {
  .recent-import-name { max-width: 120px; }
}

/* ════════════════════════════════════════════════════════════════
   D1.3 — SISTEMA DE CALLOUTS
   ════════════════════════════════════════════════════════════════
   Clase base .app-callout + 5 variantes (info/success/warning/danger/
   neutral) + subcomponentes (__icon, __body, __title, __action,
   __close). Cada variante setea custom properties locales que la
   base consume — permite extender el sistema sin duplicar reglas.

   Aplicado en D1.3 a: .legacy-banner, .legacy-banner-error,
   .preview-context, .pagadas-filter-chip y
   familias relacionadas (ver bloques específicos más abajo).

   Variante extra: --warning-soft (mismo intent que warning, pero
   más sutil — usada para chips/badges dentro de filas).
   ════════════════════════════════════════════════════════════════ */

.app-callout {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
  border: 1px solid var(--callout-border, var(--border-default));
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
  line-height: var(--leading-snug);
  background: var(--callout-bg, var(--bg-canvas));
  color: var(--callout-text, var(--text-primary));
}

.app-callout__icon {
  flex-shrink: 0;
  font-size: var(--font-size-md);
  line-height: 1;
}

.app-callout__body {
  flex: 1;
  min-width: 0;
}

.app-callout__title {
  font-weight: var(--font-weight-medium);
  margin-bottom: var(--space-1);
  line-height: var(--leading-snug);
}

.app-callout__action {
  flex-shrink: 0;
  background: transparent;
  border: none;
  color: inherit;
  font-weight: var(--font-weight-semibold);
  font-family: inherit;
  font-size: inherit;
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}
.app-callout__action:hover { text-decoration: none; }
.app-callout__action:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  text-decoration: none;
  border-radius: 2px;
}

.app-callout__close {
  flex-shrink: 0;
  background: transparent;
  border: none;
  color: inherit;
  opacity: 0.55;
  cursor: pointer;
  font-size: var(--font-size-lg);
  line-height: 1;
  padding: 0 4px;
  font-family: inherit;
}
.app-callout__close:hover { opacity: 1; }
.app-callout__close:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  opacity: 1;
}

/* ──── Variantes ─────────────────────────────────────────────── */

.app-callout--info {
  --callout-bg:     var(--callout-info-bg);
  --callout-border: var(--callout-info-border);
  --callout-text:   var(--callout-info-text);
}

.app-callout--success {
  --callout-bg:     var(--callout-success-bg);
  --callout-border: var(--callout-success-border);
  --callout-text:   var(--callout-success-text);
}

.app-callout--warning {
  --callout-bg:     var(--callout-warning-bg);
  --callout-border: var(--callout-warning-border);
  --callout-text:   var(--callout-warning-text);
}

.app-callout--warning-soft {
  --callout-bg:     var(--callout-warning-soft-bg);
  --callout-border: var(--callout-warning-soft-border);
  --callout-text:   var(--callout-warning-soft-text);
}

.app-callout--danger {
  --callout-bg:     var(--callout-danger-bg);
  --callout-border: var(--callout-danger-border);
  --callout-text:   var(--callout-danger-text);
}

.app-callout--neutral {
  --callout-bg:     var(--callout-neutral-bg);
  --callout-border: var(--callout-neutral-border);
  --callout-text:   var(--callout-neutral-text);
}

/* ════════════════════════════════════════════════════════════════
   D1.4 — SISTEMA DE EMPTY STATES
   ════════════════════════════════════════════════════════════════
   Clase base .empty-state + 3 modifiers (--initial / --inline /
   --informative) + subcomponentes (__icon, __title, __body, __actions).

   Variantes:
   - .empty-state (default): full-section, borde dashed, padding generoso.
     Para tableros principales sin data (ej. Cuentas primer uso).
   - .empty-state--initial: idem default pero más grande. Mensaje de
     bienvenida cuando es la primera vez del usuario.
   - .empty-state--inline: compacto, sin borde. Para empty states que
     viven adentro de una .card (Dashboard top-cats, Compromisos lista,
     etc.). No agrega box-in-box visual.
   - .empty-state--informative: tono "explicativo" (no acción primaria).
     Para post-filter o post-undo donde el vacío es contextual.
   ════════════════════════════════════════════════════════════════ */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--space-10) var(--space-6);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  margin-bottom: var(--space-3);
}

.empty-state__icon {
  font-size: 38px;
  line-height: 1;
  margin-bottom: var(--space-2);
  opacity: 0.85;
}

.empty-state__title {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-medium);
  color: var(--text-primary);
  margin: 0 0 var(--space-2);
  line-height: var(--leading-snug);
}

.empty-state__body {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: var(--leading-relaxed);
  max-width: 480px;
  margin: 0 auto var(--space-4);
}

.empty-state__body:last-child { margin-bottom: 0; }

.empty-state__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  justify-content: center;
  align-items: center;
}

.empty-state__actions .btn-primary,
.empty-state__actions .btn-secondary {
  width: auto;
  margin-top: 0;
  padding: var(--space-2) var(--space-5);
}

/* ──── Variantes ─────────────────────────────────────────────── */

/* Primer uso — pantalla casi vacía: ícono más grande, padding más generoso. */
.empty-state--initial {
  padding: var(--space-12) var(--space-6);
}
.empty-state--initial .empty-state__icon {
  font-size: 48px;
}

/* Inline — sin borde, padding mínimo. Para empty states dentro de .card. */
.empty-state--inline {
  border: none;
  padding: var(--space-6) var(--space-3);
  background: transparent;
  margin-bottom: 0;
}
.empty-state--inline .empty-state__icon {
  font-size: 28px;
  margin-bottom: var(--space-1);
}
.empty-state--inline .empty-state__title {
  font-size: var(--font-size-base);
  margin-bottom: var(--space-1);
}
.empty-state--inline .empty-state__body {
  font-size: var(--font-size-sm);
  margin-bottom: var(--space-3);
}

/* Informative — tono explicativo (post-filter, sin acción primaria).
   Atenúa ícono y título. Mantiene padding del default. */
.empty-state--informative .empty-state__icon {
  opacity: 0.6;
}
.empty-state--informative .empty-state__title {
  font-weight: var(--font-weight-regular);
  color: var(--text-secondary);
}

@media (max-width: 480px) {
  .empty-state {
    padding: var(--space-8) var(--space-4);
  }
  .empty-state--initial {
    padding: var(--space-10) var(--space-4);
  }
  .empty-state--inline {
    padding: var(--space-5) var(--space-2);
  }
}

/* ════════════════════════════════════════════════════════════════
   D1.7 — SISTEMA DE SKELETONS
   ════════════════════════════════════════════════════════════════
   Placeholders animados para reducir el flash de "UI vacía" mientras
   JS espera datos de Supabase. Aplicado vía:
   (a) Markup estático en index.html dentro de contenedores que JS
       sobreescribe con innerHTML (zero arch change).
   (b) Reemplazo de "Cargando..." existente por markup skeleton.

   Animación shimmer suave (1.6s ease-in-out infinite).
   Respeta prefers-reduced-motion: en ese caso, sin animación.
   ════════════════════════════════════════════════════════════════ */

.skeleton {
  background: linear-gradient(
    90deg,
    var(--skeleton-bg) 0%,
    var(--skeleton-highlight) 50%,
    var(--skeleton-bg) 100%
  );
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.6s ease-in-out infinite;
  border-radius: var(--radius-xs);
  display: block;
}

@keyframes skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton {
    animation: none;
    background: var(--skeleton-bg);
  }
}

/* Helpers de tamaño y forma — composables con .skeleton */
.skeleton-line {
  height: 12px;
  width: 100%;
  margin-bottom: var(--space-2);
}
.skeleton-line:last-child { margin-bottom: 0; }
.skeleton-line--sm { height: 10px; }
.skeleton-line--md { height: 14px; }
.skeleton-line--lg { height: 18px; }
.skeleton-line--xl { height: 24px; }

.skeleton-line--w-20 { width: 20%; }
.skeleton-line--w-30 { width: 30%; }
.skeleton-line--w-40 { width: 40%; }
.skeleton-line--w-50 { width: 50%; }
.skeleton-line--w-60 { width: 60%; }
.skeleton-line--w-70 { width: 70%; }
.skeleton-line--w-80 { width: 80%; }

.skeleton-pill {
  height: 22px;
  width: 80px;
  border-radius: var(--radius-full);
  display: inline-block;
}

.skeleton-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.skeleton-chart {
  height: 160px;
  width: 100%;
  border-radius: var(--radius-sm);
}

/* Fila tipo .tx-row o .inv-row: dot + stack texto + monto a la derecha */
.skeleton-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 9px 0;
  border-bottom: 0.5px solid var(--border-default);
}
.skeleton-row:last-child { border-bottom: none; }
.skeleton-row-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.skeleton-row-amount {
  width: 70px;
  height: 14px;
  flex-shrink: 0;
}

/* Grupo de skeleton-rows (lista mientras carga) */
.skeleton-stack {
  display: flex;
  flex-direction: column;
}

/* Tile métrica — placeholder de hero/metric card */
.skeleton-metric {
  padding: var(--space-3);
}
.skeleton-metric .skeleton-line:first-child {
  height: 10px;
  width: 60%;
  margin-bottom: var(--space-2);
}
.skeleton-metric .skeleton-line:last-child {
  height: 20px;
  width: 80%;
}
/* D2.1 — Skeleton modifier para el tile lead del hero (Ahorro).
   Mantiene proporciones con .hero-metric--lead: el padding usa el
   mismo --metric-hero-padding (que tras iter 2 = --space-3, igual
   al default), y altura del placeholder del valor coincidente con
   --font-size-display (26px desktop, 22px mobile). */
.skeleton-metric--lead {
  padding: var(--metric-hero-padding);
}
.skeleton-metric--lead .skeleton-line:last-child {
  /* D2.1 iter 2: 28→22 acompañando el ajuste de --font-size-display */
  height: 22px;
}
@media (max-width: 480px) {
  /* D2.1 iter 2: mobile lead font baja a 22px → skeleton 18px */
  .skeleton-metric--lead .skeleton-line:last-child {
    height: 18px;
  }
}

/* ════════════════════════════════════════════════════════════════
   D2.1 — TIPOGRAFÍA NUMÉRICA TABULAR
   ════════════════════════════════════════════════════════════════
   Aplica `font-variant-numeric: tabular-nums` a todas las cifras
   relevantes para que se alineen verticalmente en columnas y
   tengan ancho consistente (números fijos en lugar de proporcionales).
   Sin cambiar tamaños ni colores — solo el rendering numérico.

   Cubre: métricas, hero values, montos de transacciones, valores de
   patrimonio, totales bulk, sumarios de sección Cuentas, montos
   top categorías, sub-textos de métricas, deltas YoY y skeletons.
   ════════════════════════════════════════════════════════════════ */
.metric-value,
.metric-sub,
.cur-val,
.tx-amount,
.liq-value,
.bulk-count,
.bulk-totals,
.cat-bar-amt,
.cat-bar-pct,
.acc-section-total,
.acc-section-total-debt,
.delta-item,
.recent-import-count,
.skeleton-row-amount {
  font-variant-numeric: tabular-nums;
}

/* ════════════════════════════════════════════════════════════════
   D2.2.2 — HERO NARRATIVO MOBILE-FIRST
   ════════════════════════════════════════════════════════════════
   Reescritura del hero del Dashboard de "3 tiles enumerativos" a
   experiencia narrativa: headline editorial + microcopy comparativo
   contra mes anterior + barras proporcionales ingresos vs gastos.

   Mobile-first: el CSS base apunta a 360×667 (iPhone SE-class). Las
   reglas @media (min-width: 640px) son enhancements para desktop.

   Sin emojis ni iconografía nueva (decisión #5). Solo dots de color
   y barras tokenizadas con --bucket-liquidez / --intent-danger.

   Conserva D2.1: tabular-nums, --font-size-display, safe-area.
   ════════════════════════════════════════════════════════════════ */

/* Navegación de período: ◀ Mes YYYY ▶ — centrada y compacta en mobile */
.hero-period-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.hero-period-nav .dash-period-label {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  font-weight: var(--font-weight-medium);
  /* D2.2.2 iter: NO usar text-transform: capitalize aquí — capitaliza
     todas las palabras y rompe "Abril de 2026" → "Abril De 2026".
     fmtPeriodLabel() ya devuelve el formato correcto. */
  min-width: 130px;
  text-align: center;
}

/* D2.2.2 iter — Wrapper interno del hero para limitar ancho del
   contenido narrativo en desktop. En mobile no hace nada (viewport
   < max-width), en pantallas grandes evita que headline + microcopy
   + barras se estiren hasta el borde del card. */
.hero-narrative-inner {
  max-width: 720px;
  margin-left: auto;
  margin-right: auto;
}

/* D2.2.3 — Línea compacta de compromisos integrada al hero.
   Aparece después de las barras cuando hay compromisos pendientes
   del mes. Si no hay, el slot queda oculto (display:none vía JS).
   En error de fetch, también queda oculto — el hero sigue funcional.

   Color del dot: var(--bucket-patrimonio) — purple, consistente con
   la familia visual de commitments en el resto de la app (.badge-paid,
   .pagadas-filter-chip, etc.). */
.hero-commitments {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 0.5px solid var(--border-subtle);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  line-height: var(--leading-snug);
}
.hero-commitments .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--bucket-patrimonio);
  flex-shrink: 0;
}
.hero-commitments-text {
  flex: 1;
  min-width: 0;
  font-variant-numeric: tabular-nums;
}
.hero-commitments-link {
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  font-weight: var(--font-weight-medium);
  color: var(--intent-info);
  text-decoration: underline;
  text-underline-offset: 2px;
  flex-shrink: 0;
  white-space: nowrap;
}
.hero-commitments-link:hover { text-decoration: none; }
.hero-commitments-link:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  border-radius: 2px;
  text-decoration: none;
}

/* Headline: el dato principal del mes en formato editorial */
.hero-headline {
  font-size: 28px;
  font-weight: var(--font-weight-semibold);
  line-height: var(--leading-tight);
  color: var(--text-primary);
  margin: 0 0 var(--space-2);
}
.hero-headline .hero-amount {
  font-variant-numeric: tabular-nums;
}

/* Microcopy comparativo: trend vs mes anterior o info de contexto */
.hero-microcopy {
  font-size: var(--font-size-base);
  line-height: var(--leading-snug);
  color: var(--text-secondary);
  margin: 0 0 var(--space-4);
}

/* Barras proporcionales: ingresos como 100%, gastos como % de ingresos */
.hero-bars {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.hero-bar-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.hero-bar-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: var(--font-size-sm);
}
.hero-bar-label {
  color: var(--text-secondary);
  font-weight: var(--font-weight-medium);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.hero-bar-label .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.hero-bar-amount {
  font-variant-numeric: tabular-nums;
  font-weight: var(--font-weight-medium);
  color: var(--text-primary);
}
.hero-bar-track {
  height: 8px;
  background: var(--bg-canvas);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.hero-bar-fill {
  height: 100%;
  border-radius: var(--radius-full);
  transition: width var(--duration-slow) var(--ease-out);
}
.hero-bar-fill--ingresos { background: var(--bucket-liquidez); }
.hero-bar-fill--gastos   { background: var(--intent-danger); }

/* Desktop enhancement: headline más editorial, padding del hero más generoso */
@media (min-width: 640px) {
  .hero-headline {
    font-size: 32px;
  }
}

/* ============================================================
   BETA.1 — Trust, onboarding y acceso para beta cerrada
   ============================================================ */

/* --- Importar: bloque "Cómo se procesa tu archivo" (colapsable) --- */
.import-trust {
  border: 0.5px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--bg-canvas);
  margin-bottom: 12px;
  overflow: hidden;
}
.import-trust-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--text-secondary);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.import-trust-summary::-webkit-details-marker { display: none; }
.import-trust-summary::after {
  content: "›";
  margin-left: auto;
  font-size: 16px;
  color: var(--text-tertiary);
  transition: transform 0.18s;
  display: inline-block;
}
.import-trust[open] .import-trust-summary::after { transform: rotate(90deg); }
.import-trust-summary:hover { color: var(--text-primary); }
.import-trust-list {
  margin: 0;
  padding: 0 14px 12px 14px;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.import-trust-list li {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  padding-left: 16px;
  position: relative;
  line-height: 1.5;
}
.import-trust-list li::before {
  content: "·";
  position: absolute;
  left: 4px;
  color: var(--text-tertiary);
  font-size: 18px;
  line-height: 1.2;
}

/* --- Preview: badge de origen (IA) --- */
.badge-ai {
  font-size: var(--font-size-xs);
  padding: 2px 9px;
  border-radius: 10px;
  background: var(--color-blue-50);
  color: var(--color-blue-800);
  font-weight: var(--font-weight-medium);
}

/* --- Login: links de privacidad y trust modal --- */
.login-trust-links {
  margin-top: 4px;
  line-height: 1.6;
}
.btn-trust-link {
  display: inline;
  background: none;
  border: none;
  padding: 0;
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  font-family: inherit;
}
.btn-trust-link:hover { color: var(--text-primary); }

/* --- Trust modal: lista de items --- */
.trust-modal-list {
  margin: 0 0 0 16px;
  padding: 0;
  list-style: disc;
  display: flex;
  flex-direction: column;
  gap: 6px;
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  line-height: 1.5;
}
.trust-modal-section {
  margin-bottom: 4px;
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
}

/* --- Google OAuth button --- */
.btn-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 10px 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
  margin-bottom: 14px;
}
.btn-google:hover:not(:disabled) { background: var(--bg-canvas); }
.btn-google:disabled { opacity: 0.6; cursor: not-allowed; }
.btn-google-icon { flex: 0 0 auto; width: 18px; height: 18px; }

/* Microcopy bajo el botón Google deshabilitado */
.login-google-soon {
  margin-top: -8px;
  margin-bottom: 12px;
  text-align: center;
}

/* --- Login divider "o continuá con" --- */
.login-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  color: var(--text-tertiary);
  font-size: var(--font-size-xs);
}
.login-divider::before,
.login-divider::after {
  content: "";
  flex: 1;
  height: 0.5px;
  background: var(--border-default);
}

/* ══════════ UX-2b — Toasts ══════════
   Feedback no intrusivo que reemplaza los alert() informativos (los
   críticos siguen como alert, ver toast() en app.js). Región fija
   bottom-center; en mobile queda POR ENCIMA del FAB (56px + margen)
   para no taparlo. z-index 1100 > modales (1000): el resultado de una
   acción lanzada desde un modal debe verse. Variantes con los tokens
   --callout-* existentes (dark mode heredado de los tokens). */
.toast-region {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 24px;
  z-index: 1100;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: stretch;
  width: min(480px, calc(100vw - 32px));
  pointer-events: none; /* la región no bloquea clicks; cada toast sí los recibe */
}
@media (max-width: 640px) {
  .toast-region { bottom: calc(84px + var(--safe-bottom)); }
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--callout-neutral-border);
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-size: 13px;
  line-height: 1.45;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
  pointer-events: auto;
  animation: toast-in .18s ease-out;
}
/* pre-line: preserva los \n de los resúmenes multilínea sin tocar sus strings */
.toast-msg { white-space: pre-line; min-width: 0; }
.toast-success { background: var(--callout-success-bg); border-color: var(--callout-success-border); color: var(--callout-success-text); }
.toast-info    { background: var(--callout-info-bg);    border-color: var(--callout-info-border);    color: var(--callout-info-text); }
.toast-warning { background: var(--callout-warning-bg); border-color: var(--callout-warning-border); color: var(--callout-warning-text); }
.toast-error   { border-color: var(--red); border-left: 3px solid var(--red); }
.toast-close {
  flex-shrink: 0;
  margin-left: auto;
  background: none;
  border: none;
  padding: 0 2px;
  font-size: 15px;
  line-height: 1.2;
  color: inherit;
  opacity: .6;
  cursor: pointer;
}
.toast-close:hover { opacity: 1; }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
}
