Simplify: drop the token compiler, contrast test suite, Sass build, and ERB capture-buffer trickery; freeze everything to static CSS and content
The site now hand-maintains assets/css/tokens.css directly instead of
compiling it from _data/tokens/*.yml, concatenates plain CSS under _sass/
into style.css instead of running a Sass compiler, and renders component
demos as plain static <figure>/<details> markup instead of an ERB helper
that captured and re-rendered a block twice. Foundation pages that used to
loop over token data at build time (color, typography, space, elevation,
motion) now contain the already-computed, already-verified swatch grids,
tables, and contrast figures as static HTML, transcribed from the last
known-good build rather than recomputed.

lib/arbitrary_definitions/token_set.rb, example_helper.rb, and
color_math.rb are gone, along with the Minitest suite that gated the
deploy on WCAG contrast math — contrast facts are documented as prose now,
not asserted at build time. sass-embedded, rouge, and minitest are out of
the Gemfile; the CI workflow no longer runs `rake test`.

Also: the site's own documentation prose now renders at a plain 16px
(scoped to .ad-doc-main) instead of inheriting the design system's own
--ad-step-0 body-text token (18-20px) verbatim, which reads too large for
long-form docs; the token itself and the Typography foundation page are
unchanged. And a full sweep of British spelling in prose, comments, and
titles to American throughout the repo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013jsk87pdEh7a2BNyqk5vJQ
change
commit 43826d7d771373cd576a44ebbc116c7090b3e0a2
author Claude <noreply@anthropic.com>
date
parent 3fe11d17
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 7a288ad..99382ee 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -38,12 +38,6 @@ jobs:
       - name: Report the toolchain
         run: ruby -v
 
-      # The token tests are the deploy gate: they assert the contrast floors,
-      # that every ramp darkens monotonically, and that no unresolved token
-      # reference reaches the stylesheet.
-      - name: Test the token compiler
-        run: bundle exec rake test
-
       # The baseurl comes from _config.yml rather than actions/configure-pages,
       # so the build does not depend on the Pages API being reachable and stays
       # green on branches and forks. Change it there if the site ever moves off
@@ -56,8 +50,6 @@ jobs:
           test -s _site/index.html
           test -s _site/assets/css/tokens.css
           grep -q -- '--ad-color-accent:' _site/assets/css/tokens.css
-          # A reference that failed to resolve would appear verbatim in the CSS.
-          ! grep -Eq ':[[:space:]]*[a-z]+\.[a-z0-9]+;' _site/assets/css/tokens.css
           # Every page in the nav should have been rendered.
           for page in rationale install foundations/color foundations/typography \
                       foundations/space foundations/elevation foundations/motion \
diff --git a/Gemfile b/Gemfile
index 232564a..ce189f5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,14 +5,14 @@ source "https://rubygems.org"
 # This site is built on Ruby 4.0.
 # ruby "~> 4.0" # temporarily disabled for local verification under ruby 3.3.6
 
-# Markdown rendering (GFM) and syntax highlighting for the ERB-rendered pages
-# — see lib/arbitrary_definitions/site_builder.rb.
+# Markdown rendering (GFM) for the ERB-rendered pages — see
+# lib/arbitrary_definitions/site_builder.rb. No syntax highlighter: code
+# blocks render as plain, unhighlighted <pre><code>.
 gem "kramdown", "~> 2.5"
 gem "kramdown-parser-gfm", "~> 1.1"
-gem "rouge", "~> 4.0"
 
-# Compiles _sass/**/*.scss into assets/css/style.css.
-gem "sass-embedded", "~> 1.75"
+gem "rake", "~> 13.2"
+gem "webrick", "~> 1.9"
 
 # Standard library that ships as a bundled gem from Ruby 3.4/4.0 onward, but
 # still has to be declared explicitly rather than assumed.
@@ -22,9 +22,3 @@ gem "bigdecimal", "~> 3.1"
 gem "csv", "~> 3.3"
 gem "logger", "~> 1.6"
 gem "ostruct", "~> 0.6"
-
-group :development, :test do
-  gem "minitest", "~> 5.25"
-  gem "rake", "~> 13.2"
-  gem "webrick", "~> 1.9"
-end
diff --git a/Gemfile.lock b/Gemfile.lock
index 0de3830..d023d42 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -5,74 +5,14 @@ GEM
     benchmark (0.5.0)
     bigdecimal (3.3.1)
     csv (3.3.6)
-    google-protobuf (4.36.0)
-      bigdecimal
-      rake (~> 13.3)
-    google-protobuf (4.36.0-aarch64-linux-gnu)
-      bigdecimal
-      rake (~> 13.3)
-    google-protobuf (4.36.0-aarch64-linux-musl)
-      bigdecimal
-      rake (~> 13.3)
-    google-protobuf (4.36.0-arm64-darwin)
-      bigdecimal
-      rake (~> 13.3)
-    google-protobuf (4.36.0-x86-linux-gnu)
-      bigdecimal
-      rake (~> 13.3)
-    google-protobuf (4.36.0-x86-linux-musl)
-      bigdecimal
-      rake (~> 13.3)
-    google-protobuf (4.36.0-x86_64-darwin)
-      bigdecimal
-      rake (~> 13.3)
-    google-protobuf (4.36.0-x86_64-linux-gnu)
-      bigdecimal
-      rake (~> 13.3)
-    google-protobuf (4.36.0-x86_64-linux-musl)
-      bigdecimal
-      rake (~> 13.3)
     kramdown (2.5.2)
       rexml (>= 3.4.4)
     kramdown-parser-gfm (1.1.0)
       kramdown (~> 2.0)
     logger (1.7.0)
-    minitest (5.27.0)
     ostruct (0.6.3)
     rake (13.4.2)
     rexml (3.4.4)
-    rouge (4.7.0)
-    sass-embedded (1.103.1)
-      google-protobuf (~> 4.31)
-      rake (>= 13)
-    sass-embedded (1.103.1-aarch64-linux-android)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-aarch64-linux-gnu)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-aarch64-linux-musl)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-arm-linux-androideabi)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-arm-linux-gnueabihf)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-arm-linux-musleabihf)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-arm64-darwin)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-riscv64-linux-android)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-riscv64-linux-gnu)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-riscv64-linux-musl)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-x86_64-darwin)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-x86_64-linux-android)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-x86_64-linux-gnu)
-      google-protobuf (~> 4.31)
-    sass-embedded (1.103.1-x86_64-linux-musl)
-      google-protobuf (~> 4.31)
     webrick (1.9.2)
 
 PLATFORMS
@@ -102,11 +42,8 @@ DEPENDENCIES
   kramdown (~> 2.5)
   kramdown-parser-gfm (~> 1.1)
   logger (~> 1.6)
-  minitest (~> 5.25)
   ostruct (~> 0.6)
   rake (~> 13.2)
-  rouge (~> 4.0)
-  sass-embedded (~> 1.75)
   webrick (~> 1.9)
 
 CHECKSUMS
@@ -115,38 +52,12 @@ CHECKSUMS
   bigdecimal (3.3.1) sha256=eaa01e228be54c4f9f53bf3cc34fe3d5e845c31963e7fcc5bedb05a4e7d52218
   bundler (4.0.16) sha256=d6ca5dd440c24f9abce9844cf44cc8e18c6a553de65a47efb4544137af92c47d
   csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456
-  google-protobuf (4.36.0) sha256=2132fb002366b85a57fbb796333e2cb1f969126e05f9af32149709ba3fd397b5
-  google-protobuf (4.36.0-aarch64-linux-gnu) sha256=3fa22004853b83297d8ad4ca5c9adc8aea5e6da7b4caadf5577cb9da3eefe63f
-  google-protobuf (4.36.0-aarch64-linux-musl) sha256=6392f5e7cc5366203349f268c98e39f29c2ef9766ebbdb434819d7edb085a12c
-  google-protobuf (4.36.0-arm64-darwin) sha256=be182aac1cf55ccbbd27ec5a94d5b012069075f5c879d65423d33e6b2dec86f5
-  google-protobuf (4.36.0-x86-linux-gnu) sha256=34c327aaf29e51db012e537cce9d2e38359d9ec96cb4a699eaf47231db6020ab
-  google-protobuf (4.36.0-x86-linux-musl) sha256=621b53fd93f88cb68d3b3d25e5ecda8f9ea2977f86759c1badb701de5ae371d6
-  google-protobuf (4.36.0-x86_64-darwin) sha256=6cb938760546ee5ce57d04ed358ea10d0278b6f1ddea35e100c278f429afc25b
-  google-protobuf (4.36.0-x86_64-linux-gnu) sha256=8c253f7cb7647cdf1f54fc3f215e151206b5159e03d74fe29b8599264903de2b
-  google-protobuf (4.36.0-x86_64-linux-musl) sha256=d6c510ff9743b3dcde6acdea06512e4e4ecf53246e5c5b689879861e05d4738a
   kramdown (2.5.2) sha256=1ba542204c66b6f9111ff00dcc26075b95b220b07f2905d8261740c82f7f02fa
   kramdown-parser-gfm (1.1.0) sha256=fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729
   logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
-  minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
   ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
   rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
   rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
-  rouge (4.7.0) sha256=dba5896715c0325c362e895460a6d350803dbf6427454f49a47500f3193ea739
-  sass-embedded (1.103.1) sha256=a47bdc1dc3de2a8181c68f8da1396748eb8776dde71df08daa080dccc38f5cf0
-  sass-embedded (1.103.1-aarch64-linux-android) sha256=5905fb8567192c8b391e5aa2db8d3db9f7e866c5eff32ec3be96ac79a389085d
-  sass-embedded (1.103.1-aarch64-linux-gnu) sha256=48790fb0be3c57177121f28626a7c5cfc258a06aa499f09fd80a55054cc46c09
-  sass-embedded (1.103.1-aarch64-linux-musl) sha256=fb0bfcbab2e7baf8e6a59890e2729037cd1651859718e7c7a5878d5f9e2a6e8c
-  sass-embedded (1.103.1-arm-linux-androideabi) sha256=a2edbc498e324c51fc5b3b7a4168b75295d33cc95e49cb1dbf2aeea7e4150752
-  sass-embedded (1.103.1-arm-linux-gnueabihf) sha256=1388f692fd7c76d3fe03fb8d79a45ca89586a1fa19a8718faf875f457bed4169
-  sass-embedded (1.103.1-arm-linux-musleabihf) sha256=9e2a3cf86576d96fd16ebd1fbd4333e72c3df6b1990e5273d5a3b4d2e1eec4ac
-  sass-embedded (1.103.1-arm64-darwin) sha256=b4afa0515a8bdb9949675bc85a3625b676bd11080b4d5b5134282729762a2261
-  sass-embedded (1.103.1-riscv64-linux-android) sha256=3de9d13ba48e93fd8c361c576cbadbc89d60b98223cbb932bf5b7ce4167ebd03
-  sass-embedded (1.103.1-riscv64-linux-gnu) sha256=291f37e6d7317dc9fc07e264b8f86a7d156d68e2d3ef55b9014bb2d88d0b5234
-  sass-embedded (1.103.1-riscv64-linux-musl) sha256=49f72d4b31d668f0b90f8c02e507840078a66a91fbc96570254a9a3b3163b2b9
-  sass-embedded (1.103.1-x86_64-darwin) sha256=75514c8f62a849e5e2e98fe89268176278fb0900751208a7ae3322451a0b5285
-  sass-embedded (1.103.1-x86_64-linux-android) sha256=23e4545427223ceb3c9b293e10e5eaf60f2499151d2abdc647897a086e410775
-  sass-embedded (1.103.1-x86_64-linux-gnu) sha256=1056cb8031fbceee992a3d07c39e9118864aa280e1ba03d85bc5397bd4479333
-  sass-embedded (1.103.1-x86_64-linux-musl) sha256=2f52d9f70030a2d8ddbff8289049df1d38f20f55ba05a87a0049d99fbaa1108c
   webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131
 
 BUNDLED WITH
diff --git a/README.md b/README.md
index 4db07c3..0f697b8 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,7 @@
 A personal design system for self-hosted, single-user tools — synthesized by
 reading three real, running personal projects, not written from a spec.
 Sixteen components and five foundations, documented by a small Ruby/ERB site
-(no Jekyll) that renders every swatch, table, and demo from the same YAML the
-stylesheet is compiled from.
+(no Jekyll) with hand-maintained CSS and hand-written content.
 
 **Site:** https://kejadlen.github.io/arbitrary-definitions-design-system/
 
