/* my-finances dashboard — dark theme.
 *
 * Colors come from the dataviz reference palette (dark column), validated as a set
 * against --surface-1 below. The categorical slots are exposed as --series-1..16 so
 * the one bridge from CSS to ECharts is getComputedStyle() — theme lives here, never
 * in JS. Text always wears text tokens; only marks wear series color.
 */

:root {
  color-scheme: dark;

  /* Surfaces */
  --plane: #0d0e12;          /* page background */
  --surface-1: #16181f;      /* cards / chart surface (≈ validator surface) */
  --surface-2: #1c1f28;      /* insets, hover wash */
  --surface-3: #232733;      /* active / raised */

  /* Ink */
  --text-primary: #ffffff;
  --text-secondary: #c3c2b7;
  --text-muted: #898781;
  /* A series switched off in a chart legend, label and chip fill. ECharts' own
     #cfd2d7 measures 11.7:1 on --surface-1, above an on entry's label
     (--text-secondary, 9.9:1), so off read as the brightest thing in the legend.
     This is --text-muted at 70% over --surface-1: 3.1:1, legible enough to find
     the way back on, and plainly under the axis labels (--text-muted, 4.9:1).
     A hex, not a color-mix(): getCssVars hands ECharts the property's text as
     written, and only the browser resolves a color-mix(). */
  --legend-off: #666664;

  /* Chrome */
  --grid: #2c2c2a;
  --axis: #383835;
  --border: rgba(255, 255, 255, 0.10);
  --border-strong: rgba(255, 255, 255, 0.16);

  /* Deltas / status (legible small-text steps on the dark surface) */
  --pos: #3ddc84;
  --neg: #ff6b6b;
  --warn: #fab219;

  /* Categorical series — fixed order IS the CVD-safety mechanism; never reorder.
   * Slots 1–8 are the reference palette's dark column in its documented order.
   * Slots 9–16 are the SAME eight hues at a lighter step: with 17 accounts to draw,
   * hue alone cannot carry identity, so hue names the family and lightness the tier
   * (composite encoding). That is legal only because every series is also named — in
   * the legend, in every tooltip row, and in both tables. Measured with the dataviz
   * validator against --surface-1: chroma floor, normal-vision ΔE (worst adjacent
   * 17.9 vs floor 15) and contrast (light tier 7.2–9.5:1) all pass; worst adjacent
   * CVD ΔE is 7.1, inside the 6–8 band that mandates that secondary encoding. The
   * light steps sit above the dark lightness band on purpose — brighter on a dark
   * surface means more contrast, not less. */
  --series-1: #3987e5;  /* blue */
  --series-2: #d95926;  /* orange */
  --series-3: #199e70;  /* aqua */
  --series-4: #c98500;  /* yellow */
  --series-5: #d55181;  /* magenta */
  --series-6: #008300;  /* green */
  --series-7: #9085e9;  /* violet */
  --series-8: #e66767;  /* red */
  --series-9: #65a8fd;   /* blue    light */
  --series-10: #fe8e67;  /* orange  light */
  --series-11: #4cd39d;  /* aqua    light */
  --series-12: #f3a525;  /* yellow  light */
  --series-13: #fe8aaf;  /* magenta light */
  --series-14: #5fd658;  /* green   light */
  --series-15: #aba4fe;  /* violet  light */
  --series-16: #fe8885;  /* red     light */

  /* The two account categories are colours by name, not by slot: the net-worth
     Cash / Invest bands and the badges in the accounts table read these, so the
     chart and the table cannot disagree about which is which. */
  --cat-cash: var(--series-3);    /* aqua */
  --cat-invest: var(--series-1);  /* blue */

  /* Sequential ramp for a magnitude (the income calendar), from just above the
     empty-cell surface up to the series blue it ends on. */
  --seq-1: #172033;
  --seq-2: #1c3a63;
  --seq-3: #245089;
  --seq-4: #2f6fc0;
  --seq-5: var(--series-1);

  /* Ink on an accent fill (the pressed period, the primary button), the shade a
     pinned column casts, and the ring every focused control wears. Everything
     translucent below is one of these tokens mixed with transparent — no rgba()
     copies of a hex value that a palette change would leave behind. */
  --on-accent: #ffffff;
  --shade: #000000;
  --focus-ring: 0 0 0 3px color-mix(in srgb, var(--series-1) 55%, transparent);
  /* Laid over a control's own fill under the pointer (see Interaction below). */
  --hover-wash: color-mix(in srgb, var(--text-primary) 8%, transparent);

  --radius: 14px;
  --radius-sm: 9px;
  --gap: 16px;
  --maxw: 1180px;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--plane);
  color: var(--text-primary);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--series-1); text-decoration: none; }
