---
name: CONTRA
description: A host-blind password manager dressed as a station cloakroom's left-luggage ticket.
colors:
  stock: "light-dark(#c8dacf, #131a16)"
  stock-tint: "light-dark(#b3c9bb, #1d2621)"
  stock-manila: "light-dark(#ddc78d, #3a3018)"
  ink: "light-dark(#121512, #e6efe8)"
  ink-strong: "light-dark(#000000, #ffffff)"
  ink-soft: "light-dark(#bcd0c3, #232e28)"
  on-ink: "light-dark(#eef3ee, #101512)"
  ink-2: "light-dark(#b3301a, #ff7a5c)"
  text-muted: "light-dark(#3a4741, #a9b8ae)"
  text-faint: "light-dark(#434f49, #93a399)"
  line: "light-dark(#a3b8ab, #2c3830)"
  line-strong: "light-dark(#56655c, #7d8d83)"
  tone-negative-fg: "light-dark(#8b2310, #f0a293)"
  tone-negative-bg: "light-dark(#e3c2b6, #2a1b18)"
  tone-positive-fg: "light-dark(#1d5134, #9fc9ac)"
  tone-positive-bg: "light-dark(#b6d2be, #16241b)"
  tone-warning-fg: "light-dark(#57430f, #d6c48a)"
  tone-warning-bg: "light-dark(#d5cc9c, #24211a)"
  tone-neutral-fg: "light-dark(#33403a, #c4d2c9)"
  tone-neutral-bg: "light-dark(#b9cec2, #232e28)"
typography:
  display:
    fontFamily: "League Gothic, Oswald, Archivo Narrow, system-ui, sans-serif"
    fontSize: "clamp(2.4rem, 6vw, 3.6rem)"
    fontWeight: 400
    lineHeight: 1
    letterSpacing: "0.01em"
  headline:
    fontFamily: "League Gothic, Oswald, Archivo Narrow, system-ui, sans-serif"
    fontSize: "1.9rem"
    fontWeight: 400
    lineHeight: 1
    letterSpacing: "0.04em"
  title:
    fontFamily: "League Gothic, Oswald, Archivo Narrow, system-ui, sans-serif"
    fontSize: "1.35rem"
    fontWeight: 400
    lineHeight: 1
    letterSpacing: "0.04em"
  numbering:
    fontFamily: "League Gothic, Oswald, Archivo Narrow, system-ui, sans-serif"
    fontSize: "clamp(3rem, 12vw, 5rem)"
    fontWeight: 400
    lineHeight: 1
    letterSpacing: "0.14em"
    fontFeature: "tabular-nums"
  body:
    fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif"
    fontSize: "0.875rem"
    fontWeight: 400
    lineHeight: 1.5
    letterSpacing: "normal"
  label:
    fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif"
    fontSize: "0.71875rem"
    fontWeight: 600
    lineHeight: 1.2
    letterSpacing: "0.12em"
  serial:
    fontFamily: "ui-monospace, SF Mono, Cascadia Mono, Menlo, Consolas, monospace"
    fontSize: "0.95em"
    fontWeight: 400
    lineHeight: 1.5
    letterSpacing: "0.06em"
    fontFeature: "tabular-nums"
rounded:
  cut: "0"
  stamp: "999px"
spacing:
  sp-1: "0.25rem"
  sp-2: "0.5rem"
  sp-3: "0.75rem"
  sp-4: "1rem"
  sp-5: "1.5rem"
  sp-6: "2.5rem"
