/* ═══ Type ═══════════════════════════════════════════════════════════════
   One family, self-hosted (the CSP forbids external assets). Rubik is
   variable 300-900, so weight alone separates a heading from a figure from
   a caption, and there is no second file to load.
   ═════════════════════════════════════════════════════════════════════ */
@font-face {
  font-family: "Rubik";
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url("/fonts/rubik-hebrew.woff2") format("woff2");
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
@font-face {
  font-family: "Rubik";
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url("/fonts/rubik-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ─── Tokens ──────────────────────────────────────────────────────────────
   "בהיר ובועט". Cream ground, near-black ink, and four flat fills that carry
   meaning and nothing else: lime = money in, pink = money out, amber = money
   promised but not landed, teal = the second income. Text inside a fill is
   always the ink, never white - that is what makes the blocks read as blocks.
   The hero card is the one inverted surface on the page.
   Radius is deliberately uneven, so a card reads as drawn rather than as a
   rounded rectangle; the corner sets live in --r-*.
   ───────────────────────────────────────────────────────────────────────── */
:root {
  color-scheme: light dark;

  --bg: oklch(0.968 0.013 90);
  --surface: oklch(0.995 0.006 90);
  --surface-2: oklch(0.945 0.014 90);
  --surface-3: oklch(0.905 0.018 90);

  --ink: oklch(0.16 0.022 300);
  --ink-2: oklch(0.47 0.020 282);
  --ink-3: oklch(0.60 0.018 282);
  --line: oklch(0.875 0.014 282);
  --line-strong: oklch(0.760 0.018 282);

  --lime: oklch(0.87 0.220 135);
  --pink: oklch(0.72 0.210 355);
  --pink-deep: oklch(0.62 0.230 8);
  --amber: oklch(0.90 0.170 95);
  --teal: oklch(0.85 0.130 190);
  --on-fill: oklch(0.16 0.022 300);

  --accent: var(--lime);
  --accent-ink: oklch(0.420 0.150 152);
  --accent-soft: oklch(0.930 0.105 140);
  --accent-line: oklch(0.800 0.170 140);
  --on-accent: var(--on-fill);

  --neg: var(--pink);
  --neg-ink: oklch(0.500 0.200 12);
  --neg-soft: oklch(0.925 0.075 5);
  --neg-line: oklch(0.800 0.150 5);
  --on-neg: var(--on-fill);

  --warn-ink: oklch(0.470 0.110 78);
  --warn-soft: oklch(0.935 0.090 92);
  --warn-line: oklch(0.840 0.140 92);

  --hero-bg: oklch(0.16 0.022 300);
  --hero-fg: oklch(0.970 0.008 282);
  --hero-2: oklch(0.700 0.020 282);
  --hero-num: var(--lime);

  --drop: oklch(0.80 0.120 235);
  --focus: oklch(0.50 0.190 268);

  --font: "Rubik", "Noto Sans Hebrew", system-ui, -apple-system, "Segoe UI", sans-serif;

  --r-chip: 10px 13px 9px 12px;
  --r-control: 13px 17px 12px 16px;
  --r-tile: 18px 24px 17px 23px;
  --r-tile-alt: 23px 17px 24px 18px;
  --r-panel: 22px 30px 20px 28px;

  --t-xs: 0.75rem;
  --t-sm: 0.85rem;
  --t-base: 0.94rem;
  --t-md: 1.1rem;
  --t-lg: 1.4rem;
  --t-xl: 1.85rem;
  --t-2xl: 2.6rem;

  --shadow-card: 0 2px 0 var(--line);
  --shadow-pop: 0 18px 44px oklch(0.16 0.02 300 / 0.22);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  --z-sticky: 20;
  --z-overlay: 40;
  --z-dialog: 41;

  accent-color: var(--lime);
}

/* Night: the cream goes to near-black and the ink to cream, but the four
   fills keep their hue and their dark text. A lime block is a lime block at
   7am and at 11pm; only the paper around it changes. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: oklch(0.180 0.016 282);
    --surface: oklch(0.232 0.018 282);
    --surface-2: oklch(0.278 0.020 282);
    --surface-3: oklch(0.330 0.022 282);

    --ink: oklch(0.965 0.010 282);
    --ink-2: oklch(0.790 0.016 282);
    --ink-3: oklch(0.680 0.018 282);
    --line: oklch(0.330 0.020 282);
    --line-strong: oklch(0.450 0.022 282);

    --lime: oklch(0.855 0.215 135);
    --pink: oklch(0.735 0.205 355);
    --pink-deep: oklch(0.640 0.225 8);
    --amber: oklch(0.885 0.165 95);
    --teal: oklch(0.840 0.125 190);

    --accent-ink: oklch(0.865 0.180 142);
    --accent-soft: oklch(0.330 0.090 145);
    --accent-line: oklch(0.470 0.130 142);

    --neg-ink: oklch(0.830 0.150 8);
    --neg-soft: oklch(0.330 0.095 8);
    --neg-line: oklch(0.470 0.140 8);

    --warn-ink: oklch(0.880 0.135 92);
    --warn-soft: oklch(0.330 0.075 88);
    --warn-line: oklch(0.470 0.110 90);

    --hero-bg: oklch(0.115 0.018 282);
    --hero-fg: oklch(0.970 0.008 282);
    --hero-2: oklch(0.700 0.020 282);

    --focus: oklch(0.76 0.14 262);
    --shadow-card: 0 2px 0 oklch(0.135 0.016 282);
    --shadow-pop: 0 20px 50px oklch(0 0 0 / 0.55);
  }
}

:root[data-theme="dark"] {
  --bg: oklch(0.180 0.016 282);
  --surface: oklch(0.232 0.018 282);
  --surface-2: oklch(0.278 0.020 282);
  --surface-3: oklch(0.330 0.022 282);

  --ink: oklch(0.965 0.010 282);
  --ink-2: oklch(0.790 0.016 282);
  --ink-3: oklch(0.680 0.018 282);
  --line: oklch(0.330 0.020 282);
  --line-strong: oklch(0.450 0.022 282);

  --lime: oklch(0.855 0.215 135);
  --pink: oklch(0.735 0.205 355);
  --pink-deep: oklch(0.640 0.225 8);
  --amber: oklch(0.885 0.165 95);
  --teal: oklch(0.840 0.125 190);

  --accent-ink: oklch(0.865 0.180 142);
  --accent-soft: oklch(0.330 0.090 145);
  --accent-line: oklch(0.470 0.130 142);

  --neg-ink: oklch(0.830 0.150 8);
  --neg-soft: oklch(0.330 0.095 8);
  --neg-line: oklch(0.470 0.140 8);

  --warn-ink: oklch(0.880 0.135 92);
  --warn-soft: oklch(0.330 0.075 88);
  --warn-line: oklch(0.470 0.110 90);

  --hero-bg: oklch(0.115 0.018 282);
  --hero-fg: oklch(0.970 0.008 282);
  --hero-2: oklch(0.700 0.020 282);

  --focus: oklch(0.76 0.14 262);
  --shadow-card: 0 2px 0 oklch(0.135 0.016 282);
  --shadow-pop: 0 20px 50px oklch(0 0 0 / 0.55);
}

*, *::before, *::after { box-sizing: border-box; }

/* Nothing may pan the page sideways. `clip` rather than `hidden`, because
   `hidden` turns the root into a scroll container and kills the sticky topbar.
   `overscroll-behavior-x` stops iOS rubber-banding the page when a swipe
   starts on an inner horizontal scroller such as the tab strip. */
html {
  overflow-x: clip;
  overscroll-behavior-x: none;
}

body {
  margin: 0;
  max-width: 100%;
  overflow-x: clip;
  overscroll-behavior-x: none;
  color: var(--ink);
  background: var(--bg);
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: 1.5;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p { margin: 0; }
h1, h2, h3 { text-wrap: balance; font-weight: 800; letter-spacing: -0.025em; }

button, input, select { font: inherit; color: inherit; }
button { cursor: pointer; }

:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 6px; }

.num { font-variant-numeric: tabular-nums; letter-spacing: -0.035em; font-weight: 900; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ─── Faces ───────────────────────────────────────────────────────────────
   Four moods, one geometry. State, never decoration: something in the data
   picks the mood. Colour comes from the class, because the CSP forbids an
   inline fill. ─────────────────────────────────────────────────────────── */
.face { display: block; flex: none; }
.face-skin { stroke: var(--on-fill); stroke-width: 4.2; stroke-linejoin: round; }
.face-happy .face-skin { fill: var(--lime); }
.face-ok .face-skin { fill: var(--amber); }
.face-worried .face-skin { fill: var(--pink); }
.face-bad .face-skin { fill: var(--pink-deep); }
.face-eye { fill: var(--on-fill); }
.face-mouth, .face-brow { fill: none; stroke: var(--on-fill); stroke-width: 4; stroke-linecap: round; }
.face-drop { fill: var(--drop); stroke: var(--on-fill); stroke-width: 2.6; }

/* ─── Controls ────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 44px;
  padding: 0 18px;
  border: 0;
  border-radius: var(--r-control);
  background: var(--surface-2);
  color: var(--ink);
  font-size: var(--t-sm);
  font-weight: 700;
  white-space: nowrap;
  transition: background 160ms var(--ease), transform 120ms var(--ease);
}


.btn-primary { background: var(--lime); color: var(--on-fill); font-weight: 900; }

.btn-quiet { background: transparent; color: var(--ink-2); }

.btn-wide { width: 100%; }

.btn-icon {
  display: inline-grid;
  place-items: center;
  width: 40px;
  min-height: 40px;
  padding: 0;
  border: 0;
  border-radius: var(--r-chip);
  background: var(--surface-2);
  color: var(--ink);
  font-size: var(--t-md);
  line-height: 1;
  transition: background 160ms var(--ease);
}

.btn-mini {
  min-height: 32px;
  padding: 0 13px;
  border: 0;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--ink);
  font-size: var(--t-xs);
  font-weight: 800;
  transition: background 160ms var(--ease), color 160ms var(--ease);
}

.field { display: grid; gap: 6px; }
.field > span { color: var(--ink-2); font-size: var(--t-sm); font-weight: 700; }
.field input {
  width: 100%;
  min-height: 46px;
  padding: 0 13px;
  border: 2px solid var(--line-strong);
  border-radius: var(--r-chip);
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
}
.field input::placeholder { color: var(--ink-3); font-weight: 400; }
.field input:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; border-color: transparent; }
.field input:disabled { background: var(--surface-2); color: var(--ink-2); }

.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

.form-note { color: var(--ink-2); font-size: var(--t-sm); }
.form-error { min-height: 1.2em; color: var(--neg-ink); font-size: var(--t-sm); font-weight: 700; }
.form-ok { min-height: 1.2em; color: var(--accent-ink); font-size: var(--t-sm); font-weight: 700; }

.check {
  appearance: none;
  flex: none;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border: 2.4px solid var(--line-strong);
  border-radius: 4px 6px 3px 7px;
  background: var(--surface);
  transition: background 160ms var(--ease), border-color 160ms var(--ease);
}
.check:checked { border-color: var(--ink); background: var(--lime); }
.check:checked::after { content: "✓"; color: var(--on-fill); font-size: 14px; font-weight: 900; }

.toggle { display: flex; align-items: center; gap: 10px; padding: 13px; border-radius: var(--r-control); background: var(--surface-2); }
.toggle span { font-size: var(--t-sm); font-weight: 700; }

/* ─── Shell ───────────────────────────────────────────────────────────── */
.shell {
  width: min(1320px, 100%);
  margin-inline: auto;
  padding-block: 0 72px;
  padding-inline:
    max(clamp(14px, 3vw, 28px), env(safe-area-inset-left))
    max(clamp(14px, 3vw, 28px), env(safe-area-inset-right));
}

.topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: 14px;
  height: 66px;
  margin-bottom: 18px;
  background: color-mix(in oklab, var(--bg) 90%, transparent);
  backdrop-filter: blur(12px);
}

.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
.brand h1 { font-size: var(--t-md); font-weight: 900; white-space: nowrap; }
.mark {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  flex: none;
  border-radius: 11px 14px 10px 13px;
  background: var(--lime);
  color: var(--on-fill);
  font-size: 19px;
  font-weight: 900;
}

.month-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-inline: auto;
  padding: 3px;
  border-radius: 999px;
  background: var(--surface-2);
}
.month-nav h2 { min-width: 10ch; padding: 0 6px; font-size: var(--t-sm); font-weight: 800; text-align: center; }
.month-step {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font-size: var(--t-md);
  line-height: 1;
  transition: background 160ms var(--ease);
}

.topbar-actions { display: flex; align-items: center; gap: 7px; }
.topbar-actions .who {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  background: var(--amber);
  color: var(--on-fill);
  font-size: var(--t-sm);
  font-weight: 900;
}

/* ─── Panels ──────────────────────────────────────────────────────────── */
.panel {
  border: 0;
  border-radius: var(--r-panel);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

/* ─── flowPanel: demoted below homeHero, so it opens on a plain heading
   rather than a second hero ─────────────────────────────────────────── */
.flow { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; margin-bottom: 18px; background: none; box-shadow: none; border-radius: 0; }

.flow-head { display: flex; align-items: baseline; gap: 10px; padding: 0 6px; }
.flow-head .sub { margin-inline-start: auto; }
.flow-head .sub.is-positive { color: var(--accent-ink); }
.flow-head .sub.is-negative { color: var(--pink); }

/* ─── homeHero(): the account a direct debit actually bounces against ───
   The one dark, inverted surface on the page — there is only ever one. */
.home-hero {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 20px 24px 22px;
  border-radius: var(--r-panel);
  background: var(--hero-bg);
  color: var(--hero-fg);
}
.home-hero-main { flex-grow: 1; min-width: 0; }
.home-hero .face { width: 92px; height: auto; }

.home-hero-label { display: block; color: var(--hero-2); font-size: var(--t-sm); font-weight: 600; }

.home-hero-num {
  display: block;
  margin-top: 2px;
  font-size: var(--t-2xl);
  line-height: 1;
  letter-spacing: -0.05em;
}
.home-hero-num.is-positive { color: var(--hero-num); }
.home-hero-num.is-negative { color: var(--pink); }

.home-hero-sub { display: block; margin-top: 8px; color: var(--hero-2); font-size: var(--t-sm); font-weight: 600; }

/* ─── verdictLine(): the one sentence the next obligation earns ─────────── */
.verdict-line { display: block; margin: 10px 6px 18px; color: var(--ink-2); font-size: var(--t-sm); font-weight: 700; }

@media (max-width: 720px) {
  .home-hero { padding: 16px 18px 18px; }
}

/* ─── The blocks ──────────────────────────────────────────────────────── */
.flow-figures { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 0; }
.flow-fig {
  display: grid;
  align-content: center;
  gap: 1px;
  padding: 14px 16px 16px;
  border-radius: var(--r-tile);
  color: var(--on-fill);
}
.flow-fig:nth-child(even) { border-radius: var(--r-tile-alt); }
.flow-fig dt { order: 1; display: flex; align-items: center; gap: 6px; font-size: var(--t-sm); font-weight: 700; opacity: 0.82; }
.flow-fig dd { order: 2; margin: 0; font-size: var(--t-xl); line-height: 1.05; }
.flow-fig.is-in { background: var(--lime); }
.flow-fig.is-out { background: var(--pink); }
.flow-fig.is-wait { grid-column: 1 / -1; grid-template-columns: minmax(0, 1fr) auto; align-items: center; background: var(--amber); }
.flow-fig.is-wait dd { font-size: var(--t-lg); }

/* ─── The month, week by week ─────────────────────────────────────────── */
.weeks { padding: 16px 14px 15px; border-radius: var(--r-panel); background: var(--surface); box-shadow: var(--shadow-card); }
.weeks-head { display: flex; align-items: baseline; gap: 10px; padding: 0 6px 12px; }
.weeks-head h2 { font-size: var(--t-md); }
.weeks-head .sub { margin-inline-start: auto; color: var(--ink-2); font-size: var(--t-sm); font-weight: 600; }
.week-row { display: flex; gap: 8px; }
.week { flex-grow: 1; flex-basis: 0; display: grid; justify-items: center; gap: 5px; min-width: 0; }
.week .face { width: 100%; max-width: 62px; height: auto; }
.week b { font-size: var(--t-sm); font-weight: 900; line-height: 1; letter-spacing: -0.035em; }
.week span { color: var(--ink-2); font-size: var(--t-xs); font-weight: 600; }
.week.is-happy b { color: var(--accent-ink); }
.week.is-worried b, .week.is-bad b { color: var(--neg-ink); }
.week.is-ok b { color: var(--warn-ink); }

/* ─── The pain ────────────────────────────────────────────────────────── */
.flow-pain {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  border-radius: var(--r-tile-alt);
  background: var(--pink-deep);
  color: var(--on-fill);
}
.flow-pain .face { width: 52px; height: auto; }
.flow-pain .face-bad .face-skin { fill: var(--pink); }
.flow-pain.is-clear .face-happy .face-skin { fill: var(--surface); }
.flow-pain b { display: block; font-size: var(--t-base); font-weight: 800; }
.flow-pain > span > span { display: block; margin-top: 1px; font-size: var(--t-sm); font-weight: 600; opacity: 0.78; }
.flow-pain .pain-amount { margin-inline-start: auto; font-size: var(--t-lg); white-space: nowrap; }
.flow-pain.is-clear { background: var(--lime); }

/* ─── Two-column body ─────────────────────────────────────────────────── */
.body-grid { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 18px; align-items: start; }

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 20px;
  border-bottom: 2px solid var(--line);
}
.panel-head h2 { font-size: var(--t-md); }
.panel-head .sub { color: var(--ink-2); font-size: var(--t-sm); font-weight: 600; }
.head-tools { display: flex; align-items: center; gap: 6px; }

.count {
  display: inline-grid;
  place-items: center;
  min-width: 28px;
  height: 28px;
  padding: 0 9px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--t-xs);
  font-weight: 800;
}

/* ─── Event rows (the shared vocabulary for every list) ───────────────── */
.rows { display: grid; }
.row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 20px;
  border: 0;
  border-bottom: 2px solid var(--line);
  background: transparent;
  color: inherit;
  text-align: start;
  transition: background 150ms var(--ease);
}
.rows > .row:last-child { border-bottom: 0; }

