/* ==========================================================================
   Dashboard components
   The home screen answers "am I okay?" — every card here is either a number
   the owner worries about or a job they can do (Peter, 24 Jul 2026, rule 10).

   Four components live here:
     .needs      — "What needs you": one counted list of jobs, each tapping
                   through to a pre-filtered view. Carries a calm EMPTY state.
     .dash-money — the owed-to-you line under the account rail.
     .dash-tax   — the tax block: set-aside verdict, tax reduced, next deadline.
     .cashchart  — the forward cash line + what it's based on.
     .setup      — the pre-onboarded setup path (steps + progress + celebration).

   Everything else on the dashboard is an EXISTING component used as-is:
   .acct-rail/.acct-card (transactions), .tx-setaside (bank), .todo--card,
   .section-title, .row-card. Nothing is re-drawn here that already exists.
   ========================================================================== */

/* --------------------------------------------------------------------------
   WHAT NEEDS YOU
   The most valuable card on the screen. One card, one row per job, count
   leading so the eye lands on the number. Rows are real links to the
   pre-filtered destination — never a dead label.
   -------------------------------------------------------------------------- */
.needs {
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  overflow: hidden;
}

.needs__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.needs__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 16px 18px;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  border: none;
  background: none;
  width: 100%;
  font: inherit;
  text-align: left;
  transition: background .15s ease;
}

.needs__list > li + li .needs__row { border-top: var(--border-width) solid var(--color-border); }

.needs__row:hover { background: var(--color-bg-subtle-light); }
.needs__row:focus-visible { outline: 2px solid var(--color-text); outline-offset: -2px; }

/* ONE SIZE HERE TOO (Peter, 28 Jul) — the jobs list carried three of them, which
   is what made the card read busy next to the others. Everything is 16px regular
   now and colour does the work: the count is the only thing at full strength, so
   the eye still lands on it without it being bigger or bolder than the job. */
.needs__count,
.needs__label,
.needs__sub {
  font-size: var(--text-base);
  font-weight: var(--weight-regular);
  line-height: var(--leading-body);
}

/* The count leads — tabular so the column stays straight down the list */
.needs__count {
  flex: none;
  min-width: 34px;
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

/* The count and the job read as ONE dark statement — "22 transactions missing
   their paperwork" — with the explanation muted under it. Same rule as the
   sentence cards: what matters is the dark thing, not the big thing. */
.needs__label {
  flex: 1;
  min-width: 0;
  color: var(--color-text);
}

/* Second line under the label — what tapping it does. Optional. */
.needs__sub {
  display: block;
  color: var(--color-text-muted);
  margin-top: 2px;
}

.needs__go {
  flex: none;
  width: 20px;
  height: 20px;
  color: var(--color-text-muted);
  transition: transform .15s ease;
}
.needs__row:hover .needs__go { transform: translateX(2px); color: var(--color-text); }

/* An overdue row used to turn its count red. Black like the rest now (Peter, 28 Jul) —
   the row says what it is in words, which is what people actually read. */
.needs__row--urgent .needs__count { color: var(--color-text); }

/* EMPTY — say it plainly. Most dashboards never tell you nothing needs you,
   and it's the most reassuring thing this screen can display (Peter, rule 4).
   Same calm register as the tax calendar's caught-up months. */
.needs--clear { border-color: var(--color-border); }

.needs__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 48px 24px 44px;
}

.needs__empty-tick {
  width: 44px;
  height: 44px;
  color: var(--color-success);
  margin-bottom: 16px;
}
.needs__empty-tick svg { width: 100%; height: 100%; }

.needs__empty-title {
  font-size: var(--text-section);
  font-weight: var(--weight-medium);
  margin-bottom: 6px;
}

.needs__empty-sub {
  color: var(--color-text-muted);
  max-width: 360px;
}

/* --------------------------------------------------------------------------
   MONEY POSITION — the owed-to-you line that sits under the account rail.
   The rail itself is the transactions .acct-rail, used as-is.
   -------------------------------------------------------------------------- */
.dash-owed {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 16px 18px;
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: border-color .15s ease;
}
.dash-owed:hover { border-color: var(--color-border-strong); }
.dash-owed:focus-visible { outline: 2px solid var(--color-text); outline-offset: 2px; }

