carlos / contra Public

design: the finish review, worked

The reviewer's verdict was `fix`, and the finding underneath the eight
items is the one worth stating plainly: five devices were declared and
about a third shipped. Two were dead selectors nothing referenced, two
more only existed behind a login, and every page a visitor could
actually reach was stock, ink and a single rule. A vocabulary written
into a stylesheet is not a design; it is a plan for one.

THE PERFORATION NOW RUNS THROUGH THE SIGN-IN FORM, which is where it
was always supposed to be. The address above the line goes to the
server as a blind index it cannot reverse; the passphrase below it does
not go anywhere. That is not a diagram of the promise — it is the
promise happening inside one form, on a page anybody can open, and it
is the single clearest place in the product to draw it.

THE STOCK CODE IS WIRED. A vault belonging to somebody else prints its
whole contents on manila. Not a badge reading "shared": whose sheet you
are writing on is something you want to know before you type, which is
why a cloakroom uses different stock rather than a note in the corner.
Four lines in vault.js, and jsHighWater says so.

THE SECOND COLOUR REACHES A PUBLIC PAGE. Signup's step marker is a real
sequence, so it prints as the overprint a ticket gives one — small,
letterspaced, in the negative ink, because --contra-ink-2 is a
large-text colour and 11.5px red would have been the rule broken by the
person who wrote it. Both new pairs are measured into the table.

THE DEAD CSS IS GONE rather than left looking like intent.
[data-number] and its label were referenced by nothing.

Two competing sheet edges on every wide screen: the console draws a
rule down the rail and mine landed 55px inboard of it with a dead strip
between. The rail's is structural and stays; the page's is gone.

The field label sits on its rule now. The distance was the input's own
text box, so shortening that was the fix — the previous round removed
margin that was never there and called it done.

The ticket ends. An overprint carries the build serial on every sheet,
moved out of index.html so it is one string with one home rather than a
line the front page happened to own.

Two things the reviewer asked for that are answered rather than fixed.
The caps furniture stays on the system stack: League Gothic is a poster
face and goes spindly at 11.5px, which is the opposite of what a field
label is for. The decision and its cost are now written down where the
labels are defined. And the contract itself was corrected — it still
promised a condensed uppercase title after h1 had been given sentence
case for a good reason, and it now names the two screens the
perforation appears on rather than implying every one.

The roll is corroborable outside its own claim: seed, catalog revision,
assigned index and the ordered list of seven are in the world payload.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 0d6acaf9e1edc51279ca88b2c8b20b51fcd970e8 parent 9030879
9 files changed, +123 −35
  • .impeccable/world-payload.json +25 −0
  • internal/contra/static/contra.css +65 −28
  • internal/contra/static/pages/vault.js +4 −0
  • internal/contra/static/theme.css +3 −0
  • internal/contra/templates/index.html +0 −1
  • internal/contra/templates/layout.html +11 −4
  • internal/contra/templates/signin.html +11 −0
  • internal/contra/templates/vault.html +0 −1
  • internal/contratest/jsmodules_test.go +4 −1
