/* =====================================================================
   Bespoke Woodcraft Studio — project-wide theme system
   10 themes (5 light, 5 dark). Each theme sets a CANONICAL palette; the
   alias layer at the bottom maps that palette onto BOTH token vocabularies
   used across the suite (cream/walnut/gold AND surface/accent/text), so a
   single theme recolours every page.
   `:root[data-theme]` (0,2,0) outranks each page's own `:root` (0,1,0),
   so themes win without touching per-page CSS.
   Soft/fill tokens (accent-soft, ok-soft, danger-soft, warn-soft) stay LIGHT
   in every theme — they're light chips/notices paired with dark text.
   ===================================================================== */
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400;1,600&display=swap');

/* ONE font, site-wide (founder mandate 2026-06-26; grand-master pick = Inter). Codified here so
   every page that loads theme.css inherits it — no per-page font edits. `:root body` (0,1,1)
   outranks each page's own `body{}` (0,0,1) regardless of source order or whether a theme is
   active, and `:root .serif` (0,2,0) repoints every old Playfair display heading to Inter at a
   heavy weight, so hierarchy comes from WEIGHT within one family (Refactoring UI), not a 2nd font.
   Inter is drawn for screens (tall x-height, open apertures, tabular figures for the dimension
   tools). The Playfair <link> is removed from the few pages that still load it. */
:root{
  --font:"Inter","Inter var",-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,"Helvetica Neue",Arial,sans-serif;
  --font-num:"Inter",system-ui,sans-serif;
}
:root body{ font-family:var(--font); }
:root .serif{ font-family:var(--font); font-weight:800; letter-spacing:-.015em; }
:root .num,:root [data-num]{ font-variant-numeric:tabular-nums lining-nums; }

