/* ============================================================
   Waymore Better Component Lookbook — Design Tokens
   Layer 1: base theme (brand direction — sets bg/fg/surfaces/type)
   Layer 2: profession (sets primary/accent + content context)
   ============================================================ */

:root {
  /* Every colour token below is a light-dark(<light>, <dark>) pair; the
     browser picks the side from color-scheme (0.12.0, simplify SITE F7 /
     founder decision D3). Both schemes are declared here; activation is the
     color-scheme rules at the end of this file — no rule assigns a surface
     token twice. */
  color-scheme: light dark;

  /* --- The themeless default IS Editorial (0.15.0, design-system#21) ------
     This block used to carry a palette of its own ("Cloud Dancer"): warm
     hue-85 grounds, cool hue-260 text, and a --primary at **oklch(42% 0.08
     220)** — blue, which is banned as a brand colour (the founder's day-job
     employer owns a specific blue). Anything rendered without a data-theme
     got blue buttons, blue links and a blue focus ring: the lookbook's
     component pages ship `<html lang="en">` with no theme and are themeless
     until component-init.js applies one, so they painted blue on first paint
     and stayed blue if JS failed.

     The default is now Editorial — the brand's clean-white, warm-sienna
     ground, and the only theme whose brief says "NO blue" in as many words.
     The values below are a byte-for-byte copy of [data-theme="editorial"]'s;
     CSS cannot forward-reference, so the palette is stated twice and
     scripts/check-no-blue-primary.mjs (B3) fails the build if the two copies
     ever drift. --ring therefore derives from --primary at 70/70, exactly as
     Editorial's does, instead of dodging to --accent to avoid the blue.

     --fg-subtle's light side is 55.5% here, not Editorial's old 62%: see the
     fg-subtle note further down (design-system#9, #11). Editorial's own block
     carries the same lifted value. ------------------------------------- */
  --bg:            light-dark(oklch(100% 0 0), oklch(14% 0.008 280));
  --bg-elev:       light-dark(oklch(100% 0 0), oklch(18% 0.010 280));
  --fg:            light-dark(oklch(18% 0.01 280), oklch(94% 0.006 85));
  --fg-muted:      light-dark(oklch(45% 0.008 280), oklch(72% 0.008 280));
  --fg-subtle:     light-dark(oklch(55.5% 0.006 280), oklch(59% 0.008 280));
  --border:        light-dark(oklch(90% 0.005 280), oklch(28% 0.012 280));
  --surface:       light-dark(oklch(99% 0.003 280), oklch(18% 0.010 280));
  --surface-2:     light-dark(oklch(96% 0.005 280), oklch(23% 0.012 280));
  --primary:       light-dark(oklch(48% 0.14 35), oklch(68% 0.15 35));
  --primary-fg:    light-dark(oklch(99% 0.003 280), oklch(14% 0.008 280));
  --accent:        light-dark(oklch(52% 0.10 150), oklch(68% 0.10 150));
  --accent-fg:     light-dark(oklch(99% 0.005 150), oklch(14% 0.008 280));

  /* ---- --accent-ink: the accent, as TEXT (0.17.0) --------------------
     --accent is a decoration token and always has been. The comment on
     --badge-dot-live says so in as many words, and check-contrast.mjs
     carries nine BY-NAME allow-list entries excusing `--accent on --bg`
     at 2.35-4.49:1 on the grounds that accent is "non-text decoration
     (rule lines, chip borders, focus rings, icon strokes)" and so sits
     under SC 1.4.11's 3:1 bar rather than AA's 4.5:1.

     That is a correct reading of what --accent was for, and it is
     exactly why a profession switch was invisible: the only token a
     profession moved was one we had agreed never to paint text with.

     So 0.17.0 does not loosen the bar — it adds the second form. Mixing
     the accent TOWARDS --fg always moves it away from --bg, in both
     schemes and on every theme, because --fg is by construction the
     high-contrast end of the palette: on a light theme the mix darkens,
     on a dark theme it lightens. One declaration, derived, so a theme
     or profession that re-tints --accent gets a conforming ink for free
     and there is no per-theme literal to drift.

     --accent-ink is declared at :root ONLY and is deliberately not
     restated in any theme or profession block. [data-theme] and
     [data-profession] both match <html>, i.e. the same element this is
     declared on, so the var(--accent) inside the mix substitutes that
     element's WINNING --accent — the profession's. Restating it per
     theme would be five copies of one formula.

     scripts/check-contrast.mjs asserts --accent-ink on --bg AND on
     --surface at the full 4.5:1, across every theme x profession x
     scheme, with nothing allow-listed.

     The 55% figure is a measured trade, not a guess, and it is set by
     the WORSE of two paths. Sweeping the mix and taking the worst cell
     of each:

       mix   profession path   theme-only path
       40%        8.29              7.00
       50%        7.13              6.01
       55%        6.62              5.44     <- chosen
       60%        6.16              4.93
       70%        5.34              4.06     <- FAILS AA

     The second column is the one that set the number, and it was a real
     hole rather than a hypothetical. A page carries data-theme in its
     own markup but gets data-profession from component-init.js — so
     between first paint and that script, forever with JS off, and
     always on the-co/site (which never writes the attribute at all),
     --accent-ink derives from the THEME's own accent. Coast's is a
     hue-200 cyan at 2.35:1 raw, and at a 70% mix its eyebrow measured
     4.06:1. The `.wmb-eyebrow` surface rule in check-contrast.mjs is
     what found it, which is the argument for scoring RULES and not only
     token pairs.

     So: more accent is a stronger signal and less contrast, and the ink
     has to be legible from ANY accent it is handed, not merely the five
     profession ones. 55% is as much accent as that allows with real
     margin left (5.44 against 4.5). 100% is the pre-0.17.0 situation,
     i.e. the nine by-name excuses. Keep the ASSERTION, not this number,
     as the contract — if a theme's palette moves, the run says so. */
  --accent-ink:    color-mix(in oklch, var(--accent) 55%, var(--fg));

  --ring:          light-dark(color-mix(in oklch, var(--primary) 70%, transparent), color-mix(in oklch, var(--primary) 70%, transparent));

  /* Type */
  --font-display:  'Fraunces', Georgia, serif;
  --font-body:     'Inter', system-ui, -apple-system, sans-serif;
  --font-mono:     ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  /* Display-face emphasis — the `<em>` inside a display-face heading, lockup
     or card title (0.18.1, design-system#27). It is THREE declarations' worth
     of treatment reduced to two tokens, because a profession changes
     --font-display and not every display face has an italic to shear.

     0.17.0 gave consultant 'Space Grotesk', a family that ships NO italic in
     any weight. Seven rules in studio.css and base.css said
     `font-style: italic` outright, so every emphasised display word on a
     consultant surface came out as a faux oblique — the upright outlines
     sheared by the rasteriser. logic/site found it and patched it locally
     (weight + colour); the patch belongs here, because this package is the
     only thing that knows which face a profession just handed the heading.

     --display-em-style   italic where the first family in --font-display has
                          a REAL italic that src/fonts.css asks for; `normal`
                          where it does not. Never `oblique`: that is the
                          synthesis this token exists to stop. Declared by
                          :root and by every profession.

     --display-em-weight  the weight step that REPLACES the slant, and it is
                          deliberately NOT declared here. Only a profession
                          whose face has no italic declares it (700), and
                          every rule reads it with its own authored weight as
                          the fallback:

                            .wmb-h1 em { font-weight: var(--display-em-weight, inherit) }
                            .wmb-lockup em { font-weight: var(--display-em-weight, 500) }

                          An absolute token cannot serve both: .wmb-h1 em
                          inherits 500 from its heading, .card-title em
                          inherits 600, and .wmb-lockup em was authored 500
                          against a 600 lockup on purpose — lighter AND
                          slanted is the mark. One value for all of them
                          would have quietly re-weighted every card title in
                          the family. Undefined-unless-needed is what makes
                          this release's rendering change exactly the one it
                          claims: faces with a real italic render byte for
                          byte what 0.18.0 rendered.

     IF A CONSUMER PINS --font-display, IT MUST PIN THESE TOO. The emphasis
     tokens describe the FACE, and a site that overrides the face in its own
     stylesheet is no longer using the one its profession named.
     waymorebetterstudios.com is the live case: it runs
     data-profession="coach" for the accent but pins --font-display to
     Fraunces, because Archivo Black is a shout and the studio's voice is
     not. Taking coach's `--display-em-style: normal` there would have stood
     every display italic on the site upright — measured at 375 on /about,
     32.6% of the viewport. Taking coach's `--display-em-weight: 700` on top
     of a pinned italic reflowed every display heading, because the italic
     had always been 500. So the pin is all three lines:

       :root {
         --font-display:      'Fraunces', Georgia, serif;
         --display-em-style:  italic;
         --display-em-weight: initial;
       }

     `initial` is the un-declare: it makes the token the guaranteed-invalid
     value, so every rule below falls back to the weight it authored, which
     is exactly how a face with a real italic behaves. Verified in Chrome.

     The failure direction matters and it is the safe one: a consumer that
     pins the face and forgets these loses an italic it could have had. It
     never gains one the face cannot draw — that is the defect, and it stays
     impossible.

     Colour is deliberately NOT a token here. Each ground already has its own
     measured, contrast-asserted colour (--primary on a page, --accent-ink on
     an eyebrow, light-dark(--accent, --bg) on the footer's inverted band) and
     a single --display-em-color would be overridden by three rules on the way
     out. scripts/check-display-emphasis.mjs asserts the part a token CAN
     carry: that no profession asks a face for an italic it does not have.

     The two faces with one weight and no italic — coach's Archivo Black,
     outdoor's Bebas Neue — are the honest limit of this: weight is a no-op
     there, so colour is the whole visual signal and the `<em>` element is
     what carries the emphasis for assistive tech. Stated rather than papered
     over with letter-spacing. */
  --display-em-style:  italic;

  /* Scale */
  /* FOUR authored radii, and every profession authors all four (0.18.1,
     design-system#26). Before this, a profession set --radius and
     --radius-lg and left --radius-sm and --radius-xl at these :root
     literals, which inverted the scale at both ends:

       consultant  sm 0.375 >  lg 0.3125 > base 0.1875   (sm the LARGEST)
       petcare     lg 1.75  >  xl 1.5                    (lg past the top)

     base.css reads --radius-sm on :focus-visible and --radius-xl on
     .wmb-media img, so both were reachable, and a component reaching for
     --radius-sm to be SUBTLER than --radius got the opposite.

     WHY NOT DERIVE IT. The first attempt was
     `--radius-sm: calc(var(--radius) * 0.6)` here, so that a profession
     overriding --radius would move the whole scale and could not forget
     anything. Measured in Chrome, it does not work and the reason is in the
     spec: a var() inside a custom property is substituted at COMPUTED-VALUE
     TIME ON THE ELEMENT THAT CARRIES THE DECLARATION. --radius-sm resolves
     against the :root --radius, and descendants inherit that finished
     value — so [data-profession] setting --radius: 0.25rem still inherited
     --radius-sm 0.375rem. Proven on this machine:

       :root { --base: 10px; --derived: calc(var(--base) * 2) }
       #a    { --base: 100px }
       -> getComputedStyle(#a)['--derived'] === "calc(10px * 2)"

     Deriving would only work by repeating the calc() in every profession
     block, which is the same number of lines as a literal and harder to
     read. So the literals stay and the GUARD is the guarantee:
     scripts/check-radius-scale.mjs asserts sm < base < lg < xl for :root,
     every profession, every theme and every overlay, and that no profession
     declares three of the four. A profession that forgets one fails CI
     rather than shipping an inverted corner. */
  --radius-sm: 0.375rem;
  --radius:    0.625rem;
  --radius-lg: 1rem;
  --radius-xl: 1.5rem;

  /* Shadows are not colours — light-dark() cannot express them. Their dark
     values live in the scheme rules at the end of this file. */
  --shadow-sm: 0 1px 2px oklch(0% 0 0 / 0.05);
  --shadow:    0 4px 12px oklch(0% 0 0 / 0.08), 0 1px 3px oklch(0% 0 0 / 0.04);
  --shadow-lg: 0 20px 50px oklch(0% 0 0 / 0.12), 0 4px 12px oklch(0% 0 0 / 0.05);

  /* Motion */
  --ease-out:  cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in:   cubic-bezier(0.7, 0, 0.84, 0);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-fast:  150ms;
  --dur-med:   280ms;
  --dur-slow:  480ms;

  /* ---- Layout (0.16.0, design review F3) ----------------------------
     Content width drifted across the family: Studios and the hub started
     content at x=107 at 1440, Logic at 113, glossary at 67, lab at 80 —
     because `.container` read --content-max (1200px) while the marketing
     shell's .wmb-max carried a literal 1120px and some pages carried their
     own. One number now. --content-max stays as an alias so nothing that
     already reads it breaks; it is no longer a second knob. */
  --container:        1120px;
  --container-narrow: 760px;
  --content-max:      var(--container);   /* legacy alias — do not set independently */

  /* ---- Motion primitive (0.16.0, design review F4) -------------------
     One shared motion language instead of none. `.lift` is a card hover,
     `.reveal` a section entry; both read these and both are inert under
     `prefers-reduced-motion` (the global reduce block at the end of this
     file zeroes every duration, and base.css also drops the transforms so
     a reduced-motion viewer gets no movement at all, not fast movement). */
  --lift-y:        -2px;
  --lift-dur:      var(--dur-fast);
  --reveal-y:      8px;
  --reveal-dur:    var(--dur-med);

  /* ---- Overlay layer (0.19.0) ---------------------------------------
     One scrim, one rise, one duration, for every overlay in the family.

     --scrim is the ONLY new colour token here, and it is deliberately not a
     mix of anything: a scrim is darkness behind a panel, and deriving it
     from --fg would invert it in dark mode (where --fg is near-white). It is
     a light-dark() pair so the dark side can be heavier — a 50% veil over an
     already-dark page does not separate the panel from it.

     Read it with a literal fallback — `var(--scrim, oklch(12% 0.01 0 / 0.5))`
     — wherever it is used on ::backdrop. ::backdrop inheriting custom
     properties from its originating element is a 2023 spec change; in a
     browser that predates it, a bare var(--scrim) resolves to nothing and
     the modal gets NO scrim at all, which is a transparent overlay over a
     live page rather than a cosmetic miss.

     --overlay-y is the rise a dialog makes on entry and --overlay-x the
     distance a drawer travels. Both are zeroed under prefers-reduced-motion
     by base.css, which drops the transforms outright rather than relying on
     the global reduce block below — that block only makes motion FAST. */
  --scrim:       light-dark(oklch(12% 0.01 0 / 0.5), oklch(6% 0.01 0 / 0.68));
  --overlay-y:   12px;
  --overlay-x:   100%;
  --overlay-dur: var(--dur-med);

  /* ---- Cross-document view transition (0.20.0) -----------------------
     The cross-fade between two documents (base.css, `@view-transition`).
     Shorter than --dur-med and shorter than the UA default of about 250ms,
     on purpose: this is the one piece of motion in the system that stands
     between a visitor and the page they asked for, so it should be over
     before they notice it started. It is a token so a site can slow it
     down without reaching into the animation.

     It is NOT zeroed by the global reduce block at the end of this file,
     and does not need to be: under prefers-reduced-motion the
     `@view-transition` rule itself does not apply, so there is no
     transition to shorten. Measured in Chromium and WebKit — see the
     comment on the rule. */
  --vt-dur:      var(--dur-fast);

  /* ---- Marketing status badge (0.16.0, design review H7) -------------
     The hub's family grid painted its LIVE pill mint green — the only
     green on the site and off every theme's palette. These are aliases,
     not new colours: the chip is the page's own surface, hairline and
     muted text, and only the DOT carries the theme's accent, so the badge
     follows a theme switch and introduces nothing to keep in contrast
     step. --badge-fg on --badge-bg resolves to --fg-muted on --surface,
     which scripts/check-contrast.mjs asserts (worst 5.92:1).

     The dot is DECORATION, not the state. `.badge-status` always renders a
     text label ("Live", "Coming") and that label is what conveys the
     state — which matters, because --accent measures 2.35-2.56:1 against
     --bg/--surface on the cooler themes and would not clear SC 1.4.11's
     3:1 if it were the sole indicator. Do not build a dot-only variant. */
  --badge-bg:         var(--surface);
  --badge-border:     var(--border);
  --badge-fg:         var(--fg-muted);
  --badge-dot-live:   var(--accent);
  --badge-dot-soon:   var(--fg-subtle);
  --badge-dot-off:    var(--border);

  /* ---- Muted text on an inverted (--fg) ground (0.16.0, S16) ---------
     The footer paints --fg and writes in --bg. Its muted steps were three
     ad-hoc alpha mixes — 70% for the brand paragraph, 60% for the location
     line, 50% for the column headings — and the 50% one measured 3.54:1
     at 11px, below AA. (design-system#9 reported it from the live hub:
     "footer h3 (THE FAMILY / REACH US) 11px 3.00:1".)

     One step now, opaque rather than alpha so it is computable rather than
     composited, mixed toward the ground it actually sits on. Measured
     worst case across all ten themes x both schemes: 6.43:1.
     scripts/check-contrast.mjs asserts it through the footer surface
     rules, which required teaching it to resolve color-mix(). */
  --on-dark:          var(--bg);
  --on-dark-muted:    color-mix(in oklch, var(--bg) 70%, var(--fg));

  /* Chrome — design-tool surfaces that sit ON a themed page but are not OF it
     (the floating skin chooser). Dark by design under every theme; same
     values as gallery.css `.dark-chrome` (that sheet is loaded without
     tokens.css and keeps its own literals). Hex on purpose: chrome is not
     brand and never mixes with theme tokens. */
  --chrome-bg:           #17171C;
  --chrome-fg:           #F3F1EC;
  --chrome-surface:      #2A2A32;
  --chrome-border:       #3A3A42;
  --chrome-muted:        #8A8680;
  --chrome-hover:        #34343C;
  --chrome-accent:       #3C8574;
  --chrome-accent-hover: #4A9B89;

  /* ============================================================
     SEMANTIC LAYER (v0.3.0 / DS1)
     ------------------------------------------------------------
     These tokens carry MEANING, not mood. They intentionally live
     outside per-theme blocks — a "severe" outcome-measure score is
     severe in the Caribbean theme AND in Arena. Remapping them per
     theme would dilute the signal and make clinical affordances
     inconsistent across tenant branding. Each -bg/-fg pair carries its
     dark side inline (light-dark()) so tints composite on a dark surface
     in every theme — Arena included, since it is color-scheme: dark.

     Namespace map:
       --role-*          — identity-of-person tokens (UX-ADR-1, gap #1)
       --severity-{0..5} — 6-step ordinal ramp (UX-ADR-4, gap #2)
       --severity-unscored-* — neutral no-data surface (UX-ADR-4 2026-04-18)
       --status-*        — transient state pills (gap #3)
       --provenance-*    — AI-content authorship states (UX-ADR-2, gap #4)
     ============================================================ */

  /* -- Role tokens ------------------------------------------------
     Per-user-role affordance colors. Each role carries a base hue,
     a soft background tint, and a foreground color for text on chip.
     Avoids blue entirely (conflict constraint). Chosen to be visually
     distinct in rapid scan (N7 supervisor dashboard row-of-chips).

     Usage:
       <span class="pill" style="
         background: var(--role-practitioner-bg);
         color: var(--role-practitioner-fg);
         border-color: var(--role-practitioner);
       ">Practitioner</span>
  */
  --role-practitioner:     oklch(55% 0.11 155);   /* sage — clinical, trustworthy */
  --role-practitioner-bg:  light-dark(oklch(95% 0.025 155), oklch(26% 0.04 155));
  --role-practitioner-fg:  light-dark(oklch(28% 0.08 155), oklch(88% 0.06 155));

  --role-admin:            oklch(52% 0.14 330);   /* plum — authority, distinct from red */
  --role-admin-bg:         light-dark(oklch(95% 0.03 330), oklch(26% 0.05 330));
  --role-admin-fg:         light-dark(oklch(28% 0.11 330), oklch(88% 0.07 330));

  --role-staff:            oklch(58% 0.11 75);    /* warm ochre — supportive */
  --role-staff-bg:         light-dark(oklch(95% 0.028 75), oklch(26% 0.045 75));
  --role-staff-fg:         light-dark(oklch(30% 0.09 75), oklch(90% 0.06 75));

  --role-supervisor:       oklch(50% 0.14 45);    /* burnt amber — oversight, elevated */
  --role-supervisor-bg:    light-dark(oklch(95% 0.03 45), oklch(26% 0.05 45));
  --role-supervisor-fg:    light-dark(oklch(28% 0.12 45), oklch(90% 0.07 45));

  /* -- Severity ramp (6-step, 0..5 + unscored) --------------------
     Per UX-ADR-4 (amended 2026-04-18): universal ordinal ramp used
     across all 9 outcome-measure scales. Step 0 is intentionally
     sage (not pure gray) so "none/baseline" reads as a scored result
     rather than missing data. Chroma scales faster than hue to
     preserve ordinality in monochrome rendering (colorblind-safe
     sanity check).

     Contract:
       step  = 0   — scored as "none/baseline" (sage)
       step ∈ 1..5 — clinical bands (Minimal → Severe)
       no data-step — unscored/baseline (uses --severity-unscored-*)
                      to prevent silent-color-coding before data arrives

     Note: C-SSRS uses a separate binary risk chip overlay (ADR-010)
     and does NOT consume this ramp; see --status-error for the
     high-risk chip and --status-success for low-risk.

     Usage:
       <div class="pill" style="
         background: var(--severity-3-bg);
         color: var(--severity-3-fg);
         border-color: var(--severity-3);
       ">Elevated</div>

       <div class="severity-bar" data-step="3">…</div>   // via CSS var lookup

     Contrast: every -fg value pairs with its -bg value at ≥ 4.5:1
     on both sides of the pair (light AA-checked; dark AA-checked in
     v0.10.0).
  */
  /* Unscored / no-data baseline. Distinct from step-0.
     Used when a severity chip renders BEFORE the consumer has a
     clinical result to show — e.g., a catalog row pre-completion,
     a trend chart placeholder, a form designer preview. Chromeless
     neutral surface reads as "no data here" rather than step-0's
     "scored as minimal" signal. See UX-ADR-4 (2026-04-18 amendment)
     and the JSDoc on SEVERITY_STEPS in src/index.js for the
     consumer contract.
  */
  --severity-unscored:     light-dark(oklch(70% 0.006 260), oklch(58% 0.005 260));   /* chromaless cool gray */
  --severity-unscored-bg:  light-dark(oklch(96% 0.005 260), oklch(25% 0.008 260));
  --severity-unscored-fg:  light-dark(oklch(45% 0.01 260), oklch(78% 0.012 260));

  --severity-0:     oklch(70% 0.04 150);  /* none/baseline — calm sage (scored, not unscored) */
  --severity-0-bg:  light-dark(oklch(96% 0.012 150), oklch(25% 0.02 150));
  --severity-0-fg:  light-dark(oklch(32% 0.04 150), oklch(85% 0.03 150));

  --severity-1:     oklch(72% 0.10 130);  /* mild — chartreuse */
  --severity-1-bg:  light-dark(oklch(95% 0.025 130), oklch(25% 0.04 130));
  --severity-1-fg:  light-dark(oklch(32% 0.08 130), oklch(88% 0.06 130));

  --severity-2:     oklch(75% 0.14 90);   /* moderate — amber */
  --severity-2-bg:  light-dark(oklch(95% 0.035 90), oklch(28% 0.055 90));
  --severity-2-fg:  light-dark(oklch(32% 0.10 85), oklch(90% 0.08 90));

  --severity-3:     oklch(68% 0.17 60);   /* elevated — orange */
  --severity-3-bg:  light-dark(oklch(95% 0.04 60), oklch(28% 0.07 60));
  --severity-3-fg:  light-dark(oklch(32% 0.13 55), oklch(88% 0.10 60));

  --severity-4:     oklch(58% 0.20 30);   /* severe — red-orange */
  --severity-4-bg:  light-dark(oklch(95% 0.045 30), oklch(28% 0.08 30));
  --severity-4-fg:  light-dark(oklch(32% 0.15 25), oklch(88% 0.12 30));

  --severity-5:     oklch(48% 0.21 20);   /* extreme — deep red */
  --severity-5-bg:  light-dark(oklch(94% 0.05 20), oklch(28% 0.09 20));
  --severity-5-fg:  light-dark(oklch(30% 0.17 20), oklch(90% 0.14 20));

  /* -- Status tokens (used by .pill--* variants) ------------------
     Transient-state vocabulary. Reused by Connect onboarding (Q2),
     pen-test finding chips (T9), C-SSRS risk overlays (O3), etc.
     Kept deliberately small: 4 states cover ~all product surfaces
     when combined with role/severity/provenance tokens.
  */
  --status-success:     oklch(55% 0.14 150);
  --status-success-bg:  light-dark(oklch(95% 0.03 150), oklch(26% 0.05 150));
  --status-success-fg:  light-dark(oklch(28% 0.10 150), oklch(90% 0.08 150));

  --status-warning:     oklch(65% 0.15 75);
  --status-warning-bg:  light-dark(oklch(95% 0.035 75), oklch(28% 0.06 75));
  --status-warning-fg:  light-dark(oklch(32% 0.12 70), oklch(92% 0.10 75));

  --status-error:       oklch(55% 0.20 25);
  --status-error-bg:    light-dark(oklch(95% 0.045 25), oklch(28% 0.08 25));
  --status-error-fg:    light-dark(oklch(32% 0.15 22), oklch(90% 0.12 25));

  --status-info:        oklch(52% 0.10 280);   /* muted violet — NOT blue */
  --status-info-bg:     light-dark(oklch(95% 0.025 280), oklch(26% 0.04 280));
  --status-info-fg:     light-dark(oklch(30% 0.10 280), oklch(88% 0.07 280));

  /* Neutral status — used by .ds-badge--neutral (DS-7) for the
     "not connected / inert / dormant" state. Achromatic ramp at
     hue 260 so it reads as "no signal" against any themed surface
     and never competes with severity/role/provenance hues. */
  --status-neutral:     oklch(60% 0.008 260);
  --status-neutral-bg:  light-dark(oklch(94% 0.004 260), oklch(26% 0.008 260));
  --status-neutral-fg:  light-dark(oklch(32% 0.010 260), oklch(80% 0.012 260));

  /* ---- Categorical series ramp (0.20.3) --------------------------------
     Six steps for a chart, a legend, a swimlane: colours that mean
     "different", not "better" or "worse". Each holds >= 3:1 against its
     NEIGHBOURS (cyclically — in a donut 6 is drawn against 1) and against
     --bg and --surface, in all nine themes x both schemes.

     The lightness ALTERNATES on purpose: under a white page every step has
     to be dark, and six dark colours will not fit 3:1 apart, so odd steps
     take a mid band, even steps a deep band, and hue separates 1 from 3 from
     5. Hues jump the 200-260 band because these are :root literals
     (check-no-blue-primary.mjs B1). Arena needs no override — it declares
     color-scheme: dark, so light-dark() already hands it the dark side.

     check-contrast.mjs scores it; scripts/solve-series-ramp.mjs shows the
     algebra; CHANGELOG 0.20.3 has the measurements. */
  --series-1: light-dark(oklch(63.2% 0.200 25),  oklch(88.6% 0.058 25));
  --series-2: light-dark(oklch(33.4% 0.072 70),  oklch(56.9% 0.122 70));
  --series-3: light-dark(oklch(59.6% 0.160 130), oklch(86.5% 0.200 130));
  --series-4: light-dark(oklch(32.3% 0.054 190), oklch(55.2% 0.094 190));
  --series-5: light-dark(oklch(63.0% 0.200 300), oklch(88.5% 0.064 300));
  --series-6: light-dark(oklch(35.0% 0.148 345), oklch(59.0% 0.200 345));

  /* ---- Ink for a brand-gradient panel (0.20.3) -------------------------
     --primary-fg is picked against --primary alone, so on a --primary ->
     --accent panel it is right at one end only (measured: 2.90:1 at the
     accent end, light/caribbean).

     No ink fixes that — black and white are the extremes and on 13 of the
     120 theme x scheme x profession gradients neither clears 4.5:1. So the
     ink ships with the ground it is guaranteed against: a panel carrying
     TEXT paints --gradient-from -> --gradient-to, the brand's own pair
     pulled down in oklch until white clears 4.5:1 at every point of every
     path (worst measured: 5.15:1). A panel with no text keeps the raw pair.

     check-contrast.mjs scores it; CHANGELOG 0.20.3 has the working. */
  --gradient-from: color-mix(in oklch, var(--primary) 72%, oklch(0% 0 0));
  --gradient-to:   color-mix(in oklch, var(--accent) 72%, oklch(0% 0 0));
  --on-gradient:   oklch(100% 0 0);

  /* -- AI provenance tokens ---------------------------------------
     Per UX-ADR-2 (inline pill + diff view). Provenance describes
     WHO/WHAT produced a piece of content. Four states map the HITL
     pipeline: human-authored → ai-drafted → ai-reviewed → confirmed.

     Color strategy: a violet ramp (hue ~290) for AI states so AI
     content is immediately distinguishable from severity/status/role
     palettes. Human-authored is deliberately chromaless (muted gray)
     so it reads as "default / baseline" in a mixed feed.

     Usage:
       <span class="provenance-pill provenance-pill--ai-drafted">
         AI-drafted
       </span>

     NOT blue (constraint). The violet is intentionally desaturated
     at hue 290 to avoid creeping into purple=premium territory —
     this is a status signal, not a feature flag.
  */
  --provenance-human:           oklch(55% 0 0);        /* chromaless gray — baseline */
  --provenance-human-bg:        light-dark(oklch(96% 0 0), oklch(28% 0 0));
  --provenance-human-fg:        light-dark(oklch(30% 0 0), oklch(85% 0 0));

  --provenance-ai-drafted:      oklch(58% 0.13 290);   /* soft violet */
  --provenance-ai-drafted-bg:   light-dark(oklch(96% 0.025 290), oklch(26% 0.05 290));
  --provenance-ai-drafted-fg:   light-dark(oklch(30% 0.12 290), oklch(88% 0.08 290));

  --provenance-ai-reviewed:     oklch(50% 0.16 295);   /* deeper violet — clinician touched */
  --provenance-ai-reviewed-bg:  light-dark(oklch(95% 0.03 295), oklch(28% 0.06 295));
  --provenance-ai-reviewed-fg:  light-dark(oklch(28% 0.14 295), oklch(88% 0.10 295));

  --provenance-confirmed:       oklch(50% 0.13 155);   /* sage — verified */
  --provenance-confirmed-bg:    light-dark(oklch(95% 0.028 155), oklch(26% 0.05 155));
  --provenance-confirmed-fg:    light-dark(oklch(28% 0.10 155), oklch(88% 0.07 155));

  /* -- AI diff-view surface (UX-ADR-2 option c) -------------------
     Tint for the background of sections showing clinician edits to
     AI-drafted content. Intentionally subtle — this is a frame, not
     a callout. Paired with --provenance-ai-drafted on the pill.
  */
  --ai-diff-bg:         color-mix(in oklch, var(--provenance-ai-drafted) 6%, var(--surface));
  --ai-diff-border:     color-mix(in oklch, var(--provenance-ai-drafted) 35%, var(--border));
  /* light-dark() cannot carry the mix percentages either — the dark mix
     (12% / 45%) lives in the scheme rules at the end of this file. */
}