.row-date {
  display: grid;
  place-items: center;
  align-content: center;
  width: 44px;
  height: 44px;
  flex: none;
  border-radius: 12px 15px 11px 14px;
  background: var(--surface-2);
  color: var(--ink-2);
}
.row-date b { font-size: var(--t-md); font-weight: 900; line-height: 1; letter-spacing: -0.03em; }
.row-date span { font-size: 10px; font-weight: 700; margin-top: 1px; }
.row-date.is-today { background: var(--lime); color: var(--on-fill); }
.row-date.is-late { background: var(--pink); color: var(--on-fill); }

.row-main { min-width: 0; }
.row-main h3 { font-size: var(--t-base); font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-main p { margin-top: 1px; color: var(--ink-2); font-size: var(--t-sm); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.row-amount { font-size: var(--t-md); white-space: nowrap; }
.row-amount.in { color: var(--accent-ink); }
.row-amount.out { color: var(--neg-ink); }

.row.is-done { color: var(--ink-3); }
.row.is-done .row-main h3 { font-weight: 600; text-decoration: line-through; text-decoration-thickness: 2px; }
.row.is-done .row-amount { color: var(--ink-3); }

/* A breach has no date and no checkbox - just a name and how far over it is -
   so the row is the same vocabulary with only the two columns it needs. */
.row.is-breach { grid-template-columns: minmax(0, 1fr) auto; }

.tag { padding: 4px 10px; border-radius: 999px; font-size: var(--t-xs); font-weight: 800; white-space: nowrap; }
.tag-done { background: var(--surface-2); color: var(--ink-3); }
.tag-late { background: var(--pink); color: var(--on-fill); }
.tag-est { background: var(--lime); color: var(--on-fill); }
.tag-soon { background: var(--amber); color: var(--on-fill); }

.empty { display: grid; gap: 4px; justify-items: center; padding: 36px 18px; text-align: center; }
.empty b { font-size: var(--t-md); font-weight: 800; }
.empty span { color: var(--ink-2); font-size: var(--t-sm); font-weight: 500; }

/* ─── Snapshot side panel ─────────────────────────────────────────────── */
.snapshot { display: grid; }
.snap {
  border: 0;
  border-bottom: 2px solid var(--line);
  font: inherit;
  color: inherit;
  text-align: start;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px 12px;
  padding: 14px 20px;
  background: transparent;
}
.snapshot > .snap:last-child { border-bottom: 0; }
.snap h3 { font-size: var(--t-base); font-weight: 800; }
.snap p { color: var(--ink-2); font-size: var(--t-sm); font-weight: 500; }
.snap b { font-size: var(--t-lg); white-space: nowrap; }
.snap b.in { color: var(--accent-ink); }
.snap b.out { color: var(--neg-ink); }

/* Ten blocks, not a bar: this is a count, so it reads as one. */
.meter { display: grid; grid-column: 1 / -1; grid-template-columns: repeat(10, 1fr); gap: 3px; height: 12px; }
.meter i { display: block; height: 100%; border-radius: 4px; background: var(--surface-3); }
.meter.fill-1 i:nth-child(-n + 1),
.meter.fill-2 i:nth-child(-n + 2),
.meter.fill-3 i:nth-child(-n + 3),
.meter.fill-4 i:nth-child(-n + 4),
.meter.fill-5 i:nth-child(-n + 5),
.meter.fill-6 i:nth-child(-n + 6),
.meter.fill-7 i:nth-child(-n + 7),
.meter.fill-8 i:nth-child(-n + 8),
.meter.fill-9 i:nth-child(-n + 9),
.meter.fill-10 i:nth-child(-n + 10) { background: var(--lime); }
.meter.is-out.fill-1 i:nth-child(-n + 1),
.meter.is-out.fill-2 i:nth-child(-n + 2),
.meter.is-out.fill-3 i:nth-child(-n + 3),
.meter.is-out.fill-4 i:nth-child(-n + 4),
.meter.is-out.fill-5 i:nth-child(-n + 5),
.meter.is-out.fill-6 i:nth-child(-n + 6),
.meter.is-out.fill-7 i:nth-child(-n + 7),
.meter.is-out.fill-8 i:nth-child(-n + 8),
.meter.is-out.fill-9 i:nth-child(-n + 9),
.meter.is-out.fill-10 i:nth-child(-n + 10) { background: var(--pink); }

/* Past the ceiling: the ladder still stops at ten full blocks - there is no
   eleventh - so the overshoot has to read some other way. The blocks turn
   the same deep pink the worst face and the pain card already use for
   trouble, and the meter's own outline sits a few pixels past its own edge
   and breathes, so the bar visibly spills out of the box it is drawn in
   instead of just sitting quietly full. */
/* Deliberately static, no pulse: card 8021 sits over its limit for weeks at
   a time, and an animation that never stops is not a state transition - it
   is wallpaper that stops signalling. DESIGN.md's motion budget is
   150-260ms, state only; a permanent loop breaks both. */
.meter.is-over i:nth-child(-n + 10) { background: var(--pink-deep); }
.meter.is-over {
  outline: 2px solid var(--pink-deep);
  outline-offset: 3px;
}

/* ─── KPI row: three flat tiles on the semantic fills ────────────────── */
.kpi-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-bottom: 18px; }

/* Facility meters, the tier card and next payments stack as three plain
   panels above the demoted flowPanel - a plain grid gap keeps them apart
   without reaching for a new spacing rule on .panel itself. */
.home-stack { display: grid; gap: 18px; margin-bottom: 18px; }
.kpi-tile {
  display: grid;
  gap: 8px;
  padding: 16px 18px;
  border-radius: var(--r-tile);
  color: var(--on-fill);
}
.kpi-tile:nth-child(2) { border-radius: var(--r-tile-alt); }
.kpi-tile span { display: flex; align-items: center; gap: 6px; font-size: var(--t-sm); font-weight: 700; }
.kpi-tile b { font-size: var(--t-lg); }
.kpi-tile.is-in { background: var(--lime); }
.kpi-tile.is-out { background: var(--pink); }
.kpi-tile.is-facility { background: var(--amber); }

/* ─── Facility meters: reuse .snap, add the percentage + expiry row ─────── */
.facility-meter { grid-template-columns: minmax(0, 1fr) auto; row-gap: 8px; }
.facility-meter b.over { color: var(--neg-ink); }
.facility-tag-row { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 8px; }

/* ─── Tier card ───────────────────────────────────────────────────────── */
.tier-head { display: flex; align-items: center; gap: 14px; }
.tier-head .face { width: 48px; height: auto; flex: none; }

/* ─── Tabs ────────────────────────────────────────────────────────────── */
.tabs {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  gap: 5px;
  margin: 0 0 18px;
  padding: 5px;
  border-radius: var(--r-control);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 1 0 auto;
  min-height: 38px;
  text-decoration: none;
  padding: 0 16px;
  border: 0;
  border-radius: var(--r-chip);
  background: transparent;
  color: var(--ink-2);
  font-size: var(--t-sm);
  font-weight: 800;
  white-space: nowrap;
  transition: background 150ms var(--ease), color 150ms var(--ease);
}
.tab[aria-current="page"] { background: var(--ink); color: var(--bg); }

/* The whole tree is re-rendered on every action, so an unconditional entrance
   would replay on each tap. app.js marks the root only when the view genuinely
   changed — tab, month or day — and only that render animates. */
@media (prefers-reduced-motion: no-preference) {
  [data-fresh] .view { animation: enter 180ms var(--ease); }
  [data-fresh] .home-hero .face { animation: pop-face 200ms var(--ease); }
}
@keyframes enter { from { opacity: 0; transform: translateY(4px); } }
@keyframes pop-face { from { opacity: 0; transform: scale(0.9); } }

/* ─── Calendar ────────────────────────────────────────────────────────── */
.cal-head, .cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-head { border-bottom: 2px solid var(--line); background: var(--surface-2); }
.cal-head span { padding: 10px 8px; color: var(--ink-2); font-size: var(--t-xs); font-weight: 800; text-align: center; }

.cal-day {
  border: 0;
  font: inherit;
  display: grid;
  align-content: start;
  gap: 4px;
  min-height: 120px;
  padding: 7px;
  border-inline-start: 2px solid var(--line);
  border-bottom: 2px solid var(--line);
  background: transparent;
  text-align: start;
  color: inherit;
  transition: background 150ms var(--ease);
}
.cal-day:nth-child(7n + 1) { border-inline-start: 0; }
.cal-grid > .cal-day:nth-last-child(-n + 7) { border-bottom: 0; }
.cal-day.is-blank { background: var(--surface-2); }
.cal-day.is-rest { background: color-mix(in oklab, var(--surface-2) 55%, transparent); }
.cal-day.is-selected { background: var(--warn-soft); }

.cal-num { display: flex; align-items: center; gap: 5px; font-size: var(--t-sm); font-weight: 800; }
.cal-day.is-today .cal-num b {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  background: var(--lime);
  color: var(--on-fill);
}
.cal-note { color: var(--ink-3); font-size: 10px; font-weight: 700; }

.chip {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 6px;
  padding: 5px 8px;
  border: 2px solid transparent;
  border-radius: 7px 9px 6px 10px;
  font-size: 11px;
  font-weight: 700;
}
.chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip b { font-variant-numeric: tabular-nums; font-weight: 900; letter-spacing: -0.03em; }
.chip-in { background: var(--accent-soft); color: var(--accent-ink); }
.chip-out { background: var(--neg-soft); color: var(--neg-ink); }
.chip-est { background: transparent; border-color: var(--accent-line); color: var(--accent-ink); }
.chip-wait { background: transparent; border-color: var(--line-strong); color: var(--ink-2); }
.chip.is-done { opacity: 0.55; }
.cal-more { padding: 2px 7px; border: 0; background: transparent; color: var(--ink-3); font-size: 10px; font-weight: 800; text-align: start; }

.agenda { display: none; }

/* ─── Day drawer ──────────────────────────────────────────────────────── */
.drawer { border-top: 2px solid var(--line); background: var(--surface-2); }
.drawer .panel-head { border-bottom-color: var(--line); }
.drawer .row { background: transparent; }
.drawer .rows { background: var(--surface); }

.day-flags { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 20px 14px; }

/* ─── Debts ───────────────────────────────────────────────────────────── */
.debt { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px 12px; padding: 14px 20px; border-bottom: 2px solid var(--line); }
.debt:last-child { border-bottom: 0; }
.debt h3 { font-size: var(--t-base); font-weight: 800; }
.debt p { color: var(--ink-2); font-size: var(--t-sm); font-weight: 500; }

/* ─── Category breakdown ──────────────────────────────────────────────── */
.cats { display: grid; }
.cat { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px 12px; padding: 14px 20px; border-bottom: 2px solid var(--line); background: transparent; }
.cats > .cat:last-child { border-bottom: 0; }
.cat h3 { font-size: var(--t-base); font-weight: 800; }
.cat p { color: var(--ink-2); font-size: var(--t-sm); font-weight: 500; }
.cat b { font-size: var(--t-md); }

/* ─── Agent strip ─────────────────────────────────────────────────────── */
.agent {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 20px;
  border-top: 2px solid var(--line);
  color: var(--ink-2);
  font-size: var(--t-sm);
  font-weight: 500;
}
.dot { width: 9px; height: 9px; flex: none; border-radius: 999px; background: var(--surface-3); }
.dot.is-live { background: var(--lime); }

button.snap { transition: background 150ms var(--ease); }

.mark-box {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  flex: none;
  border: 2.4px solid var(--line-strong);
  border-radius: 5px 7px 4px 8px;
  color: var(--on-fill);
  font-size: 14px;
  font-weight: 900;
  transition: background 150ms var(--ease), border-color 150ms var(--ease);
}
.mark-box.is-on { border-color: var(--ink); background: var(--lime); }

button.agenda-day { border: 0; font: inherit; color: inherit; text-align: start; background: transparent; }

.row.is-summary { background: var(--surface-2); }
.row.is-summary .row-date { background: var(--surface); }

p.agent { display: flex; align-items: center; gap: 10px; }

/* ─── Dialogs ─────────────────────────────────────────────────────────── */
.overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  place-items: center;
  padding: 18px;
  background: oklch(0.16 0.022 300 / 0.5);
  backdrop-filter: blur(3px);
}
.overlay.hidden { display: none; }