@@ -25,23 +24,23 @@ It was synthesized, not specified — built by reading three real projects
 share. See [Why arbitrary](https://kejadlen.github.io/arbitrary-definitions-design-system/rationale/)
 for the full story.
 
-So this repository has exactly one source of truth — four YAML files under
-`_data/tokens/` — and everything else is derived from it at build time:
+This is a small, personal project, so it is built as plainly as it can be:
 
-- `assets/css/tokens.css` is generated by
-  `lib/arbitrary_definitions/token_set.rb`: a `:root` block, both dark-theme
-  selectors, and a `prefers-reduced-motion` override that zeroes every
-  duration token.
-- The documentation tables and swatches loop over the same YAML in plain ERB.
-- Contrast ratios on the colour page are computed during the build by
-  `lib/arbitrary_definitions/color_math.rb`, so a number on the page cannot
-  disagree with the hex it describes.
-- Component demos are rendered by an `example` ERB helper
-  (`lib/arbitrary_definitions/example_helper.rb`) that captures its block's
-  markup and outputs the live component and its highlighted source from that
-  one captured string, so the snippet can never drift from the demo.
+- `assets/css/tokens.css` is a hand-maintained stylesheet of custom
+  properties — a `:root` block, both dark-theme selectors, and a
+  `prefers-reduced-motion` override that zeroes every duration token. Edit
+  the values directly; there is no compiler in front of it.
+- `_sass/` holds the component and site-chrome CSS as plain, already-flat
+  files (one per component group). `rake build` concatenates them in a fixed
+  order into `assets/css/style.css` — no Sass compiler involved.
+- The documentation tables, swatches, and contrast figures are static
+  markup, transcribed once from the token values rather than recomputed on
+  every build.
+- Component demos are plain HTML: a `<figure>` with the live preview and,
+  underneath it in a `<details>`, the exact same markup as literal text.
+  There is no generator keeping the two in sync — there is only one copy.
 
-No component stylesheet contains a colour literal or a duration — every one is a `var(--ad-*)`, with one flagged exception (Select's chevron — see `_sass/components/_field.scss`).
+No component stylesheet contains a color literal or a duration — every one is a `var(--ad-*)`, with one flagged exception (Select's chevron — see `_sass/components/field.css`).
 
 ## Requirements
 
@@ -49,41 +48,20 @@ Ruby 4.0 (`.ruby-version` pins 4.0.6) and Bundler.
 
 ```console
 $ bundle install
-$ bundle exec rake          # tests, then build
+$ bundle exec rake          # build
 $ bundle exec rake serve    # http://127.0.0.1:4000
 ```
 
-## Tests
-
-`test/` is plain Minitest against `lib/`. The suite is the deploy gate in CI,
-and it asserts things that are easy to break by accident:
-
-- every pairing in `_data/tokens/contrast.yml` holds in **both** themes at
-  WCAG AA, and every `color-text-*` token except the one documented,
-  intentionally sub-AA exception is covered by the contract;
-- every lightness ramp darkens (or, for the dark-mode ramp, lightens)
-  monotonically step to step — the flat, five-hue accent palette opts out
-  explicitly, since it is not a lightness scale;
-- every semantic token resolves in both themes and documents its usage;
-- no unresolved `ramp.step` reference reaches the generated CSS, and its
-  braces balance.
-
-```console
-$ bundle exec rake test
-0 failures, 0 errors, 0 skips
-```
-
 ## Layout
 
 | Path | What it is |
 | --- | --- |
-| `_data/tokens/` | Every value in the system: ramps, semantic aliases, scales, and the contrast contract. |
-| `lib/arbitrary_definitions/` | The token compiler, WCAG colour maths, and the whole site builder (front matter, ERB, Kramdown, layouts, the `example` helper). No Jekyll anywhere. |
+| `assets/css/tokens.css` | Every color, type, space, radius, elevation, and motion value in the system, hand-maintained. |
+| `_sass/` | Plain CSS: base/reset, components (core/forms/feedback/navigation), and this documentation site's own chrome. Concatenated into `style.css` at build time. |
+| `lib/arbitrary_definitions/` | The whole site builder: front matter, ERB, and Kramdown, wrapped in a layout. No Jekyll anywhere. |
 | `_layouts/`, `_includes/` | Plain ERB templates for the page chrome. |
-| `_sass/` | Base, components (core/forms/feedback/navigation), and the documentation site's own chrome. |
-| `_foundations/`, `_components/`, `_patterns/` | The documentation pages, in Markdown with embedded ERB. |
-| `test/` | Minitest suite. |
-| `.github/workflows/pages.yml` | Build on Ruby 4.0, test, deploy to Pages. |
+| `_foundations/`, `_components/`, `_patterns/` | The documentation pages — static Markdown and HTML with a little ERB for links and nav. |
+| `.github/workflows/pages.yml` | Build on Ruby 4.0, deploy to Pages. |
 
 ## Deployment
 
@@ -94,13 +72,12 @@ so it runs on the pinned Ruby 4.0 rather than whatever Ruby the Pages runner
 ships. `.github/workflows/pages.yml` builds it with `ruby/setup-ruby` and
 uploads the result with `actions/deploy-pages`.
 
-Every branch is built and tested; only the repository's default branch
-deploys.
+Every branch is built; only the repository's default branch deploys.
 
 **One-time setup:** set **Settings → Pages → Build and deployment → Source**
 to **GitHub Actions**. The workflow token is not permitted to turn Pages on
-by itself, so the `deploy` job fails until this is done — the `build` job,
-tests included, runs regardless.
+by itself, so the `deploy` job fails until this is done — the `build` job
+runs regardless.
 
 The site's `baseurl` lives in `_config.yml` rather than coming from
 `actions/configure-pages`, which keeps the build independent of the Pages
diff --git a/Rakefile b/Rakefile
index b008b09..8bd405d 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,15 +1,7 @@
 # frozen_string_literal: true
 
-require "rake/testtask"
-
 $LOAD_PATH.unshift(File.expand_path("lib", __dir__))
 
-Rake::TestTask.new(:test) do |t|
-  t.libs << "lib" << "test"
-  t.test_files = FileList["test/test_*.rb"]
-  t.warning = false
-end
-
 desc "Build the site into _site"
 task :build do
   require "arbitrary_definitions/site_builder"
@@ -42,5 +34,4 @@ task design: :build do
   sh "ruby script/build_design_bundle.rb"
 end
 
-desc "Run the token and colour tests, then build"
-task default: %i[test build]
+task default: :build
diff --git a/_components/badge.md b/_components/badge.md
index 5e238ad..b54d7b7 100644
--- a/_components/badge.md
+++ b/_components/badge.md
@@ -10,22 +10,41 @@ Labels are catalog-style: short, mono, uppercase — `SAVED`, `RECOMMENDED`,
 
 ## Tones
 
-<% example(title: "Tones", open: true) do %>
+<figure class="example">
+<figcaption>Tones</figcaption>
+<div>
 <span class="ad-badge">Unassigned</span>
 <span class="ad-badge ad-badge--accent">Draft</span>
 <span class="ad-badge ad-badge--success">Saved</span>
 <span class="ad-badge ad-badge--danger">Overdue</span>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;span class=&quot;ad-badge&quot;&gt;Unassigned&lt;/span&gt;
+&lt;span class=&quot;ad-badge ad-badge--accent&quot;&gt;Draft&lt;/span&gt;
+&lt;span class=&quot;ad-badge ad-badge--success&quot;&gt;Saved&lt;/span&gt;
+&lt;span class=&quot;ad-badge ad-badge--danger&quot;&gt;Overdue&lt;/span&gt;</code></pre>
+</details>
+</figure>
 
 ## In a set
 
-<% example(title: "Badge set") do %>
+<figure class="example">
+<figcaption>Badge set</figcaption>
+<div>
 <div class="ad-badge-set">
   <span class="ad-badge ad-badge--accent">Recommended</span>
   <span class="ad-badge">Every 2 weeks</span>
   <span class="ad-badge ad-badge--danger">3d overdue</span>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-badge-set&quot;&gt;
+  &lt;span class=&quot;ad-badge ad-badge--accent&quot;&gt;Recommended&lt;/span&gt;
+  &lt;span class=&quot;ad-badge&quot;&gt;Every 2 weeks&lt;/span&gt;
+  &lt;span class=&quot;ad-badge ad-badge--danger&quot;&gt;3d overdue&lt;/span&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## Rules
 
diff --git a/_components/button.md b/_components/button.md
index ed35e94..03f421e 100644
--- a/_components/button.md
+++ b/_components/button.md
@@ -13,34 +13,59 @@ committing action in a screen, typically a final Save; it should be rare.
 `ghost` has no border or fill, for dismissals and tertiary actions. `danger`
 is destructive and irreversible only.
 
-<% example(title: "Variants", open: true) do %>
+<figure class="example">
+<figcaption>Variants</figcaption>
+<div>
 <button type="button" class="ad-btn">Cancel</button>
 <button type="button" class="ad-btn ad-btn--primary">Save</button>
 <button type="button" class="ad-btn ad-btn--dark">Finish setup</button>
 <button type="button" class="ad-btn ad-btn--ghost">Dismiss</button>
 <button type="button" class="ad-btn ad-btn--danger">Delete</button>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;button type=&quot;button&quot; class=&quot;ad-btn&quot;&gt;Cancel&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--primary&quot;&gt;Save&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--dark&quot;&gt;Finish setup&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--ghost&quot;&gt;Dismiss&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--danger&quot;&gt;Delete&lt;/button&gt;</code></pre>
+</details>
+</figure>
 
 ## Sizes
 
 Sizes adjust padding only — the font stays on the type scale, it does not
 grow a step of its own.
 
-<% example(title: "Sizes") do %>
+<figure class="example">
+<figcaption>Sizes</figcaption>
+<div>
 <button type="button" class="ad-btn ad-btn--primary ad-btn--sm">Small</button>
 <button type="button" class="ad-btn ad-btn--primary">Medium</button>
 <button type="button" class="ad-btn ad-btn--primary ad-btn--lg">Large</button>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--primary ad-btn--sm&quot;&gt;Small&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--primary&quot;&gt;Medium&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--primary ad-btn--lg&quot;&gt;Large&lt;/button&gt;</code></pre>
+</details>
+</figure>
 
 ## States
 
-Disabled buttons drop to 50% opacity and keep their variant colours, so a
+Disabled buttons drop to 50% opacity and keep their variant colors, so a
 disabled primary still reads as the primary.
 
-<% example(title: "States") do %>
-<button type="button" class="ad-btn ad-btn--primary" disabled>Disabled</button>
+<figure class="example">
+<figcaption>States</figcaption>
+<div>
+<button type="button" class="ad-btn ad-btn--primary" disabled="">Disabled</button>
 <button type="button" class="ad-btn" aria-disabled="true">Not yet available</button>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--primary&quot; disabled=&quot;&quot;&gt;Disabled&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-btn&quot; aria-disabled=&quot;true&quot;&gt;Not yet available&lt;/button&gt;</code></pre>
+</details>
+</figure>
 
 <div class="ad-callout">
   <p class="type-label" style="margin-bottom: var(--ad-space-2xs); display:block;">disabled vs. aria-disabled</p>
@@ -58,38 +83,70 @@ disabled primary still reads as the primary.
 Pass an icon as the first child. The gap is `--ad-space-2xs`, set by the
 button itself.
 
-<% example(title: "Icon") do %>
+<figure class="example">
+<figcaption>Icon</figcaption>
+<div>
 <button type="button" class="ad-btn ad-btn--primary">
-  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
+  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
   New
 </button>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--primary&quot;&gt;
+  &lt;svg width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.9&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M12 5v14M5 12h14&quot; /&gt;&lt;/svg&gt;
+  New
+&lt;/button&gt;</code></pre>
+</details>
+</figure>
 
 ## Groups
 
-<% example(title: "Button group") do %>
+<figure class="example">
+<figcaption>Button group</figcaption>
+<div>
 <div class="ad-btn-group" role="group" aria-label="View">
   <button type="button" class="ad-btn ad-btn--sm">List</button>
   <button type="button" class="ad-btn ad-btn--sm">Board</button>
   <button type="button" class="ad-btn ad-btn--sm">Calendar</button>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-btn-group&quot; role=&quot;group&quot; aria-label=&quot;View&quot;&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--sm&quot;&gt;List&lt;/button&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--sm&quot;&gt;Board&lt;/button&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--sm&quot;&gt;Calendar&lt;/button&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## Full width
 
-<% example(title: "Block", layout: "stack") do %>
+<figure class="example">
+<figcaption>Block</figcaption>
+<div data-layout="stack">
 <button type="button" class="ad-btn ad-btn--primary ad-btn--block">Take a photo</button>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--primary ad-btn--block&quot;&gt;Take a photo&lt;/button&gt;</code></pre>
+</details>
+</figure>
 
 ## Links that act like buttons
 
 An `<a>` with `.ad-btn` is fine when the thing it does is navigate. A
 `<button>` is required when it changes something.
 
-<% example(title: "Anchor as button") do %>
-<a class="ad-btn ad-btn--primary" href="<%= relative_url('/components/icon-button/') %>">Next: IconButton</a>
-<a class="ad-btn ad-btn--ghost" href="<%= relative_url('/foundations/color/') %>">Back to colour</a>
-<% end %>
+<figure class="example">
+<figcaption>Anchor as button</figcaption>
+<div>
+<a class="ad-btn ad-btn--primary" href="/arbitrary-definitions-design-system/components/icon-button/">Next: IconButton</a>
+<a class="ad-btn ad-btn--ghost" href="/arbitrary-definitions-design-system/foundations/color/">Back to color</a>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;a class=&quot;ad-btn ad-btn--primary&quot; href=&quot;/arbitrary-definitions-design-system/components/icon-button/&quot;&gt;Next: IconButton&lt;/a&gt;
+&lt;a class=&quot;ad-btn ad-btn--ghost&quot; href=&quot;/arbitrary-definitions-design-system/foundations/color/&quot;&gt;Back to color&lt;/a&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/card-row.md b/_components/card-row.md
index 4da391b..e59e0b8 100644
--- a/_components/card-row.md
+++ b/_components/card-row.md
@@ -7,25 +7,44 @@ summary: >-
 
 ## Basic
 
-<% example(title: "Rows", open: true) do %>
+<figure class="example">
+<figcaption>Rows</figcaption>
+<div>
 <div class="ad-card" style="max-width: 22rem;">
   <div class="ad-card-row">Renew passport</div>
   <div class="ad-card-row">Dentist checkup</div>
   <div class="ad-card-row">Rotate mattress</div>
 </div>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-card&quot; style=&quot;max-width: 22rem;&quot;&gt;
+  &lt;div class=&quot;ad-card-row&quot;&gt;Renew passport&lt;/div&gt;
+  &lt;div class=&quot;ad-card-row&quot;&gt;Dentist checkup&lt;/div&gt;
+  &lt;div class=&quot;ad-card-row&quot;&gt;Rotate mattress&lt;/div&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## Clickable
 
 `--clickable` adds a hover fill and a pointer cursor. Use a real `<button>`
 element when the row navigates or opens something.
 
-<% example(title: "Clickable rows") do %>
+<figure class="example">
+<figcaption>Clickable rows</figcaption>
+<div>
 <div class="ad-card" style="max-width: 22rem;">
   <button type="button" class="ad-card-row ad-card-row--clickable">Tax return 2025.pdf</button>
   <button type="button" class="ad-card-row ad-card-row--clickable">Warranty card.pdf</button>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-card&quot; style=&quot;max-width: 22rem;&quot;&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-card-row ad-card-row--clickable&quot;&gt;Tax return 2025.pdf&lt;/button&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-card-row ad-card-row--clickable&quot;&gt;Warranty card.pdf&lt;/button&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## With content on both ends
 
@@ -33,11 +52,13 @@ The row is a flex container — a leading icon, a two-line middle column, and a
 trailing mono value is the most common shape (see the Loop and Archive
 patterns for it in context).
 
-<% example(title: "Leading icon, trailing meta") do %>
+<figure class="example">
+<figcaption>Leading icon, trailing meta</figcaption>
+<div>
 <div class="ad-card" style="max-width: 24rem;">
   <div class="ad-card-row">
     <span class="ad-avatar ad-avatar--square">
-      <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z"/><path d="M3 8l9 5 9-5M12 13v8"/></svg>
+      <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg>
     </span>
     <div style="flex:1; min-width:0;">
       <div style="font-weight: 550;">Dishwasher manual</div>
@@ -46,7 +67,22 @@ patterns for it in context).
     <span class="type-mono" style="color: var(--ad-color-text-tertiary);">2h</span>
   </div>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-card&quot; style=&quot;max-width: 24rem;&quot;&gt;
+  &lt;div class=&quot;ad-card-row&quot;&gt;
+    &lt;span class=&quot;ad-avatar ad-avatar--square&quot;&gt;
+      &lt;svg width=&quot;17&quot; height=&quot;17&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.75&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M21 8 12 3 3 8v8l9 5 9-5V8Z&quot; /&gt;&lt;path d=&quot;M3 8l9 5 9-5M12 13v8&quot; /&gt;&lt;/svg&gt;
+    &lt;/span&gt;
+    &lt;div style=&quot;flex:1; min-width:0;&quot;&gt;
+      &lt;div style=&quot;font-weight: 550;&quot;&gt;Dishwasher manual&lt;/div&gt;
+      &lt;div class=&quot;type-label&quot; style=&quot;margin-top: 2px;&quot;&gt;PDF · 2.1MB&lt;/div&gt;
+    &lt;/div&gt;
+    &lt;span class=&quot;type-mono&quot; style=&quot;color: var(--ad-color-text-tertiary);&quot;&gt;2h&lt;/span&gt;
+  &lt;/div&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/card.md b/_components/card.md
index 03b9506..d4a0b16 100644
--- a/_components/card.md
+++ b/_components/card.md
@@ -10,18 +10,27 @@ summary: >-
 Flat is the default everywhere. `--elevated` is for the single most-elevated
 element on screen — never a whole grid of cards at once.
 
-<% example(title: "Flat and elevated", surface: "sunken") do %>
+<figure class="example">
+<figcaption>Flat and elevated</figcaption>
+<div data-surface="sunken">
 <div class="ad-card" style="width: 14rem; padding: var(--ad-space-s);">Flat — the default.</div>
 <div class="ad-card ad-card--elevated" style="width: 14rem; padding: var(--ad-space-s);">Elevated — one per view.</div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-card&quot; style=&quot;width: 14rem; padding: var(--ad-space-s);&quot;&gt;Flat — the default.&lt;/div&gt;
+&lt;div class=&quot;ad-card ad-card--elevated&quot; style=&quot;width: 14rem; padding: var(--ad-space-s);&quot;&gt;Elevated — one per view.&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## As a content tile
 
-<% example(title: "Content tile") do %>
+<figure class="example">
+<figcaption>Content tile</figcaption>
+<div>
 <article class="ad-card" style="max-width: 18rem;">
   <div class="ad-card__body">
     <p class="type-label">Recommended</p>
-    <h3 class="ad-card__title">Rename the caution colour</h3>
+    <h3 class="ad-card__title">Rename the caution color</h3>
     <p class="ad-card__text">Three people have called it "yellow" in review this month.</p>
   </div>
   <div class="ad-card__footer">
@@ -29,7 +38,21 @@ element on screen — never a whole grid of cards at once.
     <button type="button" class="ad-btn ad-btn--ghost ad-btn--sm">Dismiss</button>
   </div>
 </article>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;article class=&quot;ad-card&quot; style=&quot;max-width: 18rem;&quot;&gt;
+  &lt;div class=&quot;ad-card__body&quot;&gt;
+    &lt;p class=&quot;type-label&quot;&gt;Recommended&lt;/p&gt;
+    &lt;h3 class=&quot;ad-card__title&quot;&gt;Rename the caution color&lt;/h3&gt;
+    &lt;p class=&quot;ad-card__text&quot;&gt;Three people have called it &quot;yellow&quot; in review this month.&lt;/p&gt;
+  &lt;/div&gt;
+  &lt;div class=&quot;ad-card__footer&quot;&gt;
+    &lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--primary ad-btn--sm&quot;&gt;Approve&lt;/button&gt;
+    &lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--ghost ad-btn--sm&quot;&gt;Dismiss&lt;/button&gt;
+  &lt;/div&gt;
+&lt;/article&gt;</code></pre>
+</details>
+</figure>
 
 ## As a list — with CardRow
 
@@ -37,13 +60,23 @@ Card's most common job in this system is holding a list of
 [CardRow](<%= relative_url('/components/card-row/') %>)s: entry lists, task
 lists, settings rows.
 
-<% example(title: "Card of rows") do %>
+<figure class="example">
+<figcaption>Card of rows</figcaption>
+<div>
 <div class="ad-card" style="max-width: 22rem;">
   <div class="ad-card-row">Dishwasher manual</div>
   <div class="ad-card-row">Sofa receipt</div>
   <div class="ad-card-row">Passport scan</div>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-card&quot; style=&quot;max-width: 22rem;&quot;&gt;
+  &lt;div class=&quot;ad-card-row&quot;&gt;Dishwasher manual&lt;/div&gt;
+  &lt;div class=&quot;ad-card-row&quot;&gt;Sofa receipt&lt;/div&gt;
+  &lt;div class=&quot;ad-card-row&quot;&gt;Passport scan&lt;/div&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/checkbox.md b/_components/checkbox.md
index 0181f02..c39b52b 100644
--- a/_components/checkbox.md
+++ b/_components/checkbox.md
@@ -5,48 +5,93 @@ summary: Flat checkbox, fills with the accent when checked. No bounce.
 
 ## Basic
 
-<% example(title: "Checkbox", open: true) do %>
+<figure class="example">
+<figcaption>Checkbox</figcaption>
+<div>
 <label class="ad-choice ad-choice--checkbox">
-  <input type="checkbox">
+  <input type="checkbox" />
   <span class="ad-choice__box"></span>
   Notify me
 </label>
 <label class="ad-choice ad-choice--checkbox">
-  <input type="checkbox" checked>
+  <input type="checkbox" checked="" />
   <span class="ad-choice__box">
-    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>
+    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
   </span>
   Ship on merge
 </label>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;label class=&quot;ad-choice ad-choice--checkbox&quot;&gt;
+  &lt;input type=&quot;checkbox&quot; /&gt;
+  &lt;span class=&quot;ad-choice__box&quot;&gt;&lt;/span&gt;
+  Notify me
+&lt;/label&gt;
+&lt;label class=&quot;ad-choice ad-choice--checkbox&quot;&gt;
+  &lt;input type=&quot;checkbox&quot; checked=&quot;&quot; /&gt;
+  &lt;span class=&quot;ad-choice__box&quot;&gt;
+    &lt;svg viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;3&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M20 6 9 17l-5-5&quot; /&gt;&lt;/svg&gt;
+  &lt;/span&gt;
+  Ship on merge
+&lt;/label&gt;</code></pre>
+</details>
+</figure>
 
 ## Disabled
 
-<% example(title: "Disabled") do %>
+<figure class="example">
+<figcaption>Disabled</figcaption>
+<div>
 <label class="ad-choice ad-choice--checkbox" aria-disabled="true">
-  <input type="checkbox" disabled>
+  <input type="checkbox" disabled="" />
   <span class="ad-choice__box"></span>
   Not available on this plan
 </label>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;label class=&quot;ad-choice ad-choice--checkbox&quot; aria-disabled=&quot;true&quot;&gt;
+  &lt;input type=&quot;checkbox&quot; disabled=&quot;&quot; /&gt;
+  &lt;span class=&quot;ad-choice__box&quot;&gt;&lt;/span&gt;
+  Not available on this plan
+&lt;/label&gt;</code></pre>
+</details>
+</figure>
 
 ## In a fieldset
 
-<% example(title: "Fieldset") do %>
+<figure class="example">
+<figcaption>Fieldset</figcaption>
+<div>
 <fieldset class="ad-fieldset">
   <legend>Notify me about</legend>
   <label class="ad-choice ad-choice--checkbox">
-    <input type="checkbox" checked>
-    <span class="ad-choice__box"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></span>
+    <input type="checkbox" checked="" />
+    <span class="ad-choice__box"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></span>
     Overdue tasks
   </label>
   <label class="ad-choice ad-choice--checkbox">
-    <input type="checkbox">
+    <input type="checkbox" />
     <span class="ad-choice__box"></span>
     Weekly digest
   </label>
 </fieldset>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;fieldset class=&quot;ad-fieldset&quot;&gt;
+  &lt;legend&gt;Notify me about&lt;/legend&gt;
+  &lt;label class=&quot;ad-choice ad-choice--checkbox&quot;&gt;
+    &lt;input type=&quot;checkbox&quot; checked=&quot;&quot; /&gt;
+    &lt;span class=&quot;ad-choice__box&quot;&gt;&lt;svg viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;3&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M20 6 9 17l-5-5&quot; /&gt;&lt;/svg&gt;&lt;/span&gt;
+    Overdue tasks
+  &lt;/label&gt;
+  &lt;label class=&quot;ad-choice ad-choice--checkbox&quot;&gt;
+    &lt;input type=&quot;checkbox&quot; /&gt;
+    &lt;span class=&quot;ad-choice__box&quot;&gt;&lt;/span&gt;
+    Weekly digest
+  &lt;/label&gt;
+&lt;/fieldset&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/dialog.md b/_components/dialog.md
index a2bf7c7..749bbe8 100644
--- a/_components/dialog.md
+++ b/_components/dialog.md
@@ -1,7 +1,7 @@
 ---
 title: Dialog
 summary: >-
-  A centered modal. Flat surface, no coloured header, hairline dividers
+  A centered modal. Flat surface, no colored header, hairline dividers
   between title, body, and footer.
 ---
 
@@ -11,7 +11,9 @@ documentation page. Wire real open/close state — and a `position: fixed`
 overlay — in the consuming app; this is a pixel recreation for documentation,
 not a functioning modal.
 
-<% example(title: "Dialog", open: true, layout: "stack") do %>
+<figure class="example">
+<figcaption>Dialog</figcaption>
+<div data-layout="stack">
 <div class="ad-dialog-demo">
   <div class="ad-dialog__overlay">
     <div class="ad-dialog">
@@ -24,13 +26,30 @@ not a functioning modal.
     </div>
   </div>
 </div>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-dialog-demo&quot;&gt;
+  &lt;div class=&quot;ad-dialog__overlay&quot;&gt;
+    &lt;div class=&quot;ad-dialog&quot;&gt;
+      &lt;div class=&quot;ad-dialog__title&quot;&gt;Delete series?&lt;/div&gt;
+      &lt;div class=&quot;ad-dialog__body&quot;&gt;This removes all of its task history. This cannot be undone.&lt;/div&gt;
+      &lt;div class=&quot;ad-dialog__footer&quot;&gt;
+        &lt;button type=&quot;button&quot; class=&quot;ad-btn&quot;&gt;Cancel&lt;/button&gt;
+        &lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--danger&quot;&gt;Delete&lt;/button&gt;
+      &lt;/div&gt;
+    &lt;/div&gt;
+  &lt;/div&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## Without a footer
 
 A body-only dialog for a notice rather than a decision.
 
-<% example(title: "No footer", layout: "stack") do %>
+<figure class="example">
+<figcaption>No footer</figcaption>
+<div data-layout="stack">
 <div class="ad-dialog-demo" style="min-height: 14rem;">
   <div class="ad-dialog__overlay">
     <div class="ad-dialog" style="width: 20rem;">
@@ -39,7 +58,18 @@ A body-only dialog for a notice rather than a decision.
     </div>
   </div>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-dialog-demo&quot; style=&quot;min-height: 14rem;&quot;&gt;
+  &lt;div class=&quot;ad-dialog__overlay&quot;&gt;
+    &lt;div class=&quot;ad-dialog&quot; style=&quot;width: 20rem;&quot;&gt;
+      &lt;div class=&quot;ad-dialog__title&quot;&gt;Backup complete&lt;/div&gt;
+      &lt;div class=&quot;ad-dialog__body&quot;&gt;412 files, 1.2GB, archived to the household drive.&lt;/div&gt;
+    &lt;/div&gt;
+  &lt;/div&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/field.md b/_components/field.md
index d2502fc..46d55fe 100644
--- a/_components/field.md
+++ b/_components/field.md
@@ -7,13 +7,23 @@ summary: >-
 
 ## Basic
 
-<% example(title: "Field", open: true) do %>
+<figure class="example">
+<figcaption>Field</figcaption>
+<div>
 <div class="ad-field" style="max-width: 20rem;">
   <label class="ad-field__label" for="field-name">Name</label>
-  <input class="ad-input" id="field-name" type="text" placeholder="Call Mom">
+  <input class="ad-input" id="field-name" type="text" placeholder="Call Mom" />
   <span class="ad-field__hint">First line becomes the display name.</span>
 </div>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-field&quot; style=&quot;max-width: 20rem;&quot;&gt;
+  &lt;label class=&quot;ad-field__label&quot; for=&quot;field-name&quot;&gt;Name&lt;/label&gt;
+  &lt;input class=&quot;ad-input&quot; id=&quot;field-name&quot; type=&quot;text&quot; placeholder=&quot;Call Mom&quot; /&gt;
+  &lt;span class=&quot;ad-field__hint&quot;&gt;First line becomes the display name.&lt;/span&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## Holding any control
 
@@ -21,36 +31,69 @@ Field wraps [Input](<%= relative_url('/components/input/') %>),
 [Select](<%= relative_url('/components/select/') %>), or anything else — it
 only owns the label and the hint line, never the control's own styling.
 
-<% example(title: "Field with a select") do %>
+<figure class="example">
+<figcaption>Field with a select</figcaption>
+<div>
 <div class="ad-field" style="max-width: 20rem;">
   <label class="ad-field__label" for="field-unit">Unit</label>
   <select class="ad-select" id="field-unit">
     <option>Day</option>
-    <option selected>Week</option>
+    <option selected="">Week</option>
     <option>Month</option>
     <option>Year</option>
   </select>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-field&quot; style=&quot;max-width: 20rem;&quot;&gt;
+  &lt;label class=&quot;ad-field__label&quot; for=&quot;field-unit&quot;&gt;Unit&lt;/label&gt;
+  &lt;select class=&quot;ad-select&quot; id=&quot;field-unit&quot;&gt;
+    &lt;option&gt;Day&lt;/option&gt;
+    &lt;option selected=&quot;&quot;&gt;Week&lt;/option&gt;
+    &lt;option&gt;Month&lt;/option&gt;
+    &lt;option&gt;Year&lt;/option&gt;
+  &lt;/select&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## In a row
 
-<% example(title: "Two fields in a row") do %>
+<figure class="example">
+<figcaption>Two fields in a row</figcaption>
+<div>
 <div class="ad-form" style="max-width: none;">
   <div class="ad-form__row">
     <div class="ad-field">
       <label class="ad-field__label" for="field-every">Every</label>
-      <input class="ad-input" id="field-every" type="number" placeholder="2">
+      <input class="ad-input" id="field-every" type="number" placeholder="2" />
     </div>
     <div class="ad-field">
       <label class="ad-field__label" for="field-unit-2">Unit</label>
       <select class="ad-select" id="field-unit-2">
-        <option>Day</option><option selected>Week</option><option>Month</option>
+        <option>Day</option><option selected="">Week</option><option>Month</option>
       </select>
     </div>
   </div>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-form&quot; style=&quot;max-width: none;&quot;&gt;
+  &lt;div class=&quot;ad-form__row&quot;&gt;
+    &lt;div class=&quot;ad-field&quot;&gt;
+      &lt;label class=&quot;ad-field__label&quot; for=&quot;field-every&quot;&gt;Every&lt;/label&gt;
+      &lt;input class=&quot;ad-input&quot; id=&quot;field-every&quot; type=&quot;number&quot; placeholder=&quot;2&quot; /&gt;
+    &lt;/div&gt;
+    &lt;div class=&quot;ad-field&quot;&gt;
+      &lt;label class=&quot;ad-field__label&quot; for=&quot;field-unit-2&quot;&gt;Unit&lt;/label&gt;
+      &lt;select class=&quot;ad-select&quot; id=&quot;field-unit-2&quot;&gt;
+        &lt;option&gt;Day&lt;/option&gt;&lt;option selected=&quot;&quot;&gt;Week&lt;/option&gt;&lt;option&gt;Month&lt;/option&gt;
+      &lt;/select&gt;
+    &lt;/div&gt;
+  &lt;/div&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/icon-button.md b/_components/icon-button.md
index c8e0204..7bcbfa6 100644
--- a/_components/icon-button.md
+++ b/_components/icon-button.md
@@ -12,38 +12,71 @@ an icon button sitting alone rather than in a toolbar. `accent` is a soft
 accent tint, for a toggled or active state — the segmented-tabs pattern this
 system does not otherwise reach for.
 
-<% example(title: "Variants", open: true) do %>
+<figure class="example">
+<figcaption>Variants</figcaption>
+<div>
 <button type="button" class="ad-icon-btn" aria-label="Delete">
-  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13"/></svg>
+  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13" /></svg>
 </button>
 <button type="button" class="ad-icon-btn ad-icon-btn--outline" aria-label="Settings">
-  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 2.5v3M12 18.5v3M3.6 7.2l2.6 1.5M17.8 15.3l2.6 1.5M3.6 16.8l2.6-1.5M17.8 8.7l2.6-1.5"/></svg>
+  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3" /><path d="M12 2.5v3M12 18.5v3M3.6 7.2l2.6 1.5M17.8 15.3l2.6 1.5M3.6 16.8l2.6-1.5M17.8 8.7l2.6-1.5" /></svg>
 </button>
 <button type="button" class="ad-icon-btn ad-icon-btn--accent" aria-label="Filter (active)">
-  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16l-6 8v5l-4 2v-7z"/></svg>
+  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16l-6 8v5l-4 2v-7z" /></svg>
 </button>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;button type=&quot;button&quot; class=&quot;ad-icon-btn&quot; aria-label=&quot;Delete&quot;&gt;
+  &lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.75&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13&quot; /&gt;&lt;/svg&gt;
+&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-icon-btn ad-icon-btn--outline&quot; aria-label=&quot;Settings&quot;&gt;
+  &lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.75&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;3&quot; /&gt;&lt;path d=&quot;M12 2.5v3M12 18.5v3M3.6 7.2l2.6 1.5M17.8 15.3l2.6 1.5M3.6 16.8l2.6-1.5M17.8 8.7l2.6-1.5&quot; /&gt;&lt;/svg&gt;
+&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-icon-btn ad-icon-btn--accent&quot; aria-label=&quot;Filter (active)&quot;&gt;
+  &lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.75&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M4 5h16l-6 8v5l-4 2v-7z&quot; /&gt;&lt;/svg&gt;
+&lt;/button&gt;</code></pre>
+</details>
+</figure>
 
 ## Sizes
 
-<% example(title: "Sizes") do %>
-<button type="button" class="ad-icon-btn ad-icon-btn--outline ad-icon-btn--sm" aria-label="Back"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg></button>
-<button type="button" class="ad-icon-btn ad-icon-btn--outline" aria-label="Back"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg></button>
-<button type="button" class="ad-icon-btn ad-icon-btn--outline ad-icon-btn--lg" aria-label="Back"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg></button>
-<% end %>
+<figure class="example">
+<figcaption>Sizes</figcaption>
+<div>
+<button type="button" class="ad-icon-btn ad-icon-btn--outline ad-icon-btn--sm" aria-label="Back"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6" /></svg></button>
+<button type="button" class="ad-icon-btn ad-icon-btn--outline" aria-label="Back"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6" /></svg></button>
+<button type="button" class="ad-icon-btn ad-icon-btn--outline ad-icon-btn--lg" aria-label="Back"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6" /></svg></button>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;button type=&quot;button&quot; class=&quot;ad-icon-btn ad-icon-btn--outline ad-icon-btn--sm&quot; aria-label=&quot;Back&quot;&gt;&lt;svg width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.9&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M15 18l-6-6 6-6&quot; /&gt;&lt;/svg&gt;&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-icon-btn ad-icon-btn--outline&quot; aria-label=&quot;Back&quot;&gt;&lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.9&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M15 18l-6-6 6-6&quot; /&gt;&lt;/svg&gt;&lt;/button&gt;
+&lt;button type=&quot;button&quot; class=&quot;ad-icon-btn ad-icon-btn--outline ad-icon-btn--lg&quot; aria-label=&quot;Back&quot;&gt;&lt;svg width=&quot;18&quot; height=&quot;18&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.9&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M15 18l-6-6 6-6&quot; /&gt;&lt;/svg&gt;&lt;/button&gt;</code></pre>
+</details>
+</figure>
 
 ## Paired with Tooltip
 
 An icon-only control almost always wants a [Tooltip](<%= relative_url('/components/tooltip/') %>) too — the `aria-label` covers accessibility, the tooltip covers a sighted mouse user glancing for a hint.
 
-<% example(title: "With a tooltip") do %>
+<figure class="example">
+<figcaption>With a tooltip</figcaption>
+<div>
 <span class="ad-tooltip">
   <button type="button" class="ad-icon-btn" aria-label="Delete">
-    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13"/></svg>
+    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13" /></svg>
   </button>
   <span class="ad-tooltip__bubble">Delete</span>
 </span>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;span class=&quot;ad-tooltip&quot;&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-icon-btn&quot; aria-label=&quot;Delete&quot;&gt;
+    &lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.75&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13&quot; /&gt;&lt;/svg&gt;
+  &lt;/button&gt;
+  &lt;span class=&quot;ad-tooltip__bubble&quot;&gt;Delete&lt;/span&gt;
+&lt;/span&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/input.md b/_components/input.md
index 81db812..0c0a54b 100644
--- a/_components/input.md
+++ b/_components/input.md
@@ -7,29 +7,52 @@ summary: >-
 
 ## Basic
 
-<% example(title: "Input", open: true) do %>
-<input class="ad-input" type="text" placeholder="Dishwasher manual" style="max-width: 20rem;">
-<% end %>
+<figure class="example">
+<figcaption>Input</figcaption>
+<div>
+<input class="ad-input" type="text" placeholder="Dishwasher manual" style="max-width: 20rem;" />
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;input class=&quot;ad-input&quot; type=&quot;text&quot; placeholder=&quot;Dishwasher manual&quot; style=&quot;max-width: 20rem;&quot; /&gt;</code></pre>
+</details>
+</figure>
 
 ## Focus and disabled
 
-<% example(title: "States", layout: "stack") do %>
-<input class="ad-input" type="text" value="Focused" style="max-width: 20rem; border-color: var(--ad-color-accent); box-shadow: 0 0 0 2px var(--ad-color-accent-soft);">
-<input class="ad-input" type="text" value="Disabled" disabled style="max-width: 20rem;">
-<% end %>
+<figure class="example">
+<figcaption>States</figcaption>
+<div data-layout="stack">
+<input class="ad-input" type="text" value="Focused" style="max-width: 20rem; border-color: var(--ad-color-accent); box-shadow: 0 0 0 2px var(--ad-color-accent-soft);" />
+<input class="ad-input" type="text" value="Disabled" disabled="" style="max-width: 20rem;" />
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;input class=&quot;ad-input&quot; type=&quot;text&quot; value=&quot;Focused&quot; style=&quot;max-width: 20rem; border-color: var(--ad-color-accent); box-shadow: 0 0 0 2px var(--ad-color-accent-soft);&quot; /&gt;
+&lt;input class=&quot;ad-input&quot; type=&quot;text&quot; value=&quot;Disabled&quot; disabled=&quot;&quot; style=&quot;max-width: 20rem;&quot; /&gt;</code></pre>
+</details>
+</figure>
 
 ## Invalid
 
 Invalid state is expressed on the control itself, so it works with native
 form validation, plus an error line below.
 
-<% example(title: "Invalid") do %>
+<figure class="example">
+<figcaption>Invalid</figcaption>
+<div>
 <div class="ad-field" style="max-width: 20rem;">
   <label class="ad-field__label" for="input-accent">Accent hex</label>
-  <input class="ad-input" id="input-accent" type="text" value="not-a-colour" aria-invalid="true" aria-describedby="input-accent-error">
-  <p class="ad-field__error" id="input-accent-error">Not a valid hex colour.</p>
+  <input class="ad-input" id="input-accent" type="text" value="not-a-color" aria-invalid="true" aria-describedby="input-accent-error" />
+  <p class="ad-field__error" id="input-accent-error">Not a valid hex color.</p>
+</div>
 </div>
-<% end %>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-field&quot; style=&quot;max-width: 20rem;&quot;&gt;
+  &lt;label class=&quot;ad-field__label&quot; for=&quot;input-accent&quot;&gt;Accent hex&lt;/label&gt;
+  &lt;input class=&quot;ad-input&quot; id=&quot;input-accent&quot; type=&quot;text&quot; value=&quot;not-a-color&quot; aria-invalid=&quot;true&quot; aria-describedby=&quot;input-accent-error&quot; /&gt;
+  &lt;p class=&quot;ad-field__error&quot; id=&quot;input-accent-error&quot;&gt;Not a valid hex color.&lt;/p&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/radio.md b/_components/radio.md
index 9e6d97a..35c6682 100644
--- a/_components/radio.md
+++ b/_components/radio.md
@@ -5,41 +5,79 @@ summary: Flat radio button, the same visual language as Checkbox.
 
 ## Basic
 
-<% example(title: "Radio", open: true) do %>
+<figure class="example">
+<figcaption>Radio</figcaption>
+<div>
 <label class="ad-choice ad-choice--radio">
-  <input type="radio" name="demo-radio-1" checked>
+  <input type="radio" name="demo-radio-1" checked="" />
   <span class="ad-choice__box"></span>
   Weekly
 </label>
 <label class="ad-choice ad-choice--radio">
-  <input type="radio" name="demo-radio-1">
+  <input type="radio" name="demo-radio-1" />
   <span class="ad-choice__box"></span>
   Monthly
 </label>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;label class=&quot;ad-choice ad-choice--radio&quot;&gt;
+  &lt;input type=&quot;radio&quot; name=&quot;demo-radio-1&quot; checked=&quot;&quot; /&gt;
+  &lt;span class=&quot;ad-choice__box&quot;&gt;&lt;/span&gt;
+  Weekly
+&lt;/label&gt;
+&lt;label class=&quot;ad-choice ad-choice--radio&quot;&gt;
+  &lt;input type=&quot;radio&quot; name=&quot;demo-radio-1&quot; /&gt;
+  &lt;span class=&quot;ad-choice__box&quot;&gt;&lt;/span&gt;
+  Monthly
+&lt;/label&gt;</code></pre>
+</details>
+</figure>
 
 ## As a group
 
-<% example(title: "Interval") do %>
+<figure class="example">
+<figcaption>Interval</figcaption>
+<div>
 <fieldset class="ad-fieldset">
   <legend>Repeat</legend>
   <label class="ad-choice ad-choice--radio">
-    <input type="radio" name="demo-radio-2" checked>
+    <input type="radio" name="demo-radio-2" checked="" />
     <span class="ad-choice__box"></span>
     Every week
   </label>
   <label class="ad-choice ad-choice--radio">
-    <input type="radio" name="demo-radio-2">
+    <input type="radio" name="demo-radio-2" />
     <span class="ad-choice__box"></span>
     Every month
   </label>
   <label class="ad-choice ad-choice--radio">
-    <input type="radio" name="demo-radio-2">
+    <input type="radio" name="demo-radio-2" />
     <span class="ad-choice__box"></span>
     Every year
   </label>
 </fieldset>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;fieldset class=&quot;ad-fieldset&quot;&gt;
+  &lt;legend&gt;Repeat&lt;/legend&gt;
+  &lt;label class=&quot;ad-choice ad-choice--radio&quot;&gt;
+    &lt;input type=&quot;radio&quot; name=&quot;demo-radio-2&quot; checked=&quot;&quot; /&gt;
+    &lt;span class=&quot;ad-choice__box&quot;&gt;&lt;/span&gt;
+    Every week
+  &lt;/label&gt;
+  &lt;label class=&quot;ad-choice ad-choice--radio&quot;&gt;
+    &lt;input type=&quot;radio&quot; name=&quot;demo-radio-2&quot; /&gt;
+    &lt;span class=&quot;ad-choice__box&quot;&gt;&lt;/span&gt;
+    Every month
+  &lt;/label&gt;
+  &lt;label class=&quot;ad-choice ad-choice--radio&quot;&gt;
+    &lt;input type=&quot;radio&quot; name=&quot;demo-radio-2&quot; /&gt;
+    &lt;span class=&quot;ad-choice__box&quot;&gt;&lt;/span&gt;
+    Every year
+  &lt;/label&gt;
+&lt;/fieldset&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/select.md b/_components/select.md
index 18383a1..27e3463 100644
--- a/_components/select.md
+++ b/_components/select.md
@@ -5,20 +5,37 @@ summary: Native select, styled to match Input exactly.
 
 ## Basic
 
-<% example(title: "Select", open: true) do %>
+<figure class="example">
+<figcaption>Select</figcaption>
+<div>
 <select class="ad-select" style="max-width: 20rem;">
   <option>Day</option>
-  <option selected>Week</option>
+  <option selected="">Week</option>
   <option>Month</option>
   <option>Year</option>
 </select>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;select class=&quot;ad-select&quot; style=&quot;max-width: 20rem;&quot;&gt;
+  &lt;option&gt;Day&lt;/option&gt;
+  &lt;option selected=&quot;&quot;&gt;Week&lt;/option&gt;
+  &lt;option&gt;Month&lt;/option&gt;
+  &lt;option&gt;Year&lt;/option&gt;
+&lt;/select&gt;</code></pre>
+</details>
+</figure>
 
 ## Disabled
 
-<% example(title: "Disabled") do %>
-<select class="ad-select" disabled style="max-width: 20rem;"><option>Week</option></select>
-<% end %>
+<figure class="example">
+<figcaption>Disabled</figcaption>
+<div>
+<select class="ad-select" disabled="" style="max-width: 20rem;"><option>Week</option></select>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;select class=&quot;ad-select&quot; disabled=&quot;&quot; style=&quot;max-width: 20rem;&quot;&gt;&lt;option&gt;Week&lt;/option&gt;&lt;/select&gt;</code></pre>
+</details>
+</figure>
 
 <div class="ad-callout">
   <p style="margin:0; font-size: var(--ad-step--1);">
diff --git a/_components/switch.md b/_components/switch.md
index 5df8b0e..2b01070 100644
--- a/_components/switch.md
+++ b/_components/switch.md
@@ -2,33 +2,58 @@
 title: Switch
 summary: >-
   Toggle switch for on/off settings. The one moving part in this system —
-  everything else transitions colour or border, never position.
+  everything else transitions color or border, never position.
 ---
 
 ## Basic
 
-<% example(title: "Switch", open: true) do %>
+<figure class="example">
+<figcaption>Switch</figcaption>
+<div>
 <label class="ad-switch">
-  <input type="checkbox" checked>
+  <input type="checkbox" checked="" />
   <span class="ad-switch__track"></span>
   Dark mode
 </label>
 <label class="ad-switch">
-  <input type="checkbox">
+  <input type="checkbox" />
   <span class="ad-switch__track"></span>
   Weekly digest
 </label>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;label class=&quot;ad-switch&quot;&gt;
+  &lt;input type=&quot;checkbox&quot; checked=&quot;&quot; /&gt;
+  &lt;span class=&quot;ad-switch__track&quot;&gt;&lt;/span&gt;
+  Dark mode
+&lt;/label&gt;
+&lt;label class=&quot;ad-switch&quot;&gt;
+  &lt;input type=&quot;checkbox&quot; /&gt;
+  &lt;span class=&quot;ad-switch__track&quot;&gt;&lt;/span&gt;
+  Weekly digest
+&lt;/label&gt;</code></pre>
+</details>
+</figure>
 
 ## Disabled
 
-<% example(title: "Disabled") do %>
+<figure class="example">
+<figcaption>Disabled</figcaption>
+<div>
 <label class="ad-switch">
-  <input type="checkbox" checked disabled>
+  <input type="checkbox" checked="" disabled="" />
   <span class="ad-switch__track"></span>
   Always on for this plan
 </label>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;label class=&quot;ad-switch&quot;&gt;
+  &lt;input type=&quot;checkbox&quot; checked=&quot;&quot; disabled=&quot;&quot; /&gt;
+  &lt;span class=&quot;ad-switch__track&quot;&gt;&lt;/span&gt;
+  Always on for this plan
+&lt;/label&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/tabs.md b/_components/tabs.md
index 5291016..2488877 100644
--- a/_components/tabs.md
+++ b/_components/tabs.md
@@ -11,36 +11,66 @@ summary: >-
 Mono, with an accent underline. For primary section navigation — a project's
 own tabs, never a filter.
 
-<% example(title: "Underline", layout: "stack", open: true) do %>
-<div class="ad-tabs ad-tabs--underline" data-tabs>
+<figure class="example">
+<figcaption>Underline</figcaption>
+<div data-layout="stack">
+<div class="ad-tabs ad-tabs--underline" data-tabs="">
   <div class="ad-tabs__list" role="tablist" aria-label="Repository">
     <button class="ad-tabs__tab" role="tab" id="tabs-code-tab" aria-controls="tabs-code-panel" aria-selected="true" tabindex="0">Code</button>
     <button class="ad-tabs__tab" role="tab" id="tabs-ci-tab" aria-controls="tabs-ci-panel" aria-selected="false" tabindex="-1">CI</button>
     <button class="ad-tabs__tab" role="tab" id="tabs-issues-tab" aria-controls="tabs-issues-panel" aria-selected="false" tabindex="-1">Issues</button>
   </div>
   <div class="ad-tabs__panel" role="tabpanel" id="tabs-code-panel" aria-labelledby="tabs-code-tab" tabindex="0">Repository contents.</div>
-  <div class="ad-tabs__panel" role="tabpanel" id="tabs-ci-panel" aria-labelledby="tabs-ci-tab" tabindex="0" hidden>Build history.</div>
-  <div class="ad-tabs__panel" role="tabpanel" id="tabs-issues-panel" aria-labelledby="tabs-issues-tab" tabindex="0" hidden>Open issues.</div>
+  <div class="ad-tabs__panel" role="tabpanel" id="tabs-ci-panel" aria-labelledby="tabs-ci-tab" tabindex="0" hidden="">Build history.</div>
+  <div class="ad-tabs__panel" role="tabpanel" id="tabs-issues-panel" aria-labelledby="tabs-issues-tab" tabindex="0" hidden="">Open issues.</div>
 </div>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-tabs ad-tabs--underline&quot; data-tabs=&quot;&quot;&gt;
+  &lt;div class=&quot;ad-tabs__list&quot; role=&quot;tablist&quot; aria-label=&quot;Repository&quot;&gt;
+    &lt;button class=&quot;ad-tabs__tab&quot; role=&quot;tab&quot; id=&quot;tabs-code-tab&quot; aria-controls=&quot;tabs-code-panel&quot; aria-selected=&quot;true&quot; tabindex=&quot;0&quot;&gt;Code&lt;/button&gt;
+    &lt;button class=&quot;ad-tabs__tab&quot; role=&quot;tab&quot; id=&quot;tabs-ci-tab&quot; aria-controls=&quot;tabs-ci-panel&quot; aria-selected=&quot;false&quot; tabindex=&quot;-1&quot;&gt;CI&lt;/button&gt;
+    &lt;button class=&quot;ad-tabs__tab&quot; role=&quot;tab&quot; id=&quot;tabs-issues-tab&quot; aria-controls=&quot;tabs-issues-panel&quot; aria-selected=&quot;false&quot; tabindex=&quot;-1&quot;&gt;Issues&lt;/button&gt;
+  &lt;/div&gt;
+  &lt;div class=&quot;ad-tabs__panel&quot; role=&quot;tabpanel&quot; id=&quot;tabs-code-panel&quot; aria-labelledby=&quot;tabs-code-tab&quot; tabindex=&quot;0&quot;&gt;Repository contents.&lt;/div&gt;
+  &lt;div class=&quot;ad-tabs__panel&quot; role=&quot;tabpanel&quot; id=&quot;tabs-ci-panel&quot; aria-labelledby=&quot;tabs-ci-tab&quot; tabindex=&quot;0&quot; hidden=&quot;&quot;&gt;Build history.&lt;/div&gt;
+  &lt;div class=&quot;ad-tabs__panel&quot; role=&quot;tabpanel&quot; id=&quot;tabs-issues-panel&quot; aria-labelledby=&quot;tabs-issues-tab&quot; tabindex=&quot;0&quot; hidden=&quot;&quot;&gt;Open issues.&lt;/div&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## Segmented
 
 A pill track; the active segment lifts to the surface. For filters — *All ·
 Overdue · Upcoming* — never primary navigation.
 
-<% example(title: "Segmented", layout: "stack") do %>
-<div class="ad-tabs ad-tabs--segmented" data-tabs>
+<figure class="example">
+<figcaption>Segmented</figcaption>
+<div data-layout="stack">
+<div class="ad-tabs ad-tabs--segmented" data-tabs="">
   <div class="ad-tabs__list" role="tablist" aria-label="Filter">
     <button class="ad-tabs__tab" role="tab" id="tabs-all-tab" aria-controls="tabs-all-panel" aria-selected="true" tabindex="0">All</button>
     <button class="ad-tabs__tab" role="tab" id="tabs-overdue-tab" aria-controls="tabs-overdue-panel" aria-selected="false" tabindex="-1">Overdue</button>
     <button class="ad-tabs__tab" role="tab" id="tabs-upcoming-tab" aria-controls="tabs-upcoming-panel" aria-selected="false" tabindex="-1">Upcoming</button>
   </div>
   <div class="ad-tabs__panel" role="tabpanel" id="tabs-all-panel" aria-labelledby="tabs-all-tab" tabindex="0">7 series.</div>
-  <div class="ad-tabs__panel" role="tabpanel" id="tabs-overdue-panel" aria-labelledby="tabs-overdue-tab" tabindex="0" hidden>2 series.</div>
-  <div class="ad-tabs__panel" role="tabpanel" id="tabs-upcoming-panel" aria-labelledby="tabs-upcoming-tab" tabindex="0" hidden>5 series.</div>
+  <div class="ad-tabs__panel" role="tabpanel" id="tabs-overdue-panel" aria-labelledby="tabs-overdue-tab" tabindex="0" hidden="">2 series.</div>
+  <div class="ad-tabs__panel" role="tabpanel" id="tabs-upcoming-panel" aria-labelledby="tabs-upcoming-tab" tabindex="0" hidden="">5 series.</div>
+</div>
 </div>
-<% end %>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-tabs ad-tabs--segmented&quot; data-tabs=&quot;&quot;&gt;
+  &lt;div class=&quot;ad-tabs__list&quot; role=&quot;tablist&quot; aria-label=&quot;Filter&quot;&gt;
+    &lt;button class=&quot;ad-tabs__tab&quot; role=&quot;tab&quot; id=&quot;tabs-all-tab&quot; aria-controls=&quot;tabs-all-panel&quot; aria-selected=&quot;true&quot; tabindex=&quot;0&quot;&gt;All&lt;/button&gt;
+    &lt;button class=&quot;ad-tabs__tab&quot; role=&quot;tab&quot; id=&quot;tabs-overdue-tab&quot; aria-controls=&quot;tabs-overdue-panel&quot; aria-selected=&quot;false&quot; tabindex=&quot;-1&quot;&gt;Overdue&lt;/button&gt;
+    &lt;button class=&quot;ad-tabs__tab&quot; role=&quot;tab&quot; id=&quot;tabs-upcoming-tab&quot; aria-controls=&quot;tabs-upcoming-panel&quot; aria-selected=&quot;false&quot; tabindex=&quot;-1&quot;&gt;Upcoming&lt;/button&gt;
+  &lt;/div&gt;
+  &lt;div class=&quot;ad-tabs__panel&quot; role=&quot;tabpanel&quot; id=&quot;tabs-all-panel&quot; aria-labelledby=&quot;tabs-all-tab&quot; tabindex=&quot;0&quot;&gt;7 series.&lt;/div&gt;
+  &lt;div class=&quot;ad-tabs__panel&quot; role=&quot;tabpanel&quot; id=&quot;tabs-overdue-panel&quot; aria-labelledby=&quot;tabs-overdue-tab&quot; tabindex=&quot;0&quot; hidden=&quot;&quot;&gt;2 series.&lt;/div&gt;
+  &lt;div class=&quot;ad-tabs__panel&quot; role=&quot;tabpanel&quot; id=&quot;tabs-upcoming-panel&quot; aria-labelledby=&quot;tabs-upcoming-tab&quot; tabindex=&quot;0&quot; hidden=&quot;&quot;&gt;5 series.&lt;/div&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## Rules
 
diff --git a/_components/tag.md b/_components/tag.md
index 381208d..247d8da 100644
--- a/_components/tag.md
+++ b/_components/tag.md
@@ -7,23 +7,43 @@ summary: >-
 
 ## Default and removable
 
-<% example(title: "Tags", open: true) do %>
+<figure class="example">
+<figcaption>Tags</figcaption>
+<div>
 <span class="ad-tag">location:garage</span>
 <span class="ad-tag">category:appliance</span>
 <span class="ad-tag">
   category:receipt
   <button type="button" class="ad-tag__remove" aria-label="Remove tag">×</button>
 </span>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;span class=&quot;ad-tag&quot;&gt;location:garage&lt;/span&gt;
+&lt;span class=&quot;ad-tag&quot;&gt;category:appliance&lt;/span&gt;
+&lt;span class=&quot;ad-tag&quot;&gt;
+  category:receipt
+  &lt;button type=&quot;button&quot; class=&quot;ad-tag__remove&quot; aria-label=&quot;Remove tag&quot;&gt;×&lt;/button&gt;
+&lt;/span&gt;</code></pre>
+</details>
+</figure>
 
 ## Dashed — the "add tag" affordance
 
-<% example(title: "Add tag") do %>
+<figure class="example">
+<figcaption>Add tag</figcaption>
+<div>
 <div class="ad-tag-set">
   <span class="ad-tag">location:garage</span>
   <span class="ad-tag ad-tag--dashed">+ add tag</span>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-tag-set&quot;&gt;
+  &lt;span class=&quot;ad-tag&quot;&gt;location:garage&lt;/span&gt;
+  &lt;span class=&quot;ad-tag ad-tag--dashed&quot;&gt;+ add tag&lt;/span&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_components/toast.md b/_components/toast.md
index 93f9068..f43f59f 100644
--- a/_components/toast.md
+++ b/_components/toast.md
@@ -11,22 +11,41 @@ summary: >-
 The example below stays in document flow so it does not cover the page;
 `.ad-toast--fixed` is what actually pins it bottom-right in a real app.
 
-<% example(title: "Toast", open: true) do %>
+<figure class="example">
+<figcaption>Toast</figcaption>
+<div>
 <div class="ad-toast">
   <span class="ad-toast__message">Deleted "Call Mom"</span>
   <button type="button" class="ad-toast__action">Undo</button>
   <button type="button" class="ad-toast__dismiss" aria-label="Dismiss">×</button>
 </div>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-toast&quot;&gt;
+  &lt;span class=&quot;ad-toast__message&quot;&gt;Deleted &quot;Call Mom&quot;&lt;/span&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-toast__action&quot;&gt;Undo&lt;/button&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-toast__dismiss&quot; aria-label=&quot;Dismiss&quot;&gt;×&lt;/button&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## Without an action
 
-<% example(title: "No action") do %>
+<figure class="example">
+<figcaption>No action</figcaption>
+<div>
 <div class="ad-toast">
   <span class="ad-toast__message">Series created</span>
   <button type="button" class="ad-toast__dismiss" aria-label="Dismiss">×</button>
 </div>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class=&quot;ad-toast&quot;&gt;
+  &lt;span class=&quot;ad-toast__message&quot;&gt;Series created&lt;/span&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-toast__dismiss&quot; aria-label=&quot;Dismiss&quot;&gt;×&lt;/button&gt;
+&lt;/div&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
@@ -35,7 +54,7 @@ The example below stays in document flow so it does not cover the page;
     <thead><tr><th scope="col">Prop</th><th scope="col">Type</th><th scope="col">Notes</th></tr></thead>
     <tbody>
       <tr><td class="ad-token-name">message</td><td class="ad-table__code">string</td><td>Required.</td></tr>
-      <tr><td class="ad-token-name">actionLabel</td><td class="ad-table__code">string</td><td>Renders a destructive-coloured action, typically "Undo".</td></tr>
+      <tr><td class="ad-token-name">actionLabel</td><td class="ad-table__code">string</td><td>Renders a destructive-colored action, typically "Undo".</td></tr>
       <tr><td class="ad-token-name">onDismiss</td><td class="ad-table__code">function</td><td></td></tr>
     </tbody>
   </table>
diff --git a/_components/tooltip.md b/_components/tooltip.md
index c702f64..8ace3dd 100644
--- a/_components/tooltip.md
+++ b/_components/tooltip.md
@@ -9,25 +9,45 @@ summary: >-
 
 Hover or tab to the button below.
 
-<% example(title: "Tooltip", open: true) do %>
+<figure class="example">
+<figcaption>Tooltip</figcaption>
+<div>
 <span class="ad-tooltip">
   <button type="button" class="ad-icon-btn" aria-label="Delete">
-    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13"/></svg>
+    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13" /></svg>
   </button>
   <span class="ad-tooltip__bubble">Delete</span>
 </span>
-<% end %>
+</div>
+<details open><summary>Markup</summary>
+<pre><code>&lt;span class=&quot;ad-tooltip&quot;&gt;
+  &lt;button type=&quot;button&quot; class=&quot;ad-icon-btn&quot; aria-label=&quot;Delete&quot;&gt;
+    &lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.75&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13&quot; /&gt;&lt;/svg&gt;
+  &lt;/button&gt;
+  &lt;span class=&quot;ad-tooltip__bubble&quot;&gt;Delete&lt;/span&gt;
+&lt;/span&gt;</code></pre>
+</details>
+</figure>
 
 ## On any element
 
 A Tooltip wraps whatever it labels — most often an [IconButton](<%= relative_url('/components/icon-button/') %>), since that is the control with no visible text of its own.
 
-<% example(title: "On a badge") do %>
+<figure class="example">
+<figcaption>On a badge</figcaption>
+<div>
 <span class="ad-tooltip">
   <span class="ad-badge ad-badge--danger" tabindex="0">3d overdue</span>
   <span class="ad-tooltip__bubble">Due Tuesday, was Saturday</span>
 </span>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;span class=&quot;ad-tooltip&quot;&gt;
+  &lt;span class=&quot;ad-badge ad-badge--danger&quot; tabindex=&quot;0&quot;&gt;3d overdue&lt;/span&gt;
+  &lt;span class=&quot;ad-tooltip__bubble&quot;&gt;Due Tuesday, was Saturday&lt;/span&gt;
+&lt;/span&gt;</code></pre>
+</details>
+</figure>
 
 ## API
 
diff --git a/_config.yml b/_config.yml
index 70ef174..08d624d 100644
--- a/_config.yml
+++ b/_config.yml
@@ -3,14 +3,10 @@ tagline: A personal umbrella, given a shared visual spine
 description: >-
   Arbitrary Definitions is a personal design system for self-hosted,
   single-user tools — synthesized from three real running projects, not
-  written from a spec. Sixteen components and five foundations, rendered from
-  a single set of YAML token files by a small Ruby/ERB site builder running
-  on Ruby 4.0.
+  written from a spec. Sixteen components and five foundations, built with a
+  small Ruby/ERB site builder running on Ruby 4.0.
 
 url: https://kejadlen.github.io
 baseurl: /arbitrary-definitions-design-system
 repository: kejadlen/arbitrary-definitions-design-system
 lang: en
-
-# The prefix on every custom property and class this system emits.
-token_prefix: ad
diff --git a/_data/nav.yml b/_data/nav.yml
index cd726cd..4bb43ab 100644
--- a/_data/nav.yml
+++ b/_data/nav.yml
@@ -5,7 +5,7 @@
     - { text: Using the CSS, url: /install/ }
 - title: Foundations
   links:
-    - { text: Colour,     url: /foundations/color/ }
+    - { text: Color,      url: /foundations/color/ }
     - { text: Typography, url: /foundations/typography/ }
     - { text: Space,      url: /foundations/space/ }
     - { text: Elevation,  url: /foundations/elevation/ }
diff --git a/_data/tokens/color.yml b/_data/tokens/color.yml
deleted file mode 100644
index 3a0e851..0000000
--- a/_data/tokens/color.yml
+++ /dev/null
@@ -1,73 +0,0 @@
-# Raw palette ramps. These are the arbitrary part: hand-picked values that,
-# once written down, stop being arbitrary and start being the rule.
-#
-# Nothing in the site references a ramp step directly. Components only ever
-# reach for the semantic aliases defined in `semantic.yml`, which is what
-# makes retheming — or flipping to dark — a one-file change.
-#
-# Source values were authored in oklch() by the three projects this system was
-# synthesized from (see rationale.md). This repository's colour-maths test
-# suite only understands flat hex, so every value below is that same oklch
-# colour converted to sRGB hex — not re-picked, not rounded to a nicer number.
-#
-# `order` is a display index (0..9), independent of file position, so the docs
-# page can always render a ramp lightest-numbered-step-first regardless of the
-# order the steps had to be listed in to satisfy the monotonic-luminance test
-# below. `monotonic: false` opts a ramp out of that test entirely — it exists
-# for `accent`, which is five unrelated brand hues, not a light-to-dark scale.
-ramps:
-  - name: neutral
-    label: Neutral
-    description: >-
-      Low-chroma warm-gray — the intersection of ketchup's plain grays,
-      quire's warm cream/dark palette, and domus's warm paper. Not stark
-      white or black, and not any one project's specific warmth. Ten steps,
-      not twelve: the source defines exactly 0/50/100/150/200/300/500/600/800/900
-      — steps 400 and 700 do not exist.
-    monotonic: true
-    steps:
-      - { step: "50",  order: 1, value: "#fdfdfb", usage: "Card / surface stock — deliberately a hair lighter than the page it sits on." }
-      - { step: "0",   order: 0, value: "#faf9f7", usage: "Page background." }
-      - { step: "100", order: 2, value: "#f1f0ed", usage: "Recessed fill." }
-      - { step: "150", order: 3, value: "#e8e6e2", usage: "Recessed fill, deeper." }
-      - { step: "200", order: 4, value: "#dcdad7", usage: "Hairline border." }
-      - { step: "300", order: 5, value: "#c0bdb9", usage: "Rule / default border." }
-      - { step: "500", order: 6, value: "#888681", usage: "Muted-faint text. Intentionally sub-AA — see the Colour page." }
-      - { step: "600", order: 7, value: "#5f5d59", usage: "Muted / secondary text." }
-      - { step: "800", order: 8, value: "#2a2926", usage: "Strong text." }
-      - { step: "900", order: 9, value: "#100f0d", usage: "Ink — primary text, near-black." }
-
-  - name: neutral_dark
-    label: Neutral — dark
-    description: >-
-      The same low-chroma warm-gray scale, inverted, matching quire's
-      OS-preference dark palette. Same ten step numbers as `neutral`; `0` is
-      the darkest step here (the dark-theme page background) rather than the
-      lightest.
-    monotonic: true
-    steps:
-      - { step: "900", order: 9, value: "#e9e8e6", usage: "Primary text in dark mode, near-white." }
-      - { step: "800", order: 8, value: "#d9d7d4", usage: "Strong text in dark mode." }
-      - { step: "600", order: 7, value: "#a09e9a", usage: "Muted / secondary text in dark mode." }
-      - { step: "500", order: 6, value: "#7c7a76", usage: "Muted-faint text in dark mode." }
-      - { step: "300", order: 5, value: "#44423f", usage: "Rule / default border in dark mode." }
-      - { step: "200", order: 4, value: "#2c2b29", usage: "Hairline border in dark mode." }
-      - { step: "150", order: 3, value: "#252421", usage: "Recessed fill, deeper, in dark mode." }
-      - { step: "100", order: 2, value: "#1e1d1a", usage: "Recessed fill in dark mode." }
-      - { step: "50",  order: 1, value: "#151412", usage: "Card / surface stock in dark mode." }
-      - { step: "0",   order: 0, value: "#0e0d0b", usage: "Page background in dark mode." }
-
-  - name: accent
-    label: Accent
-    description: >-
-      One swappable accent per project — never two in the same view. Signal
-      Teal is the system default; the other four are the sanctioned menu a
-      project can switch to. This is a flat set of five brand hues, not a
-      light-to-dark scale, so it is exempt from the ramp-monotonicity test.
-    monotonic: false
-    steps:
-      - { step: "teal",     order: 0, value: "#3a6a63", usage: "Default. Neutral, technical." }
-      - { step: "clay",     order: 1, value: "#9a5a3c", usage: "Warm, archival — domus's flavour." }
-      - { step: "ink-blue", order: 2, value: "#2b4a78", usage: "Sober, utilitarian." }
-      - { step: "ochre",    order: 3, value: "#93701f", usage: "Dry, papery." }
-      - { step: "plum",     order: 4, value: "#6a3a5c", usage: "Quiet, personal." }
diff --git a/_data/tokens/contrast.yml b/_data/tokens/contrast.yml
deleted file mode 100644
index 381a4ae..0000000
--- a/_data/tokens/contrast.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-# The contrast contract.
-#
-# Colour is the one part of a design system that is not arbitrary: a ratio is
-# a fact about two colours and a pair of eyes. So these pairings are asserted
-# by test/test_token_set.rb in both themes, and the same list renders the
-# contract table on the Colour page. Adding a pair here adds it to the deploy
-# gate.
-#
-# Two things this system deliberately does NOT assert, both explained on the
-# Colour page rather than forced to pass:
-#   - `color-text-tertiary` (neutral.500) is 3.46:1 on the page in light mode —
-#     under the 4.5:1 floor. The source documents that step as "muted-faint
-#     text", reserved for placeholders and metadata, never body copy. That is
-#     a real, intentional property of the source palette, not a bug to fix by
-#     darkening the step.
-#   - Border contrast. `color-border-hairline` / `color-border-default` sit
-#     well under the 3:1 WCAG 1.4.11 asks of a meaningful boundary — the
-#     source's own words are "hairline (1px) borders and dividers do most of
-#     the elevation work", i.e. borders are deliberately decorative here. The
-#     source never claims a 3:1 bar for them, so this contract does not
-#     invent one.
-#
-# `color-accent-ink`, `color-accent-soft`, `color-success-bg`, and
-# `color-danger-bg` are `color-mix()` expressions, not flat hex, so
-# `lib/arbitrary_definitions/color_math.rb` cannot measure them — none of the
-# four appears on either side of a pair below.
-text:
-  label: Text
-  minimum: 4.5
-  standard: WCAG 2.1 SC 1.4.3 (normal-size text)
-  pairs:
-    - { foreground: color-text-primary, background: color-surface-page, note: Body copy, headings, and control labels on the page. }
-    - { foreground: color-text-secondary, background: color-surface-page, note: Supporting copy, hints, and mono meta values. }
-    - { foreground: color-accent-fg, background: color-accent, note: The label on a primary or dark-variant Button. }
-    - { foreground: color-success, background: color-accent-fg, note: Success text on a light backdrop — proxied by color-accent-fg, the system's only literal-white token, since color-success-bg is an unmeasurable color-mix() tint. }
-    - { foreground: color-danger, background: color-accent-fg, note: Overdue/destructive text on a light backdrop, proxied the same way as success above. }
diff --git a/_data/tokens/scale.yml b/_data/tokens/scale.yml
deleted file mode 100644
index 6620585..0000000
--- a/_data/tokens/scale.yml
+++ /dev/null
@@ -1,99 +0,0 @@
-# Non-colour tokens. `_plugins/token_generator.rb` turns most groups below
-# into a block of CSS custom properties under `:root`; `typography.styles` is
-# the one exception — those eight are compound rules (family + size + weight +
-# line-height + tracking in one declaration, exactly as the source ships
-# them), so they are written directly into `_sass/base/_typography.scss`
-# rather than compiled from here. This file is still their source of record:
-# the typography foundation page renders its specimen table from `styles`.
-typography:
-  label: Typography
-  description: >-
-    Three families and eight fluid Utopia steps (320→1240px viewport, 18→20px
-    body). Only one webfont is loaded — IBM Plex Mono; sans is a web-safe
-    system stack and serif is an opt-in reading variant, not loaded by default.
-  webfont_import: "@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&display=swap');"
-  families:
-    - token: font-sans
-      value: '-apple-system, "Helvetica Neue", Helvetica, Arial, system-ui, sans-serif'
-      usage: Interface text — every component, every screen. Web-safe; nothing is loaded for it.
-    - token: font-mono
-      value: '"IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Courier New", monospace'
-      usage: Every label, meta value, technical/catalog value, and code sample. The system's one loaded webfont.
-    - token: font-serif
-      value: 'Georgia, "Times New Roman", "Iowan Old Style", serif'
-      usage: Opt-in reading variant for a project that wants an editorial, archival feel (domus-style). Not loaded by default; web-safe only.
-  steps:
-    - { token: step--2, value: "clamp(0.7813rem, 0.7736rem + 0.0341vw, 0.8rem)",        px: "12.5 → 12.8", usage: "type-label — eyebrows, catalog labels, meta." }
-    - { token: step--1, value: "clamp(0.9375rem, 0.9119rem + 0.1136vw, 1rem)",          px: "15 → 16",     usage: "type-body-sm and type-mono — secondary copy, technical values." }
-    - { token: step-0,  value: "clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)",        px: "18 → 20",     usage: "type-body — the base size, and the body element's own size." }
-    - { token: step-1,  value: "clamp(1.35rem, 1.2631rem + 0.3864vw, 1.5625rem)",       px: "22 → 25",     usage: "type-h3 — card and subsection headings." }
-    - { token: step-2,  value: "clamp(1.62rem, 1.4837rem + 0.6057vw, 1.9531rem)",       px: "26 → 31",     usage: "type-h2 — section headings." }
-    - { token: step-3,  value: "clamp(1.944rem, 1.7405rem + 0.9044vw, 2.4414rem)",      px: "31 → 39",     usage: "type-h1 — screen titles." }
-    - { token: step-4,  value: "clamp(2.3328rem, 2.0387rem + 1.3072vw, 3.0518rem)",     px: "37 → 49",     usage: "type-display — one hero title per screen, at most." }
-    - { token: step-5,  value: "clamp(2.7994rem, 2.384rem + 1.8461vw, 3.8147rem)",      px: "45 → 61",     usage: "Reserved headroom. No compound style claims it yet." }
-  styles:
-    - { class: type-display, family: font-sans, weight: 600, size: step-4,  line_height: "1.08", tracking: "-0.015em", transform: none,    usage: "Page-level hero title, once per screen." }
-    - { class: type-h1,      family: font-sans, weight: 600, size: step-3,  line_height: "1.12", tracking: "-0.012em", transform: none,    usage: "Screen title." }
-    - { class: type-h2,      family: font-sans, weight: 600, size: step-2,  line_height: "1.16", tracking: "-0.01em",  transform: none,    usage: "Section heading." }
-    - { class: type-h3,      family: font-sans, weight: 600, size: step-1,  line_height: "1.25", tracking: "normal",   transform: none,    usage: "Card / subsection heading." }
-    - { class: type-body,    family: font-sans, weight: 400, size: step-0,  line_height: "1.55", tracking: "normal",   transform: none,    usage: "Body copy." }
-    - { class: type-body-sm, family: font-sans, weight: 400, size: step--1, line_height: "1.5",  tracking: "normal",   transform: none,    usage: "Secondary copy, hints." }
-    - { class: type-label,   family: font-mono, weight: 500, size: step--2, line_height: "1",    tracking: "0.08em",   transform: uppercase, usage: "Eyebrows, catalog labels, meta — SAVED, RECOMMENDED, OVERDUE." }
-    - { class: type-mono,    family: font-mono, weight: 400, size: step--1, line_height: "1.5",  tracking: "normal",   transform: none,    usage: "Technical values, IDs, dates, code." }
-
-space:
-  label: Space
-  description: >-
-    Nine fluid Utopia steps (320→1240px viewport), the same shared config as
-    type. 3xs through 3xl — no numbered scale, because there is no fixed base
-    unit here to count multiples of.
-  scale:
-    - { token: space-3xs, value: "clamp(0.3125rem, 0.3125rem + 0vw, 0.3125rem)",       px: "5",       usage: "The tightest gap — an icon to its label inside a small control." }
-    - { token: space-2xs, value: "clamp(0.5625rem, 0.5369rem + 0.1136vw, 0.625rem)",   px: "9 → 10",  usage: "Field-internal gaps, badge and tag padding." }
-    - { token: space-xs,  value: "clamp(0.875rem, 0.8494rem + 0.1136vw, 0.9375rem)",   px: "14 → 15", usage: "Control padding-inline, row-internal gaps, CardRow padding." }
-    - { token: space-s,   value: "clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)",     px: "18 → 20", usage: "Field-to-field spacing, Dialog title/footer padding." }
-    - { token: space-m,   value: "clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem)",   px: "27 → 30", usage: "Card body padding, Dialog body padding — the system's default inset." }
-    - { token: space-l,   value: "clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem)",       px: "36 → 40", usage: "Page gutters, section-to-section gaps." }
-    - { token: space-xl,  value: "clamp(3.375rem, 3.2216rem + 0.6818vw, 3.75rem)",     px: "54 → 60", usage: "Major section spacing — the gap above a dashboard's task list." }
-    - { token: space-2xl, value: "clamp(4.5rem, 4.2955rem + 0.9091vw, 5rem)",          px: "72 → 80", usage: "Page-level rhythm." }
-    - { token: space-3xl, value: "clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem)",       px: "108 → 120", usage: "Hero and empty-state breathing room." }
-
-radius:
-  label: Radius
-  description: >-
-    Small and quiet: a 4/6/10px scale, kept modest so it reads as a neutral
-    default rather than any one source project's specific softness. Nothing
-    pill-shaped except true pills.
-  scale:
-    - { token: radius-sm,   value: "4px",   usage: "Buttons, inputs, badges, tags." }
-    - { token: radius-md,   value: "6px",   usage: "IconButton, the segmented-tabs track, avatar chips." }
-    - { token: radius-lg,   value: "10px",  usage: "Cards, dialogs, toasts, photo slots — the system's \"surface\" radius." }
-    - { token: radius-full, value: "999px", usage: "True pills only — Badge, Tag, Switch, round avatars." }
-    - { token: border-w,    value: "1px",   usage: "The one hairline weight in the system." }
-
-elevation:
-  label: Elevation
-  description: >-
-    Two levels only, as every source project uses: flat — no shadow, a
-    hairline border does the work, and the default everywhere — and one
-    `shadow-float` for the single most-elevated element in a view: a Dialog,
-    a Toast, a floating menu.
-  scale:
-    - token: shadow-sm
-      value: "0 1px 2px rgba(20, 18, 14, 0.05)"
-      usage: Barely-there separation — a Switch thumb, a focused Input.
-    - token: shadow-float
-      value: "0 2px 6px rgba(20, 18, 14, 0.06), 0 10px 24px -12px rgba(20, 18, 14, 0.18)"
-      usage: The one floating layer in a view. Apply it to at most one element on screen at a time.
-
-motion:
-  label: Motion
-  description: >-
-    Two durations and one easing curve — this is an interface, not a title
-    sequence. Short ease transitions on hover/focus only; no bounce, no
-    spring, no page-transition choreography.
-  durations:
-    - { token: dur-fast, value: "0.12s", usage: "Hover and focus colour/border transitions — the default." }
-    - { token: dur-base, value: "0.16s", usage: "Switch track and thumb — the one moving part in the system." }
-  easings:
-    - { token: ease, value: "cubic-bezier(0.2, 0, 0, 1)", usage: "The only easing curve in the system. Fast out, settles gently, applied to every transition." }
diff --git a/_data/tokens/semantic.yml b/_data/tokens/semantic.yml
deleted file mode 100644
index 8d4d905..0000000
--- a/_data/tokens/semantic.yml
+++ /dev/null
@@ -1,113 +0,0 @@
-# Semantic colour aliases. Every component reads from this file and never
-# from a raw ramp. Values are either a `ramp.step` reference or a literal CSS
-# colour; `_plugins/token_generator.rb` resolves references at build time.
-#
-# `light` and `dark` are emitted as `:root` and `[data-theme="dark"]` blocks.
-# Two groups below — Accent's `-ink`/`-soft` pair and Status's `-bg` pair —
-# are literal `color-mix()` expressions rather than ramp references, exactly
-# mirroring the source's own `tokens/colors.css`. A `color-mix()` string is
-# not a flat hex, so `lib/arbitrary_definitions/color_math.rb` declines to
-# measure it (by design — see `HEX_PATTERN` / `UNMEASURABLE`); none of those
-# four tokens appears in `contrast.yml` for that reason.
-groups:
-  - name: Surfaces
-    id: surface
-    description: Backgrounds, from the page itself up to a card sitting on it.
-    tokens:
-      - token: color-surface-page
-        light: neutral.0
-        dark: neutral_dark.0
-        usage: The page itself. Nothing sits behind it.
-      - token: color-surface-card
-        light: neutral.50
-        dark: neutral_dark.50
-        usage: Cards, dialogs, toasts — anything that reads as a sheet on the page.
-      - token: color-surface-fill
-        light: neutral.100
-        dark: neutral_dark.100
-        usage: Recessed fill — a hovered row, a segmented-tab track, a photo placeholder.
-      - token: color-surface-fill-2
-        light: neutral.150
-        dark: neutral_dark.150
-        usage: A second, deeper fill step — the diagonal-grain stripe inside an empty photo slot.
-
-  - name: Borders
-    id: border
-    description: Two weights. Hairlines do most of the elevation work in this system, so there was never a reason for a third.
-    tokens:
-      - token: color-border-hairline
-        light: neutral.200
-        dark: neutral_dark.200
-        usage: Card edges, row dividers, dialog dividers. The default edge everywhere.
-      - token: color-border-default
-        light: neutral.300
-        dark: neutral_dark.300
-        usage: Input and button borders — anything a reader has to recognise as a control.
-
-  - name: Text
-    id: text
-    description: Three levels of emphasis, deliberately not four. Past three, a reader stops perceiving a hierarchy and starts perceiving noise.
-    tokens:
-      - token: color-text-primary
-        light: neutral.900
-        dark: neutral_dark.900
-        usage: Body copy, headings, and every control label.
-      - token: color-text-secondary
-        light: neutral.600
-        dark: neutral_dark.600
-        usage: Supporting copy, hints, mono meta values, type-label's colour.
-      - token: color-text-tertiary
-        light: neutral.500
-        dark: neutral_dark.500
-        usage: >-
-          Placeholders and de-emphasised metadata only — never body text. This
-          step is intentionally sub-AA against the page; see the caveat on the
-          Colour page.
-
-  - name: Accent
-    id: accent
-    description: >-
-      One swappable accent per project, held constant across a whole view.
-      `-ink` and `-soft` re-derive from `--ad-color-accent` via `color-mix()`
-      exactly as the source computes them, so retheming the accent retones
-      both automatically.
-    tokens:
-      - token: color-accent
-        light: accent.teal
-        dark: accent.teal
-        usage: Primary buttons, links, focus affordance, the active tab underline. Never two accents in one view.
-      - token: color-accent-fg
-        light: "#ffffff"
-        dark: "#ffffff"
-        usage: Label and icon colour on a filled accent surface. Fixed white, not a ramp reference.
-      - token: color-accent-ink
-        light: "color-mix(in oklch, var(--ad-color-accent) 78%, black)"
-        dark: "color-mix(in oklch, var(--ad-color-accent) 78%, black)"
-        usage: Text sitting on an `-soft` accent tint — an IconButton's active state, an accent avatar's initials.
-      - token: color-accent-soft
-        light: "color-mix(in oklch, var(--ad-color-accent) 12%, white)"
-        dark: "color-mix(in oklch, var(--ad-color-accent) 12%, white)"
-        usage: The tint behind an accent Badge, a toggled IconButton, an accent avatar chip.
-
-  - name: Status
-    id: status
-    description: >-
-      Exactly two states, both fixed — they read the same in every project and
-      are never swapped the way the accent is.
-    tokens:
-      - token: color-success
-        light: "#3f7a4a"
-        dark: "#3f7a4a"
-        usage: Success text and icons — a completed task's check, a saved-state badge.
-      - token: color-success-bg
-        light: "color-mix(in oklch, var(--ad-color-success) 12%, white)"
-        dark: "color-mix(in oklch, var(--ad-color-success) 12%, white)"
-        usage: Tint behind a success Badge. Mixed against literal white, so it stays constant across themes.
-      - token: color-danger
-        light: "#ad3a2c"
-        dark: "#ad3a2c"
-        usage: Overdue/destructive text, the Toast undo action, a danger Button's fill.
-      - token: color-danger-bg
-        light: "color-mix(in oklch, var(--ad-color-danger) 10%, white)"
-        dark: "color-mix(in oklch, var(--ad-color-danger) 10%, white)"
-        usage: Tint behind a danger Badge. Mixed against literal white, so it stays constant across themes.
diff --git a/_foundations/color.md b/_foundations/color.md
index 8d51352..077dac8 100644
--- a/_foundations/color.md
+++ b/_foundations/color.md
@@ -1,13 +1,13 @@
 ---
-title: Colour
+title: Color
 summary: >-
-  Two neutral ramps, five accents, two fixed semantic colours, seventeen
+  Two neutral ramps, five accents, two fixed semantic colors, seventeen
   semantic aliases. Low-chroma warm-gray, never stark white or black.
 ---
 
 ## The two layers
 
-The system separates *what a colour is* from *what a colour is for*.
+The system separates *what a color is* from *what a color is for*.
 
 The **ramps** below are raw material: ten steps of neutral (light), the same
 ten inverted for dark mode, and five accent hues. No component references
@@ -22,11 +22,11 @@ at different values — which is exactly what the dark block does.
   <p style="margin:0; font-size: var(--ad-step--1);">
     The neutral ramps were authored in <code>oklch()</code> by the three
     projects this system was synthesized from (see
-    <a href="<%= relative_url('/rationale/') %>">Why arbitrary</a>). This
-    site's contrast-testing suite only understands flat hex, so every value
-    below is that same colour converted to sRGB — not re-picked, not rounded.
-    Contrast figures on this page are computed from those hex values during
-    the build by <code>lib/arbitrary_definitions/color_math.rb</code>.
+    <a href="<%= relative_url('/rationale/') %>">Why arbitrary</a>). Every
+    value below is that same color converted to flat sRGB hex — not
+    re-picked, not rounded. The contrast figures on this page are computed
+    from those hex values with the standard WCAG 2.1 relative-luminance
+    formula, then transcribed here as fact rather than typed by eye.
   </p>
 </div>
 
@@ -36,34 +36,152 @@ Low-chroma warm-gray — the intersection of ketchup's plain grays, quire's
 warm cream/dark palette, and domus's warm paper. Ten steps, not twelve or
 sixteen: the source defines exactly these.
 
-<% neutral = site.data.tokens.color.ramps.find { |r| r.name == "neutral" } -%>
-<p class="ad-muted"><%= neutral.description %></p>
+<p class="ad-muted">Low-chroma warm-gray — the intersection of ketchup's plain grays, quire's warm cream/dark palette, and domus's warm paper. Not stark white or black, and not any one project's specific warmth. Ten steps, not twelve: the source defines exactly 0/50/100/150/200/300/500/600/800/900 — steps 400 and 700 do not exist.</p>
 <ul class="ad-swatch-grid">
-  <% neutral.steps.sort_by { |s| s.order }.each do |step| -%>
-  <% on = readable_on(step.value) -%>
   <li class="ad-swatch">
-    <div class="ad-swatch__chip" style="background: <%= step.value %>; color: <%= on %>;">n-<%= step.step %></div>
+    <div class="ad-swatch__chip" style="background: #faf9f7; color: #171512;">n-0</div>
     <div class="ad-swatch__meta">
-      <span class="ad-swatch__hex"><%= step.value %></span>
-      <span class="ad-swatch__ratio"><%= contrast_with(step.value, '#ffffff') %> on white</span>
+      <span class="ad-swatch__hex">#faf9f7</span>
+      <span class="ad-swatch__ratio">1.05 on white</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #fdfdfb; color: #171512;">n-50</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#fdfdfb</span>
+      <span class="ad-swatch__ratio">1.02 on white</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #f1f0ed; color: #171512;">n-100</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#f1f0ed</span>
+      <span class="ad-swatch__ratio">1.14 on white</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #e8e6e2; color: #171512;">n-150</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#e8e6e2</span>
+      <span class="ad-swatch__ratio">1.25 on white</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #dcdad7; color: #171512;">n-200</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#dcdad7</span>
+      <span class="ad-swatch__ratio">1.40 on white</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #c0bdb9; color: #171512;">n-300</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#c0bdb9</span>
+      <span class="ad-swatch__ratio">1.87 on white</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #888681; color: #171512;">n-500</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#888681</span>
+      <span class="ad-swatch__ratio">3.64 on white</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #5f5d59; color: #ffffff;">n-600</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#5f5d59</span>
+      <span class="ad-swatch__ratio">6.57 on white</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #2a2926; color: #ffffff;">n-800</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#2a2926</span>
+      <span class="ad-swatch__ratio">14.55 on white</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #100f0d; color: #ffffff;">n-900</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#100f0d</span>
+      <span class="ad-swatch__ratio">19.16 on white</span>
     </div>
   </li>
-  <% end -%>
 </ul>
 
-<% neutral_dark = site.data.tokens.color.ramps.find { |r| r.name == "neutral_dark" } -%>
-<p class="ad-muted"><%= neutral_dark.description %></p>
+<p class="ad-muted">The same low-chroma warm-gray scale, inverted, matching quire's OS-preference dark palette. Same ten step numbers as `neutral`; `0` is the darkest step here (the dark-theme page background) rather than the lightest.</p>
 <ul class="ad-swatch-grid">
-  <% neutral_dark.steps.sort_by { |s| s.order }.each do |step| -%>
-  <% on = readable_on(step.value) -%>
   <li class="ad-swatch">
-    <div class="ad-swatch__chip" style="background: <%= step.value %>; color: <%= on %>;">nd-<%= step.step %></div>
+    <div class="ad-swatch__chip" style="background: #0e0d0b; color: #ffffff;">nd-0</div>
     <div class="ad-swatch__meta">
-      <span class="ad-swatch__hex"><%= step.value %></span>
-      <span class="ad-swatch__ratio"><%= contrast_with(step.value, '#000000') %> on black</span>
+      <span class="ad-swatch__hex">#0e0d0b</span>
+      <span class="ad-swatch__ratio">1.08 on black</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #151412; color: #ffffff;">nd-50</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#151412</span>
+      <span class="ad-swatch__ratio">1.14 on black</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #1e1d1a; color: #ffffff;">nd-100</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#1e1d1a</span>
+      <span class="ad-swatch__ratio">1.25 on black</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #252421; color: #ffffff;">nd-150</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#252421</span>
+      <span class="ad-swatch__ratio">1.35 on black</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #2c2b29; color: #ffffff;">nd-200</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#2c2b29</span>
+      <span class="ad-swatch__ratio">1.48 on black</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #44423f; color: #ffffff;">nd-300</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#44423f</span>
+      <span class="ad-swatch__ratio">2.10 on black</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #7c7a76; color: #ffffff;">nd-500</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#7c7a76</span>
+      <span class="ad-swatch__ratio">4.90 on black</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #a09e9a; color: #171512;">nd-600</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#a09e9a</span>
+      <span class="ad-swatch__ratio">7.85 on black</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #d9d7d4; color: #171512;">nd-800</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#d9d7d4</span>
+      <span class="ad-swatch__ratio">14.62 on black</span>
+    </div>
+  </li>
+  <li class="ad-swatch">
+    <div class="ad-swatch__chip" style="background: #e9e8e6; color: #171512;">nd-900</div>
+    <div class="ad-swatch__meta">
+      <span class="ad-swatch__hex">#e9e8e6</span>
+      <span class="ad-swatch__ratio">17.15 on black</span>
     </div>
   </li>
-  <% end -%>
 </ul>
 
 ## Accent
@@ -71,15 +189,32 @@ sixteen: the source defines exactly these.
 One swappable accent per project — never two in the same view. Signal Teal is
 the default; the other four are the sanctioned menu a project can switch to.
 
-<% accent = site.data.tokens.color.ramps.find { |r| r.name == "accent" } -%>
 <ul class="ad-accent-grid">
-  <% accent.steps.each do |step| -%>
   <li>
-    <div class="ad-accent-demo" style="background: <%= step.value %>;"><%= step.step %><% if step.step == "teal" %> (default)<% end %></div>
-    <div class="ad-specimen__meta"><span class="ad-token-name"><%= step.value %></span><span><%= contrast_with(step.value, '#ffffff') %>:1 on white</span></div>
-    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;"><%= step.usage %></p>
+    <div class="ad-accent-demo" style="background: #3a6a63;">teal (default)</div>
+    <div class="ad-specimen__meta"><span class="ad-token-name">#3a6a63</span><span>6.14:1 on white</span></div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">Default. Neutral, technical.</p>
+  </li>
+  <li>
+    <div class="ad-accent-demo" style="background: #9a5a3c;">clay</div>
+    <div class="ad-specimen__meta"><span class="ad-token-name">#9a5a3c</span><span>5.38:1 on white</span></div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">Warm, archival — domus's flavor.</p>
+  </li>
+  <li>
+    <div class="ad-accent-demo" style="background: #2b4a78;">ink-blue</div>
+    <div class="ad-specimen__meta"><span class="ad-token-name">#2b4a78</span><span>8.92:1 on white</span></div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">Sober, utilitarian.</p>
+  </li>
+  <li>
+    <div class="ad-accent-demo" style="background: #93701f;">ochre</div>
+    <div class="ad-specimen__meta"><span class="ad-token-name">#93701f</span><span>4.59:1 on white</span></div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">Dry, papery.</p>
+  </li>
+  <li>
+    <div class="ad-accent-demo" style="background: #6a3a5c;">plum</div>
+    <div class="ad-specimen__meta"><span class="ad-token-name">#6a3a5c</span><span>8.85:1 on white</span></div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">Quiet, personal.</p>
   </li>
-  <% end -%>
 </ul>
 
 <div class="ad-callout">
@@ -99,10 +234,84 @@ the default; the other four are the sanctioned menu a project can switch to.
 Both themes, side by side. The left half of each chip is the light value and
 the right half is the dark one.
 
-<% site.data.tokens.semantic.groups.each do |group| -%>
-### <%= group.name %>
+### Surfaces
+
+<p class="ad-muted">Backgrounds, from the page itself up to a card sitting on it.</p>
+
+<div class="ad-table-wrap">
+  <table class="ad-table ad-table--compact">
+    <thead>
+      <tr>
+        <th scope="col">Token</th>
+        <th scope="col">Light</th>
+        <th scope="col">Dark</th>
+        <th scope="col">Use for</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #faf9f7"></span>
+              <span style="background: #0e0d0b"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-surface-page</span>
+          </div>
+        </td>
+        <td class="ad-table__code">neutral.0</td>
+        <td class="ad-table__code">neutral_dark.0</td>
+        <td>The page itself. Nothing sits behind it.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #fdfdfb"></span>
+              <span style="background: #151412"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-surface-card</span>
+          </div>
+        </td>
+        <td class="ad-table__code">neutral.50</td>
+        <td class="ad-table__code">neutral_dark.50</td>
+        <td>Cards, dialogs, toasts — anything that reads as a sheet on the page.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #f1f0ed"></span>
+              <span style="background: #1e1d1a"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-surface-fill</span>
+          </div>
+        </td>
+        <td class="ad-table__code">neutral.100</td>
+        <td class="ad-table__code">neutral_dark.100</td>
+        <td>Recessed fill — a hovered row, a segmented-tab track, a photo placeholder.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #e8e6e2"></span>
+              <span style="background: #252421"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-surface-fill-2</span>
+          </div>
+        </td>
+        <td class="ad-table__code">neutral.150</td>
+        <td class="ad-table__code">neutral_dark.150</td>
+        <td>A second, deeper fill step — the diagonal-grain stripe inside an empty photo slot.</td>
+      </tr>
+    </tbody>
+  </table>
+</div>
+
+### Borders
 
-<p class="ad-muted"><%= group.description %></p>
+<p class="ad-muted">Two weights. Hairlines do most of the elevation work in this system, so there was never a reason for a third.</p>
 
 <div class="ad-table-wrap">
   <table class="ad-table ad-table--compact">
@@ -115,47 +324,248 @@ the right half is the dark one.
       </tr>
     </thead>
     <tbody>
-      <% group.tokens.each do |token| -%>
-      <% flat = site.data.semantic_flat.find { |f| f.token == token.token } -%>
       <tr>
         <td>
           <div class="ad-alias-row">
-            <% if flat.light.include?("color-mix") -%>
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #dcdad7"></span>
+              <span style="background: #2c2b29"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-border-hairline</span>
+          </div>
+        </td>
+        <td class="ad-table__code">neutral.200</td>
+        <td class="ad-table__code">neutral_dark.200</td>
+        <td>Card edges, row dividers, dialog dividers. The default edge everywhere.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #c0bdb9"></span>
+              <span style="background: #44423f"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-border-default</span>
+          </div>
+        </td>
+        <td class="ad-table__code">neutral.300</td>
+        <td class="ad-table__code">neutral_dark.300</td>
+        <td>Input and button borders — anything a reader has to recognize as a control.</td>
+      </tr>
+    </tbody>
+  </table>
+</div>
+
+### Text
+
+<p class="ad-muted">Three levels of emphasis, deliberately not four. Past three, a reader stops perceiving a hierarchy and starts perceiving noise.</p>
+
+<div class="ad-table-wrap">
+  <table class="ad-table ad-table--compact">
+    <thead>
+      <tr>
+        <th scope="col">Token</th>
+        <th scope="col">Light</th>
+        <th scope="col">Dark</th>
+        <th scope="col">Use for</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #100f0d"></span>
+              <span style="background: #e9e8e6"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-text-primary</span>
+          </div>
+        </td>
+        <td class="ad-table__code">neutral.900</td>
+        <td class="ad-table__code">neutral_dark.900</td>
+        <td>Body copy, headings, and every control label.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #5f5d59"></span>
+              <span style="background: #a09e9a"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-text-secondary</span>
+          </div>
+        </td>
+        <td class="ad-table__code">neutral.600</td>
+        <td class="ad-table__code">neutral_dark.600</td>
+        <td>Supporting copy, hints, mono meta values, type-label's color.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #888681"></span>
+              <span style="background: #7c7a76"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-text-tertiary</span>
+          </div>
+        </td>
+        <td class="ad-table__code">neutral.500</td>
+        <td class="ad-table__code">neutral_dark.500</td>
+        <td>Placeholders and de-emphasized metadata only — never body text. This step is intentionally sub-AA against the page; see the caveat on the Color page.</td>
+      </tr>
+    </tbody>
+  </table>
+</div>
+
+### Accent
+
+<p class="ad-muted">One swappable accent per project, held constant across a whole view. `-ink` and `-soft` re-derive from `--ad-color-accent` via `color-mix()` exactly as the source computes them, so retheming the accent retones both automatically.</p>
+
+<div class="ad-table-wrap">
+  <table class="ad-table ad-table--compact">
+    <thead>
+      <tr>
+        <th scope="col">Token</th>
+        <th scope="col">Light</th>
+        <th scope="col">Dark</th>
+        <th scope="col">Use for</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #3a6a63"></span>
+              <span style="background: #3a6a63"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-accent</span>
+          </div>
+        </td>
+        <td class="ad-table__code">accent.teal</td>
+        <td class="ad-table__code">accent.teal</td>
+        <td>Primary buttons, links, focus affordance, the active tab underline. Never two accents in one view.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #ffffff"></span>
+              <span style="background: #ffffff"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-accent-fg</span>
+          </div>
+        </td>
+        <td class="ad-table__code">#ffffff</td>
+        <td class="ad-table__code">#ffffff</td>
+        <td>Label and icon color on a filled accent surface. Fixed white, not a ramp reference.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip" aria-hidden="true" style="background: repeating-linear-gradient(45deg, var(--ad-color-surface-fill) 0 4px, var(--ad-color-surface-card) 4px 8px);"></span>
+            <span class="ad-token-name">--ad-color-accent-ink</span>
+          </div>
+        </td>
+        <td class="ad-table__code">color-mix(in oklch, var(--ad-color-accent) 78%, black)</td>
+        <td class="ad-table__code">color-mix(in oklch, var(--ad-color-accent) 78%, black)</td>
+        <td>Text sitting on an `-soft` accent tint — an IconButton's active state, an accent avatar's initials.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
             <span class="ad-alias-chip" aria-hidden="true" style="background: repeating-linear-gradient(45deg, var(--ad-color-surface-fill) 0 4px, var(--ad-color-surface-card) 4px 8px);"></span>
-            <% else -%>
+            <span class="ad-token-name">--ad-color-accent-soft</span>
+          </div>
+        </td>
+        <td class="ad-table__code">color-mix(in oklch, var(--ad-color-accent) 12%, white)</td>
+        <td class="ad-table__code">color-mix(in oklch, var(--ad-color-accent) 12%, white)</td>
+        <td>The tint behind an accent Badge, a toggled IconButton, an accent avatar chip.</td>
+      </tr>
+    </tbody>
+  </table>
+</div>
+
+### Status
+
+<p class="ad-muted">Exactly two states, both fixed — they read the same in every project and are never swapped the way the accent is.</p>
+
+<div class="ad-table-wrap">
+  <table class="ad-table ad-table--compact">
+    <thead>
+      <tr>
+        <th scope="col">Token</th>
+        <th scope="col">Light</th>
+        <th scope="col">Dark</th>
+        <th scope="col">Use for</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
+              <span style="background: #3f7a4a"></span>
+              <span style="background: #3f7a4a"></span>
+            </span>
+            <span class="ad-token-name">--ad-color-success</span>
+          </div>
+        </td>
+        <td class="ad-table__code">#3f7a4a</td>
+        <td class="ad-table__code">#3f7a4a</td>
+        <td>Success text and icons — a completed task's check, a saved-state badge.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip" aria-hidden="true" style="background: repeating-linear-gradient(45deg, var(--ad-color-surface-fill) 0 4px, var(--ad-color-surface-card) 4px 8px);"></span>
+            <span class="ad-token-name">--ad-color-success-bg</span>
+          </div>
+        </td>
+        <td class="ad-table__code">color-mix(in oklch, var(--ad-color-success) 12%, white)</td>
+        <td class="ad-table__code">color-mix(in oklch, var(--ad-color-success) 12%, white)</td>
+        <td>Tint behind a success Badge. Mixed against literal white, so it stays constant across themes.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
             <span class="ad-alias-chip ad-alias-chip--pair" aria-hidden="true">
-              <span style="background: <%= flat.light %>"></span>
-              <span style="background: <%= flat.dark %>"></span>
+              <span style="background: #ad3a2c"></span>
+              <span style="background: #ad3a2c"></span>
             </span>
-            <% end -%>
-            <span class="ad-token-name"><%= flat.name %></span>
+            <span class="ad-token-name">--ad-color-danger</span>
           </div>
         </td>
-        <td class="ad-table__code"><%= token.light %></td>
-        <td class="ad-table__code"><%= token.dark %></td>
-        <td><%= token.usage %></td>
+        <td class="ad-table__code">#ad3a2c</td>
+        <td class="ad-table__code">#ad3a2c</td>
+        <td>Overdue/destructive text, the Toast undo action, a danger Button's fill.</td>
+      </tr>
+      <tr>
+        <td>
+          <div class="ad-alias-row">
+            <span class="ad-alias-chip" aria-hidden="true" style="background: repeating-linear-gradient(45deg, var(--ad-color-surface-fill) 0 4px, var(--ad-color-surface-card) 4px 8px);"></span>
+            <span class="ad-token-name">--ad-color-danger-bg</span>
+          </div>
+        </td>
+        <td class="ad-table__code">color-mix(in oklch, var(--ad-color-danger) 10%, white)</td>
+        <td class="ad-table__code">color-mix(in oklch, var(--ad-color-danger) 10%, white)</td>
+        <td>Tint behind a danger Badge. Mixed against literal white, so it stays constant across themes.</td>
       </tr>
-      <% end -%>
     </tbody>
   </table>
 </div>
-<% end -%>
 
 ## Contrast that has to hold
 
-Colour is the one part of this system that is not arbitrary: a ratio is a
-fact about two colours and a pair of eyes, and it does not care what the
-palette wants. The pairings below live in `_data/tokens/contrast.yml`,
-`test/test_token_set.rb` asserts every one of them in both themes, and this
-table is rendered from that same file.
-
-<% contract = site.data.tokens.contrast -%>
-<% flat = site.data.semantic_flat -%>
+Color is the one part of this system that is not arbitrary: a ratio is a
+fact about two colors and a pair of eyes, and it does not care what the
+palette wants. The pairings below are fixed facts about the token values
+above, computed once with the standard WCAG 2.1 relative-luminance formula
+and checked by hand rather than by a build-time test.
 
-<% contract.each do |_key, spec| -%>
-### <%= spec.label %> — <%= spec.minimum %>:1 minimum
+### Text — 4.5:1 minimum
 
-<p class="ad-muted"><%= spec.standard %></p>
+<p class="ad-muted">WCAG 2.1 SC 1.4.3 (normal-size text)</p>
 
 <div class="ad-table-wrap">
   <table class="ad-table ad-table--compact">
@@ -168,31 +578,64 @@ table is rendered from that same file.
       </tr>
     </thead>
     <tbody>
-      <% spec.pairs.each do |pair| -%>
-      <% fg = flat.find { |f| f.token == pair.foreground } -%>
-      <% bg = flat.find { |f| f.token == pair.background } -%>
       <tr>
         <td>
           <span class="ad-alias-row">
-            <span class="ad-alias-chip" aria-hidden="true"
-                  style="background: <%= bg.light %>; color: <%= fg.light %>; display: grid; place-items: center; font-size: 10px;">Aa</span>
-            <span class="ad-token-name"><%= pair.foreground %><br><span class="ad-subtle">on <%= pair.background %></span></span>
+            <span class="ad-alias-chip" aria-hidden="true" style="background: #faf9f7; color: #100f0d; display: grid; place-items: center; font-size: 10px;">Aa</span>
+            <span class="ad-token-name">color-text-primary<br><span class="ad-subtle">on color-surface-page</span></span>
           </span>
         </td>
-        <td><%= pair.note %></td>
-        <% light_ratio = contrast_with(fg.light, bg.light) -%>
-        <% dark_ratio = contrast_with(fg.dark, bg.dark) -%>
-        <td class="ad-table__num"><%= light_ratio %></td>
-        <td class="ad-table__num"><%= dark_ratio %></td>
+        <td>Body copy</td>
+        <td class="ad-table__num">18.21</td>
+        <td class="ad-table__num">15.86</td>
+      </tr>
+      <tr>
+        <td>
+          <span class="ad-alias-row">
+            <span class="ad-alias-chip" aria-hidden="true" style="background: #faf9f7; color: #5f5d59; display: grid; place-items: center; font-size: 10px;">Aa</span>
+            <span class="ad-token-name">color-text-secondary<br><span class="ad-subtle">on color-surface-page</span></span>
+          </span>
+        </td>
+        <td>Supporting copy</td>
+        <td class="ad-table__num">6.24</td>
+        <td class="ad-table__num">7.26</td>
+      </tr>
+      <tr>
+        <td>
+          <span class="ad-alias-row">
+            <span class="ad-alias-chip" aria-hidden="true" style="background: #3a6a63; color: #ffffff; display: grid; place-items: center; font-size: 10px;">Aa</span>
+            <span class="ad-token-name">color-accent-fg<br><span class="ad-subtle">on color-accent</span></span>
+          </span>
+        </td>
+        <td>The label on a primary or dark-variant Button.</td>
+        <td class="ad-table__num">6.14</td>
+        <td class="ad-table__num">6.14</td>
+      </tr>
+      <tr>
+        <td>
+          <span class="ad-alias-row">
+            <span class="ad-alias-chip" aria-hidden="true" style="background: #ffffff; color: #3f7a4a; display: grid; place-items: center; font-size: 10px;">Aa</span>
+            <span class="ad-token-name">color-success<br><span class="ad-subtle">on color-accent-fg</span></span>
+          </span>
+        </td>
+        <td>Success text on a light backdrop — proxied by color-accent-fg</td>
+        <td class="ad-table__num">5.13</td>
+        <td class="ad-table__num">5.13</td>
+      </tr>
+      <tr>
+        <td>
+          <span class="ad-alias-row">
+            <span class="ad-alias-chip" aria-hidden="true" style="background: #ffffff; color: #ad3a2c; display: grid; place-items: center; font-size: 10px;">Aa</span>
+            <span class="ad-token-name">color-danger<br><span class="ad-subtle">on color-accent-fg</span></span>
+          </span>
+        </td>
+        <td>Overdue/destructive text on a light backdrop</td>
+        <td class="ad-table__num">6.14</td>
+        <td class="ad-table__num">6.14</td>
       </tr>
-      <% end -%>
     </tbody>
   </table>
 </div>
-<% end -%>
-
-Every figure above was computed during this build from the token value
-itself — none of it was typed by a person.
 
 <div class="ad-callout">
   <p style="margin:0; font-size: var(--ad-step--1);">
@@ -200,7 +643,7 @@ itself — none of it was typed by a person.
     <strong>Text tertiary</strong> — <code>--ad-color-text-tertiary</code>
     (neutral.500) is roughly 3.46:1 against the page in light mode, under the
     4.5:1 floor. The source documents that step as muted-faint text —
-    placeholders and de-emphasised metadata, never body copy — so this is a
+    placeholders and de-emphasized metadata, never body copy — so this is a
     real, intentional property of the palette, not a bug to patch by
     darkening the step.
     <strong>Borders</strong> — <code>--ad-color-border-hairline</code> and
@@ -214,8 +657,8 @@ itself — none of it was typed by a person.
 
 ## Rules
 
-- **Never write a hex outside `_data/tokens/color.yml`.** If a component
-  needs a colour that does not exist yet, add the alias.
+- **Never write a hex outside `assets/css/tokens.css`.** If a component
+  needs a color that does not exist yet, add the alias.
 - **One accent per project, never two in a view.** Swapping `--ad-color-accent`
   re-derives `-ink` and `-soft` automatically via `color-mix()`.
 - **`--ad-color-success` and `--ad-color-danger` are fixed.** They read the
diff --git a/_foundations/elevation.md b/_foundations/elevation.md
index 3eba4ca..3ddfed4 100644
--- a/_foundations/elevation.md
+++ b/_foundations/elevation.md
@@ -14,16 +14,21 @@ is the default everywhere. Shadow is the exception, not the baseline.
 ## The two levels
 
 <ul class="ad-elevation-grid">
-  <% site.data.tokens.scale.elevation.scale.each do |step| -%>
   <li>
-    <div class="ad-elevation-demo" style="box-shadow: <%= step.value %>;"></div>
-    <div class="ad-specimen__meta"><span class="ad-token-name"><%= step.token %></span></div>
-    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;"><%= step.usage %></p>
+    <div class="ad-elevation-demo" style="box-shadow: 0 1px 2px rgba(20, 18, 14, 0.05);"></div>
+    <div class="ad-specimen__meta"><span class="ad-token-name">shadow-sm</span></div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">Barely-there separation — a Switch thumb, a focused Input.</p>
+  </li>
+  <li>
+    <div class="ad-elevation-demo" style="box-shadow: 0 2px 6px rgba(20, 18, 14, 0.06), 0 10px 24px -12px rgba(20, 18, 14, 0.18);"></div>
+    <div class="ad-specimen__meta"><span class="ad-token-name">shadow-float</span></div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">The one floating layer in a view. Apply it to at most one element on screen at a time.</p>
   </li>
-  <% end -%>
 </ul>
 
-<% example(title: "Flat vs. float", layout: "grid", surface: "sunken") do %>
+<figure class="example">
+<figcaption>Flat vs. float</figcaption>
+<div data-layout="grid" data-surface="sunken">
 <article class="ad-card">
   <div class="ad-card__body">
     <h3 class="ad-card__title" style="font-size: var(--ad-step--1);">Flat</h3>
@@ -36,7 +41,22 @@ is the default everywhere. Shadow is the exception, not the baseline.
     <p class="ad-card__text">shadow-float. Reserved for one element per view — a Dialog, a Toast.</p>
   </div>
 </article>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;article class="ad-card"&gt;
+  &lt;div class="ad-card__body"&gt;
+    &lt;h3 class="ad-card__title" style="font-size: var(--ad-step--1);"&gt;Flat&lt;/h3&gt;
+    &lt;p class="ad-card__text"&gt;The default. A hairline border, no shadow.&lt;/p&gt;
+  &lt;/div&gt;
+&lt;/article&gt;
+&lt;article class="ad-card ad-card--elevated"&gt;
+  &lt;div class="ad-card__body"&gt;
+    &lt;h3 class="ad-card__title" style="font-size: var(--ad-step--1);"&gt;Elevated&lt;/h3&gt;
+    &lt;p class="ad-card__text"&gt;shadow-float. Reserved for one element per view — a Dialog, a Toast.&lt;/p&gt;
+  &lt;/div&gt;
+&lt;/article&gt;</code></pre>
+</details>
+</figure>
 
 ## Elevation in the dark theme
 
diff --git a/_foundations/motion.md b/_foundations/motion.md
index 32208f4..d53cc70 100644
--- a/_foundations/motion.md
+++ b/_foundations/motion.md
@@ -15,13 +15,16 @@ easing, no page-transition choreography.
   <table class="ad-table ad-table--compact">
     <thead><tr><th scope="col">Token</th><th scope="col">Value</th><th scope="col">Use for</th></tr></thead>
     <tbody>
-      <% site.data.tokens.scale.motion.durations.each do |row| -%>
       <tr>
-        <td class="ad-token-name"><%= custom_property(row.token) %></td>
-        <td class="ad-table__code"><%= row.value %></td>
-        <td><%= row.usage %></td>
+        <td class="ad-token-name">--ad-dur-fast</td>
+        <td class="ad-table__code">0.12s</td>
+        <td>Hover and focus color/border transitions — the default.</td>
+      </tr>
+      <tr>
+        <td class="ad-token-name">--ad-dur-base</td>
+        <td class="ad-table__code">0.16s</td>
+        <td>Switch track and thumb — the one moving part in the system.</td>
       </tr>
-      <% end -%>
     </tbody>
   </table>
 </div>
@@ -31,23 +34,21 @@ easing, no page-transition choreography.
 There is exactly one curve in the system. Hover the tile below to run it.
 
 <ul class="ad-motion-grid">
-  <% site.data.tokens.scale.motion.easings.each do |row| -%>
   <li>
     <div class="ad-motion-demo" tabindex="0">
       <span class="ad-motion-demo__dot"></span>
     </div>
-    <div class="ad-specimen__meta"><span class="ad-token-name"><%= custom_property(row.token) %></span></div>
-    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;"><%= row.usage %></p>
+    <div class="ad-specimen__meta"><span class="ad-token-name">--ad-ease</span></div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">The only easing curve in the system. Fast out, settles gently, applied to every transition.</p>
   </li>
-  <% end -%>
 </ul>
 
 ## Reduced motion is not a downgrade
 
-`_plugins/token_generator.rb` emits a `prefers-reduced-motion: reduce` block
-that sets every duration token to `0ms`, and the base stylesheet clamps any
-remaining transition or animation to a hundredth of a millisecond. Components
-do not have to opt in, and cannot opt out.
+`tokens.css` includes a `prefers-reduced-motion: reduce` block that sets
+every duration token to `0ms`, and the base stylesheet clamps any remaining
+transition or animation to a hundredth of a millisecond. Components do not
+have to opt in, and cannot opt out.
 
 ```css
 @media (prefers-reduced-motion: reduce) {
@@ -59,9 +60,11 @@ do not have to opt in, and cannot opt out.
 ```
 
 The state change still happens. The Switch still moves. It simply arrives
-instead of travelling.
+instead of traveling.
 
-<% example(title: "Motion in practice") do %>
+<figure class="example">
+<figcaption>Motion in practice</figcaption>
+<div>
 <label class="ad-switch">
   <input type="checkbox" checked>
   <span class="ad-switch__track"></span>
@@ -72,13 +75,26 @@ instead of travelling.
   <span class="ad-switch__track"></span>
   <span>Weekly digest</span>
 </label>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;label class="ad-switch"&gt;
+  &lt;input type="checkbox" checked&gt;
+  &lt;span class="ad-switch__track"&gt;&lt;/span&gt;
+  &lt;span&gt;Dark mode&lt;/span&gt;
+&lt;/label&gt;
+&lt;label class="ad-switch"&gt;
+  &lt;input type="checkbox"&gt;
+  &lt;span class="ad-switch__track"&gt;&lt;/span&gt;
+  &lt;span&gt;Weekly digest&lt;/span&gt;
+&lt;/label&gt;</code></pre>
+</details>
+</figure>
 
 ## Rules
 
 - **Transition named properties, never `all`.** `transition: all` animates
   things you did not think about, including layout.
-- **Hover states shift a background one step deeper, never a colour or
+- **Hover states shift a background one step deeper, never a color or
   opacity trick.** Surface → fill is the whole vocabulary.
 - **Press is not animated.** Rely on the hover-darken plus native `:active`,
   if anything at all.
diff --git a/_foundations/space.md b/_foundations/space.md
index 3c1adf5..e7e4850 100644
--- a/_foundations/space.md
+++ b/_foundations/space.md
@@ -8,13 +8,51 @@ summary: >-
 ## The scale
 
 <div class="ad-ruler">
-  <% site.data.tokens.scale.space.scale.each do |step| -%>
   <div class="ad-ruler__row">
-    <span class="ad-token-name"><%= step.token %></span>
-    <span class="ad-ruler__num"><%= step.px %>px</span>
-    <span class="ad-ruler__bar" style="width: <%= step.value %>; min-width: 1px;"></span>
+    <span class="ad-token-name">space-3xs</span>
+    <span class="ad-ruler__num">5px</span>
+    <span class="ad-ruler__bar" style="width: clamp(0.3125rem, 0.3125rem + 0vw, 0.3125rem); min-width: 1px;"></span>
+  </div>
+  <div class="ad-ruler__row">
+    <span class="ad-token-name">space-2xs</span>
+    <span class="ad-ruler__num">9 → 10px</span>
+    <span class="ad-ruler__bar" style="width: clamp(0.5625rem, 0.5369rem + 0.1136vw, 0.625rem); min-width: 1px;"></span>
+  </div>
+  <div class="ad-ruler__row">
+    <span class="ad-token-name">space-xs</span>
+    <span class="ad-ruler__num">14 → 15px</span>
+    <span class="ad-ruler__bar" style="width: clamp(0.875rem, 0.8494rem + 0.1136vw, 0.9375rem); min-width: 1px;"></span>
+  </div>
+  <div class="ad-ruler__row">
+    <span class="ad-token-name">space-s</span>
+    <span class="ad-ruler__num">18 → 20px</span>
+    <span class="ad-ruler__bar" style="width: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem); min-width: 1px;"></span>
+  </div>
+  <div class="ad-ruler__row">
+    <span class="ad-token-name">space-m</span>
+    <span class="ad-ruler__num">27 → 30px</span>
+    <span class="ad-ruler__bar" style="width: clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem); min-width: 1px;"></span>
+  </div>
+  <div class="ad-ruler__row">
+    <span class="ad-token-name">space-l</span>
+    <span class="ad-ruler__num">36 → 40px</span>
+    <span class="ad-ruler__bar" style="width: clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem); min-width: 1px;"></span>
+  </div>
+  <div class="ad-ruler__row">
+    <span class="ad-token-name">space-xl</span>
+    <span class="ad-ruler__num">54 → 60px</span>
+    <span class="ad-ruler__bar" style="width: clamp(3.375rem, 3.2216rem + 0.6818vw, 3.75rem); min-width: 1px;"></span>
+  </div>
+  <div class="ad-ruler__row">
+    <span class="ad-token-name">space-2xl</span>
+    <span class="ad-ruler__num">72 → 80px</span>
+    <span class="ad-ruler__bar" style="width: clamp(4.5rem, 4.2955rem + 0.9091vw, 5rem); min-width: 1px;"></span>
+  </div>
+  <div class="ad-ruler__row">
+    <span class="ad-token-name">space-3xl</span>
+    <span class="ad-ruler__num">108 → 120px</span>
+    <span class="ad-ruler__bar" style="width: clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem); min-width: 1px;"></span>
   </div>
-  <% end -%>
 </div>
 
 ## How to pick one
@@ -30,7 +68,9 @@ summary: >-
 | Major section spacing | `--ad-space-xl` / `--ad-space-2xl` |
 | Hero and empty-state breathing room | `--ad-space-3xl` |
 
-<% example(title: "The same card at three densities", layout: "grid") do %>
+<figure class="example">
+<figcaption>The same card at three densities</figcaption>
+<div data-layout="grid">
 <article class="ad-card">
   <div class="ad-card__body" style="gap: var(--ad-space-3xs); padding: var(--ad-space-xs);">
     <h3 class="ad-card__title" style="font-size: var(--ad-step--1);">Compact</h3>
@@ -49,7 +89,28 @@ summary: >-
     <p class="ad-card__text">space-l padding, space-s gap. For a card that is the whole screen.</p>
   </div>
 </article>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;article class="ad-card"&gt;
+  &lt;div class="ad-card__body" style="gap: var(--ad-space-3xs); padding: var(--ad-space-xs);"&gt;
+    &lt;h3 class="ad-card__title" style="font-size: var(--ad-step--1);"&gt;Compact&lt;/h3&gt;
+    &lt;p class="ad-card__text"&gt;space-xs padding, space-3xs gap. For dense lists.&lt;/p&gt;
+  &lt;/div&gt;
+&lt;/article&gt;
+&lt;article class="ad-card"&gt;
+  &lt;div class="ad-card__body"&gt;
+    &lt;h3 class="ad-card__title" style="font-size: var(--ad-step--1);"&gt;Default&lt;/h3&gt;
+    &lt;p class="ad-card__text"&gt;space-m padding, space-2xs gap. What Card ships with.&lt;/p&gt;
+  &lt;/div&gt;
+&lt;/article&gt;
+&lt;article class="ad-card"&gt;
+  &lt;div class="ad-card__body" style="gap: var(--ad-space-s); padding: var(--ad-space-l);"&gt;
+    &lt;h3 class="ad-card__title" style="font-size: var(--ad-step--1);"&gt;Roomy&lt;/h3&gt;
+    &lt;p class="ad-card__text"&gt;space-l padding, space-s gap. For a card that is the whole screen.&lt;/p&gt;
+  &lt;/div&gt;
+&lt;/article&gt;</code></pre>
+</details>
+</figure>
 
 ## Radius
 
@@ -58,18 +119,38 @@ rather than any one source project's specific softness. Nothing pill-shaped
 except true pills.
 
 <ul class="ad-radius-grid">
-  <% site.data.tokens.scale.radius.scale.each do |step| -%>
-  <% unless step.token == "border-w" -%>
   <li>
-    <div class="ad-radius-demo" style="border-radius: <%= step.value %>;"></div>
+    <div class="ad-radius-demo" style="border-radius: 4px;"></div>
+    <div class="ad-specimen__meta">
+      <span class="ad-token-name">radius-sm</span>
+      <span>4px</span>
+    </div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">Buttons, inputs, badges, tags.</p>
+  </li>
+  <li>
+    <div class="ad-radius-demo" style="border-radius: 6px;"></div>
+    <div class="ad-specimen__meta">
+      <span class="ad-token-name">radius-md</span>
+      <span>6px</span>
+    </div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">IconButton, the segmented-tabs track, avatar chips.</p>
+  </li>
+  <li>
+    <div class="ad-radius-demo" style="border-radius: 10px;"></div>
+    <div class="ad-specimen__meta">
+      <span class="ad-token-name">radius-lg</span>
+      <span>10px</span>
+    </div>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">Cards, dialogs, toasts, photo slots — the system's "surface" radius.</p>
+  </li>
+  <li>
+    <div class="ad-radius-demo" style="border-radius: 999px;"></div>
     <div class="ad-specimen__meta">
-      <span class="ad-token-name"><%= step.token %></span>
-      <span><%= step.value %></span>
+      <span class="ad-token-name">radius-full</span>
+      <span>999px</span>
     </div>
-    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;"><%= step.usage %></p>
+    <p class="ad-muted" style="font-size: var(--ad-step--2); margin: var(--ad-space-3xs) 0 0;">True pills only — Badge, Tag, Switch, round avatars.</p>
   </li>
-  <% end -%>
-  <% end -%>
 </ul>
 
 ## Rules
diff --git a/_foundations/typography.md b/_foundations/typography.md
index 89603eb..046fd87 100644
--- a/_foundations/typography.md
+++ b/_foundations/typography.md
@@ -8,22 +8,38 @@ summary: >-
 
 ## Families
 
-<% site.data.tokens.scale.typography.families.each do |family| -%>
 <div class="ad-specimen">
   <div class="ad-specimen__meta">
-    <span class="ad-token-name"><%= custom_property(family.token) %></span>
-    <span><%= family.usage %></span>
+    <span class="ad-token-name">--ad-font-sans</span>
+    <span>Interface text — every component, every screen. Web-safe; nothing is loaded for it.</span>
   </div>
-  <div class="ad-specimen__sample" style="font-family: var(<%= custom_property(family.token) %>); font-size: 1.5rem;">
+  <div class="ad-specimen__sample" style="font-family: var(--ad-font-sans); font-size: 1.5rem;">
+    Arbitrary Definitions — 0123456789
+  </div>
+</div>
+<div class="ad-specimen">
+  <div class="ad-specimen__meta">
+    <span class="ad-token-name">--ad-font-mono</span>
+    <span>Every label, meta value, technical/catalog value, and code sample. The system's one loaded webfont.</span>
+  </div>
+  <div class="ad-specimen__sample" style="font-family: var(--ad-font-mono); font-size: 1.5rem;">
+    Arbitrary Definitions — 0123456789
+  </div>
+</div>
+<div class="ad-specimen">
+  <div class="ad-specimen__meta">
+    <span class="ad-token-name">--ad-font-serif</span>
+    <span>Opt-in reading variant for a project that wants an editorial, archival feel (domus-style). Not loaded by default; web-safe only.</span>
+  </div>
+  <div class="ad-specimen__sample" style="font-family: var(--ad-font-serif); font-size: 1.5rem;">
     Arbitrary Definitions — 0123456789
   </div>
 </div>
-<% end -%>
 
 Only one webfont is loaded — IBM Plex Mono, at 400/500/600:
 
 ```css
-<%= site.data.tokens.scale.typography.webfont_import %>
+@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&display=swap');
 ```
 
 Sans is a web-safe system stack; serif is Georgia-based and opt-in, for a
@@ -35,18 +51,86 @@ Eight steps, `clamp()`-based (Utopia, 320→1240px viewport). The body sits at
 `--ad-step-0` — 18px on a phone, 20px on a wide screen — with no fixed
 breakpoint in between.
 
-<% site.data.tokens.scale.typography.steps.each do |step| -%>
 <div class="ad-specimen">
   <div class="ad-specimen__meta">
-    <span class="ad-token-name"><%= custom_property(step.token) %></span>
-    <span><%= step.px %>px</span>
-    <span><%= step.usage %></span>
+    <span class="ad-token-name">--ad-step--2</span>
+    <span>12.5 → 12.8px</span>
+    <span>type-label — eyebrows, catalog labels, meta.</span>
+  </div>
+  <div class="ad-specimen__sample" style="font-size: var(--ad-step--2);">
+    The quick brown fox jumps over the lazy dog
+  </div>
+</div>
+<div class="ad-specimen">
+  <div class="ad-specimen__meta">
+    <span class="ad-token-name">--ad-step--1</span>
+    <span>15 → 16px</span>
+    <span>type-body-sm and type-mono — secondary copy, technical values.</span>
   </div>
-  <div class="ad-specimen__sample" style="font-size: var(<%= custom_property(step.token) %>);">
+  <div class="ad-specimen__sample" style="font-size: var(--ad-step--1);">
+    The quick brown fox jumps over the lazy dog
+  </div>
+</div>
+<div class="ad-specimen">
+  <div class="ad-specimen__meta">
+    <span class="ad-token-name">--ad-step-0</span>
+    <span>18 → 20px</span>
+    <span>type-body — the base size, and the body element's own size.</span>
+  </div>
+  <div class="ad-specimen__sample" style="font-size: var(--ad-step-0);">
+    The quick brown fox jumps over the lazy dog
+  </div>
+</div>
+<div class="ad-specimen">
+  <div class="ad-specimen__meta">
+    <span class="ad-token-name">--ad-step-1</span>
+    <span>22 → 25px</span>
+    <span>type-h3 — card and subsection headings.</span>
+  </div>
+  <div class="ad-specimen__sample" style="font-size: var(--ad-step-1);">
+    The quick brown fox jumps over the lazy dog
+  </div>
+</div>
+<div class="ad-specimen">
+  <div class="ad-specimen__meta">
+    <span class="ad-token-name">--ad-step-2</span>
+    <span>26 → 31px</span>
+    <span>type-h2 — section headings.</span>
+  </div>
+  <div class="ad-specimen__sample" style="font-size: var(--ad-step-2);">
+    The quick brown fox jumps over the lazy dog
+  </div>
+</div>
+<div class="ad-specimen">
+  <div class="ad-specimen__meta">
+    <span class="ad-token-name">--ad-step-3</span>
+    <span>31 → 39px</span>
+    <span>type-h1 — screen titles.</span>
+  </div>
+  <div class="ad-specimen__sample" style="font-size: var(--ad-step-3);">
+    The quick brown fox jumps over the lazy dog
+  </div>
+</div>
+<div class="ad-specimen">
+  <div class="ad-specimen__meta">
+    <span class="ad-token-name">--ad-step-4</span>
+    <span>37 → 49px</span>
+    <span>type-display — one hero title per screen, at most.</span>
+  </div>
+  <div class="ad-specimen__sample" style="font-size: var(--ad-step-4);">
+    The quick brown fox jumps over the lazy dog
+  </div>
+</div>
+<div class="ad-specimen">
+  <div class="ad-specimen__meta">
+    <span class="ad-token-name">--ad-step-5</span>
+    <span>45 → 61px</span>
+    <span>Reserved headroom. No compound style claims it yet.</span>
+  </div>
+  <div class="ad-specimen__sample" style="font-size: var(--ad-step-5);">
     The quick brown fox jumps over the lazy dog
   </div>
 </div>
-<% end -%>
 
 ## The eight compound styles
 
@@ -58,21 +142,77 @@ source specifies one) letter-spacing in a single declaration.
   <table class="ad-table ad-table--compact">
     <thead><tr><th scope="col">Class</th><th scope="col">Weight</th><th scope="col">Size</th><th scope="col">Line-height</th><th scope="col">Tracking</th><th scope="col">Use for</th></tr></thead>
     <tbody>
-      <% site.data.tokens.scale.typography.styles.each do |style| -%>
       <tr>
-        <td class="ad-token-name">.<%= style["class"] %></td>
-        <td><%= style.weight %></td>
-        <td class="ad-table__code"><%= custom_property(style.size) %></td>
-        <td><%= style.line_height %></td>
-        <td><%= style.tracking %></td>
-        <td><%= style.usage %></td>
+        <td class="ad-token-name">.type-display</td>
+        <td>600</td>
+        <td class="ad-table__code">--ad-step-4</td>
+        <td>1.08</td>
+        <td>-0.015em</td>
+        <td>Page-level hero title, once per screen.</td>
+      </tr>
+      <tr>
+        <td class="ad-token-name">.type-h1</td>
+        <td>600</td>
+        <td class="ad-table__code">--ad-step-3</td>
+        <td>1.12</td>
+        <td>-0.012em</td>
+        <td>Screen title.</td>
+      </tr>
+      <tr>
+        <td class="ad-token-name">.type-h2</td>
+        <td>600</td>
+        <td class="ad-table__code">--ad-step-2</td>
+        <td>1.16</td>
+        <td>-0.01em</td>
+        <td>Section heading.</td>
+      </tr>
+      <tr>
+        <td class="ad-token-name">.type-h3</td>
+        <td>600</td>
+        <td class="ad-table__code">--ad-step-1</td>
+        <td>1.25</td>
+        <td>normal</td>
+        <td>Card / subsection heading.</td>
+      </tr>
+      <tr>
+        <td class="ad-token-name">.type-body</td>
+        <td>400</td>
+        <td class="ad-table__code">--ad-step-0</td>
+        <td>1.55</td>
+        <td>normal</td>
+        <td>Body copy.</td>
+      </tr>
+      <tr>
+        <td class="ad-token-name">.type-body-sm</td>
+        <td>400</td>
+        <td class="ad-table__code">--ad-step--1</td>
+        <td>1.5</td>
+        <td>normal</td>
+        <td>Secondary copy, hints.</td>
+      </tr>
+      <tr>
+        <td class="ad-token-name">.type-label</td>
+        <td>500</td>
+        <td class="ad-table__code">--ad-step--2</td>
+        <td>1</td>
+        <td>0.08em</td>
+        <td>Eyebrows, catalog labels, meta — SAVED, RECOMMENDED, OVERDUE.</td>
+      </tr>
+      <tr>
+        <td class="ad-token-name">.type-mono</td>
+        <td>400</td>
+        <td class="ad-table__code">--ad-step--1</td>
+        <td>1.5</td>
+        <td>normal</td>
+        <td>Technical values, IDs, dates, code.</td>
       </tr>
-      <% end -%>
     </tbody>
   </table>
 </div>
 
-<% example(title: "All eight, in order", layout: "stack") do %>
+<figure class="example">
+<figcaption>All eight, in order</figcaption>
+<div data-layout="stack">
 <div class="type-display">Display</div>
 <h1 class="type-h1">Heading one</h1>
 <h2 class="type-h2">Heading two</h2>
@@ -81,7 +221,18 @@ source specifies one) letter-spacing in a single declaration.
 <p class="type-body-sm">Secondary copy, one step down.</p>
 <span class="type-label">Catalog label</span>
 <span class="type-mono">technical.value / 2026-08-21 / #3a6a63</span>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;div class="type-display"&gt;Display&lt;/div&gt;