/* ============================================================
   SCHEME PINS — see the DARK MODE section at the end for the whole story.
   An explicit pin is sovereign over the OS preference. Every colour token
   is a light-dark() pair, so these two rules are the whole activation.
   They sit ABOVE the theme blocks so that Arena's own block — the one
   theme that is color-scheme: dark in every scheme — wins the tie with
   the light pin by source order (same specificity, later wins).
   ============================================================ */
[data-color-scheme="light"] { color-scheme: light; }
[data-color-scheme="dark"], .dark { color-scheme: dark; }

/* ============================================================
   BASE THEMES (9 Waymore Better brand directions)
   A theme owns exactly one [data-theme="x"] block (check-dark-identity.mjs
   G1); Arena's also carries color-scheme: dark, which is why the scheme
   pins sit ABOVE this section.
   ============================================================ */

[data-theme="coast"] {
  --bg:         light-dark(oklch(96% 0.012 215), oklch(16% 0.02 225));
  --bg-elev:    light-dark(oklch(99% 0.004 215), oklch(20% 0.025 225));
  --fg:         light-dark(oklch(25% 0.03 230), oklch(93% 0.012 215));
  --fg-muted:   light-dark(oklch(45% 0.04 230), oklch(70% 0.018 220));
  --fg-subtle:  light-dark(oklch(53.5% 0.03 220), oklch(60% 0.018 220));
  --border:     light-dark(oklch(85% 0.02 215), oklch(30% 0.025 225));
  --surface:    light-dark(oklch(99% 0.004 215), oklch(20% 0.025 225));
  --surface-2:  light-dark(oklch(91% 0.015 215), oklch(26% 0.028 225));
  --primary:    light-dark(oklch(45% 0.12 230), oklch(68% 0.14 230));
  --primary-fg: light-dark(oklch(99% 0 0), oklch(14% 0.015 225));
  --accent:     light-dark(oklch(68% 0.14 200), oklch(75% 0.12 200));
  --accent-fg:  light-dark(oklch(20% 0.03 230), oklch(14% 0.015 225));
  --ring:       light-dark(color-mix(in oklch, var(--primary) 70%, transparent), color-mix(in oklch, var(--primary) 65%, transparent));
  --font-display: 'Fraunces', Georgia, serif;
  /* low moon over water — the dark side of each pair above. (design-system#10) */
}