@media (prefers-reduced-motion: no-preference) {
  .overlay { animation: scrim-in 200ms var(--ease); }
  /* Exit is quicker than entry: the user has already decided. */
  .overlay.is-closing { animation: scrim-out 150ms var(--ease) forwards; }
  .overlay.is-closing .dialog { animation: pop-out 150ms var(--ease) forwards; }
}
@keyframes scrim-in { from { opacity: 0; } }
@keyframes scrim-out { to { opacity: 0; } }
@keyframes pop-out { to { opacity: 0; transform: translateY(6px) scale(0.99); } }

.dialog {
  position: relative;
  z-index: var(--z-dialog);
  width: min(460px, 100%);
  max-height: 88dvh;
  overflow-y: auto;
  display: grid;
  gap: 14px;
  padding: 24px;
  border: 0;
  border-radius: var(--r-panel);
  background: var(--surface);
  box-shadow: var(--shadow-pop);
}
@media (prefers-reduced-motion: no-preference) {
  .dialog { animation: pop 200ms var(--ease); }
}
@keyframes pop { from { opacity: 0; transform: translateY(10px) scale(0.985); } }

.dialog-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.dialog-head h2 { font-size: var(--t-lg); }

.segmented { display: flex; gap: 5px; padding: 5px; border-radius: var(--r-control); background: var(--surface-2); }
.segmented label { flex: 1; }
.segmented input { position: absolute; opacity: 0; pointer-events: none; }
.segmented span {
  display: block;
  padding: 9px;
  border-radius: var(--r-chip);
  color: var(--ink-2);
  font-size: var(--t-sm);
  font-weight: 800;
  text-align: center;
  transition: background 150ms var(--ease), color 150ms var(--ease);
}
.segmented input:checked + span { background: var(--ink); color: var(--bg); }
/* Money out is never lime: the switch is coloured by what it selects. */
.segmented .seg-out input:checked + span { background: var(--pink); color: var(--on-fill); }
.segmented .seg-in input:checked + span { background: var(--lime); color: var(--on-fill); }

