/* ---------- the look: "Forward", in blue (docs/brand.md; the green version: docs/brand-green.md) ----------
   Every colour, font and corner in the app comes from these names, so a new
   look is a change here and nowhere else. Each theme is written once: theme.js
   (and the first-paint script in index.html) always sets data-theme to
   "light" or "dark", working out "System" from the device itself. Dark is
   also the fallback if that script never ran. */
@font-face { font-family: "Bricolage Grotesque"; font-style: normal; font-weight: 200 800; font-display: swap;
  src: url(/fonts/bricolage-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Bricolage Grotesque"; font-style: normal; font-weight: 200 800; font-display: swap;
  src: url(/fonts/bricolage-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 300 900; font-display: swap;
  src: url(/fonts/figtree-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 300 900; font-display: swap;
  src: url(/fonts/figtree-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }

:root {
  /* Type: Bricolage for titles and big amounts, Figtree for everything else.
     (Arabic, and anything else neither font has, falls back to the system's.) */
  --font-body: "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --font-display: "Bricolage Grotesque", "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  /* Corners. */
  --r-xs: 8px;    /* small buttons, badges' boxes */
  --r-sm: 12px;   /* fields, small buttons */
  --r-md: 16px;   /* buttons, list cards */
  --r-lg: 20px;   /* cards */
  --r-xl: 24px;   /* the Today card, sheets */
  --r-pill: 999px;

  /* Dark (also the fallback): the navy the app has always had. */
  color-scheme: dark;
  --bg: #0F172A;            /* the page */
  --bg-elevated: #1A2436;   /* cards, rows, sheets on the page */
  --bg-hover: #222D44;
  --bar: #131C2E;           /* the tab bar and the laptop sidebar */
  --field: #1F2A40;         /* inputs */
  --border: #2A3650;
  --hair: #222D44;          /* dividers inside a card */
  --text: #E8EDF7;
  --text-dim: #94A3B8;
  --accent: #8AB4FF;        /* links, the active tab, focus, selected things */
  --accent-dim: #2C4A80;
  --accent-soft: rgba(138, 180, 255, 0.12);
  --on-accent: #0B1220;
  --brand: #4C8DFF;         /* the main button, the + button */
  --on-brand: #0B1220;
  --income: #34D399;        /* money in: green, whatever the brand colour */
  --out: var(--text);       /* money out: plain text, not red */
  --single: #C4B5FD;        /* one-time entries' names */
  --balance: var(--text-dim);
  --danger: #FF8A73;        /* below zero, errors, delete */
  --danger-strong: #C2412D; /* a red fill under white text */
  --danger-soft: #3A1F1A;
  --month-bg: #1F2A40;
  --month-text: #E8EDF7;
  --month-border: #2A3650;
  --sol: #F2B552;           /* today, the lowest point, Pro */
  --sol-soft: #3A2E17;
  --sol-ink: #F6CD86;       /* text on --sol-soft */
  --on-sol: #10231C;
  --hero: #1E3A8A;          /* the Today card */
  --hero-text: #FFFFFF;
  --hero-line: #A9C7FF;
  --tint: #1C2B4A;          /* good news, the selected sidebar item */
  --tint-ink: #A9C7FF;
  --toast-bg: #2A3650;
  --toast-text: #E8EDF7;
  --scrim: rgba(2, 6, 16, 0.62);
  --shadow: rgba(0, 0, 0, 0.3);
  --ad-bg: #1A2436;
  --track: #222D44;         /* behind a segmented switch */

  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  /* In landscape, a phone's notch/Dynamic Island sits on one of these side
     edges instead of the top - only matters for the fixed sidebar, which
     sits flush against the true left edge of the screen. */
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --tabbar-h: 62px;

  /* Tag colours (the dataviz reference palette, in its order). Checked for
     colour-blind readers; every tag is also named beside the chart. */
  --cat-1: #3987e5; --cat-2: #d95926; --cat-3: #199e70; --cat-4: #c98500; --cat-5: #d55181; --cat-6: #008300; --cat-7: #9085e9; --cat-8: #e66767; --cat-other: #64748b; --cat-none: #334155;
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg: #F3F6FB;
  --bg-elevated: #FFFFFF;
  --bg-hover: #ECF0F6;
  --bar: #FFFFFF;
  --field: #FFFFFF;
  --border: #D9E0EC;
  --hair: #ECF0F6;
  --text: #0F172A;
  --text-dim: #4B5B73;
  --accent: #1D4ED8;
  --accent-dim: #A9C0F0;
  --accent-soft: rgba(29, 78, 216, 0.08);
  --on-accent: #FFFFFF;
  --brand: #1D4ED8;
  --on-brand: #FFFFFF;
  --income: #047857;
  --single: #6D28D9;
  --danger: #C2412D;
  --danger-strong: #B3361F;
  --danger-soft: #FBE9E5;
  --month-bg: #E8EDF5;
  --month-text: #0F172A;
  --month-border: #D9E0EC;
  --sol: #F2A93B;
  --sol-soft: #FDF1DC;
  --sol-ink: #7A4B00;
  --hero: #1D4ED8;
  --hero-line: #FFFFFF;
  --tint: #DCE7FB;
  --tint-ink: #1E3A8A;
  --toast-bg: #0F172A;
  --toast-text: #FFFFFF;
  --scrim: rgba(15, 23, 42, 0.45);
  --shadow: rgba(15, 23, 42, 0.08);
  --ad-bg: #E8EDF5;
  --track: #E2E8F2;
  --cat-1: #2a78d6; --cat-2: #eb6834; --cat-3: #1baf7a; --cat-4: #eda100; --cat-5: #e87ba4; --cat-6: #008300; --cat-7: #4a3aa7; --cat-8: #e34948; --cat-other: #94a3b8; --cat-none: #d9e0ec;
}

* { box-sizing: border-box; }
/* No double-tap zoom (taps on buttons are quick and never zoom the page, as
   in a native app); pinching to zoom still works, which people with low
   vision rely on. Set on every element because the menu and sheets scroll on
   their own, and the setting doesn't reach inside those from the page. */
* { touch-action: manipulation; }
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  -webkit-tap-highlight-color: transparent;
  -webkit-font-smoothing: antialiased;
}

body {
  min-height: 100vh;
  padding-bottom: calc(var(--tabbar-h) + var(--safe-bottom) + 12px);
}

button { font: inherit; color: inherit; cursor: pointer; }

/* Extra room below the status bar: iOS fades/dims content sitting right under it. */
header {
  position: sticky;
  top: 0;
  z-index: 5;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 8px;
  background: var(--bg);
  padding: calc(16px + var(--safe-top)) 16px 10px;
}
/* On the forecast, the header's colour reaches 8px further down: the gap a
   stuck month bar leaves under it (with no pinned Today card) shows nothing
   scrolling past. */
body[data-tab="forecast"] header { box-shadow: 0 8px 0 var(--bg); }
.hdr-side { display: flex; align-items: center; gap: 6px; min-width: 0; }
.hdr-right { justify-content: flex-end; }

.title-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  background: none;
  border: none;
  padding: 2px 8px;
  line-height: 1.15;
  min-width: 0;
}
/* The header's title is the page you're on (the app's own name is on the
   home screen and the sign-in screen; it needn't be on every page). */
.app-name { display: none; }
.view-name { font-family: var(--font-display); font-size: 20px; font-weight: 800; letter-spacing: -0.02em; }
.acct-sub {
  font-size: 12.5px;
  color: var(--text-dim);
  margin-top: 2px;
  max-width: 44vw;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The account's name and a chevron: where accounts are switched. */
.acct-sub { display: inline-flex; align-items: center; gap: 3px; }
.acct-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.acct-chev { width: 16px; height: 16px; flex-shrink: 0; fill: none; stroke: var(--accent); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }

.sync-status {
  background: var(--sol-soft);
  border: 1px solid transparent;
  color: var(--sol-ink);
  border-radius: var(--r-pill);
  padding: 3px 9px;
  font-size: 11.5px;
  font-weight: 600;
  max-width: 100%;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.icon-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--accent);
  padding: 6px;
  display: flex;
}
/* The + button: a round green button on a phone, "+ New" on a laptop. */
#add-btn {
  width: 44px;
  height: 44px;
  padding: 0;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--brand);
  color: var(--on-brand);
}
#add-btn svg { width: 22px; height: 22px; }

main {
  max-width: 760px;
  margin: 0 auto;
  padding: 8px 16px;
}

/* Summary */
.summary {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin: 6px 0 10px;
}
.stat {
  background: var(--bg-elevated);
  border: 1px solid transparent;
  border-radius: var(--r-lg);
  padding: 13px 15px;
  min-width: 0;
}
.stat .label { font-size: 13px; color: var(--text-dim); }
.stat .value { font-family: var(--font-display); font-size: 21px; font-weight: 700; letter-spacing: -0.01em; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.stat .sub { font-size: 11.5px; color: var(--text-dim); margin-top: 1px; }

.controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 4px 2px 6px;
  font-size: 13.5px;
  color: var(--text-dim);
}
.controls label { display: flex; align-items: center; gap: 6px; }
.controls select, .controls input[type=checkbox] { accent-color: var(--accent); }
.controls select {
  background-color: var(--field);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  padding: 4px 6px;
}

/* Ledger */
.month {
  position: sticky;
  /* A little breathing room so it doesn't look stuck flush to the header
     while pinned mid-scroll. Full width and opaque: the month that sticks
     next covers this one completely. */
  top: calc(var(--header-h, calc(66px + var(--safe-top))) + var(--today-h, 8px));
  z-index: 3;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  /* The Pro card's colours: the sun, softly (drawn by ::after, below). */
  color: var(--sol-ink);
  border-radius: var(--r-pill);
  padding: 10px 19px;
  margin: 16px 0 8px;
}
.month-name { font-family: var(--font-display); font-weight: 700; font-size: 15px; letter-spacing: -0.005em; white-space: nowrap; }
.month-net { font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; unicode-bidi: plaintext; white-space: nowrap; }
#fc-list > .fc-month:first-child > .month { margin-top: 0; }
/* Behind the bar: the page's colour, a little past its rounded ends, so
   rows sliding under a stuck bar (the highlighted one is a touch bigger)
   don't show around it. The sides are shadows, which can't widen the page
   on a phone. Not above it: nothing goes ahead of the next month as it
   pushes the stuck one away (forecast-list.js: each month is a block, which
   its bar can't stick past; the gap over a stuck bar is the header's). */
.month::before, .month::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;
}
.month::before {
  inset: 0;
  background: var(--bg);
  box-shadow: -28px 0 0 var(--bg), 28px 0 0 var(--bg);
}
/* (the bar itself: a layer of its own, as anything the bar painted itself
   would be under ::before) */
.month::after {
  inset: 0;
  background: var(--sol-soft);
  border: 1px solid color-mix(in srgb, var(--sol) 30%, transparent);
  border-radius: inherit;
}
/* The Today card stays pinned under the header while the list scrolls under
   it (past entries too), with the month bars sticking just below it - when
   that leaves the list at least half the screen (forecast-list.js adds
   .pinned). The slot is opaque and edge to edge so rows don't show around
   the card's corners; --today-h is its height while pinned. */
#today-slot.pinned:not(:empty) {
  position: sticky;
  top: var(--header-h, calc(66px + var(--safe-top)));
  z-index: 4;
  background: var(--bg);
  /* (and a little wider, the same way: the highlighted row is bigger than the
     card; and up a little, over the header's edge, which can end half a
     pixel short of the whole pixel the card starts at) */
  box-shadow: -28px 0 0 var(--bg), 28px 0 0 var(--bg), 0 -3px 0 var(--bg);
  margin: -8px -16px 0;
  padding: 8px 16px;
}
#today-slot.pinned .today-card { margin-bottom: 0; }

.row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: var(--bg-elevated);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  padding: 13px 16px;
  margin-bottom: 8px;
  text-align: start;
}
.row:active { background: var(--bg-hover); }
/* Fade contents, not the card: the card must stay opaque to hide the swipe button behind it. */
.row.past > * { opacity: 0.5; }

/* What-if: turned off, still listed, not counted */
/* (Dimmed with the dim text colour, not opacity: faded to 40% the text was
   unreadable, 2.5:1. The dashed outline, the strike-through and "Off" say it.) */
.list-item.off { background: transparent; border-color: var(--border); border-style: dashed; }
.list-item.off, .list-item.off * { color: var(--text-dim) !important; }
.row.off { background: var(--bg); border-color: var(--border); border-style: dashed; }
.row.off, .row.off * { color: var(--text-dim) !important; }
.row.off .amt { text-decoration: line-through; }