/* ---------- LIGHT ---------- */
:root[data-theme="studio-cream"]{
  --bg:#f4ede2; --surface:#fffdf8; --surface-2:#efe6d7; --surface-hi:#fff5e0;
  --text:#2a1f12; --text-muted:#6f5638; --text-faint:#7c6243;
  --line:#e7d9c2; --border:#ddcba9;
  --accent:#d4a056; --accent-deep:#a9762a; --accent-soft:#f0c98a;
  --header-bg:#2c1f12; --header-bg-2:#3e2c19; --header-bg-3:#1c130a; --header-line:#4a3520; --header-text:#f4ede2;
  --ok:#5b8a5b; --ok-soft:#e9f0e3; --warn:#b5862f; --warn-soft:#fbf0d6; --danger:#b0503c; --danger-soft:#f6e2dc;
  /* P85-3: dashboard identity hues (Dev Dashboard "who/model" chips) get their OWN tokens
     instead of reusing --danger (Fable) / --accent-deep (Opus). Values match those tokens
     for now (zero visual change), but the two are no longer coupled — retuning --danger for
     an error state can never shift Fable's identity color, and vice versa. */
  --identity-fable:#b0503c; --identity-opus:#a9762a;
  --shadow-c:0 1px 2px rgba(44,31,18,.06),0 8px 24px rgba(44,31,18,.08);
  --shadow-lg-c:0 2px 6px rgba(44,31,18,.10),0 18px 44px rgba(44,31,18,.16);
}
:root[data-theme="maple"]{
  --bg:#faf6ef; --surface:#ffffff; --surface-2:#f4ece0; --surface-hi:#fbf1dd;
  --text:#36302a; --text-muted:#6b6258; --text-faint:#7a7064;
  --line:#ece3d5; --border:#ddd2c0;
  --accent:#c98a3a; --accent-deep:#8f6120; --accent-soft:#f0d6a8;
  --header-bg:#4a3b2a; --header-bg-2:#5a4733; --header-bg-3:#3a2e20; --header-line:#6a5640; --header-text:#faf6ef;
  --ok:#5e8b57; --ok-soft:#e9f1e2; --warn:#bf8a2c; --warn-soft:#fbf0d6; --danger:#b35140; --danger-soft:#f6e3dd;
  --identity-fable:#b35140; --identity-opus:#8f6120;
  --shadow-c:0 1px 2px rgba(54,40,26,.05),0 8px 24px rgba(54,40,26,.07);
  --shadow-lg-c:0 2px 6px rgba(54,40,26,.09),0 18px 44px rgba(54,40,26,.14);
}
:root[data-theme="birch-sage"]{
  --bg:#f3f4ef; --surface:#ffffff; --surface-2:#e9ece2; --surface-hi:#eef2e6;
  --text:#2b302a; --text-muted:#5f6a5a; --text-faint:#626b5f;
  --line:#e0e4d8; --border:#cdd4c2;
  --accent:#7d9b5b; --accent-deep:#577839; --accent-soft:#d8e4c2;
  --header-bg:#313a2e; --header-bg-2:#3d4838; --header-bg-3:#262e24; --header-line:#4a5641; --header-text:#f3f4ef;
  --ok:#5d8a52; --ok-soft:#e7f0e0; --warn:#b88a2e; --warn-soft:#f7f0da; --danger:#b05040; --danger-soft:#f1e0db;
  --identity-fable:#b05040; --identity-opus:#577839;
  --shadow-c:0 1px 2px rgba(40,46,38,.05),0 8px 24px rgba(40,46,38,.08);
  --shadow-lg-c:0 2px 6px rgba(40,46,38,.09),0 18px 44px rgba(40,46,38,.14);
}
:root[data-theme="linen-ink"]{
  --bg:#f5f3ee; --surface:#ffffff; --surface-2:#ebe8e0; --surface-hi:#f0ede4;
  --text:#20242b; --text-muted:#565d66; --text-faint:#616871;
  --line:#e3e0d8; --border:#d2cfc5;
  --accent:#5c7c93; --accent-deep:#3f5a6b; --accent-soft:#cfe0eb;
  --header-bg:#20242b; --header-bg-2:#2c323b; --header-bg-3:#171a20; --header-line:#3a414b; --header-text:#f5f3ee;
  --ok:#5a8a6a; --ok-soft:#e4efe8; --warn:#b5862f; --warn-soft:#f7f0da; --danger:#b1503f; --danger-soft:#f3e1dc;
  --identity-fable:#b1503f; --identity-opus:#3f5a6b;
  --shadow-c:0 1px 2px rgba(32,36,43,.06),0 8px 24px rgba(32,36,43,.08);
  --shadow-lg-c:0 2px 6px rgba(32,36,43,.10),0 18px 44px rgba(32,36,43,.15);
}
:root[data-theme="porcelain-blue"]{
  --bg:#eef3f7; --surface:#ffffff; --surface-2:#e2eaf1; --surface-hi:#e8f1f8;
  --text:#23303a; --text-muted:#536571; --text-faint:#5b697c;
  --line:#dbe5ec; --border:#c6d3de;
  --accent:#3f7ba8; --accent-deep:#2c5b82; --accent-soft:#cce0f0;
  --header-bg:#23303a; --header-bg-2:#2f3f4b; --header-bg-3:#19232c; --header-line:#3b4d59; --header-text:#eef3f7;
  --ok:#4f8a6a; --ok-soft:#e2efe8; --warn:#b5862f; --warn-soft:#f7f0da; --danger:#b1503f; --danger-soft:#f3e1dc;
  --identity-fable:#b1503f; --identity-opus:#2c5b82;
  --shadow-c:0 1px 2px rgba(35,48,58,.07),0 8px 24px rgba(35,48,58,.09);
  --shadow-lg-c:0 2px 6px rgba(35,48,58,.11),0 18px 44px rgba(35,48,58,.16);
}

