/* ============================================================
   Asesor Financiero de Élite — estilos (paleta validada, modo claro/oscuro)
   ============================================================ */
:root {
  --page:        #f9f9f7;
  --surface:     #fcfcfb;
  --ink-1:       #0b0b0b;
  --ink-2:       #52514e;
  --muted:       #898781;
  --grid:        #e1e0d9;
  --baseline:    #c3c2b7;
  --border:      rgba(11, 11, 11, 0.10);
  --chat-bg:     #f1f5f9;   /* fondo del hilo de chat */
  --s-income:    #2a78d6;   /* azul  — ingresos / progreso */
  --s-expense:   #e34948;   /* rojo  — gastos */
  --good:        #006300;
  --critical:    #d03b3b;
  --warning:     #b45309;
  --accent:      #2a78d6;
  --border-soft: rgba(11, 11, 20, 0.06);
  --shadow-goal: 0 8px 22px rgba(42, 120, 214, 0.15);
  --shadow-card: 0 1px 2px rgba(15, 20, 40, 0.025), 0 5px 16px rgba(15, 20, 40, 0.035);
  --track:       color-mix(in srgb, var(--grid) 50%, transparent);
  /* Lavado casi imperceptible del color de marca sobre --page, para el fondo
     del hero de Home (SOFIA_DESIGN_SYSTEM.md §2). */
  --atmos:       color-mix(in srgb, var(--accent) 2.5%, var(--page));
}
@media (prefers-color-scheme: dark) {
  :root {
    --page:      #0d0d0d;
    --surface:   #1a1a19;
    --ink-1:     #ffffff;
    --ink-2:     #c3c2b7;
    --muted:     #898781;
    --grid:      #2c2c2a;
    --baseline:  #383835;
    --border:    rgba(255, 255, 255, 0.10);
    --chat-bg:   #131315;
    --s-income:  #3987e5;
    --s-expense: #e66767;
    --good:      #0ca30c;
    --critical:  #d03b3b;
    --warning:   #f59e0b;
    --accent:    #3987e5;
    --border-soft: rgba(255, 255, 255, 0.06);
    --shadow-goal: 0 8px 22px rgba(0, 0, 0, 0.26);
    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.10), 0 5px 16px rgba(0, 0, 0, 0.08);
    --track:       color-mix(in srgb, var(--grid) 65%, transparent);
    --atmos:       color-mix(in srgb, var(--accent) 3%, var(--page));
  }
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--page);
  color: var(--ink-1);
  line-height: 1.45;
}
.hidden { display: none !important; }
.muted { color: var(--muted); font-size: 0.85rem; }
.error { color: var(--critical); font-size: 0.9rem; }
.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- Botones ---------- */
.btn {
  border: 1px solid var(--border); background: var(--surface); color: var(--ink-1);
  padding: 8px 14px; border-radius: 8px; cursor: pointer; font-size: 0.9rem;
}
.btn:hover { border-color: var(--accent); }
.btn-primary {
  background: linear-gradient(135deg, var(--accent), #7a5fd0);
  color: #fff; border-color: transparent; letter-spacing: 0.015em;
  transition: transform 0.16s ease, box-shadow 0.16s ease, filter 0.16s ease;
}
.btn-primary:hover {
  filter: brightness(1.07); transform: translateY(-1.5px);
  box-shadow: 0 7px 18px rgba(42, 120, 214, 0.38);
}
.btn-primary:active { transform: translateY(0); }
.btn-ghost { background: transparent; }
.btn-small { padding: 5px 10px; font-size: 0.8rem; }
.btn-link { background: none; border: none; color: var(--accent); cursor: pointer; margin-top: 12px; font-size: 0.85rem; }
.btn-del { background: none; border: none; color: var(--muted); cursor: pointer; }
.btn-del:hover { color: var(--critical); }

/* ---------- Acceso ---------- */
/* 100vh de respaldo; 100dvh donde exista, para que en iOS no sobre pantalla
   por debajo cuando la barra de direcciones está visible. */
/* `minmax(0, 400px)` y no `place-items: center` a secas. Con la pista en `auto`,
   la rejilla CRECE con su contenido: el botón de Google mide 280 px fijos, y el
   `width: 100%` de la tarjeta se calculaba sobre esa pista ya crecida. Resultado
   medido: 354 px de tarjeta en una pantalla de 345, con scroll horizontal, en la
   PRIMERA pantalla que ve cualquiera y con la fuente en normal. El `minmax(0, …)`
   es lo que impide que la pista supere al contenedor. */
.auth-wrap {
  min-height: 100vh; min-height: 100dvh;
  display: grid; grid-template-columns: minmax(0, 400px);
  justify-content: center; align-items: center; padding: 24px;
}
.auth-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
  padding: 36px; width: 100%; max-width: 100%; text-align: center;
}
.brand { display: flex; align-items: center; justify-content: center; gap: 10px; }
.brand h1 { font-size: 1.3rem; margin: 0; }
.brand h1 span { color: var(--accent); }
.brand.small strong { font-size: 1rem; }
.brand-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--accent); }
.auth-sub { color: var(--ink-2); margin: 8px 0 24px; }
.auth-form { display: flex; flex-direction: column; gap: 14px; text-align: left; }
.auth-form label { font-size: 0.8rem; color: var(--ink-2); display: flex; flex-direction: column; gap: 4px; }
input, select {
  background: var(--page); color: var(--ink-1); border: 1px solid var(--border);
  border-radius: 8px; padding: 9px 12px; font-size: 0.9rem; font-family: inherit;
}
input:focus, select:focus { outline: 2px solid var(--accent); outline-offset: 0; border-color: transparent; }

/* ---------- Estructura ---------- */
.topbar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 24px; border-bottom: 1px solid var(--border); background: var(--surface);
  position: sticky; top: 0; z-index: 5;
}
.topbar-right { display: flex; gap: 12px; align-items: center; }
.layout {
  display: grid; grid-template-columns: 1fr 380px; gap: 20px;
  padding: 20px 24px; max-width: 1400px; margin: 0 auto;
}
@media (max-width: 1000px) { .layout { grid-template-columns: 1fr; } }
.col-main { display: flex; flex-direction: column; gap: 20px; min-width: 0; }

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 18px; padding: 20px 22px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.05);
}
/* Sección plana (§5.12 🔒): contenido secundario que no necesita el peso de una
   tarjeta, pero que convive con las que sí la tienen. Alterna la densidad para
   romper el "tarjeta, tarjeta, tarjeta" (§17.9). Es la EXCEPCIÓN, no la norma.

   §5.12 escribe `padding: 26px 19px 0` y explica que el horizontal debe ser "el
   mismo que .card". En este código .card usa 22px, no 19: el 19 venía del mockup,
   que tenía otras métricas. Se sigue la INTENCIÓN —alinear con la tarjeta— y no
   el número, porque con 19px las dos columnas de texto no cuadrarían por 3px.

   El padding-top de 26px se reduce a 6px: §5.12 lo escribió para una maqueta sin
   gap entre bloques, y aquí .col-main ya separa 20px. Sumar los dos daría 46px
   de aire solo encima de esta sección.

   Y son 23px, no 22: .card tiene borde de 1px, así que su contenido empieza a
   22+1. Medido en el navegador, con 22px los títulos caían en 47/46/47 — un
   píxel de desalineación, invisible de uno en uno y visible en la columna. */
.flat-sec { padding: 6px 23px 0; }
.flat-sec .card-head h2 { font-size: 0.95rem; }

.card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.card-head h2 { font-size: 1rem; margin: 0; }

/* ---------- Acciones rápidas: píldoras ligeras (Home v5, SOFIA_DESIGN_SYSTEM.md §5.13) ---------- */
/* `flex-wrap` para que las píldoras pasen a dos filas antes que salirse: con el
   tamaño de fuente del sistema al 160 %, «Preguntar a Sofía» se salía 38 px de
   la pantalla. Y `min-width: 0` en cada una, porque `flex: 1` NO permite encoger
   por debajo del ancho intrínseco del texto sin él. */