/* Swipe-left to toggle a single entry, Messages-style */
.swipe { position: relative; margin-bottom: 8px; }
.swipe .row {
  position: relative;
  z-index: 1;
  margin-bottom: 0;
  touch-action: pan-y;
  transition: transform 0.22s ease;
}
/* Behind the card: what a swipe to the right will do... */
.swipe-flip {
  position: absolute;
  inset: 0 30% 0 0; /* (the left part only: the card overshoots a little on its way back, and nothing shows at the far edge) */
  display: flex;
  align-items: center;
  padding-left: 16px;
  direction: ltr;
  border-radius: var(--r-md);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-dim);
  background: var(--bg-hover);
}
.swipe-flip { transition: background-color 0.15s ease, color 0.15s ease; }
.swipe.flip-armed .swipe-flip { color: var(--on-accent); background: var(--accent); }
/* ...and behind the other side: what a swipe to the left will do. */
.swipe-drop {
  position: absolute;
  inset: 0 0 0 30%;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 12px;
  direction: ltr;
  border-radius: var(--r-md);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-dim);
  background: var(--bg-hover);
  transition: background-color 0.15s ease, color 0.15s ease;
}
/* Removing has to be held (swipe.js holdToDrop): while it is, red rises behind
   "Hold to delete"; when it has been held long enough the label turns solid
   and says what letting go will do. */
.swipe-drop::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--danger-strong);
  opacity: 0.4;
  transform: scaleY(0);
  transform-origin: bottom;
}
.swipe.drop-holding .swipe-drop::before { transform: scaleY(1); transition: transform 0.5s linear; }
.swipe-drop span { position: relative; }
/* (the words fit in what shows of the label at the point the hold begins) */
.swipe-drop .drop-hold { max-width: 74px; text-align: right; line-height: 1.2; }
.swipe-drop .drop-go, .swipe.drop-armed .swipe-drop .drop-hold { display: none; }
.swipe.drop-armed .swipe-drop .drop-go { display: inline; }
.swipe.drop-armed .swipe-drop { color: #FFFFFF; background: var(--danger-strong); }
.off-tag {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--text);
  border: 1px solid var(--text-dim);
  border-radius: 5px;
  padding: 0 5px;
  line-height: 16px;
  vertical-align: 1px;
}
.row.today-row .day span { font-weight: 600; color: var(--text); }
.row .day { border-inline-end: 1px solid var(--hair); padding-inline-end: 10px; width: 48px; text-align: center; }

.day { width: 38px; flex-shrink: 0; }
.day b { display: block; font-family: var(--font-display); font-size: 21px; font-weight: 700; line-height: 1.1; }
.day span { font-size: 12.5px; color: var(--text-dim); }

.info { flex: 1; min-width: 0; }
.info .name { font-size: 16px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.info .name.single { color: var(--single); }
.info .memo { font-size: 13px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 3px; }

.amts { text-align: end; flex-shrink: 0; font-variant-numeric: tabular-nums; }
/* The entry's amount is the headline (money in green, money out in plain
   text); the balance after it sits quietly under it, red only below zero. */
.amts .amt { font-size: 16px; font-weight: 600; color: var(--out); }
.amts .amt.in { color: var(--income); font-weight: 700; }
.amts .amt.better { color: var(--income); font-weight: 700; }
.amts .amt.worse { color: var(--danger); font-weight: 700; }
.amts .amt.confirmed { color: var(--text); }
.amts .bal { font-size: 13px; color: var(--balance); margin-top: 3px; }
.neg { color: var(--danger) !important; }

/* Lists (recurring / accounts) */
.list-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: var(--bg-elevated);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  padding: 13px 16px;
  margin-bottom: 8px;
  text-align: start;
}
.list-item:active { background: var(--bg-hover); }
.list-item .grow { flex: 1; min-width: 0; }
.list-item .primary { font-size: 16px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-item .secondary { font-size: 13px; color: var(--text-dim); margin-top: 2px; }
.list-item .right { text-align: end; font-variant-numeric: tabular-nums; }
.list-item.current { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.check { color: var(--accent); font-weight: 700; width: 16px; }

.section-title {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  padding: 18px 4px 8px;
}

.btn {
  display: block;
  width: 100%;
  min-height: 48px;
  border-radius: var(--r-md);
  padding: 12px 14px;
  font-size: 15.5px;
  font-weight: 600;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  margin-top: 8px;
}
.btn.primary { background: var(--brand); color: var(--on-brand); border-color: var(--brand); font-weight: 700; }
.btn.danger { color: var(--danger); }
a.btn { display: block; text-align: center; text-decoration: none; }
.btn.subtle { background: none; }

.empty {
  text-align: center;
  color: var(--text-dim);
  padding: 50px 20px;
  line-height: 1.6;
}
.empty h2 { color: var(--text); font-family: var(--font-display); font-size: 22px; font-weight: 800; letter-spacing: -0.01em; margin: 0 0 6px; }

/* Monthly overview breakdown */
.breakdown {
  background: var(--bg-elevated);
  border: 1px solid transparent;
  border-radius: var(--r-lg);
  padding: 4px 16px 10px;
}
.bd-head, .bd-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-variant-numeric: tabular-nums;
}
.bd-head {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-dim);
  padding: 12px 0 6px;
  border-bottom: 1px solid var(--hair);
  margin-bottom: 4px;
}
.bd-row { font-size: 15px; padding: 6px 0; }
.bd-count { color: var(--text-dim); font-size: 12.5px; }

/* Graph */
.chart-wrap {
  position: relative;
  background: var(--bg-elevated);
  border: 1px solid transparent;
  border-radius: var(--r-lg);
  padding: 10px 6px 4px;
  margin-top: 6px;
}
.chart-wrap svg { width: 100%; height: auto; display: block; }
.chart-wrap text { fill: var(--text-dim); font-size: 11px; font-family: inherit; }

.chart-expand {
  position: absolute;
  top: 8px;
  right: 8px; /* the chart stays left-to-right, so its button does too */
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--r-xs);
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text-dim);
}
.chart-expand svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* The big chart: fills the screen, and rotates to a landscape layout when
   the phone is held in portrait - most phones won't auto-rotate a fixed
   web view, so this fakes it instead of asking for OS rotation. */
#chart-full {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--bg);
}
#chart-full .chart-full-close {
  position: absolute;
  top: max(12px, var(--safe-top));
  right: calc(12px + var(--safe-right));
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text);
}
#chart-full .chart-full-close svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* Landscape (including a laptop, or a phone that's physically rotated with
   its own lock off) needs no rotation - top/left/width/height/transform are
   spelled out here, matching what the portrait rule below overrides, so
   flipping between the two transitions instead of snapping. That flip still
   won't perfectly line up with the OS's own rotation animation when the
   phone is physically turned mid-view (two independent animations), but it
   softens what would otherwise be an instant jump into a brief settle. */
#chart-full .chart-rotator {
  position: absolute;
  top: 0;
  left: 0;
  width: 100dvw;
  height: 100dvh;
  transform: none;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  transition: top 0.3s ease, left 0.3s ease, width 0.3s ease, height 0.3s ease, transform 0.3s ease;
}
#chart-full .chart-rotator svg { width: 100%; height: 100%; }
#chart-full .chart-rotator text { fill: var(--text-dim); font-size: 11px; font-family: inherit; }
@media (orientation: portrait) {
  #chart-full .chart-rotator {
    top: 50%;
    left: 50%;
    width: 100dvh;
    height: 100dvw;
    padding: 24px;
    transform: translate(-50%, -50%) rotate(90deg);
  }
}

/* Tab bar */
#tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  display: flex;
  background: var(--bar);
  border-top: 1px solid var(--hair);
  padding-bottom: var(--safe-bottom);
}
.tab {
  flex: 1;
  height: var(--tabbar-h);
  background: none;
  border: none;
  color: var(--text-dim);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font-size: 12px;
  font-weight: 600;
}
.tab svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.tab.active { color: var(--accent); font-weight: 700; }

/* Sheet */
#sheet { position: fixed; inset: 0; z-index: 20; }
.sheet-backdrop { position: absolute; inset: 0; background: var(--scrim); }
.sheet-panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-width: 560px;
  margin: 0 auto;
  /* However much is in it, a sheet stops short of the top: there's always a
     strip of the page left above it to tap, which puts the sheet away. What
     doesn't fit scrolls inside the sheet. (dvh: the part of the screen really
     showing; vh is taller than that on iPhones.) */
  max-height: calc(100vh - 96px - var(--safe-top));
  max-height: calc(100dvh - 96px - var(--safe-top));
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 28px 28px 0 0;
  padding: 18px 18px calc(18px + var(--safe-bottom));
}
/* The handle at the top of a bottom sheet (sheets.js): drag it down and the
   sheet goes with the finger. It stays at the top while the sheet's contents
   scroll under it. Only where the sheet comes up from the bottom: not when
   it's a dialog in the middle or a column at the side. */
.sheet-grab {
  position: sticky;
  top: -18px; /* (the sheet's own top padding: sticky counts from inside it) */
  z-index: 3;
  height: 26px;
  margin: -18px -18px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 28px 28px 0 0;
  background: var(--bg);
  touch-action: none; /* (the drag is ours, not a scroll) */
  cursor: grab;
  -webkit-user-select: none;
  user-select: none;
}
.sheet-grab::before { content: ""; width: 40px; height: 5px; border-radius: 3px; background: var(--text-dim); opacity: 0.45; }
.sheet-close {
  position: absolute;
  top: 10px;
  inset-inline-end: 10px;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--field);
  color: var(--text-dim);
  cursor: pointer;
}
.sheet-close svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; }
.sheet-close:active { scale: 0.92; }
/* (a long title wraps before the close button) */
.sheet-panel > .sheet-grab + h2 { padding-inline-end: 44px; }
.sheet-grab.dragging { cursor: grabbing; }
.sheet-grab.dragging::before { opacity: 0.8; }
.sheet-panel h2 { margin: 0 0 6px; font-family: var(--font-display); font-size: 24px; font-weight: 800; letter-spacing: -0.015em; }
.sheet-panel .sub { color: var(--text-dim); font-size: 14px; margin-bottom: 12px; }

.field { display: block; margin-bottom: 12px; font-size: 13px; color: var(--text-dim); }
.field input, .field select {
  display: block;
  width: 100%;
  min-height: 50px;
  margin-top: 6px;
  background-color: var(--field);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 12px 14px;
  font-size: 16px; /* 16px stops iOS zooming into the field */
  font-family: inherit;
  -webkit-appearance: none;
  appearance: none;
}
.field input:focus, .field select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.field-row { display: flex; gap: 10px; }
.field-row .field { flex: 1; min-width: 0; }

.seg { display: flex; background: var(--track); border: none; border-radius: 14px; padding: 4px; margin-bottom: 14px; }
.seg button { flex: 1; min-height: 40px; background: none; border: none; border-radius: 11px; padding: 8px; font-weight: 600; color: var(--text-dim); }
.seg button.on { background: var(--bg-elevated); color: var(--text); font-weight: 700; box-shadow: 0 1px 2px var(--shadow); }
.seg button.on[data-sign="1"] { color: var(--income); }
.seg button.on[data-sign="-1"] { color: var(--text); } /* (money out is plain: red means below zero) */

.note { font-size: 13px; color: var(--text-dim); margin: -4px 0 12px; }
.note.warn { color: var(--text); background: var(--danger-soft); border-radius: var(--r-sm); padding: 10px 12px; margin: 0 0 12px; }
.note.warn a { display: inline-block; margin-top: 4px; color: var(--accent); font-weight: 600; }
.error { color: var(--danger); font-size: 13.5px; margin-bottom: 10px; }

#toast {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + 14px);
  z-index: 30;
  background: var(--toast-bg);
  color: var(--toast-text);
  padding: 11px 18px;
  border-radius: 14px;
  box-shadow: 0 8px 24px var(--shadow);
  font-size: 14px;
  max-width: calc(100% - 32px);
  text-align: center;
  pointer-events: none; /* only words: a tap goes to the button under it (e.g. a sheet's Save) */
}
/* ...unless it carries a button (Undo). */
#toast.has-action { pointer-events: auto; display: flex; align-items: center; gap: 14px; text-align: start; padding: 6px 6px 6px 16px; }
.toast-action { flex-shrink: 0; border: none; border-radius: 10px; padding: 8px 12px; background: color-mix(in srgb, var(--toast-text) 16%, transparent); color: var(--toast-text); font-size: 14px; font-weight: 700; }

