/* ============================================================
   Finance Portal — Core Styles
   Design system: dark navy theme, CSS custom properties
   ============================================================ */

:root {
  /* Colours */
  --bg-base:       #0f1523;
  --bg-surface:    #141b2d;
  --bg-card:       #1a2236;
  --bg-elevated:   #1f2b40;
  --bg-hover:      #243047;

  --accent:        #3b82f6;
  --accent-dark:   #2563eb;
  --accent-light:  #60a5fa;
  --success:       #10b981;
  --warning:       #f59e0b;
  --danger:        #ef4444;
  --danger-dark:   #dc2626;   /* for white text on a red fill */
  --info:          #06b6d4;

  /* TEXT variants of the semantic colours.
     --accent and --danger are fine as fills and borders, but as small text on
     our dark surfaces they measure 3.86:1 and 3.78:1 — below the AA 4.5:1
     minimum. These lighter tints clear it (5.59:1 and 5.14:1 on --bg-elevated)
     and are what any `color:` declaration should use. --success, --warning and
     --info already pass, so they have no separate variant. */
  --accent-text:   #60a5fa;
  --danger-text:   #f87171;
  --purple-text:   #a78bfa;

  --text-primary:  #f1f5f9;
  --text-secondary:#94a3b8;
  /* Was #64748b, which measured 3.83:1 on --bg-base, 3.33:1 on --bg-card and
     2.99:1 on --bg-elevated — below the WCAG AA 4.5:1 minimum on every surface,
     and below even the 3:1 large-text floor on elevated. It is used almost
     entirely for 10–11px hints and provenance lines, which is exactly where
     contrast matters most. #8b9ab1 clears 4.5:1 on all three (4.98:1 worst
     case) while staying visibly dimmer than --text-secondary. */
  --text-muted:    #8b9ab1;
  --text-inverse:  #0f172a;

  --border:        #2d3a52;
  --border-light:  #334155;

  /* Category colours.
   *
   * Three of these were byte-identical to a semantic token: property was
   * --danger, savings was --success, pension was --warning. So a property slice
   * in an allocation chart was the exact red of "overdue", and a savings slice
   * the exact green of "healthy".
   *
   * Category colour is an IDENTITY — this slice is property. Semantic colour is
   * a JUDGEMENT — this thing is wrong. When the two collide the judgement stops
   * carrying meaning, because the reader learns that red sometimes means
   * nothing at all. That it happened three times out of six says the palette
   * was derived from the semantic one rather than designed beside it.
   *
   * Warm hues remain for pension and commodity, which is right — but separated
   * from --warning by lightness rather than sharing its exact value. A test
   * fails on any exact collision, in either theme. */
  --cat-banking:    #60a5fa;
  --cat-savings:    #2dd4bf;
  --cat-investment: #a78bfa;
  --cat-pension:    #facc15;
  --cat-property:   #f472b6;
  --cat-commodity:  #fb923c;

  /* Spacing */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;

  /* Typography */
  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'Cascadia Code', 'Fira Code', 'SF Mono', 'Consolas', monospace;
  --text-xs:  11px;
  --text-sm:  13px;
  --text-base:15px;
  --text-lg:  17px;
  --text-xl:  20px;
  --text-2xl: 24px;
  --text-3xl: 30px;
  --text-4xl: 36px;

  /* Radii */
  --radius-sm:  6px;
  --radius:     10px;
  --radius-lg:  14px;
  --radius-xl:  20px;
  --radius-full:9999px;

  /* Shadows */
  --shadow-sm:  0 1px 3px rgba(0,0,0,.4);
  --shadow:     0 4px 12px rgba(0,0,0,.5);
  --shadow-lg:  0 8px 32px rgba(0,0,0,.6);

  /* Layout */
  --sidebar-width: 220px;
  --sidebar-collapsed: 60px;
  --header-height: 60px;
}

/* ── Reset ─────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { height: 100%; scroll-behavior: smooth; }
body {
  height: 100%;
  font-family: var(--font);
  font-size: var(--text-base);
  background: var(--bg-base);
  color: var(--text-primary);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
/* --accent-text, not --accent-light: the file already documents that the
   saturated accent is fine as a fill and fails AA as small text. In the dark
   theme the two happen to be the same value, which is why this went unnoticed
   until a light ground made them differ. */
a { color: var(--accent-text); text-decoration: none; }
a:hover { color: var(--accent); }
button { cursor: pointer; font-family: inherit; }
input, select, textarea { font-family: inherit; }

/* ── Layout ─────────────────────────────────────────────── */
.app-layout {
  display: flex;
  height: 100vh;
  overflow: hidden;
}

/* ── Sidebar ─────────────────────────────────────────────── */
.sidebar {
  width: var(--sidebar-width);
  background: var(--bg-surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  transition: width .25s ease;
  overflow: hidden;
}

.sidebar.collapsed { width: var(--sidebar-collapsed); }
.sidebar.collapsed .nav-label,
.sidebar.collapsed .brand-name,
.sidebar.collapsed .brand-user,
.sidebar.collapsed .sidebar-footer { display: none; }

/* The collapsed header keeps ONLY the toggle, centred.
 *
 * Collapsing used to hide the labels but leave the header row untouched: logo +
 * gap + padding already came to 96px of content in a 60px box, and with
 * overflow:hidden on the sidebar the toggle — last in the row — was clipped
 * entirely out of view. The sidebar could be collapsed and then only reopened by
 * reloading the page. A control that closes something must survive the closing. */
.sidebar.collapsed .sidebar-logo { display: none; }
/* .sidebar-brand is flex:1 — hiding only its children leaves the box claiming
 * the row and shoving the toggle back against the edge. */
.sidebar.collapsed .sidebar-brand { display: none; }
.sidebar.collapsed .sidebar-header {
  padding-left: 0;
  padding-right: 0;
  gap: 0;
  justify-content: center;
}

.sidebar-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4);
  border-bottom: 1px solid var(--border);
  min-height: var(--header-height);
}

