/* ==========================================================================
   STFU v0.39 — Component library
   ==========================================================================
   Every color here comes from a semantic token (tokens-v039.css) — nothing
   is hardcoded per theme, so every component in this file repaints
   correctly under html[data-theme="dark"] with zero extra rules.

   This is a REAL replacement, not an override layer: the component
   families below (recurring rows, entry-type segmented control, household
   options, settlement rows, tables, pot progress) used to live scattered
   across v05.css/dashboard-v029.css/dashboard-v022.css/ui-v037.css/
   design-v038.css — all five files are fully unlinked in index.html/sw.js.
   Their still-needed selectors are rebuilt here from scratch on tokens;
   nothing from those files is still loaded.

   styles.css stays loaded underneath this (base reset, onboarding, modal
   skeleton, forms, .stack/.two-cols/.toggle-row/.money-input,
   .transaction-row/.budget-row/.pot-row primitives) — it was the
   original light theme and already resolves through the legacy token
   aliases, so it repaints correctly in both themes without being rewritten.
   ========================================================================== */

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

* { box-sizing: border-box; }
/* The topbar is sticky, not fixed — an element scrolled to via
   scrollIntoView() can otherwise land exactly where the sticky bar still
   covers it. scroll-padding-top reserves that space app-wide (native
   behavior, not just a Playwright quirk: the same thing bites a real
   keyboard/anchor-link jump). */
html { scroll-padding-top: 240px; }
html, body { background: var(--canvas); }
body { font-family: var(--font-sans); color: var(--text-primary); }
h1, h2, h3 { font-family: var(--font-editorial); letter-spacing: -.02em; color: var(--text-primary); }
::selection { background: var(--accent-soft); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; border-radius: var(--radius-sm); }

/* -- Buttons ---------------------------------------------------------------- */
button, .chip, .quiet, .primary, .danger-button, .icon-button, .link-action { font-family: inherit; }
button:not(:disabled) { cursor: pointer; }
button:disabled { cursor: not-allowed; opacity: .6; }
button:not(.sr-only-button), .chip, .quiet, .primary, .danger-button, .icon-button {
  min-height: 44px;
  cursor: pointer;
}
.primary {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 0; border-radius: var(--radius-pill);
  padding: 12px 22px;
  background: var(--accent-strong); color: var(--text-on-dark-fill);
  font-weight: 700; font-size: var(--text-base);
  box-shadow: var(--shadow-sm);
  transition: transform var(--duration-base) var(--ease-standard), box-shadow var(--duration-base) var(--ease-standard);
}
.primary:hover { box-shadow: var(--shadow); }
.primary svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.2; stroke-linecap: round; }
.quiet {
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  padding: 10px 18px; background: var(--surface); color: var(--text-primary);
  font-weight: 600; font-size: var(--text-sm);
  transition: background var(--duration-base) var(--ease-standard), border-color var(--duration-base) var(--ease-standard);
}
.quiet:hover { border-color: var(--accent); background: var(--accent-soft); }
.quiet.full-width { display: block; width: 100%; text-align: center; margin-top: var(--space-3); }
.danger-button {
  border: 1px solid var(--danger); border-radius: var(--radius-pill);
  padding: 10px 18px; background: var(--danger-soft); color: var(--danger);
  font-weight: 700;
}
.link-action {
  border: 0; background: transparent; color: var(--accent-strong);
  font-weight: 700; font-size: var(--text-sm);
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 32px;
}
.icon-button {
  display: grid; place-items: center;
  width: 44px; height: 44px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface); color: var(--text-secondary);
  position: relative;
}
.icon-button:hover { color: var(--text-primary); border-color: var(--accent); }
.icon-button svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.notif-dot { position: absolute; top: 8px; right: 9px; width: 8px; height: 8px; border-radius: 50%; background: var(--danger); border: 2px solid var(--surface); }
.notif-dot.hidden { display: none; }

/* Theme quick toggle: shows only the icon for the *other* theme (the
   action, not the current state — matching a light-switch metaphor). */
.theme-quick-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-quick-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-quick-toggle .icon-moon { display: block; }

.row-actions button, .void-settlement, .modal-close { min-width: 44px; min-height: 44px; }
.modal-close { display: grid; place-items: center; font-size: 22px; border-radius: 50%; }
.modal-close:hover { background: var(--surface-soft); }

/* -- Cards & sections --------------------------------------------------------- */
.card, .content-card, .center-card, .welcome {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

/* -- Public sign-in / registration-status screen ----------------------------- */
/* #sign-in hosts the normal welcome screen and the registration=pending
   status screen (app.js swaps the copy; see render()). It's a real
   mobile-first page: a small brand header, a content-driven card (never a
   fixed or full-viewport-height box), and actions with a clear primary/
   secondary hierarchy — restructured as an OUTER flex column (#sign-in
   itself, canvas-colored, min-height:100dvh so it can center a short card
   on tall phones) around an INNER card (.welcome, surface-colored). Keeping
   these as two separate elements — rather than one element doing both
   jobs — is what actually fixes the visual bug this replaces: #sign-in
   previously carried .welcome directly, so its own background:var(--canvas)
   (set for full-viewport centering) beat .welcome's intended
   background:var(--surface) card fill by ID-vs-class specificity, leaving
   the "card" visually indistinguishable from the page behind it. */
#sign-in {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-5);
  min-height: 100dvh;
  padding: max(var(--space-5), env(safe-area-inset-top)) var(--space-4) max(var(--space-5), env(safe-area-inset-bottom));
  background: var(--canvas);
}

.public-brand { display: none; }