/* ─── Auth ────────────────────────────────────────────────────────────── */
.auth { display: grid; place-items: center; min-height: 100dvh; padding: 24px; }
.auth form { width: min(400px, 100%); display: grid; gap: 14px; padding: 30px; border-radius: var(--r-panel); background: var(--surface); box-shadow: var(--shadow-card); }
.auth .mark { width: 46px; height: 46px; border-radius: 14px 18px 13px 17px; font-size: 23px; }
.auth h1 { font-size: var(--t-xl); font-weight: 900; }
.auth p { color: var(--ink-2); font-size: var(--t-sm); font-weight: 500; }

/* ─── Responsive ──────────────────────────────────────────────────────── */

/* ─── The waiting-money tile is a door ────────────────────────────────────
   The amber block was the only figure on the hero that answered a question
   the page never followed up on. It is now a button, and the <dl> around the
   other two collapses so the grid is unchanged. ─────────────────────────── */
.fig-pair { display: contents; margin: 0; }
button.flow-fig { border: 0; font: inherit; text-align: start; width: 100%; cursor: pointer; }
.flow-fig .fig-dt { order: 1; display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: var(--t-sm); font-weight: 700; opacity: 0.82; }
.flow-fig .fig-dd { order: 2; font-size: var(--t-lg); line-height: 1.05; font-weight: 900; letter-spacing: -0.03em; }
.fig-cue {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 9px;
  border-radius: 999px;
  background: oklch(0.16 0.022 300 / 0.13);
  font-size: var(--t-xs);
  font-weight: 800;
  opacity: 1;
}
.fig-cue::after { content: "‹"; font-weight: 900; }