a:hover { text-decoration: underline; }

.tnum { font-variant-numeric: tabular-nums; }

/* Read by a screen reader, drawn for nobody — the words a visual mark stands for.
   Being absolute, it is placed against its nearest positioned ancestor, and only
   a box that is (or holds) that containing block clips it: a scroll box that is
   not positioned lets it through. So every box here that scrolls its overflow is
   positioned (.tbl-wrap, .kv-list); the note on .tbl-wrap has what it cost. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ── Header ─────────────────────────────────────────────────────────── */
.hdr {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px clamp(14px, 4vw, 28px);
  background: color-mix(in srgb, var(--plane) 82%, transparent);
  backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid var(--border);
}
.hdr__brand {
  font-weight: 650;
  letter-spacing: -0.01em;
  font-size: 16px;
  display: flex;
  align-items: center;
  gap: 9px;
  /* The header is one line or it is not a header: the word wraps to "my-/
     finances" at phone width before anything else gives. */
  white-space: nowrap;
}
.hdr__dot {
  /* Aqua specifically — the halo below is the same hue at low alpha. */
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--series-3);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--series-3) 16%, transparent);
}
.hdr__spacer { flex: 1; }
.hdr__sync {
  font-size: 12.5px;
  color: var(--text-muted);
  display: flex; align-items: center; gap: 7px;
  white-space: nowrap;
}
.hdr__sync .pip { width: 7px; height: 7px; border-radius: 50%; background: var(--pos); }
.hdr__sync.is-stale .pip { background: var(--warn); }
.hdr__sync.is-error .pip { background: var(--neg); }
/* Only the index page fills the badge; on the rest it must not hold a gap slot open. */
.hdr__sync:empty { display: none; }
.hdr__nav { display: flex; gap: 2px; margin-left: 4px; }
.hdr__nav a {
  padding: 6px 12px;
  border-radius: 999px;
  font-size: 13.5px;
  color: var(--text-secondary);
}
.hdr__nav a[aria-current="page"] { color: var(--text-primary); background: var(--surface-3); }
.btn-logout {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-secondary);
  border-radius: 999px;
  padding: 6px 14px;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}

/* ── Page shell ─────────────────────────────────────────────────────── */
.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(16px, 3vw, 26px) clamp(14px, 4vw, 28px) 60px;
}

.section-gap { height: var(--gap); }

/* ── Hero + period matrix ───────────────────────────────────────────── */
.stats {
  display: grid;
  grid-template-columns: minmax(230px, 1fr) minmax(0, 1.9fr);
  gap: var(--gap);
}
.card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
}
.card--hero { display: flex; flex-direction: column; justify-content: center; }

/* Change and income for every window in one card: rows are the metric, columns the
   period, so the eye compares along a row instead of across six separate tiles. */
.pgrid {
  display: grid;
  grid-template-columns: auto repeat(3, minmax(0, 1fr));
  align-items: baseline;
  column-gap: 14px;
  row-gap: 2px;
}
.pgrid__head { text-align: right; }
.pgrid__key { padding-right: 4px; white-space: nowrap; }
.pgrid__val {
  text-align: right;
  font-size: 20px; font-weight: 680; letter-spacing: -0.02em;
  white-space: nowrap;
  margin-top: 4px;
}
.pgrid__sub { text-align: right; font-size: 12px; color: var(--text-muted); }
.pgrid__spark { grid-column: 2 / -1; }
.pgrid__gap { grid-column: 1 / -1; height: 10px; }
/* The small uppercase label — a stat's name, a table's column heads, a goal's
   figure names, a table's group rows. One pattern in one rule; each place keeps
   only its size. (Four copies had drifted to two letter-spacings.) */