.welcome {
  width: min(460px, 100%);
  margin: 0;
  padding: clamp(28px, 6vw, 44px) clamp(24px, 6vw, 36px);
  text-align: left;
  box-shadow: var(--shadow);
}
.welcome .eyebrow { margin-bottom: var(--space-3); }
.welcome h1 { font-size: clamp(26px, 6vw, 38px); line-height: 1.15; margin: 0 0 var(--space-3); }
.welcome p { margin: 0; max-width: 42ch; }
.welcome small { display: block; margin-top: var(--space-5); }

.status-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-bottom: var(--space-3);
  border-radius: 50%; background: var(--accent); color: var(--text-on-accent);
}
.status-badge svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.status-badge.hidden { display: none; }

.welcome-actions {
  display: flex; flex-direction: column; align-items: stretch; gap: var(--space-3);
  width: 100%; margin-top: var(--space-5);
}
.welcome-actions .primary { width: 100%; margin: 0; }
.welcome .register-link {
  display: flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 4px 12px; border-radius: var(--radius-pill);
  color: var(--accent-strong); font-weight: 700; text-align: center; overflow-wrap: anywhere;
}
.welcome .register-link:hover { background: var(--accent-soft); }

/* Tablet/desktop: a wider, more editorial card with a side-by-side action
   row — unchanged in spirit from before this task, just now living on the
   inner .welcome card instead of #sign-in itself. The 640px line matches
   where a phone-shaped viewport ends in this app's own test matrix
   (max phone width tested is 430px; 768px starts the tablet layout
   elsewhere in the shell) — comfortably between the two. */
@media (min-width: 640px) {
  .welcome {
    width: min(710px, 100%);
    padding: clamp(40px, 7vw, 64px) clamp(36px, 7vw, 70px);
  }
  .welcome h1 { font-size: clamp(37px, 5vw, 58px); }
  .welcome p { max-width: 48ch; }
  .welcome-actions { flex-direction: row; flex-wrap: wrap; align-items: center; }
  .welcome-actions .primary { width: auto; flex: 0 0 auto; }
  .welcome .register-link { width: auto; padding: 4px 4px; }
}

@media (max-width: 639px) {
  .public-brand {
    display: flex; align-items: center; gap: 8px;
    color: var(--text-primary); font-weight: 800; font-size: var(--text-sm);
    letter-spacing: .04em; text-transform: uppercase;
  }
  .public-brand img { width: 26px; height: 26px; border-radius: 8px; flex: none; }
}

/* A warmer first impression without inventing financial data: the graphic is
   explicitly illustrative, while the actual sign-in action stays first on
   every screen size. Pending registrations keep the quieter status layout. */
#sign-in {
  background-color: var(--canvas);
  background-image:
    radial-gradient(circle at 12% 18%, var(--accent-soft), transparent 34%),
    radial-gradient(circle at 88% 82%, var(--savings-soft), transparent 32%);
}
.welcome { position: relative; overflow: hidden; }
.welcome-main { min-width: 0; }
.welcome-copy { position: relative; z-index: 1; min-width: 0; }
.welcome-visual { display: none; }
.welcome-benefits {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  margin-top: var(--space-5); padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.welcome-benefits span {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 34px; padding: 5px 10px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--surface-soft); color: var(--text-secondary);
  font-size: var(--text-xs); font-weight: 700; line-height: 1.3;
}
.welcome-benefits b { color: var(--accent-strong); font-size: 18px; line-height: 1; }
#sign-in:has(#sign-in-badge:not(.hidden)) .welcome-benefits,
#sign-in:has(#sign-in-badge:not(.hidden)) .welcome-visual { display: none; }
@media (min-width: 860px) {
  .welcome { width: min(960px, 100%); padding: clamp(42px, 5vw, 64px); }
  .welcome-main { display: grid; grid-template-columns: minmax(0, 1fr) minmax(270px, .8fr); align-items: center; gap: var(--space-7); }
  .welcome-visual {
    position: relative; display: grid; place-items: center;
    min-height: 320px; isolation: isolate;
    border-radius: var(--radius-xl);
    background: linear-gradient(145deg, var(--accent-soft), var(--info-soft) 58%, var(--savings-soft));
    overflow: hidden;
  }
  .welcome-visual-orb {
    position: absolute; right: -38px; top: -74px;
    width: 225px; height: 225px; border-radius: 50%;
    background: var(--savings-soft); border: 1px solid var(--border);
  }
  .welcome-visual-sheet {
    position: relative; display: flex; flex-direction: column; gap: 16px;
    width: 76%; max-width: 250px; padding: 22px;
    border: 1px solid var(--border); border-radius: var(--radius-lg);
    background: var(--surface-raised); box-shadow: var(--shadow-lg);
    transform: rotate(-5deg);
  }
  .welcome-visual-mark { display: flex; align-items: center; justify-content: space-between; color: var(--accent-strong); font-weight: 800; letter-spacing: .12em; font-size: 11px; }
  .welcome-visual-mark i { width: 13px; height: 13px; border-radius: 50%; background: var(--accent); }
  .welcome-visual-title { font-family: var(--font-editorial); color: var(--text-primary); font-size: 23px; line-height: 1.15; }
  .welcome-visual-line { display: flex; align-items: center; gap: 10px; }
  .welcome-visual-line b { width: 28px; height: 28px; flex: none; border-radius: 9px; background: var(--accent-soft); }
  .welcome-visual-line:nth-of-type(4) b { background: var(--expense-soft); }
  .welcome-visual-line:nth-of-type(5) b { background: var(--savings-soft); }
  .welcome-visual-line i { flex: 1; height: 7px; border-radius: var(--radius-pill); background: var(--surface-soft); }
  .welcome-visual-line:nth-of-type(4) i { width: 63%; flex: none; }
  .welcome-visual-line:nth-of-type(5) i { width: 78%; flex: none; }
  .welcome-visual-foot { border-top: 1px solid var(--border); padding-top: 12px; color: var(--text-secondary); font-size: 10px; font-weight: 700; letter-spacing: .05em; }
  .welcome-visual-spark { position: absolute; color: var(--savings); font-size: 22px; }
  .welcome-visual-spark-one { left: 17px; top: 40px; }
  .welcome-visual-spark-two { right: 23px; bottom: 29px; font-size: 16px; }
  #sign-in:has(#sign-in-badge:not(.hidden)) .welcome { width: min(710px, 100%); }
}
.content-card { padding: var(--space-5); }
/* Instellingen: on a wide screen, four full-width single-column cards
   (Thema, Voorkeuren, Huishouden & gegevens, Account) left most of that
   width empty around one narrow-looking button per row — a two-column
   gallery uses the space the page already has instead of stretching thin
   content edge to edge. Stays a single column below the breakpoint, same
   as before. */