.sidebar-logo {
  font-size: 28px;
  font-weight: 700;
  color: var(--accent);
  flex-shrink: 0;
  width: 32px;
  text-align: center;
}

.sidebar-brand { flex: 1; min-width: 0; }
.brand-name { display: block; font-weight: 600; font-size: var(--text-sm); white-space: nowrap; }
.brand-user { display: block; font-size: var(--text-xs); color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.sidebar-toggle {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 18px;
  padding: var(--space-1);
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  transition: background .15s;
}
.sidebar-toggle:hover { background: var(--bg-hover); color: var(--text-primary); }

/* ── The palette's visible trigger ────────────────────────────
   A nav row that opens a dialog, so it is sized like a nav row: 44px, and the
   label goes with every other label when the rail collapses to 60px. */
.sidebar-search { padding: var(--space-3) var(--space-4) 0; }

.palette-trigger {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-muted);
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
  transition: border-color .15s, color .15s;
}
.palette-trigger:hover { border-color: var(--accent); color: var(--text-primary); }
.palette-trigger .nav-label { font-weight: 500; }

.palette-kbd {
  margin-left: auto;
  font-size: 10px;
  font-family: inherit;
  letter-spacing: .04em;
  padding: 1px 5px;
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--text-muted);
}
/* In the 60px rail the button is the magnifier and nothing else — the shortcut
   hint has nowhere to sit and the border would draw a box round one glyph. */
.sidebar.collapsed .sidebar-search { padding: var(--space-3) 0 0; }
.sidebar.collapsed .palette-kbd { display: none; }
.sidebar.collapsed .palette-trigger { justify-content: center; border-color: transparent; }

/* ── Command palette ──────────────────────────────────────────
   Top-anchored rather than centred: the list grows downwards from where the
   eye already is, instead of the whole dialog shifting as results arrive. */
.palette-overlay {
  position: fixed;
  inset: 0;
  z-index: 8100;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 10vh var(--space-4) var(--space-4);
  background: rgba(0,0,0,.7);
}
.palette-overlay.hidden { display: none; }

.palette-box {
  width: 100%;
  max-width: 560px;
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

.palette-field {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}
.palette-field-icon { font-size: 16px; }

.palette-input {
  flex: 1;
  min-width: 0;
  background: none;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-size: var(--text-base);
}
.palette-input::placeholder { color: var(--text-muted); }

.palette-close {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 22px;
  line-height: 1;
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
}
.palette-close:hover { color: var(--text-primary); }

.palette-results {
  list-style: none;
  max-height: 50vh;
  overflow-y: auto;
}

.palette-option {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
}
/* The highlight is aria-selected's visible half. Focus never leaves the input,
   so without this the keyboard user moving down the list sees nothing move. */
.palette-option.active { background: var(--bg-hover); }

.palette-option-icon { font-size: 17px; width: 20px; text-align: center; flex-shrink: 0; }
.palette-option-text { min-width: 0; flex: 1; }
.palette-option-name { display: block; font-size: var(--text-sm); font-weight: 600; }
.palette-option-detail {
  display: block;
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.palette-option-kind {
  flex-shrink: 0;
  font-size: 10px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.palette-empty,
.palette-hint {
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.palette-hint { border-top: 1px solid var(--border); }

.nav-list {
  list-style: none;
  padding: var(--space-3) 0;
  flex: 1;
  overflow-y: auto;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  color: var(--text-secondary);
  border-radius: 0;
  transition: background .15s, color .15s;
  white-space: nowrap;
  position: relative;
}
.nav-item:hover { background: var(--bg-hover); color: var(--text-primary); }
.nav-item.active { background: rgba(59,130,246,.15); color: var(--accent-light); }
.nav-item.active::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--accent);
  border-radius: 0 3px 3px 0;
}

.nav-icon { font-size: 17px; width: 20px; text-align: center; flex-shrink: 0; }
.nav-label { font-size: var(--text-sm); font-weight: 500; }

/* Marks a page whose maths has been superseded by Retirement Planning. The
   page still works and is still reachable — this exists so the user can tell
   which of four similarly-named planners is authoritative, which is the thing
   the nav could not previously say. */
.nav-badge-old {
  margin-left: auto;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: 3px;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  opacity: 0.75;
}
.sidebar.collapsed .nav-badge-old { display: none; }

/* Unseen-count badge on a nav item (Tax Explorer change-watch digest). A quiet
   pill pushed to the far end of the row; hidden when the rail is collapsed. */
.nav-badge {
  margin-left: auto;
  min-width: 18px;
  text-align: center;
  font-size: 0.625rem;
  font-weight: 700;
  line-height: 1.5;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--info);
  color: #fff;
}
.sidebar.collapsed .nav-badge { display: none; }
.nav-separator { height: 1px; background: var(--border); margin: var(--space-2) var(--space-4); }

.nav-section-label {
  padding: var(--space-3) var(--space-4) var(--space-1);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--text-muted);
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: 8px;
}
.nav-section-label::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ── Sidebar sections (disclosure buttons) ────────────────────
   A section button is a nav row that happens to open something, so it gets a
   nav row's target: the 12px vertical padding is what keeps it past 44px.
   Do not tighten it to fit more sections in — fitting more in is the disease
   this pattern exists to treat. */