.stat__label, .tbl th, .goal__k, .row--group td {
  color: var(--text-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.stat__label { font-size: 12.5px; }
/* Only the hero card has a stat value; the 23px base and its phone override were
   outranked by the hero's own size everywhere they could have applied. */
.stat__value {
  margin-top: 6px;
  font-size: clamp(34px, 6vw, 46px);
  font-weight: 680;
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.spark { margin-top: 12px; height: 34px; width: 100%; display: block; }

/* ── Period switcher / segmented controls ───────────────────────────── */
.controls-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.seg {
  display: inline-flex;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px;
  gap: 2px;
}
.seg button {
  appearance: none; border: 0; background: transparent;
  color: var(--text-secondary);
  font: inherit; font-size: 13px; font-weight: 600;
  padding: 6px 13px; border-radius: 999px; cursor: pointer;
  transition: background .12s, color .12s;
}
.seg button[aria-pressed="true"] {
  background: var(--series-1);
  color: var(--on-accent);
}
.seg--sub button[aria-pressed="true"] { background: var(--surface-3); color: var(--text-primary); }

/* ── Chart cards ────────────────────────────────────────────────────── */
.grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gap);
}
.col-12 { grid-column: span 12; }
.col-8 { grid-column: span 8; }
.col-4 { grid-column: span 4; }

.panel {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px 18px;
  min-width: 0;               /* let grid children shrink so ECharts can resize */
  display: flex;
  flex-direction: column;     /* so a chart can claim the row's leftover height */
}
.panel__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 6px; flex-wrap: wrap;
}
.panel__title { font-size: 15px; font-weight: 640; letter-spacing: -0.01em; }
.panel__sub { font-size: 12.5px; color: var(--text-muted); margin-top: 1px; }
.chart { width: 100%; height: 300px; }
/* Grid rows stretch, and the breakdown beside it is as tall as the account list.
   Rather than leave a void under the net worth, let it take the slack — a 17-band
   stack is easier to read the more height it gets. */
.chart--tall { height: 320px; flex: 1 1 auto; }
.chart--cal { height: 190px; }
.chart--donut { height: 240px; }

.note {
  margin-top: 10px;
  font-size: 12px;
  color: var(--text-muted);
}
.note--warn { color: var(--warn); }

/* ── Tables ─────────────────────────────────────────────────────────── */
.tbl-wrap { overflow-x: auto; border-radius: var(--radius-sm); position: relative; }
/* A scroll box clips an absolute box only when that box's containing block is the
   scroll box or inside it. Unpositioned, the wrap was neither: the ledger's
   "(No FX rate: …)" (.sr-only) in an unpriced row was placed against the
   document, at its static spot 502px in, and a 390px phone page measured 503 wide
   and panned sideways. position:relative makes the wrap the containing block, so
   anything absolute in a table scrolls and clips with it. The sticky header and
   first column are untouched: sticky follows the nearest scroller, not the nearest
   positioned box. */
/* A sticky header sticks to its nearest scrolling box, and overflow-x:auto alone
   makes the wrap that box while never letting it scroll vertically — so the header
   of a 365-row breakdown scrolled away with the page. A tall table scrolls inside
   its own capped wrap instead, and both the header row and the first column stay. */