@media (min-width: 860px) {
  .settings-groups { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); align-items: start; }
}
.section-heading {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: var(--space-3); min-height: 44px;
  margin-bottom: var(--space-4);
}
.section-heading > div { min-width: 0; }
.section-heading h2 { display: flex; align-items: center; gap: 10px; min-width: 0; font-size: var(--text-lg); line-height: 1.2; margin: 0; }
.section-heading h2 svg { width: 20px; height: 20px; stroke: var(--accent-strong); fill: none; stroke-width: 1.8; }
.section-heading h3 { margin: 0 0 var(--space-2); font-size: var(--text-md); }
.section-heading .link-action, .section-heading > button { align-self: center; white-space: nowrap; }
@media (max-width: 520px) {
  .section-heading { align-items: start; }
  .section-heading .link-action { padding-inline: 0; }
}

/* Page header: the uniform opener for every full page (Transacties, Vaste
   lasten, Spaardoelen, Rekeningen, Budgetten, Instellingen) — title, short
   explanation, primary action. Top-right of the title on desktop; on
   mobile the columns stack but the action stays right under the
   explanation, so it's still the first reachable control on the page,
   never buried below a long list. */
.page-header {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: start; gap: var(--space-4); margin-bottom: var(--space-5);
}
.page-header > div:first-child { min-width: 0; }
.page-header h2 { margin: 0 0 4px; font-size: var(--text-xl); line-height: 1.25; }
.page-header p { margin: 0; }
.page-header > button, .page-header > .page-header-actions { white-space: nowrap; }
.page-header-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 640px) {
  .page-header { grid-template-columns: 1fr; }
  .page-header > button, .page-header > .page-header-actions { width: 100%; }
  .page-header-actions { flex-direction: column; }
  .page-header-actions > button { width: 100%; }
}

/* -- Forms --------------------------------------------------------------------- */
input, select, textarea {
  font-family: inherit; color: var(--text-primary); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  min-height: 44px; box-sizing: border-box;
}
input:focus, select:focus, textarea:focus { outline: 0; border-color: var(--accent-strong); box-shadow: 0 0 0 3px var(--accent-soft); }
label { color: var(--text-primary); }
.money-input { color: var(--text-secondary); }
.money-input:before { content: '€'; position: absolute; z-index: 1; left: 13px; top: 50%; transform: translateY(-50%); font-size: var(--text-base); line-height: 1; color: var(--text-secondary); pointer-events: none; }
.money-input > input { padding-left: 30px; font-variant-numeric: tabular-nums; }
.stack label { margin-top: var(--space-4); }
.stack label:first-of-type { margin-top: 0; }
.stack > .primary, .stack > button[type="submit"] { width: 100%; margin-top: var(--space-5); }
.field-help, .actual-help { font-size: var(--text-xs); line-height: 1.4; color: var(--text-secondary); margin-top: 4px; }
.household-danger-zone, [class*="danger-zone"] { border: 1px solid var(--danger); border-radius: var(--radius-lg); background: var(--danger-soft); padding: var(--space-4); margin-top: var(--space-5); }
.delegate-toggle {
  display: flex; align-items: flex-start; gap: var(--space-3);
  min-height: 44px; margin-top: var(--space-3); padding: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface-soft); cursor: pointer; line-height: 1.4;
}
.delegate-toggle:focus-within { border-color: var(--accent-strong); box-shadow: 0 0 0 3px var(--accent-soft); }
.delegate-toggle .delegate-checkbox {
  display: block; flex: 0 0 20px; width: 20px; height: 20px; min-height: 20px;
  margin: 1px 0 0; padding: 0; accent-color: var(--accent-strong);
}
.delegate-toggle span { min-width: 0; }
.delegate-toggle small { display: block; margin-top: 3px; color: var(--text-secondary); font-size: var(--text-xs); font-weight: 400; }

/* Segmented control: entry-type (Uitgave/Inkomen/Overboeking, single/
   joint payer, theme picker) — one continuous pill track. */
.entry-type, .theme-picker {
  display: flex; flex-wrap: wrap; gap: 4px;
  padding: 4px; margin: var(--space-4) 0 0;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--surface-soft);
}
.entry-type button, .theme-picker button, .theme-option {
  flex: 1 1 0; min-height: 44px; min-width: 0;
  border: 0; border-radius: var(--radius-pill);
  background: transparent; color: var(--text-secondary);
  font-weight: 600; font-size: var(--text-sm);
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.entry-type button svg, .theme-option svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2; }
.entry-type button.selected, .theme-option.active {
  background: var(--surface); color: var(--text-primary);
  box-shadow: var(--shadow-sm);
  font-weight: 700;
}
.theme-picker { max-width: 420px; }

/* -- Lists & rows --------------------------------------------------------------- */
.budget-row, .pot-row, .transaction-row, .recurring-row, .account-row, .household-option {
  border-radius: var(--radius-md);
  transition: background var(--duration-base) var(--ease-standard);
}
.transaction-list .transaction-row:hover, .recurring-row:hover { background: var(--surface-soft); }
/* .transaction-row's name and meta text are adjacent inline elements
   (<b>naam</b><small>meta</small>) with no whitespace between them in the
   generated markup, so without this they render glued together on one
   line ("TankstationGeen categorie") — the exact same fix .recurring-row
   already has just below. Reused by every transaction-shaped list: the
   transactions page, pot movements, settlement payments and the audit log. */