[data-theme="mountain"] {
  --bg:         light-dark(oklch(94% 0.015 75), oklch(17% 0.018 55));
  --bg-elev:    light-dark(oklch(98% 0.008 75), oklch(21% 0.020 55));
  --fg:         light-dark(oklch(22% 0.025 60), oklch(93% 0.015 75));
  --fg-muted:   light-dark(oklch(42% 0.03 60), oklch(70% 0.020 65));
  --fg-subtle:  light-dark(oklch(52% 0.025 60), oklch(60.5% 0.018 60));
  --border:     light-dark(oklch(82% 0.02 60), oklch(30% 0.022 55));
  --surface:    light-dark(oklch(98% 0.008 75), oklch(21% 0.020 55));
  --surface-2:  light-dark(oklch(88% 0.018 60), oklch(27% 0.024 55));
  --primary:    light-dark(oklch(42% 0.08 45), oklch(65% 0.13 45));
  --primary-fg: light-dark(oklch(97% 0.01 80), oklch(15% 0.015 55));
  --accent:     light-dark(oklch(55% 0.12 140), oklch(68% 0.14 140));
  --accent-fg:  light-dark(oklch(97% 0.01 140), oklch(15% 0.015 55));
  --ring:       light-dark(color-mix(in oklch, var(--primary) 70%, transparent), color-mix(in oklch, var(--primary) 75%, transparent));
  --font-display: 'Fraunces', Georgia, serif;
  /* campfire cabin — the dark side of each pair above. (design-system#10) */
}