.dash-owed__amount {
  font-size: 22px;
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.dash-owed__text { flex: 1; min-width: 0; font-size: var(--text-sm); color: var(--color-text-muted); }
.dash-owed__over { color: var(--color-danger); }

.dash-owed__go { flex: none; width: 20px; height: 20px; color: var(--color-text-muted); }

/* --------------------------------------------------------------------------
   TAX
   Three parts, in this order: set aside vs owed (the highest-anxiety number
   in the product), tax reduced, then the next deadline as ONE line.
   -------------------------------------------------------------------------- */
.dash-tax { display: flex; flex-direction: column; gap: var(--space-3); }

/* --- set aside vs owed --- */
.dash-aside {
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: 18px 20px;
}

.dash-aside__figs {
  display: flex;
  align-items: flex-end;
  gap: 32px;
  flex-wrap: wrap;
}

.dash-aside__fig { display: flex; flex-direction: column; gap: 2px; }

.dash-aside__label {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  letter-spacing: -0.08px;
}

.dash-aside__value {
  font-size: 26px;
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

/* The verdict line — the whole point of the card. Green when they're covered,
   danger when they're short. Never a bare pair of numbers to subtract. */
.dash-aside__verdict {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: 16px;
  padding-top: 14px;
  border-top: var(--border-width) solid var(--color-border);
  font-size: var(--text-sm);
}
.dash-aside__verdict svg { width: 18px; height: 18px; flex: none; }
.dash-aside--ok .dash-aside__verdict { color: var(--color-success); }
.dash-aside--short .dash-aside__verdict { color: var(--color-danger); }
.dash-aside__verdict strong { font-variant-numeric: tabular-nums; }

/* --- tax owed, per return, as cards (mirrors the Money-position account rail) ---
   Each card = one thing Revenue is going to take, with its due date. The set-aside anchor card
   leads (like "All accounts"), styled a touch stronger. Rides the shared .acct-rail. */
.taxcard {
  position: relative;
  flex: none;
  /* Same box as the bank account card (.acct-card): 200px wide, 92px tall. */
  width: 200px;
  height: 92px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  /* 14px, not the account card's 16px: this card carries a third line, and the box height
     is fixed to match. */
  padding: 14px 16px;
  border: var(--border-width) solid var(--color-border);
  border-radius: 14px;
  background: var(--color-surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  scroll-snap-align: start;
  text-decoration: none;
  color: inherit;
  transition: box-shadow .15s ease, border-color .15s ease;
}
.taxcard:hover { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07); }
.taxcard:focus-visible { outline: 2px solid var(--color-text); outline-offset: 2px; }
.taxcard--aside { border-color: var(--color-border-strong); }
.taxcard__amt { font-size: 18px; font-weight: var(--weight-medium); font-variant-numeric: tabular-nums; line-height: 1.1; }
.taxcard__name { font-size: var(--text-xs); color: var(--color-text-muted); }
.taxcard__due { font-size: var(--text-xs); font-weight: var(--weight-medium); margin-top: 2px; color: var(--color-text-muted);
  /* One line, so a long sub-line can't push the card past the bank card's height. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.taxcard--overdue .taxcard__due { color: var(--color-danger); }

/* Covered / short verdict, under the tax rail — the anxiety-answering line. */
.dash-verdict {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 14px 18px;
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  font-size: var(--text-sm);
  margin-top: var(--space-3);
}
.dash-verdict svg { width: 18px; height: 18px; flex: none; }
.dash-verdict strong { font-variant-numeric: tabular-nums; }
.dash-verdict--ok { color: var(--color-success); }
.dash-verdict--short { color: var(--color-danger); }

/* --- tax reduced ---
   NOT gamified (Peter, rule 11): no streak, no goal ring, no target. This is
   money already correctly claimed, shown back as reassurance. The Bank card's
   anatomy (eyebrow / amount / sub) is kept so the two read as the same thing;
   the goal block is deliberately absent. */
.dash-claim {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: 18px 20px;
  color: inherit;
  text-decoration: none;
}

.dash-claim__body { flex: 1; min-width: 0; }

.dash-claim__eyebrow {
  display: block;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--color-text-muted);
  font-weight: var(--weight-bold);
}

.dash-claim__amount {
  display: inline-block;
  font-size: 26px;
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  margin-top: 6px;
}

.dash-claim__sub {
  display: block;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin-top: 4px;
}

.dash-claim__go { flex: none; width: 20px; height: 20px; color: var(--color-text-muted); }

/* --- next deadline: ONE line. The calendar lives in Tax. --- */
.dash-next {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 14px 18px;
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: inherit;
  text-decoration: none;
  font-size: var(--text-sm);
  transition: border-color .15s ease;
}
.dash-next:hover { border-color: var(--color-border-strong); }
.dash-next:focus-visible { outline: 2px solid var(--color-text); outline-offset: 2px; }

.dash-next__date { font-weight: var(--weight-medium); font-variant-numeric: tabular-nums; }
.dash-next__sep { color: var(--color-border-strong); }
/* The estimate is of THE USER'S time, not how long the work takes (rule 6). */
.dash-next__mins { color: var(--color-text-muted); }
.dash-next__go { flex: none; margin-left: auto; width: 18px; height: 18px; color: var(--color-text-muted); }
.dash-next--late .dash-next__date { color: var(--color-danger); }

/* --------------------------------------------------------------------------
   CASH OVER TIME
   The one chart that earns a place on this screen: it answers "will I be
   short?", which the owner can't do in their head. Cash only — see the
   footnote, which states exactly what the line is built from (rule 9).
   -------------------------------------------------------------------------- */
.cashchart {
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: 20px;
}

/* One plain-English line above the graph — the whole point, before any axis-reading.
   Green when they stay in the black, danger when the line dips below zero. */
.cashchart__takeaway {
  font-size: var(--text-base);
  line-height: var(--leading-body);
  margin-bottom: 18px;
}
.cashchart__takeaway strong { font-variant-numeric: tabular-nums; }
.cashchart--short .cashchart__takeaway { color: var(--color-danger); }

.cashchart__head {
  display: flex;
  align-items: flex-end;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: 18px;
}

/* Lowest-point marker + its label on the single line */
.cashchart__lowmark { fill: var(--color-surface); stroke: var(--color-text); stroke-width: 2; }
.cashchart--short .cashchart__lowmark { stroke: var(--color-danger); }
.cashchart__lowlabeltext { font-size: 12px; font-weight: 500; fill: var(--color-text); }
.cashchart--short .cashchart__lowlabeltext { fill: var(--color-danger); }
.cashchart__todaytext { font-size: 11px; fill: var(--color-text-muted); }

/* --------------------------------------------------------------------------
   MONEY IN AND OUT — a plain month-by-month bar chart (Peter, 24 Jul 2026).
   Two bars per month: money in (green) vs money out (orange). Replaces the
   cash-over-time line, which read as confusing.
   -------------------------------------------------------------------------- */
.inout {
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: 20px;
}
/* On Home the chart sits straight on the card — no second border, no padding, no
   legend (Peter, 30 Jul: "the chart looks horrendous, that white"). A box inside
   a box, mostly empty, was the worst thing on the screen. */
.inout--bare { border: 0; background: none; padding: 0; margin-top: 4px; }
.inout__legend {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  justify-content: flex-end;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-bottom: 12px;
}
.inout__key { display: inline-flex; align-items: center; gap: 6px; }
.inout__swatch { width: 12px; height: 12px; border-radius: 3px; }
.inout__swatch--in { background: var(--color-success); }
.inout__swatch--out { background: var(--color-active); }

.inout__plot { width: 100%; height: auto; display: block; overflow: visible; }
/* Hover anywhere over the chart and the nearest month reads out — the 4px markers are
   far too small to hit, and a native <title> is slow and unstyleable (Peter, 27 Jul). */
.inout { position: relative; }
.inout__hit { fill: transparent; cursor: crosshair; }
.inout__rule { stroke: var(--color-border-strong); stroke-width: 1; pointer-events: none; }
.inout__dot.is-hot { r: 5.5; stroke-width: 2.5; }
.inout__tip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  min-width: 132px;
  /* Size to the content — a label like "Money out" was wrapping inside the narrow tip. */
  white-space: nowrap;
  padding: 10px 12px;
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-dropdown);
  transform: translate(-50%, -100%);
  transition: opacity .12s ease;
}
.inout__tip[hidden] { display: none; }
.inout__tipm { font-size: var(--text-xs); color: var(--color-text-muted); margin-bottom: 4px; }
.inout__tipr { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: var(--text-sm); color: var(--color-text); }
.inout__tipk { display: inline-flex; align-items: center; gap: 6px; }
.inout__tipv { font-variant-numeric: tabular-nums; }
.inout__tipbal {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 6px; padding-top: 6px;
  border-top: var(--border-width) solid var(--color-border);
  font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--color-text);
}
.inout__grid { stroke: var(--color-border); stroke-width: 1; }
.inout__axis { font-size: 11px; fill: var(--color-text-muted); }
/* Twin lines with hollow markers (the design Peter sent): money in green, money out orange. */
.inout__line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.inout__line--in { stroke: var(--color-success); }
.inout__line--out { stroke: var(--color-active); }
.inout__dot { fill: var(--color-surface); stroke-width: 2; }
.inout__dot--in { stroke: var(--color-success); }
.inout__dot--out { stroke: var(--color-active); }
/* gentle draw-on, reduced-motion safe */
.inout__line { stroke-dasharray: var(--len, 0); stroke-dashoffset: var(--len, 0); animation: inoutDraw 1s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
@keyframes inoutDraw { to { stroke-dashoffset: 0; } }
@media (prefers-reduced-motion: reduce) { .inout__line { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; } }

.cashchart__low { display: flex; flex-direction: column; gap: 2px; }
.cashchart__lowlabel { font-size: var(--text-xs); color: var(--color-text-muted); letter-spacing: -0.08px; }
.cashchart__lowvalue {
  font-size: 26px;
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.cashchart--short .cashchart__lowvalue { color: var(--color-danger); }

.cashchart__legend {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  flex-wrap: wrap;
}
.cashchart__key { display: inline-flex; align-items: center; gap: 6px; }
.cashchart__swatch { width: 16px; height: 0; border-top: 2px solid var(--color-text); }
.cashchart__swatch--committed { border-top-style: dashed; border-top-color: var(--color-border-strong); }

.cashchart__plot { width: 100%; height: auto; display: block; overflow: visible; }

.cashchart__line { fill: none; stroke: var(--color-text); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.cashchart__committed { fill: none; stroke: var(--color-border-strong); stroke-width: 2; stroke-dasharray: 5 4; stroke-linejoin: round; }
.cashchart__band { fill: var(--color-bg); }
.cashchart__zero { stroke: var(--color-danger); stroke-width: 1; stroke-dasharray: 3 3; }
.cashchart__grid { stroke: var(--color-border); stroke-width: 1; }
.cashchart__axis { font-size: 11px; fill: var(--color-text-muted); }

/* A committed outgoing — one dot per known payment, labelled on hover/focus */
.cashchart__mark { fill: var(--color-surface); stroke: var(--color-text); stroke-width: 2; }
.cashchart__mark--out { stroke: var(--color-danger); }

/* Draw-on, guarded for reduced motion */
.cashchart__line { stroke-dasharray: var(--len, 0); stroke-dashoffset: var(--len, 0); animation: cashDraw 1.1s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
@keyframes cashDraw { to { stroke-dashoffset: 0; } }
@media (prefers-reduced-motion: reduce) {
  .cashchart__line { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; }
}

/* What it's based on. Non-negotiable: a chart the owner can't trust is worse
   than no chart (rule 9). */
.cashchart__basis {
  margin-top: 16px;
  padding-top: 14px;
  border-top: var(--border-width) solid var(--color-border);
  font-size: var(--text-xs);
  line-height: var(--leading-footnote);
  letter-spacing: -0.08px;
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   SETUP PATH (pre-onboarded)
   A setup path, not a disabled dashboard — no €0 metric cards anywhere here.
   The steps themselves are the standard .todo--card rows.
   -------------------------------------------------------------------------- */
.setup { display: flex; flex-direction: column; gap: var(--space-3); }

.setup__progress { display: flex; align-items: center; gap: var(--space-3); }
.setup__count { font-size: var(--text-sm); color: var(--color-text-muted); white-space: nowrap; }

.setup__bar {
  flex: 1;
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--color-bg-subtle);
  overflow: hidden;
}
.setup__fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--color-text);
  width: 0;
  transition: width .6s cubic-bezier(0.16, 1, 0.3, 1);
}
@media (prefers-reduced-motion: reduce) { .setup__fill { transition: none; } }

/* Numbered step badge in the .todo__filler slot */
.setup__num {
  flex: none;
  width: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.setup__badge {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  border: var(--border-width) solid var(--color-border-strong);
  color: var(--color-text-muted);
}
.todo--active .setup__badge { background: var(--color-text); border-color: var(--color-text); color: var(--color-surface); }
.todo--done .setup__badge { border-color: var(--color-success); color: var(--color-success); }
.todo--done .setup__heading { color: var(--color-text-muted); }

.setup__tick { color: var(--color-success); }
.setup__tick svg { width: 22px; height: 22px; }

/* The first invoice is an emotional moment — celebrate it, don't just tick it
   (rule 2). Same hero + confetti as the VAT-filed / employees-added moments. */
.setup__cheer {
  text-align: center;
  padding: 8px 0 24px;
  position: relative;
}
.setup__cheer-title {
  display: inline-block;
  font-size: var(--text-title);
  font-weight: var(--weight-bold);
  margin: 0 0 8px;
  animation: setupPop .7s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes setupPop { 0% { transform: scale(0.6) translateY(10px); opacity: 0; } 100% { transform: none; opacity: 1; } }
.setup__cheer-msg { font-size: var(--text-base); color: var(--color-text-muted); margin: 0 auto; max-width: 52ch; }
@media (prefers-reduced-motion: reduce) { .setup__cheer-title { animation: none; } }

.setup-confetti { position: fixed; inset: 0; pointer-events: none; z-index: 60; overflow: hidden; }
.setup-conf {
  position: absolute;
  width: 9px;
  height: 14px;
  border-radius: 1px;
  opacity: 0;
  animation: setupConfFall 2.6s cubic-bezier(0.2, 0.6, 0.4, 1) forwards;
}
@keyframes setupConfFall {
  0%   { opacity: 0; transform: translate3d(0, 0, 0) rotate(0deg) scale(0.6); }
  8%   { opacity: 1; }
  100% { opacity: 0; transform: translate3d(var(--dx), var(--dy), 0) rotate(var(--rot)) scale(1); }
}
@media (prefers-reduced-motion: reduce) { .setup-confetti { display: none; } }

/* --------------------------------------------------------------------------
   Just-done flash — the GLOBAL highlighter (1px yellow ring, then fades).
   Used when you come back from finishing a step or clearing a job, so the
   thing you just did is obvious on return.
   -------------------------------------------------------------------------- */
.dash-flash { animation: dashFlash 4.5s ease-out forwards; }
@keyframes dashFlash {
  0%, 60% { border-color: var(--color-hilite-strong); }
  100%    { border-color: var(--color-border); }
}
@media (prefers-reduced-motion: reduce) { .dash-flash { animation: none; border-color: var(--color-hilite-strong); } }

/* --------------------------------------------------------------------------
   Narrow screens
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .needs__row { padding: 14px 15px; gap: var(--space-2); }
  .needs__count { min-width: 30px; }   /* one type size holds at mobile too (Peter, 28 Jul) */
  .dash-aside__figs { gap: 20px; }
  .dash-aside__value, .dash-claim__amount, .cashchart__lowvalue { font-size: 22px; }
  .cashchart { padding: 16px; }
  .cashchart__legend { margin-left: 0; width: 100%; }
  .setup__num { width: 44px; }
}

/* Safety valve on the needs list — only ever appears past four rows (Peter, 27 Jul).
   Quiet: this is a tail, not an action. */
.needs__more {
  align-self: flex-start;
  margin-top: 4px;
  background: none;
  border: none;
  padding: 6px 2px;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  cursor: pointer;
}
.needs__more:hover { color: var(--color-text); }

/* The "tax reduced" card sits in the Tax rail beside what is owed — money already saved,
   so it reads as good news rather than another bill (Peter, 27 Jul). Reassurance only:
   no streak, no goal ring, nothing that could nudge someone into inflating a claim. */
.taxcard--saved .taxcard__amt { color: var(--color-success); }

/* ---- The greeting (Peter, 28 Jul) -----------------------------------------
   "Morning Peter", not "Home". The name sits back in muted ink so the greeting
   reads as one warm line rather than a label plus a shout. */
.dash-greet__name { color: var(--color-text-muted); }

/* ==========================================================================
   THE CARD GRID (Peter, 28 Jul — midday's calm, our anatomy)
   Home's sections became a grid of cards, every one of them the same shape:

     label · one plain sentence with the number bold inside it ·
     a quiet comparison underneath · a quiet link at the bottom

   The link stays a link — the card is NOT clickable (Peter's call). Two up on
   desktop, one on a narrow screen; the list and the chart span both columns.
   The bottom link is the shared .answer__link the Ask answers use, so the way
   out of a card looks the same everywhere on this screen.
   ========================================================================== */
.dgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
}
@media (max-width: 640px) { .dgrid { grid-template-columns: 1fr; } }

.dcard {
  display: flex;
  flex-direction: column;
  /* Content sits on WHITE, never the pink page token. */
  background: var(--color-surface);
  /* MODERNISED (Peter, 31 Jul — Mariana: "a little dated"): the hairline box
     becomes soft elevation. A bordered box on white reads admin-panel; a card
     floating on two gentle shadows is the contemporary read (the Sana
     comparison). The faint outline keeps the edge crisp on bright screens. */
  border: var(--border-width) solid rgba(17, 17, 17, 0.04);
  border-radius: var(--radius-md);
  /* dialled back ~20% (Peter, 31 Jul: 'very nice though, just reduce about 20%') */
  box-shadow: 0 1px 2px rgba(17, 17, 17, 0.032), 0 12px 32px -18px rgba(17, 17, 17, 0.11);
  /* ROOM (Peter, 30 Jul). 20px made four cards in a row read as a dense block;
     the screens he keeps pointing at are mostly air inside the card. */
  padding: 26px;
}
/* The jobs list needs the full row — every row is a different job going to a
   different screen, so it cannot collapse into a column. */
.dcard--wide { grid-column: 1 / -1; }
/* Two columns: wide enough for a chart, narrow enough not to leave a band of
   empty white across the screen (Peter, 30 Jul). */
.dcard--half { grid-column: span 2; }
@media (max-width: 640px) { .dcard--half { grid-column: auto; } }

/* THIS WEEK SITS BESIDE WHAT NEEDS YOU (Peter, 30 Jul), and What needs you keeps
   its two columns. So This week takes column three THROUGH THE END of the row
   rather than a fixed span: at four columns it fills 3–4, at three columns it
   fills 3 alone, and either way it closes the row so In the bank starts a clean
   one underneath. A fixed span would drop it under What needs you the moment the
   grid handed out an odd number of columns, which is what happened. */
/* 1280 not 1200: right at the 3-column boundary the grid was still handing out
   two, and `3 / -1` then invented a narrow third column that did not line up with
   the cards underneath. Above 1240 there are genuinely three or more. */
@media (min-width: 1280px) { .dcard--week { grid-column: 3 / -1; } }

/* ONE SIZE, AND COLOUR DOES THE WORK (Peter, 28 Jul).
   The cards read busy because each one mixed three type sizes and shouted its
   figures in bold. Now every line in a card is the SAME size and the same
   regular weight — the sentence sits back in muted ink and only the figure
   steps forward, in full-strength text. Nothing is bold, nothing is bigger:
   the eye finds the number because it is the only dark thing in the sentence. */
.dcard__label,
.dcard__line,
.dcard__sub {
  font-size: var(--text-base);
  font-weight: var(--weight-regular);
  line-height: var(--leading-body);
  color: var(--color-text-muted);
}

/* The card's NAME is the exception: full weight, standard black (Peter, 28 Jul). It is a
   heading, and at one type size a heading has to hold its place by weight and colour or it
   reads as another line of the sentence. Everything under it stays regular and muted. */
.dcard__label {
  margin: 0 0 10px;
  font-weight: var(--weight-bold);
  color: var(--color-text);
}
.dcard__line { margin: 0; }
.dcard__sub { margin: 6px 0 0; }

/* THE SENTENCE IS THE QUESTION (Peter, 29 Jul).
   A real button so it is reachable by keyboard and announced as one — but it has
   to LOOK like the sentence it is, not a control. No underline until you go for
   it: the hover treatment is the whole affordance, the same way a client name
   behaves on the invoice screens. */
.dcard__ask {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  text-decoration: none;
  text-underline-offset: 3px;
}
/* A sentence in the quiet line under the figure is part of a running line — two
   facts separated by a "·" have to stay on that line, so the button goes inline
   and wraps with the text like the words around it. */
.dcard__sub .dcard__ask { display: inline; width: auto; }
/* THE UNDERLINE GOES ON THE SMALL PRINT, NOT THE BUTTON (Peter, 30 Jul — asked
   three times, and the first two fixes could never have worked).

   text-decoration set on an ancestor is PAINTED THROUGH its descendants. A child
   cannot take it off: `.dcard__big { text-decoration: none }` computes to none and
   the line is still drawn straight through the number. The only fix is not to put
   it on the parent at all.

   So the button carries no underline and the line under the figure — the sentence
   part — carries it instead. The number is never touched, at any depth. */
.dcard__ask:hover .dcard__under,
.dcard__ask:focus-visible .dcard__under { text-decoration: underline; text-underline-offset: 3px; }
/* NEVER UNDERLINE THE FIGURE (Peter, 30 Jul): "the 129 just looks odd when they
   get moved". A rule under a number reads as part of the number — it can look like
   a strike-through or a fraction — and the sentence above it already shows the
   line, which is all the hover needs to say. */

.dcard__ask:focus-visible { outline: 2px solid var(--color-text); outline-offset: 3px; }

/* The figure — the only thing in the card at full strength. Applies in the
   comparison line too: a figure there was rendering browser-default BOLD and
   stood out from every other number on the screen (Peter, 29 Jul). */
.dcard__line strong,
.dcard__sub strong {
  font-weight: var(--weight-regular);
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}
/* NO RED, NO GREEN ON HOME (Peter, 28 Jul). Black text only: the figures already
   step forward against the muted sentence around them, and a screen that answers
   "am I okay?" should not colour-code the answer before you have read it. So the
   overdue amount and the claimed-back amount are the same ink as every other
   figure — full-strength text against muted words. */
.dcard--good .dcard__line strong { color: var(--color-text); }
.dcard__over { color: var(--color-text); }

/* Pin the link to the bottom edge so two cards side by side line their links up,
   however long the sentence above runs. It is SMALLER than everything else in the
   card (answer.css) — a sub-call to action, not information. It used to be pulled
   back to --text-base by the one-size rule above, which is why it competed with
   the line explaining the card. */
.dcard .answer__link { margin-top: auto; padding-top: 16px; }
.dcard .answer__link:hover { color: var(--color-text); }

/* The jobs list keeps its own rows but drops its border — inside a card it
   would be a box in a box. Rows run edge to edge under the label. */
.dcard--list { padding: 20px 0 4px; }
.dcard--list .dcard__label { padding: 0 20px; margin-bottom: 12px; }
.dcard--list .needs { border: 0; border-radius: 0; background: none; }
.dcard--list .needs__row { padding: 14px 20px; }
.dcard--list .needs__more { margin-left: 18px; }

/* Same reason the jobs list drops its border: the chart is already inside a card. */
.dcard .inout { border: 0; border-radius: 0; background: none; padding: 12px 0 0; }

/* --------------------------------------------------------------------------
   THE GETTING-COMPLIANT BOARD (Peter, 29 Jul 2026)
   The same jobs list, carrying statutory tasks instead of counted work. No
   count column — a task is one thing, not a number of things — and a right-hand
   column saying when, or who is watching it.

   The calm variant is the point of the screen: a row we are handling is not a
   to-do and must not read like one, so it has no arrow, no hover, no pointer.
   -------------------------------------------------------------------------- */
.needs--tasks .needs__row { align-items: flex-start; }
.needs--tasks .needs__label { padding-right: 12px; }
/* The status column. It holds the shared .status-pill now (Peter, 29 Jul) —
   this is just the slot that keeps the pills in a straight line down the card. */
.needs__when { flex: none; align-self: center; }
.needs__row--calm { cursor: default; }
.needs__row--calm:hover { background: none; }

/* The demo entity switch — quiet text, the live one in full-strength ink.
   Same treatment as the rest of the screen: colour carries the state. */
.ob-entity { display: flex; gap: 10px; align-items: center; margin: 10px 0 0; font-size: var(--text-base); color: var(--color-text-muted); }
.ob-entity__opt { background: none; border: 0; padding: 0; font: inherit; color: var(--color-text-muted); cursor: pointer; text-underline-offset: 3px; }
.ob-entity__opt:hover { text-decoration: underline; color: var(--color-text); }
.ob-entity__opt.is-on { color: var(--color-text); }
.ob-entity__opt:focus-visible { outline: 2px solid var(--color-text); outline-offset: 3px; }

/* The jobs rows carry the same pill, between the job and the arrow. It never
   squeezes the job text — the label flexes, the pill does not. */
.needs__row > .status-pill { margin-left: auto; }
.needs__row .needs__label + .status-pill { margin-left: var(--space-2); }
@media (max-width: 640px) {
  /* At phone width the sentence needs the room more than the pill does. The
     row already says "Overdue" in words on the line beneath. */
  .needs__row > .status-pill { display: none; }
}

/* The decision nobody has made yet, said out loud on the screen rather than
   papered over with an invented split (Peter, 29 Jul). */
.ob-undecided { margin: 10px 0 0; font-size: var(--text-base); color: var(--color-text-muted); max-width: 62ch; }
.ob-entity__sep { color: var(--color-border-strong); margin: 0 2px; }

/* ==========================================================================
   HOME GOES FULL SCREEN (Peter, 29 Jul)
   --------------------------------------------------------------------------
   "Let's make the home full screen. All the other sites do it." Sana and
   midday both run the desktop dashboard to the window edges with a thick
   margin, and at 800px our six cards were stacked in a narrow ribbon down the
   middle of a wide monitor with nothing either side of them.

   SCOPED TO HOME, on purpose. Every other screen keeps the 800px column — a
   list, a document and a two-pane form all read better in one, and widening
   them was not what he asked for. The rule hangs off `.app--wide`, which only
   dashboard.html carries.

   THICK PADDING, NOT EDGE-TO-EDGE: 64px of air each side (48 at laptop widths,
   the page default on phones) and a 1680px ceiling, so on a very wide monitor
   the cards stop growing rather than sprawling.

   "BAR THE SEARCH" — the ask box and its answers keep a narrow measure. A
   1600px-wide input reads as a bar, not a question, and an answer that runs
   the width of a monitor is unreadable. Everything else lines up on the same
   left edge as before.
   ========================================================================== */
.app--wide .app__main {
  /* LOCKED at the 1440×900 rendering (Peter, 30 Jul — walkthroughs chat:
     "when it's at 1440 by 900 it looks perfect… if the page gets bigger, just
     keep that"). 1356px is exactly the room the main has at a 1440 window
     (1440 minus the 84px sidebar reserve), so the grid re-flows identically
     and the cards can never spread. Wider windows centre the spare room —
     the same rule the two-pane stages follow. Was 1680px, which let auto-fit
     add columns and the proportions drift. */
  max-width: 1356px;
  padding-inline: 64px;
  margin-inline: auto;
}

/* Cards fill the room: two on a laptop, three or four on a wide monitor. The
   wide cards (What needs you, Money in and out) still span the whole row. */
.app--wide .dgrid {
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: var(--space-6);
  /* NOT `grid-auto-rows: 1fr` (tried 30 Jul, reverted the same hour). That
     equalises EVERY row to the tallest, so the chart row dragged all the others
     up with it and the cards went enormous — Peter: "you've just made them
     fucking enormous". A floor on the card does what he actually asked: the short
     cards fill out to the height of the jobs card beside them, and nothing grows
     past its own content. Cards in one row already stretch to match each other. */
}
.app--wide .dcard { padding: 30px; min-height: 232px; }
.app--wide .dcard--wide { grid-column: 1 / -1; }

/* Prose keeps a readable measure however wide the window gets — a sentence
   running 1600px is a worse read than one that stops. The cards' own lines are
   already bounded by the card. */
.app--wide .week-line,
.app--wide .ob-undecided { max-width: 96ch; }

/* The search and its answers: their own column, on the same left edge. */
/* The ask box CENTRES on the wide layout (Peter, 30 Jul). It keeps its narrow
   measure — a 1600px input reads as a bar, not a question — and its suggestions
   and answers stay with it so the three never disagree about where they start. */
.app--wide .ask-box,
.app--wide .ask-suggests,
.app--wide .ask-answer-wrap { max-width: 860px; margin-inline: auto; }

@media (max-width: 1200px) { .app--wide .app__main { padding-inline: 48px; } }
@media (max-width: 900px)  { .app--wide .app__main { padding-inline: var(--space-4); } }
@media (max-width: 640px)  {
  .app--wide .app__main { padding-inline: var(--space-3); }
  .app--wide .dgrid { grid-template-columns: 1fr; }
}

/* THE FIGURE, LARGE AND ON ITS OWN LINE (Peter, 30 Jul — midday's Expenses card,
   the one he put an arrow on). The sentence says what it is in quiet type; the
   number stands under it and carries the weight, which is what fills the room the
   equal-height cards created.

   This is a deliberate exception to the 28 Jul "one size, nothing bold" rule, and
   only for the ONE figure a money card exists to show. Everything else on the
   screen still holds that rule — the exception works because there is exactly one
   of these per card. */
.dcard__big {
  display: block;
  margin-top: auto;
  padding-top: 18px;   /* overridden on the cards that carry small print below */
  font-size: 30px;
  line-height: 1.15;
  font-weight: var(--weight-regular);
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.4px;
}
/* The sentence has to stretch for margin-top:auto on the figure to push it down. */
.dcard__line:has(.dcard__big) { display: flex; flex-direction: column; flex: 1; }
.dcard__line:has(.dcard__big) .dcard__ask { display: flex; flex-direction: column; flex: 1; text-align: left; }

/* ==========================================================================
   ICONS AND SHORT NAMES ON THE CARD HEADS (Peter, 30 Jul — midday's anchoring)
   --------------------------------------------------------------------------
   His tester could not tell where to look. The fix is not six coloured headings
   — six colours with no key is a legend nobody can read, and it would spend the
   colour that currently means overdue / done / needs you. It is an ANCHOR: a
   quiet icon the eye lands on, and a name short enough to take in without
   reading. "Tax reduced this week" becomes "Tax reduced" with the period in
   grey beside it.

   And the FIGURE answers the heading directly. It used to read label, then half
   a sentence, then a number, so you had to assemble it; now it is Bank →
   €129,199.32 → across 6 accounts. The number is the answer, the qualifier is
   the small print.
   ========================================================================== */
.dcard__label { display: flex; align-items: center; gap: 8px; }
.dcard__icon { flex: none; display: inline-flex; color: var(--color-text); }
.dcard__icon > svg {
  width: 17px;
  height: 17px;
  display: block;
  /* Heavy enough to sit beside a bold heading rather than under it (Peter, 30 Jul).
     Stroke 1 is the global icon rule; this is the one deliberate exception, because
     the icon IS part of the title here rather than a control beside it. */
  stroke-width: 1.6;
}
.dcard__name { font-weight: inherit; }
/* The period rides along in grey — it is context, not part of the name. */
.dcard__qual { font-weight: var(--weight-regular); color: var(--color-text-muted); }

/* The small print under the figure. */
.dcard__under {
  display: block;
  margin-top: 4px;
  font-size: var(--text-base);
  color: var(--color-text-muted);
}
/* THE FIGURE SITS AT THE FOOT OF THE CARD (Peter, 30 Jul — yesterday's position,
   today's words). He preferred it there: it gives every card the same bottom edge
   and a band of air above the number, and the whole grid reads calmer for it. So
   the card is short heading → what it is → space → the figure → the way out. */
.dcard__line:has(.dcard__big) .dcard__big { margin-top: auto; padding-top: 24px; }
.dcard__line:has(.dcard__big) .dcard__under { margin-top: 2px; }

/* The overlapping account marks are retired (Peter, 30 Jul: "it just looks
   messy") — four logos at 22px were too small to recognise. The Bank card carries
   the plain bank glyph like every other card. The .dcard__mark rules below are
   kept for the switcher's avatar, which uses the same circle. */
.dcard__mark {
  width: 22px;
  height: 22px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border);
  overflow: hidden;
}
.dcard__mark + .dcard__mark { margin-left: -8px; }   /* overlap, like midday */
.dcard__mark img { width: 14px; height: 14px; object-fit: contain; filter: grayscale(1); opacity: .8; }
/* The cluster is wider than a single icon, so the name needs its own breathing
   room rather than the 8px gap the plain icons use. */
.dcard__icon--marks + .dcard__name { margin-left: 2px; }

/* ICONS BACK ON, IN THE HEADING'S OWN INK (Peter, 30 Jul). They came off when
   they were grey and cluttered; at full strength beside a short name they read as
   part of the title rather than as decoration beside it. Every card gets one, so
   it is a system and not a highlight.

   The bank-account marks are NOT back — four logos at 22px were the messy part,
   and the Bank card now carries the plain bank glyph like everything else. */
.dcard__icon { color: var(--color-text); }

/* The watermarks are GONE (Peter, 30 Jul, after a day of iterating them lighter,
   smaller and softer). The short names and the heading icons already do the
   differentiating, at the point where the eye actually reads the card — and a
   thing that has to be made nearly invisible to be tolerated is a thing the
   design does not need. The white space won. (History in git if ever wanted.) */
.dcard { position: relative; }

/* ==========================================================================
   THE HOME GRADIENT (Peter, 30 Jul)
   --------------------------------------------------------------------------
   He liked the depth in the Sana and midday shots and asked whether Home could
   have it. NOT a photograph: those examples are a marketing card and a product
   video still, and a picture behind financial figures makes contrast a
   pixel-by-pixel question on the one screen where a number has to be beyond
   doubt. This is the part of them that actually reads as product — a soft wash
   BEHIND the cards, never behind text.

   Built from the app's own pink so it reads as Bullet rather than as a stock
   gradient, and it fades out before the fold so the page does not end in a band
   of colour. The cards stay white on top of it, which keeps every contrast
   measurement we have made still true.

   HOME ONLY: it hangs off .app--wide, which no other screen carries.
   ========================================================================== */
/* FROM THE MIDDLE, not the corners (Peter, 30 Jul). Two washes hung off the top
   corners read as a header band rather than as depth. One soft pink bloom centred
   on the middle of the grid — roughly where Owed sits, under the foot of What
   needs you — fading out long before the edges. */
/* THE BLOOM — pink, decided (Peter, 30 Jul, against orange, grey and none).
   Pink because it is already Bullet's colour — the rail, the menus, the app
   background token are all this family, so the wash reads as the product's own
   warmth — and because orange has a job here (the filter dot, the money-out
   line, attention) that ambience would dilute. Home only, behind the cards,
   never behind text; the cards stay white on top of it. */
/* 20% lighter than the decided pink (Peter, 30 Jul — walkthroughs chat):
   with a wash on white, opacity IS lightness, so both stops are ×0.8
   (0.035→0.028, 0.018→0.0144). */
/* THE BLOOM IS RETIRED (Peter, 31 Jul — "maybe it's dated 'cause we put the
   pink bloom in at the back", after Mariana's "a little dated"; supersedes the
   30 Jul pink decision). Plain white behind the cards — the modern read is
   elevation, not ambience. Revert = restore the radial-gradient from git. */
.app--wide { background: var(--color-surface); }

/* THE ASK SURFACE SITS ON A BLOCK OF COLOUR (Peter, 31 Jul — the Sana
   insights canvas: their search + answer live on a tinted panel). Bullet's
   pink, soft, as a deliberate BLOCK rather than the old ambient bloom — and
   everything ON it stays white (the input, the answer cards), so the
   content-on-white rule holds. Home only. */
/* MARKETING YELLOW (Peter, 31 Jul: "one of the real vibrant colours — try
   the yellow"): #FFE000, sampled from the live blocks on bullethq.ie, flat,
   even padding. Supersedes the menu-pink block from earlier the same day.
   .ask-canvas is the alias for the SAME canvas on non-Home pages — the
   project detail wears it (Peter, 22 Aug: "just like the home page"). */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) {
  background: #ffe000;
  /* VERY curved, the Sana canvas (Peter, 31 Jul: "make the edges very curved
     like that") */
  border-radius: 32px;
  /* doubled (Peter, 31 Jul: 'ask anything feels a little lost there') */
  padding: 48px;
  /* the section's flex row-gap kept spacing the ZERO-HEIGHT helpers (folded
     chips, empty answer wrap), padding the block's bottom unevenly — the
     visible children carry their own margins instead */
  row-gap: 0;
}
/* EVEN ON ALL FOUR SIDES (Peter, 31 Jul night): inside the yellow canvas the
   box fills the padded area — the same 48px of yellow left, right, top and
   bottom. The 860px narrow measure (30 Jul) stays for ask surfaces OUTSIDE
   the canvas, where a full-width input would read as a bar; here the canvas
   does the framing. */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) > .ask-box,
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) > .ask-suggests,
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) > .ask-answer-wrap { max-width: none; margin-inline: 0; }
/* even top/bottom: the box is the first visible child (the heading is
   visually-hidden) and the folded chips must not pad the bottom */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) > .ask-box { margin-top: 0; }
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) > .ask-suggests.is-away { margin-top: 0; }
/* the empty answer wrap was padding the block's bottom by its own margin */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) > .ask-answer-wrap:has(> [hidden]) { margin-top: 0; }
/* muted greys need more ink on the yellow to stay readable; on the canvas the
   suggestion pills wear the same darker-yellow chip treatment as the answer's
   own pills — one language (Peter, 31 Jul; pills 14 Aug) */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-suggest { background: rgba(0, 0, 0, 0.075); }
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-suggest,
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-suggest > span { color: rgba(17, 17, 17, 0.62); }
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-suggest:hover { color: var(--color-text); background: rgba(0, 0, 0, 0.12); }
@media (max-width: 640px) {
  :is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) { padding: 22px; }
}
/* Borderless PILL on the yellow (Peter, 31 Jul: 'get rid of the 1px border';
   reshaped 14 Aug when the mic + Ask moved INSIDE the pill — the chrome that
   used to sit on the input now sits on the .ask-box container). A white pill
   on yellow needs no outline, just the soft lift. */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) > .ask-box {
  border: none;
  box-shadow: 0 1px 6px rgba(107, 94, 0, 0.08);
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-box .input::placeholder { color: var(--color-text); opacity: 0.8; }
/* the answering join used to re-apply the strong hairline — not on the yellow
   (Peter, 31 Jul: "that hairline needs to go, it looks terrible"). And the
   squared-bottom join is for surfaces where a WHITE panel docks under the
   pill — here the answer lives on the canvas itself, so squaring left bare
   white corners jutting into the yellow (Peter's "broken box", 14 Aug):
   the pill stays a pill. */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) > .ask-box.is-answering {
  border-color: transparent;
  border-radius: var(--radius-full);
}
/* NO focus ring on the field (Peter, 14 Aug: "when you activate the text box
   don't make us have a borderline… just leave it as is") — supersedes the
   31 Jul dark-amber ring; the caret is the signal. */