.transaction-row > div { display: grid; align-content: center; min-width: 0; }
.transaction-row > div > b { display: block; overflow-wrap: anywhere; line-height: 1.35; }
.transaction-row > div > small { display: block; margin-top: 2px; line-height: 1.4; overflow-wrap: anywhere; }
.transaction-row strong, .budget-row strong, .recurring-row strong, .pot-balance strong,
.settlement-transfer strong, .settlement-payment strong, .insights-table td,
.money-input input, #plan-income, #plan-fixed, #plan-variable, #plan-saving,
.hero-amount, .metric-card strong {
  font-variant-numeric: tabular-nums;
}
.empty, .empty-state { display: block; padding: var(--space-5) var(--space-3); color: var(--text-secondary); text-align: center; font-size: var(--text-sm); }

.load-failure { width: min(480px, 100%); text-align: center; }
.load-failure h1 { margin-bottom: var(--space-3); font-size: var(--text-xl); }
.load-failure > p:not(.eyebrow) { color: var(--text-secondary); line-height: 1.55; }
.load-failure .primary { margin-top: var(--space-4); }

/* Recurring items ("Vaste lasten" manager): reuses .transaction-row's base
   layout (icon, name column, amount, row-actions) with its own grid for
   the extra status line. */
.recurring-summary { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-4); padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface-soft); }
.recurring-summary-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); }
.recurring-summary-row > span:first-child { grid-column: 1 / -1; font-weight: 800; font-size: var(--text-sm); color: var(--text-primary); }
.recurring-summary-row b {
  display: block; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
  background: var(--surface); font-weight: 700; font-size: var(--text-xs);
  color: var(--text-secondary); line-height: 1.3; text-align: center;
}
@media (max-width: 480px) {
  .recurring-summary-row { grid-template-columns: 1fr; }
}
.recurring-row { display: grid; grid-template-columns: 34px minmax(0, 1fr) auto auto; align-items: center; column-gap: 12px; row-gap: 6px; }
.recurring-row > div { display: grid; align-content: center; min-width: 0; }
.recurring-row > div > b { display: block; overflow-wrap: anywhere; line-height: 1.35; }
.recurring-row > div > small:not(.recurring-status-line) { display: block; margin-top: 2px; line-height: 1.4; }
.recurring-row > strong { white-space: nowrap; }
.recurring-row > .row-actions { display: flex; flex-wrap: nowrap; align-items: center; justify-content: flex-end; margin-left: 4px; }
.recurring-status-line { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 4px; overflow-wrap: anywhere; }
.recurring-status-badge {
  display: inline-flex; align-items: center; min-height: 28px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  padding: 3px 10px; background: var(--surface-soft); color: var(--text-primary);
  font: inherit; font-size: 10px; font-weight: 800; letter-spacing: .03em; text-transform: uppercase;
}
.recurring-status-badge.status-paid { background: var(--income-soft); color: var(--income); border-color: var(--income); }
.recurring-status-badge.status-skipped { background: var(--expense-soft); color: var(--expense); border-color: var(--expense); }
.recurring-status-badge.status-soon { background: var(--savings-soft); color: var(--savings); border-color: var(--savings); }
.recurring-status-badge.status-today { background: var(--expense-soft); color: var(--expense); border-color: var(--expense); }
.recurring-status-badge.status-late { background: var(--danger-soft); color: var(--danger); border-color: var(--danger); font-weight: 900; }
.recurring-diff { font-weight: 800; }
.recurring-diff.over { color: var(--danger); }
.recurring-diff.under { color: var(--income); }
.recurring-linked { font-weight: 800; color: var(--text-secondary); }
/* recurringRowHtml() in app.js — an expected/today/soon/late row's amount is
   still a projection (the recurring item's configured amount, not a real
   posting), unlike a paid/skipped row's settled figure. Muted weight keeps
   that readable at a glance instead of relying only on the status-badge
   word next to it. Compound selector (not just the class) so this reliably
   beats the earlier, equal-or-higher-specificity ".recurring-row strong"
   base rule regardless of stylesheet load order — see action-center.css's
   own ".action-center.card" comment for the same recurring pitfall here. */
.recurring-row strong.recurring-amount-projected { font-weight: 600; color: var(--text-secondary); }
@media (max-width: 767px) {
  .recurring-row { grid-template-columns: 34px minmax(0, 1fr) auto; align-items: start; }
  .recurring-row > strong { align-self: center; }
  .recurring-row > .row-actions { grid-column: 2 / -1; justify-self: end; margin-left: 0; }
}

/* Household options (switch/join lists) */
.household-option {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start; width: 100%;
  min-height: 44px; padding: 12px 14px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text-primary);
  text-align: left;
}
.household-option:hover { border-color: var(--accent); }
.household-option.selected { border-color: var(--accent-strong); background: var(--accent-soft); box-shadow: 0 0 0 3px var(--accent-soft); }
.household-option b { font-size: var(--text-base); overflow-wrap: anywhere; }
.household-option small { color: var(--text-secondary); font-size: var(--text-xs); }

/* Settlement */
.settlement-overview, .modal-subsection { margin-top: var(--space-5); }
.settlement-transfer, .settlement-payment { display: flex; align-items: center; gap: var(--space-3); padding: 12px 0; border-top: 1px solid var(--border); }
.settlement-payment > div { flex: 1; min-width: 0; }
.settlement-payment > strong { white-space: nowrap; }
.settlement-payment > .void-settlement, .void-settlement {
  flex: 0 0 44px; width: 44px; height: 44px;
  border: 1px solid var(--danger); border-radius: var(--radius-sm);
  background: var(--danger-soft); color: var(--danger);
}
.settlement-maximum { display: block; margin-top: 6px; color: var(--text-secondary); font-size: var(--text-xs); }