.nav-section { display: block; }
.nav-section-row { display: flex; align-items: center; }

.nav-section-btn {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 1;
  min-width: 0;
  min-height: 44px;
  padding: var(--space-3) var(--space-4);
  background: none;
  border: none;
  text-align: left;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.nav-section-btn:hover { background: var(--bg-hover); }

.nav-section-chev {
  margin-left: auto;
  color: var(--text-muted);
  transition: transform .15s ease;
}
.nav-section-btn[aria-expanded="true"] .nav-section-chev { transform: rotate(90deg); }

.nav-sublist { list-style: none; }
.nav-sublist[hidden] { display: none; }
/* Indented so the section it belongs to is readable at a glance rather than
   inferred from the chevron. */
.nav-sublist .nav-item { padding-left: calc(var(--space-4) + var(--space-4)); }

/* The roll-up count for a collapsed section. The number is also written into
   the button's aria-label, because this pill is hidden from assistive tech. */
.nav-section-badge { margin-left: 0; }

/* The section's own destination. Its own hit area, because the classic failure
   of a row carrying two controls is a 20px chevron nobody can hit. */
.nav-hub-link {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-xs);
  flex-shrink: 0;
}
.nav-hub-link:hover { background: var(--bg-hover); color: var(--text-primary); }
/* The section icon on this link exists for the collapsed rail only — beside
   the button's own icon it would just be the same glyph twice. */
.nav-hub-link .nav-icon { display: none; }

/* ── The collapsed 60px rail ──────────────────────────────────
   Collapsing used to hide the labels and leave 25 unlabelled emoji: it reduced
   the sidebar's width and never its length. Now the rail is exactly six icons,
   one per section, each a link to that section — the hubs for the four that
   have one, the Dashboard and Settings for the two that do not. */
.sidebar.collapsed .nav-section-btn,
.sidebar.collapsed .nav-sublist { display: none; }
.sidebar.collapsed .nav-hub-link .nav-icon { display: block; font-size: 17px; }
.sidebar.collapsed .nav-hub-link { width: 100%; padding: var(--space-3) 0; }

.sidebar-footer {
  padding: var(--space-4);
  border-top: 1px solid var(--border);
}

/* ── Section hub cards ────────────────────────────────────────
   A card is a link, a name and a sentence. The sentence is the deliverable:
   a hub whose cards are the sidebar list with icons is an extra click for
   nothing. */
.hub-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--space-4);
}

.hub-card-link {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  height: 100%;
  padding: var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-primary);
  transition: border-color .15s, background .15s;
}
.hub-card-link:hover { background: var(--bg-hover); border-color: var(--accent); }

.hub-card-icon { font-size: 20px; line-height: 1.3; flex-shrink: 0; }
.hub-card-text { display: block; min-width: 0; }
.hub-card-name { display: block; font-weight: 600; font-size: var(--text-sm); }
.hub-card-blurb {
  display: block;
  margin-top: 4px;
  font-size: var(--text-xs);
  color: var(--text-muted);
  max-width: 68ch;
}

/* ── Main Content ─────────────────────────────────────────── */
.main-content {
  flex: 1;
  overflow-y: auto;
  background: var(--bg-base);
}

.page-container {
  padding: var(--space-8);
  max-width: 1400px;
  margin: 0 auto;
}

/* ── Mobile navigation drawer ─────────────────────────────── */
/* Hidden on desktop; the sidebar is always visible there. */
.mobile-nav-toggle { display: none; }
.sidebar-scrim     { display: none; }

@media (max-width: 768px) {
  .page-container { padding: var(--space-4); padding-top: calc(var(--space-4) + 44px); }
  .sidebar { position: fixed; z-index: 100; height: 100vh; transition: transform .2s ease; }
  .sidebar:not(.open) { transform: translateX(-100%); }
  .main-content { margin-left: 0; }

  .mobile-nav-toggle {
    display: flex; align-items: center; justify-content: center;
    position: fixed; top: var(--space-3); left: var(--space-3); z-index: 120;
    /* 44px, not 40: this is the only way back to the nav on a phone, so it is
       the last control in the app that should be under the WCAG 2.5.5 bar. */
    width: 44px; height: 44px;
    background: var(--bg-elevated); color: var(--text-primary);
    border: 1px solid var(--border); border-radius: var(--radius);
    font-size: var(--text-xl); line-height: 1; cursor: pointer;
    box-shadow: var(--shadow);
  }
  body.nav-open .mobile-nav-toggle { display: none; }

  .sidebar-scrim { position: fixed; inset: 0; background: rgba(0,0,0,.55); z-index: 99; }
  body.nav-open .sidebar-scrim { display: block; }

  /* Account rows: icon + name + value + three buttons cannot share 375px on
   * one line. Wrapping beats clipping — the value and actions drop below the
   * name instead of walking off-screen (2026-08c panel, U-1). */
  .account-card { flex-wrap: wrap; }
  .account-card .account-details { flex-basis: calc(100% - 60px); }

  /* WCAG 2.5.5 on a thumb. The section buttons and the overview links are 44px
     at every width already; these two are the affordances this migration added
     that are sized by their content, and a one-line blurb is under the bar. */
  .hub-card-link { min-height: 44px; }
  .palette-option { min-height: 44px; }
  /* Full-width on a phone: 10vh of empty space above a search box is a desktop
     luxury, and the on-screen keyboard takes half the viewport anyway. */
  .palette-overlay { padding: var(--space-4); align-items: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar { transition: none; }
  /* Sections open and close instantly. Height and rotation animations are the
     worst offenders for vestibular triggers, and a section that opens with no
     animation at all reads as fast rather than cheap. */
  .nav-section-btn,
  .nav-section-chev { transition: none; }
  /* Same rule for what the palette added: the trigger's hover fade and the
     hub cards' border fade. Neither is a large movement, but "reduce" is a
     request, not a severity threshold. */
  .palette-trigger,
  .hub-card-link { transition: none; }
}

/* ── Page Header ─────────────────────────────────────────── */
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-8);
  flex-wrap: wrap;
  gap: var(--space-4);
}