[data-theme="mediterranean"] {
  --bg:         light-dark(oklch(95% 0.018 60), oklch(17% 0.022 80));
  --bg-elev:    light-dark(oklch(98% 0.01 60), oklch(21% 0.024 80));
  --fg:         light-dark(oklch(22% 0.03 30), oklch(93% 0.018 60));
  --fg-muted:   light-dark(oklch(42% 0.035 30), oklch(70% 0.020 60));
  --fg-subtle:  light-dark(oklch(53% 0.03 30), oklch(60.5% 0.018 60));
  --border:     light-dark(oklch(83% 0.025 50), oklch(30% 0.025 80));
  --surface:    light-dark(oklch(98% 0.01 60), oklch(21% 0.024 80));
  --surface-2:  light-dark(oklch(89% 0.022 50), oklch(27% 0.026 80));
  --primary:    light-dark(oklch(55% 0.15 30), oklch(68% 0.15 30));
  --primary-fg: light-dark(oklch(98% 0.008 60), oklch(15% 0.018 80));
  --accent:     light-dark(oklch(60% 0.12 130), oklch(70% 0.13 130));
  --accent-fg:  light-dark(oklch(98% 0.008 130), oklch(15% 0.018 80));
  --ring:       light-dark(color-mix(in oklch, var(--primary) 80%, transparent), color-mix(in oklch, var(--primary) 70%, transparent));
  --font-display: 'Fraunces', Georgia, serif;
  /* olive grove at dusk — the dark side of each pair above. (design-system#10) */
}