diff --git a/.impeccable/world-payload.json b/.impeccable/world-payload.json
index 7a57cff..f2dc1a4 100644
--- a/.impeccable/world-payload.json
+++ b/.impeccable/world-payload.json
@@ -1,4 +1,29 @@
{
+ "roll": {
+ "script": "concept-seed.mjs --scope direction --mode operate",
+ "seed": "bfd735a7",
+ "source": "api",
+ "catalog": "c3b204a1eed6 (306/564 human-approved)",
+ "assignedIndex": 7,
+ "reproduce": "node concept-seed.mjs --scope direction --mode operate --from bfd735a7 --candidate-count 7",
+ "note": "The assignment names a position in the grounded list below, ordered by resonance before the roll was run. Index 7 is the LEAST resonant of the seven, which is the mechanism working: the top-ranked direction is what every run would ship.",
+ "groundedCandidates": [
+ "1. The bearer instrument — share certificates and banknote engraving: guilloche, intaglio, serial in a fixed field, a counterfoil stub.",
+ "2. The deed box and safe deposit — japanned tin, brass plates, the two-key drill, numbered doors in a grid.",
+ "3. Chain of custody — tamper-evident seals, numbered bags, signature blocks recording who held a thing and when.",
+ "4. The DIN/ISO safety plate — hazard registers, torque tables, consequence stated at the point of action.",
+ "5. The archival finding aid and lab notebook — numbered pages, witness columns, accession numbers.",
+ "6. Ordnance Survey chart engraving — cartographic hairlines, a legend explaining every mark, 'depths in metres'.",
+ "7. The letterpress cloakroom check — numbered stubs, perforations, tally marks, coloured ticket stock, a hand-numbering machine. <-- ASSIGNED"
+ ],
+ "rerollPool": [
+ "clouds-storms-auroras-iridescent-cloud-edge",
+ "editorial-sequence-montage-botanical-folio-season",
+ "operate-c-cracktro-scroller-queue"
+ ],
+ "chosen": "assigned",
+ "chosenBy": "Paul, 2026-09-05, through the structured question tool (no browser on this host, so the decision page fell back as the script advises)"
+ },
"title": "Choose CONTRA's visual world",
"question": "CONTRA has been running rastrillo's `plain` placeholder. The roll assigned Left Luggage. Keep it, take an alternate, or re-roll.",
"options": [
diff --git a/internal/contra/static/contra.css b/internal/contra/static/contra.css
index 25b8d97..f85d3f8 100644
--- a/internal/contra/static/contra.css
+++ b/internal/contra/static/contra.css
@@ -102,14 +102,17 @@ a:hover {
/* The page is one piece of stock. tokens.css already centres the column;
this gives it the ticket's cut edge on the wide layout, where there is
ground either side to cut it out of. */
-/* The page is one piece of stock, so its cut edges run the whole sheet
- rather than stopping wherever the content happens to end — a rule that
- halts two thirds of the way down reads as an unfinished box, which is
- what the first canary round showed. */
+/* The page is one piece of stock, printed edge to edge.
+
+ It had cut edges of its own for one round. They were wrong: the console
+ shell already draws a rule down the side of the rail, and the page's
+ own border landed about 55px inboard of it, so every wide screen showed
+ two competing sheet edges with a dead strip between them. The rail's
+ rule is the structural one and it stays; the sheet is defined by its
+ ground and its rules, which is how a ticket is actually printed. */
[rst-page] {
- border-inline: 1px solid var(--rst-line);
max-width: 60rem;
- min-block-size: calc(100vh - 4rem);
+ min-block-size: calc(100vh - 8rem);
}
/* The brand is the ticket's overprint: the issuing office's name, set
@@ -180,12 +183,23 @@ form > p:not(:has(input, textarea, select, button)) {
a control boundary rather than a divider — 12.6:1 in light, well past
1.4.11's 3:1. */
+/* The caps furniture — labels, buttons, nav, status, the overprint — is
+ set in the machine's own UI stack rather than in the display face, and
+ that is a decision rather than an omission. League Gothic is a poster
+ face: at 11.5px with 0.12em tracking it goes spindly and its condensed
+ forms stop being legible, which is the opposite of what a field label
+ is for. A workhorse UI sans is what an Operate surface wants at that
+ size, so the ticket's character comes from case, tracking, weight and
+ the rules the caps sit on, not from a second self-hosted face. The
+ cost, accepted: the small caps render in a different typeface per
+ operating system while the display line does not. */
label {
color: var(--rst-text-muted);
display: block;
font-size: var(--rst-fs-xs);
font-weight: 600;
letter-spacing: 0.12em;
+ line-height: 1.2;
margin-bottom: 0;
text-transform: uppercase;
}
@@ -221,9 +235,12 @@ select {
inline-size: 100%;
max-inline-size: 32rem;
/* The value sits ON the rule, close under its label: a printed field
- is one mark, and the first round had the label floating half an inch
- above a rule it clearly did not belong to. */
- padding: 0.15rem 0 var(--rst-sp-1);
+ is one mark, and two rounds running the label read as a heading over
+ an empty line rather than as the name of the rule beneath it. The
+ distance is the input's own text box, so closing it means shortening
+ that box rather than removing margin there never was. */
+ line-height: 1.2;
+ padding: 0 0 0.1rem;
}
textarea {
@@ -323,14 +340,19 @@ button:disabled {
numbering machine only ever counts up, which is the same promise the
vault epoch and the item version make. */
-[data-number] {
- color: var(--contra-ink-2);
- display: block;
- font-family: var(--contra-display);
- font-size: 3rem;
+/* The step overprint. A ticket prints its sequence marker small, in the
+ second colour, above the fold — and signup genuinely is a sequence, so
+ this is the device doing its job rather than a number for decoration.
+ Small red is the negative ink, never --contra-ink-2, which is a
+ large-text colour: 6.13:1 in light and 8.67:1 in dark on the stock. */
+[rst-steps] {
+ color: var(--rst-tone-negative-fg);
+ font-size: var(--rst-fs-xs);
font-variant-numeric: tabular-nums;
- letter-spacing: 0.02em;
- line-height: 0.9;
+ font-weight: 700;
+ letter-spacing: 0.14em;
+ margin: calc(-1 * var(--rst-sp-3)) 0 var(--rst-sp-4);
+ text-transform: uppercase;
}
/* The handoff code IS a claim check: eight symbols, ten minutes, and the
@@ -382,15 +404,6 @@ button:disabled {
margin-block-start: 0;
}
-[data-number-label] {
- color: var(--rst-text-muted);
- display: block;
- font-size: var(--rst-fs-xs);
- font-weight: 600;
- letter-spacing: 0.12em;
- text-transform: uppercase;
-}
-
/* Opaque identifiers are machine-printed serials: the account id you
hand somebody, the build stamp, a handoff code. Monospace here is
measurement, not costume — these are hex and Crockford Base32, and
@@ -483,11 +496,16 @@ code {
/* ── Device 5: the stock code ─────────────────────────────────────────
Manila, and it means one thing: this vault belongs to somebody else
- and you are on the list. Colour as a category, never as decoration. */
+ and you are on the list. Colour as a category, never as decoration.
+
+ It is set on the open vault's root by vault.js, so a vault that belongs
+ to somebody else prints its whole contents on different stock — which
+ is the actual cloakroom rule, not a badge saying "shared". A person can
+ tell at a glance whose sheet they are writing on. */
-[data-shared] {
+[data-vault][data-shared] {
background: var(--contra-stock-2);
- padding: var(--rst-sp-1) var(--rst-sp-2);
+ padding: var(--rst-sp-4);
}
/* ── Lists: the ticket's line items ───────────────────────────────────
@@ -563,6 +581,25 @@ code {
text-transform: uppercase;
}
+/* The overprint that ends the ticket: small, faint, letterspaced, under a
+ hairline. A sheet with nothing at the foot is a sheet that just stops. */
+[data-overprint] {
+ border-top: 1px solid var(--rst-line);
+ color: var(--rst-text-faint);
+ font-size: var(--rst-fs-xs);
+ letter-spacing: 0.12em;
+ margin: 0;
+ padding: var(--rst-sp-3) 0;
+ text-transform: uppercase;
+}
+
+[rst-shell-foot] {
+ margin: 0 auto;
+ max-width: 60rem;
+ padding: 0 var(--rst-sp-4) var(--rst-sp-5);
+ width: 100%;
+}
+
/* ── The counterfoil ──────────────────────────────────────────────────
The server's half of the ticket: everything above the tear, printed on
the tint block the way a counterfoil is. It is the only place in the
diff --git a/internal/contra/static/pages/vault.js b/internal/contra/static/pages/vault.js
index 878d7ec..4e51997 100644
--- a/internal/contra/static/pages/vault.js
+++ b/internal/contra/static/pages/vault.js
@@ -156,6 +156,10 @@ async function showVaults(root, opened) {
async function openOne(vault) {
const store = itemStore(net, vault);
await view(root, store).load();
+ // Somebody else's vault prints on different stock — the cloakroom rule,
+ // not a badge saying "shared". Whose sheet you are writing on is the
+ // thing you want to know before you type, not after.
+ root.toggleAttribute("data-shared", !vault.owned);
// Sharing needs the vault key — it seals the keyset to somebody, and
// removing somebody re-seals every item — so it only appears once a
// vault is actually open, and it is rewired per vault.
diff --git a/internal/contra/static/theme.css b/internal/contra/static/theme.css
index 552e1bf..ce0ba5b 100644
--- a/internal/contra/static/theme.css
+++ b/internal/contra/static/theme.css
@@ -124,6 +124,8 @@
--rst-text on --contra-stock-2 11.06 4.5:1 text on manila
--rst-text on negative tint 11.09 4.5:1 stamp body
--rst-text-muted on negative tint 5.87 4.5:1 stamp body
+ negative fg on --rst-surface 6.13 4.5:1 step overprint
+ negative fg on --rst-surface-2 5.11 4.5:1 step overprint
pill neutral fg on its own tint 6.55 4.5:1 text
pill positive fg on its own tint 5.68 4.5:1 text
pill warning fg on its own tint 5.84 4.5:1 text
@@ -159,6 +161,7 @@
--rst-text on --contra-stock-2 11.06 4.5:1 text on manila
--rst-text on negative tint 14.09 4.5:1 stamp body
--rst-text-muted on negative tint 8.01 4.5:1 stamp body
+ negative fg on --rst-surface 8.67 4.5:1 step overprint
pill neutral fg on its own tint 8.98 4.5:1 text
pill positive fg on its own tint 8.77 4.5:1 text
pill warning fg on its own tint 9.27 4.5:1 text
diff --git a/internal/contra/templates/index.html b/internal/contra/templates/index.html
index a149ef6..85046fc 100644
--- a/internal/contra/templates/index.html
+++ b/internal/contra/templates/index.html
@@ -41,5 +41,4 @@ it — so treat every account here as one you could lose.</p>
<a href="/signin">Sign in</a>{{end}}
</p>
-<p>Build <code>{{.Version}}</code>.</p>
{{end}}
diff --git a/internal/contra/templates/layout.html b/internal/contra/templates/layout.html
index dd105a4..76a6cfd 100644
--- a/internal/contra/templates/layout.html
+++ b/internal/contra/templates/layout.html
@@ -30,9 +30,11 @@
fields instead of boxes.
STORY: The visitor learns the server cannot help them, believes
that is the protection rather than a gap, and prints the kit.
- FIRST VIEWPORT: Ticket stock; condensed uppercase title over a 3px
- ink rule; a ruled passphrase field; one perforation across the
- sheet with the sealed half below it.
+ FIRST VIEWPORT: Ticket stock; a condensed-gothic title in sentence
+ case over a 3px ink rule (capitals are for labels, not sentences);
+ ruled fields; the step overprint in the second colour; and on the
+ two screens that have a real client/server boundary — sign-in and
+ the vault — one perforation with the sealed half below it.
FORM: Left Luggage, 7th of 7 on the ordered list; seed bfd735a7.
FINISH: unreviewed and undocumented is unfinished; this build ends
with the finish review, the verdict, and DESIGN.md -->
@@ -130,7 +132,12 @@
sectioning ancestor is <body> is the contentinfo landmark, and one
inside <main> is a plain generic footer. Grid puts it under the
page column rather than under the rail. */}}
-<footer rst-shell-foot>{{block "foot" .}}{{end}}</footer>
+{{/* The overprint that ends the ticket. Every printed ticket says which
+ office issued it and carries a serial; this one carries the build,
+ which is the same fact GET /api/version reports to the platform's
+ deploy check. It moved here from index.html so it appears on every
+ sheet rather than only the front page — the same words, one home. */}}
+<footer rst-shell-foot>{{block "foot" .}}<p data-overprint>Build <code>{{.Version}}</code>.</p>{{end}}</footer>
</div>
</body>
</html>
diff --git a/internal/contra/templates/signin.html b/internal/contra/templates/signin.html
index b6fef46..2e51a5e 100644
--- a/internal/contra/templates/signin.html
+++ b/internal/contra/templates/signin.html
@@ -13,6 +13,17 @@ from it, and signs a challenge the server chose.</p>
<label for="email">Email address</label>
<input id="email" name="email" type="email" autocomplete="username" required>
</p>
+
+ {{/* The tear-line, and it runs through the middle of this form because
+ that is where the boundary genuinely is: the address above it goes
+ to the server, as a blind index it cannot reverse. The passphrase
+ below it does not go anywhere — it is stretched here and used to
+ sign a challenge, which is what the paragraph above already says.
+
+ This is the clearest place in the whole product to draw it: not a
+ diagram of the promise, the promise happening in one form. */}}
+ <div data-tear><span>goes to the server ↑ &nbsp;·&nbsp; never leaves this page ↓</span></div>
+
<p>
<label for="passphrase">Passphrase</label>
<input id="passphrase" name="passphrase" type="password"
diff --git a/internal/contra/templates/vault.html b/internal/contra/templates/vault.html
index 54976b7..5764793 100644
--- a/internal/contra/templates/vault.html
+++ b/internal/contra/templates/vault.html
@@ -100,7 +100,6 @@
issue a new recovery sheet. <a href="/device">This device</a> — approve
another browser, or give this one your Account Key.</p>
-<p>Build <code>{{.Version}}</code>.</p>
<form method="post" action="/auth/signout">
<button type="submit">Sign out</button>
diff --git a/internal/contratest/jsmodules_test.go b/internal/contratest/jsmodules_test.go
index 2fe8b54..f085004 100644
--- a/internal/contratest/jsmodules_test.go
+++ b/internal/contratest/jsmodules_test.go
@@ -56,7 +56,10 @@ var jsHighWater = map[string]int{
"pages/devicescan.js": 96,
"pages/kit.js": 156,
"pages/signup.js": 258,
- "pages/vault.js": 222,
+ // 222 -> 226 on 2026-09-05, deliberately: the open vault marks itself
+ // when it belongs to somebody else, so a shared vault prints on
+ // different stock. Four lines, one of them the statement.
+ "pages/vault.js": 226,
"pages/vaultshare.js": 139,
}