.transfer-mark { font-weight: 600; }
/* Importing a statement: the review. */
.import-cols { margin: 4px 0 12px; }
.import-cols > summary { color: var(--accent); cursor: pointer; padding: 6px 0; font-size: 14px; }
.import-preview { width: 100%; border-collapse: collapse; font-size: 13.5px; font-variant-numeric: tabular-nums; }
.import-preview td { padding: 7px 4px; border-bottom: 1px solid var(--border); vertical-align: top; }
.import-preview td:nth-child(2) { overflow-wrap: anywhere; }
.import-preview td:last-child { text-align: end; white-space: nowrap; }
.import-recurring .drawer-row { align-items: flex-start; }
.import-recurring .grow { display: flex; flex-direction: column; gap: 2px; min-width: 0; overflow-wrap: anywhere; }
.import-recurring .note { margin: 0; }
.ins-transfers { margin: 2px 4px 10px; }

/* "Are you sure?" */
#confirm { position: fixed; inset: 0; z-index: 80; display: flex; align-items: center; justify-content: center; padding: 16px; }
#confirm[hidden] { display: none; }
.confirm-backdrop { position: absolute; inset: 0; background: var(--scrim); animation: m-fade-in 0.18s ease both; }
.confirm-panel { position: relative; width: 100%; max-width: 360px; padding: 22px 20px 16px; border-radius: var(--r-xl); border: 1px solid var(--border); background: var(--bg); box-shadow: 0 20px 50px var(--shadow); animation: m-rise 0.24s var(--ease-out) both; }
.confirm-panel p { margin: 0 0 14px; font-size: 15.5px; line-height: 1.45; }
.confirm-panel:focus-visible { outline: none; }

/* User button (top-left) */
.avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--tint);
  border: none;
  color: var(--tint-ink);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  flex-shrink: 0;
}
.avatar.big { width: 48px; height: 48px; font-size: 17px; }
.hdr-tools { display: flex; align-items: center; gap: 6px; }
.tools-row[hidden] { display: none; }

/* Sidebar */
#drawer { position: fixed; inset: 0; z-index: 25; }
.drawer-backdrop { position: absolute; inset: 0; background: var(--scrim); opacity: 0; transition: opacity 0.2s ease; }
.drawer-panel {
  position: absolute;
  top: 0;
  bottom: 0;
  inset-inline-start: 0;
  width: min(320px, 86vw);
  overflow-y: auto;
  background: var(--bg);
  border-inline-end: 1px solid var(--border);
  padding: calc(18px + var(--safe-top)) 16px calc(18px + var(--safe-bottom));
  transform: translateX(-100%);
  transition: transform 0.22s ease;
}
#drawer.open .drawer-backdrop { opacity: 1; }
#drawer.open .drawer-panel { transform: translateX(0); }
.drawer-user { display: flex; align-items: center; gap: 12px; padding-bottom: 14px; border-bottom: 1px solid var(--border); }
.drawer-user .grow { min-width: 0; }
.drawer-name { display: flex; align-items: center; gap: 7px; font-family: var(--font-display); font-size: 18px; font-weight: 700; min-width: 0; }
.drawer-name .nm, .name-line .nm { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }

/* Plan badge next to the user's name */
.plan-badge {
  flex-shrink: 0;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1;
  padding: 3px 6px;
  border-radius: 999px;
  color: var(--text-dim);
  border: 1px solid var(--border);
  background: var(--bg-hover);
}
.plan-badge.pro { color: var(--on-sol); background: var(--sol); border-color: var(--sol); }
/* The setup guide (wizard.js) */
.wz-progress { display: flex; gap: 6px; margin-bottom: 10px; }
.wz-progress span { flex: 1; height: 4px; border-radius: 999px; background: var(--border); }
.wz-progress span.on { background: var(--accent); }
.wz-step { margin: 0 0 2px; }
.wz-card { border: 1px solid var(--border); border-radius: var(--r-md); padding: 12px 12px 2px; margin-bottom: 12px; }
.wz-items { display: flex; flex-direction: column; gap: 8px; }
.wz-item, .wz-cols { display: grid; grid-template-columns: minmax(0, 1fr) 96px 76px; gap: 8px; align-items: center; }
.wz-cols { order: -1; margin: 0 0 6px; font-size: 12px; }
.wz-bills, .wz-loans { display: flex; flex-direction: column; }
.wz-tick { display: flex; align-items: center; gap: 10px; min-width: 0; font-size: 15px; }
.wz-tick input[type=checkbox] { accent-color: var(--accent); width: 20px; height: 20px; flex-shrink: 0; }
.wz-tick span { overflow: hidden; text-overflow: ellipsis; }
.wz-item input:not([type=checkbox]), .wz-item select {
  width: 100%;
  min-width: 0;
  background-color: var(--field);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 10px 11px;
  font-size: 16px; /* 16px stops iOS zooming in */
}
.wz-item:not(.on) .wz-amount, .wz-item:not(.on) .wz-day { opacity: 0.6; }
.wz-item input:focus, .wz-item select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
#wz-form .link-btn { align-self: flex-start; margin: 10px 0 4px; }

/* Account security sheets (security.js) */
.device-list { list-style: none; margin: 0 0 8px; padding: 0; }
.device { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.device .grow { flex: 1; min-width: 0; }
.device .note { display: block; margin: 2px 0 0; }
.device .btn { width: auto; margin: 0; padding: 6px 12px; }
.field[for] { margin-bottom: 0; }
/* Connect your AI: the other apps and the access key fold away. */
.ai-more { margin: 4px 0 10px; border: 1px solid var(--border); border-radius: var(--r-md); padding: 0 12px; }
/* The entry form's "More details" (payee, tag, notes, photos), folded away. */
.form-extras { margin: 4px 0 14px; border: 1px solid var(--border); border-radius: var(--r-sm); }
.form-extras > summary { color: var(--text); font-size: 15px; display: flex; align-items: center; gap: 10px; min-height: 50px; padding: 0 14px 0 16px; cursor: pointer; list-style: none; }
.form-extras > summary::-webkit-details-marker { display: none; }
.fx-title { font-weight: 600; flex-shrink: 0; }
.fx-hint { flex: 1; min-width: 0; color: var(--text-dim); font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: end; }
.form-extras.filled .fx-hint { color: var(--accent); }
.fx-chev { width: 20px; height: 20px; flex-shrink: 0; fill: none; stroke: var(--text-dim); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: rotate 0.25s var(--ease-out, ease); }
.form-extras[open] .fx-chev { rotate: 180deg; }
.form-extras[open] .fx-hint { visibility: hidden; }
.fx-body { padding: 2px 14px 14px; }
.form-extras[open] .fx-body { animation: fx-open 0.28s var(--ease-out, ease) both; }
@keyframes fx-open { from { opacity: 0; translate: 0 -6px; } }
.ai-more summary { cursor: pointer; padding: 11px 0; font-weight: 600; }
.ai-more[open] { padding-bottom: 8px; }
#ai-claude { display: block; text-align: center; text-decoration: none; margin-bottom: 8px; }
.copy-row { display: flex; gap: 8px; align-items: stretch; margin: 5px 0 12px; }
.copy-row input {
  flex: 1;
  min-width: 0;
  background: var(--field);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 11px 12px;
  font: 15px ui-monospace, SFMono-Regular, Menlo, monospace;
}
.copy-row .btn { width: auto; margin: 0; padding: 0 16px; }
.qr { display: block; margin: 4px auto 12px; border-radius: var(--r-sm); background: #FFFFFF; } /* (a QR code reads best on white, in either theme) */
.recovery-codes {
  margin: 0 0 12px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  font: 16px/1.7 ui-monospace, SFMono-Regular, Menlo, monospace;
  columns: 2;
  text-align: center;
  user-select: all;
}

.pro-tag {
  display: inline-block;
  margin-inline-start: 6px;
  padding: 2px 6px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  vertical-align: middle;
  color: var(--on-sol);
  background: var(--sol);
}

/* Low-balance alert (Pro) at the top of the forecast; tapping it opens the account. */
.low-alert {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  margin: 0 0 12px;
  padding: 13px 15px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--danger-soft);
  color: var(--text);
  font-size: 14.5px;
  line-height: 1.4;
  text-align: start;
}
.low-alert svg {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  margin-top: 1px;
  fill: none;
  stroke: var(--danger);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.drawer-email { font-size: 13px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.drawer-panel .btn { margin-top: 6px; }
.drawer-panel #sign-out { margin-top: 22px; }
.drawer-foot { text-align: center; color: var(--text-dim); font-size: 12px; margin-top: 18px; line-height: 1.8; }
.drawer-foot a, .auth-legal a { color: var(--text-dim); }
.btn.danger-text { color: var(--danger); }

/* A text-only button ("Forgot password?"). */
.link-btn {
  display: block;
  margin: 14px auto 0;
  background: none;
  border: none;
  color: var(--accent);
  font-size: 14.5px;
  font-weight: 600;
}
.link-btn.inline { display: inline; margin: 0; padding: 0; font-size: inherit; }
.auth-legal { margin-top: 18px; text-align: center; color: var(--text-dim); font-size: 12.5px; line-height: 1.5; }

/* Read by screen readers, not shown. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* A clear ring on whatever has keyboard focus. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sheet-panel:focus-visible, .drawer-panel:focus-visible, #chart-full:focus-visible { outline: none; }

/* "development" next to the name on a non-production copy. */
.env-badge {
  display: inline-block;
  vertical-align: middle;
  margin-inline-start: 6px;
  padding: 2px 7px;
  border-radius: 6px;
  background: var(--danger-strong);  /* (a solid red in both themes: white on the dark theme's lighter red is too faint) */
  color: #FFFFFF;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
#theme-seg button.on { color: var(--accent); }

body.chart-full-open { overflow: hidden; }

/* Sign in / register */
body.signed-out header,
body.signed-out main,
body.signed-out #tabbar { display: none; }
body.signed-out { padding-bottom: 0; }
#auth {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(24px + var(--safe-top)) 16px calc(24px + var(--safe-bottom));
}
.auth-card { width: 100%; max-width: 380px; }
.bot-check { display: flex; justify-content: center; margin: 4px 0 12px; }
.bot-check:empty { display: none; }
.auth-logo { width: 64px; height: 64px; border-radius: 18px; display: block; margin: 0 auto 12px; }
.auth-card h1 { text-align: center; margin: 0; font-family: var(--font-display); font-size: 34px; font-weight: 800; letter-spacing: -0.03em; text-transform: lowercase; }
.auth-tagline { text-align: center; color: var(--text-dim); font-size: 14.5px; margin: 4px 0 22px; }
.auth-card .seg button.on { color: var(--accent); }
.auth-message { background: var(--tint); color: var(--tint-ink); border: none; border-radius: var(--r-sm); padding: 9px 12px; margin: 0 0 14px; color: var(--text); }
.btn:disabled { opacity: 0.6; }
.btn.busy::before {
  content: "";
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-inline-end: 8px;
  vertical-align: -0.12em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: btn-spin 0.8s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }
/* Less motion when the person's device asks for it: no slides or fades, and
   the busy spinner just turns slowly. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .btn.busy::before { animation-duration: 2.4s !important; animation-iteration-count: infinite !important; }
}

.drawer-link {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  margin-top: 10px;
  padding: 13px 16px;
  background: var(--bg-elevated);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  text-align: start;
}
.drawer-link:active { background: var(--bg-hover); }
.drawer-link .grow { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.drawer-link-title { font-size: 16px; font-weight: 600; }
.drawer-link-sub { font-size: 13px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.drawer-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  margin-top: 10px;
  padding: 13px 16px;
  min-height: 52px;
  background: var(--bg-elevated);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font-size: 15px;
}
.drawer-row input[type=checkbox] { accent-color: var(--accent); width: 18px; height: 18px; }
.drawer-row .grow { flex: 1; }
.drawer-row .two-line { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.row-sub { font-size: 13px; color: var(--text-dim); line-height: 1.35; }
.drawer-row select {
  background-color: var(--field);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 8px;
  font-size: 14px;
  font-family: inherit;
}
.drawer-link .chev { color: var(--text-dim); font-size: 22px; line-height: 1; }
.summary.four { grid-template-columns: repeat(2, 1fr); }

/* ---------- Responsive: tablet and laptop ----------
   Phones: header + tab bar, edits open as bottom sheets.
   >= 760px: a persistent sidebar replaces the tab bar and menu button;
             edits open as a centered dialog.
   >= 1100px: three columns - sidebar, list, and a detail column on the
              right where entries open for editing. */
#sidebar, #detail-empty, .add-label { display: none; }
/* Phones reach Edit by swiping, which keyboards and screen readers can't do,
   so the card's Edit button stays for them: out of sight until it's focused. */
.card-edit:not(:focus-visible) { position: absolute; width: 1px; height: 1px; padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); }
.card-edit:focus-visible {
  position: absolute; z-index: 2; top: 50%; inset-inline-end: 10px; width: 36px; height: 36px; margin-top: -18px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 10px; border: 1px solid var(--border); background: var(--bg); color: var(--text-dim);
}
.card-edit:focus-visible svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

@media (hover: hover) {
  .row:hover, .list-item:hover, .drawer-link:hover { background: var(--bg-hover); }
  .row.off:hover { background: var(--bg-elevated); }
}
.row.selected, .list-item.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* The highlighted transaction: whichever one you last tapped, or today's (or
   the next upcoming) entry by default. Lifted a little off the list, like
   the rest of the stack is behind it, and it stays - it only moves when a
   different entry is tapped. */
/* Background stays the normal opaque --bg-elevated, not --accent-soft: a
   row's background has to stay opaque to hide the swipe button behind it
   (see the .row.past note above), and --accent-soft is translucent. */
.row.highlighted {
  position: relative;
  z-index: 2;
  transform: scale(1.035);
  border-color: var(--sol);
  box-shadow: 0 10px 22px -6px var(--shadow), 0 0 0 2px var(--sol);
  transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
}

@media (min-width: 760px) {
  /* A phone turned sideways can land either way up, so the notch/Dynamic
     Island may be on the left or the right; --safe-left covers it when
     it's on this side, and is 0px on a laptop or the other rotation. */
  :root { --sidebar-w: calc(236px + var(--safe-left)); }
  body { padding-inline-start: var(--sidebar-w); padding-bottom: 32px; }
  #tabbar, header .hdr-side:first-child { display: none; }

  #sidebar {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    bottom: 0;
    inset-inline-start: 0;
    z-index: 6;
    width: var(--sidebar-w);
    overflow-y: auto;
    background: var(--bar);
    border-inline-end: 1px solid var(--hair);
    padding: calc(16px + var(--safe-top)) 12px 12px calc(12px + var(--safe-left));
  }
  .side-brand { display: flex; align-items: center; gap: 10px; padding: 4px 10px 20px; font-family: var(--font-display); font-size: 24px; font-weight: 800; letter-spacing: -0.03em; text-transform: lowercase; }
  .side-brand img { width: 34px; height: 34px; border-radius: 10px; }
  .side-btn {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    min-height: 44px;
    padding: 10px 14px;
    margin-bottom: 4px;
    border: none;
    border-radius: var(--r-sm);
    background: none;
    color: var(--text-dim);
    font-size: 15px;
    font-weight: 600;
    text-align: start;
  }
  .side-btn:hover { background: var(--bg-hover); color: var(--text); }
  .side-btn.active { background: var(--tint); color: var(--tint-ink); font-weight: 700; }
  .side-btn svg { width: 22px; height: 22px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
  .side-btn .grow { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  #sidebar .section-title { padding: 18px 12px 6px; }
  .side-user {
    margin-top: auto;
    padding-top: 12px;
    border-top: 1px solid var(--hair);
  }
  .side-user .side-btn { color: var(--text); padding: 8px 10px; }
  .side-user .who { display: flex; flex-direction: column; min-width: 0; flex: 1; }
  .side-user .who > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .side-user .name-line { display: flex; align-items: center; gap: 6px; }
  .side-user .who .email { font-size: 12.5px; font-weight: 400; color: var(--text-dim); }

  /* The header spans the whole middle column; its padding lines its contents
     up with the centered list below (max-width 860px + 24px padding). */
  header {
    display: flex;
    flex-wrap: wrap; /* the ad card (#top-ad) takes its own row */
    justify-content: space-between;
    border-bottom: none;
    padding-top: calc(16px + var(--safe-top));
    padding-bottom: 6px;
    padding-inline-start: max(24px, calc((100% - 860px) / 2 + 24px));
    /* The other rotation puts the notch/Dynamic Island over this edge
       instead of the sidebar's - see --safe-right. */
    padding-inline-end: calc(max(24px, calc((100% - 860px) / 2 + 24px)) + var(--safe-right));
  }
  .title-btn { align-items: flex-start; padding: 0; }
  .view-name { display: block; font-size: 30px; font-weight: 800; letter-spacing: -0.025em; }
  .acct-sub { max-width: 40vw; font-size: 13px; }
  .add-label { display: inline; font-weight: 700; font-size: 14.5px; }
  #add-btn {
    width: auto;
    gap: 6px;
    padding: 0 18px 0 14px;
    border-radius: var(--r-pill);
  }
  #add-btn svg { width: 18px; height: 18px; }
  /* The right edge here is the true screen edge (the left already sits past
     the sidebar, which reserves its own --safe-left) - see --safe-right. */
  main { max-width: 860px; padding: 8px calc(24px + var(--safe-right)) 8px 24px; }

  /* Edits as a centered dialog rather than a bottom sheet. */
  .sheet-grab { display: none; }
  .sheet-panel {
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    max-width: 520px;
    max-height: calc(100vh - 48px);
    border-bottom: 1px solid var(--border);
    border-radius: var(--r-xl);
    padding-bottom: 20px;
    box-shadow: 0 20px 50px var(--shadow);
  }
  #toast { bottom: 24px; left: calc(50% + (var(--sidebar-w) - var(--detail-w, 0px)) / 2); }

  body.signed-out { padding-inline: 0; }
  body.signed-out #sidebar, body.signed-out #detail-empty { display: none; }
}

/* Tap/swipe still work everywhere, but reaching Edit by swiping is awkward
   with a mouse or trackpad, so the cards carry their own Edit button when
   there's room AND the pointer is a mouse/trackpad - not on a touch device
   just because its screen happens to be wide (a big tablet, or a phone in
   landscape), where swiping is still the natural way in.
   Hidden again at >= 1100px, where a click opens the entry instead. */
@media (min-width: 760px) and (hover: hover) and (pointer: fine) {
  .swipe .row { padding-inline-end: 58px; }
  .card-edit, .card-edit:not(:focus-visible) {
    clip-path: none;
    overflow: visible;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    z-index: 2;
    top: 50%;
    inset-inline-end: 10px;
    width: 36px;
    height: 36px;
    margin-top: -18px;
    border-radius: var(--r-xs);
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--text-dim);
  }
  .card-edit:hover { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-soft); }
  .card-edit svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
}

