/* ---------------------------------------------------------------------------
 * fo-tokens.css — single source of truth for FO design tokens
 * ---------------------------------------------------------------------------
 *
 * Shipped by PR #12.e.1 (Release 5.0 patch 31) — the first
 * sub-PR of the colour-token revamp announced in PR #12.a.hotfix
 * (`inst/news.md` for Release 5.0 patch 30). This file is intentionally
 * inert at the time it ships: it only defines CSS custom properties
 * (`--fo-*`) under `:root` plus the two dark-mode selectors, and it is
 * NOT linked from `R/app_ui.R` or any page-UI module yet. Dropping it
 * into `inst/www/` therefore has zero visible effect.
 *
 * Follow-up PRs consume the tokens:
 *
 *   * PR #12.e.2 — extend the dark-mode bridge in `R/app_ui.R` so it
 *     also toggles `body.dark-mode` when `<html data-bs-theme>` flips.
 *     With both signals live, every rule below applies uniformly under
 *     the legacy bs4Dash shell AND the opt-in bslib shell.
 *   * PR #12.e.3 — replace the brittle `body:not(.dark-mode) … :not(…)
 *     :not(…) { color: #212529 !important }` chain in `custom.css`
 *     with `color: var(--fo-text)`.
 *   * PR #12.e.4 — re-skin landing / login / welcome against the
 *     `--fo-brand` / `--fo-landing-bg` tokens (replacing the half-dozen
 *     hard-coded `#007bff` / `#6aa9ff` linear-gradients in
 *     `inst/www/landing.css`).
 *   * PR #12.e.5 / #12.e.6 — Dashboard / Financial Statement / Metrics
 *     tables consume `--fo-asset` / `--fo-liability` / `--fo-networth`
 *     for section-row left borders and KPI cards.
 *   * PR #12.e.7 — chart palette + buttons / badges audit re-points
 *     `summary_page_server.R`'s hard-coded palette to
 *     `FO:::fo_chart_palette()` (the R-side mirror of these tokens).
 *
 * The tokens themselves are calibrated to the colours already used
 * across the app today (so the eventual consume-PRs are pure
 * indirection, not a re-skin):
 *
 *   * `--fo-brand`           — `#007bff` (FO blue) — buttons, sidebar
 *     accents, primary links. Matches `fo_bs_theme()` primary and the
 *     `.sidebar-dark-primary` accent in `custom.css:571`.
 *   * `--fo-brand-light`     — `#6aa9ff` — second stop of the landing
 *     hero gradient (`landing.css:86,159,284,326,398`) and the
 *     dark-mode info / link accent in `fo_bs_theme()`.
 *   * `--fo-asset`           — `#8ad6a6` (pastel green) — assets,
 *     positive deltas in dark mode. Matches the canonical pastel
 *     palette documented in `R/summary_page_server.R:526-532`.
 *   * `--fo-liability`       — `#d68aba` (pastel pink) — liabilities,
 *     negative deltas in dark mode.
 *   * `--fo-networth`        — `#8abad6` (pastel blue) — net worth,
 *     informational accents.
 *   * `--fo-accent-purple`   — `#a68ad6` (pastel purple) — fourth
 *     palette slot used by the Dashboard pie chart (4+ slices).
 *   * `--fo-asset-soft`      — `#b8e6c8` — 5th-slice asset tint.
 *   * `--fo-accent-soft`     — `#c4b8e6` — 6th-slice accent tint.
 *   * `--fo-neutral`         — `#6c757d` light / `#adb5bd` dark — the
 *     one palette token that DOES shift between themes; it doubles
 *     as muted text / zero-delta colour, so it lightens on dark
 *     surfaces for AA contrast. 7th-slice fallback on either theme.
 *
 * Surface / text / border / chrome tokens follow the same
 * already-shipped values:
 *
 *   * `--fo-surface`         — `#ffffff` light / `#1F2937` dark.
 *   * `--fo-surface-alt`     — `#f8f9fa` light / `#333333` dark.
 *   * `--fo-surface-muted`   — `#f4f6f9` light / `#0b0e14` dark
 *     (matches the existing `custom.css` sidebar-hidden background and
 *     dark-mode body background).
 *   * `--fo-text`            — `#212529` light / `#ffffff` dark.
 *   * `--fo-text-muted`      — `#6c757d` light / `#d0d4da` dark.
 *   * `--fo-text-on-brand`   — `#ffffff` in both themes — copy on
 *     `--fo-brand` / `--fo-brand-light` surfaces.
 *   * `--fo-border`          — `#dee2e6` light / `#444444` dark.
 *   * `--fo-topbar`          — `#007bff` light (matches the legacy
 *     bs4Dash navbar accent and the `fo_bs_theme()` primary) /
 *     `#1F2937` dark.
 *   * `--fo-sidebar`         — `#343a40` in both themes — the legacy
 *     bs4Dash `sidebar-dark-primary` panel background (`custom.css:583`).
 *   * `--fo-landing-bg`      — the 135deg brand → brand-light gradient
 *     used by the landing hero / CTA / divider (`landing.css:86`).
 *   * `--fo-landing-bg-horizontal` — the 90deg `#007bff → #6aa9ff`
 *     gradient (`landing.css:284`, `.landing-mockup-bar-fill`). Distinct
 *     from `--fo-landing-bg` because the angle differs (90deg vs 135deg)
 *     and any CSS that swaps the angle alone would change rendered
 *     pixels. Theme-invariant for the same reason as `--fo-landing-bg`
 *     — the landing page is always dark.
 *   * `--fo-landing-surface` — `#0b0e14` in both themes — the landing
 *     page body / nav background (`landing.css:16,24`). Landing is
 *     always dark regardless of `body.dark-mode`, so this token is
 *     pinned to the dark-on-dark value across all three blocks.
 *   * `--fo-landing-text`    — `#e6e9f2` in both themes — landing
 *     primary text colour (`landing.css:17,63,141,300,339,375,411`).
 *   * `--fo-landing-text-muted` — `#a8b0c8` in both themes — landing
 *     muted / secondary text (`landing.css:73,148,240,264,345,381,454`).
 *   * `--fo-landing-accent`  — `#6aa9ff` in both themes — landing
 *     accent colour. Distinct from `--fo-brand` (which is theme-variant
 *     and resolves to `#007bff` in light mode) because the landing
 *     page is always dark and needs the lighter blue for AA contrast.
 *   * `--fo-landing-footer-muted` — `#7a8199` in both themes — the
 *     barely-muted footer text colour on the landing page
 *     (`landing.css:465`, `.landing-footer-bottom`). One shade darker
 *     than `--fo-landing-text-muted` (`#a8b0c8`); a separate token
 *     rather than a second use of the existing muted scale so the
 *     consume-step in PR #12.e.4.5 is pure indirection.
 *   * `--fo-landing-surface-strong`, `--fo-landing-surface-overlay`,
 *     `--fo-landing-surface-tint` — three opacity variants of the
 *     `--fo-landing-surface` anchor (`#0b0e14`), shipped inert in
 *     PR #12.e.4.6.1 and consumed in PR #12.e.4.6.2:
 *       * `--fo-landing-surface-strong:  rgba(11, 14, 20, 0.95);` —
 *         landing sticky-nav blurred background (`landing.css:33`).
 *       * `--fo-landing-surface-overlay: rgba(11, 14, 20, 0.8);` —
 *         landing footer background (`landing.css:429`).
 *       * `--fo-landing-surface-tint:    rgba(11, 14, 20, 0.5);` —
 *         landing terminal-mockup body background (`landing.css:256`).
 *     Theme-invariant for the same reason as the rest of the landing
 *     palette — the landing page is always dark regardless of
 *     `body.dark-mode` / `[data-bs-theme]`. Three separate tokens
 *     rather than a single token with alpha tuning because each alpha
 *     has exactly ONE use site today (1:1 mapping) and that keeps the
 *     PR #12.e.4.6.2 consume-step pure indirection — same rhythm as
 *     `--fo-landing-bg-horizontal` / `--fo-landing-footer-muted`.
 *   * `--fo-landing-accent-faintest`, `--fo-landing-accent-faint`,
 *     `--fo-landing-accent-soft`, `--fo-landing-accent-muted`,
 *     `--fo-landing-accent-strong` — five opacity variants of the
 *     `--fo-landing-accent` anchor (`#6aa9ff`), shipped inert in
 *     PR #12.e.4.6.3 ahead of the consume-step in PR #12.e.4.6.4:
 *       * `--fo-landing-accent-faintest: rgba(106, 169, 255, 0.05);` —
 *         faint accent washes on cards / sections / mockup chrome
 *         (`landing.css:310,358,389`, plus the same alpha inside the
 *         135deg diagonal gradient at `landing.css:202`).
 *       * `--fo-landing-accent-faint:    rgba(106, 169, 255, 0.08);` —
 *         single-use slightly-stronger card wash (`landing.css:320`).
 *       * `--fo-landing-accent-soft:     rgba(106, 169, 255, 0.1);` —
 *         sticky-nav border + secondary backgrounds + footer top borders
 *         (`landing.css:35,127,194,236,430,462`).
 *       * `--fo-landing-accent-muted:    rgba(106, 169, 255, 0.2);` —
 *         medium borders on cards / mockup chrome
 *         (`landing.css:134,203,257,311,390`).
 *       * `--fo-landing-accent-strong:   rgba(106, 169, 255, 0.3);` —
 *         strong borders, box-shadow tints, and accent backgrounds
 *         (`landing.css:101,174,186,220,277`).
 *     Same theme-invariant pinning as the rest of the landing palette.
 *     Five separate tokens (rather than one with alpha tuning) because
 *     the use sites cluster naturally by alpha role — graded by visual
 *     weight (`faintest` → `strong`) so the PR #12.e.4.6.4 consume-step
 *     reviewers can audit each swap without having to read the alpha
 *     back from the token value. `rgba(...)` literals rather than
 *     `color-mix()` / `rgb(from var(--fo-landing-accent) r g b / X)`
 *     because the landing page is FO's public marketing surface — the
 *     wider browser-version reach matters more than the indirection
 *     through `--fo-landing-accent`, and the same rationale used for
 *     `--fo-landing-surface-{strong,overlay,tint}` applies. The
 *     modernization can be revisited as a sweep in PR #12.e.8
 *     (typography + WCAG verification) if desired.
 *   * `--fo-landing-bg-diagonal`, `--fo-landing-brand-strong` — the two
 *     brand-side (`#007bff`-anchored) tokens that round out the landing
 *     palette, shipped inert in PR #12.e.4.6.5 ahead of the consume-step
 *     in PR #12.e.4.6.6:
 *       * `--fo-landing-bg-diagonal: linear-gradient(135deg,
 *         rgba(106, 169, 255, 0.05) 0%, rgba(0, 123, 255, 0.05) 100%);`
 *         — compound atomic gradient mirroring `--fo-landing-bg-horizontal`.
 *         Consumed by the `.landing-mockup` background at `landing.css:202`,
 *         which PR #12.e.4.6.4 already left half-tokenised (accent stop →
 *         `var(--fo-landing-accent-faintest)`, brand stop still literal).
 *         The whole gradient expression is swapped in one go because the
 *         two stops aren't decomposable into existing tokens without
 *         retuning rendered colour — they're at the same 0.05 alpha but
 *         different anchors. Theme-invariant for the same reason as
 *         `--fo-landing-bg` / `--fo-landing-bg-horizontal` — the landing
 *         page is always dark.
 *       * `--fo-landing-brand-strong: rgba(0, 123, 255, 0.3);` —
 *         single-use brand-tint background on the active mockup-sidebar
 *         item (`landing.css:244`, `.landing-mockup-sidebar-item.active`).
 *         The name mirrors `--fo-landing-accent-strong` (same 0.3 alpha,
 *         "strong" weight) but anchored on `--fo-brand` (`#007bff`)
 *         rather than `--fo-landing-accent` (`#6aa9ff`) — the two
 *         anchors share the brand identity but resolve to different
 *         pixels, so the consume-step keeps them as separate tokens.
 *     Same theme-invariant pinning as the rest of the landing palette.
 *     The lone `color: #007bff;` hover literal at `landing.css:~423`
 *     is NOT covered by either token — it is opaque (no alpha) while
 *     both tokens here are tinted. Its inert-foundation step (shipping
 *     a `--fo-landing-brand` opaque token) is scoped to PR #12.e.4.6.7,
 *     with the consume-step in PR #12.e.4.6.8. PR #12.e.4.6.6 swept
 *     the two literals these tokens cover.
 *   * `--fo-landing-brand` — the opaque brand-side companion to
 *     `--fo-landing-accent` (`#6aa9ff`), shipped inert in PR #12.e.4.6.7
 *     ahead of the consume-step in PR #12.e.4.6.8:
 *       * `--fo-landing-brand: #007bff;` — single-use opaque hover
 *         colour on the contact-email link (`landing.css:~423`,
 *         `.landing-contact-email:hover`). Anchored on `--fo-brand`
 *         (`#007bff`) rather than `--fo-landing-accent` (`#6aa9ff`) —
 *         both anchors share the brand identity but resolve to
 *         different pixels, mirroring the
 *         `--fo-landing-brand-strong` vs `--fo-landing-accent-strong`
 *         split established in PR #12.e.4.6.5. Theme-invariant for
 *         the same reason as the rest of the landing palette: the
 *         landing page is always dark regardless of theme. Opaque (no
 *         alpha) so it is NOT covered by any of the existing tinted
 *         `--fo-landing-…` brand/accent tokens — the only remaining
 *         `#007bff`/landing-brand literal in `landing.css` after
 *         PR #12.e.4.6.6, and the last colour literal on the landing
 *         page once PR #12.e.4.6.8 consumes it.
 *
 * Conventions:
 *
 *   * Every token is defined in BOTH themes (no theme falls back to
 *     `inherit`/light values silently). This is enforced by
 *     `tests/testthat/test-fo-design-tokens.R`.
 *   * Dark mode is defined under two selectors — `body.dark-mode`
 *     (legacy bs4Dash shell, drives the existing `custom.css` rules)
 *     AND `[data-bs-theme="dark"]` (the bslib shell signal). PR #12.e.2
 *     wires the bridge so both are set in lock-step; the duplication
 *     here means a regression in the bridge can never strand the bslib
 *     shell on light-mode tokens.
 *   * No selectors style any element directly. Adding such a rule here
 *     would re-introduce the "shipped but inert" risk that broke
 *     PR #12.a — keep the consume-rules in `custom.css` / page-UI
 *     blocks, and keep this file 100% custom-property definitions.
 * ---------------------------------------------------------------------------
 */