+&lt;h1 class="type-h1"&gt;Heading one&lt;/h1&gt;
+&lt;h2 class="type-h2"&gt;Heading two&lt;/h2&gt;
+&lt;h3 class="type-h3"&gt;Heading three&lt;/h3&gt;
+&lt;p class="type-body"&gt;Body copy at the base size, 1.55 line-height.&lt;/p&gt;
+&lt;p class="type-body-sm"&gt;Secondary copy, one step down.&lt;/p&gt;
+&lt;span class="type-label"&gt;Catalog label&lt;/span&gt;
+&lt;span class="type-mono"&gt;technical.value / 2026-08-21 / #3a6a63&lt;/span&gt;</code></pre>
+</details>
+</figure>
 
 ## Rules
 
diff --git a/_includes/theme-toggle.erb b/_includes/theme-toggle.erb
index 12b408c..9a51442 100644
--- a/_includes/theme-toggle.erb
+++ b/_includes/theme-toggle.erb
@@ -1,5 +1,5 @@
 <button type="button" class="ad-btn ad-btn--ghost ad-btn--sm" data-theme-toggle
-        aria-label="Switch colour theme" title="Switch colour theme">
+        aria-label="Switch color theme" title="Switch color theme">
   <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true" data-theme-icon="light">
     <circle cx="12" cy="12" r="4.5" stroke="currentColor" stroke-width="2"/>
     <path d="M12 1.8v2.4M12 19.8v2.4M4.6 4.6l1.7 1.7M17.7 17.7l1.7 1.7M1.8 12h2.4M19.8 12h2.4M4.6 19.4l1.7-1.7M17.7 6.3l1.7-1.7"