@media (min-width: 1100px) {
  :root { --detail-w: clamp(340px, 30vw, 440px); }
  .card-edit, .card-edit:not(:focus-visible), .card-edit:focus-visible { display: none; }  /* (a click or Enter opens the entry here) */
  .swipe .row { padding-inline-end: 14px; }
  body { padding-inline-end: var(--detail-w); }

  #detail-empty, #sheet, .sheet-ghost {
    position: fixed;
    top: 0;
    bottom: 0;
    inset-inline-end: 0;
    inset-inline-start: auto;
    width: var(--detail-w);
    background: var(--bg);
  }
  #detail-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 28px; z-index: 4; }
  #detail-empty svg { width: 40px; height: 40px; fill: none; stroke: var(--text-dim); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; opacity: 0.6; }
  #detail-empty h2 { font-size: 17px; margin-top: 10px; }
  #sheet, .sheet-ghost { z-index: 7; }
  .sheet-backdrop { display: none; }
  .sheet-panel {
    position: absolute;
    inset: 0;
    transform: none;
    max-width: none;
    max-height: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: calc(20px + var(--safe-top)) 22px 24px;
    /* Vertically centered; the auto margins (unlike justify-content: center)
       collapse to 0 when the content is taller than the column, so it
       still scrolls from the top. */
    display: flex;
    flex-direction: column;
  }
  .sheet-panel > .sheet-grab + * { margin-top: auto; } /* (the first thing in it: the handle isn't shown here) */
  .sheet-panel > :last-child { margin-bottom: auto; }
}

@media (min-width: 1280px) {
  .summary.four { grid-template-columns: repeat(4, 1fr); }
}

/* ---------- free vs Pro ---------- */
/* The ad slot (a "Go Pro" house ad until an ad network is connected). */
.ad-slot {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 12px 0;
  padding: 12px 14px;
  border: 1px dashed var(--border);
  border-radius: var(--r-md);
  background: var(--bg);
}
.ad-label {
  position: absolute;
  top: 6px;
  inset-inline-end: 10px;
  font-size: 10.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.ad-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; font-size: 13.5px; color: var(--text-dim); }
.ad-body b { color: var(--text); font-size: 15px; }
.btn.ad-cta { width: auto; margin: 0; padding: 8px 14px; flex-shrink: 0; }

/* AdSense on the web: Google draws inside the slot, so no card around it.
   The ads inside the page sit in a layer of their own over the page (pro.js
   syncWebAds): it scrolls with the page, under the header and month names. */
.ad-slot.ad-adsense { justify-content: center; margin-inline: auto; padding: 0; border: 0; border-radius: 0; background: none; overflow: hidden; }
.ad-slot.ad-adsense[data-web-ad]:not([style*="height:0px"]) { margin-block: 12px; }
#web-ads { position: absolute; top: 0; left: 0; width: 100%; height: 0; z-index: 2; }
.web-ad { position: absolute; overflow: hidden; }
.web-ad[hidden] { display: none; }
#top-ad .ad-slot.ad-adsense, #bottom-ad .ad-slot.ad-adsense { min-height: 0; padding: 0; }

/* Ad formats (units in js/ads.js). Test ads ("placeholder" provider) are
   drawn at the size a network would fill and made to look like a real ad: a
   picture (img/sample-ad.svg), a headline, a line of copy and a button, for
   a made-up advertiser. The small line under the copy says which unit, size,
   mode and tier it is. */