.tbl-wrap--tall { max-height: min(70vh, 720px); overflow: auto; }
table.tbl {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tbl th, .tbl td {
  padding: 9px 12px;
  text-align: right;
  border-bottom: 1px solid var(--border);
}
.tbl th {
  font-size: 12px;
  position: sticky; top: 0;
  background: var(--surface-1);
  z-index: 1;
}
.tbl th:first-child, .tbl td:first-child {
  text-align: left;
  position: sticky; left: 0;
  background: var(--surface-1);
  z-index: 2;
  /* Two independent shadows, each switched on by its own custom property rather
     than by a rule that would replace the other's box-shadow: a row's accent (the
     overdue plan's inset bar) and the pinned column's scroll shadow below. */
  box-shadow: var(--row-accent, 0 0 transparent), var(--pin-shadow, 0 0 transparent);
}
.tbl th:first-child { z-index: 3; }
/* An empty state is one cell spanning the table: centred, and not pinned like the
   first cell of a real row. Declared after the first-child rule it has to beat. */
.tbl td.empty { text-align: center; padding: 40px 18px; position: static; }
/* A group header is a label, not a row to point at, so it takes no hover wash. One
   rule, specific enough to beat the pinned first cell's own background. */
.tbl tbody tr:not(.row--group):hover td { background: var(--surface-2); }
.tbl tfoot td {
  font-weight: 680;
  border-top: 1px solid var(--border-strong);
  border-bottom: 0;
}
/* Closed accounts sit under the live ones: present, readable, and plainly not part
   of the figures above. The rule above the first of them is the divider. */
.row--archived td { color: var(--text-secondary); }
.tbl tbody tr:not(.row--archived) + .row--archived td { border-top: 1px solid var(--border-strong); }
.cell-native { color: var(--text-muted); font-size: 11.5px; margin-top: 2px; }
.pos { color: var(--pos); }
.neg { color: var(--neg); }
/* Two meanings, one ink: .muted is a figure that is exactly zero, .dash is no
   figure at all (unpriced, or nothing to show). */
.muted, .dash { color: var(--text-muted); }
.warn { color: var(--warn); }

/* A text column reads as text — the ledger's and the planned panel's project,
   account and note — while the money and the actions stay right. Marked on the
   cells the script builds, not by column position: a column added or moved used
   to leave the wrong one left-aligned. */
.tbl :is(th, td).t-text { text-align: left; }

/* Every table pins its first column, and a wide one — the daily breakdown with
   its column per account, the all-income matrix on a phone — scrolls sideways
   under it. While it does, the pinned column casts a soft shadow over what slides
   beneath, so a half-hidden figure reads as going under the column instead of
   losing its "$". Toggled from the scroll position (tables.bindScrollShadow) on
   every table of both pages; at the left end there is nothing underneath and no
   shadow. */
.tbl-wrap.is-scrolled {
  --pin-shadow: 12px 0 14px -12px color-mix(in srgb, var(--shade) 70%, transparent);
}

.badge {
  display: inline-block;
  font-size: 11px; font-weight: 600;
  padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  text-transform: capitalize;
}
/* The category colours (--cat-*), the same ones the net-worth bands wear; the
   border is the same hue at 40%. */
.badge--invest { color: var(--cat-invest); border-color: color-mix(in srgb, var(--cat-invest) 40%, transparent); }
.badge--cash { color: var(--cat-cash); border-color: color-mix(in srgb, var(--cat-cash) 40%, transparent); }

.swatch {
  display: inline-block; width: 9px; height: 9px; border-radius: 2px;
  margin-right: 7px; vertical-align: baseline;
}
.swatch--sm { width: 8px; height: 8px; margin-right: 6px; }

/* ── Chart tooltips ─────────────────────────────────────────────────── */
/* ECharts builds its tooltip as a DOM node inside the chart's container, so these
   rules reach it like any other markup — and every chart on both pages wears the
   same ones. The lit row is the band under the pointer (charts.js hoveredBand);
   the total closes the list under a rule, at the bottom on every chart. */
.tip__head { font-weight: 600; margin-bottom: 4px; color: var(--text-primary); }
.tip__row {
  display: flex; justify-content: space-between; gap: 16px;
  color: var(--text-secondary); font-weight: 400;
}
.tip__row.is-lit { color: var(--text-primary); font-weight: 700; }
.tip__foot {
  color: var(--text-primary);
  border-top: 1px solid var(--grid);
  margin-top: 4px; padding-top: 3px;
}
/* A figure stated beside the total rather than inside it — the inflows plan. */
.tip__aside { color: var(--text-muted); margin-top: 2px; }
.tip__note { color: var(--text-muted); }
.tip__warn { color: var(--warn); margin-top: 4px; font-size: 11.5px; }

.empty {
  padding: 40px 18px; text-align: center; color: var(--text-muted); font-size: 14px;
}

/* Key-value list under the donut — every account or currency, none hidden, but
   capped so one long roster can't set the height of the whole chart row. It
   scrolls, so it is positioned for the same reason .tbl-wrap is. */
.kv-list {
  margin-top: 12px;
  display: flex; flex-direction: column;
  max-height: 300px;
  overflow-y: auto;
  position: relative;
}
.kv-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline; gap: 10px;
  padding: 5px 2px; font-size: 12.5px;
  border-bottom: 1px solid var(--border);
}
.kv-row:last-child { border-bottom: 0; }
.kv-code { color: var(--text-secondary); font-weight: 600; }
/* The figures carry .tnum like every other figure on the page. */
.kv-native { color: var(--text-muted); font-weight: 500; }
.kv-usd { color: var(--text-primary); min-width: 78px; text-align: right; }
.kv-share { color: var(--text-muted); min-width: 40px; text-align: right; }