/* ─── The archive ─────────────────────────────────────────────────────── */
.docs { display: grid; }
.doc {
  display: grid;
  grid-template-columns: 54px minmax(0, 1fr) auto;
  align-items: center;
  gap: 13px;
  width: 100%;
  padding: 12px 20px;
  border: 0;
  border-bottom: 2px solid var(--line);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: start;
  transition: background 150ms var(--ease);
}
.docs > .doc:last-child { border-bottom: 0; }
.doc-thumb {
  width: 54px;
  height: 54px;
  flex: none;
  object-fit: cover;
  border-radius: 12px 15px 11px 14px;
  background: var(--surface-2);
}
.doc-thumb.is-file { display: grid; place-items: center; color: var(--ink-3); }
.doc-main { min-width: 0; }
.doc-main h3 { font-size: var(--t-base); font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.doc-main p { color: var(--ink-2); font-size: var(--t-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.doc-side { display: grid; justify-items: end; gap: 5px; }
.doc-side b { font-size: var(--t-base); font-weight: 900; letter-spacing: -0.02em; }

.dialog.is-doc, .dialog.is-plan { width: min(560px, 100%); }
.doc-full { width: 100%; max-height: 38dvh; object-fit: contain; border-radius: var(--r-tile); background: var(--surface-2); }
.doc-summary { font-size: var(--t-base); font-weight: 600; line-height: 1.5; }
.doc-meta { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 0; }
.doc-meta div { display: grid; gap: 2px; padding: 9px 12px; border-radius: var(--r-chip); background: var(--surface-2); }
.doc-meta dt { color: var(--ink-2); font-size: var(--t-xs); font-weight: 700; }
.doc-meta dd { margin: 0; font-size: var(--t-sm); font-weight: 800; overflow-wrap: anywhere; }
a.btn { text-decoration: none; }
.is-danger-text { color: var(--neg-ink); background: transparent; }

/* ─── The plan for money that has not landed ──────────────────────────── */
.plan-block { display: grid; gap: 8px; }
.plan-block h3 { font-size: var(--t-sm); font-weight: 800; color: var(--ink-2); }
.plan-rows { display: grid; border-radius: var(--r-tile); background: var(--surface-2); overflow: hidden; }
.plan-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  border-bottom: 2px solid var(--surface);
}
.plan-rows > .plan-row:last-child { border-bottom: 0; }
.plan-row.is-step { grid-template-columns: 20px auto minmax(0, 1fr) auto; }
.plan-row.is-done { opacity: 0.5; }
.plan-row.is-done .plan-main b { text-decoration: line-through; }
.plan-num { color: var(--ink-3); font-size: var(--t-sm); font-weight: 900; }
.plan-main { display: grid; gap: 1px; min-width: 0; }
.plan-main b { font-size: var(--t-base); font-weight: 800; }
.plan-main span { color: var(--ink-2); font-size: var(--t-xs); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plan-amount { display: grid; justify-items: end; gap: 1px; }
.plan-amount b { font-size: var(--t-base); font-weight: 900; letter-spacing: -0.02em; }
.plan-amount > span { color: var(--ink-3); font-size: 11px; font-weight: 700; }
.plan-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  border-radius: var(--r-tile);
  font-size: var(--t-sm);
  font-weight: 800;
  color: var(--on-fill);
}
.plan-bottom b { font-size: var(--t-lg); font-weight: 900; letter-spacing: -0.03em; }
.plan-bottom.is-clear { background: var(--lime); }
.plan-bottom.is-short { background: var(--pink); }


/* ─── "Who recorded this" ─────────────────────────────────────────────────
   The agent has always known the actor; the page never showed it. The chip
   sits inside the row's note line so it reads as part of the sentence rather
   than as another column competing with the amount. ─────────────────────── */
.row-who {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-inline-end: 7px;
  padding: 1px 7px 2px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 11px;
  font-weight: 800;
}
.row-who svg { opacity: 0.7; }

/* Destructive, so it stays quiet until the row is approached. It is always in
   the layout (never appearing on hover alone) so touch users can reach it. */
.row-drop { width: 32px; padding: 0; color: var(--ink-3); background: transparent; }
@media (hover: hover) and (pointer: fine) {
  .row-drop { opacity: 0.45; }
  .rows .row:hover .row-drop { opacity: 1; }
  .row-drop:hover { background: var(--neg-soft); color: var(--neg-ink); }
}

/* The recorded panel sits between the forward-looking week and the tabs. */
.body-grid + .panel { margin-top: 18px; }


/* ─── Loans ───────────────────────────────────────────────────────────────
   A debt page has to answer "how bad is it" before "what do I do", so the
   stats read first and loudest, the cards carry the detail, and the plan sits
   underneath as a consequence of both. ──────────────────────────────────── */
.loan-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 2px; background: var(--line); border-bottom: 2px solid var(--line); }
.loan-stat { display: grid; align-content: start; gap: 2px; padding: 15px 18px 16px; background: var(--surface); }
.loan-stat > span { color: var(--ink-2); font-size: var(--t-xs); font-weight: 700; }
.loan-stat b { font-size: var(--t-lg); font-weight: 900; letter-spacing: -0.03em; }
.loan-stat i { color: var(--ink-3); font-size: 11px; font-weight: 600; font-style: normal; }
.loan-stat.is-lead { background: var(--hero-bg); color: var(--hero-fg); }
.loan-stat.is-lead > span { color: var(--hero-2); }
.loan-stat.is-lead b { color: var(--neg); font-size: var(--t-xl); }

.loans { display: grid; gap: 2px; background: var(--line); }
.loan { display: grid; gap: 12px; padding: 16px 20px 15px; background: var(--surface); }
.loan-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.loan-name h3 { font-size: var(--t-md); font-weight: 800; }
.loan-name p { color: var(--ink-2); font-size: var(--t-sm); font-weight: 600; }
.loan-balance { display: grid; justify-items: end; gap: 1px; text-align: end; }
.loan-balance b { font-size: var(--t-lg); font-weight: 900; letter-spacing: -0.03em; }
.loan-balance span { color: var(--ink-3); font-size: 11px; font-weight: 700; }
.loan-meter { display: grid; gap: 5px; }
.loan-meter > span { color: var(--ink-3); font-size: 11px; font-weight: 700; }

.loan-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(105px, 1fr)); gap: 8px; margin: 0; }
.loan-facts > div { display: grid; gap: 1px; padding: 8px 11px 9px; border-radius: var(--r-chip); background: var(--surface-2); }
.loan-facts dt { color: var(--ink-2); font-size: 11px; font-weight: 700; }
.loan-facts dd { margin: 0; font-size: var(--t-base); font-weight: 900; letter-spacing: -0.02em; }
/* A rate in the teens is the whole reason this page exists. */
.loan-facts > div.is-hot { background: var(--neg-soft); }
.loan-facts > div.is-hot dd { color: var(--neg-ink); }

.loan-flag { display: grid; gap: 2px; padding: 11px 14px 12px; border-radius: var(--r-tile); }
.loan-flag b { font-size: var(--t-base); font-weight: 900; }
.loan-flag span { font-size: var(--t-sm); font-weight: 600; line-height: 1.45; }
.loan-flag.is-warn { background: var(--warn-soft); color: var(--warn-ink); }
.loan-flag.is-bad { background: var(--neg-soft); color: var(--neg-ink); }
.loan-flag.is-wide { margin: 16px 20px; }
.loan-note { color: var(--ink-2); font-size: var(--t-sm); font-weight: 600; }
.loan-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--ink-3); font-size: var(--t-xs); font-weight: 700; }
.loan.is-balloon { box-shadow: inset 3px 0 0 var(--warn-line); }

.plan-order { grid-template-columns: 22px minmax(0, 1fr) auto auto; }
.rows > .row.is-best { background: var(--accent-soft); }
.loan-explain { padding: 0 20px 16px; }

@media (max-width: 560px) {
  .loan-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .loan-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .loan-stat.is-lead { grid-column: 1 / -1; }
  .kpi-row { grid-template-columns: 1fr; }
}


/* ─── The credit-card bill ────────────────────────────────────────────────
   One withdrawal made of many charges. Splitting it into "already planned"
   and "never counted" is the whole point: the second half is the reason the
   month looked healthier than it was. ────────────────────────────────────── */
.card-bill { display: grid; gap: 9px; padding: 15px 18px 16px; border-radius: var(--r-panel); background: var(--surface); box-shadow: var(--shadow-card); }
.card-bill-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.card-bill-head h3 { font-size: var(--t-md); font-weight: 800; }
.card-bill-head b { font-size: var(--t-lg); font-weight: 900; letter-spacing: -0.03em; }
.card-bill-split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.card-bill-split span { display: grid; gap: 1px; padding: 9px 12px 10px; border-radius: var(--r-chip); background: var(--surface-2); font-size: var(--t-xs); font-weight: 700; color: var(--ink-2); }
.card-bill-split i { font-size: var(--t-base); font-weight: 900; font-style: normal; color: var(--ink); letter-spacing: -0.02em; }
.card-bill-split .is-gap { background: var(--neg-soft); color: var(--neg-ink); }
.card-bill-split .is-gap i { color: var(--neg-ink); }
.card-bill > p { color: var(--ink-2); font-size: var(--t-sm); font-weight: 600; line-height: 1.45; }
@media (max-width: 560px) { .card-bill-split { grid-template-columns: 1fr; } }