:root {
  /* Brand */
  --fo-brand:         #007bff;
  --fo-brand-light:   #6aa9ff;

  /* Canonical chart / KPI palette */
  --fo-asset:         #8ad6a6;
  --fo-liability:     #d68aba;
  --fo-networth:      #8abad6;
  --fo-accent-purple: #a68ad6;
  --fo-asset-soft:    #b8e6c8;
  --fo-accent-soft:   #c4b8e6;
  --fo-neutral:       #6c757d;

  /* Pastel amber — completes the palette unification (Items 1 + 2)
   * announced in inst/news.md for Release 5.0 patch 63. Used for warning
   * surfaces app-wide (bslib `warning` theme, `.fo-extra-warning-card`
   * accent strips, etc.) so warning chrome reads in the same pastel
   * register as `--fo-asset` (green), `--fo-liability` (pink) and
   * `--fo-networth` (blue). Theme-invariant for the same reason as the
   * rest of the chart pastels: the hue was picked for AA contrast on
   * both light and dark surfaces. */
  --fo-amber:         #e6c88a;

  /* Surfaces (light) */
  --fo-surface:        #ffffff;
  --fo-surface-alt:    #f8f9fa;
  --fo-surface-muted:  #f4f6f9;

  /* Text (light) */
  --fo-text:           #212529;
  --fo-text-muted:     #6c757d;
  --fo-text-on-brand:  #ffffff;

  /* Borders (light) */
  --fo-border:         #dee2e6;

  /* Card border — INERT until PR #12.e.4.17 consumes it on
   * `.welcome-feature-card` / `.welcome-usage-card` in
   * `R/welcome_page_ui.R`. Light value `rgba(127, 127, 127, 0.18)`
   * matches the existing light card border literal; dark value
   * `#2a3447` matches the existing `body.dark-mode` card border
   * override. Drifts vs `--fo-border` under both themes (light
   * `#dee2e6` is fully opaque grey; dark `#444444` is warmer and
   * lighter), which is why the cards earned a dedicated token —
   * same calibre of call PR #12.e.4.13 made for `--fo-text-tagline`.
   */
  --fo-card-border:    rgba(127, 127, 127, 0.18);

  /* Welcome-hero brand-tint gradient stops. The welcome page hero
   * uses a two-stop linear-gradient anchored on `--fo-brand`
   * (`#007bff`) and `--fo-brand-light` (`#6aa9ff`) but at low alpha,
   * so the resulting tint is too faint to fold into the brand
   * tokens directly. PR #12.e.4.18 ships two dedicated per-stop
   * tokens whose values match the existing welcome-hero literals
   * byte for byte under both themes — same calibre of two-step
   * slice as PR #12.e.4.17 (`--fo-card-border`).
   *
   *   * `--fo-brand-tint-strong` — first stop, anchored on
   *     `--fo-brand`. Light `rgba(0, 123, 255, 0.10)` matches
   *     `R/welcome_page_ui.R` `.welcome-hero` light gradient; dark
   *     `rgba(0, 123, 255, 0.16)` matches the
   *     `body.dark-mode .welcome-hero` override.
   *   * `--fo-brand-tint-soft` — second stop, anchored on
   *     `--fo-brand-light`. Light `rgba(106, 169, 255, 0.06)` /
   *     dark `rgba(106, 169, 255, 0.08)`.
   *
   * "Strong" / "soft" refer to the alpha intensity within a theme,
   * not a theme name — both tokens flip under `body.dark-mode` and
   * `[data-bs-theme="dark"]` to their darker, more opaque values
   * exactly as the pre-PR override rule used to override them.
   */
  --fo-brand-tint-strong: rgba(0, 123, 255, 0.10);
  --fo-brand-tint-soft:   rgba(106, 169, 255, 0.06);

  /* Welcome-hero border token (PR #12.e.4.19, paired ship+consume).
   * Replaces the two welcome-hero border literals at
   * `R/welcome_page_ui.R` — light `rgba(127, 127, 127, 0.18)` (the
   * same neutral grey the cards used before they earned
   * `--fo-card-border`) paired with dark `rgba(255, 255, 255, 0.06)`
   * (a faint white wash, distinct from `--fo-card-border`'s dark
   * `#2a3447`). The different dark value vs the cards is precisely
   * why the hero earned its own token rather than folding into
   * `--fo-card-border` — same calibre of dedicated-token call as
   * PR #12.e.4.17 (`--fo-card-border`) and PR #12.e.4.13
   * (`--fo-text-tagline`). */
  --fo-hero-border:    rgba(127, 127, 127, 0.18);

  /* Heading text token (PR #12.e.4.19, paired ship+consume).
   * Replaces the three `#212529` light + `#f5f7fa` dark pairs on
   * `.welcome-section-title` / `.welcome-feature-title` /
   * `.welcome-usage-card li strong` in `R/welcome_page_ui.R`. The
   * light value `#212529` happens to match `--fo-text` light
   * exactly, but the dark value `#f5f7fa` drifts vs `--fo-text` dark
   * `#ffffff` (a softer off-white tuned for heading weight on dark
   * cards), so the headings earn a dedicated token rather than
   * consuming `--fo-text` with drift — same conservative call as
   * `--fo-text-tagline` / `--fo-text-body`. A future PR may revisit
   * whether to collapse the light side into `--fo-text`. */
  --fo-text-heading:   #212529;

  /* Chrome (light) */
  --fo-topbar:         #007bff;
  --fo-sidebar:        #343a40;

  /* Landing hero gradient (theme-invariant, brand-coloured) */
  --fo-landing-bg:     linear-gradient(135deg, #007bff 0%, #6aa9ff 100%);

  /* Landing hero gradient — 90deg variant (theme-invariant). Inert
   * until PR #12.e.4.5 swaps the literal at `landing.css:284`. */
  --fo-landing-bg-horizontal: linear-gradient(90deg, #007bff 0%, #6aa9ff 100%);

  /* Landing palette — theme-invariant. The landing page is always
   * dark regardless of `body.dark-mode`, so these values are pinned
   * to the dark-on-dark colours the page has always shipped. Same
   * byte-identical-across-blocks rhythm as `--fo-landing-bg`. */
  --fo-landing-surface:    #0b0e14;
  --fo-landing-text:       #e6e9f2;
  --fo-landing-text-muted: #a8b0c8;
  --fo-landing-accent:     #6aa9ff;

  /* Landing footer micro-token — theme-invariant (`landing.css:465`,
   * `.landing-footer-bottom`). Inert until PR #12.e.4.5. */
  --fo-landing-footer-muted: #7a8199;

  /* Landing surface opacity variants (theme-invariant) — three
   * single-use alphas of `--fo-landing-surface` (`#0b0e14`). Inert
   * until PR #12.e.4.6.2 swaps the matching literals in `landing.css`.
   * Each token maps 1:1 to one site so the consume-step is pure
   * indirection — same rhythm as `--fo-landing-bg-horizontal` /
   * `--fo-landing-footer-muted`. */
  --fo-landing-surface-strong:  rgba(11, 14, 20, 0.95);
  --fo-landing-surface-overlay: rgba(11, 14, 20, 0.8);
  --fo-landing-surface-tint:    rgba(11, 14, 20, 0.5);

  /* Landing accent opacity variants (theme-invariant) — five
   * usage-graded alphas of `--fo-landing-accent` (`#6aa9ff`). Inert
   * until PR #12.e.4.6.4 swaps the matching literals in `landing.css`.
   * Names are graded by visual weight (`faintest` → `strong`) so the
   * consume-step is pure indirection — same rhythm as
   * `--fo-landing-surface-{strong,overlay,tint}`. `rgba(...)` literals
   * rather than `color-mix()` because the landing page is FO's public
   * marketing surface (wider browser-version reach). */
  --fo-landing-accent-faintest: rgba(106, 169, 255, 0.05);
  --fo-landing-accent-faint:    rgba(106, 169, 255, 0.08);
  --fo-landing-accent-soft:     rgba(106, 169, 255, 0.1);
  --fo-landing-accent-muted:    rgba(106, 169, 255, 0.2);
  --fo-landing-accent-strong:   rgba(106, 169, 255, 0.3);

  /* Landing brand-side tokens (theme-invariant) — round out the
   * landing palette by tokenising the two remaining `#007bff`-anchored
   * use sites in `landing.css`. Inert until PR #12.e.4.6.6 swaps the
   * matching literals. `--fo-landing-bg-diagonal` is a compound atomic
   * gradient mirroring `--fo-landing-bg-horizontal` (two stops swapped
   * in one go); `--fo-landing-brand-strong` mirrors
   * `--fo-landing-accent-strong` (same 0.3 alpha, "strong" weight) but
   * anchored on `--fo-brand` (`#007bff`) rather than
   * `--fo-landing-accent` (`#6aa9ff`). The opaque
   * `--fo-landing-brand` companion below mirrors `--fo-landing-accent`
   * (also opaque, `#6aa9ff`) — shipped inert in PR #12.e.4.6.7 ahead
   * of the consume-step in PR #12.e.4.6.8, which sweeps the lone
   * `color: #007bff;` hover at `landing.css:~423`
   * (`.landing-contact-email:hover`). */
  --fo-landing-bg-diagonal:   linear-gradient(135deg, rgba(106, 169, 255, 0.05) 0%, rgba(0, 123, 255, 0.05) 100%);
  --fo-landing-brand-strong:  rgba(0, 123, 255, 0.3);
  --fo-landing-brand:         #007bff;

  /* Danger / note-box tokens (PR #12.e.4.9, inert; consumed in #12.e.4.10
   * to replace the four `.welcome-note-box` literals at
   * `R/welcome_page_ui.R:156-163`). `--fo-danger` is theme-invariant
   * (`#EF4444` in both modes — the existing dark-mode rule only
   * re-tints the surface, not the red itself). `--fo-danger-surface`
   * IS theme-variant (light `0.06` alpha, dark `0.10` alpha) so the
   * note-box stays legible on both surfaces — same usage-graded alpha
   * rhythm as `--fo-landing-accent-{faintest,faint,soft,muted,strong}`. */
  --fo-danger:         #EF4444;
  --fo-danger-surface: rgba(239, 68, 68, 0.06);

  /* Body-text token (PR #12.e.4.11, inert; the paired consume slice
   * PR #12.e.4.12 sweeps the eight body-text literals in
   * `R/welcome_page_ui.R` — `#495057` × 4 on light surfaces
   * (L75 tagline-intro, L118 feature-body, L148 usage-card li,
   * L169 note-box li) and `#D1D5DB` × 4 on dark surfaces (L77, L120,
   * L150, L171). `--fo-text-body` is the "softer than `--fo-text`,
   * darker than `--fo-text-muted`" tier — the standard body-copy
   * colour on cards / paragraphs sitting on `--fo-surface`. Sits
   * between `--fo-text` (`#212529` / `#ffffff`) and `--fo-text-muted`
   * (`#6c757d` / `#d0d4da`). */
  --fo-text-body:      #495057;

  /* Tagline token (PR #12.e.4.13, inert; the paired consume slice
   * PR #12.e.4.14 sweeps the two welcome-tagline literals in
   * `R/welcome_page_ui.R` — `#6c757d` at L67 on the light surface
   * and `#9CA3AF` at L71 on the dark surface). Light value happens
   * to match `--fo-text-muted` (`#6c757d`), but dark `#9CA3AF` drifts
   * meaningfully vs `--fo-text-muted` dark `#d0d4da` (~30 RGB units
   * per channel, a noticeably cooler / darker grey), so the tagline
   * gets a dedicated token rather than consuming `--fo-text-muted`
   * with drift — same conservative call the PR #12.e.4.11 body-text
   * token made vs `--fo-text` / `--fo-text-muted`. */
  --fo-text-tagline:   #6c757d;

  /* Pre-login helper-class tokens (PR #12.e.4.21, paired ship +
   * consume). Replace the three hard-coded helper-class literals in
   * `inst/www/custom.css:924,951,959` — `.fo-back-link { color:
   * #007bff; }`, `.fo-error-msg { color: red; }`,
   * `.fo-success-msg { color: green; }`. These three rules sit in the
   * pre-login surface (login / signup / forgot-password / reset-password
   * cards in `R/failed_login_page.R`, `R/forgot_password_page.R`,
   * `R/password_reset_page.R`) and were the last colour literals
   * flagged by the PR #12.e.4.20 audit on the pre-login bslib shell.
   *
   *   * `--fo-link` — the "Back to Home" / "Forgot Username/Password?"
   *     link colour. Light value `#007bff` matches `--fo-brand` light
   *     and the pre-PR `.fo-back-link` literal byte for byte. Dark
   *     value `#6aa9ff` matches `--fo-brand-light` light (the standard
   *     dark-mode link/brand accent — same value `--fo-brand` flips
   *     to under `body.dark-mode`), so links keep AA contrast on
   *     dark surfaces. Drift vs `--fo-brand` under dark mode is
   *     intentional: under dark mode `--fo-brand` also resolves to
   *     `#6aa9ff`, so `--fo-link` could fold into `--fo-brand` in a
   *     future sweep. Same conservative dedicated-token call as
   *     `--fo-text-tagline` / `--fo-text-body`.
   *   * `--fo-success` — green confirmation-message colour (the
   *     dark counterpart of `--fo-danger`). Light value `#008000`
   *     matches the CSS named colour `green` byte for byte (the
   *     pre-PR `.fo-success-msg` literal). Dark value `#8ad6a6`
   *     matches `--fo-asset` (the pastel green used for positive
   *     deltas / asset rows on dark surfaces), so success messages
   *     read as the same pastel green the dashboard uses. Drift
   *     light/dark is acknowledged and mirrors the alpha-graded
   *     drift `--fo-danger-surface` carries. */
  --fo-link:           #007bff;
  --fo-success:        #008000;
}

/* Dark theme — legacy bs4Dash shell (body.dark-mode is toggled by the
 * existing #fo_dark_mode_btn JS in `R/app_ui.R`'s body_inner). */
body.dark-mode {
  /* Brand — accents shift to the lighter brand stop in dark mode so
   * blue links and primary buttons keep AA contrast on dark surfaces. */
  --fo-brand:         #6aa9ff;
  --fo-brand-light:   #007bff;

  /* The six chart pastels are theme-invariant — they were picked
   * for dark mode in the first place and remain readable on light
   * surfaces, so the Dashboard pie / Statement deltas / Metrics
   * sparklines render byte-identically under both themes. */
  --fo-asset:         #8ad6a6;
  --fo-liability:     #d68aba;
  --fo-networth:      #8abad6;
  --fo-accent-purple: #a68ad6;
  --fo-asset-soft:    #b8e6c8;
  --fo-accent-soft:   #c4b8e6;

  /* Pastel amber — theme-invariant, same value as `:root`. */
  --fo-amber:         #e6c88a;

  /* `--fo-neutral` is the only palette token that DOES shift between
   * themes: it doubles as muted text / zero-delta colour, so it
   * lightens to `#adb5bd` on dark surfaces for AA contrast. */
  --fo-neutral:       #adb5bd;

  /* Surfaces (dark) */
  --fo-surface:        #1F2937;
  --fo-surface-alt:    #333333;
  --fo-surface-muted:  #0b0e14;

  /* Text (dark) */
  --fo-text:           #ffffff;
  --fo-text-muted:     #d0d4da;
  --fo-text-on-brand:  #ffffff;

  /* Borders (dark) */
  --fo-border:         #444444;

  /* Card border — see `:root` for the inert-ship rationale.
   * Dark value `#2a3447` matches the existing welcome-card
   * `body.dark-mode` border override. */
  --fo-card-border:    #2a3447;

  /* Welcome-hero brand-tint gradient stops — see `:root` for the
   * inert-ship rationale. Dark values match the pre-PR
   * `body.dark-mode .welcome-hero` gradient stops byte for byte. */
  --fo-brand-tint-strong: rgba(0, 123, 255, 0.16);
  --fo-brand-tint-soft:   rgba(106, 169, 255, 0.08);

  /* Welcome-hero border — see `:root` for the inert+consume rationale.
   * Dark value `rgba(255, 255, 255, 0.06)` matches the pre-PR
   * `body.dark-mode .welcome-hero` border-color override byte for
   * byte. Distinct from `--fo-card-border` dark `#2a3447`. */
  --fo-hero-border:    rgba(255, 255, 255, 0.06);

  /* Heading text — see `:root` for the inert+consume rationale.
   * Dark value `#f5f7fa` matches the pre-PR
   * `body.dark-mode .welcome-{section,feature}-title` /
   * `.welcome-usage-card li strong` overrides byte for byte. Drift
   * vs `--fo-text` dark (`#ffffff`) is what motivated the dedicated
   * token. */
  --fo-text-heading:   #f5f7fa;

  /* Chrome (dark) */
  --fo-topbar:         #1F2937;
  --fo-sidebar:        #343a40;

  /* Landing hero gradient stays brand-coloured in both themes. */
  --fo-landing-bg:     linear-gradient(135deg, #007bff 0%, #6aa9ff 100%);
  --fo-landing-bg-horizontal: linear-gradient(90deg, #007bff 0%, #6aa9ff 100%);

  /* Landing palette — same theme-invariant values as :root. */
  --fo-landing-surface:    #0b0e14;
  --fo-landing-text:       #e6e9f2;
  --fo-landing-text-muted: #a8b0c8;
  --fo-landing-accent:     #6aa9ff;
  --fo-landing-footer-muted: #7a8199;
  --fo-landing-surface-strong:  rgba(11, 14, 20, 0.95);
  --fo-landing-surface-overlay: rgba(11, 14, 20, 0.8);
  --fo-landing-surface-tint:    rgba(11, 14, 20, 0.5);
  --fo-landing-accent-faintest: rgba(106, 169, 255, 0.05);
  --fo-landing-accent-faint:    rgba(106, 169, 255, 0.08);
  --fo-landing-accent-soft:     rgba(106, 169, 255, 0.1);
  --fo-landing-accent-muted:    rgba(106, 169, 255, 0.2);
  --fo-landing-accent-strong:   rgba(106, 169, 255, 0.3);
  --fo-landing-bg-diagonal:   linear-gradient(135deg, rgba(106, 169, 255, 0.05) 0%, rgba(0, 123, 255, 0.05) 100%);
  --fo-landing-brand-strong:  rgba(0, 123, 255, 0.3);
  --fo-landing-brand:         #007bff;

  /* Danger / note-box tokens — `--fo-danger` is theme-invariant; the
   * surface alpha shifts to 0.10 in dark mode to compensate for the
   * darker substrate. See `:root` for the inert-ship rationale. */
  --fo-danger:         #EF4444;
  --fo-danger-surface: rgba(239, 68, 68, 0.10);

  /* Body-text token — see `:root` for the inert-ship rationale.
   * `--fo-text-body` lightens to `#D1D5DB` on dark surfaces (the dark
   * counterpart paired with the light `#495057` at the eight
   * `R/welcome_page_ui.R` body-text literals). */
  --fo-text-body:      #D1D5DB;

  /* Tagline token — see `:root` for the inert-ship rationale.
   * Dark value `#9CA3AF` is the welcome-tagline literal at
   * `R/welcome_page_ui.R:71`. Drift vs `--fo-text-muted` dark
   * (`#d0d4da`) is what motivated the dedicated token. */
  --fo-text-tagline:   #9CA3AF;

  /* Pre-login helper-class tokens — see `:root` for the paired
   * ship+consume rationale. Dark `--fo-link` `#6aa9ff` matches the
   * standard brand-light accent used for links / primary buttons
   * across dark mode. Dark `--fo-success` `#8ad6a6` matches
   * `--fo-asset` (the pastel green used for positive deltas / asset
   * rows on dark surfaces). */
  --fo-link:           #6aa9ff;
  --fo-success:        #8ad6a6;
}

/* Dark theme — bslib opt-in shell (Bootstrap 5 toggles
 * <html data-bs-theme="dark">). Kept in lock-step with the
 * body.dark-mode block above so PR #12.e.2's bridge can fail open and
 * the bslib branch still gets the right tokens. */
[data-bs-theme="dark"] {
  --fo-brand:         #6aa9ff;
  --fo-brand-light:   #007bff;

  --fo-asset:         #8ad6a6;
  --fo-liability:     #d68aba;
  --fo-networth:      #8abad6;
  --fo-accent-purple: #a68ad6;
  --fo-asset-soft:    #b8e6c8;
  --fo-accent-soft:   #c4b8e6;
  --fo-neutral:       #adb5bd;

  /* Pastel amber — theme-invariant, same value as `:root`. */
  --fo-amber:         #e6c88a;

  --fo-surface:        #1F2937;
  --fo-surface-alt:    #333333;
  --fo-surface-muted:  #0b0e14;

  --fo-text:           #ffffff;
  --fo-text-muted:     #d0d4da;
  --fo-text-on-brand:  #ffffff;

  --fo-border:         #444444;

  /* Card border — see `:root` for the inert-ship rationale. Same
   * value as `body.dark-mode` so the bslib branch lands on the same
   * `#2a3447` dark card border. */
  --fo-card-border:    #2a3447;

  /* Welcome-hero brand-tint gradient stops — see `:root` for the
   * inert-ship rationale. Same values as `body.dark-mode` so the
   * bslib branch lands on the same gradient stops. */
  --fo-brand-tint-strong: rgba(0, 123, 255, 0.16);
  --fo-brand-tint-soft:   rgba(106, 169, 255, 0.08);

  /* Welcome-hero border — see `:root` for the inert+consume rationale.
   * Same value as `body.dark-mode` so the bslib branch lands on the
   * same `rgba(255, 255, 255, 0.06)` dark hero border. */
  --fo-hero-border:    rgba(255, 255, 255, 0.06);

  /* Heading text — see `:root` for the inert+consume rationale.
   * Same value as `body.dark-mode` so the bslib branch lands on the
   * same `#f5f7fa` dark heading colour. */
  --fo-text-heading:   #f5f7fa;

  --fo-topbar:         #1F2937;
  --fo-sidebar:        #343a40;

  --fo-landing-bg:     linear-gradient(135deg, #007bff 0%, #6aa9ff 100%);
  --fo-landing-bg-horizontal: linear-gradient(90deg, #007bff 0%, #6aa9ff 100%);

  /* Landing palette — same theme-invariant values as :root. */
  --fo-landing-surface:    #0b0e14;
  --fo-landing-text:       #e6e9f2;
  --fo-landing-text-muted: #a8b0c8;
  --fo-landing-accent:     #6aa9ff;
  --fo-landing-footer-muted: #7a8199;
  --fo-landing-surface-strong:  rgba(11, 14, 20, 0.95);
  --fo-landing-surface-overlay: rgba(11, 14, 20, 0.8);
  --fo-landing-surface-tint:    rgba(11, 14, 20, 0.5);
  --fo-landing-accent-faintest: rgba(106, 169, 255, 0.05);
  --fo-landing-accent-faint:    rgba(106, 169, 255, 0.08);
  --fo-landing-accent-soft:     rgba(106, 169, 255, 0.1);
  --fo-landing-accent-muted:    rgba(106, 169, 255, 0.2);
  --fo-landing-accent-strong:   rgba(106, 169, 255, 0.3);
  --fo-landing-bg-diagonal:   linear-gradient(135deg, rgba(106, 169, 255, 0.05) 0%, rgba(0, 123, 255, 0.05) 100%);
  --fo-landing-brand-strong:  rgba(0, 123, 255, 0.3);
  --fo-landing-brand:         #007bff;

  /* Danger / note-box tokens — see `:root` for the inert-ship
   * rationale. Same values as `body.dark-mode` so the bslib branch
   * lands on the darker 0.10 alpha. */
  --fo-danger:         #EF4444;
  --fo-danger-surface: rgba(239, 68, 68, 0.10);

  /* Body-text token — see `:root` for the inert-ship rationale. Same
   * value as `body.dark-mode` so the bslib branch lands on the same
   * `#D1D5DB` dark body-text colour. */
  --fo-text-body:      #D1D5DB;

  /* Tagline token — see `:root` for the inert-ship rationale. Same
   * value as `body.dark-mode` so the bslib branch lands on the same
   * `#9CA3AF` dark tagline colour. */
  --fo-text-tagline:   #9CA3AF;

  /* Pre-login helper-class tokens — see `:root` for the paired
   * ship+consume rationale. Same values as `body.dark-mode` so the
   * bslib branch lands on the same dark link/success colours. */
  --fo-link:           #6aa9ff;
  --fo-success:        #8ad6a6;
}