Warm Meridian — Implementation Spec
Warm Meridian — Implementation Spec
Section titled “Warm Meridian — Implementation Spec”Updated: 31/03/2026
Date: 6 March 2026 (original), 31 March 2026 (contrast audit — border/input
darkened) Status: All phases complete — migration finished, zero raw
Tailwind colours remain Source: docs/design/warm-meridian-philosophy.md,
docs/design/warm-meridian-identity.pdf
1. Implementation Summary
Section titled “1. Implementation Summary”All five phases of the Warm Meridian design system migration are complete. The codebase is fully semantic-token-driven with zero raw Tailwind colour classes in components, no HSL fallbacks, and automatic light/dark mode via CSS custom properties.
| Phase | Scope | Status |
|---|---|---|
| 1 | Token swap (globals.css) | Complete |
| 2 | Font swap (Instrument Sans) | Complete |
| 3 | Status component migration | Complete |
| 4 | Domain colour recalibration | Complete |
| 5 | Cleanup (HSL fallbacks, dead deps) | Complete |
2. Architecture
Section titled “2. Architecture”Two-Layer Token System
Section titled “Two-Layer Token System”Primitive tokens (Stone 50-900) | vSemantic tokens (:root for light, .dark for dark) | v@theme inline (registers as Tailwind v4 utilities) | vComponents use `text-freshness-fresh`, `bg-bid-active-bg`, etc.All tokens are defined in app/globals.css. The @theme inline block registers
semantic tokens as first-class Tailwind utilities, enabling IDE autocomplete and
avoiding arbitrary value syntax.
Font Stack
Section titled “Font Stack”- Primary: Instrument Sans (loaded via
next/font/googleinapp/layout.tsx) - CSS variable:
--font-sans: 'Instrument Sans', system-ui, -apple-system, sans-serif - Accessibility override: Dyslexia-friendly mode swaps to Atkinson
Hyperlegible via
a11y.css
3. Primitive Palette — Stone (Hue 48)
Section titled “3. Primitive Palette — Stone (Hue 48)”Warm stone neutrals replace the original cool slate (hue 250). All values use OKLCH colour space.
| Token | Value | Purpose |
|---|---|---|
--stone-50 | oklch(0.98 0.008 48) | Lightest background |
--stone-100 | oklch(0.95 0.010 48) | Secondary/muted surfaces |
--stone-200 | oklch(0.90 0.012 48) | Borders, input borders |
--stone-300 | oklch(0.82 0.014 48) | Subtle dividers |
--stone-400 | oklch(0.65 0.012 48) | Disabled state |
--stone-500 | oklch(0.55 0.014 48) | Muted text |
--stone-600 | oklch(0.45 0.016 48) | Secondary text |
--stone-700 | oklch(0.40 0.016 48) | Strong secondary text |
--stone-800 | oklch(0.25 0.016 48) | Primary text (light mode) |
--stone-900 | oklch(0.18 0.014 48) | Page background (dark mode) |
4. Semantic Tokens — shadcn-Compatible
Section titled “4. Semantic Tokens — shadcn-Compatible”Core Tokens (Light Mode)
Section titled “Core Tokens (Light Mode)”| Token | Value | Notes |
|---|---|---|
--background | oklch(0.98 0.008 48) | Stone 50 |
--foreground | oklch(0.25 0.016 48) | Stone 800 |
--card | oklch(0.99 0.005 48) | Slightly warmer than bg |
--card-foreground | oklch(0.25 0.016 48) | Stone 800 |
--popover | oklch(0.99 0.005 48) | Matches card |
--popover-foreground | oklch(0.25 0.016 48) | Stone 800 |
--primary | oklch(0.65 0.16 55) | Amber 400 — signal colour |
--primary-foreground | oklch(0.99 0.003 48) | White/near-white |
--secondary | oklch(0.95 0.010 48) | Stone 100 |
--secondary-foreground | oklch(0.25 0.016 48) | Stone 800 |
--muted | oklch(0.95 0.010 48) | Stone 100 |
--muted-foreground | oklch(0.55 0.014 48) | Stone 500 |
--accent | oklch(0.95 0.010 48) | Stone 100 |
--accent-foreground | oklch(0.25 0.016 48) | Stone 800 |
--destructive | oklch(0.55 0.20 20) | Rose 400 |
--destructive-foreground | oklch(0.99 0.003 48) | White |
--border | oklch(0.86 0.012 48) | Between Stone 200 (0.90) and Stone 300 (0.82) — darkened for contrast |
--input | oklch(0.86 0.012 48) | Between Stone 200 (0.90) and Stone 300 (0.82) — darkened for contrast |
--ring | oklch(0.65 0.16 55) | Amber 400 (matches primary) |
--success | oklch(0.55 0.15 160) | Teal 400 |
Core Tokens (Dark Mode)
Section titled “Core Tokens (Dark Mode)”| Token | Value | Notes |
|---|---|---|
--background | oklch(0.18 0.014 48) | Stone 900 |
--foreground | oklch(0.95 0.010 48) | Stone 100 |
--card | oklch(0.22 0.014 48) | Elevated surface |
--primary | oklch(0.72 0.14 55) | Lighter amber for dark bg |
--primary-foreground | oklch(0.18 0.014 48) | Stone 900 |
--secondary | oklch(0.25 0.016 48) | Stone 800 |
--muted | oklch(0.25 0.016 48) | Stone 800 |
--muted-foreground | oklch(0.65 0.012 48) | Stone 400 |
--border | oklch(0.30 0.014 48) | Lighter than bg |
--ring | oklch(0.72 0.14 55) | Lighter amber |
--success | oklch(0.45 0.12 160) | Teal (reduced lightness) |
5. Status Semantic Tokens
Section titled “5. Status Semantic Tokens”All status tokens are defined in both :root (light) and .dark blocks, and
registered in @theme inline as Tailwind utilities.
Freshness
Section titled “Freshness”| Token | Light | Dark | Use |
|---|---|---|---|
--color-freshness-fresh | oklch(0.55 0.15 160) | oklch(0.70 0.13 160) | Text/icon |
--color-freshness-fresh-bg | oklch(0.93 0.04 160) | oklch(0.25 0.06 160) | Badge bg |
--color-freshness-aging | oklch(0.60 0.14 70) | oklch(0.72 0.12 70) | Text/icon |
--color-freshness-aging-bg | oklch(0.97 0.02 70) | oklch(0.25 0.04 70) | Badge bg |
--color-freshness-stale | oklch(0.55 0.20 20) | oklch(0.70 0.17 20) | Text/icon |
--color-freshness-stale-bg | oklch(0.95 0.03 20) | oklch(0.25 0.05 20) | Badge bg |
--color-freshness-expired | oklch(0.50 0.20 20) | oklch(0.65 0.17 20) | Text/icon |
--color-freshness-expired-bg | oklch(0.95 0.03 20) | oklch(0.25 0.05 20) | Badge bg |
Confidence
Section titled “Confidence”| Token | Light | Dark | Use |
|---|---|---|---|
--color-confidence-strong | oklch(0.55 0.15 160) | oklch(0.70 0.13 160) | Teal |
--color-confidence-strong-bg | oklch(0.93 0.04 160) | oklch(0.25 0.06 160) | |
--color-confidence-strong-border | oklch(0.80 0.06 160) | oklch(0.35 0.08 160) | |
--color-confidence-partial | oklch(0.60 0.14 70) | oklch(0.72 0.12 70) | Sand |
--color-confidence-partial-bg | oklch(0.97 0.02 70) | oklch(0.25 0.04 70) | |
--color-confidence-partial-border | oklch(0.82 0.06 70) | oklch(0.35 0.06 70) | |
--color-confidence-needs-sme | oklch(0.55 0.12 240) | oklch(0.70 0.10 240) | Blue |
--color-confidence-needs-sme-bg | oklch(0.95 0.02 240) | oklch(0.25 0.04 240) | |
--color-confidence-needs-sme-border | oklch(0.80 0.06 240) | oklch(0.35 0.06 240) | |
--color-confidence-none | oklch(0.55 0.014 48) | oklch(0.65 0.012 48) | Stone (neutral) |
--color-confidence-none-bg | oklch(0.95 0.010 48) | oklch(0.25 0.016 48) | |
--color-confidence-none-border | oklch(0.82 0.014 48) | oklch(0.35 0.014 48) |
Bid Status
Section titled “Bid Status”10 states, each with text, bg, border, and (where applicable) dot tokens.
| State | Hue | Light text | Light bg |
|---|---|---|---|
| Draft | 48 (stone) | oklch(0.55 0.014 48) | oklch(0.95 0.010 48) |
| Discovery | 240 (blue) | oklch(0.55 0.12 240) | oklch(0.95 0.02 240) |
| Active | 70 (sand) | oklch(0.60 0.14 70) | oklch(0.97 0.02 70) |
| In Review | 290 (purple) | oklch(0.55 0.12 290) | oklch(0.95 0.02 290) |
| Export Ready | 195 (cyan) | oklch(0.55 0.15 195) | oklch(0.93 0.04 195) |
| Submitted | 160 (teal) | oklch(0.50 0.15 160) | oklch(0.93 0.04 160) |
| Won | 160 (teal) | oklch(0.50 0.15 160) | oklch(0.93 0.04 160) |
| Lost | 20 (rose) | oklch(0.55 0.20 20) | oklch(0.95 0.03 20) |
| Withdrawn | 48 (stone) | oklch(0.55 0.014 48) | oklch(0.95 0.010 48) |
| Overdue | 20 (rose) | oklch(0.50 0.20 20) | oklch(0.95 0.03 20) |
Full token names follow the pattern: --color-bid-{state},
--color-bid-{state}-bg, --color-bid-{state}-border,
--color-bid-{state}-dot.
Governance
Section titled “Governance”| State | Hue | Light text | Light bg |
|---|---|---|---|
| Pending | 70 (sand) | oklch(0.60 0.14 70) | oklch(0.97 0.02 70) |
| Approved | 160 (teal) | oklch(0.55 0.15 160) | oklch(0.93 0.04 160) |
| Changes | 45 (amber) | oklch(0.60 0.16 45) | oklch(0.97 0.02 45) |
| Reverted | 20 (rose) | oklch(0.55 0.20 20) | oklch(0.95 0.03 20) |
| Draft | 48 (stone) | oklch(0.55 0.014 48) | oklch(0.95 0.010 48) |
Full token names: --color-governance-{state}, -bg, -border.
Streaming Phases
Section titled “Streaming Phases”| Token | Light | Use |
|---|---|---|
--color-phase-analysing | oklch(0.55 0.12 240) | Blue |
--color-phase-drafting | oklch(0.55 0.15 290) | Purple |
--color-phase-quality | oklch(0.60 0.14 70) | Sand |
--color-phase-saving | oklch(0.55 0.12 240) | Blue |
--color-phase-done | oklch(0.55 0.15 160) | Teal |
--color-phase-done-bg | oklch(0.93 0.04 160) | |
--color-phase-done-border | oklch(0.80 0.06 160) | |
--color-phase-active-bg | oklch(0.95 0.02 240) | Active phase container |
--color-phase-active-border | oklch(0.80 0.06 240) |
Template Field Review
Section titled “Template Field Review”| State | Hue | Token prefix |
|---|---|---|
| Unreviewed | 240 (blue) | --color-template-unreviewed |
| Confirmed | 160 (teal) | --color-template-confirmed |
| Rejected | 48 (stone) | --color-template-rejected |
| Manual | 70 (sand) | --color-template-manual |
| Unmapped | 20 (rose) | --color-template-unmapped |
Each has -bg variant for background.
Quality Score
Section titled “Quality Score”| Token | Light | Use |
|---|---|---|
--color-quality-good | oklch(0.55 0.15 160) | Good score (teal) |
--color-quality-good-bg | oklch(0.93 0.04 160) | |
--color-quality-moderate | oklch(0.60 0.14 70) | Moderate score (sand) |
--color-quality-moderate-bg | oklch(0.97 0.02 70) | |
--color-quality-severity-warning | oklch(0.60 0.14 70) | Warning indicator |
--color-quality-severity-info | oklch(0.55 0.12 240) | Info indicator |
Tag Frequency Tiers
Section titled “Tag Frequency Tiers”| Token | Light | Use |
|---|---|---|
--color-tag-rare | oklch(0.60 0.014 48) | Rare tag (stone) |
--color-tag-rare-bg | oklch(0.95 0.010 48) | |
--color-tag-core | oklch(0.55 0.15 160) | Core tag (teal) |
--color-tag-core-bg | oklch(0.93 0.04 160) |
Verified
Section titled “Verified”| Token | Light | Use |
|---|---|---|
--color-verified | oklch(0.55 0.15 160) | Teal |
--color-verified-bg | oklch(0.93 0.04 160) |
Entity Type Badges
Section titled “Entity Type Badges”10 entity types, each with -bg and -text tokens.
| Entity Type | Hue | Light bg | Light text |
|---|---|---|---|
| Organisation | 240 | oklch(0.93 0.04 240) | oklch(0.35 0.12 240) |
| Certification | 160 | oklch(0.93 0.04 160) | oklch(0.35 0.12 160) |
| Regulation | 20 | oklch(0.93 0.04 20) | oklch(0.35 0.15 20) |
| Framework | 300 | oklch(0.93 0.04 300) | oklch(0.35 0.12 300) |
| Capability | 70 | oklch(0.93 0.04 70) | oklch(0.40 0.12 70) |
| Person | 350 | oklch(0.93 0.04 350) | oklch(0.38 0.12 350) |
| Technology | 195 | oklch(0.93 0.04 195) | oklch(0.35 0.12 195) |
| Project | 275 | oklch(0.93 0.04 275) | oklch(0.35 0.12 275) |
| Sector | 55 | oklch(0.93 0.04 55) | oklch(0.40 0.12 55) |
| Product | 170 | oklch(0.93 0.04 170) | oklch(0.35 0.12 170) |
Token names: --color-entity-{type}-bg, --color-entity-{type}-text.
Draft/Publish Actions
Section titled “Draft/Publish Actions”| Token | Light | Use |
|---|---|---|
--color-draft-badge-bg | oklch(0.93 0.04 70) | Draft content badge |
--color-draft-badge-text | oklch(0.50 0.14 70) | |
--color-draft-badge-border | oklch(0.82 0.06 70) | |
--color-action-publish | oklch(0.55 0.14 55) | Publish button |
--color-action-publish-hover | oklch(0.50 0.15 55) |
6. Utility and Infrastructure Tokens
Section titled “6. Utility and Infrastructure Tokens”Progress and Review
Section titled “Progress and Review”| Token | Light | Dark | Use |
|---|---|---|---|
--color-progress | oklch(0.65 0.16 55) | oklch(0.72 0.14 55) | Progress indicator (amber) |
--color-success | oklch(0.55 0.15 160) | oklch(0.45 0.12 160) | General success |
--color-progress-bar | oklch(0.65 0.12 70) | oklch(0.55 0.10 70) | Progress fill (warm sand) |
--color-progress-mid | oklch(0.58 0.10 65) | oklch(0.45 0.08 65) | Mid-progress transition |
--color-progress-complete | oklch(0.55 0.15 160) | oklch(0.45 0.12 160) | Complete (teal) |
--color-review-skip | oklch(0.7 0.08 15) | oklch(0.35 0.08 15) | Skip swipe direction |
--color-review-read | oklch(0.55 0.15 160) | oklch(0.35 0.12 160) | Read swipe direction |
--color-review-star | oklch(0.7 0.15 85) | oklch(0.4 0.12 85) | Star rating |
Highlight and Status
Section titled “Highlight and Status”| Token | Light | Dark | Use |
|---|---|---|---|
--color-highlight-bg | oklch(0.96 0.03 55) | oklch(0.22 0.03 55) | Highlighted card bg |
--color-highlight-border | oklch(0.85 0.06 55) | oklch(0.3 0.05 55) | |
--color-highlight-mark | oklch(0.925 0.12 55) | oklch(0.35 0.08 55) | Search highlight mark |
--color-star | oklch(0.795 0.184 86) | oklch(0.855 0.15 86) | Star icon |
--color-priority-high | oklch(0.637 0.237 25) | oklch(0.704 0.191 22) | High priority |
--color-priority-medium | oklch(0.769 0.188 70) | oklch(0.82 0.15 70) | Medium priority |
--color-priority-low | oklch(0.723 0.191 261) | oklch(0.78 0.15 261) | Low priority |
--color-status-success | oklch(0.65 0.2 160) | oklch(0.72 0.17 160) | |
--color-status-warning | oklch(0.75 0.18 70) | oklch(0.82 0.15 70) | |
--color-status-error | oklch(0.63 0.24 25) | oklch(0.7 0.19 22) | |
--color-status-info | oklch(0.65 0.16 55) | oklch(0.72 0.14 55) |
Component Shadows
Section titled “Component Shadows”| Token | Light | Dark |
|---|---|---|
--shadow-review-bar | 0 -2px 10px oklch(0.18 0.014 48 / 0.08) | 0 -2px 10px oklch(0.10 0.014 48 / 0.15) |
Accessibility Variables
Section titled “Accessibility Variables”| Token | Default | Purpose |
|---|---|---|
--a11y-letter-spacing | normal | Adjusted in high-contrast/dyslexia modes |
--a11y-word-spacing | normal | |
--a11y-line-height | 1.5 | |
--a11y-font-weight | 400 | |
--a11y-paragraph-spacing | 1em | |
--a11y-max-width | none | Constrains line length for readability |
--a11y-font-size-scale | 1 | Multiplier for large text mode |
7. Domain Colours
Section titled “7. Domain Colours”7 domains, each with -bg, -text, and -surface tokens for light and dark.
| Domain | Hue | Light bg | Light text | Notes |
|---|---|---|---|---|
| Security | 220 | oklch(0.93 0.04 220) | oklch(0.35 0.12 220) | Blue — universally security |
| Compliance | 275 | oklch(0.93 0.04 275) | oklch(0.35 0.12 275) | Purple |
| Implementation | 155 | oklch(0.93 0.04 155) | oklch(0.35 0.12 155) | Green |
| Support | 85 | oklch(0.93 0.04 85) | oklch(0.4 0.12 85) | Yellow-green (shifted from 45 to avoid amber overlap) |
| Corporate | 200 | oklch(0.93 0.02 200) | oklch(0.4 0.08 200) | Neutral blue-grey |
| Product | 145 | oklch(0.93 0.04 145) | oklch(0.35 0.12 145) | Teal-green |
| Methodology | 340 | oklch(0.93 0.04 340) | oklch(0.38 0.12 340) | Pink/magenta |
Surface variants (for home page cards) use lightness 0.96 and chroma 0.02 (light) / lightness 0.2 and chroma 0.03 (dark).
8. Chart Colours
Section titled “8. Chart Colours”| Token | Light | Dark |
|---|---|---|
--chart-1 | oklch(0.65 0.16 55) | oklch(0.72 0.14 55) |
--chart-2 | oklch(0.55 0.20 20) | oklch(0.65 0.15 20) |
--chart-3 | oklch(0.55 0.15 160) | oklch(0.55 0.12 160) |
--chart-4 | oklch(0.60 0.14 70) | oklch(0.65 0.12 70) |
--chart-5 | oklch(0.55 0.12 275) | oklch(0.65 0.12 275) |
9. Workspace Colour Picker
Section titled “9. Workspace Colour Picker”12 preset colours harmonised with the Warm Meridian palette, grouped into 3 rows:
| Warm | Organic | Neutral |
|---|---|---|
| Amber (#d4880f) | Teal (#2b9e82) | Slate (#7a756c) |
| Copper (#c27840) | Sage (#5a8a6c) | Charcoal (#5c5750) |
| Terracotta (#c4604a) | Ocean (#3b82b6) | Stone (#a8a49c) |
| Rose (#c25670) | Indigo (#5b6abf) | |
| Plum (#8b5cc0) |
Located in components/workspace-colour-picker.tsx. Custom hex input also
supported.
10. Spacing and Radius
Section titled “10. Spacing and Radius”Defined in the @theme inline block:
| Token | Value |
|---|---|
--radius | 0.5rem |
--radius-sm | calc(var(--radius) - 4px) |
--radius-md | calc(var(--radius) - 2px) |
--radius-lg | var(--radius) |
--radius-xl | calc(var(--radius) + 4px) |
--spacing-1 | 0.25rem (4px grid) |
--spacing-2 | 0.5rem |
--spacing-3 | 0.75rem |
--spacing-4 | 1rem |
--spacing-6 | 1.5rem |
--spacing-8 | 2rem |
--spacing-12 | 3rem |
--spacing-16 | 4rem |
11. Contrast Guidelines
Section titled “11. Contrast Guidelines”Surface Hierarchy
Section titled “Surface Hierarchy”Current token lightness values (OKLCH L channel):
| Token | Lightness | Purpose |
|---|---|---|
white | 1.0 | Header, search inputs, elevated interactive elements |
--card / --popover | 1.0 | Cards, popovers, active toggle states |
--accent / --muted | 0.95 | Hover states, wells, subtle backgrounds |
--background | 0.94 | Page background |
--border / --input | 0.78 | Borders, input outlines |
- Minimum 0.10 OKLCH lightness delta between adjacent surfaces (e.g. border on background, card on page)
- Use
--bordertoken for all component edges (inputs, selects, cards, toggle groups) - Never use opacity modifiers on borders that reduce visibility (e.g.
border-border/60)
Search Inputs
Section titled “Search Inputs”All page-level search inputs use bg-white shadow-sm to lift them clearly above
the 0.94 page background. This applies to browse, guides, Q&A library, entities,
and tags search bars. Search inputs inside overlays (sheets, popovers, drawers)
do not need this treatment as they sit on elevated surfaces already.
Header
Section titled “Header”The site header uses bg-white (not bg-background) for clear separation from
the page background (0.94). It includes a warm shadow
(shadow-[0_1px_3px_oklch(0.18_0.014_48/0.06)]) and border-b border-border.
Active nav tabs use font-semibold text-foreground; inactive tabs use
text-muted-foreground.
Tailwind v4 Border Behaviour
Section titled “Tailwind v4 Border Behaviour”Tailwind v4 removed the default border-color: var(--border) preflight. Bare
border now uses currentColor. The global rule in globals.css
(*, ::after, ::before { border-color: var(--border) }) restores the expected
behaviour. Settings cards intentionally rely on currentColor for dark borders,
so this rule must remain but should not be extended to override per-component
border colours.
Select Triggers
Section titled “Select Triggers”SelectTrigger uses bg-card (not bg-transparent) for a warm near-white fill
against page backgrounds. The dark:bg-input/30 override still applies for dark
mode.
Toggle Groups
Section titled “Toggle Groups”Segmented toggles (e.g. coverage Cards/Heatmap) use a well pattern: container
has bg-muted/50, active button has bg-card shadow-sm (raised from the well),
inactive button has hover:bg-card/50.
12. Prose Colour Overrides
Section titled “12. Prose Colour Overrides”Typography plugin colours inherit from semantic tokens for automatic dark mode:
.prose { --tw-prose-body: var(--foreground); --tw-prose-headings: var(--foreground); --tw-prose-links: var(--primary); --tw-prose-bold: var(--foreground); --tw-prose-counters: var(--muted-foreground); --tw-prose-bullets: var(--muted-foreground); --tw-prose-hr: var(--border); --tw-prose-quotes: var(--foreground); --tw-prose-quote-borders: var(--border); --tw-prose-captions: var(--muted-foreground); --tw-prose-code: var(--foreground); --tw-prose-pre-code: var(--foreground); --tw-prose-pre-bg: var(--muted); --tw-prose-th-borders: var(--border); --tw-prose-td-borders: var(--border);}12. Migration Status
Section titled “12. Migration Status”All migrations complete. Verification results:
- Zero raw Tailwind colour classes (
text-green-*,bg-amber-*, etc.) in component files - Zero HSL fallbacks in component files
tailwindcss-animatedependency removed- All 23 shadcn/ui components fully semantic (no changes needed)
components/domain-badge.tsx— usesvar(--domain-{name}-bg/text/surface)components/verification-badge.tsx— usesvar(--color-status-success)- Workspace colour picker — 12 hex presets recalibrated for warm palette
- Review action bar shadow — tokenised as
--shadow-review-bar - Font — Instrument Sans via
next/font/google
Total Token Count
Section titled “Total Token Count”| Category | Tokens (light + dark) |
|---|---|
| Primitive (Stone) | 10 |
| Core semantic (shadcn) | ~30 per mode |
| Freshness | 8 per mode |
| Confidence | 12 per mode |
| Bid status | ~40 per mode |
| Governance | 15 per mode |
| Streaming phases | 9 per mode |
| Template field review | 10 per mode |
| Quality score | 6 per mode |
| Tag frequency | 4 per mode |
| Entity type badges | 20 per mode |
| Domain colours | 21 per mode |
| Gap summary banner | 7 per mode |
| Draft/publish actions | 5 per mode |
| Verified | 2 per mode |
| Progress/review | 8 per mode |
| Highlight/status/priority | ~12 per mode |
| Accessibility | 7 (mode-independent) |
| Total | ~230 light + ~200 dark |
Implementation spec produced: 6 March 2026 Token reference updated: 23 March 2026 — all phases complete, 0 raw colours remaining