/* Pots: balance figures + progress track (also used by the new dashboard
   pot cards in dashboard-v039.css, which extends rather than repeats
   these two base rules). */
.pot-balance {
  display: grid;
  gap: 4px;
  margin-top: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-soft);
}
.modal-heading-with-icon + .pot-balance { margin-top: var(--space-4); }
.pot-balance > small {
  display: block;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: 1.4;
}
.pot-balance > strong {
  display: block;
  color: var(--text-primary);
  font-size: clamp(1.35rem, 3vw, 1.8rem);
  line-height: 1.15;
}
.pot-balance strong small { font-size: var(--text-sm); font-weight: 700; color: var(--text-secondary); }
.pot-progress { display: block; height: 8px; margin-top: 9px; border-radius: var(--radius-pill); background: var(--surface-soft); overflow: hidden; }
.pot-progress:after { content: ''; display: block; width: var(--progress); height: 100%; border-radius: inherit; background: var(--accent-strong); }

/* Period navigation inside the transactions-overview modal (app.js's own
   transactionsOverview() still generates this markup unchanged). */
.period-switcher { display: inline-flex; align-items: center; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); margin: var(--space-4) 0 var(--space-3); }
.period-switcher button { border: 0; background: transparent; color: var(--text-primary); font-weight: 700; padding: 8px 12px; min-height: 40px; }
.period-switcher #tx-period-button { border-left: 1px solid var(--border); border-right: 1px solid var(--border); font-size: var(--text-sm); }

/* Insights report summary cards (insightsScreen() in app.js). */
.insights-summary-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-2); margin-top: var(--space-4); }
.summary { border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); padding: var(--space-4); }
.summary span, .summary small { display: block; color: var(--text-secondary); font-size: var(--text-xs); }
.summary strong { font-size: var(--text-xl); letter-spacing: -.02em; margin: 9px 0 4px; display: block; }
.summary.dark { background: var(--text-primary); color: var(--canvas); border-color: var(--text-primary); }
.summary.dark span, .summary.dark small { color: var(--canvas); opacity: .75; }

/* Tables (Inzichten, saldocontrole-historie, cashflow) */
.table-scroll { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.insights-table { width: 100%; min-width: 420px; border-collapse: collapse; font-size: var(--text-xs); }
.insights-table caption { text-align: left; }
.insights-table th, .insights-table td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
.insights-table thead th { color: var(--text-secondary); font-size: 10px; text-transform: uppercase; letter-spacing: .04em; font-weight: 800; }
.insights-table tbody th { font-weight: 700; color: var(--text-primary); }

/* Category picker / member list (household, categories) */
.category-picker { display: flex; gap: 6px; align-items: center; }
.member-list { margin: var(--space-4) 0; display: flex; flex-direction: column; gap: var(--space-2); }
.member-row-actions button { min-height: 44px; }

/* Row actions (edit/delete icon pair). Budget rows use explicit grid areas,
   keeping the controls in a reserved column instead of floating them over
   an amount or label. */
.row-actions { display: inline-flex; gap: 6px; margin-left: 8px; }
.row-actions button { width: 44px; height: 44px; padding: 0; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text-primary); font-size: 15px; }
.row-actions button.repeat-transaction { width: auto; min-width: 44px; padding: 0 10px; font-size: var(--text-sm); font-weight: 700; }
.row-actions button.delete-budget, .row-actions button.delete-recurring, .row-actions button.delete-category, .row-actions button.delete-transaction, .row-actions button.delete-movement { color: var(--danger); border-color: var(--danger); }
.budget-row {
  position: relative; display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "title actions" "meta actions" "progress progress" "actual actual";
  column-gap: 14px; align-items: start;
}
.budget-row .budget-title { grid-area: title; padding-right: 0; align-items: baseline; }
.budget-row .budget-meta { grid-area: meta; padding-right: 0; }
.budget-row > progress { grid-area: progress; }
.budget-row > .compact-actual { grid-area: actual; }
.budget-row > .row-actions { grid-area: actions; position: static; align-self: start; margin-left: 0; }
.budget-title > strong {
  display: inline-flex; align-items: baseline; justify-content: flex-end;
  gap: 4px; white-space: nowrap; flex: none; line-height: 1.2;
}
.budget-title > strong small { font-weight: 600; }
/* The name truncates before it can push the amount into the action column;
   a financial figure itself must always stay fully legible. */