/* ---------- DARK ---------- (surfaces+text flip; soft/fill stay light) */
:root[data-theme="walnut-night"]{
  --bg:#1b130b; --surface:#261a0f; --surface-2:#2f2114; --surface-hi:#38281a;
  --text:#f0e6d6; --text-muted:#c4ad90; --text-faint:#9a866c;
  --line:#3a2a1a; --border:#4a3520;
  --accent:#d4a056; --accent-deep:#e6bd7c; --accent-soft:#f0c98a;
  --header-bg:#130c05; --header-bg-2:#221710; --header-bg-3:#0d0703; --header-line:#3a2a1a; --header-text:#f0e6d6;
  --ok:#7faf6f; --ok-soft:#e9f0e3; --warn:#d8a657; --warn-soft:#fbf0d6; --danger:#d77a64; --danger-soft:#f6e2dc;
  --identity-fable:#d77a64; --identity-opus:#e6bd7c;
  --shadow-c:0 1px 2px rgba(0,0,0,.3),0 8px 24px rgba(0,0,0,.4);
  --shadow-lg-c:0 2px 6px rgba(0,0,0,.4),0 18px 44px rgba(0,0,0,.55);
}
:root[data-theme="graphite"]{
  --bg:#17191c; --surface:#202327; --surface-2:#2a2e33; --surface-hi:#33383e;
  --text:#e9eaec; --text-muted:#a9b0b8; --text-faint:#91979e;
  --line:#2e3338; --border:#3c424a;
  --accent:#d8a64a; --accent-deep:#ecc06a; --accent-soft:#f0d29a;
  --header-bg:#101214; --header-bg-2:#1d2024; --header-bg-3:#0a0c0e; --header-line:#2e3338; --header-text:#e9eaec;
  --ok:#6fae74; --ok-soft:#e6f0e2; --warn:#d8a657; --warn-soft:#f7eed5; --danger:#d77a64; --danger-soft:#f3e0db;
  --identity-fable:#d77a64; --identity-opus:#ecc06a;
  --shadow-c:0 1px 2px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.45);
  --shadow-lg-c:0 2px 6px rgba(0,0,0,.45),0 18px 44px rgba(0,0,0,.6);
}
:root[data-theme="espresso"]{
  --bg:#1e1611; --surface:#2a201a; --surface-2:#352a22; --surface-hi:#40332a;
  --text:#f1e9df; --text-muted:#c3b3a2; --text-faint:#988675;
  --line:#3a2e26; --border:#4c3d32;
  --accent:#d98c4e; --accent-deep:#ecaa72; --accent-soft:#f0cfa6;
  --header-bg:#160f0b; --header-bg-2:#251a14; --header-bg-3:#100a07; --header-line:#3a2e26; --header-text:#f1e9df;
  --ok:#7faf6f; --ok-soft:#e9f0e3; --warn:#d8a657; --warn-soft:#fbf0d6; --danger:#dd836b; --danger-soft:#f6e2dc;
  --identity-fable:#dd836b; --identity-opus:#ecaa72;
  --shadow-c:0 1px 2px rgba(0,0,0,.32),0 8px 24px rgba(0,0,0,.42);
  --shadow-lg-c:0 2px 6px rgba(0,0,0,.42),0 18px 44px rgba(0,0,0,.58);
}
:root[data-theme="midnight-oak"]{
  --bg:#11161c; --surface:#1a212a; --surface-2:#232c37; --surface-hi:#2c3744;
  --text:#e6edf3; --text-muted:#a3b0bd; --text-faint:#8994a1;
  --line:#283340; --border:#374453;
  --accent:#4aa3c0; --accent-deep:#74c4dd; --accent-soft:#bfe0ec;
  --header-bg:#0b1016; --header-bg-2:#161e27; --header-bg-3:#070b0f; --header-line:#283340; --header-text:#e6edf3;
  --ok:#5fb08a; --ok-soft:#e2efe8; --warn:#d8a657; --warn-soft:#f7eed5; --danger:#dd7a6a; --danger-soft:#f3e0db;
  --identity-fable:#dd7a6a; --identity-opus:#74c4dd;
  --shadow-c:0 1px 2px rgba(0,0,0,.38),0 8px 24px rgba(0,0,0,.5);
  --shadow-lg-c:0 2px 6px rgba(0,0,0,.5),0 18px 44px rgba(0,0,0,.62);
}
:root[data-theme="forest-dark"]{
  --bg:#121a14; --surface:#1a241c; --surface-2:#233026; --surface-hi:#2c3c30;
  --text:#e8efe6; --text-muted:#a6b6a4; --text-faint:#8b9a8b;
  --line:#28362b; --border:#374a3a;
  --accent:#7fb567; --accent-deep:#9fd086; --accent-soft:#d3e6c2;
  --header-bg:#0c130d; --header-bg-2:#17211a; --header-bg-3:#070c08; --header-line:#28362b; --header-text:#e8efe6;
  --ok:#7fb567; --ok-soft:#e7f0e0; --warn:#d2a857; --warn-soft:#f7eed5; --danger:#d77a64; --danger-soft:#f3e0db;
  --identity-fable:#d77a64; --identity-opus:#9fd086;
  --shadow-c:0 1px 2px rgba(0,0,0,.36),0 8px 24px rgba(0,0,0,.46);
  --shadow-lg-c:0 2px 6px rgba(0,0,0,.46),0 18px 44px rgba(0,0,0,.6);
}

