  :root {
    --bg: #f6f5f1; --panel: #ffffff; --line: #e9e7e0; --text: #22221f; --muted: #6a665c;
    --faint: #efede7; --good: #5d7f6c; --spend: #c47a58; --income: #9fb8a8; --warn: #b8893a;
    --radius: 20px; --shadow: 0 18px 50px rgba(34, 34, 31, .12);
  }
  @media (prefers-color-scheme: dark) {
    :root {
      --bg: #151614; --panel: #1d1e1b; --line: #2b2c28; --text: #ecebe6; --muted: #8f8d85;
      --faint: #262723; --good: #8fb39e; --spend: #d9936f; --income: #4f6b5b; --warn: #d6a95a;
      --shadow: 0 18px 50px rgba(0, 0, 0, .5);
    }
  }

  /* ---------- Themes (set on <html data-theme="…">; none = follow the system) ---------- */
  /* The colours used as text on a light background are darker than the ones used for fills and charts, to reach 4.5:1 (WCAG AA). Dark themes use the fill colours as they are */
  :root { --spend-t: var(--spend); --good-t: var(--good); --warn-t: var(--warn); }
  html[data-theme="light"] { --spend-t: #a64d2a; --good-t: #3f6b54; --warn-t: #86600f; }
  html[data-theme="sunrise"] { --spend-t: #c0301b; --good-t: #0a7650; --warn-t: #8a5800; }
  html[data-theme="rose"] { --spend-t: #b8335b; --good-t: #1b7552; --warn-t: #855808; }
  @media (prefers-color-scheme: light) { html:not([data-theme]) { --spend-t: #a64d2a; --good-t: #3f6b54; --warn-t: #86600f; } }
  :root { --accent: var(--text); --on-accent: var(--bg); color-scheme: light dark; --ease-glide: cubic-bezier(.32, .72, 0, 1); }
  html[data-theme="light"] { --bg: #f6f5f1; --panel: #ffffff; --line: #e9e7e0; --text: #22221f; --muted: #6a665c; --faint: #efede7;
    --good: #5d7f6c; --spend: #c47a58; --income: #9fb8a8; --warn: #b8893a; --shadow: 0 18px 50px rgba(34,34,31,.12); color-scheme: light; }
  html[data-theme="dark"] { --bg: #151614; --panel: #1d1e1b; --line: #2b2c28; --text: #ecebe6; --muted: #8f8d85; --faint: #262723;
    --good: #8fb39e; --spend: #d9936f; --income: #4f6b5b; --warn: #d6a95a; --shadow: 0 18px 50px rgba(0,0,0,.5); color-scheme: dark; }
  html[data-theme="midnight"] { --bg: #0a0f1e; --panel: #111830; --line: #1f2946; --text: #e8ecfa; --muted: #8b97bb; --faint: #19223f;
    --good: #34d399; --spend: #fb7185; --income: #38bdf8; --warn: #fbbf24; --accent: #7c5cff; --on-accent: #ffffff;
    --hero-grad: linear-gradient(90deg, #a78bfa, #38bdf8); --shadow: 0 18px 50px rgba(0,0,0,.6); color-scheme: dark; }
  html[data-theme="neon"] { --bg: #0d0221; --panel: #170838; --line: #2c1860; --text: #f6f2ff; --muted: #a79bd6; --faint: #221049;
    --good: #2ef2b4; --spend: #ff4d9d; --income: #00e0ff; --warn: #ffd166; --accent: #ff2e97; --on-accent: #ffffff;
    --hero-grad: linear-gradient(90deg, #ff2e97, #ffb347 55%, #00e0ff); --shadow: 0 18px 60px rgba(255,46,151,.15); color-scheme: dark; }
  html[data-theme="ocean"] { --bg: #041318; --panel: #0a2029; --line: #15343f; --text: #e2f5f8; --muted: #7ea5ae; --faint: #0f2b35;
    --good: #2dd4bf; --spend: #ff8a4c; --income: #3b9eff; --warn: #facc15; --accent: #14b8a6; --on-accent: #032126;
    --hero-grad: linear-gradient(90deg, #5eead4, #3b9eff); --shadow: 0 18px 50px rgba(0,0,0,.55); color-scheme: dark; }
  html[data-theme="sunrise"] { --bg: #fff7ee; --panel: #ffffff; --line: #f3e2cf; --text: #221a14; --muted: #725d4c; --faint: #fdecd9;
    --good: #0f9d6b; --spend: #e8452c; --income: #2f7ff0; --warn: #d98b00; --accent: #e8452c; --on-accent: #ffffff;
    --hero-grad: linear-gradient(90deg, #e8452c, #f59e0b); --shadow: 0 18px 50px rgba(232,69,44,.12); color-scheme: light; }
  html[data-theme="forest"] { --bg: #0e1612; --panel: #15211b; --line: #233429; --text: #e7f0e9; --muted: #8aa596; --faint: #1b2a22;
    --good: #6fcf97; --spend: #f2994a; --income: #56ccf2; --warn: #f2c94c; --accent: #6fcf97; --on-accent: #08130d;
    --hero-grad: linear-gradient(90deg, #6fcf97, #f2c94c); --shadow: 0 18px 50px rgba(0,0,0,.55); color-scheme: dark; }
  html[data-theme="nord"] { --bg: #242933; --panel: #2e3440; --line: #3b4252; --text: #eceff4; --muted: #a3acbd; --faint: #353c4a;
    --good: #a3be8c; --spend: #d08770; --income: #88c0d0; --warn: #ebcb8b; --accent: #88c0d0; --on-accent: #242933;
    --hero-grad: linear-gradient(90deg, #88c0d0, #b48ead); --shadow: 0 18px 50px rgba(0,0,0,.45); color-scheme: dark; }
  html[data-theme="rose"] { --bg: #fdf6f7; --panel: #ffffff; --line: #f1dfe3; --text: #2a1b20; --muted: #72525b; --faint: #f9e8ec;
    --good: #2f9e72; --spend: #d6456e; --income: #6b7fe0; --warn: #c98a1b; --accent: #d6456e; --on-accent: #ffffff;
    --hero-grad: linear-gradient(90deg, #d6456e, #9b6bdf); --shadow: 0 18px 50px rgba(214,69,110,.12); color-scheme: light; }
  html[data-theme="carbon"] { --bg: #161618; --panel: #1f1f23; --line: #2f2f36; --text: #f3f3f5; --muted: #9b9ba6; --faint: #2a2a30;
    --good: #39ff9f; --spend: #ff3d81; --income: #19e3ff; --warn: #ffe14d; --accent: #39ff9f; --on-accent: #0b0b0c;
    --hero-grad: linear-gradient(90deg, #39ff9f, #19e3ff); --shadow: 0 18px 50px rgba(0,0,0,.6); color-scheme: dark; }
  html[data-theme="carbon"] .fill { box-shadow: 0 0 10px color-mix(in srgb, var(--spend) 70%, transparent); }
  html[data-theme="carbon"] .fill.good { box-shadow: 0 0 10px color-mix(in srgb, var(--good) 70%, transparent); }
  html[data-theme="carbon"] .btn.primary { box-shadow: 0 0 16px color-mix(in srgb, var(--accent) 45%, transparent); }
  /* selected nav: a soft accent tint plus a short glowing pill, instead of an outlined box with a corner-clipped bar */
  html[data-theme="carbon"] .nav a.on { position: relative; background: color-mix(in srgb, var(--accent) 9%, transparent); box-shadow: none; }
  html[data-theme="carbon"] .nav a.on::before { content: ""; position: absolute; left: 0; top: 50%; width: 3px; height: 16px; margin-top: -8px; border-radius: 0 3px 3px 0;
    background: var(--accent); box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 60%, transparent); }
  html[data-theme="carbon"] .nav a.on svg { filter: drop-shadow(0 0 3px color-mix(in srgb, var(--accent) 50%, transparent)); }
  html[data-theme="carbon"] .chart rect { filter: drop-shadow(0 0 6px currentColor); }
  html[data-theme="carbon"] .pos, html[data-theme="carbon"] .down { text-shadow: 0 0 10px color-mix(in srgb, var(--good) 45%, transparent); }
  html[data-theme="carbon"] .neg, html[data-theme="carbon"] .up { text-shadow: 0 0 10px color-mix(in srgb, var(--spend) 45%, transparent); }
  html[data-theme="carbon"] .big { filter: drop-shadow(0 0 18px rgba(57,255,159,.25)); }
  /* glow on the underline only, inside the tab: an outer shadow is clipped by the scrolling tab row and leaves a sliver at the tab's right edge */
  html[data-theme="carbon"] .tabs a.on { box-shadow: 0 0 14px color-mix(in srgb, var(--accent) 45%, transparent); }
  html[data-theme="carbon"] .bulkbar { box-shadow: 0 0 20px color-mix(in srgb, var(--accent) 25%, transparent); }
  html[data-theme] .big.negative { --hero-grad: linear-gradient(90deg, var(--spend), var(--warn)); }
  html[data-theme="carbon"] .big.negative { filter: drop-shadow(0 0 18px rgba(255,61,129,.3)); }
  html[data-theme] .big { background: var(--hero-grad, none); -webkit-background-clip: text; background-clip: text; }
  html[data-theme="midnight"] .big, html[data-theme="neon"] .big, html[data-theme="ocean"] .big, html[data-theme="sunrise"] .big, html[data-theme="carbon"] .big, html[data-theme="glass"] .big,
  html[data-theme="forest"] .big, html[data-theme="nord"] .big, html[data-theme="rose"] .big { color: transparent; }
  .btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
  .nav a.on svg { color: var(--accent); }
  .seg button.on { color: var(--accent); }
  .tabs a.on { border-color: var(--accent); }
  input:focus, select:focus, textarea:focus { border-color: var(--accent); }
  .themes { display: flex; flex-wrap: wrap; gap: 8px; padding: 6px 12px 12px; }
  .settings .themes button.on { outline: 2px solid var(--accent); outline-offset: 2px; }
  .settings .themes button { display: inline-block; width: 34px; height: 34px; border-radius: 50%; border: 2px solid var(--line); cursor: pointer; padding: 0; }
  .foot .label { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); padding: 0 12px; }
  * { box-sizing: border-box; }
  html, body { margin: 0; background: var(--bg); color: var(--text); }
  body { font: 14.5px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif; -webkit-font-smoothing: antialiased; }
  a { color: inherit; }
  button, input, select, textarea { font: inherit; color: inherit; }
  .num { font-variant-numeric: tabular-nums; }
  [hidden] { display: none !important; }

  /* ---------- Shell ---------- */
  .app { display: grid; grid-template-columns: 224px 1fr; min-height: 100vh; }
  .side { position: sticky; top: 0; height: 100vh; padding: 28px 16px; border-right: 1px solid var(--line); display: flex; flex-direction: column; gap: 4px; overflow-y: auto; }
  .brand { font-weight: 650; font-size: 17px; letter-spacing: -.01em; padding: 0 12px 22px; }
  .nav a { display: flex; align-items: center; gap: 11px; padding: 8px 12px; border-radius: 10px; text-decoration: none; color: var(--muted); font-weight: 500; }
  .nav a:hover { background: var(--faint); color: var(--text); }
  .nav a.on { background: var(--panel); color: var(--text); box-shadow: 0 1px 0 var(--line), 0 0 0 1px var(--line); }
  .nav svg { width: 17px; height: 17px; flex: none; }
  .nav-h { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); padding: 16px 12px 5px; }
  .side .foot .nav a { display: flex; align-items: center; gap: 11px; padding: 8px 12px; border-radius: 10px; font-size: inherit; font-weight: 500; margin-bottom: 6px; }
  .side .foot .nav a.on { color: var(--text); }
  .list.menu .row { padding: 13px 0; align-items: center; }
  .list.menu .row .name { display: flex; gap: 12px; align-items: center; }
  .list.menu .ic { width: 20px; height: 20px; display: inline-flex; flex: none; }
  .list.menu .ic svg { width: 20px; height: 20px; flex: none; }     /* sized outright: Safari can give an unsized svg in a flex box no height */
  .list.menu + .list.menu { margin-top: 12px; border-top: 1px solid var(--line); }     /* Settings, set apart from the pages above it */
  .list.menu + .list.menu .row:first-child { padding-top: 13px; }
  .menu-h { margin: 22px 0 2px; }
  .side .spacer { flex: 1; }
  .side .foot a, .side .foot button { display: block; width: 100%; text-align: left; background: none; border: 0; padding: 7px 12px; color: var(--muted); font-size: 13px; text-decoration: none; cursor: pointer; border-radius: 8px; }
  .side .foot a:hover, .side .foot button:hover { background: var(--faint); color: var(--text); }
  main { padding: 34px 44px 72px; width: 100%; }

  /* ---------- Page head ---------- */
  .head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 28px; flex-wrap: wrap; }
  .head h1 { font-size: 26px; font-weight: 600; letter-spacing: -.02em; margin: 0; }
  .head .sub { color: var(--muted); margin: 2px 0 0; font-size: 13.5px; }
  .head .actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
  .month { display: flex; align-items: center; gap: 4px; }
  .month span { min-width: 132px; white-space: nowrap; text-align: center; font-weight: 500; }
  .month .today-gap { visibility: hidden; }
  /* the label in full ("September 2026"), or short ("Sep 2026") where Overview's header row is tight on a phone */
  .month i { font-style: normal; } .month .ms { display: none; }
  @media (max-width: 440px) { .head .actions:has(.dash-edit) .month :is(.ml, .ms) { display: none; } .head .actions:has(.dash-edit) .month .ms { display: inline; } }
  @media (max-width: 360px) { .head .actions:has(.dash-edit) .month { gap: 4px; } .head .actions:has(.dash-edit) .month .round { width: 30px; height: 30px; }
    .head .actions:has(.dash-edit) .month .btn { padding-inline: 9px; font-size: 13px; } }
  .month span small.cycle { display: block; font-size: 11px; font-weight: 400; color: var(--muted); line-height: 1.2; }
  /* Month label sliding to another month (morph.ts slideLabel): copies of the old and new label move (and fade) over
     the real one, which keeps its place hidden */
  .month > span { position: relative; }
  .month > span.msliding > :not(.mghost) { visibility: hidden; }
  .month .mghost { position: absolute; inset: 0; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; pointer-events: none; }
  .round { width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--line); background: var(--panel); cursor: pointer; font-size: 15px; line-height: 1; }
  .round:disabled { opacity: .35; cursor: default; }

  /* ---------- Buttons & inputs ---------- */
  .btn { display: inline-flex; align-items: center; gap: 6px; padding: 8px 15px; border-radius: 10px; border: 1px solid var(--line); background: var(--panel); cursor: pointer; font-weight: 500; text-decoration: none; white-space: nowrap; }
  .btn:hover { border-color: var(--muted); }
  .btn.primary:hover { opacity: .88; }
  .btn.danger { color: var(--spend); }
  .btn.small { padding: 5px 11px; font-size: 13px; border-radius: 8px; }
  .btn:disabled { opacity: .5; cursor: default; }
  .link { background: none; border: 0; padding: 0; color: var(--muted); cursor: pointer; text-decoration: underline; text-underline-offset: 3px; font-size: 13px; }
  .link:hover { color: var(--text); }
  label { display: block; font-size: 12.5px; color: var(--muted); margin-bottom: 14px; }
  label > input, label > select, label > textarea { display: block; width: 100%; margin-top: 5px; }
  input[type=text], input[type=number], input[type=date], input[type=password], input[type=search], select, textarea {
    padding: 9px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); font-size: 14.5px; outline: none; min-width: 0; max-width: 100%; }
  input:focus, select:focus, textarea:focus { border-color: var(--muted); }
  textarea { resize: vertical; }
  label.check { display: flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--text); }
  label.check input { width: auto; margin: 0; }
  .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
  .grid2 > * { min-width: 0; }
  .seg { display: inline-flex; flex-wrap: wrap; max-width: 100%; background: var(--faint); border-radius: 12px; padding: 3px; margin-bottom: 18px; }
  .seg button { border: 0; background: none; padding: 7px 16px; border-radius: 9px; cursor: pointer; color: var(--muted); font-weight: 500; }
  .seg button.on { background: var(--panel); color: var(--text); box-shadow: 0 1px 3px rgba(0,0,0,.08); }
  .tabs { display: flex; gap: 22px; border-bottom: 1px solid var(--line); margin-bottom: 20px; overflow-x: auto; }
  .tabs a { padding: 0 0 11px; text-decoration: none; color: var(--muted); font-weight: 500; border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; }
  .tabs a.on { color: var(--text); border-color: var(--accent); }
  .toolbar { display: flex; gap: 12px; align-items: center; justify-content: space-between; margin-bottom: 16px; flex-wrap: wrap; }
  .cleanup-list { display: flex; flex-direction: column; gap: 20px; max-width: 720px; }
  .cleanup .row.check { justify-content: flex-start; align-items: center; gap: 10px; margin: 0; cursor: pointer; }
  .cleanup .btn { margin-top: 14px; }
  .report-empty { margin: -4px 4px 20px; }
  /* Reports: what it went on (categories, budgets, tags) on the left, who it went to or came from on the right */
  .report-cols:empty { display: none; }
  .search { position: relative; flex: 1; max-width: 420px; }
  .search input { width: 100%; padding-left: 36px; }
  .search svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: var(--muted); pointer-events: none; }

  /* ---------- Panels ---------- */
  .panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 26px; }
  .panel h2 { font-size: 15px; font-weight: 600; margin: 0 0 3px; }
  .panel .lead { color: var(--muted); font-size: 13px; margin: 0 0 20px; }
  .panel.flush { padding: 0; overflow: hidden; }
  .panel-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
  .eyebrow { font-size: 11.5px; text-transform: uppercase; letter-spacing: .09em; color: var(--muted); margin-bottom: 5px; }
  .grid { display: grid; grid-template-columns: 1.25fr 1fr; gap: 20px; }
  .stack { display: grid; gap: 20px; align-content: start; }
  .full { grid-column: 1 / -1; }
  /* Overview cards: flow into as many ~380px columns as fit, balanced automatically */
  .cards { columns: 3 380px; column-gap: 20px; margin-bottom: 20px; }
  .cards > .panel { break-inside: avoid; display: inline-block; width: 100%; margin-bottom: 20px; }
  /* Justified card layouts (features/flow.ts): as many columns as fit, sharing the width so there's no gap at the right,
     each card under the shortest column. Until then (and on a phone) a plain stack. */
  .flow { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; align-items: start; margin-bottom: 20px; }
  .flow > * { min-width: 0; margin: 0; }
  .flow.on { grid-template-columns: repeat(var(--fc), minmax(0, 1fr)); grid-auto-rows: 1px; row-gap: 0; margin-bottom: 0; }
  .flow > .full { grid-column: 1 / -1; }
  /* Settings: named groups of rows in one column, with rarely used forms folded away */
  .settings { max-width: 720px; }
  /* One category at a time (settings.ts). Wide screens: the title and the list of categories on the left, staying in
     place (it sticks at main's top padding, where it starts, so it never moves). Phones: the list is the first screen. */
  .set-nav { display: none; }
  @media (min-width: 1100px) {
    .set-layout { display: grid; grid-template-columns: 184px minmax(0, 720px); gap: 36px; align-items: start; }
    .set-side { position: sticky; top: 34px; }
    html.compact .set-side { top: 24px; }
    .set-side .head { margin-bottom: 20px; padding-left: 12px; }
    .set-nav { display: flex; flex-direction: column; gap: 2px; }
    .set-nav a { padding: 8px 12px; border-radius: 9px; color: var(--muted); font-size: 14px; font-weight: 500; text-decoration: none; transition: background .12s, color .12s; }
    .set-nav a:hover { background: var(--faint); color: var(--text); }
    .set-nav a.on { background: var(--panel); color: var(--text); box-shadow: 0 0 0 1px var(--line); }
  }
  @media (max-width: 1099px) {
    .settings > .set-group > .set-h { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }   /* the page title already names it */
  }
  .set-menu a.set-row { color: var(--text); text-decoration: none; }
  .set-menu .set-label { display: flex; flex-direction: column; gap: 1px; }
  .set-menu .chev { width: 16px; height: 16px; flex: none; color: var(--muted); }
  .set-back { display: inline-flex; align-items: center; gap: 2px; margin: 0 0 6px -10px; padding: 4px 8px 4px 2px; border-radius: 8px; color: var(--accent); font-size: 15px; font-weight: 500; text-decoration: none; }
  .set-back svg { width: 20px; height: 20px; flex: none; }
  .set-group { margin-bottom: 28px; }
  .set-h { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); margin: 0 0 8px 4px; }
  .set-panel { padding: 0 20px; }
  .set-foot { font-size: 12.5px; color: var(--muted); margin: 8px 4px 0; line-height: 1.45; }
  .set-row, .set-more > summary { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 13px 0; min-height: 50px; box-sizing: border-box; }
  .set-panel > * + * { border-top: 1px solid var(--line); }
  .set-label { min-width: 0; flex: 1 1 auto; }
  .set-label .sub-s { margin-top: 2px; line-height: 1.4; }
  .set-ctl { display: flex; align-items: center; gap: 10px; flex: none; max-width: 60%; color: var(--muted); }
  .set-ctl select { width: auto; min-width: 120px; margin: 0; }
  .set-ctl .themes { padding: 0; justify-content: flex-end; }
  .set-ctl .themes button { width: 26px; height: 26px; }
  .set-code { overflow-wrap: anywhere; font-size: 12.5px; }
  .set-key input { width: 130px; min-width: 0; margin: 0; font-family: ui-monospace, monospace; font-size: 13px; }
  .set-more > summary { cursor: pointer; list-style: none; }
  .set-more > summary::-webkit-details-marker { display: none; }
  .set-more > summary::after { content: ''; flex: none; width: 7px; height: 7px; margin: 0 4px 0 -6px; border: solid var(--muted); border-width: 0 1.5px 1.5px 0; transform: rotate(-45deg); transition: transform .15s; }
  .set-more[open] > summary::after { transform: rotate(45deg); }
  /* Folded sections open and close by sliding to their height, where the browser can animate to a natural height
     (Chrome and Edge); elsewhere they open at once, as before */
  @supports (interpolate-size: allow-keywords) { @media (prefers-reduced-motion: no-preference) {
    :root { interpolate-size: allow-keywords; }
    :is(.set-more, .rulegroup)::details-content { height: 0; overflow: clip; transition: height .3s var(--ease-glide), content-visibility .3s allow-discrete; }
    :is(.set-more, .rulegroup)[open]::details-content { height: auto; }
  } }
  .set-more > summary > :first-child { flex: 1; }
  .set-body { padding: 0 0 16px; }
  .set-body > p.sub-s { line-height: 1.45; }
  /* Customize: colour wells and sliders in the Appearance group */
  .set-ctl input[type=color] { -webkit-appearance: none; appearance: none; width: 44px; height: 30px; margin: 0; padding: 2px; border: 1px solid var(--line); border-radius: 9px; background: none; cursor: pointer; }
  .set-ctl input[type=color]::-webkit-color-swatch-wrapper { padding: 0; }
  .set-ctl input[type=color]::-webkit-color-swatch { border: 0; border-radius: 6px; }
  .set-ctl input[type=color]::-moz-color-swatch { border: 0; border-radius: 6px; }
  .set-ctl input[type=range] { width: 150px; margin: 0; accent-color: var(--accent); }
  .set-ctl output { min-width: 40px; text-align: right; font-variant-numeric: tabular-nums; font-size: 13px; }
  .theme-custom > * + * { border-top: 1px solid var(--line); }
  .set-inline { display: flex; gap: 8px; } .set-inline select { flex: 1; margin: 0; }
  input.switch { -webkit-appearance: none; appearance: none; flex: none; width: 46px; height: 28px; margin: 0; padding: 0; border: 0; border-radius: 99px; background: var(--line); position: relative; cursor: pointer; transition: background .18s; }
  input.switch::after { content: ''; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.25); transition: transform .18s; }
  input.switch:checked { background: var(--accent); }
  input.switch:checked::after { transform: translateX(18px); }
  input.switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  @media (max-width: 520px) {
    .set-panel { padding: 0 16px; }
    .set-row:has(.themes), .set-row:has(.set-key) { flex-wrap: wrap; }
    .set-row:has(.themes) .set-ctl, .set-row:has(.set-key) .set-ctl { max-width: none; width: 100%; }
    .set-ctl .themes { justify-content: flex-start; }
    .set-key input { flex: 1; }
    .set-ctl input[type=range] { width: 110px; }
  }
  .wide { display: grid; grid-template-columns: 1fr; gap: 20px; }
  @media (min-width: 1700px) { .wide { grid-template-columns: 1fr 1.4fr; } }
  .hero { display: grid; grid-template-columns: 1.3fr 2fr; gap: 32px; align-items: end; margin-bottom: 40px; }
  .big { font-size: 52px; font-weight: 300; letter-spacing: -.03em; line-height: 1; }
  .stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
  .stat .val { font-size: 25px; letter-spacing: -.02em; }
  .stat .sub, .sub-s { font-size: 12.5px; color: var(--muted); }
  .kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 16px; margin-bottom: 22px; }
  /* two to a row on a phone: an odd one out at the end takes the whole row instead of sitting alone on the left */
  /* On phones the KPI cards sit two to a row, compact like the Overview's stat cards, so the page's table or chart stays near the top */
  @media (max-width: 573px) { .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-bottom: 16px; } .kpis > .kpi:last-child:nth-child(odd) { grid-column: 1 / -1; }
    .kpi { padding: 12px 14px; } .kpi .val { font-size: 19px; } .kpi .sub-s { overflow-wrap: anywhere; } }
  .kpi { background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 16px 18px; }
  .kpi .val { font-size: 22px; letter-spacing: -.02em; }
  .total { font-size: 28px; font-weight: 300; letter-spacing: -.02em; margin-bottom: 14px; }
  .up { color: var(--spend-t); } .down { color: var(--good-t); }
  .neg { color: var(--spend-t); } .pos { color: var(--good-t); } .neutral { color: var(--muted); }

  .row { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
  .row .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .list .row { padding: 10px 0; border-top: 1px solid var(--line); }
  .list .row:first-child { border-top: none; padding-top: 0; }
  .w[data-w="bills"] .list .row:not(.more) { align-items: center; }
  .bill-end { display: flex; align-items: center; gap: 10px; flex: none; }
  .bill-end .btn { flex: none; white-space: nowrap; }
  .list a.row { text-decoration: none; }
  .list a.row:hover .name { text-decoration: underline; text-underline-offset: 3px; }
  .track { position: relative; height: 6px; background: var(--faint); border-radius: 99px; margin: 7px 0 4px; }
  .fill { position: absolute; inset: 0 auto 0 0; background: var(--spend); border-radius: 99px; opacity: .85; max-width: 100%; }
  .fill.good { background: var(--good); } .fill.over { background: var(--spend); opacity: 1; }
  .fill.warn { background: var(--warn, #d9a13b); } .track.pace .today { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; background: var(--text); opacity: .45; border-radius: 2px; }
  .panel.drafts { margin-bottom: 16px; } .panel.drafts .row .name { font: inherit; color: inherit; }
  .tick { position: absolute; top: -4px; width: 2px; height: 14px; background: var(--text); opacity: .35; border-radius: 1px; }
  .cat { margin-bottom: 16px; } .cat:last-child { margin-bottom: 0; }
  .cat .sub { font-size: 12px; color: var(--muted); }
  .legend { display: flex; gap: 16px; font-size: 12px; color: var(--muted); margin-top: 18px; }
  .legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }
  .chart svg { width: 100%; height: auto; display: block; }
  .chart text { fill: var(--muted); font-size: 12px; }
  .empty { color: var(--muted); font-size: 14px; padding: 6px 0; }
  .center-empty { text-align: center; color: var(--muted); padding: 48px 20px; }
  .loading { color: var(--muted); padding: 90px 0; text-align: center; }
  .error { background: var(--faint); border-radius: 14px; padding: 16px 18px; color: var(--text); }
  .chip { display: inline-block; font-size: 11.5px; padding: 1px 8px; border-radius: 99px; background: var(--faint); color: var(--muted); margin-right: 4px; white-space: nowrap; }
  .chip.good { color: var(--good-t); } .chip.warn { color: var(--warn-t); } .chip.bad { color: var(--spend-t); }
  .saved-reports { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 0 0 16px; } .saved-reports .chip { font-size: 13px; padding: 3px 4px 3px 10px; display: inline-flex; gap: 4px; align-items: center; margin: 0; } .saved-reports .chip a { color: inherit; text-decoration: none; } .saved-reports .chip.on { color: var(--text); box-shadow: inset 0 0 0 1px var(--line); } .saved-reports .chip button { border: 0; background: none; color: inherit; cursor: pointer; font-size: 14px; padding: 0 4px; } .save-report { display: inline-flex; gap: 6px; } .save-report input { width: 190px; padding: 4px 10px; font-size: 13px; }
  .tagcloud { display: flex; flex-wrap: wrap; gap: 8px; }
  .tagcloud .chip { font-size: 13px; padding: 4px 6px 4px 12px; display: inline-flex; gap: 6px; align-items: center; }
  .tagcloud .chip a { text-decoration: none; color: var(--text); }
  .tagcloud .chip button { border: 0; background: none; cursor: pointer; color: var(--muted); padding: 0 4px; font-size: 14px; }

  /* ---------- Tables ---------- */
  table.tbl { width: 100%; border-collapse: collapse; }
  .tbl th { text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); font-weight: 500; padding: 14px 16px 10px; border-bottom: 1px solid var(--line); }
  .tbl td { padding: 12px 16px; border-bottom: 1px solid var(--line); vertical-align: middle; }
  .tbl tr:last-child td { border-bottom: none; }
  .tbl tr.click { cursor: pointer; }
  .tbl tr.click:hover td { background: var(--faint); }
  .tbl .r { text-align: right; }
  .tbl .date { color: var(--muted); white-space: nowrap; width: 80px; }
  .tbl .desc { font-weight: 500; }
  .tbl .meta { margin-top: 3px; }
  .tbl .fromto { color: var(--muted); font-size: 13px; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tbl .amt { text-align: right; white-space: nowrap; font-weight: 500; font-variant-numeric: tabular-nums; }
  .tbl .act { text-align: right; white-space: nowrap; width: 1%; }
  .tbl input.inline { width: 120px; padding: 6px 10px; text-align: right; }
  .tbl .sel { width: 36px; padding-right: 0; }
  .tbl .sel input { width: 16px; height: 16px; cursor: pointer; accent-color: var(--accent); }
  .bulkbar { position: sticky; top: calc(10px + env(safe-area-inset-top, 0px)); z-index: 6; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; background: var(--panel); border: 1px solid var(--accent);
    border-radius: 14px; padding: 10px 14px; margin-bottom: 14px; box-shadow: var(--shadow); }
  .bulkbar strong { margin-right: 6px; }
  .bulkbar select, .bulkbar input { padding: 6px 10px; font-size: 13.5px; }
  .extra { background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 16px 18px; margin-top: 14px; }
  .extra h3 { font-size: 14px; margin: 0 0 10px; }
  .rulegroup summary { cursor: pointer; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 16px 20px; }
  .rulegroup summary::-webkit-details-marker { display: none; }
  .rulegroup[open] summary { border-bottom: 1px solid var(--line); }
  /* on a phone the name and description get the full width; the buttons go on a row under them */
  @media (max-width: 600px) { .rulegroup summary { flex-wrap: wrap; row-gap: 10px; } .rulegroup summary > :first-child { flex: 1 1 100%; } }
  .editor-row { display: grid; grid-template-columns: 1fr 1fr auto; gap: 8px; align-items: center; margin-bottom: 8px; }
  .editor-row .x { border: 0; background: none; cursor: pointer; color: var(--muted); font-size: 18px; }
  .pager { display: flex; justify-content: space-between; align-items: center; padding: 14px 16px; border-top: 1px solid var(--line); color: var(--muted); font-size: 13px; }

  /* ---------- Drawer, dialog, toast ---------- */
  .scrim { position: fixed; inset: 0; background: rgba(20, 20, 18, .28); opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .2s, visibility 0s .2s; z-index: 20; }
  /* hidden, not just see-through, while closed: Safari 26 tints the status bar with any fixed element's colour, even an invisible one */
  .scrim.open { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity .2s; }
  .drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(520px, 100vw); background: var(--bg); box-shadow: var(--shadow); transform: translateX(105%); transition: transform .24s ease; z-index: 21; display: flex; flex-direction: column; }
  .drawer.open { transform: none; }
  .drawer header { display: flex; justify-content: space-between; align-items: center; padding: 22px 26px 8px; }
  .drawer header h2 { margin: 0; font-size: 18px; font-weight: 600; }
  .drawer .body { padding: 14px 26px 26px; overflow-y: auto; flex: 1; }
  .drawer footer { display: flex; gap: 10px; justify-content: flex-end; padding: 16px 26px; border-top: 1px solid var(--line); background: var(--bg); }
  .drawer footer .grow { flex: 1; }
  .split { background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 18px 18px 4px; margin-bottom: 14px; }
  .split .split-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; font-weight: 600; font-size: 13px; }
  .form-err { background: var(--faint); color: var(--spend); border-radius: 10px; padding: 10px 12px; margin-bottom: 14px; font-size: 13.5px; }
  .hint { font-size: 12px; color: var(--muted); margin: -8px 0 14px; }
  dialog { border: 1px solid var(--line); border-radius: 18px; padding: 24px; background: var(--panel); color: var(--text); max-width: 400px; box-shadow: var(--shadow); }
  dialog::backdrop { background: rgba(20, 20, 18, .3); }
  /* Every dialog (a Delete? confirm too) arrives like What's new: a soft rise with the dim fading in behind it */
  dialog:not(.whatsnew)[open] { animation: wn-in .26s var(--ease-glide); }
  dialog:not(.whatsnew)[open]::backdrop { animation: wn-fade .2s ease; }
  dialog h3 { margin: 0 0 8px; font-size: 16px; }
  dialog p { margin: 0 0 20px; color: var(--muted); }
  dialog .btns { display: flex; justify-content: flex-end; gap: 10px; }
  /* What's new (features/whatsnew.ts): the release's highlights, once after an update */
  dialog.whatsnew { width: min(440px, calc(100vw - 32px)); max-width: none; max-height: calc(100dvh - 48px); padding: 0; overflow: hidden; flex-direction: column; }
  dialog.whatsnew[open] { display: flex; animation: wn-in .32s cubic-bezier(.2, .9, .3, 1.15); }
  dialog.whatsnew[open]::backdrop { animation: wn-fade .25s ease; }
  @keyframes wn-in { from { opacity: 0; transform: translateY(14px) scale(.97); } }
  @keyframes wn-fade { from { opacity: 0; } }
  .whatsnew header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 22px 24px 0; }
  .whatsnew h3 { margin: 2px 0 0; font-size: 22px; font-weight: 650; letter-spacing: -.01em; }
  .whatsnew .wn-x { width: 32px; height: 32px; color: var(--muted); }
  .whatsnew .wn-x svg { display: block; width: 14px; height: 14px; }
  .whatsnew .wn-lead { margin: 8px 24px 0; font-size: 14px; line-height: 1.45; }
  .whatsnew .wn-list { list-style: none; margin: 0; padding: 18px 24px 4px; display: grid; gap: 16px; overflow-y: auto; overscroll-behavior: contain; }
  .whatsnew .wn-list li { display: flex; gap: 14px; align-items: flex-start; }
  .whatsnew .wn-ic { flex: none; width: 38px; height: 38px; border-radius: 12px; display: grid; place-items: center; background: var(--faint); color: var(--accent); }
  .whatsnew .wn-ic svg { display: block; width: 20px; height: 20px; }
  .whatsnew .wn-list li > div > strong { display: block; font-size: 14.5px; font-weight: 600; line-height: 1.3; }
  .whatsnew .wn-list li > div > span { display: block; margin-top: 2px; font-size: 13.5px; line-height: 1.45; color: var(--muted); }
  .whatsnew .btns { padding: 18px 24px 22px; flex-wrap: wrap; }
  .whatsnew .btns .btn { justify-content: center; }
  @media (max-width: 520px) {
    .whatsnew .btns .btn { flex: 1; padding: 11px 15px; }
  }
  @media (prefers-reduced-motion: reduce) { dialog[open], dialog[open]::backdrop { animation: none; } }
  .toast { position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 20px); background: var(--text); color: var(--bg); padding: 10px 18px; border-radius: 99px; font-size: 13.5px; opacity: 0; transition: opacity .2s ease, transform .32s var(--ease-glide); z-index: 40; pointer-events: none; max-width: 90vw; }
  .toast.show { opacity: 1; transform: translate(-50%, 0); }

  /* ---------- Login ---------- */
  .login { min-height: 100vh; display: grid; place-items: center; padding: calc(20px + env(safe-area-inset-top, 0px)) 20px calc(20px + env(safe-area-inset-bottom, 0px)); }
  .login form { width: min(360px, 100%); background: var(--panel); border: 1px solid var(--line); border-radius: 24px; padding: 34px 30px; }
  .login h1 { margin: 0 0 4px; font-size: 22px; font-weight: 600; }
  .login p { margin: 0 0 24px; color: var(--muted); font-size: 13.5px; }
  .login .btn { width: 100%; justify-content: center; padding: 10px; margin-top: 6px; }

  /* ---------- Motion & snappiness ---------- */
  .btn, .round, .nav a, .tbl tr.click td, .seg button { transition: background .12s, border-color .12s, transform .08s, opacity .12s; }
  .btn:active, .round:active, .fab:active { transform: scale(.97); }
  #view.enter > * { animation: fadeUp .22s ease both; }
  #view.enter.anim:not(.calm) > * { animation-name: fadeIn; }     /* the cards inside rise on their own (.anim), so the page only fades */
  #view.enter :is(.cards, .flow) > * { animation: fadeUp .28s ease both; }
  #view.enter :is(.cards, .flow) > :nth-child(2) { animation-delay: .03s; } #view.enter :is(.cards, .flow) > :nth-child(3) { animation-delay: .06s; }
  #view.enter :is(.cards, .flow) > :nth-child(4) { animation-delay: .09s; } #view.enter :is(.cards, .flow) > :nth-child(n+5) { animation-delay: .12s; }
  @keyframes fadeUp { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
  .sk { background: linear-gradient(90deg, var(--faint) 25%, var(--line) 50%, var(--faint) 75%); background-size: 200% 100%; border-radius: 8px; animation: shimmer 1.1s infinite linear; }
  @keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
  .drawer { transition: transform .2s cubic-bezier(.2,.8,.2,1); }
  @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }
  .tbl, :is(.cards, .flow) > .panel { contain: layout paint; }
  .attention { border-color: var(--warn); }
  .attention .row .name { white-space: normal; }
  /* in a narrow tile the action drops under the text instead of squeezing it to a word per line */
  .attention .row { flex-wrap: wrap; row-gap: 4px; } .attention .row .name { flex: 1 1 9em; }
  .offline { padding-top: calc(6px + env(safe-area-inset-top, 0px) + var(--edge)) !important; position: fixed; top: 0; left: 0; right: 0; z-index: 70; background: var(--warn); color: #111; text-align: center; font-size: 13px; padding: 6px 12px; }
  /* ---------- Floating add button ---------- */
  .fab { position: fixed; right: 28px; bottom: 28px; width: 56px; height: 56px; border-radius: 50%; border: 0; background: var(--accent); color: var(--on-accent);
    font-size: 28px; line-height: 1; cursor: pointer; box-shadow: var(--shadow); z-index: 15; }
  .fab:hover { filter: brightness(1.08); }
  /* Covering some text: slid mostly off the right edge, with a lip left to grab (features/fabdodge.ts). translate is its own property, so the phone's scale pop-in is untouched */
  .fab { transition: translate .3s var(--ease-glide, ease), opacity .2s; }
  .fab.peek { translate: 40px 0; opacity: .9; }
  /* ---------- Undo toast ---------- */
  .undo { position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%); background: var(--text); color: var(--bg); border-radius: 99px; padding: 8px 8px 8px 18px;
    display: flex; gap: 14px; align-items: center; z-index: 41; font-size: 13.5px; line-height: 1.3; box-shadow: var(--shadow);
    width: max-content; max-width: min(520px, calc(100vw - 28px)); box-sizing: border-box; }
  .undo > span:first-child { flex: 1; min-width: 0; }
  .undo button { flex: none; background: var(--bg); color: var(--text); border: 0; border-radius: 99px; padding: 6px 14px; font-weight: 600; cursor: pointer; }
  .undo .bar { position: absolute; left: 18px; right: 18px; bottom: 3px; height: 2px; background: var(--bg); opacity: .4; transform-origin: left; animation: drain 5s linear forwards; border-radius: 2px; }
  @keyframes drain { to { transform: scaleX(0); } }
  /* ---------- Chart tooltip ---------- */
  .tip { position: fixed; z-index: 45; pointer-events: none; background: var(--text); color: var(--bg); padding: 8px 12px; border-radius: 10px; font-size: 12.5px; line-height: 1.5; box-shadow: var(--shadow); }
  /* Hover band: drawn behind the bars and only over the plot area, so it stops at the axis and leaves the month label alone */
  .flowchart .band { fill: var(--text); fill-opacity: 0; pointer-events: none; transition: fill-opacity .12s; }
  .flowchart .col:hover .band { fill-opacity: .06; }
  .flowchart .col:hover text { fill: var(--text); }
  .chart rect:is(.hit, .band) { filter: none !important; }
  .chart .pt { cursor: default; }
  .chart .pt .dot { transition: r .1s; }
  .chart .pt:hover .dot { r: 6.5; }
  .tip { white-space: pre-line; max-width: 320px; }
  /* ---------- Charts: smooth lines, donuts, progress rings ---------- */
  .lines { display: block; width: 100%; height: auto; overflow: visible; }
  .lines .grid line { stroke: var(--line); stroke-dasharray: 3 5; }
  .lines text { font-size: 11px; fill: var(--muted); }
  .chart .lines rect { filter: none !important; }
  .lines + .legend { margin-top: 10px; }
  .report-top { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); gap: 20px; margin-bottom: 20px; align-items: stretch; }
  .report-top.one { grid-template-columns: minmax(0, 1fr); }
  .report-top > .panel { margin: 0; }
  @media (max-width: 900px) { .report-top { grid-template-columns: minmax(0, 1fr); } }
  .donut, .ring { position: relative; display: grid; place-items: center; }
  .donut { width: min(180px, 64%); margin: 4px auto 18px; }
  .donut svg, .ring svg { width: 100%; height: auto; transform: rotate(-90deg); overflow: visible; }
  :is(.donut, .ring) circle { fill: none; }
  .donut .base { stroke: var(--faint); stroke-width: 14; }
  .donut .seg { stroke-width: 14; }
  .donut .seg { transition: stroke-width .15s, opacity .15s; cursor: pointer; }
  .donut .seg:hover, .donut.hot .seg.on { stroke-width: 18; }
  .donut.hot .seg:not(.on) { opacity: .35; }
  .share .part { transition: opacity .15s; }
  .share:has(.donut.hot) .part:not(.hot) { opacity: .45; }
  :is(.donut, .ring) .mid { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; line-height: 1.2; pointer-events: none; }
  .donut .mid strong { font-size: 22px; font-weight: 650; letter-spacing: -.01em; }
  .donut .mid .eyebrow { margin: 0 0 2px; }
  .parts { display: flex; flex-direction: column; gap: 10px; }
  .part { display: grid; grid-template-columns: 10px minmax(0, 8fr) minmax(0, 5fr) 38px; gap: 10px; align-items: center; font-size: 13.5px; }
  .part > i { width: 9px; height: 9px; border-radius: 50%; }
  .part .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .part .name a { color: inherit; text-decoration: none; }
  .part .name .sub-s { display: block; overflow: hidden; text-overflow: ellipsis; margin-top: 1px; }
  .part .track { margin: 0; height: 5px; }
  .part .track .fill { opacity: 1; }
  .part .num { text-align: right; color: var(--muted); font-size: 12.5px; }
  /* a ring chart in a page card (Categories, Reports, Recap): once the card is wide enough the ring sits on the left
     and its list on the right, the pair centred, so the card stays short and the bars stay close to their names.
     The Overview tiles have their own rules (.cards .share). */
  .panel:has(> .share):not(.cards .panel) { container-type: inline-size; }
  @container (min-width: 520px) {
    .panel:not(.cards .panel) > .share { display: grid; grid-template-columns: clamp(150px, 30%, 220px) minmax(0, 560px); justify-content: center; align-items: center; gap: clamp(28px, 6cqi, 72px); margin-top: 6px; }
    .panel:not(.cards .panel) > .share .donut { width: 100%; margin: 0; }
    .panel:not(.cards .panel) > .share .part { grid-template-columns: 10px minmax(0, 220px) minmax(0, 1fr) 38px; }
  }
  /* Change bars (charts.ts changeBars): out from a middle line, less to the left, more to the right */
  .chg { display: flex; flex-direction: column; gap: 11px; }
  .chg-row { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 8fr) 76px; gap: 12px; align-items: center; font-size: 13.5px; }
  .chg-row .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .chg-row .name a { color: inherit; text-decoration: none; }
  .chg-row .num { text-align: right; font-size: 12.5px; }
  .chg-bar { position: relative; height: 8px; border-radius: 99px; background: var(--faint); }
  .chg-bar::before { content: ''; position: absolute; left: 50%; top: -3px; bottom: -3px; width: 1px; background: var(--muted); opacity: .5; }
  .chg-bar i { position: absolute; top: 0; bottom: 0; border-radius: 99px; }
  .chg-bar i.chg-up { left: 50%; background: var(--spend); } .chg-bar i.chg-dn { right: 50%; background: var(--good); }
  .chg + .legend { margin-top: 16px; }
  /* Budgets' ring chart: bigger rings, centred in the card however many there are */
  .brings.page-rings { display: flex; flex-wrap: wrap; justify-content: center; gap: 22px 28px; padding: 6px 0 4px; }
  .page-rings .bring { width: 118px; } .page-rings .bring .ring { width: 88px; } .page-rings .bring .ring .mid strong { font-size: 16px; }
  @media (max-width: 600px) { .page-rings .bring { width: calc(50% - 14px); } .page-rings .bring .ring { width: 72px; } }
  /* Customize a page (pageopts.ts): Settings' rows in the sheet */
  .page-opts .set-h { margin: 18px 0 8px 4px; } .page-opts .set-h:first-child { margin-top: 4px; }
  .page-opts > .sub-s { margin: 14px 2px 0; }
  .ring { width: 92px; flex: none; }
  .ring .base { stroke: var(--faint); stroke-width: 10; }
  .ring .seg { stroke: var(--accent); stroke-width: 10; stroke-linecap: round; }
  .ring.good .seg { stroke: var(--good); }
  .ring .mid strong { font-size: 19px; font-weight: 650; }
  .ring .mid span { font-size: 11px; color: var(--muted); }
  .goal { display: flex; align-items: center; gap: 18px; margin: 4px 0 16px; }
  .goal.goal-bar { display: block; }
  .goal-nums { display: flex; align-items: baseline; gap: 8px; } .goal-nums .total { margin: 0; }
  .goal-nums .goal-pct { margin-left: auto; color: var(--muted); font-size: 13px; }
  .goal-bar .track { height: 10px; margin: 12px 0 10px; }
  /* ---------- Page entrance (polish.ts playIn adds .anim; a card that arrives late gets .pop) ---------- */
  @media (prefers-reduced-motion: no-preference) {
    #view.anim:not(.calm) :is(.hero, .kpi, .panel, .insight) { animation: riseIn .5s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(var(--i, 0) * 45ms); }
    :is(#view.anim, .pop) .draw { stroke-dasharray: 1; animation: drawIn 1.1s .2s cubic-bezier(.45, 0, .2, 1) both; }
    :is(#view.anim, .pop) .draw.late { animation-delay: .4s; }
    :is(#view.anim, .pop) .area { animation: fadeIn .9s .55s ease both; }
    :is(#view.anim, .pop) :is(.flowchart, .barchart, .sbars, .pairbars) .bar { transform-box: fill-box; transform-origin: 50% 100%; animation: growUp .7s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(.15s + var(--j, 0) * 60ms); }
    :is(#view.anim, .pop) .fill { transform-origin: left; animation: growRight .8s .2s cubic-bezier(.2, .8, .2, 1) both; }
    :is(#view.anim, .pop) :is(.donut, .ring) .seg { animation: sweepIn .6s cubic-bezier(.3, 0, .2, 1) both; animation-delay: calc(.25s + var(--at, 0) * .9s); }
    :is(#view.anim, .pop) .chart .pt .dot { animation: fadeIn .4s 1.1s both; }
  }
  /* ---------- Morph (core/morph.ts): a redraw in the same layout moves from the old values to the new ---------- */
  /* An amount rolling like an odometer: each changed digit is a column one line tall, its strip of digits moved by
     JS in whole pixels. vertical-align: top + the line's own height keeps every digit on the text's baseline. */
  .odo { display: inline-block; overflow: hidden; vertical-align: top; }
  .odo-s { display: block; white-space: pre; }
  /* The window reaches a little above and below the line (negative margins, so the line doesn't grow) and its top
     and bottom fade out over --odo-f, which JS deepens while the digits spin and eases back as they settle: digits
     roll through a soft edge, like a speedometer drum, never a hard cut. At rest the fade stays outside the line. */
  .odo { margin-block: calc(-1 * var(--odo-p, 0px));
    -webkit-mask-image: linear-gradient(transparent, #000 var(--odo-f, 0px), #000 calc(100% - var(--odo-f, 0px)), transparent);
    mask-image: linear-gradient(transparent, #000 var(--odo-f, 0px), #000 calc(100% - var(--odo-f, 0px)), transparent); }
  /* Waiting for another month's numbers: the page stays and dims, then redraws in place */
  /* Another month taking a moment: only the month's figures dim (amounts, charts, bars, lists). Titles, headings
     and card frames stay as they are, and the month picker already shows the new month */
  #view.stale > :not(.head) :is(.num, svg, .fill, .dbars, .hm-cell, .tbl tbody, .list, .empty, .center-empty) { opacity: .5; transition: opacity .25s ease; }
  /* ---------- Overview: Add cards (Customize), the newer charts ---------- */
  .dash-add { flex-basis: 100%; margin-top: 6px; }
  .dash-add .eyebrow { margin-bottom: 8px; }
  .add-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 8px; }
  .add-card { display: flex; gap: 12px; align-items: center; text-align: left; padding: 10px 14px 10px 10px; border: 1px dashed color-mix(in srgb, var(--text) 22%, var(--line));
    border-radius: 16px; background: transparent; color: var(--text); font: inherit; cursor: pointer; }
  .add-card:hover { border-style: solid; background: var(--faint); }
  .add-card strong { display: block; font-size: 14px; font-weight: 600; }
  .add-card .sub-s { display: block; margin-top: 1px; }
  .add-card .plus-i { flex: none; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); font-size: 18px; font-weight: 600; }
  .barchart { display: block; width: 100%; height: auto; overflow: visible; }
  .barchart text { font-size: 11px; fill: var(--muted); }
  .barchart text.bv { font-size: 11.5px; font-weight: 600; fill: var(--text); }
  .barchart .bar { fill: var(--good); }
  .barchart .bar.neg { fill: var(--spend); }
  .trends { display: grid; }
  .trend-row { display: grid; grid-template-columns: minmax(0, 1fr) auto 76px; gap: 14px; align-items: center; padding: 10px 0; border-top: 1px solid var(--line); }
  .trend-row:first-child { border-top: 0; padding-top: 2px; }
  .trend-row .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .trend-row .sub-s { display: block; margin-top: 1px; }
  .trend-row .num { text-align: right; }
  .sbars { display: block; }
  .sbars .bar { fill: color-mix(in srgb, var(--muted) 40%, transparent); }
  .sbars .bar.now { fill: var(--accent); }
  @media (hover: hover) {
    .sbars g[data-tip]:hover .bar { fill: color-mix(in srgb, var(--muted) 70%, transparent); }
    .sbars g[data-tip]:hover .bar.now { fill: color-mix(in srgb, var(--accent) 80%, var(--text)); }
  }
  /* Drill-down: a number, label, slice or bar with data-go opens the transactions behind it (polish.ts). Hover shows a
     hand and a quiet lift, the same on every chart; nothing changes at rest, so the charts read as before. */
  [data-go] { cursor: pointer; }
  .donut .mid[data-go] > * { pointer-events: auto; }
  a.go-num { text-decoration: none; border-radius: 6px; }
  .kpi a.val { display: block; width: fit-content; }
  @media (hover: hover) {
    :is(.part, .chg-row, .io-r)[data-go]:hover .name, :is(.part, .chg-row)[data-go]:hover .name > a, .io-r[data-go]:hover .io-l, .go-num:is(a, [data-go]):hover, .donut .mid[data-go]:hover strong { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); }
    :is(.barchart, .pairbars) g[data-go]:hover .bar { opacity: .82; }
    .sbars g[data-go]:hover .bar:not(.now) { fill: color-mix(in srgb, var(--muted) 85%, transparent); }
  }
  a.go-num:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  @keyframes riseIn { from { opacity: 0; transform: translateY(14px); } }
  @keyframes drawIn { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
  @keyframes fadeIn { from { opacity: 0; } }
  @keyframes growUp { from { transform: scaleY(0); } }
  @keyframes growRight { from { transform: scaleX(0); } }
  @keyframes sweepIn { from { stroke-dasharray: 0 100; } }
  /* ---------- Command palette ---------- */
  .palette { position: fixed; inset: 0; background: rgba(20,20,18,.35); z-index: 50; display: grid; justify-items: center; align-items: start; padding-top: 12vh; }
  .palette-box { width: min(620px, 92vw); background: var(--panel); border: 1px solid var(--line); border-radius: 18px; box-shadow: var(--shadow); overflow: hidden; animation: palIn .22s var(--ease-glide) both; }
  /* Opens and closes as one piece: the dim fades while the box settles in from just above (palette.ts holds it
     open for the out-motion before hiding it). The phone's full-screen search has neither. */
  @media (min-width: 901px) {
    .palette { animation: fadeIn .18s ease both; }
    .palette.out { pointer-events: none; animation: vtFadeOut .14s ease both; }
    .palette.out .palette-box { animation: palOut .14s ease both; }
  }
  @keyframes palIn { from { opacity: 0; transform: translateY(-8px) scale(.985); } }
  @keyframes palOut { to { opacity: 0; transform: translateY(-4px) scale(.985); } }
  /* #palQ: an id so the themes' input fills and focus rings (html[data-theme] :is(input[type=search], …)) can't paint a box inside the header */
  .pal-top { display: flex; align-items: center; gap: 10px; padding: 0 16px 0 20px; border-bottom: 1px solid var(--line); }
  .pal-top > svg { width: 18px; height: 18px; flex: none; color: var(--muted); }
  .pal-top #palQ { flex: 1; min-width: 0; width: 100%; border: 0; border-radius: 0; padding: 18px 0; font-size: 16px; background: transparent; box-shadow: none; outline: none; -webkit-appearance: none; appearance: none; }
  .pal-top input::-webkit-search-cancel-button { display: none; }
  .pal-cancel { display: none; flex: none; font-size: 16px; }
  .pal-list { max-height: 52vh; overflow-y: auto; padding: 6px; overscroll-behavior: contain; }
  .pal-h { font-size: 11.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); padding: 10px 14px 4px; }
  .pal-item { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 14px; border-radius: 10px; cursor: pointer; }
  .pal-main { display: flex; flex-direction: column; min-width: 0; }
  .pal-l, .pal-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pal-sub { font-size: 12.5px; color: var(--muted); margin-top: 1px; }
  .pal-item .k, .pal-item .num { flex: none; font-size: 12px; color: var(--muted); }
  .pal-item .num { font-size: 14px; } .pal-item .num.neg { color: var(--text); } .pal-item .num.pos { color: var(--good-t); }
  .pal-wait { cursor: default; } .pal-wait .spin { margin-right: 8px; }
  details.pal-flash { border-radius: 8px; }
  .pal-flash { animation: palFlash 1.6s ease-out; scroll-margin-top: calc(env(safe-area-inset-top, 0px) + 64px); }
  @keyframes palFlash { 0%, 30% { box-shadow: 0 0 0 3px var(--accent); } 100% { box-shadow: 0 0 0 3px transparent; } }
  .side-search { display: flex; align-items: center; gap: 10px; margin: 0 0 14px; padding: 8px 10px 8px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); color: var(--muted); font: inherit; font-size: 14px; cursor: pointer; text-align: left; }
  .side-search svg { width: 16px; height: 16px; flex: none; } .side-search span { flex: 1; } .side-search kbd { font-size: 11px; padding: 2px 6px; }
  .side-search:hover { color: var(--text); border-color: color-mix(in srgb, var(--text) 25%, var(--line)); }
  .search-btn { display: none; }
  .sheet-search { display: flex; align-items: center; gap: 10px; width: 100%; margin: 0 0 8px; padding: 11px 12px; border: 0; border-radius: 10px; background: var(--faint); color: var(--muted); font: inherit; font-size: 16px; text-align: left; cursor: pointer; }
  .sheet-search svg { width: 18px; height: 18px; flex: none; }
  .pal-item.on { background: var(--faint); box-shadow: inset 3px 0 0 var(--accent); }
  .pal-foot { display: flex; gap: 16px; padding: 10px 16px; border-top: 1px solid var(--line); font-size: 12px; color: var(--muted); }
  /* ---------- Date picker ---------- */
  input[type=date]::-webkit-calendar-picker-indicator { opacity: .55; cursor: pointer; }
  .calpop { position: fixed; z-index: 60; background: var(--panel); border: 1px solid var(--line); border-radius: 16px; box-shadow: var(--shadow); padding: 12px; width: 270px; animation: fadeUp .12s ease both; }
  .calpop .ch { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; font-weight: 600; }
  .calpop .ch button { border: 0; background: none; cursor: pointer; font-size: 18px; color: var(--muted); padding: 2px 8px; border-radius: 8px; }
  .calpop .ch button:hover { background: var(--faint); color: var(--text); }
  .calpop .grid7 { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; text-align: center; }
  .calpop .dow { font-size: 11px; color: var(--muted); padding: 4px 0; }
  .calpop .d { border: 0; background: none; padding: 7px 0; border-radius: 8px; cursor: pointer; font-size: 13px; }
  .calpop .d:hover { background: var(--faint); }
  .calpop .d.out { color: var(--muted); opacity: .5; }
  .calpop .d.today { box-shadow: inset 0 0 0 1px var(--accent); }
  .calpop .d.sel { background: var(--accent); color: var(--on-accent); }
  .calpop .cf { display: flex; justify-content: space-between; margin-top: 8px; }
  /* ---------- Phone tab bar ---------- */
  .tabbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 16; background: var(--panel); border-top: 1px solid var(--line);
    display: none; grid-template-columns: repeat(5, 1fr); padding: 6px 4px calc(6px + env(safe-area-inset-bottom)); }
  .tabbar a, .tabbar button { display: flex; flex-direction: column; align-items: center; gap: 2px; font-size: 11px; color: var(--muted); text-decoration: none; background: none; border: 0; padding: 6px 0; cursor: pointer; }
  .tabbar a.on { color: var(--accent); }
  .tabbar svg { width: 22px; height: 22px; }
  .fab { display: grid; place-items: center; padding: 0; }
  .fab svg { width: 26px; height: 26px; }
  .tabbar .plus svg { width: 24px; height: 24px; }
  .tabbar .plus { width: 46px; height: 46px; margin: -18px auto 0; border-radius: 50%; background: var(--accent); color: var(--on-accent); font-size: 26px; box-shadow: var(--shadow); padding: 0; display: grid; place-items: center; }
  /* ---------- Grouped transaction feed ---------- */
  .panel.flush { overflow: clip; }
  /* Rows are grids and each day is its own block, so a day's header pushes the previous one away while scrolling */
  .tbl.feed { contain: none; display: block; --cols: minmax(0, 2.2fr) minmax(0, 1.4fr) minmax(0, 1fr) minmax(110px, auto); }
  .tbl.feed.has-sel { --cols: 44px minmax(0, 2.2fr) minmax(0, 1.4fr) minmax(0, 1fr) minmax(110px, auto); }
  .tbl.feed tbody.day { display: block; }
  .tbl.feed tr { display: grid; grid-template-columns: var(--cols); }
  .tbl.feed tr.dayhead { display: block; position: sticky; top: env(safe-area-inset-top, 0px); z-index: 3; }
  .tbl.feed tr.dayhead td { display: block; }
  .tbl.feed tr[data-gid] td { display: flex; align-items: center; min-width: 0; }
  .tbl.feed tr[data-gid] td.amt { justify-content: flex-end; }
  .tbl.feed td.sel { width: auto; padding-right: 0; }
  .tbl.feed td.fromto { max-width: none; }
  .tbl.feed td.fromto > span, .tbl.feed td.catcol > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  .tbl.feed tr.dayhead td { padding: 0; border-bottom: 1px solid var(--line); background: transparent; }
  .tbl.feed tr.dayhead div { display: flex; justify-content: space-between; align-items: center; padding: 8px 16px; font-size: 12px; font-weight: 600; letter-spacing: .02em; color: var(--muted);
    background: color-mix(in srgb, color-mix(in srgb, var(--faint) 60%, var(--panel)) 90%, transparent); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }
  .tbl.feed tr.dayhead .num { font-weight: 500; }
  .tbl.feed tr.dayhead .num.pos { color: var(--good); }
  .tbl.feed tbody.day:not(:first-child) tr.dayhead div { box-shadow: inset 0 1px 0 var(--line); }
  .tbl.feed tbody.day tr:last-child td { border-bottom: none; }
  .tbl.feed td { padding-top: 11px; padding-bottom: 11px; }
  .txmain { display: flex; align-items: center; gap: 12px; min-width: 0; }
  .txmain .txt { min-width: 0; }
  .txmain .desc { overflow: hidden; text-overflow: ellipsis; }
  .tbl.feed .meta { display: flex; flex-wrap: wrap; gap: 4px 0; align-items: center; margin-top: 2px; }
  .cat-s { display: none; font-size: 12.5px; color: var(--muted); margin-right: 8px; }
  .show-s { display: none; }
  .tbl .namecol { min-width: 220px; }
  form.range { display: flex; gap: 8px; align-items: center; flex: 0 1 460px; min-width: 0; }
  form.range input { flex: 1 1 0; width: 0; }
  .tbl.feed .catcol { color: var(--muted); font-size: 13.5px; }
  .av { --h: 220; --c: hsl(var(--h) 62% 48%); flex: none; width: 36px; height: 36px; border-radius: 50%; display: inline-grid; place-items: center;
    font-size: 13px; font-weight: 650; letter-spacing: -.02em; line-height: 1;
    background: color-mix(in oklab, var(--c) 17%, var(--panel)); color: color-mix(in oklab, var(--c) 48%, var(--text));
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--c) 22%, transparent); }
  .av.xfer { --c: var(--muted); }
  .av { position: relative; overflow: hidden; }
  /* A logo fetched through /logo/ covers the monogram; if there is none, the img is removed (core/logo.ts) */
  .av img.logo { position: absolute; inset: 0; width: 100%; height: 100%; box-sizing: border-box; padding: 16%; object-fit: contain; background: #fff; border-radius: inherit; opacity: 0; transition: opacity .15s; }
  .av img.logo.ok { opacity: 1; }
  .av.sm { width: 26px; height: 26px; font-size: 11px; }
  .acct { display: inline-flex; align-items: center; gap: 10px; }
  .acct + .sub-s { margin-left: 36px; }
  .av svg { width: 17px; height: 17px; }
  .av.lg { width: 52px; height: 52px; font-size: 19px; }
  .av.lg svg { width: 24px; height: 24px; }
  .selall { margin: 0 0 0 auto; font-size: 13px; color: var(--muted); }
  .nm { display: inline-block; }
  .head h1 { width: fit-content; }
  /* ---------- Drawer hero ---------- */
  .drawer header { align-items: flex-start; }
  .dhero { display: flex; gap: 14px; align-items: center; min-width: 0; }
  .dhero .eyebrow { margin-bottom: 1px; }
  .dhero h2 { font-size: 19px !important; line-height: 1.25; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 340px; }
  .dh-txt { min-width: 0; }
  /* ---------- Insight cards ---------- */
  main#view { min-width: 0; }
  .insights { min-width: 0; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(260px, 1fr); gap: 14px; margin: -14px 0 24px; overflow-x: auto; scroll-snap-type: x mandatory;
    scrollbar-width: none; padding: 2px; }
  .insights::-webkit-scrollbar { display: none; }
  .insight { --tone: var(--muted); position: relative; scroll-snap-align: start; display: flex; gap: 12px; align-items: flex-start; text-decoration: none;
    background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 14px 36px 14px 14px; transition: border-color .15s, transform .15s; }
  .insight:hover { border-color: color-mix(in srgb, var(--tone) 55%, var(--line)); transform: translateY(-1px); }
  .insight.t-up { --tone: var(--spend); } .insight.t-down { --tone: var(--good); } .insight.t-warn { --tone: var(--warn); }
  .insight .ic { flex: none; width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; color: var(--tone); background: color-mix(in srgb, var(--tone) 18%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone) 22%, transparent); }
  .insight .ic svg { width: 18px; height: 18px; }
  .insight .t { font-weight: 600; font-size: 14px; line-height: 1.35; }
  .insight .d { font-size: 12.5px; color: var(--muted); margin-top: 2px; line-height: 1.45; }
  .insight .x { position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; border: 0; border-radius: 50%; background: none; color: var(--muted); cursor: pointer; font-size: 16px; line-height: 1; opacity: 0; transition: opacity .15s, background .15s; }
  .insight:hover .x, .insight .x:focus-visible { opacity: 1; }
  .insight .x:hover { background: var(--faint); color: var(--text); }
  @media (hover: none) { .insight .x { opacity: .7; } }
  /* drawn icons (ICON_X / ICON_PREV / ICON_NEXT) in the small glyph buttons: centred in a box, sized to the old glyph */
  :is(.tagcloud .chip, .saved-reports .chip) button, .editor-row .x, .calpop .ch button, .insight .x { display: inline-grid; place-items: center; line-height: 0; }
  :is(.tagcloud .chip, .saved-reports .chip) button { width: 20px; height: 20px; padding: 0; border-radius: 50%; }
  :is(.tagcloud .chip, .saved-reports .chip) button svg { width: 10px; height: 10px; }
  .editor-row .x { width: 28px; height: 28px; padding: 0; border-radius: 50%; } .editor-row .x svg { width: 13px; height: 13px; }
  .calpop .ch button { width: 32px; height: 28px; padding: 0; } .calpop .ch button svg { width: 14px; height: 14px; }
  .insight .x { padding: 0; } .insight .x svg { width: 11px; height: 11px; }
  .round svg { width: 14px; height: 14px; } .month .round svg { width: 15px; height: 15px; }
  /* ---------- Part 2: widgets, sheets, pickers, charts ---------- */
  .cards .w { break-inside: avoid; display: inline-block; width: 100%; margin-bottom: 20px; vertical-align: top; }
  .cards .w > .panel { contain: layout paint; }
  .cards .w.wide { column-span: all; display: block; }
  .dash-bar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; background: var(--panel); border: 1px dashed var(--accent); border-radius: 14px; padding: 10px 14px; margin-bottom: 18px; font-size: 13px; color: var(--muted); }
  .dash-bar .grow, .wctl .grow { flex: 1; }
  .dash-opts { display: flex; gap: 10px 16px; align-items: center; flex-wrap: wrap; flex-basis: 100%; padding-top: 10px; border-top: 1px solid var(--line); }
  .dash-opts .grow { flex: 1; }
  .dash-opts label.check { font-size: 13px; }
  .seg.mini { margin: 0; } .seg.mini button { padding: 4px 12px; font-size: 12.5px; }
  /* Customizable overview (ui.js): bands of --n columns you arrange yourself, with wide cards between the bands */
  .dash-wrap { --gap: 20px; }
  .cards.free { columns: auto; }
  /* The insight strip uses the same columns and gutters as the cards, so their edges line up (it still scrolls sideways
     when there are more insights than columns; on phones it keeps its peek-at-the-next-card width) */
  .dash-wrap:not([data-n="1"]) .insights { grid-auto-columns: calc((100% - (var(--n) - 1) * var(--gap)) / var(--n)); gap: var(--gap); padding: 2px 0; margin-bottom: calc(var(--gap) - 2px); }
  .dash-wrap .insight { border-radius: var(--radius); }
  .cards.free .band { display: grid; grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr)); gap: var(--gap); align-items: start; margin-bottom: var(--gap); }
  .cards.free .dcol { display: flex; flex-direction: column; gap: var(--gap); min-width: 0; }
  .cards.free .w { display: block; margin-bottom: 0; }
  .cards.free > .w, .cards.free .dash-rest > .w { margin-bottom: var(--gap); }
  .dash-more { display: block; width: 100%; padding: 12px; }
  /* Freeform tiles (ui.js gridLayout): 24 columns, 8px rows; each tile's spot is set inline */
  .cards.grid { display: grid; grid-template-columns: repeat(24, minmax(0, 1fr)); grid-auto-rows: 8px; column-gap: var(--gap); row-gap: 0; align-items: start; margin-bottom: var(--gap); }
  .cards.grid > .w { display: block; margin: 0; min-width: 0; }
  /* while customizing, faint column guides show what the tiles snap to */
  .cards.grid.editing { background-image: linear-gradient(to right, color-mix(in srgb, var(--accent) 3.5%, transparent) 0 calc(100% - var(--gap)), transparent 0);
    background-size: calc((100% + var(--gap)) / 24) 100%; border-radius: 12px; }
  .cards.grid .rsz { position: absolute; right: -6px; bottom: -6px; width: 22px; height: 22px; cursor: nwse-resize; z-index: 2; touch-action: none; border-radius: 6px;
    background: linear-gradient(135deg, transparent 0 45%, var(--accent) 45% 55%, transparent 55% 65%, var(--accent) 65% 75%, transparent 75%); }
  .cards.grid.editing .w > .rsz { pointer-events: auto; }
  .cards.free.editing .dcol { min-height: 110px; border-radius: var(--radius); background: color-mix(in srgb, var(--accent) 5%, transparent); }
  .cards.editing .w { position: relative; border-radius: var(--radius); outline: 1.5px dashed color-mix(in srgb, var(--accent) 45%, transparent); outline-offset: 3px; cursor: grab; }
  .cards.editing .w > :not(.wctl):not(.rsz):not(.wopts) { pointer-events: none; }
  .cards.editing .w.off > :not(.wctl):not(.rsz):not(.wopts) { opacity: .35; filter: grayscale(1); }
  .cards.editing .w { user-select: none; -webkit-user-select: none; }
  /* the spot the tile will land in, while you carry a solid copy of it */
  .cards.editing .w.dragging { background: color-mix(in srgb, var(--accent) 7%, transparent); outline-color: color-mix(in srgb, var(--accent) 70%, transparent); }
  .cards.editing .w.dragging > * { opacity: 0; }
  .cards.moving, .cards.moving * { cursor: grabbing !important; }
  .dash-ghost { position: fixed; z-index: 60; margin: 0; pointer-events: none; border-radius: var(--radius); will-change: transform; transform-origin: 50% 40%; display: flex; flex-direction: column; }
  .dash-ghost > .panel { flex: 1; }
  .dash-ghost > .panel { background: linear-gradient(var(--panel), var(--panel)), var(--bg); box-shadow: 0 24px 60px rgba(0,0,0,.28), 0 4px 14px rgba(0,0,0,.12); }
  .dash-ghost > :not(.wctl) { pointer-events: none; }
  .wctl { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; font-size: 12.5px; }
  .wctl .grip { cursor: grab; color: var(--muted); font-size: 16px; }
  .wctl button { border: 1px solid var(--line); background: var(--panel); border-radius: 8px; padding: 3px 9px; cursor: pointer; font-size: 12.5px; }
  .wctl button:hover { border-color: var(--muted); }
  /* a tile's own settings, opened with its ⚙ button while customizing */
  .wopts { display: grid; gap: 8px; margin: 0 0 10px; padding: 12px 14px; background: var(--panel); border: 1px solid var(--line); border-radius: 12px; font-size: 13px; cursor: default; }
  .wopts[hidden] { display: none; }
  .wopts .wopt { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
  .wopts .wopt select { width: auto; margin: 0; padding: 4px 8px; }
  .row .spark { flex: none; margin-left: auto; margin-right: 12px; align-self: center; overflow: visible; }
  .spark polyline { fill: none; stroke: var(--muted); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
  .spark circle { fill: var(--muted); }
  .spark.good polyline { stroke: var(--good); } .spark.good circle { fill: var(--good); }
  .spark.bad polyline { stroke: var(--spend); } .spark.bad circle { fill: var(--spend); }
  .hm { display: grid; gap: 6px; }
  .hm-dow, .hm-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; }
  .hm-dow span { text-align: center; font-size: 11px; color: var(--muted); }
  .hm-cell { aspect-ratio: 1; border-radius: 8px; border: 0; padding: 0; font-size: 11px; color: var(--muted); display: grid; place-items: center; cursor: pointer;
    background: color-mix(in srgb, var(--spend) var(--p, 0%), var(--faint)); transition: transform .12s; }
  .hm-cell[style*="--p"] { color: color-mix(in srgb, var(--text) 80%, transparent); }
  .hm-cell:not(:disabled):hover { transform: scale(1.08); }
  .hm-cell:disabled { cursor: default; }
  .hm-cell.pad { background: none; }
  .hm-cell.fut { background: none; box-shadow: inset 0 0 0 1px var(--line); opacity: .6; }
  .hm-cell.today { box-shadow: inset 0 0 0 2px var(--accent); }
  .hm-foot { display: flex; justify-content: space-between; gap: 10px; margin-top: 14px; font-size: 12.5px; color: var(--muted); flex-wrap: wrap; }
  .sankey svg { width: 100%; height: auto; display: block; }
  .sankey .lk { fill-opacity: .26; transition: fill-opacity .15s; }
  .sankey .lk:hover { fill-opacity: .55; }
  .sankey .lk, .sankey .sk-node { cursor: pointer; }
  .sankey .sk-node:hover { filter: brightness(1.15); }
  .sankey .sk-name { fill: var(--text); font-size: 12.5px; font-weight: 500; }
  .sankey .sk-amt { fill: var(--muted); font-size: 11.5px; }
  /* Labels inside the chart (narrow cards) sit over the bands, which are faint enough to read text on; taps go
     through them to the band underneath */
  .sankey .sk-in text { pointer-events: none; }
  .sankey .sk-in .sk-name { font-weight: 600; }
  .tbl.feed .inl { cursor: pointer; border-radius: 6px; transition: background .12s, box-shadow .12s; }
  .tbl.feed .catcol .inl { padding: 3px 7px; margin-left: -7px; }
  .tbl.feed .inl:hover { background: var(--faint); box-shadow: 0 0 0 1px var(--line); color: var(--text); }
  .chip.ghost { background: none; box-shadow: inset 0 0 0 1px dashed var(--line); border: 1px dashed var(--line); opacity: 0; }
  .tbl.feed tr:hover .chip.ghost, .tbl.feed tr.kfocus .chip.ghost { opacity: 1; }
  @media (hover: none) { .chip.ghost { display: none; } }
  .tbl.feed tr[data-gid] { scroll-margin: 48px 0 90px; }
  .tbl.feed tr.kfocus td, .tbl.feed tr.click:focus-visible td { background: color-mix(in srgb, var(--accent) 7%, var(--panel)); }
  .tbl.feed tr.kfocus td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
  .tbl tr.click:focus { outline: none; }
  .tbl.feed tr.click:focus-visible td, .tbl tr.click:focus-visible td { box-shadow: inset 0 2px 0 var(--accent), inset 0 -2px 0 var(--accent); }
  .tbl.feed tr.click:focus-visible td:first-child, .tbl tr.click:focus-visible td:first-child { box-shadow: inset 3px 0 0 var(--accent), inset 0 2px 0 var(--accent), inset 0 -2px 0 var(--accent); }
  .tbl.feed tr.click:focus-visible td:last-child, .tbl tr.click:focus-visible td:last-child { box-shadow: inset -2px 0 0 var(--accent), inset 0 2px 0 var(--accent), inset 0 -2px 0 var(--accent); }
  .cap-note { margin: 14px 0 0; text-align: center; }
  .tbl.feed tr.saving { opacity: .55; }
  .tbl.feed tr.saving .amt, .tbl.feed tr.saving .inl { background: linear-gradient(90deg, transparent, var(--faint), transparent); background-size: 200% 100%; animation: shimmer 1s infinite linear; }
  .tbl.feed tr.saved td { animation: savedFlash .9s ease; }
  @keyframes savedFlash { from { background: color-mix(in srgb, var(--good) 16%, var(--panel)); } }
  .tbl.feed tbody.day { content-visibility: auto; contain-intrinsic-size: auto 260px; }
  .more { display: flex; gap: 10px; align-items: center; justify-content: center; padding: 18px; color: var(--muted); font-size: 13px; border-top: 1px solid var(--line); }
  .spin { width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--line); border-top-color: var(--accent); animation: spin .8s linear infinite; display: inline-block; }
  @keyframes spin { to { transform: rotate(360deg); } }
  .pickpop { position: fixed; z-index: 62; background: var(--panel); border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow); overflow: hidden; animation: fadeUp .12s ease both; }
  .pickpop input { width: 100%; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; padding: 11px 14px; background: transparent; }
  .pickpop .pk-list { max-height: 260px; overflow-y: auto; padding: 4px; }
  .pickpop .pal-item { padding: 8px 12px; }
  kbd { font: 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 4px 7px; border-radius: 6px; border: 1px solid var(--line); border-bottom-width: 2px; background: var(--panel); color: var(--text); }
  .toggles { display: grid; gap: 2px; margin: 4px 0 6px; }
  .toggles button { display: flex !important; align-items: center; gap: 9px; }
  .toggles button svg { width: 15px; height: 15px; flex: none; }
  .toggles button.on { color: var(--text) !important; }
  .toggles.sheet { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
  .toggles.sheet button svg { width: 16px; height: 16px; }
  .ptr { position: fixed; top: env(safe-area-inset-top, 0px); left: 50%; z-index: 30; width: 40px; height: 40px; border-radius: 50%; background: var(--panel); box-shadow: var(--shadow); display: grid; place-items: center;
    color: var(--muted); transform: translate(-50%, -50px); opacity: 0; transition: transform .25s, opacity .25s; pointer-events: none; }
  .ptr svg { width: 20px; height: 20px; }
  .ptr.armed { color: var(--accent); }
  .ptr.busy svg { animation: spin .8s linear infinite; color: var(--accent); }
  .nlq { position: relative; margin-bottom: 18px; }
  .nlq > svg { position: absolute; left: 13px; top: 13px; width: 17px; height: 17px; color: var(--accent); pointer-events: none; }
  .nlq input { width: 100%; padding: 11px 14px 11px 38px; border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line)); border-radius: 12px; background: var(--panel); font-size: 15px; color: var(--text); }
  .nlchips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
  .nlchips:empty { display: none; }
  .nlchip { font-size: 12px; background: color-mix(in srgb, var(--accent) 10%, var(--panel)); color: var(--text); border-radius: 99px; padding: 3px 10px; animation: fadeUp .15s ease both; }
  .nlchip em { font-style: normal; color: var(--muted); margin-right: 5px; }
  input.filled { box-shadow: 0 0 0 2px color-mix(in srgb, var(--good) 35%, transparent); }
  .grab { display: none; }
  /* Privacy: blur money everywhere (hover to peek on desktop) */
  html.privacy .num, html.privacy .amt, html.privacy .big, html.privacy .val, html.privacy .total, html.privacy .insight .d, html.privacy .tip, html.privacy .hm-foot strong,
  html.privacy .dhero .sub-s, html.privacy .cat .sub, html.privacy .sub-s, html.privacy .kpi .val { filter: blur(7px); transition: filter .15s; }
  html.privacy .settings .sub-s { filter: none; }   /* Settings hints hold no amounts */
  html.privacy .amtx { visibility: hidden; }
  html.privacy .spark { opacity: .5; }
  @media (hover: hover) { html.privacy .num:hover, html.privacy .amt:hover, html.privacy .val:hover, html.privacy .total:hover, html.privacy .big:hover { filter: none; } }
  /* Compact density */
  html.compact body { font-size: 13.5px; }
  html.compact main { padding-top: 24px; }
  html.compact .panel:not(.flush) { padding: 18px 20px; }   /* flush panels (tables, Rules groups) keep padding 0 */
  html.compact .rulegroup summary { padding-block: 12px; }
  html.compact .cards { column-gap: 14px; } html.compact .cards:not(.free) .w { margin-bottom: 14px; } html.compact .dash-wrap { --gap: 14px; }
  html.compact .flow { column-gap: 14px; } html.compact .flow:not(.on) { row-gap: 14px; }
  html.compact .tbl.feed td { padding-top: 6px; padding-bottom: 6px; }
  html.compact .tbl.feed tr.dayhead div { padding-top: 5px; padding-bottom: 5px; }
  html.compact .av { width: 28px; height: 28px; font-size: 11px; }
  html.compact .av svg { width: 14px; height: 14px; }
  html.compact .hero { margin-bottom: 26px; } html.compact .big { font-size: 42px; }
  html.compact .list .row { padding: 7px 0; }
  html.compact .head { margin-bottom: 18px; }
  html.compact .kpi { padding: 12px 14px; }

  /* ---------- Theme: Liquid glass neon ---------- */
  html[data-theme="glass"] { --bg: #07060f; --panel: rgba(24, 20, 48, .52); --line: rgba(255, 255, 255, .13); --text: #f5f3ff; --muted: #aaa4cc; --faint: rgba(255, 255, 255, .07);
    --good: #3dffb5; --spend: #ff4fd8; --income: #37f5ff; --warn: #ffd84d; --accent: #37f5ff; --on-accent: #04121a;
    --hero-grad: linear-gradient(90deg, #37f5ff, #b86bff 50%, #ff4fd8); --shadow: 0 20px 60px rgba(0, 0, 0, .55); --radius: 22px; color-scheme: dark;
    --glass: linear-gradient(150deg, rgba(255,255,255,.11), rgba(255,255,255,.035) 42%, rgba(255,255,255,.06));
    --glass-edge: inset 0 1px 0 rgba(255,255,255,.22), inset 0 -1px 0 rgba(255,255,255,.05), inset 1px 0 0 rgba(255,255,255,.06);
    /* what Settings → Appearance → Customize can change (themevars.ts sets these on <html>) */
    --glow1: #ff4fd8; --glow2: #37f5ff; --glow3: #8c5cff; --glass-base: #14102a; --glass-tint: 38%; --glass-blur: 26px;
    background: var(--bg); }
  html[data-theme="glass"] body { background: transparent; }
  /* drifting neon light behind the glass. The drift keeps the browser drawing every frame even when nothing else moves,
     so it stays still unless you turn Moving light on (Settings → Appearance → Customize; themevars.ts sets --glass-motion: running) */
  html[data-theme="glass"] body::after { content: ''; position: fixed; inset: -20vmax; z-index: -1; pointer-events: none;
    background:
      radial-gradient(38vmax 38vmax at 18% 22%, color-mix(in srgb, var(--glow1) 42%, transparent), transparent 62%),
      radial-gradient(42vmax 42vmax at 82% 30%, color-mix(in srgb, var(--glow2) 30%, transparent), transparent 60%),
      radial-gradient(46vmax 46vmax at 55% 88%, color-mix(in srgb, var(--glow3) 40%, transparent), transparent 62%),
      radial-gradient(30vmax 30vmax at 90% 85%, color-mix(in srgb, var(--good) 16%, transparent), transparent 60%);
    filter: saturate(130%); animation: glassDrift 38s ease-in-out infinite alternate; animation-play-state: var(--glass-motion, paused); }
  @keyframes glassDrift { 0% { transform: translate3d(0, 0, 0) rotate(0deg); } 50% { transform: translate3d(4vmax, -3vmax, 0) rotate(8deg); } 100% { transform: translate3d(-3vmax, 3vmax, 0) rotate(-6deg); } }
  /* frosted surfaces */
  html[data-theme="glass"] :is(.panel, .daycards tbody.day, .daycards > .center-empty, .kpi, .insight, .extra, .split, .login form, .dash-bar) {
    background: var(--glass), color-mix(in srgb, var(--glass-base) var(--glass-tint), transparent); border: 1px solid rgba(255, 255, 255, .14);
    box-shadow: var(--glass-edge), 0 12px 40px rgba(0, 0, 0, .35);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(170%); backdrop-filter: blur(var(--glass-blur)) saturate(170%); }
  html[data-theme="glass"] :is(.drawer, dialog, .calpop, .pickpop, .palette-box, .bulkbar, .ff-pop) {
    background: var(--glass), rgba(14, 11, 30, .72); border-color: rgba(255, 255, 255, .16);
    -webkit-backdrop-filter: blur(34px) saturate(180%); backdrop-filter: blur(34px) saturate(180%); }
  html[data-theme="glass"] .drawer { box-shadow: var(--glass-edge), -20px 0 80px rgba(0, 0, 0, .5); }
  html[data-theme="glass"] .drawer footer { background: transparent; border-top-color: rgba(255, 255, 255, .1); }
  html[data-theme="glass"] .tabbar { background: rgba(14, 11, 30, .55); border-top-color: rgba(255, 255, 255, .12);
    -webkit-backdrop-filter: blur(30px) saturate(180%); backdrop-filter: blur(30px) saturate(180%); }
  html[data-theme="glass"] :is(input[type=text], input[type=number], input[type=date], input[type=password], input[type=search], select, textarea, .btn, .round, .wctl button) {
    background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .15); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08); }
  html[data-theme="glass"] select option { background: #16122e; }
  html[data-theme="glass"] :is(input, select, textarea):focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent), 0 0 18px color-mix(in srgb, var(--accent) 25%, transparent); }
  html[data-theme="glass"] .seg { background: rgba(255, 255, 255, .06); }
  html[data-theme="glass"] :is(.seg button, .tabs a).on { background: rgba(255, 255, 255, .14); color: var(--text); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .2); }
  html[data-theme="glass"] .tbl.feed tr.dayhead div { background: rgba(20, 16, 42, .55); }
  html[data-theme="glass"] .tbl tr.click:hover td { background: rgba(255, 255, 255, .05); }
  html[data-theme="glass"] .side { border-right-color: rgba(255, 255, 255, .08); background: rgba(10, 8, 22, .35); -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); }
  /* neon glow */
  html[data-theme="glass"] .btn.primary, html[data-theme="glass"] .fab, html[data-theme="glass"] .tabbar .plus {
    background: linear-gradient(135deg, var(--accent), var(--glow3)); border-color: transparent; color: var(--on-accent); box-shadow: 0 0 22px color-mix(in srgb, var(--accent) 45%, transparent), inset 0 1px 0 rgba(255, 255, 255, .5); }
  html[data-theme="glass"] .nav a.on { background: rgba(255, 255, 255, .08); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12), 0 0 0 1px rgba(255, 255, 255, .1); }
  html[data-theme="glass"] .nav a.on svg, html[data-theme="glass"] .tabbar a.on svg { filter: drop-shadow(0 0 6px var(--accent)); }
  html[data-theme="glass"] .fill { box-shadow: 0 0 12px color-mix(in srgb, var(--spend) 70%, transparent); }
  html[data-theme="glass"] .fill.good { box-shadow: 0 0 12px color-mix(in srgb, var(--good) 70%, transparent); }
  html[data-theme="glass"] .track { background: rgba(255, 255, 255, .08); }
  html[data-theme="glass"] .big { filter: drop-shadow(0 0 22px rgba(184, 107, 255, .35)); }
  html[data-theme="glass"] .pos, html[data-theme="glass"] .down { text-shadow: 0 0 12px color-mix(in srgb, var(--good) 45%, transparent); }
  html[data-theme="glass"] .neg, html[data-theme="glass"] .up { text-shadow: 0 0 12px color-mix(in srgb, var(--spend) 40%, transparent); }
  html[data-theme="glass"] .chart rect { filter: drop-shadow(0 0 6px currentColor); }
  html[data-theme="glass"] .sankey .lk { fill-opacity: .32; }
  html[data-theme="glass"] .tabs a.on { text-shadow: 0 0 12px color-mix(in srgb, var(--accent) 60%, transparent); }
  html[data-theme="glass"] .tabs { background: rgba(255, 255, 255, .06); }
  html[data-theme="glass"] .hm-cell[style*="--p"] { box-shadow: 0 0 10px color-mix(in srgb, var(--spend) calc(var(--p) * .6), transparent); }
  html[data-theme="glass"] .av { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 0 0 1px color-mix(in oklab, var(--c) 35%, transparent), 0 0 14px color-mix(in oklab, var(--c) 30%, transparent); }
  html[data-theme="glass"] .appbar.scrolled { background: rgba(10, 8, 24, .45); }
  html[data-theme="glass"] .scrim { background: rgba(4, 3, 12, .45); }
  @media (prefers-reduced-motion: reduce) { html[data-theme="glass"] body::after { animation: none; } }
  /* Moving light under blurred glass makes the browser re-blur every blurred surface on every frame. Pause it while scrolling
     (the cards and sidebar lose their live blur below, with the Frosted ones). */
  html.scrolling[data-theme="glass"] body::after { animation-play-state: paused; }
  /* ---------- Alignment: centered labels, even controls, room to breathe ---------- */
  .btn { justify-content: center; text-align: center; }
  .round { display: inline-grid; place-items: center; padding: 0; flex: none; }
  .month { gap: 6px; }
  .month .round { width: 34px; height: 34px; }
  .month .btn { height: 34px; padding-block: 0; }
  .row > span:is(.neutral, .num):last-child { white-space: nowrap; }
  .panel-head { flex-wrap: wrap; row-gap: 8px; }
  .panel-head > .check { white-space: nowrap; }
  .show-s { display: none; }
  .review-row > .btn { flex: none; }
  .drawer footer { flex-wrap: wrap; align-items: center; }
  @media (min-width: 901px) { main { padding-bottom: 112px; } }     /* the last card scrolls clear of the + button */
  @media (max-width: 600px) {
    .tbl .show-s { display: block; margin-top: 4px; }
    .review-row > .btn .lbl { display: none; }
    .review-row > .btn { width: 36px; height: 36px; padding: 0; border-radius: 50%; }
    .drawer footer { gap: 8px; padding: 14px 16px calc(14px + env(safe-area-inset-bottom)); }
    .drawer footer .btn { padding: 9px 12px; }
    .drawer footer .grow { flex: 1 1 0; min-width: 0; }
  }
  /* ---------- Shape, every theme: pill buttons and switches, a floating sidebar and tab bar ----------
     Colors come from the theme; Frosted glass and the glass/neon themes lay their own on top. */
  .btn, .btn.small { border-radius: 999px; }
  .seg { border-radius: 22px; }
  .seg button { border-radius: 999px; }
  :is(.seg button, .tabs a).on { background: var(--accent); color: var(--on-accent); box-shadow: none; }
  .tabs { width: fit-content; max-width: 100%; gap: 2px; padding: 4px; border: 0; border-radius: 999px; background: var(--faint); box-shadow: inset 0 0 0 1px var(--line); scrollbar-width: none; }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs a { padding: 7px 14px; margin: 0; border: 0; border-radius: 999px; line-height: 1.2; }
  .tabs a.on .neutral { color: inherit; opacity: .7; }
  @media (min-width: 901px) {
    .side { margin: 14px 0 14px 14px; height: calc(100vh - 28px); top: 14px; border: 1px solid var(--line); border-radius: 26px; background: var(--panel); box-shadow: var(--shadow); }
  }
  .nav a { border-radius: 999px; }
  .nav a.on { background: color-mix(in srgb, var(--accent) 13%, transparent); box-shadow: none; }
  html[data-theme="carbon"] .nav a.on::before { display: none; }
  .tabbar { left: 14px; right: 14px; bottom: calc(10px + env(safe-area-inset-bottom)); padding: 6px; border: 1px solid var(--line); border-radius: 999px; align-items: center;
    background: color-mix(in srgb, var(--panel) 84%, transparent); box-shadow: var(--shadow); -webkit-backdrop-filter: blur(24px) saturate(160%); backdrop-filter: blur(24px) saturate(160%); }
  .tabbar :is(a, button) { position: relative; z-index: 1; }
  .tabbar a { border-radius: 999px; padding: 7px 0; margin: 0 3px; }
  .tabbar .plus { margin: 0 auto; width: 48px; height: 48px; }
  .tab-pill { display: block; position: absolute; left: 0; top: 6px; bottom: 6px; border-radius: 999px; pointer-events: none; background: color-mix(in srgb, var(--accent) 15%, transparent); }
  .tab-pill.off { opacity: 0; }
  .pill-ready .tab-pill { transition: transform .42s cubic-bezier(.3, 1.3, .5, 1), width .42s cubic-bezier(.3, 1.3, .5, 1), opacity .2s; }
  /* Folded while scrolling down (phone.ts): the bar shrinks away into the + button at the bottom right (#fab).
     Only transform and opacity move, so the phone never has to lay out or re-blur anything per frame. */
  .tabbar { transform-origin: calc(100% - 30px) 50%; transition: transform .34s cubic-bezier(.3, 1.1, .4, 1), opacity .22s ease, visibility 0s; }
  .tabbar.mini { transform: scale(.18, .7); opacity: 0; visibility: hidden; pointer-events: none;
    transition: transform .3s cubic-bezier(.5, 0, .7, .4), opacity .2s ease .06s, visibility 0s .3s; }
  @media (max-width: 900px) {
    main { padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)) !important; }
    :is(.undo, .toast) { bottom: calc(env(safe-area-inset-bottom, 0px) + 92px); }
    /* Phones: the bar spans the screen above the floating tab bar instead of a narrow centred pill that wraps */
    .undo { left: 14px; right: 14px; transform: none; width: auto; max-width: none; border-radius: 22px; padding: 10px 10px 10px 18px; font-size: 14.5px; }
    .undo button { padding: 10px 18px; font-size: 14.5px; }
  }
  /* ---------- Theme: Frosted glass (warm light glass over a soft-focus room) ---------- */
  html[data-theme="frosted"] { --bg: #d9c7b3; --panel: rgba(255, 250, 244, .5); --line: rgba(255, 255, 255, .55); --text: #1d1915; --muted: #6a5e54; --faint: rgba(255, 255, 255, .35);
    --good: #2a9163; --spend: #d94f27; --income: #2a9163; --warn: #c98a12; --accent: #e2552c; --on-accent: #ffffff;
    --shadow: 0 18px 50px rgba(60, 35, 15, .18); --radius: 22px; color-scheme: light;
    --ink: #17130f; --frost-edge: inset 0 1px 0 rgba(255, 255, 255, .75), inset 0 0 0 1px rgba(255, 255, 255, .35);
    /* what Settings → Appearance → Customize can change (themevars.ts sets these on <html>): the room's colors and the glass */
    --glow1: #8a5a3c; --glow2: #f1e4d3; --glow3: #5f6b4a; --glass-base: #fffaf4; --glass-tint: 22%; --glass-blur: 28px;
    background: var(--bg); }
  html[data-theme="frosted"] body { background: transparent; }
  /* the room: wood, a bright window, a dark screen, a plant, a rug */
  html[data-theme="frosted"] body::after { content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
    background:
      radial-gradient(30vmax 40vmax at 12% 18%, color-mix(in srgb, var(--glow1) 70%, transparent), transparent 70%),
      radial-gradient(40vmax 26vmax at 70% 12%, color-mix(in srgb, var(--glow2) 95%, transparent), transparent 70%),
      radial-gradient(34vmax 22vmax at 55% 48%, rgba(40, 32, 28, .5), transparent 70%),
      radial-gradient(28vmax 28vmax at 88% 70%, color-mix(in srgb, var(--glow3) 55%, transparent), transparent 70%),
      radial-gradient(50vmax 30vmax at 40% 95%, color-mix(in srgb, var(--glow1) 75%, transparent), transparent 70%),
      linear-gradient(180deg, color-mix(in srgb, var(--bg) 40%, #fff), var(--bg)); }
  html[data-theme="frosted"] :is(.panel, .daycards tbody.day, .daycards > .center-empty, .kpi, .insight, .extra, .split, .login form, .dash-bar) {
    background: linear-gradient(160deg, rgba(255, 255, 255, .42), rgba(255, 255, 255, .18)), color-mix(in srgb, var(--glass-base) var(--glass-tint), transparent);
    border: 1px solid rgba(255, 255, 255, .6); box-shadow: var(--frost-edge), 0 10px 30px rgba(70, 40, 20, .12);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(150%); backdrop-filter: blur(var(--glass-blur)) saturate(150%); }
  html[data-theme="frosted"] :is(.drawer, dialog, .calpop, .pickpop, .palette-box, .bulkbar, .ff-pop) {
    background: rgba(250, 243, 235, .8); border-color: rgba(255, 255, 255, .7);
    -webkit-backdrop-filter: blur(36px) saturate(160%); backdrop-filter: blur(36px) saturate(160%); }
  html[data-theme="frosted"] .drawer footer { background: transparent; border-top-color: rgba(255, 255, 255, .6); }
  html[data-theme="frosted"] :is(input[type=text], input[type=number], input[type=date], input[type=password], input[type=search], select, textarea, .round, .wctl button) {
    background: rgba(255, 255, 255, .45); border-color: rgba(255, 255, 255, .7); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .6); }
  html[data-theme="frosted"] select option { background: #f6efe7; }
  html[data-theme="frosted"] :is(input, select, textarea):focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
  /* pill buttons: ink for the main action, glass for the rest */
  html[data-theme="frosted"] .btn { background: rgba(255, 255, 255, .45); border-color: rgba(255, 255, 255, .7); }
  html[data-theme="frosted"] .btn.primary { background: var(--ink); color: #fff; border-color: var(--ink); box-shadow: 0 6px 18px rgba(23, 19, 15, .25); }
  html[data-theme="frosted"] .seg { background: rgba(255, 255, 255, .35); }
  html[data-theme="frosted"] .seg button.on { background: var(--ink); color: #fff; }
  html[data-theme="frosted"] .tbl.feed td { background: rgba(255, 250, 244, .42); border-color: rgba(255, 255, 255, .5); }
  html[data-theme="frosted"] .tbl.feed tr.dayhead div { background: rgba(255, 248, 240, .6); -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); }
  html[data-theme="frosted"] .tbl tr.click:hover td { background: rgba(255, 255, 255, .35); }
  html[data-theme="frosted"] .tabs { background: rgba(255, 255, 255, .35); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .5); }
  html[data-theme="frosted"] .tabs a.on { background: var(--ink); color: #fff; }
  html[data-theme="frosted"] .track { background: rgba(255, 255, 255, .5); }
  html[data-theme="frosted"] .big { color: var(--text); }
  html[data-theme="frosted"] .appbar.scrolled { background: rgba(240, 228, 214, .55); }
  html[data-theme="frosted"] .scrim { background: rgba(40, 25, 12, .25); }
  /* floating glass sidebar, a white pill on the current page */
  html[data-theme="frosted"] .side { background: rgba(255, 250, 244, .38); border: 1px solid rgba(255, 255, 255, .6); box-shadow: var(--frost-edge), 0 10px 30px rgba(70, 40, 20, .12);
    -webkit-backdrop-filter: blur(28px) saturate(150%); backdrop-filter: blur(28px) saturate(150%); }
  html[data-theme="frosted"] .nav a.on { background: #fff; color: var(--text); box-shadow: 0 4px 14px rgba(70, 40, 20, .14); }
  /* floating glass tab bar on phones, a white pill on the current tab */
  html[data-theme="frosted"] .tabbar { background: rgba(255, 250, 244, .55); border: 1px solid rgba(255, 255, 255, .7); box-shadow: var(--frost-edge), 0 14px 40px rgba(60, 35, 15, .22);
    -webkit-backdrop-filter: blur(30px) saturate(170%); backdrop-filter: blur(30px) saturate(170%); }
  html[data-theme="frosted"] .tabbar a { color: #5d5249; }
  html[data-theme="frosted"] .tabbar a.on { color: var(--text); }
  html[data-theme="frosted"] .tab-pill { background: #fff; box-shadow: 0 3px 12px rgba(70, 40, 20, .16); }
  html[data-theme="frosted"] :is(.tabbar .plus, .fab) { background: var(--ink); color: #fff; box-shadow: 0 6px 18px rgba(23, 19, 15, .3); }
  /* Other scenes (Settings → Appearance → Customize colors → Scene; themevars.ts sets data-scene). Each sets its own
     colors for the three --glow variables, so your own colors still apply on top. */
  html[data-theme="frosted"][data-scene="dunes"] { --bg: #e7a07f; --glow1: #b5654a; --glow2: #fff1d6; --glow3: #f3c9b8; }
  html[data-theme="frosted"][data-scene="dunes"] body::after { background:
      radial-gradient(16vmax 16vmax at 72% 26%, var(--glow2), transparent 70%),
      radial-gradient(60vmax 40vmax at 72% 26%, color-mix(in srgb, var(--glow2) 45%, transparent), transparent 70%),
      radial-gradient(70vmax 18vmax at 50% 72%, color-mix(in srgb, var(--glow1) 45%, transparent), transparent 70%),
      radial-gradient(90vmax 30vmax at 15% 100%, var(--glow1), transparent 70%),
      radial-gradient(80vmax 26vmax at 90% 95%, color-mix(in srgb, var(--glow1) 70%, var(--glow2)), transparent 70%),
      linear-gradient(180deg, var(--glow3), var(--bg)); }
  html[data-theme="frosted"][data-scene="garden"] { --bg: #cfd8bf; --glow1: #5f8050; --glow2: #fffbe6; --glow3: #a9bf8e; --accent: #d9662f; }
  html[data-theme="frosted"][data-scene="garden"] body::after { background:
      radial-gradient(50vmax 34vmax at 55% 8%, var(--glow2), transparent 70%),
      radial-gradient(26vmax 34vmax at 6% 30%, var(--glow1), transparent 70%),
      radial-gradient(30vmax 26vmax at 96% 22%, color-mix(in srgb, var(--glow1) 80%, transparent), transparent 70%),
      radial-gradient(40vmax 26vmax at 80% 80%, var(--glow3), transparent 70%),
      radial-gradient(44vmax 24vmax at 18% 92%, color-mix(in srgb, var(--glow1) 65%, var(--glow3)), transparent 70%),
      linear-gradient(180deg, color-mix(in srgb, var(--glow2) 60%, var(--bg)), var(--bg)); }
  html[data-theme="frosted"][data-scene="coast"] { --bg: #e8d8bd; --glow1: #3f87a3; --glow2: #f2f8fb; --glow3: #9ccbd8; --accent: #e0612f; }
  html[data-theme="frosted"][data-scene="coast"] body::after { background:
      radial-gradient(60vmax 20vmax at 50% 0%, var(--glow2), transparent 70%),
      radial-gradient(18vmax 14vmax at 78% 18%, #fff, transparent 70%),
      radial-gradient(100vmax 22vmax at 50% 52%, var(--glow1), transparent 70%),
      radial-gradient(70vmax 12vmax at 30% 44%, var(--glow3), transparent 70%),
      linear-gradient(180deg, var(--glow3) 0%, var(--glow2) 30%, color-mix(in srgb, var(--glow1) 60%, var(--glow3)) 48%, var(--glow1) 58%, var(--bg) 72%, var(--bg)); }
  html[data-theme="frosted"][data-scene="blossom"] { --bg: #f1d3df; --glow1: #d47fa8; --glow2: #fff4f8; --glow3: #a596e0; --accent: #d9467a; }
  html[data-theme="frosted"][data-scene="blossom"] body::after { background:
      radial-gradient(36vmax 30vmax at 15% 15%, var(--glow1), transparent 70%),
      radial-gradient(44vmax 30vmax at 70% 10%, var(--glow2), transparent 70%),
      radial-gradient(36vmax 36vmax at 92% 60%, var(--glow3), transparent 70%),
      radial-gradient(30vmax 24vmax at 30% 70%, color-mix(in srgb, var(--glow1) 55%, var(--glow2)), transparent 70%),
      radial-gradient(50vmax 26vmax at 50% 100%, color-mix(in srgb, var(--glow3) 70%, var(--glow1)), transparent 70%),
      linear-gradient(180deg, var(--glow2), var(--bg)); }
  /* Your photo, softened like the scenes */
  html[data-theme="frosted"][data-scene="photo"] body::after { inset: -60px; background: var(--photo) center / cover no-repeat; filter: blur(22px) saturate(115%); }
  /* Night city: the same glass, dark */
  html[data-theme="frosted"][data-scene="night"] { --bg: #120f1c; --glow1: #ff7a3d; --glow2: #3b2a6b; --glow3: #2bb3c9; --text: #f4efe9; --muted: #aba2b6; --faint: rgba(255, 255, 255, .08); --panel: rgba(29, 24, 40, .6);
    --line: rgba(255, 255, 255, .14); --glass-base: #1d1828; --glass-tint: 45%; --accent: #ff7a3d; --on-accent: #1a0d05; --ink: #f4efe9; --good: #47d18c; --income: #47d18c; --spend: #ff7a3d; color-scheme: dark;
    --frost-edge: inset 0 1px 0 rgba(255, 255, 255, .16), inset 0 0 0 1px rgba(255, 255, 255, .05); }
  html[data-theme="frosted"][data-scene="night"] body::after { background:
      /* soft, dim city lights: bright spots behind text made it hard to read */
      radial-gradient(18vmax 18vmax at 14% 26%, color-mix(in srgb, var(--glow1) 34%, transparent), transparent 70%),
      radial-gradient(16vmax 16vmax at 70% 18%, color-mix(in srgb, color-mix(in srgb, var(--glow1) 60%, #ffd36b) 26%, transparent), transparent 70%),
      radial-gradient(18vmax 18vmax at 92% 52%, color-mix(in srgb, var(--glow3) 30%, transparent), transparent 70%),
      radial-gradient(16vmax 16vmax at 30% 70%, color-mix(in srgb, color-mix(in srgb, var(--glow3) 50%, #b56bff) 26%, transparent), transparent 70%),
      radial-gradient(60vmax 40vmax at 50% 20%, var(--glow2), transparent 70%),
      linear-gradient(180deg, #1b1530, var(--bg)); }
  html[data-theme="frosted"][data-scene="night"] :is(.panel, .daycards tbody.day, .daycards > .center-empty, .kpi, .insight, .extra, .split, .login form, .dash-bar) {
    background: linear-gradient(160deg, rgba(255, 255, 255, .1), rgba(255, 255, 255, .03)), color-mix(in srgb, var(--glass-base) var(--glass-tint), transparent);
    border-color: rgba(255, 255, 255, .13); box-shadow: var(--frost-edge), 0 12px 40px rgba(0, 0, 0, .35); }
  html[data-theme="frosted"][data-scene="night"] :is(.drawer, dialog, .calpop, .pickpop, .palette-box, .bulkbar, .ff-pop) { background: rgba(26, 21, 36, .82); border-color: rgba(255, 255, 255, .14); }
  html[data-theme="frosted"][data-scene="night"] :is(input[type=text], input[type=number], input[type=date], input[type=password], input[type=search], select, textarea, .round, .wctl button, .btn, .seg, .tabs) {
    background: rgba(255, 255, 255, .07); border-color: rgba(255, 255, 255, .14); box-shadow: none; }
  html[data-theme="frosted"][data-scene="night"] select option { background: #1d1828; }
  html[data-theme="frosted"][data-scene="night"] :is(.btn.primary, .seg button.on, .tabs a.on, .tabbar .plus, .fab) { background: var(--ink); color: #15111c; border-color: var(--ink); }
  html[data-theme="frosted"][data-scene="night"] :is(.tbl.feed td) { background: rgba(29, 24, 40, .45); border-color: rgba(255, 255, 255, .08); }
  html[data-theme="frosted"][data-scene="night"] .tbl.feed tr.dayhead div { background: rgba(26, 21, 36, .7); }
  html[data-theme="frosted"][data-scene="night"] .side { background: rgba(26, 21, 36, .5); border-color: rgba(255, 255, 255, .14); }
  html[data-theme="frosted"][data-scene="night"] .tabbar { background: rgba(26, 21, 36, .8); border-color: rgba(255, 255, 255, .14); }
  html[data-theme="frosted"][data-scene="night"] .tabbar a { color: var(--muted); }
  html[data-theme="frosted"][data-scene="night"] :is(.tab-pill, .nav a.on) { background: rgba(255, 255, 255, .16); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .2); }
  html[data-theme="frosted"][data-scene="night"] :is(.tabbar a.on, .nav a.on) { color: var(--text); }
  html[data-theme="frosted"][data-scene="night"] .track { background: rgba(255, 255, 255, .1); }
  html[data-theme="frosted"][data-scene="night"] .appbar.scrolled { background: rgba(18, 15, 28, .55); }
  /* Both glass themes: what's behind a card is only soft light (no card overlaps another), so a live blur under every card
     looks the same but makes the browser re-blur each card on every scroll frame, every entrance animation and, in
     Liquid glass, every frame of the moving light. Only floating things keep it (tab bar, sheets, menus).
     Same selector list as the rules that add the blur: :is() takes its most specific entry, so a shorter list loses. */
  html:is([data-theme="glass"], [data-theme="frosted"]) :is(.panel, .daycards tbody.day, .daycards > .center-empty, .kpi, .insight, .extra, .split, .dash-bar) { -webkit-backdrop-filter: none; backdrop-filter: none; }
  html[data-theme="glass"] .side { -webkit-backdrop-filter: none; backdrop-filter: none; }
  @media (max-width: 900px) {
    .tbl.feed tr.dayhead div { -webkit-backdrop-filter: none; backdrop-filter: none; }     /* one blur per day header adds up on a long feed */
    html[data-theme="glass"] .tbl.feed tr.dayhead div { background: rgba(20, 16, 42, .9); }
    html[data-theme="frosted"] .tbl.feed tr.dayhead div { background: rgba(255, 248, 240, .88); }
    html[data-theme="frosted"][data-scene="night"] .tbl.feed tr.dayhead div { background: rgba(30, 25, 40, .92); }
  }
  /* ---------- iPhone: notch / home bar, no accidental zoom, native-app shell ---------- */
  body { touch-action: manipulation; }
  /* iOS 26 draws its own blur under the status bar in a Home Screen app, reaching about 30pt below the safe area. Nothing in the page can turn it off,
     so the title bar, search field and other top controls sit --edge lower than the safe area. Landscape phones hide the status bar. */
  :root { --edge: 0px; }
  @media (orientation: portrait) { html.ios-web { --edge: 28px; } }
  /* No fixed strip behind the status bar: Safari 26 paints the colour of a fixed element at the top edge as a solid band there,
     which hid the page (see .scrim too). Without one, Safari and the Home Screen app show the page through their own blur. */
  html.native { -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; }
  html.native body { -webkit-user-select: none; user-select: none; }
  html.native input, html.native textarea, html.native [contenteditable] { -webkit-user-select: text; user-select: text; }
  @media (max-width: 900px) {
    main#view { padding-top: calc(22px + env(safe-area-inset-top, 0px)) !important;
      padding-left: calc(16px + env(safe-area-inset-left, 0px)) !important; padding-right: calc(16px + env(safe-area-inset-right, 0px)) !important; }
    input[type=text], input[type=number], input[type=date], input[type=password], input[type=search], select, textarea { font-size: 16px; }
    .drawer { max-height: calc(100dvh - env(safe-area-inset-top, 0px) - var(--edge) - 12px); }
  }
  /* ---------- App shell (iPhone app / Home Screen): iOS title bar, sheets, touch feel ---------- */
  .appbar { display: none; }
  @media (max-width: 900px) {
    html.shell .appbar { display: flex; position: fixed; top: 0; left: 0; right: 0; z-index: 18; height: calc(env(safe-area-inset-top, 0px) + var(--edge) + 44px);
      padding: calc(env(safe-area-inset-top, 0px) + var(--edge)) 70px 0; align-items: center; justify-content: center; pointer-events: none; transition: transform .3s cubic-bezier(.2, .8, .2, 1), background-color .2s, box-shadow .2s; }
    html.shell .appbar.scrolled { background: color-mix(in srgb, var(--bg) 80%, transparent); -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px);
      box-shadow: 0 .5px 0 color-mix(in srgb, var(--text) 18%, transparent); }
    html.shell .ab-title { font-weight: 600; font-size: 17px; opacity: 0; transform: translateY(6px); transition: opacity .18s, transform .18s; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    html.shell .appbar.scrolled .ab-title { opacity: 1; transform: none; }
    html.shell .ab-back { position: absolute; left: 4px; bottom: 0; height: 44px; display: flex; align-items: center; gap: 3px; padding: 0 10px 0 6px; color: var(--accent);
      text-decoration: none; font-size: 17px; pointer-events: auto; max-width: 45%; }
    html.shell .ab-back span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    html.shell .ab-back svg { width: 12px; height: 21px; flex: none; }
    html.shell .ab-back:active { opacity: .35; }
    html.shell main#view { padding-top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 44px) !important; }
    html.shell #view > .shell-hide { display: none; }
    html.shell .head { margin-bottom: 18px; }
    html.shell .head h1 { font-size: 32px; font-weight: 700; letter-spacing: .003em; line-height: 1.12; }
    html.shell .tbl.feed tr.dayhead { top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 44px); }
    html.shell .bulkbar { top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 52px); }
    html.shell .ptr { top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 44px); }
    /* Scrolling down slides the title bar away (phone.ts foldOnScroll); scrolling up or reaching the top brings it back */
    html.shell.scroll-down .appbar { transform: translateY(-100%); }
    html.shell :is(.tbl.feed tr.dayhead, .bulkbar) { transition: top .3s cubic-bezier(.2, .8, .2, 1); }
    html.shell.scroll-down .tbl.feed tr.dayhead { top: calc(env(safe-area-inset-top, 0px) + var(--edge)); }
    html.shell.scroll-down .bulkbar { top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 8px); }
    /* Pages without a back button have no title bar row: the large title starts just under the status bar blur, with search
       on its right (html.ab-row marks a back button, shell.ts). The title row's centre matches the search button's (top + 18px). */
    html.shell:not(.ab-row) .appbar { display: none; }
    html.shell:not(.ab-row) main#view { padding-top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 4px) !important; }
    html.shell:not(.ab-row) #view > .head { padding-right: 46px; }
    html.shell:not(.ab-row) :is(.tbl.feed tr.dayhead, .ptr) { top: calc(env(safe-area-inset-top, 0px) + var(--edge)); }
    html.shell:not(.ab-row) .bulkbar { top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 8px); }
    /* Native tab bar in the iPhone app replaces the web one */
    html.native-tabs .tabbar { display: none !important; }
    html.native-tabs main#view { padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 49px + 28px) !important; }
    html.native-tabs .undo, html.native-tabs .toast { bottom: calc(env(safe-area-inset-bottom, 0px) + 62px); }
    /* iOS page sheet: the page behind steps back while a sheet is open */
    html.shell #app { transform-origin: 50% var(--sy, 0px); transition: transform .42s cubic-bezier(.2,.9,.25,1), filter .42s; }
    html.shell.sheet-open #app { transform: scale(.94); filter: brightness(.9); }
    html.shell.sheet-open, html.shell.sheet-open body { background: #000; }
    html.shell .scrim { background: rgba(0, 0, 0, .25); }
    html.shell .drawer { border-radius: 14px 14px 0 0; }
  }
  /* Touch: iOS-style press feedback instead of mouse hover effects */
  @media (hover: none) {
    .btn:hover { border-color: var(--line); } .btn.primary:hover { opacity: 1; }
    .tbl tr.click:hover td { background: transparent; }
    .nav a:hover, .side .foot a:hover, .side .foot button:hover { background: none; }
    .insight:hover { transform: none; border-color: var(--line); }
    .tbl.feed .inl:hover { background: none; box-shadow: none; color: inherit; }
    .list a.row:hover .name { text-decoration: none; }
    .tbl.feed tr.click:active td, .tbl tr.click:active td { background: var(--faint); transition: none; }
    .btn:active, .round:active, .link:active, .list a.row:active, .insight:active, .hm-cell:active, .tabbar a:active, .pal-item:active, .wctl button:active { opacity: .45; transform: none; transition: none; }
    .tbl.feed .inl:active { background: var(--faint); }
  }
  /* Push / pop page transitions (like navigating in an iOS app) */
  html[data-vtdir="push"]::view-transition-old(page) { animation: vtPushOut .4s cubic-bezier(.2,.9,.25,1) both; }
  html[data-vtdir="push"]::view-transition-new(page) { animation: vtPushIn .4s cubic-bezier(.2,.9,.25,1) both; }
  html[data-vtdir="pop"]::view-transition-old(page) { animation: vtPopOut .34s cubic-bezier(.3,.8,.3,1) both; z-index: 2; }
  html[data-vtdir="pop"]::view-transition-new(page) { animation: vtPopIn .34s cubic-bezier(.3,.8,.3,1) both; }
  @keyframes vtPushIn { from { transform: translateX(100%); } }
  @keyframes vtPushOut { to { transform: translateX(-28%); opacity: .6; } }
  @keyframes vtPopIn { from { transform: translateX(-28%); opacity: .6; } }
  @keyframes vtPopOut { to { transform: translateX(100%); } }
  /* ---------- View Transitions ---------- */
  ::view-transition-group(*) { animation-duration: .32s; animation-timing-function: cubic-bezier(.2,.8,.2,1); }
  html.vt-page #view { view-transition-name: page; }
  /* The tab bar stays put while the page under it moves (otherwise the page's picture covers it mid-slide) */
  html.vt-page .tabbar { view-transition-name: tabbar; }
  ::view-transition-group(tabbar), ::view-transition-new(tabbar) { animation: none; }
  ::view-transition-old(tabbar) { display: none; }
  ::view-transition-old(page) { animation: vtFadeOut .14s ease both; }
  ::view-transition-new(page) { animation: vtFadeUp .26s cubic-bezier(.2,.8,.2,1) both; }
  /* (Another month never slides the page: it changes in place, see core/morph.ts. Only the month's name slides.) */
  ::view-transition-old(ptitle), ::view-transition-new(ptitle) { height: 100%; width: auto; object-fit: contain; object-position: left center; }
  ::view-transition-group(ptitle) { animation-duration: .36s; z-index: 2; }
  html.vt-drawer-open .drawer, html.vt-drawer-close .drawer { view-transition-name: drawer; transition: none; }
  html.vt-drawer-open .scrim, html.vt-drawer-close .scrim { view-transition-name: scrim; transition: none; }
  html.vt-drawer-open::view-transition-old(drawer) { display: none; }
  html.vt-drawer-open::view-transition-new(drawer) { animation: vtInDrawer .34s cubic-bezier(.2,.8,.2,1) both; }
  html.vt-drawer-close::view-transition-new(drawer) { display: none; }
  html.vt-drawer-close::view-transition-old(drawer) { animation: vtOutDrawer .26s cubic-bezier(.4,0,.6,1) both; }
  ::view-transition-group(txav) { animation-duration: .38s; z-index: 5; }
  ::view-transition-old(txav), ::view-transition-new(txav) { height: 100%; width: 100%; }
  ::view-transition-old(txav) { animation: vtFadeOut .12s ease both; }
  /* An Overview tile changing size (or moving): the card's outline glides to its new shape while its contents stay
     their real size, pinned top left, instead of the old picture being stretched to fit (big blurry numbers) */
  ::view-transition-group(.dw) { overflow: clip; border-radius: var(--radius); }
  ::view-transition-old(.dw), ::view-transition-new(.dw) { width: 100%; height: 100%; object-fit: none; object-position: left top; }
  ::view-transition-old(.dw) { animation-duration: .16s; }
  /* the payee and amount grow from the row into the sheet's header (and back), held at their left edge like the page title */
  ::view-transition-group(txname), ::view-transition-group(txamt) { animation-duration: .38s; z-index: 5; }
  ::view-transition-old(txname), ::view-transition-new(txname), ::view-transition-old(txamt), ::view-transition-new(txamt) { height: 100%; width: auto; object-fit: contain; object-position: left center; }
  ::view-transition-new(txav) { animation: none; }
  html.vt-theme::view-transition-old(root), html.vt-theme::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
  html.vt-theme::view-transition-group(root) { animation-duration: .52s; }
  @keyframes vtFadeOut { to { opacity: 0; } }
  @keyframes vtFadeUp { from { opacity: 0; transform: translateY(10px); } }
  @keyframes vtInDrawer { from { transform: translateX(100%); } }
  @keyframes vtOutDrawer { to { transform: translateX(100%); } }
  @keyframes vtInSheet { from { transform: translateY(100%); } }
  @keyframes vtOutSheet { to { transform: translateY(100%); } }
  /* Printing (or saving as PDF): ink on white whatever the theme, no floating controls, and the coloured dots, bars and fills kept.
     Browsers drop background colours when printing unless told otherwise, and that's what the bars and legend dots are made of;
     Safari also prints a fading SVG gradient as a solid block, so chart fills become a flat tint of their colour (set on the path). */
  @media print {
    /* Prints in the chosen theme, backdrop and all. Browsers drop backgrounds unless "Background graphics" is ticked,
       and print-color-adjust: exact overrides that for the page, cards, bars, dots and swatches. No page margin so the
       theme's background reaches the paper's edges; main's padding repeats on every page instead (box-decoration-break). */
    @page { margin: 0; }
    html, body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    *, *::before, *::after { -webkit-print-color-adjust: exact; print-color-adjust: exact; text-shadow: none !important; }
    html { --edge: 0px !important; }
    .side, .head .actions, .toolbar, .drawer, .scrim, .toast, .undo, .tabbar, .fab, .search-btn, .demo-badge, .tip, .ptr, .offline, .saved-reports, .save-report { display: none !important; }
    .app { display: block; }
    main, main#view { padding: 14mm 12mm !important; max-width: none; -webkit-box-decoration-break: clone; box-decoration-break: clone; }
    :is(.panel, .kpi, .insight, .extra, .split, .dash-bar, .daycards tbody.day) { box-shadow: none !important; animation: none !important; }
    .panel, .kpi, .part, .tbl tr { break-inside: avoid; }
    .report-cols .panel { break-inside: auto; }
    .flow.on { display: grid !important; grid-template-columns: minmax(0, 1fr) !important; grid-auto-rows: auto !important; row-gap: 20px !important; margin-bottom: 20px !important; }
    .flow > * { grid-column: 1 / -1 !important; grid-row: auto !important; }
    .panel-head, .panel .lead, .panel h2 { break-after: avoid; } .donut { break-inside: avoid; }
    /* WebKit prints a stop-opacity gradient as a solid block: flat tint instead */
    :is(.area, .soft) { fill: currentColor; fill-opacity: .16; animation: none !important; }
    :is(.draw, .fill, .seg, .bar) { animation: none !important; } .fill { box-shadow: none !important; }
    /* iOS WebKit prints a fixed backdrop once, as a block on the last page: the plain theme colour there */
    @supports (-webkit-touch-callout: none) { body::before, body::after { display: none !important; } }
  }
  @media (max-width: 900px) {
    .tabbar:not([hidden]) { display: grid; }
    .fab { right: calc(14px + env(safe-area-inset-right, 0px)); bottom: calc(13px + env(safe-area-inset-bottom, 0px)); z-index: 17;
      opacity: 0; visibility: hidden; transform: scale(.4); pointer-events: none; transition: transform .2s ease, opacity .15s ease, visibility 0s .2s; }
    .fab.on { opacity: 1; visibility: visible; transform: none; pointer-events: auto; transition: transform .42s cubic-bezier(.3, 1.5, .5, 1) .1s, opacity .2s ease .1s, visibility 0s; }
    .side { display: none !important; }
    main { padding-bottom: 110px !important; }
    .btn { padding: 10px 16px; } .btn.small { padding: 8px 13px; }
    .tbl td { padding-top: 14px; padding-bottom: 14px; }
    .tbl .sel { display: none; }
    .app { grid-template-columns: 1fr; }
    .side { position: sticky; z-index: 5; height: auto; flex-direction: row; align-items: center; padding: 10px 12px; overflow-x: auto; background: var(--bg); border-right: 0; border-bottom: 1px solid var(--line); }
    .brand { padding: 0 10px 0 4px; }
    .nav { display: flex; gap: 2px; }
    .nav a span { display: none; }
    html[data-theme="carbon"] .nav a.on::before { display: none; }
    .side .spacer, .nav-h, .side .foot > :not(#navFoot) { display: none; }
    .side .foot .nav a { margin: 0; } .side .foot .nav a span { display: none; }
    main { padding: 22px 16px 60px; }
    .hero, .grid { grid-template-columns: 1fr; }
    .big { font-size: 40px; }
    .stats { gap: 12px; } .stat .val { font-size: 19px; }
    .panel { padding: 20px; }
    .tbl .hide-s { display: none; }
    /* Names in share lists wrap on a phone instead of being cut short with an ellipsis ("Old Accounts (untracked)") */
    .part .name, .part .name .sub-s { white-space: normal; overflow-wrap: anywhere; }
    /* The bills table has to fit a 320px phone, Edit buttons included: tighter cell padding, and a long name wraps */
    .tbl.bills-tbl :is(td, th) { padding-left: 10px; padding-right: 10px; }
    .tbl.bills-tbl td:first-child { min-width: 0; overflow-wrap: break-word; }
    .tbl.bills-tbl td.amt { white-space: normal; }
    /* Pay now stacks over Edit so both fit beside the amount; same width, so their edges line up */
    .tbl.bills-tbl td.act .btn:is(.primary) { display: block; width: 100%; margin-bottom: 6px; }
    .tbl.bills-tbl td.act .btn:not(.primary):not(:first-child) { display: block; width: 100%; }
    .cat-s { display: inline; }
    .show-s { display: initial; }
    div.show-s { display: block; }
    .tbl .namecol { min-width: 0; }
    .tbl input.inline { width: 96px; }
    .grid2.stack-s { grid-template-columns: 1fr; }
    form.range { flex-basis: 100%; gap: 6px; }
    form.range > span { display: none; }
    .stat input { max-width: 100%; }
    .tbl.feed, .tbl.feed.has-sel { --cols: minmax(0, 1fr) auto; }
    .tbl.feed .hide-s, .tbl.feed .sel { display: none !important; }
    .tbl.feed tr.dayhead div { padding: 7px 12px; }
    .av { width: 38px; height: 38px; }
    .insights { grid-auto-columns: 82%; margin-top: -18px; }
    .dhero h2 { max-width: 62vw; }
    /* Bottom sheet */
    .drawer { top: auto; left: 0; right: 0; width: 100%; max-height: 92vh; max-height: 92dvh; border-radius: 22px 22px 0 0; transform: translateY(105%); box-shadow: 0 -10px 40px rgba(0,0,0,.25); }
    .drawer.open { transform: none; }
    .drawer.dragging { transition: none !important; }
    .drawer::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 120px; background: inherit; }     /* pulled up past its place (gestures.ts): no gap under it */
    .drawer .body { overscroll-behavior-y: contain; }
    .grab { display: block; width: 40px; height: 5px; border-radius: 99px; background: var(--line); margin: 9px auto -4px; flex: none; }
    .drawer header { padding-top: 14px; touch-action: none; }
    .drawer footer { padding-bottom: calc(16px + env(safe-area-inset-bottom)); }
    /* More buttons than fit (Delete, Duplicate, Refund…): the ones before the gap go on a row of their own */
    .drawer footer:is(:has(> .btn + .btn + .grow), :has(> .btn + .grow + .btn + .btn)) { flex-wrap: wrap; }
    .drawer footer:is(:has(> .btn + .btn + .grow), :has(> .btn + .grow + .btn + .btn)) > .grow { flex: 0 0 100%; margin: -5px 0; }
    .drawer footer:is(:has(> .btn + .btn + .grow), :has(> .btn + .grow + .btn + .btn)) > .btn { flex: 1 1 0; justify-content: center; text-align: center; padding-left: 8px; padding-right: 8px; }
    html.vt-drawer-open::view-transition-new(drawer) { animation-name: vtInSheet; }
    html.vt-drawer-close::view-transition-old(drawer) { animation-name: vtOutSheet; }
    .cards.editing .w { cursor: default; }
    .wctl .grip { touch-action: none; font-size: 20px; padding: 4px 8px 4px 2px; margin: -4px 0; }
    .tbl td, .tbl th { padding-left: 12px; padding-right: 12px; }
  }
  @media (max-width: 400px) {
    .tbl td, .tbl th { padding-left: 9px; padding-right: 9px; }
  }

  /* ---------- Universal search on phones: a search button up top, and a full-screen search ---------- */
  @media (max-width: 900px) {
    .search-btn { display: grid; place-items: center; position: fixed; z-index: 19; top: calc(env(safe-area-inset-top, 0px) + 6px); right: calc(16px + env(safe-area-inset-right, 0px));
      width: 36px; height: 36px; padding: 0; border-radius: 50%; border: 1px solid var(--line); background: color-mix(in srgb, var(--panel) 85%, transparent);
      -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); color: var(--text); cursor: pointer; }
    .search-btn svg { width: 18px; height: 18px; }
    html.shell .search-btn { top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 4px); right: calc(10px + env(safe-area-inset-right, 0px)); border-color: transparent; background: none; -webkit-backdrop-filter: none; backdrop-filter: none; color: var(--accent); }
    html.shell .search-btn svg { width: 22px; height: 22px; }
    /* leaves with the title bar, by the same distance so the two move as one */
    .search-btn { transition: transform .3s cubic-bezier(.2, .8, .2, 1), opacity .2s; }
    html.scroll-down .search-btn { transform: translateY(calc(-1 * (env(safe-area-inset-top, 0px) + var(--edge) + 44px))); opacity: 0; pointer-events: none; }
    html:not(.shell) #view > .head:first-child { padding-right: 52px; }
    html.pal-open .search-btn, #app[hidden] ~ .search-btn { display: none; }
    .palette { padding: 0; align-items: stretch; background: var(--bg); }
    .palette-box { width: 100%; height: 100vh; height: 100dvh; border: 0; border-radius: 0; box-shadow: none; display: flex; flex-direction: column; background: var(--bg); animation: none; }
    .pal-top { padding: calc(env(safe-area-inset-top, 0px) + max(8px, var(--edge) + 4px)) calc(14px + env(safe-area-inset-right, 0px)) 8px calc(16px + env(safe-area-inset-left, 0px)); border-bottom: 0; gap: 12px; }
    .pal-top #palQ { padding: 10px 12px 10px 36px; margin-left: -34px; border-radius: 10px; background: var(--faint); font-size: 17px; }
    .pal-top > svg { position: relative; z-index: 1; margin-left: 10px; }
    .pal-cancel { display: block; color: var(--accent); }
    .pal-list { flex: 1; max-height: none; padding: 4px calc(8px + env(safe-area-inset-right, 0px)) calc(20px + env(safe-area-inset-bottom, 0px)) calc(8px + env(safe-area-inset-left, 0px)); -webkit-overflow-scrolling: touch; }
    .pal-item { padding: 12px 10px; min-height: 48px; }
    .pal-item.on { background: none; box-shadow: none; }
    .pal-foot { display: none; }
  }
  @media (max-width: 900px) and (hover: none) { .pal-item:active { background: var(--faint); opacity: 1; } }

  /* ---------- Bills calendar ---------- */
  .bcal-dow, .bcal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
  .bcal-dow span { font-size: 11.5px; color: var(--muted); padding: 0 4px 6px; }
  .bcal-day { min-height: 92px; border-radius: 10px; background: var(--faint); padding: 6px; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
  .bcal-day.pad { background: none; }
  .bcal-day.past:not(.today) .d { opacity: .55; }
  .bcal-day.today { box-shadow: inset 0 0 0 2px var(--accent); }
  .bcal-day .d { font-size: 12px; color: var(--muted); }
  .bpill { display: flex; justify-content: space-between; gap: 6px; border: 0; border-radius: 7px; padding: 3px 6px; font: inherit; font-size: 11.5px; cursor: pointer; text-align: left;
    color: var(--text); background: color-mix(in srgb, var(--tone, var(--muted)) 18%, var(--panel)); border-left: 3px solid var(--tone, var(--muted)); }
  .bpill .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .bpill .num { flex: none; color: var(--muted); }
  .bpill.good { --tone: var(--good); } .bpill.warn { --tone: var(--warn); } .bpill.bad { --tone: var(--spend); }
  .bpill:hover { filter: brightness(1.05); }
  .bcal-agenda { display: none; }
  .bcal-agenda-day + .bcal-agenda-day { margin-top: 16px; }
  .bcal-agenda button.row { width: 100%; border: 0; border-top: 1px solid var(--line); background: none; font: inherit; color: inherit; padding: 9px 0; cursor: pointer; text-align: left; }
  @media (max-width: 700px) {
    .bcal { display: none; } .bcal-agenda { display: block; }
  }

  /* ---------- Monthly recap ---------- */
  .recap-notes { margin-bottom: 20px; }
  .recap-notes ul { margin: 0; padding-left: 18px; display: grid; gap: 6px; font-size: 15px; }
  .list button.row { width: 100%; border: 0; border-top: 1px solid var(--line); background: none; font: inherit; color: inherit; padding: 10px 0; cursor: pointer; text-align: left; align-items: center; }
  .list button.row:first-child { border-top: none; padding-top: 0; }
  .list button.row:hover .name > span:last-child { text-decoration: underline; text-underline-offset: 3px; }

  /* ---------- Payee page ---------- */
  .payee-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 14px 24px; }

  /* ---------- Forecast what-ifs ---------- */
  .whatif { display: grid; grid-template-columns: 2fr 1fr 1fr 1.2fr 1.2fr auto; gap: 8px; align-items: center; }
  @media (max-width: 800px) { .whatif { grid-template-columns: 1fr 1fr; } .whatif input[name=label] { grid-column: 1 / -1; } }

  /* ---------- Review queue ---------- */
  .review-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-top: 1px solid var(--line); transition: opacity .2s, transform .2s; }
  .review-row:first-child { border-top: none; padding-top: 0; }
  .review-row.gone { opacity: 0; transform: translateX(24px); }
  .review-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; border: 0; background: none; font: inherit; color: inherit; cursor: pointer; text-align: left; padding: 0; }
  .review-main .txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
  .review-main .desc, .review-main .sub-s { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .review-main:hover .desc { text-decoration: underline; text-underline-offset: 3px; }
  @media (max-width: 600px) { .review-row { flex-wrap: wrap; } .review-row > .btn { margin-left: auto; } }

  /* Map page (pages/map.ts): a Leaflet map of OpenStreetMap pictures with pins on top */
  /* isolate keeps Leaflet's z-index 400–1000 panes and controls under the tab bar and other fixed chrome */
  .map { height: min(62vh, 560px); min-height: 320px; background: var(--faint, #eee); touch-action: none; overscroll-behavior: contain; isolation: isolate; }
  .map.leaflet-container { background: var(--faint, #eee); font: inherit; font-size: 12px; }
  .leaflet-marker-icon.map-pin { border-radius: 50%; border: 2px solid #fff; background: var(--spend, #e5484d); color: #fff; font-size: 12px; font-weight: 600; line-height: 1;
    display: grid; place-items: center; cursor: pointer; box-shadow: 0 2px 6px rgba(0,0,0,.3); box-sizing: border-box; }
  .leaflet-marker-icon.map-pin.on { background: var(--accent); color: var(--on-accent); }
  form.ask { display: flex; align-items: center; gap: 10px; background: var(--panel); border: 1px solid var(--line); border-radius: 14px; padding: 6px 14px; margin-bottom: 16px; }
  form.ask:focus-within { border-color: var(--muted); }
  form.ask svg { width: 18px; height: 18px; color: var(--muted); flex: none; }
  /* #askQ: an id so no theme input rule (Frosted night's html[data-theme][data-scene] :is(…) is 0,3,2) can paint a second box inside the bar */
  form.ask #askQ, form.ask #askQ:focus { flex: 1; min-width: 0; width: 100%; border: 0; border-radius: 0; background: transparent; font-size: 16px; padding: 8px 0; outline: none; box-shadow: none; -webkit-appearance: none; appearance: none; }
  /* :not(#_) adds id weight so Frosted night's input fill (0,3,2) can't win here either */
  html .pickpop input[type]:not(#_), html .pickpop input[type]:not(#_):focus { background: transparent; box-shadow: none; border-color: var(--line); }
  .ask-answer .ask-title { font-size: 18px; line-height: 1.45; margin: 6px 0 12px; }
  .ask-answer .list button.row { width: 100%; text-align: left; font: inherit; color: inherit; background: none; border: 0; border-top: 1px solid var(--line); border-radius: 0; padding-left: 0; padding-right: 0; cursor: pointer; }
  .ask-answer { margin-bottom: 16px; }
  .ask-examples { display: flex; flex-wrap: wrap; gap: 8px; }
  .ask-examples .chip { font-size: 13px; padding: 5px 12px; border: 0; cursor: pointer; white-space: normal; text-align: left; }

  /* ---------- Transaction feed: day titles, category colours, quieter amounts ---------- */
  .tbl.feed tr.dayhead .dd { color: var(--muted); font-weight: 500; }
  .tbl.feed tr.dayhead .num { padding: 2px 9px; border-radius: 999px; background: var(--faint); color: var(--text); font-size: 12px; font-weight: 600; }
  .tbl.feed tr.dayhead .num.pos { color: var(--good); background: color-mix(in srgb, var(--good) 13%, transparent); }
  /* Money out reads in the text colour; only money in is green */
  .tbl.feed .amt { font-weight: 600; }
  .tbl.feed .amt.neg { color: var(--text); text-shadow: none; }
  /* The category's colour (the same as its slice in "Where it went"): a ring round the avatar and a dot by its name */
  .tbl.feed tr[style*="--cat"] .av { outline: 2px solid var(--cat); outline-offset: 2px; }
  .tbl.feed tr[style*="--cat"] :is(.cat-s, .catcol > span)::before { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--cat); margin-right: 6px; vertical-align: 1px; }
  .tbl.feed tr[style*="--cat"] :is(.cat-s, .catcol > span) { color: color-mix(in oklab, var(--cat) 38%, var(--muted)); }
  .bud-s, .tag-s { font-size: 12.5px; color: var(--muted); margin-right: 8px; white-space: nowrap; }
  /* Swipe a row left for Categorize and Delete (features/phone.ts): the cells slide by --sx and the buttons fill the gap */
  .tbl.feed tr[data-gid] { position: relative; }
  .tbl.feed tr[data-sw] > td:not(.swacts) { transform: translateX(var(--sx, 0px)); transition: transform .34s cubic-bezier(.25, 1, .4, 1); }
  .tbl.feed tr[data-sw].swiping > td { transition: none; }
  .tbl.feed tr[data-gid] td.swacts { position: absolute; top: 0; right: 0; bottom: 0; width: calc(-1 * var(--sx, 0px)); padding: 0; border: 0; display: flex; align-items: stretch; overflow: hidden; transition: width .34s cubic-bezier(.25, 1, .4, 1); }
  .swacts button { flex: 1 1 0; min-width: 0; border: 0; border-radius: 0; padding: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
    font: inherit; font-size: 11.5px; font-weight: 600; color: #fff; overflow: hidden; white-space: nowrap; cursor: pointer; }
  .swacts .sw-cat { background: #4a7bd8; }
  .swacts .sw-del { background: var(--spend); }
  .swacts svg { width: 20px; height: 20px; flex: none; }

  /* ---------- Transactions page: the month at a glance, search and filters in one row, each day a card ---------- */
  .txsum { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px 36px; padding: 18px 22px; margin-bottom: 16px; }
  .txsum .ts-k { display: flex; gap: 30px; }
  .txsum .ts-k > div { display: flex; flex-direction: column; gap: 3px; }
  .txsum .ts-k strong { font-size: 21px; font-weight: 600; letter-spacing: -.01em; }
  .txsum .ts-k strong.pos { text-shadow: none; }
  .dbars { flex: 1 1 280px; display: flex; align-items: flex-end; gap: 2px; height: 48px; min-width: 0; }
  .dbars .db { flex: 1 1 0; min-width: 0; height: 100%; padding: 0; border: 0; background: none; display: flex; align-items: flex-end; cursor: pointer; }
  .dbars .db i { display: block; width: 100%; height: max(3px, calc(var(--v) * 100%)); border-radius: 3px; background: color-mix(in srgb, var(--spend) 50%, var(--faint)); transform-origin: bottom; transition: background .15s; }
  .dbars .db:hover i, .dbars .db:active i { background: var(--spend); }
  .dbars .db.now i { background: var(--accent); }
  .dbars .db.fut { cursor: default; }
  .dbars .db.fut i { background: var(--faint); }
  #view.anim:not(.calm) .dbars .db i { animation: growUp .55s cubic-bezier(.2, .8, .2, 1) both; }
  .txbar { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; }
  .txbar .search { flex: 0 1 340px; max-width: none; }
  .txbar .search input { border-radius: 999px; }
  .txbar .tabs { margin: 0; flex: none; }
  .txbar .selall { margin-left: auto; }
  .daycards tbody.day { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: clip; margin-bottom: 12px;
    scroll-margin-top: calc(env(safe-area-inset-top, 0px) + 14px); -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
  .daycards .tbl.feed tr.dayhead { position: static; }
  .daycards .tbl.feed tr.dayhead td { border-bottom: 0; }
  #view .daycards .tbl.feed tr.dayhead div { background: none; -webkit-backdrop-filter: none; backdrop-filter: none; box-shadow: none; padding: 13px 16px 2px; font-size: 13px; letter-spacing: 0; color: var(--text); }
  .daycards > .center-empty { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); }
  .daycards tbody.day.flash { animation: dayFlash 1.4s ease; }
  @keyframes dayFlash { 0%, 35% { box-shadow: 0 0 0 2px var(--accent); } 100% { box-shadow: 0 0 0 2px transparent; } }
  @media (max-width: 900px) {
    .head .new-tx { display: none; }       /* the + in the corner does this */
    .txsum { padding: 14px 16px; gap: 12px; }
    .txsum .ts-k { width: 100%; justify-content: space-between; gap: 12px; }
    .txsum .ts-k strong { font-size: 19px; }
    .dbars { flex-basis: 100%; height: 40px; }
    .txbar { gap: 8px; }
    /* Search folds to a round button next to the filters, and takes the row while it's in use */
    .txbar .search { flex: 0 0 40px; min-width: 0; transition: flex-basis .25s ease; }
    /* padding 0 while folded: the 38px icon padding made the input 52px wide, spilling out of its 40px circle under the filters */
    .txbar .search input { height: 40px; padding: 0; }
    .txbar.searching .search input, .txbar .search:focus-within input, .txbar .search input:not(:placeholder-shown) { padding: 0 12px 0 38px; }
    .txbar .search input::placeholder { color: transparent; }
    .txbar .search:focus-within input::placeholder { color: var(--muted); }
    .txbar.searching .search, .txbar .search:focus-within, .txbar .search:has(input:not(:placeholder-shown)) { flex: 1 1 auto; }
    .txbar.searching .tabs, .txbar:has(.search:focus-within) .tabs, .txbar:has(.search input:not(:placeholder-shown)) .tabs { display: none; }
    /* the filters fill the rest of the row, so it ends on the cards' right edge */
    .txbar .tabs { flex: 1 1 auto; min-width: 0; overflow-x: auto; }
    .txbar .tabs a { flex: 1 0 auto; padding: 7px 11px; text-align: center; }
    .txbar .selall { display: none; }
    @media (max-width: 380px) { .txbar .tabs a { padding: 7px 8px; } }
    #view .daycards .tbl.feed tr.dayhead div { padding: 12px 14px 2px; }
  }

  .txsum:empty { min-height: 95px; }
  .txsum.wait { pointer-events: none; }
  .txsum.wait > * { opacity: .55; transition: opacity .2s ease .15s; }
  @media (max-width: 900px) { .txsum:empty { min-height: 136px; } }

  /* ---------- Hours of work (core/worklens.ts): under money out in the feed ---------- */
  .tbl.feed tr[data-gid] td.amt:has(.hrs) { flex-direction: column; align-items: flex-end; justify-content: center; gap: 1px; }
  .tbl .amt .hrs { font-size: 11.5px; font-weight: 500; color: var(--muted); letter-spacing: .01em; }
  .set-num { width: 110px; text-align: right; }

  /* ---------- Net worth time scrubber (features/networth.ts) ---------- */
  .nw-scrub { position: relative; margin-top: auto; touch-action: pan-y; cursor: ew-resize; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
  .panel.stat.nw .nw-scrub .mini-line { margin-top: 0; }
  .nw-scrub .nw-guide { position: absolute; top: 12px; bottom: 0; left: var(--x, 0); width: 0; border-left: 1px dashed var(--muted); opacity: 0; pointer-events: none; transition: opacity .15s; }
  .nw-scrub .nw-knob { position: absolute; left: var(--x, 0); top: var(--ky, 0); width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: 50%;
    box-shadow: 0 0 0 2.5px var(--panel); opacity: 0; pointer-events: none; transition: opacity .15s; }
  .nw.scrubbing .nw-guide, .nw.scrubbing .nw-knob { opacity: 1; }
  .nw.scrubbing > .big { color: var(--accent); }
  .nw-open { margin-left: 4px; font: inherit; font-weight: 600; }

  /* ---------- Year in Money (pages/year.ts): a full-screen story ---------- */
  html:has(#story) { overflow: hidden; }
  html:has(#story) .tabbar, html:has(#story) .fab { visibility: hidden; }
  .story { --st-pad: max(28px, calc(50% - 240px)); position: fixed; inset: 0; z-index: 60; background: var(--bg); color: var(--text); overflow: hidden; touch-action: none;
    -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
  /* The bars, the × and every card's text share one column (--st-pad), so their edges line up */
  .st-prog { position: absolute; z-index: 3; top: calc(10px + env(safe-area-inset-top) + var(--edge)); left: var(--st-pad); right: var(--st-pad); display: flex; gap: 4px; }
  .st-prog i { flex: 1; height: 3px; border-radius: 2px; background: color-mix(in srgb, var(--text) 16%, transparent); overflow: hidden; }
  .st-prog b { display: block; height: 100%; width: 0; background: var(--text); }
  .st-prog i.done b { width: 100%; }
  .st-prog i.run b { animation: stFill 6.5s linear forwards; }
  .story.paused .st-prog i.run b { animation-play-state: paused; }
  @keyframes stFill { to { width: 100%; } }
  /* An icon, not the × character: a font's × sits on its baseline, so it lands low and differs by system font */
  .st-x { position: absolute; z-index: 4; top: calc(22px + env(safe-area-inset-top) + var(--edge)); right: var(--st-pad); width: 40px; height: 40px; min-width: 0; min-height: 0; padding: 0; margin: 0;
    display: grid; place-items: center; border: 0; border-radius: 50%; background: color-mix(in srgb, var(--text) 9%, transparent); color: var(--text); line-height: 0; cursor: pointer; }
  .st-x svg { display: block; width: 16px; height: 16px; }
  .st-deck { position: absolute; inset: 0; }
  .st-card { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: center; gap: 6px;
    padding: calc(80px + env(safe-area-inset-top) + var(--edge)) var(--st-pad) calc(60px + env(safe-area-inset-bottom));
    background: radial-gradient(90% 45% at 50% 0%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 70%);
    opacity: 0; visibility: hidden; transform: scale(1.04); transition: opacity .35s ease, transform .5s cubic-bezier(.2, .8, .2, 1), visibility 0s .35s; }
  .st-card.on { opacity: 1; visibility: visible; transform: none; transition: opacity .35s ease, transform .5s cubic-bezier(.2, .8, .2, 1); }
  .st-card.past { transform: scale(.96); }
  .st-in { opacity: 0; transform: translateY(18px); }
  .st-card.on .st-in { animation: stIn .6s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(var(--d) * .32s + .1s); }
  @keyframes stIn { to { opacity: 1; transform: none; } }
  .st-kick { font-size: 13px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin-bottom: 8px; }
  .st-big { font-size: clamp(84px, 26vw, 150px); line-height: .95; margin: 0; font-weight: 800; letter-spacing: -.04em; }
  .st-h { font-size: clamp(30px, 8vw, 44px); line-height: 1.1; margin: 2px 0; font-weight: 750; letter-spacing: -.02em; overflow-wrap: anywhere; }
  .st-num { font-size: clamp(46px, 14vw, 76px); line-height: 1.05; font-weight: 700; color: var(--accent); letter-spacing: -.03em; margin: 2px 0 6px; }
  .st-num small { font-size: .42em; font-weight: 650; letter-spacing: 0; opacity: .85; }
  .st-p { font-size: 18px; line-height: 1.4; margin: 4px 0; color: var(--muted); }
  .st-p strong { color: var(--text); }
  .st-p strong { font-weight: 750; }
  .st-hint { margin-top: 28px; font-size: 14px; opacity: .65; animation: stPulse 1.8s ease-in-out infinite; }
  @keyframes stPulse { 50% { opacity: .3; } }
  .st-years { display: flex; gap: 10px; margin-top: 18px; }
  .st-yr { color: var(--text); text-decoration: none; font-weight: 600; font-size: 14px; padding: 7px 14px; border-radius: 999px; background: var(--faint, var(--line)); }
  .st-day { height: 14px; border-radius: 7px; background: var(--faint, var(--line)); margin: 16px 0 10px; overflow: hidden; }
  .st-day i { display: block; height: 100%; width: 0; border-radius: 7px; background: var(--accent); }
  .st-card.on .st-day i { animation: stGrow 1.1s cubic-bezier(.2, .8, .2, 1) .9s forwards; }
  @keyframes stGrow { to { width: var(--p); } }
  .st-bars { display: grid; gap: 8px; margin-top: 18px; }
  .st-bar { position: relative; display: flex; justify-content: space-between; gap: 10px; padding: 9px 12px; border-radius: 10px; font-size: 15px; overflow: hidden; background: var(--faint, var(--line)); }
  .st-bar::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 0; background: color-mix(in srgb, var(--accent) 22%, transparent); border-radius: inherit; }
  .st-card.on .st-bar::before { animation: stGrowW .9s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(.8s + var(--j) * .08s); }
  @keyframes stGrowW { to { width: var(--w); } }
  .st-bar > * { position: relative; }
  .st-bar span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .st-list { display: grid; gap: 4px; margin-top: 16px; }
  .st-row { display: flex; align-items: center; gap: 10px; font-size: 15px; padding: 6px 0; border-top: 1px solid var(--line); }
  .st-row span:not(.av) { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .st-months { display: grid; grid-template-columns: repeat(12, 1fr); gap: 5px; align-items: end; height: 170px; margin: 18px 0 8px; }
  .st-months > div { display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 6px; height: 100%; }
  .st-months i { display: block; width: 100%; height: 0; border-radius: 5px; background: color-mix(in srgb, var(--text) 22%, transparent); }
  .st-months .hi i { background: var(--accent); }
  .st-months .fut i { background: var(--line); }
  .st-card.on .st-months i { animation: stGrowH .8s cubic-bezier(.2, .8, .2, 1) both; animation-delay: calc(.7s + var(--j) * .05s); }
  @keyframes stGrowH { from { height: 0; } to { height: max(3px, var(--v)); } }
  .st-months span { font-size: 11px; opacity: .7; }
  .st-btns { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 26px; }
  .st-btns .btn { min-width: 0; }
  
  @media (prefers-reduced-motion: reduce) {
    .st-card, .st-card.on { transition: opacity .2s; transform: none; }
    .st-card.on .st-in, .st-card.on .st-day i, .st-card.on .st-bar::before, .st-card.on .st-months i { animation-duration: .01s; animation-delay: 0s; }
  }
  /* ---------- Bento Overview (dashboard.ts): tiles small, medium or large; phones two small ones to a row ---------- */
  .cards .w { container-type: inline-size; }
  .cards.bento { display: grid; grid-template-columns: repeat(var(--cols, 12), minmax(0, 1fr)); gap: var(--gap); align-items: stretch; grid-auto-flow: row; margin-bottom: var(--gap); }
  .cards.bento.editing { grid-auto-flow: row; }
  .cards.bento > .w, .cards.bento .dash-rest > .w { display: flex; flex-direction: column; margin: 0; min-width: 0; grid-column: span 6; }
  .cards.bento > .w > .panel, .cards.bento .dash-rest > .w > .panel { flex: 1; }
  .cards.bento > .w.m, .cards.bento .dash-rest > .w.m { grid-column: span 12; }      /* a unit is 6 columns (BENTO_SUB) */
  .cards.bento > .w.l, .cards.bento .dash-rest > .w.l { grid-column: 1 / -1; }
  .cards.bento .dash-rest:not([hidden]) { display: contents; }
  .cards.bento .dash-more { grid-column: 1 / -1; }
  .cards.bento .wctl { flex-wrap: nowrap; }
  .cards.bento .wctl strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .cards.bento .wctl button { flex: none; }
  @media (max-width: 900px) {
    .dash-wrap { --gap: 12px; }
    .cards.bento .panel { padding: 18px 16px; }
    .cards.bento .panel h2 { font-size: 14.5px; }
  }
  /* number tiles */
  .panel.stat { display: flex; flex-direction: column; }
  .panel.stat .eyebrow { margin-bottom: 6px; }
  .panel.stat .panel-head { margin-bottom: 4px; } .panel.stat .panel-head .eyebrow { margin: 0; }
  .stat-v { font-size: 30px; font-weight: 400; letter-spacing: -.025em; line-height: 1.1; }
  .panel.stat .big { font-size: clamp(34px, 11cqi, 52px); }
  .panel.stat .sub-s { margin-top: 4px; }
  .panel.stat .sbars { width: 100%; height: auto; max-height: 56px; margin-top: auto; padding-top: 14px; }
  .panel.stat .ring { width: 64px; margin: auto 0 0 auto; padding-top: 10px; }
  .panel.stat .ring .mid strong { font-size: 14px; } .panel.stat .ring .mid span { font-size: 10px; }
  .panel.stat.nw .mini-line { display: block; width: 100%; height: 64px; margin-top: auto; padding-top: 12px; overflow: visible; }
  .panel.stat.nw .sk { margin-top: auto; }
  @container (min-width: 240px) { .panel.stat .ring { width: 84px; } .panel.stat .ring .mid strong { font-size: 16px; } }
  /* the insights, one at a time and swiped through on a phone, stacked in a wide tile */
  .cards .w[data-w="insights"] .insights { margin: 0; padding: 0; grid-auto-columns: 100%; gap: 10px; }
  .cards .w[data-w="insights"] .insight { height: 100%; }
  @container (max-width: 520px) { .cards .w[data-w="insights"] .insights { grid-auto-columns: 88%; } .cards .w[data-w="insights"] .insights:has(> :only-child) { grid-auto-columns: 100%; } }
  @container (min-width: 521px) { .cards .w[data-w="insights"] .insights { grid-auto-flow: row; overflow: visible; } }
  @container (min-width: 760px) { .cards .w[data-w="insights"] .insights { grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: var(--gap, 20px); } }
  /* ring charts beside their list, once the tile has room for both */
  @container (min-width: 300px) {
    .cards .share { display: grid; grid-template-columns: minmax(104px, 38%) minmax(0, 1fr); gap: 18px; align-items: center; }
    .cards .share .donut { width: 100%; margin: 0; }
    .cards .share .part { grid-template-columns: 10px minmax(0, 1fr) 34px; gap: 8px; }
    .cards .share .part .track { display: none; }
  }
  @container (max-width: 380px) { .cards .share .donut .mid strong { font-size: 17px; } .cards .share .donut .mid .eyebrow { font-size: 10.5px; } }
  /* budgets as rings */
  .brings { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 16px 10px; margin-top: 6px; }
  .bring { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 2px; min-width: 0; color: inherit; text-decoration: none; }
  .bring .ring { width: 64px; margin-bottom: 6px; }
  .bring .ring .mid strong { font-size: 13.5px; }
  .bring .name { font-size: 13px; font-weight: 500; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .bring .sub-s { font-size: 11.5px; }
  .ring.warn .seg { stroke: var(--warn); } .ring.over .seg { stroke: var(--spend); }
  .list .row.more { color: var(--muted); font-size: 13.5px; text-decoration: none; }
  /* Customize as an icon on a phone, so the month picker and it share one row */
  .dash-edit { display: inline-flex; align-items: center; gap: 6px; }
  .dash-edit svg { width: 16px; height: 16px; flex: none; display: none; }
  @media (max-width: 900px) {
    .dash-edit { width: 38px; height: 38px; padding: 0 !important; justify-content: center; border-radius: 50%; }
    .dash-edit svg { display: block; } .dash-edit span { display: none; }
    /* a page's Customize beside full-size buttons (no month picker): as tall as they are */
    .head .actions:not(:has(.month)) .dash-edit.page-cust { width: 44px; height: 44px; }
  }

  /* ---------- Collapsible sidebar (prefs.ts toggleSide): folds down to its icons ---------- */
  .brand { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
  .side-tg { display: grid; place-items: center; width: 30px; height: 30px; margin: -6px -6px -6px 0; padding: 0; border: 0; border-radius: 9px; background: none; color: var(--muted); cursor: pointer; }
  .side-tg:hover { background: var(--faint); color: var(--text); }
  .side-tg svg { width: 18px; height: 18px; transition: transform .34s var(--ease-glide); }
  /* Folding is one continuous motion: nothing is taken out of the layout. The rail narrows, every icon glides to
     the same spot it has when folded (a 40px column, the icon centred in it), labels fade and are clipped as their
     row narrows instead of wrapping, and the group headings shrink to their divider line. Labels go quickly on the
     way in and come back once there's room on the way out, so text never slides across an icon. */
  @media (min-width: 901px) {
    .app { transition: grid-template-columns .34s var(--ease-glide); }
    html.side-glide .app { overflow-x: clip; }
    html.side-glide main#view { width: var(--glide-w); max-width: none; }     /* one width for the whole fold (prefs.ts toggleSide) */
    .side { transition: padding .34s var(--ease-glide); }
    .brand { flex: none; transition: padding .34s var(--ease-glide), gap .34s var(--ease-glide); }
    .brand > span { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; }
    .side-tg { flex: none; transition: margin .34s var(--ease-glide), background .12s, color .12s; }
    .side-search { flex: none; height: 38px; padding-block: 0; overflow: hidden; white-space: nowrap; transition: padding .34s var(--ease-glide), color .12s, border-color .12s; }
    .side-search kbd { flex: none; }
    .side .foot .toggles { grid-template-columns: minmax(0, 1fr); }     /* so a nowrap label can't hold the column open */
    .side .nav a, .side .foot .toggles button { transition: padding .34s var(--ease-glide), background .12s, transform .08s; }
    :is(.side .nav a, .side-search, .side .foot .toggles button) > span { min-width: 0; overflow: hidden; white-space: nowrap; }
    :is(.brand, .side .nav a, .side-search, .side .foot .toggles button) > span, .side-search kbd { transition: opacity .2s ease .1s; }
    .nav-h { position: relative; height: 37.5px; overflow: hidden; white-space: nowrap; transition: height .34s var(--ease-glide), padding .34s var(--ease-glide), color .2s ease .1s; }
    .nav-h::after { content: ""; position: absolute; left: 11px; right: 11px; top: 10px; height: 1px; background: var(--line); opacity: 0; transition: opacity .2s ease; }
    .side .foot > :is(#ffLink, button) { max-height: 40px; overflow: hidden; white-space: nowrap; transition: max-height .34s var(--ease-glide), padding .34s var(--ease-glide), opacity .2s ease .1s, visibility 0s, background .12s, color .12s; }

    html.side-mini .app { grid-template-columns: 70px 1fr; }
    html.side-mini .side { padding-left: 7px; padding-right: 7px; overflow-x: hidden; }
    html.side-mini .brand { gap: 0; padding-left: 0; padding-right: 0; }
    html.side-mini .side-tg { margin-right: 5px; }
    html.side-mini .side-tg svg { transform: scaleX(-1); }
    html.side-mini .side-search { padding-left: 11px; padding-right: 0; }
    html.side-mini .side .nav a { padding-left: 11.5px; padding-right: 0; }
    html.side-mini .side .foot .toggles button { padding-left: 12.5px; padding-right: 0; }
    html.side-mini :is(.brand, .side .nav a, .side-search, .side .foot .toggles button) > span, html.side-mini .side-search kbd { opacity: 0; transition: opacity .1s ease; }
    html.side-mini .nav-h { height: 21px; padding-top: 0; padding-bottom: 0; color: transparent; transition: height .34s var(--ease-glide), padding .34s var(--ease-glide), color .1s ease; }
    html.side-mini .nav-h::after { opacity: 1; transition: opacity .2s ease .12s; }
    html.side-mini .side .foot > :is(#ffLink, button) { max-height: 0; padding-top: 0; padding-bottom: 0; opacity: 0; visibility: hidden; transition: max-height .34s var(--ease-glide), padding .34s var(--ease-glide), opacity .1s ease, visibility 0s .34s; }
  }
  @media (max-width: 520px) {
    .head .actions:has(.dash-edit) { gap: 6px; row-gap: 8px; flex: 1 1 100%; }
    /* the row under the title is clear of the search button, so only the title keeps out of its way */
    html:not(.shell) #view > .head:first-child:has(.dash-edit) { padding-right: 0; }
    html:not(.shell) #view > .head:first-child:has(.dash-edit) > :first-child { padding-right: 52px; }
    /* the same in the iPhone app's pages without a title bar row, where search sits beside the title (46px) */
    html.shell:not(.ab-row) #view > .head:has(.dash-edit) { padding-right: 0; }
    html.shell:not(.ab-row) #view > .head:has(.dash-edit) > :first-child { padding-right: 46px; }
    .head .actions:has(.dash-edit) .month span { min-width: 0; padding: 0 2px; }
    .head .actions:has(.dash-edit) .month .btn { padding-inline: 11px; margin-left: 0 !important; }
    .month .today-gap { display: none; }
  }
  /* a narrow tile: bills put the amount under the name rather than cutting the name short; budget rings two to a row */
  @container (max-width: 240px) {
    .w[data-w="bills"] .list .row:not(.more) { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; }
    .w[data-w="bills"] .list .row .name { white-space: normal; }
    .w[data-w="bills"] .list .row .num { text-align: left; font-weight: 600; }
    .w[data-w="bills"] .list .row .bill-end { justify-content: space-between; }
    .brings { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 6px; }
    .bring .ring { width: 54px; } .bring .ring .mid strong { font-size: 12px; }
  }
  @container (max-width: 240px) { .w[data-w="bills"] .lead { display: none; } }
  @container (max-width: 380px) { .w[data-w="bills"] .list .row:not(.more) .name { white-space: normal; overflow-wrap: anywhere; } .bill-end { gap: 8px; } .bill-end .btn { padding-inline: 11px; } }
  .cards.bento .w[data-w="budgets"] > .panel { display: flex; flex-direction: column; }
  .cards.bento .w[data-w="budgets"] .brings { margin-block: auto; padding-top: 10px; }
  /* a half-width tile's controls, small enough to share the row with its name */
  #dash.bento .w.s .wctl { gap: 4px; }
  #dash.bento .w.s .wctl button { padding: 3px 7px; font-size: 12px; }
  #dash.bento .w.s .wctl .grip { padding-right: 0; }
  @container (max-width: 200px) { #dash.bento .wctl strong { display: none; } }     /* (the tile shows its own title just below) */
  /* Add cards: a sketch of each card (tilepreview.ts), popping up on hover, or a thumbnail on a touch screen */
  .add-card { position: relative; }
  .add-card > span:nth-child(2) { flex: 1; min-width: 0; }
  .add-card .pv { flex: none; display: block; color: var(--text); }
  .add-card .tpv { display: block; width: 100%; height: auto; }
  @media (hover: hover) {
    .add-card .pv { position: absolute; z-index: 20; left: 50%; bottom: calc(100% + 10px); width: 240px; padding: 10px; border: 1px solid var(--line); border-radius: 16px;
      background: linear-gradient(var(--panel), var(--panel)), var(--bg); box-shadow: var(--shadow); opacity: 0; visibility: hidden; transform: translate(-50%, 6px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility 0s .15s; }
    .add-card:is(:hover, :focus-visible) .pv { opacity: 1; visibility: visible; transform: translate(-50%, 0); transition: opacity .15s ease .12s, transform .15s ease .12s, visibility 0s .12s; }
  }
  @media (hover: none) { .add-card .pv { width: 72px; padding: 4px; border-radius: 8px; background: color-mix(in srgb, var(--panel) 70%, transparent); border: 1px solid var(--line); } }
  /* Earned: in and out on one scale */
  .panel.stat .io { display: grid; gap: 8px; margin-top: auto; padding-top: 14px; }
  .io-r { display: grid; grid-template-columns: 2.4em minmax(0, 1fr) auto; align-items: center; gap: 8px; font-size: 12px; }
  .io-l { color: var(--muted); }
  .io-t { height: 8px; border-radius: 99px; background: color-mix(in srgb, var(--muted) 16%, transparent); overflow: hidden; }
  .io-t i { display: block; height: 100%; border-radius: inherit; transition: width .5s cubic-bezier(.2, .8, .2, 1); }
  .io-t i.in { background: var(--good); } .io-t i.out { background: color-mix(in srgb, var(--spend) 70%, transparent); } .io-t i.over { background: var(--spend); }
  .io-v { min-width: 4.5em; text-align: right; color: var(--muted); }
  @container (max-width: 190px) { .io-v { display: none; } .io-r { grid-template-columns: 2.4em minmax(0, 1fr); } }
  /* a card with nothing for this month keeps its spot */
  .panel.quiet .lead { margin-bottom: 0; }
  .attention .all-clear { display: flex; align-items: center; gap: 8px; margin: 14px 0 0; color: var(--muted); font-size: 13.5px; }
  .attention .all-clear span { display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: color-mix(in srgb, var(--good) 18%, transparent); color: var(--good); font-size: 12px; }
  .attention .att-more { display: contents; }
  .attention .att-more[hidden] { display: none; }
  @media (min-width: 700px) { .attention :is(.list, .all-clear) { min-height: var(--att-h, 88px); } }
  .attention .all-clear { align-items: flex-start; }
  .dash-ghost > .wctl { visibility: hidden; }
  /* Bento: what's inside a card grows with the card (cqi = 1% of the card's width), within sensible bounds */
  .cards.bento .panel.stat .stat-v { font-size: clamp(28px, 13cqi, 48px); }
  .cards.bento .panel.stat .sbars { flex: 1 1 auto; height: clamp(56px, 26cqi, 130px); max-height: none; }
  .cards.bento .panel.stat.nw .mini-line { height: clamp(64px, 20cqi, 150px); }
  .cards.bento .panel.stat .io { gap: clamp(10px, 4cqi, 18px); }
  .cards.bento .io-r { grid-template-columns: minmax(0, 1fr) auto; row-gap: 5px; font-size: clamp(12px, 4.4cqi, 14.5px); }
  .cards.bento .io-r .io-t { grid-column: 1 / -1; grid-row: 2; }
  .cards.bento .io-r .io-v { display: block; min-width: 0; }
  .cards.bento .io-t { height: clamp(9px, 4cqi, 14px); }
  .cards.bento .w[data-w="safe"] .total { font-size: clamp(28px, 7.5cqi, 46px); }
  /* a short card in a taller row: its figure and line sit in the middle of what's left under the title, not at the top with space below */
  .cards.bento .w[data-w="safe"] > .panel { display: flex; flex-direction: column; }
  .cards.bento .w[data-w="safe"] .total { margin-top: auto; }
  .cards.bento .w[data-w="safe"] .total ~ .sub-s { margin-bottom: auto !important; }
  .cards.bento .brings { grid-template-columns: repeat(auto-fill, minmax(clamp(84px, 34cqi, 170px), 1fr)); }
  .cards.bento .bring .ring { width: clamp(54px, 28cqi, 120px); }
  .cards.bento .bring .ring .mid strong { font-size: clamp(13.5px, 4.4cqi, 20px); }
  .cards.bento .share .donut .mid strong { font-size: clamp(17px, 4.6cqi, 26px); }
  /* a very wide category card: the ring and its list stay together rather than the percentages drifting to the far edge */
  @container (min-width: 760px) { .cards .share { grid-template-columns: minmax(104px, 280px) minmax(0, 460px); justify-content: center; gap: 40px; } }
  /* Bento: what's inside also fills the card's height, since a row is as tall as its tallest card */
  .cards.bento > .w > .panel:is(.stat, .sankey, .attention), .cards.bento .w:is([data-w="spending"], [data-w="payees"], [data-w="income"], [data-w="goals"], [data-w="cash"], [data-w="cards"], [data-w="loans"], [data-w="bills"]) > .panel { display: flex; flex-direction: column; }
  .cards.bento .panel.stat.nw .mini-line { flex: 1 1 0; height: auto; min-height: clamp(64px, 20cqi, 150px); }
  .cards.bento .panel.stat .sbars { flex: 1 1 0; height: auto; min-height: clamp(56px, 26cqi, 130px); }
  .cards.bento .sankey .sk-box { flex: 1; position: relative; min-height: var(--sk-h, 220px); }
  .cards.bento .sankey .sk-box > svg { position: absolute; inset: 0; width: 100%; height: 100%; }
  .cards.bento .w:is([data-w="spending"], [data-w="payees"], [data-w="income"]) .share { flex: 1; align-items: center; }
  .cards.bento .w:is([data-w="spending"], [data-w="payees"], [data-w="income"]) .share .parts { align-self: stretch; display: flex; flex-direction: column; justify-content: space-evenly; }
  /* room for its most rows (seven), so the card doesn't change height with the month's number of categories */
  .cards.bento .w:is([data-w="spending"], [data-w="payees"], [data-w="income"]) .share .parts { min-height: calc(7 * 2.7em + 60px); }
  /* a list spreads down the card rather than leaving its bottom empty */
  .cards.bento .w:is([data-w="cash"], [data-w="cards"], [data-w="bills"]) .list, .cards.bento .w[data-w="loans"] .loan-rows, .cards.bento .w[data-w="goals"] .goal-rows { flex: 1; display: flex; flex-direction: column; justify-content: space-evenly; }
  /* the ring grows into a tall card too */
  .cards.bento .w:is([data-w="spending"], [data-w="payees"], [data-w="income"]) .share { grid-template-columns: minmax(104px, min(46%, 340px)) minmax(0, 1fr); }
  /* the month's days stretch to the card's height */
  .cards.bento .w[data-w="heatmap"] > .panel { display: flex; flex-direction: column; }
  .cards.bento .w[data-w="heatmap"] .hm { flex: 1; grid-template-rows: auto 1fr; }
  .cards.bento .w[data-w="heatmap"] .hm-grid { grid-auto-rows: 1fr; }
  .cards.bento .w[data-w="heatmap"] .hm-cell { aspect-ratio: auto; min-height: clamp(22px, 9cqi, 34px); }
  /* a narrow balances card: name and amount get the room, the 30-day line steps aside */
  @container (max-width: 320px) { .w:is([data-w="cash"], [data-w="cards"]) .list .row > svg { display: none; } }

  /* ---------- Timeline: months down a line, newest on top ----------
     Phones: the line runs down the left with each month to its right. Wide screens: the line runs down the middle,
     months alternate sides, and each one's date sits across the line from it. Each row draws its own piece of the
     line (--sx is where it crosses the row), so the line can stop at the last dot. */
  .tl { list-style: none; margin: 0 auto; padding: 0; max-width: 1080px; --dot: 14px; }
  .tl > li { position: relative; --sx: 12px; }
  .tl > li::before { content: ""; position: absolute; top: 0; bottom: 0; left: var(--sx); width: 2px; margin-left: -1px; background: var(--line); }
  .tl > li:first-child::before { top: 50%; }
  .tl-item, .tl-more { padding: 0 0 16px 36px; }
  .tl-dot { position: absolute; left: var(--sx); top: var(--dy, 31px); width: var(--dot); height: var(--dot); margin: calc(var(--dot) / -2) 0 0 calc(var(--dot) / -2); border-radius: 50%; z-index: 1;
    background: var(--panel); border: 2.5px solid var(--muted); box-sizing: border-box; }
  .tl-dot.pos { background: var(--good); border-color: var(--good); box-shadow: 0 0 0 4px color-mix(in srgb, var(--good) 20%, transparent); }
  .tl-dot.neg { background: var(--spend); border-color: var(--spend); box-shadow: 0 0 0 4px color-mix(in srgb, var(--spend) 20%, transparent); }
  .tl-date { display: none; }
  .tl .tl-card.panel { padding: 20px 22px; margin: 0; }
  .tl-card h2 { margin: 0; font-size: 15px; line-height: 20px; }
  .tl-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
  .tl-tag { font-size: 11px; font-weight: 600; line-height: 18px; padding: 0 8px; border-radius: 999px; background: var(--faint); color: var(--muted); white-space: nowrap; }
  .tl-nw { display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px 10px; margin: 12px 0 16px; }
  .tl-nw .eyebrow { width: 100%; margin: 0; }
  .tl-nw strong { font-size: 26px; font-weight: 650; letter-spacing: -.01em; line-height: 1.15; }
  .tl-nw > span:last-child { font-size: 13px; font-weight: 500; }
  .tl-flow { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
  .tl-flow > div { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
  .tl-flow .eyebrow { margin: 0; }
  .tl-flow .num { font-size: 15px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .tl-flow .sub-s { font-size: 12px; white-space: nowrap; }
  @container (max-width: 380px) { .tl-of { display: none; } }
  .tl .tl-card.panel { container-type: inline-size; }
  .tl-flow i { display: block; height: 4px; border-radius: 2px; width: var(--w); min-width: 3px; margin-top: 3px; }
  .tl-top { margin: 14px 0 0; font-size: 13px; color: var(--muted); }
  .tl-top strong { color: var(--text); font-weight: 600; }
  .tl-moments { list-style: none; padding: 0; margin: 12px 0 0; display: flex; flex-wrap: wrap; gap: 6px; }
  .tl-moments li { font-size: 12.5px; font-weight: 500; line-height: 18px; padding: 3px 10px; border-radius: 999px; background: var(--faint); }
  .tl-moments li.pos { color: var(--good); background: color-mix(in srgb, var(--good) 14%, transparent); }
  .tl-moments li.neg { color: var(--spend); background: color-mix(in srgb, var(--spend) 14%, transparent); }
  .tl-links { display: flex; gap: 18px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
  .tl-go { display: inline-flex; align-items: center; gap: 1px; background: none; border: 0; padding: 0; color: var(--muted); font: inherit; font-size: 13px; font-weight: 500; line-height: 20px; cursor: pointer; }
  .tl-go svg { width: 14px; height: 14px; }
  @media (hover: hover) { .tl-go:hover { color: var(--text); } }
  .tl-item.quiet { --dy: 25px; }
  .tl-item.quiet .tl-card.panel { padding-top: 14px; padding-bottom: 14px; }
  .tl-item.quiet .tl-card p { margin: 2px 0 0; }
  .tl-year { padding: 6px 0 18px; }
  .tl-year span { position: relative; z-index: 1; display: inline-block; font-size: 13px; font-weight: 700; line-height: 26px; padding: 0 12px; border-radius: 999px; background: var(--accent); color: var(--on-accent); font-variant-numeric: tabular-nums; }
  .tl-end { padding: 0 0 8px 36px; --dy: 11px; }
  .tl-end::before { bottom: auto !important; height: 11px; }
  .tl-end p { margin: 0; color: var(--muted); font-size: 13px; line-height: 22px; }
  .tl-end p strong { color: var(--text); }
  .tl-new { animation: riseIn .45s cubic-bezier(.2, .8, .2, 1) both; }
  @media (min-width: 1000px) {
    .tl { --gap: 40px; }
    .tl > li { --sx: 50%; }
    .tl-item, .tl-more { width: 50%; box-sizing: border-box; }
    .tl-item.l { padding: 0 var(--gap) 22px 0; --sx: 100%; }
    .tl-item.r, .tl-more { margin-left: 50%; padding: 0 0 22px var(--gap); --sx: 0%; }
    .tl-date { display: block; position: absolute; top: var(--dy, 31px); transform: translateY(-50%); color: var(--muted); font-size: 12px; line-height: 16px; font-variant-numeric: tabular-nums; }
    .tl-date strong { display: block; color: var(--text); font-size: 17px; line-height: 22px; font-weight: 650; }
    .tl-item.l .tl-date { left: calc(100% + var(--gap)); }
    .tl-item.r .tl-date { right: calc(100% + var(--gap)); text-align: right; }
    .tl-year, .tl-end { text-align: center; }
    .tl-end { padding: 26px 0 8px; --dy: 7px; }
    .tl-end::before { height: 7px; }
  }
  /* the Overview's Net worth heading opens the timeline */
  .nw-tl { display: inline-flex; align-items: center; gap: 1px; color: var(--muted); text-decoration: none; }
  .nw-tl svg { width: 13px; height: 13px; }
  @media (hover: hover) { .nw-tl:hover { color: var(--text); } }
  /* ---------- Hover: things lean toward the mouse (mouse and trackpad only, never with Reduce motion) ---------- */
  /* Nothing with text in it moves by CSS transition or by a part of a pixel: Chrome on Windows draws text that's
     animating (or sitting between pixels) blurred until it settles. So dock.ts and magnet.ts move text in whole device
     pixels themselves, and only icons (SVG, which stays sharp) scale with transitions. */
  /* The sidebar swells like the macOS Dock: dock.ts sets --dock (0 to 1) on each link by how near the pointer is, and
     --dock-x, the label's nudge in whole pixels. The rows and the hover pill stay where they are. */
  @media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
    .side .nav a svg { scale: calc(1 + var(--dock, 0) * .3); transition: scale .2s cubic-bezier(.2, .8, .2, 1); }
    .side .nav a span { translate: var(--dock-x, 0px) 0; }
    html.side-mini .side .nav a svg { scale: calc(1 + var(--dock, 0) * .42); }
    .side-search svg, .side-tg svg { transition: scale .2s cubic-bezier(.2, .8, .2, 1), transform .34s var(--ease-glide); }
    .side-search:hover svg, .side-tg:hover svg { scale: 1.14; }
    /* Magnetic hover (magnet.ts, off unless turned on in Settings) writes its lean straight into the element's
       `translate` while .mag is set, and removes it once it's home, since any translate makes a stacking context. */
  }
  /* Collapsed sidebar: the name of the icon under the mouse (dock.ts), in the chart tooltip's style, pointing at it */
  .side-tip { z-index: 60; padding: 6px 11px; font-size: 13px; font-weight: 500; line-height: 1.35; white-space: nowrap; opacity: 0; visibility: hidden; }
  .side-tip::before { content: ""; position: absolute; left: -4px; top: 50%; width: 8px; height: 8px; margin-top: -4px; background: inherit; border-radius: 2px; transform: rotate(45deg); }
  .side-tip.on { opacity: 1; visibility: visible; }
  @media (prefers-reduced-motion: no-preference) { .side-tip.on { transition: opacity .12s ease .06s; } }

  /* ---------- Firefly III connection pill (ffstatus.ts): under "Money" in the sidebar, atop the phone's More menu,
     and "connected again" floating at the bottom for a moment once Firefly is back ---------- */
  .ff-slot { display: none; }
  .ff-pill, .ff-alert, .ff-badge { --ff: var(--muted); }
  :is(.ff-pill, .ff-alert, .ff-badge)[data-ff="ok"] { --ff: var(--good); }
  :is(.ff-pill, .ff-alert, .ff-badge)[data-ff="down"] { --ff: var(--spend); }
  :is(.ff-pill, .ff-alert, .ff-badge)[data-ff="auth"] { --ff: var(--warn); }
  :is(.ff-pill, .ff-alert, .ff-badge) > i { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--ff); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ff) 22%, transparent); }
  .ff-pill { display: inline-flex; align-items: center; gap: 7px; flex: none; align-self: flex-start; box-sizing: border-box; height: 24px; max-width: 100%; padding: 0 10px 0 8.5px; margin: 0;
    border: 1px solid color-mix(in srgb, var(--ff) 30%, transparent); border-radius: 99px; background: color-mix(in srgb, var(--ff) 11%, transparent);
    color: var(--ff); font-family: inherit; font-size: 12px; font-weight: 600; line-height: 1; letter-spacing: 0; white-space: nowrap; overflow: hidden; cursor: pointer; }
  .ff-pill:hover { background: color-mix(in srgb, var(--ff) 18%, transparent); }
  .ff-pill > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  :is(.ff-pill[data-ff="checking"], .ff-pill.busy) > i { animation: ff-pulse 1s ease-in-out infinite alternate; }
  @keyframes ff-pulse { to { opacity: .3; } }
  @media (prefers-reduced-motion: reduce) { :is(.ff-pill[data-ff="checking"], .ff-pill.busy) > i { animation: none; } }
  .side > .ff-pill { margin: -10px 0 14px 12px; }
  .ff-pill.wide { height: 32px; padding: 0 14px 0 12px; margin: 0 0 12px; gap: 9px; font-size: 14px; }
  .ff-pill.wide > i { width: 8px; height: 8px; }
  .ff-alert { position: fixed; z-index: 39; bottom: calc(28px + env(safe-area-inset-bottom, 0px)); left: 50%; display: flex; align-items: center; gap: 10px;
    box-sizing: border-box; height: 42px; max-width: calc(100vw - 32px); padding: 0 16px; border: 1px solid var(--line); border-radius: 99px;
    background: color-mix(in srgb, var(--panel) 88%, transparent); -webkit-backdrop-filter: blur(16px) saturate(1.4); backdrop-filter: blur(16px) saturate(1.4);
    color: var(--text); box-shadow: var(--shadow); font-size: 13.5px; font-weight: 550; line-height: 1; white-space: nowrap;
    opacity: 0; visibility: hidden; transform: translate(-50%, 10px); transition: opacity .2s ease, transform .32s var(--ease-glide), visibility 0s .32s; }
  .ff-alert.show { opacity: 1; visibility: visible; transform: translate(-50%, 0); transition: opacity .2s ease, transform .32s var(--ease-glide), visibility 0s; }
  .ff-alert > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  @media (max-width: 900px) { .ff-alert { bottom: calc(92px + env(safe-area-inset-bottom, 0px)); } }      /* above the tab bar */
  @media (min-width: 901px) {
    .ff-alert { left: calc(50% + 112px); }      /* the middle of the page, beside the sidebar */
    html.side-mini .ff-alert { left: calc(50% + 35px); }
    /* Folding: the label fades and the pill narrows to its dot, which lines up with the icons below */
    .side > .ff-pill { transition: padding .34s var(--ease-glide), margin .34s var(--ease-glide), gap .34s var(--ease-glide), background .12s; }
    .side > .ff-pill > span { max-width: 160px; transition: opacity .2s ease .1s, max-width .34s var(--ease-glide); }
    html.side-mini .side > .ff-pill { gap: 0; padding: 0 7.5px; margin-left: 8px; }
    html.side-mini .side > .ff-pill > span { max-width: 0; opacity: 0; transition: opacity .1s ease, max-width .34s var(--ease-glide); }
  }
  /* While Firefly can't be reached: the page gives way to this screen (no saved data to show instead) */
  .app > .ff-out { display: none; }
  html.ff-out .app > #view, html.ff-out .fab { display: none !important; }
  html.ff-out .app > .ff-out { display: grid; place-items: center; min-height: 100vh; min-height: 100dvh; box-sizing: border-box; padding: 40px 16px 120px; }
  .ff-out-card { --ff: var(--spend); display: flex; flex-direction: column; align-items: center; max-width: 420px; text-align: center; }
  .ff-out[data-ff="auth"] .ff-out-card { --ff: var(--warn); }
  .ff-out[data-ff="offline"] .ff-out-card { --ff: var(--muted); }
  .ff-out-ic { display: grid; place-items: center; width: 64px; height: 64px; padding: 0; border-radius: 50%; color: var(--ff); background: color-mix(in srgb, var(--ff) 13%, transparent); box-shadow: 0 0 0 1px color-mix(in srgb, var(--ff) 28%, transparent) inset; }
  .ff-out-ic svg { width: 28px; height: 28px; }
  .ff-out-card h2 { margin: 20px 0 8px; font-size: 22px; font-weight: 600; letter-spacing: -.015em; }
  .ff-out-card p { margin: 0; color: var(--muted); font-size: 14.5px; line-height: 1.55; }
  .ff-out-card .btn { margin-top: 22px; }
  .ff-out-card .ff-out-note { margin-top: 12px; font-size: 12.5px; }
  /* The pill's popover: which part is down, the server, version, last answer and token */
  .ff-pop { position: fixed; z-index: 64; width: 300px; max-width: calc(100vw - 24px); box-sizing: border-box; padding: 16px; background: var(--panel); border: 1px solid var(--line); border-radius: 18px; box-shadow: var(--shadow); color: var(--text); font-size: 13.5px; animation: fadeUp .12s ease both; }
  .ff-pop[hidden] { display: none; }
  .ff-pop-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; }
  .ff-pop-head > b { font-size: 15px; font-weight: 600; }
  .ff-badge { display: inline-flex; align-items: center; gap: 6px; height: 22px; padding: 0 9px 0 8px; border-radius: 99px; box-sizing: border-box; border: 1px solid color-mix(in srgb, var(--ff) 30%, transparent);
    background: color-mix(in srgb, var(--ff) 11%, transparent); color: var(--ff); font-size: 11.5px; font-weight: 600; line-height: 1; white-space: nowrap; }
  /* the chain: three steps joined by a line, each with a dot coloured by how it's doing */
  .ff-chain { --ok: var(--good); --bad: var(--spend); list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
  .ff-chain li { --k: var(--muted); position: relative; display: grid; grid-template-columns: 18px 1fr auto; align-items: center; column-gap: 10px; min-height: 30px; }
  .ff-chain li[data-k="ok"] { --k: var(--ok); } .ff-chain li[data-k="bad"] { --k: var(--bad); }
  .ff-chain li > i { justify-self: center; width: 9px; height: 9px; border-radius: 50%; box-sizing: border-box; background: var(--k); box-shadow: 0 0 0 3px color-mix(in srgb, var(--k) 22%, transparent); }
  .ff-chain li[data-k="unknown"] > i { background: transparent; border: 1.5px solid var(--k); box-shadow: none; }
  .ff-chain li + li::before { content: ""; position: absolute; left: 8.25px; top: -10.5px; width: 1.5px; height: 21px; background: var(--line); }
  .ff-chain li > b { font-weight: 500; }
  .ff-chain li > span { color: var(--k); font-weight: 600; font-size: 12.5px; }
  .ff-chain li[data-k="unknown"] > span { color: var(--muted); font-weight: 500; }
  .ff-facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 8px 14px; margin: 14px 0 0; padding: 14px 0 0; border-top: 1px solid var(--line); }
  .ff-facts dt { color: var(--muted); }
  .ff-facts dd { margin: 0; text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
  .ff-facts dd a { color: inherit; }
  .ff-facts dd[data-k="ok"] { color: var(--good); font-weight: 600; } .ff-facts dd[data-k="bad"] { color: var(--warn); font-weight: 600; }
  .ff-pop-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 16px; }
  .ff-pop-foot > span { color: var(--muted); font-size: 12px; }
  /* on the "can't reach" screen: the same chain, as a small card under the text */
  .ff-out-card .ff-chain { width: 100%; max-width: 300px; margin-top: 22px; padding: 10px 16px; box-sizing: border-box; border: 1px solid var(--line); border-radius: 14px; background: color-mix(in srgb, var(--panel) 70%, transparent); text-align: left; font-size: 13.5px; }
  @media print { .ff-alert, .ff-pill, .ff-pop { display: none !important; } }

  /* ---------- Pinned page head (pinhead.ts): the title row and its controls (month, Today, Customize, + New…) stay at the top
     while the page scrolls. Once pinned, a blur behind it fades the content out as it slides under. --head-top is where it
     pins: under the iPhone app's title bar, and up to the edge while that bar is slid away. ---------- */
  :root { --head-top: 14px; --head-fade: 28px; --head-hold: 8px; --head-l: 44px; --head-r: 44px; }
  html.compact { --head-top: 10px; --head-fade: 18px; --head-hold: 4px; }
  @media (max-width: 900px) {
    :root { --head-top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 4.5px); --head-fade: 18px; --head-hold: 4px;      /* the title row lines up with the search button */
      --head-l: calc(16px + env(safe-area-inset-left, 0px)); --head-r: calc(16px + env(safe-area-inset-right, 0px)); }
    html.shell { --head-top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 44px); }
    html.shell.scroll-down { --head-top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 6px); }
    html.shell:not(.ab-row) { --head-top: calc(env(safe-area-inset-top, 0px) + var(--edge) + 4px); }
    /* search sits on the pinned title row there, so it stays with it instead of sliding away */
    html.shell.pinhead:not(.ab-row) .search-btn { transform: none; opacity: 1; pointer-events: auto; }
    html.shell.pinhead #view > .head { transition: top .3s cubic-bezier(.2, .8, .2, 1); }
  }
  html.pinhead #view > .head { position: sticky; top: var(--head-top); z-index: 12; }
  /* A clear blur, no tint, in two steps so it fades without a smeared half-sharp band: ::before blurs strongly behind the title
     row and stops just past it, ::after blurs lightly below that and dissolves on an eased curve over --head-fade. */
  html.pinhead #view > .head::before, html.pinhead #view > .head::after { content: ''; position: absolute; z-index: -1; pointer-events: none;
    top: calc(-1 * var(--head-top) - 2px); left: calc(-1 * var(--head-l)); right: calc(-1 * var(--head-r));
    --hx: linear-gradient(#000, #000);
    opacity: 0; visibility: hidden; transition: opacity .2s ease, visibility 0s .2s; }
  html.pinhead #view > .head::before { bottom: calc(-1 * var(--head-hold) - 12px);
    -webkit-backdrop-filter: blur(20px) saturate(1.5); backdrop-filter: blur(20px) saturate(1.5);
    --fy: linear-gradient(to bottom, #000 calc(100% - 14px), transparent); }
  html.pinhead #view > .head::after { bottom: calc(-1 * var(--head-fade));
    -webkit-backdrop-filter: blur(10px) saturate(1.3); backdrop-filter: blur(10px) saturate(1.3);
    --f0: calc(100% - var(--head-fade) + var(--head-hold));
    --fy: linear-gradient(to bottom, transparent calc(var(--f0) - 16px), #000 var(--f0), rgba(0, 0, 0, .82) calc(var(--f0) + (100% - var(--f0)) * .25),
      rgba(0, 0, 0, .5) calc(var(--f0) + (100% - var(--f0)) * .5), rgba(0, 0, 0, .2) calc(var(--f0) + (100% - var(--f0)) * .75), transparent); }
  html.pinhead #view > .head::before, html.pinhead #view > .head::after {
    -webkit-mask-image: var(--fy), var(--hx); mask-image: var(--fy), var(--hx); -webkit-mask-composite: source-in; mask-composite: intersect; }
  /* Beside the sidebar they fade in from the left across the gap, so there's no hard vertical edge next to it */
  @media (min-width: 901px) {
    html.pinhead #view > .head::before, html.pinhead #view > .head::after {
      --hx: linear-gradient(to right, transparent, rgba(0, 0, 0, .5) calc(var(--head-l) * .45), #000 var(--head-l)); }
  }
  html.head-pinned #view > .head::before, html.head-pinned #view > .head::after { opacity: 1; visibility: visible; transition: opacity .2s ease, visibility 0s; }
  /* the day headings on Transactions and the bulk bar stick just below it */
  html.pinhead .tbl.feed tr.dayhead { top: calc(var(--head-top) + var(--head-h, 0px) + 4px) !important; z-index: 13; }
  html.pinhead .bulkbar { top: calc(var(--head-top) + var(--head-h, 0px) + 10px) !important; z-index: 13; }
  html.pinhead.shell .tbl.feed tr.dayhead, html.pinhead.shell .bulkbar { transition: top .3s cubic-bezier(.2, .8, .2, 1); }
  @media print { html.pinhead #view > .head { position: static; } html.pinhead #view > .head::before, html.pinhead #view > .head::after { display: none; } }

  /* ---------- Scrollbars: thin, themed pills on a clear track (mouse and trackpad only; touch keeps its own) ---------- */
  /* The ::-webkit-scrollbar rules are not behind @supports selector(): Safari answers that test "no" and then drew its default bar.
     Firefox ignores them and gets its own block below. Nothing here sets scrollbar-width or scrollbar-color for Chrome or Safari,
     because either one makes them ignore ::-webkit-scrollbar. */
  @media (hover: hover) and (pointer: fine) {
    :root { --sb-thumb: color-mix(in srgb, var(--text) 22%, transparent); --sb-thumb-hover: color-mix(in srgb, var(--text) 40%, transparent); }
    ::-webkit-scrollbar { width: 10px; height: 10px; background: transparent; }
    ::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: transparent; }
    ::-webkit-scrollbar-thumb { background: var(--sb-thumb); background-clip: padding-box; border: 2px solid transparent; border-radius: 99px; min-height: 40px; }
    ::-webkit-scrollbar-thumb:hover { background-color: var(--sb-thumb-hover); }
    ::-webkit-scrollbar-button { display: none; }
    /* Chrome and Safari: the sidebar always keeps its slot (6px padding + 10px slot = the 16px on the left), so a long nav never
       pushes the rows sideways; its pill is softer and stays clear of the rounded corners */
    @supports not (-moz-appearance: none) {
      @media (min-width: 901px) {
        .side { overflow-y: scroll; padding-right: 6px; }
        .side::-webkit-scrollbar-track { margin-block: 22px; }
        .side::-webkit-scrollbar-thumb { background-color: color-mix(in srgb, var(--text) 14%, transparent); }
        .side::-webkit-scrollbar-thumb:hover { background-color: var(--sb-thumb-hover); }
        /* folded to its icons there is no room for a slot: it still scrolls with the wheel */
        html.side-mini .side { padding-right: 7px; scrollbar-width: none; }
        html.side-mini .side::-webkit-scrollbar { display: none; }
      }
    }
    /* Firefox */
    @supports (-moz-appearance: none) {
      html { scrollbar-width: thin; scrollbar-color: var(--sb-thumb) transparent; }
      html.side-mini .side { scrollbar-width: none; }
    }
  }

  /* ---------- Edge glow (glass themes, mouse and trackpad only): rims near the pointer light up (features/edge.ts) ---------- */
  /* edge.ts writes the pointer's spot inside the element as --ex/--ey. They don't inherit, so moving the mouse restyles only
     the lit element and its ring, not everything inside the card. Every rim gets the same 1px line, the same reach and the
     same falloff, whatever its size. */
  @property --ex { syntax: '<length>'; inherits: false; initial-value: -9999px; }
  @property --ey { syntax: '<length>'; inherits: false; initial-value: -9999px; }
  @property --es { syntax: '<length>'; inherits: false; initial-value: 0px; }
  @property --edge-in { syntax: '<length>'; inherits: false; initial-value: -1px; }
  @media (hover: hover) and (pointer: fine) {
    html:is([data-theme="glass"], [data-theme="frosted"]) { --edge-r: 140px; }
    html[data-theme="glass"] { --edge-c: color-mix(in srgb, color-mix(in srgb, var(--accent) 65%, #fff) 60%, transparent); --edge-c2: color-mix(in srgb, var(--accent) 18%, transparent); }
    /* white vanishes on the pale frosted cards, so a warm light in the theme's accent */
    html[data-theme="frosted"] { --edge-c: color-mix(in srgb, var(--accent) 45%, transparent); --edge-c2: color-mix(in srgb, var(--accent) 14%, transparent); }
    /* :where() so any rule that already places the element (absolute, fixed, sticky) wins */
    :where(html:is([data-theme="glass"], [data-theme="frosted"]) .edge) { position: relative; }
    /* The ring lies on the element's own 1px border. Card grids keep their painting inside (contain: paint) and day cards clip
       (overflow: clip), which would cut that off, so a lit element lets its painting reach the border (overflow-clip-margin).
       edge.ts sets --edge-in: minus the border width, or 0 where the ring has to sit just inside (boxes that scroll, like the
       sidebar, and clipping boxes in Safari). */
    html:is([data-theme="glass"], [data-theme="frosted"]) .edge { overflow-clip-margin: border-box; }
    /* overflow: hidden ignores overflow-clip-margin; clip looks the same and honours it (edge.ts only does this where it's supported) */
    html:is([data-theme="glass"], [data-theme="frosted"]) .edge.edge-h { overflow: clip; }
    html:is([data-theme="glass"], [data-theme="frosted"]) .edge::before { content: ''; position: absolute; z-index: 1; border-radius: inherit; padding: 1px; pointer-events: none;
      --ex: inherit; --ey: inherit; --es: inherit; --edge-in: inherit;
      /* in a box that scrolls, the ring is held in place against the scroll (--es) */
      left: var(--edge-in, -1px); right: var(--edge-in, -1px); top: calc(var(--es) + var(--edge-in, -1px)); bottom: calc(var(--edge-in, -1px) - var(--es));
      background: radial-gradient(var(--edge-r) circle at calc(var(--ex) - var(--edge-in, -1px) - 1px) calc(var(--ey) - var(--edge-in, -1px) - 1px),
        var(--edge-c), var(--edge-c) 15%, var(--edge-c2) 50%, transparent) border-box;
      -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor;
      mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0); }
    /* form fields can't hold the ring: their border brightens as a whole, by the same falloff (--eg), drawn as an outline
       over the border so the field's own focus look is untouched */
    html:is([data-theme="glass"], [data-theme="frosted"]) .edge-f:not(:focus) { outline: 1px solid color-mix(in srgb, var(--edge-c) calc(var(--eg, 0) * 65%), transparent); outline-offset: -1px; }
  }

/* ---------- Touch targets: small controls get a larger invisible hit area on touch screens (about 44px), with no change to how they look ---------- */
@media (pointer: coarse) {
  :is(.ask-examples .chip, .seg.mini button, .insight .x, .dbars .db, .part .name a, #view button.link, #view .panel-head a.link) { position: relative; }
  :is(.ask-examples .chip, .seg.mini button, .insight .x, .dbars .db, .part .name a, #view button.link, #view .panel-head a.link)::after { content: ""; position: absolute; }
  .ask-examples .chip::after { inset: -7px 0; }
  .seg.mini button::after { inset: -9px -3px; }
  .insight .x::after { inset: -10px; }
  .dbars .db::after { inset: -2px -1px; }     /* a day's bar can't be 44px wide: there are up to 31 side by side */
  .part .name a::after { inset: -14px -6px; }
  #view button.link::after, #view .panel-head a.link::after { inset: -12px -6px; }
}

  /* ---------- Charts keep their true text and line size in any card (features/chartfit.ts sets --sx and --sy on the svg:
     how much the drawing is magnified across and down). A card taller than the drawing stretches it to fill, centred ---------- */
  svg[style*="--sx"] text { font-size: calc(11px / var(--sy, 1)); transform-box: fill-box; transform-origin: 0 50%; transform: scaleX(calc(var(--sy, 1) / var(--sx, 1))); }
  svg[style*="--sx"] text[text-anchor="middle"] { transform-origin: 50% 50%; }
  svg[style*="--sx"] text[text-anchor="end"] { transform-origin: 100% 50%; }
  svg[style*="--sx"] text.bv { font-size: calc(11.5px / var(--sy, 1)); }
  .chart svg[style*="--sx"] text { font-size: calc(12px / var(--sy, 1)); }
  svg[style*="--sx"] path:is(.draw, [fill="none"][stroke]) { stroke-width: calc(2.4px / sqrt(var(--sx, 1) * var(--sy, 1))); }
  svg[style*="--sx"] .dot, svg[style*="--sx"] .pace-dot { r: calc(4.5px / var(--sy, 1)); transform-box: fill-box; transform-origin: 50% 50%; transform: scaleX(calc(var(--sy, 1) / var(--sx, 1))); }
  .chart .pt:hover .dot { r: calc(6.5px / var(--sy, 1)); }
  .panel:has(> svg:is(.lines, .barchart, .flowchart)) { display: flex; flex-direction: column; }
  .panel > svg:is(.lines, .barchart, .flowchart) { flex: 1 1 auto; min-height: 0; margin-block: auto; }

  /* ---------- Rings and donuts use the room their card has, and sit in the middle of it ---------- */
  .donut { width: min(240px, 72%); }
  @container (min-width: 520px) {
    .panel:not(.cards .panel) > .share { grid-template-columns: clamp(150px, 34%, 300px) minmax(0, 560px); }
  }
  .panel:has(> .share):not(.cards .panel) { display: flex; flex-direction: column; }
  .panel:has(> .share):not(.cards .panel) > .share { flex: 1; align-content: center; }
  .panel:has(> .share):not(.cards .panel) > .share:not(:has(.parts)) { align-content: center; }
  .cards.bento .bring .ring { width: min(100%, clamp(54px, 46cqi, 170px)); }
  .cards.bento .bring .name { white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
  .cards.bento .w[data-w="budgets"] .brings { grid-template-columns: repeat(auto-fit, minmax(clamp(84px, 34cqi, 170px), 1fr)); flex: 1; align-content: space-evenly; margin-block: 0; }

/* ================= Premium pass (Light and Dark: depth, type) ================= */
/* One type scale for the whole app, used by every theme */
:root { --fs-hero: clamp(34px, 6vw, 52px); --ctl: 38px; }
@media (max-width: 600px) { :root { --ctl: 40px; } }
body { font-feature-settings: "tnum" 1, "cv11" 1; }
.eyebrow, .nav-h, .tbl th, .kpi .sub-s:first-child, .kpi > :is(.lbl, .k) { font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
.head h1 { font-size: 28px; font-weight: 650; letter-spacing: -.028em; }
.head .sub { font-size: 14px; }
.kpi .val, .total, .big, .tbl .amt, .row .amt { font-variant-numeric: tabular-nums lining-nums; }
.kpi .val { font-weight: 600; letter-spacing: -.025em; }
@media (max-width: 573px) { .head h1 { font-size: 26px; } }

:is(html[data-theme="light"], html[data-theme="dark"], html:not([data-theme])) {
  --lift: 0 1px 1px rgba(34,34,31,.04), 0 2px 4px rgba(34,34,31,.04), 0 12px 28px -14px rgba(34,34,31,.16);
  --rim: inset 0 1px 0 rgba(255,255,255,.9);
  --hero-grad: linear-gradient(180deg, var(--text) 35%, color-mix(in srgb, var(--text) 58%, var(--panel)));
}
@media (prefers-color-scheme: dark) {
  html:not([data-theme]) { --lift: 0 1px 0 rgba(0,0,0,.4), 0 14px 32px -16px rgba(0,0,0,.7); --rim: inset 0 1px 0 rgba(255,255,255,.05); }
}
html[data-theme="dark"] { --lift: 0 1px 0 rgba(0,0,0,.4), 0 14px 32px -16px rgba(0,0,0,.7); --rim: inset 0 1px 0 rgba(255,255,255,.05); }
:is(html[data-theme="light"], html[data-theme="dark"], html:not([data-theme])) :is(.panel, .kpi, .insight, .daycards tbody.day, .daycards > .center-empty) {
  box-shadow: var(--rim), var(--lift); border-color: color-mix(in srgb, var(--line) 80%, transparent);
}
:is(html[data-theme="light"], html[data-theme="dark"], html:not([data-theme])) :is(.side) { background: color-mix(in srgb, var(--bg) 70%, var(--panel)); }
:is(html[data-theme="light"], html[data-theme="dark"], html:not([data-theme])) .btn.primary { box-shadow: 0 1px 2px rgba(0,0,0,.18), 0 6px 14px -8px rgba(0,0,0,.4); }
:is(html[data-theme="light"], html[data-theme="dark"], html:not([data-theme])) .big { color: transparent; background: var(--hero-grad); -webkit-background-clip: text; background-clip: text; }
:is(html[data-theme="light"], html[data-theme="dark"], html:not([data-theme])) .big.negative { --hero-grad: linear-gradient(180deg, var(--spend-t), var(--warn-t)); }
:is(html[data-theme="light"], html[data-theme="dark"], html:not([data-theme])) .panel.stat .big, :is(html[data-theme="light"], html[data-theme="dark"], html:not([data-theme])) .dhero .big { font-weight: 250; }
@media print { .panel, .kpi { box-shadow: none !important; } .big { color: #000 !important; background: none !important; } }

/* ================= Empty states ================= */
.empty-state { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 52px 24px 56px; max-width: 460px; margin: 0 auto; }
.empty-state .es-ic { width: 60px; height: 60px; border-radius: 20px; display: grid; place-items: center; color: var(--accent); margin-bottom: 18px;
  background: color-mix(in srgb, var(--accent) 9%, var(--panel)); border: 1px solid color-mix(in srgb, var(--accent) 16%, var(--line)); }
.empty-state .es-ic svg { display: block; width: 26px; height: 26px; }
.empty-state h3 { margin: 0 0 6px; font-size: 17px; font-weight: 600; letter-spacing: -.015em; }
.empty-state p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.55; text-wrap: balance; }
.empty-state .es-act { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 22px; }

/* ================= Page-shaped loading placeholders ================= */
.sk-kpis .kpi { min-height: 84px; }
.sk-row { display: flex; align-items: center; gap: 14px; padding: 14px 18px; border-top: 1px solid var(--line); }
.sk-row:first-child { border-top: 0; }
.sk-grow { flex: 1; min-width: 0; }

/* ================= Page headers: same control height and order everywhere ================= */
.head .actions { --ctl: 40px; }
@media (max-width: 600px) { .head .actions { --ctl: 44px; } }
@media (max-width: 360px) { .head .actions { --ctl: 36px; } }
html:not(#_) .head .actions > :is(.btn, select, .dash-edit), html:not(#_) .head .actions .month :is(.btn, .round) { height: var(--ctl); min-height: var(--ctl); box-sizing: border-box; padding-block: 0; }
html:not(#_) .head .actions .month .round { width: var(--ctl); }
@media (max-width: 900px) { html:not(#_) .head .actions > .dash-edit { width: var(--ctl); } }     /* round icon button on tablets and phones; a text button above that */
.head .actions > .btn.primary { order: 99; }
@media (max-width: 600px) {
  .head { gap: 12px; margin-bottom: 20px; }
  .head > .actions { width: 100%; }
}

/* ================= Getting started card (new installs) ================= */
.started { margin-bottom: 20px; }
.started .panel-head { align-items: flex-start; }
.started .panel-head .round { flex: none; }
.started .st-track { margin: 4px 0 14px; height: 5px; }
.started .st-track .fill { opacity: 1; transition: width .4s var(--ease-glide); }
.st-list { list-style: none; margin: 0; padding: 0; }
.st-list li { display: flex; align-items: center; gap: 14px; padding: 12px 0; border-top: 1px solid var(--line); }
.st-list li:first-child { border-top: 0; }
.st-ck { flex: none; width: 24px; height: 24px; border-radius: 50%; border: 1.5px solid var(--line); display: grid; place-items: center; color: var(--on-accent); box-sizing: border-box; }
.st-ck svg { display: block; width: 14px; height: 14px; }
.st-list li.done .st-ck { background: var(--good); border-color: var(--good); }
.st-t { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.st-t strong { font-weight: 600; font-size: 14.5px; }
.st-t span { color: var(--muted); font-size: 13px; line-height: 1.4; }
.st-list li.done .st-t strong { color: var(--muted); font-weight: 500; text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--muted) 50%, transparent); }
.st-list li .btn { flex: none; }

/* The "Looks like a bill" rows: on a phone the name and its detail wrap over the full width and the buttons sit under them, instead of cutting both short */
@media (max-width: 600px) {
  #subsBox .list .row { flex-wrap: wrap; row-gap: 10px; }
  #subsBox .list .row > .name { flex: 1 1 100%; white-space: normal; overflow: visible; }
  #subsBox .list .row > .name .sub-s { white-space: normal; overflow: visible; text-overflow: clip; }
}

/* Touch targets: small buttons, the phone search button and the Timeline links keep their look but get a 44px tap area on touch screens */
@media (pointer: coarse) {
  .btn.small, .tl-go, .search-btn { position: relative; }
  .search-btn { position: fixed; }
  .btn.small::after, .tl-go::after, .search-btn::after { content: ""; position: absolute; left: 50%; top: 50%; width: max(100%, 44px); height: max(100%, 44px); transform: translate(-50%, -50%); }
}