[data-theme="latin"] {
  --bg:         light-dark(oklch(96% 0.012 25), oklch(16% 0.025 30));
  --bg-elev:    light-dark(oklch(99% 0.005 25), oklch(20% 0.028 30));
  --fg:         light-dark(oklch(22% 0.03 20), oklch(94% 0.015 25));
  --fg-muted:   light-dark(oklch(42% 0.035 20), oklch(71% 0.020 25));
  --fg-subtle:  light-dark(oklch(53.5% 0.03 20), oklch(60% 0.020 25));
  --border:     light-dark(oklch(85% 0.02 20), oklch(30% 0.030 25));
  --surface:    light-dark(oklch(99% 0.005 25), oklch(20% 0.028 30));
  --surface-2:  light-dark(oklch(90% 0.02 20), oklch(26% 0.032 30));
  --primary:    light-dark(oklch(55% 0.18 15), oklch(68% 0.17 20));
  --primary-fg: light-dark(oklch(98% 0.006 25), oklch(14% 0.020 30));
  --accent:     light-dark(oklch(65% 0.16 55), oklch(75% 0.15 55));
  --accent-fg:  light-dark(oklch(20% 0.03 30), oklch(14% 0.020 30));
  --ring:       light-dark(color-mix(in oklch, var(--primary) 80%, transparent), color-mix(in oklch, var(--primary) 70%, transparent));
  --font-display: 'Fraunces', Georgia, serif;
  /* fired clay at night — the dark side of each pair above. (design-system#10) */
}

[data-theme="caribbean"] {
  --bg:         light-dark(oklch(97% 0.01 190), oklch(16% 0.025 210));
  --bg-elev:    light-dark(oklch(99% 0.004 190), oklch(20% 0.028 210));
  --fg:         light-dark(oklch(22% 0.035 210), oklch(93% 0.015 195));
  --fg-muted:   light-dark(oklch(42% 0.04 210), oklch(70% 0.020 200));
  --fg-subtle:  light-dark(oklch(54% 0.035 200), oklch(59.5% 0.020 200));
  --border:     light-dark(oklch(85% 0.02 190), oklch(30% 0.030 205));
  --surface:    light-dark(oklch(99% 0.004 190), oklch(20% 0.028 210));
  --surface-2:  light-dark(oklch(91% 0.022 190), oklch(26% 0.030 210));
  --primary:    light-dark(oklch(62% 0.16 195), oklch(74% 0.15 195));
  --primary-fg: light-dark(oklch(20% 0.03 210), oklch(14% 0.020 210));
  --accent:     light-dark(oklch(70% 0.15 60), oklch(75% 0.15 60));
  --accent-fg:  light-dark(oklch(20% 0.03 60), oklch(14% 0.020 210));
  /* light side is --primary darkened 62%→50% L (same 0.16/195 chroma+hue):
     opaque --primary alone tops out at 2.90:1 against Caribbean's pale
     sand/foam ground — see scripts/check-ring-contrast.mjs (design-system#20) */
  --ring:       light-dark(color-mix(in oklch, oklch(50% 0.16 195) 80%, transparent), color-mix(in oklch, var(--primary) 55%, transparent));
  --font-display: 'Fraunces', Georgia, serif;
  /* reef at depth — the dark side of each pair above. (design-system#10) */
}

[data-theme="south"] {
  --bg:         light-dark(oklch(95% 0.015 130), oklch(16% 0.020 145));
  --bg-elev:    light-dark(oklch(98% 0.008 130), oklch(20% 0.022 145));
  --fg:         light-dark(oklch(22% 0.03 145), oklch(93% 0.015 140));
  --fg-muted:   light-dark(oklch(42% 0.035 145), oklch(70% 0.020 145));
  --fg-subtle:  light-dark(oklch(52.5% 0.03 145), oklch(59.5% 0.020 145));
  --border:     light-dark(oklch(83% 0.02 130), oklch(30% 0.025 145));
  --surface:    light-dark(oklch(98% 0.008 130), oklch(20% 0.022 145));
  --surface-2:  light-dark(oklch(89% 0.02 130), oklch(26% 0.026 145));
  --primary:    light-dark(oklch(38% 0.08 150), oklch(68% 0.14 150));
  --primary-fg: light-dark(oklch(97% 0.012 130), oklch(14% 0.018 145));
  --accent:     light-dark(oklch(55% 0.14 35), oklch(72% 0.15 35));
  --accent-fg:  light-dark(oklch(98% 0.006 35), oklch(14% 0.018 145));
  --ring:       light-dark(color-mix(in oklch, var(--primary) 65%, transparent), color-mix(in oklch, var(--primary) 65%, transparent));
  --font-display: 'Fraunces', Georgia, serif;
  /* rainforest canopy at night — the dark side of each pair above. (design-system#10) */
}