@media (min-width: 1081px) {
  /* No hero to put beside the blocks any more - the heading runs full width,
     and the week strip and pain card still run full width underneath. */
  .flow { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); grid-template-areas: "head head" "figs figs" "weeks weeks" "pain pain" "card card"; }
  .flow-head { grid-area: head; }
  .flow-figures { grid-area: figs; }
  .weeks { grid-area: weeks; }
  .flow-pain { grid-area: pain; }
  .card-bill { grid-area: card; }
  .week .face { max-width: 92px; }
  .week b { font-size: var(--t-md); }
}

@media (max-width: 1080px) {
  .body-grid { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  /* Four controls, a month stepper and the brand mark do not fit at 390px.
     Everything shrinks a step rather than the mark being squeezed to nothing. */
  .topbar { height: 60px; gap: 8px; }
  .brand h1 { display: none; }
  .mark { width: 32px; height: 32px; border-radius: 10px 13px 9px 12px; font-size: 17px; }
  .month-nav { gap: 0; }
  .month-nav h2 { min-width: 8.5ch; padding: 0 2px; font-size: var(--t-xs); }
  .month-step { width: 28px; height: 28px; }
  .topbar-actions { gap: 5px; }
  .topbar-actions .who { width: 30px; height: 30px; }
  .btn-icon { width: 34px; min-height: 34px; }

  .weeks { padding: 14px 10px 13px; }
  /* The heading and its note fight for one line at 390px; stack them. */
  .weeks-head { flex-direction: column; align-items: flex-start; gap: 1px; padding-bottom: 10px; }
  .weeks-head .sub { margin-inline-start: 0; }
  .week-row { gap: 5px; }
  .week b { font-size: var(--t-xs); }
  .week span { font-size: 10px; }
  .flow-pain { gap: 11px; padding: 12px 14px; }
  .flow-pain .face { width: 44px; }
  .flow-pain .pain-amount { font-size: var(--t-md); }

  .row { padding: 12px 16px; gap: 10px; }
  .panel-head { padding: 14px 16px; }
  .snap, .debt, .cat { padding: 13px 16px; }
  .cal-grid, .cal-head { display: none; }
  .agenda { display: grid; }
  .agenda-day { display: grid; grid-template-columns: 46px minmax(0, 1fr); gap: 12px; padding: 12px 16px; border-bottom: 2px solid var(--line); }
  .agenda-day:last-child { border-bottom: 0; }
  .agenda-day > div:last-child { display: grid; gap: 5px; }
  .agenda .chip { padding: 8px 10px; font-size: var(--t-sm); border-radius: var(--r-chip); }
  /* Five tabs, 174px of text, 296px of room at 320px — the strip only
     overflowed because it kept desktop padding. Share the row instead of
     scrolling: a phone should never have to drag the tab bar sideways. */
  /* Eight destinations will not fit one row on a phone, and wrapping to two
     rows steals height from every page under a sticky bar. A snapping strip
     keeps the bar one row tall and the labels full-length. */
  .tab { flex: 0 0 auto; min-width: 0; padding: 0 14px; scroll-snap-align: center; }
  .tabs { flex-wrap: nowrap; scroll-snap-type: inline mandatory; }
  .doc { grid-template-columns: 48px minmax(0, 1fr) auto; padding: 12px 16px; }
  .doc-thumb { width: 48px; height: 48px; }
  .doc-meta { grid-template-columns: 1fr; }
}

@media (max-width: 420px) {
  .shell { padding-inline: 12px; }
  .tab { padding: 0 6px; }
  .flow-figures { grid-template-columns: 1fr; }
  .flow-fig { grid-template-columns: minmax(0, 1fr) auto; align-items: center; padding: 12px 15px 13px; }
  .flow-fig dd { font-size: var(--t-lg); }
}

/* ─── Pointer-only hover ──────────────────────────────────────────────
   Gated so a tap on a touch screen does not leave a sticky hover state
   behind on the row or tab that was pressed. ─────────────────────────── */
@media (hover: hover) and (pointer: fine) {
  .btn:hover { background: var(--surface-3); }
  .btn-primary:hover { background: var(--accent-line); }
  .btn-quiet:hover { background: var(--surface-2); color: var(--ink); }
  .btn-icon:hover { background: var(--surface-3); }
  .btn-mini:hover { background: var(--surface-3); }
  .btn-mini.is-danger:hover { background: var(--pink); color: var(--on-fill); }
  .check:hover { border-color: var(--ink); }
  .month-step:hover { background: var(--surface-3); }
  button.row:hover { background: var(--surface-2); }
  .tab:hover { background: var(--surface-2); color: var(--ink); }
  button.cal-day:hover { background: var(--surface-2); }
  button.snap:hover { background: var(--surface-2); }
  button.row:hover .mark-box { border-color: var(--ink); }
  button.agenda-day:hover { background: var(--surface-2); }
  .doc:hover { background: var(--surface-2); }
  .flow-fig.is-wait:hover .fig-cue { background: oklch(0.16 0.022 300 / 0.22); }
}

/* ─── Press feedback ──────────────────────────────────────────────────────
   Every pressable surface answers the finger. Compact controls scale; wide
   rows would look wrong scaling against their neighbours, so they take an
   immediate background instead plus a scale small enough to read as depth. */
.btn:active,
.btn-icon:active,
.btn-mini:active,
.month-step:active,
.tab:active,
button.snap:active { transform: scale(0.97); }

button.cal-day:active { transform: scale(0.94); }

button.row:active,
button.doc:active,
button.agenda-day:active {
  background: var(--surface-3);
  transform: scale(0.995);
}

button.flow-fig:active { transform: scale(0.99); }

.btn-icon, .btn-mini, .month-step, .tab, button.snap, button.cal-day,
button.row, button.agenda-day, button.doc, button.flow-fig { transition-property: background, color, border-color, transform; }

/* Statement queue rows carry three choices, which do not fit the four-column
   row on a phone. The actions drop to their own line under the merchant while
   the date keeps its column, so a thumb has a full-width target per decision. */
.row.is-stack {
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "date main amount" ". acts acts";
  row-gap: 8px;
}
.row.is-stack .row-date { grid-area: date; }
.row.is-stack .row-main { grid-area: main; }
.row.is-stack .row-amount { grid-area: amount; }
.row-acts { grid-area: acts; display: flex; flex-wrap: wrap; gap: 8px; }
.row-acts .btn-mini { flex: 1 1 auto; justify-content: center; }
.row-main .hint {
  margin-top: 4px;
  color: var(--ink-2);
  font-size: var(--t-sm);
  font-weight: 600;
  white-space: normal;
}

/* What ticking a line will actually move. The effect used to be invisible —
   and for loans it was not happening at all. */
.row-main .settles {
  margin-top: 2px;
  color: var(--accent-ink);
  font-size: var(--t-xs, var(--t-sm));
  font-weight: 700;
  white-space: normal;
}

/* ═══ The advisor ════════════════════════════════════════════════════════
   This page carries three things the rest of the app does not: a sentence
   written about the household, the figures behind it, and an admission of
   what could not be seen. So it gets its own tile rather than a row - a row
   is for a fact, and none of these is only a fact - but it stays inside the
   panel, the tag vocabulary and the token set everything else uses.
   ═════════════════════════════════════════════════════════════════════ */

/* Both cards are usually stale, so this banner is the page's normal opening.
   Amber, because it is the token for money that has not landed: a figure
   that is missing, not a figure that is bad. */
.gap-banner {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 14px;
  padding: 14px 18px;
  border-radius: var(--r-tile-alt);
  background: var(--amber);
  color: var(--on-fill);
}
.gap-banner .face { width: 46px; height: auto; flex: none; }
/* The face keeps its own pink skin rather than taking a neutral one: the
   neutral surface is near-black at night, and the eyes and mouth are always
   --on-fill, so a "light" skin token would swallow the face after dark. */
.gap-banner > div { flex-grow: 1; min-width: 0; }
.gap-banner b { display: block; font-size: var(--t-base); font-weight: 800; line-height: 1.35; }
.gap-banner span { display: block; margin-top: 3px; font-size: var(--t-sm); font-weight: 600; line-height: 1.45; opacity: 0.82; }
.gap-banner .btn-mini { flex: none; background: var(--surface); color: var(--ink); }

.advice-feed { display: grid; gap: 12px; padding: 14px 20px 6px; }

.advice {
  display: grid;
  gap: 9px;
  padding: 15px 17px 14px;
  border-radius: var(--r-tile);
  background: var(--surface-2);
}
.advice:nth-child(even) { border-radius: var(--r-tile-alt); }
/* Severity 4 is the only one that changes the surface. Everything below it is
   carried by the tag, so an ordinary month does not look like an emergency. */
.advice.is-urgent { background: var(--neg-soft); }
.advice.is-handled h3 { font-weight: 700; color: var(--ink-2); }

.advice-head { display: flex; align-items: flex-start; gap: 9px; }
.advice-head .tag { flex: none; margin-top: 2px; }
.advice-head h3 { max-width: 72ch; font-size: var(--t-base); font-weight: 800; line-height: 1.45; text-wrap: pretty; }

.advice-do { max-width: 72ch; font-size: var(--t-sm); font-weight: 700; line-height: 1.45; text-wrap: pretty; }
.advice-do span { display: block; color: var(--ink-2); font-size: var(--t-xs); font-weight: 800; }

/* The provenance. Native <details>, so it needs no script and survives the
   re-render that follows every decision. */
.why { border-top: 2px solid var(--line); padding-top: 9px; }
.advice.is-urgent .why { border-top-color: var(--neg-line); }
.why > summary {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--ink-2);
  font-size: var(--t-xs);
  font-weight: 800;
  cursor: pointer;
  list-style: none;
}
.why > summary::-webkit-details-marker { display: none; }
.why > summary::after {
  content: "";
  width: 6px;
  height: 6px;
  border-inline-end: 2px solid currentColor;
  border-block-end: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
.why > summary:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; border-radius: 6px; }
.why[open] > summary::after { transform: translateY(1px) rotate(225deg); }