/* ── Login ──────────────────────────────────────────────────────────── */
.login-shell {
  min-height: 100dvh;
  display: grid; place-items: center;
  padding: 24px;
}
.login-card {
  width: 100%; max-width: 360px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px 26px 26px;
}
.login-card h1 {
  margin: 0 0 4px; font-size: 20px; font-weight: 680; letter-spacing: -0.01em;
  display: flex; align-items: center; gap: 10px;
}
.login-card p.sub { margin: 0 0 20px; color: var(--text-muted); font-size: 13.5px; }
.field { margin-bottom: 14px; }
.field label { display: block; font-size: 12.5px; color: var(--text-secondary); margin-bottom: 6px; }
/* Every control in a form, one rule: an input, a select and a date picker in the
   same row used to render 43.8, 40 and 45.8px tall, each browser widget sizing
   itself from its own padding. A single-line control is 44px (a comfortable touch
   target); a textarea starts there and grows. */
.field :is(input:not([type="checkbox"]), select, textarea) {
  width: 100%;
  min-height: 44px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font: inherit; font-size: 15px;
  padding: 10px 12px;
}
.field :is(input:not([type="checkbox"]), select) { height: 44px; }
.field :is(input, select, textarea):focus {
  outline: none;
  border-color: var(--series-1);
  box-shadow: var(--focus-ring);
}
.btn-primary {
  width: 100%;
  appearance: none; border: 0; cursor: pointer;
  background: var(--series-1);
  color: var(--on-accent); font: inherit; font-size: 15px; font-weight: 640;
  padding: 11px; border-radius: var(--radius-sm);
  margin-top: 4px;
}
.form-error {
  background: color-mix(in srgb, var(--neg) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--neg) 35%, transparent);
  color: var(--neg);
  border-radius: var(--radius-sm);
  padding: 9px 12px; font-size: 13px; margin-bottom: 16px;
}

/* ── Inflows: the entry form ────────────────────────────────────────── */
/* The login card's .field, widened to the selects a ledger row needs. The
   grid itself reflows to one column on a phone; the fields do the rest. */
.form-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: 14px;
  row-gap: 14px;
}
.form-grid .field { margin-bottom: 0; }
.form-grid .form-error { grid-column: 1 / -1; margin-bottom: 0; }
.field--wide { grid-column: 1 / -1; }
.form-grid__submit { display: flex; justify-content: flex-end; align-items: flex-end; }
.form-grid .btn-primary { width: auto; min-width: 160px; }
.field__err { margin-top: 5px; font-size: 12.5px; color: var(--neg); }
.msgs { margin: 10px 0 12px; display: flex; flex-direction: column; gap: 6px; }
.note--ok { color: var(--pos); }

/* ── Inflows: goal cards ────────────────────────────────────────────── */
.stats--goals { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.goal { display: flex; flex-direction: column; gap: 10px; }
.goal__name { font-size: 15px; font-weight: 640; }
.goal__figures { display: flex; justify-content: space-between; gap: 12px; }
.goal__k { font-size: 11px; }
.goal__v { font-size: 17px; font-weight: 680; letter-spacing: -0.01em; }
.goal__bar {
  height: 8px; border-radius: 999px;
  background: var(--surface-3);
  overflow: hidden;
}
.goal__fill { height: 100%; background: var(--series-1); border-radius: 999px; }
.goal__pct { text-align: right; font-size: 11.5px; color: var(--text-muted); margin-top: -4px; }
.goal-parts { margin-top: 2px; }
.goal-part {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 4px 0; font-size: 12.5px;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--border);
}
.goal-part:last-child { border-bottom: 0; }