components:
  button-primary:
    backgroundColor: "{colors.ink}"
    textColor: "{colors.on-ink}"
    typography: "{typography.label}"
    rounded: "{rounded.cut}"
    padding: "0.5rem 1.5rem"
  button-primary-hover:
    backgroundColor: "{colors.ink-strong}"
    textColor: "{colors.on-ink}"
  button-primary-disabled:
    backgroundColor: "{colors.stock-tint}"
    textColor: "{colors.text-faint}"
  button-ruled:
    backgroundColor: "transparent"
    textColor: "{colors.ink}"
    typography: "{typography.label}"
    rounded: "{rounded.cut}"
    padding: "0.5rem 1.5rem"
  button-ruled-hover:
    backgroundColor: "{colors.ink-soft}"
    textColor: "{colors.ink}"
  button-marginal:
    backgroundColor: "transparent"
    textColor: "{colors.ink}"
    rounded: "{rounded.cut}"
    padding: "0.15rem 0.5rem"
  field-label:
    textColor: "{colors.text-muted}"
    typography: "{typography.label}"
  field-input:
    backgroundColor: "transparent"
    textColor: "{colors.ink}"
    typography: "{typography.body}"
    rounded: "{rounded.cut}"
    padding: "0 0 0.1rem"
    width: "100%"
  notice-stamp:
    backgroundColor: "{colors.tone-negative-bg}"
    textColor: "{colors.ink}"
    rounded: "{rounded.cut}"
    padding: "{spacing.sp-4}"
    width: "68ch"
  counterfoil:
    backgroundColor: "{colors.stock-tint}"
    textColor: "{colors.ink}"
    rounded: "{rounded.cut}"
    padding: "{spacing.sp-4}"
  vault-shared:
    backgroundColor: "{colors.stock-manila}"
    textColor: "{colors.ink}"
    rounded: "{rounded.cut}"
    padding: "{spacing.sp-4}"
  numbering-code:
    backgroundColor: "transparent"
    textColor: "{colors.ink-2}"
    typography: "{typography.numbering}"
  step-overprint:
    backgroundColor: "transparent"
    textColor: "{colors.tone-negative-fg}"
    typography: "{typography.label}"
  status-pill:
    backgroundColor: "{colors.tone-neutral-bg}"
    textColor: "{colors.tone-neutral-fg}"
    typography: "{typography.label}"
    rounded: "{rounded.stamp}"
    padding: "0.1rem 0.6rem"
  nav-link:
    backgroundColor: "transparent"
    textColor: "{colors.ink}"
    typography: "{typography.label}"
    padding: "0.5rem 0"
  footer-overprint:
    backgroundColor: "transparent"
    textColor: "{colors.text-faint}"
    typography: "{typography.label}"
    padding: "0.75rem 0"
---

🤖 Machine-authored record of CONTRA's visual system, written from the shipped
stylesheets and templates rather than from intention. Where the direction
contract in `layout.html` and the built code disagree, the code is recorded and
the divergence is noted. This file is the visual system only; the cryptographic
protocol lives in `PROTOCOL.md`, which carried the name `DESIGN.md` until
2026-09-04 and has nothing to do with anything below.

# Design System: CONTRA

## Overview

**Creative North Star: "Left Luggage"**

A station cloakroom holds your bag, never learns what is in it, and will not
release it without the stub. That is the product's argument and it is also the
material: coloured ticket stock, black ink, one vermilion spent only on the
numbering machine, ruled fields with small letterspaced caps printed above the
rule, and one perforation where the ticket tears. The world was not chosen to
look like a ticket; six of the product's mechanics — the counterfoil, the
never-repeating epoch, the coloured stock that codes a category, the expiring
claim check, the perforation between client and server — already *are* ticket
mechanics, so the visual system describes the protocol rather than costuming it.

The register is institutional, plain, and unapologetic about consequences,
which is the same voice `PRODUCT.md` records for the product. Ticket printing
as a station cloakroom would do it: every mark is doing a job. Ticket printing
as a fairground would do it — grain, distress, torn-paper shadows, rotated
stamps, fake staples, perforated GIFs used as ornament — is refused in
`contra.css` by name, because the moment the material gets texture it turns
twee and the institution stops being credible. The category default is refused
equally and explicitly: no deep navy, no rounded cards, no lock mark, no
green-for-safe.

Density is that of a printed form rather than a dashboard: one sheet of stock
edge to edge, no cards, ruled rows for line items, more space above a heading
than below it. Light is the primary scheme, and that is a decision about a
scene — a person at a desk in daylight with a printer running, holding two
sheets they were told can never be reissued. Dark is the same counter at night
and is a complete scheme, not an inversion; every token is declared once as
`light-dark(<light>, <dark>)` under a single `:root`, and
`:root[data-theme=light|dark]` setting `color-scheme` is the whole toggle.