.ad-slot.ad-placeholder {
  gap: 10px;
  padding: 0;
  overflow: hidden;
  border-style: solid;
  background: var(--bg-elevated);
  cursor: pointer;
  text-align: start;
}
.ad-placeholder .ad-img { display: block; flex-shrink: 0; object-fit: cover; background: #e9a868; }
.ad-placeholder .ad-label { top: 4px; inset-inline: 4px auto; padding: 1px 5px; border-radius: 4px; font-size: 9px; font-weight: 700; color: #fff; background: rgba(0, 0, 0, 0.55); }
.ad-placeholder .ad-body { gap: 1px; padding: 6px 0; }
.ad-placeholder .ad-body b { font-size: 14px; line-height: 1.25; }
.ad-placeholder .ad-body > * { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ad-placeholder .ad-copy { font-size: 12.5px; }
.ad-placeholder .ad-meta { font-size: 9.5px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.ad-placeholder .ad-go { flex-shrink: 0; margin-inline-end: 10px; padding: 6px 11px; border-radius: 999px; font-size: 12.5px; font-weight: 700; white-space: nowrap; color: #fff; background: #1a73e8; }
/* A banner: the picture at one end, two lines, the button. */
.ad-placeholder.ad-banner { height: 50px; max-width: 728px; margin-inline: auto; }
.ad-placeholder.ad-banner .ad-img { width: 64px; height: 100%; }
.ad-placeholder.ad-banner .ad-copy { display: none; }
/* A card in a list: like one of the list's own cards. */
.ad-placeholder.ad-native { min-height: 78px; }
.ad-placeholder.ad-native .ad-img { width: 62px; height: 62px; margin-inline-start: 8px; border-radius: 10px; }
/* The rectangles: the picture on top, the words under it. */
.ad-placeholder.ad-mrec, .ad-placeholder.ad-side { flex-direction: column; align-items: stretch; gap: 0; }
.ad-placeholder.ad-mrec { width: 300px; max-width: 100%; height: 250px; margin-inline: auto; }
:is(.ad-mrec, .ad-side).ad-placeholder .ad-img { width: 100%; flex: 1; min-height: 0; }
:is(.ad-mrec, .ad-side).ad-placeholder .ad-body { flex: none; padding: 8px 12px 0; }
:is(.ad-mrec, .ad-side).ad-placeholder .ad-body b { font-size: 16px; }
:is(.ad-mrec, .ad-side).ad-placeholder .ad-copy { white-space: normal; }
:is(.ad-mrec, .ad-side).ad-placeholder .ad-go { align-self: flex-end; margin: 6px 12px 10px; }
/* The phone apps' ads inside the page (pro.js syncInlineAds): an empty box
   the phone draws the ad over. No height, and no gap around it, until its ad
   has arrived. */
.ad-slot.ad-inline { display: block; margin: 0 auto; padding: 0; border: 0; border-radius: 0; background: none; overflow: hidden; }
.ad-slot.ad-inline:not([style*="height:0px"]):not([style*="height: 0px"]) { margin-block: 12px; }
.ad-slot.ad-inline.ad-mrec { width: 300px; max-width: 100%; }
/* The banner pinned under the header: a full-width last row of it. */
#top-ad { grid-column: 1 / -1; flex-basis: 100%; min-width: 0; max-width: 100%; } /* (never wider than the header: on an unfolded phone the entry's pane is beside it) */
#top-ad:empty { display: none; }
#top-ad .ad-slot { margin: 2px 0 0; min-height: 50px; padding: 6px 12px; }
#top-ad .ad-slot.ad-placeholder { padding: 0; }
/* The phone apps' AdMob banner is drawn natively over this box; it only keeps the space. */
#top-ad .ad-slot.ad-admob { height: var(--native-ad-h, 50px); min-height: 0; padding: 0; border: 0; background: none; }
@media (min-width: 1100px) {
  #top-ad .ad-placeholder.ad-banner { height: 90px; } /* 728×90 */
  #top-ad .ad-placeholder.ad-banner .ad-img { width: 130px; }
  #top-ad .ad-placeholder.ad-banner .ad-copy { display: block; }
}

/* Privacy choices: a card above the tab bar until the person chooses. */
#consent {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--tabbar-h) + var(--safe-bottom));
  z-index: 12;
  padding: 0 12px 12px;
  pointer-events: none;
}
.consent-card {
  pointer-events: auto;
  max-width: 560px;
  margin: 0 auto;
  padding: 18px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 28px var(--shadow);
}
.consent-card h2 { font-family: var(--font-display); font-size: 18px; font-weight: 700; margin: 0 0 6px; }
.consent-card p { font-size: 13.5px; color: var(--text-dim); margin: 0 0 12px; }
.consent-card a { color: var(--accent); }
.consent-actions { display: flex; gap: 8px; }
.consent-actions .btn { flex: 1; margin: 0; }
.consent-card .btn.subtle { margin: 8px 0 0; }
body.consent-open main { padding-bottom: 240px; }
.consent-row { align-items: flex-start; }
.consent-row .grow { display: flex; flex-direction: column; gap: 2px; }
.consent-row .note { margin: 0; }
@media (min-width: 760px) {
  #consent { bottom: 0; inset-inline-start: var(--sidebar-w); } /* no tab bar; beside the sidebar */
}
@media (min-width: 1100px) {
  #consent { inset-inline-end: var(--detail-w); }
}

/* The end of the free plan's look-ahead in the forecast list. */
.look-ahead-end {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
  margin: 14px 0 24px;
  padding: 18px 16px;
  border: none;
  border-radius: var(--r-lg);
  background: var(--sol-soft);
  color: var(--sol-ink);
  font-size: 14px;
}
.look-ahead-end b { color: var(--sol-ink); font-family: var(--font-display); font-size: 17px; }
.look-ahead-end .btn { width: auto; margin-top: 6px; }

/* The upgrade sheet. */
.plan-compare { width: 100%; border-collapse: collapse; margin: 10px 0 16px; font-size: 14px; }
.plan-compare th, .plan-compare td { padding: 8px 6px; border-bottom: 1px solid var(--border); text-align: center; }
.plan-compare th:first-child, .plan-compare td:first-child { text-align: start; color: var(--text-dim); }
.plan-compare th:last-child, .plan-compare td:last-child { color: var(--accent); font-weight: 700; }
.plan-options { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
.trial-offer {
  display: inline-block;
  margin: 2px 0 10px;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  background: var(--sol);
  color: var(--on-sol);
  font-weight: 700;
  font-size: 14px;
}
.trial-offer.small { margin: 6px 0 0; padding: 2px 8px; font-size: 12px; }
.plan-option {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-elevated);
  color: var(--text);
  text-align: start;
}
.plan-option b { font-family: var(--font-display); font-size: 21px; }
.plan-option span { font-size: 13px; color: var(--text-dim); }
.plan-option.on { border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand); }
.test-note { color: var(--accent); }

/* ---------- right-to-left (Arabic) ----------
   The layout mirrors itself through the logical properties above
   (margin-inline-start, inset-inline-end, text-align: start, ...); these are
   the few things that need spelling out. */
[dir="rtl"] .drawer-panel { transform: translateX(100%); }
[dir="rtl"] #drawer.open .drawer-panel { transform: translateX(0); }
[dir="rtl"] .flip { display: inline-block; transform: scaleX(-1); }
@media (min-width: 760px) {
  [dir="rtl"] #toast { left: calc(50% - (var(--sidebar-w) - var(--detail-w, 0px)) / 2); }
}
/* Amounts read in their own direction, so "-$1,234.56" never gets reordered
   inside Arabic text (and riyal amounts written in Arabic still read right). */
.money, .amt, .bal, .stat .value, .list-item .right, .bd-row > span:last-child, .bd-head > span:last-child {
  unicode-bidi: plaintext;
}
/* One look for every drop-down: no browser chrome, the app's own chevron
   (drawn with two gradients so it follows the theme's --text-dim), on the
   end side so it flips for Arabic. */
select {
  -webkit-appearance: none;
  appearance: none;
  font-family: inherit;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
    linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
  background-size: 5px 5px, 5px 5px;
  background-position: right 15px center, right 10px center;
  background-repeat: no-repeat;
  padding-inline-end: 28px !important;
}
/* Suggestion fields (Name) get the same chevron in place of Chrome's white one. */
input[list] {
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
    linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
  background-size: 5px 5px, 5px 5px;
  background-position: right 15px center, right 10px center;
  background-repeat: no-repeat;
  padding-inline-end: 28px !important;
}
input[list]::-webkit-calendar-picker-indicator { opacity: 0; }
/* The name field's suggestions (sheets.js nameComboHtml): a big button, its own list. */
.combo { position: relative; display: block; }
.combo input[role="combobox"] { padding-inline-end: 52px; }
.combo-btn {
  position: absolute; top: 1px; bottom: -4px; inset-inline-end: 0; width: 48px; /* (44px+ to tap, a little past the field) */
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; color: var(--text-dim); border-radius: 0 10px 10px 0; cursor: pointer;
}
.combo-btn svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; transition: transform 0.15s ease; }
.combo-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
.combo-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* It floats over the form below (absolute), instead of pushing it down. */
.combo-list {
  position: absolute; z-index: 5; top: calc(100% + 4px); left: 0; right: 0;
  list-style: none; margin: 0; padding: 4px; max-height: 206px; /* (4 and a half names: the cut one says "scroll for more") */ overflow-y: auto; overscroll-behavior: contain;
  border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--bg-elevated);
  box-shadow: 0 10px 24px -8px var(--shadow); color: var(--text); font-size: 16px;
}
.combo-list li { padding: 12px; min-height: 44px; border-radius: 8px; cursor: pointer; touch-action: pan-y; }
.combo-list li:hover, .combo-list li[aria-selected="true"] { background: var(--bg-hover); }
[dir="rtl"] select, [dir="rtl"] input[list] { background-position: left 10px center, left 15px center; }
select:disabled { opacity: 0.6; }
/* The menu's language picker matches the Appearance switch under it. */
.lang-select {
  display: block;
  width: 100%;
  margin: 0 0 14px;
  background-color: var(--field);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 12px 14px;
  font-size: 16px; /* 16px stops iOS zooming in */
  font-weight: 600;
}
.lang-select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* ---------- motion (js/app/motion.js decides when; none with reduce motion) ---------- */
:root { --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1); --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1); }

/* Sheets rise from the bottom over a fading backdrop; leaving, a copy sinks. */
#sheet.m-open .sheet-backdrop { animation: m-fade-in 0.22s ease both; }
#sheet.m-open .sheet-panel { animation: m-sheet-up 0.34s var(--ease-out) both; }
.motion-ghost { pointer-events: none; }
.sheet-ghost { position: fixed; inset: 0; z-index: 19; }
.sheet-ghost .sheet-backdrop { animation: m-fade-out 0.2s ease both; }
.sheet-ghost .sheet-panel { animation: m-sheet-down 0.22s ease-in both; }
/* A new step inside an open sheet (the setup guide, a form after a list) slides across. */
.sheet-panel.m-swap > :not(.sheet-grab) { animation: m-swap-in 0.26s var(--ease-out) both; }
@media (min-width: 1100px) {
  /* On a laptop the sheet is the column on the right: it eases in from the side. */
  #sheet.m-open .sheet-panel { animation: m-from-end 0.3s var(--ease-out) both; }
  .sheet-ghost .sheet-panel { animation: m-fade-out 0.16s ease both; }
}

/* The menu: slides in (a transition, as before) and now also slides out. */
.drawer-ghost { position: fixed; inset: 0; z-index: 25; }
.drawer-ghost .drawer-backdrop { animation: m-fade-out 0.2s ease both; }
.drawer-ghost .drawer-panel { animation: m-drawer-out 0.22s ease-in both; }
[dir="rtl"] .drawer-ghost .drawer-panel { animation-name: m-drawer-out-rtl; }

/* Messages pop up and fade back down. */
#toast:not([hidden]) { animation: m-toast-in 0.3s var(--ease-spring) both; }
#toast.m-out { animation: m-toast-out 0.2s ease both; }

/* A new screen (tab or account): the content fades up, the first items one after another. */
#main.m-enter :is(.summary .stat, .swipe, .list-item, .month, .chart-wrap, .controls, .breakdown > *, .empty, .section-title) {
  animation: m-rise 0.4s var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 28ms);
}
/* ...and the chart draws its line, then marks the lowest point. */
#main.m-enter .chart-line { stroke-dasharray: 1; animation: m-draw 0.9s ease-out 0.15s both; }
#main.m-enter .chart-area { animation: m-fade-in 0.6s ease 0.35s both; }
#main.m-enter .chart-low { transform-box: fill-box; transform-origin: center; animation: m-pop 0.35s var(--ease-spring) 0.95s both; }

/* The same screen after a change: a new entry slides in with a glow; one turned off or on gives a small pulse. */
.m-new { animation: m-row-in 0.42s var(--ease-out) both; }
.swipe.m-new > .row, .list-item.m-new { animation: m-glow 1.4s ease both; }
.swipe.m-toggled > .row, .list-item.m-toggled { animation: m-pulse 0.34s var(--ease-spring); }
/* Totals that changed count to the new value (motion.js) with a little lift. */
.value.m-changed { animation: m-lift 0.5s var(--ease-spring); }

/* Everything you press gives a little. */
.btn, .seg button, .tab, .list-item, .drawer-link, .side-btn, .plan-option, .consent-card .btn {
  transition: transform 0.12s ease, background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}
.btn:active, .drawer-link:active, .side-btn:active, .plan-option:active { transform: scale(0.97); }
.list-item:active { transform: scale(0.985); }
.tab.active svg { animation: m-pop 0.32s var(--ease-spring); }

/* Things that appear on their own: the privacy card, the sign-in card, the full-screen chart. */
.consent-card { animation: m-rise 0.45s var(--ease-out) both; }
#auth:not([hidden]) .auth-card { animation: m-rise 0.45s var(--ease-out) both; }
#chart-full:not([hidden]) { animation: m-fade-in 0.2s ease both; }

/* Switching light and dark: colours fade into each other. */
.theming, .theming *, .theming *::before, .theming *::after {
  transition: background-color 0.35s ease, color 0.35s ease, border-color 0.35s ease, fill 0.35s ease, stroke 0.35s ease !important;
}

@keyframes m-fade-in { from { opacity: 0; } }
@keyframes m-fade-out { to { opacity: 0; } }
@keyframes m-sheet-up { from { translate: 0 100%; } }
@keyframes m-sheet-down { to { translate: 0 100%; } }
@keyframes m-from-end { from { opacity: 0; translate: 24px 0; } }
@keyframes m-swap-in { from { opacity: 0; translate: 14px 0; } }
@keyframes m-drawer-out { from { transform: translateX(0); } to { transform: translateX(-100%); } }
@keyframes m-drawer-out-rtl { from { transform: translateX(0); } to { transform: translateX(100%); } }
@keyframes m-toast-in { from { opacity: 0; translate: 0 14px; scale: 0.96; } }
@keyframes m-toast-out { to { opacity: 0; translate: 0 10px; } }
@keyframes m-rise { from { opacity: 0; translate: 0 10px; } }
@keyframes m-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes m-pop { from { scale: 0.6; opacity: 0.4; } }
@keyframes m-row-in { from { opacity: 0; translate: 0 -8px; } }
/* The swipe hint (swipe.js hintSwipe): the first card slides right to show "Exclude" and springs back. */
.swipe > .row.swipe-hint { animation: swipe-hint 1.3s cubic-bezier(0.3, 0, 0.2, 1) both; }
@keyframes swipe-hint { /* (translate, not transform: the highlighted card keeps its lift) */
  0% { translate: 0; }
  30%, 55% { translate: 84px 0; }
  78% { translate: -6px 0; }
  90% { translate: 2px 0; }
  100% { translate: 0; }
}
@keyframes m-glow { from { box-shadow: inset 0 0 0 999px var(--accent-soft), 0 0 0 1px var(--accent); } } /* (a tint over the row, never see-through: the swipe actions sit behind it) */
@keyframes m-pulse { 50% { scale: 0.97; } }
@keyframes m-lift { 40% { scale: 1.06; } }
[dir="rtl"] .sheet-panel.m-swap > * { animation-name: m-swap-in-rtl; }
@keyframes m-swap-in-rtl { from { opacity: 0; translate: -14px 0; } }
@media (min-width: 1100px) {
  [dir="rtl"] #sheet.m-open .sheet-panel { animation-name: m-from-start; }
}
@keyframes m-from-start { from { opacity: 0; translate: -24px 0; } }