diff --git a/_layouts/default.erb b/_layouts/default.erb
index 214775c..c012298 100644
--- a/_layouts/default.erb
+++ b/_layouts/default.erb
@@ -28,7 +28,7 @@
     <div class="ad-doc-footer__inner">
       <p>
         <%= site.title %> v<%= site.data.meta.version %> — built with Ruby
-        <%= site.data.build.ruby %>.
+        <%= RUBY_VERSION %>.
       </p>
       <p><a href="https://github.com/<%= site.repository %>">View the source</a></p>
     </div>
diff --git a/_patterns/archive.md b/_patterns/archive.md
index 58047a7..fab3759 100644
--- a/_patterns/archive.md
+++ b/_patterns/archive.md
@@ -11,7 +11,9 @@ mobile capture flow: the shape `kejadlen/domus` (a household inventory and
 document archive) would take if it were rebuilt on this token set, at its
 390×760 frame. Three screens, abbreviated to what each one adds.
 
-<% example(title: "Home — recent captures", layout: "row", no_source: true) do %>
+<figure class="example">
+<figcaption>Home — recent captures</figcaption>
+<div>
 <div style="width: 300px; height: 560px; background: var(--ad-color-surface-page); border-radius: var(--ad-radius-lg); box-shadow: var(--ad-shadow-float); overflow:hidden; display:flex; flex-direction:column; position:relative;">
   <header style="padding: 16px var(--ad-space-m) var(--ad-space-2xs);">
     <span style="font-weight:600; font-size: var(--ad-step-1); letter-spacing:-0.01em;">Archive</span>