**Key Characteristics:**
- Eau-de-nil ticket stock as the ground, emphatically not cream and never white.
- The accent is the ink: links and primary buttons are black, not coloured.
- One second colour (vermilion), spent only on counting and on what cannot be undone.
- Square-cut throughout (`0` radius); the status pill is the one round thing, and it is a rubber stamp.
- Flat by conviction: ink on paper has no depth.
- Five named devices — the field box, the numbering, the tear-line, the stamp, the stock code — and every screen is built from them.

## Colors

One stock, one ink, one second colour, plus a manila category code; ticket
printing has no third colour and neither does this.

### Primary
- **Cloakroom Ink** (`{colors.ink}`): the accent *is* the ink. Body text, headings, link colour, the primary button's fill, the 3px rule under the page title, and the top rule that opens a list. A link is black text that underlines (1px thickness, 0.18em offset, thickening to 2px on hover), never a coloured word. **Ink Strong** (`{colors.ink-strong}`) is the hover state of anything filled with ink; **On Ink** (`{colors.on-ink}`) is the label printed on it; **Ink Soft** (`{colors.ink-soft}`) is the wash a ruled button takes on hover.

### Secondary
- **Numbering Vermilion** (`{colors.ink-2}`, `--contra-ink-2`): the numbering machine's ink, and the only colour on the page. It prints the handoff claim code, the device ladder's rung counters, and the caret in every text field. Its hue also carries the negative tone, so the second colour lands exactly where the product's consequences are: something being counted, or something that cannot be undone.

### Tertiary
- **Manila Stock** (`{colors.stock-manila}`, `--contra-stock-2`): the category code, coding exactly one thing — a vault that belongs to somebody else and you are on the list. `vault.js` sets `data-shared` on the open vault's root, so the whole sheet changes stock the way a cloakroom prints luggage tickets on different stock from coat tickets. It is also the `::selection` background. Never a badge, never decoration.

### Neutral
- **Eau-de-Nil Stock** (`{colors.stock}`): the page. `--rst-bg` and `--rst-surface` are the same value on purpose — the page is one sheet, and a card would be a second sheet on a counter that only ever holds one.
- **Printed Tint Block** (`{colors.stock-tint}`): the darker band a ticket uses to group a field. Carries the counterfoil and disabled controls. A flatter surface, never a raised one.
- **Muted / Faint Text** (`{colors.text-muted}`, `{colors.text-faint}`): field labels, secondary list text and status lines take muted; the tear-line caption, placeholders and the footer overprint take faint.
- **Hairline** (`{colors.line}`) and **Rule** (`{colors.line-strong}`): the hairline divides rows and closes the footer; the rule is the control boundary — field underlines, the perforation's holes, the serial's underline.
- **Status tones** (`{colors.tone-neutral-fg}` … `{colors.tone-negative-bg}`): the rubber stamps, held at low chroma so the page still reads as stock and ink at a glance. Negative is the exception, because that is the second colour doing its other job.

### Named Rules

**The One Stock, One Ink, One Second Colour Rule.** A screen has a ground, a
black ink, and at most one vermilion mark. If a new element seems to need a
fourth colour, it does not need a colour; it needs a rule, a case change, or a
position.

**The Colour Means A Category Rule.** Manila codes a vault owned by somebody
else, and nothing else, ever. A colour that does not carry a category does not
get used.

**The Large-Only Rule.** `--contra-ink-2` measures 4.29:1 on the stock in
light. It is a **large-text colour only**: 24px and up, or 18.66px bold. Small
red text is `--rst-tone-negative-fg`, which clears 4.5:1 on its own tint. Never
reach for the numbering ink at body size — darkening it until it passes at 14px
turns a vermilion into a brick and costs the theme its second colour.

### Accessibility floors

WCAG 2.2 AA is binding (`PRODUCT.md`). `theme.css`'s header carries the full
measured table for both schemes, computed from the exact hex values; if a token
changes, its rows are recomputed there. Normal text needs 4.5:1; large text and
UI component boundaries need 3:1. The tightest text pair in the build is
`--rst-text-faint` on the tint block at **4.89:1** in light against a 4.5:1
floor. Text on stock measures 12.60:1 light / 15.06:1 dark; a button label on
the ink 16.37:1; the control border `--rst-line-strong` 4.22:1 light / 5.06:1
dark against a 3:1 floor; the numbering ink 4.29:1 light / 6.90:1 dark against
its large-text 3:1 floor, and 3.76:1 on manila.