/* ---------- NATIVE FIRST-VISIT FALLBACK ----------
   theme.js applies NO data-theme until the user picks one (theme.js:24,26,36), so
   on the first paint the :root[data-theme] palette + the alias layer below would be
   inert. This block gives EVERY page that loads theme.css a correct, consistent
   studio-cream palette out of the box — no transparent fills, missing borders, or
   invisible selected-states — and is the single source of the un-themed baseline.
   The alias layer (below) and the studio-cream --accent-text override both list
   :root:not([data-theme]) too, so the full token vocabulary resolves on first visit.
   Specificity (0,2,0) equals :root[data-theme]; when a theme IS chosen the :not()
   stops matching, so the chosen theme always wins. A page's OWN
   :root:not([data-theme]) block is later in source order and still wins over this.
   Values mirror the studio-cream theme exactly. */
:root:not([data-theme]){
  --bg:#f4ede2; --surface:#fffdf8; --surface-2:#efe6d7; --surface-hi:#fff5e0;
  --text:#2a1f12; --text-muted:#6f5638; --text-faint:#7c6243;
  --line:#e7d9c2; --border:#ddcba9;
  --accent:#d4a056; --accent-deep:#a9762a; --accent-soft:#f0c98a;
  --header-bg:#2c1f12; --header-bg-2:#3e2c19; --header-bg-3:#1c130a; --header-line:#4a3520; --header-text:#f4ede2;
  --ok:#5b8a5b; --ok-soft:#e9f0e3; --warn:#b5862f; --warn-soft:#fbf0d6; --danger:#b0503c; --danger-soft:#f6e2dc;
  --identity-fable:#b0503c; --identity-opus:#a9762a;
  --shadow-c:0 1px 2px rgba(44,31,18,.06),0 8px 24px rgba(44,31,18,.08);
  --shadow-lg-c:0 2px 6px rgba(44,31,18,.10),0 18px 44px rgba(44,31,18,.16);
}

/* ---------- ALIAS LAYER ----------
   Maps the canonical palette onto every legacy token name used across the
   suite. Applies to ANY themed page AND the un-themed first-visit fallback above;
   resolves to the active theme's values (or studio-cream when no theme is set). */