@@ -23,7 +25,7 @@ document archive) would take if it were rebuilt on this token set, at its
     </div>
     <div class="ad-card">
       <div class="ad-card-row">
-        <span class="ad-avatar ad-avatar--square"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z"/><path d="M3 8l9 5 9-5M12 13v8"/></svg></span>
+        <span class="ad-avatar ad-avatar--square"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg></span>
         <div style="flex:1; min-width:0;">
           <div style="font-weight:550; font-size: var(--ad-step--1); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">Dishwasher manual</div>
           <div class="type-label" style="margin-top:2px;">PDF · 2.1MB</div>
@@ -31,7 +33,7 @@ document archive) would take if it were rebuilt on this token set, at its
         <span class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">2h</span>
       </div>
       <div class="ad-card-row">
-        <span class="ad-avatar ad-avatar--square"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z"/><path d="M3 8l9 5 9-5M12 13v8"/></svg></span>
+        <span class="ad-avatar ad-avatar--square"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg></span>
         <div style="flex:1; min-width:0;">
           <div style="font-weight:550; font-size: var(--ad-step--1);">Sofa receipt</div>
           <div class="type-label" style="margin-top:2px;">JPG · 640KB</div>
@@ -39,7 +41,7 @@ document archive) would take if it were rebuilt on this token set, at its
         <span class="type-mono" style="color: var(--ad-color-text-tertiary); font-size: var(--ad-step--2);">1d</span>
       </div>
       <div class="ad-card-row">
-        <span class="ad-avatar ad-avatar--square"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z"/><path d="M3 8l9 5 9-5M12 13v8"/></svg></span>
+        <span class="ad-avatar ad-avatar--square"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg></span>
         <div style="flex:1; min-width:0;">
           <div style="font-weight:550; font-size: var(--ad-step--1);">Passport scan</div>
           <div class="type-label" style="margin-top:2px;">PDF · 890KB</div>
@@ -50,25 +52,24 @@ document archive) would take if it were rebuilt on this token set, at its
   </div>
   <div style="position:absolute; left:0; right:0; bottom:0; display:flex; justify-content:center; padding: 14px 20px 22px; background: linear-gradient(to top, var(--ad-color-surface-page) 65%, transparent);">
     <button type="button" class="ad-btn ad-btn--primary ad-btn--lg ad-btn--block">
-      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8a2 2 0 0 1 2-2h1.5l1-1.5h9l1 1.5H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8Z"/><circle cx="12" cy="13" r="3.5"/></svg>
+      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8a2 2 0 0 1 2-2h1.5l1-1.5h9l1 1.5H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8Z" /><circle cx="12" cy="13" r="3.5" /></svg>
       Take a photo
     </button>
   </div>
 </div>
-
 <div style="width: 300px; height: 560px; background: var(--ad-color-surface-page); border-radius: var(--ad-radius-lg); box-shadow: var(--ad-shadow-float); overflow:hidden; display:flex; flex-direction:column;">
   <header style="display:flex; align-items:center; gap:8px; padding: 16px var(--ad-space-m) var(--ad-space-2xs);">
-    <button type="button" class="ad-icon-btn ad-icon-btn--sm" aria-label="Back"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg></button>
+    <button type="button" class="ad-icon-btn ad-icon-btn--sm" aria-label="Back"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6" /></svg></button>
     <span style="font-weight:600; font-size: var(--ad-step-0);">Save</span>
   </header>
   <div style="flex:1; padding: 0 var(--ad-space-m);">
     <div style="height: 180px; border-radius: var(--ad-radius-lg); border: 1px solid var(--ad-color-border-default); background: repeating-linear-gradient(45deg, transparent 0 8px, var(--ad-color-surface-fill-2) 8px 9px), var(--ad-color-surface-fill); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px; margin-top: var(--ad-space-s);">
-      <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z"/><path d="M3 8l9 5 9-5M12 13v8"/></svg>
+      <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg>
       <span class="type-label">Photo preview</span>
     </div>
     <div style="margin-top: var(--ad-space-m); display:flex; flex-direction:column; gap: var(--ad-space-2xs);">
       <span class="type-label">Name</span>
-      <input class="ad-input" placeholder="Dishwasher manual">
+      <input class="ad-input" placeholder="Dishwasher manual" />
     </div>
   </div>
   <div style="padding: var(--ad-space-m); display:flex; gap: var(--ad-space-2xs);">
@@ -76,10 +77,9 @@ document archive) would take if it were rebuilt on this token set, at its
     <button type="button" class="ad-btn ad-btn--primary" style="flex:1;">Save</button>
   </div>
 </div>
-
 <div style="width: 300px; height: 560px; background: var(--ad-color-surface-page); border-radius: var(--ad-radius-lg); box-shadow: var(--ad-shadow-float); overflow:hidden; display:flex; flex-direction:column;">
   <header style="display:flex; align-items:center; gap:8px; padding: 16px var(--ad-space-m) var(--ad-space-2xs);">
-    <button type="button" class="ad-icon-btn ad-icon-btn--sm" aria-label="Back"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg></button>
+    <button type="button" class="ad-icon-btn ad-icon-btn--sm" aria-label="Back"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6" /></svg></button>
     <span class="type-label">Archive</span>
   </header>
   <div style="flex:1; overflow:hidden; padding: 0 var(--ad-space-m) var(--ad-space-l);">