/* ── Inflows: the all-income matrix ─────────────────────────────────── */
.row--group td {
  font-size: 11px;
  padding-top: 16px;
  padding-bottom: 4px;
}
.row--subtotal td { color: var(--text-secondary); font-weight: 640; }
.miss {
  color: var(--warn);
  font-weight: 700;
  cursor: help;
  margin-left: 5px;
}

/* ── Inflows: the planned flag and the receive flow ─────────────────── */
/* The checkbox sits inline with its label, on the controls' line of the
   grid row — the stacked label-above-field shape does not fit a box. */
.field--check {
  display: flex; align-items: center; gap: 9px;
  align-self: end; padding-bottom: 10px;
}
.field--check label { margin: 0; }
.field--check input { width: auto; accent-color: var(--series-1); }
/* The receiving banner: what the next save will update, and the way out. */
.note--info { color: var(--series-1); }
.note--info a { color: inherit; }
/* A button that reads as the edit link beside it — same colour, same voice. */
.btn-link {
  appearance: none; border: 0; padding: 0;
  background: none; font: inherit;
  color: var(--series-1); cursor: pointer;
}
.btn-link:hover { text-decoration: underline; }
.plan__actions .btn-link { margin-right: 12px; }
/* An overdue plan is still expected, and says so twice: the badge names it,
   the left accent carries it down the row without painting the figures. */
.badge--overdue { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
#planned tr.is-overdue { --row-accent: inset 3px 0 0 var(--warn); }
/* The plan's promise on a goal card: muted apart from the parts that count. */
.goal-part--plan { color: var(--text-muted); }

/* ── Interaction ────────────────────────────────────────────────────── */
/* The pill-shaped controls — the page links, Sign out, every segment — and the
   primary button answer the pointer one way: the ink comes up to primary and a
   light wash is laid over whatever fill the control already has. A background
   image, so it layers on that fill instead of replacing it. They used to answer
   four ways: a background, a background and a colour, a colour alone, and a
   brightness filter. A pressed segment keeps its fill as it is. Text links and
   the link-buttons beside them underline — the other, deliberate, treatment. */
:is(.hdr__nav a, .btn-logout, .seg button:not([aria-pressed="true"]), .btn-primary):hover {
  color: var(--text-primary);
  background-image: linear-gradient(var(--hover-wash), var(--hover-wash));
  text-decoration: none;
}
/* One focus ring for the keyboard, the one the form fields already wear. A tab
   stop used to show the browser's own outline on a button or a link and this
   ring on a field; the pointer still draws neither. */
:is(a, button):focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* ── Responsive ─────────────────────────────────────────────────────── */
@media (max-width: 860px) {
  .col-8, .col-4 { grid-column: span 12; }
  .stats { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  body { font-size: 14px; }
  .chart { height: 244px; }
  .chart--tall { height: 260px; }
  .hdr { gap: 10px; }
  /* The sync words give way to the pip on a phone — from sight only. display:none
     took them out of the accessibility tree too, and the pip is colour alone.
     The .sr-only rule, restated because a media query cannot borrow a class. */
  .hdr__sync .sync-text {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  .hdr__nav a { padding: 6px 9px; font-size: 12.5px; }
  .btn-logout { padding: 6px 11px; font-size: 12.5px; }
  /* Two nav pills beside the word do not fit a phone header, and shrinking all
     of them still leaves it short: the dot stays (the page keeps its mark),
     the word gives way. font-size 0 rather than display:none so a screen
     reader still hears the site's name. */
  .hdr__brand { font-size: 0; gap: 0; }
  /* The three period columns are the whole point of the card, and at 390px they
     cannot share the row with a label column: "-$6,116.46" alone needs 87px, and
     beside a 114px label a column had 61 — the figure spilled out of the card. So
     each metric's label takes its own line above its figures, the placeholder
     cells beside the headings go, and the periods get the full width. */
  .pgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 8px; }
  .pgrid__blank { display: none; }
  .pgrid__key, .pgrid__spark { grid-column: 1 / -1; }
  .pgrid__key { margin-top: 2px; }
  .pgrid__val { font-size: 16px; }
  .pgrid__sub { font-size: 11px; }
  .stat__label { font-size: 11px; letter-spacing: 0.02em; }
  /* One field per row on a phone; the entry form is typed with one thumb. */
  .form-grid { grid-template-columns: 1fr; }
}