.budget-title > b { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.budget-meta { display: flex; gap: 7px; align-items: center; justify-content: space-between; margin-top: 4px; flex-wrap: wrap; }
.budget-cycle { display: inline-block; border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 2px 8px; color: var(--text-secondary); font-size: 10px; font-weight: 800; }
.over-budget { color: var(--danger) !important; }
.period-locked-note { color: var(--expense); font-size: var(--text-xs); line-height: 1.5; padding: 8px 0; margin: 0; }

/* Compact-mode "actual spent this period" inline form on a budget row. */
.compact-actual { display: grid; grid-template-columns: minmax(180px, 1.15fr) minmax(170px, .8fr) minmax(120px, .45fr) minmax(120px, .45fr); gap: 10px; align-items: end; margin-top: var(--space-4); padding: 13px; border: 1px solid var(--accent); border-radius: var(--radius-md); background: var(--accent-soft); }
.compact-actual label { margin: 0; font-size: 11px; color: var(--text-secondary); }
.compact-actual input, .compact-actual select { margin-top: 5px; }
.compact-actual .money-input > input { padding-left: 32px; }
.compact-actual .quiet { min-height: 44px; padding: 0 10px; }
.compact-actual .actual-help { grid-column: 1 / -1; margin: 0; }
@media (max-width: 1050px) {
  .compact-actual { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .compact-actual .quiet { width: 100%; }
}
@media (max-width: 700px) {
  .budget-row { grid-template-areas: "title actions" "meta meta" "progress progress" "actual actual"; }
  .budget-row .budget-title { flex-direction: column; align-items: flex-start; justify-content: center; gap: 5px; }
  .budget-title > strong { justify-content: flex-start; margin-left: 0; }
  .budget-row > .row-actions { align-self: center; }
  .compact-actual { grid-template-columns: 1fr; gap: 8px; }
  .compact-actual .quiet { width: 100%; margin-top: 4px; }
}

/* Pot list/manager action buttons */
.pot-title > div { display: grid; gap: 3px; }
.pot-title > div small { font-size: 10px; }
/* v0.42 Deel C: the optional short spaardoel description, on the full
   spaardoelen-page row. Only ever rendered when non-empty (see
   renderPotsPage() in app.js), so an empty description leaves no gap here —
   there's simply no element to take up space. */
.pot-description { margin: 6px 0 0; color: var(--text-secondary); font-size: var(--text-xs); line-height: 1.45; overflow-wrap: anywhere; }
.pot-actions { display: grid; grid-template-columns: 1fr 1fr auto; gap: 7px; margin-top: 9px; }
.pot-actions button, .pot-manager-actions .quiet { min-height: 40px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text-primary); font-size: 11px; font-weight: 800; }
.pot-actions button:first-child { border-color: var(--accent); color: var(--accent-strong); }
.pot-manager-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin: 17px 0; position: sticky; top: -1px; z-index: 2; padding: 10px 0; background: linear-gradient(180deg, var(--surface-raised) 80%, transparent); }
.pot-manager-actions > * { width: 100%; justify-content: center; }

/* Generic form helpers reused across several modals */
.inset-form { margin-top: var(--space-5); padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface-soft); }
.inset-form h3, .modal-subsection h3 { margin: 0; }
.inset-form > p { margin: 5px 0 0; font-size: var(--text-xs); }
.pot-manager-sheet .inset-form { padding: var(--space-3); }
/* "Meer opties" — a native <details> gets keyboard operability and correct
   aria-expanded semantics for free. Styled as an obvious tappable row
   (never a bare text link) with a 44px summary and a rotating marker. */
.more-options { margin-top: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface-soft); overflow: hidden; }
.more-options summary { display: flex; align-items: center; justify-content: space-between; min-height: 44px; padding: 0 16px; color: var(--text-primary); font-weight: 800; font-size: var(--text-sm); cursor: pointer; list-style: none; }
.more-options summary::-webkit-details-marker { display: none; }
.more-options summary::after { content: '▾'; transition: transform var(--duration-base) var(--ease-standard); }
.more-options[open] summary::after { transform: rotate(180deg); }
.more-options-body { display: grid; gap: var(--space-4); padding: 2px 16px 16px; }

/* Insights (report) widgets. .insights-range-choices combines with
   .entry-type.status-choices on the same fieldset (4 preset buttons need
   their own column count) so it must out-specificity that pairing. */
.entry-type.status-choices.insights-range-choices { grid-template-columns: repeat(4, 1fr); gap: 6px; }
.insights-range-choices button { padding: 10px 2px; font-size: 10px; }
.insights-trend-figure { margin: var(--space-4) 0 var(--space-3); }
.insights-trend-figure figcaption { margin-bottom: 8px; color: var(--text-secondary); font-size: var(--text-xs); }
.insights-trend-svg { display: block; width: 100%; height: auto; }
.insights-baseline { stroke: var(--border); stroke-width: 1; }
.insights-bar-track { display: inline-block; width: 60px; height: 6px; border-radius: var(--radius-pill); background: var(--surface-soft); vertical-align: middle; overflow: hidden; }
.insights-bar-fill { display: block; height: 100%; border-radius: inherit; background: var(--accent-strong); }

/* Transactions-overview modal: filter bar/panel and its sticky "add" button */
.transactions-overview-sheet .tx-filter-bar { display: flex; align-items: center; gap: var(--space-3); margin: 14px 0 4px; flex-wrap: wrap; }
.transactions-overview-sheet .tx-filter-bar button { flex: 0 0 auto; min-height: 42px; }
.transactions-overview-sheet .tx-filter-bar p { min-width: 0; margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--text-sm); }
.transactions-overview-sheet .tx-filter-panel { margin-top: 10px; padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface-soft); }
.transactions-overview-sheet .tx-overview-primary { position: sticky; top: -1px; z-index: 4; margin: 12px 0 14px; padding: 8px 0 10px; background: linear-gradient(180deg, var(--surface-raised) 72%, transparent); }
.transactions-overview-sheet .tx-overview-primary button { min-height: 50px; font-size: 1rem; }
@media (max-width: 700px) {
  .transactions-overview-sheet .tx-filter-bar { align-items: flex-start; flex-direction: column; }
  .transactions-overview-sheet .tx-filter-bar p { white-space: normal; }
  .transactions-overview-sheet .tx-filter-bar button { width: 100%; }
}

/* Clickable list rows that link to a transaction/movement candidate
   (recurringPostingForm's link list, potContributionsOverview). */
.plan-detail-row { display: grid; grid-template-columns: 34px minmax(0, 1fr) auto; gap: 12px; align-items: center; width: 100%; padding: 13px 0; border: 0; border-top: 1px solid var(--border); background: transparent; color: var(--text-primary); text-align: left; }
.plan-detail-row:first-child { border-top: 0; }
.plan-detail-row > span:nth-child(2) { display: grid; min-width: 0; }
.plan-detail-row b, .plan-detail-row small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plan-detail-row small { margin-top: 3px; color: var(--text-secondary); font-size: 11px; }
.plan-detail-row strong { font-size: 14px; }
.plan-detail-row .transaction-icon { display: grid; width: 34px; height: 34px; place-items: center; border-radius: var(--radius-sm); background: var(--accent-soft); color: var(--accent-strong); font-weight: 800; }
.candidate-row { border-top: 1px solid var(--border); }