- `--rst-line` is a hairline **divider**, not a control border: 1.4.11's 3:1 does not apply to it. Control borders use `--rst-line-strong`.
- `--rst-header-rule` is decorative — the ticket's rule under its title — and carries no contrast floor. It resolves to the ink, which makes it the heaviest mark on the sheet.
- The shadow tokens and the overlay paint translucent layers over unknown grounds, so no ratio applies to them.

## Typography

**Display Font:** League Gothic (self-hosted WOFF2, ~10KB, OFL 1.1, `font-display: swap`), falling back to Oswald, Archivo Narrow, then the UI stack.
**Body / Label Font:** the machine's own UI stack (`system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial`).
**Serial Font:** `ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas`.

**Character:** an Alternate Gothic revival — the condensed face poster and
ticket printing has been set in for a century — over a workhorse UI sans. The
display face does the ticket's shouting; the sans does the ticket's work. The
webfont is same-origin because this app fetches nothing from anywhere, and a
CDN font would be one more party the browser trusts on a page holding vault
keys.

### Hierarchy
- **Display / h1** (League Gothic 400, `clamp(2.4rem, 6vw, 3.6rem)`, line-height 1, 0.01em): the page title, in **sentence case**, over a 3px ink rule with `0.5rem` of clearance and `1.5rem` beneath. One per screen.
- **Headline / h2** (League Gothic 400, 1.9rem, line-height 1, 0.04em, uppercase): section labels. `2.5rem` above, `0.75rem` below.
- **Title / h3** (League Gothic 400, 1.35rem, line-height 1, 0.04em, uppercase): sub-sections and the device ladder's rung headings.
- **Numbering** (League Gothic 400, `clamp(3rem, 12vw, 5rem)`, tabular, 0.14em, in `{colors.ink-2}`): the handoff claim code, printed at the size a cloakroom prints a stub number, because a person reads it off one screen and types it into another. Rung counters are the same voice at 2.6rem.
- **Brand overprint** (League Gothic, 1.5rem, 0.14em, uppercase): the issuing office's name in the shell bar.
- **Body** (UI stack 400, 0.875rem/14px, line-height 1.5): prose is capped at **68ch**; the column itself is sized for forms and lists.
- **Label** (UI stack 600, 0.71875rem/11.5px, 0.12em, uppercase): field names, disclosure summaries, the tear-line caption, the footer overprint. Buttons are the same voice at 0.78125rem/700/0.14em; nav links at 0.78125rem/600/0.08em; the status line at 0.78125rem/0.06em.
- **Serial** (mono, 0.95em, tabular figures, 0.06em, underlined with `{colors.line-strong}`, `word-break: break-all`): opaque ids, build stamps, handoff codes — values a person transcribes by hand. Monospace here is measurement, not costume, and a serial sits on a ruled line rather than in a grey box.

### Named Rules

**The Caps Are For Labels Rule.** Capitals name things; they do not speak
sentences. `h2` and `h3` are section labels and are uppercased; `h1` keeps
sentence case and takes its character from the face and the rule under it,
because this app's title is sometimes a whole sentence, and a whole sentence in
condensed capitals is a poster nobody reads and a screen reader may spell out.

**The Overprint Prints What It Is Rule.** Uppercasing stops at a serial. A
build hash the reader will paste and compare renders in its own case
(`[data-overprint] code`), because a page that exists to report a value must
not restyle the value into a lie.

**The Display Face Stays Big Rule.** League Gothic is a poster face. At 11.5px
with 0.12em tracking it goes spindly and its condensed forms stop being
legible, so none of the caps furniture — labels, buttons, nav, status, the
overprint — uses it. That furniture is set in the UI stack and takes its ticket
character from case, tracking, weight and the rule it sits on. **Recorded cost,
accepted in the build:** the small caps render in a different typeface per
operating system while the display line does not.

## Layout