/* The menu's own avatar sits exactly over the header's: the same tap closes the menu. */
.drawer-me { flex-shrink: 0; padding: 0; cursor: pointer; margin-block: 3px -3px; margin-inline-start: -4px; }
.drawer-pro { margin-top: 14px; }

/* Pro features on the free plan: a gold lock and "PRO" on the button. */
.pro-lock { display: inline-flex; align-items: center; gap: 3px; margin-inline-start: 8px; padding: 2px 7px; border-radius: var(--r-xs);
  background: var(--sol); color: var(--on-sol); font-size: 10.5px; font-weight: 800; letter-spacing: 0.05em; vertical-align: middle; }
.pro-lock svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.btn.locked { border-style: dashed; border-color: var(--border); }
/* The Go Pro card at the top of the menu (free plan). */
.pro-card { margin: 14px 0 6px; padding: 16px; border-radius: var(--r-lg); border: none; background: var(--sol-soft); color: var(--sol-ink); }
.pro-card-title { font-family: var(--font-display); font-weight: 800; font-size: 19px; color: var(--sol-ink); }
.pro-card .note { color: var(--sol-ink); }
/* Its main button is the sun colour, as in the design: Pro is amber, not the brand blue. */
.pro-card .btn.primary { background: var(--sol); border-color: var(--sol); color: var(--on-sol); }
.pro-card .btn:not(.primary) { background: transparent; border-color: color-mix(in srgb, var(--sol-ink) 35%, transparent); color: var(--sol-ink); }
.pro-card-list { margin: 8px 0 4px; padding-inline-start: 18px; font-size: 13.5px; line-height: 1.6; }
.pro-card .btn { margin-top: 8px; }
.drawer-panel { touch-action: pan-y; } /* (sideways swipes close it; vertical ones scroll) */

/* While the menu, a sheet or the full chart is up (core.js lockPageScroll). */
html.scroll-locked, body.scroll-locked { overflow: hidden; }
/* Scrolling inside them stops at their edges instead of passing to the page. */
.drawer-panel, .sheet-panel { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }

/* The phone apps' AdMob banner, pinned to the bottom of the screen (pro.js
   syncNativeAds). It's drawn natively above the app, so everything that
   reaches the bottom stops above it instead: the tab bar, sheets, the menu,
   the full-screen chart, toasts and the privacy card. AdMob places it above
   the phone's own navigation area, so that stays empty beneath it. */
/* (--native-ad-gap: on Android phones with an older WebView the banner sits
   that much above the bottom of the page: pro.js, Platform.ads.bottomGap.) */
:is(html.native-ad-bottom, html.web-ad-bottom) { --ad-bottom: calc(var(--native-ad-h, 50px) + var(--safe-bottom) + var(--native-ad-gap, 0px)); }
:is(html.native-ad-bottom, html.web-ad-bottom) body { padding-bottom: calc(var(--tabbar-h) + var(--ad-bottom) + 12px); }
:is(html.native-ad-bottom, html.web-ad-bottom) #tabbar { bottom: var(--ad-bottom); padding-bottom: 0; }
:is(html.native-ad-bottom, html.web-ad-bottom) #sheet,
:is(html.native-ad-bottom, html.web-ad-bottom) #chart-full { bottom: var(--ad-bottom); }
:is(html.native-ad-bottom, html.web-ad-bottom) .sheet-panel { padding-bottom: 16px; max-height: calc(100% - 96px - var(--safe-top)); }
:is(html.native-ad-bottom, html.web-ad-bottom) #toast { bottom: calc(var(--tabbar-h) + var(--ad-bottom) + 14px); }
:is(html.native-ad-bottom, html.web-ad-bottom) #consent { bottom: calc(var(--tabbar-h) + var(--ad-bottom)); }
:is(html.native-ad-bottom, html.web-ad-bottom) #chart-full .chart-rotator { height: calc(100dvh - var(--ad-bottom)); }
@media (orientation: portrait) {
  :is(html.native-ad-bottom, html.web-ad-bottom) #chart-full .chart-rotator { width: calc(100dvh - var(--ad-bottom)); }
}
/* Below the banner is the phone's own navigation area: cover it so the list
   doesn't show through there (jade: "things showing past the banner"). */
:is(html.native-ad-bottom, html.web-ad-bottom) body::after {
  content: "";
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--safe-bottom) + var(--native-ad-gap, 0px));
  background: var(--bar);
  z-index: 70;
  pointer-events: none;
}

/* ---------- Settings page ---------- */
.settings-page { max-width: 560px; margin: 0 auto; }
.settings-user { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: var(--r-lg); background: var(--bg-elevated); margin: 4px 0 6px; }
.settings-user .grow { min-width: 0; }
.settings-page .btn { margin-top: 6px; }
.settings-page #sign-out { margin-top: 22px; }

/* ---------- search and filter ---------- */
.list-tools { margin-bottom: 4px; }
.tools-row { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
.tools-title { flex: 1; }
.tool-btn { position: relative; width: 44px; height: 44px; border-radius: 14px; border: 1px solid transparent; background: var(--bg-elevated); color: var(--text); display: grid; place-items: center; cursor: pointer; }
.tool-btn svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.tool-btn.on { color: var(--accent); border-color: var(--accent); }
.tool-count { position: absolute; top: -6px; inset-inline-end: -6px; min-width: 18px; height: 18px; border-radius: 9px; background: var(--sol); color: var(--on-sol); font-size: 11px; font-weight: 700; display: grid; place-items: center; padding: 0 4px; }
#list-search { width: 100%; min-height: 48px; font-size: 16px; padding: 11px 16px; border-radius: var(--r-pill); border: 1px solid var(--border); background: var(--field); color: var(--text); }
#list-search:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.filter-summary { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 13px; color: var(--text-dim); }
.filter-summary:not(:empty) { margin-top: 8px; }
.filter-summary > span { min-width: 0; overflow-wrap: anywhere; } /* (a long search word wraps instead of widening the page) */
.filter-summary > button { flex-shrink: 0; }
.chip-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.chip { display: inline-flex; align-items: center; gap: 7px; min-height: 38px; border: 1px solid var(--border); background: var(--bg-elevated); color: var(--text); border-radius: var(--r-pill); padding: 7px 14px; font-size: 14px; cursor: pointer; }
.chip.on { border: 2px solid var(--text); padding: 6px 13px; font-weight: 700; }
.look-ahead-end span { color: var(--text-dim); }

/* ---------- tags ---------- */
/* A tag on a card: its colour as a dot, then its name. */
.tag-pill { display: inline-flex; align-items: center; gap: 5px; color: var(--text-dim); }
.primary .tag-pill, .top-head .tag-pill { margin-inline-start: 6px; font-size: 13px; font-weight: 500; vertical-align: 1px; }
.tag-pip { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.tag-list { list-style: none; margin: 8px 0 12px; padding: 0; }
.tag-list li { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--hair); }
.tag-list .grow { flex: 1; }
.tag-name { flex: 1; text-align: start; background: none; border: none; padding: 6px 0; color: var(--text); font-size: 15px; cursor: text; }
.tag-count { font-size: 12.5px; color: var(--text-dim); white-space: nowrap; }
.tag-edit { display: flex; gap: 6px; flex: 1; min-width: 0; align-items: center; }
.tag-edit input { flex: 1; min-width: 0; font-size: 16px; padding: 8px 11px; border-radius: var(--r-sm); border: 2px solid var(--accent); background: var(--field); color: var(--text); }
.tag-edit .btn { width: auto; margin: 0; padding: 7px 12px; }
.tag-add input { flex: 1; min-width: 0; font-size: 16px; padding: 11px 14px; border-radius: var(--r-sm); border: 1px dashed var(--text-dim); background: var(--field); color: var(--text); }
.tag-x { width: 36px; height: 36px; color: var(--text-dim); }
.tag-add { display: flex; align-items: stretch; gap: 8px; } /* (the box and its button: the same height, on one line) */
.tag-add .grow { flex: 1; margin-bottom: 0; }
.tag-add .btn { width: auto; margin: 0; padding-block: 0; padding-inline: 18px; }

/* ---------- Insights: spending by tag ---------- */
/* (The eight tag colours are with the other colours at the top.) */
#tag-view { margin-bottom: 8px; }
#tag-period { margin-bottom: 10px; }
.tag-chart { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.tag-chart svg { width: 180px; height: 180px; flex-shrink: 0; margin: 0 auto; }
.donut-label { font-size: 11px; fill: var(--text-dim); }
.donut-total { font-size: 17px; font-weight: 700; fill: var(--text); }
.tag-rows { flex: 1; min-width: 220px; }
.tag-row { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 4px; background: none; border: none; border-bottom: 1px solid var(--hair); color: var(--text); font-size: 14px; text-align: start; cursor: pointer; }
.tag-row .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tag-row .swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.tag-amt { font-variant-numeric: tabular-nums; }
.tag-pct { width: 3.2em; text-align: end; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.tag-add ~ .note, .tag-add ~ .error { margin-top: 10px; }

/* Phone-size web: the banner pinned to the bottom, under the tab bar (as in the phone apps). */
#bottom-ad { position: fixed; left: 0; right: 0; bottom: var(--safe-bottom); z-index: 71; background: var(--bar); border-top: 1px solid var(--hair); }
#bottom-ad:empty { display: none; }
#bottom-ad .ad-slot { margin: 0; border: 0; border-radius: 0; min-height: 50px; padding: 4px 12px; }
#bottom-ad .ad-slot.ad-placeholder { padding: 0; }
#bottom-ad .ad-placeholder.ad-banner { max-width: none; }

/* Laptops: the side ad, always in view at the bottom of the right-hand
   column; an entry's details and "Nothing selected" end above it. */
#side-ad { display: none; }
@media (min-width: 1100px) {
  #side-ad:not(:empty) {
    display: flex; justify-content: center; position: fixed; bottom: 0; inset-inline-end: 0; width: var(--detail-w);
    z-index: 8; background: var(--bg); padding: 12px 0 calc(12px + var(--safe-bottom)); border-top: 1px solid var(--hair);
  }
  html.side-ad-on #detail-empty, html.side-ad-on #sheet, html.side-ad-on .sheet-ghost { bottom: var(--side-ad-h); }
  body.signed-out #side-ad { display: none; }
}
.ad-side { width: 300px; max-width: calc(100% - 32px); margin: 0; }
.ad-placeholder.ad-side { height: 250px; }
@media (min-width: 1100px) and (min-height: 1000px) {
  .ad-placeholder.ad-side { height: 600px; }
}

/* ---------- notes and receipt photos on entries (js/app/attachments.js) ---------- */
.field textarea {
  display: block;
  width: 100%;
  margin-top: 5px;
  min-height: 76px;
  resize: vertical;
  background-color: var(--field);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 12px 14px;
  font-size: 16px; /* 16px stops iOS zooming into the field */
  font-family: inherit;
  line-height: 1.4;
}
.field textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* The small marks on a card's second line: it has notes, it has photos. */
.extra-marks { display: inline-flex; gap: 6px; vertical-align: -3px; }
.extra-mark { display: inline-flex; align-items: center; gap: 2px; font-size: 12px; }
.extra-mark svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* The part of a card that opens under it: only while that card is the
   highlighted (or open) one, so the list stays a list. It sits just inside
   the card's width, like a drawer pulled out from under it. */
.entry-extra {
  display: none;
  margin: -2px 10px 12px;
  padding: 12px 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 0 0 var(--r-md) var(--r-md);
  font-size: 14.5px;
  line-height: 1.45;
}
.swipe:has(> .row.highlighted) + .entry-extra,
.swipe:has(> .row.selected) + .entry-extra { display: block; animation: m-fade-in 0.18s ease both; }
.extra-note { margin: 0 0 8px; overflow-wrap: anywhere; unicode-bidi: plaintext; }
.extra-note:last-child { margin-bottom: 0; }
.extra-note + .extra-note { color: var(--text-dim); }
.extra-note a { color: var(--accent); text-decoration: underline; }
.extra-note.in-sheet { font-size: 14.5px; line-height: 1.45; color: var(--text-dim); margin: 0 0 12px; }

.receipt-thumbs { display: flex; flex-wrap: wrap; gap: 10px; }
.receipt-thumbs[hidden] { display: none; }
.receipt-item { position: relative; display: inline-flex; }
.receipt-thumb {
  width: 72px;
  height: 72px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--bg);
  cursor: zoom-in;
}
.receipt-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.receipt-thumb img:not([src]) { visibility: hidden; }
.receipt-thumb.missing { border-style: dashed; cursor: default; }
.receipt-remove {
  position: absolute;
  top: -8px;
  inset-inline-end: -8px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text);
  font-size: 17px;
  line-height: 1;
  padding: 0;
}
/* (a bigger target than it looks, for thumbs) */
.receipt-remove::after { content: ""; position: absolute; inset: -9px; }