/* Status picker (recurring posting: Verwacht/Betaald/Overgeslagen) and the
   insights date-range picker both extend .entry-type with a fixed-column
   grid instead of the default flexible pill track. */
.entry-type.status-choices { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-top: var(--space-4); }
.entry-type.status-choices button { padding: 11px 2px; font-size: 11px; }

/* Vaste lasten page filter tabs (Alles/Inkomsten/Lasten/Nog verwacht/
   Afgerond): 5 buttons need their own column count, same pattern as
   .insights-range-choices above. */
#recurring-filter-tabs.entry-type.status-choices { grid-template-columns: repeat(5, 1fr); gap: 4px; }
#recurring-filter-tabs button { padding: 9px 2px; font-size: 10px; }

/* Audit log rows (household activity) */
.audit-row { align-items: flex-start; flex-wrap: wrap; }
.audit-row > div { flex: 1; min-width: 0; }
.audit-row small { display: block; }
.audit-when { color: var(--text-secondary); font-size: 11px; white-space: nowrap; }

/* Household switcher (list of other households to switch into) */
.household-switcher { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-5); }

/* -- Modal / sheet ------------------------------------------------------------- */
/* Above .toast (styles.css: z-index 20) — an open modal must always win
   interaction priority over a transient notification, otherwise a toast
   still on screen when a modal opens can sit on top of and block its
   controls (observed: the bottom-sheet modal's submit button on a narrow
   phone, right after an onboarding-complete toast). */
.modal { z-index: 25; background: rgba(20, 25, 35, .45); backdrop-filter: blur(4px); }
.modal-sheet { background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-xl); box-shadow: var(--shadow-lg); max-width: min(570px, calc(100vw - 32px)); scroll-padding: 24px; }
/* The submit bar at the bottom of a long form (and a modal's own action
   row) stay reachable without scrolling past them — only the padding
   piece of this survived the v05.css retirement, position:sticky itself
   did not. */
.tx-submit-bar, .modal-actions { position: sticky; bottom: 0; margin-top: var(--space-4); padding-top: 14px; background: linear-gradient(180deg, transparent, var(--surface-raised) 35%); }
@media (max-width: 700px) {
  .modal { padding: 0; align-items: end; }
  .modal-sheet { width: 100%; min-width: 0; max-width: 100%; max-height: 92dvh; border-radius: var(--radius-xl) var(--radius-xl) 0 0; padding-inline: clamp(16px, 5vw, 22px); }
  .tx-submit-bar, .modal-actions { padding-bottom: max(14px, env(safe-area-inset-bottom)); }
}
.toast { background: var(--text-primary); color: var(--canvas); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); }
/* toast(message,'error') from app.js/accounts.js/period-close.js/month-routine.js
   — the only visual difference between "saved" and "failed to save" on the
   floating toast before this was none at all (both rendered as the same
   neutral pill). Reuses the same danger tokens accounts.js's inline
   .accounts-message.error already uses, so both error surfaces agree. */
.toast-error { background: var(--danger); color: #fff; }
/* Form errors must stay visible while a dialog is open. Place only error
   messages above the sheet, at the top of the viewport rather than over
   the submit bar; pointer-events keeps every dialog control reachable. */
body.modal-open > .toast-error {
  z-index: 26;
  top: max(12px, env(safe-area-inset-top));
  bottom: auto;
  max-width: calc(100vw - 24px);
  pointer-events: none;
}
/* app.js's modal()/close() already toggle this class on <body> — the
   scroll-lock itself just needs restoring here (only ever lived in the
   now-retired v05.css). Background content also shouldn't stay reachable
   by a screen reader's swipe cursor while a dialog has the page. */
body.modal-open { overflow: hidden; }
body.modal-open .mobile-nav { visibility: hidden; }

/* -- Bottom sheet (mobile "Meer" menu) ------------------------------------------ */
.bottom-sheet { position: fixed; inset: 0; z-index: 30; background: rgba(20, 25, 35, .45); display: flex; align-items: flex-end; }
.bottom-sheet.hidden { display: none; }
.bottom-sheet-panel {
  width: 100%; background: var(--surface-raised); border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: var(--space-5) var(--space-4) calc(var(--space-5) + env(safe-area-inset-bottom));
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; gap: 4px;
  position: relative;
}
.bottom-sheet-close { position: absolute; top: 12px; right: 12px; width: 44px; height: 44px; border-radius: 50%; border: 0; background: var(--surface-soft); color: var(--text-secondary); font-size: 22px; display: grid; place-items: center; }
.sheet-link { min-height: 48px; border: 0; border-radius: var(--radius-md); background: transparent; color: var(--text-primary); font-weight: 600; text-align: left; padding-inline: 12px; text-decoration: none; display: flex; align-items: center; }
.sheet-link:hover { background: var(--surface-soft); }
.sheet-link.logout { color: var(--danger); margin-top: var(--space-2); border-top: 1px solid var(--border); border-radius: 0; padding-top: 16px; }

/* Always position() this explicitly — position:fixed with no offsets just
   sits wherever the element happens to fall in normal flow, which (this
   button lives right before #modal, at the very end of #app-root) reads as
   a stray chip randomly placed near whatever content is currently visible.
   Fixed positioning also means toggling .hidden on/off never shifts any
   other layout, in either state. */
.install-chip {
  position: fixed; z-index: 5;
  right: max(var(--space-4), env(safe-area-inset-right));
  bottom: max(var(--space-4), env(safe-area-inset-bottom));
}
@media (max-width: 767px) {
  /* Clear the fixed bottom nav in the authenticated app. */
  .install-chip { bottom: calc(78px + env(safe-area-inset-bottom)); }
}
/* On the public sign-in/status screen specifically, present it as a calm
   secondary action anchored under the card rather than a corner utility
   chip. #install-app-button always comes after #sign-in in the DOM
   (regardless of which is currently visible), so the general sibling
   combinator reaches it without any JS wiring. */
#sign-in:not(.hidden) ~ .install-chip {
  left: 50%; right: auto; transform: translateX(-50%);
  bottom: max(var(--space-6), calc(env(safe-area-inset-bottom) + var(--space-4)));
}