.why-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
  gap: 7px;
  margin: 10px 0 0;
}
.why-facts > div { display: grid; gap: 1px; padding: 8px 11px; border-radius: var(--r-chip); background: var(--surface); }
.why-facts dt { color: var(--ink-2); font-size: var(--t-xs); font-weight: 700; }
.why-facts dd { margin: 0; font-size: var(--t-sm); font-weight: 800; overflow-wrap: anywhere; }
.why-note { margin-top: 9px; color: var(--ink-3); font-size: var(--t-xs); font-weight: 600; line-height: 1.45; }

.advice-acts { display: flex; flex-wrap: wrap; gap: 8px; }
/* Three decisions, not three banners: on a wide screen they stay the size of
   their own words and sit at the start edge. The phone gets full-width
   targets instead, further down. */
.advice-acts .btn-mini { flex: 0 0 auto; }
.advice-spend, .advice-note { padding: 10px 20px 16px; line-height: 1.45; }

.empty.is-advice { max-width: 46ch; margin-inline: auto; }
.empty.is-advice .face { width: 60px; height: auto; margin-bottom: 6px; }
.empty.is-advice span { line-height: 1.5; }

/* The months the baseline was built from take the biggest slot on the row,
   because that is the number that says how much the row is worth. */
.base-row {
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "months main amount" ". tag tag";
  row-gap: 7px;
}
.base-row .row-date { grid-area: months; }
.base-row .row-main { grid-area: main; }
.base-row .row-amount { grid-area: amount; }
.base-row .tag { grid-area: tag; justify-self: start; }
.base-row.is-thin .row-date { background: var(--warn-soft); color: var(--warn-ink); }
/* The count and the cadence are the evidence for the figure beside them, so
   at phone width they wrap instead of being cut off mid-word. */
.base-row .row-main p { white-space: normal; }

@media (min-width: 721px) {
  .base-row { grid-template-columns: auto minmax(0, 1fr) auto auto; grid-template-areas: "months main amount tag"; row-gap: 0; }
  .base-row .tag { justify-self: end; }
}

/* At 360px the three decisions stop sharing a line and the banner's link
   drops under its own text rather than squeezing the sentence to nothing. */
@media (max-width: 480px) {
  .gap-banner { flex-wrap: wrap; gap: 11px 12px; padding: 13px 15px; }
  .gap-banner .face { width: 40px; }
  .gap-banner .btn-mini { width: 100%; }
  .advice-feed { padding: 12px 14px 4px; }
  .advice { padding: 13px 14px 12px; }
  .advice-acts .btn-mini { flex: 1 1 100%; justify-content: center; }
  .advice-spend, .advice-note { padding: 10px 16px 14px; }
  .why-facts { grid-template-columns: minmax(0, 1fr); }
}

@media (hover: hover) and (pointer: fine) {
  .why > summary:hover { color: var(--ink); }
  .gap-banner .btn-mini:hover { background: var(--surface-2); }
  .btn-mini.is-yes:hover { background: var(--lime); color: var(--on-fill); }
}

/* ── Figures the system cannot stand behind ──────────────────────────────
   A balance is an anchor plus every ledger event since. When the ledger stops
   short of today the number is still the best available, but it is no longer a
   fact — so it is never allowed to look like one. The date it was last true is
   printed next to it, and the figure itself loses the full-strength ink that
   verified numbers carry. */
.as-of {
  display: inline-block;
  margin-inline-start: 6px;
  color: var(--warn-ink);
  background: var(--warn-soft);
  border: 1px solid var(--warn-line);
  border-radius: 999px;
  padding: 1px 7px;
  font-size: 0.72em;
  font-weight: 700;
  white-space: nowrap;
  vertical-align: middle;
}
.num.is-unsure, .btn-mini.is-unsure { color: var(--ink-2); }
.loan-balance .num.is-unsure { opacity: 0.82; }
.form-note.is-unsure-note { color: var(--warn-ink); }

/* On a narrow screen the badge drops under its figure rather than pushing the
   row sideways — the same overflow trap the dashboard hit before. */
@media (max-width: 420px) {
  .as-of { margin-inline-start: 0; margin-block-start: 3px; display: block; width: fit-content; }
}

/* ── היועץ ───────────────────────────────────────────────────────────────
   A consulting surface inside a money app, so it borrows the app's
   vocabulary rather than importing a chat aesthetic. No bubbles on the
   household's own messages: alignment is enough to say who spoke, and a
   second bubble shape would be one more rounded rectangle on a page whose
   whole radius system exists to avoid that. */
.panel-talk { display: flex; flex-direction: column; gap: 14px; }

.talk-log {
  display: flex; flex-direction: column; gap: 16px;
  /* No min-height. Reserving space the conversation has not filled yet leaves
     a void between the last answer and the composer that reads as a broken
     layout rather than as room to breathe. The log grows with the exchange and
     starts scrolling at max-height, which settles the composer after two or
     three turns. */
  max-height: 62vh; overflow-y: auto;
  padding-block: 4px; padding-inline: 2px;
  scrollbar-width: thin;
}

.talk { max-width: 82%; }
.talk p { margin: 0 0 8px; line-height: 1.62; }
.talk p:last-child { margin-bottom: 0; }

/* The household's own words: no container at all. Weight and colour carry the
   turn, which is enough when the answer below sits on its own surface — and a
   second bubble shape would be one more rounded rectangle on a page whose whole
   radius system exists to avoid exactly that. Full width rather than pushed to
   the far side: a short question ragged against a wide answer reads as a
   layout accident, not as a speaker change. */
.talk-mine {
  max-width: 100%;
  margin-block-start: 6px;
  color: var(--ink);
}
.talk-mine p { font-weight: 700; }
.talk-mine + .talk-theirs { margin-block-start: -4px; }

.talk-theirs {
  background: var(--surface-2);
  border-radius: var(--r-tile);
  padding: 14px 16px;
}
.talk-theirs .talk-body { max-width: 68ch; }

/* A figure the answer states that is not in the data. Not an error — a
   derived number is legitimate — so it is marked, not coloured. The four
   fills are semantic here (lime in, pink out, amber promised, teal second
   income); borrowing one would say something untrue about the money. A
   dotted rule says "provisional" and claims none of them. */
.fig-unsure {
  border-block-end: 1.5px dotted var(--ink-3);
  padding-block-end: 1px;
  cursor: help;
}

.talk-check {
  margin-block-start: 10px;
  color: var(--ink-3);
  font-size: var(--t-xs);
  font-weight: 600;
}
.talk-check .num { font-size: inherit; }