.qa-row { display: flex; gap: 8px; flex-wrap: wrap; }
.qa-pill {
  flex: 1 1 0; min-width: 0; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font: inherit; font-size: 0.74rem; font-weight: 550; color: var(--ink-2); letter-spacing: 0.006em;
  background: var(--surface); border: 1px solid var(--border-soft); border-radius: 999px;
  padding: 0 4px; min-height: 40px; cursor: pointer;
}
.qa-pill svg { width: 15px; height: 15px; flex-shrink: 0; display: block; }
.qa-pill.out svg  { color: var(--critical); }
.qa-pill.in svg   { color: var(--good); }
.qa-pill.goal svg { color: #d68a12; }
.qa-pill.ai svg   { color: #7a5fd0; }

/* ---------- Bloque 1 — checklist (wireframe-home-v1.md), Home v5 ---------- */
.check-chips {
  display: flex; flex-wrap: wrap; align-items: center; gap: 7px; min-height: 22px;
  width: 100%; background: none; border: 0; padding: 0; margin: 0 0 14px; cursor: pointer;
  font: inherit; text-align: left;
}
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.67rem; font-weight: 600; letter-spacing: 0.012em;
  padding: 5px 11px; border-radius: 999px; line-height: 1;
}
.chip.ok   { background: color-mix(in srgb, var(--good) 8%, transparent); color: var(--good); }
.chip.warn { background: color-mix(in srgb, var(--warning) 10%, transparent); color: var(--warning); }
.chip svg { flex-shrink: 0; display: block; }

/* ---------- Meta Norte (Home v5, SOFIA_DESIGN_SYSTEM.md §7.4/§20.2) ---------- */
.goal-card {
  background: linear-gradient(135deg, var(--accent) 0%, #7a5fd0 100%);
  border: none; color: #fff;
  box-shadow: var(--shadow-goal);
  padding: 19px 20px;
}
.goal-card .goal-top { display: flex; justify-content: space-between; align-items: center; font-size: 0.78rem; font-weight: 650; letter-spacing: 0.005em; }
.goal-card .goal-name { display: inline-flex; align-items: center; gap: 7px; }
.goal-card .goal-name svg { display: block; opacity: 0.95; }
.goal-top .muted-w { color: rgba(255,255,255,0.72); font-weight: 500; }
.goal-pct { font-size: 1.9rem; font-weight: 730; margin: 10px 0 12px; font-variant-numeric: tabular-nums; letter-spacing: -0.016em; line-height: 1; }
.goal-card .goal-bar { height: 7px; border-radius: 999px; background: rgba(255,255,255,0.22); overflow: hidden; }
.goal-card .goal-bar > div { height: 100%; border-radius: 999px; background: #fff; transition: width 0.6s ease; }
.goal-note { margin-top: 12px; font-size: 0.82rem; color: rgba(255,255,255,0.93); line-height: 1.55; }
.goal-note b { font-weight: 720; }

/* La tarjeta vive sobre fondo oscuro: el botón es de vidrio, no de acento, para
   no competir con el porcentaje, que sigue siendo lo primero que se lee. */
.goal-cta {
  margin-top: 14px; width: 100%; min-height: 44px;
  font-family: inherit; font-size: 0.8rem; font-weight: 700; color: #fff;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px; padding: 11px 0; cursor: pointer;
  transition: background 140ms ease;
}
.goal-cta:hover { background: rgba(255, 255, 255, 0.2); }
.goal-cta:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .goal-cta { transition: none; } }

/* ---------- Análisis de Sofía (burbuja de diálogo) ---------- */
.sofia-insight-card {
  display: flex; gap: 14px; align-items: flex-start;
  background: linear-gradient(135deg, rgba(122, 95, 208, 0.12), rgba(42, 120, 214, 0.08));
  border-color: rgba(122, 95, 208, 0.30);
}
.insight-avatar {
  width: 56px; height: 56px; border-radius: 50%; object-fit: cover;
  object-position: center 26%; border: 2px solid var(--accent); flex-shrink: 0;
  box-shadow: 0 3px 10px rgba(42, 120, 214, 0.30);
}
.insight-bubble {
  position: relative; flex: 1; background: var(--surface);
  border: 1px solid var(--border); border-radius: 14px; padding: 10px 14px 12px;
}
.insight-bubble::before {
  content: ''; position: absolute; left: -7px; top: 20px;
  width: 12px; height: 12px; background: var(--surface);
  border-left: 1px solid var(--border); border-bottom: 1px solid var(--border);
  transform: rotate(45deg);
}
.insight-head { display: flex; justify-content: space-between; align-items: center; font-size: 0.85rem; }
/* Cerrar la bienvenida (Paso 17.6). Discreta: es un mensaje suyo, no un anuncio
   que estorbe — pero tiene que poder cerrarse, o pasa a ser justo eso. */
.insight-x {
  background: none; border: none; cursor: pointer; padding: 0 2px;
  font-size: 1.15rem; line-height: 1; color: var(--muted); font-family: inherit;
}
.insight-x:hover { color: var(--ink-2); }
#insight-text { margin: 6px 0 0; font-size: 0.88rem; line-height: 1.5; color: var(--ink-2); }

/* ---------- "Sofía detectó esto": tarjeta condicional de aviso (§7.4, bloque 3)
   Valores tomados literalmente de home-mockup-final.html. Las clases hijas van
   acotadas bajo .insight-card para no confundirse con el #insight-text del
   Diagnóstico, que es otra cosa. La variante .good queda definida pero sin uso
   todavía: la tarjeta de celebración necesita un detector que aún no existe. */
.insight-card { display: flex; gap: 12px; align-items: flex-start; }
.insight-card.warn { border-color: color-mix(in srgb, var(--warning) 22%, var(--border-soft)); }
.insight-card.good { border-color: color-mix(in srgb, var(--good) 18%, var(--border-soft)); }
.insight-ico {
  width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
  display: grid; place-items: center; margin-top: 1px;
}
.insight-card.warn .insight-ico { background: color-mix(in srgb, var(--warning) 11%, transparent); color: var(--warning); }
.insight-card.good .insight-ico { background: color-mix(in srgb, var(--good) 9%, transparent); color: var(--good); }
.insight-card .insight-title { font-size: 0.8rem; font-weight: 700; margin-bottom: 4px; letter-spacing: 0.002em; }
.insight-card .insight-text { font-size: 0.82rem; color: var(--ink-2); line-height: 1.55; }
.insight-card .insight-action {
  display: inline-block; margin-top: 8px; font-size: 0.76rem; font-weight: 700;
  color: var(--accent); background: none; border: none; padding: 0;
  font-family: inherit; cursor: pointer;
}
/* Sofía tenía algo que decir y no pudo escribirlo. Anula el ámbar de .warn: no
   es una alerta sobre el dinero del usuario, es una avería nuestra, y pintarla
   con el color del peligro le haría mirar sus cuentas con miedo por un fallo
   que no es suyo. Se retira al gris del sistema y no ofrece ninguna acción. */
.insight-card.aviso-fallo { border-color: var(--border-soft); }
.insight-card.aviso-fallo .insight-ico { background: color-mix(in srgb, var(--ink-2) 8%, transparent); color: var(--ink-2); }
.insight-card.aviso-fallo .insight-title { color: var(--ink-2); font-weight: 600; }

/* ---------- Fichas KPI (rejilla asimétrica + micro-interacciones) ---------- */
.tiles { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; }
.tile {
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  padding: 16px 18px; display: flex; flex-direction: column; gap: 4px;
  position: relative; grid-column: span 2;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.tile:hover { transform: translateY(-3px); box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12); }
.tile-ico { position: absolute; top: 14px; right: 14px; color: var(--muted); opacity: 0.85; }
.hero-tile {
  grid-column: span 3;
  background: linear-gradient(135deg, rgba(42, 120, 214, 0.13), rgba(122, 95, 208, 0.10));
  border-color: rgba(42, 120, 214, 0.32);
}
.hero-tile .tile-value { font-size: 2rem; }
.hero-tile .tile-ico { color: var(--accent); }
.tile-label { font-size: 0.72rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.tile-value { font-size: 1.45rem; font-weight: 680; }
.tile-value.positive { color: var(--good); }
.tile-value.negative { color: var(--critical); }

/* ---------- Formularios embebidos ---------- */
.inline-form { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.inline-form input, .inline-form select { flex: 1 1 130px; }

/* ---------- Gráficos ---------- */
/* .chart-box, .legend, .legend-item y .legend-swatch se borraron el 2026-08-16
   con la gráfica de flujo de caja -eran suyas y de nadie más- y VUELVEN el
   2026-09-03 con el Balance del Libro, que es la misma gráfica recuperada y
   arreglada. La línea de patrimonio sigue sin usarlas: no lleva leyenda (una
   sola serie) y su SVG es responsivo por width:100%.

   El overflow-x de .chart-box no es un detalle: seis grupos de barras no caben
   en un móvil estrecho, y sin él la PÁGINA entera se desplazaría de lado. */
.chart-box { width: 100%; overflow-x: auto; }
.chart-box svg { display: block; }
.legend { display: flex; gap: 16px; margin: 4px 0 8px; font-size: 0.8rem; color: var(--ink-2); }
.legend-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-swatch { width: 10px; height: 10px; border-radius: 3px; }
.tooltip {
  position: fixed; pointer-events: none; z-index: 50;
  background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
  padding: 8px 10px; font-size: 0.78rem; color: var(--ink-1);
  box-shadow: 0 4px 14px rgba(0,0,0,0.18); max-width: 240px;
}
.tooltip .tt-title { font-weight: 600; margin-bottom: 2px; }
.tooltip .tt-row { display: flex; align-items: center; gap: 6px; font-variant-numeric: tabular-nums; }

/* ---------- Presupuesto del mes (rediseño premium) ---------- */
.bud-hero {
  background: linear-gradient(135deg, #1e293b 0%, #24324a 55%, #2a3f66 100%);
  border-radius: 16px; padding: 16px 16px 14px; margin-bottom: 14px;
  color: #e2e8f0; box-shadow: 0 8px 22px rgba(15, 23, 42, 0.28);
  position: relative; overflow: hidden;
}
.bud-hero::after {
  content: ''; position: absolute; top: -50%; right: -20%;
  width: 60%; height: 200%; pointer-events: none;
  background: radial-gradient(closest-side, rgba(42, 120, 214, 0.28), transparent 70%);
}
.bud-hero-kpis { display: flex; gap: 8px; position: relative; z-index: 1; }
.bud-kpi {
  flex: 1; background: rgba(255, 255, 255, 0.07); border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 12px; padding: 8px 10px; backdrop-filter: blur(4px);
}
.bud-kpi span {
  display: block; font-size: 0.6rem; text-transform: uppercase;
  letter-spacing: 0.08em; color: #94a3b8; font-weight: 700;
}
.bud-kpi b {
  display: block; font-size: 0.98rem; font-weight: 750; margin-top: 2px;
  color: #f8fafc; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.bud-kpi.is-pend b { color: #93c5fd; }
.bud-hero-track {
  height: 8px; border-radius: 5px; background: rgba(255, 255, 255, 0.12);
  overflow: hidden; margin-top: 12px; position: relative; z-index: 1;
}
.bud-hero-fill {
  height: 100%; border-radius: 5px; min-width: 6px;
  background: linear-gradient(90deg, #2a78d6, #7a5fd0);
  box-shadow: 0 0 10px rgba(122, 95, 208, 0.55);
  transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.bud-hero-note { font-size: 0.7rem; color: #94a3b8; margin-top: 7px; position: relative; z-index: 1; }
.bud-hero-note b { color: #e2e8f0; }

.bud-sec {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--muted); font-weight: 700; margin: 14px 2px 8px;
}
.bud-sec-sum { font-variant-numeric: tabular-nums; text-transform: none; letter-spacing: 0; font-weight: 650; }
#budget-body .bud-sec:first-child { margin-top: 2px; }

.bud-row {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 11px 12px 9px; border: 1px solid var(--border); border-radius: 14px;
  margin-bottom: 8px; background: var(--surface);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.bud-row:hover { transform: translateY(-1px); box-shadow: 0 5px 14px rgba(0, 0, 0, 0.08); }
.bud-row.is-done { border-color: color-mix(in srgb, var(--good) 35%, var(--border)); }
.bud-row.is-over { border-color: color-mix(in srgb, var(--critical) 40%, var(--border)); }
.bud-ico {
  width: 36px; height: 36px; border-radius: 12px; flex: none;
  display: grid; place-items: center; margin-top: 2px;
}
.bud-main { flex: 1; min-width: 0; }
.bud-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: 0.86rem; }
.bud-name { font-weight: 650; text-transform: capitalize; display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.bud-dia {
  font-size: 0.62rem; color: var(--accent); background: rgba(42, 120, 214, 0.12);
  padding: 1px 8px; border-radius: 999px; font-weight: 600; text-transform: none;
}
.bud-dia.alt { color: #8b5cf6; background: rgba(139, 92, 246, 0.13); }
.bud-nums { font-variant-numeric: tabular-nums; font-size: 0.82rem; white-space: nowrap; }
.bud-nums b { color: var(--ink-1); font-weight: 750; }
.bud-nums i { font-style: normal; color: var(--muted); font-size: 0.72rem; margin-left: 3px; }
.bud-track { height: 7px; border-radius: 5px; background: var(--grid); overflow: hidden; margin-top: 7px; }
.bud-fill {
  height: 100%; border-radius: 5px; min-width: 4px;
  transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.bud-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 6px; }
.bud-pill {
  font-size: 0.68rem; font-weight: 650; padding: 2px 9px; border-radius: 999px;
  background: rgba(42, 120, 214, 0.10); color: var(--accent);
}
.bud-pill.ok { background: color-mix(in srgb, var(--good) 14%, transparent); color: var(--good); }
.bud-pill.neg { background: color-mix(in srgb, var(--critical) 13%, transparent); color: var(--critical); }
.bud-pill.wait { background: rgba(42, 120, 214, 0.10); color: var(--accent); }
.bud-pill.idle { background: color-mix(in srgb, var(--muted) 14%, transparent); color: var(--muted); }

/* ---------- Suscripción: tarjetas de plan ---------- */
.billing-offers { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 8px; }
@media (max-width: 420px) { .billing-offers { grid-template-columns: 1fr; } }
.plan-card {
  position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  padding: 14px 14px 12px; border-radius: 14px; cursor: pointer; text-align: left;
  background: var(--surface); border: 1.5px solid var(--border);
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.plan-card:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.10); border-color: var(--accent); }
.plan-card.destacado {
  border-color: var(--accent);
  background: linear-gradient(160deg, rgba(42, 120, 214, 0.08), rgba(122, 95, 208, 0.08));
}
.plan-badge {
  position: absolute; top: -9px; right: 10px;
  font-size: 0.58rem; font-weight: 800; letter-spacing: 0.05em; color: #fff;
  background: linear-gradient(90deg, var(--accent), #7a5fd0);
  padding: 2px 9px; border-radius: 999px; box-shadow: 0 3px 8px rgba(42, 120, 214, 0.4);
}
.plan-name { font-size: 0.74rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.plan-price { font-size: 1.35rem; font-weight: 800; color: var(--ink-1); font-variant-numeric: tabular-nums; }
.plan-price small { font-size: 0.72rem; font-weight: 600; color: var(--muted); }
.plan-note { font-size: 0.68rem; color: var(--muted); }
.plan-note s { opacity: 0.75; }

/* ---------- Onboarding de bienvenida + paywall (overlay) ---------- */
/* ============ ONBOARDING — direccion "editorial" (2026-09-05) ============
   Antes era una pantalla oscura con degradados cian y rosa: un lenguaje visual
   propio que no salia de ninguna parte del design system y que se veia igual de
   oscuro aunque el usuario tuviera la app en claro. Ahora todo sale de los
   tokens, asi que la bienvenida se ve como la app que viene despues -y sigue el
   tema del usuario- en vez de prometer una estetica que luego no aparece.

   La ilustracion SANGRA por arriba y el texto va a la izquierda: es lo que
   distingue esta direccion de la anterior, centrada. */
#onboarding {
  /* SIEMPRE CLARO, pase lo que pase con el tema del telefono.
     Los tokens se redefinen AQUI en vez de repartir !important por todas las
     reglas de abajo: las propiedades personalizadas heredan, asi que cada
     descendiente sigue escrito con var() -legible, y facil de cambiar- pero
     resuelve a la paleta clara aunque el usuario tenga la app en oscuro.

     Por que se fija. La maqueta aprobada era clara con un lavado azul; al
     implementarla con --atmos, que sigue el tema, en un telefono en oscuro
     salia un negro con un 2,5 % de azul: otra pantalla distinta de la aprobada.
     Es una decision, no un descuido -y su precio es que quien tenga la app en
     oscuro vera esta pantalla clara y luego la app oscura-. Se acepta porque el
     onboarding se ve UNA vez y porque las ilustraciones se recortaron sobre
     blanco: sobre fondo oscuro su fleco claro se convierte en un halo visible. */
  --page:        #f9f9f7;
  --surface:     #fcfcfb;
  --ink-1:       #0b0b0b;
  --ink-2:       #52514e;
  --muted:       #898781;
  --baseline:    #c3c2b7;
  --accent:      #2a78d6;
  --border-soft: rgba(11, 11, 20, 0.06);
  --shadow-card: 0 1px 2px rgba(15, 20, 40, 0.025), 0 5px 16px rgba(15, 20, 40, 0.035);
  --atmos:       #f9f9f7;

  position: fixed; inset: 0; z-index: 2000;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--page);
  color: var(--ink-1);
  /* Sin transicion de entrada: el fundido de 0.5s sobre un fondo claro que
     aparece encima de una app oscura es justo el destello que se queria evitar.
     La salida (.ob-out) si se mantiene, que es hacia la app y no molesta. */
  opacity: 1;
  padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom);
}
#onboarding.ob-out { opacity: 0; pointer-events: none; transition: opacity 0.5s ease; }
#onboarding.hidden { display: none; }

#ob-skip {
  position: absolute; top: calc(14px + env(safe-area-inset-top)); right: 16px; z-index: 2;
  background: transparent; border: 0; color: var(--muted);
  font-size: 0.78rem; font-weight: 600; padding: 7px 10px; border-radius: 999px; cursor: pointer;
  transition: color 0.15s ease;
}
#ob-skip:hover { color: var(--ink-2); }
#ob-skip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

#ob-track {
  flex: 1; display: flex; align-items: stretch;
  /* min-height: 0 NO es decorativo: sin el, el boton de abajo se sale de la
     pantalla en cualquier movil corto.

     Un hijo de flex trae `min-height: auto`, que significa "no me encojas por
     debajo de mi contenido". Con la banda de arte a 50vh mas el texto, el track
     se plantaba en 636 px dentro de una ventana de 667, empujaba el pie a y=636 y
     el boton acababa en y=722: 55 px por debajo del borde, y el `overflow: hidden`
     del contenedor lo cortaba. En un iPhone SE el boton no se veia ENTERO.

     Medido: pasa a partir de unos 740 px de alto de ventana, asi que afectaba a
     los moviles pequeños, a cualquiera con las barras del navegador a la vista, y
     a un telefono girado. */
  min-height: 0;
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}
.ob-slide {
  min-width: 100%; display: flex; flex-direction: column;
  align-items: stretch; justify-content: flex-start; text-align: left;
  gap: 0; padding: 0 0 12px;
  /* Red de seguridad para pantallas MUY cortas -un movil girado, por ejemplo-:
     si el contenido no entra ni con el track encogido, se desplaza la tarjeta en
     vez de quedarse fuera. El boton no se pierde nunca. */
  min-height: 0; overflow-y: auto;
}
/* La banda de arte: llega hasta los bordes y se funde hacia abajo con el fondo,
   sin linea de corte. El degradado usa el acento al 8 %, el mismo lavado que
   --atmos hace en el hero de Home. */
.ob-media {
  /* Altura FIJA, para que las cinco pantallas compartan composicion y el
     titular caiga siempre a la misma altura al pasar de una a otra.

     Se probo que la banda se ajustara a la imagen y quedaba peor: las poses son
     mas anchas que altas (1142x1024) y en un movil las limita el ANCHO, asi que
     la banda se encogia y dejaba un agujero sobre el boton distinto en cada
     pantalla. Con altura fija el titular cae siempre igual.

     La altura vive en una VARIABLE porque la usan dos -la banda y el tope de la
     imagen- y tenerla escrita dos veces era garantia de que un dia cambiara una
     sola. `.ob-media-sm` solo redefine la variable. */
  --banda: min(50vh, 400px);
  position: relative; width: 100%; height: var(--banda);
  display: grid; place-items: end center; overflow: hidden;
  /* Respiro arriba: en `neutra` y `contenta` la figura empieza a 2 px del techo
     de su lienzo y sin esto queda pegada al borde de la pantalla. */
  padding-top: 18px;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 8%, var(--page)) 0%,
    var(--atmos) 100%);
  border: 0; border-radius: 0; margin: 0 0 22px;
}
.ob-media img {
  /* EL TOPE DE ALTURA ES ABSOLUTO, NO UN PORCENTAJE, y ese es todo el arreglo.
     Con `max-height: 100%` la imagen se dibujaba a su tamano NATURAL -1142x1024-
     dentro de una banda de 400 px, y el `overflow: hidden` la recortaba: en
     escritorio solo se veian la frente y los ojos. El porcentaje no resolvia
     porque la fila del grid es automatica, asi que crecia con la imagen y el
     100% acababa siendo su propia altura: un tope que nunca aprieta.

     Lo unico que limitaba la imagen era `max-width: 92%`, y eso deja de morder
     en cuanto la ventana pasa de ~463 px, que es donde 92% supera los 426 px que
     caben de ancho. Por debajo -o sea, en cualquier telefono- se veia bien; por
     encima, recortada. Se verifico a 390 px y ese era justo el unico ancho donde
     el defecto no aparece.

     `calc()` sobre una longitud absoluta no depende de que nadie resuelva nada. */
  max-width: 92%; max-height: calc(var(--banda) - 18px); width: auto; height: auto; object-fit: contain;
  -webkit-mask-image: linear-gradient(180deg, #000 78%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 78%, transparent 100%);
}
/* DESPUES de .ob-media img a proposito: misma especificidad, asi que gana por
   orden. Puesta antes, esta linea no hacia nada y la tarjeta del precio se
   quedaba con la ilustracion grande, empujando el boton fuera de pantalla. */
.ob-media-sm { --banda: min(28vh, 235px); }
/* El emoji es el RESPALDO, no una capa encima. Antes .ob-media img iba
   position:absolute y lo tapaba; al pasar la banda a grid los dos se volvieron
   hermanos visibles y el emoji aparecia flotando sobre Sofia en las cinco
   pantallas. Ahora solo se ve si no hay <img>, que es lo que deja el onerror. */
.ob-media:has(img) .ob-fallback { display: none; }
.ob-fallback { font-size: clamp(3rem, 14vw, 4.6rem); }

/* El texto, alineado a la izquierda y con una medida legible. */
.ob-texto {
  display: flex; flex-direction: column; gap: 10px;
  padding: 0 30px; max-width: 460px; width: 100%;
  /* El texto va PEGADO a su ilustracion -son una pareja, y separarlos con aire
     rompe esa lectura- y el espacio sobrante se acumula abajo, antes del boton.
     Se probo repartirlo arriba y abajo con `margin: auto` y quedaba peor: la
     ilustracion se despegaba de su propio titular. El agujero que quedaba antes
     se cierra subiendo la banda de arte, no separando la pareja. */
  margin: 0 auto auto;
}
.ob-eyebrow {
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--accent);
}
.ob-slide h2 {
  font-size: clamp(1.45rem, 6.4vw, 1.9rem); font-weight: 780;
  letter-spacing: -0.018em; margin: 0; line-height: 1.16; text-wrap: balance;
}
/* Era un degradado cian con resplandor. Ahora es el acento del sistema, a secas:
   un solo color de marca, el mismo que el resto de la app. */
.ob-glow { color: var(--accent); }
.ob-slide p {
  margin: 0; max-width: 42ch; font-size: 0.94rem; line-height: 1.6;
  color: var(--ink-2);
}

.ob-perks { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.ob-perks li {
  display: flex; align-items: flex-start; gap: 10px; text-align: left;
  font-size: 0.88rem; line-height: 1.45; color: var(--ink-2); font-weight: 500;
}
.ob-perks li::before {
  content: ""; flex: none; width: 6px; height: 6px; margin-top: 0.5em;
  border-radius: 50%; background: var(--accent);
}
.ob-offer {
  margin-top: 14px; padding: 13px 15px; border-radius: 12px; text-align: left;
  background: var(--surface); border: 1px solid var(--border-soft);
  box-shadow: var(--shadow-card);
  display: flex; flex-direction: column; gap: 3px;
}
.ob-offer b { display: block; font-size: 1rem; color: var(--ink-1); }
.ob-offer span { font-size: 0.82rem; color: var(--ink-2); line-height: 1.5; }
.ob-offer span strong { color: var(--ink-1); font-variant-numeric: tabular-nums; }
/* El 17 % y los 13,98 EUR salen de 6,99 x 12 = 83,88 frente a 69,90. Si el precio
   cambia en stripe.service.js, esta frase deja de ser cierta. */
.ob-offer span u { text-decoration: none; color: var(--accent); font-weight: 700; }
.ob-offer small { font-size: 0.72rem; color: var(--muted); }
/* Sin fuego ni "tiempo limitado": no hay caducidad en el codigo, y fabricar
   urgencia esta prohibido por la Ley 5 de SOFIA_CONSTITUCION.md. */
.ob-offer-tag {
  display: block; font-style: normal; font-size: 0.64rem; font-weight: 700;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted); margin-bottom: 2px;
}

.ob-footer {
  display: flex; flex-direction: column; align-items: center; gap: 16px;
  padding: 14px 28px calc(26px + env(safe-area-inset-bottom));
}
#ob-dots { display: flex; gap: 7px; }
/* Estos tres venian del fondo oscuro: puntos en blanco al 25 % y un boton
   blanco con texto marino. Sobre el fondo claro del sistema el boton
   desaparecia y los puntos no se veian — el fallo clasico de un color que solo
   existe para un tema. Ahora los tres salen de tokens. */
.ob-dot {
  width: 8px; height: 8px; border-radius: 999px;
  background: var(--baseline);
  transition: width 0.3s ease, background 0.3s ease;
}
.ob-dot.active { width: 26px; background: var(--accent); }
.ob-btn {
  width: min(100%, 340px); padding: 15px 20px; border: 0; border-radius: 999px;
  background: var(--ink-1); color: var(--page); font-size: 0.9rem; font-weight: 800;
  letter-spacing: 0.06em; cursor: pointer; font-family: inherit;
  box-shadow: var(--shadow-card);
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.25s ease;
}
.ob-btn:hover { transform: translateY(-2px); }
.ob-btn:active { transform: scale(0.97); }
.ob-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  #ob-track { transition: none; }
  .ob-btn:hover { transform: none; }
}
/* El boton de las pantallas de conversion. Era un degradado del acento a
   #7a5fd0, un morado que no esta en la paleta del design system: un resto del
   onboarding oscuro anterior, y el unico elemento saturado de una pantalla que
   ya se habia limpiado de eso. Ahora es el acento liso, que distingue igual sin
   introducir un color que no existe en ningun otro sitio de la app. */
.ob-btn.grad {
  background: var(--accent);
  color: var(--page);
}
.ob-btn:disabled { opacity: 0.6; cursor: wait; }

/* ---------- Acceso: ojito de contraseña + Google ---------- */
.pass-wrap { position: relative; display: block; }
.pass-wrap input { width: 100%; padding-right: 42px; box-sizing: border-box; }
.pass-toggle {
  position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; color: var(--muted); cursor: pointer;
  padding: 6px; display: grid; place-items: center; border-radius: 8px;
  transition: color 0.15s ease;
}
.pass-toggle:hover, .pass-toggle:focus-visible { color: var(--ink-1); }
.pass-toggle svg { display: block; }
.auth-divider {
  display: flex; align-items: center; gap: 10px; margin: 16px 0 12px;
  color: var(--muted); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
}
.auth-divider::before, .auth-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.google-signin { display: flex; flex-direction: column; }
.google-signin.oculto-vista { display: none; }
#google-btn { display: flex; justify-content: center; min-height: 44px; }
.auth-info {
  color: var(--good); font-size: 0.82rem; font-weight: 600;
  background: color-mix(in srgb, var(--good) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--good) 30%, transparent);
  border-radius: 10px; padding: 9px 12px; margin-top: 10px; line-height: 1.45;
}

/* ---------- Mi divisa ---------- */
.currency-select {
  width: 100%; padding: 11px 14px; border-radius: 12px;
  border: 1.5px solid var(--border); background: var(--surface); color: var(--ink-1);
  font-size: 0.9rem; font-weight: 600; cursor: pointer;
  transition: border-color 0.15s ease;
}
.currency-select:hover, .currency-select:focus { border-color: var(--accent); outline: none; }

/* ---------- Notificaciones: interruptor con estado ---------- */
.push-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 14px; border: 1px solid var(--border); border-radius: 14px;
  background: var(--surface); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
}
.push-info { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.push-label { font-weight: 650; font-size: 0.88rem; }
.push-state {
  font-size: 0.7rem; font-weight: 650; color: var(--muted);
  display: inline-flex; align-items: center; width: fit-content;
  padding: 1px 9px; border-radius: 999px;
  background: color-mix(in srgb, var(--muted) 12%, transparent);
  transition: color 0.2s ease, background 0.2s ease;
}
.push-state.on { color: var(--good); background: color-mix(in srgb, var(--good) 14%, transparent); }

.switch { position: relative; display: inline-block; width: 52px; height: 30px; flex: none; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch-slider {
  position: absolute; inset: 0; cursor: pointer; border-radius: 999px;
  background: var(--grid); border: 1px solid var(--border);
  transition: background 0.25s ease, border-color 0.25s ease;
}
.switch-slider::before {
  content: ''; position: absolute; height: 24px; width: 24px;
  left: 2px; top: 50%; transform: translateY(-50%);
  border-radius: 50%; background: #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
  transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.switch input:checked + .switch-slider {
  background: linear-gradient(90deg, #0ca30c, #16b981);
  border-color: transparent;
  box-shadow: 0 0 10px rgba(12, 163, 12, 0.35) inset;
}
.switch input:checked + .switch-slider::before { transform: translate(22px, -50%); }
.switch input:disabled + .switch-slider { opacity: 0.45; cursor: not-allowed; }
.switch input:focus-visible + .switch-slider { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- FODA ---------- */
.foda-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 640px) { .foda-grid { grid-template-columns: 1fr; } }
.foda-quad {
  border: 1px solid var(--border); border-left-width: 4px;
  border-radius: 12px; padding: 12px 14px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.foda-quad:hover { transform: translateY(-1.5px); box-shadow: 0 6px 14px rgba(0, 0, 0, 0.08); }
.foda-fortaleza   { border-left-color: #10b981; }
.foda-oportunidad { border-left-color: var(--accent); }
.foda-debilidad   { border-left-color: #f59e0b; }
.foda-amenaza     { border-left-color: var(--critical); }
.foda-fortaleza h3   { color: #10b981; }
.foda-oportunidad h3 { color: var(--accent); }
.foda-debilidad h3   { color: #d97706; }
.foda-amenaza h3     { color: var(--critical); }
.foda-quad h3 { margin: 0 0 8px; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; }
.foda-quad ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.foda-quad li { display: flex; justify-content: space-between; gap: 8px; font-size: 0.85rem; }
.foda-param { color: var(--muted); font-size: 0.75rem; }

/* ---------- Libro Financiero (Home v5 → Libro v1, libro-mockup-final.html) ---------- */

/* Cabecera de periodo = control de navegación (§2.1, §5.15) */
.period-head { padding: 4px 1px 0; cursor: pointer; }
.period-name { display: inline-flex; align-items: center; gap: 7px; font-size: 1.2rem; font-weight: 720; letter-spacing: -0.012em; }
.period-name svg { color: var(--muted); margin-top: 2px; }
.period-net { margin-top: 3px; font-size: 0.78rem; font-weight: 600; color: var(--good); font-variant-numeric: tabular-nums; }
.period-net.neg { color: var(--critical); }
.period-net .vs { color: var(--muted); font-weight: 500; }

/* Búsqueda (solo categoría/moneda, honesto — ver Fase 3) + filtros permanentes */
.search-row { display: flex; gap: 8px; margin-top: 16px; }
.search-pill {
  flex: 1; display: flex; align-items: center; gap: 8px;
  background: var(--surface); border: 1px solid var(--border-soft); border-radius: 999px;
  padding: 0 14px; min-height: 40px; color: var(--ink-1); font-size: 0.84rem; font-family: inherit;
}
.search-pill svg { color: var(--muted); flex-shrink: 0; }
.search-pill input { flex: 1; border: none; background: none; font: inherit; color: inherit; min-width: 0; padding: 0; }
.search-pill input::placeholder { color: var(--muted); }
.search-pill input:focus { outline: none; }
.filter-row { display: flex; gap: 7px; margin-top: 10px; }
.f-select {
  font-size: 0.68rem; font-weight: 600; letter-spacing: 0.012em; line-height: 1;
  padding: 6px 10px; border-radius: 999px; font-family: inherit;
  background: var(--surface); border: 1px solid var(--border-soft); color: var(--ink-2);
}

/* Encabezados de grupo (Hoy/Ayer/mes...) */
.group-head { display: flex; align-items: baseline; justify-content: space-between; margin: 22px 2px 10px; }
.group-label { font-size: 0.63rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.11em; color: var(--muted); }
.group-net { font-size: 0.7rem; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }

/* Grupo = una tarjeta; filas separadas por hairline, no cada una su propia tarjeta */
.tx-card { background: var(--surface); border: 1px solid var(--border-soft); border-radius: 18px; box-shadow: var(--shadow-card); overflow: hidden; }
.tx-row {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 15px; cursor: pointer;
  border-top: 1px solid var(--border-soft);
}
.tx-row:first-child { border-top: none; }
.tx-ico { width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0; display: grid; place-items: center; }
.tx-ico svg { width: 15px; height: 15px; }
.tx-ico.out { background: color-mix(in srgb, var(--critical) 9%, transparent); color: var(--critical); }
.tx-ico.in  { background: color-mix(in srgb, var(--good) 9%, transparent); color: var(--good); }
.tx-ico.sav { background: color-mix(in srgb, #7a5fd0 10%, transparent); color: #7a5fd0; }
.tx-main { flex: 1; min-width: 0; }
.tx-desc {
  font-size: 0.84rem; font-weight: 600; letter-spacing: -0.003em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  display: flex; align-items: center; gap: 6px;
}
/* Tres datos en la línea más estrecha de la fila. Si no caben, el que cede es el
   ORIGEN: está en el filtro y en el detalle, mientras que la fecha y la categoría
   solo están aquí. Se recorta con puntos suspensivos en vez de desbordar. */
.tx-sub {
  font-size: 0.7rem; color: var(--muted); margin-top: 2px; text-transform: capitalize;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tx-fecha { font-variant-numeric: tabular-nums; }

/* ---------- Con qué se pagó una partida ----------
   Una lista, no una tabla: son pocas filas y en móvil una tabla obliga a
   desplazar en horizontal justo lo que se quiere comparar de un vistazo. */
.bud-ver {
  font-family: inherit; font-size: 0.68rem; font-weight: 650; color: var(--accent);
  background: none; border: none; padding: 2px 0; margin-left: auto; cursor: pointer;
}
.bud-detalle { margin-top: 8px; border-top: 1px solid var(--line); padding-top: 8px; }
.bpm-row, .bpm-cand {
  display: grid; grid-template-columns: 4.2rem 1fr auto auto; gap: 8px; align-items: center;
  padding: 6px 0; font-size: 0.72rem;
}
.bpm-cand {
  width: 100%; font-family: inherit; text-align: left; cursor: pointer;
  background: none; border: none; border-radius: 8px;
}
.bpm-cand:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.bpm-fecha { color: var(--muted); font-variant-numeric: tabular-nums; }
.bpm-desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: flex; align-items: center; gap: 6px; }
.bpm-monto { font-weight: 650; font-variant-numeric: tabular-nums; }
.bpm-undo {
  font-family: inherit; font-size: 0.64rem; font-weight: 650; color: var(--critical);
  background: none; border: none; cursor: pointer; padding: 2px 4px;
}
.bpm-undo.vacio { color: var(--muted); font-weight: 500; cursor: default; }
.bpm-marcar {
  margin-top: 8px; width: 100%; min-height: 40px; font-family: inherit;
  font-size: 0.74rem; font-weight: 700; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  border-radius: 999px; cursor: pointer;
}
.bpm-picker { margin-top: 6px; }
.bpm-ayuda, .bpm-vacio { font-size: 0.7rem; color: var(--muted); margin: 4px 0; }

/* ---------- Declarar el patrimonio desde el hero ----------
   Discreto a propósito: es una puerta abierta, no una petición. No compite con
   la cifra que ocupará este mismo sitio en cuanto exista. */
.hero-decl { display: flex; align-items: center; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.hero-decl input {
  flex: 1 1 10rem; min-width: 0; min-height: 44px; font-family: inherit; font-size: 0.9rem;
  padding: 10px 14px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface); color: var(--ink); font-variant-numeric: tabular-nums;
}
.hero-decl-div { font-size: 0.8rem; font-weight: 650; color: var(--muted); }
.hero-decl-btn {
  min-height: 44px; padding: 0 20px; font-family: inherit; font-size: 0.82rem; font-weight: 700;
  color: #fff; background: linear-gradient(135deg, var(--accent), #7a5fd0);
  border: none; border-radius: 999px; cursor: pointer;
}
.hero-decl-btn:disabled { opacity: 0.6; cursor: default; }
.hero-decl-pie { margin-top: 8px; font-size: 0.72rem; color: var(--muted); line-height: 1.5; }
@media (max-width: 360px) { .tx-origen { display: none; } }
.tx-amount { font-size: 0.85rem; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tx-amount.in { color: var(--good); }
.tx-approx { display: block; font-size: 0.68rem; color: var(--muted); font-weight: 500; text-transform: none; }
.s-chip {
  font-size: 0.6rem; font-weight: 650; letter-spacing: 0.015em; line-height: 1;
  padding: 3px 8px; border-radius: 999px; flex-shrink: 0;
}
.s-chip.neutral { background: color-mix(in srgb, var(--muted) 10%, transparent); color: var(--muted); }

/* Próximos: misma tarjeta, tinta secundaria (todavía no ocurrieron) */
.up-amount { font-size: 0.85rem; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink-2); }
.up-due { color: var(--accent); font-weight: 600; }

/* Botón flotante de registro (§5.14) — geometría compartida; el color depende de si la
   pantalla ya tiene su propia superficie de color (.accent si no la tiene; .neutral si sí). */
.fab {
  position: fixed; right: 18px; bottom: calc(92px + env(safe-area-inset-bottom)); z-index: 15;
  width: 54px; height: 54px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer; border: none;
}
.fab svg { width: 22px; height: 22px; }
.fab.accent { background: linear-gradient(135deg, var(--accent), #7a5fd0); color: #fff; box-shadow: var(--shadow-goal); }
.fab.neutral {
  background: var(--surface); color: var(--accent); border: 1px solid var(--border-soft);
  box-shadow: 0 2px 4px rgba(15, 20, 40, 0.05), 0 8px 22px rgba(15, 20, 40, 0.10);
}
@media (prefers-reduced-motion: no-preference) {
  .fab { transition: transform 0.15s ease; }
  .fab:active { transform: scale(0.94); }
}

/* Barra compacta con blur (§5.16), aparece al hacer scroll */
.sticky-bar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 16;
  display: flex; align-items: center; justify-content: space-between; padding: 10px 20px;
  background: color-mix(in srgb, var(--page) 88%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-soft);
  opacity: 0; pointer-events: none; transform: translateY(-4px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.sticky-bar.show { opacity: 1; pointer-events: auto; transform: translateY(0); }
.sticky-period { font-size: 0.8rem; font-weight: 650; display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }
.sticky-period svg { color: var(--muted); }

/* Overlay: selector de periodo, estilo Apple Photos (§5.15) */
.overlay {
  position: fixed; inset: 0; z-index: 17;
  background: color-mix(in srgb, var(--page) 94%, transparent);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  display: none; flex-direction: column; padding: 54px 24px 24px;
}
.overlay.open { display: flex; }
.ov-close {
  position: absolute; top: 30px; right: 20px; width: 32px; height: 32px; border-radius: 50%;
  border: none; cursor: pointer; background: color-mix(in srgb, var(--muted) 12%, transparent);
  color: var(--ink-2); display: grid; place-items: center; font-size: 0.9rem;
}
.year-row { display: flex; gap: 8px; margin-bottom: 22px; flex-wrap: wrap; }
.year-pill {
  font: inherit; font-size: 0.78rem; font-weight: 650; padding: 8px 16px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--border-soft); color: var(--ink-2); cursor: pointer;
}
.year-pill.active { background: var(--ink-1); color: var(--page); border-color: transparent; }
.month-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.month-cell {
  padding: 14px 0; text-align: center; border-radius: 14px; font: inherit;
  font-size: 0.78rem; font-weight: 600;
  background: var(--surface); border: 1px solid var(--border-soft); color: var(--ink-1); cursor: pointer;
}
.month-cell.now { background: linear-gradient(135deg, var(--accent), #7a5fd0); color: #fff; border-color: transparent; font-weight: 700; }
.month-cell.off { color: var(--muted); background: transparent; border-color: transparent; cursor: default; opacity: 0.55; }
.ov-hint { margin-top: auto; text-align: center; font-size: 0.72rem; color: var(--muted); }

/* Overlay: detalle de movimiento — bottom sheet (§5.7) */
.scrim { position: fixed; inset: 0; z-index: 17; background: rgba(0,0,0,0.28); display: none; }
.scrim.open { display: block; }
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 18;
  background: var(--surface); border-radius: 20px 20px 0 0; padding: 10px 22px calc(26px + env(safe-area-inset-bottom));
  box-shadow: 0 -10px 40px rgba(0,0,0,0.18); display: none;
}
.sheet.open { display: block; }
.sheet-handle { width: 36px; height: 4px; border-radius: 999px; background: var(--grid); margin: 0 auto 18px; }
.sheet-head { display: flex; align-items: center; gap: 12px; }
.sheet-title { font-size: 1.02rem; font-weight: 720; letter-spacing: -0.008em; }
.sheet-sub { font-size: 0.74rem; color: var(--muted); margin-top: 2px; }
.sheet-amount { margin-left: auto; font-size: 1.15rem; font-weight: 720; font-variant-numeric: tabular-nums; }
.sheet-meta { display: flex; gap: 7px; margin: 14px 0 18px; flex-wrap: wrap; }
.sheet-actions { display: flex; flex-direction: column; gap: 8px; }
.sh-btn {
  display: flex; align-items: center; gap: 10px; width: 100%;
  font-family: inherit; font-size: 0.84rem; font-weight: 600; color: var(--ink-1);
  background: var(--page); border: 1px solid var(--border-soft); border-radius: 12px;
  padding: 12px 14px; cursor: pointer; min-height: 44px; text-align: left;
}
.sh-btn svg { width: 16px; height: 16px; color: var(--ink-2); flex-shrink: 0; }
.sh-btn.ai svg { color: #7a5fd0; }
.sh-btn.danger { color: var(--critical); background: color-mix(in srgb, var(--critical) 7%, transparent); border-color: color-mix(in srgb, var(--critical) 22%, transparent); }
.sh-btn.danger svg { color: var(--critical); }
/* Variante Metas del bottom sheet (§5.7): más contenido que un movimiento, con scroll propio y entrada animada */
.sheet { max-height: calc(100% - 56px); overflow-y: auto; scrollbar-width: none; }
.sheet::-webkit-scrollbar { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .sheet.open { animation: sheet-up 0.28s cubic-bezier(0.22, 1, 0.36, 1); }
  @keyframes sheet-up { from { transform: translateY(16px); opacity: 0.6; } to { transform: none; opacity: 1; } }
}

/* ---------- Metas (Home v5 → Metas v4, metas-mockup-final.html) ----------
   Fuera de alcance en esta fase: wealth-strip (Comprometido/Libre), advice-card,
   tratamiento de imagen real (scene-wrap/picker), es_meta_norte, meta_id. */

.sec-label { font-size: 0.63rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.11em; color: var(--muted); margin: 26px 2px 10px; }
.sec-label.first { margin-top: 6px; }

/* Meta Norte: la única superficie de color de la pantalla (§17.9/§20.2) */
.north-card { background: linear-gradient(135deg, var(--accent) 0%, #7a5fd0 100%); border-radius: 20px; padding: 22px 22px 20px; color: #fff; box-shadow: var(--shadow-goal); position: relative; overflow: hidden; }
.north-name { display: flex; align-items: center; gap: 8px; font-size: 1.02rem; font-weight: 700; letter-spacing: -0.008em; }
.north-name svg { opacity: 0.95; flex-shrink: 0; }
.north-pct { font-size: 2.5rem; font-weight: 730; letter-spacing: -0.022em; font-variant-numeric: tabular-nums; line-height: 1; margin: 16px 0 12px; }
.north-bar { height: 6px; border-radius: 999px; background: rgba(255,255,255,0.22); overflow: hidden; position: relative; }
.north-bar > div { height: 100%; border-radius: 999px; background: #fff; }
.north-amounts { margin-top: 12px; font-size: 0.84rem; font-weight: 600; font-variant-numeric: tabular-nums; color: rgba(255,255,255,0.95); }
.north-amounts .of { font-weight: 500; color: rgba(255,255,255,0.72); }
.north-phase { margin-top: 13px; font-size: 0.83rem; font-weight: 650; color: rgba(255,255,255,0.95); }
.north-note { margin-top: 6px; font-size: 0.8rem; line-height: 1.55; color: rgba(255,255,255,0.88); }
.north-cta {
  margin-top: 16px; display: inline-flex; align-items: center; justify-content: center;
  background: #fff; color: var(--accent); border: none; cursor: pointer; font-family: inherit;
  font-size: 0.8rem; font-weight: 700; letter-spacing: 0.01em; padding: 10px 24px; border-radius: 999px; min-height: 40px;
}
.north-art { position: absolute; right: -6px; bottom: -8px; pointer-events: none; opacity: 0.45; }

/* Hitos de progreso (§20.6): puntos discretos en el tramo aún no recorrido */
.g-dot { position: absolute; top: 50%; width: 3px; height: 3px; border-radius: 50%; background: var(--grid); transform: translate(-50%, -50%); }
.north-bar .g-dot { background: rgba(255,255,255,0.35); }

/* Grupos de metas por plazo: filas serenas, una sola barra fina */
.goal-cardlist { background: var(--surface); border: 1px solid var(--border-soft); border-radius: 18px; box-shadow: var(--shadow-card); overflow: hidden; }
.g-row { display: flex; gap: 12px; padding: 14px 16px 15px; border-top: 1px solid var(--border-soft); cursor: pointer; }
.g-row:first-child { border-top: none; }
.g-ico { width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0; margin-top: 1px; display: grid; place-items: center; background: color-mix(in srgb, var(--muted) 8%, transparent); color: var(--ink-2); }
.g-ico svg { width: 14px; height: 14px; }
.g-row.paused .g-ico { opacity: 0.55; }
.g-main { flex: 1; min-width: 0; }
.g-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.g-name { font-size: 0.84rem; font-weight: 600; letter-spacing: -0.003em; display: inline-flex; align-items: center; gap: 7px; min-width: 0; }
.g-name span.txt { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g-right { text-align: right; flex-shrink: 0; }
.g-pct { font-size: 0.82rem; font-weight: 650; font-variant-numeric: tabular-nums; }
.g-amounts { font-size: 0.66rem; color: var(--muted); font-variant-numeric: tabular-nums; margin-top: 1px; }
.g-track { position: relative; height: 4px; border-radius: 999px; background: var(--track); overflow: hidden; margin-top: 10px; }
.g-fill { height: 100%; border-radius: 999px; background: var(--accent); }
@media (prefers-reduced-motion: no-preference) { .north-bar > div, .g-fill { transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1); } }

/* Pausada = congelada, no deshabilitada (§7.2): toda la fila baja de temperatura */
.g-row.paused .g-name { color: var(--ink-2); font-weight: 550; }
.g-row.paused .g-fill { background: var(--muted); opacity: 0.5; }
.g-row.paused .g-pct { color: var(--muted); font-weight: 600; }
.g-row.paused .g-amounts { opacity: 0.7; }
.g-pausenote { font-size: 0.66rem; color: var(--muted); margin-top: 8px; }

/* Casi cumplida (≥90%): la barra pasa a verde, sin celebración todavía */
.g-row.almost .g-pct { font-weight: 720; }
.g-row.almost .g-fill { background: var(--good); }
.g-hint { font-size: 0.68rem; font-weight: 650; color: var(--good); margin-top: 8px; }
.g-hint.info { color: var(--muted); font-weight: 500; }

.s-chip.warn { background: color-mix(in srgb, var(--warning) 10%, transparent); color: var(--warning); }
.s-chip.good { background: color-mix(in srgb, var(--good) 9%, transparent); color: var(--good); }

/* Detalle de meta: extensión del sheet base con barra de progreso e hitos en texto */
.sheet-amounts { margin-top: 14px; font-size: 0.92rem; font-weight: 650; font-variant-numeric: tabular-nums; }
.sheet-amounts .of { color: var(--muted); font-weight: 500; }
.sheet-track { height: 5px; border-radius: 999px; background: var(--track); overflow: hidden; margin-top: 10px; position: relative; }
.sheet-fill { height: 100%; border-radius: 999px; background: var(--good); }
.sheet-phase { margin-top: 10px; font-size: 0.76rem; font-weight: 650; }
/* El ritmo que exige la meta: qué pide, con qué se cuenta, y qué hacer.
   Tres líneas en jerarquía descendente — la exigencia es el dato, el contraste
   es el contexto, el veredicto es la única que lleva color de estado. */
.ms-ritmo { margin-top: 12px; display: grid; gap: 4px; }
.ms-ritmo:empty { display: none; }
.msr-line { font-size: 0.74rem; line-height: 1.45; font-variant-numeric: tabular-nums; }
.msr-exige { font-weight: 650; color: var(--ink-2); }
.msr-real  { color: var(--muted); }
.msr-vered { font-weight: 650; }
.msr-vered.ok   { color: var(--good); }
.msr-vered.warn { color: var(--warning); }

.mile-row { margin-top: 6px; font-size: 0.68rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.mile-row b { color: var(--good); font-weight: 650; }
.sheet-cta {
  margin-top: 16px; width: 100%; font-family: inherit; font-size: 0.84rem; font-weight: 700;
  color: #fff; background: linear-gradient(135deg, var(--accent), #7a5fd0);
  border: none; border-radius: 999px; padding: 12px 0; min-height: 44px; cursor: pointer;
}
.sheet-aporte { display: none; gap: 8px; margin-top: 10px; }
.sheet-aporte.open { display: flex; }
.sheet-aporte input {
  flex: 1; background: var(--page); border: 1px solid var(--border-soft); border-radius: 10px;
  padding: 9px 12px; font-size: 0.9rem; font-family: inherit; color: var(--ink-1);
}

/* Celebración a pantalla completa (§20.5) — única toma total de color de toda la app */
.celebrate {
  position: fixed; inset: 0; z-index: 19; display: none;
  background: linear-gradient(160deg, var(--accent) 0%, #7a5fd0 100%);
  color: #fff; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 40px 34px;
}
.celebrate.open { display: flex; }
.cel-ring { width: 92px; height: 92px; }
.cel-ring circle { stroke-dasharray: 264; stroke-dashoffset: 264; }
.cel-ring path { stroke-dasharray: 40; stroke-dashoffset: 40; }
.cel-name { margin-top: 26px; font-size: 1.3rem; font-weight: 720; letter-spacing: -0.014em; }
.cel-date { margin-top: 7px; font-size: 0.8rem; color: rgba(255,255,255,0.78); }
.cel-amount { margin-top: 3px; font-size: 0.92rem; font-weight: 650; font-variant-numeric: tabular-nums; color: rgba(255,255,255,0.95); }
.cel-quote { margin-top: 22px; font-size: 0.88rem; line-height: 1.6; color: rgba(255,255,255,0.92); max-width: 26ch; }
.cel-cta {
  margin-top: 30px; font-family: inherit; font-size: 0.84rem; font-weight: 700;
  color: var(--accent); background: #fff; border: none; border-radius: 999px; padding: 13px 30px; min-height: 44px; cursor: pointer;
}
.cel-el { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  .celebrate.open .cel-ring circle { animation: cel-draw 0.8s ease 0.15s forwards; }
  .celebrate.open .cel-ring path { animation: cel-draw 0.45s ease 0.85s forwards; }
  @keyframes cel-draw { to { stroke-dashoffset: 0; } }
  .celebrate.open .cel-el { animation: rise-in-cel 0.4s cubic-bezier(0.25, 0.8, 0.35, 1) forwards; }
  .celebrate.open .cel-name   { animation-delay: 1.1s; }
  .celebrate.open .cel-date   { animation-delay: 1.25s; }
  .celebrate.open .cel-amount { animation-delay: 1.35s; }
  .celebrate.open .cel-quote  { animation-delay: 1.55s; }
  .celebrate.open .cel-cta    { animation-delay: 1.85s; }
  @keyframes rise-in-cel { to { opacity: 1; } }
}
@media (prefers-reduced-motion: reduce) {
  .cel-ring circle, .cel-ring path { stroke-dashoffset: 0; }
  .cel-el { opacity: 1; }
}

/* Acceso e historial (cumplidas/pausadas/canceladas) */
.hist-link {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 26px; padding: 14px 16px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--border-soft); border-radius: 18px; box-shadow: var(--shadow-card);
  font-size: 0.8rem; font-weight: 600; color: var(--ink-2);
}
.hist-link svg { color: var(--muted); }
.hist-head { display: flex; align-items: center; gap: 10px; padding: 4px 1px 0; font-size: 1.02rem; font-weight: 720; letter-spacing: -0.008em; cursor: pointer; }
.hist-head svg { color: var(--ink-2); }
.hist-story { margin: 14px 1px 0; font-size: 0.8rem; color: var(--ink-2); line-height: 1.5; }
.hist-story b { font-variant-numeric: tabular-nums; color: var(--ink-1); font-weight: 650; }
.tab-row { display: flex; gap: 7px; margin: 16px 0 18px; }
.tab { font: inherit; font-size: 0.72rem; font-weight: 650; padding: 7px 14px; border-radius: 999px; background: var(--surface); border: 1px solid var(--border-soft); color: var(--ink-2); cursor: pointer; }
.tab.active { background: var(--ink-1); color: var(--page); border-color: transparent; }
.done-row { display: flex; align-items: center; gap: 12px; padding: 13px 16px; border-top: 1px solid var(--border-soft); }
.done-row:first-child { border-top: none; }
.done-ico { width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0; display: grid; place-items: center; background: color-mix(in srgb, var(--good) 9%, transparent); color: var(--good); }
.done-name { font-size: 0.84rem; font-weight: 600; }
.done-sub { font-size: 0.7rem; color: var(--muted); margin-top: 2px; }
.done-amount { margin-left: auto; font-size: 0.82rem; font-weight: 650; font-variant-numeric: tabular-nums; }

/* Estado vacío (§13, ninguna meta creada todavía) */
.empty-wrap { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 64px 12px 0; }
.empty-ico { width: 62px; height: 62px; border-radius: 50%; display: grid; place-items: center; background: color-mix(in srgb, var(--accent) 8%, transparent); color: var(--accent); }
.empty-title { margin: 20px 0 0; font-size: 1.15rem; font-weight: 720; letter-spacing: -0.012em; }
.empty-text { margin: 10px 0 0; font-size: 0.86rem; color: var(--ink-2); line-height: 1.6; max-width: 28ch; }
.empty-cta {
  margin-top: 22px; font-family: inherit; font-size: 0.84rem; font-weight: 700; color: #fff;
  background: linear-gradient(135deg, var(--accent), #7a5fd0); border: none; cursor: pointer;
  padding: 12px 26px; border-radius: 999px; min-height: 44px;
}
.empty-alt { display: block; margin-top: 14px; font-size: 0.78rem; font-weight: 650; color: var(--accent); background: none; border: none; cursor: pointer; font-family: inherit; }

/* Transferencia: explicación permanente, obligatoria mientras tipo = ahorro/inversión (§3.3) */
/* El aviso de divisa distinta a la de la cuenta. Comparte forma con
   .transfer-note -misma familia: una nota dentro del formulario- pero en
   ambar, porque esta si pide una decision. */
/* ---- Balance del Libro ----
   La bajada NO es decorativa y por eso no es .muted a secas: es lo unico que
   separa esta tarjeta del hero de patrimonio del Panel. Se lee siempre, no solo
   la primera vez. */
.balance-sub { font-size: 0.76rem; color: var(--ink-2); margin: -2px 0 12px; }
.balance-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 10px; }
.balance-kpi { display: flex; flex-direction: column; gap: 2px; }
.balance-kpi span { font-size: 0.7rem; color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.04em; }
.balance-kpi b { font-size: 1.05rem; font-variant-numeric: tabular-nums; }
.balance-kpi b.in { color: var(--s-income); }
.balance-kpi b.out { color: var(--s-expense); }
.balance-reconcilia { font-size: 0.76rem; color: var(--ink-2); line-height: 1.5; background: var(--page); border: 1px solid var(--border-soft); border-radius: 10px; padding: 9px 11px; margin: 0 0 10px; }
.balance-aviso { font-size: 0.76rem; color: #b45309; line-height: 1.5; margin: 0 0 10px; }
@media (max-width: 380px) {
  .balance-kpis { grid-template-columns: 1fr; gap: 6px; }
  .balance-kpi { flex-direction: row; justify-content: space-between; align-items: baseline; }
}
.divisa-aviso { font-size: 0.76rem; color: #b45309; line-height: 1.5; background: var(--page); border: 1px solid #b45309; border-radius: 10px; padding: 9px 11px; }
.transfer-note { font-size: 0.76rem; color: var(--ink-2); line-height: 1.5; background: var(--page); border: 1px solid var(--border-soft); border-radius: 10px; padding: 9px 11px; }

/* ---------- Alertas ---------- */
.alerts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.alerts li { border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 8px; padding: 10px 12px; font-size: 0.85rem; }
.alerts li.tipo-limite_diario { border-left-color: var(--critical); }
.alerts li .alert-title { font-weight: 600; display: block; }
.alerts li .alert-msg { color: var(--ink-2); white-space: pre-line; }
.alerts li .alert-actions { margin-top: 6px; }

/* ---------- Chat de Sofía: cabecera fija compacta ---------- */
/* En escritorio la columna se queda fija junto al panel. La altura se acota para
   que el scroll ocurra dentro de .chat-log y no en la página.
   Se declara dos veces a propósito: 100vh es el respaldo para navegadores sin
   unidades dinámicas (iOS < 15.4, Chrome < 108), que descartan la segunda línea
   y conservan la primera. Donde dvh existe, gana por ser la última. */
.col-avatar {
  display: flex; flex-direction: column; gap: 12px; align-self: start;
  position: sticky; top: 76px;
  max-height: calc(100vh - 96px);
  max-height: calc(100dvh - 96px);
}
/* Sin position: sticky a propósito. La cabecera no necesita pegarse porque no
   se desplaza: quien tiene scroll propio es .chat-log (flex:1 + overflow-y).
   Antes era sticky DENTRO de .col-avatar, que también lo era, y el sticky
   anidado es frágil en cualquier motor — en Safari se perdía al hacer scroll. */
.chat-header {
  display: flex; align-items: center; gap: 12px;
  padding: 4px 4px 10px; background: var(--surface);
  border-bottom: 1px solid var(--grid);
}
.chat-head-info { flex: 1; min-width: 0; text-align: left; }
.chat-head-info h2 { margin: 0; font-size: 1.06rem; font-weight: 700; letter-spacing: 0.01em; }
.chat-status {
  margin: 2px 0 0; font-size: 0.72rem; color: var(--muted);
  display: flex; align-items: center; gap: 6px; white-space: nowrap; overflow: hidden;
}
.status-dot {
  width: 7px; height: 7px; border-radius: 50%; background: #10b981; flex-shrink: 0;
  animation: dot-pulse 2.4s ease-in-out infinite;
}
@keyframes dot-pulse {
  0%, 100% { box-shadow: 0 0 0 2.5px rgba(16, 185, 129, 0.20); }
  50%      { box-shadow: 0 0 0 5px rgba(16, 185, 129, 0.06); }
}
.voice-btn {
  width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0;
  display: grid; place-items: center; cursor: pointer;
  color: var(--muted); border: 1px solid var(--border);
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.voice-btn:hover { color: var(--accent); border-color: var(--accent); }
.voice-btn .v-off { display: none; }
.voice-btn:has(input:not(:checked)) .v-on { display: none; }
.voice-btn:has(input:not(:checked)) .v-off { display: block; }
.voice-btn:has(input:not(:checked)) { color: var(--critical); }
.avatar-figure {
  width: 56px; height: 56px; position: relative; flex-shrink: 0;
  display: grid; place-items: center;
}
.avatar-core {
  width: 48px; height: 48px; border-radius: 50%;
  background: linear-gradient(145deg, var(--accent), #7a5fd0);
  color: #fff; display: grid; place-items: center; z-index: 2;
  overflow: hidden; border: 2px solid var(--accent);
  box-shadow: 0 3px 12px rgba(42, 120, 214, 0.32);
}
.avatar-img {
  width: 100%; height: 100%; object-fit: cover; object-position: center 26%;
  border-radius: 50%; display: block;
}
/* Al hablar: la imagen "habla" con un vaivén rítmico */
.speaking .avatar-img { animation: talk-bob 0.38s ease-in-out infinite alternate; }
@keyframes talk-bob {
  from { transform: scale(1.00) rotate(-0.8deg) translateY(0); }
  to   { transform: scale(1.05) rotate(1.0deg)  translateY(-1.5px); }
}
/* Ecualizador de voz bajo el avatar */
.voice-eq {
  display: flex; gap: 3px; justify-content: center; align-items: flex-end;
  height: 16px; margin: 0 2px; opacity: 0; transition: opacity 0.25s; flex-shrink: 0;
}
.chat-header:has(.avatar-figure.speaking) .voice-eq { opacity: 1; }
.voice-eq span {
  width: 4px; height: 100%; background: var(--accent); border-radius: 2px;
  transform-origin: bottom; animation: eq-bounce 0.7s ease-in-out infinite;
}
.voice-eq span:nth-child(1) { animation-delay: 0s; }
.voice-eq span:nth-child(2) { animation-delay: 0.15s; }
.voice-eq span:nth-child(3) { animation-delay: 0.3s; }
.voice-eq span:nth-child(4) { animation-delay: 0.1s; }
.voice-eq span:nth-child(5) { animation-delay: 0.25s; }
@keyframes eq-bounce {
  0%, 100% { transform: scaleY(0.25); }
  50%      { transform: scaleY(1); }
}
.ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid var(--accent); opacity: 0; z-index: 1;
}
.avatar-figure.speaking .ring { animation: pulse 1.8s ease-out infinite; }
.avatar-figure.speaking .r2 { animation-delay: 0.6s; }
.avatar-figure.speaking .r3 { animation-delay: 1.2s; }
@keyframes pulse {
  0%   { transform: scale(0.75); opacity: 0.7; }
  100% { transform: scale(1.25); opacity: 0; }
}

/* ---- Sofía animada: respira, parpadea y mueve la boca al hablar ---- */
.avatar-core { animation: idle-sway 5s ease-in-out infinite; }
@keyframes idle-sway {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(2.5px) rotate(-1.2deg); }
}
.sofia-svg .lid { transform-origin: center 45px; transform: scaleY(0); animation: blink 4.6s infinite; }
.sofia-svg .eye:last-of-type .lid { animation-delay: 0.05s; }
@keyframes blink {
  0%, 93%, 100% { transform: scaleY(0); }
  95.5%, 97.5%  { transform: scaleY(1); }
}
.sofia-svg .mouth { transform-origin: 60px 73px; transform: scaleY(0.32); transition: transform 0.15s; }
.sofia-svg .smile { opacity: 1; }
.speaking .sofia-svg .mouth { animation: talk 0.26s ease-in-out infinite alternate; }
.speaking .sofia-svg .smile { opacity: 0; }
@keyframes talk {
  from { transform: scaleY(0.25) scaleX(0.9); }
  to   { transform: scaleY(1.1)  scaleX(1.05); }
}
.listening .sofia-svg { filter: drop-shadow(0 0 6px rgba(57, 135, 229, 0.8)); }
.listening .avatar-core {
  border-color: #34d399;
  box-shadow: 0 0 0 4px rgba(52, 211, 153, 0.25), 0 0 22px rgba(52, 211, 153, 0.6);
}

/* ---- Adjuntar imagen y micrófono ---- */
.btn-icon { padding: 8px 10px; font-size: 1.05rem; line-height: 1; display: grid; place-items: center; cursor: pointer; }
.btn-icon.recording { background: var(--critical); border-color: transparent; animation: rec-pulse 1.2s ease-in-out infinite; }
@keyframes rec-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.55; } }
.image-preview {
  display: flex; align-items: center; gap: 8px;
  border: 1px dashed var(--border); border-radius: 10px; padding: 6px 8px;
}
.image-preview img { height: 52px; border-radius: 6px; }
.msg .msg-img { max-width: 100%; border-radius: 8px; margin-top: 6px; display: block; }

.chat-log {
  flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 11px;
  padding: 14px 10px; min-height: 220px;
  background: var(--chat-bg); border-radius: 14px;
}
.msg {
  max-width: 86%; padding: 11px 15px; border-radius: 18px;
  font-size: 0.87rem; line-height: 1.55; white-space: pre-wrap;
}
.msg.user {
  align-self: flex-end; margin-left: 14%;
  background: linear-gradient(135deg, var(--accent), #7a5fd0); color: #fff;
  border-bottom-right-radius: 5px;
  box-shadow: 0 3px 10px rgba(42, 120, 214, 0.25);
}
.msg.assistant {
  align-self: flex-start; margin-right: 14%;
  background: var(--surface); border: 1px solid var(--border);
  border-bottom-left-radius: 5px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.msg.action {
  align-self: center; background: transparent; border: 1px dashed var(--good);
  color: var(--good); font-size: 0.78rem; padding: 5px 10px; border-radius: 999px;
}
/* Caja de entrada minimalista (píldora con iconos integrados) */
.chat-form {
  display: flex; align-items: center; gap: 2px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 999px; padding: 5px 6px;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.07);
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
}
.chat-form:focus-within { border-color: var(--accent); box-shadow: 0 3px 16px rgba(42, 120, 214, 0.18); }
.chat-form input {
  flex: 1; border: none; background: transparent; box-shadow: none;
  min-height: 40px; padding: 6px 8px; min-width: 0;
}
.chat-form input:focus { outline: none; border: none; }
.chat-icon-btn {
  width: 38px; height: 38px; min-height: 38px; border-radius: 50%; border: none;
  background: transparent; color: var(--muted); cursor: pointer;
  display: grid; place-items: center; flex-shrink: 0; padding: 0;
  transition: background 0.15s ease, color 0.15s ease;
}
.chat-icon-btn:hover { background: rgba(42, 120, 214, 0.10); color: var(--accent); }
.chat-icon-btn.recording {
  background: var(--critical); color: #fff;
  animation: rec-pulse 1.2s ease-in-out infinite;
}
.chat-send {
  width: 40px; height: 40px; min-height: 40px; border-radius: 50%; border: none;
  background: linear-gradient(135deg, var(--accent), #7a5fd0); color: #fff;
  display: grid; place-items: center; cursor: pointer; flex-shrink: 0; padding: 0;
  box-shadow: 0 3px 10px rgba(42, 120, 214, 0.35);
  transition: transform 0.14s ease, box-shadow 0.14s ease, filter 0.14s ease;
}
.chat-send:hover { filter: brightness(1.08); transform: translateY(-1px); box-shadow: 0 6px 14px rgba(42, 120, 214, 0.45); }
.chat-send:active { transform: scale(0.88); }
.chat-send:disabled { opacity: 0.55; transform: none; }


/* ---------- Top categorías (barras de progreso) ---------- */
.cats-list { display: flex; flex-direction: column; gap: 12px; }
.cat-row { display: flex; flex-direction: column; gap: 4px; font-size: 0.85rem; }
.cat-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.cat-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--ink-2); text-transform: capitalize; font-weight: 550;
}
.cat-val { font-variant-numeric: tabular-nums; color: var(--muted); font-size: 0.8rem; white-space: nowrap; }
.cat-track { height: 10px; border-radius: 6px; background: var(--grid); overflow: hidden; }
.cat-fill {
  height: 100%; border-radius: 6px; min-width: 6px;
  background: linear-gradient(90deg, var(--accent), #7a5fd0);
  transition: width 0.5s ease;
}

/* ---------- Navegación inferior (app nativa) ---------- */
.bottom-nav { display: none; }

/* ---------- Móvil primero: la app vive en el teléfono ---------- */
@media (max-width: 1000px) {
  /* El avatar y el chat de Sofía van ARRIBA: es el corazón de la app */
  .layout { display: flex; flex-direction: column; padding: 12px; gap: 14px; }
  /* ⚠️ `align-self: stretch` NO ES OPCIONAL, y no es lo que parece.
     .col-avatar lleva `align-self: start` para la REJILLA de escritorio, donde
     mantiene la columna pegada arriba. Aquí el layout pasa a columna flex, y ahí
     ese mismo valor hace que la columna se dimensione POR SU CONTENIDO en vez de
     estirarse al ancho de la pantalla. Con el tamaño de fuente del sistema al
     130 % medía 405 px sobre una pantalla de 360 y el botón de enviar quedaba
     fuera; al 160 %, 449 px. Y con fuente NORMAL ya medía 367: se salía por poco
     en cualquier móvil algo más estrecho, sin accesibilidad de por medio.

     `min-width: 0` es la corrección que parece obvia —es la que lleva .col-main—
     y AQUÍ NO ARREGLA NADA: probado y medido, cifras idénticas. La pieza que
     resuelve es `align-self`. Si alguien retoca esto, que empiece por ahí. */
  .col-avatar { order: -1; position: static; max-height: none; align-self: stretch; min-width: 0; }
  .chat-log { min-height: 160px; }
  .chat-header { top: 56px; }

  /* Objetivos táctiles cómodos (≥44px) */
  .btn { min-height: 44px; }
  .chat-icon-btn, .chat-send { width: 42px; height: 42px; min-height: 42px; }
  .btn-small { min-height: 38px; }
  input, select { min-height: 44px; font-size: 16px; } /* 16px evita el auto-zoom de iOS */
  .inline-form input, .inline-form select { flex: 1 1 100%; }

  .card { padding: 14px; border-radius: 12px; }
  /* Fichas compactas: 1 destacada a todo el ancho + el resto en rejilla 2×2 */
  .tiles { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .tile { grid-column: auto; padding: 13px 15px; }
  .hero-tile { grid-column: 1 / -1; }
  .tile-value { font-size: 1.2rem; }
  .hero-tile .tile-value { font-size: 1.7rem; }
  .insight-avatar { width: 46px; height: 46px; }
  .topbar { padding: 10px 12px; padding-top: max(10px, env(safe-area-inset-top)); }
  body { padding-bottom: env(safe-area-inset-bottom); }
}
@media (max-width: 420px) {
  .tiles { gap: 8px; }
  .tile { padding: 12px 13px; }
}

/* ---------- Pestañas móviles: una sección a la vez, como app de banco ---------- */
@media (max-width: 1000px) {
  .layout { padding-bottom: calc(78px + env(safe-area-inset-bottom)); }
  [data-tabpane] { display: none !important; }
  /* El :not(.hidden) NO es decorativo. Sin él, `[data-tabpane].tab-on` (0,2,0)
     gana a `.hidden` (0,1,0) aunque ambas sean !important, y todo lo que el JS
     esconda con classList.add('hidden') sigue viéndose por debajo de 1000px.
     Medido en producción: #install-card con .hidden computaba display:block.
     Afectaba a la tarjeta de instalación —que reaparecía tras «Ahora no»—, a la
     de avisos y a la del acuse de pago. */
  [data-tabpane].tab-on:not(.hidden) { display: block !important; }

  /* SEGUNDA CARA DEL MISMO FALLO. La regla de arriba no solo ignoraba .hidden:
     también pisa el `display` propio de cada elemento, porque `block` es lo que
     declara y gana por especificidad. Todo lo que no sea block hay que
     restituirlo aquí, uno por uno.

     Barrido de los 20 elementos con data-tabpane (2026-08-16): 5 salían mal,
     los 5 en el Panel y los 5 flex→block. Dos de ellos —.qa-row y .check-chips—
     llevaban MESES así en el móvil de todos los usuarios sin que nadie lo
     reportara: no parece un error, parece descuido, y eso nadie lo escribe.

     Medido en .insight-card antes del arreglo: icono en (27, 1199) y título en
     (27, 1171), misma x, apilados. Después: (27, 1171) y (67, 1170), a la par.

     Si se añade una tarjeta de panel que no sea block, va en esta lista. */
  .col-avatar[data-tabpane].tab-on:not(.hidden),
  .qa-row[data-tabpane].tab-on:not(.hidden),
  .check-chips[data-tabpane].tab-on:not(.hidden),
  .install-card[data-tabpane].tab-on:not(.hidden),
  .sofia-insight-card[data-tabpane].tab-on:not(.hidden),
  .pago-card[data-tabpane].tab-on:not(.hidden),
  .insight-card[data-tabpane].tab-on:not(.hidden) { display: flex !important; }
  /* Las fichas son una rejilla: al activarse deben seguir siendo grid, no block */
  .tiles[data-tabpane].tab-on:not(.hidden) { display: grid !important; }
  /* En móvil la columna NO es sticky: ocupa el alto visible y punto. Con
     min-height crecía sin límite, la página entera se desplazaba en vez del
     registro, y por eso hacía falta pegar la cabecera. Con altura acotada el
     scroll vuelve a .chat-log y la cabecera se queda quieta sin trucos.
     100dvh sigue la altura REAL visible: en iOS la barra de direcciones aparece
     y desaparece al hacer scroll, y 100vh siempre vale el viewport grande. */
  .col-avatar {
    position: static; min-height: 0;
    height: calc(100vh - 190px);
    height: calc(100dvh - 190px);
  }
  .chat-log { max-height: none; flex: 1; }

  .bottom-nav {
    display: flex; position: fixed; bottom: 0; left: 0; right: 0; z-index: 20;
    background: var(--surface); border-top: 1px solid var(--border);
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
  }
  .bottom-nav button {
    flex: 1; background: none; border: none; cursor: pointer;
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    font-size: 0.64rem; color: #94a3b8; padding: 6px 0 8px; position: relative;
    min-height: 50px; font-family: inherit; letter-spacing: 0.01em;
    transition: color 0.18s ease;
  }
  .bottom-nav button .nav-ico { transition: transform 0.18s ease; }
  .bottom-nav button.active { color: var(--accent); font-weight: 650; }
  .bottom-nav button.active .nav-ico { transform: translateY(-1.5px); }
  .bottom-nav button.active::after {
    content: ''; position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
    width: 18px; height: 3px; border-radius: 3px;
    background: linear-gradient(90deg, var(--accent), #7a5fd0);
    box-shadow: 0 0 8px rgba(42, 120, 214, 0.55);
  }
  .nav-badge {
    position: absolute; top: 2px; right: 24%;
    background: var(--critical); color: #fff; font-size: 0.62rem; font-weight: 700;
    min-width: 16px; height: 16px; border-radius: 999px;
    display: grid; place-items: center; padding: 0 4px;
  }
}

/* ---------- Tarjeta de instalación en la pantalla de inicio ----------
   Tarjeta dentro del flujo, no un modal: a quien acaba de pagar, un modal se le
   pone entre él y lo que compró. Hereda .card, así que no inventa radio, sombra
   ni color propios. */
.install-card { display: flex; flex-direction: column; gap: 8px; }
.install-title { margin: 0; font-size: 1rem; font-weight: 700; }
.install-text { margin: 0; font-size: 0.88rem; line-height: 1.45; }
.install-hint { margin: 0; font-size: 0.8rem; color: var(--muted); line-height: 1.45; }
.install-actions { display: flex; gap: 8px; margin-top: 4px; }
.install-actions .btn { min-height: 40px; }

/* ---------- HERO del patrimonio (§5.11: dato protagonista SIN tarjeta) ----------
   Sin fondo ni borde a propósito: el tamaño y el aire hacen la jerarquía. Meterlo
   en una caja lo igualaría al resto de bloques de la pantalla, y ahí se pierde.
   Reservado a UN dato por pantalla; este es el de esta. */
.hero { padding: 26px 19px 18px; }
.hero-eyebrow {
  margin: 0 0 6px; font-size: 0.68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted);
}
.hero-cifra {
  margin: 0; font-size: clamp(2.1rem, 9vw, 2.9rem); font-weight: 700;
  letter-spacing: -0.03em; line-height: 1.05; font-variant-numeric: tabular-nums;
}
.hero-pie { margin: 8px 0 0; font-size: 0.82rem; color: var(--ink-2); line-height: 1.5; }
.hero-vacio { margin: 0; font-size: 0.95rem; color: var(--ink-2); line-height: 1.55; max-width: 42ch; }
/* El delta solo existe cuando hay dos declaraciones. No hay estado "sin cambio":
   un 0 % o una flecha plana afirmarían que no varió, y lo que pasa es que no se
   sabe. La clase .igual queda por si dos declaraciones dan lo mismo, que sí es
   un hecho: entonces el usuario dijo dos veces la misma cifra. */
.hero-delta { font-weight: 700; font-variant-numeric: tabular-nums; }
.hero-delta.sube  { color: var(--good); }
.hero-delta.baja  { color: var(--s-expense); }
.hero-delta.igual { color: var(--ink-2); }

/* Botón de WhatsApp (Paso 15). Verde de WhatsApp y no el acento de la app: es un
   salto FUERA de Sofía, y conviene que se lea como tal antes de pulsarlo. */
.btn-whatsapp {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 12px;
  background: #25d366; border-color: #25d366; color: #0b2e19; text-decoration: none;
}
.btn-whatsapp:hover { background: #1fb958; border-color: #1fb958; }

/* ---------- Eliminar cuenta (Paso 14) ----------
   Rojo SOLO aquí. El borde tintado la separa del resto sin gritar: es la única
   acción irreversible de la app y tiene que leerse como tal, pero no debe
   competir por la atención con lo que el usuario venía a hacer. */
.zona-peligro { border-color: color-mix(in srgb, var(--critical) 30%, var(--border-soft)); }
.btn-peligro {
  background: var(--critical); color: #fff; border: 1px solid var(--critical);
  min-height: 44px; padding: 0 18px; border-radius: 12px;
  font: inherit; font-weight: 650; cursor: pointer;
}
.btn-peligro:disabled { opacity: 0.45; cursor: not-allowed; }
.borrar-resumen {
  margin: 14px 0 0; font-size: 0.86rem; line-height: 1.55; font-weight: 600;
  color: var(--ink-1);
}
.borrar-label { display: block; margin: 12px 0 0; font-size: 0.82rem; color: var(--ink-2); }
.borrar-label input {
  display: block; width: 100%; margin-top: 6px; min-height: 44px;
  padding: 0 12px; border: 1px solid var(--border); border-radius: 10px;
  font: inherit; background: var(--surface); color: var(--ink-1);
}
.borrar-acciones { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
#borrar-estado { margin-top: 10px; font-size: 0.82rem; line-height: 1.5; }

/* ---------- Presencia de Sofía en la interfaz (Paso 35 · §20.45) ----------
   Ninguna de estas lleva animación de entrada: el personaje acompaña el momento,
   no se anuncia. La única animada es la celebración, y la fija §20.5. */

/* La pose sustituye al retrato en la tarjeta de bienvenida: mismo hueco de 46px,
   pero recortada, así que se le quita el círculo y se deja respirar. */
.insight-avatar.pose {
  border-radius: 0; object-fit: contain; object-position: center bottom;
  background: none; border: none;
}

/* Sofía en la celebración de meta cumplida. Entra con la misma cascada que el
   resto (.cel-el) — no tiene animación propia: §20.5 fija la secuencia y el
   personaje la acompaña, no compite con el anillo. */
.cel-sofia { width: 116px; height: auto; margin-bottom: 6px; }

/* Cara preocupada en el aviso crítico, en el hueco del triángulo. Sin el fondo
   ámbar del icono: es una persona mirándote, no un símbolo de peligro. */
.insight-ico.con-sofia { background: none; width: 40px; height: 40px; overflow: visible; }
.insight-ico.con-sofia img { width: 40px; height: auto; display: block; }

/* Estado vacío con Sofía explicando. Solo donde se le pide algo al usuario. */
.vacio-sofia { display: flex; gap: 12px; align-items: flex-start; }
.vacio-sofia img { width: 62px; height: auto; flex-shrink: 0; margin-top: -4px; }
.vacio-sofia p { margin: 0; }

/* Sofía trabajando mientras se prepara el informe. Es una espera de verdad, y
   mensual: §20.45 permite aquí que el personaje aparezca y se mueva. */
.trabajando-sofia { display: inline-flex; align-items: center; gap: 9px; }
.trabajando-sofia img { width: 44px; height: auto; }
@media (prefers-reduced-motion: no-preference) {
  /* Respiración: escala mínima y lenta. Lo justo para que se lea "está pasando"
     y no "se ha colgado". Más amplitud y pasaría a ser un rebote de dibujo. */
  .trabajando-sofia .respira { animation: sofia-respira 2.2s ease-in-out infinite; transform-origin: center bottom; }
  @keyframes sofia-respira {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.045); }
  }
}

/* ---------- Patrimonio declarado ----------
   Cifra grande sin caja propia (§5.11): ya está dentro de una tarjeta, y meterle
   otra la igualaría al resto. El pie explica de dónde sale, porque un número sin
   procedencia invita a creer que lo calculamos nosotros. */
.patri-cifra {
  font-size: 1.85rem; font-weight: 700; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums; line-height: 1.15;
}
.patri-grafica { margin-top: 10px; }
.patri-grafica svg { display: block; width: 100%; height: auto; }
.patri-pie { margin-top: 6px; font-size: 0.8rem; line-height: 1.5; }

/* ---------- Acuse de recibo del pago ----------
   Tono de éxito, no de fiesta: la celebración a toma total de color (§20.5) está
   reservada en exclusiva a completar una meta, y confundir "he pagado" con "lo
   he conseguido" sería gamificar el cobro (Ley 18). Un borde tintado basta. */
.pago-card {
  display: flex; gap: 12px; align-items: flex-start;
  border-color: color-mix(in srgb, var(--good) 34%, var(--border-soft));
  background: linear-gradient(180deg, color-mix(in srgb, var(--good) 5%, var(--surface)) 0%, var(--surface) 60%);
}
.pago-ico {
  width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0; margin-top: 1px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--good) 12%, transparent); color: var(--good);
}
.pago-cuerpo { flex: 1; min-width: 0; }
.pago-titulo { font-size: 0.92rem; font-weight: 700; margin-bottom: 4px; }
.pago-texto { font-size: 0.84rem; color: var(--ink-2); line-height: 1.55; }
.pago-lista { margin: 10px 0 0; padding-left: 18px; font-size: 0.84rem; color: var(--ink-2); line-height: 1.6; }
.pago-lista li { margin-bottom: 3px; }
.pago-lista li::marker { color: var(--good); }
.pago-cerrar { margin-top: 12px; min-height: 40px; }
/* Mientras se espera al webhook el check aún no es verdad: se atenúa y late.
   Afirmar el éxito antes de confirmarlo es exactamente lo que no se hace. */
.pago-card.is-esperando .pago-ico { background: color-mix(in srgb, var(--muted) 14%, transparent); color: var(--muted); }
@media (prefers-reduced-motion: no-preference) {
  .pago-card.is-esperando .pago-ico { animation: pago-latido 1.4s ease-in-out infinite; }
  @keyframes pago-latido { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
}

/* ---------- Transición compuesta entre pestañas (design system §10.1) ----------
   Dos capas en ejes distintos: el contenedor viaja en horizontal y las tarjetas
   se asientan en vertical. Ninguna de las dos toca la opacidad — animarla en
   ambas lava el color, porque cada capa se ve al 50 % de la otra y sale gris. */

/* Capa 1 — contenedor. Solo transform, 300 ms. */
.tab-dir-der [data-tabpane].tab-on,
.tab-dir-izq [data-tabpane].tab-on { animation: tab-contenedor 300ms cubic-bezier(.22, .9, .3, 1) both; }
.tab-dir-izq [data-tabpane].tab-on { animation-name: tab-contenedor-izq; }

@keyframes tab-contenedor     { from { transform: translateX(22px); }  to { transform: none; } }
@keyframes tab-contenedor-izq { from { transform: translateX(-22px); } to { transform: none; } }

/* Capa 2 — tarjetas. 18 px vertical + escala 0.955, con rebote al asentarse.
   Por debajo de 0.92 deja de parecer que se asienta y parece un zoom. */
.tab-anim [data-tabpane].tab-on > * { animation: tab-tarjeta 440ms cubic-bezier(.32, 1.3, .5, 1) both; }
.tab-anim [data-tabpane].tab-on > *:nth-child(1) { animation-delay: 0ms; }
.tab-anim [data-tabpane].tab-on > *:nth-child(2) { animation-delay: 60ms; }
.tab-anim [data-tabpane].tab-on > *:nth-child(3) { animation-delay: 120ms; }
/* Tope en la cuarta: más allá, el escalonado se percibe como lentitud. */
.tab-anim [data-tabpane].tab-on > *:nth-child(n+4) { animation-delay: 180ms; }

@keyframes tab-tarjeta { from { transform: translateY(18px) scale(0.955); } to { transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .tab-dir-der [data-tabpane].tab-on,
  .tab-dir-izq [data-tabpane].tab-on,
  .tab-anim [data-tabpane].tab-on > * { animation: none; }
}

/* ---------- Paso 18 · Microanimaciones de entrada de Home ----------
   La transición de pestañas (§10.1) NO cubre esto: `indicePrevio` arranca en
   'panel', así que en la primera carga el destino coincide con el previo y no se
   dispara nada. Esta cascada es para ese momento, y se ejecuta UNA vez.

   Aquí sí se anima la opacidad, al contrario que en §10.1. El motivo de aquella
   prohibición era concreto —dos capas visibles a la vez se lavan de color al
   cruzarse— y en una entrada no hay dos capas: solo una que llega. */
@media (prefers-reduced-motion: no-preference) {
  .col-main.entrada > * { animation: entrada-bloque 520ms cubic-bezier(0.22, 1, 0.36, 1) both; }
  .col-main.entrada > *:nth-child(1) { animation-delay: 0ms; }
  .col-main.entrada > *:nth-child(2) { animation-delay: 70ms; }
  .col-main.entrada > *:nth-child(3) { animation-delay: 140ms; }
  /* Mismo tope que §10.1: más allá de la cuarta, el escalonado se lee como lentitud. */
  .col-main.entrada > *:nth-child(n+4) { animation-delay: 210ms; }
  @keyframes entrada-bloque {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
  }

  /* Revelado del gráfico: el trazo se dibuja, y lo demás llega detrás. El área,
     los puntos y las etiquetas entran por opacidad para no competir con la línea. */
  .patri-grafica .g-entra { animation: g-aparecer 600ms ease 500ms both; }
  @keyframes g-aparecer { from { opacity: 0; } to { opacity: 1; } }
}

/* ---------- Enlaces dentro de los estados vacíos ----------
   Un botón que se lee como enlace: la acción va dentro de la frase que explica
   qué aparecerá en ese bloque, no en un botón aparte que rompa la lectura. */
.link-inline {
  font: inherit; color: var(--accent); background: none; border: none;
  padding: 0; cursor: pointer; text-decoration: underline;
  text-underline-offset: 2px; font-weight: 650;
}
.link-inline:hover { text-decoration-thickness: 2px; }
.link-inline:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.foda-vacio { line-height: 1.6; padding: 4px 2px 2px; max-width: 62ch; }

/* La conversión de un movimiento en otra divisa, para poder comprobarla en vez
   de creérsela: importe original × tasa = euros, y de qué día es esa tasa. */
.sheet-conv {
  margin-top: 6px; font-size: 0.72rem; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* La cuenta de origen en la fila del Libro. Discreta: es contexto, no el dato
   principal, y solo aparece cuando el usuario tiene mas de una cuenta. */
.tx-cuenta { color: var(--ink-2); }

/* ---------- Cuentas (Paso 39.a) ----------
   Vive en el Libro, junto al registro de movimientos, porque ahí es donde nace
   la pregunta "¿de qué cuenta salió esto?". Discreta: es una herramienta, no el
   contenido de la pantalla. */
.acc-box { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line); }
.acc-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.acc-titulo { font-size: 0.78rem; font-weight: 700; color: var(--ink-2); }
.acc-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 10px;
  align-items: center; padding: 7px 0; font-size: 0.74rem;
  border-bottom: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
}
.acc-nombre { font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acc-meta { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.acc-arch {
  font-family: inherit; font-size: 0.66rem; font-weight: 650; color: var(--muted);
  background: none; border: none; cursor: pointer; padding: 4px 6px; min-height: 32px;
}
.acc-arch:hover { color: var(--critical); }
.acc-vacio { font-size: 0.72rem; line-height: 1.55; margin: 8px 0 0; }
@media (max-width: 380px) {
  .acc-row { grid-template-columns: minmax(0, 1fr) auto; }
  .acc-meta { grid-column: 1 / -1; }
}

/* Aviso de voz muda. Solo aparece cuando Sofía NO ha conseguido sonar, así que
   no ensucia la conversación de quien la oye bien —que hoy son todos menos los
   de iPhone—. Discreto: informa de un fallo, no lo dramatiza. */
.voz-aviso {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-top: 8px; padding-top: 8px; font-size: 0.72rem; color: var(--muted);
  border-top: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
}
.voz-reintento {
  font-family: inherit; font-size: 0.7rem; font-weight: 650; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  border-radius: 999px; padding: 6px 12px; min-height: 32px; cursor: pointer;
}
.voz-reintento:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.voz-pista { flex-basis: 100%; line-height: 1.5; }