/* v0.41 "Visual Goals & Payments" — the shared icon/photo picker
   (site/icons.js) used by spaardoelen, vaste lasten/inkomsten and
   categorieën. A calm compact row (current icon or photo + Wijzigen/
   Verwijderen) that expands *inline*, never as a second modal on top of an
   already-open one — this is what keeps it simple on both desktop and a
   narrow mobile viewport with no separate stacking/focus-trap logic. */
.modal-heading-with-icon { display: flex; align-items: flex-start; gap: var(--space-3); margin-bottom: var(--space-2); }
.modal-heading-with-icon h2 { margin: 0; }
.modal-heading-with-icon > div { min-width: 0; display: grid; gap: 4px; }
.modal-heading-with-icon .link-action { justify-self: start; min-height: 32px; padding: 0; text-align: left; }
.icon-field { margin: var(--space-2) 0 var(--space-4); }
.icon-field-label { display: block; font-size: var(--text-sm); color: var(--text-secondary); margin-bottom: var(--space-2); }
.icon-field-row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.icon-field-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.icon-field-actions button { min-height: 44px; padding-inline: var(--space-3); }

/* A fixed-size, fixed-aspect-ratio box reserved up front — an icon renders
   instantly and a photo never causes layout shift while it loads. */
.icon-swatch {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 44px; height: 44px; border-radius: var(--radius-md);
  background: var(--surface-soft); border: 1px solid var(--border);
  color: var(--text-secondary); overflow: hidden; aspect-ratio: 1 / 1;
}
.icon-swatch-lg { width: 64px; height: 64px; }
.icon-swatch svg { width: 55%; height: 55%; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.icon-swatch-photo { background: var(--surface); }
.icon-swatch-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

.icon-field-panel { margin-top: var(--space-3); padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface-soft); }
.icon-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(44px, 1fr)); gap: var(--space-2); margin-bottom: var(--space-3); }
.icon-grid-option {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; min-width: 44px; min-height: 44px; padding: 0;
  border-radius: var(--radius-sm); border: 1.5px solid var(--border);
  background: var(--surface); color: var(--text-secondary); cursor: pointer;
}
.icon-grid-option svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.icon-grid-option:hover { border-color: var(--accent); }
.icon-grid-option:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
/* The selected option stays identifiable without relying on color alone —
   a filled accent background, a visible border, AND an explicit checkmark. */
.icon-grid-option.selected { background: var(--accent-soft); border-color: var(--accent-strong); color: var(--text-on-accent); }
.icon-grid-check { display: none; position: absolute; bottom: -4px; right: -4px; width: 16px; height: 16px; border-radius: 50%; background: var(--accent-strong); color: var(--text-on-dark-fill); font-size: 10px; line-height: 16px; }
.icon-grid-option.selected .icon-grid-check { display: block; }
.icon-field-upload { display: flex; justify-content: center; min-height: 44px; align-items: center; cursor: pointer; }
/* styles.css's generic `input{display:block}` outranks the browser's own
   [hidden] default (an author rule always beats a user-agent one,
   regardless of selector specificity) — so the file input needs its own
   explicit, higher-specificity rule to actually stay hidden. */
.icon-field-file { display: none; }
.icon-field-hint { margin: var(--space-2) 0 0; font-size: var(--text-xs); }
.icon-field-error { margin: var(--space-2) 0 0; color: var(--danger); font-size: var(--text-sm); }
@media (max-width: 480px) {
  .icon-grid { grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); }
}

/* v0.43: calm page-level decoration. It lives behind the real controls,
   never changes document flow and disappears in high-contrast/reduced-
   motion contexts. Each workspace gets a slightly different colour cue so
   the full pages feel intentional without turning finance data into noise. */
.page-section { position: relative; isolation: isolate; overflow: clip; }
.page-section::before,
.page-section::after {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  border-radius: 999px; opacity: .62;
}
.page-section::before {
  width: clamp(150px, 22vw, 340px); aspect-ratio: 1;
  top: -34px; right: -52px;
  background: radial-gradient(circle at 38% 38%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 68%);
}
.page-section::after {
  width: clamp(90px, 13vw, 190px); aspect-ratio: 1;
  top: 76px; right: 7%;
  border: 1px dashed color-mix(in srgb, var(--accent) 26%, transparent);
}
#page-spaardoelen::before { background: radial-gradient(circle at 38% 38%, color-mix(in srgb, var(--savings) 25%, transparent), transparent 68%); }
#page-vaste-lasten::before { background: radial-gradient(circle at 38% 38%, color-mix(in srgb, var(--expense) 18%, transparent), transparent 68%); }
#page-rekeningen::before { background: radial-gradient(circle at 38% 38%, color-mix(in srgb, var(--info) 22%, transparent), transparent 68%); }
.page-section > * { position: relative; z-index: 1; }
@media (max-width: 700px) {
  .page-section::before { right: -96px; top: -58px; opacity: .45; }
  .page-section::after { display: none; }
}
@media (forced-colors: active) {
  .page-section::before, .page-section::after { display: none; }
}
