Matt Rodenbeck
Wireframe. Masked boxes stand where a record is still missing.Slot inventoryTurn off

System · Paper and Plate · tokens 1 · 5e9cbec

This site is built from one record, and this page is that record rendered.

Every colour, space and duration on the site comes from one file. This page reads the same file, so what it shows can't disagree with what you're looking at. The checks that keep it honest run on every push and are linked beside each rule.

01 Colour

Two palettes and a fixed plate. Light is the base, dark only redefines, and the plate is dark in both because the work reads at cinema contrast whatever the page does. Every ratio below is computed from the record when the site is built.

paper#f1f2ef · #0f1113page ground
ink#141619 · #e6e8e4text, the H1, rules on plates
muted#62676d · #9aa0a6secondary text, captions, nav at rest, spec rail labels
rule#d3d6d3 · #262a2e1px hairlines only, never text, about 1.3 to 1 on paper
field#e6e8e5 · #171a1dcode blocks, the spec sheet, the filmstrip track, a tone rather than a card
marker#b92d22 · #ff6157the one accent: frame numbers, the active chapter, hover, focus, the record dot
blueprint#35648a · #7aa7ccbuild chapters only: graph strokes, histogram bars, code links
plate#0b0c0d · fixedevery media band in both themes, so the work reads at cinema contrast whatever the page does
plate-ink#e6e8e4 · fixedon the plate
plate-muted#9aa0a6 · fixedon the plate
plate-marker#ff6157 · fixedon the plate
plate-rule#1a1d20 · fixedon the plate
Contrast, WCAG 2 relative luminance, computed at build. Text pairs need 4.5 to 1 and the build fails under it.
PairLightDarkUse
ink on paper16.1315.34text
muted on paper5.087.17text
marker on paper5.406.40text
blueprint on paper5.597.41text
muted on field4.636.62text
ink on field14.7114.17text
rule on paper1.301.31decorative, never text
plate-ink on plate15.8715.87text
plate-muted on plate7.417.41text
plate-marker on plate6.626.62text

02 Space and grid

The reading column, the gutter and the gap, as the page uses them.

content-max
72rem
gutter
clamp(1rem, 4vw, 3rem)
grid-gap
1.5rem
measure
66ch

03 Motion

The work is the motion and the interface stays still. Names say what a movement is for rather than what it does, because a token named for a mechanism ends up serving two jobs with opposite needs and then neither can be tuned. Two columns: the value, and what it becomes under reduced motion, which is a decision per movement rather than a switch that zeroes everything. A colour change and an opacity fade are not movement. This visit is receiving the defaultreduced column. Each row plays on request only, so this page never moves until you ask it to.