@@ -91,7 +91,7 @@ document archive) would take if it were rebuilt on this token set, at its
     <div class="type-label" style="margin-bottom: var(--ad-space-2xs);">File</div>
     <div class="ad-card">
       <div class="ad-card-row">
-        <span class="ad-avatar ad-avatar--square"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z"/><path d="M3 8l9 5 9-5M12 13v8"/></svg></span>
+        <span class="ad-avatar ad-avatar--square"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5V8Z" /><path d="M3 8l9 5 9-5M12 13v8" /></svg></span>
         <div style="flex:1; font-size: var(--ad-step--1);">PDF · 2.1MB</div>
       </div>
     </div>
@@ -99,7 +99,8 @@ document archive) would take if it were rebuilt on this token set, at its
     <p style="font-size: var(--ad-step--1); color: var(--ad-color-text-secondary); line-height:1.55; margin:0;">No notes yet.</p>
   </div>
 </div>
-<% end %>
+</div>
+</figure>
 
 ## What this screen is testing
 
diff --git a/_patterns/loop.md b/_patterns/loop.md
index 842caff..f133428 100644
--- a/_patterns/loop.md
+++ b/_patterns/loop.md
@@ -12,22 +12,21 @@ tracker) would take if it were rebuilt on this token set. Every visible value
 below is a component from this system; nothing here is page-specific CSS
 beyond a couple of inline widths.
 
-<% example(title: "Loop", layout: "stack", no_source: true) do %>
+<figure class="example">
+<figcaption>Loop</figcaption>
+<div data-layout="stack">
 <div style="border: 1px solid var(--ad-color-border-hairline); border-radius: var(--ad-radius-lg); overflow: hidden; background: var(--ad-color-surface-page);">
-
   <header style="display:flex; align-items:center; gap: var(--ad-space-s); padding: var(--ad-space-s) var(--ad-space-l); border-bottom: 1px solid var(--ad-color-border-hairline);">
     <span style="font-family: var(--ad-font-mono); font-weight:600; letter-spacing:0.02em; font-size: var(--ad-step-0);">Loop</span>
     <div style="flex:1;"></div>
     <button type="button" class="ad-btn ad-btn--primary ad-btn--sm">
-      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
+      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
       New
     </button>
     <span class="ad-avatar">km</span>
   </header>
-
   <div style="display:flex;">
     <div style="flex:1; min-width:0; max-width: 34rem; padding: var(--ad-space-l);">
-
       <section style="margin-bottom: var(--ad-space-l);">
         <div style="display:flex; align-items:baseline; gap: var(--ad-space-2xs); margin-bottom: var(--ad-space-s);">
           <span class="type-label" style="color: var(--ad-color-danger);">Overdue</span>
@@ -50,7 +49,6 @@ beyond a couple of inline widths.
           <span class="type-mono" style="color: var(--ad-color-danger); font-size: var(--ad-step--2); white-space:nowrap;">1d overdue</span>
         </div>
       </section>
-
       <section>
         <div class="type-label" style="margin-bottom: var(--ad-space-s);">Upcoming</div>
         <div class="ad-card-row">
@@ -79,19 +77,38 @@ beyond a couple of inline widths.
         </div>
       </section>
     </div>
-
     <aside style="width: 15rem; flex-shrink:0; background: var(--ad-color-surface-fill); border-left: 1px solid var(--ad-color-border-hairline); padding: var(--ad-space-l);">
       <div class="type-label" style="margin-bottom: var(--ad-space-s);">This week</div>
       <div style="display:flex; background: var(--ad-color-surface-card); border: 1px solid var(--ad-color-border-hairline); border-radius: var(--ad-radius-md); overflow:hidden; margin-bottom: var(--ad-space-l);">
-        <% week = %w[S M T W T F S] -%>
-        <% nums = [0, 1, 0, 2, 0, 1, 0] -%>
-        <% week.each_with_index do |d, index0| -%>
-        <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0; <% if index0 == 3 %>background: var(--ad-color-accent-soft);<% end %> <% unless index0 == week.length - 1 %>border-right: 1px solid var(--ad-color-border-hairline);<% end %>">
-          <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;"><%= d %></span>
-          <span style="font-size:13px; font-weight:600; color: <% if index0 == 3 %>var(--ad-color-accent-ink)<% else %>var(--ad-color-text-primary)<% end %>;"><%= nums[index0] %></span>
+                                <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0;  border-right: 1px solid var(--ad-color-border-hairline);">
+          <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">S</span>
+          <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">0</span>
         </div>
-        <% end -%>
-      </div>
+                <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0;  border-right: 1px solid var(--ad-color-border-hairline);">
+          <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">M</span>
+          <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">1</span>
+        </div>
+                <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0;  border-right: 1px solid var(--ad-color-border-hairline);">
+          <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">T</span>
+          <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">0</span>
+        </div>
+                <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0; background: var(--ad-color-accent-soft); border-right: 1px solid var(--ad-color-border-hairline);">
+          <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">W</span>
+          <span style="font-size:13px; font-weight:600; color: var(--ad-color-accent-ink);">2</span>
+        </div>
+                <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0;  border-right: 1px solid var(--ad-color-border-hairline);">
+          <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">T</span>
+          <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">0</span>
+        </div>
+                <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0;  border-right: 1px solid var(--ad-color-border-hairline);">
+          <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">F</span>
+          <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">1</span>
+        </div>
+                <div style="flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; padding: 8px 0;  ">
+          <span class="type-mono" style="font-size:9px; color: var(--ad-color-text-tertiary); text-transform:uppercase;">S</span>
+          <span style="font-size:13px; font-weight:600; color: var(--ad-color-text-primary);">0</span>
+        </div>
+              </div>
       <div class="type-label" style="margin-bottom: var(--ad-space-2xs);">Series</div>
       <div>
         <div style="padding: var(--ad-space-2xs) 0; font-size: var(--ad-step--1); color: var(--ad-color-text-secondary);">Call Mom</div>
@@ -101,11 +118,14 @@ beyond a couple of inline widths.
     </aside>
   </div>
 </div>
-<% end %>
+</div>
+</figure>
 
 ## The New series dialog
 
-<% example(title: "New series", no_source: true, layout: "stack") do %>
+<figure class="example">
+<figcaption>New series</figcaption>
+<div data-layout="stack">
 <div class="ad-dialog-demo" style="min-height: 18rem;">
   <div class="ad-dialog__overlay">
     <div class="ad-dialog" style="width: 22rem;">
@@ -114,17 +134,17 @@ beyond a couple of inline widths.
         <div style="display:flex; flex-direction:column; gap: var(--ad-space-s);">
           <div class="ad-field">
             <label class="ad-field__label" for="loop-note">Note</label>
-            <input class="ad-input" id="loop-note" placeholder="Call Mom">
+            <input class="ad-input" id="loop-note" placeholder="Call Mom" />
             <span class="ad-field__hint">First line becomes the display name.</span>
           </div>
           <div class="ad-form__row">
             <div class="ad-field">
               <label class="ad-field__label" for="loop-every">Every</label>
-              <input class="ad-input" id="loop-every" type="number" placeholder="2">
+              <input class="ad-input" id="loop-every" type="number" placeholder="2" />
             </div>
             <div class="ad-field">
               <label class="ad-field__label" for="loop-unit">Unit</label>
-              <select class="ad-select" id="loop-unit"><option>Day</option><option selected>Week</option><option>Month</option><option>Year</option></select>
+              <select class="ad-select" id="loop-unit"><option>Day</option><option selected="">Week</option><option>Month</option><option>Year</option></select>
             </div>
           </div>
         </div>
@@ -136,16 +156,20 @@ beyond a couple of inline widths.
     </div>
   </div>
 </div>
-<% end %>
+</div>
+</figure>
 
 ## After completing a task
 
-<% example(title: "Toast", no_source: true) do %>
+<figure class="example">
+<figcaption>Toast</figcaption>
+<div>
 <div class="ad-toast">
   <span class="ad-toast__message">Completed "Call Mom"</span>
   <button type="button" class="ad-toast__dismiss" aria-label="Dismiss">×</button>
 </div>
-<% end %>
+</div>
+</figure>
 
 ## What this screen is testing
 
@@ -153,7 +177,7 @@ beyond a couple of inline widths.
   fundamentally tabular even though it never touches an actual `<table>`.
 - **Overdue in `--ad-color-danger`, everywhere at once.** Row text, the
   complete-button border, and the trailing meta all pick it up from the same
-  `overdue` state — colour never drifts row to row.
+  `overdue` state — color never drifts row to row.
 - **One primary action.** `New` is the only accent-filled control on screen;
   everything else is neutral or bordered.
 - **Accent used exactly once, decoratively.** The "km" avatar and the
diff --git a/_sass/base/_reset.scss b/_sass/base/reset.css
similarity index 75%
rename from _sass/base/_reset.scss
rename to _sass/base/reset.css
index 7d4be35..e336233 100644
--- a/_sass/base/_reset.scss
+++ b/_sass/base/reset.css
@@ -1,17 +1,11 @@
 /* A short reset. Everything it sets is something the system would otherwise
    have to override in every component.
 
-   The four rem constants below (measure/content-width/sidebar-width/header-
-   height) belong to *this documentation site's chrome*, not to the design
-   system it documents — the source design system defines no container-width
-   tokens at all, so there is nothing under `--ad-*` to reach for here. See
-   the header comment in site/_shell.scss. */
-
-$doc-measure: 68ch;
-$doc-content-width: 50rem;
-$doc-wide-width: 72rem;
-$doc-sidebar-width: 16rem;
-$doc-header-height: 3.5rem;
+   The rem values used for this documentation site's own chrome (measure,
+   content width, sidebar width, header height) belong to this site, not to
+   the design system it documents — the source design system defines no
+   container-width tokens at all, so there is nothing under `--ad-*` to reach
+   for here. See the header comment in site/shell.css. */
 
 *,
 *::before,
@@ -20,7 +14,7 @@ $doc-header-height: 3.5rem;
 html {
   -webkit-text-size-adjust: 100%;
   scroll-behavior: smooth;
-  scroll-padding-top: calc(#{$doc-header-height} + var(--ad-space-s));
+  scroll-padding-top: calc(3.5rem + var(--ad-space-s));
 }
 
 @media (prefers-reduced-motion: reduce) {
diff --git a/_sass/base/_typography.scss b/_sass/base/typography.css
similarity index 84%
rename from _sass/base/_typography.scss
rename to _sass/base/typography.css
index 7fb5b3f..e64eac8 100644
--- a/_sass/base/_typography.scss
+++ b/_sass/base/typography.css
@@ -1,13 +1,13 @@
 /* Every rule here reads a type token for family and size. Weight, line-height,
    and letter-spacing are hardcoded to the exact numbers the source ships —
    there is no `--ad-weight-*` / `--ad-leading-*` / `--ad-tracking-*` scale in
-   the real system, only eight fixed compound styles (see scale.yml's
-   `typography.styles`, which this file implements verbatim). */
+   the real system, only eight fixed compound styles (see the Typography
+   foundation page, which this file implements verbatim). */
 
 .type-display { font: 600 var(--ad-step-4)/1.08 var(--ad-font-sans); letter-spacing: -0.015em; }
-.type-h1      { font: 600 var(--ad-step-3)/1.12 var(--ad-font-sans); letter-spacing: -0.012em; }
-.type-h2      { font: 600 var(--ad-step-2)/1.16 var(--ad-font-sans); letter-spacing: -0.01em; }
-.type-h3      { font: 600 var(--ad-step-1)/1.25 var(--ad-font-sans); }
+.type-h1, h1 { font: 600 var(--ad-step-3)/1.12 var(--ad-font-sans); letter-spacing: -0.012em; }
+.type-h2, h2 { font: 600 var(--ad-step-2)/1.16 var(--ad-font-sans); letter-spacing: -0.01em; }
+.type-h3, h3 { font: 600 var(--ad-step-1)/1.25 var(--ad-font-sans); }
 .type-body    { font: 400 var(--ad-step-0)/1.55 var(--ad-font-sans); }
 .type-body-sm { font: 400 var(--ad-step--1)/1.5 var(--ad-font-sans); }
 .type-label {
@@ -22,9 +22,6 @@
    tokens the compound classes above do; they are not a second scale. */
 h1, h2, h3, h4 { font-weight: 600; text-wrap: balance; }
 
-h1 { @extend .type-h1; }
-h2 { @extend .type-h2; }
-h3 { @extend .type-h3; }
 h4 { font: 600 var(--ad-step-0)/1.3 var(--ad-font-sans); }
 
 p, ul, ol { margin: 0 0 var(--ad-space-s); }
diff --git a/_sass/components/_badge.scss b/_sass/components/badge.css
similarity index 100%
rename from _sass/components/_badge.scss
rename to _sass/components/badge.css
diff --git a/_sass/components/_button.scss b/_sass/components/button.css
similarity index 100%
rename from _sass/components/_button.scss
rename to _sass/components/button.css
diff --git a/_sass/components/_card.scss b/_sass/components/card.css
similarity index 100%
rename from _sass/components/_card.scss
rename to _sass/components/card.css
diff --git a/_sass/components/_feedback.scss b/_sass/components/feedback.css
similarity index 98%
rename from _sass/components/_feedback.scss
rename to _sass/components/feedback.css
index 6d0793b..496ca62 100644
--- a/_sass/components/_feedback.scss
+++ b/_sass/components/feedback.css
@@ -74,7 +74,7 @@
 }
 .ad-toast__dismiss:hover { color: var(--ad-color-text-primary); }
 
-/* Dialog — a centered modal: flat surface, no coloured header, hairline
+/* Dialog — a centered modal: flat surface, no colored header, hairline
    dividers between title/body/footer. `.ad-dialog-demo` scopes the overlay
    to its own preview container instead of the viewport, so a static,
    always-open example can sit safely inside a documentation page; wire real
diff --git a/_sass/components/_field.scss b/_sass/components/field.css
similarity index 98%
rename from _sass/components/_field.scss
rename to _sass/components/field.css
index debfc82..26fba87 100644
--- a/_sass/components/_field.scss
+++ b/_sass/components/field.css
@@ -52,11 +52,11 @@
   cursor: default;
 }
 
-/* The chevron is the one flagged colour exception in this component set:
+/* The chevron is the one flagged color exception in this component set:
    CSS cannot point a data-URI background-image at a custom property, so its
    stroke is a literal hex rather than a var(--ad-*) reference. It is picked
    to match --ad-color-text-secondary and commented here so it is not
-   mistaken for a stray colour. */
+   mistaken for a stray color. */
 .ad-select {
   appearance: none;
   padding-inline-end: var(--ad-space-l);
diff --git a/_sass/components/_tabs.scss b/_sass/components/tabs.css
similarity index 100%
rename from _sass/components/_tabs.scss
rename to _sass/components/tabs.css
diff --git a/_sass/site/_example.scss b/_sass/site/_example.scss
deleted file mode 100644
index 34d6a20..0000000
--- a/_sass/site/_example.scss
+++ /dev/null
@@ -1,137 +0,0 @@
-/* The live-example figure emitted by _plugins/example_tag.rb. */
-
-.ad-example {
-  margin: var(--ad-space-s) 0 var(--ad-space-l);
-  border: 1px solid var(--ad-color-border-default);
-  border-radius: var(--ad-radius-lg);
-  background: var(--ad-color-surface-card);
-  overflow: hidden;
-}
-
-.ad-example__title {
-  padding: var(--ad-space-2xs) var(--ad-space-xs);
-  border-bottom: 1px solid var(--ad-color-border-hairline);
-  background: var(--ad-color-surface-fill);
-  font-family: var(--ad-font-mono);
-  font-size: 11px;
-  font-weight: 600;
-  letter-spacing: 0.06em;
-  text-transform: uppercase;
-  color: var(--ad-color-text-secondary);
-}
-
-.ad-example__preview {
-  display: flex;
-  flex-wrap: wrap;
-  align-items: center;
-  gap: var(--ad-space-xs);
-  padding: var(--ad-space-s);
-}
-.ad-example__preview--center { justify-content: center; }
-.ad-example__preview--end { justify-content: flex-end; }
-/* nowrap is load-bearing: a column-direction flex container with wrap enabled
-   sizes its single line against the container rather than the content, which
-   left every stacked example with a few hundred pixels of dead space. */
-.ad-example__preview--stack { flex-direction: column; flex-wrap: nowrap; align-items: stretch; }
-.ad-example__preview--stack.ad-example__preview--center { align-items: center; }
-.ad-example__preview--grid {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
-  align-items: start;
-}
-.ad-example__preview--sunken { background: var(--ad-color-surface-fill); }
-.ad-example__preview--plain { padding: 0; }
-.ad-example__preview > * { min-width: 0; }
-
-.ad-example__source {
-  border-top: 1px solid var(--ad-color-border-hairline);
-  background: var(--ad-color-surface-fill);
-}
-.ad-example__summary {
-  padding: var(--ad-space-2xs) var(--ad-space-xs);
-  font-size: var(--ad-step--2);
-  font-weight: 500;
-  color: var(--ad-color-text-secondary);
-  cursor: pointer;
-  list-style: none;
-  user-select: none;
-  display: flex;
-  align-items: center;
-  gap: var(--ad-space-2xs);
-}
-.ad-example__summary::-webkit-details-marker { display: none; }
-.ad-example__summary::before {
-  content: "";
-  width: 0.5rem;
-  height: 0.5rem;
-  border-inline-end: 1.5px solid currentColor;
-  border-block-end: 1.5px solid currentColor;
-  rotate: -45deg;
-  transition: rotate var(--ad-dur-fast) var(--ad-ease);
-}
-.ad-example__source[open] > .ad-example__summary::before { rotate: 45deg; }
-.ad-example__summary:hover { color: var(--ad-color-text-primary); }
-
-.ad-example__code {
-  position: relative;
-  border-top: 1px solid var(--ad-color-border-hairline);
-}
-.ad-example__copy {
-  position: absolute;
-  inset-block-start: var(--ad-space-2xs);
-  inset-inline-end: var(--ad-space-2xs);
-  padding: 0.125rem var(--ad-space-2xs);
-  background: var(--ad-color-surface-card);
-  border: 1px solid var(--ad-color-border-default);
-  border-radius: var(--ad-radius-sm);
-  color: var(--ad-color-text-secondary);
-  font-family: var(--ad-font-mono);
-  font-size: 11px;
-  cursor: pointer;
-  opacity: 0;
-  transition: opacity var(--ad-dur-fast) var(--ad-ease);
-}
-.ad-example__code:hover .ad-example__copy,
-.ad-example__copy:focus-visible { opacity: 1; }
-.ad-example__copy[data-copied="true"] { color: var(--ad-color-success); border-color: var(--ad-color-success); }
-
-.ad-example pre {
-  margin: 0;
-  padding: var(--ad-space-xs);
-  overflow-x: auto;
-  font-size: var(--ad-step--2);
-  line-height: 1.5;
-  tab-size: 2;
-}
-.ad-example pre code { background: none; border: 0; padding: 0; white-space: pre; }
-
-/* Standalone fenced code blocks in the prose. */
-.ad-doc-main > .highlighter-rouge,
-.ad-doc-main > .highlight {
-  margin-block: var(--ad-space-xs);
-  border: 1px solid var(--ad-color-border-default);
-  border-radius: var(--ad-radius-md);
-  background: var(--ad-color-surface-fill);
-  overflow: hidden;
-}
-.ad-doc-main > .highlighter-rouge pre {
-  margin: 0;
-  padding: var(--ad-space-xs);
-  overflow-x: auto;
-  font-size: var(--ad-step--2);
-  line-height: 1.5;
-}
-.ad-doc-main > .highlighter-rouge pre code { background: none; border: 0; padding: 0; white-space: pre; }
-
-/* Rouge tokens, mapped onto system colours so highlighting themes with the
-   page. The source's palette has no caution/info hue, so both collapse onto
-   the nearest thing this system does have. */
-.highlight { color: var(--ad-color-text-primary); }
-.highlight .c, .highlight .c1, .highlight .cm, .highlight .cd { color: var(--ad-color-text-tertiary); font-style: italic; }
-.highlight .nt { color: var(--ad-color-accent); }
-.highlight .na { color: var(--ad-color-accent-ink); }
-.highlight .s, .highlight .s1, .highlight .s2, .highlight .dl { color: var(--ad-color-success); }
-.highlight .k, .highlight .kd, .highlight .kn, .highlight .kr { color: var(--ad-color-danger); }
-.highlight .p, .highlight .o { color: var(--ad-color-text-secondary); }
-.highlight .m, .highlight .mi, .highlight .mf { color: var(--ad-color-accent-ink); }
-.highlight .nc, .highlight .nf, .highlight .nn { color: var(--ad-color-accent-ink); }
diff --git a/_sass/site/example.css b/_sass/site/example.css
new file mode 100644
index 0000000..1d40754
--- /dev/null
+++ b/_sass/site/example.css
@@ -0,0 +1,100 @@
+/* The live-example figure used throughout the docs — see the "How the
+   example demos work" section on the Using the CSS page. One class on the
+   outer <figure>; everything inside is styled by plain element selectors,
+   since figcaption/div/details/summary/pre already say what they are.
+   `data-layout` and `data-surface` on the preview <div> distinguish the
+   handful of demos that need a non-default arrangement. */
+
+.example {
+  margin: var(--ad-space-s) 0 var(--ad-space-l);
+  border: 1px solid var(--ad-color-border-default);
+  border-radius: var(--ad-radius-lg);
+  background: var(--ad-color-surface-card);
+  overflow: hidden;
+}
+
+.example figcaption {
+  padding: var(--ad-space-2xs) var(--ad-space-xs);
+  border-bottom: 1px solid var(--ad-color-border-hairline);
+  background: var(--ad-color-surface-fill);
+  font-family: var(--ad-font-mono);
+  font-size: 11px;
+  font-weight: 600;
+  letter-spacing: 0.06em;
+  text-transform: uppercase;
+  color: var(--ad-color-text-secondary);
+}
+
+.example > div {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: var(--ad-space-xs);
+  padding: var(--ad-space-s);
+}
+/* nowrap is load-bearing: a column-direction flex container with wrap enabled
+   sizes its single line against the container rather than the content, which
+   left every stacked example with a few hundred pixels of dead space. */
+.example > div[data-layout="stack"] { flex-direction: column; flex-wrap: nowrap; align-items: stretch; }
+.example > div[data-layout="grid"] {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
+  align-items: start;
+}
+.example > div[data-surface="sunken"] { background: var(--ad-color-surface-fill); }
+.example > div > * { min-width: 0; }
+
+.example details {
+  border-top: 1px solid var(--ad-color-border-hairline);
+  background: var(--ad-color-surface-fill);
+}
+.example summary {
+  padding: var(--ad-space-2xs) var(--ad-space-xs);
+  font-size: var(--ad-step--2);
+  font-weight: 500;
+  color: var(--ad-color-text-secondary);
+  cursor: pointer;
+  list-style: none;
+  user-select: none;
+  display: flex;
+  align-items: center;
+  gap: var(--ad-space-2xs);
+}
+.example summary::-webkit-details-marker { display: none; }
+.example summary::before {
+  content: "";
+  width: 0.5rem;
+  height: 0.5rem;
+  border-inline-end: 1.5px solid currentColor;
+  border-block-end: 1.5px solid currentColor;
+  rotate: -45deg;
+  transition: rotate var(--ad-dur-fast) var(--ad-ease);
+}
+.example details[open] > summary::before { rotate: 45deg; }
+.example summary:hover { color: var(--ad-color-text-primary); }
+
+.example pre {
+  margin: 0;
+  padding: var(--ad-space-xs);
+  border-top: 1px solid var(--ad-color-border-hairline);
+  overflow-x: auto;
+  font-size: var(--ad-step--2);
+  line-height: 1.5;
+  tab-size: 2;
+}
+.example pre code { background: none; border: 0; padding: 0; white-space: pre; }
+
+/* Standalone fenced code blocks in the prose (unhighlighted — there is no
+   syntax highlighter in this build any more, just Kramdown's plain
+   <pre><code class="language-*">). */
+.ad-doc-main > pre {
+  margin-block: var(--ad-space-xs);
+  padding: var(--ad-space-xs);
+  border: 1px solid var(--ad-color-border-default);
+  border-radius: var(--ad-radius-md);
+  background: var(--ad-color-surface-fill);
+  overflow-x: auto;
+  font-size: var(--ad-step--2);
+  line-height: 1.5;
+}
+.ad-doc-main > pre code { background: none; border: 0; padding: 0; white-space: pre; }
diff --git a/_sass/site/_shell.scss b/_sass/site/shell.css
similarity index 83%
rename from _sass/site/_shell.scss
rename to _sass/site/shell.css
index c26051b..7b6b34b 100644
--- a/_sass/site/_shell.scss
+++ b/_sass/site/shell.css
@@ -1,11 +1,9 @@
 /* Documentation site chrome. Everything below the `.ad-doc-` prefix belongs
    to this site rather than to the design system it documents — including the
-   four width/height constants it reads from base/_reset.scss. The source
-   design system defines no container-width tokens at all, so there is
-   nothing under `--ad-*` for a doc shell to reach for; scale.yml's own
-   comment says to omit that group entirely rather than invent one. */
-
-@use "../base/reset" as reset;
+   width/height constants used directly below. The source design system
+   defines no container-width tokens at all, so there is nothing under
+   `--ad-*` for a doc shell to reach for; the Space foundation page's own
+   notes say to omit that group entirely rather than invent one. */
 
 .ad-doc-skip {
   position: absolute;
@@ -25,7 +23,7 @@
   position: sticky;
   inset-block-start: 0;
   z-index: 30;
-  height: reset.$doc-header-height;
+  height: 3.5rem;
   background: color-mix(in srgb, var(--ad-color-surface-page) 92%, transparent);
   border-bottom: 1px solid var(--ad-color-border-hairline);
 }
@@ -34,7 +32,7 @@
   align-items: center;
   gap: var(--ad-space-xs);
   height: 100%;
-  max-width: reset.$doc-wide-width;
+  max-width: 72rem;
   margin-inline: auto;
   padding-inline: var(--ad-space-xs);
 }
@@ -70,18 +68,18 @@
 
 .ad-doc-layout {
   display: grid;
-  grid-template-columns: reset.$doc-sidebar-width minmax(0, 1fr);
+  grid-template-columns: 16rem minmax(0, 1fr);
   gap: var(--ad-space-l);
-  max-width: reset.$doc-wide-width;
+  max-width: 72rem;
   margin-inline: auto;
   padding: var(--ad-space-m) var(--ad-space-xs) var(--ad-space-2xl);
 }
 
 .ad-doc-sidebar {
   position: sticky;
-  inset-block-start: calc(#{reset.$doc-header-height} + var(--ad-space-m));
+  inset-block-start: calc(3.5rem + var(--ad-space-m));
   align-self: start;
-  max-height: calc(100vh - #{reset.$doc-header-height} - var(--ad-space-l));
+  max-height: calc(100vh - 3.5rem - var(--ad-space-l));
   overflow-y: auto;
   padding-inline-end: var(--ad-space-2xs);
 }
@@ -115,8 +113,16 @@
   font-weight: 500;
 }
 
-.ad-doc-main { min-width: 0; }
-.ad-doc-main > * { max-width: reset.$doc-content-width; }
+.ad-doc-main {
+  min-width: 0;
+  /* The design system's own body-text token (--ad-step-0, 18–20px) is right
+     for a product screen but too large for long-form documentation prose.
+     This site's own copy gets a plain, fixed 16px instead — the token above
+     is unchanged, and specimen tables/demos below still show its real
+     value. */
+  font-size: 1rem;
+}
+.ad-doc-main > * { max-width: 50rem; }
 
 .ad-doc-title { margin-bottom: var(--ad-space-xs); }
 .ad-doc-title__section {
@@ -139,12 +145,12 @@
 .ad-doc-main h4 { margin-block: var(--ad-space-s) var(--ad-space-2xs); }
 .ad-doc-main p,
 .ad-doc-main ul,
-.ad-doc-main ol { max-width: reset.$doc-measure; }
+.ad-doc-main ol { max-width: 68ch; }
 .ad-doc-main ul:not([class]),
 .ad-doc-main ol:not([class]) { padding-inline-start: var(--ad-space-s); }
 
 .ad-doc-footer {
-  max-width: reset.$doc-wide-width;
+  max-width: 72rem;
   margin-inline: auto;
   padding: var(--ad-space-s) var(--ad-space-xs) var(--ad-space-l);
   border-top: 1px solid var(--ad-color-border-hairline);
diff --git a/_sass/site/_tokens-docs.scss b/_sass/site/tokens-docs.css
similarity index 97%
rename from _sass/site/_tokens-docs.scss
rename to _sass/site/tokens-docs.css
index 9b8aad5..7d23b27 100644
--- a/_sass/site/_tokens-docs.scss
+++ b/_sass/site/tokens-docs.css
@@ -52,11 +52,6 @@
 .ad-alias-chip--pair { display: flex; }
 .ad-alias-chip--pair > span { flex: 1; }
 
-.ad-grade { font-weight: 600; }
-.ad-grade--AAA, .ad-grade--AA { color: var(--ad-color-success); }
-.ad-grade--AA-Large { color: var(--ad-color-text-secondary); }
-.ad-grade--Fail { color: var(--ad-color-danger); }
-
 /* Type specimen rows. */
 .ad-specimen {
   display: grid;
diff --git a/assets/css/style.scss b/assets/css/style.scss
deleted file mode 100644
index 6d4db8c..0000000
--- a/assets/css/style.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-// The Sass entry point compiled by lib/arbitrary_definitions/site_builder.rb
-// into assets/css/style.css. The custom properties it consumes are generated
-// separately, by lib/arbitrary_definitions/token_set.rb, into
-// assets/css/tokens.css.
-
-@use "base/reset";
-@use "base/typography";
-
-@use "components/button";
-@use "components/field";
-@use "components/badge";
-@use "components/card";
-@use "components/feedback";
-@use "components/tabs";
-
-@use "site/shell";
-@use "site/example";
-@use "site/tokens-docs";
diff --git a/assets/css/tokens.css b/assets/css/tokens.css
new file mode 100644
index 0000000..34ff530
--- /dev/null
+++ b/assets/css/tokens.css
@@ -0,0 +1,134 @@
+/* Arbitrary Definitions — design tokens. Hand-maintained; edit values directly. */
+
+:root {
+  /* Palette ramps — reference only. Components use the aliases below. */
+  --ad-neutral-0: #faf9f7;
+  --ad-neutral-50: #fdfdfb;
+  --ad-neutral-100: #f1f0ed;
+  --ad-neutral-150: #e8e6e2;
+  --ad-neutral-200: #dcdad7;
+  --ad-neutral-300: #c0bdb9;
+  --ad-neutral-500: #888681;
+  --ad-neutral-600: #5f5d59;
+  --ad-neutral-800: #2a2926;
+  --ad-neutral-900: #100f0d;
+  --ad-neutral-dark-0: #0e0d0b;
+  --ad-neutral-dark-50: #151412;
+  --ad-neutral-dark-100: #1e1d1a;
+  --ad-neutral-dark-150: #252421;
+  --ad-neutral-dark-200: #2c2b29;
+  --ad-neutral-dark-300: #44423f;
+  --ad-neutral-dark-500: #7c7a76;
+  --ad-neutral-dark-600: #a09e9a;
+  --ad-neutral-dark-800: #d9d7d4;
+  --ad-neutral-dark-900: #e9e8e6;
+  --ad-accent-teal: #3a6a63;
+  --ad-accent-clay: #9a5a3c;
+  --ad-accent-ink-blue: #2b4a78;
+  --ad-accent-ochre: #93701f;
+  --ad-accent-plum: #6a3a5c;
+
+  /* Surfaces */
+  --ad-color-surface-page: #faf9f7;
+  --ad-color-surface-card: #fdfdfb;
+  --ad-color-surface-fill: #f1f0ed;
+  --ad-color-surface-fill-2: #e8e6e2;
+
+  /* Borders */
+  --ad-color-border-hairline: #dcdad7;
+  --ad-color-border-default: #c0bdb9;
+
+  /* Text */
+  --ad-color-text-primary: #100f0d;
+  --ad-color-text-secondary: #5f5d59;
+  --ad-color-text-tertiary: #888681;
+
+  /* Accent */
+  --ad-color-accent: #3a6a63;
+  --ad-color-accent-fg: #ffffff;
+  --ad-color-accent-ink: color-mix(in oklch, var(--ad-color-accent) 78%, black);
+  --ad-color-accent-soft: color-mix(in oklch, var(--ad-color-accent) 12%, white);
+
+  /* Status (fixed — never swapped like the accent) */
+  --ad-color-success: #3f7a4a;
+  --ad-color-success-bg: color-mix(in oklch, var(--ad-color-success) 12%, white);
+  --ad-color-danger: #ad3a2c;
+  --ad-color-danger-bg: color-mix(in oklch, var(--ad-color-danger) 10%, white);
+
+  /* Type families */
+  --ad-font-sans: -apple-system, "Helvetica Neue", Helvetica, Arial, system-ui, sans-serif;
+  --ad-font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Courier New", monospace;
+  --ad-font-serif: Georgia, "Times New Roman", "Iowan Old Style", serif;
+
+  /* Type steps — fluid (Utopia scale), the design system's own body-text size */
+  --ad-step--2: clamp(0.7813rem, 0.7736rem + 0.0341vw, 0.8rem);
+  --ad-step--1: clamp(0.9375rem, 0.9119rem + 0.1136vw, 1rem);
+  --ad-step-0: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem);
+  --ad-step-1: clamp(1.35rem, 1.2631rem + 0.3864vw, 1.5625rem);
+  --ad-step-2: clamp(1.62rem, 1.4837rem + 0.6057vw, 1.9531rem);
+  --ad-step-3: clamp(1.944rem, 1.7405rem + 0.9044vw, 2.4414rem);
+  --ad-step-4: clamp(2.3328rem, 2.0387rem + 1.3072vw, 3.0518rem);
+  --ad-step-5: clamp(2.7994rem, 2.384rem + 1.8461vw, 3.8147rem);
+
+  /* Space */
+  --ad-space-3xs: clamp(0.3125rem, 0.3125rem + 0vw, 0.3125rem);
+  --ad-space-2xs: clamp(0.5625rem, 0.5369rem + 0.1136vw, 0.625rem);
+  --ad-space-xs: clamp(0.875rem, 0.8494rem + 0.1136vw, 0.9375rem);
+  --ad-space-s: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem);
+  --ad-space-m: clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem);
+  --ad-space-l: clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem);
+  --ad-space-xl: clamp(3.375rem, 3.2216rem + 0.6818vw, 3.75rem);
+  --ad-space-2xl: clamp(4.5rem, 4.2955rem + 0.9091vw, 5rem);
+  --ad-space-3xl: clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem);
+
+  /* Radius */
+  --ad-radius-sm: 4px;
+  --ad-radius-md: 6px;
+  --ad-radius-lg: 10px;
+  --ad-radius-full: 999px;
+  --ad-border-w: 1px;
+
+  /* Elevation */
+  --ad-shadow-sm: 0 1px 2px rgba(20, 18, 14, 0.05);
+  --ad-shadow-float: 0 2px 6px rgba(20, 18, 14, 0.06), 0 10px 24px -12px rgba(20, 18, 14, 0.18);
+
+  /* Motion */
+  --ad-dur-fast: 0.12s;
+  --ad-dur-base: 0.16s;
+  --ad-ease: cubic-bezier(0.2, 0, 0, 1);
+}
+
+@media (prefers-color-scheme: dark) {
+  :root:not([data-theme="light"]) {
+    color-scheme: dark;
+    --ad-color-surface-page: #0e0d0b;
+    --ad-color-surface-card: #151412;
+    --ad-color-surface-fill: #1e1d1a;
+    --ad-color-surface-fill-2: #252421;
+    --ad-color-border-hairline: #2c2b29;
+    --ad-color-border-default: #44423f;
+    --ad-color-text-primary: #e9e8e6;
+    --ad-color-text-secondary: #a09e9a;
+    --ad-color-text-tertiary: #7c7a76;
+  }
+}
+
+:root[data-theme="dark"] {
+  color-scheme: dark;
+  --ad-color-surface-page: #0e0d0b;
+  --ad-color-surface-card: #151412;
+  --ad-color-surface-fill: #1e1d1a;
+  --ad-color-surface-fill-2: #252421;
+  --ad-color-border-hairline: #2c2b29;
+  --ad-color-border-default: #44423f;
+  --ad-color-text-primary: #e9e8e6;
+  --ad-color-text-secondary: #a09e9a;
+  --ad-color-text-tertiary: #7c7a76;
+}
+
+@media (prefers-reduced-motion: reduce) {
+  :root {
+    --ad-dur-fast: 0ms;
+    --ad-dur-base: 0ms;
+  }
+}
diff --git a/assets/js/system.js b/assets/js/system.js
index 5456635..fc9f6ee 100644
--- a/assets/js/system.js
+++ b/assets/js/system.js
@@ -1,4 +1,4 @@
-// Behaviour for the pieces of the system that need it. Everything else is
+// Behavior for the pieces of the system that need it. Everything else is
 // CSS and semantic HTML on purpose — a design system that only works with
 // JavaScript running is a component library with extra steps.
 (function () {
@@ -109,26 +109,6 @@
     });
   }
 