One centred column, one sheet of stock. The framework's `tokens.css` declares
the column at `64rem`; **CONTRA narrows it to `60rem`** in `contra.css` and
gives it `min-block-size: calc(100vh - 8rem)` so the sheet reaches the fold on
a short page. Column padding is `1.5rem 1rem 2.5rem`. The shell footer matches
the column at `60rem`.

The sheet has **no border of its own**. It had cut edges for one round and they
were wrong: the console shell already rules the side of the navigation rail,
and the page's own border landed about 55px inboard of it, showing two
competing sheet edges with a dead strip between them. The rail's rule is the
structural one; the sheet is defined by its ground and its rules, which is how
a ticket is actually printed.

Spacing is the framework's six-step rhythm — `0.25 / 0.5 / 0.75 / 1 / 1.5 /
2.5rem` — and the build uses all six. Vertical rhythm is asymmetric on purpose:
more space above a heading than below it. Form rows carry no top margin and
`1rem` beneath.

The shell is a top bar plus a side rail. Below 800px both fold behind **one**
`<details>` disclosure with two reveals — the bar's tail via the
adjacent-sibling combinator, the rail from the shell root via `:has()` —
written as hide-when-closed, so an engine without `:has()` degrades to
everything visible rather than to nothing. The summary control is drawn at
390px.

Reading measures: prose `68ch`, notices `68ch`, checkbox and radio sentence
labels `68ch`, text inputs `32rem`.

The `@media print` block strips the bar, rail, footer and skip link and removes
the column cap, so a printed screen comes out black on white with no chrome.

**The One Sheet Rule.** There are no cards. A card is a second sheet of paper
on a counter that only ever holds one. Grouping is done with a rule, a tint
block, or a change of stock.

## Elevation & Depth

Flat by conviction. Ink on paper has no depth and nothing in this app floats.
The shadow tokens exist only because the framework's stylesheet paints with
`var(--rst-shadow-*)` unconditionally and a theme cannot opt out of a property
it must declare, so they are set to the least a theme can declare and still be
declaring something: a single hairline layer, `0 1px 0`. You get an edge, not a
lift. Depth, where it is needed at all, is carried by tonal layering — the tint
block behind a counterfoil, manila behind a shared vault — and by rules.

### Shadow Vocabulary
- **Pop edge** (`box-shadow: 0 1px 0 light-dark(rgba(18,21,18,0.22), rgba(0,0,0,0.55))`): the framework's popovers and menus.
- **Knob edge** (`box-shadow: 0 1px 0 light-dark(rgba(18,21,18,0.30), rgba(0,0,0,0.5))`): switch and slider knobs.
- **Lift edge** (`box-shadow: 0 1px 0 light-dark(rgba(18,21,18,0.12), rgba(0,0,0,0.35))`): the framework's lightest lift.
- **Overlay** (`light-dark(rgba(18,21,18,0.55), rgba(0,0,0,0.68))`): the only real darkening in the app, because a modal genuinely is a second sheet laid over the first.

**The No Lift Rule.** Never add a shadow to make something look raised. If an
element must separate from the sheet, give it a rule, a tint, or a perforation.
The three shadow tokens are inherited obligations held at their minimum, not an
elevation ramp to climb.

## Shapes

Cut, not moulded. `--rst-radius` and `--rst-radius-sm` are `0`, and every
control CONTRA draws is a square rectangle: buttons, inputs, the notice stamp,
the counterfoil, the manila block. The pill radius (`999px`) survives only
because `tokens.css` paints status pills and row actions with it; in this world
a pill is a rubber stamp, and a stamp is the one round thing on a ticket.

The recurring geometry is **the rule**: 3px in the ink under the page title,
2px under every field, 1px in the ink over a list, 1px hairlines between rows
and above the footer, 3px in the negative foreground across the top of a stamp.
Weight encodes rank — title, control, boundary, divider — and the build never
substitutes a box for a rule.

The one non-rectilinear mark in the app is the perforation, and it is drawn as
real holes rather than as a dashed border.

## Components

### Buttons
- **Shape:** square-cut (`0` radius), 2px border in the ink.
- **Primary:** a solid rectangle of ink (`{colors.ink}` fill, `{colors.on-ink}` label), 0.78125rem/700 uppercase at 0.14em tracking, padded `0.5rem 1.5rem`. What a ticket office rubber-stamps.
- **Hover:** fill and border shift to `{colors.ink-strong}`. No transition, no lift, no transform.
- **Focus:** `outline: 2px solid {colors.ink}` at `3px` offset. The same ring everywhere in the app.
- **Ruled (secondary):** the same rectangle ruled rather than filled — transparent fill, ink label and border; hover washes to `{colors.ink-soft}`. Used for `type="button"`, cancel, edit, delete.
- **Marginal:** row-level edit and delete drop to a 1px border and `0.15rem 0.5rem` of padding. They are marginal notes on a line item, not stamps.
- **Disabled:** tint-block fill, faint text, `{colors.line-strong}` border, `cursor: not-allowed`.

### Inputs / Fields — Device 1, THE FIELD BOX
- **Style:** no box. A small letterspaced cap label sits directly on a 2px `{colors.line-strong}` rule, with the value on the rule. Transparent background, no border but the underline, `line-height: 1.2` and `padding: 0 0 0.1rem` so the value sits close under its label — the label names the rule beneath it rather than heading an empty line. Width 100%, capped at `32rem`.
- **Focus:** the underline turns to the numbering vermilion and the 2px ink outline appears at 3px offset. The caret is vermilion in every field, always.
- **Placeholder:** `{colors.text-faint}`.
- **Checkbox / radio:** opt out of the caps entirely — a checkbox label is a sentence, so it takes body size, normal tracking, sentence case, and a `68ch` measure. `accent-color` is the vermilion; the control is `1.1rem` square.
- **Textarea:** `6rem` minimum block size, vertical resize only.

### Lists
Ruled rows, never cards. A 1px ink rule opens the list, 1px hairlines separate
rows, rows are baseline-aligned and wrap from a `12rem` flex basis with
`0.75rem` of vertical padding. Secondary text in the row is muted.

### Navigation
Rail links are uppercase 0.78125rem/600 at 0.08em, undecorated, with `0.5rem`
of vertical padding, underlining on hover. The shell's disclosure summaries
take the label voice (0.71875rem/700/0.12em uppercase, square) so a control
never arrives in the framework's title case.

### Status
The live region reads as a printed endorsement rather than a toast: muted,
0.78125rem, 0.06em, uppercase, with `1.4em` of reserved height so a message
appearing does not shift the sheet.

### Signature components

**Device 2 — THE NUMBERING.** Anything counted prints tabular in the second
colour. Three live instances and no others: `[data-code]`, the handoff claim
code, in the display face at `clamp(3rem, 12vw, 5rem)`; `[data-rungs]`
counters, the device ladder, at 2.6rem in the display face, absolutely
positioned in a 3.5rem gutter beside each rung; and `[rst-steps]`, the signup
step overprint, which is *small* and therefore prints in
`{colors.tone-negative-fg}`, never in the numbering ink. Section numbers are
usually decoration; these carry meaning — the rungs are ordered by what works
with the least, and the last one always works.

**Device 3 — THE TEAR-LINE.** A real perforation: `radial-gradient` holes,
1.7px solid to 1.8px transparent, repeated at a fixed 12px pitch
(`--contra-perf`, declared once so every tear-line in the app is the same
machine), 9px tall, flanking a centred uppercase caption. **Exactly one per
screen, and only on a genuine client/server boundary** — sign-in, where it runs
through the middle of the form because that is where the boundary genuinely is,
and the vault. Above it what the server holds; below it what only this browser
knows. Never decoration, never twice.

**Device 4 — THE STAMP.** `[data-notice]` and `[data-kind="warning"]`: a block
on the negative tint with a **3px rule across the top** in the negative
foreground, `1rem` padding, a `68ch` measure, and strong text in the negative
foreground. This is how a ticket prints a condition of carriage. Never a
coloured left border.

**Device 5 — THE STOCK CODE.** `[data-vault][data-shared]` prints the entire
open vault on manila with `1rem` of padding, set from `vault.js` when the vault
is not owned. The whole sheet changes stock; there is no "shared" badge.

**The counterfoil.** `[data-counterfoil]` is the tint block above the tear-line
carrying the server's half of the ticket — opaque ids, counts, times. It is the
only place in the app that changes colour without changing meaning, and the
meaning it carries is "this side is theirs".

**The overprint.** `[data-overprint]` ends the sheet: small, faint,
letterspaced caps under a hairline, aligned to the start, carrying the build
serial. A sheet with nothing at the foot is a sheet that just stops.

### Motion

**The One Authored Moment Rule.** The world's native motion is the tear, so
there is exactly one animation in the app: the vault opening. The sealed half
below the line is revealed once on unlock — `clip-path: inset(0 0 100% 0)` to
`inset(0 0 0 0)` with opacity, `620ms`, `cubic-bezier(0.16, 1, 0.3, 1)`, `both`
— easing the way paper separates, fast at the start and settling at the end.
Nothing else animates; there is not one hover transition in `contra.css`. The
keyframes' end state is the visible one, so an engine that ignores the
animation shows the content immediately, and `prefers-reduced-motion: reduce`
removes it outright.

### Browser surfaces

The parts nobody drew still carry the design: selection is manila under ink,
scrollbars are thin in `{colors.line-strong}` on the tint block, and the caret
is the numbering vermilion. On a page whose whole argument is deliberateness, a
default that belongs to no design system shows.

## Do's and Don'ts

### Do:
- **Do** print the page on one sheet: `{colors.stock}` edge to edge, the column at `60rem`, no border, no card.
- **Do** build every input as the field box — cap label, 2px rule, value sitting on the rule.
- **Do** keep the second colour large: 24px up, or 18.66px bold. Small red is `{colors.tone-negative-fg}`.
- **Do** draw the tear-line only where a real client/server boundary exists, once per screen, at the shared 12px pitch.
- **Do** group with a rule, a tint block, or a change of stock.
- **Do** set headings in the display face and keep `h1` in sentence case over its 3px ink rule.
- **Do** recompute the contrast rows in `theme.css`'s header whenever a colour token changes; the table is the record and 4.5:1 / 3:1 are binding.
- **Do** let `code` print its own case and its own tabular figures — a serial is transcribed, not styled.
- **Do** declare any new token once as `light-dark(<light>, <dark>)` under the single `:root`, so both schemes stay complete and the toggle keeps working.

### Don't:
- **Don't** edit `internal/contra/static/tokens.css`. It is vendored from the rastrillo framework and pinned byte-identical by a test; it contributes the spacing steps, the type scale, the framework component rules and the `64rem` column default. CONTRA's overrides go in `theme.css` (colour, type, shape) and `contra.css` (grammar and components), which loads last and wins.
- **Don't** add a card, a rounded corner, or a shadow that reads as a lift. Radius is `0` except the status pill, which is a rubber stamp.
- **Don't** add texture: no distress, no paper grain, no torn-paper shadows, no rotated stamps, no fake staples, no perforated GIFs as decoration. That is a fairground; this is a station cloakroom.
- **Don't** reach for the category default — deep navy, rounded cards, a lock mark, green-for-safe.
- **Don't** colour a link or a primary button. The accent is the ink.
- **Don't** spend manila on anything but a vault owned by somebody else, or vermilion on anything but counting and the irreversible.
- **Don't** set a sentence in condensed capitals, and don't set caps furniture in League Gothic — it is a poster face and goes spindly at label size.
- **Don't** add hover transitions or a second animation. One authored moment; everything else is instant.
- **Don't** put a notice behind a coloured left border; it is a top-ruled stamped block.

## Known gaps in the shipped build

Recorded so the next pass inherits the truth rather than rediscovering it.
These are defects or accepted costs the build carries; none of them is a rule
for a new surface to imitate.

- **The caps furniture is in the system UI stack, not the display face.** The reasoning is written above the `label` rule in `contra.css` and is a legibility argument, not an omission. The accepted cost: the small caps render in a different typeface per operating system while the display line does not.
- **The Emergency Kit PDFs** (`internal/contra/static/crypto/kit.js`, `pdf.js`) still use PDF base-14 fonts and have not been brought into this world. It is the product's only printed artefact and the largest outstanding item. Do not read its current typography as a system decision.
- **`index.html`'s prose** carries two stale sentences awaiting a copy review. Copy, not visual system; out of scope for this file.