.page-title { font-size: var(--text-2xl); font-weight: 700; color: var(--text-primary); }
.page-subtitle { font-size: var(--text-sm); color: var(--text-muted); margin-top: 2px; }

/* ── Cards ─────────────────────────────────────────────── */
.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}

.card-sm { padding: var(--space-4); }
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-5); }
.card-title { font-size: var(--text-base); font-weight: 600; color: var(--text-secondary); text-transform: uppercase; letter-spacing: .05em; font-size: var(--text-xs); }

/* ── KPI Metrics ─────────────────────────────────────────── */
.metric-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

.metric-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-5) var(--space-6);
}

.metric-label { font-size: var(--text-xs); font-weight: 600; text-transform: uppercase; letter-spacing: .07em; color: var(--text-muted); margin-bottom: var(--space-2); }
.metric-value { font-size: var(--text-3xl); font-weight: 700; color: var(--text-primary); line-height: 1.1; font-variant-numeric: tabular-nums; }
.metric-change { font-size: var(--text-sm); margin-top: var(--space-1); }
.metric-change.up   { color: var(--success); }
.metric-change.down { color: var(--danger-text); }   /* text, so the AA tint */
.metric-change.flat { color: var(--text-muted); }

/* ── Levers (FIRE planner interactive controls) ──────────── */
.lever-section { border-bottom: 1px solid var(--border); }
.lever-section:last-child { border-bottom: none; }
.lever-header {
  width: 100%; display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; background: none; border: none; color: var(--text);
  font-size: var(--text-sm); font-weight: 600; cursor: pointer; text-align: left;
  transition: background .15s;
}
.lever-header:hover { background: var(--bg-elevated); }
.lever-header span:first-child { font-size: 10px; color: var(--text-muted); width: 12px; }

/* ── Charts ─────────────────────────────────────────────── */
.chart-container { position: relative; height: 280px; }
.chart-container-lg { height: 360px; }
.chart-container-sm { height: 180px; }

/* ── Tables ─────────────────────────────────────────────── */
.table-wrapper { overflow-x: auto; border-radius: var(--radius); border: 1px solid var(--border); }

table { width: 100%; border-collapse: collapse; }
thead { background: var(--bg-elevated); }
th {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
}
td {
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--bg-hover); }
.text-right { text-align: right; }
.text-center { text-align: center; }

/* ── Buttons ─────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  font-weight: 500;
  border: 1px solid transparent;
  transition: background .15s, border-color .15s, opacity .15s;
  white-space: nowrap;
}
.btn:disabled { opacity: .5; cursor: not-allowed; }
/* White on --accent (#3b82f6) is only 3.68:1 — below AA for button text at
   these sizes. --accent-dark takes it to 5.17:1 with no perceptible change to
   the design language; hover goes one step darker again to stay distinct. */