.receipts-field { margin-bottom: 12px; }
.receipts-field .field-label { font-size: 13px; color: var(--text-dim); margin-bottom: 8px; }
.receipts-field .receipt-thumbs { margin: 4px 0 12px; }
.receipts-field .receipt-add { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
.receipts-field .receipt-add svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.receipts-field .receipt-hint { margin: 6px 0 0; }
.receipts-field .error { margin-top: 8px; }
#occ-extra-form { margin-bottom: 4px; }

/* A receipt photo, full screen. A tap anywhere closes it. */
#receipt-view {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(12px + env(safe-area-inset-top)) 12px calc(12px + env(safe-area-inset-bottom));
  background: rgba(0, 0, 0, 0.92);
  cursor: zoom-out;
  animation: m-fade-in 0.18s ease both;
}
#receipt-view[hidden] { display: none; }
#receipt-view img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 6px; background: #fff; }
#receipt-view img:not([src]) { visibility: hidden; }
#receipt-view.missing::after { content: attr(title); color: #fff; font-size: 15px; }
#receipt-view .receipt-close {
  position: absolute;
  top: calc(10px + env(safe-area-inset-top));
  inset-inline-end: 12px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.35);
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 26px;
  line-height: 1;
}

/* A tag's color: the dot beside its name on the Accounts page, and the row of eight it opens. */
.tag-dot { flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--bg-elevated); box-shadow: 0 0 0 1px var(--border); padding: 0; position: relative; }
.tag-dot::after { content: ""; position: absolute; inset: -11px; } /* (a 44px target) */
.tag-list li.tag-colors { padding: 10px 0 12px; }
.tag-colors > div { display: flex; flex-wrap: wrap; gap: 10px; }
.tag-colors button { width: 36px; height: 36px; border-radius: 50%; border: 2px solid var(--bg-elevated); box-shadow: 0 0 0 1px var(--border); padding: 0; display: grid; place-items: center; }
.tag-colors button[aria-checked="true"] { box-shadow: 0 0 0 2px var(--text); }
.tag-colors svg { width: 18px; height: 18px; fill: none; stroke: #fff; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.7)); }

/* Net by tag: a bar each side of a zero line. */
.net-summary { font-size: 14px; color: var(--text-dim); margin: 2px 0 10px; font-variant-numeric: tabular-nums; }
.net-summary b { font-weight: 700; }
.net-row { display: block; width: 100%; padding: 10px 4px 9px; background: none; border: none; border-bottom: 1px solid var(--hair); color: var(--text); font-size: 14px; text-align: start; cursor: pointer; }
.net-head { display: flex; align-items: center; gap: 10px; }
.net-head .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.net-head .swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.net-amt { font-weight: 600; font-variant-numeric: tabular-nums; unicode-bidi: plaintext; }
.net-amt.pos, .net-summary .pos { color: var(--income); }
.net-amt.neg, .net-summary .neg { color: var(--danger); }
.net-track { display: block; position: relative; height: 10px; margin: 7px 0 5px; direction: ltr; }
.net-track::before { content: ""; position: absolute; left: 50%; top: -3px; bottom: -3px; width: 1px; background: var(--text-dim); opacity: 0.6; }
.net-bar { position: absolute; top: 0; bottom: 0; min-width: 0; }
.net-bar.pos { left: calc(50% + 1px); background: var(--income); border-radius: 0 4px 4px 0; }
.net-bar.neg { right: calc(50% + 0px); background: var(--danger); border-radius: 4px 0 0 4px; }
.net-parts { display: block; font-size: 12.5px; color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* ---------- Insights: one period, then every chart for it (js/app/insights.js) ---------- */
/* The period row: scrolls sideways on a narrow phone rather than wrapping or shrinking. */
.chips { display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 10px; margin-bottom: 4px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.chips::-webkit-scrollbar { display: none; }
.chips button { flex: 0 0 auto; min-height: 40px; padding: 8px 16px; border-radius: var(--r-pill); border: 1px solid transparent; background: var(--bg-elevated); color: var(--text-dim); font-weight: 600; font-size: 14px; white-space: nowrap; }
.chips button.on { background: var(--text); border-color: var(--text); color: var(--bg); }
.stat .value.pos { color: var(--income); }

.ins-card { background: var(--bg-elevated); border: 1px solid transparent; border-radius: var(--r-lg); padding: 16px; }
.ins-card .note { margin: 10px 0 0; }
/* The line above a chart: the period's figures, or those of the bar or day being pointed at. */
.ins-readout { font-size: 13.5px; color: var(--text-dim); min-height: 1.4em; margin-bottom: 8px; font-variant-numeric: tabular-nums; }
.chart-wrap.flat { background: none; border: none; box-shadow: none; padding: 0; margin: 0; }
.ins-hit { cursor: pointer; }
.ins-hit:hover { fill: var(--text); fill-opacity: 0.06; }
.ins-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 8px; font-size: 13px; color: var(--text-dim); }
.ins-legend span { display: inline-flex; align-items: center; gap: 6px; }
.key { display: inline-block; width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.key.line { width: 16px; height: 2px; border-radius: 1px; background: var(--text); position: relative; }
.key.line::after { content: ""; position: absolute; left: 4px; top: -3px; width: 8px; height: 8px; border-radius: 50%; background: var(--text); }
.ins-table { margin-top: 10px; font-size: 13.5px; }
.ins-table summary { color: var(--accent); cursor: pointer; padding: 6px 0; }
.ins-table table { width: 100%; border-collapse: collapse; margin-top: 4px; font-variant-numeric: tabular-nums; }
.ins-table th, .ins-table td { text-align: end; padding: 6px 4px; border-bottom: 1px solid var(--hair); font-weight: 400; color: var(--text); }
.ins-table thead th { color: var(--text-dim); font-size: 12.5px; }
.ins-table th:first-child { text-align: start; }

/* One bar split into parts, 2px of the card showing between them. */
.dollar-bar { display: flex; gap: 2px; height: 22px; border-radius: 6px; overflow: hidden; direction: ltr; }
.dollar-bar span { min-width: 3px; }
.dollar-key { list-style: none; margin: 10px 0 0; padding: 0; }
.dollar-key li { display: flex; align-items: center; gap: 10px; padding: 6px 0; font-size: 14px; border-bottom: 1px solid var(--hair); }
.dollar-key li:last-child { border-bottom: none; }
.dollar-key .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.ins-top { list-style: none; margin: 0; padding: 0; }
.ins-top li { padding: 10px 4px 9px; border-bottom: 1px solid var(--hair); font-size: 14px; }
.top-head { display: flex; align-items: baseline; gap: 10px; }
.top-head .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.top-track { display: block; height: 8px; margin: 7px 0 5px; }
.top-track span { display: block; height: 100%; min-width: 3px; background: var(--accent); border-radius: 0 4px 4px 0; }
[dir="rtl"] .top-track span { border-radius: 4px 0 0 4px; }
div.net-row { cursor: default; }

.ins-lines p { margin: 0 0 8px; font-size: 14.5px; line-height: 1.45; color: var(--text-dim); }
.ins-lines p:last-child { margin-bottom: 0; }
.ins-lines b { color: var(--text); font-variant-numeric: tabular-nums; }
.ins-lines b.neg { color: var(--danger); }

/* A month's calendar: the day's number in ordinary text, and under it a square
   that's darker the more goes out (never text on a colored square). */
.cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; direction: ltr; }
.cal .dow { text-align: center; font-size: 11.5px; color: var(--text-dim); padding-bottom: 2px; }
.cal .day { position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 5px 0 6px; border-radius: 8px; border: 1px solid transparent; }
.cal .day.today { border-color: var(--text-dim); }
.cal .day b { font-size: 12.5px; font-weight: 500; color: var(--text); }
.cal .day:not(.blank) { cursor: pointer; }
.heat { display: inline-block; width: 18px; height: 18px; border-radius: 5px; background: var(--border); }
.heat.s1 { background: color-mix(in srgb, var(--accent) 28%, var(--bg-elevated)); }
.heat.s2 { background: color-mix(in srgb, var(--accent) 52%, var(--bg-elevated)); }
.heat.s3 { background: color-mix(in srgb, var(--accent) 76%, var(--bg-elevated)); }
.heat.s4 { background: var(--accent); }
.in-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--income); }
.cal .day .in-dot { position: absolute; top: 4px; right: 5px; box-shadow: 0 0 0 2px var(--bg-elevated); }
.ins-legend.heat-key { gap: 4px; align-items: center; }
.ins-legend.heat-key .heat { width: 14px; height: 14px; border-radius: 4px; }
.ins-legend.heat-key span:first-child { margin-inline-end: 4px; }
.ins-legend.heat-key span:last-child { margin-inline-start: 4px; }

/* A moved date of a recurring item: where it came from, and the way back. */
.moved-box { border: none; background: var(--tint); color: var(--tint-ink); border-radius: var(--r-sm); padding: 12px 14px; margin: 0 0 14px; font-size: 14px; color: var(--text-dim); }
.moved-box .btn { margin: 10px 0 0; }

/* ---------- unfolded foldable phones and small tablets ---------- */
/* An iPhone Duo or Galaxy Z Fold opened flat (or an iPad mini): a touch screen
   wider than a phone, narrower than a laptop. Two panes, like the laptop
   layout: the list on one half and the entry on the other, so the fold sits
   between them. Insights takes the whole width instead, its views in two
   columns. Folded, these phones are ordinary phone screens and none of this
   applies. (The same query as core.js isFold.) */