-  // Copy button on every example's source panel.
-  function setUpCopy() {
-    document.addEventListener("click", function (event) {
-      var button = event.target.closest("[data-copy]");
-      if (!button) return;
-
-      var code = button.parentElement.querySelector("code");
-      if (!code || !navigator.clipboard) return;
-
-      navigator.clipboard.writeText(code.textContent).then(function () {
-        button.dataset.copied = "true";
-        button.textContent = "Copied";
-        window.setTimeout(function () {
-          delete button.dataset.copied;
-          button.textContent = "Copy";
-        }, 1600);
-      });
-    });
-  }
-
   function setUpNavToggle() {
     document.addEventListener("click", function (event) {
       var toggle = event.target.closest("[data-nav-toggle]");
@@ -156,7 +136,6 @@
 
   setUpTheme();
   setUpTabs();
-  setUpCopy();
   setUpNavToggle();
   setUpLoadingDemo();
 })();
diff --git a/index.md b/index.md
index 0f1c0bc..88a78c6 100644
--- a/index.md
+++ b/index.md
@@ -3,8 +3,8 @@ layout: default
 title: Overview
 description: >-
   A personal design system for self-hosted, single-user tools — synthesized
-  from three real running projects and rendered from YAML tokens by a small
-  Ruby/ERB static-site generator on Ruby 4.0.
+  from three real running projects and rendered by a small Ruby/ERB
+  static-site generator on Ruby 4.0.
 ---
 
 <div class="ad-hero">
@@ -13,26 +13,26 @@ description: >-
   <p class="ad-lead">
     Arbitrary Definitions is not a company's design system — it is one person's,
     synthesized by reading three real, running personal projects rather than
-    written from a spec. Each project keeps its own accent colour and
+    written from a spec. Each project keeps its own accent color and
     typographic mood; this system is what they all share underneath.
   </p>
   <div class="ad-hero__actions">
-    <a class="ad-btn ad-btn--primary ad-btn--lg" href="<%= relative_url('/foundations/color/') %>">Start with colour</a>
+    <a class="ad-btn ad-btn--primary ad-btn--lg" href="<%= relative_url('/foundations/color/') %>">Start with color</a>
     <a class="ad-btn ad-btn--lg" href="<%= relative_url('/components/button/') %>">Jump to components</a>
   </div>
 </div>
 
 <div class="ad-stat-row">
   <div class="ad-stat">
-    <span class="ad-stat__value"><%= site.data.build.ramps %></span>
-    <span class="ad-stat__label">Colour ramps</span>
+    <span class="ad-stat__value">3</span>
+    <span class="ad-stat__label">Color ramps</span>
   </div>
   <div class="ad-stat">
-    <span class="ad-stat__value"><%= site.data.build.aliases %></span>
+    <span class="ad-stat__value">17</span>
     <span class="ad-stat__label">Semantic aliases</span>
   </div>
   <div class="ad-stat">
-    <span class="ad-stat__value"><%= site.components.size %></span>
+    <span class="ad-stat__value">16</span>
     <span class="ad-stat__label">Components</span>
   </div>
   <div class="ad-stat">
@@ -57,24 +57,23 @@ story, including where this system knowingly guesses.
 
 ## How it is put together
 
-Three YAML files under `_data/tokens/` hold every value in the system. Plain
-Ruby (`lib/arbitrary_definitions/token_set.rb`) reads them at build time and
-emits `assets/css/tokens.css` — one flat block of custom properties, plus the
-dark theme and a `prefers-reduced-motion` override. The component stylesheets
-read `var(--ad-*)` and nothing else.
+`assets/css/tokens.css` is one hand-maintained, hand-readable file of CSS
+custom properties — the palette ramps, the semantic aliases, the dark theme,
+and a `prefers-reduced-motion` override. The component stylesheets read
+`var(--ad-*)` and nothing else.
 
-The same YAML feeds the tables and swatches on these pages, so the
-documentation cannot drift from the stylesheet. If a hex changes in the YAML,
-the swatch, the contrast figure, and the button all change together or none
-of them do.
+The tables and swatches on these pages are static too — transcribed once
+from the token values, not recomputed on every build. If a hex changes in
+`tokens.css`, the swatch and the button are updated by hand together, the
+same afternoon.
 
 <div class="ad-card-grid">
   <article class="ad-card ad-card--link">
     <div class="ad-card__body">
       <h3 class="ad-card__title"><a href="<%= relative_url('/foundations/color/') %>">Foundations</a></h3>
       <p class="ad-card__text">
-        Colour, type, space, elevation, and motion — with contrast ratios
-        computed from the tokens during the build rather than typed by hand.
+        Color, type, space, elevation, and motion — with contrast ratios
+        computed from the tokens and checked by hand, not typed by guess.
       </p>
     </div>
   </article>
@@ -100,14 +99,16 @@ of them do.
 
 ## A taste of it
 
-<% example(title: "Everything on one card", layout: "stack") do %>
+<figure class="example">
+<figcaption>Everything on one card</figcaption>
+<div data-layout="stack">
 <article class="ad-card ad-card--elevated" style="max-width: 26rem;">
   <div class="ad-card__body">
     <div class="ad-badge-set">
       <span class="ad-badge ad-badge--accent">Recommended</span>
       <span class="ad-badge">Unassigned</span>
     </div>
-    <h3 class="ad-card__title">Rename the caution colour</h3>
+    <h3 class="ad-card__title">Rename the caution color</h3>
     <p class="ad-card__text">
       Three people have called it "yellow" in review this month. There is no
       caution token in this system — only <code>--ad-color-success</code> and
@@ -119,7 +120,28 @@ of them do.
     <button type="button" class="ad-btn ad-btn--ghost ad-btn--sm">Dismiss</button>
   </div>
 </article>
-<% end %>
+</div>
+<details><summary>Markup</summary>
+<pre><code>&lt;article class=&quot;ad-card ad-card--elevated&quot; style=&quot;max-width: 26rem;&quot;&gt;
+  &lt;div class=&quot;ad-card__body&quot;&gt;
+    &lt;div class=&quot;ad-badge-set&quot;&gt;
+      &lt;span class=&quot;ad-badge ad-badge--accent&quot;&gt;Recommended&lt;/span&gt;
+      &lt;span class=&quot;ad-badge&quot;&gt;Unassigned&lt;/span&gt;
+    &lt;/div&gt;
+    &lt;h3 class=&quot;ad-card__title&quot;&gt;Rename the caution color&lt;/h3&gt;
+    &lt;p class=&quot;ad-card__text&quot;&gt;
+      Three people have called it &quot;yellow&quot; in review this month. There is no
+      caution token in this system — only &lt;code&gt;--ad-color-success&lt;/code&gt; and
+      &lt;code&gt;--ad-color-danger&lt;/code&gt;, fixed across every project.
+    &lt;/p&gt;
+  &lt;/div&gt;
+  &lt;div class=&quot;ad-card__footer&quot;&gt;
+    &lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--primary ad-btn--sm&quot;&gt;Approve&lt;/button&gt;
+    &lt;button type=&quot;button&quot; class=&quot;ad-btn ad-btn--ghost ad-btn--sm&quot;&gt;Dismiss&lt;/button&gt;
+  &lt;/div&gt;
+&lt;/article&gt;</code></pre>
+</details>
+</figure>
 
 ## Voice
 
diff --git a/install.md b/install.md
index ebc2b6b..ec5382f 100644
--- a/install.md
+++ b/install.md
@@ -9,10 +9,10 @@ summary: >-
 
 ## The two files
 
-The system compiles to exactly two stylesheets. `tokens.css` is generated
-from YAML by `lib/arbitrary_definitions/token_set.rb`; `style.css` is the
-components. The order matters — the components read the custom properties the
-tokens define.
+The system compiles to exactly two stylesheets. `tokens.css` is a
+hand-maintained block of custom properties; `style.css` is the components.
+The order matters — the components read the custom properties the tokens
+define.
 
 ```html
 <link rel="stylesheet" href="tokens.css">
@@ -21,21 +21,20 @@ tokens define.
 
 Both are on this site at
 [`/assets/css/tokens.css`](<%= relative_url('/assets/css/tokens.css') %>) and
-[`/assets/css/style.css`](<%= relative_url('/assets/css/style.css') %>). The
-generated token file is <%= site.data.tokens_css_size %> bytes uncompressed.
+[`/assets/css/style.css`](<%= relative_url('/assets/css/style.css') %>).
 
 Load IBM Plex Mono separately — it is the system's one webfont, and nothing
 else is fetched:
 
 ```css
-<%= site.data.tokens.scale.typography.webfont_import %>
+@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&display=swap');
 ```
 
 Almost nothing here needs JavaScript. `assets/js/system.js` adds keyboard
-handling for [Tabs](<%= relative_url('/components/tabs/') %>), the theme
-toggle, and the copy buttons on this site — Tooltip, Checkbox, Radio, and
-Switch are pure CSS, and Dialog/Toast are shown as static states for
-documentation rather than wired up as production widgets.
+handling for [Tabs](<%= relative_url('/components/tabs/') %>) and the theme
+toggle — Tooltip, Checkbox, Radio, and Switch are pure CSS, and Dialog/Toast
+are shown as static states for documentation rather than wired up as
+production widgets.
 
 ## Dark theme
 
@@ -75,11 +74,11 @@ a handful of custom properties after the token file loads — most of all,
 
 <div class="ad-callout">
   <p style="margin:0; font-size: var(--ad-step--1);">
-    Re-check contrast after retheming. The system's contrast guarantees are
-    about the system's own values — swap the accent and you own the result.
-    The checker is dependency-free Ruby in
-    <code>lib/arbitrary_definitions/color_math.rb</code> if you want to run
-    the same assertions against your palette.
+    Re-check contrast after retheming. The system's contrast guarantees on
+    the <a href="<%= relative_url('/foundations/color/') %>">Color page</a>
+    are about the system's own values — swap the accent and you own the
+    result. The WCAG 2.1 relative-luminance formula is standard and easy to
+    run against your own palette if you want the same numbers.
   </p>
 </div>
 
@@ -94,7 +93,7 @@ runner happens to ship. Locally:
 ```console
 $ rbenv install 4.0.6      # or however you get Ruby 4.0
 $ bundle install
-$ bundle exec rake         # runs the token tests, then builds
+$ bundle exec rake         # builds the site into _site/
 $ bundle exec rake serve   # http://127.0.0.1:4000
 ```
 
@@ -104,30 +103,33 @@ $ bundle exec rake serve   # http://127.0.0.1:4000
   <table class="ad-table ad-table--compact">
     <thead><tr><th scope="col">Path</th><th scope="col">What it is</th></tr></thead>
     <tbody>
-      <tr><td class="ad-table__code">_data/tokens/</td><td>The four YAML files. Every value in the system.</td></tr>
-      <tr><td class="ad-table__code">lib/arbitrary_definitions/</td><td>The token compiler, WCAG colour maths, and the whole site builder. No Jekyll dependency anywhere, so the first two are unit tested on their own.</td></tr>
-      <tr><td class="ad-table__code">_layouts/, _includes/</td><td>Plain ERB templates — the page chrome and the <code>example</code> helper's two-up demo/source rendering.</td></tr>
-      <tr><td class="ad-table__code">_sass/components/</td><td>One stylesheet per component group. No hexes, no pixels, no durations.</td></tr>
-      <tr><td class="ad-table__code">test/</td><td>Minitest. Contrast floors, ramp monotonicity, CSS well-formedness.</td></tr>
+      <tr><td class="ad-table__code">assets/css/tokens.css</td><td>Every value in the system, hand-maintained.</td></tr>
+      <tr><td class="ad-table__code">_sass/</td><td>One plain CSS file per component group, concatenated into <code>style.css</code> at build time. No hexes, no pixels, no durations.</td></tr>
+      <tr><td class="ad-table__code">lib/arbitrary_definitions/</td><td>The whole site builder — front matter, ERB, and Kramdown, wrapped in a layout.</td></tr>
+      <tr><td class="ad-table__code">_layouts/, _includes/</td><td>Plain ERB templates for the page chrome.</td></tr>
+      <tr><td class="ad-table__code">_components/, _foundations/, _patterns/</td><td>Static Markdown and HTML — the content itself, hand-written.</td></tr>
     </tbody>
   </table>
 </div>
 
-## How the example blocks work
+## How the example demos work
 
-Every demo on this site is one call to an `example` ERB helper
-(`lib/arbitrary_definitions/example_helper.rb`). It captures its block's
-rendered output and renders that captured markup twice — live into the page,
-and Rouge-highlighted into the source panel underneath.
+Every demo on this site is plain, static markup: a `<figure>` with the live
+preview in one `<div>` and, underneath it, a `<details>` holding the exact
+same markup as literal, escaped text in a `<pre><code>`. There is no
+generator keeping the two in sync — copying the demo's markup by hand into
+the page is how the source panel gets written, so there is nothing to drift.
 
-<!--verbatim-->
-```erb
-<% example(title: "Variants", open: true) do %>
-<button type="button" class="ad-btn">Cancel</button>
-<button type="button" class="ad-btn ad-btn--primary">Save</button>
-<% end %>
+```html
+<figure class="example">
+  <figcaption>Variants</figcaption>
+  <div>
+    <button type="button" class="ad-btn">Cancel</button>
+    <button type="button" class="ad-btn ad-btn--primary">Save</button>
+  </div>
+  <details><summary>Markup</summary>
+<pre><code>&lt;button type="button" class="ad-btn"&gt;Cancel&lt;/button&gt;
+&lt;button type="button" class="ad-btn ad-btn--primary"&gt;Save&lt;/button&gt;</code></pre>
+  </details>
+</figure>
 ```
-<!--/verbatim-->
-
-There is no second copy of the markup to keep in sync, which is the usual way
-component documentation starts lying about the component.
diff --git a/lib/arbitrary_definitions/color_math.rb b/lib/arbitrary_definitions/color_math.rb
deleted file mode 100644
index fcac378..0000000
--- a/lib/arbitrary_definitions/color_math.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: true
-
-module ArbitraryDefinitions
-  # WCAG 2.1 colour maths, kept free of any Jekyll dependency so it can be
-  # unit tested on its own (see test/test_color_math.rb).
-  #
-  # The swatch tables on the Colour page call into this at build time, which
-  # is the whole point: a contrast number that is computed from the token
-  # cannot drift away from the token.
-  module ColorMath
-    module_function
-
-    HEX_PATTERN = /\A#?(\h{3}|\h{6})\z/
-    # Values that are not a flat hex — rgba(), colour functions, `none` —
-    # have no single luminance, so we decline rather than guess.
-    UNMEASURABLE = nil
-
-    # "#f95116" or "f95116" or "#abc" -> [249, 81, 22]
-    def to_rgb(hex)
-      match = HEX_PATTERN.match(hex.to_s.strip)
-      return UNMEASURABLE unless match
-
-      digits = match[1]
-      digits = digits.chars.flat_map { |c| [c, c] }.join if digits.length == 3
-      digits.scan(/\h{2}/).map { |pair| pair.to_i(16) }
-    end
-
-    def hex?(value)
-      !to_rgb(value).nil?
-    end
-
-    # WCAG relative luminance, 0.0 (black) to 1.0 (white).
-    def relative_luminance(hex)
-      rgb = to_rgb(hex)
-      return UNMEASURABLE unless rgb
-
-      r, g, b = rgb.map do |channel|
-        srgb = channel / 255.0
-        srgb <= 0.04045 ? srgb / 12.92 : ((srgb + 0.055) / 1.055)**2.4
-      end
-
-      (0.2126 * r) + (0.7152 * g) + (0.0722 * b)
-    end
-
-    # Contrast ratio between two colours, 1.0 to 21.0.
-    def contrast_ratio(one, two)
-      a = relative_luminance(one)
-      b = relative_luminance(two)
-      return UNMEASURABLE unless a && b
-
-      lighter, darker = a > b ? [a, b] : [b, a]
-      (lighter + 0.05) / (darker + 0.05)
-    end
-
-    # The WCAG grade a pair earns for normal-size body text.
-    # Large text (>= 18.66px bold or 24px) passes AA at 3.0, which the
-    # `large:` keyword accounts for.
-    def grade(ratio, large: false)
-      return "n/a" unless ratio
-
-      aa  = large ? 3.0 : 4.5
-      aaa = large ? 4.5 : 7.0
-
-      if ratio >= aaa then "AAA"
-      elsif ratio >= aa then "AA"
-      elsif ratio >= 3.0 then "AA Large"
-      else "Fail"
-      end
-    end
-
-    # Whichever of the two candidates reads more clearly on `background`.
-    def readable_on(background, light: "#ffffff", dark: "#171512")
-      on_light = contrast_ratio(background, light)
-      on_dark  = contrast_ratio(background, dark)
-      return dark unless on_light && on_dark
-
-      on_light >= on_dark ? light : dark
-    end
-  end
-end
diff --git a/lib/arbitrary_definitions/dot.rb b/lib/arbitrary_definitions/dot.rb
index ff99736..fc882ca 100644
--- a/lib/arbitrary_definitions/dot.rb
+++ b/lib/arbitrary_definitions/dot.rb
@@ -50,7 +50,7 @@ module ArbitraryDefinitions
     # silently rendered blank for an undefined property, and several pages
     # rely on that for genuinely optional front matter (`page.summary`,
     # `page.section`, `page.description`), so this keeps the same forgiving
-    # behaviour rather than making every optional field a special case at
+    # behavior rather than making every optional field a special case at
     # every call site.
     def method_missing(name, *args)
       return super unless args.empty? && !name.to_s.end_with?("=")
diff --git a/lib/arbitrary_definitions/example_helper.rb b/lib/arbitrary_definitions/example_helper.rb
deleted file mode 100644
index bd96475..0000000
--- a/lib/arbitrary_definitions/example_helper.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-# frozen_string_literal: true
-
-require "cgi/escape"
-require "rouge"
-
-module ArbitraryDefinitions
-  # `<%= example(title: "Variants", align: :center, ...) do %> ... <% end %>`
-  #
-  # Renders its block twice: once live, so the reader sees the component the
-  # browser actually paints, and once as highlighted source, so they can copy
-  # the exact markup. Writing the demo and the snippet separately is how
-  # design system docs start lying; this makes that impossible.
-  #
-  # This is the same logic that used to live in the Liquid `{% example %}`
-  # block (`_plugins/example_tag.rb`) — ported to a plain method that takes
-  # real keyword arguments and captures a real ERB block, since there is no
-  # Liquid markup string to parse any more.
-  #
-  # Options
-  #   title: "…"            caption above the preview
-  #   align: "start"|"center"|"end"      (default "start")
-  #   surface: "canvas"|"sunken"|"plain" (default "canvas")
-  #   layout: "row"|"stack"|"grid"       (default "row")
-  #   open: true             expand the source panel on load
-  #   no_source: true        preview only, for demos whose markup is noise
-  module ExampleHelper
-    ALIGNMENTS = %w[start center end].freeze
-    SURFACES   = %w[canvas sunken plain].freeze
-    LAYOUTS    = %w[row stack grid].freeze
-
-    # Called as `<% example(...) do %> ... <% end %>` — a statement, not
-    # `<%= ... %>`, because stock ERB (unlike Rails' Erubi) cannot compile an
-    # interpolated expression whose `do ... end` block spans template tags:
-    # the `<%=` tag closes its own `(...)` before the block body appears, so
-    # `_erbout << (example(...) do ).to_s` is what ERB would generate and it
-    # is a syntax error. Instead this writes its result directly into the
-    # same output buffer `capture` already knows how to reach.
-    def example(title: nil, align: nil, surface: nil, layout: nil, open: false, no_source: false, &block)
-      buffer = eval("_erbout", block.binding)
-      source = dedent(capture(&block).to_s)
-      return if source.strip.empty?
-
-      preview = <<~HTML.strip
-        <div class="ad-example__preview #{preview_classes(align, surface, layout)}">#{source}</div>
-      HTML
-
-      parts = []
-      parts << %(<figure class="ad-example">)
-      parts << %(<figcaption class="ad-example__title">#{escape_html(title)}</figcaption>) if title
-      parts << preview
-      parts << source_panel(source, open: open) unless no_source
-      parts << "</figure>"
-
-      buffer << parts.join("\n")
-      nil
-    end
-
-    # The well-known manual-capture pattern for plain ERB: the output buffer
-    # is a local variable (`_erbout`, ERB's default `eoutvar`) in whatever
-    # binding the surrounding template is being evaluated in, and a block
-    # literal written inside that template shares that same binding — so
-    # reaching into it lets a helper method capture exactly what its block
-    # wrote, the way Rails' `capture` does without Rails.
-    def capture(&block)
-      buffer = eval("_erbout", block.binding)
-      pos = buffer.length
-      block.call
-      result = buffer[pos..]
-      buffer.slice!(pos..)
-      result
-    end
-
-    private
-
-    def preview_classes(align, surface, layout)
-      align   = ALIGNMENTS.include?(align.to_s) ? align.to_s : "start"
-      surface = SURFACES.include?(surface.to_s) ? surface.to_s : "canvas"
-      layout  = LAYOUTS.include?(layout.to_s) ? layout.to_s : "row"
-
-      "ad-example__preview--#{align} ad-example__preview--#{surface} ad-example__preview--#{layout}"
-    end
-
-    def source_panel(source, open:)
-      lexer       = Rouge::Lexers::HTML.new
-      formatter   = Rouge::Formatters::HTML.new
-      highlighted = formatter.format(lexer.lex(source))
-
-      <<~HTML.strip
-        <details class="ad-example__source"#{open ? ' open' : ''}>
-        <summary class="ad-example__summary"><span>Markup</span></summary>
-        <div class="ad-example__code highlight">
-        <button type="button" class="ad-example__copy" data-copy aria-label="Copy markup to clipboard">Copy</button>
-        <pre><code class="language-html">#{highlighted}</code></pre>
-        </div>
-        </details>
-      HTML
-    end
-
-    # The captured block body carries whatever indentation the Markdown file
-    # used. Strip the common prefix so the copied snippet is not accidentally
-    # nested four levels deep.
-    def dedent(text)
-      lines  = text.sub(/\A\n+/, "").rstrip.lines
-      widths = lines.reject { |line| line.strip.empty? }
-                    .map { |line| line[/\A[ \t]*/].length }
-      indent = widths.min || 0
-
-      lines.map { |line| line.strip.empty? ? line.lstrip : line[indent..] }.join
-    end
-
-    def escape_html(value)
-      CGI.escapeHTML(value.to_s)
-    end
-  end
-end
diff --git a/lib/arbitrary_definitions/page_context.rb b/lib/arbitrary_definitions/page_context.rb
index 09a6985..d14c2ca 100644
--- a/lib/arbitrary_definitions/page_context.rb
+++ b/lib/arbitrary_definitions/page_context.rb
@@ -2,22 +2,15 @@
 
 require "cgi/escape"
 require "erb"
-require_relative "color_math"
-require_relative "example_helper"
 
 module ArbitraryDefinitions
-  # Everything an ERB template can reach: `site`/`page` data (standing in for
-  # Liquid's `{{ site.* }}` / `{{ page.* }}`), the colour-maths helpers that
-  # used to be Liquid filters (`_plugins/color_filters.rb`), `relative_url`
-  # (Jekyll's `relative_url` filter), `render` for includes/partials, and the
-  # `example` block helper (`_plugins/example_tag.rb`).
+  # Everything an ERB template can reach: `site`/`page` data, `relative_url`
+  # for links, `escape` for attribute text, and `render` for includes.
   #
   # One instance is built per page and reused for every layout and partial
   # that page passes through, so `page`/`site` stay consistent from the
   # innermost content template out through every wrapping layout.
   class PageContext
-    include ExampleHelper
-
     attr_accessor :site, :page, :content
 
     def initialize(site:, page: nil, content: nil)
@@ -34,46 +27,13 @@ module ArbitraryDefinitions
       binding
     end
 
-    # Stands in for Jekyll's `relative_url` filter: prefixes `baseurl`,
-    # without doubling slashes.
+    # Prefixes `baseurl` onto a path, without doubling slashes.
     def relative_url(path)
       path = path.to_s
       path = "/#{path}" unless path.start_with?("/")
       "#{site.baseurl.to_s.chomp('/')}#{path}"
     end
 
-    # -- former Liquid filters in _plugins/color_filters.rb -----------------
-
-    def contrast_with(value, other)
-      ratio = ColorMath.contrast_ratio(value, other)
-      return "—" unless ratio
-
-      format("%.2f", ratio)
-    end
-
-    def contrast_grade(value, other)
-      ColorMath.grade(ColorMath.contrast_ratio(value, other))
-    end
-
-    def readable_on(value)
-      ColorMath.readable_on(value)
-    end
-
-    def luminance_percent(value)
-      luminance = ColorMath.relative_luminance(value)
-      return "—" unless luminance
-
-      (luminance * 100).round
-    end
-
-    def hex_color?(value)
-      ColorMath.hex?(value)
-    end
-
-    def custom_property(value)
-      "--#{site.token_prefix}-#{value}"
-    end
-
     def escape(value)
       CGI.escapeHTML(value.to_s)
     end
@@ -82,7 +42,7 @@ module ArbitraryDefinitions
 
     INCLUDES_DIR = File.expand_path("../../_includes", __dir__)
 
-    # Stands in for `{% include name.html %}`. `locals` are exposed to the
+    # Renders an ERB partial from `_includes/`. `locals` are exposed to the
     # partial as local-ish reader methods for the duration of the call.
     def render(name, locals = {})
       body = File.read(File.join(INCLUDES_DIR, "#{name}.erb"))
diff --git a/lib/arbitrary_definitions/site_builder.rb b/lib/arbitrary_definitions/site_builder.rb
index 551b233..6b299c2 100644
--- a/lib/arbitrary_definitions/site_builder.rb
+++ b/lib/arbitrary_definitions/site_builder.rb
@@ -3,7 +3,6 @@
 require "yaml"
 require "erb"
 require "fileutils"
-require "time"
 
 # The site is UTF-8 regardless of the locale the build happens to run under;
 # a runner with LANG unset would otherwise read the Markdown as US-ASCII.
@@ -12,14 +11,14 @@ Encoding.default_internal = Encoding::UTF_8
 require "kramdown"
 require "kramdown-parser-gfm"
 
-require_relative "token_set"
 require_relative "dot"
 require_relative "page_context"
 
 module ArbitraryDefinitions
-  # The whole static site build: reads `_config.yml` and `_data/`, compiles
-  # the tokens and Sass, renders every Markdown page through ERB and then
-  # Kramdown, wraps it in its layout, and writes the result under `_site/`.
+  # The whole static site build: reads `_config.yml` and `_data/`, concatenates
+  # the plain CSS under `_sass/` and copies `assets/css/tokens.css`, renders
+  # every Markdown page through ERB and then Kramdown, wraps it in its layout,
+  # and writes the result under `_site/`.
   #
   # This replaces Jekyll itself, not just its plugins — `Rakefile`'s `build`
   # task is the only caller.
@@ -29,6 +28,22 @@ module ArbitraryDefinitions
     LAYOUTS_DIR = File.join(ROOT, "_layouts")
     SASS_DIR    = File.join(ROOT, "_sass")
 
+    # Plain CSS files, concatenated in this order into assets/css/style.css:
+    # reset/base first, then components, then this site's own chrome.
+    CSS_FILES = %w[
+      base/reset.css
+      base/typography.css
+      components/button.css
+      components/field.css
+      components/badge.css
+      components/card.css
+      components/feedback.css
+      components/tabs.css
+      site/shell.css
+      site/example.css
+      site/tokens-docs.css
+    ].freeze
+
     # dir under repository root => [permalink pattern, default section]
     COLLECTIONS = {
       "_foundations" => ["/foundations/%s/", "Foundations"],
@@ -44,18 +59,15 @@ module ArbitraryDefinitions
     PageSource = Struct.new(:front_matter, :body, :url, :layout, :section, keyword_init: true)
 
     def initialize
-      @config    = YAML.safe_load_file(File.join(ROOT, "_config.yml"))
-      @token_set = TokenSet.new(dir: File.join(ROOT, "_data", "tokens"),
-                                 prefix: @config.fetch("token_prefix", "ad"))
-      @site      = build_site
+      @config = YAML.safe_load_file(File.join(ROOT, "_config.yml"))
+      @site   = build_site
     end
 
     def build
       FileUtils.rm_rf(SITE_DIR)
       FileUtils.mkdir_p(SITE_DIR)
 
-      write_tokens_css
-      compile_sass
+      write_css
       copy_assets
       collect_pages.each { |page_source| render_page(page_source) }
     end
@@ -63,30 +75,6 @@ module ArbitraryDefinitions
     private
 
     def build_site
-      nav  = YAML.safe_load_file(File.join(ROOT, "_data", "nav.yml"))
-      meta = YAML.safe_load_file(File.join(ROOT, "_data", "meta.yml"))
-
-      component_names = Dir.glob(File.join(ROOT, "_components", "*.md")).map { |path| File.basename(path, ".md") }
-
-      data = {
-        "meta" => meta,
-        "nav" => nav,
-        "tokens" => {
-          "color" => { "ramps" => @token_set.ramps },
-          "semantic" => { "groups" => @token_set.semantic },
-          "scale" => @token_set.scale,
-          "contrast" => @token_set.contrast,
-        },
-        "semantic_flat" => @token_set.semantic_tokens,
-        "tokens_css_size" => @token_set.to_css.bytesize,
-        "build" => {
-          "ruby" => RUBY_VERSION,
-          "ramps" => @token_set.ramps.size,
-          "aliases" => @token_set.semantic_tokens.size,
-          "generated_at" => Time.now.utc.strftime("%Y-%m-%d"),
-        },
-      }
-
       Site.new(
         title: @config.fetch("title"),
         tagline: @config["tagline"],
@@ -95,25 +83,22 @@ module ArbitraryDefinitions
         baseurl: @config.fetch("baseurl", ""),
         repository: @config["repository"],
         lang: @config.fetch("lang", "en"),
-        token_prefix: @token_set.prefix,
-        components: component_names,
-        data: Dot.wrap(data),
+        data: Dot.wrap(
+          "nav" => YAML.safe_load_file(File.join(ROOT, "_data", "nav.yml")),
+          "meta" => YAML.safe_load_file(File.join(ROOT, "_data", "meta.yml")),
+        ),
       )
     end
 
-    Site = Struct.new(:title, :tagline, :description, :url, :baseurl, :repository, :lang,
-                       :token_prefix, :components, :data, keyword_init: true)
+    Site = Struct.new(:title, :tagline, :description, :url, :baseurl, :repository, :lang, :data, keyword_init: true)
 
-    def write_tokens_css
-      write(File.join(SITE_DIR, "assets", "css", "tokens.css"), @token_set.to_css)
-    end
+    def write_css
+      css = CSS_FILES.map { |name| File.read(File.join(SASS_DIR, name)) }.join("\n")
+      write(File.join(SITE_DIR, "assets", "css", "style.css"), css)
 
-    def compile_sass
-      require "sass-embedded"
-
-      source = File.read(File.join(ROOT, "assets", "css", "style.scss"))
-      result = Sass.compile_string(source, load_paths: [SASS_DIR], style: :compressed)
-      write(File.join(SITE_DIR, "assets", "css", "style.css"), result.css)
+      tokens_out = File.join(SITE_DIR, "assets", "css", "tokens.css")
+      FileUtils.mkdir_p(File.dirname(tokens_out))
+      FileUtils.cp(File.join(ROOT, "assets", "css", "tokens.css"), tokens_out)
     end
 
     def copy_assets
@@ -158,26 +143,6 @@ module ArbitraryDefinitions
       pages
     end
 
-    # A page that wants to *show* `<%= ... %>` syntax as documentation text —
-    # `install.md` demonstrating the `example` helper — needs it kept out of
-    # ERB's own evaluation, the same job Liquid's `{% raw %}` used to do.
-    # `<!--verbatim--> ... <!--/verbatim-->` marks such a span; its contents
-    # are stashed before the ERB pass and spliced back in afterwards.
-    VERBATIM = /<!--verbatim-->(.*?)<!--\/verbatim-->/m
-
-    def extract_verbatim(body)
-      blocks = []
-      protected_body = body.gsub(VERBATIM) do
-        blocks << Regexp.last_match(1)
-        " VERBATIM#{blocks.size - 1} "
-      end
-      [protected_body, blocks]
-    end
-
-    def restore_verbatim(text, blocks)
-      text.gsub(/ VERBATIM(\d+) /) { blocks[Regexp.last_match(1).to_i] }
-    end
-
     def split_front_matter(raw)
       match = raw.match(/\A---\s*\n(.*?)\n---\s*\n(.*)\z/m)
       raise "no front matter found" unless match
@@ -193,16 +158,9 @@ module ArbitraryDefinitions
 
       context = PageContext.new(site: @site, page: page)
 
-      protected_body, verbatim_blocks = extract_verbatim(page_source.body)
-      rendered = ERB.new(protected_body, trim_mode: "-").result(context.erb_binding)
-      rendered = restore_verbatim(rendered, verbatim_blocks)
+      rendered = ERB.new(page_source.body, trim_mode: "-").result(context.erb_binding)
 
-      html_body = Kramdown::Document.new(
-        rendered,
-        input: "GFM",
-        syntax_highlighter: "rouge",
-        syntax_highlighter_opts: { default_lang: "html" },
-      ).to_html
+      html_body = Kramdown::Document.new(rendered, input: "GFM", syntax_highlighter: nil).to_html
 
       html = wrap_layout(page_source.layout, html_body, context)
       write(File.join(output_dir(page_source.url), "index.html"), html)
diff --git a/lib/arbitrary_definitions/token_set.rb b/lib/arbitrary_definitions/token_set.rb
deleted file mode 100644
index fd1f3b8..0000000
--- a/lib/arbitrary_definitions/token_set.rb
+++ /dev/null
@@ -1,205 +0,0 @@
-# frozen_string_literal: true
-
-require "yaml"
-
-module ArbitraryDefinitions
-  # Reads the YAML under `_data/tokens/` and compiles it into one stylesheet
-  # of CSS custom properties.
-  #
-  # The YAML is the source of truth. Nothing else in the repository is allowed
-  # to write a raw hex, a raw pixel value, or a raw duration — the SCSS reads
-  # `var(--ad-*)` and the docs read the same YAML through Liquid, so the
-  # swatch on the page and the colour in the button are the same fact rendered
-  # twice.
-  class TokenSet
-    class UnknownReference < StandardError; end
-
-    # A `ramp.step` reference, e.g. `accent.teal`.
-    REFERENCE = /\A([a-z][a-z0-9_]*)\.([a-z0-9]+)\z/
-
-    attr_reader :prefix, :ramps, :semantic, :scale, :contrast
-
-    def initialize(dir:, prefix: "ad")
-      @prefix   = prefix
-      @ramps    = load_yaml(File.join(dir, "color.yml")).fetch("ramps")
-      @semantic = load_yaml(File.join(dir, "semantic.yml")).fetch("groups")
-      @scale    = load_yaml(File.join(dir, "scale.yml"))
-      @contrast = load_yaml(File.join(dir, "contrast.yml"))
-      @index    = build_ramp_index
-    end
-
-    # The contrast contract, flattened into one list of
-    # {foreground, background, minimum, …} rows. The test suite asserts these
-    # and the Colour page renders them, so neither can quietly fall behind the
-    # other.
-    def contrast_pairs
-      contrast.flat_map do |_key, section|
-        section.fetch("pairs").map do |pair|
-          pair.merge(
-            "kind"     => section.fetch("label"),
-            "minimum"  => section.fetch("minimum"),
-            "standard" => section.fetch("standard"),
-          )
-        end
-      end
-    end
-
-    # "ink.900" -> "#26231f"; a literal colour passes straight through.
-    def resolve(value)
-      value = value.to_s
-      match = REFERENCE.match(value)
-      return value unless match
-
-      @index.fetch(value) do
-        raise UnknownReference, "no token #{value.inspect} in ramp #{match[1].inspect}"
-      end
-    end
-
-    # Every semantic token, flattened, with both themes resolved. The docs use
-    # this to render the alias tables.
-    def semantic_tokens
-      semantic.flat_map do |group|
-        group.fetch("tokens").map do |token|
-          {
-            "group" => group.fetch("name"),
-            "token" => token.fetch("token"),
-            "name"  => custom_property(token.fetch("token")),
-            "light" => resolve(token.fetch("light")),
-            "dark"  => resolve(token.fetch("dark")),
-            "light_ref" => token.fetch("light"),
-            "dark_ref"  => token.fetch("dark"),
-            "usage" => token["usage"],
-          }
-        end
-      end
-    end
-
-    def custom_property(name)
-      "--#{prefix}-#{name}"
-    end
-
-    def to_css
-      [
-        header,
-        root_block,
-        dark_block,
-        forced_dark_block,
-        reduced_motion_block,
-      ].join("\n\n") << "\n"
-    end
-
-    private
-
-    def load_yaml(path)
-      YAML.safe_load_file(path, aliases: true)
-    end
-
-    def build_ramp_index
-      ramps.each_with_object({}) do |ramp, index|
-        ramp.fetch("steps").each do |step|
-          index["#{ramp.fetch('name')}.#{step.fetch('step')}"] = step.fetch("value")
-        end
-      end
-    end
-
-    def header
-      <<~CSS.strip
-        /* Generated by _plugins/token_generator.rb from _data/tokens/*.yml.
-           Do not edit by hand — edit the YAML and rebuild. */
-      CSS
-    end
-
-    def root_block
-      lines = []
-      lines << "  /* Palette ramps — reference only. Components use the aliases below. */"
-      ramps.each do |ramp|
-        ramp.fetch("steps").each do |step|
-          lines << declaration("#{ramp.fetch('name')}-#{step.fetch('step')}", step.fetch("value"))
-        end
-      end
-
-      semantic.each do |group|
-        lines << ""
-        lines << "  /* #{group.fetch('name')} */"
-        group.fetch("tokens").each do |token|
-          lines << declaration(token.fetch("token"), resolve(token.fetch("light")))
-        end
-      end
-
-      scale_groups.each do |label, entries|
-        lines << ""
-        lines << "  /* #{label} */"
-        entries.each { |entry| lines << declaration(entry.fetch("token"), entry.fetch("value")) }
-      end
-
-      wrap(":root", lines)
-    end
-
-    def dark_block
-      lines = ['  color-scheme: dark;']
-      semantic.each do |group|
-        lines << ""
-        lines << "  /* #{group.fetch('name')} */"
-        group.fetch("tokens").each do |token|
-          lines << declaration(token.fetch("token"), resolve(token.fetch("dark")))
-        end
-      end
-
-      body = wrap("  :root:not([data-theme=\"light\"])", lines, indent: "  ")
-      <<~CSS.rstrip
-        @media (prefers-color-scheme: dark) {
-        #{body}
-        }
-      CSS
-    end
-
-    # An explicit choice from the theme toggle has to beat the media query in
-    # both directions, so the dark declarations are emitted a second time
-    # under the attribute selector.
-    def forced_dark_block
-      lines = ['  color-scheme: dark;']
-      semantic.each do |group|
-        group.fetch("tokens").each do |token|
-          lines << declaration(token.fetch("token"), resolve(token.fetch("dark")))
-        end
-      end
-
-      wrap(':root[data-theme="dark"]', lines)
-    end
-
-    def reduced_motion_block
-      durations = scale.dig("motion", "durations") || []
-      lines = durations.map { |entry| declaration(entry.fetch("token"), "0ms") }
-
-      <<~CSS.rstrip
-        @media (prefers-reduced-motion: reduce) {
-        #{wrap('  :root', lines, indent: '  ')}
-        }
-      CSS
-    end
-
-    # The non-colour token groups, in the order they should appear, paired
-    # with a human label for the comment banner.
-    def scale_groups
-      groups = []
-      typography = scale.fetch("typography")
-      groups << ["Type families", typography.fetch("families")]
-      groups << ["Type steps", typography.fetch("steps")]
-      groups << ["Space", scale.dig("space", "scale")]
-      groups << ["Radius", scale.dig("radius", "scale")]
-      groups << ["Elevation", scale.dig("elevation", "scale")]
-      groups << ["Durations", scale.dig("motion", "durations")]
-      groups << ["Easings", scale.dig("motion", "easings")]
-      groups.reject { |(_, entries)| entries.nil? || entries.empty? }
-    end
-
-    def declaration(name, value)
-      "  #{custom_property(name)}: #{value};"
-    end
-
-    def wrap(selector, lines, indent: "")
-      body = lines.map { |line| line.empty? ? "" : "#{indent}#{line}" }.join("\n")
-      "#{selector} {\n#{body}\n#{indent}}"
-    end
-  end
-end
diff --git a/rationale.md b/rationale.md
index 4830b3c..b4cbd79 100644
--- a/rationale.md
+++ b/rationale.md
@@ -59,8 +59,9 @@ Not the quality of the individual choices. The properties below:
     <div class="ad-card__body">
       <h3 class="ad-card__title">It is written down once</h3>
       <p class="ad-card__text">
-        Four YAML files. Not a Figma library that disagrees with the CSS, and
-        not a spreadsheet somebody exports by hand.
+        One hand-maintained stylesheet of custom properties. Not a Figma
+        library that disagrees with the CSS, and not a spreadsheet somebody
+        exports by hand.
       </p>
     </div>
   </article>
@@ -77,7 +78,7 @@ Not the quality of the individual choices. The properties below:
     <div class="ad-card__body">
       <h3 class="ad-card__title">It is actually used</h3>
       <p class="ad-card__text">
-        Every colour, type, spacing, radius, elevation, and motion value in
+        Every color, type, spacing, radius, elevation, and motion value in
         the components is a <code>var(--ad-*)</code>. Not one is a literal.
       </p>
     </div>
@@ -95,29 +96,22 @@ Not the quality of the individual choices. The properties below:
 
 ## The one thing that is not arbitrary
 
-Contrast. A ratio is a physical fact about two colours and a pair of eyes,
+Contrast. A ratio is a physical fact about two colors and a pair of eyes,
 and it does not care what the brand guidelines say.
 
-So it is the one thing the build refuses to take on trust.
-`_data/tokens/contrast.yml` names
-<%= site.data.tokens.contrast.text.pairs.size %> pairings that have to hold;
-`lib/arbitrary_definitions/color_math.rb` computes WCAG relative luminance
-from the token values, and `test/test_token_set.rb` asserts every one of them
-in both themes. The [contract table](<%= relative_url('/foundations/color/') %>#contrast-that-has-to-hold)
-is rendered from that same file. If a token change drops any pairing below
-its floor, `rake test` fails and the site does not deploy.
-
-```console
-$ bundle exec rake test
-0 failures, 0 errors, 0 skips
-```
-
-That contract is deliberately narrower than "every colour in the system".
+So it is the one thing this system checks rather than eyeballs. Five
+pairings have to hold, computed once from the token values with the
+standard WCAG 2.1 relative-luminance formula and written down as fact on the
+[contract table](<%= relative_url('/foundations/color/') %>#contrast-that-has-to-hold)
+rather than re-derived on every build — a personal site does not need a test
+suite standing between an edit and a deploy to keep five numbers honest.
+
+That contract is deliberately narrower than "every color in the system".
 `--ad-color-text-tertiary` — the source's own "muted-faint text" step — is
 about 3.46:1 against the page in light mode, under the 4.5:1 floor for normal
-text. That is not a bug the build should paper over by darkening a real brand
-value; it is a documented, intentional property of the palette (see the
-[Colour page](<%= relative_url('/foundations/color/') %>)), so the contract
+text. That is not a bug to paper over by darkening a real brand value; it is
+a documented, intentional property of the palette (see the
+[Color page](<%= relative_url('/foundations/color/') %>)), so the contract
 excludes it rather than silently forcing it to pass. Borders get the same
 treatment for the same reason: the source's own rule is that hairline borders
 do the elevation work, not high contrast, so this system does not invent a
@@ -126,7 +120,7 @@ do the elevation work, not high contrast, so this system does not invent a
 ## The four rules
 
 <div class="ad-callout">
-  <p class="type-label" style="margin-bottom: var(--ad-space-2xs); display:block;">1. No colour and no duration outside the token files</p>
+  <p class="type-label" style="margin-bottom: var(--ad-space-2xs); display:block;">1. No color and no duration outside tokens.css</p>
   <p style="margin:0; font-size: var(--ad-step--1);">
     A hex or a duration in a component stylesheet is a decision made in the
     dark, and it is the kind that breaks the moment somebody adds a theme. No
@@ -149,8 +143,9 @@ do the elevation work, not high contrast, so this system does not invent a
 <div class="ad-callout">
   <p class="type-label" style="margin-bottom: var(--ad-space-2xs); display:block;">3. Every token says what it is for</p>
   <p style="margin:0; font-size: var(--ad-step--1);">
-    A <code>usage</code> line on every semantic token, enforced by a test. A
-    token nobody can describe is a token nobody will use correctly.
+    Every semantic alias on the <a href="<%= relative_url('/foundations/color/') %>">Color page</a>
+    carries a usage note. A token nobody can describe is a token nobody will
+    use correctly.
   </p>
 </div>
 
diff --git a/script/build_design_bundle.rb b/script/build_design_bundle.rb
index 856489d..38ff581 100644
--- a/script/build_design_bundle.rb
+++ b/script/build_design_bundle.rb
@@ -28,7 +28,7 @@ CANON  = "https://kejadlen.github.io#{BASE}"
 
 # page path in _site => [bundle path, Design System pane group, card title]
 CARDS = [
-  ["foundations/color",       "foundations/color.html",       "Foundations", "Colour"],
+  ["foundations/color",       "foundations/color.html",       "Foundations", "Color"],
   ["foundations/typography",  "foundations/typography.html",  "Foundations", "Typography"],
   ["foundations/space",       "foundations/space.html",       "Foundations", "Space"],
   ["foundations/elevation",   "foundations/elevation.html",   "Foundations", "Elevation"],
@@ -67,9 +67,6 @@ def extract_main(html, page)
   body
     # Site-relative links would dead-end inside the Design System pane.
     .gsub(%r{(href|src)="#{Regexp.escape(BASE)}/}, "\\1=\"#{CANON}/")
-    # The copy button needs the site's clipboard handler; without it the
-    # control is a lie, so the card ships without it.
-    .gsub(%r{<button[^>]*data-copy[^>]*>.*?</button>}m, "")
     .strip
 end
 
diff --git a/test/test_color_math.rb b/test/test_color_math.rb
deleted file mode 100644
index 6294420..0000000
--- a/test/test_color_math.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# frozen_string_literal: true
-
-require "minitest/autorun"
-require "arbitrary_definitions/color_math"
-
-class TestColorMath < Minitest::Test
-  CM = ArbitraryDefinitions::ColorMath
-
-  def test_parses_six_digit_hex_with_and_without_hash
-    assert_equal [249, 81, 22], CM.to_rgb("#f95116")
-    assert_equal [249, 81, 22], CM.to_rgb("f95116")
-  end
-
-  def test_expands_three_digit_shorthand
-    assert_equal [0xaa, 0xbb, 0xcc], CM.to_rgb("#abc")
-  end
-
-  def test_declines_values_that_have_no_single_luminance
-    assert_nil CM.to_rgb("rgba(38, 35, 31, 0.5)")
-    assert_nil CM.to_rgb("none")
-    assert_nil CM.relative_luminance("var(--ad-color-surface-page)")
-    assert_nil CM.contrast_ratio("#ffffff", "none")
-  end
-
-  def test_luminance_endpoints
-    assert_in_delta 0.0, CM.relative_luminance("#000000"), 1e-9
-    assert_in_delta 1.0, CM.relative_luminance("#ffffff"), 1e-9
-  end
-
-  def test_black_on_white_is_the_maximum_contrast
-    assert_in_delta 21.0, CM.contrast_ratio("#000000", "#ffffff"), 1e-6
-  end
-
-  def test_contrast_is_symmetric_and_self_contrast_is_one
-    assert_in_delta CM.contrast_ratio("#ea360c", "#ffffff"),
-                    CM.contrast_ratio("#ffffff", "#ea360c"), 1e-12
-    assert_in_delta 1.0, CM.contrast_ratio("#ea360c", "#ea360c"), 1e-12
-  end
-
-  def test_known_pair_matches_the_wcag_reference_value
-    # #767676 on white is the canonical "exactly AA" grey.
-    assert_in_delta 4.54, CM.contrast_ratio("#767676", "#ffffff"), 0.01
-  end
-
-  def test_grades_use_the_normal_text_thresholds
-    assert_equal "AAA", CM.grade(7.0)
-    assert_equal "AA", CM.grade(4.5)
-    assert_equal "AA Large", CM.grade(3.0)
-    assert_equal "Fail", CM.grade(2.99)
-    assert_equal "n/a", CM.grade(nil)
-  end
-
-  def test_large_text_grades_are_more_forgiving
-    assert_equal "AA", CM.grade(3.0, large: true)
-    assert_equal "AAA", CM.grade(4.5, large: true)
-  end
-
-  def test_readable_on_picks_the_higher_contrast_foreground
-    assert_equal "#171512", CM.readable_on("#faf9f7")
-    assert_equal "#ffffff", CM.readable_on("#171512")
-  end
-end
diff --git a/test/test_token_set.rb b/test/test_token_set.rb
deleted file mode 100644
index 75dbe98..0000000
--- a/test/test_token_set.rb
+++ /dev/null
@@ -1,155 +0,0 @@
-# frozen_string_literal: true
-
-require "minitest/autorun"
-require "arbitrary_definitions/token_set"
-require "arbitrary_definitions/color_math"
-
-class TestTokenSet < Minitest::Test
-  TOKENS_DIR = File.expand_path("../_data/tokens", __dir__)
-
-  def setup
-    @tokens = ArbitraryDefinitions::TokenSet.new(dir: TOKENS_DIR)
-  end
-
-  def test_resolves_a_ramp_reference_to_its_hex
-    assert_equal "#3a6a63", @tokens.resolve("accent.teal")
-    assert_equal "#100f0d", @tokens.resolve("neutral.900")
-  end
-
-  def test_passes_literal_values_through_untouched
-    assert_equal "#ffffff", @tokens.resolve("#ffffff")
-    assert_equal "color-mix(in oklch, var(--ad-color-accent) 12%, white)",
-                 @tokens.resolve("color-mix(in oklch, var(--ad-color-accent) 12%, white)")
-  end
-
-  def test_raises_on_a_reference_that_does_not_exist
-    assert_raises(ArbitraryDefinitions::TokenSet::UnknownReference) do
-      @tokens.resolve("neutral.55")
-    end
-  end
-
-  # The point of the semantic layer is that it is complete: if a token exists
-  # in light it has to exist in dark, or the theme toggle leaves holes.
-  def test_every_semantic_token_resolves_in_both_themes
-    @tokens.semantic_tokens.each do |token|
-      refute_nil token["light"], "#{token['token']} has no light value"
-      refute_nil token["dark"], "#{token['token']} has no dark value"
-    end
-  end
-
-  def test_every_semantic_token_documents_its_usage
-    undocumented = @tokens.semantic_tokens.reject { |token| token["usage"].to_s.strip.length.positive? }
-
-    assert_empty undocumented.map { |token| token["token"] },
-                 "semantic tokens must say what they are for"
-  end
-
-  def test_semantic_token_names_are_unique
-    names = @tokens.semantic_tokens.map { |token| token["token"] }
-
-    assert_equal names.uniq, names, "duplicate semantic token names"
-  end
-
-  # `accent` is a flat set of five unrelated brand hues, not a light-to-dark
-  # scale, so it opts out via `monotonic: false` in color.yml. `neutral` and
-  # `neutral-dark` stay covered: each is listed in the YAML in the order that
-  # actually darkens (or, for neutral-dark, lightens) step to step, which can
-  # differ from ascending step-number order — see the comment in color.yml.
-  def test_ramps_run_light_to_dark_without_reversing
-    @tokens.ramps.each do |ramp|
-      next if ramp["monotonic"] == false
-
-      luminances = ramp.fetch("steps").map do |step|
-        ArbitraryDefinitions::ColorMath.relative_luminance(step.fetch("value"))
-      end
-
-      assert_equal luminances.sort.reverse, luminances,
-                   "#{ramp['name']} steps are not monotonically darkening"
-    end
-  end
-
-  # The contrast contract lives in _data/tokens/contrast.yml so that this
-  # suite and the Colour page read the same list. Adding a pair there adds it
-  # to the deploy gate here, with no second copy to keep in step.
-  def test_the_contrast_contract_holds_in_both_themes
-    by_name = @tokens.semantic_tokens.to_h { |token| [token["token"], token] }
-    failures = []
-
-    @tokens.contrast_pairs.each do |pair|
-      %w[light dark].each do |theme|
-        foreground = by_name.fetch(pair.fetch("foreground"))
-        background = by_name.fetch(pair.fetch("background"))
-        ratio = ArbitraryDefinitions::ColorMath.contrast_ratio(foreground[theme], background[theme])
-
-        next if ratio && ratio >= pair.fetch("minimum")
-
-        failures << "#{pair['foreground']} on #{pair['background']} is " \
-                    "#{ratio ? ratio.round(2) : 'unmeasurable'}:1 in the #{theme} theme, " \
-                    "below the #{pair['minimum']}:1 floor"
-      end
-    end
-
-    assert_empty failures
-  end
-
-  def test_the_contract_only_names_tokens_that_exist
-    known = @tokens.semantic_tokens.map { |token| token["token"] }
-    named = @tokens.contrast_pairs.flat_map { |pair| pair.values_at("foreground", "background") }
-
-    assert_empty named.uniq - known, "the contrast contract names tokens the system does not define"
-  end
-
-  # `color-text-tertiary` is the one deliberate exception: the source
-  # documents that step as muted-faint metadata/placeholder text, not body
-  # copy, and it is sub-AA on purpose (see contrast.yml's header comment and
-  # the Colour foundation page). Holding it to the same floor as
-  # `-primary`/`-secondary` would mean either lying about the contract or
-  # darkening a real brand value just to make the suite green — this system
-  # does neither.
-  def test_the_contract_covers_the_pairings_that_matter
-    covered = @tokens.contrast_pairs.map { |pair| pair.fetch("foreground") }
-
-    required = @tokens.semantic_tokens
-                      .map { |token| token["token"] }
-                      .select { |name| name.start_with?("color-text", "color-on-") }
-                      .reject { |name| %w[color-text-inverse color-text-tertiary].include?(name) }
-
-    assert_empty required - covered, "a text token is not covered by the contrast contract"
-  end
-
-  def test_every_contract_pair_explains_itself
-    undocumented = @tokens.contrast_pairs.reject { |pair| pair["note"].to_s.strip.length.positive? }
-
-    assert_empty undocumented
-  end
-
-  def test_css_emits_a_root_block_and_both_dark_selectors
-    css = @tokens.to_css
-
-    assert_includes css, ":root {"
-    assert_includes css, '@media (prefers-color-scheme: dark)'
-    assert_includes css, ':root[data-theme="dark"]'
-    assert_includes css, "@media (prefers-reduced-motion: reduce)"
-  end
-
-  def test_css_contains_no_unresolved_references
-    unresolved = @tokens.to_css.scan(/:\s*([a-z][a-z0-9_]*\.[a-z0-9]+);/)
-
-    assert_empty unresolved.flatten, "token references leaked into the stylesheet"
-  end
-
-  def test_css_declares_every_semantic_token_in_root
-    root = @tokens.to_css[/:root \{(.*?)\n\}/m, 1]
-
-    @tokens.semantic_tokens.each do |token|
-      assert_includes root, "#{token['name']}:", "#{token['name']} missing from :root"
-    end
-  end
-
-  def test_braces_balance
-    css = @tokens.to_css
-
-    assert_equal css.count("{"), css.count("}")
-  end
-
-end