
/* GRADIENT TEXT ---------------------------------------------------------------------------
   Names and hero figures are painted with the brand gradient and color:transparent. On a light
   page the violet-pink gradient is far too pale to read - his screenshot of "Bhavneet Singh" on
   Profile. It also cannot be caught by a contrast check, because the text has no colour to
   measure; that is why every sweep I ran passed it.
   A separate token so the gradient can darken for text without touching gradient BUTTONS, which
   carry white text and must stay bright. */
:root         { --grad-text: linear-gradient(90deg,#2DD4BF,#38BDF8,#8B8CF8); }
body.light    { --grad-text: linear-gradient(90deg,#0F766E,#0369A1,#4338CA); }
body.v2       { --grad-text: linear-gradient(90deg,var(--g1),var(--g2),var(--g3)); }
body.v2.light { --grad-text: linear-gradient(90deg,#4C1D95,#6D28D9,#9D174D); }

/* The 2.0 "quiet" grey was #5c5e72, which measures 2.77:1 on the card - genuinely hard to read, and
   it is used for subtab labels, tile captions and KPI keys, so it is everywhere. Raised to var(--quiet),
   which clears AA on the same surface and keeps the same muted character. Dark is the DEFAULT
   theme, so this was hitting every user, not just the ones who switch. */
/* Muse Nexus Money — mobile-first PWA */
* { margin:0; padding:0; box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
:root {
  color-scheme: dark;   /* native selects, date pickers & calendars follow the app theme */
  --bg:#0B1020; --bg2:#070B16; --card:#0D1426; --line:#1E2A44;
  /* #E7ECF5 measured 15.5:1 against the card — roughly double what dark-mode reading wants, and
     he felt it ("white is making eyes fishy"). #C9D2E4 is 12.1:1 — his pick, B. Paired with dropping
     the 800-weight figures to 700, since heavy bold reads brighter than its hex suggests. */
  --text:#C9D2E4; --dim:#8FA3C8; --mid:#B9C6DE;
  --teal:#2DD4BF; --blue:#38BDF8; --purple:#8B8CF8;
  --gold:#F59E0B; --red:#F87171;
  --grad:linear-gradient(90deg,#2DD4BF,#38BDF8,#8B8CF8);
}
html { -webkit-text-size-adjust:100%; text-size-adjust:100%; }
html,body { height:100%; }
body {
  background:
    radial-gradient(900px 600px at 85% -10%, rgba(56,189,248,.13), transparent 60%),
    radial-gradient(760px 500px at -10% 108%, rgba(45,212,191,.10), transparent 55%),
    linear-gradient(165deg,var(--bg) 0%,var(--bg2) 100%);
  color:var(--text);
  /* Explicit base. Without one, anything not given a size inherits the browser's 16px default --
     an invisible extra tier that put "Ask your Money Coach" and "Back up your data" a size above
     every other body line. 13.5px is the app's dominant body step. */
  font-size:13.5px;
  font-family:'Segoe UI', system-ui, -apple-system, Roboto, Arial, sans-serif;
  min-height:100dvh;
  overflow-x:hidden;   /* nothing should ever scroll/shift the page sideways */
}
#app { display:flex; flex-direction:column; min-height:100dvh; max-width:560px; margin:0 auto; }
/* On anything wider than a phone (tablet, landscape, browser) the centered column used to sit on flat
   black — "sides are empty all over the app". Give the gutters the same glow as the app itself. */
@media (min-width:600px) {
  #app { box-shadow:0 0 80px rgba(4,7,15,.55); border-left:1px solid rgba(255,255,255,.05); border-right:1px solid rgba(255,255,255,.05); }
}

/* branded launch screen — the logo "magically appears", then it fades out */
#splash { position:fixed; inset:0; z-index:400; display:flex; align-items:center; justify-content:center; padding:24px;
  overflow:hidden;
  background:
    radial-gradient(900px 620px at 85% -12%, rgba(56,189,248,.18), transparent 60%),
    radial-gradient(760px 520px at -12% 110%, rgba(139,140,248,.14), transparent 55%),
    linear-gradient(165deg,var(--bg) 0%,var(--bg2) 100%);
  opacity:1; transition:opacity .5s ease, visibility .5s; }
#splash.hide { opacity:0; visibility:hidden; pointer-events:none; }
/* soft aura that blooms behind everything */
.sp-aura { position:absolute; width:560px; height:560px; border-radius:50%; pointer-events:none;
  background:radial-gradient(circle, rgba(56,189,248,.22), rgba(139,140,248,.10) 45%, transparent 70%);
  filter:blur(6px); opacity:0; animation:sp-aura 2.4s ease-out forwards; }
.sp-inner { position:relative; text-align:center; }
.sp-logo-wrap { position:relative; width:132px; height:132px; margin:0 auto; }
.sp-logo { width:132px; height:132px; border-radius:30px; display:block;
  box-shadow:0 22px 60px rgba(4,7,15,.55), 0 0 70px rgba(56,189,248,.35);
  opacity:0; transform:scale(1.35); filter:blur(16px);
  animation:sp-reveal 1.05s cubic-bezier(.2,.7,.2,1) forwards, sp-float 3.4s ease-in-out 1.1s infinite; }
/* expanding rings that burst out as the logo lands */
.sp-ring { position:absolute; inset:0; margin:auto; width:132px; height:132px; border-radius:34px;
  border:2px solid rgba(56,189,248,.55); opacity:0; animation:sp-burst 1.15s ease-out .35s forwards; }
.sp-ring.r2 { border-color:rgba(139,140,248,.5); animation-delay:.5s; }
/* product-hero splash: small MUSE NEXUS endorsement line, big MONEY HQ (the product) */
.sp-eyebrow { margin-top:24px; font-size:12.5px; color:var(--mid); letter-spacing:5px; opacity:0; animation:sp-rise .6s ease-out .45s forwards; }
.sp-word { margin-top:7px; font-size:33px; letter-spacing:3px; opacity:0; animation:sp-rise .6s ease-out .55s forwards; }
.sp-word .m { font-weight:300; color:var(--text); }
.sp-word .n { font-weight:800; background:var(--grad-text); -webkit-background-clip:text; background-clip:text; color:transparent; }
.sp-sub { margin-top:8px; font-size:13.5px; color:var(--mid); letter-spacing:2.2px; text-transform:uppercase;
  font-weight:600; opacity:0; animation:sp-rise .6s ease-out .72s forwards; }
.sp-bar { margin:26px auto 0; width:148px; height:3px; border-radius:99px; background:rgba(255,255,255,.09);
  overflow:hidden; opacity:0; animation:sp-rise .5s ease-out .9s forwards; }
.sp-bar span { display:block; height:100%; width:42%; border-radius:99px; background:var(--grad); animation:sp-load 1.15s ease-in-out infinite; }
@keyframes sp-reveal {
  0%   { opacity:0; transform:scale(1.35) rotate(-6deg); filter:blur(16px) brightness(1.7); }
  60%  { opacity:1; transform:scale(.97) rotate(1.5deg); filter:blur(0) brightness(1.15); }
  100% { opacity:1; transform:scale(1) rotate(0); filter:blur(0) brightness(1); } }
@keyframes sp-burst { 0%{ opacity:.9; transform:scale(.7); } 100%{ opacity:0; transform:scale(1.9); } }
@keyframes sp-aura  { 0%{ opacity:0; transform:scale(.4); } 55%{ opacity:1; } 100%{ opacity:.7; transform:scale(1); } }
@keyframes sp-float { 0%,100%{ transform:translateY(0); } 50%{ transform:translateY(-7px); } }
@keyframes sp-load  { 0%{ transform:translateX(-130%); } 100%{ transform:translateX(360%); } }
@keyframes sp-rise  { from{ opacity:0; transform:translateY(12px); } to{ opacity:1; transform:none; } }
@media (prefers-reduced-motion: reduce) {
  .sp-aura, .sp-logo, .sp-ring, .sp-word, .sp-sub, .sp-bar, .sp-bar span { animation:none; opacity:1; filter:none; transform:none; }
}

/* top bar */
#topbar { display:flex; align-items:center; justify-content:space-between; gap:6px;
  padding:calc(env(safe-area-inset-top, 0px) + 14px) 14px 12px; }
.tb-left { flex:1 1 0; display:flex; align-items:center; gap:8px; min-width:0; }   /* logo (+ back) sit top-left; equal-width sides keep the title centred */
.brand-logo { cursor:pointer; }   /* tap the MN mark to go home */
.brand { flex:0 1 auto; min-width:0; text-align:center; font-size:17px; letter-spacing:3px; padding:0 6px; overflow:hidden; }
.brand-logo { width:30px; height:30px; border-radius:8px; object-fit:cover; flex:0 0 auto; }
.brand .m { font-weight:300; color:#fff; }
.brand .n { font-weight:800; background:var(--grad-text); -webkit-background-clip:text; background-clip:text; color:transparent; }
.brand .sub { font-weight:400; color:var(--dim); letter-spacing:1px; font-size:13.5px; margin-left:6px; }
.brand .sub.page-title { color:var(--text); font-weight:800; font-size:15px; letter-spacing:.3px; margin-left:0; text-transform:none;
  display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:100%; }   /* never collide with the side buttons — shrink/ellipsize first */
.iconbtn { background:var(--card); border:1px solid var(--line); color:var(--mid);
  border-radius:12px; width:38px; height:38px; font-size:17px; cursor:pointer; position:relative; }
/* unread-count badge on the notification bell */
/* top:-4px right:-4px poked the badge past its own button into the 3px gap the squeezed 2.0 nav
   leaves, so "9+" sat on top of the NEIGHBOURING icon - his "top right icons mix up". Contained now:
   it may overlap its own bell, never the button next door. */
.notif-badge { position:absolute; top:-3px; right:0; min-width:17px; height:17px; padding:0 3px; box-sizing:border-box;
  background:var(--red); color:#fff; font-size:10px; font-weight:800; line-height:13px; text-align:center;
  border-radius:9px; border:2px solid var(--bg); }   /* line-height = height − 2×border, so the count sits INSIDE the circle */
.notif-badge[hidden] { display:none; }
/* notification panel */
.notif-list { display:flex; flex-direction:column; gap:2px; margin:2px -4px 4px; max-height:64vh; overflow-y:auto; }
.notif-row { display:flex; align-items:flex-start; gap:11px; width:100%; text-align:left; padding:12px 10px;
  background:none; border:none; border-radius:12px; cursor:pointer; color:var(--text); font:inherit; }
.notif-row:not(:last-child) { border-bottom:1px solid rgba(30,42,68,.55); }
.notif-row:active { background:rgba(45,212,191,.07); }
.notif-row.unread { background:rgba(56,189,248,.07); }
.notif-ic { flex:0 0 auto; width:34px; height:34px; border-radius:10px; display:flex; align-items:center; justify-content:center;
  font-size:17px; background:var(--bg2); border:1px solid var(--line); }
.notif-body { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.notif-title { font-size:13.5px; font-weight:700; overflow-wrap:anywhere; line-height:1.3; }
.notif-row.unread .notif-title::before { content:"•"; color:var(--blue); font-weight:900; margin-right:6px; }
.notif-sub { font-size:12.5px; color:var(--dim); overflow-wrap:anywhere; line-height:1.35; }
.notif-when { flex:0 0 auto; font-size:11.5px; color:var(--dim); white-space:nowrap; padding-top:2px; }
.notif-empty { text-align:center; padding:26px 12px; color:var(--mid); font-size:15px; }
.notif-foot { margin-top:10px; padding-top:12px; border-top:1px solid var(--line); }
.notif-foot .btn { width:100%; margin-top:0; }
/* notification-preference rows in Settings */
.np-row { font-size:13.5px; }
.np-row:not(.np-master){ padding-left:13px; padding-right:13px; }
.np-row .dim { font-size:11.5px; }
.np-row.np-master { border:1px solid var(--line); border-radius:12px; padding:11px 12px; background:var(--bg2); font-weight:700; }
.notif-head { display:flex; align-items:center; justify-content:space-between; margin:2px 2px 6px; font-size:12.5px; color:var(--dim); }
.notif-clear { background:none; border:none; color:var(--red); font-size:12.5px; font-weight:800; cursor:pointer; padding:4px 6px; }
.notif-del { flex:0 0 auto; align-self:center; width:26px; height:26px; border-radius:8px; border:1px solid var(--line);
  background:var(--card); color:var(--dim); font-size:11.5px; cursor:pointer; display:flex; align-items:center; justify-content:center; }
.notif-del:active { transform:scale(.9); }

/* main scroll area */
/* 96px was less than the FAB's own footprint (it sits 78px up and is 56px tall), so the last
   row could never be scrolled out from under it. Ducking the FAB while scrolling wasn't enough
   on its own — at rest it still covered the amount. 156px lets content clear it completely. */
#view { flex:1; overflow-y:auto; overflow-x:hidden; padding:4px 16px 92px; }
/* the extra room is only needed where the ＋ button actually floats — every other page
   was reserving 156px of dead space below its last card (his: "so much space at bottom") */
body.has-fab #view { padding-bottom:150px; }
#app { overflow-x:hidden; }

/* bottom tab bar */
body.kb-open #tabbar, body.kb-open .fab { display:none !important; }
/* The profile picker is a GATE, not a page. While it is up the bottom bar and the add button are
   gone, because a bar is a way around a gate: a kid handed the phone could tap More and walk
   straight into the parent's side without ever choosing a profile. Same mechanism as kb-open,
   which already hides both when the keyboard is up. */
body.gate #tabbar, body.gate .fab { display:none !important; }
.fs-range { width:100%; margin:10px 0 2px; accent-color:var(--acc,#a855f7); height:26px; }
.fs-ends { display:flex; justify-content:space-between; color:var(--mid); font-size:11.5px; margin-bottom:8px; }
.fs-prev { border:1px dashed var(--line); border-radius:12px; padding:11px 12px; color:var(--txt);
           font-size:14px; margin-bottom:10px; }
.dv-goal-row { display:flex; align-items:center; gap:10px; margin-top:12px; }
.dv-goal-row .l { flex:1; min-width:0; }
.dv-goal-row .t { font-weight:700; font-size:14.5px; }
.dv-goal-row .d { color:var(--mid); font-size:12.5px; margin-top:2px; }
.v2ps-warn { border-color:rgba(251,191,36,.45); background:rgba(251,191,36,.08); cursor:pointer; margin-bottom:12px; }
.v2ps-warn-t { font-weight:800; font-size:14px; margin-bottom:3px; }
.v2ps-warn-d { color:var(--mid); font-size:12.5px; line-height:1.5; }
body.gate .wrap, body.gate #app { padding-bottom:24px !important; }
#tabbar { position:fixed; bottom:0; left:50%; transform:translateX(-50%);
  width:100%; max-width:560px; display:flex; background:rgba(9,14,28,.92);
  backdrop-filter:blur(12px); border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom, 0px); z-index:50; }
.tab { flex:1; background:none; border:none; color:var(--dim); font-size:10.5px;
  padding:9px 0 8px; display:flex; flex-direction:column; align-items:center; gap:3px; cursor:pointer; }
.tab .ti { font-size:17px; line-height:1; }
.tab.active { color:var(--teal); }

/* cards */
.card { background:var(--card); border:1px solid var(--line); border-radius:18px;
  padding:18px; margin-top:14px; }
.card h3 { font-size:13.5px; color:var(--dim); text-transform:uppercase; letter-spacing:1.6px;
  font-weight:600; margin-bottom:10px; }
/* NOWRAP IS THE BUG FIX, NOT THE SIZE. His report: "minus sign seems upwarda squueze the text when
   digits increases", with a screenshot of Safe to spend reading
        —
     C$12,200.00
   The value was too wide for 360px, so it wrapped — and the only break opportunity was right after
   the minus, which left the sign stranded on its own line looking like a stray dash rather than
   part of the number. A figure that can wrap anywhere can wrap somewhere that changes its meaning.
   The size steps below then keep a long value inside the card instead of clipping it. */
.hero-num { font-size:44px; font-weight:800; line-height:1.05; text-align:center;
  background:var(--grad-text); -webkit-background-clip:text; background-clip:text; color:transparent;
  font-variant-numeric:tabular-nums; white-space:nowrap; }
.hero-num.len-l  { font-size:34px; letter-spacing:-.5px; }
.hero-num.len-xl { font-size:27px; letter-spacing:-.7px; }
/* HIS QUESTION: "why safe to spend xolour changes- green or red thats what values define right".
   Answer: until now it did not mean anything. .hero-num paints with --grad-text, which is the
   ACCENT the user picked in Appearance — so the number was brand-coloured whether he had $4,000 of
   room or was $12,200 over, and changing accent changed the "meaning". He was right to read a
   colour as a signal; it simply was not one.
   Now the one state that genuinely is bad says so. Green is deliberately NOT added for the normal
   case: every healthy month would be green, which makes green the background and drains the red of
   its force. Red only, only when over budget.
   -webkit-text-fill-color is required because the gradient paints through a transparent fill;
   setting `color` alone does nothing, which is the trap .v2net-n.bad already documents. */
.hero-num.bad { background:none; color:#ef4444; -webkit-text-fill-color:#ef4444; }
body.light .hero-num.bad { color:#B91C1C; -webkit-text-fill-color:#B91C1C; }
.hero-sub { color:var(--dim); font-size:13.5px; margin-top:6px; text-align:center; }
.hero-card { cursor:pointer; }

.grid2 { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:14px; }
.stat { background:var(--card); border:1px solid var(--line); border-radius:16px; padding:14px; cursor:pointer; }
.stat .k { color:var(--dim); font-size:11.5px; text-transform:uppercase; letter-spacing:1.2px; white-space:normal; overflow-wrap:anywhere; line-height:1.25; }
.stat .v { font-size:21px; font-weight:700; margin-top:5px; font-variant-numeric:tabular-nums; }
.stat .s { font-size:12.5px; color:var(--dim); margin-top:3px; }

/* rows / lists */
.row { display:flex; justify-content:space-between; align-items:center; gap:10px;
  padding:12px 2px; border-bottom:1px solid rgba(30,42,68,.55); }
.row:last-child { border-bottom:none; }
/* family-budget expense list: tighter, consistent rows (user: "reduce the excessive spacing") */
#fam-exps .row { padding:8px 2px; }
#fam-exps .row .t { font-size:13.5px; }
#fam-exps .row .d { font-size:11.5px; }
#fam-exps .swrow { border-bottom-width:0; }
.row .l  { min-width:0; flex:1; }   /* fill the middle — otherwise space-between floats the text between icon and amount (Coming-up rows looked randomly centered) */
.subico { flex:0 0 auto; width:38px; height:38px; border-radius:11px; display:flex; align-items:center;
  justify-content:center; font-size:19px; background:rgba(45,212,191,.10); border:1px solid var(--line); margin-right:11px; }
.tl-ic { flex:0 0 auto; width:36px; height:36px; border-radius:10px; display:flex; align-items:center;
  justify-content:center; font-size:18px; background:var(--bg2); border:1px solid var(--line); margin-right:11px; }
.tl-row { cursor:pointer; }
.tl-row.soon .tl-ic { background:rgba(248,113,113,.12); border-color:rgba(248,113,113,.30); }
.fc-dows { display:grid; grid-template-columns:repeat(7,1fr); gap:4px; margin:10px 0 4px; }
.fc-dows div { text-align:center; font-size:11.5px; font-weight:700; color:var(--dim); text-transform:uppercase; }
.fc-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:4px; width:100%; }
.fc-cell { position:relative; aspect-ratio:1/0.82; min-width:0; display:flex; align-items:center; justify-content:center; border-radius:9px;
  font-size:13.5px; color:var(--mid); background:var(--bg2); border:1px solid transparent; }
.fc-cell.empty { background:none; }
.fc-cell.has { color:var(--text); font-weight:700; background:rgba(120,140,175,.10); }
.fc-cell.today { border-color:var(--teal); color:var(--teal); font-weight:800; }
.fc-dot { position:absolute; bottom:4px; width:4px; height:4px; border-radius:50%; background:var(--mid); }
.fc-dot.bad { background:var(--red); } .fc-dot.good { background:var(--teal); } .fc-dot.gold { background:var(--gold); }
.lesson-row { cursor:pointer; }
.lesson-row.rec .tl-ic { background:rgba(45,212,191,.14); border-color:rgba(45,212,191,.30); }
.dupcard { border:1px solid rgba(245,158,11,.28); background:rgba(245,158,11,.05); }
/* his 2026-09-02 "design flaw — screen within screen": the old bordered, re-grounded box drew a
   card INSIDE the card. Groups now read flat in the card's own ground, separated by a dashed
   divider — grouping stays legible, the nesting goes. */
.dupgroup { border:0; border-radius:0; background:transparent; padding:0; margin-top:6px; }
.dupgroup + .dupgroup { border-top:1px dashed var(--line); margin-top:10px; padding-top:8px; }
.lesson-body p { font-size:13.5px; line-height:1.6; color:var(--mid); margin:0 0 12px; }
.lesson-body p:last-child { margin-bottom:0; }
.lesson-body b { color:var(--text); }
.row .t  { font-size:15px; font-weight:600; overflow-wrap:anywhere; line-height:1.3; }
.row[data-row] .t { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* v678 spending redesign: expense rows are exactly TWO lines — title + one meta line — so a long
   description ellipsizes on its single line instead of stacking a third (his "3-line wrapping,
   ~6 rows per screen"). The generic [data-row] rule above already trims with …; this keeps only
   the smaller size expenses always had. */
.row[data-row^="budget.txns"] .t { font-size:13.5px; line-height:1.28; }
.row[data-row^="insurance|"] .amt { font-size:12.5px; white-space:nowrap; }   /* quieter $/yr so the name gets the room */
.row .d  { font-size:12.5px; color:var(--dim); margin-top:2px; overflow-wrap:anywhere; }
.row .amt.mbal { font-size:12.5px; white-space:nowrap; }   /* member owes/gets-back: quieter than money amounts */
.row .r  { text-align:right; flex-shrink:0; display:flex; align-items:center; justify-content:flex-end; gap:9px; }   /* real gap between value · info · delete (user: policies value needs a gap) */
/* ⇄ MOVE TO THE FAMILY BUDGET, back on the row (his instruction: it was hidden inside the expense
   sheet and he wants it outside for quick access). Sized 36px so a thumb can hit it without
   catching the ✕ or the amount beside it — this is the row's most destructive neighbourhood, and a
   cramped glyph here means someone deletes an expense they meant to share. It sits BEFORE the
   amount so the money stays where the eye already looks for it, hard against the right edge. */
.row .rowmove { flex:none; width:36px; height:36px; border-radius:10px; border:1px solid var(--line);
  background:rgba(255,255,255,.04); color:var(--dim); font-size:16px; line-height:1; cursor:pointer;
  display:grid; place-items:center; padding:0; }
.row .rowmove:hover, .row .rowmove:focus-visible { color:var(--fg); border-color:var(--acc); }
/* dividends income history: year blocks + wrapped month grid (never scrolls sideways) */
.dh-year { margin-top:10px; }
.dh-head { display:flex; justify-content:space-between; align-items:center; font-size:13.5px; margin-bottom:6px; }
.dh-grid { display:grid; grid-template-columns:repeat(4, 1fr); gap:5px; }
.dh-cell { background:rgba(255,255,255,.03); border:1px solid var(--line); border-radius:9px; padding:5px 4px; text-align:center; }
.dh-cell.has { border-color:rgba(45,212,191,.45); }
.dh-m { display:block; font-size:9px; text-transform:uppercase; letter-spacing:.3px; color:var(--dim); }
.dh-v { display:block; font-size:12.5px; font-weight:700; margin-top:1px; font-variant-numeric:tabular-nums; }
.dh-cell:not(.has) .dh-v { color:var(--dim); font-weight:400; }
.row.agrow { padding:7px 0; min-height:0; }
.row.agrow .t { font-size:13.5px; }
.row.agrow .amt { font-size:13.5px; }
/* subscription names show in FULL — wrap instead of truncating to "Amazon Pri…" (the inline KEEP/TRIAL tag flows after) */
.row.subrow .t { display:flex; align-items:center; gap:7px; min-width:0; line-height:1.3; }
.row.subrow .subnm { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; font-size:15px; }   /* subscription names stay on ONE line, same size as Policies */
.row.subrow .statuschip { flex:0 0 auto; }
/* status chip (keep/cancel/trial) sits UNDER the price, bottom-right of each row */
.row.subrow .r.sub-r { flex-direction:column; align-items:flex-end; gap:6px; }
/* invest tickers show in full — a long ticker (BRK.B, BTC-USD) wraps instead of clipping to "BRK.…"; the
   $/yr value column already holds its width (flex-shrink:0), so the ticker just gets more room (user: "if ticker value does not fit expand the columns") */
.row[data-row^="divs."] .t, .row[data-row^="port."] .t { white-space:normal; overflow:visible; text-overflow:clip; line-height:1.3; }
.row .d.wrapok { white-space:normal; line-height:1.45; }   /* detail lines may wrap — better than losing the provider */
/* kids hero: emoji stacked above the label */
.btnrow.kidsrow button { display:flex; flex-direction:column; align-items:center; gap:3px; padding:10px 12px; flex:1; }
.btnrow.kidsrow .bi { font-size:22px; line-height:1; }
.btnrow.kidsrow .bl { font-size:12.5px; font-weight:700; }
.attach-thumb { max-width:110px; max-height:110px; border-radius:10px; border:1px solid var(--line); display:block; margin-bottom:8px; cursor:pointer; }
/* sort chips above the big lists */
.sortbar { display:flex; align-items:center; gap:6px; flex-wrap:wrap; margin:2px 0 8px; font-size:11.5px; color:var(--dim); }
.sortchip { background:var(--bg2); border:1px solid var(--line); color:var(--mid); border-radius:99px; padding:4px 11px; font-size:11.5px; cursor:pointer; }
.sortchip.on { background:rgba(45,212,191,.14); border-color:var(--teal); color:var(--teal); font-weight:700; }
.rstack { display:flex; flex-direction:column; align-items:flex-end; }
.rstack .d { font-size:11.5px; margin-top:1px; }
.row .amt { font-size:15px; font-weight:700; font-variant-numeric:tabular-nums; }
.row .sub2 { font-size:11.5px; color:var(--dim); margin-top:2px; }

.good { color:var(--teal); } .bad { color:var(--red); } .gold { color:var(--gold); }
.blue { color:var(--blue); } .dim { color:var(--dim); }

/* buttons */
.btn { display:block; width:100%; background:var(--grad); color:#06111c; border:none;
  font-weight:800; font-size:15px; border-radius:14px; padding:14px; margin-top:14px; cursor:pointer; }
/* secondary buttons carry a subtle brand tint (not a faint grey outline) so actions read as tappable app-wide */
.btn.ghost { background:rgba(45,212,191,.11); color:var(--teal); border:1px solid rgba(45,212,191,.32); font-weight:700; }
.btn.ghost:active { transform:scale(.98); }
.btn.ghost[style*="F87171"], .btn.ghost[style*="f87171"] { background:rgba(248,113,113,.11); border-color:rgba(248,113,113,.32); }   /* destructive ghost buttons get a red tint to match their red text */
.btn.small { padding:10px; font-size:13.5px; margin-top:10px; }
.btn.danger { background:linear-gradient(90deg,#F87171,#FB7185); color:#fff; }
.btn.gold   { background:linear-gradient(90deg,#F59E0B,#FBBF24); color:#1a1200; }         /* money actions (log a payment) get their own amber */
.btn.rollbtn { background:linear-gradient(90deg,#F59E0B,#FBBF24); color:#1a1200; }        /* rollover toggle: amber so it stands out */
.btn.rollbtn.on { background:linear-gradient(90deg,#34D399,#2DD4BF); color:#06111c; }      /* green when rollover is active */
.btn:active { transform:scale(.985); }
.ins-teaser { margin-top:12px; padding:12px 14px; border-radius:12px; background:rgba(56,189,248,.08); border:1px solid rgba(56,189,248,.16); font-size:13.5px; line-height:1.5; }
/* goal-achieved celebration */
.celebrate-bg { position:fixed; inset:0; z-index:600; display:flex; align-items:center; justify-content:center; padding:24px;
  background:rgba(4,7,15,.55); animation:viewIn .2s ease; overflow:hidden; }
.confetti-wrap { position:absolute; inset:0; pointer-events:none; overflow:hidden; }
.confetti { position:absolute; top:-12px; width:9px; height:14px; border-radius:2px; opacity:.95;
  animation:confettiFall linear forwards; }
@keyframes confettiFall { 0%{ transform:translateY(-10px) rotate(0deg); opacity:1; } 100%{ transform:translateY(105vh) rotate(720deg); opacity:.9; } }
.celebrate-card { position:relative; text-align:center; background:#0E1730; border:1px solid var(--line); border-radius:20px;
  padding:26px 24px; max-width:340px; box-shadow:0 20px 60px rgba(0,0,0,.6); animation:tourpop .32s cubic-bezier(.22,.8,.3,1); }
.celebrate-emoji { font-size:52px; line-height:1; animation:celebrateBounce .6s ease; }
@keyframes celebrateBounce { 0%{ transform:scale(0); } 55%{ transform:scale(1.25); } 100%{ transform:scale(1); } }
.celebrate-t { font-size:20px; font-weight:800; margin:12px 0 4px; }
.celebrate-s { font-size:15px; color:var(--mid); margin-bottom:16px; }
body.light .celebrate-card { background:#fff; }
@media (prefers-reduced-motion: reduce) { .confetti { display:none; } .celebrate-emoji, .celebrate-card { animation:none; } }

/* success popup — a polished confirmation for meaningful adds (calmer than the confetti party) */
.spop-bg { position:fixed; inset:0; z-index:640; display:flex; align-items:center; justify-content:center; padding:24px;
  background:rgba(4,7,15,.6); backdrop-filter:blur(3px); animation:viewIn .18s ease; }
.spop-bg.out { animation:spopOut .2s ease forwards; }
@keyframes spopOut { to { opacity:0; } }
.spop-card { text-align:center; background:#0E1730; border:1px solid var(--line); border-radius:22px;
  padding:26px 26px 22px; max-width:320px; width:100%; box-shadow:0 20px 60px rgba(0,0,0,.6);
  animation:spopRise .34s cubic-bezier(.22,.9,.3,1); }
body.light .spop-card { background:#fff; }
@keyframes spopRise { from { opacity:0; transform:translateY(18px) scale(.96); } to { opacity:1; transform:none; } }
.spop-badge { position:relative; width:84px; height:84px; margin:2px auto 6px; }
.spop-ring { position:absolute; inset:0; width:84px; height:84px; transform:rotate(-90deg); }
.spop-ring circle { fill:rgba(45,212,191,.08); stroke:var(--teal); stroke-width:4; stroke-linecap:round;
  stroke-dasharray:182; stroke-dashoffset:182; animation:spopDraw .6s .06s cubic-bezier(.6,0,.3,1) forwards; }
@keyframes spopDraw { to { stroke-dashoffset:0; } }
.spop-emoji { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:40px; line-height:1;
  animation:spopPop .5s .26s cubic-bezier(.22,.9,.3,1) both; }
@keyframes spopPop { 0%{ transform:scale(0); } 60%{ transform:scale(1.22); } 100%{ transform:scale(1); } }
.spop-t { font-size:19px; font-weight:800; margin:10px 0 4px; }
.spop-s { font-size:13.5px; color:var(--mid); margin-bottom:16px; line-height:1.4; overflow-wrap:anywhere; }
.spop-card .btn { width:100%; }
@media (prefers-reduced-motion: reduce) { .spop-ring circle { animation:none; stroke-dashoffset:0; } .spop-emoji, .spop-card { animation:none; } }
.confirm-card { margin-top:12px; padding:14px; border-radius:13px; background:rgba(45,212,191,.08); border:1px solid rgba(45,212,191,.28); animation:tourpop .2s ease; }
.confirm-h { font-weight:800; font-size:13.5px; margin-bottom:6px; }
.confirm-b { font-size:13.5px; line-height:1.5; margin-bottom:12px; }
.prolock { margin-top:12px; padding:16px; border-radius:14px; text-align:center;
  background:linear-gradient(180deg, rgba(245,158,11,.10), rgba(245,158,11,.04)); border:1px solid rgba(245,158,11,.28); cursor:pointer; }
.prolock-h { font-weight:800; font-size:15px; margin-bottom:6px; }
.prolock-b { font-size:12.5px; color:var(--mid); line-height:1.55; margin-bottom:12px; }
.cardbadge { display:inline-block; vertical-align:middle; font-size:9px; font-weight:800; letter-spacing:.4px;
  color:#fff; padding:2px 6px; border-radius:5px; line-height:1; box-shadow:0 1px 3px rgba(0,0,0,.35);
  font-family:'Segoe UI',system-ui,sans-serif; text-shadow:0 1px 1px rgba(0,0,0,.25); }
/* wrap is a safety net, not a layout: rows that already fit are untouched, but a row that CAN'T fit
   now drops a button to a second line instead of pushing it out through the side of the card. The
   Split group's 4-button row did exactly that on a 360dp phone — ✨ Insights hung over the edge. */
.btnrow { display:flex; gap:10px; flex-wrap:wrap; }
.btnrow .btn { flex:1; }
/* Four actions don't fit one phone row, and wrapping left the last one stranded on a line of its
   own — small, unlabelled and looking like an afterthought. Two equal columns instead: every tile
   the same size, every tile named. The container carries the top margin so the grid gap alone
   spaces the rows. */
.btnrow.grid2 { display:grid; grid-template-columns:repeat(2,1fr); gap:10px; margin-top:14px; }
.btnrow.grid2 .btn { flex:none; width:100%; margin-top:0; }
/* a bare ✕ next to pill buttons looked stray — give it a matching bordered-button body */
.btnrow .del { flex:0 0 auto; align-self:stretch; display:flex; align-items:center; justify-content:center;
  min-width:38px; border:1px solid var(--line); border-radius:12px; background:var(--bg2); padding:0 10px; }
.chip { display:inline-block; background:rgba(45,212,191,.12); color:var(--teal);
  border:1px solid rgba(45,212,191,.25); font-size:11.5px; border-radius:999px; padding:3px 10px; }
.chip.red { background:rgba(248,113,113,.10); color:var(--red); border-color:rgba(248,113,113,.25); }
.chip.gold { background:rgba(245,158,11,.10); color:var(--gold); border-color:rgba(245,158,11,.3); }

.del { background:none; border:none; color:#44557a; font-size:15px; cursor:pointer; padding:6px; }

/* segmented control */
.seg { display:flex; background:var(--bg2); border:1px solid var(--line); border-radius:12px; overflow:hidden; margin-top:12px; }
.seg button { flex:1; min-width:0; background:none; border:none; color:var(--dim); padding:9px; font-size:13.5px; cursor:pointer; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.seg button.on { background:rgba(45,212,191,.14); color:var(--teal); font-weight:700; }

/* charts */
canvas.chart { width:100%; height:130px; margin-top:8px; }

/* debt countdown + payoff progress ring */
.countdown { font-size:32px; font-weight:800; line-height:1.1; margin-top:2px; text-wrap:balance; text-align:center;
  background:var(--grad-text); -webkit-background-clip:text; background-clip:text; color:transparent; }
.ringwrap { position:relative; width:176px; height:176px; margin:10px auto 0; }
.ring { width:100%; height:100%; }
.ring-c { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; pointer-events:none; }
.ring-pct { font-size:30px; font-weight:800; font-variant-numeric:tabular-nums; }
.ring-lb { font-size:12.5px; color:var(--dim); margin-top:2px; }
.ring-legend { display:flex; gap:20px; justify-content:center; margin-top:14px; font-size:13.5px; color:var(--mid); font-variant-numeric:tabular-nums; }
.ring-legend i { display:inline-block; width:11px; height:11px; border-radius:3px; margin-right:7px; vertical-align:middle; }
.ring-legend .dot-g { background:#34D399; } .ring-legend .dot-r { background:#F87171; }

/* debt pro: smart move + utilization */
.smart-card { border-color:rgba(45,212,191,.4); background:linear-gradient(160deg,rgba(45,212,191,.08),transparent); }
.smart-line { font-size:13.5px; line-height:1.55; color:var(--text); margin-top:2px; }
.smart-save { font-size:22px; font-weight:800; color:#34D399; margin-top:8px; }
.xfer-list { display:flex; flex-direction:column; gap:8px; margin:10px 0 4px; }
.xfer-row { display:flex; align-items:center; gap:11px; padding:9px 11px; border:1px solid rgba(45,212,191,.22); border-radius:12px; background:rgba(45,212,191,.05); }
.xfer-rank { width:24px; height:24px; flex-shrink:0; border-radius:50%; background:var(--teal); color:#04201d; font-weight:800; font-size:13.5px; display:flex; align-items:center; justify-content:center; }
.xfer-body { flex:1; min-width:0; }
.xfer-line { font-size:13.5px; color:var(--text); line-height:1.4; }
.xfer-sub { font-size:12.5px; color:var(--dim); margin-top:2px; font-variant-numeric:tabular-nums; }
.imp-list { max-height:44vh; overflow-y:auto; margin:4px -4px 12px; border:1px solid var(--line); border-radius:12px; }
.imp-row { display:flex; align-items:center; gap:10px; padding:9px 11px; border-bottom:1px solid rgba(30,42,68,.5); cursor:pointer; }
.imp-row:last-child { border-bottom:none; }
.imp-ck { width:18px; height:18px; flex-shrink:0; accent-color:var(--teal); }
.imp-main { flex:1; min-width:0; }
.imp-desc { font-size:13.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.imp-meta { font-size:11.5px; color:var(--dim); font-variant-numeric:tabular-nums; }
.imp-cat { flex-shrink:0; max-width:110px; background:var(--bg2); border:1px solid var(--line); color:var(--text); border-radius:8px; padding:5px 6px; font-size:12.5px; }
.imp-amt { flex-shrink:0; font-size:13.5px; font-weight:700; font-variant-numeric:tabular-nums; min-width:56px; text-align:right; }
.recur-acts { display:flex; align-items:center; gap:6px; }
.recur-acts .track-recur { padding:6px 12px; margin:0; }
.merch-row { padding:9px 0; border-bottom:1px solid rgba(30,42,68,.5); }
.merch-row:last-child { border-bottom:none; }
.merch-top { display:flex; justify-content:space-between; align-items:baseline; gap:10px; }
.merch-name { font-size:13.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.merch-amt { font-size:13.5px; font-weight:800; font-variant-numeric:tabular-nums; flex-shrink:0; }
.merch-bar { height:7px; border-radius:5px; background:var(--bg2); margin:6px 0 4px; overflow:hidden; }
.merch-fill { height:100%; border-radius:5px; background:linear-gradient(90deg,var(--teal),#7c9cff); }
.merch-sub { font-size:11.5px; color:var(--dim); font-variant-numeric:tabular-nums; }
.fc-cell.heat { background:rgba(120,140,175,.08); color:var(--dim); font-weight:600; }
.fc-cell.heat.h1 { background:rgba(45,212,191,.20); color:var(--text); }
.fc-cell.heat.h2 { background:rgba(45,212,191,.38); color:var(--text); }
.fc-cell.heat.h3 { background:rgba(45,212,191,.60); color:#04201d; font-weight:800; }
.fc-cell.heat.h4 { background:rgba(45,212,191,.90); color:#04201d; font-weight:800; }
.fc-cell.heat.today { box-shadow:0 0 0 2px var(--teal) inset; }
.heat-legend { display:flex; align-items:center; gap:5px; justify-content:flex-end; margin:10px 0 2px; font-size:11.5px; color:var(--dim); }
.heat-legend .hl { width:14px; height:14px; border-radius:4px; }
.heat-legend .hl.h1 { background:rgba(45,212,191,.20); } .heat-legend .hl.h2 { background:rgba(45,212,191,.38); }
.heat-legend .hl.h3 { background:rgba(45,212,191,.60); } .heat-legend .hl.h4 { background:rgba(45,212,191,.90); }
.mc-head { display:flex; flex-direction:column; gap:2px; margin-bottom:10px; }
.mc-big { font-size:30px; font-weight:800; line-height:1.1; font-variant-numeric:tabular-nums; }
.mc-big.bad { color:#F87171; } .mc-big.good { color:#34D399; }
.mc-sub { font-size:13.5px; color:var(--dim); }
.mc-row { display:flex; align-items:baseline; gap:10px; padding:8px 0; border-bottom:1px solid rgba(30,42,68,.5); }
.mc-row:last-of-type { border-bottom:none; }
.mc-name { flex:1; min-width:0; font-size:13.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.mc-delta { font-size:13.5px; font-weight:700; font-variant-numeric:tabular-nums; }
.mc-delta.bad { color:#F87171; } .mc-delta.good { color:#34D399; }
.mc-vals { font-size:11.5px; color:var(--dim); font-variant-numeric:tabular-nums; min-width:88px; text-align:right; }
.sn-big { font-size:34px; font-weight:800; line-height:1.1; margin-top:2px; font-variant-numeric:tabular-nums; }
.sn-unit { font-size:15px; font-weight:600; color:var(--dim); }
.sn-track { position:relative; height:10px; border-radius:6px; background:var(--bg2); margin:12px 0 5px; overflow:hidden; }
.sn-fill { height:100%; border-radius:6px; background:var(--teal); transition:width .4s; }
.sn-fill.start { background:#F87171; } .sn-fill.building { background:var(--gold); }
.sn-fill.solid { background:#34D399; } .sn-fill.done { background:var(--teal); }
.sn-mark { position:absolute; top:-2px; left:50%; width:2px; height:14px; background:rgba(255,255,255,.35); }
.sn-scale { display:flex; justify-content:space-between; font-size:11.5px; color:var(--dim); font-variant-numeric:tabular-nums; }
.cf-row { display:flex; align-items:center; gap:10px; padding:6px 0; }
.cf-mo { width:34px; flex-shrink:0; font-size:12.5px; font-weight:700; color:var(--dim); text-transform:uppercase; }
.cf-track { flex:1; height:12px; border-radius:6px; background:var(--bg2); overflow:hidden; }
.cf-fill { height:100%; border-radius:6px; }
.cf-fill.pos { background:#34D399; } .cf-fill.neg { background:#F87171; }
.cf-net { flex-shrink:0; min-width:74px; text-align:right; font-size:13.5px; font-weight:700; font-variant-numeric:tabular-nums; }
.grow-top { display:flex; justify-content:space-between; align-items:baseline; gap:10px; margin-bottom:8px; }
.grow-name { font-size:15px; font-weight:700; cursor:pointer; }
.grow-pct { font-size:15px; font-weight:800; color:var(--dim); font-variant-numeric:tabular-nums; }
.grow-pct.good { color:#34D399; }
.grow-detail { font-size:12.5px; color:var(--dim); margin-top:6px; font-variant-numeric:tabular-nums; }
.grow-actions { display:flex; align-items:center; gap:8px 10px; margin-top:10px; flex-wrap:wrap; }
.grow-with { flex:0 1 auto; min-width:0; max-width:55%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:11.5px; font-weight:700; color:var(--dim); background:var(--chip,rgba(255,255,255,.05)); border:1px solid var(--line,rgba(255,255,255,.08)); padding:4px 11px; border-radius:999px; }
.grow-share { flex:1 1 130px; min-width:0; }
.ch-stat { font-size:13.5px; color:var(--dim); font-variant-numeric:tabular-nums; }
.ch-stat b { color:var(--text); }
.ch-grid { display:grid; grid-template-columns:repeat(8,1fr); gap:6px; margin-top:12px; }
.ch-wk { aspect-ratio:1/1; min-width:0; padding:0; border-radius:9px; border:1px solid var(--line,rgba(255,255,255,.1)); background:var(--chip,rgba(255,255,255,.04)); color:var(--dim); font-size:11.5px; font-weight:700; font-variant-numeric:tabular-nums; cursor:pointer; transition:transform .08s ease; }
.ch-wk:active { transform:scale(.9); }
.ch-wk.on { background:linear-gradient(135deg,#2DD4BF,#38BDF8); border-color:transparent; color:#04121a; }
.tlh-figs { display:flex; gap:10px; margin-bottom:12px; }
.tlh-fig { flex:1 1 0; min-width:0; background:var(--chip,rgba(255,255,255,.04)); border:1px solid var(--line,rgba(255,255,255,.07)); border-radius:12px; padding:10px 12px; }
.tlh-fig .k { font-size:11.5px; color:var(--dim); margin-bottom:3px; white-space:normal; overflow-wrap:anywhere; line-height:1.25; }
.tlh-fig .v { font-size:20px; font-weight:800; font-variant-numeric:tabular-nums; }
.tlh-list { display:flex; flex-direction:column; gap:8px; }
.tlh-lot { display:flex; justify-content:space-between; align-items:center; gap:10px; padding:9px 11px; background:var(--chip,rgba(255,255,255,.03)); border:1px solid var(--line,rgba(255,255,255,.06)); border-radius:10px; }
.tlh-lot .l { min-width:0; }
.tlh-tk { font-size:13.5px; font-weight:700; }
.tlh-lot .d { font-size:12.5px; color:var(--dim); margin-top:2px; font-variant-numeric:tabular-nums; }
.tlh-lot .r { font-size:15px; font-weight:800; flex:0 0 auto; font-variant-numeric:tabular-nums; }
.tlh-term { font-size:10.5px; font-weight:800; text-transform:uppercase; letter-spacing:.4px; padding:2px 7px; border-radius:999px; vertical-align:middle; }
.tlh-term.short { color:#FBBF24; background:rgba(251,191,36,.14); }
.tlh-term.long { color:#60A5FA; background:rgba(96,165,250,.14); }
.tlh-note { font-size:11.5px; line-height:1.5; color:var(--dim); margin:12px 0 10px; }
.rb-list { display:flex; flex-direction:column; gap:13px; }
.rb-head { display:flex; justify-content:space-between; align-items:baseline; gap:10px; margin-bottom:6px; }
.rb-cls { font-size:13.5px; font-weight:700; }
.rb-act { font-size:13.5px; font-weight:800; font-variant-numeric:tabular-nums; }
.rb-act.good { color:#34D399; } .rb-act.bad { color:#F87171; }
.rb-bar { position:relative; height:8px; border-radius:999px; background:var(--track,rgba(255,255,255,.08)); overflow:visible; }
.rb-fill { height:100%; border-radius:999px; background:linear-gradient(90deg,#22D3EE,#6366F1); }
.rb-target { position:absolute; top:-3px; width:2px; height:14px; background:var(--text,#fff); border-radius:2px; transform:translateX(-1px); opacity:.85; }
.rb-nums { font-size:11.5px; color:var(--dim); margin-top:5px; font-variant-numeric:tabular-nums; }
.ach-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:14px 0; }   /* breathing room after the yellow streak card (user: "make a gap") */
/* badge tiles: icon on top, centred text — uniform, roomy, nothing squeezes (user report) */
.ach { position:relative; min-width:0; display:flex; flex-direction:column; align-items:center; text-align:center; gap:4px;
  padding:10px 8px 9px; border:1px solid var(--line); border-radius:12px; background:var(--card); opacity:.55; }
.ach.on { opacity:1; border-color:rgba(45,212,191,.4); background:linear-gradient(160deg,rgba(45,212,191,.09),transparent); }
.ach-ic { font-size:24px; flex-shrink:0; filter:grayscale(1); line-height:1; }
.ach.on .ach-ic { filter:none; }
.ach-body { min-width:0; width:100%; }
.ach-n { font-size:13.5px; font-weight:700; overflow-wrap:anywhere; line-height:1.25; }
.ach-d { font-size:11.5px; color:var(--dim); line-height:1.35; margin-top:3px; overflow-wrap:anywhere; }
.ach-ck { position:absolute; top:8px; right:9px; font-size:12.5px; }
.ach.on .ach-ck { color:#34D399; font-weight:800; }
.streak-card { border-color:rgba(245,158,11,.35); background:linear-gradient(160deg,rgba(245,158,11,.07),transparent); }
.reorder-list { display:flex; flex-direction:column; gap:8px; }
.reorder-row { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:12px 14px; border:1px solid var(--line); border-radius:14px; background:var(--card); }
.ro-name { font-size:15px; font-weight:700; }
.ro-btns { display:flex; gap:8px; flex-shrink:0; }
.ro-btn { width:40px; height:40px; border-radius:10px; border:1px solid var(--line); background:var(--bg2); color:var(--text); font-size:15px; cursor:pointer; }
.ro-btn:disabled { opacity:.3; cursor:default; }
.ro-btn:not(:disabled):active { background:var(--teal); color:#04201d; }
.wd-chart { display:flex; align-items:flex-end; gap:8px; height:104px; margin:12px 0 2px; }
.wd-col { flex:1; display:flex; flex-direction:column; align-items:center; gap:6px; height:100%; }
.wd-bar { flex:1; width:70%; display:flex; align-items:flex-end; background:var(--bg2); border-radius:6px; overflow:hidden; }
.wd-fill { width:100%; border-radius:6px; background:rgba(45,212,191,.35); transition:height .3s; }
.wd-fill.top { background:var(--teal); }
.wd-lbl { font-size:11.5px; font-weight:700; color:var(--dim); text-transform:uppercase; }
.wd-lbl.top { color:var(--teal); }
.backup-card { border-color:rgba(245,158,11,.35); background:linear-gradient(160deg,rgba(245,158,11,.06),transparent); }
.cat-sugg { display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-size:12.5px; color:var(--dim); margin-top:6px; }
.cat-sugg .btn { padding:4px 12px; margin:0; }
.cat-warn { font-size:12.5px; color:#F59E0B; margin-top:6px; line-height:1.45; }
.save-nudge { border-color:rgba(52,211,153,.32); background:linear-gradient(160deg,rgba(52,211,153,.07),transparent); }
.setup-list { display:flex; flex-direction:column; gap:2px; margin-top:6px; }
.setup-row { display:flex; align-items:center; gap:11px; padding:10px 4px; border-bottom:1px solid rgba(30,42,68,.5); }
.setup-row:last-child { border-bottom:none; }
.setup-row[data-go] { cursor:pointer; }
.setup-ck { width:22px; height:22px; flex-shrink:0; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:12.5px; font-weight:800; background:var(--bg2); color:var(--dim); }
.setup-row.done .setup-ck { background:var(--teal); color:#04201d; }
.setup-lbl { flex:1; font-size:13.5px; font-weight:600; }
.setup-row.done .setup-lbl { color:var(--dim); text-decoration:line-through; }
.setup-row .ar { color:var(--dim); }
.util-overall { display:flex; align-items:center; justify-content:space-between; margin-top:10px;
  padding:12px 14px; border-radius:12px; background:var(--bg2); border:1px solid var(--line); font-size:13.5px; }
.util-overall b { font-size:22px; font-variant-numeric:tabular-nums; }
.util-overall.good b { color:#34D399; } .util-overall.gold b { color:var(--gold); } .util-overall.bad b { color:var(--red); }
.utilrow { margin-top:12px; }
.utilrow .ut-top { display:flex; justify-content:space-between; font-size:13.5px; margin-bottom:6px; }
.utilrow .ut-top .amt { font-variant-numeric:tabular-nums; color:var(--mid); }

/* debt coach + goal + what-if */
.impact-line { margin-top:12px; padding:11px 13px; border-radius:12px; font-size:13.5px; line-height:1.5;
  background:rgba(52,211,153,.10); border:1px solid rgba(52,211,153,.3); color:var(--text); }
.impact-line.pro-tease { cursor:pointer; background:rgba(245,158,11,.10); border-color:rgba(245,158,11,.35); }
.impact-line.pro-tease:active { transform:scale(.99); }
.coach-card { border-color:rgba(139,140,248,.4); background:linear-gradient(160deg,rgba(139,140,248,.10),transparent); }
.coach-main { font-size:15px; font-weight:700; line-height:1.5; margin-top:2px; }
.coach-sub { font-size:13.5px; color:var(--mid); line-height:1.5; margin-top:8px; }
.goal-req { font-size:24px; font-weight:800; margin-top:8px; }
.goal-req b { background:var(--grad-text); -webkit-background-clip:text; background-clip:text; color:transparent; }
.whatif-out { margin-top:14px; }
.wi-row { display:flex; justify-content:space-between; font-size:13.5px; padding:9px 0; border-bottom:1px solid var(--line); color:var(--mid); }
.wi-row b { color:var(--text); }
.wi-save { margin-top:12px; font-size:18px; font-weight:800; color:#34D399; text-align:center; }
.fc-bal { font-size:11.5px; color:var(--dim); font-variant-numeric:tabular-nums; }
.fc-bal.bad { color:var(--red); font-weight:700; }
/* split participants */
.sp-part { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:9px 2px; border-bottom:1px solid rgba(30,42,68,.5); font-size:13.5px; }
.sp-part:last-child { border-bottom:0; }
.sp-part input[type=number] { width:110px; background:var(--bg2); border:1px solid var(--line); color:var(--text); border-radius:9px; padding:8px 10px; font-size:15px; text-align:right; }
.sp-part input[type=number]:focus { outline:none; border-color:var(--teal); }
.sp-part input[type=checkbox] { width:20px; height:20px; accent-color:var(--teal); }

/* custom in-app calendar picker (replaces the native date UI) */
input[type="date"] { cursor:pointer; }
input[type="date"]::-webkit-calendar-picker-indicator { opacity:0; display:none; }
.calpop-bg { position:fixed; inset:0; z-index:500; background:rgba(4,7,15,.62); display:flex; align-items:center; justify-content:center; padding:20px; }
.calpop { width:100%; max-width:340px; background:#0E1730; border:1px solid var(--line); border-radius:20px; padding:16px 16px 14px; animation:up .18s ease-out; box-shadow:0 20px 60px rgba(4,7,15,.5); }
body.light .calpop { background:#FFFFFF; }
/* 2.0 is a dark design, full stop. With the LIGHT theme saved, body.light turned this popup
   white while the 2.0 page behind it stayed dark — a white card of pale-grey numbers you could
   not read (his screenshot). The 2.0 rules come after, and are scoped to body.v2, so they win
   over body.light without touching 1.0's light theme at all. */
body.v2 .calpop { background:#171824; border-color:rgba(255,255,255,.08); color:#f1f1f6; }
body.v2 .cal-d { color:#f1f1f6; }
body.v2 .cal-dow span, body.v2 .cal-w { color:#9294a8; }
body.v2 .cal-title, body.v2 .cal-nav { color:#f1f1f6; }
body.v2 .cal-d.other, body.v2 .cal-d:disabled { color:var(--quiet); }
body.v2 .cal-foot .btn { color:var(--g2); }
.cal-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
.cal-title { font-weight:800; font-size:15px; }
.cal-nav { background:var(--card); border:1px solid var(--line); color:var(--mid); width:38px; height:38px; border-radius:11px; font-size:18px; cursor:pointer; }
.cal-nav:active { transform:scale(.92); }
.cal-dow { display:grid; grid-template-columns:repeat(7,1fr); gap:2px; margin-bottom:4px; }
.cal-dow span { text-align:center; font-size:11.5px; color:var(--dim); font-weight:700; }
.cal-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:3px; }
.cal-d { height:38px; background:none; border:none; color:var(--text); border-radius:10px; font-size:13.5px; cursor:pointer; font-variant-numeric:tabular-nums; }
.cal-d:active { transform:scale(.9); }
.cal-d.today { color:var(--teal); font-weight:800; box-shadow:inset 0 0 0 1px rgba(45,212,191,.5); }
.cal-d.sel { background:var(--grad); color:#06111c; font-weight:800; }
.cal-foot { display:flex; gap:8px; margin-top:12px; }
.cal-foot .btn { flex:1; margin-top:0; }
/* tap the title to jump months/years (fast for far-back dates like DOB) */
.cal-tbtn { background:none; border:none; color:var(--text); font-weight:800; font-size:15px; cursor:pointer; padding:6px 12px; border-radius:9px; }
.cal-tbtn:active { background:var(--card); }
.cal-mgrid { display:grid; grid-template-columns:repeat(3,1fr); gap:6px; }
.cal-m { padding:15px 0; background:var(--card); border:1px solid var(--line); color:var(--text); border-radius:11px; font-size:13.5px; cursor:pointer; font-variant-numeric:tabular-nums; }
.cal-m.sel { background:var(--grad); color:#06111c; font-weight:800; border-color:transparent; }
.cal-m.out { color:var(--dim); background:transparent; border-color:transparent; }
.cal-m:active { transform:scale(.94); }

/* bill calendar (month-at-a-glance grid inside Budget) */
.bcal-dow { display:grid; grid-template-columns:repeat(7,1fr); gap:3px; margin:4px 0; }
.bcal-dow span { text-align:center; font-size:10.5px; color:var(--dim); font-weight:700; }
.bcal-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:3px; }
.bcal-d { position:relative; aspect-ratio:1/1; display:flex; flex-direction:column; align-items:center; justify-content:center;
  background:var(--bg2); border-radius:9px; font-size:12.5px; color:var(--mid); font-variant-numeric:tabular-nums; }
.bcal-d.has { color:var(--text); font-weight:700; }
.bcal-d.today { box-shadow:inset 0 0 0 1.5px var(--teal); color:var(--teal); }
.bcal-e { aspect-ratio:1/1; }
.bcal-d .bnum { line-height:1; }
.bcal-d .bdots { display:flex; gap:2px; margin-top:3px; min-height:5px; }
.bdot { width:5px; height:5px; border-radius:50%; display:inline-block; vertical-align:middle; }
.bdot.in { background:var(--teal); }
.bdot.out { background:#F87171; }

/* family sharing */
.famtop { display:flex; align-items:center; justify-content:space-between; gap:10px; }
.famtop > div:first-child { min-width:0; }   /* let a long email shrink/truncate instead of pushing the button off-screen */
.famtop h3 { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.famtop .dim { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.famtop .btn { margin-top:0; flex:0 0 auto; width:auto; }   /* base .btn is width:100% — override so it doesn't balloon */
.invite { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; background:var(--bg2);
  border:1px solid var(--line); border-radius:12px; padding:12px 14px; margin-top:4px; }
.invite > div:first-child { min-width:0; }
.invite .code { font-size:22px; font-weight:800; letter-spacing:3px; font-variant-numeric:tabular-nums; color:var(--teal); overflow-wrap:anywhere; }
.invite .btn { margin-top:0; flex:0 0 auto; width:auto; }
/* two-code invite block: a 👥 member code + a 🧒 kid code */
.invite2 { display:flex; flex-direction:column; gap:8px; margin-top:4px; }
.ic2 { display:flex; align-items:center; justify-content:space-between; gap:12px; background:var(--bg2); border:1px solid var(--line); border-radius:12px; padding:10px 14px; }
.ic2 > div:first-child { min-width:0; }
.ic2-t { font-size:11.5px; text-transform:uppercase; letter-spacing:.8px; color:var(--dim); font-weight:700; }
.ic2 .code { font-size:20px; font-weight:800; letter-spacing:3px; font-variant-numeric:tabular-nums; color:var(--teal); overflow-wrap:anywhere; }
.ic2-d { font-size:11.5px; color:var(--dim); margin-top:2px; }
.ic2 .btn { margin-top:0; flex:0 0 auto; width:auto; }
.ic2.ic2-off { opacity:.6; } .ic2.ic2-off .code { color:var(--dim); font-size:13.5px; letter-spacing:0; }

/* transaction tags */
.tagline { display:flex; flex-wrap:wrap; gap:5px; margin-top:4px; }
.ttag { font-size:10.5px; font-weight:700; color:var(--teal); background:rgba(45,212,191,.10);
  border:1px solid rgba(45,212,191,.22); border-radius:999px; padding:2px 8px; }

/* custom in-app dropdown (themed replacement for the native <select> popup) */
.selpop-bg { position:fixed; inset:0; z-index:500; background:rgba(4,7,15,.62); display:flex; align-items:flex-end; justify-content:center; padding:0; }
.selpop { width:100%; max-width:480px; max-height:66vh; display:flex; flex-direction:column;
  background:#0E1730; border:1px solid var(--line); border-bottom:none; border-radius:20px 20px 0 0;
  padding:8px 8px 14px; animation:up .18s ease-out; box-shadow:0 -12px 50px rgba(4,7,15,.5); }
body.light .selpop { background:#FFFFFF; }
/* the sheet itself was #0E1730 - a 1.0 navy that reads blue against every 2.0 surface around it.
   His design has one sheet colour and it already has a token. */
body.v2 .selpop { background:var(--sheet); }
/* "if you see notification appear st top / its in diffetn colur usign V1" - the toast was #12203c,
   a 1.0 navy, with a teal action link and a teal "good" border. Same fault as the picker: three
   1.0 colours baked in where the accent should reach. His design has no toast, so it takes the
   elevated card surface every other floating thing uses (--s2 in his file), and the coloured parts
   follow the accent. 1.0 keeps the navy - this is scoped to body.v2. */
body.v2 #toast { background:var(--card); border-color:var(--line); color:var(--text); }
body.v2 #toast .toast-act { color:var(--teal); }
body.v2 #toast.good { border-color:rgba(var(--teal-rgb,45,212,191),.5); }
/* "make this smaller to much big consuming lot of screen" - measured before touching it: each
   repeat-expense tile was 165.8 x 138.4 and the whole card 476.7px, i.e. 52% of the screen for
   three shortcuts, before his text-size setting scales it further. The height came from stacking
   icon / name / amount / button in one column. The icon now sits BESIDE the name and spans it and
   the amount, so the tile keeps every piece of information and loses the dead vertical space.
   .rx-add has different children, so it keeps its own centred layout. */
body.v2 .rx-grid { gap:8px; margin:10px 0 2px; }
body.v2 .rx-chip:not(.rx-add) { display:grid; grid-template-columns:auto 1fr; column-gap:9px;
  grid-template-areas:"ic name" "ic amt" "act act"; align-content:start; padding:10px 11px 10px; border-radius:16px; }
body.v2 .rx-chip:not(.rx-add) .rx-ic { grid-area:ic; font-size:19px; align-self:center; }
body.v2 .rx-chip:not(.rx-add) .rx-name { grid-area:name; margin-top:0; font-size:12.5px; align-self:end; }
/* the ✕ is absolutely positioned top-right, and pulling the name up onto the icon's row put it
   straight underneath — "Tim Hortons" rendered as "Tim Horton✕". Padding the name clear of it just
   wrapped every name onto two lines and gave the height straight back, so the ✕ moves instead: the
   action row is left-aligned, so its right end is empty and costs nothing. */
body.v2 .rx-chip:not(.rx-add) .rx-del { top:auto; bottom:10px; right:10px; }
body.v2 .rx-chip:not(.rx-add) .rx-amt { grid-area:amt; font-size:12.5px; align-self:start; }
body.v2 .rx-chip:not(.rx-add) .rx-act { grid-area:act; margin-top:8px; justify-self:start;
  font-size:9.5px; padding:4px 9px; }
body.v2 .rx-add { padding:10px; }
/* the POST chip and the "posted" tile edge were still literal 1.0 teal — the same V1 colour he
   called out on the toast, sitting on a violet screen. Both follow the accent now. */
body.v2 .rx-act { background:rgba(var(--teal-rgb,45,212,191),.12); border-color:rgba(var(--teal-rgb,45,212,191),.3); }
body.v2 .rx-chip.posted { border-color:rgba(var(--teal-rgb,45,212,191),.32); background:rgba(var(--teal-rgb,45,212,191),.06); }
.sel-title { font-size:12.5px; text-transform:uppercase; letter-spacing:1.2px; color:var(--dim); font-weight:700; padding:12px 12px 8px; }
.sel-list { overflow-y:auto; -webkit-overflow-scrolling:touch; }
.sel-opt { display:block; width:100%; text-align:left; background:none; border:none; color:var(--text);
  font-size:15px; padding:14px 14px; border-radius:12px; cursor:pointer; }
.sel-opt:active { transform:scale(.99); }
/* Goal jars — every savings goal is a glass jar that fills with liquid (user: "go which is best") */
.gjar-row { display:flex; gap:16px; align-items:stretch; }
.gjar-body { flex:1; min-width:0; }
/* .cb-top was only ever laid out inside .catbar, so in a jar the name and the amount ran together
   — "Japan trip$3,000". Applies to the household jars too; they had the same latent flaw. */
.gjar-body .cb-top { display:flex; justify-content:space-between; align-items:baseline; gap:10px; font-size:13.5px; font-weight:700; }
.gjar-body .cb-top .amt { color:var(--mid); font-weight:700; font-variant-numeric:tabular-nums; white-space:nowrap; }
.gjar { position:relative; flex:0 0 86px; height:118px; align-self:flex-start; border:2px solid var(--line2); border-top:0;
  border-radius:12px 12px 22px 22px; overflow:hidden; background:rgba(255,255,255,.035);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.08); }
.gjar-lid { position:absolute; top:0; left:-2px; right:-2px; height:9px; border-radius:6px;
  background:linear-gradient(180deg, rgba(255,255,255,.22), rgba(255,255,255,.08)); }
.gjar-shine { position:absolute; top:14px; left:9px; width:9px; height:56%; border-radius:99px;
  background:linear-gradient(180deg, rgba(255,255,255,.22), transparent); pointer-events:none; }
/* brand-teal LIQUID fill (user voice note: "liquid is the color of Muse Nexus — just do the liquid") */
.gjar-liq { position:absolute; left:0; right:0; bottom:0; height:calc(var(--p) * 1%);
  background:linear-gradient(180deg, rgba(45,212,191,.85), rgba(56,189,248,.92));
  animation:gjarRise 1s cubic-bezier(.22,1,.36,1); }
.gjar-liq::before { content:""; position:absolute; top:-5px; left:-12%; width:124%; height:10px; border-radius:50%;
  background:rgba(190,250,240,.55); animation:gjarWave 2.6s ease-in-out infinite; }
.gjar.full .gjar-liq { background:linear-gradient(180deg, rgba(94,234,212,.95), rgba(56,189,248,.95));
  filter:drop-shadow(0 0 9px rgba(45,212,191,.6)); }
.gjar-pct { position:absolute; left:0; right:0; bottom:8px; text-align:center; font-weight:900; font-size:15px;
  color:#eafffb; text-shadow:0 1px 6px rgba(3,18,26,.6); z-index:2; }
@keyframes gjarRise { from { height:0; } }
@keyframes gjarWave { 0%,100% { transform:translateX(-6px); } 50% { transform:translateX(6px); } }
/* "family svaing goal is stretched" — this narrow-phone rule was written for the BIG jar, but it
   also caught the 34x44 .sm jar in the Saving-together rows, and min-height beats height, so on a
   360px screen (his) the small jar was forced to 100px: a thin stretched tube. Excluding .sm keeps
   the big jar's narrow-screen sizing without touching the small one. */
@media (max-width:360px) { .gjar:not(.sm) { flex-basis:72px; min-height:100px; } }

/* Kids savings jars — a glass that visibly fills */
.kjar-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(140px, 1fr)); gap:12px; margin-bottom:8px; }
.kjar { text-align:center; }
.kjar-glass { position:relative; width:74px; height:88px; margin:0 auto; border:2px solid var(--line); border-top-width:6px;
  border-radius:10px 10px 18px 18px; overflow:hidden; background:rgba(255,255,255,.03); }
.kjar-fill { position:absolute; left:0; right:0; bottom:0; background:linear-gradient(180deg, rgba(var(--teal-rgb,45,212,191),.75), rgba(var(--g1-rgb,56,189,248),.85));
  transition:height .5s ease; }
.kjar-fill.full { background:linear-gradient(180deg, rgba(245,158,11,.85), rgba(245,158,11,.65)); }
/* Kids delight: jar "liquid" splash on deposit, a glowing +$ badge, and a confetti spark */
.kjar-fill.kjar-splash { animation:kjarSplash .75s cubic-bezier(.34,1.56,.64,1); }
@keyframes kjarSplash { 0%{ transform:scaleY(.55); transform-origin:bottom; } 55%{ transform:scaleY(1.1); } 78%{ transform:scaleY(.96); } 100%{ transform:scaleY(1); } }
.kidpop { position:fixed; transform:translate(-50%,-50%); z-index:9999; pointer-events:none; font-weight:900; font-size:13.5px;
  color:#052018; background:linear-gradient(135deg,#34D399,#2DD4BF); padding:6px 13px; border-radius:999px;
  box-shadow:0 0 20px rgba(52,211,153,.75), 0 4px 12px rgba(0,0,0,.35); animation:kidpop 1s ease-out forwards; white-space:nowrap; }
@keyframes kidpop { 0%{ opacity:0; transform:translate(-50%,-30%) scale(.5); } 22%{ opacity:1; transform:translate(-50%,-70%) scale(1.18); } 100%{ opacity:0; transform:translate(-50%,-165%) scale(1); } }
.kidburst { position:fixed; z-index:9998; pointer-events:none; }
.kidburst span { position:absolute; left:0; top:0; width:8px; height:8px; border-radius:2px; animation:kidburst .85s ease-out forwards; }
@keyframes kidburst { 0%{ opacity:1; transform:translate(0,0) scale(1) rotate(0); } 100%{ opacity:0; transform:translate(var(--dx),var(--dy)) scale(.3) rotate(140deg); } }
/* a gentle periodic nudge on a chore's Done button — still most of the time, then a quick wiggle to catch a kid's eye */
.kchore-do { animation:kchoreNudge 3s ease-in-out infinite; transform-origin:center; }
/* KID MODE type: chunky, glowing, a little bouncy — a kid's app, not a bank statement
   (user: "more customized animated kid type — bold") */
/* "kid sesction should notbavcess B" — the avatar opens the grown-up Profile, so a kid never sees
   it. !important because the v2 base rule further down ties on specificity and would win. */
body.kidmode .v2nav-av { display:none !important; }
/* Kid mode scales the section labels up, but it only ever named `h3`. Home writes the same
   labels as .bud-sl, which is 9px -- so "Yours to spend" was 9px on Home and 17px on every other
   kid page, the same words at half the size. His report: "Home screen text size not same as
   other pages". Both selectors now get the same treatment, which is the point of the rule. */
body.kidmode #view .card h3,
body.kidmode #view .bud-sl { font-size:17px; font-weight:900; letter-spacing:1.8px;
  text-shadow:0 0 16px rgba(45,212,191,.35); }
/* .bud-sl carries opacity .55 of its own, which at 17px reads as washed out rather than quiet. */
body.kidmode #view .bud-sl { opacity:.8; text-transform:uppercase; margin-bottom:6px; }
body.kidmode #view .hero-num { font-weight:900; letter-spacing:.5px;
  animation:kidPopIn .5s cubic-bezier(.2,1.5,.4,1); }
@keyframes kidPopIn { 0% { transform:scale(.82); opacity:0; } 60% { transform:scale(1.06); } 100% { transform:scale(1); opacity:1; } }
body.kidmode #view .btn { border-radius:16px; font-weight:800; }
body.kidmode #view .subtab { font-weight:800; }
body.kidmode #view .subtab.on { animation:kidPopIn .35s cubic-bezier(.2,1.5,.4,1); }
body.kidmode #view .kpi .v { font-weight:900; }
@media (prefers-reduced-motion:reduce) { body.kidmode #view .hero-num, body.kidmode #view .subtab.on { animation:none; } }
@keyframes kchoreNudge { 0%,86%,100% { transform:none; } 90% { transform:rotate(-5deg) scale(1.05); } 94% { transform:rotate(5deg) scale(1.05); } 98% { transform:rotate(-2deg); } }
@media (prefers-reduced-motion:reduce){ .kjar-fill.kjar-splash{ animation:none } .kidpop,.kidburst span{ animation-duration:.01s } .kchore-do{ animation:none } }
.kjar-pct { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:13.5px; font-weight:800; color:#fff; text-shadow:0 1px 4px rgba(0,0,0,.55); }
.kjar-n { font-size:13.5px; font-weight:700; margin-top:6px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.kjar-d { font-size:11.5px; color:var(--dim); margin-top:1px; }
/* Kids "Month at a glance" mini bar chart */
.km-chart { display:flex; gap:10px; align-items:flex-end; height:96px; margin-top:12px; }
.km-col { flex:1; display:flex; flex-direction:column; align-items:center; gap:5px; height:100%; }
.km-bars { flex:1; width:100%; display:flex; align-items:flex-end; justify-content:center; gap:3px; }
.km-bars .km-bar { width:22%; max-width:12px; border-radius:4px 4px 0 0; align-self:flex-end; }
.km-earn, .km-bars .km-earn { background:var(--teal); }
.km-spend, .km-bars .km-spend { background:var(--red); }
.km-jar, .km-bars .km-jar { background:var(--gold); }
.km-m { font-size:10.5px; font-weight:700; color:var(--dim); }
.km-key { display:flex; gap:16px; justify-content:center; margin-top:10px; font-size:11.5px; color:var(--dim); font-weight:600; }
.km-key span { display:inline-flex; align-items:center; gap:5px; }
.km-key i { width:10px; height:10px; border-radius:3px; display:inline-block; }
/* Kids money quiz: see your answer light up right (green) or wrong (red), then the correct one + why */
/* ☀️ cinematic "Today" hero — a time-of-day sky, a drifting sun/moon, and a staged reveal */
.today-hero { position:relative; overflow:hidden; border:none; }
.today-hero::before { content:""; position:absolute; inset:0; z-index:0; opacity:.9; }
.today-hero.th-morn::before  { background:linear-gradient(135deg,#F9A03F 0%,#F06B6B 40%,#7C5CE0 100%); }
.today-hero.th-aft::before   { background:linear-gradient(135deg,#2DD4BF 0%,#3B6FE0 55%,#7C5CE0 100%); }
.today-hero.th-eve::before   { background:linear-gradient(135deg,#F0729B 0%,#7C5CE0 55%,#2A3566 100%); }
.today-hero.th-night::before { background:linear-gradient(135deg,#1B2550 0%,#3B2E6E 55%,#0E1526 100%); }
.today-hero > *:not(.th-sky) { position:relative; z-index:1; }
/* ---- the sky: a real CSS-drawn sun/moon that RISES, with light rays, a warm bloom & a horizon glow ---- */
.th-sky { position:absolute; inset:0; z-index:0; overflow:hidden; border-radius:inherit; pointer-events:none; }
/* celestial body wrapper (rises on load); the disc + rays + bloom live inside so rise & spin don't fight */
.th-cel { position:absolute; top:20px; right:40px; width:56px; height:56px; animation:celRise 2.2s cubic-bezier(.16,.8,.3,1) backwards; }
.th-disc { position:absolute; inset:0; border-radius:50%;
  background:radial-gradient(circle at 50% 42%, #FFFEF4 0%, #FFE9A6 34%, #FFC64D 62%, #FF8F35 100%);
  box-shadow:0 0 28px 9px rgba(255,196,90,.6), 0 0 66px 24px rgba(255,140,55,.38); }
/* rotating light rays fanning out from the sun */
.th-cel::before { content:""; position:absolute; inset:-42px; border-radius:50%; z-index:-1;
  background:repeating-conic-gradient(from 0deg, rgba(255,236,170,.55) 0deg 3deg, transparent 3deg 17deg);
  -webkit-mask:radial-gradient(circle, transparent 34%, #000 42%, transparent 74%);
  mask:radial-gradient(circle, transparent 34%, #000 42%, transparent 74%);
  animation:celRays 70s linear infinite; opacity:.65; }
/* soft breathing bloom of warm light */
.th-cel::after { content:""; position:absolute; inset:-26px; border-radius:50%; z-index:-2;
  background:radial-gradient(circle, rgba(255,220,140,.45), transparent 62%); animation:celBloom 5.5s ease-in-out infinite; }
@keyframes celRise { from{ transform:translateY(70px) scale(.75); opacity:0; } 60%{ opacity:1; } to{ transform:translateY(0) scale(1); opacity:1; } }
@keyframes celRays { to{ transform:rotate(360deg); } }
@keyframes celBloom { 0%,100%{ opacity:.5; transform:scale(1); } 50%{ opacity:.9; transform:scale(1.16); } }
/* NIGHT: the sun becomes a glowing moon with a crescent shadow — no rays */
.th-night .th-disc { background:radial-gradient(circle at 40% 38%, #F6F4FF 0%, #D2D5F2 66%, #A7ABDD 100%);
  box-shadow:0 0 22px 6px rgba(200,205,255,.5), inset -9px -7px 0 -1px rgba(60,62,110,.28); }
.th-night .th-cel::before { display:none; }
.th-night .th-cel::after { background:radial-gradient(circle, rgba(200,205,255,.35), transparent 62%); }
/* drifting clouds (thin, layered — read as clouds, not blobs) */
.th-clouds span { position:absolute; height:12px; border-radius:12px; background:rgba(255,255,255,.55);
  box-shadow:16px -5px 0 -2px rgba(255,255,255,.45), 34px 2px 0 -4px rgba(255,255,255,.4); filter:blur(3px); }
.th-clouds span:nth-child(1){ width:46px; top:26%; animation:thCloud 30s linear infinite; }
.th-clouds span:nth-child(2){ width:32px; top:56%; animation:thCloud 44s linear infinite; animation-delay:-12s; opacity:.7; }
.th-clouds span:nth-child(3){ width:58px; top:80%; animation:thCloud 36s linear infinite; animation-delay:-22s; opacity:.55; }
@keyframes thCloud { from{ transform:translateX(-90px); } to{ transform:translateX(520px); } }
/* twinkling stars (night) */
.th-stars { position:absolute; inset:0; display:none; }
.th-stars i { position:absolute; width:2.5px; height:2.5px; border-radius:50%; background:#fff; box-shadow:0 0 6px rgba(255,255,255,.9);
  animation:thTwinkle 2.6s ease-in-out infinite; }
@keyframes thTwinkle { 0%,100%{ opacity:.15; transform:scale(.6); } 50%{ opacity:1; transform:scale(1.2); } }
/* warm dawn/dusk glow rising off the horizon at the bottom */
.th-horizon { position:absolute; left:0; right:0; bottom:0; height:55%; background:linear-gradient(to top, rgba(255,150,75,.4), rgba(255,150,75,.08) 55%, transparent); }
.th-aft .th-horizon { background:linear-gradient(to top, rgba(255,225,150,.28), transparent 60%); }
.th-eve .th-horizon { background:linear-gradient(to top, rgba(255,110,120,.42), transparent 60%); }
.th-night .th-horizon { background:linear-gradient(to top, rgba(120,110,190,.32), transparent 60%); }
/* which sky elements each time of day shows */
.today-hero.th-night .th-clouds { display:none; }
.today-hero.th-night .th-stars { display:block; }
/* ---- live weather: greeting row + weather chip, and condition-driven sky ---- */
.th-top { display:flex; align-items:flex-start; justify-content:space-between; gap:10px; }
/* dark-glass pill so the temperature/condition stays readable even over the bright sun (device: "temperature not visible") */
.th-wx { flex-shrink:0; font:inherit; font-size:12.5px; font-weight:800; color:#fff; cursor:pointer; white-space:nowrap;
  background:rgba(10,15,30,.5); border:1px solid rgba(255,255,255,.3); border-radius:999px; padding:5px 12px;
  -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px); box-shadow:0 2px 8px rgba(0,0,0,.28); }
.th-wx.ghost { background:rgba(10,15,30,.4); font-weight:700; }
.th-wx-ico { padding:0; width:32px; height:32px; font-size:15px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; }   /* icon-only weather toggle — doesn't cover the sun */
.th-wx:active { transform:scale(.95); }
/* clouds thin out when it's clear, thicken & grey over when it's cloudy/rainy/stormy */
.today-hero.wx-clear .th-clouds { opacity:.25; }
.today-hero.wx-cloudy .th-clouds span, .today-hero.wx-rain .th-clouds span, .today-hero.wx-storm .th-clouds span { background:rgba(230,232,240,.75); height:15px; filter:blur(4px); }
.today-hero.wx-cloudy .th-cel::before, .today-hero.wx-rain .th-cel::before, .today-hero.wx-storm .th-cel::before { opacity:.2; }
.today-hero.wx-rain::after, .today-hero.wx-storm::after, .today-hero.wx-cloudy::after { content:""; position:absolute; inset:0; z-index:0; background:rgba(40,48,66,.28); border-radius:inherit; pointer-events:none; }
/* falling rain */
.th-rain { position:absolute; inset:0; overflow:hidden; }
.th-rain i { position:absolute; top:-14px; width:1.5px; height:14px; border-radius:2px; background:linear-gradient(rgba(190,215,255,.1), rgba(190,215,255,.75));
  animation:thRain linear infinite; }
@keyframes thRain { from{ transform:translateY(-10px); } to{ transform:translateY(150px); } }
/* falling snow */
.th-snow { position:absolute; inset:0; overflow:hidden; }
.th-snow i { position:absolute; top:-8px; width:5px; height:5px; border-radius:50%; background:rgba(255,255,255,.9); box-shadow:0 0 4px rgba(255,255,255,.6);
  animation:thSnow linear infinite; }
@keyframes thSnow { from{ transform:translateY(-8px) translateX(0); } to{ transform:translateY(150px) translateX(14px); } }
@media (prefers-reduced-motion:reduce){ .th-cel,.th-cel::before,.th-cel::after,.th-clouds span,.th-stars i,.th-rain i,.th-snow i{ animation:none; } .th-cel{ transform:none; } .th-rain,.th-snow{ display:none; } }
/* base state is VISIBLE (opacity:1); the animation only adds an entrance (backwards fill holds the
   from-state during the delay) — so content is never stuck invisible if the animation doesn't run */
.today-hero .th-greet { font-size:20px; font-weight:850; letter-spacing:-.01em; color:#fff; text-shadow:0 2px 10px rgba(0,0,0,.3);
  animation:thIn .55s cubic-bezier(.2,.7,.3,1) .05s backwards; }
.today-hero .th-date { font-size:12.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:rgba(255,255,255,.82);
  margin-top:3px; animation:thIn .55s cubic-bezier(.2,.7,.3,1) .16s backwards; }
.today-hero .th-brief { font-size:13.5px; font-weight:600; line-height:1.5; color:#fff; margin-top:12px; text-shadow:0 1px 8px rgba(0,0,0,.28);
  animation:thIn .6s cubic-bezier(.2,.7,.3,1) .3s backwards; }
/* the hero sits on a colored sky, so its KPI cards use a FIXED dark glass (not theme tokens) —
   reads the same on light OR dark app themes (device: "works best on light and dark") */
.today-hero .th-kpis { margin-top:14px; background:rgba(255,255,255,.16); animation:thIn .6s cubic-bezier(.2,.7,.3,1) .48s backwards; }
.today-hero .th-kpis .kpi { background:rgba(10,15,30,.44); border:none; -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px); }
.today-hero .th-kpis .k { color:rgba(255,255,255,.82); }
.today-hero .th-kpis .v { color:#fff; }
.today-hero .th-kpis .v.bad { color:#FFC9C9; }
@keyframes thIn { from{ opacity:0; transform:translateY(10px); } to{ opacity:1; transform:translateY(0); } }
@media (prefers-reduced-motion:reduce){ .today-hero .th-orb{ animation:none } .today-hero .th-greet,.today-hero .th-date,.today-hero .th-brief,.today-hero .th-kpis{ animation-duration:.01s } }
/* after the first appearance, switching back to Home shows the hero instantly — no replayed entrance animation */
.today-hero.th-still .th-cel, .today-hero.th-still .th-greet, .today-hero.th-still .th-date, .today-hero.th-still .th-brief, .today-hero.th-still .th-kpis { animation:none; }
/* Kids page section dividers — a bit of structure over the long scroll */
.kids-sec { display:flex; align-items:center; gap:10px; margin:22px 2px 2px; font-size:12.5px; font-weight:800;
  letter-spacing:.12em; text-transform:uppercase; color:var(--teal); }
.kids-sec::after { content:""; flex:1; height:1.5px; border-radius:2px;
  background:linear-gradient(90deg, rgba(45,212,191,.4), transparent); }
.qz-q { font-size:15px; font-weight:700; margin:6px 0 12px; line-height:1.45; }
.qz-opts { display:flex; flex-direction:column; gap:8px; }
.qz-opt { text-align:left; display:flex; align-items:center; gap:9px; padding:12px 14px; border-radius:12px; cursor:pointer;
  font-size:13.5px; font-weight:650; color:var(--text); background:var(--bg2); border:1.5px solid var(--line); transition:all .15s; }
.qz-opt:not([disabled]):active { transform:scale(.98); }
.qz-opt .qz-mark { width:16px; font-weight:900; text-align:center; flex-shrink:0; }
.qz-opt.qz-correct { background:rgba(52,211,153,.14); border-color:var(--teal); color:#8FF3D9; }
.qz-opt.qz-correct .qz-mark { color:var(--teal); }
.qz-opt.qz-wrong { background:rgba(248,113,113,.14); border-color:var(--red); color:#F9B4B4; }
.qz-opt.qz-wrong .qz-mark { color:var(--red); }
.qz-opt.qz-dim { opacity:.5; }
.qz-feedback { margin-top:12px; padding:12px 14px; border-radius:12px; border:1px solid var(--line); animation:qzpop .35s ease; }
.qz-feedback.ok { background:rgba(52,211,153,.1); border-color:rgba(52,211,153,.3); }
.qz-feedback.no { background:rgba(245,158,11,.1); border-color:rgba(245,158,11,.3); }
.qz-feedback .qz-head { font-weight:800; font-size:13.5px; margin-bottom:4px; }
.qz-feedback .qz-why { font-size:12.5px; color:var(--dim); line-height:1.5; }
@keyframes qzpop { 0%{ opacity:0; transform:translateY(6px); } 100%{ opacity:1; transform:translateY(0); } }
/* Home widgets: customize mode — launcher-style widget frames, drag to reorder,
   ✕ removes, and a Samsung-style corner handle you DRAG to resize */
.launch.arrange .tile { position:relative; cursor:grab; touch-action:pan-y; animation:wjiggle 1.6s ease-in-out infinite;   /* pan-y: swipes still scroll; a 200ms hold starts the drag */
  border-radius:22px; outline:1.5px dashed rgba(45,212,191,.35); outline-offset:3px; }
.launch.arrange .tile:nth-child(2n) { animation-delay:.2s; }
.launch.arrange .tile.dragging { animation:none; opacity:.85; transform:scale(1.04); z-index:5; box-shadow:0 10px 30px rgba(0,0,0,.5); cursor:grabbing; }
.launch.arrange .tile.selq { animation:none; outline:2px solid var(--teal); outline-offset:3px; }
.wbadge { position:absolute; width:34px; height:34px; border-radius:50%; border:1px solid var(--line);
  display:flex; align-items:center; justify-content:center; font-size:15px; cursor:pointer; z-index:2;
  background:#141E38; color:var(--text); box-shadow:0 2px 8px rgba(0,0,0,.4); }
.wbadge.wx { top:6px; right:6px; color:#F87171; border-color:rgba(248,113,113,.5); }
/* corner resize handle — a rounded bracket hugging the bottom-right corner */
.whandle { position:absolute; right:2px; bottom:2px; width:34px; height:34px; z-index:2; cursor:nwse-resize;
  background:none; border:none; padding:0; touch-action:none; }
.whandle::after { content:""; position:absolute; right:5px; bottom:5px; width:18px; height:18px;
  border-right:3.5px solid var(--teal); border-bottom:3.5px solid var(--teal); border-bottom-right-radius:14px;
  filter:drop-shadow(0 1px 3px rgba(0,0,0,.5)); }
/* pull-to-refresh spinner (front page) */
#ptr { position:fixed; left:50%; top:-44px; transform:translateX(-50%); width:38px; height:38px; border-radius:50%;
  background:#141E38; border:1px solid var(--line); display:flex; align-items:center; justify-content:center;
  font-size:18px; color:var(--teal); z-index:250; opacity:0; transition:opacity .15s; pointer-events:none;
  box-shadow:0 4px 14px rgba(0,0,0,.4); }
#ptr.show { opacity:1; }
#ptr.armed { background:var(--teal); color:#06121F; transform:translateX(-50%) rotate(180deg); }
/* the floating copy that follows the finger while dragging */
.drag-ghost { position:fixed; z-index:900; pointer-events:none; opacity:.97; transform:scale(1.05) rotate(-1.6deg);
  box-shadow:0 22px 60px rgba(0,0,0,.78), 0 0 0 2px var(--teal); border-color:var(--teal) !important; animation:none !important; }   /* the COMPLETE tile, clearly lifted & tilted, riding the finger */
.placeholder { opacity:.4; outline:2px dashed var(--teal); outline-offset:-2px; border-radius:16px; }   /* a visible drop-slot so it's obvious where the card will land */
.tile.add-tile { border-style:dashed; opacity:.8; }
.sec-arr { position:relative; touch-action:pan-y; cursor:grab; }
/* per-page customize mode: cards jiggle, X badge, bottom Done bar */
.card.pgc { position:relative; touch-action:pan-y; cursor:grab; animation:wjiggle 1.6s ease-in-out infinite; outline:1.5px dashed rgba(45,212,191,.35); outline-offset:3px; }
.card.pgc:nth-child(2n) { animation-delay:.2s; }
#pgcbar { position:fixed; left:12px; right:12px; bottom:calc(76px + env(safe-area-inset-bottom, 0px)); z-index:85;
  background:var(--bg2); border:1px solid var(--line); border-radius:16px; padding:10px; box-shadow:0 8px 30px rgba(0,0,0,.5); }
#pgcbar .btn { width:100%; }
#pgcbar .pgc-off { display:flex; flex-wrap:wrap; gap:8px; margin-top:8px; }
@media (prefers-reduced-motion: reduce) { .card.pgc { animation:none; } }
.sec-arr.dragging { opacity:.85; z-index:5; cursor:grabbing; }
.sec-arr .wbadge.wx { top:2px; right:2px; }
.sec-off-row { display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin:4px 0 10px; }
@keyframes wjiggle { 0%,100% { transform:rotate(-.5deg); } 50% { transform:rotate(.5deg); } }
@media (prefers-reduced-motion: reduce) { .launch.arrange .tile { animation:none; } }
/* the budget hero's money math — labeled cells instead of a run-on sentence */
.bh-eq { display:grid; grid-template-columns:1fr 1fr; gap:6px; margin:8px 0 4px; }
.bh-eq .be[data-bh] { cursor:pointer; }
.bh-eq .be[data-bh]:active { transform:scale(.97); }   /* 2×2 — labels never clip (user: "two tabs below income and bills") */
.bh-eq .be { background:rgba(255,255,255,.04); border:1px solid var(--line); border-radius:10px; padding:7px 3px; text-align:center; min-width:0; }
.bh-eq .k { font-size:9px; text-transform:uppercase; letter-spacing:.2px; color:var(--dim); white-space:normal; line-height:1.3; }   /* wraps, never ellipsizes — labels must not cut off */
.bh-eq .v { font-size:clamp(12.5px, 3.4vw, 13.5px); font-weight:700; margin-top:2px; font-variant-numeric:tabular-nums; white-space:nowrap; }
.bh-eq .v.good { color:var(--teal); }
.bh-eq .v.bad { color:#F87171; }
.bh-eq .v.teal { color:var(--sky); }
/* branded wait state — the MN mark pulses while a save syncs */
#mnload { position:fixed; inset:0; z-index:700; display:flex; align-items:center; justify-content:center;
  background:rgba(4,7,15,.45); backdrop-filter:blur(2px); animation:viewIn .15s ease; }
.mnload-card { display:flex; flex-direction:column; align-items:center; gap:10px; }
.mnload-logo { width:58px; height:58px; border-radius:15px; animation:mnpulse 1s ease-in-out infinite; }
.mnload-t { font-size:12.5px; letter-spacing:3px; }
.mnload-t .m { font-weight:300; color:#E7ECF5; }
.mnload-t .n { font-weight:800; background:var(--grad-text); -webkit-background-clip:text; background-clip:text; color:transparent; }
.mnload-l { font-size:11.5px; color:var(--dim); letter-spacing:1px; }
@keyframes mnpulse { 0%,100% { transform:scale(1); opacity:1; } 50% { transform:scale(.88); opacity:.75; } }
@media (prefers-reduced-motion: reduce) { .mnload-logo { animation:none; } }
/* rows folded behind a "Show all" toggle — searching reveals them all */
.capped-hide { display:none; }
.card.searching .capped-hide { display:flex; }
/* one primary action on top, two equal-width companions beneath (Portfolio hero) */
.btnrow.even2 { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:8px; }
.btnrow.even2 .btn { width:100%; white-space:nowrap; }
/* history-typeahead chips under a form field */
.sug-box { display:flex; flex-wrap:wrap; gap:6px; margin:6px 0 2px; }
.sug-opt { font-size:12.5px; padding:6px 12px; border-radius:99px; border:1px solid var(--line);
  background:rgba(255,255,255,.04); color:var(--text); cursor:pointer; }
.sug-opt:active { transform:scale(.97); }
/* "dailougue not matching theme.cour" - the selected row's tint was the literal 1.0 teal, so the
   text went violet with the accent (var(--teal) is remapped) while the highlight behind it stayed
   teal. --teal-rgb is the form the colour picker rewrites, which is what makes a translucent tint
   follow the accent; the fallback is the original teal, so 1.0 is unchanged. */
.sel-opt.sel { background:rgba(var(--teal-rgb,45,212,191),.14); color:var(--teal); font-weight:700; }
.sel-opt.sel::after { content:"✓"; float:right; }
@media (min-width:560px){ .selpop-bg { align-items:center; } .selpop { border:1px solid var(--line); border-radius:18px; } }


/* budget quick income + expense add (two clean buttons, side by side) */
.addbar2 { display:flex; gap:10px; padding-top:15px; padding-bottom:15px; }
.addbar2 .btn { flex:1; margin-top:0; white-space:nowrap; padding-left:8px; padding-right:8px; }

/* money coach */
.coach-intro { background:linear-gradient(135deg, rgba(139,140,248,.14), rgba(45,212,191,.10)); border-color:rgba(139,140,248,.28); }
.coach-badge { display:inline-block; font-size:11.5px; font-weight:700; letter-spacing:.4px; color:var(--teal);
  background:rgba(45,212,191,.12); border:1px solid rgba(45,212,191,.3); border-radius:999px; padding:4px 10px; margin-bottom:9px; }
.coach-badge.slim { font-size:10.5px; padding:3px 9px; margin-bottom:0; opacity:.9; white-space:nowrap; flex-shrink:0; }
.coachcard { cursor:pointer; }
.ccrow { display:flex; align-items:center; gap:12px; }
.ccrow .ai { font-size:24px; flex:none; }
.ccrow .ar { margin-left:auto; color:var(--dim); font-size:20px; flex:none; }
.cthread { display:flex; flex-direction:column; gap:9px; max-height:52vh; overflow-y:auto; -webkit-overflow-scrolling:touch; padding:2px; margin:13px 0 6px; }
.chips-h { font-size:11.5px; color:var(--dim); font-weight:700; letter-spacing:.3px; margin:6px 0 8px; }
.cpair { display:flex; flex-direction:column; gap:9px; }
.cpair + .cpair { border-top:1px solid var(--line); padding-top:12px; margin-top:3px; }
.cbub { max-width:86%; padding:11px 13px; border-radius:15px; font-size:15px; line-height:1.5; white-space:normal; word-wrap:break-word; }
.cbub.you { align-self:flex-end; background:var(--grad); color:#06111c; font-weight:600; border-bottom-right-radius:5px; }
.cbub.coach { align-self:flex-start; background:var(--bg2); border:1px solid var(--line); color:var(--text); border-bottom-left-radius:5px; }
.cgo { display:block; margin-top:9px; background:rgba(45,212,191,.12); border:1px solid rgba(45,212,191,.32);
  color:var(--teal); font-size:12.5px; font-weight:700; padding:7px 11px; border-radius:10px; cursor:pointer; }
.chips { display:flex; flex-wrap:wrap; gap:7px; margin:13px 0 11px; }
.chip { background:var(--bg2); border:1px solid var(--line); color:var(--mid); font-size:12.5px;
  padding:8px 12px; border-radius:999px; cursor:pointer; }
.chip:active { transform:scale(.96); }
/* itemized receipt split: tight initial-chips per item ("who had this?") */
.itchips { display:flex; flex-wrap:wrap; gap:6px; }
.itchips .itc { width:30px; height:30px; padding:0; border-radius:50%; font-size:12.5px; font-weight:800;
  display:inline-flex; align-items:center; justify-content:center; }
.itchips .itc:not(.on) { opacity:.35; filter:grayscale(1); }
.itchips .itc.on::before { content:""; }   /* the ✓ prefix is for sentence chips, not initials */
/* Splitwise-style "how was this split?" sentence choices — a vertical one-tap list */
.sge-hows { display:flex; flex-direction:column; gap:8px; align-items:stretch; }
.sge-hows .chip { text-align:left; white-space:normal; line-height:1.4; font-size:13.5px; padding:10px 14px; }
/* the picked preset must be OBVIOUS (there was no .on style at all — selection was invisible) */
/* The two stops were literal 1.0 teal/blue while the border and the tick used var(--teal), so on any
   other accent the selected chip was two colours at once - the violet tick on a teal pill visible in
   the V1 Classic dialogue. Tokenised, with 1.0's own values as the fallback so 1.0 is unchanged. */
.chip.on { background:linear-gradient(90deg, rgba(var(--teal-rgb,45,212,191),.28), rgba(var(--g1-rgb,96,165,250),.28));
  border-color:var(--teal); color:#fff; font-weight:700;
  box-shadow:0 0 0 1px rgba(var(--teal-rgb,45,212,191),.35), 0 4px 14px rgba(var(--teal-rgb,45,212,191),.18); }
.chip.on::before { content:"✓ "; color:var(--teal); font-weight:800; }
/* "cant read how shared costs split when highlighted in white theme". The selected chip paints a
   28%-alpha tint - nearly white over a white card - and then sets color:#fff. White on white. It
   measured "fine" because the tint lives in background-IMAGE, so a computed backgroundColor read
   reports the card underneath; sampling the actual pixels is what showed it (252,251,254 behind
   255,255,255 text). In light theme the selected chip gets a solid accent fill and keeps white
   text on it; the tick turns white too so it does not vanish into the fill. */
body.light .chip.on, body.v2.light .chip.on {
  background:#4B3FAE; border-color:transparent; color:#fff; font-weight:700;
  box-shadow:0 2px 10px rgba(var(--g1-rgb,108,92,231),.3); }
body.light .chip.on::before, body.v2.light .chip.on::before { color:#fff; }
/* the newest coach answer arrives like a chat message: a brief "typing" beat, then the text reveals */
.cbub-t { display:block; }
.cbub.coach.cbub-fresh { animation:cbubIn .3s cubic-bezier(.2,.7,.3,1) both; }
.cbub.coach.cbub-fresh .cbub-t { animation:cbubText .45s ease .36s both; }
.cbub.coach.cbub-fresh::before { content:"• • •"; display:block; color:var(--teal); font-weight:800;
  letter-spacing:1px; line-height:1; margin-bottom:4px; animation:cbubDots .55s ease-in forwards; }
@keyframes cbubIn   { from{ opacity:0; transform:translateY(10px) scale(.97); } to{ opacity:1; transform:none; } }
@keyframes cbubText { from{ opacity:0; transform:translateY(2px); } to{ opacity:1; transform:none; } }
@keyframes cbubDots { 0%{ opacity:.35; } 45%{ opacity:1; } 100%{ opacity:0; height:0; margin:0; overflow:hidden; } }
.askrow { display:flex; gap:8px; align-items:stretch; }
.askin { flex:1 1 auto; min-width:0; background:var(--bg2); border:1px solid var(--line); color:var(--text);
  border-radius:12px; padding:12px 13px; font-size:15px; outline:none; }
.askin:focus { border-color:var(--teal); }
/* a kid's note, opened from Home — it should read like a message, not a table row */
.knote-big { background:var(--bg2); border:1px solid var(--line); border-radius:14px; padding:14px 15px;
  font-size:15px; line-height:1.5; word-break:break-word; }
/* a kid's photo on the parent's per-kid card, sized like the emoji avatar it replaces */
.pk-face { width:22px; height:22px; border-radius:50%; object-fit:cover; vertical-align:-4px; }
/* The kid's own greeting is 26px, so the 22px .pk-face used elsewhere reads as a stray dot there.
   Sized to the line it sits on rather than reusing a face built for a 15px list row. */
.kh-face { width:32px; height:32px; border-radius:50%; object-fit:cover; vertical-align:-6px; }
/* the kid's space announces WHOSE it is — one big name, ONE lock control (was: two lock icons) */
.kid-hello { display:flex; align-items:center; justify-content:space-between; gap:10px; margin:2px 2px 12px; }
.kh-left { min-width:0; }
/* "if we select whatever colour it should appear" - the glow follows the chosen accent now, and the
   day sits cleanly UNDER the name instead of being crammed beside it ("show sunday properly"). */
.kh-name { font-size:26px; font-weight:900; letter-spacing:.3px; text-shadow:0 0 18px rgba(var(--teal-rgb,45,212,191),.35); min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* a kid's request on the parent's Home: sentence on top, two EQUAL buttons on one line below */
.kreq-item { padding:10px 2px 12px; border-bottom:1px solid var(--line); }
.kreq-item:last-of-type { border-bottom:0; }
.kreq-item .t { font-size:15px; line-height:1.5; }
.kreq-item .d { color:var(--dim); font-size:12.5px; margin-top:3px; }
.kreq-btns { display:flex; gap:8px; margin-top:9px; }
.kreq-btns .btn { flex:1 1 0; width:auto; margin-top:0; }
/* wishlist rows: text full-width, buttons on their own line (they were crushing the text sideways) */
.kw-item { padding:10px 2px; border-bottom:1px solid var(--line); }
/* the wish's glass jar, back by request — compact and lined up with the wish's own text
   (v118's first cut floated it mid-row and let the meta wrap — "wishlist ui absurd") */
.kw-hasjar { display:flex; gap:11px; align-items:flex-start; }
.kw-glass { flex:0 0 auto; margin-top:2px; }
.kw-glass .kjar-glass { width:44px; height:54px; border-top-width:4px; }
.kw-glass .kjar-pct { font-size:10.5px; }
.kw-body { flex:1 1 auto; min-width:0; }
.kw-hasjar .kw-top .t { font-size:15px; }
.kw-hasjar .d { font-size:12.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.kw-hasjar .btnrow { margin-top:6px !important; }
.kw-hasjar .btnrow .btn { padding:8px 14px; font-size:12.5px; }
.kw-item:last-of-type { border-bottom:0; }
.kw-top { display:flex; align-items:flex-start; justify-content:space-between; gap:8px; }
.kw-top .t { font-weight:600; min-width:0; word-break:break-word; }
.kw-item .d { margin-top:2px; }
.kw-item .btnrow .btn { width:auto; flex:0 1 auto; }
.askrow .btn { margin-top:0; width:auto; padding-left:18px; padding-right:18px; flex:0 0 auto; }
/* coach voice: mic + read-aloud toggle */
.askmic { flex:0 0 auto; width:46px; border-radius:50%; height:46px; align-self:center; display:inline-flex; align-items:center; justify-content:center;
  background:var(--bg2); border:1px solid var(--line); color:var(--text); cursor:pointer; }
.askmic:active { transform:scale(.94); }
.askmic.listening { background:rgba(248,113,113,.18); border-color:var(--red); animation:micpulse 1s ease-in-out infinite; }
@keyframes micpulse { 0%,100%{ box-shadow:0 0 0 0 rgba(248,113,113,.5); } 50%{ box-shadow:0 0 0 8px rgba(248,113,113,0); } }
.coach-head { display:flex; align-items:center; gap:8px; margin-bottom:9px; min-height:1px; }
.voice-toggle { margin-left:auto; background:var(--bg2); border:1px solid var(--line); color:var(--dim); border-radius:999px;
  padding:6px 12px; font-size:11.5px; font-weight:800; cursor:pointer; white-space:nowrap; }
.voice-toggle.on { color:var(--teal); background:rgba(45,212,191,.12); border-color:rgba(45,212,191,.35); }
/* coach chat rows w/ avatar */
.crow { display:flex; gap:8px; align-items:flex-end; }
.crow.you { justify-content:flex-end; }
.crow .cbub { max-width:82%; }
.cav { flex:0 0 auto; width:30px; height:30px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  font-size:15px; background:rgba(139,140,248,.16); border:1px solid rgba(139,140,248,.42); }
/* family chat rows w/ avatar initials */
.fmsg-row { display:flex; gap:8px; align-items:flex-end; }
.fmsg-row.mine { justify-content:flex-end; }
/* message send/arrive animation — feels like a real chat */
.msg-in { animation:msgIn .3s cubic-bezier(.2,.7,.3,1) both; }
@keyframes msgIn { from { opacity:0; transform:translateY(12px) scale(.97); } to { opacity:1; transform:none; } }
.fmsg-row.sending { opacity:.62; }
.fmsg-row.sending .fmsg-b { position:relative; }
.fmsg-row.failed .fmsg-b { border-color:var(--red); }
.fmsg-row.failed .fmsg-m { color:var(--red); }
@media (prefers-reduced-motion: reduce) { .msg-in { animation:none; } }
.fav { flex:0 0 auto; width:29px; height:29px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  font-size:12.5px; font-weight:800; color:#fff; text-shadow:0 1px 2px rgba(0,0,0,.35); }

/* empty state */
.empty { text-align:center; color:var(--dim); padding:26px 10px; font-size:13.5px; line-height:1.7; }

/* modal */
#modal-root:empty { display:none; }
.modal-bg { position:fixed; inset:0; background:rgba(4,7,15,.72); z-index:90;
  display:flex; align-items:flex-end; justify-content:center; }
/* the passcode-recovery modal must rise ABOVE the lock screen (z 300) — was opening underneath, looking dead */
body:has(#lock.show) .modal-bg { z-index:400; }
/* same problem one floor down: the first-run wizard is z 200, so the currency sheet it opens (and
   the "you already have an account" alert finishOnboarding shows) rendered UNDERNEATH it */
body:has(#onboard.show) .modal-bg { z-index:400; }
.modal { position:relative; background:#0E1730; border:1px solid var(--line); border-radius:22px 22px 0 0;
  width:100%; max-width:560px; padding:18px 16px calc(env(safe-area-inset-bottom, 0px) + 18px);
  max-height:92dvh; overflow-y:auto; animation:up .18s ease-out; }
/* the sheet's non-scrolling frame: .modal scrolls, so anything absolute inside it rides the
   scroll and clips at the top edge — the ✕/‹ anchor to the shell instead and stay put */
.modal-shell { position:relative; width:100%; max-width:560px; animation:up .18s ease-out; }
.modal-shell .modal { animation:none; }   /* the shell carries the slide — both running doubles it */
@keyframes up { from { transform:translateY(40px); opacity:.4; } to { transform:none; opacity:1; } }
.modal h2 { font-size:18px; margin-bottom:10px; padding-right:36px; }
.modal-x { position:absolute; top:14px; right:14px; width:32px; height:32px; border-radius:10px;
  background:var(--bg2); border:1px solid var(--line); color:var(--mid); font-size:15px; line-height:1;
  cursor:pointer; z-index:3; display:flex; align-items:center; justify-content:center; transition:transform .1s ease; }
.modal-x:active { transform:scale(.9); }
body.modal-open { overflow:hidden; }
/* floating quick-add button + its action sheet */
.fab { position:fixed; right:18px; bottom:calc(78px + env(safe-area-inset-bottom,0px)); z-index:45;
  width:52px; height:52px; border-radius:17px; border:none; cursor:pointer; font-size:28px; font-weight:400; line-height:1;
  color:#06111c; background:var(--grad); box-shadow:0 8px 24px rgba(45,212,191,.35); display:flex; align-items:center; justify-content:center;
  transition:transform .12s ease, opacity .18s ease; }
.fab:active { transform:scale(.9); opacity:1; }
/* A button pinned to the bottom-right will always sit over the bottom-right of the list, which is
   exactly where right-aligned AMOUNTS are — "$197.16" was reading as "$19". The answer then was to
   make it translucent so the figure underneath showed through.
   That traded one fault for a worse one: at 62% the amount BLENDS INTO the button and the pair
   read as neither — his words, "gets mixed up when text increased", and the bigger default text
   made it plainer. A solid button is at least understood as an object sitting ON the page; a
   half-transparent one just looks like broken text.
   So: opaque, and it still ducks out of the way the moment the list moves, which is what actually
   lets you read what is under it. Slightly smaller too, so it covers less to begin with. */
.fab { opacity:1; }
.fab:hover, .fab:focus-visible { opacity:1; }
/* ducked while the list under it is scrolling — see the scroll handler by the FAB's creation */
.fab.fab-away { opacity:0; transform:scale(.82) translateY(10px); pointer-events:none; }
.qa-sheet-bg { position:fixed; inset:0; z-index:95; background:rgba(4,7,15,.6); display:flex; align-items:flex-end; justify-content:center; animation:viewIn .18s ease; }
.qa-sheet { width:100%; max-width:520px; background:#0E1730; border:1px solid var(--line); border-radius:20px 20px 0 0;
  padding:16px 14px calc(env(safe-area-inset-bottom,0px) + 16px); animation:up .2s ease-out; }
body.light .qa-sheet { background:#fff; }
.qa-title { font-size:12.5px; text-transform:uppercase; letter-spacing:1px; color:var(--dim); font-weight:700; text-align:center; margin-bottom:10px; }
.qa-opt { display:block; width:100%; text-align:center; background:var(--bg2); border:1px solid var(--line); color:var(--text);
  font-size:15px; font-weight:600; padding:14px; border-radius:13px; margin-top:8px; cursor:pointer; }
.qa-opt:active { transform:scale(.98); }
.qa-opt.qa-cancel { color:var(--dim); font-weight:500; background:none; }
/* first-run guided tour — a bottom sheet that sits above the app but keeps the screen behind visible */
#tour { position:fixed; inset:0; z-index:300; pointer-events:none; }
.tour-scrim { position:absolute; inset:0; background:rgba(4,7,15,.42); pointer-events:auto; }
.tour-card { position:absolute; left:12px; right:12px; bottom:calc(74px + env(safe-area-inset-bottom,0px));
  margin:0 auto; max-width:520px; background:#0E1730; border:1px solid var(--line); border-radius:18px;
  padding:18px; box-shadow:0 14px 44px rgba(0,0,0,.55); pointer-events:auto; animation:tourpop .28s ease; }
@keyframes tourpop { from { opacity:0; transform:translateY(16px); } to { opacity:1; transform:none; } }
.tour-ic { font-size:30px; line-height:1; }
.tour-step { font-size:11.5px; letter-spacing:.6px; text-transform:uppercase; color:var(--dim); margin:8px 0 4px; font-weight:700; }
.tour-card h3 { margin:0 0 6px; font-size:17px; }
.tour-card p { margin:0; font-size:13.5px; line-height:1.55; color:var(--mid); }
.tour-dots { display:flex; gap:6px; justify-content:center; margin:14px 0 12px; }
.tour-dots span { width:6px; height:6px; border-radius:50%; background:var(--line); transition:.2s; }
.tour-dots span.on { background:var(--teal); width:18px; border-radius:3px; }
.tour-btns { display:flex; gap:8px; align-items:center; }
.tour-btns #tour-skip { margin-right:auto; }
.tour-btns #tour-next { min-width:92px; }
body.light .tour-card { background:#fff; }
/* compact fields — the old sizing made an 8-field form (e.g. Add subscription) fill the whole screen */
.field { margin-top:9px; }
.field label { display:block; font-size:11.5px; color:var(--dim); text-transform:uppercase;
  letter-spacing:.9px; margin-bottom:4px; }
.field input, .field select, .field textarea { width:100%; background:var(--bg2); border:1px solid var(--line);
  color:var(--text); border-radius:11px; padding:9px 12px; font-size:13.5px; outline:none; }
/* iOS Safari force-zooms any focused input under 16px — keep 16 there; everyone else gets the compact 14 */
@supports (-webkit-touch-callout: none) { .field input, .field select, .field textarea { font-size:15px; } }
.field input:focus, .field select:focus, .field textarea:focus { border-color:var(--teal); }
/* textareas were never covered by the field styling — the Pinned-notes editor rendered as a raw
   grey browser box in the middle of a dark app ("pinned notes text field — not good") */
.field textarea { font-family:inherit; line-height:1.55; resize:vertical; min-height:110px; }
.field input.err, .field select.err { border-color:var(--red); background:rgba(248,113,113,.06); }
.field-err { color:var(--red); font-size:11.5px; margin-top:5px; font-weight:600; }
/* show/hide 👁 toggle sitting inside a password field */
.pw-wrap { position:relative; display:block; }
.pw-wrap input { padding-right:46px; }
.pw-eye { position:absolute; right:10px; top:50%; transform:translateY(-50%); background:none; border:0;
  font-size:17px; line-height:1; cursor:pointer; padding:8px; opacity:.6; border-radius:8px; }
.pw-eye:hover, .pw-eye:active { opacity:1; }
.reqnote { font-size:11.5px; color:var(--dim); margin-top:12px; }
/* ticker auto-fill status line (under the Ticker field) */
.tk-status { font-size:12.5px; margin-top:6px; min-height:1px; line-height:1.4; }
.tk-status .tk-loading { color:var(--dim); }
.tk-status .tk-loading::before { content:"🔎 "; }
.tk-status .tk-ok { color:var(--teal); font-weight:700; }
.tk-status .tk-miss { color:var(--gold); font-weight:600; }
.tk-status .tk-hint { color:var(--dim); }
.field input.tk-filled { border-color:var(--teal); background:rgba(45,212,191,.08); transition:background .5s, border-color .5s; }
.form-alert { background:rgba(248,113,113,.12); border:1px solid rgba(248,113,113,.45); color:var(--red);
  font-size:13.5px; font-weight:700; border-radius:12px; padding:11px 13px; margin:4px 0 14px;
  animation:up .18s ease-out; }
.form-alert::before { content:"⚠ "; }
.frow { display:flex; gap:10px; } .frow .field { flex:1; }

/* month bars (interactive) */
.barswrap { margin-top:12px; }
.bars { display:flex; align-items:flex-end; gap:5px; height:120px; }
.bars .b { flex:1; height:100%; display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:5px; cursor:pointer; }
.bars .bar { width:100%; background:linear-gradient(180deg,#38BDF8,#2DD4BF); border-radius:5px 5px 2px 2px; min-height:2px; transition:filter .1s; }
.bars .b.sel .bar { filter:brightness(1.25) saturate(1.3); box-shadow:0 0 0 2px rgba(45,212,191,.4); }
.bars .b.sel .lb { color:var(--teal); font-weight:800; }
.bars .lb { font-size:9px; color:var(--dim); flex-shrink:0; }
.bars-cap { text-align:center; font-size:12.5px; color:var(--mid); margin-top:9px; font-variant-numeric:tabular-nums; }
/* 2.0 charts follow the chosen accent, not v1's teal (his: "income by months … does not follow theme style") */
body.v2 .bars .bar { background:linear-gradient(180deg,var(--g1),var(--g2)); }
body.v2 .bars .b.sel .bar { box-shadow:0 0 0 2px var(--g2); }
body.v2 .bars .b.sel .lb { color:var(--g2); }

/* toast */
/* dark "snackbar" toast — always light text, on both themes */
/* Toasts sit at the TOP, app-wide: at the bottom they landed mid-screen over modal buttons
   (e.g. "Wrong PIN — try again" covering Continue) and under the on-screen keyboard. */
#toast { position:fixed; top:calc(env(safe-area-inset-top, 0px) + 12px); left:50%; transform:translateX(-50%) translateY(-20px);
  background:#12203c; border:1px solid rgba(255,255,255,.12); color:#E7ECF5; font-size:13.5px;
  padding:12px 16px; border-radius:16px; opacity:0; transition:.25s; z-index:120; pointer-events:none;
  box-shadow:0 8px 24px rgba(4,7,15,.35); max-width:calc(100% - 32px); text-align:left; line-height:1.45;
  display:flex; align-items:center; gap:11px; width:max-content; }
#toast.show { opacity:1; transform:translateX(-50%) translateY(0); }
#toast:not(.show) { pointer-events:none !important; }   /* a hidden toast must NEVER eat taps under it */
#toast .toast-act { color:#5EEAD4; margin-left:10px; font-weight:800; cursor:pointer; }
#toast .tst-logo { width:28px; height:28px; border-radius:8px; flex-shrink:0; }
#toast .tst-body { min-width:0; }
#toast.bad  { border-color:rgba(248,113,113,.55); box-shadow:0 8px 24px rgba(4,7,15,.35), 0 0 0 1px rgba(248,113,113,.18) inset; }
#toast.good { border-color:rgba(45,212,191,.5); }

.note { font-size:12.5px; color:var(--dim); line-height:1.6; margin-top:10px; }
.progress { height:8px; background:var(--bg2); border-radius:99px; overflow:hidden; margin-top:8px; }
.progress > div { height:100%; background:var(--grad); border-radius:99px; max-width:100%; }
.progress.red > div { background:linear-gradient(90deg,#F59E0B,#F87171); }

/* section subtitle / helper line under a hero */
.subline { font-size:12.5px; color:var(--dim); margin-top:4px; }

/* KPI grids inside a card */
.kpis { display:grid; grid-template-columns:1fr 1fr; gap:1px; background:var(--line);
  border:1px solid var(--line); border-radius:14px; overflow:hidden; margin-top:12px; }
.kpis.three { grid-template-columns:1fr 1fr 1fr; }
/* A label that wraps to two lines ("Savings rate" at 360px) used to push its VALUE down a line,
   leaving the three numbers on a ragged baseline. Making each cell a column and pushing the value
   to the bottom keeps the row of figures on ONE line however the labels wrap. */
.kpi { background:var(--card); padding:12px 12px; min-width:0;
  display:flex; flex-direction:column; justify-content:space-between; }
.kpi .v { margin-top:auto; }
.kpi .k { font-size:10.5px; color:var(--dim); text-transform:uppercase; letter-spacing:1px; white-space:normal; overflow-wrap:anywhere; line-height:1.25; }
.kpi .v { font-size:18px; font-weight:800; margin-top:4px; font-variant-numeric:tabular-nums; line-height:1.15; overflow-wrap:anywhere; }
/* One formula for every KPI value, whatever the column count. Before, a 3-up row clamped to
   13.5px while everything else sat at 15px, so the SAME stat rendered 13.5px on Home/Budget/Subs/
   Net worth and 15px on Debt/Investing — measured, not guessed. Size now depends only on how wide
   the screen is, never on how many stats happen to sit beside it. The clamp still protects the
   3-up case on narrow phones, it just applies to everyone equally. */
.kpi .v.sm { font-size:clamp(13.5px,4.2vw,15px); }
.kpis.three .kpi .v{ font-size:clamp(13.5px,4.2vw,15px); white-space:nowrap; overflow-wrap:normal; }

/* budget hero — vs-last-month chip + income allocation bar */
.bud-hero .bh-head { display:flex; align-items:flex-start; justify-content:center; gap:10px; }
.bud-hero .hero-num{ text-align:center; }
.bud-hero .bh-head h3 { margin:0; }
.trend-chip { flex-shrink:0; display:inline-flex; align-items:baseline; gap:4px; padding:3px 9px; border-radius:999px;
  font-size:11.5px; font-weight:700; font-variant-numeric:tabular-nums; line-height:1; white-space:nowrap; }
.trend-chip.good { color:var(--teal); background:rgba(45,212,191,.12); border:1px solid rgba(45,212,191,.3); }
.trend-chip.bad  { color:var(--red);  background:rgba(248,113,113,.12); border:1px solid rgba(248,113,113,.3); }
.trend-chip .tc-sub { font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.4px; opacity:.85; }
.alloc { margin:14px 0 2px; }
.al-bar { display:flex; height:13px; border-radius:8px; overflow:hidden; background:var(--bg2); border:1px solid var(--line); }
.al-seg { height:100%; min-width:2px; }
.al-seg + .al-seg { box-shadow:inset 1px 0 0 rgba(7,11,22,.55); }
.al-bills { background:var(--red); } .al-subs { background:var(--purple); }
.al-spent { background:var(--gold); } .al-saved { background:var(--teal); }
.al-left  { background:var(--blue); }
.al-legend { display:flex; flex-wrap:wrap; gap:7px 13px; margin-top:9px; font-size:11.5px; color:var(--dim); }
.al-key { display:inline-flex; align-items:center; white-space:nowrap; }
.al-key i { width:9px; height:9px; border-radius:3px; display:inline-block; margin-right:5px; }
.al-key b { color:var(--mid); font-weight:700; margin-left:4px; font-variant-numeric:tabular-nums; }
.al-key.al-over { color:var(--red); font-weight:700; }

/* repeat-expense quick-add tray — tappable shortcut chips */
.rx-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:12px 0 2px; }
.rx-chip { position:relative; min-width:0; display:flex; flex-direction:column; align-items:flex-start; text-align:left;
  padding:13px 13px 12px; border:1px solid var(--line); border-radius:15px; background:var(--bg2); cursor:pointer; font:inherit; color:var(--text);
  transition:transform .1s ease, border-color .12s, background .12s; }
.rx-chip:active { transform:scale(.97); }
.rx-chip.posted { border-color:rgba(45,212,191,.32); background:rgba(45,212,191,.06); }
.rx-ic { font-size:22px; line-height:1; }
.rx-name { font-size:13.5px; font-weight:700; margin-top:6px; overflow-wrap:anywhere; line-height:1.25; }
.rx-amt { font-size:13.5px; color:var(--mid); font-variant-numeric:tabular-nums; margin-top:1px; }
.rx-act { margin-top:9px; font-size:10.5px; font-weight:800; letter-spacing:.5px; text-transform:uppercase;
  color:var(--teal); background:rgba(45,212,191,.12); border:1px solid rgba(45,212,191,.3); border-radius:999px; padding:4px 10px; }
.rx-chip.posted .rx-act { color:var(--dim); background:rgba(143,163,200,.10); border-color:var(--line); }
.rx-del { position:absolute; top:8px; right:8px; width:23px; height:23px; border-radius:8px; border:1px solid var(--line);
  background:var(--card); color:var(--dim); font-size:11.5px; line-height:1; cursor:pointer; display:flex; align-items:center; justify-content:center; }
.rx-del:active { transform:scale(.9); }
.rx-add { align-items:center; justify-content:center; text-align:center; border-style:dashed; background:transparent; color:var(--teal); min-height:104px; }
.rx-add .rx-plus { font-size:28px; font-weight:300; line-height:1; }
.rx-add .rx-addl { font-size:12.5px; font-weight:800; margin-top:3px; }
.rx-postall { width:100%; margin-top:12px; }

/* horizontal category bars */
.catbar { margin-top:10px; }
.catbar .cb-top .cb-r { display:inline-flex; align-items:center; gap:10px; }
.catbar .cb-top .cb-r .del { position:static; margin:0; }
.fx-sec { font-size:13.5px; font-weight:800; letter-spacing:.4px; text-transform:uppercase; color:var(--mid); margin:14px 0 5px; }
/* ---- redesigned family rows: icon tile + category chip + member avatar (payments & shared expenses) ---- */
/* swipe-to-reveal actions: a red Delete (+ Edit) panel sits behind each row; the foreground slides left over it */
.swrow { position:relative; overflow:hidden; border-bottom:1px solid rgba(255,255,255,.05); background:var(--card); }
.swrow:last-child { border-bottom:none; }
/* PILLS, NOT SLABS (2026-09-01, his screenshot: "edit and delete buttom stretched when swapped").
   The old panel was align-items:stretch, so the buttons grew to whatever the row's height was —
   fine on a one-line row, a stretched colour slab beside a two-line spending row. Fixed-height
   rounded pills, centred, capped below the row on short rows. The panel itself is transparent, so
   the old 1px seam inset (GPU seam under the sliding foreground: "red line appears") is obsolete —
   there is no colour edge left to peek. The drag JS reads act.offsetWidth (app.js:36008), so the
   reveal distance follows this styling by itself. */
.swrow-act { position:absolute; top:0; right:0; bottom:0; display:flex; align-items:center; gap:7px; padding:0 8px; }
/* his follow-up ("edit icone and edit lable seems smaller"): the pill shape stays, the CONTENT
   goes back to the old reading size — 66px wide, 12.5px label, 18px glyph. */
.swrow-act button { width:66px; height:48px; max-height:calc(100% - 6px); border:none; border-radius:13px;
  color:#fff; font-weight:800; font-size:12.5px; padding:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; cursor:pointer; white-space:nowrap; }
.swrow-act button * { pointer-events:none; }   /* the whole button is the tap target (the delegated [data-del] handler reads e.target) */
.swrow-act button .swi { font-size:18px; line-height:1; }
.swrow-edit { background:#3A4B6E; }
.swrow-del  { background:#C62B30; }   /* was #E5484D: white text on it measured 3.91:1 in BOTH themes */
.swrow-fg { position:relative; background:var(--card); transition:transform .22s cubic-bezier(.4,0,.2,1); will-change:transform; touch-action:pan-y; }
.swrow-fg.swrow-drag { transition:none; }
.swrow-fg .fx-item { border-bottom:none; }
.swipe-tip { color:var(--dim2); font-size:11.5px; font-weight:600; opacity:.75; }
.fx-item { display:flex; align-items:flex-start; gap:11px; padding:12px 2px; border-bottom:1px solid rgba(255,255,255,.05); }
.fx-item:last-child { border-bottom:none; }
.fx-tile { width:40px; height:40px; flex-shrink:0; border-radius:12px; display:flex; align-items:center; justify-content:center;
  font-size:19px; background:linear-gradient(145deg,#1B2540,#141B30); border:1px solid rgba(255,255,255,.06); position:relative; }
.fx-tile::after { content:""; position:absolute; inset:0; border-radius:13px; border:1.5px solid transparent; }
.fx-tile.cat-sub::after  { border-color:rgba(91,141,239,.42); }
.fx-tile.cat-ins::after  { border-color:rgba(167,139,250,.42); }
.fx-tile.cat-debt::after { border-color:rgba(240,114,155,.42); }
.fx-tile.cat-food::after { border-color:rgba(45,212,191,.42); }
.fx-tile.cat-bill::after { border-color:rgba(91,141,239,.30); }
.fx-tile.cat-kids::after { border-color:rgba(251,191,36,.4); }
.fx-b { flex:1; min-width:0; }
.fx-top { display:flex; align-items:center; gap:8px; flex-wrap:wrap; min-width:0; }
.fx-nm { font-size:15px; font-weight:750; overflow-wrap:anywhere; word-break:break-word;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.fx-r { margin-top:1px; padding-left:10px; }   /* top-align the amount + keep a clear gap so a long name never touches the price */
.fx-meta { font-size:11.5px; color:var(--dim); margin-top:3px; line-height:1.65; }   /* inline flow so the leading avatar sits ON the line, not wrapped alone */
/* the SHARING list carries two meta lines (who paid, who owes what) — on a phone they wrapped into
   a four-plus-line tower per expense ("need to squeeze up / align shared expenses"). One truncated
   line per fact: nowrap + ellipsis, and the name clamps to a single line there too. */
/* WRAP, never amputate. nowrap+ellipsis here turned big-font phones into "Aug 4 · rom…" — the
   date survived and everything else was cut mid-word ("UI is cutting off — can't see properly").
   At normal font scale this is still one line (nothing to wrap); at a large accessibility scale
   it takes a second line and shows EVERYTHING. Height is negotiable, information is not. */
/* the avatar lives in a fixed left gutter and every text line aligns at 25px — including wrapped
   ones ("are they aligned?"). NOT text-indent: that shifted the avatar's letter out of its circle. */
#fam-exps .fx-meta { line-height:1.5; position:relative; padding-left:25px; }
#fam-exps .fx-meta .fx-av { position:absolute; left:0; top:2px; }
#fam-exps .fx-item { padding:7px 2px; }
/* v227: "not squeezed up" — TWO lines per expense now. The chip rides inside the single meta line
   (mini-sized) and the who-owes-what folds in after the payer, one ellipsized line for the lot. */
/* chip rides NEXT TO the name (fx-top wraps if tight) so the single meta line keeps its full
   width for date · payer · who-owes-what — in the meta it was eating the line and big font
   scales truncated everything after "R Jul…" */
#fam-exps .fx-top .catchip { font-size:9px; padding:1.5px 6px; }
.catchip { font-size:10.5px; font-weight:800; letter-spacing:.03em; padding:2px 8px; border-radius:6px; text-transform:uppercase; white-space:nowrap; }
.catchip.cat-sub  { color:#7FA9F5; background:rgba(91,141,239,.14); }
.catchip.cat-ins  { color:#BCA6FB; background:rgba(167,139,250,.14); }
.catchip.cat-debt { color:#F390B0; background:rgba(240,114,155,.14); }
.catchip.cat-food { color:var(--teal); background:rgba(45,212,191,.14); }
.catchip.cat-bill { color:#7FA9F5; background:rgba(91,141,239,.12); }
.catchip.cat-kids { color:var(--gold); background:rgba(251,191,36,.14); }
.catchip.cat-other{ color:var(--dim); background:rgba(255,255,255,.06); }
.catchip.cat-paid { color:var(--teal); background:rgba(45,212,191,.14); }
.fx-av { width:19px; height:19px; flex-shrink:0; border-radius:50%; display:inline-flex; align-items:center; justify-content:center;
  font-size:10.5px; font-weight:850; color:#08121c; vertical-align:middle; margin-right:5px; position:relative; top:-1px; }
.fx-meta .fx-av + * , .fx-meta .fx-split { margin-left:0; }
.fx-split { margin-left:2px; }
.fx-av.me { background:linear-gradient(135deg,var(--violet,#A78BFA),#F0729B); }
.fx-split { font-size:9px; font-weight:800; letter-spacing:.04em; padding:2px 7px; border-radius:999px; text-transform:uppercase; white-space:nowrap; }
.fx-split.sp { color:var(--teal); background:rgba(45,212,191,.1); border:1px solid rgba(45,212,191,.24); }
.fx-split.hh { color:var(--gold); background:rgba(251,191,36,.1); border:1px solid rgba(251,191,36,.28); }
/* One tidy, aligned row for every tag on an expense — category, 🔁 monthly, split/household.
   All pills share the same height/radius/spacing so they line up cleanly instead of scattering. */
.fx-chips { display:flex; align-items:center; flex-wrap:wrap; gap:6px; margin-top:6px; }
.fx-chips .catchip, .fx-chips .fx-split, .fx-chips .tag {
  display:inline-flex; align-items:center; gap:4px; height:21px; padding:0 9px; margin:0;
  border-radius:999px; font-size:10.5px; font-weight:800; letter-spacing:.04em; line-height:1; white-space:nowrap;
}
.catchip.cat-monthly { color:#8FB3F5; background:rgba(91,141,239,.16); }
.fx-r { display:flex; align-items:center; gap:8px; flex-shrink:0; }
/* 700 not 800 — part of B: heavy bold reads brighter than its colour, so this does as much for
   the glare as the token did. Still the loudest thing in the row. */
.fx-r .amt { font-size:15px; font-weight:700; font-variant-numeric:tabular-nums; }
.fx-r .del { position:static; margin:0; }
.fx-pay { margin-top:7px; display:inline-flex; align-items:center; gap:5px; font-size:12.5px; font-weight:800;
  color:#3A2500; background:linear-gradient(135deg,#FBBF24,#F59E0B); border:none; border-radius:9px; padding:6px 12px; cursor:pointer; }
.fx-pay:active { transform:scale(.97); }
/* family-budget-style "share to family" control — a small single-line chip (never wraps, keeps rows compact) */
.fam-share { margin-top:5px; display:inline-flex; align-items:center; gap:4px; font-size:10.5px; font-weight:600; white-space:nowrap;
  padding:2px 8px; border-radius:999px; cursor:pointer; color:#C3C9FF;
  background:rgba(139,140,248,.14); border:1px solid rgba(139,140,248,.3); }
.fam-share:active { transform:scale(.97); }
.fam-share.on { cursor:default; color:var(--teal); background:rgba(45,212,191,.12); border-color:rgba(45,212,191,.28); }
/* His: "add to family budget under subs and insurance not visible under white theme".
   The colours above are hard-coded dark-theme literals — rgb(139,140,248) is a frozen copy of the
   v1 dark --purple — on a rule with no theme scope at all. So light was not leaking through; the
   pill was painting over the light theme with dark paint, composited to 1.30:1 (measured, gate:
   money-app/tests/famshare-light.cjs). Solid fills rather than alpha, because this chip sits on the
   page background on 2.0 detail pages and on a white card in the 1.0 list rows — translucency gave
   it two different contrasts depending on where it appeared. Covers body.light and body.v2.light
   alike, since both carry .light. */
body.light .fam-share { color:#5B21B6; background:#EDE7FD; border-color:#8B5CF6; }
body.light .fam-share.on { color:#0F766E; background:#D8F5F0; border-color:#14B8A6; }
body.light .fam-share.off { color:#54506E; background:#EFEDF5; border-color:#B9B4CC; }
/* local kid-profile avatar picker */
.kid-avs { display:flex; flex-wrap:wrap; gap:8px; }
.kid-av { width:44px; height:44px; border-radius:12px; font-size:22px; line-height:1; display:flex; align-items:center; justify-content:center;
  background:var(--card); border:1.5px solid var(--line); cursor:pointer; padding:0; }
.kid-av.on { border-color:var(--teal); background:rgba(var(--teal-rgb,45,212,191),.12); }
.kn-photo-img { width:72px; height:72px; border-radius:16px; object-fit:cover; display:block; margin-bottom:8px; }
/* "Who's using the app?" profile picker — Parent + Kid tiles */
.prof-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-top:6px; }
.prof-tile { display:flex; flex-direction:column; align-items:center; gap:8px; cursor:pointer; padding:6px 2px; border-radius:14px; }
.prof-tile:active { transform:scale(.96); }
.prof-av { width:74px; height:74px; border-radius:18px; display:flex; align-items:center; justify-content:center; font-size:34px; line-height:1;
  background:var(--card); border:2px solid var(--line); background-size:cover; background-position:center; }
.prof-tile:hover .prof-av { border-color:var(--teal); }
.prof-av.add { border-style:dashed; color:var(--dim); }
.prof-nm { font-size:12.5px; font-weight:700; text-align:center; overflow-wrap:anywhere; }
/* category picker grid (e.g. settle-up → log to budget) */
.cat-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:14px; }
.cat-grid .catpick { margin:0; text-align:left; justify-content:flex-start; }
/* greyed / disabled: the family budget is a Couple & Family plan feature — visible but clearly not usable here */
.fam-share.off { color:var(--dim); background:rgba(255,255,255,.045); border-color:rgba(255,255,255,.12); opacity:.8; }
.catbar .cb-top { display:flex; justify-content:space-between; font-size:12.5px; margin-bottom:5px; }
.catbar .cb-top .amt { color:var(--mid); font-variant-numeric:tabular-nums; }
/* the one habit to fix first — same pick as Home's Focus line & the Coach */
.catbar.cb-weak { padding:8px 10px; margin-left:-10px; margin-right:-10px; border-radius:10px; background:rgba(245,158,11,.09); border:1px solid rgba(245,158,11,.22); }
.cb-tag { font-size:10.5px; font-weight:700; color:var(--gold); background:rgba(245,158,11,.14); padding:1px 6px; border-radius:6px; margin-left:6px; white-space:nowrap; }
.track { height:9px; background:var(--bg2); border-radius:99px; overflow:hidden; }
.track > div { height:100%; border-radius:99px; }
.track .fill-teal { background:linear-gradient(90deg,#2DD4BF,#38BDF8); }
.track .fill-gold { background:linear-gradient(90deg,#F59E0B,#FBBF24); }
.track .fill-red  { background:linear-gradient(90deg,#F87171,#FB7185); }
.track .fill-purple { background:linear-gradient(90deg,#8B8CF8,#38BDF8); }

/* tables (scroll horizontally on small screens) */
.tblwrap { overflow-x:auto; margin-top:10px; -webkit-overflow-scrolling:touch; }
/* width:max-content lets a wide table grow to its content and SCROLL inside .tblwrap
   (min-width:100% keeps narrow tables full-width) — otherwise width:100% squeezes columns until headers overlap */
table.tbl { width:max-content; min-width:100%; border-collapse:collapse; font-size:12.5px; white-space:nowrap; }
table.tbl th, table.tbl td { padding-left:11px; padding-right:11px; }
table.tbl th { color:var(--dim); font-weight:600; text-transform:uppercase; letter-spacing:.6px;
  font-size:10.5px; text-align:right; padding:7px 9px; border-bottom:1px solid var(--line); position:sticky; }
table.tbl th:first-child, table.tbl td:first-child { text-align:left; position:sticky; left:0; background:var(--card); }
table.tbl td { padding:8px 9px; text-align:right; border-bottom:1px solid rgba(30,42,68,.5);
  font-variant-numeric:tabular-nums; }
table.tbl tr:last-child td { border-bottom:none; }
table.tbl tr.tot td { font-weight:800; border-top:1px solid var(--line); color:var(--text); }
table.tbl tr.fut { opacity:.4; }
table.tbl tr.cur td { color:var(--teal); font-weight:700; }
/* the dot that marks a month carrying one-time money — quiet, but it gives the number a reason */
.ymark { color:var(--teal); font-weight:800; margin-left:3px; }
table.tbl tr[data-month] { cursor:pointer; }
table.tbl tr[data-month]:active td { background:rgba(45,212,191,.10); }
/* compact comparison — fits a phone with no side-scroll */
/* compact tables (4 cols) are meant to FIT the screen, not scroll — force full width + tight cells */
table.tbl.cmp { width:100%; font-size:11.5px; white-space:normal; }
table.tbl.cmp th, table.tbl.cmp td { padding-left:5px; padding-right:5px; }
table.tbl.cmp th, table.tbl.cmp td { padding:8px 5px; }
table.tbl.cmp th:first-child, table.tbl.cmp td:first-child { position:static; }
table.tbl td .x { color:#44557a; cursor:pointer; }

/* calendar heat cells */
.cal { display:grid; grid-template-columns:repeat(12,1fr); gap:3px; margin-top:10px; }
.cal .c { aspect-ratio:1; border-radius:5px; background:var(--bg2); display:flex; align-items:center;
  justify-content:center; font-size:9px; color:var(--dim); }
.callabels { display:grid; grid-template-columns:repeat(12,1fr); gap:3px; margin-top:5px; }
.callabels span { text-align:center; font-size:9px; color:var(--dim); }

/* launcher tiles (More screen + home hub) */
.launch { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:14px; }
.tile { background:var(--card); border:1px solid var(--line); border-radius:16px; padding:15px 14px;
  cursor:pointer; transition:border-color .15s; }
.tile:active { border-color:var(--teal); }
.tile .ic { font-size:20px; position:relative; display:inline-block; }
/* per-tracker health dot — how that slice of your money is doing (green / amber / red) */
.tile-health { position:absolute; top:-2px; right:-9px; width:9px; height:9px; border-radius:50%; border:2px solid var(--card); }
.tile-health.good { background:var(--teal); }
.tile-health.warn { background:var(--gold); }
.tile-health.bad  { background:var(--red); }
.tile .nm { font-size:15px; font-weight:700; margin-top:8px; }
.tile .vv { font-size:12.5px; color:var(--mid); margin-top:3px; font-variant-numeric:tabular-nums; }
.tile .ds { font-size:11.5px; color:var(--dim); margin-top:2px; }
.launch .tile.lg { grid-column:1 / -1; display:flex; flex-wrap:wrap; align-items:center; column-gap:12px; }
.launch .tile.xl { grid-row:span 2; align-content:center; }
.tile.xl .ic { font-size:34px; }
.tile.xl .nm { font-size:18px; }
.tile.xl .vv { font-size:32px; }
.tile.xl .ds { font-size:13.5px; }
.tile.lg .ic { font-size:24px; }
.tile.lg .nm { font-size:15px; margin-top:0; }
.tile.lg .vv { flex-basis:100%; font-size:26px; font-weight:800; margin-top:9px; }
.tile.lg .ds { flex-basis:100%; font-size:12.5px; margin-top:3px; }
.ro-btn.sz-btn.on { background:var(--teal,#2DD4BF); color:#04121a; border-color:transparent; }
.ro-tag { font-size:10.5px; font-weight:800; text-transform:uppercase; letter-spacing:.4px; color:var(--teal,#2DD4BF); margin-left:6px; }

/* status tags */
.tag { display:inline-block; font-size:10.5px; border-radius:6px; padding:2px 7px; font-weight:700;
  letter-spacing:.4px; text-transform:uppercase; }
.tag.keep   { background:rgba(45,212,191,.12); color:var(--teal); }
.tag.cancel { background:rgba(248,113,113,.12); color:var(--red); }
.tag.trial  { background:rgba(245,158,11,.14); color:var(--gold); }
/* the status badge IS the control now — tap it to cycle Keep → Cancel → Trial (replaces the ⟳ button) */
.statuschip { border:1px solid currentColor; cursor:pointer; font:inherit; font-size:10.5px; font-weight:800; letter-spacing:.02em;
  text-transform:uppercase; display:inline-flex; align-items:center; gap:4px; line-height:1.35; }
.statuschip:active { transform:scale(.94); }
.tag.win    { background:rgba(45,212,191,.12); color:var(--teal); }
.tag.loss   { background:rgba(248,113,113,.12); color:var(--red); }
.tag.paid   { background:rgba(45,212,191,.12); color:var(--teal); }
.tag.due    { background:rgba(245,158,11,.14); color:var(--gold); }
.tag.over   { background:rgba(248,113,113,.12); color:var(--red); }
/* .tag.good was USED in three places and defined nowhere — it fell through to bare .good, which
   v2 remaps to purple, so "✓ added" read as an alert (his "still it shows that it is added"). */
.tag.good   { background:rgba(34,197,94,.14); color:#16a34a; }
body.v2 .tag.good { background:rgba(52,211,153,.14); color:#34d399; }
/* floating chat popup + minimized bubble (bottom-right, above the nav bar) */
/* household chat is a FULL-SCREEN page now (user: "household chat in full screen") — the minimize
   button still tucks it into the corner bubble */
.chatpop { position:fixed; inset:0; z-index:80;
  width:auto; height:100dvh; display:flex; flex-direction:column;
  background:var(--bg); border:none; border-radius:0;
  box-shadow:none; animation:cpop-in .32s cubic-bezier(.22,.8,.3,1); overflow:hidden;
  /* TOP inset too: targeting SDK 35+ Android enforces edge-to-edge, so inset:0 starts UNDER the
     status bar — the chat header (back, name, private pill) sat half-hidden behind the clock.
     The main app header already pads env(safe-area-inset-top); this is the same treatment. */
  padding-top:env(safe-area-inset-top, 0px);
  padding-bottom:env(safe-area-inset-bottom, 0px); }
@keyframes cpop-in { 0%{ opacity:0; transform:translateY(18px) scale(.92); } 60%{ opacity:1; transform:translateY(-4px) scale(1.01); } 100%{ opacity:1; transform:none; } }
/* photo messages: compact bubble image, tap to view full-screen */
.cmsg-img { max-width:180px; max-height:180px; border-radius:12px; display:block; cursor:pointer; }
.cp-attach { flex:0 0 auto; width:40px; border-radius:12px; background:var(--bg2); border:1px solid var(--line); color:var(--text); font-size:15px; cursor:pointer; }
.imgview { position:fixed; inset:0; z-index:120; background:rgba(2,8,16,.93); display:flex; align-items:center; justify-content:center; }
.imgview img { max-width:96vw; max-height:92vh; border-radius:10px; }
.cp-head { display:flex; align-items:center; justify-content:space-between; padding:10px 12px;
  border-bottom:1px solid var(--line); background:var(--bg2); }
.cp-title { font-size:13.5px; font-weight:800; }
.cp-btns button { width:38px; height:38px; margin-left:8px; border-radius:10px; border:1px solid var(--line);
  background:var(--card); color:var(--dim); font-size:12.5px; cursor:pointer; }
.cp-body { flex:1; overflow-y:auto; -webkit-overflow-scrolling:touch; padding:10px; display:flex; flex-direction:column; gap:8px; }
.cp-compose { display:flex; gap:8px; padding:10px; border-top:1px solid var(--line); background:var(--bg2); }
.cp-compose input { flex:1 1 auto; min-width:0; background:var(--card); border:1px solid var(--line); color:var(--text);
  border-radius:11px; padding:9px 11px; font-size:13.5px; outline:none; }
.cp-compose input:focus { border-color:var(--teal); }
.cp-compose .btn { flex:0 0 auto; width:auto; margin:0; padding-left:16px; padding-right:16px; }
.chatbub { position:fixed; right:14px; bottom:calc(78px + env(safe-area-inset-bottom, 0px)); z-index:80;
  width:52px; height:52px; border-radius:50%; border:1px solid rgba(45,212,191,.4);
  background:linear-gradient(135deg, rgba(45,212,191,.18), rgba(99,102,241,.18)), var(--card);
  font-size:22px; cursor:pointer; box-shadow:0 10px 30px rgba(0,0,0,.5); animation:tourpop .2s ease; }
.chatbub:active { transform:scale(.92); }
.chatbub { animation:bubIn .28s cubic-bezier(.2,.9,.3,1.4); }
@keyframes bubIn { from { transform:scale(0); opacity:0; } to { transform:scale(1); opacity:1; } }
body.kb-open .chatpop { bottom:8px; }   /* keyboard open: hug the bottom like the nav does */
/* unread count pill inside the ☰ menu's Notifications row */
.menu-count { display:inline-block; min-width:18px; padding:1px 6px; border-radius:9px; background:var(--red);
  color:#fff; font-size:11.5px; font-weight:800; text-align:center; margin-left:6px; vertical-align:1px; }
/* ⓘ member-info button — the 💬 beside it is a 38px teal-tinted circle and this was a 28px dark
   square, so the pair looked like two different apps ("the chat icon which appears beside ⓘ —
   designs are not same"). Same geometry now, sibling tints: teal chat, blue info. */
.mi-info { width:34px; height:34px; border-radius:50%; border:1px solid rgba(56,189,248,.40); background:rgba(56,189,248,.12);
  color:var(--blue); font-size:15px; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; }
.mi-info:active { transform:scale(.92); }
/* the kids report-card info button is small + inline so it doesn't tower over the header text */
#rc-info { width:20px; height:20px; border-radius:6px; font-size:11.5px; vertical-align:middle; }
/* goal pace / at-risk line in the Goals hub */
.grow-risk { font-size:12.5px; line-height:1.45; margin-top:7px; overflow-wrap:anywhere; }
/* subscription price-hike chip */
.hike-chip { display:inline-block; font-size:11.5px; font-weight:800; color:var(--gold);
  background:rgba(245,158,11,.12); border:1px solid rgba(245,158,11,.32); border-radius:6px; padding:1px 6px; white-space:nowrap; }
.hike-chip[data-phist] { cursor:pointer; text-decoration:underline dotted; text-underline-offset:2px; }   /* tappable → price history */
/* Split's activity log — a quiet pill. It used to borrow .hike-chip, which is a loud gold ALERT
   badge for subscription price rises; at the top of the card it read as a warning and sat too
   heavy. Its own class so toning it down here never quietens a real price-hike warning. */
.log-pill{float:right;display:inline-flex;align-items:center;gap:4px;font-size:10.5px;font-weight:700;
  letter-spacing:.2px;color:var(--dim);background:rgba(255,255,255,.05);border:1px solid var(--line);
  border-radius:999px;padding:2px 9px;white-space:nowrap;cursor:pointer;margin-top:2px}
.log-pill:hover{color:var(--mid);background:rgba(255,255,255,.09)}
/* paid-off debts — a proud, cleared-out section rather than a bare red $0 */
.cleared-head { margin:16px 0 4px; font-size:11.5px; font-weight:800; letter-spacing:1px; text-transform:uppercase; color:var(--teal); }
.row.debt-done { opacity:.78; }
.row.debt-done .t { text-decoration:none; }

/* segmented sub-nav that can hold 2-3 */
.subnav { display:flex; gap:6px; margin:2px 0 0; }
.subnav button { flex:1; background:var(--card); border:1px solid var(--line); color:var(--dim);
  padding:9px; border-radius:11px; font-size:13.5px; cursor:pointer; font-weight:600; }
.subnav button.on { background:rgba(45,212,191,.14); color:var(--teal); border-color:rgba(45,212,191,.3); }

/* small inline label + edit affordance */
.editrow { display:flex; align-items:center; justify-content:space-between; margin-top:10px; gap:10px; }
.editrow .lab { font-size:12.5px; color:var(--dim); min-width:0; }
.editrow .btn { margin-top:0; width:auto; flex:0 0 auto; white-space:nowrap; }   /* keep the toggle/Edit button from ballooning full-width */
.miniform { display:flex; gap:8px; align-items:center; }
.miniform input { width:110px; background:var(--bg2); border:1px solid var(--line); color:var(--text);
  border-radius:10px; padding:9px 11px; font-size:15px; text-align:right; }

/* pill list (milestones) */
.mile { display:flex; align-items:center; gap:10px; padding:9px 2px; border-bottom:1px solid rgba(30,42,68,.5); }
.mile:last-child { border-bottom:none; }
.mile .dot { width:9px; height:9px; border-radius:50%; background:var(--line); flex-shrink:0; }
.mile .dot.on { background:var(--teal); box-shadow:0 0 8px rgba(45,212,191,.6); }
.mile .mt { font-size:13.5px; font-weight:600; flex:1; }
.mile .mv { font-size:12.5px; color:var(--dim); font-variant-numeric:tabular-nums; }

/* two-column compact facts */
.facts { margin-top:8px; }
.facts .f { display:flex; justify-content:space-between; align-items:baseline; gap:14px; padding:7px 0; border-bottom:1px solid rgba(30,42,68,.45); font-size:13.5px; }
.facts .f:last-child { border-bottom:none; }
/* ".amount coming out" - the LABEL was the thing refusing to shrink, so "FI number (annual
   expenses x 25)" held its full width and pushed "C$1,200,000" out through the side of the card.
   Backwards: the label can wrap, the number cannot be cut. */
.facts .f .fk { color:var(--dim); min-width:0; overflow-wrap:break-word; }
.facts .f .fv { font-weight:700; font-variant-numeric:tabular-nums; text-align:right; flex:0 0 auto; white-space:nowrap; }
.fx-live { font-size:11.5px; font-weight:800; color:#34D399; background:rgba(52,211,153,.14); padding:2px 8px; border-radius:999px; vertical-align:middle; margin-left:4px; }
.cat-guess { font-size:12.5px; color:var(--dim); margin-top:7px; }
.cat-guess b { color:var(--text); }
.cat-guess .cg-use { color:var(--teal,#2DD4BF); font-weight:800; cursor:pointer; text-decoration:underline; }
.fam-chat { display:flex; flex-direction:column; gap:9px; max-height:300px; overflow-y:auto; margin:6px 0 12px; }
.fmsg { max-width:82%; align-self:flex-start; }
.fmsg.mine { align-self:flex-end; }
.fmsg-b { display:inline-block; padding:8px 12px; border-radius:14px; background:var(--chip,rgba(255,255,255,.06)); border:1px solid var(--line,rgba(255,255,255,.06)); font-size:13.5px; line-height:1.4; text-align:left; word-break:break-word; }
.fmsg.mine .fmsg-b { background:linear-gradient(135deg,#2DD4BF,#6366F1); border-color:transparent; color:#04121a; }
.fmsg-m { font-size:10.5px; color:var(--dim); margin-top:3px; }
.fmsg.mine .fmsg-m { text-align:right; }
.fam-compose { display:flex; gap:8px; align-items:stretch; }
.fam-compose input { flex:1 1 auto; min-width:0; background:var(--bg2,#0e1524); border:1px solid var(--line,rgba(255,255,255,.12)); color:var(--text,#fff); border-radius:12px; padding:10px 12px; font-size:13.5px; }
.fam-compose .btn { flex:0 0 auto; width:auto; margin:0; padding-left:20px; padding-right:20px; }

.hint { font-size:11.5px; color:var(--dim); margin-top:6px; line-height:1.5; }
.rowbtns { display:flex; gap:8px; }
.section-h { font-size:13.5px; color:var(--dim); text-transform:uppercase; letter-spacing:1.4px;
  font-weight:600; margin:22px 4px 0; }
/* More menu: search bar + grouped sections + tap-list rows */
.more-search { margin:8px 0 2px; }
.more-search .askin { width:100%; }
.more-sec { margin-bottom:4px; }
/* Settings used to sit here as a full row, duplicating the top-right menu. Now just a quiet
   build stamp — still tells support which version they are on, without offering a second door. */
.more-build { margin:14px 2px 4px; font-size:12.5px; color:var(--dim); text-align:center; line-height:1.5; }
.more-list { margin-top:10px; background:var(--card); border:1px solid var(--line); border-radius:16px; overflow:hidden; }
.more-row { display:flex; align-items:center; gap:12px; width:100%; padding:13px 14px; cursor:pointer;
  background:none; border:none; color:var(--text); font:inherit; text-align:left; }
.more-row:not(:last-child) { border-bottom:1px solid rgba(30,42,68,.55); }
.more-row:active { background:rgba(45,212,191,.06); }
.more-row .mr-ic { flex:0 0 auto; width:34px; height:34px; border-radius:10px; display:flex; align-items:center; justify-content:center; font-size:17px; background:var(--bg2); border:1px solid var(--line); }
.more-row .mr-body { flex:1; min-width:0; display:flex; flex-direction:column; gap:1px; }
.more-row .mr-name { font-size:15px; font-weight:700; }
.more-row .mr-sub { font-size:12.5px; color:var(--dim); overflow-wrap:anywhere; }
.more-row .ar { color:var(--dim); font-size:18px; flex:0 0 auto; }
.more-row.trk-on .ar.plus { color:var(--teal); font-weight:700; font-size:20px; }
.more-row.trk-on { opacity:.92; }

/* help & legal */
.legal-links { display:flex; flex-wrap:wrap; gap:8px; margin-top:12px; }

/* Muse Nexus Pro upsell */
.preview-card { border:1px solid rgba(139,140,248,.35); background:linear-gradient(135deg,rgba(45,212,191,.08),rgba(139,140,248,.12)); }
.pv-badge { font-size:11.5px; font-weight:800; text-transform:uppercase; letter-spacing:.5px; color:#04121a; background:linear-gradient(135deg,#2DD4BF,#8B8CF8); padding:3px 10px; border-radius:999px; }
.pv-txt { font-size:13.5px; line-height:1.5; color:var(--text); margin:10px 0 12px; }
.plan-list { display:flex; flex-direction:column; gap:11px; margin:14px 0 6px; }
.plan-card{position:relative}
.plan-card.featured{border-color:var(--teal);box-shadow:0 0 0 1px var(--teal),0 10px 30px rgba(45,212,191,.12)}
.plan-badge{position:absolute;top:-9px;left:16px;background:var(--grad);color:#03121a;font-size:10.5px;font-weight:800;letter-spacing:.4px;padding:3px 10px;border-radius:999px}
.plan-card { border:1px solid var(--line,rgba(255,255,255,.1)); border-radius:16px; padding:14px; background:var(--card,rgba(255,255,255,.02)); }
.plan-top { display:flex; justify-content:space-between; align-items:flex-start; gap:12px; }
.plan-name { font-size:15px; font-weight:800; }
.plan-blurb { font-size:12.5px; color:var(--dim); margin-top:2px; }
.plan-price { font-size:19px; font-weight:800; white-space:nowrap; }
.plan-per { font-size:12.5px; font-weight:600; color:var(--dim); }
.plan-seats { font-size:11.5px; font-weight:700; color:var(--teal,#2DD4BF); background:rgba(45,212,191,.12); display:inline-block; padding:3px 10px; border-radius:999px; margin:9px 0 11px; }
.plan-choose { width:100%; }
.pro-list { margin:14px 0 4px; }
.pro-feat { display:flex; gap:12px; align-items:flex-start; padding:10px 0; border-bottom:1px solid var(--line); }
.pro-feat:last-child { border-bottom:0; }
.pf-ic { font-size:20px; line-height:1.3; flex-shrink:0; }
.pro-feat b { font-size:13.5px; }
.pf-d { font-size:12.5px; color:var(--dim); line-height:1.5; margin-top:2px; }

/* settings tabs */
.settabs { display:flex; gap:6px; background:var(--bg2); border:1px solid var(--line); border-radius:12px; padding:4px; margin-bottom:16px; }
.settab { flex:1; background:none; border:none; color:var(--dim); font-size:12.5px; font-weight:700; padding:9px 6px; border-radius:9px; cursor:pointer; transition:background .12s, color .12s; }
.settab.on { background:var(--card); color:var(--text); box-shadow:0 1px 3px rgba(4,7,15,.25); }
/* budget sub-tabs (Overview / Bills & Expenses / Advanced) */
.subtabs { display:flex; gap:6px; background:var(--bg2); border:1px solid var(--line); border-radius:13px; padding:4px 5px; margin:0 0 14px; }
/* text needs room INSIDE its pill — with only 4px a long label ran right up to the active
   (gradient) pill next door and the two read as one smudged block on narrow phones. */
/* Wraps, never ellipsizes — same rule as .bh-eq .k above: a tab label that reads
   "Subscripti…" is worse than one on two lines. It clipped 21px off "Subscriptions"
   at larger text sizes (his: "dont cutt off text / wrap it"). */
.subtab { flex:1; min-width:0; background:none; border:none; color:var(--dim); font-size:12.5px; font-weight:700; padding:10px 7px; border-radius:10px; cursor:pointer; white-space:normal; overflow-wrap:anywhere; hyphens:auto; line-height:1.25; text-align:center; transition:background .12s, color .12s; }
.subtab.on { background:var(--grad); color:#06111c; box-shadow:0 1px 4px rgba(45,212,191,.25); }
@media (max-width:400px) { .subtab { font-size:11.5px; padding:10px 6px; letter-spacing:-.1px; } }
@media (max-width:340px) { .subtab { font-size:10.5px; padding:9px 4px; } }
.pro-feat { font-size:13.5px; line-height:1.55; color:var(--mid); padding:9px 0; border-top:1px solid var(--line); }
.pro-feat:first-of-type { border-top:none; }
.pro-feat b { color:var(--text); }

/* cloud account */
.auth-links { display:flex; gap:18px; justify-content:center; margin-top:16px; flex-wrap:wrap; }
.auth-links a { color:var(--teal); font-size:13.5px; font-weight:600; cursor:pointer; }
.auth-links a:hover { text-decoration:underline; }
.acc-email { background:var(--bg2); border:1px solid var(--line); border-radius:12px; padding:12px 13px; font-size:15px; color:var(--text); word-break:break-all; }
.legal-item { padding:12px 0; border-bottom:1px solid var(--line); }
.legal-item:last-child { border-bottom:0; }
.legal-item b { display:block; font-size:13.5px; color:var(--text); margin-bottom:4px; }
.legal-item > div { font-size:13.5px; color:var(--dim); line-height:1.6; }

/* top-bar button group */
.topbtns { flex:1 1 0; display:flex; gap:8px; align-items:center; justify-content:flex-end; }

/* home financial-health card */
.hcard { display:flex; gap:16px; align-items:center; cursor:pointer; }
.hleft { text-align:center; flex-shrink:0; min-width:72px; }
/* score ring — the number sits inside a progress donut tinted by tone */
.hring { position:relative; width:72px; height:72px; margin:0 auto; }
.hring svg { width:72px; height:72px; transform:rotate(-90deg); display:block; }
.hr-bg { fill:none; stroke:var(--bg2); stroke-width:6; }
.hr-fg { fill:none; stroke-width:6; stroke-linecap:round; }
.hring.good .hr-fg { stroke:var(--teal); } .hring.gold .hr-fg { stroke:var(--gold); } .hring.bad .hr-fg { stroke:var(--red); }
.hr-num { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-size:23px; font-weight:800; font-variant-numeric:tabular-nums; }
.hring.good .hr-num { color:var(--teal); } .hring.gold .hr-num { color:var(--gold); } .hring.bad .hr-num { color:var(--red); }
.hgrade { font-size:10.5px; color:var(--dim); text-transform:uppercase; letter-spacing:1px; margin-top:6px; }
.hverd { font-size:13.5px; color:var(--mid); line-height:1.5; }
.hfocus { font-size:12.5px; color:var(--mid); line-height:1.45; margin-top:8px; padding:7px 10px; border-radius:9px; background:var(--bg2); border:1px solid var(--line); }
.hfocus b { color:var(--gold); font-weight:700; }
.hlink { font-size:12.5px; color:var(--teal); margin-top:8px; font-weight:700; }

/* health gauge */
.gaugerow { display:flex; gap:14px; align-items:center; }
.gauge { position:relative; width:148px; height:126px; flex-shrink:0; }
.gauge canvas { width:100%; height:100%; }
.gauge-c { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; }
.gs { font-size:38px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums; }
.gs.g3 { font-size:30px; letter-spacing:-1px; }   /* a 3-digit score (100) fits inside the ring without touching it */
.gg { font-size:10.5px; color:var(--dim); margin-top:3px; text-transform:uppercase; letter-spacing:1px; }
.gaugemeta { flex:1; }

/* cashflow stacked bar */
.flowbar { display:flex; height:26px; border-radius:8px; overflow:hidden; margin-top:6px; background:var(--bg2); }
.flowseg { height:100%; }
.legend { display:flex; flex-wrap:wrap; gap:9px 14px; margin-top:11px; }
.legend.col { flex-direction:column; gap:9px; flex:1; }
.lg { display:flex; align-items:center; gap:7px; font-size:12.5px; color:var(--mid); }
.lg i { width:10px; height:10px; border-radius:3px; flex-shrink:0; }
.lg .dim { margin-left:auto; }

/* spending donut */
.donutwrap { display:flex; align-items:center; gap:16px; margin-top:6px; }
.donutwrap canvas { width:128px; height:128px; flex-shrink:0; }

/* self-explaining subheads under section titles */
.subhead { font-size:12.5px; color:var(--dim); line-height:1.5; margin:-4px 0 2px; }
.infobtn { border:none; background:rgba(45,212,191,.14); color:var(--teal); width:22px; height:22px; border-radius:50%; font-size:13.5px; cursor:pointer; vertical-align:middle; margin-left:6px; line-height:1; padding:0; }
.infobtn:active { transform:scale(.9); }
/* Same reasoning as .ibtn: the circle stays 22px so it reads as a quiet mark next to a heading, but
   a 22px TARGET is well under any touch guidance. The ::after grows what a thumb can hit to 36px
   without changing a single visible pixel. */
.infobtn { position:relative; }
.infobtn::after { content:""; position:absolute; top:50%; left:50%; width:36px; height:36px;
  transform:translate(-50%,-50%); border-radius:50%; }
.insight-card .coach-main { font-size:13.5px; line-height:1.55; color:var(--text); }
.chipwrap { display:flex; flex-wrap:wrap; gap:8px; }
.chip.qsub { cursor:pointer; }
.inforow { display:flex; gap:10px; padding:8px 0; border-bottom:1px solid var(--line); font-size:13.5px; line-height:1.5; }
.inforow:last-child { border-bottom:none; }
.inforow .il { flex:0 0 92px; font-weight:700; color:var(--text); }
.inforow .ir { flex:1; color:var(--mid); }
.sep { border:none; border-top:1px solid var(--line); margin:8px 0; }

/* home greeting */
.greeting { font-size:18px; font-weight:700; color:var(--text); margin:6px 2px 2px; letter-spacing:.2px; }
.greeting .wave { font-weight:400; }

/* install banner */
.install-row { display:flex; gap:12px; align-items:center; }
.install-row .ai { font-size:22px; flex-shrink:0; }

/* list search box */
.search { width:100%; background:var(--bg2); border:1px solid var(--line); color:var(--text);
  border-radius:11px; padding:9px 12px; font-size:13.5px; margin:2px 0 6px; outline:none; }
.search:focus { border-color:var(--teal); }

/* month navigator */
/* one-line expense entry on Budget — log a coffee without opening a form */
.qline { display:flex; gap:8px; align-items:stretch; margin-top:12px; }
.qline .qline-amt { flex:1 1 90px; min-width:0; background:var(--bg2); border:1px solid var(--line);
  color:var(--text); border-radius:11px; padding:10px 12px; font-size:17px; font-weight:800; outline:none;
  font-variant-numeric:tabular-nums; }
.qline .qline-amt:focus { border-color:var(--teal); }
.qline .qline-amt.err { border-color:var(--red); background:rgba(248,113,113,.06); }
.qline .qline-cat { flex:1 1 110px; min-width:0; background:var(--bg2); border:1px solid var(--line);
  color:var(--text); border-radius:11px; padding:10px 8px; font-size:13.5px; outline:none; }
.qline .qline-go { flex:0 0 auto; width:auto; margin-top:0; padding:10px 16px; white-space:nowrap; }
.monthnav { display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:4px; }
.monthnav button { background:var(--card); border:1px solid var(--line); color:var(--mid);
  width:34px; height:34px; border-radius:10px; font-size:15px; cursor:pointer; flex-shrink:0; }
.monthnav .mlabel { flex:1; text-align:center; font-weight:700; font-size:15px; }
.monthnav .mnow { font-size:10.5px; color:var(--teal); font-weight:700; text-transform:uppercase; letter-spacing:.5px;
  background:rgba(45,212,191,.12); border:1px solid rgba(45,212,191,.35); border-radius:7px; padding:2px 7px; margin-left:6px; }
/* .btn.small outranked the old .backnow selector, so the compact pill styles never applied */
.btn.small.backnow { margin:8px 0 14px !important; width:auto; display:inline-block; padding:4px 11px; font-size:11.5px; border-radius:999px; }
.subtabs { position:relative; z-index:46; }
/* bill paid toggle — clear labelled state */
.billrow .t { white-space:normal; }               /* bill names wrap instead of truncating to "Rent …" */
.billrow .r { display:flex; align-items:center; gap:8px; }
.bamt { color:var(--text); font-weight:800; }     /* amount now lives in the meta line, freeing the row */
.paybtn { background:var(--card); border:1px solid var(--line); color:var(--mid); border-radius:9px;
  padding:6px 11px; font-size:12.5px; font-weight:700; cursor:pointer; white-space:nowrap;
  transition:transform .1s ease, background .12s, border-color .12s, color .12s; }
.paybtn:active { transform:scale(.95); }
.paybtn.on { background:rgba(45,212,191,.16); border-color:rgba(45,212,191,.5); color:var(--teal); }
.paybtn.gold { background:rgba(245,158,11,.16); border-color:rgba(245,158,11,.5); color:var(--gold); }   /* compact ✓ Log on pending dividends */
.monthnav .mtoday { font-size:11.5px; color:var(--teal); cursor:pointer; background:rgba(45,212,191,.12);
  border:1px solid rgba(45,212,191,.4); border-radius:8px; padding:3px 8px; font-weight:700; margin-left:4px; }
.monthnav button.off { opacity:.3; }

/* signup / onboarding wizard */
/* THE STRIP OF SCREEN THE PHONE WILL NOT LET ANYTHING BE DRAWN INTO, named once.
 *
 * env() cannot be faked from script, so a safe-area reservation written inline is a reservation
 * no test can prove — which is exactly how this one got deleted twice (see the note on #onboard's
 * short-screen padding below). Behind a custom property a test can substitute a real gesture-bar
 * value and watch the wizard move, so a revert to a flat padding fails a check instead of shipping.
 *
 * It is divided by --zoom at every USE site and never here: env() is a device length, and inside
 * #app's zoomed subtree one CSS px is bigger than one device px by exactly that factor. Same
 * correction, same reason, as body.v2 .modal at :3847. */
:root { --sab: env(safe-area-inset-bottom, 0px); }
#onboard { position:fixed; inset:0; z-index:200; display:none; align-items:flex-start; justify-content:center;
  padding:20px; overflow-y:auto;
  /* THE WIZARD'S OWN COORDINATE SPACE, MADE QUERYABLE.
   *
   * app.js:9723 sets #app.style.zoom = fontScale() and the shipped default is 1.10, so on a
   * 360x740 phone this overlay — which is position:fixed inset:0 inside #app — is 740 DEVICE px
   * tall and 740/1.10 = 673 LAYOUT px tall. Every @media (max-height:...) in this file tests the
   * 740, and the wizard is laid out in the 673. At the top of the slider it is laid out in 462
   * and the media query still says 740. That is why one notch up from the default (the slider
   * steps 5%) took steps 3 and 4 from fitting to 22 and 16px of scroll and two notches took them
   * to 83 and 44 — measured, layout px: the compaction rules simply never noticed.
   *
   * container-type:size makes this element a size query container, and a @container query on it
   * is answered in ITS coordinate space — the 673, the 462 — which is the space the card is
   * actually laid out in. No JS, no --zoom arithmetic, no specificity change (@container adds
   * none, exactly like @media), so the delicate source-order fights this file documents are all
   * preserved. The tiers are at the end of the file. */
  container-type: size; container-name: ob;
  background:
    radial-gradient(900px 600px at 85% -10%, rgba(56,189,248,.14), transparent 60%),
    radial-gradient(760px 500px at -10% 108%, rgba(45,212,191,.11), transparent 55%),
    linear-gradient(165deg,var(--bg) 0%,var(--bg2) 100%); }
#onboard.show { display:flex; }
.ob-card { width:100%; max-width:440px; background:var(--card); border:1px solid var(--line); border-radius:22px;
  padding:20px 20px calc(env(safe-area-inset-bottom, 0px) + 16px); animation:up .22s ease-out; box-shadow:0 20px 60px rgba(4,7,15,.4);
  /* auto margins (not align-items:center) so tall phones center the card but an overflowing
     card still scrolls from its top — S25 Ultra showed everything crammed up with a dead band below */
  margin-top:auto; margin-bottom:auto; }
.ob-brand { font-size:15px; letter-spacing:3px; text-align:center; margin-bottom:10px; }
/* brand above product, same lockup as the splash — onboarding used to say only MUSE NEXUS,
   and the product being signed up for never appeared ("should also say Money HQ") */
.ob-eyebrow { font-size:9px; letter-spacing:4px; color:var(--dim); font-weight:600; margin-bottom:4px; }
.ob-brand .m { font-weight:300; color:var(--text); }
.ob-brand .n { font-weight:800; background:var(--grad-text); -webkit-background-clip:text; background-clip:text; color:transparent; }
.ob-dots { display:flex; gap:6px; justify-content:center; margin-bottom:12px; }
.ob-dots span { flex:1 1 0; max-width:40px; height:4px; border-radius:99px; background:var(--line); transition:.25s; }
.ob-dots span.on { background:var(--grad); box-shadow:0 0 10px -2px var(--teal); }
.ob-dots span.done { background:var(--teal); }
/* his .land-brand: the STEP N OF 4 label above each onboarding title */
.ob-step { font-size:10px; font-weight:700; letter-spacing:3px; text-transform:uppercase;
  color:var(--quiet); margin-bottom:5px; }
.ob-h { font-size:19px; font-weight:800; line-height:1.18; text-wrap:balance; margin-bottom:7px; }
.ob-p { font-size:12.5px; color:var(--dim); line-height:1.45; margin-bottom:10px; }
/* .ob-hint WAS NEVER STYLED AT ALL — used seven times across the wizard and matching no rule in
   this file, so every one of them inherited the 16px body size and rendered as large as the
   headings. That is what he screenshotted and called "stretched": the invite-code explainer, the
   kid-profiles note and the sample-data note all shouting at body size in a column of small print.
   It is hint text and it reads like .ob-p, so it IS .ob-p with a little more room. */
.ob-hint { font-size:12.5px; color:var(--dim); line-height:1.5; margin-bottom:10px; }

/* SHORT SCREENS. Measured on his phone size (360x740): the household step rendered 788px of card
   into 673px of room — 83px of scroll on a page whose whole job is "read this and tap one of two
   buttons". "squeezed pages, have to scroll" is the report, and shrinking type alone did not fix
   it because the height is in the GAPS, not the words: the invite card alone was 154px.
   Below 780px tall the wizard tightens instead of scrolling. Nothing is removed — every sentence
   and every control is still there, sitting closer together, which is what a short screen wants. */
@media (max-height: 780px) {
  /* THE FRAME, AND THE GESTURE BAR.  (the safe-area half of this)
   *
   * .ob-card's own padding-bottom used to carry calc(env(safe-area-inset-bottom) + 16px) — see
   * :1864 — and three successive short-screen passes overrode it with flat values (padding-bottom
   * :14px, then padding:12px 14px !important), each one dropping the env() term without noticing.
   * Measured: at 800px tall the query is off and the inset is present; at 780 and at 740 the card
   * ends a flat 12px above the physical edge, so on exactly the phones this block exists for, the
   * Create button sat under the gesture bar.
   *
   * The reserve moves here, to the SCROLL CONTAINER, and that is the actual fix rather than a
   * fourth override: #onboard's padding is set in one place, it is the box whose bottom edge IS
   * the bottom of the screen, and no amount of card-padding rebalancing downstream can delete it
   * again. Divided by --zoom because env() is a device length and this is inside #app's zoomed
   * subtree (same correction as body.v2 .modal at :3847).
   *
   * It pays for itself: 20px top + 20px bottom of frame becomes 4 + 4, which hands 32 layout px
   * back to every step. On a phone with a 34px gesture bar the net is 4 + 4 + 31 = 39 against the
   * old 40 — no worse than today on room, and the last control now clears the bar. */
  #onboard { padding: 4px 20px calc(4px + var(--sab, 0px) / var(--zoom, 1)); }
  .ob-card { padding-top:14px; padding-bottom:14px; }
  .ob-brand { margin-bottom:8px; transform:scale(.92); transform-origin:top center; }
  .ob-h { font-size:18px; margin-bottom:4px; }
  .ob-p { margin-bottom:7px; }
  .ob-hint { margin-bottom:7px; line-height:1.45; }
  .ob-card .card { padding:12px 12px; }
  .ob-row, .ob-selrow { margin-top:8px; }
  .ob-or { margin:8px 0; }
  .ob-card .btn { padding-top:11px; padding-bottom:11px; }

}
.ob-hint b { color:var(--text); font-weight:700; }
.ob-row { display:flex; gap:10px; margin-top:12px; }
.ob-acct { margin-top:5px; padding-top:8px; border-top:1px dashed var(--line); }
/* profile step must FIT with no scroll (user request): tighter fields inside onboarding only;
   inputs stay 16px — smaller triggers iOS zoom-on-focus */
#onboard .field { margin-top:6px; }
#onboard .field label { font-size:9px; letter-spacing:.6px; margin-bottom:2px; }
#onboard .field input, #onboard .field select { padding:6px 10px; border-radius:10px; font-size:13.5px; }
/* first-run currency chips. Same .chip as Preferences, sized for the wizard card and without the
   ✓ prefix — the tick reflows the row on every pick, and .prefs-tight drops it for the same reason.
   NOT .v2chips: that rule is gated on body.v2, which onboarding paints before the class lands. */
.ob-curchips { display:flex; flex-wrap:wrap; gap:6px; }
.ob-curchips .chip { padding:6px 10px; font-size:12px; cursor:pointer; }
.ob-curchips .chip.on::before { content:none; }
/* the "or" rule between signing up with Google and filling the form in by hand */
.ob-or { display:flex; align-items:center; gap:10px; margin:14px 2px 10px; color:var(--dim); font-size:11.5px; }
.ob-or::before, .ob-or::after { content:""; flex:1; height:1px; background:var(--line); }
.ob-req { display:flex; gap:14px; font-size:10.5px; font-weight:700; color:var(--dim); margin:4px 2px 0; }
.ob-req span.ok { color:var(--teal); }
/* trackers-page search: placeholder was clipping at 16px (user report) */
.more-search .askin { font-size:13.5px; }
.more-search .askin::placeholder { font-size:13.5px; letter-spacing:.1px; }
.ob-row .btn { flex:1; margin-top:0; }
/* guest / profile mode choice cards */
.ob-mode { display:block; width:100%; text-align:left; background:var(--bg2); border:1px solid var(--line);
  border-radius:16px; padding:16px; margin-top:12px; cursor:pointer; transition:border-color .12s, transform .1s ease; }
.ob-mode:active { transform:scale(.98); }
.ob-mode:hover { border-color:rgba(45,212,191,.5); }
.ob-signin { text-align:center; margin-top:15px; color:var(--dim); font-size:13.5px;
  display:flex; align-items:center; justify-content:center; gap:7px; }
.ob-signin .obs-q { font-size:13.5px; }
.ob-signin a { color:var(--teal); font-weight:800; cursor:pointer; }
.ob-signin .ob-spark { color:var(--teal); opacity:.7; }
.obm-ic { font-size:24px; line-height:1; }
.obm-t { font-size:15px; font-weight:800; color:var(--text); margin-top:8px; }
.obm-d { font-size:12.5px; color:var(--dim); line-height:1.5; margin-top:4px; }
/* premium glass landing (step 1): ambient orbs + neon tier cards + line-art icons */
.ob-card--landing { position:relative; overflow:hidden; }
.ob-card--landing > *:not(.ob-orb) { position:relative; z-index:1; }
.ob-orb { position:absolute; z-index:0; border-radius:50%; filter:blur(48px); opacity:.55; pointer-events:none; }
.ob-orb-1 { width:190px; height:190px; background:rgba(45,212,191,.55); top:-40px; left:-45px; }
.ob-orb-2 { width:170px; height:170px; background:rgba(244,114,182,.42); top:46%; right:-60px; }
.ob-orb-3 { width:150px; height:150px; background:rgba(56,189,248,.40); bottom:6%; left:-40px; }
/* the product wordmark on the landing step — small MUSE NEXUS brand above, big MONEY HQ below,
   the same lockup as the splash, emails and launcher ("first page didn't match the flow") */
.ob-wordmark { text-align:center; font-size:27px; font-weight:300; letter-spacing:5px; color:var(--text); margin:2px 0 12px; }
.ob-wordmark span { font-weight:800; background:var(--grad-text); -webkit-background-clip:text; background-clip:text; color:transparent; }
.ob-h-landing { font-size:21px; font-weight:800; letter-spacing:.2px; line-height:1.22;
  text-wrap:balance; margin-bottom:6px; }
.ob-seclabel { display:flex; align-items:center; gap:11px; margin:13px 0 10px; }
/* the first screen must FIT — trim the frame on shorter phones so nothing needs a scroll */
@media (max-height:760px) {
  .ob-card--landing { padding-top:18px; }
  .ob-card--landing .ob-brand { margin-bottom:10px; }
  .ob-card--landing .ob-dots { margin-bottom:12px; }
  .ob-h-landing { font-size:19px; }
  .ob-card--landing .ob-p { margin-bottom:8px; font-size:13.5px; }
  .ob-guestbtn { margin-top:12px; padding:13px; }
  .ob-card--landing .ob-signin { margin-top:10px; }
}
.ob-seclabel::after { content:""; flex:1; height:1px; background:linear-gradient(90deg,var(--line),transparent); }
.ob-seclabel span { font-size:11.5px; font-weight:800; letter-spacing:1.5px; text-transform:uppercase; color:var(--dim); }
.ob-tiers { display:flex; flex-direction:column; gap:10px; }
/* Compact horizontal tier rows — the tall centered cards pushed Family + the buttons below the fold
   (user: "don't let user scroll on first page — fit in one screen"). Icon left, words right. */
.ob-tier { display:grid; grid-template-columns:42px 1fr; grid-template-rows:auto auto; column-gap:14px;
  align-items:center; position:relative; width:100%; text-align:left; cursor:pointer;
  background:var(--bg2);
  border:1px solid var(--line); border-left:3px solid var(--tierbd); border-radius:17px; padding:12px 15px;
  transition:transform .14s ease, border-color .22s ease; }
.ob-tier:hover { transform:translateY(-2px); border-color:var(--tierbd); }
.ob-tier:active { transform:scale(.985); }
.ot-sel { position:absolute; top:13px; right:13px; width:15px; height:15px; border-radius:50%;
  border:2px solid var(--tierbd); }
.ot-ic { grid-row:1 / span 2; display:block; width:38px; height:38px; margin:0; color:var(--tier);
 }
.ot-ic svg { width:100%; height:100%; display:block; }
.ot-t { grid-column:2; display:block; font-size:15px; font-weight:800; letter-spacing:1px; text-transform:uppercase; color:var(--text); padding-right:20px; }
.ot-d { grid-column:2; display:block; font-size:12.5px; color:var(--dim); line-height:1.45; margin-top:2px; }
.ot-d b { color:var(--mid); font-weight:800; text-transform:uppercase; letter-spacing:.4px; }
.ob-guestbtn { display:block; width:100%; margin-top:20px; border:none; cursor:pointer;
  background:var(--grad); color:#06131a; font-size:15px; font-weight:800; letter-spacing:.5px; text-transform:uppercase;
  border-radius:99px; padding:15px; box-shadow:0 8px 22px -10px rgba(45,212,191,.5);
  transition:transform .12s ease, box-shadow .2s ease; }
/* extra-tall screens (S25 Ultra & co): let the landing breathe instead of huddling at the top.
   Must sit BELOW the base rules it overrides — same specificity, cascade order decides. */
@media (min-height:850px) {
  .ob-card { padding:26px 22px calc(env(safe-area-inset-bottom, 0px) + 22px); }
  .ob-h-landing { font-size:24px; }
  .ob-card--landing .ob-p { font-size:13.5px; }
  .ob-seclabel { margin:17px 0 13px; }
  .ob-tiers { gap:13px; }
  .ob-tier { padding:15px 16px; }
  .ot-t { font-size:15px; }
  .ot-d { font-size:13.5px; }
  .ob-guestbtn { margin-top:24px; padding:17px; font-size:15px; }
  .ob-card--landing .ob-signin { margin-top:18px; }
}
.ob-guestbtn:hover { box-shadow:0 0 34px -2px rgba(45,212,191,.72); }
.ob-guestbtn:active { transform:scale(.98); }
.prof-badge { display:inline-block; font-size:12.5px; font-weight:800; padding:6px 12px; border-radius:99px;
  background:var(--bg2); border:1px solid var(--line); color:var(--mid); margin-bottom:12px; }
.prof-badge.pro { background:rgba(45,212,191,.12); border-color:rgba(45,212,191,.4); color:var(--teal); }

/* privacy passcode lock */
#lock { position:fixed; inset:0; z-index:300; display:none; align-items:center; justify-content:center; padding:20px;
  background:
    radial-gradient(900px 600px at 85% -10%, rgba(56,189,248,.14), transparent 60%),
    radial-gradient(760px 500px at -10% 108%, rgba(45,212,191,.11), transparent 55%),
    linear-gradient(165deg,var(--bg) 0%,var(--bg2) 100%); }
#lock.show { display:flex; }
.lock-card { width:100%; max-width:360px; background:var(--card); border:1px solid var(--line); border-radius:22px;
  padding:34px 24px calc(env(safe-area-inset-bottom, 0px) + 28px); text-align:center;
  animation:up .22s ease-out; box-shadow:0 20px 60px rgba(4,7,15,.4); }
.lock-brand { font-size:18px; letter-spacing:3px; margin-bottom:6px; }
.lock-brand .m { font-weight:300; color:var(--text); }
.lock-brand .n { font-weight:800; background:var(--grad-text); -webkit-background-clip:text; background-clip:text; color:transparent; }
.lock-sub { font-size:13.5px; color:var(--dim); margin-bottom:20px; }
.lock-input { width:100%; background:var(--bg2); border:1px solid var(--line); color:var(--text);
  border-radius:12px; padding:14px; font-size:20px; text-align:center; letter-spacing:6px; }
.lock-input:focus { outline:none; border-color:var(--teal); }
.lock-err { min-height:18px; font-size:12.5px; color:#F87171; margin:10px 0 4px; }
.lock-card.shake { animation:shake .32s; }
@keyframes shake { 10%,90%{transform:translateX(-2px);} 30%,70%{transform:translateX(5px);} 50%{transform:translateX(-7px);} }

/* tracker picker chips */
.chipgrid { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:8px; }
.trk-chip { background:var(--bg2); border:1px solid var(--line); color:var(--dim); border-radius:11px;
  padding:11px 10px; font-size:12.5px; font-weight:600; cursor:pointer; text-align:left; transition:border-color .12s, background .12s;
  /* "seems like screen stretched" on onboarding step 3. The tick is appended as ::after, so on the
     longer names - "Debt Payoff", "Trading Journal" - it wrapped onto a second line ON ITS OWN and
     doubled that row's height. Eleven trackers of that pushed the card past the bottom of the
     screen, which is what looked stretched. The label balances across lines and the tick stays
     with the last word. */
  display:flex; align-items:center; gap:4px; min-height:44px; line-height:1.25;
  text-wrap:balance; overflow-wrap:break-word; }
/* THE TICK IS ALWAYS THERE, IT JUST IS NOT ALWAYS VISIBLE — and that is the fix, not a style.
   It used to exist only on .on, so selecting a tracker ADDED width to the label. On the longer
   names that pushed the text onto a second line, the row grew, and because .chipgrid is a
   two-column grid its NEIGHBOUR grew with it. Deselect and everything sprang back. Tapping down a
   column of eleven made the page jump on every tap, which is the second half of what he reported
   as "when selected tracker screenshakes" (2026-08-28). Reserving the space permanently means a
   toggle changes colour and nothing else: no re-wrap, no row height change, no reflow. */
.trk-chip::after { content:" ✓"; flex:0 0 auto; font-weight:800; color:var(--teal);
  opacity:0; transition:opacity .12s; }
.trk-chip.on { background:rgba(45,212,191,.12); border-color:rgba(45,212,191,.45); color:var(--text); }
.trk-chip.on::after { opacity:1; }   /* unmistakable selected state */
.trk-chip { overflow-wrap:anywhere; line-height:1.35; }
.hero-num { overflow-wrap:anywhere; } .hero-sub { overflow-wrap:anywhere; }
.edit-trk { color:var(--teal); cursor:pointer; text-transform:none; letter-spacing:0; font-size:11.5px; font-weight:700; }

/* onboarding steps */
.steps { display:flex; flex-direction:column; gap:13px; margin:15px 0 4px; }
.stp { display:flex; gap:12px; align-items:flex-start; }
.stp .sn { width:24px; height:24px; border-radius:50%; background:rgba(45,212,191,.14); color:var(--teal);
  font-weight:800; font-size:13.5px; display:flex; align-items:center; justify-content:center; flex-shrink:0; margin-top:1px; }
.stp b { font-size:13.5px; }
.stp .dim { font-size:12.5px; margin-top:2px; line-height:1.5; }

/* inline "how it works" explainer */
.explain { margin-top:13px; border-top:1px solid var(--line); padding-top:11px; }
.explain summary { font-size:12.5px; color:var(--teal); cursor:pointer; font-weight:700; list-style:none; }
.explain summary::-webkit-details-marker { display:none; }
.explain summary::before { content:"＋  "; font-weight:700; }
.explain[open] summary::before { content:"−  "; }
.explain > div { font-size:12.5px; color:var(--dim); line-height:1.6; margin-top:9px; }

/* home 'needs attention' alerts */
.alert { display:flex; align-items:center; gap:11px; padding:12px 2px; cursor:pointer;
  border-bottom:1px solid rgba(30,42,68,.5); }
.alert:last-child { border-bottom:none; }
.alert .ai { font-size:17px; flex-shrink:0; }
.alert .at { flex:1; font-size:13.5px; font-weight:600; }
.alert .ar { color:var(--dim); font-size:18px; }
/* slim one-line "needs attention" banner on Home that opens the 🔔 panel */
.attn-slim { display:flex; align-items:center; gap:11px; cursor:pointer; }
.attn-slim .ai { font-size:18px; flex-shrink:0; }
.attn-slim .at { flex:1; font-size:13.5px; color:var(--mid); }
.attn-slim .at b { color:var(--text); }
.attn-slim .ar { color:var(--dim); font-size:18px; }

/* ============================================================
   LIGHT THEME
   ============================================================ */
body.light {
  --bg:#EDF1F8; --bg2:#F5F8FC; --card:#FFFFFF; --line:#E2E8F3;
  --text:#0E1526; --dim:#66748E; --mid:#3A465E;
  background:
    radial-gradient(900px 600px at 85% -10%, rgba(56,189,248,.10), transparent 60%),
    radial-gradient(760px 500px at -10% 108%, rgba(45,212,191,.09), transparent 55%),
    linear-gradient(165deg,#F2F5FB 0%,#E7ECF6 100%);
}
body.light .brand .m { color:#0E1526; }
body.light .card, body.light .stat, body.light .tile { box-shadow:0 1px 2px rgba(16,24,40,.04); }
body.light #tabbar { background:rgba(255,255,255,.92); }
.gbtn{display:flex;align-items:center;justify-content:center;gap:10px}
.gicon{display:inline-flex;background:#fff;border-radius:50%;padding:3px;line-height:0}
.auth-or{text-align:center;color:var(--dim);font-size:12.5px;margin:12px 0}
body.light .modal { background:#FFFFFF; }
body.light .del { color:#9AA7BE; }
body.light table.tbl td .x { color:#9AA7BE; }

/* keyboard focus visibility (mouse/touch users never see these) */
.btn:focus-visible, .iconbtn:focus-visible, .tile:focus-visible, .stat:focus-visible,
.tab:focus-visible, .trk-chip:focus-visible, .ob-mode:focus-visible, .ob-tier:focus-visible, .modal-x:focus-visible,
.del:focus-visible, .seg button:focus-visible, .subnav button:focus-visible { outline:2px solid var(--blue); outline-offset:2px; }

/* ===== motion: a single subtle page fade + tactile press feedback ===== */
/* one gentle fade on the whole view when you change tabs — nothing animates the fields themselves */
#view.view-in { animation:viewIn .24s ease-out; }
@keyframes viewIn { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:none; } }
/* a gentle staggered entrance for the launcher tiles (home / More) — feels alive without being busy */
#view.view-in .launch .tile { animation:tileIn .34s cubic-bezier(.22,.8,.3,1) both; }
#view.view-in .launch .tile:nth-child(2) { animation-delay:.045s; }
#view.view-in .launch .tile:nth-child(3) { animation-delay:.09s; }
#view.view-in .launch .tile:nth-child(4) { animation-delay:.135s; }
#view.view-in .launch .tile:nth-child(5) { animation-delay:.18s; }
#view.view-in .launch .tile:nth-child(6) { animation-delay:.225s; }
@keyframes tileIn { from { opacity:0; transform:translateY(10px) scale(.98); } to { opacity:1; transform:none; } }

.btn, .iconbtn, .stat { transition:transform .10s ease; }
.btn:active, .iconbtn:active, .stat:active { transform:scale(.965); }
.tile { transition:border-color .15s, transform .10s ease; }
.tile:active { transform:scale(.97); }
.tab { transition:color .15s, transform .10s ease; }
.tab:active { transform:scale(.93); }
.tab.active .ti { animation:tabPop .28s ease-out; }
@keyframes tabPop { 0%{ transform:scale(.8); } 60%{ transform:scale(1.18); } 100%{ transform:scale(1); } }

@media (prefers-reduced-motion: reduce) {
  #view.view-in, #view.view-in .card, #view.view-in .stat, #view.view-in .tile,
  #view.view-in .launch, #view.view-in table.tbl, .tab.active .ti,
  .cbub.coach.cbub-fresh, .cbub.coach.cbub-fresh .cbub-t { animation:none; }
  .cbub.coach.cbub-fresh::before { display:none; }
  .btn, .iconbtn, .stat, .tile, .tab { transition:none; }
  .btn:active, .iconbtn:active, .stat:active, .tile:active, .tab:active { transform:none; }
}

/* Split & Settle — full-screen group chat (money woven into the conversation) */
/* same safe-area treatment as .chatpop: edge-to-edge (forced at targetSdk 35+) puts inset:0 under
   the status bar, and the DM header sat half-hidden behind the clock — .scf is the DM/thread
   full-screen, a SEPARATE component from .chatpop, which is why fixing chatpop didn't touch it */
.scf{position:fixed;inset:0;z-index:150;background:var(--bg);display:flex;flex-direction:column;
  padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}
/* ONE chat design ("if it is split, designs are not same — redesign it"): the split/DM chrome now
   speaks the household chat's language — same bar weight, same title size, and the preset wall
   below became a one-line quick-reply strip. The bubbles were already shared (.fmsg). */
.scf-head{display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid var(--line);background:var(--bg2)}
.scf-back{background:none;border:none;color:var(--text);font-size:26px;line-height:1;cursor:pointer;padding:0 4px}
.scf-title{font-weight:800;flex:1;font-size:13.5px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.scf-chip{font-size:12.5px;font-weight:600;padding:5px 11px;border-radius:999px;background:var(--line);color:var(--mid);white-space:nowrap}
.scf-chip.good{color:#34D399}
.scf-chip.bad{color:#F87171}
.scf-body{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:9px}
.scf-event{align-self:center;text-align:center;font-size:12.5px;color:var(--dim);background:var(--card);border:1px solid var(--line);border-radius:999px;padding:6px 14px;max-width:92%}
.scf-event .hike-chip{cursor:pointer}
.scf-head .scf-min{background:none;border:none;color:var(--text);font-size:28px;line-height:.5;cursor:pointer;padding:0 4px 6px;margin-left:2px}
/* pre-written openers above the compose box (tap fills the input; Send is the second tap).
   They WRAP now — the old hidden horizontal scroll cut chips off at the edge and gave no clue
   more existed (user: "prepopulated under group can't scroll, it's fuzzy / make it font bigger"). */
.scf-chips{display:flex;flex-wrap:nowrap;overflow-x:auto;gap:6px;padding:8px 12px;background:var(--card);scrollbar-width:none}
.scf-chips::-webkit-scrollbar{display:none}
.scf-chips .chip{flex:0 0 auto;white-space:nowrap;line-height:1.3;font-size:12.5px;padding:7px 11px}   /* quick-reply strip: one line, swipe sideways for more (matches every messenger) */
/* "Tuesday" day dividers between messages from different days */
.scf-day{display:flex;justify-content:center;margin:10px 0 4px}
.scf-day span{font-size:11.5px;color:var(--dim);background:var(--bg2);border:1px solid var(--line);border-radius:99px;padding:3px 12px}
/* the DM lock line — only the two of you see this thread */
.dm-banner{margin:10px 12px 6px;padding:10px 12px;border:1px solid var(--line);border-left:3px solid var(--teal);border-radius:12px;font-size:12.5px;line-height:1.5;color:var(--dim);background:var(--bg2)}
.dm-banner b{color:var(--teal)}
.scf-compose{display:flex;gap:8px;align-items:center;padding:10px 12px;border-top:1px solid var(--line);background:var(--card)}
.scf-compose input{flex:1 1 auto;min-width:0;margin:0;padding:12px 14px;font-size:15px}
.scf-compose .btn{flex:0 0 auto;width:auto;margin:0;padding-left:20px;padding-right:20px}
/* Debt extra-payment slider */
.dx-row{display:flex;justify-content:space-between;align-items:center;margin:12px 0 8px}
.dx-amt{font-size:22px;font-weight:800;color:var(--teal)}
.dx-range{width:100%;-webkit-appearance:none;appearance:none;height:8px;border-radius:6px;background:var(--line);outline:none;margin:2px 0}
.dx-range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:28px;height:28px;border-radius:50%;background:var(--grad);cursor:pointer;box-shadow:0 2px 10px rgba(0,0,0,.45)}
.dx-range::-moz-range-thumb{width:28px;height:28px;border:none;border-radius:50%;background:var(--grad);cursor:pointer;box-shadow:0 2px 10px rgba(0,0,0,.45)}
.dx-scale{display:flex;justify-content:space-between;align-items:center;font-size:11.5px;color:var(--dim);margin-top:5px}
.scf-bub{position:fixed;right:16px;bottom:80px;z-index:149;width:58px;height:58px}
.scf-bub-main{width:100%;height:100%;border-radius:50%;border:none;background:var(--grad);color:#06111c;font-size:25px;box-shadow:0 8px 22px rgba(0,0,0,.45);cursor:pointer;display:flex;align-items:center;justify-content:center;position:relative}
.scf-bub-x{position:absolute;top:-7px;left:-7px;width:24px;height:24px;border-radius:50%;border:none;background:var(--bg2);color:var(--text);font-size:12.5px;line-height:1;box-shadow:0 2px 8px rgba(0,0,0,.45);cursor:pointer;display:flex;align-items:center;justify-content:center}
.scf-bub-badge{position:absolute;top:-3px;right:-3px;min-width:20px;height:20px;border-radius:10px;background:#F0453A;color:#fff;font-size:11.5px;font-weight:800;display:flex;align-items:center;justify-content:center;padding:0 5px;box-shadow:0 0 0 2px var(--bg)}

/* Split & Settle — sleeker shared-expense row (category icon + your-share) */
/* Grid, not flex: the name gets the whole middle column and the per-person split drops to its own
   row underneath. As a flex sibling of the total it was capped at 170px but still stole that width
   from the name, so names ellipsised and the "who paid · date" line wrapped — every row a different
   height. Columns are icon / name / amount; the split spans the last two. */
/* Splitwise-shape row: date block · icon · title/"payer paid total" · your stake right-aligned */
.sxp{display:grid;grid-template-columns:30px 42px minmax(0,1fr) auto;grid-template-areas:"date ic main amt";align-items:center;column-gap:10px;padding:11px 2px;border-bottom:1px solid var(--line)}
.sxp-date{grid-area:date;display:flex;flex-direction:column;align-items:center;line-height:1.15;color:var(--dim)}
.sxp-date span{font-size:10.5px;text-transform:uppercase;letter-spacing:.04em}
.sxp-date b{font-size:15px;color:var(--mid);font-weight:700}
.sxp-rel{font-size:10.5px;color:var(--dim);text-align:right;white-space:nowrap}
.sxp-rel.good{color:#34D399}.sxp-rel.bad{color:#F87171}
.sxp-total.good{color:#34D399}.sxp-total.bad{color:#F87171}
.sxp:last-child{border-bottom:none;padding-bottom:2px}
.sxp-ic{grid-area:ic;width:42px;height:42px;border-radius:13px;display:flex;align-items:center;justify-content:center;font-size:20px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.08)}
.sxp-main{grid-area:main;min-width:0}
.sxp-t{font-weight:700;font-size:15px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* one line, always — "You paid · Aug 2" wrapping was what made rows different heights */
/* Was nowrap, so the category chip and "You paid C$701.04" fought over one line and the loser
   got ellipsized. Let the row WRAP: chip keeps its line, the who-paid line drops under it and
   stays whole. Nothing truncates on a 360px phone any more. */
.sxp-d{font-size:12.5px;color:var(--dim);margin-top:2px;display:flex;flex-wrap:wrap;row-gap:2px;min-width:0}
/* The AMOUNT must never be the thing that gets cut. "You paid C$233.68" was ellipsizing to
   "You paid C$..." — losing the one number the line exists to show. Name shortens, money never. */
.sxp-who{display:flex;gap:4px;align-items:baseline;min-width:0;overflow:hidden}
.sxp-whoname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* NOT bold. Making this stand out was my overcorrection for the truncation bug: it left each row
   with two loud numbers — what was paid AND your stake — and the eye had nowhere to land
   ("when i read expense i feel difficult reading it"). The right-hand you-lent/you-borrowed is
   the number that matters; this one is context. Unbreakable, but quiet. */
.sxp-whoamt{flex:0 0 auto;white-space:nowrap;font-weight:400;color:var(--dim)}
.sxp-when{flex:0 0 auto}
.sxp-amt{grid-area:amt;text-align:right;white-space:nowrap}
.sxp-total{font-weight:800;font-size:15px;font-variant-numeric:tabular-nums}
/* per-person breakdown: quiet secondary text — a teal wall under every amount drowned the totals
   ("green words are not good"). Your own share alone is picked out. */
.sxp-share{grid-area:share;font-size:11.5px;color:var(--dim);line-height:1.45;max-width:none}
/* break BETWEEN people, never through "Ekam C$17.53" */
.sxp-share .nw,.subhead .nw,.sxp-shares .nw{white-space:nowrap}
.sxp-share b{color:var(--mid);font-weight:700}
.sxp{cursor:pointer}   /* rows open the detail sheet (visible delete lives there) */
/* settle-up rows: stacked so buttons can NEVER crush the name into a one-letter column */
.strow{padding:10px 0;border-bottom:1px solid var(--line)}
.strow:last-of-type{border-bottom:none}
.strow-t{font-weight:700;line-height:1.35}
.strow-t .dim{font-weight:400;font-size:12.5px}
.strow-r{display:flex;align-items:center;justify-content:flex-end;gap:9px;margin-top:7px;flex-wrap:wrap}
.strow-r .amt{margin-right:auto;font-size:15px}
/* v249 settle-row redesign: face + name line, money + one pill action under it */
.strow-hd{display:flex;align-items:center;gap:10px;min-width:0}
.strow-hd .fav{width:34px;height:34px;font-size:15px}
.strow-hd .strow-t{min-width:0}
.strow .strow-r{margin-top:8px;padding-left:44px}
.strow .strow-r .amt{font-size:19px;font-weight:800;letter-spacing:.2px}
.stpay{width:auto;padding:6px 14px;border-radius:999px;white-space:nowrap}

.sxp-del{flex:0 0 auto;margin-left:2px}

/* direct-message button on a member row + its unread pip */
.dmbtn { flex:0 0 auto; margin-left:10px; width:34px; height:34px; border-radius:50%;
  background:rgba(45,212,191,.13); border:1px solid rgba(45,212,191,.40); color:var(--teal);
  font-size:17px; line-height:1; display:flex; align-items:center; justify-content:center;
  position:relative; cursor:pointer; }
.dmbtn:active { transform:scale(.92); }
.dmdot { position:absolute; top:-4px; right:-4px; min-width:17px; height:17px; padding:0 4px;
  border-radius:999px; background:var(--red); color:#fff; font-size:10.5px; font-weight:800;
  display:flex; align-items:center; justify-content:center; border:2px solid var(--card); }
/* the DM thread reuses the full-screen split-chat shell (.scf) */
#dm-pop { z-index:96; }

/* ---- Full-screen Pro paywall ---- */
.paywall{position:fixed;inset:0;z-index:200;color:#fff;display:flex;flex-direction:column;
  background:radial-gradient(120% 70% at 50% -6%,#12243f 0%,#0a1322 52%,#070c15 100%)}
.pw-top{display:flex;align-items:center;gap:12px;padding:14px 16px}
.pw-x{width:38px;height:38px;flex:0 0 auto;border-radius:50%;border:1px solid rgba(255,255,255,.14);
  background:rgba(255,255,255,.06);color:#fff;font-size:15px;cursor:pointer}
.pw-title{font-weight:700;font-size:17px;letter-spacing:.3px}
.pw-title b{color:var(--teal)}
.pw-fixed{flex:0 0 auto;padding:2px 13px 0}
.pw-scroll{flex:1;min-height:0;overflow-y:auto;padding:2px 18px 16px;-webkit-overflow-scrolling:touch}
.pw-hero{text-align:center;padding:4px 4px 14px}
.pw-h2{font-weight:800;font-size:23px;line-height:1.12;letter-spacing:-.6px}
.pw-sub{margin-top:7px;font-size:12.5px;color:var(--mid);line-height:1.45}
.pw-plans{display:flex;gap:7px;margin-top:6px}
.pw-plan{flex:1;min-width:0;position:relative;display:flex;flex-direction:column;align-items:center;gap:3px;
  padding:13px 5px 11px;border-radius:14px;border:1.5px solid rgba(255,255,255,.12);
  background:rgba(255,255,255,.03);cursor:pointer;text-align:center;color:#fff;font-family:inherit}
.pw-plan.sel{border-color:var(--teal);background:linear-gradient(180deg,rgba(45,212,191,.13),rgba(45,212,191,.02));
  box-shadow:0 0 0 1px var(--teal),0 14px 32px rgba(45,212,191,.16)}
.pw-tag{position:absolute;top:-9px;left:50%;transform:translateX(-50%);white-space:nowrap;
  background:var(--grad);color:#03121a;font-size:9px;font-weight:800;letter-spacing:.2px;padding:2px 7px;border-radius:999px}
.pw-pname{font-weight:800;font-size:12.5px}
.pw-pprice{font-weight:800;font-size:17px;line-height:1.1;white-space:nowrap}
.pw-pprice small{font-size:9px;color:var(--dim);font-weight:600}
.pw-pseat{font-size:9px;color:var(--mid)}
.pw-pkid{font-size:10.5px;color:var(--teal);margin-top:3px;line-height:1.25}
.pw-inc{margin:14px 2px 10px;font-weight:800;font-size:12.5px;letter-spacing:.5px;color:var(--mid);text-transform:uppercase}
.pw-feats{display:flex;flex-direction:column;gap:10px}
.pw-feat{display:flex;align-items:center;gap:11px;font-size:13.5px;font-weight:600;line-height:1.28}
.pw-ft{flex:1}
.pw-ftag{flex:0 0 auto;font-size:9px;font-weight:800;letter-spacing:.2px;color:#7fe0cf;background:rgba(45,212,191,.12);border:1px solid rgba(45,212,191,.32);padding:3px 8px;border-radius:999px;white-space:nowrap}
.pw-fic{width:31px;height:31px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  border-radius:10px;background:rgba(45,212,191,.12);font-size:15px}
.pw-more{margin:15px 2px 4px;font-size:12.5px;color:var(--dim);font-style:italic}
.pw-cta{padding:12px 18px calc(14px + env(safe-area-inset-bottom));border-top:1px solid rgba(255,255,255,.08);
  background:rgba(7,12,21,.86);backdrop-filter:blur(10px)}
.pw-go{width:100%;font-size:15px;padding:15px}
.pw-fine{margin-top:9px;text-align:center;font-size:10.5px;color:var(--dim);line-height:1.55}
.pw-fine a{color:var(--teal);text-decoration:underline;cursor:pointer}


/* ---- Settings: grouped cards ---- */
.set-card{margin-top:12px}
.set-card:first-child{margin-top:0}
.set-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;min-height:22px}
.set-head b{font-size:13.5px;font-weight:800}
.set-head .dim{font-size:12.5px}
.set-pro{border-color:rgba(45,212,191,.32);box-shadow:0 0 0 1px rgba(45,212,191,.14)}


/* Menu & Settings render as full-screen pages, not bottom sheets */
.modal-bg.full{align-items:stretch}
.modal.full{width:100%;max-width:none;height:100%;max-height:100%;margin:0;align-self:stretch;border-radius:0;padding-top:calc(16px + env(safe-area-inset-top))}


/* full-page back button (Menu / Settings / Notifications) */
.modal-back{position:absolute;top:calc(12px + env(safe-area-inset-top, 0px));left:12px;width:36px;height:36px;
  border-radius:11px;border:1px solid var(--line);background:rgba(255,255,255,.05);color:var(--text);
  font-size:26px;line-height:1;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:3}
.modal-back:active{transform:scale(.9)}
.modal.full > h2{padding-left:48px}
.modal.full .modal-x, .modal-bg.full .modal-shell .modal-x{top:calc(12px + env(safe-area-inset-top, 0px))}
.modal-bg.full .modal-shell{max-width:none;align-self:stretch}
/* toggle switches instead of tick checkboxes */
.np-row input[type=checkbox]{appearance:none;-webkit-appearance:none;flex:0 0 auto;width:46px;height:27px;margin:0;
  border-radius:999px;background:rgba(255,255,255,.16);position:relative;cursor:pointer;transition:background .18s}
.np-row input[type=checkbox]::after{content:"";position:absolute;top:3px;left:3px;width:21px;height:21px;border-radius:50%;
  background:#fff;transition:transform .18s;box-shadow:0 1px 3px rgba(0,0,0,.3)}
.np-row input[type=checkbox]:checked{background:var(--teal)}
.np-row input[type=checkbox]:checked::after{transform:translateX(19px)}

.mc-budget{font-size:12.5px;color:var(--mid);margin:6px 0 2px}
.mc-budget b.good{color:var(--teal)}
.mc-budget b.bad{color:#F87171}


/* card-based shared-expenses list (redesign — colored left accent per item) */
.fx-cards .swrow { border:1px solid var(--line); border-radius:14px; margin-bottom:10px; background:var(--card); box-shadow:0 1px 3px rgba(0,0,0,.20); }
.fx-cards .swrow:last-child { margin-bottom:0; }
/* Left edge stays as structure, not decoration. Per-row accent colours (blue bill / amber sub /
   red insurance / orange debt, and a per-name colour on expenses) turned a plain list into a
   rainbow without encoding anything the row didn't already say in words — user asked for them off. */
.fx-cards .swrow-fg { border-left:4px solid var(--line); }
.fx-cards .fx-item { padding:13px 12px; align-items:center; border-bottom:none; }
.fx-cards .fx-tile { width:46px; height:46px; border-radius:13px; }
.fx-cards .fx-r { display:flex; align-items:center; gap:7px; }
.fx-cards .fx-chev { color:var(--dim); font-size:19px; font-weight:400; line-height:1; }


/* Recent-payments month filter dropdown */
.pay-filter{ margin:2px 0 10px; }
.mn-select{ width:100%; padding:9px 11px; border-radius:10px; border:1px solid var(--line); background:var(--card); color:var(--text); font-family:inherit; font-size:13.5px; -webkit-appearance:none; appearance:none; }


/* profile switcher: center the picker vertically on the full screen */
.modal.prof-full { display:flex; flex-direction:column; justify-content:center; }
.modal.prof-full > h2 { text-align:center; }
.modal.prof-full .prof-grid { margin-top:6px; }


/* compact the family expense cards + sort tabs (user: "everything is so broad — shrink font & tab sizes") */
.fx-cards .fx-item { padding:9px 10px; gap:9px; }
.fx-cards .fx-tile { width:36px; height:36px; border-radius:10px; }
.fx-cards .fx-nm { font-size:13.5px; font-weight:700; }
.fx-cards .fx-chips { display:flex; flex-wrap:wrap; gap:5px; margin-top:3px; }
.fx-cards .catchip, .fx-cards .fx-split, .fx-cards .fx-chips .tag { font-size:9px; padding:0 7px; height:17px; }
.fx-cards .fx-meta { font-size:10.5px; margin-top:2px; }
.fx-cards .amt { font-size:13.5px; }
.fx-cards .fx-chev { font-size:15px; }
.fx-cards .swrow { margin-bottom:8px; border-radius:12px; }
.fx-cards .fx-r { gap:4px; }
.fx-cards .sortbar { gap:5px; margin:2px 0 8px; }
.fx-cards .sortchip { padding:3px 8px; font-size:10.5px; }


/* compact the Regular Payments (bills) rows (user: "shrink the size for regular payments") */
.fx-bills .fx-item { padding:8px 2px; gap:9px; }
.fx-bills .fx-tile { width:36px; height:36px; border-radius:10px; }
.fx-bills .fx-nm { font-size:13.5px; }
.fx-bills .amt { font-size:13.5px; }
.fx-bills .fx-meta { font-size:10.5px; }
.fx-bills .fx-sec { font-size:11.5px; margin:10px 0 4px; }
.fx-bills .fx-pay { font-size:10.5px; padding:4px 9px; margin-top:5px; border-radius:8px; }
.fx-bills .fx-chips { gap:5px; margin-top:3px; }
.fx-bills .catchip, .fx-bills .fx-split, .fx-bills .fx-chips .tag { font-size:9px; padding:0 7px; height:17px; }


/* Waiting-on-you rows: title full width, buttons in an aligned row below (user: "one line, align buttons") */
.waitrow { padding:11px 2px; border-bottom:1px solid rgba(255,255,255,.05); }
.waitrow:last-child { border-bottom:none; }
.waitrow .t { font-size:13.5px; font-weight:700; line-height:1.3; }
.waitrow .d { font-size:12.5px; color:var(--dim); margin-top:2px; }
.wr-btns { display:flex; gap:8px; margin-top:9px; }
.wr-btns .btn { flex:1; padding:9px 10px; font-size:12.5px; }


/* small-screen: keep tabs + chips from overflowing (user: smaller phone, "squeeze all tabs") */
@media (max-width:360px){
  .subtabs button, .seg button, .settab, .fbtab { font-size:12.5px; padding:8px 6px; }
  .subtab { font-size:12.5px !important; padding:8px 6px !important; }
  .sortchip { font-size:10.5px; padding:3px 8px; }
  .kpis .kpi .k { font-size:10.5px; }
  /* No .v size rule here on purpose. A second, differently-tuned clamp at this breakpoint was the
     same bug one level down (3.6vw here vs 3.4vw above), which pushed Debt and Investing a size
     bigger again under 360px. The clamp on .kpi .v.sm already has a floor — it covers this. */
  h2, .modal h2 { font-size:19px; }
}

.my-bar { display:flex; gap:8px; margin:2px 0 10px; }
.my-bar .mn-select { flex:1; }
/* Bills card controls: month/year picker + sort. Wraps to a second line on narrow phones so
   the sort dropdown never squeezes the month name down to "Aug…". */
.bill-ctl { display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin:2px 0 10px; }
.bill-ctl .my-bar { flex:1 1 190px; margin:0; }
.bill-ctl #bill-sort { flex:1 1 150px; width:auto; }
.bill-ctl .btn { flex:0 0 auto; }

/* Members lists (Split & Settle + Family): a long two-currency amount ("C$673 (≈ $480)") was
   squeezing the name column to nothing, stacking the name one LETTER per line. The name now keeps
   a guaranteed share (truncating with … if needed) and the amount wraps under itself on the right. */
.mem-card .row .l { flex:1 1 auto; min-width:0; }
.mem-card .row .l .t { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.mem-card .row .r { flex:0 1 auto; min-width:0; display:flex; flex-direction:column; align-items:flex-end; gap:2px; text-align:right; }
.mem-card .row .r .amt { white-space:nowrap; }

/* trade-log note gets its own quiet line instead of running into the meta (user request) */
.tj-note { font-size:12.5px; color:var(--mid); font-style:italic; margin-top:3px; line-height:1.45;
  overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }

/* customize modes: cards & tiles are DRAG targets — text selection must never hijack the hold */
.pgc, .pgc *, .launch .tile, .launch .tile * { -webkit-user-select:none !important; user-select:none !important; -webkit-touch-callout:none !important; }

/* Regular payments match the shared-expense card design (user: "not same in design") */
.fx-bills .swrow { border:1px solid var(--line); border-radius:14px; margin-bottom:10px; background:var(--card); box-shadow:0 1px 3px rgba(0,0,0,.20); }
.fx-bills .swrow:last-child { margin-bottom:0; }
.fx-bills .swrow-fg { border-left:4px solid var(--line); border-radius:14px; }
.fx-bills .fx-item { padding:13px 12px; border-bottom:none; }

/* a disabled field must LOOK disabled (signed-in account email etc.) */
input:disabled, select:disabled, textarea:disabled { opacity:.55; color:var(--dim);
  -webkit-text-fill-color:var(--dim); background:var(--bg2); cursor:not-allowed; }

/* capped (hidden) rows must hide their swipe WRAPPER too — the leftover shells were stacking
   into an empty grey bar under the expenses list */
.swrow:has(.capped-hide) { display:none; }
.card.searching .swrow:has(.capped-hide) { display:block; }

/* the little i: a quiet circle on section titles that explains the section on tap */
.ibtn { display:inline-flex; align-items:center; justify-content:center; width:17px; height:17px;
  margin-left:7px; border-radius:50%; border:1.5px solid var(--dim); background:none; color:var(--dim);
  font-size:11.5px; font-weight:800; font-style:italic; font-family:Georgia,serif; line-height:1;
  cursor:pointer; vertical-align:2px; padding:0; }
.ibtn:active { transform:scale(.9); }
/* The ⓘ is drawn 17px so it stays a quiet mark beside a heading — but a 17px TARGET is well under
   any touch guidance and is genuinely hard to hit. This grows the tappable area to ~33px without
   changing a pixel of what you see: the circle keeps its size, the reach around it doubles. */
.ibtn { position:relative; }
.ibtn::after { content:""; position:absolute; top:50%; left:50%; width:33px; height:33px;
  transform:translate(-50%,-50%); border-radius:50%; }

/* the chore count riding on the kid's Earn tab — the bottom bar is position-relative already,
   so pin it to the button rather than letting it push the label off-centre */
#tabbar .tab { position:relative; }
#tabbar .tab-count { position:absolute; top:4px; left:50%; margin-left:6px; }

/* group whiteboard — the pinned shared note (Splitwise reference) */
.wb-card .wb-body{font-size:13.5px;line-height:1.65;color:var(--mid);background:var(--bg2);border:1px solid var(--line);border-radius:12px;padding:10px 13px;margin-top:4px;overflow-wrap:anywhere}

/* family-page hero — from his household-redesign mockup: the balance IS the headline */
.fam-hero .fh-k{font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);font-weight:700}
.fam-hero .fh-num{font-size:42px;font-weight:800;line-height:1.1;margin-top:4px;font-variant-numeric:tabular-nums}
.fam-hero .fh-num.good{color:#34D399}
.fam-hero .fh-num.bad{color:#F87171}
.fam-hero .fh-line{font-size:13.5px;line-height:1.6;color:var(--mid);margin-top:8px}

/* v258 — Split & Settle landing: group rows as cards (his voice note: "animated and
   fancy… group name in the middle of the box, make it better") */
.sgrow { display:flex; align-items:center; gap:13px; padding:13px 2px; cursor:pointer;
  border-radius:14px; transition:transform .12s ease, background .12s ease; }
.sgrow:active { transform:scale(.98); background:rgba(45,212,191,.05); }
.sg-tile { flex:0 0 auto; width:48px; height:48px; border-radius:14px; font-size:23px;
  display:flex; align-items:center; justify-content:center;
  box-shadow:0 4px 14px rgba(0,0,0,.25); }
.sg-trip   { background:linear-gradient(135deg,#0891B2,#2DD4BF); }
.sg-home   { background:linear-gradient(135deg,#059669,#84CC16); }
.sg-couple { background:linear-gradient(135deg,#DB2777,#F472B6); }
.sg-other  { background:linear-gradient(135deg,#7C3AED,#A78BFA); }
.sg-main { flex:1 1 auto; min-width:0; display:flex; flex-direction:column; justify-content:center; }
.sg-name { font-size:17px; font-weight:800; letter-spacing:.2px; line-height:1.25; overflow-wrap:anywhere; }
.sg-sub { font-size:12.5px; color:var(--dim); margin-top:2px; }
.sg-own { color:#FBBF24; font-weight:700; text-transform:uppercase; font-size:10.5px; letter-spacing:.6px; }
.sg-ar { flex:0 0 auto; font-size:20px; color:var(--dim); }
.sg-in { animation:sgIn .38s cubic-bezier(.2,.7,.3,1) both; }
@keyframes sgIn { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:none; } }
@media (prefers-reduced-motion: reduce) { .sg-in { animation:none; } .sgrow, .sgrow:active { transition:none; transform:none; } }

/* v259 — his pick "C, and colours are loud": full-width group cards, quiet kind tint */
.sgcard { display:flex; align-items:center; gap:13px; padding:16px 14px; margin:0;
  border-radius:16px; border:1px solid var(--line); background:rgba(255,255,255,.025);
  cursor:pointer; transition:transform .12s ease, border-color .12s ease; position:relative; overflow:hidden; }
/* the swipe layer must hug the card: margin lives on the OUTER wrapper, and the wrapper clips —
   otherwise slivers of the red Delete layer poke out beside the card's rounded corners */
.swrow.sg-in { margin:9px 0; border-radius:16px; overflow:hidden; border-bottom:none; }
.sgcard::before { content:""; position:absolute; inset:0 auto 0 0; width:3px; }
.sgcard:active { transform:scale(.985); }
.sgc-trip::before   { background:rgba(45,212,191,.55); }
.sgc-home::before   { background:rgba(132,204,22,.5); }
.sgc-couple::before { background:rgba(244,114,182,.5); }
.sgc-other::before  { background:rgba(167,139,250,.5); }
.sgc-trip   { background:linear-gradient(100deg, rgba(45,212,191,.07), transparent 55%); }
.sgc-home   { background:linear-gradient(100deg, rgba(132,204,22,.06), transparent 55%); }
.sgc-couple { background:linear-gradient(100deg, rgba(244,114,182,.06), transparent 55%); }
.sgc-other  { background:linear-gradient(100deg, rgba(167,139,250,.06), transparent 55%); }
.sg-emoji { flex:0 0 auto; width:42px; height:42px; border-radius:12px; font-size:21px;
  background:rgba(255,255,255,.04); border:1px solid var(--line);
  display:flex; align-items:center; justify-content:center; }
.sgcard .sg-name { font-size:18px; }
@media (prefers-reduced-motion: reduce) { .sgcard, .sgcard:active { transition:none; transform:none; } }

/* v260 — "tabs make it smaller under split and settle": compact subtab variant */
.subtabs.subtabs-sm { padding:3px 4px; border-radius:11px; gap:4px; }
.subtabs-sm .subtab { padding:7px 6px; font-size:11.5px; border-radius:8px; }
@media (max-width:400px) { .subtabs-sm .subtab { font-size:10.5px; padding:7px 4px; } }

/* v262 — member-row actions: one level line, and a chat pill that looks pressable
   (his note: "make it something popping a person clicks on") */
.mrow-r { display:flex; align-items:center; gap:8px; flex:0 0 auto; }
/* .mem-card .row .r goes column (big-font safety) and outranks .mrow-r — inside it the
   amount sits on top and the two actions share ONE line below, so rows stay short */
.mem-card .row .r.mrow-r { flex-direction:column; align-items:flex-end; gap:7px; }
.mrow-btns { display:flex; align-items:center; gap:8px; }
.mrow-btns:empty { display:none; }
.mem-card .row { align-items:center; padding:9px 0; }
.chatpill { display:inline-flex; align-items:center; gap:5px; padding:8px 13px; border:none;
  border-radius:999px; background:var(--grad); color:#06111c; font-weight:800; font-size:12.5px;
  line-height:1; cursor:pointer; position:relative; flex:0 0 auto;
  box-shadow:0 2px 10px rgba(45,212,191,.35); transition:transform .1s ease; }
.chatpill:active { transform:scale(.93); }
.chatpill .dmdot { position:absolute; top:-4px; right:-4px; }
@media (prefers-reduced-motion: reduce) { .chatpill, .chatpill:active { transition:none; transform:none; } }

/* v264 — "shrink the group-name tab, give the tabs a little gap": slimmer head card */
.sghead { padding:11px 14px; }
.sghead h3 { font-size:15px; }
.sghead .form-alert { margin-top:8px; }

/* ============================================================
   v270 — DESKTOP SHELL ("I want something for the laptop — full screen,
   not the phone squeezed into the website"). Pure CSS layer: >=1080px gets
   a left sidebar nav, a wide centred content area and two-column card flow.
   Phones and the APK WebView are untouched.
   ============================================================ */
@media (min-width: 1080px) {
  #app { max-width: none; margin: 0; box-shadow: none; border: none;
    padding-left: 232px; min-height: 100dvh; }
  /* bottom tab bar becomes a left sidebar */
  #tabbar { left: 0; top: 0; bottom: 0; transform: none; width: 232px; max-width: none;
    flex-direction: column; justify-content: flex-start; gap: 4px;
    padding: 84px 14px 20px; border-top: none; border-right: 1px solid var(--line);
    background: rgba(7,11,22,.97); }
  #tabbar .tab { flex: 0 0 auto; flex-direction: row; justify-content: flex-start;
    gap: 12px; font-size:15px; font-weight: 700; padding: 12px 14px;
    border-radius: 12px; width: 100%; }
  #tabbar .tab.on { background: rgba(45,212,191,.10); }
  #tabbar .tab-count { position: absolute; top: 8px; left: auto; right: 10px; margin-left: 0; }
  /* content: wide, centred, cards flow into two columns */
  #view { padding: 18px 36px 120px; }
  #view > * { max-width: 1160px; margin-left: auto; margin-right: auto; }
  #view { column-count: 2; column-gap: 20px; column-fill: balance; display: block; }
  #view > * { break-inside: avoid; -webkit-column-break-inside: avoid; margin-bottom: 16px; }
  /* full-width rows that must not split into columns */
  #view > .subtabs, #view > h2 { column-span: all; }
  .fab { right: 34px; bottom: 30px; }
  /* modals keep a sane reading width on big screens */
  .modal, .modal-shell { max-width: 560px; }
  .scf, .chatpop { max-width: 720px; left: 50%; transform: translateX(-50%); }
}

/* v271 — real desktop portal layer (replaces the v270 reflow) */
.d-only, .nav-sec { display: none; }
@media (min-width: 1080px) {
  /* top bar: fixed across the content area */
  #topbar { position: fixed; top: 0; left: 232px; right: 0; z-index: 45;
    background: rgba(6,10,20,.88); backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--line); padding: 10px 28px; }
  #view { padding-top: 84px; }
  /* sidebar: brand-coloured rail, every tracker, hover + active states */
  #tabbar { padding-top: 24px; overflow-y: auto; scrollbar-width: thin; }
  #tabbar .tab { transition: background .12s, color .12s; }
  #tabbar .tab:hover { background: rgba(255,255,255,.05); color: #fff; }
  #tabbar .tab.active { background: rgba(45,212,191,.12); color: var(--teal); }
  .d-only { display: flex; }
  .nav-sec { display: block; font-size: 10.5px; font-weight: 800; letter-spacing: 1.6px;
    text-transform: uppercase; color: var(--dim); margin: 20px 14px 6px; }
  #tabbar .ti { font-size: 18px; }
}
@media (min-width: 1500px) {
  #view { column-count: 3; }
  #view > * { max-width: 1460px; }
  #app { padding-left: 248px; }
  #tabbar { width: 248px; }
  #topbar { left: 248px; }
}

/* ============================================================
   v274 — MONEY HQ 2.0, phase A (his mockup musenexus-full_2.html):
   the violet/pink identity as a body.v2 layer over the same app.
   ============================================================ */
body.v2 {
  /* EXACT tokens from his musenexus-full_2.html (:root) — "original colour as per designs" */
  /* His file has TWO surfaces: --s1 #111219 is the sheet, --s2 #171824 is a card. --card was set
     to the sheet colour while the .card rule below hard-coded the card colour, so anything using
     var(--card) - swipeable rows, most of a tracker list - painted a DARKER block inside the card
     holding it. That is the "why is this a different colour / grey black" he reported. One value now. */
  --bg:#0a0b0f; --bg2:#0a0b0f; --card:#171824; --sheet:#111219; --line:rgba(255,255,255,.06);
  --text:#f1f1f6; --dim:#9294a8; --mid:#b9bbcf;
  --teal:#a855f7; --blue:#6c5ce7; --purple:#a855f7;
  /* the accent, broken into the three gradient stops the 2.0 look is built from, plus an rgb
     form for the translucent tints. The colour picker rewrites these, which is what makes the
     WHOLE app follow the accent instead of only the handful of things that used var(--teal). */
  --g1:#6c5ce7; --g2:#a855f7; --g3:#ec4899; --teal-rgb:168,85,247; --g1-rgb:108,92,231;
  --gold:#f59e0b; --red:#ef4444;
  --grad:linear-gradient(90deg,var(--g1),var(--g2),var(--g3));
  background:#0a0b0f;
}
body.v2 .subtab.on { color:#0E0B1A; }
body.v2 .btn { color:#0E0B1A; }
body.v2 .btn.ghost { color:var(--teal); }
body.v2 #tabbar .tab.active, body.v2 .tab.active { color:var(--teal); }
body.v2 #tabbar .tab.active { background:rgba(139,92,246,.14); }
body.v2 .fab { background:linear-gradient(135deg,#8B5CF6,var(--g3)); }
body.v2 .chatpill { background:var(--grad); box-shadow:0 2px 10px rgba(139,92,246,.4); }
/* v395 — "Your trackers" read as 1.0 inside a 2.0 sheet: body.v2 remaps --teal to violet, so the
   ✓ turned violet while the tint and border stayed 1.0 teal, because those two are written as raw
   rgba(45,212,191,…) literals and a literal can't follow a token. Restate them from --teal so the
   selected state matches whichever identity is active. */
body.v2 .trk-chip.on {
  background:color-mix(in srgb, var(--teal) 12%, transparent);
  border-color:color-mix(in srgb, var(--teal) 45%, transparent);
}
/* v399 — the file's pg-pin. Asking a child for a PIN with a password text field means the phone
   keyboard in a grown-up's layout; his file draws a proper 10-key pad. Geometry is lifted from the
   file (64px keys, 14px dots, 12px gaps, 260px grid); only the colours are restated from the app's
   tokens so it follows whichever identity is on. */
/* Full screen, matching his .land (min-height:100vh). Above #lock's z-index:300 so the two can
   never half-overlap, and opaque so nothing of the grown-up's screen shows through the gate. */
#pingate { position:fixed; inset:0; z-index:320; display:none; align-items:center;
  justify-content:center; padding:24px; background:var(--bg); overflow-y:auto; }
#pingate.show { display:flex; }
body.v2 .pinpad { display:flex; flex-direction:column; align-items:center; padding:6px 0 2px; }
body.v2 .pin-av { width:64px; height:64px; border-radius:50%; background:var(--grad);
  display:flex; align-items:center; justify-content:center; font-size:26px; }
body.v2 .pin-lab { font-size:12px; color:var(--dim); margin-top:10px; }
body.v2 .pin-nm { font-size:20px; font-weight:900; color:var(--text); margin-bottom:20px; }
body.v2 .pin-dots { display:flex; gap:12px; margin-bottom:24px; }
body.v2 .pin-dot { width:14px; height:14px; border-radius:50%;
  border:2px solid rgba(255,255,255,.1); transition:all .2s; }
body.v2 .pin-dot.filled { background:var(--teal); border-color:var(--teal); }
body.v2 .pin-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:12px;
  max-width:260px; width:100%; margin:0 auto; }
body.v2 .pin-key { width:64px; height:64px; border-radius:50%; border:1px solid var(--line);
  background:var(--bg2); color:var(--text); font-size:22px; font-weight:700; cursor:pointer;
  margin:0 auto; transition:all .15s; }
body.v2 .pin-key:active { background:var(--card); transform:scale(.95); }
body.v2 .pinpad.wrong { animation:pinshake .3s; }
@keyframes pinshake { 0%,100% { transform:translateX(0); } 25% { transform:translateX(-6px); } 75% { transform:translateX(6px); } }
body.v2 .sgc-trip::before { background:rgba(167,139,250,.6); }
body.v2 .fam-hero .fh-num.good, body.v2 .amt.good { color:#34D399; }

/* v277 — every category, its own quiet colour (chips + icon-tile rings) */
.catchip.cat-trans  { color:#7DD3FC; background:rgba(56,189,248,.14); }
.catchip.cat-shop   { color:#F9A8D4; background:rgba(244,114,182,.14); }
.catchip.cat-health { color:#FCA5A5; background:rgba(248,113,113,.14); }
.catchip.cat-travel { color:#C4B5FD; background:rgba(139,92,246,.14); }
.catchip.cat-fun    { color:#FDBA74; background:rgba(251,146,60,.14); }
.catchip.cat-edu    { color:#86EFAC; background:rgba(74,222,128,.14); }
.catchip.cat-save   { color:var(--teal); background:rgba(45,212,191,.14); }
.fx-tile.cat-trans::after  { border-color:rgba(56,189,248,.42); }
.fx-tile.cat-shop::after   { border-color:rgba(244,114,182,.42); }
.fx-tile.cat-health::after { border-color:rgba(248,113,113,.42); }
.fx-tile.cat-travel::after { border-color:rgba(139,92,246,.42); }
.fx-tile.cat-fun::after    { border-color:rgba(251,146,60,.42); }
.fx-tile.cat-edu::after    { border-color:rgba(74,222,128,.42); }
.fx-tile.cat-save::after   { border-color:rgba(45,212,191,.42); }
.fx-tile.cat-other::after  { border-color:rgba(255,255,255,.14); }

/* ============================================================
   v278 — 2.0 phase B: the mockup's HOME structure (body.v2 only).
   Welcome header as naked text, gradient-framed hero with the big
   violet number, coach before health (mockup order), tile polish.
   ============================================================ */
/* The bottom padding is not cosmetic: .today-hero is overflow:hidden, and 2.0 hides both
   .th-brief and .th-kpis (see further down), so .th-date became the LAST child of a box that
   ended 0px after it — the descenders of "Winding down · Saturday, Aug 15" were clipped by the
   card's own overflow. 1.0 never showed it because the brief sat underneath. Gate: hero-clip.cjs */
body.v2 .today-hero { background:none !important; border:none; box-shadow:none; padding:8px 4px 6px; }
body.v2 .today-hero * { text-shadow:none; }
body.v2 .today-hero .th-sun, body.v2 .today-hero .wx, body.v2 .today-hero .th-sky { display:none; }
body.v2 .today-hero::before { background:none !important; }
body.v2 .today-hero .th-horizon { display:none; }
body.v2 .hero-card { position:relative; border:1.5px solid transparent;
  background:linear-gradient(var(--card),var(--card)) padding-box,
             linear-gradient(90deg,#8B5CF6,var(--g3)) border-box; }
body.v2 .hero-card .hero-num { color:#C4B5FD; font-size:46px; letter-spacing:-1px; }
body.v2 .hcard { border-radius:18px; }
body.v2 .coachcard { border-radius:18px; }
@media (max-width:1079px) {
  body.v2 #view { display:flex; flex-direction:column; }
  body.v2 #view > * { order:10; }
  body.v2 #view > .today-hero { order:1; }
  body.v2 #view > .hero-card { order:2; }
  body.v2 #view > .coachcard { order:3; }
  body.v2 #view > .hcard { order:4; }
  body.v2 #view > .attn-slim { order:5; }
}
body.v2 .launch > * { border-radius:18px; border:1px solid var(--line); background:rgba(255,255,255,.025); }
body.v2 .launch > *:active { transform:scale(.97); }
body.v2 #tabbar .tab.active { background:rgba(139,92,246,.16); border-radius:12px; }

/* v279 — "I don't like scrolling, make these fields smaller": compact form fields.
   Inputs stay ~40px (thumb-safe); the saving is padding + gaps, so most forms fit
   without scrolling. */
.field { margin-top:6px; }
.field label { margin-bottom:2px; }
.field input, .field select { padding:7px 11px; }
.field textarea { min-height:76px; }
.modal h2 { margin-bottom:6px; }

/* v280 — "at least 2 fields in one line": the modal packs SHORT fields (numbers,
   dates) two per row automatically; text, selects, notes and buttons keep the full
   width. One rule, every add-form in the app. */
.modal { display:grid; grid-template-columns:repeat(6,1fr); column-gap:9px; align-content:start; }
.modal > * { grid-column:span 6; min-width:0; }
/* "still it can be 3 lines where it can be adjusted — whole app like that":
   plain numbers pack THREE per line, dates two; labels ellipsize in narrow cells */
.modal > .field:has(> input[type="number"]) { grid-column:span 2; }
/* "make it two or three fields in one line to squeeze up" - plain TEXT fields each took a whole
   row, so Add debt ran Name, then Lender, each alone. Short text pairs up now; anything that
   genuinely needs the width (name, description, notes, email, search) keeps it. */
.modal > .field:has(> input[type="text"]) { grid-column:span 3; }
.modal > .field:has(> input#ff-name), .modal > .field:has(> input#ff-desc),
.modal > .field:has(> input#ff-notes), .modal > .field:has(> input[type="email"]),
.modal > .field:has(> textarea) { grid-column:1/-1; }
.modal > .field:has(> input[type="text"]) label { white-space:normal; overflow-wrap:break-word; line-height:1.25; }
.modal > .field:has(> input[type="date"]) { grid-column:span 3; }
/* A label cut to "Annual dividen…" or "Still owed — de…" stops telling you what to type, which is
   the entire job of a label. In a 103px cell "Renews on (day 1–28, if monthly/weekly)" needed
   231px. Let them wrap; the field is a column so every input in a row still lines up at the
   bottom however many lines its label takes. */
.modal > .field:has(> input[type="number"]), .modal > .field:has(> input[type="date"]) {
  display:flex; flex-direction:column; }
.modal > .field:has(> input[type="number"]) label, .modal > .field:has(> input[type="date"]) label {
  white-space:normal; overflow:visible; text-overflow:clip; overflow-wrap:break-word; line-height:1.25; flex:1 0 auto; }

/* v282 — "category images don't make sense, occupying space": the colour now lives
   in the chip (v277), so the big icon tiles go. Rows drop to two tight lines. */
#fam-exps .fx-tile, .fx-cards .fx-tile { display:none; }
#fam-exps .fx-item, .fx-cards .fx-item { gap:0; }
#fam-exps .fx-b, .fx-cards .fx-b { margin-left:0; }
/* (row padding left alone - tightening it exposed the swipe layer) */

/* v284 — his call + mine: the category chip sits BELOW the name, leading the meta line,
   identically on Family sharing and Family budget. Names stay clean, rows stay 2 lines. */
.meta-chips { display:inline-flex; gap:5px; vertical-align:middle; }
/* v287 — "aligned, category same size": the chip line and the paid-by line share the
   same left edge, height and type size, so the two rows read as one aligned block */
.meta-chips .catchip { font-size:11.5px; padding:2.5px 8px; }
#fam-exps .fx-meta, .fx-cards .fx-meta { display:flex; align-items:center; gap:5px; min-height:20px; margin-top:3px; }
/* v288 — the chip-only line drops the avatar gutter: chip, name and avatar all share
   one flush left edge ("aligned like category") */
#fam-exps .fx-meta:has(> .meta-chips:only-child), .fx-cards .fx-meta:has(> .meta-chips:only-child) { padding-left:0 !important; }

/* v289 preview — "make circle smaller": meta-line avatars shrink to a subtle dot-badge */
#fam-exps .fx-av, .fx-cards .fx-av { width:15px; height:15px; font-size:9px; top:4px; }

/* v290 preview — "squeeze tabs, lower gap": tracker tiles tighten up.
   Icon and name share one line; padding and grid gap come down; ~40% more per screen. */
.launch { gap:8px; margin-top:10px; }
.tile { padding:10px 12px; border-radius:14px; }
.tile .ic { font-size:17px; float:left; margin:1px 8px 0 0; }
.tile .tile-nm, .tile b { display:inline; }
.tile .tv, .tile .vv { margin-top:2px; }

/* v290b — "squeeze upper tabs too, user scrolls a lot": every card tightens.
   Padding 18->13, card gap 14->9, hero number 44->34, headings closer to content. */
.card { padding:13px 14px; margin-top:9px; }
.card h3 { margin-bottom:6px; }
.hero-num { font-size:34px; }
.hero-sub, .subhead { margin-top:3px; }
.coachcard { padding:11px 14px; }
.hcard { padding:12px 14px; }

/* v290c — organizing the shared list: month/category section headers + search */
.fx-msec { display:flex; justify-content:space-between; align-items:center;
  margin:10px 0 4px; padding:5px 2px 4px; border-bottom:1px solid var(--line);
  font-size:11.5px; font-weight:800; letter-spacing:1.2px; text-transform:uppercase; color:var(--mid); }
.fx-msec .fxm-amt { font-weight:700; letter-spacing:.2px; color:var(--dim); text-transform:none; }
.fxq { width:100%; background:var(--bg2); border:1px solid var(--line); border-radius:11px;
  padding:8px 12px; color:var(--text); font-size:13.5px; outline:none; margin:2px 0 8px; }
.fxq:focus { border-color:var(--teal); }

/* v290d — "squeeze all the tabs in More section too": rows and icons tighten */
.more-row { padding:9px 12px; gap:10px; }
.more-row .mr-ic { width:28px; height:28px; font-size:15px; border-radius:8px; }
.more-row .mr-name { font-size:13.5px; }
.more-row .mr-sub { font-size:11.5px; }
.more-list { margin-top:8px; }

/* v290e — "make this tab same squeeze, fields occupying spaces": dropdowns pair
   two per line app-wide (labels ellipsize); toggle rows tighten */
.modal > .field:has(select) { grid-column:span 3; }
.modal > .field:has(select) label { white-space:normal; overflow:visible; text-overflow:clip; overflow-wrap:break-word; line-height:1.25; }
.sp-part { padding:6px 0; }
.modal .hint { margin-top:4px; }

/* the Paid-by/Split pair lives inside the #fe-split wrapper (Private toggles it) —
   give the wrapper the same packing grid */
#fe-split { display:grid; grid-template-columns:repeat(6,1fr); column-gap:9px; }
#fe-split > * { grid-column:1 / -1; min-width:0; }
#fe-split > .field:has(select) { grid-column:span 3; }

/* v292 — mockup-exact styling layer: flat near-black ground, Inter-style type,
   the mockup's radii and its violet glow accents. Brand logo (MN) stays. */
body.v2 { font-family:'Inter',-apple-system,system-ui,sans-serif; }
/* (an earlier duplicate of the .card rule set the SHEET colour here; the real one is below) */
body.v2 .btn { background:linear-gradient(135deg,var(--g1),var(--g2),var(--g3)); border-radius:14px; color:#fff; }
body.v2 .btn.ghost { background:#171824; border:1px solid rgba(255,255,255,.08); color:#f1f1f6; }
body.v2 .hero-card { background:linear-gradient(#111219,#111219) padding-box, linear-gradient(90deg,var(--g1),var(--g2),var(--g3)) border-box; }
body.v2 .hero-num { background:linear-gradient(90deg,var(--g2),var(--g3)); -webkit-background-clip:text; background-clip:text; }
body.v2 .catchip { border-radius:8px; }
body.v2 .fab { background:linear-gradient(135deg,var(--g2),var(--g3)); box-shadow:0 8px 24px rgba(var(--teal-rgb),.35); }
body.v2 #tabbar { background:rgba(10,11,15,.94); border-top:1px solid rgba(255,255,255,.06); }
body.v2 #tabbar .tab.active { background:rgba(var(--teal-rgb),.12); color:var(--g2); }
body.v2 .subtab.on { background:linear-gradient(90deg,var(--g1),var(--g2)); color:#fff; }
body.v2 .chatpill { background:linear-gradient(90deg,var(--g2),var(--g3)); color:#fff; }
body.v2 .hring .hr-fg { stroke:var(--g2); }
body.v2 .tile { background:#111219; border-radius:20px; }
body.v2 .more-list { background:#111219; }

/* v294 — the six-item squeeze batch (his screenshots ①-⑤) */
/* ① category chips back down */
.meta-chips .catchip { font-size:9px; padding:1.5px 6px; }
/* ② budget category rows: name + amount + ✕ share ONE line (cb-top never had flex here) */
.catbar .cb-top { display:flex; justify-content:space-between; align-items:center; gap:8px; }
.catbar .cb-top > span:first-child { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.catbar .cb-r { white-space:nowrap; flex:0 0 auto; display:inline-flex; align-items:center; gap:6px; }
.catbar { margin:8px 0; }
/* ③④ Split & Settle / family subtabs tighter */
.subtabs.subtabs-sm { padding:2px 3px; }
.subtabs-sm .subtab { padding:5px 4px; font-size:11.5px; }
/* shared rows: meta lines trim */
#fam-exps .fx-meta, .fx-cards .fx-meta { min-height:16px; margin-top:2px; }
/* ⑤ the greeting card takes less space */
.today-hero { padding:13px 14px; }
.today-hero .th-kpis { margin-top:9px; }

/* v295 — "subscriptions still too much gap": rows tighten like everything else */
.row.subrow { padding:8px 0; }
.subico { width:34px; height:34px; font-size:17px; border-radius:10px; }
.row.subrow .fambtn, .row.subrow [data-tofam] { padding:2px 8px; font-size:10.5px; }
.row.subrow .d { margin-top:1px; }
.row.subrow .r.sub-r { gap:4px; }

/* v295b — "fill the gap on split and settle": group cards tighten */
.sgcard { padding:10px 12px; gap:11px; }
.sg-emoji { width:34px; height:34px; font-size:17px; border-radius:10px; }
.sgcard .sg-name { font-size:15px; }
.swrow.sg-in { margin:6px 0; }

/* v295c — "highlight the totals": shared-expense + regular-payment totals become
   hero lines under their headings (family budget & sharing) */
.h3-total { font-size:26px; font-weight:800; letter-spacing:-.3px; color:var(--teal); margin:2px 0 6px; }
.h3-total .h3t-sfx { font-size:13.5px; font-weight:700; color:var(--dim); margin-left:2px; }

/* v295d — "images consistent among budget/family budget": the S&S expense rows go
   tile-free like every other money list (v282 rule) — date block + text carry the row */
.sxp .sxp-ic { display:none; }
.sxp { grid-template-columns:30px minmax(0,1fr) auto; grid-template-areas:"date main amt"; }

/* v295e — trade analytics redesign: hero net per breakdown + proportional P&L bars */
.h3-total.neg { color:#F87171; }
.bd-row { display:flex; align-items:center; gap:10px; padding:7px 0; border-bottom:1px solid var(--line); }
.bd-row:last-child { border-bottom:0; }
.bd-l { flex:1; min-width:0; }
.bd-l .t { font-size:13.5px; font-weight:700; }
.bd-sub { font-size:11.5px; font-weight:600; color:var(--dim); margin-left:4px; }
.bd-bar { height:5px; border-radius:99px; background:rgba(255,255,255,.07); overflow:hidden; margin-top:4px; }
.bd-fill { height:100%; border-radius:99px; }
.bd-fill.good { background:linear-gradient(90deg,var(--teal),#34D399); }
.bd-fill.bad { background:linear-gradient(90deg,#F87171,#EF4444); }

/* v295f — "better logos for expense, small": S&S rows carry the same small category
   chip as the family lists (consistent images doctrine), not the old 42px tiles */
.sxp-d { align-items:center; gap:6px; }
.sxp-d .catchip { font-size:9px; padding:1.5px 6px; flex:0 0 auto; }

/* v295g — settle rows tightened (voice note: "gap between the names and the amount"):
   money on the name line, one compact action line, no third row */
.strow { padding:8px 0; }
.strow-hd .strow-t { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.strow-hd .amt { margin-left:auto; flex:0 0 auto; font-size:17.5px; font-weight:800; letter-spacing:.2px; }
.strow .strow-r { margin-top:5px; padding-left:44px; justify-content:space-between; flex-wrap:nowrap; }
.strow .strow-r .amt { display:none; }
.st-nudge { font-size:11.5px; }

/* ============================================================
   v295h — 2.0 PHASE D: the mockup's component language, applied
   to every screen through the body.v2 scope. Sources: .cd cards,
   .li list items, .tabs strip, .sb stat boxes, .pb bars, .st
   section titles from musenexus-full_2.html. 1.0 untouched.
   ============================================================ */
body.v2 .card { background:var(--card); border:1px solid rgba(255,255,255,.06); border-radius:20px; }
body.v2 .row { padding:12px 0; }
/* "make imsurance same like subscriptions": the Subscriptions list draws its row icons with
   .v2li-i on a neutral #262842, while .subico carried 1.0's literal teal wash - which is why the
   policy icons read green and ignored the accent. Same size AND same fill as the list he is
   comparing it to. 1.0 keeps its teal; this is scoped to body.v2. */
body.v2 .subico { width:36px; height:36px; border-radius:8px; font-size:15px; background:#262842; }
body.v2 .row .t { font-size:13.5px; font-weight:600; }
body.v2 .row .d { font-size:11.5px; color:#9294a8; }
body.v2 .row .amt { font-size:13.5px; font-weight:700; }
body.v2 .subtabs, body.v2 .subtabs-sm { background:#171824; border:1px solid rgba(255,255,255,.06); border-radius:8px; padding:2px; }
/* "size too small for tabs" - 11.5px is below what anyone should have to read to find their way
   around, and these are the app's main navigation inside a tracker. 14px, and a 40px row so they
   are a real target rather than a strip of small print. */
body.v2 .subtab { border-radius:6px; font-size:14px; font-weight:700; color:var(--quiet);
  min-height:40px; padding:8px 10px; }
body.v2 .subtab.on { background:linear-gradient(135deg,var(--g1),var(--g2)); color:#fff; }
body.v2 .kpi { background:#171824; border:1px solid rgba(255,255,255,.06); border-radius:14px; padding:12px; text-align:center; }
body.v2 .kpi .k { font-size:9px; font-weight:600; letter-spacing:.8px; text-transform:uppercase; color:var(--quiet); }
body.v2 .h3-total { background:linear-gradient(90deg,var(--g2),var(--g3)); -webkit-background-clip:text; background-clip:text; color:transparent; }
body.v2 .h3-total.neg { background:none; -webkit-background-clip:initial; background-clip:initial; color:#ef4444; }
body.v2 .catchip { border-radius:5px; font-size:9px; letter-spacing:.3px; }
body.v2 .btn { background:linear-gradient(135deg,var(--g1),var(--g2)); color:#fff; border-radius:12px; }
/* "make it according to ui ux design style" — Switch/Settings/Alerts, Cloud sync/Sign out, Manage
   subscription/seats, Set a passcode and the member actions were all a hairline outline drawn on
   nothing, which reads as an empty input, not a button. Everything else in 2.0 fills its surface
   (.card, .kpi above), so the secondary button gets a real tinted face, a top highlight and a
   shadow that lifts it off the card it sits on. The tint is written through --teal-rgb so the
   accent picker still owns the colour — a hard-coded violet would strand every other palette. */
body.v2 .btn.ghost {
  background:linear-gradient(180deg, rgba(255,255,255,.055), rgba(255,255,255,.012)),
             rgba(var(--teal-rgb,45,212,191),.10);
  border:1px solid rgba(var(--teal-rgb,45,212,191),.26);
  color:var(--g2); border-radius:12px; font-weight:700;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07), 0 3px 10px rgba(0,0,0,.28);
}
body.v2 .btn.ghost:active { transform:scale(.97); filter:brightness(1.14); }
/* the destructive twins (Sign out, Delete my cloud copy, Remove <member>) mark themselves with an
   inline color:var(--neg); that inline colour keeps winning, so only the face needs to turn red. */
body.v2 .btn.ghost[style*="--neg"] {
  background:linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.012)), rgba(239,68,68,.11);
  border-color:rgba(239,68,68,.30);
}
/* "fromdesing plus button is not same" — the design file's FAB is a CIRCLE with a coloured glow,
   ours was a 17px-radius squircle with a dark glyph. His numbers, verbatim from the design:
   50px, border-radius:50%, white 24px/300 glyph, box-shadow 0 4px 20px at .4 alpha.
   The shadow colour tracks the accent picker rather than being hard-coded purple, so a user on a
   different accent still gets a glow that belongs to their palette. */
body.v2 .fab { width:50px; height:50px; border-radius:50%; color:#fff; font-size:24px; font-weight:300;
  background:linear-gradient(135deg,var(--g1),var(--g3));
  box-shadow:0 4px 20px rgba(var(--teal-rgb),.4); }
/* "also the animation not same it has a boncy animation when clicked all over the desings" — the
   design's easing is cubic-bezier(.34,1.56,.64,1): the 1.56 overshoots past the end point and
   settles back, which is the bounce. A plain ease just glides. Applied to the press on everything
   tappable in 2.0, not only the FAB, because that is what "all over the designs" means. */
/* "clicking Plus sign doe es not give bouncy animations" — and he was right, even though the
   easing was correct. `:active` only holds while the finger is DOWN. A tap is ~80ms; the .3s
   transition had barely left the starting position before release, then eased back from almost
   nowhere. What you saw was nothing.
   A keyframe runs to completion regardless of how briefly you touched it, so the squash and the
   overshoot both actually happen. The class is added on pointerdown and removed when the animation
   ends (see bounceTap). */
body.v2 .fab { transition:opacity .18s ease, box-shadow .3s ease; }
/* TONED DOWN on his "make it less bouncy animation" (2026-08-15). He asked for the bounce in the
   first place and has not asked for it gone, so this is a smaller version of the same thing, not
   its removal: the press still reads as a press.
   The dial is one variable rather than the eight scattered copies of .34,1.56,.64,1 it replaces —
   1.56 overshot 56% past the end point, 1.18 just past it. Turning the bounce up or down again is
   now one number in one place instead of a hunt. */
body.v2 { --v2-spring:cubic-bezier(.34,1.18,.64,1); }
@keyframes v2bounce {
  0%   { transform:scale(1); }
  35%  { transform:scale(.94); }   /* was .86 — a 14% squash on a small control is a lurch */
  70%  { transform:scale(1.02); }  /* was 1.06 */
  100% { transform:scale(1); }
}
body.v2 .bouncing { animation:v2bounce .26s var(--v2-spring); }
/* a wide row squashing reads as the page twitching, so it stays gentler still */
@keyframes v2bounceRow {
  0%   { transform:scale(1); }
  35%  { transform:scale(.99); }
  70%  { transform:scale(1.004); }
  100% { transform:scale(1); }
}
body.v2 .row.bouncing, body.v2 .li-row.bouncing, body.v2 .card.bouncing { animation:v2bounceRow .24s var(--v2-spring); }
@media (prefers-reduced-motion: reduce) { body.v2 .bouncing { animation:none; } }
body.v2 .btn, body.v2 .chip, body.v2 .tile, body.v2 .row, body.v2 .li-row, body.v2 .sel-opt,
body.v2 .qa-opt, body.v2 .tab, body.v2 .v2minitile, body.v2 .sx-sh, body.v2 .rx-tile,
body.v2 .modal-x, body.v2 .v2st, body.v2 [role="button"] {
  transition:transform .22s var(--v2-spring), background .2s ease, border-color .2s ease, color .2s ease; }
body.v2 .btn:active, body.v2 .chip:active, body.v2 .tile:active, body.v2 .row:active,
body.v2 .li-row:active, body.v2 .sel-opt:active, body.v2 .qa-opt:active, body.v2 .tab:active,
body.v2 .v2minitile:active, body.v2 .sx-sh:active, body.v2 .rx-tile:active, body.v2 .modal-x:active,
body.v2 .v2st:active, body.v2 [role="button"]:active { transform:scale(.975); }
/* rows are wide — a 4% squeeze on a full-width row reads as the whole page twitching, so they get
   a gentler one than the small controls do */
body.v2 .row:active, body.v2 .li-row:active { transform:scale(.985); }
@media (prefers-reduced-motion: reduce) {
  body.v2 .fab, body.v2 .btn, body.v2 .chip, body.v2 .tile, body.v2 .row, body.v2 .li-row,
  body.v2 .sel-opt, body.v2 .qa-opt, body.v2 .tab, body.v2 .v2minitile, body.v2 .rx-tile { transition:none; }
  body.v2 .fab:active, body.v2 .btn:active, body.v2 .chip:active, body.v2 .tile:active,
  body.v2 .row:active, body.v2 .li-row:active { transform:none; }
}
body.v2 .bd-bar { height:6px; border-radius:3px; background:#262842; }
body.v2 .bd-fill.good { background:linear-gradient(90deg,var(--g1),var(--g2)); }
body.v2 .strow-hd .fav { border-radius:10px; }
body.v2 .sxp-date b { color:var(--g2); }

/* v295i — per-person shares on S&S rows (VN: "each person's amount, make it good") */
/* was nowrap+ellipsis, so the third person vanished as "Eka…" — the whole point of the line is
   that everyone can see their own number. Let it wrap to a second line instead. */
.sxp-shares { font-size:10.5px; color:var(--dim); margin-top:2px; line-height:1.4; }

/* v295j — equal-split member ticks */
.sp-eq { cursor:pointer; }
.sp-eq input[type=checkbox] { width:19px; height:19px; accent-color:var(--teal); }

/* v295k — PHASE D home: mockup structure on the 2.0 home (welcome-back hero, gradient-border
   safe card, coach pill, compact health row) */
body.v2 .th-greet { font-size:25px; font-weight:800; letter-spacing:-.4px; }
body.v2 .th-date { font-size:12.5px; color:#9294a8; text-transform:none; letter-spacing:.2px; }
body.v2 .hero-card { border:1px solid transparent;
  background:linear-gradient(#141520,#141520) padding-box, linear-gradient(90deg,var(--g1),var(--g2),var(--g3)) border-box; }
body.v2 .coachcard { border-radius:26px; }
body.v2 .coachcard .ai { width:44px; height:44px; border-radius:50%; font-size:20px;
  background:linear-gradient(135deg,rgba(var(--g1-rgb),.4),rgba(236,72,153,.32));
  display:flex; align-items:center; justify-content:center; }
body.v2 .hcard { padding:13px 14px; }

/* v295l — exactness pass (his order: follow the file, not my taste): the mockup's big
   money numbers are WHITE (see its subs page) — no gradient text. Appended last = wins. */
body.v2 .h3-total { background:none; -webkit-background-clip:initial; background-clip:initial; color:#f1f1f6; }
body.v2 .h3-total.neg { color:#ef4444; }
body.v2 .hero-num { color:#f1f1f6; }
/* was #c4b5fd - the mockup's lilac, hard-coded, so the biggest number on Home ignored the accent
   entirely: pick Green and everything turns green except the one figure you look at first. It
   follows the accent now, and on the default violet it still lands on that lilac. */
body.v2 .hero-card .hero-num { color:var(--g3, #c4b5fd); }
/* 2.0 Phase D1 — Health page to the mockup's pg-health: centered 120px ring,
   Habits/History sub-tabs, habit bars with weight·target captions, 12-bar history. */
body.v2 .hl-hero { text-align:center; margin:14px 0 16px; }
body.v2 .hl-ring { position:relative; width:120px; height:120px; margin:0 auto; }
body.v2 .hl-ring svg { width:120px; height:120px; transform:rotate(-90deg); }
body.v2 .hl-bg { fill:none; stroke:rgba(255,255,255,.07); stroke-width:7; }
body.v2 .hl-fg { fill:none; stroke-width:7; stroke-linecap:round; transition:stroke-dashoffset .6s; }
body.v2 .hl-num { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; }
body.v2 .hl-score { font-size:38px; font-weight:900; line-height:1; }
body.v2 .hl-grade { font-size:13.5px; font-weight:700; opacity:.75; margin-top:2px; }
body.v2 .hl-focus { font-size:12.5px; opacity:.7; margin-top:6px; cursor:pointer; }
body.v2 .hb { margin-bottom:12px; }
body.v2 .hb:last-child { margin-bottom:2px; }
body.v2 .hb-top { display:flex; justify-content:space-between; align-items:center; margin-bottom:4px; gap:8px; }
body.v2 .hb-n { font-size:12.5px; font-weight:600; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
body.v2 .hb-s { font-size:12.5px; font-weight:700; flex:0 0 auto; }
body.v2 .hb-pb { height:6px; border-radius:4px; background:rgba(255,255,255,.07); overflow:hidden; }
body.v2 .hb-pf { height:100%; border-radius:4px; }
body.v2 .hb-cap { font-size:9px; opacity:.55; margin-top:3px; }
body.v2 .hl-bars { display:flex; align-items:flex-end; justify-content:center; gap:8px; height:96px; margin-top:8px; }
body.v2 .hl-col { flex:1; max-width:40px; display:flex; flex-direction:column; align-items:center; justify-content:flex-end; height:100%; gap:3px; }
body.v2 .hl-col .hl-val { font-size:10.5px; font-weight:700; color:var(--quiet); }
body.v2 .hl-bar { width:100%; border-radius:3px 3px 0 0; min-height:4px; }

/* 2.0 Phase D2 — Profile page: mockup's centered header, 12-row menu, Family Pro card */
body.v2 .pf-head { text-align:center; cursor:pointer; }
body.v2 .pf-soon { font-size:9px; font-weight:800; letter-spacing:.4px; text-transform:uppercase; background:rgba(var(--teal-rgb),.16); color:#c084fc; border-radius:6px; padding:1.5px 6px; margin-left:5px; vertical-align:2px; }
body.v2 .pf-pro { margin-top:10px; text-align:center; cursor:pointer; background:linear-gradient(135deg,rgba(var(--g1-rgb),.10),rgba(236,72,153,.10)); border:1px solid rgba(var(--teal-rgb),.25); }
body.v2 .pf-pro-t { font-size:13.5px; font-weight:800; margin-bottom:3px; }
body.v2 .pf-pro-p { font-size:22px; font-weight:900; margin-top:6px; }
body.v2 .pf-pro-p span { font-size:11.5px; font-weight:500; opacity:.65; }

/* 2.0 Phase D5 — global chrome to the mockup's nav (MUSE NEXUS mark, his rule — not MQ):
   brand block left, 🔔 🔍 ⚙ + avatar right, tabbar gradient pip on the active tab. */
/* "top pavel needs to br normal", with a photo of the logo clipped at the top and hanging out the
   bottom. A FIXED height:56px was fighting the base bar's padding of `env(safe-area-inset-top) +
   14px` top / 12px bottom: on a phone the notch inset is real, so 56px minus that padding left
   barely any interior and the 48px logo spilled out both ends. On desktop the inset is 0px, which
   is exactly why this looked right on my screen and wrong on his.
   The inset belongs ABOVE the bar, not inside it - content-box + min-height keeps the bar a true
   56px under any inset, which is what his design file specifies: .nav{height:56px;padding:0 16px}. */
body.v2 #topbar { background:#0a0b0f; border-bottom:1px solid rgba(255,255,255,.06);
  height:auto; min-height:calc(env(safe-area-inset-top, 0px) + 56px);
  padding:env(safe-area-inset-top, 0px) 16px 0; }
/* NB: keep the global border-box here. Switching this element to content-box does fix the height,
   but the bar is `position:fixed; width:100%`, so its 16px side padding then lands OUTSIDE the
   412px width - the whole lockup slides to the screen edge. Carrying the inset in min-height
   instead gets the same 56px interior with the width still correct. */
/* the whole .topbtns container must go, not just its buttons — it keeps flex:1 1 0 and steals half the bar */
body.v2 #brand-logo, body.v2 #topbar .brand, body.v2 #topbar .topbtns { display:none; }
body.v2 #btn-back { background:none; border:none; color:var(--g2); font-size:20px; padding:2px 6px 2px 0; }
/* "top bar - button comes out, even brand name too - make it sync". The brand was white-space:
   nowrap with nothing allowed to shrink, while the buttons are fixed circles. Android's WebView
   scales px TEXT with the system font size but not those fixed widths, so the brand grew until it
   pushed the row apart. Now the brand is the only thing that gives (it ellipsizes), the buttons
   never shrink, and the row cannot wrap - so the bar looks the same at every text size. */
body.v2 #topbar { flex-wrap:nowrap; }
body.v2 .v2nav-l { display:flex; align-items:center; gap:8px; min-width:0; flex:1 1 auto; overflow:hidden; }
/* "increase size of logos, the whole line is not good" - the mark and the wordmark were sized to
   tuck under the icon row rather than to be read. Bigger mark, bigger lockup, and the row gets the
   height to carry them. */
body.v2 .v2nav-logo { width:48px; height:48px; border-radius:8px; overflow:hidden; background:linear-gradient(135deg,var(--g1),var(--g3)); flex:0 0 auto; }
body.v2 .v2nav-logo img { width:100%; height:100%; object-fit:cover; display:block; }
body.v2 .v2nav-brand { font-size:14.5px; font-weight:800; letter-spacing:1.5px; text-transform:uppercase; opacity:.9; line-height:1.2; white-space:nowrap; min-width:0; overflow:hidden; text-overflow:ellipsis; }
body.v2 .v2nav-brand span { display:block; font-size:12px; letter-spacing:2px; color:var(--g2); margin-top:-1px; }
body.v2 .v2nav-r { display:flex; align-items:center; gap:4px; flex:0 0 auto; }
body.v2 .v2nav-btn { flex:0 0 auto; }
body.v2 .v2nav-btn { width:38px; height:38px; border-radius:50%; border:1px solid rgba(255,255,255,.08); background:#171824; font-size:13.5px; cursor:pointer; display:flex; align-items:center; justify-content:center; position:relative; padding:0; }
body.v2 .v2nav-av { width:38px; height:38px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:11.5px; font-weight:800; color:#fff; cursor:pointer; flex:0 0 auto; }
body.v2 #tabbar .tab.active { background:none; position:relative; }
body.v2 #tabbar .tab.active::before { content:''; position:absolute; top:0; left:50%; transform:translateX(-50%); width:16px; height:2px; border-radius:2px; background:linear-gradient(90deg,var(--g1),var(--g3)); }

/* 2.0 Phase D3 — Home strips: savings-goal jars (mockup .jar) + Coming Up row */
body.v2 .jar-strip { display:flex; gap:12px; overflow-x:auto; padding:4px 2px 10px; scrollbar-width:none; }
body.v2 .jar-strip::-webkit-scrollbar { display:none; }
body.v2 .jar-cell { flex:0 0 auto; width:96px; text-align:center; cursor:pointer; }
body.v2 .jar { position:relative; width:70px; height:100px; margin:0 auto 6px; border:3px solid rgba(255,255,255,.08); border-top:none; border-radius:0 0 14px 14px; background:#262842; overflow:hidden; }
body.v2 .jar::before { content:''; position:absolute; top:-7px; left:-3px; right:-3px; height:9px; border:3px solid rgba(255,255,255,.08); border-radius:4px; background:#262842; }
body.v2 .jar-f { position:absolute; bottom:0; left:0; right:0; background:linear-gradient(to top,var(--g1),var(--g3)); border-radius:0 0 11px 11px; transition:height .6s; }
body.v2 .jar-p { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:13.5px; font-weight:800; text-shadow:0 1px 4px rgba(0,0,0,.5); }
/* Jar cells sit in a fixed grid, so these wrap to two lines and stop — free wrapping would
   let one long goal name stretch its cell and knock the row out of alignment. Two lines shows
   the whole of almost every real goal name; ellipsis showed about half. */
body.v2 .jar-n { font-size:11.5px; font-weight:600; white-space:normal; overflow-wrap:anywhere; line-height:1.25;
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
body.v2 .jar-d { font-size:9px; opacity:.55; margin-top:1px; white-space:normal; overflow-wrap:anywhere; line-height:1.3;
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
/* "saving goal on.home page- doe s ot.spefiy- is it budget goal or family b7dget goal? kids goall?"
   — the source of each jar, as a small accent-tinted tag. It sits between the name and the numbers
   because the question it answers ("whose goal is this?") comes before "how much". */
body.v2 .jar-src { display:inline-block; font-size:8px; font-weight:700; letter-spacing:.4px;
  text-transform:uppercase; margin-top:3px; padding:1.5px 5px; border-radius:5px;
  color:var(--g2); background:rgba(var(--teal-rgb,45,212,191),.14); }
body.v2 .cu-card { padding:12px 10px; }
body.v2 .cu-strip { display:flex; gap:14px; overflow-x:auto; scrollbar-width:none; }
body.v2 .cu-strip::-webkit-scrollbar { display:none; }
body.v2 .cu-cell { flex:0 0 auto; width:64px; text-align:center; cursor:pointer; }
body.v2 .cu-ic { width:36px; height:36px; margin:0 auto 4px; border-radius:50%; background:#171824; border:1px solid rgba(255,255,255,.08); display:flex; align-items:center; justify-content:center; font-size:15px; }
body.v2 .cu-a { font-size:12.5px; font-weight:700; }
body.v2 .cu-a.good { color:#22c55e; }
body.v2 .cu-d { font-size:9px; color:#f59e0b; font-weight:600; margin-top:1px; }

/* 2.0 Phase D4 — tracker tiles to the mockup's .cd cards: 38px tinted icon chips
   (mockup's exact color map per tracker), 18px values, 9px sub-lines. v2 only. */
body.v2 .tile { display:block; background:#171824; border:1px solid rgba(255,255,255,.06); border-radius:20px; padding:16px 14px; }
body.v2 .tile .ic { float:none; width:38px; height:38px; border-radius:8px; display:flex; align-items:center; justify-content:center; font-size:18px; margin:0 0 10px; background:rgba(var(--teal-rgb),.10); position:relative; }
/* "follow accent" - these were four fixed tints (green/blue/red/amber) per tracker, so five
   of your tiles stayed blue and green no matter which accent you picked. One tint now, from
   the accent, at the same 10% strength the fixed ones used. */
body.v2 .tile .ic { background:rgba(var(--teal-rgb,168,85,247),.10) !important; }
body.v2 .tile .nm { font-size:11.5px; font-weight:600; color:#9294a8; margin-bottom:3px; }
body.v2 .tile .vv { font-size:18px; font-weight:800; letter-spacing:-.4px; line-height:1.1; margin-bottom:2px; }
body.v2 .tile .ds { font-size:9px; color:var(--quiet); line-height:1.3; }

/* 2.0 replica pass — the mockup's motion + micro-layouts ("same animations · same look") */
@keyframes v2wave { 0%,60%,100%{transform:rotate(0)} 10%{transform:rotate(14deg)} 20%{transform:rotate(-8deg)} 30%{transform:rotate(14deg)} 40%{transform:rotate(-4deg)} 50%{transform:rotate(10deg)} }
body.v2 .greet-w { display:inline-block; animation:v2wave 2.5s ease-in-out infinite; transform-origin:70% 70%; }
@keyframes v2fadeUp { from{opacity:0; transform:translateY(12px)} to{opacity:1; transform:translateY(0)} }
body.v2 #view > * { animation:v2fadeUp .3s ease both; }
body.v2 #tabbar .tab .ti { opacity:.4; transition:opacity .2s; }
body.v2 #tabbar .tab.active .ti { opacity:1; }
body.v2 #tabbar .tab span:last-child { font-size:9px; font-weight:700; letter-spacing:.4px; }
body.v2 .hero-nudge { display:flex; align-items:center; gap:9px; background:rgba(255,255,255,.045); border-radius:14px; padding:11px 13px; margin-top:10px; cursor:pointer; }
body.v2 .hero-nudge .hn-ic { width:30px; height:30px; border-radius:50%; background:rgba(245,158,11,.14); display:flex; align-items:center; justify-content:center; font-size:15px; flex:0 0 auto; }
body.v2 .hero-nudge .hn-t { flex:1; min-width:0; font-size:12.5px; font-weight:600; line-height:1.35; }
body.v2 .hmini { display:flex; align-items:center; gap:12px; }
body.v2 .hm-ring { position:relative; width:44px; height:44px; flex:0 0 auto; }
body.v2 .hm-ring svg { width:44px; height:44px; transform:rotate(-90deg); }
body.v2 .hm-bg { fill:none; stroke:rgba(255,255,255,.07); stroke-width:4; }
body.v2 .hm-fg { fill:none; stroke-width:4; stroke-linecap:round; }
body.v2 .hm-ring.good .hm-fg { stroke:#22c55e; } body.v2 .hm-ring.gold .hm-fg { stroke:#f59e0b; } body.v2 .hm-ring.bad .hm-fg { stroke:#ef4444; }
body.v2 .hm-num { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:13.5px; font-weight:800; }
body.v2 .hm-body { flex:1; min-width:0; }
body.v2 .hm-t { font-size:13.5px; font-weight:800; }
body.v2 .hm-t b.good { color:#22c55e; } body.v2 .hm-t b.gold { color:#f59e0b; } body.v2 .hm-t b.bad { color:#ef4444; }
body.v2 .hm-f { font-size:11.5px; opacity:.65; margin-top:2px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* 2.0 inner pages — Budget header to the mockup's pg-budget: 32px hero, remaining line,
   10px gradient progress, three stat boxes (Spent / Remaining / Daily pace). */
body.v2 .bud-hero-num { font-size:32px; font-weight:900; letter-spacing:-1px; margin-bottom:2px; }
body.v2 .bud-hero-sub { font-size:12.5px; opacity:.7; margin-bottom:10px; }
body.v2 .bud-hero-sub .good { color:#22c55e; } body.v2 .bud-hero-sub .bad { color:#ef4444; }
body.v2 .bud-pb { height:10px; border-radius:5px; background:#262842; overflow:hidden; }
body.v2 .bud-pf { height:100%; border-radius:5px; background:linear-gradient(90deg,#22c55e,#f59e0b); transition:width .6s; }
body.v2 .bud-sr { display:flex; gap:8px; margin:10px 0 4px; }
/* "ui coming off" - flex:1 still means min-width:AUTO, so a stat box could not shrink below its
   own price and three futures prices (C$22,850.00) pushed the third box off the screen. min-width:0
   lets them share the row; the value scales down instead of escaping and never wraps mid-number. */
body.v2 .bud-sb { flex:1 1 0; min-width:0; background:#171824; border:1px solid rgba(255,255,255,.06); border-radius:14px; padding:12px 6px; text-align:center; }
body.v2 .bud-sl { font-size:9px; font-weight:600; letter-spacing:.8px; text-transform:uppercase; opacity:.55; margin-bottom:4px; }
/* no ellipsis: fitKpiValues() shrinks-then-wraps these, and a clipped figure reads as a real,
   wrong number ("C$25,55…") */
body.v2 .bud-sv { font-size:clamp(12px, 4.4vw, 18px); font-weight:800; letter-spacing:-.4px; white-space:nowrap; }

/* "add debts squeeze to make non scrollable please" — the compact form sheet. Tighter rows and a
   smaller date-hint, and the redundant bottom Close goes (the ✕ and the backdrop both close a
   sheet). Numbers from the Add-debt form, the longest one in the app. */
body.v2 .modal.sheet-compact { padding-top:14px; }
body.v2 .modal.sheet-compact h2 { margin-bottom:6px; font-size:17px; }
body.v2 .modal.sheet-compact .field { margin-top:6px; }
body.v2 .modal.sheet-compact .field label { margin-bottom:2px; font-size:11.5px; }
body.v2 .modal.sheet-compact .field input, body.v2 .modal.sheet-compact .field select { padding:8px 12px; border-radius:11px; }
body.v2 .modal.sheet-compact .field textarea { padding:8px 12px; min-height:44px; }
body.v2 .modal.sheet-compact .hint { font-size:10.5px; margin:3px 0 4px !important; }
body.v2 .modal.sheet-compact .reqnote { margin:6px 0 0; font-size:10.5px; }
body.v2 .modal.sheet-compact .subtabs, body.v2 .modal.sheet-compact .v2chips { margin-top:4px; }
body.v2 .modal.sheet-compact > #m-ok { margin-top:10px; }
body.v2 .modal.sheet-compact > #m-x { display:none; }
body.v2 .bud-sv.good { color:#22c55e; } body.v2 .bud-sv.bad { color:#ef4444; }
body.v2 .bud-ss { font-size:10.5px; opacity:.7; margin-top:2px; }

/* Budget replica pass — the mockup's own sections, and its MUTED active tab chip
   (the gradient pill was never in the file; this later rule overrides it globally) */
body.v2 .subtab.on { background:#262842; color:#f1f1f6; }
body.v2 .v2sh { display:flex; align-items:center; justify-content:space-between; margin:14px 0 8px; }
body.v2 .v2st { font-size:10.5px; font-weight:700; letter-spacing:1.2px; text-transform:uppercase; opacity:.55; }
body.v2 .v2sa-row { display:flex; gap:6px; }
/* min-height 32px: sweep's tap-target rule — "see all ›" was a 22px sliver */
body.v2 .v2sa { font-size:11.5px; font-weight:600; color:var(--g2); background:none; border:none; cursor:pointer; padding:3px 6px; border-radius:4px; min-height:32px; display:inline-flex; align-items:center; }
body.v2 .section-h { font-size:10.5px; font-weight:700; letter-spacing:1.2px; text-transform:uppercase; opacity:.55; margin:18px 2px 8px; }
body.v2 .v2pg { margin-bottom:10px; }
body.v2 .v2pg:last-child { margin-bottom:2px; }
body.v2 .v2pg-top { display:flex; justify-content:space-between; align-items:center; margin-bottom:3px; }
body.v2 .v2pg-n { font-size:11.5px; font-weight:600; }
body.v2 .v2pg-n i { display:inline-block; width:7px; height:7px; border-radius:50%; margin-right:5px; }
body.v2 .v2pg-amt { font-size:11.5px; font-weight:700; }
body.v2 .v2pg-amt.good { color:#22c55e; } body.v2 .v2pg-amt.bad { color:#ef4444; }
/* flex so a bar can carry two segments: what is spent, then what is committed but not yet out.
   The other two v2pb bars have a single child, which lays out identically either way. */
body.v2 .v2pb { height:6px; border-radius:3px; background:#262842; overflow:hidden; margin:6px 0; display:flex; }
body.v2 .v2pf { height:100%; border-radius:3px; transition:width .6s; flex:0 0 auto; }
/* committed money is real but hasn't moved — dimmer than spent, still clearly not empty track */
body.v2 .v2pf-com { opacity:.34; }
body.v2 .v2pg-cap { font-size:9px; opacity:.5; }
body.v2 .v2bars { display:flex; align-items:flex-end; gap:2px; height:70px; padding-top:6px; }
body.v2 .v2bar { flex:1; border-radius:2px 2px 0 0; background:linear-gradient(to top,var(--g1),var(--g2)); min-width:3px; }
body.v2 .v2bars-lab { display:flex; justify-content:space-between; font-size:9px; opacity:.5; margin-top:4px; }
body.v2 .v2mini2 { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin:10px 0; }
body.v2 .v2minitile { padding:12px 10px; text-align:left; }
body.v2 .v2minitile .ic { margin-bottom:8px; }  /* same 38px icon square as the Home tiles (his: 'matching the logos') */
body.v2 .v2minitile .nm { font-size:10.5px; font-weight:700; color:inherit; margin:0; }
/* Tiles used as a CHOICE (the activity filter) need a selected state — without one the
   filter looked identical whether a tracker was picked or not. Matches .subtab.on. */
body.v2 .v2minitile.on { border:1.5px solid transparent;
  background:linear-gradient(#181a30,#181a30) padding-box, linear-gradient(90deg,var(--g1),var(--g2)) border-box; }
body.v2 .v2minitile.on .nm { color:var(--text); }
body.v2 .trkstate { margin-top:6px; display:inline-block; font-size:9.5px; font-weight:800; letter-spacing:.5px;
  border-radius:99px; padding:2px 9px; border:1px solid rgba(255,255,255,.14); color:var(--dim); text-transform:uppercase; }
body.v2 .trkstate.on { background:linear-gradient(90deg,var(--g1),var(--g2)); border-color:transparent; color:#fff; }
body.v2 .v2chips { display:flex; flex-wrap:wrap; gap:4px; margin-bottom:10px; }
body.v2 .v2chip { display:inline-block; padding:3px 9px; border-radius:16px; font-size:10.5px; font-weight:600; background:#262842; border:1px solid rgba(255,255,255,.06); cursor:pointer; }
/* Fill and border already followed the accent; the LABEL was a literal violet-400, so a selected
   chip read violet inside an otherwise green or teal dialogue. #c084fc is just a lightened --teal,
   so say that instead. The plain-accent line first is the fallback for anything without color-mix. */
body.v2 .v2chip.on { background:rgba(var(--teal-rgb),.12); border-color:rgba(var(--teal-rgb),.3);
  color:var(--teal); color:color-mix(in srgb, var(--teal) 68%, #ffffff); }
body.v2 .v2badge { display:inline-block; padding:2px 7px; border-radius:5px; font-size:9px; font-weight:700; letter-spacing:.3px; }
body.v2 .v2badge.bg { background:rgba(34,197,94,.10); color:#22c55e; }
body.v2 .v2badge.br { background:rgba(239,68,68,.10); color:#ef4444; }
body.v2 .v2alert { background:linear-gradient(135deg,rgba(245,158,11,.06),rgba(239,68,68,.06)); border-color:rgba(239,68,68,.2); }
/* "even the tabs below need to be ui ux style under our money" — the rows themselves were fine;
   what read as unfinished was the icon, a flat grey square identical on every row. It now carries
   the accent as a soft gradient tile, which is what makes a list look designed rather than dumped.
   Deliberately NO border: body.v2.light overrides these with a `background` shorthand further down,
   and a border set here would survive into the light theme where nothing resets it. */
body.v2 .v2li-i { width:36px; height:36px; border-radius:10px; display:flex; align-items:center; justify-content:center; font-size:15px; flex:0 0 auto;
  background:linear-gradient(160deg, rgba(var(--teal-rgb,45,212,191),.18), rgba(var(--teal-rgb,45,212,191),.05)), #262842; }
body.v2 .v2li-i.inc { background:rgba(34,197,94,.10); }
body.v2 .row .v2li-i + .l .t > :first-child { margin-right:0; }
/* Spending tab replica: the file shows chips → bare list. The card's own chrome
   (title, explainer, month picker, search, sort) hides behind the header's "filter". */
body.v2 #bud-expenses > h3, body.v2 #bud-expenses > .subhead { display:none; }
body.v2 #bud-expenses:not(.v2tools) .my-bar, body.v2 #bud-expenses:not(.v2tools) .search, body.v2 #bud-expenses:not(.v2tools) .sortbar { display:none; }
body.v2 #bud-expenses.v2tools > h3 { display:block; }
/* Quick Add as the file's 3-column emoji tile grid */
body.v2 .qa-grab { width:36px; height:4px; border-radius:2px; background:rgba(255,255,255,.25); margin:0 auto 14px; }
/* his file: .sheet-t{font-size:16px;font-weight:800;letter-spacing:-.3px;margin-bottom:14px} -
   left aligned, sentence case, normal text colour. Ours inherited the 1.0 .qa-title, which is
   centred, UPPERCASE and dim, so the sheet read "QUICK ADD" in grey where the design says
   "Quick Add" in white on the left. */
body.v2 .qa-title { font-size:16px; font-weight:800; letter-spacing:-.3px; margin-bottom:14px;
  text-transform:none; text-align:left; color:var(--text); }
/* 1fr means minmax(AUTO,1fr), so a column can never shrink below its longest label - "Choose
   bottom-bar tabs & order" dragged one column out to 176px, squeezed another to 78px and pushed
   three tiles clean off the right of a 360px phone. minmax(0,1fr) makes them equal thirds and
   lets the labels wrap instead. */
body.v2 .qa-grid { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:8px; }
body.v2 .qa-grid > * { min-width:0; overflow-wrap:break-word; }
body.v2 .qa-tile { background:#171824; border:1px solid rgba(255,255,255,.06); border-radius:20px; padding:14px 6px; text-align:center; cursor:pointer; color:inherit; }
body.v2 .qa-tile:active { background:#21223a; }
body.v2 .qa-ti { display:block; font-size:22px; margin-bottom:4px; }
/* two lines beat "Customize das..." - once the columns stopped overflowing there is room to
   wrap, and a tile the user cannot read is no better than one off the screen. */
/* his file: font-size:10px;font-weight:600;color:var(--t2). --t2 is #9294a8, which is the same
   mid-grey that fails on white, so this uses --quiet - the readable equivalent in both themes. */
body.v2 .qa-tl { display:block; font-size:10px; font-weight:600; color:var(--quiet); white-space:normal;
  overflow-wrap:break-word; line-height:1.22; }
body.v2 .v2permo { font-size:13.5px; font-weight:600; opacity:.55; margin-left:2px; }
/* bare .li list card + rows (the file's list language) */
body.v2 .v2list { padding:4px 14px; }
body.v2 .li-row { display:flex; align-items:center; gap:12px; padding:12px 0; border-bottom:1px solid rgba(255,255,255,.06); cursor:pointer; }
body.v2 .li-row:last-child { border-bottom:none; }
body.v2 .li-row .l { flex:1; min-width:0; }
/* Wraps to two lines instead of ellipsising. A notification title losing 164px — "2 subscriptions
   raised price…" — throws away the only part that says what happened, which is the whole reason
   the row exists. Clamped rather than free, so a long title still can't push a list row to four
   lines and shove everything below it off the screen. Same call as the v322 subtab fix. */
body.v2 .li-row .t { font-size:13.5px; font-weight:600;
  white-space:normal; overflow-wrap:anywhere; line-height:1.3;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
body.v2 .li-row .d { font-size:11.5px; opacity:.6; margin-top:1px; }
body.v2 .li-row .r { text-align:right; flex:0 0 auto; display:flex; flex-direction:column; align-items:flex-end; gap:3px; }
body.v2 .li-row .amt { font-size:13.5px; font-weight:700; }
body.v2 .v2badge.by { background:rgba(245,158,11,.10); color:#f59e0b; }
body.v2 .v2badge.bb { background:rgba(59,130,246,.10); color:#3b82f6; }

/* 2.0 forms — every sheet + input wears the file's language ("all input designs are old style") */
/* "dailogue box colour not matches accent colour even theme". Two separate faults in this one line:
   the sheet colour was the literal #111219, so (a) switching accent moved everything except the
   dialogue, and (b) this rule sits AFTER `body.light .modal{background:#FFFFFF}` at equal
   specificity, so it won on order and every 2.0 sheet stayed near-black on the light theme.
   --sheet was already declared next to --card and used by nothing; it is the theme half now, and
   the accent half is a tint off --g1-rgb, which applyAccent() rewrites with the swatch. */
body.v2 .modal { background:linear-gradient(180deg, rgba(var(--g1-rgb),.13), rgba(var(--g1-rgb),0) 230px), var(--sheet);
  border:1px solid rgba(var(--g1-rgb),.22); border-radius:20px 20px 0 0; }
body.v2 .modal h3 { font-size:17px; font-weight:800; letter-spacing:-.3px; }
body.v2 .field label { font-size:12.5px; font-weight:600; letter-spacing:0; text-transform:none; opacity:.7;
  margin-bottom:3px; line-height:1.25; }
/* "squeeze this screen to not a scroll", second pass. Shortening the labels (v514) stopped them
   wrapping - visible in his shot - but the sheet still did not fit, because I measured its HEIGHT
   and never checked where it STARTS: it opens part-way down the screen, so 775px of sheet from
   250px down still runs off. The room has to come from the rows themselves. */

body.v2 .field input, body.v2 .field select, body.v2 .field textarea { background:#171824; border:1px solid rgba(255,255,255,.08); border-radius:14px; padding:11px 14px; font-size:13.5px; color:#f1f1f6; transition:border-color .2s; }
body.v2 .field input:focus, body.v2 .field select:focus, body.v2 .field textarea:focus { border-color:var(--g2); }
body.v2 .field input::placeholder, body.v2 .field textarea::placeholder { color:var(--quiet); }
body.v2 .modal::before { content:''; display:block; width:36px; height:4px; border-radius:2px; background:rgba(var(--g1-rgb),.45); margin:2px auto 12px; }
body.v2 .modal #ff-amt { font-size:26px; font-weight:800; letter-spacing:-.5px; }
body.v2 .v2selwrap select { display:none; }
body.v2 .v2selwrap .ffchips { margin:2px 0 4px; }
body.v2 .ffchips .v2chip { font-size:11.5px; padding:5px 11px; }
/* the settings-row On/Off toggles measured 29px tall at 360dp (sweep finding: "On → 29px in
   r/flex") and sit alone in their row — nothing adjacent catches a near-miss tap. 32px is the
   floor for a control a thumb has to hit. Scoped to the li-row right slot so buttons elsewhere
   keep their size. */
body.v2 .li-row .r .btn.small { min-height:32px; }
/* type-scale audit vs the file: card titles are 12px/700 there, not the 1.0 15px */
body.v2 .card > h3 { font-size:12.5px; font-weight:700; letter-spacing:.2px; }
body.v2 .card > h3 .h3-total { font-size:26px; }
body.v2 .v2stack { display:flex; height:12px; border-radius:6px; overflow:hidden; gap:2px; }
body.v2 .subnav { background:#171824; border:1px solid rgba(255,255,255,.06); border-radius:8px; padding:2px; }
body.v2 .subnav button { color:var(--quiet); font-size:11.5px; font-weight:600; border-radius:6px; background:transparent; border:none; }
body.v2 .subnav button.on { background:#262842; color:#f1f1f6; }
body.v2 .bud-hero-num.bad { color:#ef4444; }
body.v2 .v2streak { display:flex; gap:3px; margin-bottom:6px; }
body.v2 .v2sq { flex:1; height:28px; border-radius:5px; display:flex; align-items:center; justify-content:center; font-size:13.5px; }
body.v2 .v2sq.w { background:rgba(34,197,94,.14); color:#22c55e; }
body.v2 .v2sq.x { background:rgba(239,68,68,.12); color:#ef4444; }
/* the file's greeting is 👋 + name + time·date ONLY — no briefing line, no KPI strip */
body.v2 .today-hero .th-brief, body.v2 .today-hero .th-kpis { display:none; }
/* the file's search bar: icon outside a transparent input, one bordered container */
body.v2 .v2search { display:flex; align-items:center; gap:8px; background:#171824; border:1px solid rgba(255,255,255,.06); border-radius:14px; padding:10px 14px; margin-bottom:12px; }
body.v2 .v2search-ic { opacity:.5; font-size:13px; }
body.v2 .v2search input { flex:1; background:transparent; border:none; padding:0; font-size:13px; color:#f1f1f6; outline:none; }
body.v2 .v2search input::placeholder { color:var(--quiet); }
/* kid home per the file: big greeting + the lime-green SAVED card */
body.v2 .v2kid-greet { display:flex; align-items:center; gap:6px; margin-top:14px; }
body.v2 .v2kid-name { font-size:20px; font-weight:800; letter-spacing:-.4px; }
body.v2 .v2kid-saved { text-align:center; background:linear-gradient(135deg,rgba(132,204,22,.10),rgba(34,197,94,.10)); border-color:rgba(132,204,22,.2); }
body.v2 .v2kid-amt { font-size:36px; font-weight:900; letter-spacing:-1px; color:var(--teal); }
/* the file's goals page: 3-column jar grid */
/* 1fr never shrinks below the label's min-content, so long goal names pushed the third
   column off-screen (his shot: "New car" / "Financial indep…" clipped). minmax(0,1fr) lets
   the columns shrink and the labels ellipsize instead. */
body.v2 .v2jargrid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px; margin-bottom:16px; }
body.v2 .v2jarcell { text-align:center; cursor:pointer; min-width:0; }
body.v2 .v2jar-n { font-size:11px; font-weight:700; margin-top:4px; white-space:normal; overflow-wrap:anywhere; line-height:1.25;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
body.v2 .v2jar-d { font-size:9px; opacity:.5; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* the file's achievements: 3-column badge grid, earned vs locked */
body.v2 .v2achgrid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; }
body.v2 .v2ach { text-align:center; padding:14px 6px; }
body.v2 .v2ach.off { opacity:.45; }
body.v2 .v2ach-ic { font-size:24px; margin-bottom:4px; }
body.v2 .v2ach-n { font-size:10px; font-weight:700; }
body.v2 .v2ach-s { font-size:8px; opacity:.7; margin-top:2px; overflow:hidden; text-overflow:ellipsis; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }
body.v2 .v2ach-s.good { color:#22c55e; opacity:1; }
body.v2 .v2fut-num { font-size:40px; font-weight:900; letter-spacing:-.05em;
  /* was -2px. That tracking is tuned for the 40px hero figure, but the class is reused at
     ~18px for the name on Profile & account, where a fixed -2px crushes the letters into each
     other - his "romeo appears so tighted up". -.05em is the same look at 40px and scales. */ background:linear-gradient(135deg,#f1f1f6,var(--g2)); -webkit-background-clip:text; background-clip:text; color:transparent; }

body.v2 .v2rev-num { font-size:36px; font-weight:900; letter-spacing:-2px; background:linear-gradient(135deg,var(--g2),var(--g3)); -webkit-background-clip:text; background-clip:text; color:transparent; }

body.v2 .v2rec { background:linear-gradient(135deg,rgba(var(--g1-rgb),.08),rgba(236,72,153,.08)); border-color:rgba(var(--teal-rgb),.2); cursor:pointer; }
body.v2 .v2rec-lbl { font-size:10px; font-weight:700; letter-spacing:1px; text-transform:uppercase; color:var(--g2); margin-bottom:6px; }

body.v2 .v2coach-lock { font-size:9px; opacity:.5; }
body.v2 .coach-card { background:#111219; }

body.v2 .v2hhcode { font-size:22px; font-weight:900; letter-spacing:4px; color:var(--g2); }
body.v2 .card > h3:empty { display:none; }

/* the file's pg-plans tiers */
body.v2 .v2plan { margin-bottom:10px; }
body.v2 .v2plan.pop { border-color:rgba(var(--teal-rgb),.45); background:linear-gradient(135deg,rgba(var(--g1-rgb),.06),rgba(236,72,153,.06)); }
body.v2 .v2plan-b { display:inline-block; padding:2px 7px; border-radius:4px; font-size:8px; font-weight:800; letter-spacing:1px; text-transform:uppercase; background:rgba(var(--teal-rgb),.12); color:var(--g2); margin-bottom:6px; }
body.v2 .v2plan-n { font-size:14px; font-weight:800; margin-bottom:2px; }
body.v2 .v2plan-p { font-size:28px; font-weight:900; letter-spacing:-1px; }
body.v2 .v2plan-p .per { font-size:12px; font-weight:500; opacity:.6; }
body.v2 .v2plan-f { margin-top:12px; display:flex; flex-direction:column; gap:5px; }
body.v2 .v2plan-fi { font-size:11px; opacity:.75; display:flex; align-items:center; gap:6px; }
body.v2 .v2plan-fi::before { content:'✓'; color:#22c55e; font-weight:700; font-size:12px; }
body.v2 .v2plan-cur { margin-top:10px; text-align:center; font-size:12px; font-weight:700; color:#22c55e; }

body.v2 .li-row.v2unread { background:rgba(var(--teal-rgb),.05); }
body.v2 .v2when { font-size:9px; opacity:.5; }
/* The file marks an unread notification with a purple dot in the right-hand slot and a read one
   with a chevron. Small and solid, so a column of them scans as "those four are new" at a
   glance — the only job this element has. It replaced a grey word ("reminder"), which sat in
   the same slot and told you nothing. */
body.v2 .v2dot { display:block; width:8px; height:8px; border-radius:99px; background:var(--g2); }

body.v2 .wi-v { font-size:13px; font-weight:800; color:var(--g2); }
/* the browser's default blue track ignored the accent he picked. Every 2.0 slider (what-if,
   balance transfer) now wears the theme: accent-color covers the filled track, and the thumb
   is drawn explicitly so it matches on WebKit and Firefox too. */
body.v2 input[type=range] { width:100%; accent-color:var(--g2); height:26px; background:transparent;
  -webkit-appearance:none; appearance:none; }
body.v2 input[type=range]::-webkit-slider-runnable-track { height:8px; border-radius:6px;
  background:linear-gradient(90deg, var(--g1), var(--g2)); }
body.v2 input[type=range]::-webkit-slider-thumb { -webkit-appearance:none; appearance:none;
  width:24px; height:24px; margin-top:-8px; border-radius:50%; background:var(--g2);
  border:2px solid #fff2; box-shadow:0 2px 10px rgba(0,0,0,.5); cursor:pointer; }
body.v2 input[type=range]::-moz-range-track { height:8px; border-radius:6px;
  background:linear-gradient(90deg, var(--g1), var(--g2)); }
body.v2 input[type=range]::-moz-range-thumb { width:24px; height:24px; border:none; border-radius:50%;
  background:var(--g2); box-shadow:0 2px 10px rgba(0,0,0,.5); cursor:pointer; }

body.v2 .v2days { display:flex; gap:4px; }
body.v2 .v2day { flex:1; text-align:center; min-width:0; }
body.v2 .v2day-l { font-size:10px; opacity:.5; }
body.v2 .v2day-v { font-size:12px; font-weight:700; }
body.v2 .v2day-v.hot { color:#ef4444; }

/* the file's milestones timeline */
body.v2 .v2tl { position:relative; padding-left:22px; }
body.v2 .v2tl::before { content:''; position:absolute; left:8px; top:4px; bottom:4px; width:2px; background:rgba(255,255,255,.08); }
body.v2 .v2tl-item { position:relative; margin-bottom:14px; }
body.v2 .v2tl-dot { position:absolute; left:-17px; top:14px; width:16px; height:16px; border-radius:50%; background:#0a0b0f; border:2px solid rgba(255,255,255,.10); display:flex; align-items:center; justify-content:center; font-size:8px; }
body.v2 .v2tl-dot.done { background:#22c55e; border-color:#22c55e; color:#0a0b0f; }
body.v2 .v2tl-dot.next { border-color:var(--g2); }
body.v2 .v2tl-card.done { border-color:rgba(34,197,94,.45); }
body.v2 .v2tl-card.next { border-color:rgba(var(--teal-rgb),.45); background:rgba(var(--teal-rgb),.06); }
body.v2 .v2tl-card.future .v2tl-amt, body.v2 .v2tl-card.future .v2tl-sub { opacity:.45; }
body.v2 .v2tl-when { font-size:9px; font-weight:600; opacity:.55; }
body.v2 .v2tl-when.good { color:#22c55e; opacity:1; }
body.v2 .v2tl-when.acc { color:var(--g2); opacity:1; }
body.v2 .v2tl-amt { font-size:14px; font-weight:800; }
body.v2 .v2tl-sub { font-size:10px; opacity:.6; }

body.v2 .v2bm-num { font-size:24px; font-weight:900; margin-top:3px; }
body.v2 .v2bm-num.good { color:#22c55e; } body.v2 .v2bm-num.bad { color:#ef4444; }

/* Home tile ＋ (the file's .add): a 24px ghost circle in the tile's corner */
body.v2 .tile { position:relative; }
/* sweep: "tap targets are at least 32px tall" — the button is a 36px invisible hit ring; the
   24px circle he sees is drawn by ::before, exactly where it always was (top 4+6=10). */
body.v2 .tile-add { position:absolute; top:4px; right:4px; width:36px; height:36px; border-radius:50%;
  background:none; border:none; color:var(--quiet); z-index:1;
  font-size:14px; line-height:1; display:flex; align-items:center; justify-content:center;
  padding:0; cursor:pointer; transition:all .2s; }
body.v2 .tile-add::before { content:""; position:absolute; inset:6px; border-radius:50%; z-index:-1;
  background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.06); transition:all .2s; }
body.v2 .tile-add:active { transform:scale(.86); color:#f1f1f6; }
body.v2 .tile-add:active::before { background:rgba(var(--teal-rgb),.16); }
/* the file's empty state: centred glyph + title + line + two buttons */
body.v2 .v2empty { text-align:center; padding:36px 20px; }
body.v2 .v2e-i { font-size:38px; margin-bottom:10px; opacity:.55; }
body.v2 .v2e-t { font-size:15px; font-weight:800; margin-bottom:5px; }
/* #9294a8 reads fine on the dark canvas and drops to 2.75:1 on white - the same mid-grey trap
   the --quiet token exists to close. This is the FIRST line a new user reads on an empty home. */
body.v2 .v2e-d { font-size:11.5px; color:var(--quiet); line-height:1.6; max-width:280px; margin:0 auto 16px; }
body.v2 .v2e-a { display:flex; gap:8px; justify-content:center; flex-wrap:wrap; }
body.v2 .v2e-a .btn { width:auto; }

/* Switch profile (the file's pg-profileswitch): centred title, 2-up round avatars */
body.v2 .v2ps-head { text-align:center; padding:26px 0 16px; }
body.v2 .v2ps-t { font-size:22px; font-weight:900; letter-spacing:-.5px; margin-bottom:4px; }
body.v2 .v2ps-d { font-size:12px; color:#9294a8; }
body.v2 .v2psgrid { grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; }
body.v2 .tile.v2ps { text-align:center; padding:16px 10px; min-width:0; }
body.v2 .v2ps-av { display:flex; justify-content:center; margin-bottom:8px; }
body.v2 .v2ps-av .prof-av { width:52px; height:52px; border-radius:50%; font-size:26px;
  background:linear-gradient(135deg,var(--g1),var(--g3)); border:none; color:#fff; }
body.v2 .v2ps-av .prof-av.add { background:rgba(255,255,255,.04); border:1px dashed rgba(255,255,255,.16); color:#9294a8; }
body.v2 .v2ps-n { font-size:12px; font-weight:800; line-height:1.3; }
body.v2 .v2ps-s { font-size:9px; color:var(--quiet); margin-top:2px; line-height:1.3; }

/* Profile header, per the new file: avatar row + plan line + edit, then three actions */
body.v2 .card.v2pf { padding:14px; }
body.v2 .v2pf-row { display:flex; align-items:center; gap:12px; }
body.v2 .fav.v2pf-av { width:46px; height:46px; min-width:46px; font-size:19px; font-weight:900;
  border-radius:50%; display:flex; align-items:center; justify-content:center; color:#fff; margin:0; }
body.v2 .v2pf-b { flex:1; min-width:0; }
body.v2 .v2pf-n { font-size:15px; font-weight:800; line-height:1.3; }
body.v2 .v2pf-s { font-size:10px; color:#9294a8; margin-top:2px; line-height:1.4; }
body.v2 .v2pf-acts { display:flex; gap:6px; margin-top:12px; }
/* "switch settings alerd / make.tabs bigger in size" - measured at 109.8 x 34.9 with 10px type:
   the smallest text anywhere in the app, on a target below the 44px minimum. These are the three
   doors off Profile, so they should read like doors. Nowrap + a small horizontal pad keeps all
   three on one line at 360px, because a cut-off label is the other thing he has called out. */
body.v2 .v2pf-acts .btn { flex:1; width:auto; padding:12px 4px; font-size:13.5px;
  min-height:44px; white-space:nowrap; }
body.v2 .v2sa-row { display:flex; gap:8px; align-items:center; }

/* Split: the file's NET POSITION card */
body.v2 .card.v2net { text-align:center; background:linear-gradient(135deg,rgba(var(--g1-rgb),.08),rgba(236,72,153,.08));
  border-color:rgba(var(--teal-rgb),.15); margin-bottom:10px; }
body.v2 .v2net-l { font-size:10px; font-weight:700; letter-spacing:1px; color:var(--quiet); }
body.v2 .v2net-n { font-size:32px; font-weight:900; letter-spacing:-1px; margin:2px 0; }
body.v2 .v2net-s { font-size:11px; color:#9294a8; }
body.v2 .v2net-a { display:flex; gap:6px; margin-top:11px; }
body.v2 .v2net-a .btn { flex:1; width:auto; padding:8px 0; font-size:11px; margin:0; }
body.v2 .v2net-n.good { color:#22c55e; -webkit-text-fill-color:#22c55e; background:none; }
body.v2 .v2net-n.bad  { color:#ef4444; -webkit-text-fill-color:#ef4444; background:none; }
body.v2 .v2kid-wk { display:inline-block; margin-top:9px; padding:4px 11px; border-radius:20px;
  background:rgba(132,204,22,.14); font-size:10px; font-weight:800; color:#22c55e; }

/* Split group head (the file's pg-splitgroup) */
body.v2 .v2sgnet { text-align:left; }
body.v2 .v2sg-top { display:flex; align-items:center; gap:10px; }
body.v2 .v2sg-l { flex:1; min-width:0; }
body.v2 .v2sg-n { font-size:28px; font-weight:900; letter-spacing:-1px; margin:1px 0; }
body.v2 .v2sg-n.good { color:#22c55e; } body.v2 .v2sg-n.bad { color:#ef4444; }
body.v2 .sg-avs { display:flex; flex:none; }
body.v2 .sg-av { width:28px; height:28px; border-radius:50%; display:flex; align-items:center;
  justify-content:center; font-size:10px; font-weight:800; color:#fff; border:2px solid #171824; }

/* Safe to spend: the derivation list */
body.v2 .v2list .li-a.good { color:#22c55e; }
body.v2 .v2list .li-a.bad  { color:#ef4444; }
/* His 2026-08-22 screenshots: the grey inset band made "Safe to spend" / "Genuinely spare"
   look like a second design pasted into the card. The total row is now the same surface as its
   card — emphasis comes from the rule above it and the bold weight, not a different background. */
body.v2 .li-row.v2sts-total { background:none; border-bottom:none; border-top:1px solid rgba(255,255,255,.14); }
body.v2 .li-row.v2sts-total .li-a { font-size:14px; }

/* Calendar (the file's pg-calendar) */
body.v2 .cal-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:8px; }
body.v2 .cal-m { font-size:14px; font-weight:800; }
body.v2 .cal-grid { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:2px; text-align:center; }
body.v2 .cal-w { font-size:8px; color:var(--quiet); padding:3px 0; }
body.v2 .cal-c { padding:6px 0; font-size:10px; color:var(--quiet); border-radius:5px; min-width:0; }
body.v2 .cal-c.cal-pad { visibility:hidden; }
body.v2 .cal-c.cal-due  { background:rgba(245,158,11,.15); color:#f59e0b; font-weight:800; cursor:pointer; }
body.v2 .cal-c.cal-soon { background:rgba(239,68,68,.15);  color:#ef4444; font-weight:800; cursor:pointer; }
body.v2 .cal-c.cal-in   { background:rgba(34,197,94,.15);  color:#22c55e; font-weight:800; cursor:pointer; }
body.v2 .cal-c.cal-today { outline:1px solid rgba(var(--teal-rgb),.55); color:#f1f1f6; }
body.v2 .li-row.li-flash { background:rgba(var(--teal-rgb),.14); transition:background .3s; }
body.v2 .cal-m { background:none; border:none; padding:0; box-shadow:none; }
body.v2 .cal-head .v2sa { font-size:16px; padding:2px 10px; }

/* Goal detail (the file's pg-goaldetail) */
body.v2 .v2gd-jar { text-align:center; margin:16px 0; }
body.v2 .v2gd-n { font-size:24px; font-weight:900; color:#22c55e; }
body.v2 .v2gd-s { font-size:13px; color:#9294a8; }
body.v2 .v2gd-add { display:flex; gap:8px; margin-top:12px; }
body.v2 .v2gd-add input { flex:1; min-width:0; text-align:center; font-size:14px; font-weight:700;
  background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.08); border-radius:12px;
  color:#f1f1f6; padding:10px; }
body.v2 .v2gd-add .btn { flex:1; width:auto; margin:0; }
body.v2 .v2gd-d { font-size:12px; color:#9294a8; width:58px; flex-shrink:0; }
body.v2 .bud-sv.gold { color:#f59e0b; }

/* 2.0 landing (the file's pg-landing) — first screen, inside the onboarding overlay.
   v674 "squeeze up the tabs": his screenshot showed the plan cards and hero pushing the three
   CTA buttons below the fold on a phone. Compaction, not redesign — same lockup, same cards,
   same MOST PICKED pill, just a tighter vertical rhythm so Create/Guest/Sign-in sit on screen
   (welcome-squeeze.cjs holds this at a 412×640 viewport). */
body.v2 .v2land { display:flex; flex-direction:column; align-items:center; text-align:center; padding:2px 4px 4px; }
body.v2 .v2land-logo { width:44px; height:44px; border-radius:12px; object-fit:cover; margin-bottom:6px; }
body.v2 .v2land-brand { font-size:10px; font-weight:700; letter-spacing:3px; color:#9294a8; }
body.v2 .v2land-app { font-size:22px; font-weight:300; letter-spacing:4px; margin:2px 0 7px; }
body.v2 .v2land-app span { font-weight:900; color:var(--g2); letter-spacing:1px; }
body.v2 .v2land-t { font-size:19px; font-weight:900; line-height:1.22; letter-spacing:-.5px; margin-bottom:5px; }
body.v2 .v2land-d { font-size:12px; color:#9294a8; line-height:1.45; max-width:320px; margin-bottom:10px; }
body.v2 .v2persona-r { display:flex; gap:6px; width:100%; margin-bottom:11px; }
body.v2 .v2persona { flex:1; min-width:0; background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.07);
  border-radius:14px; padding:8px 6px; cursor:pointer; color:#f1f1f6; display:flex; flex-direction:column;
  align-items:center; gap:2px; transition:all .2s; }
body.v2 .v2persona:active { transform:scale(.96); border-color:rgba(var(--teal-rgb),.5); background:rgba(var(--teal-rgb),.10); }
body.v2 .v2p-i { font-size:20px; }
body.v2 .v2p-n { font-size:11.5px; font-weight:800; }
body.v2 .v2p-s { font-size:9px; color:var(--quiet); line-height:1.3; }
/* The price is the reason someone picks one of these, so it gets its own weight rather than
   hiding in the sub-line. The badge is positioned, so the card needs a stacking context. */
body.v2 .v2persona { position:relative; }
body.v2 .v2p-price { font-size:10px; font-weight:800; color:var(--g2); margin-top:3px; white-space:nowrap; }
body.v2 .v2p-badge { position:absolute; top:-7px; left:50%; transform:translateX(-50%);
  font-size:7.5px; font-weight:800; letter-spacing:.5px; padding:2px 6px; border-radius:99px;
  background:linear-gradient(135deg,var(--g1),var(--g3)); color:#fff; white-space:nowrap; }
/* "N of 11 selected" sits with its Select-all button on one line */
body.v2 .ob-selrow { display:flex; align-items:center; justify-content:space-between; gap:10px;
  margin:10px 2px; font-size:11.5px; font-weight:700; color:#9294a8; text-transform:uppercase; letter-spacing:.4px; }
body.v2 .v2land-acts { width:100%; display:flex; flex-direction:column; gap:6px; }
body.v2 .v2land-acts .btn { width:100%; margin:0; padding-top:12px; padding-bottom:12px; }
body.v2 .v2land-note { margin-top:8px; font-size:10px; color:var(--quiet); }
body.v2 .v2land-also { margin-top:14px; padding-top:12px; border-top:1px solid rgba(255,255,255,.06); width:100%; }
body.v2 .v2land-allbl { font-size:9px; font-weight:700; letter-spacing:2px; color:var(--quiet); margin-bottom:10px; }
body.v2 .v2land-chips { display:flex; flex-wrap:wrap; gap:6px; justify-content:center; }
body.v2 .v2land-chip { padding:7px 11px; font-size:10px; font-weight:600; border-radius:999px;
  background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.07); color:#f1f1f6; cursor:pointer; }
body.v2 .v2land-chip:active { transform:scale(.95); border-color:rgba(var(--teal-rgb),.45); }
/* the landing card carries no ob-brand/dots header (bare), so its own top padding is all the
   headroom the logo needs — part of the v674 squeeze */
body.v2 .ob-card--bare { padding-top:12px; }

/* Subscription detail (the file's pg-subdetail) */
body.v2 .v2sd-ic { width:52px; height:52px; border-radius:12px; background:rgba(var(--teal-rgb),.12);
  margin:0 auto 10px; display:flex; align-items:center; justify-content:center; font-size:24px; }
body.v2 .v2sd-p { font-size:22px; font-weight:900; }
body.v2 .v2sd-p span { font-size:12px; font-weight:500; color:#9294a8; }
body.v2 .v2sd-warn { font-size:12px; color:#f59e0b; margin-top:3px; }
body.v2 .v2dd-n { font-size:32px; font-weight:900; letter-spacing:-1px; color:#ef4444; }
body.v2 .v2hd-nm { font-size:12px; color:#9294a8; margin-bottom:4px; }
body.v2 .v2hd-v { font-size:32px; font-weight:900; letter-spacing:-1px; }
body.v2 .v2hd-g { font-size:13px; margin-top:4px; font-weight:700; }
body.v2 .v2td-tag { font-size:11px; color:#9294a8; margin-bottom:6px; }
body.v2 .v2hd-v.good { color:#22c55e; } body.v2 .v2hd-v.bad { color:#ef4444; }

/* ---- Split & Settle 2.0 (his twelve-frame redesign) ---------------------------------- */
/* the balance each group row carries, where the › used to be */
body.v2 .sg-bal { flex:0 0 auto; text-align:right; display:flex; flex-direction:column; align-items:flex-end; gap:2px; }
body.v2 .sg-bal .amt { font-size:15px; font-weight:800; }
body.v2 .sg-set { font-size:14px; font-weight:800; color:#2dd4bf; }
body.v2 .sg-st { font-size:9px; font-weight:700; letter-spacing:.8px; color:var(--quiet); text-transform:uppercase; }
body.v2 .sg-name .sg-own { margin-left:6px; }

/* the group header: back, name + one sub-line, chat, sync */
body.v2 .sp-ghead { display:flex; align-items:center; gap:10px; margin:4px 2px 14px; }
body.v2 .sp-back { flex:0 0 auto; width:30px; height:30px; border-radius:9px; border:1px solid rgba(255,255,255,.08);
  background:rgba(255,255,255,.03); color:#f1f1f6; font-size:17px; line-height:1; cursor:pointer; }
body.v2 .sp-gh-l { flex:1 1 auto; min-width:0; }
body.v2 .sp-gh-n { font-size:19px; font-weight:900; letter-spacing:-.3px; line-height:1.2; overflow-wrap:anywhere; }
body.v2 .sp-gh-s { font-size:11.5px; color:#9294a8; margin-top:2px; }
body.v2 .sp-chat { flex:0 0 auto; position:relative; padding:8px 11px; }
body.v2 .sp-msg { width:auto; padding:6px 12px; font-size:11.5px; margin:0; position:relative; }

/* an expense row leads with its date, and its right column says YOUR SHARE — never "you borrowed" */
body.v2 .sx-date { flex:0 0 auto; width:34px; text-align:center; line-height:1.05; }
body.v2 .sx-date .m { display:block; font-size:8.5px; font-weight:700; letter-spacing:.7px; color:var(--quiet); }
body.v2 .sx-date .d { display:block; font-size:16px; font-weight:800; color:#f1f1f6; font-variant-numeric:tabular-nums; }
body.v2 .sx-row .r .dim { font-size:9.5px; letter-spacing:.4px; text-transform:uppercase; }

/* settling is a checklist: tap a row to tick it, the button records the ticked ones */
body.v2 .sp-pay { cursor:pointer; }
body.v2 .sp-tick { flex:0 0 auto; width:26px; height:26px; border-radius:99px; border:1.5px solid rgba(255,255,255,.16);
  display:flex; align-items:center; justify-content:center; font-size:13px; font-weight:800; color:#0b0b12; }
body.v2 .sp-pay.on .sp-tick { background:#2dd4bf; border-color:#2dd4bf; }
body.v2 .sp-pay.on { background:rgba(45,212,191,.08); border-radius:12px; }
body.v2 .sp-hero .v2net-s { font-size:12.5px; line-height:1.5; }
body.v2 .sp-done { border-color:rgba(45,212,191,.35); }

/* a template that hasn't been posted this month is a heads-up, not an error */
body.v2 .form-alert.warn { background:rgba(245,158,11,.10); border-color:rgba(245,158,11,.40); color:#f59e0b; }
body.v2 .form-alert.warn::before { content:"" }

/* the split home's header is a page TITLE, not the small grey section rule 2.0 uses elsewhere */
body.v2 .sp-hhead { align-items:center; }
body.v2 .sp-title { font-size:23px; font-weight:900; letter-spacing:-.4px; }
body.v2 .sp-join { color:var(--g2); font-weight:700; }
/* colour lives on the little label and on the per-group rows; the headline figure stays white */
body.v2 .sp-hl { color:var(--g3); }
body.v2 .sp-hl.good { color:#2dd4bf; }
body.v2 .sp-hl.bad { color:var(--g3); }
/* the frame's two tiles: centred, word-only */
body.v2 .sp-two { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:12px; }
body.v2 .sp-tile { text-align:center; padding:16px 12px; border-radius:14px; cursor:pointer;
  background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.07); }
body.v2 .sp-tile:active { transform:scale(.97); border-color:rgba(var(--teal-rgb),.45); }
body.v2 .sp-tile .nm { font-size:14px; font-weight:800; }
body.v2 .sp-tile .ds { font-size:11px; color:#9294a8; margin-top:3px; }
/* the frame writes "Budget C$1,500" in sentence case; .bh-eq .k is uppercased by the 2.0 sheet */
body.v2 .sp-budrow { display:flex; align-items:baseline; justify-content:space-between; gap:10px;
  font-size:13px; color:#9294a8; }
body.v2 .sp-budrow b { font-size:15px; color:#f1f1f6; }
body.v2 .sp-budrow b.bad { color:#ef4444; }

/* ---- the missing pickers: dates, budget, currency, and the code boxes ---------------- */
body.v2 .sd-edges { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
body.v2 .sd-edge { text-align:left; padding:10px 12px; border-radius:12px; cursor:pointer;
  background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.08); color:#f1f1f6; }
body.v2 .sd-edge.on { border-color:var(--g2); background:rgba(var(--teal-rgb),.10); }
body.v2 .sd-edge .k { display:block; font-size:9px; font-weight:700; letter-spacing:.9px;
  text-transform:uppercase; color:var(--quiet); margin-bottom:3px; }
body.v2 .sd-edge b { font-size:15px; font-weight:800; }
body.v2 .sd-cal { margin-top:12px; padding:10px; border-radius:14px;
  background:rgba(255,255,255,.02); border:1px solid rgba(255,255,255,.06); }
body.v2 .sd-mh { display:flex; align-items:center; justify-content:space-between; font-size:14px; padding:2px 4px 8px; }
body.v2 .sd-nav { width:28px; height:28px; border-radius:8px; border:1px solid rgba(255,255,255,.08);
  background:rgba(255,255,255,.03); color:#f1f1f6; font-size:15px; line-height:1; cursor:pointer; }
body.v2 .sd-dow, body.v2 .sd-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:3px; }
body.v2 .sd-dow span { text-align:center; font-size:9.5px; font-weight:700; color:var(--quiet); padding-bottom:4px; }
body.v2 .sd-d { aspect-ratio:1; border:none; background:transparent; color:#f1f1f6; border-radius:9px;
  font-size:13px; font-weight:600; cursor:pointer; font-variant-numeric:tabular-nums; }
body.v2 .sd-d.out { color:#4a4c5e; }
body.v2 .sd-d.today { box-shadow:inset 0 0 0 1px rgba(var(--teal-rgb),.5); }
body.v2 .sd-d.mid { background:rgba(var(--teal-rgb),.16); border-radius:0; }
body.v2 .sd-d.sel { background:var(--g2); color:#fff; font-weight:800; }

body.v2 .sb-num { text-align:center; font-size:38px; font-weight:900; letter-spacing:-1.5px; }
body.v2 .sb-sub { text-align:center; font-size:12px; color:#9294a8; margin-top:2px; }
body.v2 .sb-pad { display:grid; grid-template-columns:repeat(3,1fr); gap:8px; margin-top:14px; }
body.v2 .sb-k { padding:13px 0; border-radius:12px; font-size:18px; font-weight:700; cursor:pointer;
  background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.07); color:#f1f1f6; }
body.v2 .sb-k:active { background:rgba(var(--teal-rgb),.18); }

body.v2 .sc2-list { max-height:46vh; overflow-y:auto; }
body.v2 .sc2-tick { color:#2dd4bf; font-weight:800; }

/* the six code boxes are a picture of one real input, so paste and autofill still work */
body.v2 .sj-row { display:grid; grid-template-columns:repeat(6,1fr); gap:8px; margin-top:14px; cursor:text; }
body.v2 .sj-box { aspect-ratio:1; display:flex; align-items:center; justify-content:center;
  border-radius:12px; background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.09);
  font-size:20px; font-weight:800; color:var(--quiet); }
body.v2 .sj-box.filled { color:var(--g2); border-color:rgba(var(--teal-rgb),.45); }
body.v2 .sj-box.on { border-color:var(--g2); }
body.v2 .sj-input { position:absolute; opacity:0; width:1px; height:1px; pointer-events:none; }
body.v2 .sj-or { text-align:center; font-size:10px; font-weight:700; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--quiet); margin-bottom:8px; }
body.v2 .sp-cardh { display:flex; align-items:center; justify-content:space-between; gap:10px; }
body.v2 .sp-cardh .k { font-size:10px; font-weight:700; letter-spacing:1px; text-transform:uppercase; color:var(--quiet); }
/* the family budget month stepper: ‹ August 2026 · All months › › */
body.v2 .fb-mrow { display:flex; align-items:center; gap:8px; margin:0 2px 14px; }
body.v2 .fb-mlbl { flex:1 1 auto; font-size:14px; font-weight:800; }
body.v2 .fb-mrow .sp-back[disabled] { opacity:.35; }

/* ---- measured against frame 2a, side by side, after he said it still did not match --------
   Four things were mine, not the data's: a 3px coloured stripe down the left of every group row
   that the frame does not have, an OWNER pill the frame does not have, rows a third taller than
   the frame's, and a hero card in purple where the frame's is pink. */
body.v2 .sgcard::before { display:none; }
body.v2 .sgcard { padding:12px 13px; gap:12px; border-radius:14px;
  background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.06); }
body.v2 .sgcard .sg-name { font-size:16px; }
body.v2 .sgcard .sg-sub { font-size:12px; }
body.v2 .sg-emoji { width:38px; height:38px; font-size:18px; border-radius:11px; }
body.v2 .swrow.sg-in { border-bottom:none; background:transparent; margin-bottom:9px; border-radius:14px; }
body.v2 .swrow.sg-in:last-of-type { margin-bottom:0; }
body.v2 .sp-hero { border-color:rgba(236,72,153,.30);
  background:linear-gradient(155deg, rgba(236,72,153,.12), rgba(109,40,217,.10) 55%, rgba(255,255,255,.02)); }
body.v2 .sp-hero .btn.ghost { color:#f1f1f6; border-color:rgba(255,255,255,.22); }
/* the frame's balance colours: a soft coral, and TEAL for owed-to-you — not the app's green */
body.v2 .sg-bal .amt.bad { color:#f87171; }
body.v2 .sg-bal .amt.good { color:#2dd4bf; }

/* “tabs use same size as design” — his frame's Expenses/People/Settings are big, tall pills,
   not the small sub-tabs 1.0 uses. Split & family 2.0 only. */
body.v2 .sp-tabs { padding:5px; border-radius:16px; gap:4px; }
body.v2 .sp-tabs .subtab { padding:12px 6px; font-size:15px; font-weight:700; border-radius:12px; }
body.v2 .sp-tabs .subtab.on { border:1px solid rgba(var(--teal-rgb),.55); background:rgba(var(--teal-rgb),.12); color:#f1f1f6; }

/* the 2e sheet: amount first and big, then the rest */
body.v2 .sx-lbl { font-size:10px; font-weight:700; letter-spacing:1.1px; text-transform:uppercase;
  color:var(--quiet); margin:16px 2px 8px; }
/* "user dont see where to add values" — this was border:none on a transparent background, so the
   amount rendered as a big number sitting on the sheet with nothing to say it could be typed into.
   Every other field on the same sheet (.sx-txt) has a box; the one field people must fill first
   had none. It keeps the large type — the number is still the loudest thing on the sheet — and
   gains the same box, so it reads as a field instead of a heading. */
body.v2 .sx-big { width:100%; color:#f1f1f6; text-align:center;
  font-size:38px; font-weight:900; letter-spacing:-1.5px; padding:10px 12px;
  border-radius:16px; background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.12);
  transition:border-color .15s, box-shadow .15s; }
body.v2 .sx-big:focus { outline:none; border-color:var(--g2);
  box-shadow:0 0 0 3px rgba(var(--teal-rgb), .18); }
body.v2 .sx-big::placeholder { color:rgba(241,241,246,.28); font-weight:800; }
body.v2 .sx-txt { width:100%; padding:12px 13px; border-radius:12px; font-size:15px;
  background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.08); color:#f1f1f6; }
body.v2 .sx-sh { width:52px; margin-right:8px; padding:6px 8px; text-align:center; border-radius:9px;
  background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.10); color:#f1f1f6; font-weight:700; }

/* “how peoplevwill know abouy this” — the line under the split chips is the ANSWER to the question
   the chips are asking, so it must not look like the footnotes around it. Brighter than a hint, and
   fenced off with a rule so it cannot read as one paragraph with the label for the boxes below —
   which is exactly how it looked the first time it was drawn. Pinned under body.v2 so the light
   palette can't leak through it. */
body.v2 .sx-conseq { color:var(--mid); font-size:12.5px; line-height:1.55; margin:8px 2px 2px;
  padding-left:9px; border-left:2px solid rgba(var(--teal-rgb),.45); }

/* The "rent is usually a household cost" offer. It has to read as an OFFER, not as a state that has
   already been applied — so it is a button, it says "Tap to switch", and it never looks like the
   confirmed row (.hh-on) it becomes. */
body.v2 .hh-nudge { display:flex; flex-direction:column; gap:2px; width:100%; text-align:left;
  margin:10px 0 2px; padding:10px 12px; border-radius:12px; cursor:pointer;
  background:rgba(var(--teal-rgb),.09); border:1px dashed rgba(var(--teal-rgb),.45); }
body.v2 .hh-on { display:flex; flex-direction:column; gap:2px; margin:10px 0 2px; padding:10px 12px;
  border-radius:12px; background:rgba(var(--teal-rgb),.10); border:1px solid rgba(var(--teal-rgb),.40); }
body.v2 .hh-n-t { font-size:13px; font-weight:700; color:#f1f1f6; }
body.v2 .hh-n-s { font-size:11.5px; line-height:1.45; color:var(--mid); }
body.v2 .hh-on .btn { align-self:flex-start; margin-top:6px; }
/* The reason, under the chip rather than instead of it. His "it shows differnt things for each of
   it" was this explanation BEING the button on rent/utility categories and a plain chip everywhere
   else — one job, two controls, swapping under his thumb. Now the chip never changes and this line
   appears beneath it, so it must not read as something to press. */
body.v2 .hh-why { display:flex; flex-direction:column; gap:2px; margin:8px 0 2px; padding:9px 12px;
  border-radius:12px; background:rgba(var(--teal-rgb),.07); border:1px solid rgba(var(--teal-rgb),.22); }
/* Reserve the tick so choosing a category cannot rewrap the row and shunt the rest of the sheet
   down a line. Transparent, not hidden: it must occupy exactly the width it will occupy when it
   turns teal. */
body.v2 .chip[data-fxc]::before { content:"\2713 "; color:transparent; font-weight:800; }
body.v2 .chip[data-fxc].on::before { color:var(--teal); }
body.v2.light .chip[data-fxc].on::before { color:#fff; }

/* “user needs to scroll a lot” — his frame 2e fits on one screen and mine did not. Every gap in
   this sheet is smaller; nothing is removed. */
body.v2 .sx-lbl { margin:11px 2px 6px; }
/* padding:0 here would flatten the box back to bare text on exactly the small screens where the
   "where do I type?" problem bites hardest. Tighter, not gone. */
body.v2 .sx-big { font-size:30px; padding:7px 10px; }
body.v2 .sx-txt { padding:10px 12px; font-size:14px; }
body.v2 #sx-wrap .v2chips, body.v2 #fx-wrap .v2chips { gap:6px; }
body.v2 #sx-wrap .chip, body.v2 #fx-wrap .chip { padding:8px 12px; font-size:12.5px; }
body.v2 #sx-wrap .card, body.v2 #fx-wrap .card { padding:10px 12px; margin-top:8px; }
body.v2 #sx-wrap .li-row, body.v2 #fx-wrap .li-row { padding:5px 0 !important; }
body.v2 #sx-wrap .sg-av, body.v2 #fx-wrap .sg-av { width:26px; height:26px; font-size:11px; }
body.v2 #sx-wrap .field, body.v2 #fx-wrap .field { margin-top:8px !important; }
body.v2 #sx-wrap .hint, body.v2 #fx-wrap .hint { font-size:11.5px; }
/* the pinned action row from frame 2e: Save is always reachable, whatever the sheet's height */
body.v2 .modal.sheet-pin { padding-bottom:0; }
/* "for settle up screen cross sbove not reachable" - and it wasn't: only the bottom sliver of the ✕
   showed. The sheet is bottom-anchored at max-height:92dvh, but with viewport-fit=cover the dvh
   INCLUDES the status-bar strip, so a tall sheet's top edge slides underneath it and takes the ✕
   with it. Cap the height by the inset so the top of the sheet always lands below the status bar.
   (1.0 has the same geometry; scoped to 2.0 because that is what he is testing.) */
body.v2 .modal { max-height:calc((92dvh - env(safe-area-inset-top, 0px)) / var(--zoom, 1)); }
body.v2 .modal.sheet-pin > #m-ok { position:sticky; bottom:0; z-index:3; margin-bottom:0;
  box-shadow:0 -14px 18px -8px rgba(10,10,16,.95); }
body.v2 .modal.sheet-pin > #m-x { display:none; }   /* the ✕ already closes it */

/* ---- 2.0 is a dark design, and the light theme must not reach into it -----------------
   He has now hit this three times: the date picker, Settings, and the currency picker — each
   rendering WHITE on top of a dark 2.0 page. I patched the first one element-by-element, which
   was the wrong size of fix; every popup body.light hard-codes to #fff was going to surface
   eventually.

   So this pins it at the root instead. body.v2.light re-declares the dark tokens, and every
   surface body.light paints white is painted back. One rule to keep in step, not six. */
/* SUPERSEDED (his: "ligh them not workinh" / "colour selector for theme not added yet to the design
   and flow"). The block above forced 2.0 to stay dark when you picked light, which is why the theme
   switch appeared to do nothing here. It also re-declared 1.0's dark tokens (#0B1020 / #C9D2E4)
   rather than 2.0's (#0a0b0f / #f1f1f6), so choosing light actually degraded the 2.0 palette into
   the old one - a bug even under the old "stay dark" policy.

   2.0 now has a real light theme: the same violet identity on light surfaces. The accent darkens to
   #7C3AED because var(--g2) on white fails contrast for text. Every surface the old block painted
   dark is painted light here, or the popups would stay black on a white page. */
body.v2.light {
  --bg:#F6F4FF; --bg2:#FFFFFF; --card:#FFFFFF; --sheet:#FFFFFF; --line:rgba(17,18,25,.10);
  --text:#15121F; --dim:#54506E;   /* was #6A6580. It measured 5.55:1 - passing - and he still could not read the meta lines on a white card. Measurement is a floor, not proof; his eyes win. ~7:1 now, same muted character. */ --mid:#3C3752;
  --teal:#7C3AED; --blue:#5b4bd6; --purple:#7C3AED;
  --gold:#B45309; --red:#DC2626;
  --grad:linear-gradient(90deg,var(--g1),var(--g2),var(--g3));
  color-scheme: light;
  /* solid colour first: the three layers above are all background-IMAGES, so without this the page
     has no background-COLOR at all and anything that fails to paint them falls through to white-on
     -white. The dark side gets this for free because body.v2 sets a flat colour. */
  background-color:#F6F4FF;
  background:
    radial-gradient(900px 600px at 85% -10%, rgba(var(--teal-rgb),.10), transparent 60%),
    radial-gradient(760px 500px at -10% 108%, rgba(236,72,153,.07), transparent 55%),
    linear-gradient(165deg,#FAF8FF 0%,#F1EDFB 100%);
  background-color:#F6F4FF;
}
body.v2.light .celebrate-card,
body.v2.light .spop-card,
body.v2.light .calpop,
body.v2.light .selpop,
body.v2.light .qa-sheet,
body.v2.light .tour-card { background:#FFFFFF; color:#15121F; border-color:rgba(17,18,25,.10); }
/* .modal and .qa-sheet used to sit in the list above, which flattened them back to plain white and
   undid the accent tint - the light half of "not matches accent colour even theme". They get the
   same tint as dark here, over a white sheet instead of a near-black one. */
body.v2.light .modal, body.v2.light .qa-sheet {
  background:linear-gradient(180deg, rgba(var(--g1-rgb),.10), rgba(var(--g1-rgb),0) 230px), #FFFFFF;
  color:#15121F; border-color:rgba(var(--g1-rgb),.24); }
body.v2.light #tabbar  { background:rgba(255,255,255,.94); }
body.v2.light .brand .m { color:#15121F; }
body.v2.light .card, body.v2.light .stat, body.v2.light .tile { box-shadow:0 1px 2px rgba(17,18,25,.05); }
body.v2.light .del     { color:#8b88a0; }
/* the rows inside those popups follow the surface they sit on */
body.v2.light .cal-d, body.v2.light .selpop .sel-row, body.v2.light .selpop button { color:#15121F; }
/* HIS REPORT: "i cant read date- month at top". The month label and the ‹ › arrows were the only
   things in the date picker still painted for the dark theme — line 585 sets
   `body.v2 .cal-title, body.v2 .cal-nav { color:#f1f1f6 }`, and `body.v2` keeps matching in light
   mode because the light theme ADDS .light rather than removing .v2. The popup's own light rule
   above turns the sheet white and sets a dark colour on .calpop, but .cal-title carries its own
   colour so it never inherited that — near-white text on a white card, which is what the
   screenshot shows. The day numbers were fixed here already; the header was missed.
   The arrows get a real surface too: a var(--card) background is a dark-theme card colour. */
body.v2.light .cal-title { color:#15121F; }
body.v2.light .cal-nav   { color:#3a3550; background:#F3F1FA; border-color:rgba(17,18,25,.12); }
body.v2.light .cal-nav:disabled { color:#a9a4bb; }
body.v2.light .cal-dow span, body.v2.light .cal-w { color:#6b6580; }
/* Every surface 2.0 paints with a hard-coded dark hex needs a light counterpart, or the page turns
   light while the cards stay black - which is what "absurd / white" looked like. Grouped by job so
   this stays one place to maintain, not thirty.
   SURFACES (were #111219 / #171824) */
body.v2.light .card,
body.v2.light .tile,
body.v2.light .more-list,
body.v2.light .kpi,
body.v2.light .cu-ic,
body.v2.light .qa-tile,
body.v2.light .bud-sb,
body.v2.light .v2search,
body.v2.light .subnav,
body.v2.light .subtabs, body.v2.light .subtabs-sm,
body.v2.light .v2nav-btn,
body.v2.light .btn.ghost,
body.v2.light .coach-card,
body.v2.light .field input, body.v2.light .field select, body.v2.light .field textarea
  { background:#FFFFFF; border-color:rgba(17,18,25,.10); color:var(--text); }
body.v2.light .hero-card { background:#FFFFFF; }
body.v2.light .qa-tile:active { background:#F1ECFB; }
/* TRACKS AND FILLS (were #262842) - light grey, or they vanish on white */
body.v2.light .v2pb,
body.v2.light .bud-pb,
body.v2.light .bd-bar,
body.v2.light .jar,
body.v2.light .jar::before { background:rgba(17,18,25,.09); }
/* SELECTED / TINTED CHIPS (were #262842) - the violet identity, quietly */
/* THE ONE SELECTOR IN THIS GROUP THAT WAS MISSING ITS STATE CLASS. His: "under white theme nav
   bar selected are not highlighted". Every other line here says .on; .v2chip did not, so it
   painted EVERY chip with the selected treatment. It also sits several hundred lines below
   `body.v2 .v2chip.on` at identical specificity (0,3,1), so source order decided and the selected
   rule lost both its background and its text colour. Measured in light theme:
     selected    bg rgb(239,233,252)  text rgb(21,18,31)  border violet .3
     unselected  bg rgb(239,233,252)  text rgb(21,18,31)  border WHITE .06
   Background and text identical, and the only surviving difference was a border of white at 6%
   opacity ON A PALE LAVENDER GROUND, which is nothing at all. Selection was invisible. */
body.v2.light .subtab.on,
body.v2.light .v2li-i,
body.v2.light .subnav button.on { background:#EFE9FC; color:var(--text); }
/* Unselected: a light surface, and a border dark enough to be a border on it. */
body.v2.light .v2chip { background:#F4F2F8; color:var(--text); border-color:rgba(17,18,25,.12); }
/* Selected: the accent, the same way dark theme does it. Placed after the line above so it wins
   on source order rather than relying on the two never being reordered. */
body.v2.light .v2chip.on { background:rgba(var(--teal-rgb),.14); border-color:rgba(var(--teal-rgb),.55);
  color:color-mix(in srgb, var(--teal) 78%, #000000); font-weight:700; }
/* CHROME */
body.v2.light #topbar { background:rgba(255,255,255,.94); border-bottom-color:rgba(17,18,25,.08); }
body.v2.light .v2tl-dot { background:var(--bg); }
body.v2.light .pin-dot { border-color:rgba(17,18,25,.18); }
body.v2.light #pingate { background:var(--bg); }
/* filled buttons keep white text on the violet gradient; ghost buttons take the accent */
body.v2.light .btn { color:#FFFFFF; }
body.v2.light .btn.ghost { color:var(--teal); }
/* The greeting is hard-coded #fff for the weather hero's sky - but 2.0 removes that sky entirely
   (background:none, .th-sky hidden), so it sits straight on the page: white on dark reads, white on
   light is invisible. It follows the text token here. */
body.v2.light .today-hero .th-greet { color:var(--text); }
body.v2.light .today-hero .th-date, body.v2.light .th-date { color:var(--dim); }
/* the active tab label: var(--g2) on white measures 3.96:1, under AA for text this size. One step
   darker clears it without changing the identity. */
body.v2.light #tabbar .tab.active, body.v2.light .tab.active { color:#6D28D9; }
body.v2.light #tabbar .tab.active { background:rgba(109,40,217,.10); }


/* one hero figure size across the whole of 2.0 — the split screens were 28px and the family
   screens 32px, which reads as two different apps when you move between them */
body.v2 .v2sg-n { font-size:32px; font-weight:900; letter-spacing:-1px; }

/* “when within tracker no top bar shows” — on a phone #topbar was STATIC, so on a long tracker
   page it scrolled away and took the back button with it. It is only fixed in the wide-screen
   rule, which is why this never showed up on a desktop. Sticky keeps it, and the back arrow,
   reachable wherever you are on the page. */
/* sticky does not engage here: #app is a flex COLUMN, so the header is a flex item and has no
   scrolling ancestor to stick to. Fixed, with matching room reserved on the view, is the version
   that actually holds. Width is pinned to the app column so it does not span a wide screen. */
body.v2 #topbar { position:fixed; top:0; left:50%; transform:translateX(-50%);
  width:100%; max-width:560px; z-index:46;
  background:rgba(11,11,18,.94); backdrop-filter:blur(14px);
  border-bottom:1px solid rgba(255,255,255,.06); }
/* the bar is now (inset + 56 + 1px border) tall, so the room reserved under it has to carry the
   inset too - a flat 72px hid the top of every page by exactly the notch height on a real phone */
body.v2 #view { padding-top:calc(env(safe-area-inset-top, 0px) + 66px); }
@supports not (backdrop-filter: blur(2px)) { body.v2 #topbar { background:#0b0b12; } }

/* A section action that rode along into the page header sits on the right, not jammed against
   the title. .sp-gh-l takes the slack so one action or a row of them both land in the corner. */
body.v2 .sp-ghead .sp-gh-l { flex:1 1 auto; min-width:0; }
body.v2 .sp-ghead > .v2sa, body.v2 .sp-ghead > .v2sa-row { flex:0 0 auto; margin-left:auto; }

/* The Family Budget chat composer. Sits under the thread, not over it — a tab is scrolled, and
   a floating bar would cover the newest message, which is the one you are replying to. */
body.v2 .fbc-send { display:flex; gap:8px; align-items:center; margin-top:12px; }
body.v2 .card > .fbc-send:first-child { margin-top:0; }   /* no stray gap when it opens a card */
/* These inputs sit outside .field, so they get none of its styling and fall back to the raw
   browser box — which is exactly what it looked like. Same tokens as every other field. */
body.v2 .fbc-send input { flex:1 1 auto; min-width:0; background:var(--bg2);
  border:1px solid var(--line); color:var(--text); border-radius:11px; padding:11px 13px;
  font-size:14px; outline:none; }
body.v2 .fbc-send input:focus { border-color:var(--teal); }
body.v2 .fbc-send input::placeholder { color:var(--dim); }
@supports (-webkit-touch-callout: none) { body.v2 .fbc-send input { font-size:16px; } }
body.v2 .fbc-send .btn { flex:0 0 auto; width:auto; padding-left:18px; padding-right:18px; }
body.v2 #fbc-list { max-height:46vh; overflow-y:auto; }

/* A destructive action sitting on a member row should read as destructive without shouting —
   .btn.danger alone is a filled red gradient, which is right for a confirm dialog's final
   button and wrong for a row you are only reading. Ghost keeps it quiet, red keeps it honest. */
/* The FILLED destructive button lost its red under 2.0. .btn.danger is defined once, early, and
   `body.v2 .btn` sets the violet gradient later in the file - same specificity, so source order
   won and "Delete" rendered identically to "Save". The comment above already says a filled red is
   right for a confirm dialog's final button; 2.0 simply never got the rule. A delete that looks
   like a save is the kind of thing you only notice after you have tapped it.
   Ghost stays quiet - it has three classes, so it still wins over this. */
body.v2 .btn.danger { background:linear-gradient(90deg,#F87171,#FB7185); color:#fff; }
body.v2.light .btn.danger { background:linear-gradient(90deg,#DC2626,#E11D48); color:#fff; }
body.v2 .btn.ghost.danger { background:transparent; color:#F87171; border-color:rgba(248,113,113,.45); }
body.v2 .btn.ghost.danger:active { background:rgba(248,113,113,.12); }

/* His 1s puts the day beside the greeting — quieter than the name, on the same line. */
body.v2 .kid-hello .kh-day { font-weight:600; font-size:12.5px; color:var(--quiet); margin:2px 0 0 2px; }

/* ---- light theme: text that had no colour ------------------------------------------------
   These rules paint their letters as a window onto --grad (`background-clip:text` +
   `color:transparent`). On a dark ground that reads; on a light one it does not, and there is no
   `color` underneath to fall back to — which is why "Welcome back!" rendered as nothing at all.

   The selectors below are GENERATED from the stylesheet's own clip rules, so this cannot drift
   from what it is fixing. -webkit-text-fill-color is the line that matters: it wins over `color`
   on a clipped element, so setting `color` alone leaves the text invisible in WebKit — which is
   every iPhone.

   Note this bug is invisible to a contrast check: the computed `color` IS `transparent`, so a
   luminance test skips the only text that is broken. It reported zero problems on a screen you
   could see was wrong. Render and look. */
body.v2.light .brand .n,
body.v2.light .countdown,
body.v2.light .goal-req b,
body.v2.light .hero-num,
body.v2.light .lock-brand .n,
body.v2.light .mnload-t .n,
body.v2.light .ob-brand .n,
body.v2.light .ob-wordmark span,
body.v2.light .sp-word .n,
/* These three read `body.v2.light body.v2 .x` until 2026-08-15, which asks for a body.v2 nested
   INSIDE a body.v2.light. There is one body, so the selector could never match and the rule has
   never once applied -- the three biggest numbers in the app stayed near-white (#f1f1f6) on a
   white card. His screenshot: "unable to read number net balance ... on white theme", with
   C$597.70 invisible next to a perfectly readable "net". */
body.v2.light .h3-total,
body.v2.light .v2fut-num,
body.v2.light .v2rev-num {
  background: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  color: var(--purple);
  -webkit-text-fill-color: var(--purple);
}

/* The toast is an overlay, not a surface — in light mode its background was being lightened
   while its text stayed near-white, so it read as a blank lilac band. Pin both: a dark toast on
   a light UI is normal and legible, and it keeps the same shape in either theme. */
body.v2.light #toast { background:#12203c; color:#E7ECF5; border-color:rgba(255,255,255,.14); }
body.v2.light #toast .toast-act { color:#5EEAD4; }

/* Tile names were a hardcoded dark-theme grey (#9294a8). On a white card that is 2.6:1 — under
   the 4.5:1 minimum, and lighter than the number beneath it, so the label read as less important
   than its own value. Use the palette's mid tone, which is chosen per theme. */
body.v2.light .tile .nm { color:var(--mid); }

/* LIGHT-MODE TEXT COLOURS ---------------------------------------------------------------
   Everything below was a hard-coded colour chosen against a near-black page. On white they
   measured 2.3-3.9:1 - "nit readable", and he was right. Each is restated to clear AA on white
   without moving the identity: the violet darkens, the greens/reds deepen, greys follow --dim. */
body.v2.light .nm, body.v2.light .ds, body.v2.light .dim,
body.v2.light .d, body.v2.light .hint, body.v2.light .subhead,
body.v2.light .bud-sl, body.v2.light .v2pg-cap, body.v2.light .merch-sub,
body.v2.light .li-d, body.v2.light .sg-sub { color:var(--dim); }
/* Three of the greys above ALSO carry a dark-theme opacity dimmer (.li-row .d at .6, .bud-sl
   at .55, .v2pg-cap at .5) — following --dim on top of it stacked two dimmers, and every list
   row's subtitle sat at 2.89:1 on white. In light the colour does the dimming; the opacity
   comes off. Dark keeps both halves of its own recipe. */
body.v2.light .li-row .d, body.v2.light .bud-sl, body.v2.light .v2pg-cap { opacity:1; }
/* accent text on a light surface */
body.v2.light .v2sa, body.v2.light .edit-trk,
body.v2.light .brand .n, body.v2.light .brand-sub,
body.v2.light a, body.v2.light .link { color:#6D28D9; }
/* semantic money colours: the dark-mode green and red are far too light on white */
body.v2.light .good, body.v2.light .amt.good, body.v2.light .fh-num.good { color:#15803D; }
body.v2.light .bad,  body.v2.light .amt.bad  { color:#B91C1C; }
body.v2.light .tag.due { color:#B45309; }
/* ...and the rest of the amber due cues — all hard-coded #f59e0b for the dark card and never
   given a light value (body.v2 keeps matching because the theme ADDS .light): the Coming-up
   date (2.15:1 on white), the "due in Nd" / "still due" badge (1.99:1 on its own tint), the
   calendar's due day (1.92:1) and the spending heat-map's mid band. The plain surface follows
   --gold's light value; the ones sitting on their own amber tint go a step deeper — #B45309
   measures only 4.48:1 there — same move as the green badge further down. */
body.v2.light .cu-d { color:#B45309; }
body.v2.light .v2badge.by, body.v2.light .cal-c.cal-due, body.v2.light .v2cal-c.mid { color:#92400E; }

/* the last two: the brand's violet sub-line and every semantic green, both hard-coded per element
   with selectors more specific than the generic .good rule above. */
body.v2.light .v2nav-brand span { color:#6D28D9; }
body.v2.light .cu-a.good,
body.v2.light .hm-t b.good,
body.v2.light .bud-hero-sub .good,
body.v2.light .bud-sv.good { color:#15803D; }
body.v2.light .hm-t b.bad,
body.v2.light .bud-hero-sub .bad,
body.v2.light .bud-sv.bad { color:#B91C1C; }
body.v2.light .hm-t b.gold, body.v2.light .bud-sv.gold { color:#B45309; }

/* LIGHT MODE: the remaining semantic colours, found by sweeping all 13 tabs rather than guessing.
   Same rule throughout - the dark-mode green/amber/red/violet are tuned for a near-black card and
   sit at 1.9-4.0:1 on white. Deepened to clear AA, identity unchanged. */
body.v2.light .hero-num, body.v2.light .hero-num span,
body.v2.light .hl-score, body.v2.light .hb-s.good,
body.v2.light .sg-st.good { color:#15803D; }
body.v2.light .hb-s.bad { color:#B91C1C; }
body.v2.light .bud-sv { color:var(--text); }
body.v2.light .bud-sv.good { color:#15803D; }
body.v2.light .bud-sv.gold, body.v2.light .bud-sv.warn { color:#B45309; }
body.v2.light .v2pf-s, body.v2.light .sg-sub, body.v2.light .prof-sub { color:var(--dim); }
/* any remaining violet used as small text */
body.v2.light .pct, body.v2.light .share, body.v2.light .v2pct { color:#6D28D9; }

/* SEMANTIC MONEY COLOURS as tokens. These exist because a handful of figures carry their colour
   inline in the markup, where no stylesheet rule can reach them - which is why "good" greens stayed
   at 2.3:1 on white however many overrides I added. Inline styles resolve variables, so the markup
   now asks for the meaning and each theme answers with a colour that is readable on its own page. */
:root      { --pos:#34D399; --neg:#ef4444; --warn:#f59e0b; }
body.light { --pos:#15803D; --neg:#B91C1C; --warn:#B45309; }
body.v2.light { --pos:#15803D; --neg:#B91C1C; --warn:#B45309; }

/* The coach badge sits on a teal-tinted card, not the usual dark one, so the violet lands at
   3.55:1 there while it clears elsewhere. Lightened for this surface only. */
body.v2:not(.light) .coach-badge { color:#C89BFB; }
/* ...and the opposite problem on the light page: that same teal-tinted card needs a DARK violet.
   Scoping matters - my first attempt at this rule was unscoped and took light from 0 failures to 31. */
body.v2.light .coach-badge { color:#5B21B6; }

/* --quiet is 2.0's "quiet" text: subtab labels, tile captions, KPI keys.
   It has to differ per theme and that is the whole point. The original #5c5e72 is a DARK grey:
   fine on a white page at 5.6:1, but only 2.77:1 on the dark card where it is actually used.
   Swapping it globally for a lighter grey fixed dark and broke light - so it is a token now. */
body.v2       { --quiet:#8B8DA6; }
body.v2.light { --quiet:#5C5E72; }

/* Locked achievement tiles stack two opacities — the tile's .45 times the hint's own .7 left
   the 8px "how to unlock" line at 2.04:1 on a white tile. In light the fade moves to the icon
   and the words take --dim at full strength; earned tiles and the dark theme keep their look. */
body.v2.light .v2ach.off { opacity:1; }
body.v2.light .v2ach.off .v2ach-ic { opacity:.45; }
body.v2.light .v2ach.off .v2ach-n,
body.v2.light .v2ach.off .v2ach-s { opacity:1; color:var(--dim); }

/* LIGHT MODE, PASS 2 -----------------------------------------------------------------------
   My first pass swept 13 TABS. He then found unreadable text on "Mine and ours", which is a
   sub-page - so the sweep had a hole in it, not the theme. Re-run across all 147 views, these are
   every remaining offender. The worst was his screenshot exactly: a near-white #f1f1f6 figure
   ("$4,238 left") on a white card at 1.13:1 - the DARK theme's text colour, hard-coded. */
body.v2.light .sp-budrow b,
body.v2.light .cal-c.cal-today { color:var(--text); }
/* muted greys */
body.v2.light .v2net-s,
body.v2.light .sp-budrow span,
body.v2.light .prof-av.add { color:var(--dim); }
/* semantic colours tuned for a black card */
body.v2.light .v2badge.bg,
body.v2.light .v2list .li-a.good { background:rgba(21,128,61,.10); color:#15803D; }
body.v2.light .v2badge.br,
body.v2.light .v2list .li-a.bad,
body.v2.light .v2day-v.hot,
body.v2.light .sp-budrow b.bad { color:#B91C1C; }
body.v2.light .btn.ghost.danger, body.v2.light .btn.ghost[style*="F87171"] { color:#B91C1C; }
/* accent-coloured labels */
body.v2.light .v2plan-b,
body.v2.light .v2rec-lbl,
body.v2.light .v2tl-when.acc,
body.v2.light .wi-v { color:#6D28D9; }
body.v2.light .v2net-l.sp-hl { color:#BE185D; }
/* white-on-tint chips: deepen the tint so white still reads. The Who-paid-in card lost this
   rule to specificity: `body.v2.light .sp-budrow span` (the grey meta text, above) outranks the
   bare .sg-av selector, so the initial went --dim on the avatar's own dark chip — 1.08:1. The
   scoped selector wins that war without touching the row's real meta spans. */
body.v2.light .sg-av, body.v2.light .sp-budrow .sg-av { color:#fff; }

/* Light theme: four more places that froze a dark-theme colour into the rule instead of taking
   it from the palette. Each one measured on white before and after.
     .sp-budrow b   #f1f1f6  1.13:1  -> --text   16.0:1   ("$2,689 left", "$1,389 each")
     .sp-budrow     #9294a8  2.99:1  -> --mid    11.3:1
     .sp-gh-s       (dim)    2.99:1  -> --mid    11.3:1   (every 2.0 page subtitle)
     .sp-back       #f1f1f6  1.13:1  -> --text            (the back arrow on ~every 2.0 page)
   The back button also carried a white-on-white border and fill, so it had no edge at all. */
body.v2.light .sp-budrow { color:var(--mid); }
body.v2.light .sp-budrow b { color:var(--text); }
body.v2.light .sp-gh-s { color:var(--mid); }
body.v2.light .sp-back { color:var(--text); background:rgba(17,18,25,.04); border-color:rgba(17,18,25,.12); }

/* LIGHT MODE, PASS 3 - the last few, each measured rather than guessed.
   The swipe-to-delete red carries white text: #E5484D gives 3.91:1, so it deepens on a light page.
   The green badge sits on its own green tint, which costs contrast the plain colour did not. */
body.v2.light .v2badge.bg,
body.v2.light .v2list .li-a.good { color:#14532D; }

/* LIGHT MODE, KID SCREENS -------------------------------------------------------------------
   His screenshot: the quiz answers were pale mint and pale pink - colours picked to glow on a
   black card - sitting on their own pale tint. Unreadable. My sweep missed them because the
   marked-up states only exist AFTER a question is answered, which I never seeded. */
body.v2.light .qz-opt.qz-correct { color:#0F5132; }
body.v2.light .qz-opt.qz-wrong   { color:#842029; }
body.v2.light .v2kid-amt         { color:#15803D; }

/* LIGHT MODE, SIGNED-IN -----------------------------------------------------------------------
   Found only after sweeping while SIGNED IN - the profile and plan screens render different markup
   once there is an account, so a signed-out sweep never sees these. */
body.v2.light .v2plan-cur { color:#15803D; }
/* removed: '#view b' is an ID selector, so it outranked every per-element rule and forced
   bold text to inherit the dark colour. Overreaching selector, my mistake. */
/* the date/currency pickers on "new group" print their value in a <b> fixed at the dark theme's
   text colour - 1.13:1 on a white sheet. */
body.v2.light .sd-edge b { color:var(--text); }

/* THE ONE HE KEPT REPORTING ------------------------------------------------------------------
   The name on "Profile & account" is .v2fut-num, painted with its OWN gradient that starts at
   #f1f1f6 - near-white. On the dark page that is the bright end of the sweep; on a white page the
   first half of the name is invisible, which is exactly what his screenshots showed.
   It has its own gradient rather than var(--grad), so neither the token work nor any contrast
   sweep could reach it: transparent text has no colour to measure. Solid --text on light. */
body.v2.light .v2fut-num {
  background:none; -webkit-background-clip:border-box; background-clip:border-box;
  color:var(--text); -webkit-text-fill-color:var(--text);
}
/* the add-expense sheet's tabs: panes stay in the DOM so nothing is lost on save */
body.v2 .sxtab { display:none; }
body.v2 .sxtab.on { display:block; }

/* The top bar carries five things plus the avatar since the theme button went back in, and on a
   360px phone with a back arrow and a 9+ badge the brand was being clipped to "MUSE NEXU".
   His words: "need to squeeze up". Tighter buttons and gaps buy back ~24px, which is enough for
   the wordmark at every width the app supports. */
body.v2 .v2nav-r, body.v2 .v2nav-actions { gap:3px; }
body.v2 .v2nav-btn { width:30px; height:30px; font-size:13px; }
body.v2 .v2nav-av { width:28px; height:28px; font-size:12px; }
@media (max-width:370px) {
  body.v2 .v2nav-btn { width:28px; height:28px; font-size:12px; }
  body.v2 .v2nav-brand { font-size:9.5px; }
}

/* "how we can align this" - the expense rows. The text column must be allowed to shrink or it
   forces the row wider than the phone and the title wraps; the amount column must not shrink or
   the figures stop lining up with each other down the list. */
body.v2 .row[data-row^="budget.txns"] .l { min-width:0; }
body.v2 .row[data-row^="budget.txns"] .l .t { overflow-wrap:break-word; }
body.v2 .row[data-row^="budget.txns"] .r { flex:0 0 auto; white-space:nowrap; }
/* the MINE/SHARED label now rides the meta line, so it sits with the text rather than stealing a column */
body.v2 .v2badge.rowtag { margin-right:7px; vertical-align:middle; position:relative; top:-1px; }
body.v2 .tagline { margin-top:2px; }
body.v2 .ttag { white-space:nowrap; }

/* "squeeze tab" - Budget's four tabs are the file's own labels (Overview / Transactions / Income /
   Forecast) so they cannot be renamed, and "Transactions" did not fit a quarter of a 360px screen.
   It wrapped, and because grid cells match heights, all four went to two lines.
   Note the ordering trap this hid behind: line 2341 already shrinks these under 400px, but the
   plain .subtabs-sm rule at 2652 comes LATER in the file and put the size back. Scoped under
   body.v2 so it wins, and nowrap so a tight fit shows as a tight fit rather than silently wrapping. */
body.v2 .subtabs-sm .subtab { white-space:nowrap; letter-spacing:-.1px; }
/* The 360px rule up at line ~2218 sets .subtab { font-size:12.5px !important } - added for an
   earlier "squeeze all tabs" pass, but 12.5px is BIGGER than the 11.5px base and the 10.5px that
   kicks in under 400px, so on a 360px phone it un-squeezed them and "Transactions" wrapped. That
   rule is tuned for 1.0's shorter 3-up strips, so it stays; 2.0's 4-up strip overrides it here. */
@media (max-width:412px) {
  body.v2 .subtabs-sm .subtab { font-size:10px !important; padding:7px 2px !important; }
  body.v2 .subtabs.subtabs-sm { gap:2px; padding:2px; }
}
@media (max-width:340px) { body.v2 .subtabs-sm .subtab { font-size:9px !important; padding:7px 1px !important; } }

/* Renewal checklist (his pg-insurance frame): four steps, ticked ones struck through, the next one
   you have not done called out in the accent. Boxes are 18px like his. */
body.v2 .rnw-step { display:flex; align-items:center; gap:8px; padding:8px 0;
  border-bottom:1px solid rgba(255,255,255,.06); cursor:pointer; }
body.v2 .rnw-step:last-of-type { border-bottom:none; }
body.v2 .rnw-box { width:18px; height:18px; flex:0 0 18px; border-radius:4px;
  border:2px solid var(--line); display:flex; align-items:center; justify-content:center;
  font-size:11px; line-height:1; color:var(--teal); }
body.v2 .rnw-lbl { font-size:12.5px; color:var(--quiet); }
body.v2 .rnw-step.next .rnw-box { border-color:var(--teal); }
body.v2 .rnw-step.next .rnw-lbl { color:var(--text); font-weight:700; }
body.v2 .rnw-step.done .rnw-box { border-color:var(--teal); }
body.v2 .rnw-step.done .rnw-lbl { text-decoration:line-through; opacity:.75; }
body.v2 .rnw-step:active { opacity:.7; }

/* Member contributions (his pg-familybudget): one row per person - who put in what, their share
   of the month and how many transactions it took. */
body.v2 .fbc-row { padding:10px 0; border-bottom:1px solid rgba(255,255,255,.06); }
body.v2 .fbc-row:last-child { border-bottom:none; }
body.v2 .fbc-nm { display:flex; align-items:center; gap:8px; font-weight:700; font-size:13px; }
body.v2 .fbc-row .track { margin:6px 0 4px; }
body.v2 .fbc-row .hint { font-size:11px; }

/* landing highlight when an entry point routes you to a card (e.g. Profile -> Import Expenses) */
body.v2 .card.flash { animation: cardflash 1.5s ease; }
@keyframes cardflash { 0%,100% { box-shadow:none; } 25% { box-shadow:0 0 0 3px var(--teal); } }


/* --- 2.0 sub-pages: the header collapses to a back button (his tracker design) ------------------
   The brand lockup and the icon row are the FRONT DOOR's furniture. On a page you opened to do one
   thing, his file shows a single "9 Back" and nothing else, so the page's own title is the first
   thing you read instead of the third. */
body.v2.v2sub #v2nav-l,
body.v2.v2sub #v2nav-r { display:none !important; }
body.v2.v2sub #topbar { justify-content:flex-start; }
/* "top bar is absurd - make it bigger and reasonably visible - follow designs". His frame's back
   control is a proper target you can hit without aiming: chevron plus the word, at reading size.
   Mine was 15px in the accent colour on a dark bar, which is legible but looks like an afterthought
   next to his. Bigger type, a real tap target, and it keeps the accent so it still reads as a link. */
body.v2.v2sub #btn-back {
  display:flex !important; align-items:center; gap:6px;
  background:none; border:0; padding:6px 10px 6px 0; margin-left:-2px;
  color:var(--g2); font-size:22px; font-weight:800; width:auto; height:auto;
  min-height:44px; line-height:1; }
/* The word used to be hard-coded here as ::after{content:"Back"}. Since the button now carries
   the real destination name in .bk-w, that literal printed BESIDE it — his screenshot reads
   "‹ Home Back", and his word for it was "absurd". The typography his design asked for
   (reading size, not an afterthought) moves onto the real word instead.
   2026-09-01, his "screen behind screen" screenshot + pick "1": at 19px/800 in the accent colour
   the word read as a SECOND PAGE TITLE stacked above the real one — he thought two screens were
   layered. The chevron keeps its size and accent (that is the tap affordance); the word drops to
   caption weight in the muted token — a colour change, not an opacity, because stacking a dimmer
   on a token is the exact light-mode bug fixed elsewhere in this file today. */
body.v2.v2sub #btn-back .bk-w { font-size:13.5px; font-weight:650; letter-spacing:.2px; max-width:52vw;
  color:var(--dim);
  /* OPTICAL, not geometric: flexbox centres the BOXES to within 0.1px (measured), but the
     guillemet's ink sits low in its 22px line box while a 13.5px word's ink floats high — his
     "profile and back button not being aligned", visible in the screenshot. The nudge aligns
     the INK. If the chevron's font-size at :4403 changes, re-eyeball this. */
  transform:translateY(2.5px); }
/* the sub-page rule above is !important, so hiding the chevron from JS silently did nothing on
   the profile picker — the inline display:none lost to it. This class is how a screen opts out. */
body.v2.v2sub.nobk #btn-back, body.v2.nobk #btn-back { display:none !important; }
/* "back button invisible" - and it was, once he scrolled. On a sub-page the header IS the back
   control, and it scrolled away with the page like any other content, so the only way back off a
   long screen was the hardware button. Pinned to the top on sub-pages only; the front-door screens
   keep their scrolling header, because there the bar is branding, not a control you need. */
/* sticky was not enough - the BODY scrolls here, and it slid away with everything else (measured:
   the control ended up 509px above the viewport). Fixed, with the page given matching room so
   nothing hides underneath it. */
body.v2.v2sub #topbar {
  /* a sub-page bar carries one 44px back control, not the 56px brand lockup, so it keeps its own
     min-height - otherwise the shared 56px plus this 6/6 padding would make it taller than the
     front-door bar it replaces */
  min-height:44px;
  padding-top:calc(env(safe-area-inset-top, 0px) + 6px); padding-bottom:6px;
  /* "back button invisible" - measured, and it was literally off-screen: the front-door bar centres
     itself with left:50% + translateX(-50%), and this rule used to override left/right WITHOUT
     resetting the transform, so the whole sub-page bar was dragged half its width to the left
     (back control at -192.6px in a 412px viewport). Centre it the same way the front-door bar does,
     so both align to the app column instead of fighting each other. */
  position:fixed; top:0; left:50%; right:auto; transform:translateX(-50%);
  width:100%; max-width:560px; z-index:60; background:var(--bg);
  border-bottom:1px solid var(--line); }
body.v2.v2sub #view { padding-top:calc(env(safe-area-inset-top, 0px) + 58px); }


/* --- frame 3c: the Add-something sheet reads top to bottom ------------------------------------- */
body.v2 .qa-sub { font-weight:600; font-size:12.5px; color:var(--quiet); letter-spacing:0; }
body.v2 .qa-sh { font-size:10.5px; font-weight:800; letter-spacing:1.6px; text-transform:uppercase;
  color:var(--quiet); margin:14px 2px 8px; }
body.v2 .qa-row { margin:0 0 2px; }
body.v2 .qa-row .chip { white-space:nowrap; }
body.v2 .qa-note { font-size:12px; color:var(--dim); margin:6px 2px 0; line-height:1.45; }
body.v2 .qa-sheet { max-height:86vh; overflow-y:auto; }


/* the two Budget add buttons: compact, one line each, equal width */
body.v2 .bud-addrow { display:flex; gap:8px; }
body.v2 .bud-addrow .btn { flex:1 1 0; width:auto; margin-top:0; min-height:40px;
  font-size:13.5px; font-weight:700; white-space:nowrap; padding:8px 10px; }

/* Month divider inside the forecast list - a 12-month window is ~200 rows and unreadable as one run. */
.fc-mh { font-size:12px; font-weight:800; letter-spacing:.4px; text-transform:uppercase;
  color:var(--dim); margin:12px 0 4px; padding-top:8px; border-top:1px solid var(--line); }
.fc-mh:first-child { margin-top:2px; padding-top:0; border-top:0; }

/* "shqred expense regualr payment seems srretched". .v2net-a .btn is padding:8px 0 - no horizontal
   padding at all - which never showed on the group header because "Add expense" and "Trip card"
   are short. "Regular payment" is not, so at flex:1 the label ran the full width of the pill and
   read as stretched. Same height and type he asked for; the pills just hug their text now. This is
   a modifier so the group header row he pointed at is untouched. */
/* "put in the middle of two tabs not centerd": hugging content left the two pills DIFFERENT widths
   (Shared expense 165, Regular payment 185), so the pair sat lopsided rather than centred. A grid
   with equal auto-columns and a fit-content width makes both pills the same size and centres the
   pair as one block. The margin lives here, not inline - `margin:10px 0 6px` sets left/right to 0
   and would cancel the centring. */
body.v2 .v2net-a.v2addrow { display:grid; grid-auto-flow:column; grid-auto-columns:1fr;
  /* "little upwards from centre" - 8px higher, with the top and bottom margins still adding to 16
     so the block occupies the same height and nothing below it shifts. */
  /* Measured off the rendered pixels, not element rects: the gap above was 22 and below 7, so the
     row sat low and my earlier +2/-14 nudge did nothing - a positive top margin just COLLAPSES
     into the card's bottom margin. A negative one actually pulls it up. Centre is -7.5 here and
     -5 on Budget. Measured empirically at 2px of movement per 1px of margin here, so -4 lands
     both a little above centre - gap 10 above, 19 below - which is what he asked for. */
  width:fit-content; max-width:100%; margin:0 auto 20px; }
/* Budget sits under three stat tiles whose bottom margin is much smaller than the Family hero
   card's, so the same rule left it 17 above / 48 below - well past "a little" above centre.
   Its own top margin, measured on the page rather than guessed from the other one. */
body.v2 .v2net-a.v2addrow-bud { margin-top:15px; }   /* 14 sat too high, 28 too low, 18 a shade low */
body.v2 .v2net-a.v2addrow .btn { padding:8px 18px; white-space:nowrap; }
/* the yearly figure on a policy row keeps its unit — a subscription's amount needs no suffix
   because the row is already about one cycle, but "$1,440" on its own reads as monthly */
body.v2 .li-row .amt .amt-sfx { font-size:.72em; font-weight:600; opacity:.55; margin-left:1px; }
/* the badge on a bill row IS the mark-paid control (his frame shows a badge, not a button) */
body.v2 .v2billbtn { background:none; border:0; padding:0; margin-top:4px; cursor:pointer; display:block; }
body.v2 .v2billbtn:active { transform:scale(.94); }
/* "buttons are very big ... make it adapt to the design" — the three choices on a captured payment
   were full-size chips that wrapped onto two lines and made every row 122px tall. Small enough to
   sit on one line under the merchant, which is what the rest of the 2.0 lists look like. */
/* "fix the cut off on it split cutting off" — nowrap was the wrong half of that trade. With a
   household AND a split group there are FOUR chips (Mine · Share · Split · ✕), and measured at his
   360px width they need 245px inside a 185px column: 60px of overflow, the last chip ending at
   363px on a 360px screen, straight under the amount. Wrapping costs a second line only in that
   four-chip case; clipping cost him the Split button entirely. min-width:0 lets the column
   actually shrink to its share instead of forcing the overflow. */
body.v2 .tb-acts { gap:6px; flex-wrap:wrap; min-width:0; }
/* With the ✕ gone there are at most three, so they share the width evenly and can never collide
   with the amount — a better answer than the wrapping this rule used to need. */
body.v2 .tb-acts .chip { flex:1; text-align:center; }
/* Dismiss is a different QUESTION, so it gets a different voice: quiet, worded, and undoable. */
body.v2 .tb-ignore { display:flex; align-items:center; gap:8px; margin-top:9px; padding-top:8px;
  border-top:1px solid rgba(255,255,255,.05); font-size:11.5px; color:var(--quiet); }
body.v2 .tb-ignore .lnk { margin-left:auto; background:none; border:0; padding:0; font-size:11.5px;
  font-weight:600; color:var(--g2); }
body.v2 .tb-acts .chip { font-size:11.5px; padding:4px 10px; min-height:0; line-height:1.5; white-space:nowrap; }
/* "when we split under white theme cant read the splitter amouny.. its greyed out" — and it was
   white-on-white. 2.0 is a dark design, so these controls hardcode near-white text; the light
   theme's input rule only covers inputs inside a .field, and none of these are. Anything that
   takes or shows a typed value has to state its colour in BOTH themes. */
body.v2.light .sx-sh, body.v2.light .sx-big, body.v2.light .v2search input,
body.v2.light .sx-date .d { color:#15121F; }
body.v2.light .sx-sh { background:#FFFFFF; border-color:rgba(17,18,25,.20); }
body.v2.light .sx-sh::placeholder, body.v2.light .sx-big::placeholder { color:#8b88a0; }
body.v2.light .sp-hero .btn.ghost { color:#15121F; border-color:rgba(17,18,25,.22); }

/* ── Auto-categorise: the design's rule toggle ────────────────────────────────────────────────
   His numbers, verbatim: 40×22 track, 11px radius, a 16px white knob 2px in that slides 18px.
   The knob uses the bouncy easing — it is the one place in the design file that already did. */
.v2tog { position:relative; width:40px; height:22px; border-radius:11px; flex-shrink:0; cursor:pointer;
  background:var(--bg2); border:1px solid var(--line); padding:0; transition:background .2s, border-color .2s; }
.v2tog::after { content:''; position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%;
  background:#fff; transition:transform .2s cubic-bezier(.34,1.18,.64,1); }
.v2tog.on { background:var(--g2); border-color:var(--g2); }
.v2tog.on::after { transform:translateX(18px); }
/* HIS REPORT: "disable button under auto catgorize not visible under whitw theme when disbaled".
   The OFF state is --bg2 with a --line border and a white knob. In dark that is a grey pill on a
   dark card and reads fine; in light all three are near-white and it sits on a white card, so a
   paused rule showed an empty space where the other rows show an orange switch. The ON state was
   never the problem — an OFF control that cannot be seen cannot be turned back ON.
   A real grey track, a bordered knob, and a darker edge, so the switch is visibly a switch in both
   positions and both themes. */
body.v2.light .v2tog { background:#DEDAEC; border-color:#8B84A3; }
body.v2.light .v2tog::after { background:#FFFFFF; box-shadow:0 1px 2px rgba(17,18,25,.28); }
body.v2.light .v2tog.on { background:var(--g2); border-color:var(--g2); }
body.v2 .li-row.ruleoff .v2li-i, body.v2 .li-row.ruleoff .l { opacity:.5; }
/* the small grey note that sits opposite a section heading ("from your history") */
.v2sn { font-size:10px; color:var(--dim); font-weight:600; }
/* the design's accented footnote card under the suggestions */
.v2tipcard { background:rgba(var(--teal-rgb,45,212,191),.08); border-color:rgba(var(--teal-rgb,45,212,191),.35); }
.v2tipcard .t { font-size:12.5px; font-weight:800; margin-bottom:3px; }
.v2tipcard .d { font-size:11.5px; color:var(--dim2, var(--dim)); }
@media (prefers-reduced-motion: reduce) { .v2tog::after { transition:none; } }

/* "renewal date make.it bold" — the value that carries the deadline, weighted above its neighbours */
body.v2 .v2list .li-a.strong { font-weight:800; }
/* the invite code is read aloud / typed by someone else — spaced monospace so 0/O and 1/I differ */
.invcode { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; letter-spacing:1.5px; }

/* the design's amber "Repeat & reminder" card — a thing you may act on, not a warning */
.v2warncard { background:rgba(245,158,11,.07); border-color:rgba(245,158,11,.35); }
.v2warncard .t { font-size:13px; font-weight:800; margin-bottom:4px; }
.v2warncard .d { font-size:12px; color:var(--dim); line-height:1.45; }
body.light .v2warncard { background:rgba(180,83,9,.08); border-color:rgba(180,83,9,.3); }

/* ── Insights → Calendar, per his design file ────────────────────────────────────────────────
   Amount-in-the-cell, banded by colour. min-width:0 on the cells is what stops a long figure from
   forcing the grid wider than the phone: without it a 7-column 1fr grid grows to fit its content
   and the whole page scrolls sideways. */
.v2cal-h { font-size:12.5px; font-weight:800; margin-bottom:8px; }
.v2cal-dows { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:3px; text-align:center;
  font-size:9px; font-weight:700; color:var(--dim); padding-bottom:4px; }
.v2cal-grid { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:3px; text-align:center; }
.v2cal-c { min-width:0; padding:6px 2px; border-radius:5px; font-size:10px; font-weight:700;
  background:var(--bg2); color:var(--dim); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.v2cal-c.empty { background:none; }
.v2cal-c.fut   { opacity:.35; }
.v2cal-c.zero  { background:rgba(34,197,94,.12);  color:#22c55e; }
.v2cal-c.lo    { background:var(--bg2);           color:var(--text); }
.v2cal-c.mid   { background:rgba(245,158,11,.14); color:#f59e0b; }
.v2cal-c.hi    { background:rgba(239,68,68,.16);  color:#ef4444; }
.v2cal-c.today { outline:1.5px solid var(--g2); outline-offset:-1.5px; }
/* TRADING P/L CALENDAR. Its own two bands rather than reusing the spending heat-map's, because the
   colours mean the OPPOSITE thing there: on Spending, red/hi is a big spend and green/zero is a day
   you spent nothing. On a trading calendar green is a winning day and red is a losing one, and
   sharing classes would have made one of the two screens lie. */
.v2cal-c.win  { background:rgba(34,197,94,.16);  color:#22c55e; }
.v2cal-c.lose { background:rgba(239,68,68,.16);  color:#ef4444; }
.v2cal-c.flat { background:var(--bg2);           color:var(--mid); }
/* The day number and the figure stack inside one square. The number is always shown — a P/L
   calendar is read as a log ("which day was the −$260?"), not as a shape like the spending
   heat-map, and without it the squares are unidentifiable. */
.v2cal-c { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:1px; line-height:1.1; }
.v2cal-dn  { font-size:9px; font-weight:700; opacity:.62; }
/* His note: "profit month is very close to the calendar". The verdict line sat directly on top of
   the weekday row with nothing between them, so the sentence read as part of the grid's header
   rather than as the month's summary. The gap is what separates the two ideas. */
.v2cal-dows { margin-top:2px; padding-bottom:2px; }
.v2cal-amt { font-size:9.5px; font-weight:800; }
/* The standing "not in Budget yet" banner on the P/L calendar. Deliberately a NOTE, not an alert:
   nothing is wrong, there is simply something the journal is still holding. It only exists while
   there is something to do, so it never becomes furniture people learn to ignore. */
.v2note { border:1px solid rgba(var(--g1-rgb),.30); background:rgba(var(--g1-rgb),.07);
  border-radius:12px; padding:10px 12px; font-size:12.5px; }
body.light .v2note { border-color:rgba(var(--g1-rgb),.28); background:rgba(var(--g1-rgb),.06); }
body.light .v2cal-c.win  { background:rgba(21,128,61,.13); color:#15803D; }
body.light .v2cal-c.lose { background:rgba(185,28,28,.12); color:#B91C1C; }
/* the design's green Round-Up card */
.v2goodcard { background:linear-gradient(135deg,rgba(34,197,94,.07),rgba(6,182,212,.07)); border-color:rgba(34,197,94,.3); }
.v2goodcard .t { font-size:12.5px; font-weight:800; margin-bottom:3px; }
.v2goodcard .v { font-size:22px; font-weight:900; color:#22c55e; line-height:1.15; }
.v2goodcard .d { font-size:10.5px; color:var(--dim); margin-top:2px; }
body.light .v2cal-c.zero { background:rgba(21,128,61,.12); color:#15803D; }
body.light .v2cal-c.mid  { background:rgba(180,83,9,.14);  color:#B45309; }
body.light .v2cal-c.hi   { background:rgba(185,28,28,.13); color:#B91C1C; }
body.light .v2goodcard .v { color:#15803D; }

/* ── New group, compacted to one screen ──────────────────────────────────────────────────────
   The saving is in the scaffolding, not the controls: section headings live inside the card they
   head, and the gaps between cards close up. Every control keeps its full tap target. */
.sc-tight .card { margin-top:10px; padding:12px 14px; }
.sc-tight .card:first-child { margin-top:0; }
.sc-lbl { font-size:10.5px; font-weight:800; letter-spacing:1px; text-transform:uppercase;
  color:var(--dim); margin-bottom:7px; }
.sc-tight .field { margin-bottom:0; }
.sc-tight .hint { margin-top:6px; font-size:11px; line-height:1.4; }
.sc-tight .btn { margin-top:12px; }
.sc-alt { text-align:center; font-size:11.5px; color:var(--dim); margin-top:10px; padding-bottom:4px; }
.lnk { background:none; border:none; padding:0; font:inherit; font-weight:700; color:var(--g2);
  cursor:pointer; text-decoration:underline; text-underline-offset:2px; }
/* the five "what is it for" chips wrapped onto two rows purely on padding — trimmed so they sit on
   one, which is worth 41px on a screen that has to end before the fold */
.sc-tight .v2chips .chip { padding:6px 9px; font-size:11.5px; }
.sc-tight .card { margin-top:8px; }
/* The ✓ prefix widens the selected chip by ~18px, which is what pushed "What is it for?" onto a
   second row. On this screen the chip is already filled and outlined when chosen, so the tick is
   saying it a third time — and the row it costs is a row he has to scroll past. */
.sc-tight .v2chips .chip.on::before { content:none; }
.sc-tight .sd-edge { padding:8px 10px; }
.sc-tight .card { padding:11px 13px; }
.sc-title { font-size:19px; font-weight:900; letter-spacing:-.2px; margin:0 2px 10px; }
/* Hand-written page headers carry their own "‹" too. On a 2.0 sub-page the topbar IS the Back
   button, so the second one is hidden rather than edited out of 28 screens one at a time. */
body.v2.v2sub .sp-ghead .sp-back { display:none; }
body.v2.v2sub .sp-ghead { padding-left:2px; }
/* a cancelled subscription is still a real row you can open — just visibly retired */
.subcancelled .t { text-decoration:line-through; text-decoration-thickness:1px; opacity:.75; }

/* ── "why tabs uneven" / "some big some.small" ────────────────────────────────────────────────
   Numbers pack three per row (span 2) so short amounts share a line; selects take half (span 3).
   Put one of each on the same row — Value + Category on Edit asset — and you get 2 + 3: two boxes
   of different widths with a sixth of the row left empty, and "12500" clipped inside the narrow
   one. When a number and a select are neighbours they each take half, so the pair is even and the
   number gets room. Runs of numbers still pack three to a line. */
/* NOTE: :has() may not contain another :has() — `.field:has(+ .field:has(select))` is a parse
   error and takes the whole rule down with it, silently. Only measuring catches that.

   Pair rules were the first attempt and they did not hold: a sweep of every form in the app found
   seven more uneven rows (Target 118 next to Saved so far 182, Ticker 182 next to Shares 118…),
   and one of my own rules was CAUSING some of them by widening whichever number happened to be
   last. Chasing each combination with another selector was going to keep missing one.

   So every short field is now the same half-row. Numbers, dates, short text and dropdowns all take
   span 3, which makes an uneven row impossible by construction rather than by enumeration, and
   gives a number room for "12500" instead of clipping it.

   The trade, stated plainly: plain numbers used to pack THREE to a line, which he asked for once.
   Three per line is what made them 118px wide, and 118px is what clipped the amount and left the
   odd one out beside a dropdown. Even and readable beats one more field per row. */
.modal > .field:has(> input[type="number"]) { grid-column:span 3; }

/* Preferences, compacted: section names live inside the card they head instead of as standalone
   rules with margins of their own. Same technique as New group. */
.prefs-tight .card { margin-top:10px; padding:12px 14px; }
.prefs-tight .card:first-child { margin-top:0; }
.p-lbl { font-size:10.5px; font-weight:800; letter-spacing:1px; text-transform:uppercase;
  color:var(--dim); margin-bottom:6px; }
.prefs-tight .v2list .li-row:first-of-type { border-top:none; }
/* the currency chips wrapped to two rows on padding alone; the ✓ on the selected one is redundant
   next to a filled chip and was worth a whole row */
.prefs-tight .v2chips .chip { padding:6px 9px; font-size:11.5px; }
.prefs-tight .v2chips .chip.on::before { content:none; }
.prefs-tight .card { margin-top:8px; padding:11px 13px; }
/* The last dozen pixels between the bottom row and the tab bar. The section labels carry the
   spacing now, so the cards themselves need less of it. */
.prefs-tight .card { padding:9px 13px; margin-top:6px; }
.prefs-tight .hint { margin-top:5px; }
.prefs-tight .p-lbl { margin-bottom:5px; }
/* "what spent by day values make it smaller" — the day amounts were set at the same 10px as the
   rest of the grid but sit in a bold weight inside a 50px cell, so they read as the loudest thing
   on the tab. Smaller and lighter; the colour band still carries the reading at a glance. */
.v2cal-c { font-size:9px; font-weight:600; padding:5px 2px; }
.v2cal-dows { font-size:8.5px; }

/* "values not in bold at end" — the label on the left of a 2.0 list row is bold and the amount on
   the right inherited a normal weight, so on "How it's worked out" every figure read as quieter
   than the word next to it. The number is the point of the row. `.strong` stays heavier still, for
   the one value on a card that carries a deadline. */
body.v2 .v2list .li-a { font-weight:700; }
body.v2 .li-row.v2sts-total .li-a { font-weight:800; }
/* His phone is 891 CSS px tall, not the 915 I first measured against — and with the system bars it
   is less again. Rows on this page carry a little less air so it clears on the short ones too. */
/* prefs-fit.cjs went red again — ends 1116 against a bar at 838 — and the cause was not new
   content but a lost specificity war: the 2.0 skin polish later added `body.v2 .li-row`
   (12px padding) and `body.v2 .li-row .r .btn.small` (32px floor), which outrank every plain
   `.prefs-tight` rule below their line count. The squeeze was still in the file and applying to
   nothing. body.v2-prefixed now, so the page-specific rules win on the page they were written for. */
body.v2 .prefs-tight .li-row { padding-top:7px; padding-bottom:7px; }
/* He runs the app at 112% text, and everything on this page scales with it — which is why the page
   still ran past the fold for him when it fitted at 100%. The description lines are the part that
   can give: they are context, not the control, so they sit tighter and wrap less. */
body.v2 .prefs-tight .li-row .d { font-size:11px; line-height:1.3; margin-top:1px; }
body.v2 .prefs-tight .li-row .t { line-height:1.25; }
.prefs-tight .p-lbl { margin-bottom:4px; letter-spacing:.8px; }
/* A row carrying an On/Off button was 83px against 65px for a plain one — the button's own height
   was setting the row's, not the text. And "Back to 100%" only appears when the text size is off
   default, which is exactly when the page is tightest, so it stops being a full-width slab. */
body.v2 .prefs-tight .li-row .r .btn.small { padding:5px 12px; min-height:0; line-height:1.2; }
.prefs-tight #p-fs-reset { width:auto; padding:4px 10px; margin:0; font-size:12px; }
.prefs-tight .fs-range { margin:6px 0 2px; height:22px; }
/* The stepper row holds FOUR controls once "↺ normal" appears (− % + reset) — .btnrow's default
   flex-wrap dropped the reset to a second line and handed the row back the 60px the squeeze had
   just won. One line, no stretch: the LEFT column is the flexible one here. */
body.v2 .prefs-tight .li-row .btnrow { flex-wrap:nowrap; gap:4px; }
body.v2 .prefs-tight .li-row .btnrow .btn { flex:0 0 auto; width:auto; white-space:nowrap; padding:5px 9px; }   /* width:auto matters: base .btn is width:100%, and flex-basis:auto resolves to it — each stepper ballooned to the full row and shoved its neighbours off the right edge (overflow.cjs, 360px) */

/* the Home join-request banner: request on top, the two actions underneath */
.v2joinask .ja-row { padding:4px 0; }
.v2joinask .ja-row + .ja-row { border-top:1px solid var(--line); margin-top:8px; padding-top:10px; }
.v2joinask .ja-row .t { font-size:13px; font-weight:800; margin-bottom:3px; }
.v2joinask .ja-row .d { font-size:11.5px; color:var(--dim); }
.v2joinask .tb-acts { margin-top:8px; display:flex; gap:8px; }

/* "these buttons to big amke ti alligned and small" — the waiting-to-be-confirmed card's Got it /
   Not yet were full .btn small stacked into a wide column, dwarfing the row and squeezing the
   amount off the edge. Chips, right-aligned, one line. */
body.v2 .btnrow [data-payok], body.v2 .btnrow [data-payno] {
  padding:6px 14px; min-height:0; font-size:12.5px; width:auto; }
body.v2 .btnrow:has([data-payok]) { display:flex; gap:8px; justify-content:flex-end; flex-wrap:nowrap; }

/* "save expense -- button is touching the bottom.screen might get cut off" — the pinned Save sat
   flush on the screen edge, under the gesture bar on his phone. The first fix leaned on
   env(safe-area-inset-bottom, 10px), but Android's WebView SUPPORTS env() and reports 0 — the
   fallback never fires — so the label still sat 12px from the edge and the gesture bar chopped it
   ("save chanegs button is cuttong off make.it up"). Then the bar treatment itself failed the eye
   test: "it's so stretched... make a pill for that button. It's like absurd." So: a floating PILL —
   fully rounded, symmetric padding, lifted clear of the gesture bar both while stuck (bottom
   offset) and at rest (margin), with the sheet showing beneath it. max() floor because of the
   env()=0 WebView above. */
body.v2 .modal.sheet-pin > #m-ok { bottom:max(env(safe-area-inset-bottom, 0px), 22px);
  margin-bottom:max(env(safe-area-inset-bottom, 0px), 22px);
  border-radius:999px; padding:14px;
  box-shadow:0 8px 22px rgba(10,10,16,.8); }

/* v582.1 · "categories should be a gray pill like the UI has, text a different colour,
   and squeeze it" — the settle-to-budget picker now wears the app's own chip look */
body.v2 .cat-grid { gap:6px; margin-top:12px; }
body.v2 .cat-grid .catpick { background:var(--bg2); border:1px solid var(--line); color:var(--text);
  border-radius:999px; padding:7px 12px; font-size:13px; min-height:0; line-height:1.3; }
body.v2 .cat-grid .catpick:active { transform:scale(.96); }
body.v2 .cat-grid .catpick.on { border-color:var(--g1); color:var(--g1); font-weight:700; }


/* v583 · "squeeze the tab selectors — still need to scroll on adding group": the five
   what-is-it-for chips now fit one row on a phone instead of wrapping to two */
body.v2 .sc-tight .v2chips { display:flex; flex-wrap:nowrap; gap:6px; overflow-x:auto; scrollbar-width:none; }
body.v2 .sc-tight .v2chips::-webkit-scrollbar { display:none; }
body.v2 .sc-tight .v2chips .chip { padding:7px 11px; font-size:12.5px; white-space:nowrap; flex:none; }
body.v2 .sj-qrimg { margin-top:10px; text-align:center; }
body.v2 .sj-qrimg img { border-radius:12px; background:#fff; padding:8px; }

/* v584 · "this whole dialogue box from the + button shows blue colour from v1 — mentioned
   multiple times": .qa-sheet's base is v1's blue #0E1730 and only v2 LIGHT ever overrode
   it, so 2.0 dark inherited v1 blue. Pinned to the same surface recipe as body.v2 .modal. */
body.v2 .qa-sheet { background:linear-gradient(180deg, rgba(var(--g1-rgb),.13), rgba(var(--g1-rgb),0) 230px), var(--sheet); }

/* v585 · sweep after the qa-sheet catch: every OTHER surface still sitting on v1's blue
   #0E1730 with no v2-dark override. The selector sheet is used by half the app's pickers —
   he'd have hit each of these one screenshot at a time. */
body.v2 .selpop { background:linear-gradient(180deg, rgba(var(--g1-rgb),.13), rgba(var(--g1-rgb),0) 230px), var(--sheet); }
body.v2 .celebrate-card, body.v2 .spop-card, body.v2 .tour-card { background:#171824; border-color:rgba(255,255,255,.08); }

/* small jar for list rows (family "Saving together" got its jars back).
   His v599 verdict: "small, small, tiny jars" that "follow the theme color" — so the sm jar
   shrank again, and every 2.0 jar liquid pours in the chosen accent instead of v1's teal. */
.gjar.sm { flex:0 0 34px; height:44px; border-width:2px; }
.gjar.sm .gjar-pct { font-size:8px; bottom:3px; }
.gjar.sm .gjar-lid { height:4px; }
body.v2 .gjar-liq { background:linear-gradient(180deg, var(--g1), var(--g2)); }
body.v2 .gjar-liq::before { background:rgba(255,255,255,.45); }
body.v2 .gjar.full .gjar-liq { background:linear-gradient(180deg, var(--g2), var(--g3)); filter:drop-shadow(0 0 9px var(--g2)); }
/* the home/budget strips squeeze too — the 70x100 jars read as "that big" on a phone */
body.v2 .jar-strip .jar-cell { width:72px; }
body.v2 .jar-strip .jar { width:48px; height:68px; border-width:2px; }
body.v2 .jar-strip .jar-p { font-size:11px; }

/* "this month in two separate lines" — the chip broke off the month label on his phone */
.mlabel { white-space:nowrap; display:flex; align-items:center; gap:7px; justify-content:center; }
.mnow { white-space:nowrap; }

/* Tight days — the running in-vs-out chart on Coming up. Bars grow from a zero line rather
   than from the floor, because the whole point is which side of zero a day lands on. */
.rw-wrap { position:relative; display:flex; align-items:stretch; gap:1px; height:96px; margin-top:12px; }
.rw-b { position:relative; flex:1; min-width:0; }
.rw-b i { position:absolute; left:0; right:0; display:block; border-radius:2px;
  background:linear-gradient(180deg,#38BDF8,#2DD4BF); }
.rw-b i.neg { background:linear-gradient(180deg,#FB7185,#E11D48); }
.rw-b.lo i { box-shadow:0 0 0 1.5px rgba(255,255,255,.55); }
.rw-zero { position:absolute; left:0; right:0; height:1px; background:rgba(255,255,255,.16); pointer-events:none; }
.rw-cap { text-align:center; font-size:11.5px; color:var(--dim); margin-top:7px; font-variant-numeric:tabular-nums; }
body.v2 .rw-b i { background:linear-gradient(180deg,var(--g1),var(--g2)); }
body.v2 .rw-b i.neg { background:linear-gradient(180deg,#FB7185,#E11D48); }

/* The way out of kid mode, in the top bar so it is the same corner on every kid page. Hidden for
   grown-ups, who reach profiles through the avatar beside it. Driven by body.kidmode because that
   class is re-toggled every render, unlike the bar, which is built once at startup. */
#v2n-parent { display:none; }
/* margin-left:auto keeps it hard right even on subpages, where #topbar switches to flex-start
   and there is no nav row left to push against. */
body.v2.kidmode #v2n-parent { display:flex; margin-left:auto; flex:0 0 auto; }
body.kidmode .v2nav-av { display:none !important; }

/* ---- v678 SPENDING LIST redesign (owner-approved): day-grouped, two-line rows ---- */
/* day header: the day left, its spending total right — the total is what lets one header per
   calendar day earn its line (bare per-date headings were "a table of contents") */
.section-h.sp-day { display:flex; justify-content:space-between; align-items:baseline; gap:10px; }
/* A FOLDED DAY FOLDS ITS HEADER TOO — and this line is what makes that true.
   app.js:29038 already stamps the header "capped-hide" when its rows are past the cap, with a
   comment saying exactly that. It still showed, because `.capped-hide { display:none }` up at :963
   is ONE class and loses to `.section-h.sp-day` above, which is two and comes later. So the rows
   obeyed display:none and the header kept display:flex: a date with a total and nothing under it.
   Reported 2026-08-24 with a screenshot — AUG 9, AUG 6, AUG 5, AUG 3, AUG 2, all empty.
   Three classes, so it outranks the line above it. Search is unaffected: :4613 hides every sp-day
   header while searching regardless. */
.section-h.sp-day.capped-hide { display:none; }
.sp-day-amt { font-variant-numeric:tabular-nums; letter-spacing:.4px; }
/* the ONE metadata line: chips ride the same line as the category and the whole thing trims with
   an ellipsis rather than ever taking a second line */
.row .d.sp-meta, .li-row .d.sp-meta { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.sp-meta .ttag { white-space:nowrap; }
/* the SHARED badge must read as a badge outside 2.0 too (the body.v2 rules restyle it there) */
.sp-meta .v2badge { display:inline-block; padding:1px 6px; border-radius:5px; font-size:9px;
  font-weight:700; letter-spacing:.3px; background:rgba(59,130,246,.10); color:#3b82f6; margin-right:7px; }
/* denser without crowding: with the third line gone the old 12px of row padding read as air —
   8px is what turns his "~6 rows per screen" into roughly double, and the row stays a 56px tap.
   The body.v2 variant is spelled out because `body.v2 .row { padding:12px 0 }` outguns a bare
   class+attribute selector on specificity (that body element counts). */
.row[data-row^="budget.txns"],
body.v2 .row[data-row^="budget.txns"] { padding-top:8px; padding-bottom:8px; }
/* search results cross days, so the day headers (and their now-wrong totals) sit the search out —
   this outranks .card.searching .capped-hide by coming later in the sheet */
.card.searching .section-h.sp-day { display:none; }

/* ---- v678 SPENDING LIST FILTER ROW ("add a filter to the redesign thing") ---- */
/* Deliberately NOT a .card: a card's padding and margins would spend ~70px of a 375px screen on a
   control strip, which is the list-eating the whole redesign was about. This is a bare strip that
   scrolls sideways — the same idiom .sc-tight .v2chips already uses for a row of choices that must
   not wrap onto a second line. */
.spf { margin:0 0 8px; }
.spf .spf-row, body.v2 .spf .spf-row {
  display:flex; flex-wrap:nowrap; gap:6px; overflow-x:auto; overflow-y:hidden;
  touch-action:pan-x;   /* v691 — his "activity under tabs cant scroll": Android WebView won't hand a nested horizontal scroller its drags without an explicit pan-x */
  scrollbar-width:none; -webkit-overflow-scrolling:touch; margin:0; padding:2px 0 4px; align-items:center; }
.spf .spf-row::-webkit-scrollbar { display:none; }
.spf .spf-row > * { flex:none; }
.spf .v2chip, body.v2 .spf .v2chip {
  display:inline-flex; align-items:center; gap:5px; white-space:nowrap;
  padding:6px 11px; font-size:12px; border-radius:999px; line-height:1.25; }
.spf .spf-v { opacity:.55; font-size:9px; }
/* the ✕ is a control inside a control: big enough to hit, quiet enough not to read as the chip */
.spf .spf-x { background:none; border:0; padding:0 0 0 1px; margin:0; cursor:pointer;
  font-size:11px; line-height:1; color:inherit; opacity:.75; min-width:16px; min-height:16px; }
.spf .spf-x:active { opacity:1; transform:scale(.9); }
.spf .spf-clr { border-style:dashed; opacity:.85; }
.spf .spf-n { font-size:11px; color:var(--dim); font-variant-numeric:tabular-nums; padding-left:2px; }
/* the expanded picker: the one place the row is allowed a second line, because it is a menu and
   it closes the moment you choose */
.spf .spf-opts, body.v2 .spf .spf-opts { display:flex; flex-wrap:wrap; gap:6px; margin:0 0 4px; }
.spf .spf-opts .chip { padding:6px 11px; font-size:12px; white-space:nowrap; }
body.v2 .spf .spf-opts .chip.on::before { content:none; }   /* the fill already says "chosen" */
.spf .v2chip.open { border-style:dashed; }

/* ==== v683 COMPACT LEDGER (the transactions list only) =======================================
   The owner, after living with v678: "I don't like this design — it is occupying a lot of
   space." What was eating it, measured on his screenshot: a 36px gradient icon TILE per row
   (the tile, not the glyph, set the row's height), 12px of padding top AND bottom, and a day
   header that spent 18px of top margin announcing itself in 700-weight caps. None of that is
   the information — the information is a name, a category and a number.
   So: the tile becomes a bare 22px glyph (simpler, no box to draw the eye), rows drop to 6px
   padding with a one-line ellipsised title (the sheet has the full text — tap through), the
   day header goes quiet (smaller, lighter, tighter margins — it is a divider, not a heading),
   and the amount column stays hard right in tabular figures. Roughly half the row height of
   v678 → clearly more rows per screen, which was the ask.
   Everything is scoped under .sp-ledger — the class app.js stamps on the Budget→Spending card
   and the Activity day cards (the two faces of the ONE v678 template) — so bills, subs, debts
   and every other .row/.li-row in the app keep their current shape. Colors are all inherited
   or var()-based: light/dark and the v2 themes keep working untouched. */
/* the leading icon: glyph, not tile — the 36px box was the single biggest space spend */
.sp-ledger .v2li-i,
body.v2 .sp-ledger .v2li-i { width:22px; height:22px; border-radius:6px; font-size:13px; background:none; }
body.v2.light .sp-ledger .v2li-i { background:none; }   /* the light theme repaints .v2li-i lilac further up — this outranks it by coming later */
/* rows: 6px vertical, tighter gaps — the attribute variant restates the v678 8px rule below at
   equal-or-higher specificity so "later wins" settles it */
.sp-ledger .row, body.v2 .sp-ledger .row,
.sp-ledger .li-row, body.v2 .sp-ledger .li-row { padding-top:6px; padding-bottom:6px; gap:9px; }
.sp-ledger .row[data-row^="budget.txns"],
body.v2 .sp-ledger .row[data-row^="budget.txns"] { padding-top:6px; padding-bottom:6px; }
/* one line per fact: the title trims with an ellipsis instead of wrapping (the tap-through sheet
   carries the full text) and the meta line hugs it */
.sp-ledger .row .t, body.v2 .sp-ledger .row .t,
.sp-ledger .li-row .t, body.v2 .sp-ledger .li-row .t {
  font-size:13px; line-height:1.25; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; overflow-wrap:normal; }
.sp-ledger .row .d, body.v2 .sp-ledger .row .d,
.sp-ledger .li-row .d, body.v2 .sp-ledger .li-row .d { font-size:11px; margin-top:0; }
/* the number column: hard right, tabular, one size — the ledger look */
.sp-ledger .row .amt, body.v2 .sp-ledger .row .amt,
.sp-ledger .li-row .amt, body.v2 .sp-ledger .li-row .amt { font-size:13px; font-variant-numeric:tabular-nums; }
.sp-ledger .row .r, body.v2 .sp-ledger .row .r { gap:6px; }
/* the ⇄ family-move stays on the row (his call, v678) and KEEPS its ≥34px hit target — the
   spending-redesign test measures exactly that ("a real hit target, not a glyph shrunk to
   fit"). What goes is its VISUAL bulk: no border, no fill, just the glyph — and the negative
   vertical margins take its 34px out of the row-height math (a flex item's outer size is
   height + margins), so a movable row sits as low as its neighbours while the full 34px
   square still catches the thumb, overlapping the row padding. */
.sp-ledger .row .rowmove, body.v2 .sp-ledger .row .rowmove {
  width:34px; height:34px; margin-top:-5px; margin-bottom:-5px;
  border:none; background:none; border-radius:8px; font-size:14px; }
/* day headers: a divider, not a heading — smaller, lighter, and without the 18px top margin.
   The header sits OUTSIDE the card on Activity, so this styles .sp-day itself; .sp-day only
   exists on this list, which keeps the scope honest. Weight 600 and .5 opacity is "lighter"
   while the caps + letter-spacing keep it scannable. */
.section-h.sp-day, body.v2 .section-h.sp-day {
  font-size:10px; font-weight:600; letter-spacing:.8px; opacity:.5; margin:10px 4px 2px; }
.section-h.sp-day .sp-day-amt { font-size:10.5px; font-weight:600; letter-spacing:.2px; }

/* ==== v683 GLOBAL SEARCH OVERLAY ("Search everything — even trackers, settings, all features")
   One layer, two indexes: screens/settings by name, then records. Sits at z-index 92 — over the
   page and any sheet (its openers all live in the chrome, never inside a form), under the toast.
   Every colour is a var(), so 1.0/2.0 and light/dark all keep working without a special case. */
#gsx { position:fixed; inset:0; z-index:92; background:rgba(4,7,15,.62);
  -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px);
  display:flex; justify-content:center; align-items:flex-start;
  padding:calc(env(safe-area-inset-top, 0px) + 14px) 12px 12px; }
.gsx-panel { width:100%; max-width:560px; background:var(--card); border:1px solid var(--line);
  border-radius:16px; box-shadow:0 18px 50px rgba(0,0,0,.45); overflow:hidden;
  display:flex; flex-direction:column; max-height:min(72vh, 620px); animation:gsxIn .16s ease-out; }
@keyframes gsxIn { from { opacity:0; transform:translateY(-8px); } to { opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce) { .gsx-panel { animation:none; } }
.gsx-bar { display:flex; align-items:center; gap:8px; padding:10px 12px; border-bottom:1px solid var(--line); flex:0 0 auto; }
.gsx-ic { font-size:15px; opacity:.7; flex:0 0 auto; }
#gsx-in { flex:1; min-width:0; background:none; border:none; outline:none; color:var(--text);
  font-size:15px; padding:6px 0; }
#gsx-in::placeholder { color:var(--dim); opacity:.75; }
.gsx-x { flex:0 0 auto; background:none; border:none; color:var(--dim); font-size:14px;
  padding:6px 8px; cursor:pointer; border-radius:8px; min-width:34px; min-height:34px; }
.gsx-x:hover, .gsx-x:focus-visible { color:var(--text); }
.gsx-res { overflow-y:auto; padding:4px 6px 8px; -webkit-overflow-scrolling:touch; }
.gsx-h { font-size:10px; font-weight:700; letter-spacing:1.1px; text-transform:uppercase;
  color:var(--dim); margin:10px 8px 3px; }
.gsx-row { display:flex; align-items:center; gap:10px; padding:7px 8px; border-radius:10px; cursor:pointer; }
.gsx-row.on, .gsx-row:hover { background:rgba(var(--teal-rgb,45,212,191),.10); }
.gsx-rico { flex:0 0 auto; width:24px; text-align:center; font-size:14px; }
.gsx-body { flex:1; min-width:0; display:flex; flex-direction:column; }
.gsx-t { font-size:13.5px; font-weight:600; color:var(--text);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.gsx-d { font-size:11px; color:var(--dim); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.gsx-amt { flex:0 0 auto; font-size:12.5px; font-weight:700; font-variant-numeric:tabular-nums; text-align:right; }
.gsx-go { flex:0 0 auto; color:var(--dim); font-size:15px; opacity:.6; }
.gsx-none { padding:22px 14px; text-align:center; color:var(--dim); font-size:13px; }
/* the subpage 🔍 (built into #topbar by buildV2Nav): only exists where the nav row — which
   carries the home-screen 🔍 — is stripped, so there is exactly one search door per screen */
#v2n-subsearch { display:none; }
body.v2.v2sub #v2n-subsearch { display:flex; margin-left:auto; flex:0 0 auto; }
body.v2.v2sub.kidmode #v2n-subsearch { display:none; }   /* a kid's subpage corner belongs to the 🔓 — and their search has no grown-up screens to offer anyway */

/* Budget "By category" rows: amount · share · › on ONE line instead of a three-line stack
   (owner 2026-08-27, screenshot: "can we shrink this to gaps"). Scoped to the category rows
   only - other .li-row lists keep their column layout. */
body.v2 .li-row[data-bcat] { padding:9px 0; }
body.v2 .li-row[data-bcat] .r { flex-direction:row; align-items:center; gap:8px; }
body.v2 .li-row[data-bcat] .r .dim { font-size:12px; min-width:36px; text-align:right; }
body.v2 .li-row[data-bcat] .r .ar { color:var(--dim); font-size:18px; line-height:1; margin-left:2px; }

/* v691 — his call: "can we shrink this to gaps" (Budget → By category rows were one giant row each:
   amount, percent and chevron stacked three-high because the flex rule above targets .row .r, not
   .li-row .r). Scoped to the budget category list only (rows carry data-bcat). */
body.v2 .li-row[data-bcat] { padding:8px 0; gap:10px; }
body.v2 .li-row[data-bcat] .v2li-i { width:28px; height:28px; border-radius:8px; font-size:13px; }
body.v2 .li-row[data-bcat] .r { display:flex; align-items:center; justify-content:flex-end; gap:8px; text-align:right; flex-shrink:0; }
body.v2 .li-row[data-bcat] .r .dim { font-size:12px; }

/* v693 scroll-jump guard — his "page scrolls to the bottom when popup appears", on-device only.
   Desktop instrumentation proved no app code moves scroll on open; the one mechanism left is the
   browser itself reacting to content appearing at the END of the body (#modal-root lives there):
   layout/scroll-anchoring can shift the scroller the instant innerHTML lands, before the fixed
   .modal-bg takes it out of flow. height:0 makes the mount point permanently layout-invisible —
   its children are position:fixed and render unaffected — so nothing injected there can ever
   move the page again. overflow-anchor:none belts-and-braces the anchoring path on the scroller. */
#modal-root { height: 0; }
html, body { overflow-anchor: none; }


/* SHORT SCREENS, PART TWO: THE TRACKER STEP.
 *
 * Its height is not copy and not gaps — it is eleven chips in a two-column grid, six rows, 409px
 * of the 673px a 740px-tall phone actually offers. Measured, not guessed.
 *
 * THIS BLOCK LIVES AT THE END OF THE FILE ON PURPOSE. The first version sat beside the other
 * short-screen rules, several hundred lines ABOVE `.trk-chip`, and lost to it on source order —
 * same specificity, later wins. The measurement did not move by a single pixel and that is the
 * only reason it was caught.
 *
 * The 44px minimum height stays. It is a touch target, and shrinking a list nobody can reliably
 * tap in order to win vertical pixels is a worse bug than scrolling. What comes off is the padding
 * around the words and the gap between rows, which costs nothing to press. */
@media (max-height: 780px) {
  .chipgrid { gap:6px; margin-top:6px; }
  .trk-chip { padding:6px 9px; font-size:12px; line-height:1.25; }
  .ob-selrow { margin:6px 0; }
  /* The last 26px, and it comes from the one thing on this step that is decoration: the wizard's
     own brand block. On a screen this short the reader knows what app they are in — they are four
     taps into setting it up. The step heading stays; the logo lockup gives up its margin. */
  .ob-card--trackers .ob-brand, .ob-brand { margin-bottom:4px; }
  .ob-h { margin-bottom:3px; }
  .ob-p { margin-bottom:5px; }
  .ob-hint { margin-bottom:5px; }
  .ob-row { margin-top:8px; }
}


/* THE OPTIONAL BLOCK IS NOW A DISCLOSURE, and only on a short screen does it start closed.
 *
 * Measured at 360x740: the account step rendered 1065px into 730px of room — 335px over, a third
 * of a screen, with the Create button below the fold. 335px does not come out of padding, and
 * three attempts at shaving it proved that: one moved it seven pixels, one made the page WORSE.
 *
 * The largest single block on the page is 283px and the page itself calls it "Account & sync —
 * OPTIONAL". A block that big, that nobody has to fill in, standing between a person and the
 * button they came to press, is the page asking for the wrong thing first.
 *
 * Nothing is hidden and nothing is removed — it is one tap from exactly where it always was, and
 * on a tall screen it is still open by default, so this changes nothing for anyone with room. */
summary.chips-h::-webkit-details-marker { display:none; }
details.ob-acct > summary { position:relative; padding-right:20px; }
details.ob-acct > summary::after {
  content:"▾"; position:absolute; right:4px; top:50%; transform:translateY(-50%);
  color:var(--dim); font-size:11px; transition:transform .15s;
}
details.ob-acct[open] > summary::after { transform:translateY(-50%) rotate(180deg); }
details.ob-acct:not([open]) > summary { padding-bottom:6px; }

/* The remaining 143px on the account step, taken from real measured elements — the intro
   paragraph at 100px, the brand lockup at 36px, and the padding inside the fields. Class names
   read off the live DOM, because the last two attempts at this page guessed and one of them made
   it worse. */
@media (max-height: 780px) {
  .ob-card > .ob-brand { margin-bottom:6px; transform:scale(.9); transform-origin:top center; }
  .ob-card > .ob-p { font-size:12.5px; line-height:1.45; margin-bottom:8px; }
  .ob-card .gbtn { padding-top:9px; padding-bottom:9px; margin-bottom:8px; }
  .ob-card .field { margin-bottom:7px; }
  .ob-card .field input { padding-top:8px; padding-bottom:8px; }
  .ob-card .field label { margin-bottom:2px; }
  .ob-curchips { gap:5px; margin-top:4px; }
  .ob-curchips .chip { padding:5px 8px; font-size:11.5px; }
  .ob-card > .hint { font-size:11.5px; margin:4px 0 !important; }
  .ob-acct { margin-top:6px; padding-top:6px; }
}
/* The last of it, both pages. Selectors read off the live DOM. */
@media (max-height: 780px) {
  /* landing: "ALSO INSIDE" is a taster strip, not a control — it gives up the 40px */
  .v2land-also { margin-top:8px !important; padding-top:6px !important; }
  .v2land-also .chip { padding:4px 8px; font-size:11px; }
  .v2land-acts .btn { margin-top:6px; }
  .v2persona-r { margin:6px 0 !important; }
  /* account step: the row and the chips */
  .ob-card > .ob-row { margin-top:8px; }
  .ob-card > .ob-row .btn { padding-top:10px; padding-bottom:10px; }
  .ob-curchips .chip { padding:4px 7px; }
}
/* Final pass, measured: 29px on the landing and 57px on the account step. Both come from the card
   itself — .ob-card carries generous padding that is right on a tall phone and is exactly the
   margin these two pages are short by. */
@media (max-height: 780px) {
  .ob-card { padding:12px 14px !important; }
  .ob-card--bare { padding:8px 10px !important; }
  .v2land { padding-top:4px !important; padding-bottom:4px !important; }
  /* And the three pixels the rebalance above needed, taken from the gap under the logo rather
     than out of the button stack. On a 740px-tall phone the wordmark's breathing room is the
     cheapest thing on this screen; a primary CTA's tap target and the space around it are not. */
  .v2land-logo { width:38px !important; height:38px !important; margin-bottom:3px !important; }
  .v2land-t { font-size:18px !important; }
  .ob-card > .ob-p { margin-bottom:6px !important; }
}
/* The last 53px and 16px.
 *
 * On the account step the currency row carries a preview line under it ("Amounts will show as
 * $1,250 — US Dollar"). On a tall screen that is a nice confirmation; on a short one the chip
 * itself already shows the symbol and the code, so the line is repeating what is two centimetres
 * above it and costing the Create button its place on screen. Hidden below 780px only.
 *
 * The landing's last pixels come off the persona row's own padding — the three plan cards keep
 * their full tap area, only the space around the words gives way. */
@media (max-height: 780px) {
  .ob-card > .hint { display:none; }
  .v2persona { padding:8px 6px !important; }
  .v2land-d { margin-bottom:6px !important; font-size:12px !important; }
  /* NOT display:none. Hiding it closed the last 40px in one line and I nearly left it there — but
     "ALSO INSIDE" is where Health Score, What-If, the Weekly Report and the Coach are advertised,
     and a small phone is not a reason to stop selling to someone. It stays, compact. The landing
     page may scroll a little; it is a page you read, not a form with a button below the fold. */
  .v2land-also { margin-top:6px !important; padding-top:5px !important; }
  .v2land-also .chip { padding:3px 7px; font-size:10.5px; }
}
/* The line that replaces three prices. It has to be plainly readable rather than fine print,
   because it is the thing correcting an expectation the cards used to set wrongly. */
/* THE FREE LINE BELONGS TO THE CARDS, NOT TO THE BUTTON.
   His report: "that text in middle of buttons ... it is about to touch create your account". It
   was not about to — it WAS touching. Measured at 360x740 the gap above it was 12px and the gap
   below it was 0.0px, so a sentence explaining the three cards was glued to the primary CTA and
   drifting away from the thing it explains. Exactly backwards.
   The fix is a swap, not an addition: 12px above and 0 below becomes 6 above and 12 below. Same
   total, so the first screen still fits a 360x740 phone with no scrolling — which it only just
   does, and which was worth more than finding new pixels for. */
.v2land-free { font-size:12px; color:var(--dim); text-align:center; margin:4px 4px 16px; line-height:1.45; }
@media (max-height: 780px) { .v2land-free { font-size:11.5px; margin-top:0; margin-bottom:12px; } }

/* THE THREE DOORS ON THE FIRST SCREEN HAVE TO BE THE SAME SIZE. His words, looking at the
   screenshot: "tabs are not same size". Measured at 360x740 they were 49.4 / 47.4 / 70.4 px —
   three different sizes, for two different reasons, neither of them spacing:

     * 70.4  — "Already have an account? Sign in ✦" is 290px of text in a 255px box, so it
              wrapped to two lines. The label is now one that fits (app.js), but a label is a
              thing that changes and this must not depend on it again.
     * 49.4 vs 47.4 — the primary is font-weight 800 and the ghosts are 700, and with
              line-height:normal the line box is measured from the FONT'S OWN metrics. A heavier
              face is a taller face. Two pixels, invisible alone, obvious in a stack of three.

   So the height stops being a consequence of the text: an explicit line-height replaces `normal`,
   a min-height sets the size, and flex centring puts the label in the middle of it. Now every
   button on this screen is 48px whatever it says and whatever weight it is drawn in.

   It also gives 23px back — the three used to total 167px and now total 144 — which is most of
   what the landing was overflowing by. Pinned in tests/onboard-tracker-shake.cjs. */
body.v2 .v2land-acts .btn {
  display:flex; align-items:center; justify-content:center;
  min-height:48px; line-height:1.2;
}

/* ==============================================================================================
 * THE WIZARD MEASURED IN ITS OWN PIXELS.
 *
 * Every @media (max-height:...) above tests the VIEWPORT. On a 360x740 phone the viewport is
 * 740px and the wizard is laid out in 740/zoom — 673 at the shipped 1.10 default, 462 at the top
 * of the text-size slider. So the compaction above is answering a question nobody asked: it turns
 * on at 740 and then never changes again, however small the box the card is actually being drawn
 * into gets. Measured, before any of this: one notch up from the default (the slider steps 5%)
 * took step 3 from fitting to 22px of scroll and step 2 from 15 to 44; two notches took them to
 * 83 and 109. Thirty-six onboarding rules and not one of them could see it.
 *
 * #onboard is now a size query container (see :1867), so the queries below are answered in the
 * card's own coordinate space. @container adds no specificity — exactly like @media — so the
 * source-order fights this file documents at :1955 and :4848 are all still decided the same way.
 *
 * WHAT THE THRESHOLDS MEAN, on a 360x740 phone, after the 8px frame above:
 *     zoom  0.8   0.9   1.0  1.05 1.10* 1.15  1.2  1.25  1.3   1.4   1.5   1.6
 *     box   917   814   732   697  665   635  609   584  561   521   485   455
 *     tier   -     -     A     A    A    A+B  A+B  ABC   ABC   ABC   ABC   ABC
 * A is the compaction the 740px phone already had; putting it on the effective height rather
 * than the viewport is what fixes a TALL phone at a big text size, where the media queries above
 * are all off and the wizard is being drawn into 560 layout px with no compaction at all.
 * At 0.8 and 0.9 the box is bigger than 780 and tier A is off — correct, and the point: someone
 * who has asked for SMALL text has a big screen and should get the roomy layout, which is what
 * they got before any of the short-screen work existed.
 *
 * NOTHING HERE TOUCHES EITHER LANDING. The 2.0 landing is rendered `bare` (app.js:12562) so it
 * has no .ob-brand and no .ob-dots to take from, and every rule that could otherwise reach the
 * first screen is excluded by :not(.ob-card--landing) by name. Pinned in
 * tests/onboard-fits-its-own-box.cjs and in tests/landing-doors-same-size.cjs.
 *
 * WHAT IS NOT TAKEN, deliberately: no input, no button and no chip gives up height. Those are
 * tap targets and a form nobody can reliably hit is a worse bug than a scrollbar. Everything
 * below is margin, line-height, or the wizard's own decoration.
 * ============================================================================================== */

/* ---- TIER A: the box is at most 780 of its own pixels ---------------------------------------- */
@container ob (max-height: 780px) {
  /* The overlay's own brand lockup. Not on the 2.0 landing at all, and excluded from the 1.0 one
     by name. Note the existing transform:scale(.92) above buys NOTHING in height — a transform
     does not change the layout box — so this is font-size and margin, which do. */
  #onboard .ob-card:not(.ob-card--landing) > .ob-brand { font-size:12.5px; letter-spacing:2.2px; margin-bottom:4px; }
  #onboard .ob-card:not(.ob-card--landing) > .ob-brand .ob-eyebrow { font-size:8px; letter-spacing:3px; margin-bottom:1px; }
  #onboard .ob-card:not(.ob-card--landing) > .ob-dots { margin-bottom:6px; }
  /* The intro paragraph runs to three lines, so line-height is worth three times what the margin
     is. !important because .ob-card > .ob-p already carries one at :4930. */
  #onboard .ob-card:not(.ob-card--landing) > .ob-p { line-height:1.36; margin-bottom:5px !important; }
  /* Separators: the "or fill it in yourself" rule and the little section labels are frame. */
  #onboard .ob-or { margin:4px 2px; }
  #onboard .chips-h { margin:2px 0 3px; }
  #onboard .ob-curchips { margin-top:2px; gap:4px; }
  /* Fields give up the GAP between them. The input keeps every pixel of its own height. */
  #onboard .ob-card .field { margin-top:4px; margin-bottom:4px; }
  /* Buttons give up the space AROUND them and not a pixel of themselves. The first draft of this
     rule took 2px of padding off "Sign up with Google" and measured it: 47.1 device px became
     42.7, under the 44 minimum, at the shipped default zoom. Four pixels is not worth a control
     a thumb misses, so the padding went back and only the margin gives. */
  #onboard .ob-card:not(.ob-card--landing) .gbtn { margin-bottom:5px; }
  #onboard .ob-card:not(.ob-card--landing) > .ob-row { margin-top:6px; }
  /* The optional sync panel — the +194px anyone who wants sync has to open. Its explainer is the
     one piece of it that is not a field; inline styles on the element mean !important or nothing. */
  #onboard details.ob-acct { margin-top:4px; padding-top:5px; }
  #onboard details.ob-acct .hint { font-size:10.5px; line-height:1.4; margin:3px 4px 0 !important; }
  #onboard .ob-hint { margin-bottom:5px; }
}

/* ---- TIER B: at most 650 — one notch up the slider and beyond -------------------------------- */
@container ob (max-height: 650px) {
  #onboard .ob-card:not(.ob-card--landing) > .ob-brand { font-size:11.5px; margin-bottom:2px; }
  #onboard .ob-card:not(.ob-card--landing) > .ob-dots { margin-bottom:4px; }
  #onboard .ob-card:not(.ob-card--landing) > .ob-h { font-size:16.5px; margin-bottom:2px; }
  #onboard .ob-card:not(.ob-card--landing) > .ob-p { font-size:12px; line-height:1.3; margin-bottom:3px !important; }
  #onboard .ob-or { margin:2px 2px; font-size:11px; }
  #onboard .chips-h { margin:1px 0 2px; }
  #onboard .ob-card .field { margin-top:3px; margin-bottom:3px; }
  #onboard .ob-card .field label { font-size:8.5px; margin-bottom:1px; }
  #onboard .ob-card:not(.ob-card--landing) .gbtn { margin-bottom:3px; }
  #onboard .ob-card:not(.ob-card--landing) > .ob-row { margin-top:4px; }
  #onboard details.ob-acct { margin-top:3px; padding-top:4px; }
  #onboard details.ob-acct .hint { font-size:10px; margin:2px 4px 0 !important; }
  #onboard .ob-hint { font-size:11.5px; line-height:1.35; margin-bottom:3px; }
  #onboard .chipgrid { gap:5px; margin-top:4px; }
  #onboard .ob-selrow { margin:4px 0; }
  #onboard .ob-card .card { padding:8px 10px; }
}

/* ---- TIER C: at most 595 — the top third of the slider ---------------------------------------
 * A THRESHOLD MUST NOT LAND BETWEEN TWO NOTCHES OF THE SLIDER, and this one took two goes to get
 * right because it is not obvious what "between" means here. The slider steps 5%, so the box goes
 * 609 (1.20), 584 (1.25), 561 (1.30), 540 (1.35) — and a tier that switches on partway down that
 * ladder hands the LARGER text size a whole tier the smaller one did not get. Measured: at 560
 * this tier missed 1.30 by one pixel and 1.30 came out needing 30px MORE scroll than 1.40; at 570
 * it caught 1.30 but not 1.25, and 1.25 came out worse than 1.30. 595 sits in the middle of the
 * 609/584 gap, so 1.25 and everything above it gets the tier and 1.20 — which already fits with
 * room — does not. Pinned as a relationship in tests/onboard-fits-its-own-box.cjs: no text size
 * may need less scrolling than a smaller one.
 *
 * Here the brand lockup goes. It is the only thing on these pages that is pure decoration, the
 * reader is three taps into setting the app up and knows what they are in, and at this text size
 * the person has told us in the plainest possible way that they would rather have the words than
 * the logo. The STEP N OF 4 dots stay: those tell you where you are. */
@container ob (max-height: 595px) {
  #onboard .ob-card:not(.ob-card--landing) > .ob-brand { display:none; }
  #onboard .ob-card:not(.ob-card--landing) > .ob-h { font-size:15.5px; }
  #onboard .ob-card:not(.ob-card--landing) > .ob-p { font-size:11.5px; margin-bottom:2px !important; }
  #onboard .ob-card .field { margin-top:2px; margin-bottom:2px; }
  #onboard .ob-or { margin:1px 2px; }
  #onboard details.ob-acct .hint { display:none; }
  #onboard .ob-hint { font-size:11px; margin-bottom:2px; }
  #onboard .chipgrid { gap:4px; margin-top:3px; }
}


/* THE BACK BUTTON NAMES WHERE YOU CAME FROM. The chevron keeps its size and tap target; the word
   sits beside it and is the only part allowed to shrink. Guarded in CSS rather than by truncating
   in code, so a long name at the phone's largest font setting ellipsises instead of wrapping the
   top bar onto two lines. */
#btn-back { display:flex; align-items:center; gap:2px; width:auto; max-width:46vw; padding-inline:6px; }
#btn-back .bk-w { font-size:14px; font-weight:700; line-height:1;
  max-width:38vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }


/* ============================================================================================
   HERO NUMBER MODIFIERS. These have to outrank the base rules, and until now they did not.

   .hero-num carries three modifiers -- .bad (this figure is a loss), .len-l and .len-xl (this
   figure is long, shrink it). All three are ONE class: specificity 0,2,0. The rules they must
   beat are not:

       body.v2 .hero-card .hero-num   { font-size:46px; color:... }   0,3,1
       body.v2.light .hero-num        { color:#15803D; }              0,3,1

   So every modifier lost, silently, everywhere:

   * OVER BUDGET PAINTED AS GOOD NEWS. His home screen read "-C$12,200.00" in GREEN with
     "over budget by C$12,200.00" underneath it. Measured, .bad resolved to #15803D green in
     light and #EC4899 pink in dark. Never once red.
   * THE NUMBER CAME OUT OF ITS BOX -- his words. .len-l/.len-xl never applied, so a twelve
     character amount stayed at 46px and ran past the card edge instead of stepping down.

   Both are the same cascade bug, so they are fixed together and gated together. Placed at the
   END of the file: equal specificity is settled by source order, and anything that repaints
   .hero-num later would take the modifier out again. */
body.v2 .hero-card .hero-num.len-l,  body.v2 .hero-num.len-l  { font-size:34px; letter-spacing:-.5px; }
body.v2 .hero-card .hero-num.len-xl, body.v2 .hero-num.len-xl { font-size:26px; letter-spacing:-.7px; }
/* A loss is red in both themes. Set background + clip too: the base rule paints the number with a
   gradient through background-clip:text, and colour alone cannot switch that off. */
body.v2 .hero-card .hero-num.bad, body.v2 .hero-num.bad {
  background:none; -webkit-background-clip:border-box; background-clip:border-box;
  color:#EF4444; -webkit-text-fill-color:#EF4444; }
/* Deepened on white for AA, same as every other semantic colour in the light sweep. */
body.v2.light .hero-card .hero-num.bad, body.v2.light .hero-num.bad {
  background:none; -webkit-background-clip:border-box; background-clip:border-box;
  color:#B91C1C; -webkit-text-fill-color:#B91C1C; }

/* The rule row's two actions sit side by side under the switch, so gaining an edit control does not
   make every row in the list a line taller. Both get a real tap target: the bare 15px glyph was a
   ~27px hit area, which is under the 44px floor and was already easy to miss on the delete. */
body.v2 .li-row .rule-acts { display:inline-flex; align-items:center; gap:2px; }
body.v2 .li-row .rule-acts .del { min-width:40px; min-height:40px; display:inline-flex;
  align-items:center; justify-content:center; padding:4px; border-radius:9px; }
body.v2 .li-row .rule-acts .del:active { background:rgba(255,255,255,.07); }
body.v2.light .li-row .rule-acts .del:active { background:rgba(17,18,25,.07); }

/* ============================================================
   MONEY COACH — chat-first (2026-09-02). His complaint: "I don't like design — need to
   scroll, and whenever I ask a question it's not user friendly." The page is a chat now:
   #view stops scrolling and becomes a flex column, the thread scrolls in the middle, and
   the ask bar docks at the bottom of the screen — never below the fold. The tab bar is
   position:fixed and ~51px tall + the safe-area inset; env() is a DEVICE length inside
   #app's zoomed subtree, so it is divided by --zoom at the use site (the onboarding
   overlay's idiom, style.css ~1966). WIRE.coach carries a JS fallback for a WebView
   without :has(). Gate: tests/coach-chat-ui.cjs.
   ============================================================ */
#view:has(> .coach-wrap) { display:flex; flex-direction:column; overflow-y:hidden;
  padding-bottom:calc(58px + var(--sab, 0px) / var(--zoom, 1)); }
/* keyboard up: the tab bar hides (body.kb-open), so the dock drops to sit on the keyboard */
body.kb-open #view:has(> .coach-wrap) { padding-bottom:8px; }
.coach-wrap { flex:1; min-height:0; display:flex; flex-direction:column; }
.coach-scroll { flex:1; min-height:0; overflow-y:auto; overflow-x:hidden;
  -webkit-overflow-scrolling:touch; padding:2px 2px 10px; }
/* one scroll surface: inside the chat page the thread gives up its own inner scroll */
.coach-scroll .cthread { max-height:none; overflow:visible; margin:9px 0 6px; }
.coach-dock { flex:none; border-top:1px solid var(--line); padding-top:10px; }
.coach-dock .hint { margin-top:7px; }
.coach-topline { display:flex; align-items:center; justify-content:space-between; gap:8px; min-height:26px; }
.coach-scroll .coach-chips-h { margin:10px 0 8px; }
.coach-scroll .v2chips, .coach-scroll .chips { margin:0 0 6px; }
/* the miss log's quiet doorway — a footnote, not a card (the list itself opens as a sheet) */
.coach-missrow { display:flex; align-items:center; justify-content:space-between; gap:8px;
  color:var(--dim); font-size:12.5px; padding:12px 2px 2px; cursor:pointer; }
.coach-missrow .ar { color:var(--dim); font-size:16px; }

/* ☆ PIN-TO-BAR — in every 2.0 tracker's header (his 2026-09-05 ask, gate tests/nav-pin.cjs). It sits
   beside the page title, because it belongs to the page, not to the action row on the right: hollow
   while the tracker is off the bar, the accent fill once it is on. Same box as .sp-back so the head's
   controls read as one family. Light mode: the same token swap .sp-back makes at :4293.
   Shrunk 32→24px box / 17→12px glyph on his "make that star smaller" (2026-09-05); the ::after hit-slop
   keeps the tap target 40x40 without growing the visible frame. */
body.v2 .sp-gh-l.has-pin .sp-gh-n { display:inline-block; vertical-align:middle; max-width:calc(100% - 38px); }
body.v2 .nav-pin { display:inline-flex; align-items:center; justify-content:center; vertical-align:middle; margin-left:8px;
  position:relative; width:24px; height:24px; border-radius:8px; border:1px solid rgba(255,255,255,.08); background:rgba(255,255,255,.03);
  color:#9294a8; font-size:12px; line-height:1; padding:0; cursor:pointer; }
body.v2 .nav-pin::after { content:""; position:absolute; top:-8px; right:-8px; bottom:-8px; left:-8px; } /* 40x40 tap target */
body.v2 .nav-pin:active { transform:scale(.92); }
body.v2 .nav-pin.on { color:var(--g2); border-color:rgba(var(--teal-rgb),.35); background:rgba(var(--teal-rgb),.12); }
body.v2.light .nav-pin { color:var(--dim); background:rgba(17,18,25,.04); border-color:rgba(17,18,25,.12); }
body.v2.light .nav-pin.on { color:var(--g2); background:rgba(var(--teal-rgb),.14); border-color:rgba(var(--teal-rgb),.55); }