.ins-sec { display: block; }
/* Each section folds under its title. */
.ins-sum { display: flex; align-items: center; justify-content: space-between; gap: 8px; cursor: pointer; list-style: none; -webkit-tap-highlight-color: transparent; }
.ins-sum::-webkit-details-marker { display: none; }
.ins-sum:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
.ins-chev { width: 18px; height: 18px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: rotate 0.18s ease; }
details.ins-sec:not([open]) .ins-chev { rotate: -90deg; }
[dir="rtl"] details.ins-sec:not([open]) .ins-chev { rotate: 90deg; }
details.ins-sec:not([open]) { border-bottom: 1px solid var(--hair); }
@media (min-width: 600px) and (max-width: 1099.98px) and (min-height: 520px) and (pointer: coarse) {
  :root {
    --pane-w: calc((100vw - var(--sidebar-w, 0px)) / 2);
    --pane-edge: var(--pane-w); /* how far the list stops from the far edge */
    /* The pane ends above the tab bar (and the ad banner under it). */
    --pane-bottom: calc(var(--tabbar-h) + var(--ad-bottom, var(--safe-bottom)));
  }
  body[data-tab]:not([data-tab="insights"]):not(.signed-out) { --detail-w: var(--pane-w); padding-inline-end: var(--pane-edge); }

  body[data-tab]:not([data-tab="insights"]) :is(#detail-empty, #sheet, .sheet-ghost) {
    position: fixed;
    top: 0;
    bottom: var(--pane-bottom);
    inset-inline-end: 0;
    inset-inline-start: auto;
    width: var(--pane-w);
    background: var(--bg);
    border-inline-start: 1px solid var(--border);
  }
  body[data-tab]:not([data-tab="insights"]):not(.signed-out) #detail-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 28px; z-index: 4; padding: 0 16px; text-align: center; }
  #detail-empty svg { width: 40px; height: 40px; fill: none; stroke: var(--text-dim); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; opacity: 0.6; }
  #detail-empty h2 { font-size: 17px; margin-top: 10px; }
  body[data-tab]:not([data-tab="insights"]) :is(#sheet, .sheet-ghost) { z-index: 7; }
  body[data-tab]:not([data-tab="insights"]) .sheet-backdrop { display: none; }
  body[data-tab] .sheet-grab { display: none; } /* (a pane beside the list, or a dialog on Insights: nothing to drag down) */
  body[data-tab]:not([data-tab="insights"]) .sheet-panel {
    position: absolute;
    inset: 0;
    transform: none;
    max-width: none;
    max-height: none;
    margin: 0;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: calc(20px + var(--safe-top)) calc(18px + var(--safe-right)) 24px 18px;
    display: flex;
    flex-direction: column;
  }
  body[data-tab]:not([data-tab="insights"]) .sheet-panel > .sheet-grab + * { margin-top: auto; }
  body[data-tab]:not([data-tab="insights"]) .sheet-panel > :last-child { margin-bottom: auto; }
  body[data-tab]:not([data-tab="insights"]) #sheet.m-open .sheet-panel { animation: m-from-end 0.3s var(--ease-out) both; }
  body[data-tab]:not([data-tab="insights"]) .sheet-ghost .sheet-panel { animation: m-fade-out 0.16s ease both; }
  [dir="rtl"] body[data-tab]:not([data-tab="insights"]) #sheet.m-open .sheet-panel { animation-name: m-from-start; }
  /* The list's own half: the header's and the list's side padding as on a phone. */
  body[data-tab]:not([data-tab="insights"]) main { padding-inline: 12px; }
  body[data-tab]:not([data-tab="insights"]) header { padding-inline: 12px; }
  /* Messages over the list's half, not across the fold. */
  body[data-tab]:not([data-tab="insights"]) #toast { left: calc((100vw + var(--sidebar-w, 0px) - var(--pane-w)) / 2); }
  [dir="rtl"] body[data-tab]:not([data-tab="insights"]) #toast { left: calc((100vw - var(--sidebar-w, 0px) + var(--pane-w)) / 2); }

  /* Insights: the whole width. Anything it opens is a dialog in the middle. */
  body[data-tab="insights"] .sheet-panel {
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    max-width: 520px;
    max-height: calc(100vh - 48px);
    border-bottom: 1px solid var(--border);
    border-radius: var(--r-xl);
    padding-bottom: 20px;
    box-shadow: 0 20px 50px var(--shadow);
  }
  body[data-tab="insights"] main { max-width: 960px; }
  body[data-tab="insights"] .summary.four { grid-template-columns: repeat(4, 1fr); }
  .ins-cols { columns: 2; column-gap: 16px; }
  .ins-sec { break-inside: avoid; }
}

/* 760px and up has the sidebar (see above): beside two panes it shrinks to a
   rail of icons, so each pane keeps a phone's width. Accounts are switched
   from the title at the top, as on a phone. */
@media (min-width: 760px) and (max-width: 1099.98px) and (min-height: 520px) and (pointer: coarse) {
  :root { --sidebar-w: calc(76px + var(--safe-left)); --pane-bottom: var(--ad-bottom, 0px); }
  #sidebar { padding: calc(14px + var(--safe-top)) 6px 10px calc(6px + var(--safe-left)); }
  .side-brand { justify-content: center; padding: 2px 0 14px; font-size: 0; gap: 0; }
  .side-brand img { width: 34px; height: 34px; }
  #sidebar nav .side-btn { flex-direction: column; gap: 4px; padding: 9px 2px; margin-bottom: 4px; font-size: 10.5px; text-align: center; }
  #sidebar nav .side-btn svg { width: 24px; height: 24px; }
  #sidebar nav .side-btn .grow { flex: none; max-width: 100%; }
  .side-user .who { display: none; }
  .side-user .side-btn { justify-content: center; padding: 8px 0; }
  body[data-tab="insights"] header {
    padding-inline-start: max(24px, calc((100% - 960px) / 2 + 24px));
    padding-inline-end: calc(max(24px, calc((100% - 960px) / 2 + 24px)) + var(--safe-right));
  }
}

/* Half folded, where the browser reports the fold (viewport segments): the
   panes end exactly at it. Upright like a book, the fold is between them... */
@media (min-width: 600px) and (max-width: 1099.98px) and (min-height: 520px) and (pointer: coarse) and (horizontal-viewport-segments: 2) {
  :root {
    --pane-w: env(viewport-segment-width 1 0, 50vw);
    --pane-edge: calc(100vw - env(viewport-segment-right 0 0, 50vw));
  }
  [dir="rtl"]:root {
    --pane-w: env(viewport-segment-width 0 0, 50vw);
    --pane-edge: env(viewport-segment-left 1 0, 50vw);
  }
}
/* ...and flat on a table like a laptop, the list is on the upright half and
   the entry on the half lying down. */
@media (min-width: 600px) and (max-width: 1099.98px) and (min-height: 520px) and (pointer: coarse) and (vertical-viewport-segments: 2) {
  :root { --pane-edge: 0px; }
  body[data-tab]:not([data-tab="insights"]):not(.signed-out) { padding-bottom: calc(100vh - env(viewport-segment-bottom 0 0, 50vh) + 12px); }
  body[data-tab]:not([data-tab="insights"]) :is(#detail-empty, #sheet, .sheet-ghost) {
    top: env(viewport-segment-top 0 1, 50vh);
    inset-inline-start: var(--sidebar-w, 0px);
    width: auto;
    border-inline-start: none;
    border-top: 1px solid var(--border);
  }
  body[data-tab]:not([data-tab="insights"]) .sheet-panel { padding-top: 18px; }
  body[data-tab]:not([data-tab="insights"]) #toast { left: 50%; }
  /* The list's half is too short to keep the Today card pinned over it. */
  #today-slot.pinned:not(:empty) { position: static; margin: 0; padding: 0; background: none; box-shadow: none; }
  #today-slot.pinned .today-card { margin-bottom: 8px; }
}

/* ---------- the app with no account (guest.js) ---------- */
/* A guest's avatar: a plain figure instead of initials. */
.avatar svg { width: 58%; height: 58%; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.guest-card .note { margin: 0 0 6px; }
.guest-card .btn + .btn { margin-top: 8px; }
.auth-guest-note { margin-top: 14px; text-align: center; }
/* Two budgets: what's on each side, then three choices of equal weight. */
.merge-sides { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 4px 0 14px; }
.merge-sides > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--r-md); background: var(--bg-elevated); font-size: 13.5px; color: var(--text-dim); }
.merge-sides b { color: var(--text); font-size: 14px; }
.merge-options { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.btn.merge-option { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; margin: 0; padding: 12px 14px; text-align: start; height: auto; white-space: normal; }
.merge-option b { font-size: 15.5px; }
.merge-option span { font-size: 13.5px; font-weight: 400; color: var(--text-dim); line-height: 1.4; }
/* Development copies: test settings at the bottom of Settings. */
.dev-field { margin-top: 18px; padding: 10px 12px; border: 1px dashed var(--border); border-radius: var(--r-md); }
.settings-user + .dev-field { margin-top: 0; margin-bottom: 14px; }
.auth-card .dev-field { margin-top: 22px; text-align: start; }

/* ---------- the Today card (js/app/today.js) ---------- */
/* Today's balance, where it's heading, the lowest point ahead. A tap goes to
   today in the list. Settings → Forecast can hide it. */
.today-card {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  width: 100%;
  margin: 4px 0 8px;
  padding: 18px 20px 16px;
  border: none;
  border-radius: var(--r-xl);
  background: var(--hero);
  color: var(--hero-text);
  text-align: start;
  font-variant-numeric: tabular-nums;
  transition: transform 0.12s ease;
}
.today-card:active { transform: scale(0.985); }
/* Its faces (today.js: a tap turns to the next): all in one grid cell, so the
   card is as tall as the tallest and the list under it never moves. */
.today-faces { display: grid; }
.today-face { grid-area: 1 / 1; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.today-face:not(.on):not(.leaving) { visibility: hidden; }
.today-say { font-size: 14px; font-weight: 500; opacity: 0.92; }
.today-card .nw { white-space: nowrap; }
.today-say b { font-weight: 700; }
.today-face .today-facts { margin-top: auto; padding-top: 8px; }
.today-list { display: grid; gap: 3px; margin: 6px 0 auto; font-size: 14px; }
.today-list > span:not(.tl-more) { display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: baseline; }
.tl-date { opacity: 0.8; min-width: 3.6em; }
.tl-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.today-list b { font-weight: 700; unicode-bidi: plaintext; }
.tl-more { font-size: 13px; opacity: 0.8; }
/* Which face is showing: the current one a longer dash. */
.today-dots { display: flex; justify-content: center; gap: 6px; margin-top: 12px; }
.today-dots i { width: 6px; height: 6px; border-radius: 3px; background: var(--hero-text); opacity: 0.4; transition: width 0.35s var(--ease-spring), opacity 0.35s ease; }
.today-dots i.on { width: 18px; opacity: 0.95; }
/* Turning: the old face blurs up and away, the new one's lines rise in one
   after another, and a soft light sweeps across the card. */
.today-face.leaving { animation: tc-out 0.26s ease-in both; pointer-events: none; }
.today-face.turning > * { animation: tc-in 0.46s var(--ease-out) both; }
.today-face.turning > :nth-child(1) { animation-delay: 0.08s; }
.today-face.turning > :nth-child(2) { animation-delay: 0.13s; }
.today-face.turning > :nth-child(3) { animation-delay: 0.18s; }
.today-face.turning > :nth-child(4) { animation-delay: 0.23s; }
.today-face.turning > :nth-child(5) { animation-delay: 0.28s; }
.today-face.turning .chart-line { stroke-dasharray: 1; animation: m-draw 0.8s ease-out 0.2s both; }
.today-face.turning .today-low { animation: m-pop 0.35s var(--ease-spring) 0.75s both; }
.today-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 35%, rgba(255, 255, 255, 0.16) 50%, transparent 65%);
  translate: -110% 0;
  pointer-events: none;
}
.today-card.turning::after { animation: tc-sweep 0.75s ease-out; }
.today-card.turning { animation: tc-bump 0.4s var(--ease-spring); }
@keyframes tc-out { to { opacity: 0; translate: 0 -10px; filter: blur(6px); } }
@keyframes tc-in { from { opacity: 0; translate: 0 12px; filter: blur(6px); } }
@keyframes tc-sweep { from { translate: -110% 0; } to { translate: 110% 0; } }
@keyframes tc-bump { 35% { scale: 0.98; } }
.today-date { font-size: 14px; font-weight: 500; padding-inline-end: 36px; }
/* Its ✕ (beside the card, not in it: a button can't hold a button) hides it. */
.today-wrap { position: relative; }
.today-close {
  position: absolute;
  top: 10px;
  inset-inline-end: 10px;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: color-mix(in srgb, var(--hero-text) 16%, transparent);
  color: var(--hero-text);
}
.today-close svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; }
.today-close:active { scale: 0.9; }
.today-wrap.closing { animation: tc-close 0.18s ease-in both; }
@keyframes tc-close { to { opacity: 0; scale: 0.96; } }
.today-balance { font-family: var(--font-display); font-size: 44px; font-weight: 800; letter-spacing: -0.03em; line-height: 1.05; }
.today-card .today-balance.neg { color: var(--hero-text) !important; }
.today-line { position: relative; display: block; height: 56px; margin: 8px 0 2px; direction: ltr; }
.today-line svg { display: block; width: 100%; height: 100%; overflow: visible; }
.today-line .chart-line { fill: none; stroke: var(--hero-line); stroke-width: 2.2; stroke-linejoin: round; }
.today-zero { stroke: var(--hero-text); stroke-opacity: 0.45; stroke-dasharray: 4 4; stroke-width: 1; }
/* The lowest point: a sun-coloured dot, ringed so it shows on the green. */
.today-low { position: absolute; width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px; border-radius: 50%; background: var(--sol); box-shadow: 0 0 0 2px var(--hero); }
.today-facts { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 16px; font-size: 14px; }
.today-facts > span { font-weight: 500; }
.today-facts b { font-weight: 700; }
.today-warn { display: inline-block; padding: 2px 10px; border-radius: var(--r-pill); background: var(--sol); color: var(--on-sol); font-weight: 700; }
#main.m-enter .today-wrap { animation: m-rise 0.4s var(--ease-out) both; }
#main.m-enter .today-low { animation: m-pop 0.35s var(--ease-spring) 0.95s both; }
.today-balance.m-changed { animation: m-lift 0.5s var(--ease-spring); }
@media (min-width: 760px) {
  .today-card { padding: 24px 28px 20px; border-radius: 28px; }
  .today-balance { font-size: 52px; }
  .today-line { height: 84px; }
}

/* Sign in with Google / Apple (providers.js): their buttons over the email
   form, then "or". Google draws its own button (in a frame, so its colours
   are Google's); Apple's is ours, in Apple's black or white. */
.sso { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-bottom: 14px; }
.sso[hidden] { display: none; }
.sso-google { min-height: 44px; display: flex; justify-content: center; width: 100%; color-scheme: light; }
.btn.sso-apple { display: flex; align-items: center; justify-content: center; gap: 8px; margin: 0; background: #000; color: #fff; border-color: #000; border-radius: var(--r-pill); font-weight: 600; }
:root[data-theme="dark"] .btn.sso-apple { background: #fff; color: #000; border-color: #fff; }
.sso .error { width: 100%; }
.or-line { display: flex; align-items: center; gap: 12px; width: 100%; color: var(--text-dim); font-size: 13px; margin-top: 4px; }
.or-line::before, .or-line::after { content: ""; flex: 1; height: 1px; background: var(--border); }
#sm-link { margin-bottom: 8px; }