.talk-empty { color: var(--ink-2); max-width: 54ch; }
.talk-empty h3 { margin: 0 0 6px; font-size: var(--t-lg); font-weight: 800; color: var(--ink); }
.talk-empty p { margin: 0 0 12px; line-height: 1.6; }
.talk-sees { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; padding: 0; list-style: none; }
.talk-sees li {
  background: var(--surface-2); border-radius: var(--r-chip);
  padding: 7px 12px; font-size: var(--t-sm); font-weight: 700; color: var(--ink);
}
.talk-limit {
  border-block-start: 1px solid var(--line);
  padding-block-start: 12px;
  color: var(--ink-3); font-size: var(--t-sm);
}

.talk-error {
  margin: 0; color: var(--neg-ink); font-size: var(--t-sm); font-weight: 700;
}

.talk-compose { display: flex; gap: 10px; align-items: flex-end; }
.talk-compose textarea {
  flex: 1; resize: none;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-control);
  padding: 12px 14px;
  font: inherit; font-size: var(--t-base); line-height: 1.5;
}
.talk-compose textarea::placeholder { color: var(--ink-3); }
.talk-compose textarea:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 1px; }
.talk-compose textarea:disabled { opacity: 0.6; }
.talk-compose .btn { flex: 0 0 auto; }

/* The composer grows with the question. A class ladder, not a measured pixel
   height: the CSP forbids inline styles and the app already sizes this way. */
.talk-compose textarea.rows-1 { height: 48px; }
.talk-compose textarea.rows-2 { height: 72px; }
.talk-compose textarea.rows-3 { height: 96px; }
.talk-compose textarea.rows-4 { height: 120px; }
.talk-compose textarea.rows-5 { height: 144px; }

/* The one piece of motion on this page, and it is feedback rather than
   decoration: the answer takes several seconds and silence reads as failure. */
.talk-wait { display: flex; gap: 6px; align-items: center; width: fit-content; }
.talk-wait .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--ink-3);
  animation: talk-pulse 1.25s ease-in-out infinite;
}
.talk-wait .dot:nth-child(2) { animation-delay: 0.16s; }
.talk-wait .dot:nth-child(3) { animation-delay: 0.32s; }
@keyframes talk-pulse {
  0%, 60%, 100% { opacity: 0.3; }
  30% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .talk-wait .dot { animation: none; opacity: 0.55; }
}

@media (max-width: 560px) {
  .talk { max-width: 92%; }
  .talk-log { max-height: 56vh; }
  .talk-compose { gap: 8px; }
}

/* ── Navigation: a strip on desktop, a sheet on a phone ───────────────────
   Eleven destinations do not fit across a phone. The horizontal scroller kept
   four of them off-screen behind a gesture with no affordance, which is the
   same failure the page architecture was meant to fix: the app looked smaller
   than it is. Below 720px the strip is replaced by a bar that names the page
   you are on and opens the rest — so the current location is always stated,
   which the scroller never did either.

   Both are always in the DOM and CSS chooses between them. Switching on a JS
   breakpoint would leave a resized window with no navigation at all. */
.nav { margin: 0 0 18px; }
/* The strip carried its own bottom margin; inside .nav that would double up. */
.nav .tabs { margin: 0; }
.nav-open { display: none; }

@media (max-width: 720px) {
  .nav { position: sticky; top: 0; z-index: 20; }
  .nav .tabs { display: none; }
  .nav-open {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 12px 14px;
    border: 0;
    border-radius: var(--r-control);
    background: var(--surface);
    box-shadow: var(--shadow-card);
    color: var(--ink);
    font: inherit;
    font-size: var(--t-base);
    font-weight: 800;
    text-align: start;
    cursor: pointer;
  }
  .nav-open:active { transform: translateY(1px); }
}

.nav-sheet { position: fixed; inset: 0; z-index: 60; }
.nav-sheet.hidden { display: none; }
.nav-scrim {
  position: absolute; inset: 0;
  background: oklch(0.16 0.022 300 / 0.5);
  backdrop-filter: blur(3px);
}

/* Anchored to the start edge, which in RTL is the right — the side the thumb
   and the button are both already on. */
.nav-panel {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: min(82vw, 320px);
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 16px 14px calc(16px + env(safe-area-inset-bottom));
  background: var(--bg);
  box-shadow: var(--shadow-card);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.nav-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-block-end: 10px;
}
.nav-panel-head h2 { margin: 0; font-size: var(--t-lg); font-weight: 800; }

.nav-link {
  display: block;
  padding: 13px 14px;
  border-radius: var(--r-chip);
  color: var(--ink);
  font-size: var(--t-base);
  font-weight: 700;
  text-decoration: none;
}
.nav-link[aria-current="page"] { background: var(--ink); color: var(--bg); }
.nav-link:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 1px; }

/* The page behind must not scroll under the thumb; on a phone that reads as
   the sheet itself sliding rather than the page. */
body.nav-locked { overflow: hidden; }

@media (prefers-reduced-motion: no-preference) {
  .nav-sheet { animation: scrim-in 200ms var(--ease); }
  .nav-panel { animation: nav-in 220ms var(--ease); }
  /* Exit is quicker than entry: the user has already decided. */
  .nav-sheet.is-closing { animation: scrim-out 150ms var(--ease) forwards; }
  .nav-sheet.is-closing .nav-panel { animation: nav-out 150ms var(--ease) forwards; }
}
/* translateX is physical and is NOT mirrored by `direction: rtl`, so with the
   panel anchored to the inline start (the right, in Hebrew) it slides in from
   positive X. Getting this backwards sends the sheet across the screen from
   the far side. */
@keyframes nav-in { from { transform: translateX(100%); } }
@keyframes nav-out { to { transform: translateX(100%); } }

@media (hover: hover) and (pointer: fine) {
  .nav-link:hover { background: var(--surface-2); }
  .nav-link[aria-current="page"]:hover { background: var(--ink); }
  .nav-open:hover { background: var(--surface-2); }
}

/* ── Facility and account cards ──────────────────────────────────────────
   The overview used to lead with a utilisation percentage, which reads as a
   grade rather than an amount: nobody can act on "135%". These cards lead
   with the money — what is free, or by how much the line has been passed —
   and demote the percentage to a fact alongside the ceiling. Built as one
   column at every width: on a phone two of these side by side would put a
   four-figure sum in a 40px column, and the page is read on a phone. */
.fac-list { display: grid; gap: 12px; }
.fac {
  display: grid;
  gap: 10px;
  padding: 16px 18px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  box-shadow: var(--shadow-card);
}
.fac.is-over { border-color: var(--neg-line); background: var(--neg-soft); }
.fac-top { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.fac-top h3 { margin: 0; font-size: var(--t-base); font-weight: 700; }
.fac-bill { margin-inline-start: auto; font-size: var(--t-xs); color: var(--ink-2); }
.fac-headline { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.fac-free-label { font-size: var(--t-xs); color: var(--ink-2); }
.fac-free { font-size: var(--t-lg); font-weight: 800; letter-spacing: -0.02em; }
.fac-free.is-free { color: var(--accent-ink); }
.fac-free.is-over { color: var(--neg-ink); }

/* Two columns of facts on a phone, four once there is room. `dl` keeps the
   label bound to its figure for a screen reader, which a pair of spans
   would not. */
.fac-facts { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px 14px; margin: 0; }
.fac-facts > div { display: grid; gap: 1px; }
.fac-facts dt { font-size: var(--t-xs); color: var(--ink-3); }
.fac-facts dd { margin: 0; font-size: var(--t-sm); font-weight: 700; }
.fac-note { margin: 0; font-size: var(--t-xs); color: var(--ink-2); }
@media (min-width: 640px) {
  .fac-facts { grid-template-columns: repeat(4, 1fr); }
}

/* ── The alarm ───────────────────────────────────────────────────────────
   Reserved for a dated breach that has not happened yet, so it must not
   become ordinary furniture: `billAlarm` returns nothing when the account
   covers the bill. It is the one element allowed above the hero. */
.alarm {
  display: grid;
  gap: 8px;
  padding: 18px 20px;
  margin-bottom: 14px;
  background: var(--neg);
  color: var(--on-neg);
  border-radius: var(--r-panel);
}
.alarm-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.alarm-when {
  font-size: var(--t-xs);
  font-weight: 800;
  padding: 2px 9px;
  border-radius: 999px;
  background: oklch(1 0 0 / 0.35);
}
.alarm-head h2 { margin: 0; font-size: var(--t-base); font-weight: 700; }
.alarm-num { font-size: var(--t-2xl); font-weight: 800; letter-spacing: -0.03em; line-height: 1.05; }
.alarm-line { margin: 0; font-size: var(--t-sm); }
.alarm-gap {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 2px;
  padding-top: 10px;
  border-top: 1px solid oklch(1 0 0 / 0.4);
  font-size: var(--t-sm);
  font-weight: 700;
}
.alarm-gap b { font-size: var(--t-md); }