[data-theme="loud"] {
  --bg:         light-dark(oklch(98% 0.008 90), oklch(12% 0.015 20));
  --bg-elev:    light-dark(oklch(100% 0 0), oklch(16% 0.018 20));
  --fg:         light-dark(oklch(15% 0.03 270), oklch(96% 0.005 90));
  --fg-muted:   light-dark(oklch(38% 0.04 270), oklch(72% 0.012 270));
  --fg-subtle:  light-dark(oklch(55% 0.03 270), oklch(58% 0.012 270));
  --border:     light-dark(oklch(85% 0.015 270), oklch(28% 0.020 20));
  --surface:    light-dark(oklch(100% 0 0), oklch(16% 0.018 20));
  --surface-2:  light-dark(oklch(92% 0.02 90), oklch(22% 0.022 20));
  --primary:    light-dark(oklch(58% 0.22 20), oklch(65% 0.24 20));
  --primary-fg: light-dark(oklch(99% 0 0), oklch(15% 0.02 20));
  --accent:     light-dark(oklch(72% 0.20 60), oklch(78% 0.20 60));
  --accent-fg:  light-dark(oklch(15% 0.03 270), oklch(12% 0.015 20));
  --ring:       light-dark(color-mix(in oklch, var(--primary) 75%, transparent), color-mix(in oklch, var(--primary) 75%, transparent));
  --font-display: 'Archivo Black', Impact, sans-serif;
  --font-body:   'Inter', system-ui, sans-serif;

  /* --primary-fg, dark side: white (oklch(99% 0 0)) measured 3.57:1 on the
     dark --primary — every sibling theme uses a ~14-18% L dark value in its
     own primary hue here instead
     (see coast/mountain/mediterranean/latin/caribbean/south/arena/editorial
     above and below); loud was the one outlier, carried as an allow-listed
     exception (loud:dark:--primary-fg:--primary) since v0.7.0. loud is not
     used by any live site (the-co + studios = mountain, logic = arena; loud
     appears only on 5 lookbook demo pages), so this is a low-risk dark-on-
     red flip matching the sibling pattern — measured 5.38:1. Alternative
     considered: keep white + the allow-list entry — reverting this one
     commit (and re-adding the ALLOW entry removed alongside it) restores
     that. (simplify SITE F1) */
  /* boombox in an alley — the dark side of each pair above. (design-system#10) */
}

/* Arena — dark-native, one palette, one block. color-scheme: dark in every
   scheme: this block follows the [data-color-scheme="light"] pin (declared
   right after :root) in source order, so it wins that tie (G1 asserts the
   order) — the UA paints its scrollbars and form controls dark, and every
   :root light-dark() pair on the page — semantic chips, --ai-diff-* —
   resolves to its dark side on Arena even under an explicit light pin.
   Measured on 0.10.0: <html data-theme="arena" data-color-scheme="light">
   computed --bg: oklch(22% 0.02 40), Arena's own ground, on both a light
   and a dark OS. Its surface tokens are plain values (no pair — there is
   no light side); its shadows and --ai-diff-* mixes sit here too, and the
   generic residue rules at the end exclude Arena so they are assigned
   once. The per-chip Arena overrides that existed until 0.11.0 were a
   second copy of the :root pairs' dark side. (design-system#10) */
[data-theme="arena"] {
  color-scheme: dark;
  --bg:         oklch(22% 0.02 40);
  --bg-elev:    oklch(28% 0.025 40);
  --fg:         oklch(92% 0.015 80);
  --fg-muted:   oklch(72% 0.02 80);
  --fg-subtle:  oklch(65% 0.02 70);
  --border:     oklch(35% 0.03 40);
  --surface:    oklch(28% 0.025 40);
  --surface-2:  oklch(34% 0.03 40);
  --primary:    oklch(68% 0.15 60);
  --primary-fg: oklch(18% 0.02 40);
  --accent:     oklch(55% 0.18 20);
  --accent-fg:  oklch(95% 0.015 80);
  --ring:       color-mix(in oklch, var(--primary) 75%, transparent);
  --font-display: 'Playfair Display', Georgia, serif;
  --shadow-sm:      0 1px 2px oklch(0% 0 0 / 0.3);
  --shadow:         0 4px 12px oklch(0% 0 0 / 0.4);
  --shadow-lg:      0 20px 50px oklch(0% 0 0 / 0.5);
  --ai-diff-bg:     color-mix(in oklch, var(--provenance-ai-drafted) 12%, var(--surface));
  --ai-diff-border: color-mix(in oklch, var(--provenance-ai-drafted) 45%, var(--border));
}

[data-theme="editorial"] {
  /* Pure white editorial — generous whitespace, confident type, restrained
     warm accent. Inspired by New York Review of Books, Apartamento, and
     well-made museum catalogs. NO blue. */
  --bg:         light-dark(oklch(100% 0 0), oklch(14% 0.008 280));         /* pure white */
  --bg-elev:    light-dark(oklch(100% 0 0), oklch(18% 0.010 280));         /* also pure white — no layering */
  --fg:         light-dark(oklch(18% 0.01 280), oklch(94% 0.006 85));     /* near-black, slightly cool */
  --fg-muted:   light-dark(oklch(45% 0.008 280), oklch(72% 0.008 280));    /* medium gray */
  --fg-subtle:  light-dark(oklch(55.5% 0.006 280), oklch(59% 0.008 280));  /* lighter gray — light side lifted from 62% for AA (#11) */
  --border:     light-dark(oklch(90% 0.005 280), oklch(28% 0.012 280));    /* hairline gray */
  --surface:    light-dark(oklch(99% 0.003 280), oklch(18% 0.010 280));    /* barely-off white for cards */
  --surface-2:  light-dark(oklch(96% 0.005 280), oklch(23% 0.012 280));    /* faint wash for alt sections */
  --primary:    light-dark(oklch(48% 0.14 35), oklch(68% 0.15 35));      /* warm burnt sienna — editorial red */
  --primary-fg: light-dark(oklch(99% 0.003 280), oklch(14% 0.008 280));    /* near-white on red */
  --accent:     light-dark(oklch(52% 0.10 150), oklch(68% 0.10 150));     /* muted sage for rare accents */
  --accent-fg:  light-dark(oklch(99% 0.005 150), oklch(14% 0.008 280));
  --ring:       light-dark(color-mix(in oklch, var(--primary) 70%, transparent), color-mix(in oklch, var(--primary) 70%, transparent));
  --font-display: 'Fraunces', Georgia, serif;
  --shadow-sm: 0 1px 2px oklch(0% 0 0 / 0.04);
  --shadow:    0 4px 16px oklch(0% 0 0 / 0.06);
  --shadow-lg: 0 24px 56px oklch(0% 0 0 / 0.08);
  /* Editorial's DARK shadows are not colours, so they cannot ride a
     light-dark() pair: see the editorial-scoped scheme rules at the end. */
  /* broadsheet at midnight — the dark side of each pair above. (design-system#10) */
}

/* ============================================================
   PROFESSION OVERRIDES — "this is my industry" (0.17.0)
   ------------------------------------------------------------
   Theme is the PALETTE. Profession is the AUDIENCE.

   Until 0.17.0 a profession set --accent, --accent-fg and --trust and
   nothing else. Measured on the live lookbook on 2026-10-17, at
   theme=mountain, switching Therapist -> Consultant changed **0.00% of
   the pixels** on the services fixture and on pages/therapist.html, and
   0.00% on the hero fixture between music and consultant (their accents
   were both hue 25). The reason is arithmetic, not taste:

     --accent  was used 3x in this package and 78x in the lookbook
     --primary was used 21x in this package and 463x in the lookbook
     --trust   was used ZERO times anywhere, in any consumer repo

   So the one token a profession actually moved was the one almost
   nothing painted with, and the one token nothing painted with at all.
   A control that changes nothing visible is not a control.

   A profession now carries three things, each of which the eye can
   find:

     1. --accent / --accent-ink — the accent, and the AA-safe text form
        of it (see --accent-ink at :root). base.css paints eyebrows,
        marks, chips, step numbers, quote glyphs, rules and selected
        states with it, so the accent lands on the places a reader's eye
        goes rather than on three decorative gradients.
     2. --font-display — type personality. Every face here is already in
        src/fonts.css, which is the ONE Google Fonts request for the
        family; adding a profession means adding its face there too.
     3. --radius — how soft the corners are. A counselling practice and
        a strategy consultancy do not round their boxes the same amount.

   Hue separation is deliberate, because two professions that resolve to
   neighbouring hues are a switch that does nothing (music and
   consultant were both hue 25 before this release):

     consultant  25   oxide red        sharp, declarative
     coach       75   amber gold       warm, high-energy
     outdoor    145   deep forest      earth; NOT blue — see below
     therapist  170   eucalyptus sage  calm, clinical-adjacent
     petcare    295   iris violet      friendly, at-your-door (0.18.0)
     music      340   mulberry plum    expressive

   NO BLUE. Outdoor's accent was hue 225 — ocean blue — which is inside
   the banned band (see scripts/check-no-blue-primary.mjs and the
   workspace rule: the founder's day-job employer owns a blue). It was
   excused by three BY-NAME entries in that guard's allow-list on the
   grounds that "profession tints --accent only, so it cannot make a
   Waymore surface blue". That reasoning died with this release: the
   accent is now text and marks on every surface. Outdoor is a deep
   forest green, the allow-list entries are deleted, and the guard now
   covers professions with nothing excused.

   --trust is GONE. It was declared five times and read nowhere — grepped
   across every repo in the workspace before deleting. If a trust accent
   is wanted later it should be a semantic token with a contrast
   assertion behind it, not a fourth colour nobody paints with.
   ============================================================ */