/* NOTHING on the yellow wears a hairline (Peter, 31 Jul: "they don't need to
   float, they don't need a border") — the mic, the answer cards, all of it.
   The focus line above is the one deliberate exception: it says "you're in". */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-mic { border-color: transparent; }
/* every card state — including the joined first card, whose rule re-set a
   strong border and outranked the plain one */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .answer,
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-box.is-answering ~ .ask-answer-wrap .answer:first-child {
  border-color: transparent;
}
/* the follow-up chips ("How much tax do I owe?") drop their outlines too —
   a quiet grey fill carries the affordance instead */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .answer__followup {
  border-color: transparent;
  background: var(--color-bg-subtle);
}

/* THE NEWEST ANSWER LIVES ON THE YELLOW ITSELF (Peter, 31 Jul — the Sana
   insights canvas, sent twice): big dark ink straight on the tint, white
   figure tiles for the numbers, chips as pale pills. Older answers still
   fold into white cards below, so a run of questions stays readable.
   Delete this block to go back to all-white cards. */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child {
  background: transparent;
  box-shadow: none;
  padding-left: 6px;
  padding-right: 6px;
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__headline {
  font-size: 24px;
  line-height: 1.3;
  font-weight: var(--weight-medium);
}
/* FULL INK on the yellow (Peter, 31 Jul: "I don't know if the grey works on
   the yellow" — it doesn't; a bright canvas needs contrast, not mutedness). */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__headline {
  color: var(--color-text);
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__body {
  color: rgba(17, 17, 17, 0.88);
}
/* SECONDARY = THE CANVAS, DARKENED (Peter, 31 Jul — "Sana takes the blue and
   makes it a darker blue; we take the yellow and make it darker"): labels
   and quiet text go dark amber, not grey. */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__lead {
  color: #6b5e00;
}
/* ONE LANGUAGE FOR EVERY ACTION (Peter, 31 Jul: "it's not obvious why you
   have them styled different — make them all pills, along the bottom, and
   they'll work it out from the language"): the show/report links join the
   follow-up chips as identical darker-yellow pills in one wrapped row. */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__link {
  background: rgba(0, 0, 0, 0.075);
  color: var(--color-text);
  border-radius: var(--radius-full);
  padding: 7px 16px;
  font-size: var(--text-base);
  margin: 16px 8px 0 0;
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__link:hover {
  background: rgba(0, 0, 0, 0.12);
  color: var(--color-text);
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__followups {
  display: inline;
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__followup {
  margin: 16px 8px 0 0;
}
/* THE ANSWER RUNS OUT TO THE ASK BUTTON (Peter, 31 Jul: "the answers should
   go right out to the ask bar") — the button-width inset is an inline-page
   device; on the canvas the answer owns the full row. */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-box.is-answering ~ .ask-answer-wrap .answer-stack {
  margin-right: 0;
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__flag {
  color: #6b5e00;
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__fig-label,
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__fig-sub {
  color: #6b5e00;
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__flagrow {
  border-color: rgba(17, 17, 17, 0.12);
}
/* Translucent tiles (Peter, 14 Aug — the solid white "jumped too big"): the
   canvas glows through, the pill stays the whitest thing on screen, the dark
   figures keep the contrast. Border off — the veil is the shape. */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__fig {
  background: rgba(255, 255, 255, 0.5);
  border-color: transparent;
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__followup {
  /* darker yellow pills, the Sana chip treatment */
  background: rgba(0, 0, 0, 0.075);
  color: var(--color-text);
}
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) .ask-answer-wrap .answer:first-child .answer__followup:hover { background: rgba(0, 0, 0, 0.12); }
/* (The always-out prompts lasted one round — Peter, same day: "I want the
   ask dialogue collapsed". Focus-only everywhere again.) */
/* Home's nav goes WHITE; the pink comes back the moment you step into a
   section (Peter, 31 Jul) — the yellow block is the only colour on Home.
   The pink lives on .sidebar__col, not the bar itself. */
.app--wide .sidebar .sidebar__col { background: var(--color-surface); }
/* (the pink-fade-in rule lives in sidebar.css — it has to exist on the page
   being ARRIVED AT, and dashboard.css is only linked on Home) */
@media (prefers-contrast: more) {
  :is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) { background: var(--color-surface); }
}

/* The hint hangs just under the SENTENCE, not under the whole button — the button
   runs the height of the card, so `top: 100%` put the tooltip over the link at the
   bottom, which is the same problem as covering the title, upside down. */
.dcard__ask[data-tip-below]::after { top: 28px; }

/* ==========================================================================
   THE FIGURE AND THE WAY OUT SHARE THE LAST ROW (Peter, 30 Jul)
   --------------------------------------------------------------------------
   The number reads as the answer and the link sits quietly to its right on the
   same baseline, which frees the middle of the card for the watermark and stops
   the card having a dead centre. Pinned to the foot, so two cards side by side
   still line their bottoms up however long the line above runs.
   ========================================================================== */
.dcard__foot {
  margin-top: auto;
  padding-top: 24px;
  display: flex;
  /* CENTRED (Peter, 8 Sep: "the pills are never centred, they always look like they're floating to the
     top"). Supersedes the 30 Jul bottoms-level rule: a 40px pill beside a 30px figure reads centred only
     when the two boxes share a middle. */
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.dcard__fig {
  font-size: 30px;
  line-height: 1.15;
  font-weight: var(--weight-regular);
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.4px;
}
.dcard__foot .answer__link { margin-top: 0; padding-top: 7px; flex: none; }   /* even 7px top and bottom, the pill's own rhythm. `.dcard .answer__link` above sets 16px top for a link under a sentence; in a foot the old padding-top:0 left the label 2px from the pill's top and 10px from its bottom: THAT was the 'floating to the top' (Peter, 8 Sep, twice) */
/* On a phone they stack rather than squeeze. */
@media (max-width: 640px) {
  .dcard__foot { flex-wrap: wrap; }
  .dcard__foot .answer__link { flex: 1 0 100%; }
}

/* WHERE PETER CIRCLED (30 Jul, annotated screenshot): the bank building sits in
   the open middle of the card — right of centre, between the small print and the
   figure — whole and recognisable, rather than a sliver hanging off the right
   edge. Cropped a little by the card top so it still reads as a watermark and
   not as an illustration that was placed there. */
/* Framed whole in the top-right space (Peter, 30 Jul) — nothing cropped, the
   building fills the room between the heading row and the figure. */

/* ============================================================================
   THE CANVAS ANSWERS BEFORE YOU ASK (Peter, 15 Aug 2026 — the Sana insights
   screens he sent: a coloured canvas, a headline sentence, the chart straight
   on the colour, and "Explore more" pills along the bottom).
   Home's yellow block now holds the money in/out chart in that shape. The card
   it used to live in is gone from the grid — one home, not two.
   ============================================================================ */
.canvas-chart { margin-top: 48px; }

/* NO ENTRANCE ANIMATION ON THE CHART (Peter, 15 Aug — tried and pulled the
   same night). Collapsing it CLIPPED the plot and sliced the hover card in
   half: shrinking a chart means redrawing it at a smaller scale, not cropping
   it, and redrawing on a timer is a lot of motion for very little. Either
   direction also shifts the page under someone who is already reading. So the
   chart just sits at a height that leaves the cards below it in view. */

.canvas-chart__line {
  margin: 0 0 6px;
  font-size: 24px;
  line-height: 1.3;
  font-weight: var(--weight-medium);
  color: var(--color-text);
  max-width: 40ch;
}
/* The chart is INK ON THE CANVAS: no card, no white plot area, no grid box —
   the yellow shows through, exactly like the reference. */
.canvas-chart .inout { background: none; border: none; padding: 0; }
/* THREE DOTTED RULES — floor, midpoint, ceiling (Peter, 15 Aug; supersedes the
   solid ladder at every step, and the no-rules pass before it). Dotted and pale
   so they sit under the plot rather than across it. */
.canvas-chart .inout__grid {
  stroke: rgba(107, 94, 0, 0.22);   /* half again (Peter, 15 Aug) */
  stroke-width: 1;
  stroke-dasharray: 1 5;
  stroke-linecap: round;
}
/* THE LABELS ARE ALMOST NOT THERE (Peter, 15 Aug — "make the fonts much
   smaller, see the purple example… the graph bg info is very faint and the
   lines are the focus"). Tiny and barely inked: they are there for the one
   moment you need to place the scale, and invisible the rest of the time, so
   the plot is the only thing the eye has to look at. */
.canvas-chart .inout__axis {
  font-size: 9px;
  letter-spacing: 0.02em;
  fill: rgba(107, 94, 0, 0.38);
}

.canvas-chart .inout__rule { stroke: rgba(17, 17, 17, 0.35); }
/* Two lines still, two colours still — but read against yellow, so money in is
   the dark ink of the canvas and money out is the darker amber beside it
   (green on yellow is the one pairing that goes muddy). */
.canvas-chart .inout__line--in, .canvas-chart .inout__dot--in { stroke: var(--color-text); }
.canvas-chart .inout__line--out, .canvas-chart .inout__dot--out { stroke: #a8901c; }
/* Half the weight (Peter, 15 Aug) — a finer line reads better against a flat
   colour than it does on white. */
.canvas-chart .inout__line { stroke-width: 2; }

/* "Explore more" — the label the reference puts above its pills. */
.ask-explore {
  margin: 22px 0 8px;
  font-size: var(--text-sm);
  color: #6b5e00;
}
.ask-explore[hidden] { display: none; }

/* Asking hides the chart: the answer is what the canvas is for, and two charts
   of attention in one block is the busyness we keep taking out. */
.dash-sec:has(.ask-box.is-answering) .canvas-chart,
.dash-sec:has(.ask-box.is-answering) .ask-explore { display: none; }

@media (max-width: 640px) {
  .canvas-chart__line { font-size: 20px; }
}

/* ---- TAX, NESTED IN THIS WEEK (Peter, 15 Aug: "like there are 2 boxes within
   each other") — its own box inside the week's card, hairline-separated so it
   reads as a thing within a thing rather than a second card. */
.dcard__inner {
  margin-top: 20px;
  padding-top: 18px;
  border-top: var(--border-width) solid var(--color-border);
  display: flex;
  flex-direction: column;
  flex: 1;
}
.dcard--week { display: flex; flex-direction: column; }

/* THE PILLS ARE PART OF THE PICTURE, not a focus affordance (Peter, 15 Aug —
   the reference has them sitting under the chart at all times). On Home's
   canvas they stay put; the fold-away behaviour still applies everywhere else,
   and an open answer still stands them down. */
:is(.app--wide .dash-sec, .ask-canvas):has(> .ask-box) > .ask-suggests.is-away {
  opacity: 1;
  transform: none;
  max-height: 480px;
  margin-top: 14px;
  pointer-events: auto;
}
.dash-sec:has(.ask-box.is-answering) > .ask-suggests { display: none; }
/* ON A PROJECT'S CANVAS THE WAY OUT STAYS PUT (Peter, 25 Aug: "these two
   options... and then the pills underneath should remain there when you click
   on one of them. Currently they disappear and you can't get back to them").
   Home keeps its own behaviour — the answer IS what that canvas is for, and
   that was a deliberate call; this is scoped to .ask-canvas. */
.ask-canvas:has(.ask-box.is-answering) > .ask-suggests { display: flex; }
.ask-canvas:has(.ask-box.is-answering) > .ask-explore { display: flex; }
/* the one you just asked steps back so the others read as "what next" */
.ask-suggest.is-asked { opacity: .45; }
.ask-suggest.is-asked:hover { opacity: 1; }

/* The reference's chart is a clean line — the point markers on every month read
   as clutter at this size. They come back under the cursor (the hover rule
   already draws the tip + rule). */
.canvas-chart .inout__dot { opacity: 0; }
.canvas-chart .inout__dot--live { opacity: 1; }

/* The way out sits with the pills (the link lives in .ask-explore now). */
.ask-explore { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }


/* ---- THE CHART'S READ-OUT ON THE YELLOW (Peter, 15 Aug) --------------------
   The old card carried a green square and an orange square, which fight the
   canvas. Redrawn on the reference he sent: a soft white card, the month quiet
   at the top, each figure in a badge coloured like the line it belongs to, the
   label beside it, and the balance as the conclusion under a hairline. */
/* FLAT AND SMALL, AND LAID OUT LIKE THE REFERENCE (Peter, 15 Aug): a header
   row — month at one end, the balance at the other — a hairline under it, then
   the two figures side by side. Almost flat: it sits on the canvas rather than
   floating above it. */
.canvas-chart .inout__tip {
  min-width: 0;
  padding: 13px 16px 14px;
  border: none;
  border-radius: 12px;
  /* NO SHADOW (Peter, 15 Aug — the reference has none): white on a flat colour
     separates on its own; a shadow only muddies the yellow around it. */
  box-shadow: none;
}
.inout__tiphead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 9px;
  border-bottom: var(--border-width) solid var(--color-border);
  font-size: 13px;
  color: var(--color-text-muted);
}
.inout__tipbalv { font-variant-numeric: tabular-nums; color: var(--color-text); }
.inout__tiprow { display: flex; align-items: center; gap: 14px; margin-top: 9px; }
.inout__tipcell { display: inline-flex; align-items: center; gap: 6px; }
.canvas-chart .inout__tipk { color: var(--color-text-muted); font-size: 13px; }
.inout__badge {
  display: inline-flex;
  align-items: center;
  border-radius: 7px;
  padding: 3px 9px;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  line-height: 1.35;
}
/* the two series, in the same ink as their lines on the canvas */
.inout__badge--in { background: var(--color-text); color: #fff; }
.inout__badge--out { background: rgba(168, 144, 28, 0.16); color: #6b5e00; }

/* ---- THE NEW CUSTOMER GETS THE SAME PAGE (Peter, 15 Aug) -------------------
   Their state was a stack of bare headings while a running business got the
   yellow canvas. Same block, same spacing: the ask box, then their situation as
   one sentence in the canvas's headline size, the demo entity switches quiet
   underneath, and the two task lists as ordinary cards below. */
.app--wide .dash-sec--ob { margin-bottom: var(--space-8); }
.dash-sec--ob .canvas-chart__line { margin: 26px 0 0; }
.dash-sec--ob .ob-entity,
.dash-sec--ob .ob-undecided { color: #6b5e00; max-width: 68ch; }
.dash-sec--ob .ob-entity__opt { color: #6b5e00; }
.dash-sec--ob .ob-entity__opt.is-on,
.dash-sec--ob .ob-entity__opt:hover { color: var(--color-text); }
.dash-sec--ob .ob-entity { margin-top: 16px; }
.dash-sec--ob .ob-undecided { margin-top: 10px; font-size: var(--text-sm); }

/* THE EMAIL VALUE-MOMENT OFFER (28 Aug 2026) — a quiet second line under the
   "missing paperwork" job, shown only while no mailbox is connected. A button,
   not a link: it opens the shared bulletEmailConnect flow in place. */
.needs__offer {
  display: block;
  border: none;
  background: none;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  cursor: pointer;
  text-align: left;
  padding: 2px 0 10px 46px;   /* aligns under the label, past the count */
  text-decoration: underline;
  text-underline-offset: 3px;
}
.needs__offer:hover { color: var(--color-text); }