:root[data-theme], :root:not([data-theme]){
  /* surfaces / backgrounds */
  --cream:var(--bg); --paper:var(--bg); --cream-card:var(--surface);
  --cream-2:var(--surface-2); --surface2:var(--surface-2); --cream-hi:var(--surface-hi); --highlight:var(--surface-hi);
  /* text */
  --ink:var(--text); --ink-soft:var(--text-muted); --muted:var(--text-muted);
  --ink-faint:var(--text-faint); --faint:var(--text-faint);
  /* lines / borders */
  --cream-line:var(--line); --cream-line-2:var(--border);
  /* accent (gold / electric) */
  --gold:var(--accent); --gold-deep:var(--accent-deep); --gold-soft:var(--accent-soft);
  /* grouping-chip hue — visually distinct violet for .tag.cab (distinct from the accent/blue family) */
  --cab:#6f5bd0;
  /* SPRINT HUES (FD72, founder 2026-08-05: "I want you to color-code the Sprints to make it
     easier for me to catch them and follow them").

     Twelve categorical hues, all at ONE fixed perceptual lightness (OKLCH L 0.615, chroma capped
     at 0.155 and clipped to the sRGB gamut), with the twelve hue angles chosen by maximising the
     smallest pairwise OKLab distance rather than by eye. One lightness for all twelve means no
     sprint reads louder than its neighbours, and it is what lets ONE set serve all ten themes
     instead of ten per-theme overrides: every hue clears 3.4:1 against the lightest theme
     surface (#ffffff) and 4.5:1 against the darkest (#11161c).

     THEY ARE NEVER A TEXT COLOUR. tests/site-lint/theme-contrast.cjs checks a FIXED pair list, so
     a token added here is not automatically contrast-checked and a bad text pair would ship
     silently. These drive borders, dots and 12%-tint fills only; the label stays var(--text) and
     always spells the sprint out in words, so colour is never the only signal. */
  --sprint-1:#d25853;  --sprint-2:#c86701;  --sprint-3:#a87d01;  --sprint-4:#818e02;
  --sprint-5:#3e9c42;  --sprint-6:#019b7c;  --sprint-7:#0096a7;  --sprint-8:#048dcf;
  --sprint-9:#5f7de1;  --sprint-10:#906dd5; --sprint-11:#b260b7; --sprint-12:#c95789; --sprint-13:#d9694f; --sprint-14:#4f9d8a; --sprint-15:#7a8f3d; --sprint-16:#8a7f65; --sprint-17:#5b8fa8; --sprint-18:#a3622e;
  /* on-accent text/fill pair for solid accent-filled pills (selected segment, M/L/XXL
     on-state). Every theme's accent is light/mid enough for near-black text to clear
     WCAG AA; the two slate-blue light themes override below (their mid-tone accent
     fails dark text, so they use the deep accent + white). */
  --on-accent-bg:var(--accent); --on-accent-fg:#1a1410;
  /* Canonical on-accent pair for ANY solid accent-FILLED control (buttons, brand marks, badges,
     tags, step numbers, selected segments). --on-accent = the text, --on-accent-fill = the fill.
     Resolves per theme via the bg/fg pair above (the 2 slate light themes already override to
     deep-accent + white). Verified WCAG AA >=4.5:1 on all 10 themes (min 5.83:1, birch-sage).
     Route every "dark/white text on --gold/--accent" element here instead of hardcoding. */
  --on-accent:var(--on-accent-fg); --on-accent-fill:var(--on-accent-bg);
  --electric:var(--accent); --electric-deep:var(--accent-deep); --electric-tint:var(--accent-soft);
  --accent-dim:var(--accent-deep);
  /* accent used as SMALL-TEXT fill on the light page surfaces (--surface, --cream, the #bank
     gradient). Defaults to --accent-deep, which already clears WCAG AA 4.5:1 on those surfaces
     in every theme EXCEPT the gold light themes (their warm mid-gold --accent-deep is only
     ~3.2-3.9:1 on near-white) — studio-cream overrides this below. Mirrors --ok-text/--danger-text. */
  --accent-text:var(--accent-deep);
  /* brand header / walnut family */
  --walnut:var(--header-bg); --walnut-2:var(--header-bg-2); --walnut-3:var(--header-bg-3);
  --walnut-deep:var(--header-bg); --walnut-line:var(--header-line); --walnut-tint:var(--header-bg-2);
  --wood-deep:var(--header-bg-3);
  /* semantic */
  --green:var(--ok); --green-bg:var(--ok-soft); --green-dim:var(--ok); --ok-bg:var(--ok-soft);
  --amber:var(--warn); --amber-bg:var(--warn-soft); --no:var(--danger); --red:var(--danger); --red-bg:var(--danger-soft);
  /* on-danger pair for SOLID danger-filled buttons (the destructive confirm). Light
     themes' --danger is a deep brick red, so white text on it already clears WCAG AA
     (~5:1). The dark themes tune --danger as a BRIGHT terracotta accent (so over-wall
     markers read on dark surfaces), where white text would be only ~3:1 — they override
     --on-danger-bg with a deeper red below. Keep --danger as the button BORDER so its
     boundary stays >=3:1 against the (dark) modal card. Mirrors the --on-accent pair. */
  --on-danger-bg:var(--danger); --on-danger-fg:#ffffff;
  /* dark "ink" text for use ON the light semantic soft-fills (the *-soft tints stay
     light in every theme), so chips/badges/readouts clear WCAG AA in all themes.
     ACCENT text on a light --accent-soft fill: use --accent-ink (fixed deep slate), NOT
     --accent-text (which resolves bright in dark themes and fails on the light soft fill). */
  --ok-ink:#2f5a3f; --warn-ink:#6e5114; --danger-ink:#7a3326; --accent-ink:#3f5a6b;
  /* theme-adaptive semantic TEXT — for semantic-coloured text drawn on a surface that
     FLIPS dark (page canvas --bg/--cream, --surface/--cream-card, --surface-2, menus).
     Resolves to the deep -ink in light themes and a bright AA tint in dark (override
     below). Mirrors --on-accent / --on-danger. For text on the *-soft fills (which stay
     light in every theme) keep -ink; for text on the hardcoded light --drop-bg keep -ink. */
  --ok-text:var(--ok-ink); --warn-text:var(--warn-ink); --danger-text:var(--danger-ink);
  /* semantic text on a PERMANENTLY-DARK surface (the inventory rail / header family,
     dark in EVERY theme): always the bright tint so light themes clear AA on it too. */
  --ok-on-dark:#9ed39a; --warn-on-dark:#e8c067; --danger-on-dark:#f0a98f;
  /* =====================================================================
     P164 / P166 / P167 — the PLAN CLASS-COLOR language. ONE hue per
     cabinet class drives THREE derived tokens that the floor-plan box
     FILL, the on-box NAME label, the dimension LADDER (lines + numbers +
     tag) and the legend all read, so the fill, the label, the ladder and
     the key can never disagree — one colour language, one source.
       --cc-<class>-hue    the class's base hue (a theme token by default;
                           P167 overrides just THIS at the #plan-panel scope
                           and the fill + ink re-derive automatically).
       --cc-<class>-fill   the light box tint = the P108/P120 formula
                           (hue mixed into --surface), byte-identical to
                           the old inline .cabfill-* mixes, per class.
       --cc-<class>-ink    the DEEP variant for label text + ladder line/
                           number: the hue driven toward --text so it stays
                           AA on --surface AND on its own fill in EVERY theme
                           (light themes -> darker, dark themes -> lighter,
                           by construction). "Inkier than the fill," derived
                           — never the raw fill value. The P167 runtime floor
                           deepens a user pick further only when it must. */
  --cc-base-hue:var(--accent);
  --cc-upper-hue:var(--ok);
  --cc-tall-hue:var(--danger);
  --cc-island-hue:var(--cab);
  --cc-appliance-hue:var(--text-muted);
  --cc-base-fill:color-mix(in srgb, var(--cc-base-hue) 24%, var(--surface));
  --cc-upper-fill:color-mix(in srgb, var(--cc-upper-hue) 48%, var(--surface));
  --cc-tall-fill:color-mix(in srgb, var(--cc-tall-hue) 24%, var(--surface));
  --cc-island-fill:color-mix(in srgb, var(--cc-island-hue) 34%, var(--surface));
  --cc-appliance-fill:color-mix(in srgb, var(--cc-appliance-hue) 20%, var(--surface));
  --cc-base-ink:color-mix(in srgb, var(--cc-base-hue) 52%, var(--text));
  --cc-upper-ink:color-mix(in srgb, var(--cc-upper-hue) 52%, var(--text));
  --cc-tall-ink:color-mix(in srgb, var(--cc-tall-hue) 55%, var(--text));
  --cc-island-ink:color-mix(in srgb, var(--cc-island-hue) 55%, var(--text));
  --cc-appliance-ink:color-mix(in srgb, var(--cc-appliance-hue) 40%, var(--text));
  /* effects */
  --shadow:var(--shadow-c); --shadow-lg:var(--shadow-lg-c);
  /* P58 — tokenized ELEVATION SCALE (a depth vocabulary; adding tokens is not
     palette work, and it does not touch any theme's colours). Apply consistently:
       --elev-1  resting cards / tiles          (= the existing card shadow)
       --elev-2  popovers, dropdowns, toasts    (= the existing large shadow)
       --elev-3  modals (they sit over a scrim) — a monochrome ambient so it reads
                 as the highest layer on ANY theme without per-theme tuning.
     A DOCKED panel / inspector divider stays a BORDER, never a shadow. */
  --elev-1:var(--shadow-c); --elev-2:var(--shadow-lg-c);
  --elev-3:0 2px 8px rgba(0,0,0,.12),0 28px 64px rgba(0,0,0,.26);
}
/* Gold/warm light themes: their warm mid-gold --accent-deep is only ~3.0-4.3:1 as small text
   on the near-white card/page/#bank surfaces (below WCAG AA 4.5:1), so override --accent-text
   with a deeper shade of the SAME hue. Verified >=4.5:1 on --surface, --cream, and the #bank
   gradient (including its 9% gold-radial top edge). Slate-blue + dark themes keep --accent-deep
   (already AA). Placed after the alias layer so equal-specificity source order lets it win. */