Every movement on the site, its default and its reduced value, and why.
TokenDefaultReducedUsed byWhy the reduced value
--respond120ms120mslink and row hover, focus, the press, the hover intent timerA direct answer to a pointer, so it is spent while somebody waits. Past about 200ms that reads as lag and people click again. A colour change and a hair of scale are not the movement reduce protects against.
--beat120ms120msthe gap between the hero sentence and the line under itA delay rather than a duration. Nothing moves during it, so reduce has nothing to cut, and the offset is what makes two lines read as one arrival instead of two.
--enter-page200ms200msthe hero rise on first paintIt stands between a reader and the sentence, so it takes the tightest expressive budget on the site. The travel is 8px, well under any vestibular threshold, so reduce keeps it rather than snapping the text into place.
--enter-overlay480ms0msthe still growing from its tile to full sizeSeveral hundred pixels and a scale, so the number that is slow on the hero is brisk here. Nothing is blocked while it runs, which makes it the one place the page performs. Zero under reduce, because a frame scaling across the viewport is the large area movement the setting exists to stop, and the dialog still opens.
--move-mark230ms0msthe chapter rail mark sliding under its labelPosition change becomes a cut, same end state.
--slip230ms0msthe misregistration offset and the fringe tint that rides with itHover triggered, but nothing waits on it, because the word is already readable and the ghost is decoration. So it is spent as an expressive movement rather than as a response. Offset and tint share one value because they are one event, not two.
--ease-outcubic-bezier(0.2, 0, 0, 1)linearanything entering or movingNothing moves under reduce, so the curve is moot and linear says so.
--ease-incubic-bezier(0.4, 0, 1, 1)linearanything leavingAs above.
--slip-x0.17em0.17emhow far a misregistered word slipsA distance, not a duration. The travel is cut under reduce by --slip going to zero, so the offset itself does not change and the fringe still prints. Halved from 0.34em on 2026-09-21: at the old value the ghosts spread far enough that the word underneath stopped being readable on hover, which is the one thing a decorative effect on a title must not do.
--rise-y0.5rem0.5remhow far the hero rises on first paintSame reason. --enter-page carries the same value in both columns because a fade is not the movement reduce protects against, so this distance holds too.
--press0.9851the scale a tile takes while pressedUnitless, fed to scale(). It flattens to 1 under reduce, because a press that moves is the one part of this that is movement rather than feedback.
--slip-band13rem13remhow far from the cursor a word still liftsA radius, not a duration. The fringe fades out with distance from the pointer so a title answers where the eye is rather than all at once, and the words themselves never move apart.
--drift2000ms0msthe fringe sweeping across a title after a finger lets go of itTouch has no hover, so on a phone the fringe froze wherever the finger last was once the page scrolled. Letting go hands it to a slow sweep, one end of the line to the other and back, instead. Two seconds a pass, chosen by eye on 2026-09-25 once it ran at its real speed, a wave rather than a crawl. The strobe it showed on the first night was a unit bug, the minified 3.2s read as 3.2 milliseconds, never this value. Zero under reduce, where letting go clears the fringe instead, because an effect that keeps moving on its own is what the setting exists to stop.
--linger700ms700mshow long a cursor rests on a title before the fringe starts to sweepA delay rather than a duration, so nothing moves during it. Long enough that a cursor passing over a title never starts it, short enough that one resting there sees it before looking away. The same under reduce, where --drift is zero and the sweep never starts anyway.
scrollsmoothautoanchor jumpsA jump is a jump.

04 Rules, with their teeth beside them

Each rule names what enforces it. The ones that nothing enforces yet say so, which is more honest than a page of ticks.

  1. Rule is a hairline and never text. It measures about 1.3 to 1 on paper, so an absent chapter is muted with a line through it, never set in rule.

    tools/check_tokens.mjs
  2. The interface stays still and the work is the motion. Every movement has a name here and a reduced value that's a choice rather than a zero.

    tools/check_tokens.mjs
  3. Plate is dark in both themes and carries its own ink, muted and marker, because the light marker on it measures 3.2 to 1 and fails.

    data/design/tokens.json
  4. Nothing shifts. Every image and video declares its box before its bytes arrive, and the build fails on any layout shift at all.

    site/lighthouserc.json
  5. Nothing is defined for the first time inside a media query or a data attribute block. Light is the base and the dark blocks only redefine.

    tools/tokens.mjs
  6. A component never writes a literal. It names a token, and a hex, a millisecond or a curve written by hand fails the build.

    tools/check_tokens.mjs
  7. One inline script runs before paint, for the visitor's own preferences, and every page is complete without it.

    docs/decisions/0013-preferences-before-paint.md
  8. Nothing on a branded surface is a flat fill or a stroke. Boxes, rules, icons and display type are lit cells on the lattice, and the smallest tier of text comes off the display entirely because the display cannot do small.

    docs/decisions/0018-the-display.md

05 Endpoints

The record this page is rendered from, served as it is on disk.

tokens
/system/tokens.json
design tokens format
/system/tokens.dtcg.json, the W3C community group shape, with light and dark and default and reduced as modes, which is what a Figma variable import reads
source
data/design/tokens.json at 5e9cbec
renderer
tools/tokens.mjs
guard
tools/check_tokens.mjs
motion lab
/wire/motion, the four open interactions running for real, each option naming the token it would claim
wireframe
/wire, every slot on the site filled or masked and every interaction built or still open. Two of the durations above are documented here and performed nowhere, and that page says which.