[data-profession="therapist"] {
  /* Eucalyptus sage. Moved 155 -> 170 to open a gap against outdoor's
     forest green; the two were 10 degrees apart and read as one colour. */
  --accent:     light-dark(oklch(51% 0.07 170), oklch(72% 0.09 170));
  --accent-fg:  light-dark(oklch(98% 0.005 170), oklch(15% 0.015 170));
  /* A soft serif: a first session is a conversation, not a pitch. */
  --font-display: 'Fraunces', Georgia, serif;
  /* Fraunces draws its italic, and fonts.css asks for it — so there is no
     weight step to declare and every em renders what 0.18.0 rendered. */
  --display-em-style:  italic;
  /* All four, in order: 0.17.0 said a profession carries corner
     personality, and three of four is how the scale inverted. */
  --radius-sm:  0.4375rem;
  --radius:     0.75rem;
  --radius-lg:  1.25rem;
  --radius-xl:  1.875rem;
}

[data-profession="coach"] {
  /* Warm gold — unchanged hue. waymorebetterstudios.com runs
     data-profession="coach" on all ten pages, so a hue move here is a
     live brand change rather than a lookbook one. */
  --accent:     light-dark(oklch(60% 0.12 75), oklch(75% 0.14 75));
  --accent-fg:  light-dark(oklch(16% 0.03 60), oklch(15% 0.015 60));
  /* A confident grotesk. Archivo Black has one weight (900) and is meant
     for display sizes only — which is what --font-display is for. */
  --font-display: 'Archivo Black', 'Inter', sans-serif;
  /* Archivo Black ships ONE weight (900) and no italic at all, so both
     levers are blunt here: the slant would be synthesised and the weight
     cannot move. Colour carries the emphasis on a coach surface, and the
     `<em>` element carries it for assistive tech. The 700 is what the
     'Inter' fallback renders if the display face fails to load. */
  --display-em-style:  normal;
  --display-em-weight: 700;
  /* The :root scale exactly, stated so it is a choice rather than an
     omission — and so coach renders the pixels it rendered in 0.18.0. */
  --radius-sm:  0.375rem;
  --radius:     0.625rem;
  --radius-lg:  1rem;
  --radius-xl:  1.5rem;
}

[data-profession="music"] {
  /* Mulberry plum. Moved 25 -> 340: terracotta-red was indistinguishable
     from consultant's oxide red (measured 0.00% pixel difference on the
     hero fixture). */
  --accent:     light-dark(oklch(52% 0.15 340), oklch(72% 0.16 340));
  --accent-fg:  light-dark(oklch(98% 0.006 340), oklch(15% 0.015 340));
  /* High-contrast expressive display — a recital programme, a poster. */
  --font-display: 'Playfair Display', Georgia, serif;
  /* A high-contrast didone with a drawn italic — the one face here where
     the italic is half the reason to pick the family. */
  --display-em-style:  italic;
  --radius-sm:  0.5rem;
  --radius:     0.875rem;
  --radius-lg:  1.375rem;
  --radius-xl:  2rem;
}

[data-profession="outdoor"] {
  /* Deep forest. Was hue 225 ocean BLUE, which the no-blue rule forbids
     and which three allow-list entries excused; both the colour and the
     entries are gone. Earth and canopy, not water. */
  --accent:     light-dark(oklch(42% 0.12 145), oklch(70% 0.14 145));
  --accent-fg:  light-dark(oklch(98% 0.010 145), oklch(15% 0.018 145));
  /* Condensed display — a trailhead sign, a boat name. */
  --font-display: 'Bebas Neue', 'Archivo Black', sans-serif;
  /* Bebas Neue is one weight (400), all caps, no italic. A trailhead sign
     does not lean. Same honest limit as coach: colour and the element. */
  --display-em-style:  normal;
  --display-em-weight: 700;
  /* A trailhead sign is cut, not moulded. The whole scale is tight, which
     is the point: 2px of softening at the small end, 8px at the top. */
  --radius-sm:  0.125rem;
  --radius:     0.25rem;
  --radius-lg:  0.375rem;
  --radius-xl:  0.5rem;
}

[data-profession="consultant"] {
  /* Oxide red. Chroma eased 0.22 -> 0.20 so --accent-ink stays a colour
     rather than a smear at small sizes. */
  --accent:     light-dark(oklch(55% 0.20 25), oklch(70% 0.21 25));
  --accent-fg:  light-dark(oklch(99% 0.005 25), oklch(14% 0.018 25));
  /* A sharp geometric sans. Space Grotesk is new to src/fonts.css in
     0.17.0; studios/site already loaded it for one specimen page, so it
     is a face the family already uses rather than a tenth family. */
  --font-display: 'Space Grotesk', 'Inter', sans-serif;
  /* Space Grotesk ships NO italic in any weight — this is the profession
     that produced design-system#27. It has the full 400-700 range though,
     so the emphasis goes up a step instead of leaning: 700 against the
     600 of a lockup, 700 against the 500 of a heading. That is the
     treatment logic/site worked out locally on waymorebetterlogic.co and
     the reason these two tokens exist. */
  --display-em-style:  normal;
  --display-em-weight: 700;
  /* Near-square, and now near-square at every step. The inherited
     --radius-sm 0.375rem and --radius-xl 1.5rem were THE bug in
     design-system#26: a focus ring and a hero image rounder than the
     boxes around them, on the one profession whose corners are its
     voice. */
  --radius-sm:  0.0625rem;
  --radius:     0.1875rem;
  --radius-lg:  0.3125rem;
  --radius-xl:  0.4375rem;
}

[data-profession="petcare"] {
  /* Iris violet, hue 295. The sixth profession (0.18.0): dog walkers, pet
     sitters, groomers, house cleaners, mobile services — the trades that
     come to your door.

     WHY 295 AND NOT A GREEN. The hue budget left two openings wide
     enough for P3 (check-profession-identity.mjs, 20 degrees minimum
     against every other accent): ~105 yellow-green and ~280-300 violet.
     105 is the obvious "grass and leashes" read, and it was rejected on
     purpose — outdoor is 145 and therapist is 170, so a third green
     would have put half the professions in one family and made the
     Profession control look like it barely moved. 295 is the largest
     spread available: 45 degrees from music's mulberry and 35 from the
     banned band's violet edge.

     It is NOT blue. The banned band is 200-260 (check-no-blue-primary.mjs,
     and the workspace rule behind it); 295 at this chroma is a clear
     violet-purple, the hue family consumer home-service brands already
     live in. Stated here so the next reader does not have to re-derive
     why a purple is allowed where a blue is not. */
  --accent:     light-dark(oklch(50% 0.16 295), oklch(74% 0.15 295));
  --accent-fg:  light-dark(oklch(99% 0.004 295), oklch(15% 0.016 295));
  /* A rounded geometric sans. Rubik's softened corners read friendly at
     display size without reading like a daycare — the same face has to
     carry a groomer's shopfront and a house cleaner's invoice. New to
     src/fonts.css in 0.18.0. */
  --font-display: 'Rubik', 'Inter', sans-serif;
  /* Rubik draws a true italic. fonts.css did not ask for it in 0.18.0 and
     now does, at the one weight this token renders — a variable family, so
     the italic is a second file the browser fetches only when an <em>
     actually paints. A dog walker's practice name gets a real slant. */
  --display-em-style:  italic;
  /* Rubik's italic is asked for at 500 — the weight every rule that leans
     inherits from the heading or lockup above it. */
  /* The softest corners in the set, and the furthest from consultant's
     near-square 0.1875rem: this is a neighbourhood service, not a
     deliverable. */
  --radius-sm:  0.625rem;
  --radius:     1rem;
  --radius-lg:  1.75rem;
  --radius-xl:  2.5rem;
}