:root[data-theme="studio-cream"], :root:not([data-theme]){ --accent-text:#7e5316; }
:root[data-theme="maple"]{        --accent-text:#82591b; }
:root[data-theme="birch-sage"]{   --accent-text:#49662e; }
/* Slate-blue light themes: their mid-tone accent (#5c7c93 / #3f7ba8) fails dark text
   on a solid fill, so the on-accent pill uses the deep accent + white (~7:1). Placed
   after the alias layer so equal-specificity source order lets it win. */
:root[data-theme="linen-ink"], :root[data-theme="porcelain-blue"]{
  --on-accent-bg:var(--accent-deep); --on-accent-fg:#ffffff;
}
/* Dark themes tune --danger as a bright terracotta ACCENT (over-wall bars/markers on
   dark surfaces), so white text on it is only ~3:1. Solid danger BUTTONS therefore use
   a deeper red fill (white text ~5:1) while keeping the bright --danger as the border.
   Placed after the alias layer so equal-specificity source order lets it win. */
:root[data-theme="walnut-night"], :root[data-theme="graphite"], :root[data-theme="espresso"], :root[data-theme="midnight-oak"], :root[data-theme="forest-dark"]{
  --on-danger-bg:#b35140;
  /* semantic TEXT goes bright on the dark surfaces (the fixed dark -ink tints drop to
     ~1.5-2.1:1 there). Verified 7.0-10.7:1 on every dark surface, all 5 dark themes. */
  --ok-text:#9ed39a; --warn-text:#e8c067; --danger-text:#f0a98f;
}
/* Smooth the flip; respect reduced-motion. Targets :root body (0,1,1) so the FIRST
   theme application (un-themed fallback → chosen theme) animates too, not just
   theme-to-theme switches. */
@media (prefers-reduced-motion: no-preference){
  :root body{ transition: background-color .25s ease, color .25s ease; }
}

/* =====================================================================
   P58 — the suite MOTION STANDARD. State changes animate in 150-250ms,
   transform/opacity ONLY (never width/height/top/left — those thrash
   layout), and are SILENCED under prefers-reduced-motion. Shared as tokens
   + opt-in helper classes so nothing animates unless it asks for it.
     --bws-motion / --bws-motion-slow  standard duration+easing for
       per-component transitions, e.g. `transition: opacity var(--bws-motion)`.
     .bws-fade-in / .bws-pop-in  enter animations for panels/menus/toasts.
   ===================================================================== */
:root{
  --bws-ease:cubic-bezier(.4,0,.2,1);
  --bws-motion:.18s var(--bws-ease);
  --bws-motion-slow:.24s var(--bws-ease);
}
@media (prefers-reduced-motion: no-preference){
  .bws-fade-in{ animation:bws-fade-in .18s var(--bws-ease) both; }
  .bws-pop-in{ animation:bws-pop-in .2s var(--bws-ease) both; }
  @keyframes bws-fade-in{ from{ opacity:0; transform:translateY(4px); } to{ opacity:1; transform:none; } }
  @keyframes bws-pop-in{ from{ opacity:0; transform:scale(.96); } to{ opacity:1; transform:none; } }
}
@media (prefers-reduced-motion: reduce){
  .bws-fade-in, .bws-pop-in{ animation:none !important; }
}

/* P58 — alignment for the shared line-icon hosts (shared/bws-icons.js fills any
   [data-bws-icon] span with an inline SVG). Centres the glyph inside
   buttons/tabs (flex ignores vertical-align) AND inline labels/headings
   (vertical-align:middle), without per-page CSS. */
[data-bws-icon]{ display:inline-flex; align-items:center; flex:0 0 auto; vertical-align:middle; }