.btn-primary { background: var(--accent-dark); color: #fff; border-color: var(--accent-dark); }
.btn-primary:hover:not(:disabled) { background: #1d4ed8; border-color: #1d4ed8; }
.btn-secondary { background: var(--bg-elevated); color: var(--text-primary); border-color: var(--border-light); }
.btn-secondary:hover:not(:disabled) { background: var(--bg-hover); }
/* White on --danger is 3.76:1. --danger-dark takes it to 4.83:1 with no
   perceptible change to the design, matching what .btn-primary already does. */
.btn-danger { background: var(--danger-dark); color: #fff; border-color: var(--danger-dark); }
.btn-danger:hover:not(:disabled) { background: #dc2626; }
.btn-ghost { background: transparent; color: var(--text-secondary); border-color: transparent; }
.btn-ghost:hover:not(:disabled) { background: var(--bg-hover); color: var(--text-primary); }
.btn-sm { padding: var(--space-1) var(--space-3); font-size: var(--text-xs); }
.btn-lg { padding: var(--space-3) var(--space-6); font-size: var(--text-base); }
.btn-full { width: 100%; justify-content: center; }
.btn-icon { padding: var(--space-2); border-radius: var(--radius-sm); min-width: 32px; justify-content: center; }
/* A control that reads as a link but is a button, because it performs an
   action rather than going somewhere. --accent-text, not --accent: the palette
   already records that the fill colour is too light as small text. */
.btn-link { background: none; border: none; padding: 0; color: var(--accent-text);
            font: inherit; cursor: pointer; text-decoration: underline; }
.btn-link:hover { color: var(--accent); }

/* ── Forms ─────────────────────────────────────────────── */
.form-group { display: flex; flex-direction: column; gap: var(--space-1); margin-bottom: var(--space-4); }
label { font-size: var(--text-sm); font-weight: 500; color: var(--text-secondary); }

input[type="text"],
input[type="number"],
input[type="password"],
input[type="date"],
input[type="email"],
select,
textarea {
  background: var(--bg-elevated);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  transition: border-color .15s;
  width: 100%;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(59,130,246,.2);
}

/* ── Keyboard focus ──────────────────────────────────────────────────────────
   The rule above was the only focus styling in either stylesheet. It handles
   inputs properly (outline removed but replaced with a border and ring), but
   every button, link, tab and toggle fell back to the browser default — which
   varies by engine and is low-contrast against the dark ground.

   :focus-visible rather than :focus, so the ring appears for keyboard and
   assistive-tech users without putting an outline around every mouse click. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}
/* Inputs keep their border+ring treatment; give them a matching outline too so
   the indicator is consistent regardless of how the element is reached. */
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
select option { background: var(--bg-surface); }
.form-hint { font-size: var(--text-xs); color: var(--text-muted); }
.form-error { font-size: var(--text-sm); color: var(--danger-text); margin-top: var(--space-2); }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
@media (max-width: 600px) { .form-row { grid-template-columns: 1fr; } }
.form-section-heading {
  font-size: var(--text-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
  margin: var(--space-4) 0 var(--space-3);
}

/* ── Login Screen ─────────────────────────────────────────── */
.login-screen {
  position: fixed;
  inset: 0;
  background: var(--bg-base);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.login-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: var(--space-10);
  width: 100%;
  max-width: 400px;
  box-shadow: var(--shadow-lg);
}

.login-logo { text-align: center; margin-bottom: var(--space-8); }
.login-logo .logo-icon {
  display: inline-block;
  font-size: 48px;
  font-weight: 700;
  color: var(--accent);
  margin-bottom: var(--space-3);
  font-style: normal;
}
.login-logo h1 { font-size: var(--text-2xl); font-weight: 700; }
.login-logo p { color: var(--text-muted); font-size: var(--text-sm); margin-top: 4px; }

/* ── Tabs ─────────────────────────────────────────────── */
.tabs { display: flex; gap: 2px; background: var(--bg-elevated); border-radius: var(--radius); padding: 3px; margin-bottom: var(--space-6); }
.tab {
  flex: 1;
  padding: var(--space-2) var(--space-3);
  border-radius: calc(var(--radius) - 2px);
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 500;
  transition: background .15s, color .15s;
}
.tab.active { background: var(--bg-card); color: var(--text-primary); }

/* ── Badges ─────────────────────────────────────────────── */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
}
.badge-banking    { background: rgba(59,130,246,.15);  color: #93c5fd; }
.badge-savings    { background: rgba(16,185,129,.15);  color: #6ee7b7; }
.badge-investment { background: rgba(139,92,246,.15);  color: #c4b5fd; }
.badge-pension    { background: rgba(245,158,11,.15);  color: #fcd34d; }
.badge-property   { background: rgba(239,68,68,.15);   color: #fca5a5; }
.badge-commodity  { background: rgba(249,115,22,.15);  color: #fdba74; }
.badge-success    { background: rgba(16,185,129,.15);  color: #6ee7b7; }
.badge-warning    { background: rgba(245,158,11,.15);  color: #fcd34d; }
.badge-danger     { background: rgba(239,68,68,.15);   color: #fca5a5; }
.badge-info       { background: rgba(6,182,212,.15);   color: #67e8f9; }
.badge-company    { background: rgba(139,92,246,.15);  color: #c4b5fd; }

/* ── Grid layouts ─────────────────────────────────────────── */
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-6); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6); }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); }
@media (max-width: 1200px) { .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 900px)  { .grid-3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* ── Allowance progress bars ─────────────────────────────── */
.allowance-bar { margin-bottom: var(--space-4); }
.allowance-label { display: flex; justify-content: space-between; font-size: var(--text-xs); color: var(--text-muted); margin-bottom: var(--space-1); }
.allowance-label strong { color: var(--text-primary); }
.progress-track { height: 6px; background: var(--bg-elevated); border-radius: var(--radius-full); overflow: hidden; }
.progress-fill {
  height: 100%;
  background: var(--accent);
  border-radius: var(--radius-full);
  transition: width .4s ease;
}
.progress-fill.near   { background: var(--warning); }
.progress-fill.full   { background: var(--danger); }

/* ── FIRE score ─────────────────────────────────────────── */
.fire-score {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--space-6);
}
.fire-gauge {
  position: relative;
  width: 180px;
  height: 90px;
  overflow: hidden;
}
.fire-percent {
  font-size: var(--text-4xl);
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1;
}
.fire-label { font-size: var(--text-sm); color: var(--text-muted); margin-top: var(--space-2); }

/* ── Utility ─────────────────────────────────────────────── */
.hidden { display: none !important; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
.loading-spinner { display: flex; justify-content: center; padding: var(--space-12); color: var(--text-muted); }
.empty-state { text-align: center; padding: var(--space-12); color: var(--text-muted); }
.empty-state h3 { font-size: var(--text-lg); color: var(--text-secondary); margin-bottom: var(--space-2); }
.truncate { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.mono { font-family: var(--font-mono); }
/* These are TEXT utilities, so they take the text variants. --danger is 3.78:1
   as text and only exists as a fill and border colour; --danger-text clears AA.
   --success and --warning already pass, so they are unchanged. */
.text-success { color: var(--success); }
.text-danger  { color: var(--danger-text); }
.text-warning { color: var(--warning); }
.text-accent  { color: var(--accent-text); }
.text-muted   { color: var(--text-muted); }
.text-sm      { font-size: var(--text-sm); }
.text-xs      { font-size: var(--text-xs); }
.font-bold    { font-weight: 700; }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-2 { gap: var(--space-2); }
.gap-4 { gap: var(--space-4); }
.mb-4 { margin-bottom: var(--space-4); }
.mb-6 { margin-bottom: var(--space-6); }
.mt-4 { margin-top: var(--space-4); }

/* ── Member colour dots ─────────────────────────────────── */
.member-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}

/* ── Account card in list ─────────────────────────────────── */
.account-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  transition: border-color .15s;
  cursor: pointer;
}
.account-card:hover { border-color: var(--border-light); background: var(--bg-hover); }

/* Dashboard expandable wrapper — replaces the plain .account-card for the accounts list */
.account-card-wrap {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  margin-bottom: 8px;
  overflow: hidden;
  transition: border-color .15s;
}
.account-card-wrap:hover { border-color: var(--border-light); }
.account-card-wrap .account-card {
  border: none !important;
  border-radius: 0 !important;
  background: transparent !important;
  margin: 0 !important;
}
.account-card .account-icon {
  width: 44px;
  height: 44px;
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  flex-shrink: 0;
}
.account-card .account-details { flex: 1; min-width: 0; }
.account-card .account-name { font-weight: 600; font-size: var(--text-base); }
.account-card .account-meta { font-size: var(--text-xs); color: var(--text-muted); margin-top: 2px; }
.account-card .account-value { text-align: right; flex-shrink: 0; }
.account-card .value-amount { font-size: var(--text-lg); font-weight: 700; font-variant-numeric: tabular-nums; }
.account-card .value-date { font-size: var(--text-xs); color: var(--text-muted); }

/* ── Privacy toggle switch (shared by sidebar + dashboard header) ── */
.priv-toggle-wrap {
  display: flex; align-items: center; gap: 7px;
  font-size: .78rem; color: var(--text-muted); cursor: pointer; user-select: none;
}
.priv-switch { position: relative; width: 36px; height: 20px; flex-shrink: 0; }
.priv-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.priv-slider {
  position: absolute; inset: 0; background: #334155; border-radius: 10px; transition: .2s;
}
.priv-slider::before {
  content: ''; position: absolute; left: 3px; top: 3px; width: 14px; height: 14px;
  background: #64748b; border-radius: 50%; transition: .2s;
}
.priv-switch input:checked + .priv-slider { background: #3b82f6; }
.priv-switch input:checked + .priv-slider::before { transform: translateX(16px); background: #fff; }

/* ── Privacy mode ─────────────────────────────────────────── */
/*
 * When body.privacy-on is active, blur all .pv spans (explicit wrapper),
 * plus the common value display classes used across pages so they are
 * covered without needing per-page code changes.
 */
body.privacy-on .pv,
body.privacy-on .value-amount,
body.privacy-on .metric-value {
  filter: blur(7px);
  user-select: none;
  border-radius: 3px;
  display: inline-block;
  transition: filter .2s;
}

/* Sidebar privacy toggle */
.sidebar-privacy {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  color: var(--text-muted);
  font-size: var(--text-xs);
  cursor: pointer;
  user-select: none;
  border-radius: var(--radius);
  transition: background .15s;
  width: 100%;
  background: none;
  border: none;
}
.sidebar-privacy:hover { background: var(--bg-hover); color: var(--text-base); }
/* Sidebar uses slightly smaller toggle — override just the dimensions */
.sidebar-privacy .priv-switch { width: 32px; height: 18px; }
/* When collapsed, hide the label but keep the icon */
.sidebar.collapsed .sidebar-privacy .priv-label { display: none; }

/* ── Dashboard account expand ─────────────────────────────── */
.acc-expand-btn {
  background: none; border: none; color: var(--text-muted);
  font-size: .65rem; padding: 6px 8px; cursor: pointer;
  flex-shrink: 0; transition: transform .18s, color .15s; line-height: 1;
  border-radius: 4px;
}
.acc-expand-btn:hover { color: var(--text-base); background: rgba(255,255,255,.05); }
.acc-expand-btn.open  { transform: rotate(180deg); color: var(--accent, #3b82f6); }
.acc-expand-body {
  display: none;
  padding: 10px 16px 14px 76px;
  border-top: 1px solid var(--border);
  background: rgba(255,255,255,.02);
  font-size: .8rem;
  animation: expandIn .15s ease;
}
.acc-expand-body.open { display: block; }
@keyframes expandIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.acc-expand-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 8px 20px;
  margin-top: 4px;
}
.acc-expand-item .ei-label { color: var(--text-muted); font-size: .7rem; margin-bottom: 1px; }
.acc-expand-item .ei-val   { font-weight: 600; }

/* ── Scrollbar ─────────────────────────────────────────────── */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: var(--bg-surface); }
::-webkit-scrollbar-thumb { background: var(--border-light); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }

/* ── Accessibility ─────────────────────────────────────────────
   Baseline keyboard and screen-reader affordances. */

/* Skip link — hidden until focused, then pinned over the sidebar. */
.skip-link {
  position: absolute;
  left: 8px;
  top: -100px;
  z-index: 2000;
  padding: 10px 16px;
  /* --accent-dark, not --accent: white on the lighter accent measures 3.68:1,
     below the AA minimum. This was true in the dark theme before the light one
     existed — a light ground just made it worth measuring. */
  background: var(--accent-dark);
  color: #fff;
  font-weight: 600;
  border-radius: var(--radius-sm, 6px);
  text-decoration: none;
  transition: top .15s;
}
.skip-link:focus { top: 8px; }

/* A visible focus ring on everything reachable by keyboard. :focus-visible
   means pointer users never see it, so there is no reason to suppress it —
   which is how these normally get removed and never put back. */
:focus-visible {
  outline: 2px solid var(--accent-light);
  outline-offset: 2px;
  border-radius: 2px;
}
/* Elements given button/tab semantics by A11y.makeButton are focusable but
   carry their own backgrounds; keep the ring clear of them. */
[role="tab"]:focus-visible,
[role="button"]:focus-visible { outline-offset: 3px; }

/* Screen-reader-only text — for names that must be announced but not shown. */
.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;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Glossary term markup — <abbr data-term="badr">BADR</abbr>.
   Dotted underline rather than the browser default, which varies and is often
   invisible on dark backgrounds. Focusable, so keyboard users reach the
   definition too: the previous title-only tooltips were pointer-only. */
abbr.gloss {
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
  cursor: help;
}
abbr.gloss:hover,
abbr.gloss:focus-visible { border-bottom-color: var(--accent-text); color: var(--accent-text); }


/* ── Model limits ─────────────────────────────────────────────────────────────
   Deliberately quiet. A page that shouts about its own limitations either gets
   ignored or frightens the reader off numbers that are mostly sound; the point
   is that the list is FINDABLE, not that it dominates. */
.model-limits {
  margin: 20px 0 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elevated);
}
.model-limits > summary {
  padding: 10px 14px;
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  list-style: none;
}
.model-limits > summary::-webkit-details-marker { display: none; }
.model-limits > summary::before { content: '▸ '; }
.model-limits[open] > summary::before { content: '▾ '; }
.model-limits > summary:hover { color: var(--text-primary); }
.model-limits-body { padding: 0 14px 12px 30px; }
.model-limits-body ul { margin: 0; padding-left: 16px; }
.model-limits-body li {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-bottom: 6px;
  max-width: 78ch;
}
.model-limits-note,
.model-disclaimer {
  font-size: var(--text-xs);
  color: var(--text-muted);
  max-width: 78ch;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.model-disclaimer { margin: 16px 0 0; border-top: none; padding-top: 0; }


/* ── Error states ─────────────────────────────────────────────────────────────
   Deliberately not red-and-alarming. Most of these are a dropped connection,
   and dressing that as a crisis is what makes people stop trusting the app. */
.error-state {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  padding: 24px;
  max-width: 68ch;
  margin: 24px auto;
}
.error-state h3 { margin: 0 0 8px; }
.error-state-reassure {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin: 0 0 16px;
}
.error-state-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.error-state-detail { margin-top: 16px; }
.error-state-detail summary {
  cursor: pointer; font-size: var(--text-xs); color: var(--text-muted);
}
.error-state-detail pre {
  margin: 8px 0 0; padding: 8px 10px; overflow-x: auto;
  font-size: var(--text-xs); color: var(--text-secondary);
  background: var(--bg-card); border-radius: 3px;
}

/* ── Numbers line up ──────────────────────────────────────────────────────────
   Proportional figures in a money table are the most visible amateur tell in a
   finance app, and it is one declaration. Decimal points stack, columns can be
   compared by eye, and a changing figure stops jittering on re-render. */
td, .num, .metric-value, .value-amount {
  font-variant-numeric: tabular-nums;
}
th.num, td.num { text-align: right; }

/* Prose that runs the full 1400px container is about 170 characters a line,
   roughly 2.3x comfortable. Applies to the narrative blocks only — tables and
   dashboards still want the width. */
.prose, .rp-note, .ho-note, .model-limits-body li { max-width: 68ch; }


/* ── Digest ───────────────────────────────────────────────────────────────────
   Above the fold, because for someone opening the portal quarterly this is the
   only thing on the page they need. Quiet is the common case and is styled to
   be skimmed past, not to demand attention. */
.digest-card {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elevated); padding: 14px 18px; margin-bottom: 20px;
  max-width: 78ch;
}
.digest-card.is-quiet {
  font-size: var(--text-sm); color: var(--text-secondary); padding: 10px 18px;
}
.digest-head {
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-muted); margin-bottom: 8px;
}
.digest-card ul { list-style: none; margin: 0; padding: 0; }
.digest-card li { padding: 6px 0; font-size: var(--text-sm); }
.digest-card li + li { border-top: 1px solid var(--border); }
.digest-detail { font-size: var(--text-xs); color: var(--text-secondary); margin-top: 2px; max-width: 72ch; }
/* Only things that genuinely need the user get a marker. A market move does
   not, because there is nothing to do about it. */
.digest-attention > strong::before { content: '• '; color: var(--warning, #f59e0b); }


/* ── Verdicts ─────────────────────────────────────────────────────────────────
   The grade is carried by the WORD, with colour as reinforcement only. Red and
   green is the textbook failing pair for colourblind readers, this app already
   uses it for gain and loss, and a verdict that exists only as a hue does not
   survive being printed. */
.verdict {
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  padding: 14px 18px;
  max-width: 72ch;
}
.verdict-label {
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-muted);
}
.verdict-value {
  font-size: 1.6rem; font-weight: 700; margin: 2px 0 6px;
  font-variant-numeric: tabular-nums;
}
.verdict-say { font-size: var(--text-sm); }
.verdict-grade { font-weight: 600; margin-right: 4px; }
.verdict-bar, .verdict-action, .verdict-lever {
  font-size: var(--text-xs); color: var(--text-secondary); margin-top: 6px; max-width: 68ch;
}
.verdict-bar { color: var(--text-muted); }

.verdict-critical { border-left-color: #ef4444; }
.verdict-low      { border-left-color: #f59e0b; }
.verdict-adequate { border-left-color: #60a5fa; }
.verdict-strong   { border-left-color: #10b981; }
.verdict-neutral  { border-left-color: #94a3b8; }


/* ══════════════════════════════════════════════════════════════════════════════
   LIGHT THEME
   ──────────────────────────────────────────────────────────────────────────────
   Every colour below was checked against the same WCAG AA 4.5:1 bar the dark
   theme documents, on all five surfaces. Worst case is 4.81:1; the ratios are
   asserted in tests/theme-contrast.test.js rather than trusted, because a
   light theme is exactly where contrast quietly fails — dark text on white is
   easy, and mid-grey hints on a tinted card are not.

   It is a warm off-white (#f7f6f3), not a clinical #fff. A pure-white finance
   app under a desk lamp at 11pm is its own kind of unpleasant, and the warmth
   costs nothing.

   Only TOKENS are redefined. Every component rule already reads them, so the
   theme is a palette swap rather than a second stylesheet to keep in step.
   ════════════════════════════════════════════════════════════════════════════ */

:root[data-theme="light"] {
  --bg-base:       #f7f6f3;
  --bg-surface:    #ffffff;
  --bg-card:       #ffffff;
  --bg-elevated:   #f2f1ec;
  --bg-hover:      #e6e3dc;

  --accent:        #2563eb;
  --accent-dark:   #1d4ed8;
  --accent-light:  #3b82f6;
  --success:       #0f7a5a;
  --warning:       #b45309;
  --danger:        #c62828;
  --danger-dark:   #a41f1f;
  --info:          #0e7490;

  /* Text variants, darkened for a light ground — the mirror of the dark
     theme's lightened tints, and for the same reason: a saturated colour that
     works as a fill fails as small text. */
  --accent-text:   #1d4ed8;
  --danger-text:   #b3261e;
  --purple-text:   #6d28d9;

  --text-primary:  #1a2233;
  --text-secondary:#44506b;
  --text-muted:    #54627a;
  --text-inverse:  #ffffff;

  --border:        #ddd9d0;
  --border-light:  #c9c4b8;

  /* Asset-category colours. These are used as chart fills AND as small text
     labels beside them, so they are darkened to clear 4.5:1 on every surface
     (worst case 4.98:1) rather than reused from the dark theme, where they
     would be pastel smudges on white.

     A partial override here was the bug the contrast test caught: leaving
     these out meant six tokens stayed dark while the rest flipped. */
  /* Light equivalents. These never collided with the semantic tokens — the
   * light palette was designed beside them rather than from them — but they are
   * held to the same rule by the same test. */
  --cat-banking:    #1d4ed8;
  --cat-savings:    #0f766e;
  --cat-investment: #6d28d9;
  --cat-pension:    #854d0e;
  --cat-property:   #be185d;
  --cat-commodity:  #a63406;
}

/* Shadows have to change substance, not just opacity. On a dark ground a
   shadow is a subtle glow; on a light one the same rule reads as grime. */
:root[data-theme="light"] .card,
:root[data-theme="light"] .metric-card {
  box-shadow: 0 1px 2px rgba(26, 34, 51, .06);
}

/* The sidebar carries the app's identity, so it keeps a tinted ground rather
   than dissolving into the page. */
:root[data-theme="light"] .sidebar {
  background: #eceae4;
  border-right: 1px solid var(--border);
}

/* Charts read their colours from JS, not CSS, so the grid and tick colours are
   set by Chart.defaults at boot — see setTheme() in app.js. */


/* ── Theme picker ─────────────────────────────────────────────────────────────
   A three-way segmented control rather than a switch, because "Auto" is the
   default and a two-state toggle cannot express it. */
.theme-picker {
  display: flex; gap: 2px; margin: 0 0 20px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 2px;
}
.theme-opt {
  flex: 1; padding: 6px 10px; border: 0; border-radius: calc(var(--radius) - 2px);
  background: transparent; color: var(--text-secondary);
  font-size: var(--text-xs); font-weight: 600; cursor: pointer;
}
.theme-opt:hover { color: var(--text-primary); }
.theme-opt[aria-checked="true"] {
  background: var(--bg-card); color: var(--text-primary);
  box-shadow: 0 1px 2px rgba(0,0,0,.12);
}
.theme-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