/* ============================================================
   DARK MODE — DS2 (v0.7.0), restructured v0.10.0 (design-system#10),
   light-dark() since v0.12.0 (simplify SITE F7, founder decision D3)
   ------------------------------------------------------------
   Activation, in priority order:
     1. Explicit:  <html data-color-scheme="dark">
                   <html class="dark">           (legacy alias)
     2. Implicit:  the OS preference, when no data-color-scheme
                   attribute is set on <html>.

   STRUCTURE (changed in v0.12.0 — read this before editing):

     :root declares `color-scheme: light dark`, and every colour token
     — in :root, in each [data-theme="x"] block, in each
     [data-profession="x"] block — is a single light-dark(<light>,
     <dark>) pair. The browser picks the side from the element's
     color-scheme. There is no dark payload: nothing below assigns
     --bg, --surface, --primary, ... a second time, so there is no
     specificity race left to lose and no duplicate to keep in sync.

     The two SCHEME PINS (declared right after :root) only set
     color-scheme:
       - an explicit light pin is sovereign over the OS preference;
       - an explicit dark pin (or the legacy .dark class) is dark;
     and Arena's own theme block adds color-scheme: dark — Arena is dark
     in every scheme; it has one palette.
     With `light dark` on :root and no pin, the UA follows the OS on
     its own. component-init.js no longer mirrors the OS into an
     attribute, and no page needs it for correct colour.

     Before v0.12.0 the dark side lived in a per-theme "dark twin" of
     each token and two byte-identical 60-line activation payloads
     (explicit + @media) that assigned every surface token by var()
     indirection onto the twin; a guard (G2) existed only to keep the two copies
     identical. Why that shape existed — v0.9.0's generic dark block
     out-ordered [data-theme="arena"] and painted Arena generic slate,
     and the @media fallback out-specified every theme — is history the
     pair shape makes impossible: a theme's dark side is declared in
     the same declaration as its light side.

   INVARIANTS (enforced — see scripts/check-dark-identity.mjs and
   tests/visual/dark-identity.spec.ts):
     - every theme declares every surface token as a light-dark() pair
       (arena: a plain value, plus color-scheme: dark, declared AFTER the
       light pin in source order); a theme owns exactly one
       [data-theme="x"] block
     - no rule other than :root and the theme/profession blocks assigns
       a surface token — the scheme rules below carry only color-scheme
       and the non-colour residue; the one exception is the @supports
       fallback at the end, which restates :root's light sides

   Non-colour residue (the only scheme-scoped declarations left):
     light-dark() accepts <color> only. The three --shadow-* lists and
     the two --ai-diff-* mixes (a percentage each) cannot be pairs, so
     they keep one scheme rule — five declarations — with an @media
     twin for the OS path. Surface tokens never appear there (G3 fails
     the build if one does).

   Theme mood in the dark — design intent:
     editorial     — broadsheet at midnight: deep cool paper, cream type
     coast         — low moon over water: navy-teal depths, silver foam
     mountain      — campfire cabin: woodsmoke brown, ember glow
     mediterranean — olive grove at dusk: deep warm green-umber
     latin         — fired clay at night: rust-brown darks, terracotta glow
     caribbean     — reef at depth: deep ocean teal, bioluminescent accent
     south         — rainforest canopy: deep pine-black, bromeliad warm
     loud          — boombox in an alley: near-black with neon hot
     arena         — dark-native: Arena's dark IS Arena. Stated
                     explicitly since v0.10.0 rather than assumed.
   ============================================================ */

/* fg-subtle note: every theme's dark fg-subtle was measured below WCAG AA
   against its own dark ground (3.02:1 on Arena, 3.48:1 on Mountain West).
   Lightness was lifted to the minimum that clears 4.5:1 on BOTH the dark
   --bg and the dark --surface; hue and chroma are untouched, so the mood is
   unchanged.

   0.15.0 does the LIGHT half the same way (design-system#9, #11). It was the
   last allow-listed entry in check-contrast.mjs and the single largest source
   of real failures on the live sites — 121 of 176 on waymorebetterstudios.com,
   103 of them --fg-subtle on --surface at 4.08:1, all of it 9–14px text. The
   allow-list entry is gone and the pair is now asserted in both schemes.

   Only the L step moved, by 4.5–8.5 points, hue and chroma untouched:

     theme          light L        on --bg          on --surface
     default/edit.  62  → 55.5%    3.64 → 4.76      3.54 → 4.62
     coast          62  → 53.5%    3.22 → 4.57      3.51 → 4.97
     mountain       58  → 52%      3.61 → 4.64      4.07 → 5.23
     mediterranean  58  → 53%      3.74 → 4.61      4.09 → 5.04
     latin          58  → 53.5%    3.85 → 4.65      4.22 → 5.09
     caribbean      58  → 54%      3.88 → 4.58      4.10 → 4.84
     south          58  → 52.5%    3.67 → 4.61      4.00 → 5.03
     loud           55  (unchanged, 4.59 / 4.86 — already passing)
     arena          65  (unchanged, 5.36 / 4.53 — one palette, already passing)

   The hierarchy survives: --fg-muted sits at L 38–45 and --fg at 15–25, so
   subtle is still 7.5–14 lightness points lighter than muted in every theme.
   The minimum was found by search, not by eye — scripts/check-contrast.mjs is
   the arbiter and no entry has slack above ~0.3. */

/* ---- Non-colour residue ---------------------------------------
   Non-colour tokens light-dark() cannot express. Surface tokens never appear
   here (check-dark-identity.mjs G3 fails the build if one does). The explicit
   rule and the @media twin carry the same five declarations. Both exclude
   Arena, which carries its own five in its theme block (a later rule of
   equal or higher specificity would otherwise override them).
------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  html:not([data-color-scheme="light"]):not([data-theme="arena"]) {
    --shadow-sm:      0 1px 2px oklch(0% 0 0 / 0.35);
    --shadow:         0 4px 12px oklch(0% 0 0 / 0.50), 0 1px 3px oklch(0% 0 0 / 0.30);
    --shadow-lg:      0 20px 50px oklch(0% 0 0 / 0.60), 0 4px 12px oklch(0% 0 0 / 0.40);
    --ai-diff-bg:     color-mix(in oklch, var(--provenance-ai-drafted) 12%, var(--surface));
    --ai-diff-border: color-mix(in oklch, var(--provenance-ai-drafted) 45%, var(--border));
  }
  /* Editorial's own dark shadows on the OS path. (0,2,1) and later, so it
     wins the tie with the generic (0,2,1) rule above. */
  html[data-theme="editorial"]:not([data-color-scheme="light"]) {
    --shadow-sm: 0 1px 2px oklch(0% 0 0 / 0.40);
    --shadow:    0 4px 16px oklch(0% 0 0 / 0.55);
    --shadow-lg: 0 24px 56px oklch(0% 0 0 / 0.65);
  }
}
[data-color-scheme="dark"]:not([data-theme="arena"]), .dark:not([data-theme="arena"]) {
  --shadow-sm:      0 1px 2px oklch(0% 0 0 / 0.35);
  --shadow:         0 4px 12px oklch(0% 0 0 / 0.50), 0 1px 3px oklch(0% 0 0 / 0.30);
  --shadow-lg:      0 20px 50px oklch(0% 0 0 / 0.60), 0 4px 12px oklch(0% 0 0 / 0.40);
  --ai-diff-bg:     color-mix(in oklch, var(--provenance-ai-drafted) 12%, var(--surface));
  --ai-diff-border: color-mix(in oklch, var(--provenance-ai-drafted) 45%, var(--border));
}
/* Editorial's own dark shadows on the explicit path. */
[data-theme="editorial"][data-color-scheme="dark"], [data-theme="editorial"].dark {
  --shadow-sm: 0 1px 2px oklch(0% 0 0 / 0.40);
  --shadow:    0 4px 16px oklch(0% 0 0 / 0.55);
  --shadow-lg: 0 24px 56px oklch(0% 0 0 / 0.65);
}


/* ---- Below the light-dark() floor ----------------------------
   Chrome 123 / Safari 17.5 / Firefox 120 (Baseline 2024). An older browser
   keeps a custom property whose value it cannot parse, so `background:
   var(--bg)` becomes invalid at computed-value time — `unset` — and the
   page renders with NO design-system colour at all (UA black on white).
   This block gives it the themeless light default (Editorial's light
   sides, since 0.15.0 / design-system#21) for the surface tokens instead, plus --ring (base.css's
   focus-ring fallback reads --ring, which IS defined below the floor, so
   its var() fallback never fires — without this the shorthand goes
   invalid and every :focus-visible ring disappears below the floor); the
   brand theme is not restored. check-dark-identity.mjs exempts exactly
   this wrapper from G3 and asserts these values equal the :root pairs'
   light sides. */
@supports not (color: light-dark(#000, #fff)) {
  :root {
    --bg:         oklch(100% 0 0);
    --bg-elev:    oklch(100% 0 0);
    --fg:         oklch(18% 0.01 280);
    --fg-muted:   oklch(45% 0.008 280);
    --fg-subtle:  oklch(55.5% 0.006 280);
    --border:     oklch(90% 0.005 280);
    --surface:    oklch(99% 0.003 280);
    --surface-2:  oklch(96% 0.005 280);
    --primary:    oklch(48% 0.14 35);
    --primary-fg: oklch(99% 0.003 280);
    --accent:     oklch(52% 0.10 150);
    --accent-fg:  oklch(99% 0.005 150);
    --ring:       color-mix(in oklch, var(--primary) 70%, transparent);
  }
}

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