Rollout

Stage 12, closed by the founder on 2026-08-17. The rest of the product in colour, and the account of it: what was built, what each screen does with a wider window, and what is measured rather than remembered.

17 screens 55 pages, every one with a coloured twin 0 orphans on either side 0 screens carrying a style of their own 3190 width measurements 7 reading measures fixed 0 boxes moved at 360 8 rows opened, 8 closed

What this stage is, and why its account came last

The screens of this stage were built before the account of it was written, and across four stages rather than one. Stage 07 coloured a sample of seven screens to prove the language, stage 09 built Alerts out of the system alone to prove the system, the public page arrived on its own in August, and the remaining ten screens were coloured in one pass afterwards, because a screen is built once, when everything it needs already exists. What was missing was never the pixels. It was the thing a stage is judged on: a page of its own, a row per screen, and a number that a script can reproduce.

So this page reports rather than plans. Every figure on it comes from an instrument in design/kit/screens/, each re-runnable and each deriving its corpus from a registry rather than from a list somebody typed. rollout12.cjs reads the source; width12.cjs walks all 55 pages across 58 widths in a browser; shape12.cjs reads what each screen does between a real 360 and a real 1440; fp12.cjs fingerprints every box on both sides of a change; inert12.cjs re-asks the nine "checked rather than assumed" sweeps on the corpus that exists now. And the counter it inherited was extended rather than trusted: counts.cjs had read the CSS header alone since stage 11, while the same sentence lives twice, in the header and on the component's page. It reads both copies now, and the first run of the second column found 18 pages disagreeing on a corpus the old run called clean.

The corpus: 55 and 55, and nothing on either side alone

55grey pages in wireframes/
55coloured pages in design/
0grey with no coloured twin
0coloured with no grey original

Both counts exclude overview.html on each side, because a hub is a stand and not a product screen: the same basis the component counter and the inventory already use. The pairing is the whole of what "the rollout is built" means, and it is now a pairing rather than a claim: the two folders are read, matched by filename, and any file alone on its side is named. The count was 28 coloured pages when stage 09 closed and 32 when stage 10 did.

The audit row per screen, which stage 10 asked this stage for

Stage 10 wrote one row per screen saying what a wider window should give the person standing there, in three categories and a fourth that stayed empty: SAME, WIDER by grid, WIDER by air, and new behaviour nowhere. Ten of those seventeen rows had no coloured screen to check against at the time. Here is the built product measured against them, at a real 360 and a real 1440, with the scrollbar suppressed so the container is the number it says.

ScreenPagesStage 10 saidThe built screen doesPane 360 → 1440What changes arrangement
Welcome 1.11WIDER gridas planned360 → 1440the three steps 1→3, the footer 1→4, and six control rows unwrap
Path Choice 1.21SAMEas planned360 → 620the two doors 1→2 inside the column
Connect Bank 1.35SAMEas planned360 → 620nothing but the action row
Add a Subscription 1.44WIDER gridas planned360 → 620the preset tiles 2→3, and the grid is inside a 620 column rather than in a wide pane
Guided Reveal 1.52SAMEas planned360 → 620nothing but a two-item list
Sign In 1.63SAMEas planned360 → 620nothing
Home 2.65WIDER gridas planned360 → 1220the dashboard head 1→2, the doors 1→2, the groups 1→3
Subscription Detail 2.76WIDER gridas planned360 → 1220the screen itself 1→2: two columns of 546px, counted rather than placed
Alerts 3.84WIDER airas planned360 → 1220nothing structural: a list of events is rows, which is what the plan said
Cancel Guide 4.93WIDER airthe category was wrong360 → 1220the screen itself 1→2. Stage 10's own how-column said "point 900, two columns already" and its category said air. The note was right and the category was not
Cancel Win 4.101SAMEas planned360 → 620nothing. One number in the middle, which is the whole screen
Share Snapshot 4.113SAMEas planned360 → 620nothing
History and Trends 5.125WIDER gridwider by air360 → 1220the chart grows and nothing re-arranges. The plan's reason was "a chart is the one thing that genuinely grows with width", which is air: the word was wrong, the behaviour is the one asked for
Upgrade to Pro 5.134WIDER gridas planned360 → 760the plan row 1→3
Connections 6.144SAMEwider by grid360 → 1220the source doors 1→2. SAME was never available to this screen: it stands in the shell, and past the desktop point the shell gives its pane 1220
Data and Privacy 6.152WIDER airas planned360 → 1220nothing. Continuous prose, held at the reading measure
Settings 6.162SAMEwider by air360 → 1220nothing re-arranges, but the pane is 1220. The same finding as Connections, without the grid
The one thing the audit had wrong is worth more than the sixteen it had right, and it is not a defect in a screen. Three of the four disagreements are the same disagreement: an app-frame screen cannot be SAME. A flow screen grows from a phone to --container-column, 620, and stops, which is exactly what "SAME, container" meant. A screen inside the shell is handed the pane, and past the desktop point that pane is 1220 whatever the screen would prefer. Connections and Settings were written SAME in a table whose other seven shell screens were all WIDER, and no instrument existed to catch it because neither screen had been coloured yet. The fourth, Cancel Guide, was a category that contradicted its own note. New behaviour is still zero, on all seventeen, and that half of the audit holds exactly as written.

A screen carries no style of its own, re-proved on all 55

The rule in CLAUDE.md is four bans and one of them is the width rule stage 10 asked this stage to re-check: no style block, no style attribute, no @media, no class the system does not define, and no width literal anywhere in a screen file. Stage 10 proved it on 32 pages. It is re-proved here on all 55, by script, over the source rather than over the render.

What was looked forFoundWhere
<style> blocks0all 55 coloured screen files
style="" attributes0all 55
@media and @container0 and 0all 55
a width declaration of any kind0all 55. width, min-width, max-width and inline-size, comments stripped first because a screen is allowed to talk about width
stylesheets besides system/index.css and _screen.css0all 55
classes the system does not define0all 55, matched whole against every selector in design/system/, the reviewer chrome, and the three classes a script writes
links leaving the colour for the frozen grey0 of 581the first sweep, run on a hunch, found four. They were fixed on 2026-08-17 and the check is standing now
links whose target does not exist0 of 581every internal href resolved against the file system
Zero was the expected answer and the check is still worth its run, because the previous zero was measured on a corpus 23 pages smaller and the twenty screens built since got their markup for the first time. A ban nobody re-runs is a ban that was true once.

The width column, recounted on the whole system

Every row of docs/inventory.md carries a "behaviour at width" cell, and an empty one would mean "behaves unknown on a wide screen". Stage 10 filled it on 60 entries and left a note saying the count would be re-taken when the corpus stopped moving, which is this stage. The corpus is 72 now: 69 component files and 3 patterns.

How it adaptsStage 10, on 60Stage 12, on 72Which
a point, one of the two registered59app bar, category group, chart placeholder, dialog sheet, groups column set, tab bar, landing shell, landing final, list column
point + fluid46app shell, dashboard head, grid, landing bar, landing plan, landing steps
point + fluid + a reading measure05faq list, landing facts, landing paths, landing story, site footer
a container threshold + fluid22save-focus candidate, subscription row
container thresholds + fluid + a measure01landing orbit, and its three local thresholds ask a named container rather than the page
fluid only44action row, alert item, meta row, merchant chip group
a reading measure, and no query at all712readout, text block, empty block, muted line, wash block, promise list, landing hero, and the five this stage added: card, form field, numbered steps, panel, share card
a cap and no query10the card was the only one and it is in the row above now: 560 stayed as the card's width and the prose inside it took the measure
does not adapt3733the rest, and nothing is written on their pages: an empty "does not adapt" note on thirty-three pages is noise
Every one of the twelve entries added since stage 10 adapts, which took the adapting half from 23 to 35: eleven landing organisms and the FAQ list, all built for a page that is one long column on a phone and a laid-out page on a desk. The fixes at the foot of this page then moved four more, so the final reading is 39 adapt and 33 do not: the form field, the numbered steps, the panel and the share card each declare a reading measure now where they declared nothing, and the card moved from a bare cap to a measure. The instrument reproduces every verdict stage 10 wrote by hand, which is what makes the two columns comparable, and getting there needed one distinction: flex-wrap: wrap means the arrangement answers the width, flex: 1 means the component divides whatever it is handed and arranges itself the same way at 320 and at 1920.

Nine cells were wrong and are corrected. Eight of the twelve new entries had their width cell filled with what the organism is rather than what it does with width, which is the column answering a different question; the ninth, the FAQ list, said "does not adapt" while its file carries a desktop point and a reading measure. Both are in docs/inventory.md now, and the prose that was displaced is under the organisms table rather than deleted.

The width sweep: 55 pages by 58 widths, and what it caught

Stage 10 swept 32 pages and its argument for sweeping rather than sampling was exact: a defect lives between the points, and the worst width is the one where the content has stopped fitting and the query has not fired. Twenty-three pages had never been walked. The run is 3190 measurements, and it drives the browser itself rather than asking a person to press Run.

0documents scrolling sideways
0panes scrolling sideways
0elements leaving the viewport
0pages changing the number of navigation carriers

Two of those four are not the clean answers they look like, and both are worth the sentence. The pane check is the one that found the 28px band at stage 10, where a layout scrolled sideways inside .screen while the document stayed clean; it is asked of every scroll container on the page here, and it is silent on all 55. And the landing runs 128 elements past the window on purpose: a marquee track is wider than its box and hidden by it. The first run of the sweep reported all of them at all 58 widths, which is a fault in the instrument rather than in the page, so a clipped element is now counted separately instead of being reported or silently dropped.

What it did find is prose, and all six are fixed. Six blocks read past the system's 52ch measure, four of them on screens that had never been swept. Each fix is a measure on the element that holds the words, in the component that owns it, and the re-run reads zero prose leaves past 52ch anywhere in the product, worst 52.0:

WhereReadAtWhat it was, and what closed itNow
cancel-guide, a step81.2ch750.steps li had no reading measure at all: the worst line in the product, on the one component whose brief is an instruction being followed on another screen. `numbered-steps.css`, and it is the system's one computed width: calc(var(--container-text) + var(--space-40)), because border-box would otherwise let the numeral's gutter eat eight characters52ch
index, the story head's lead80.2ch720.lp-story.fromcircle .storyhead capped itself at 38rem, a raw literal in no register, and the lead inside it is two type steps smaller, so the same box was 52ch for the heading and 80 for the sentence. `landing-story.css`: the literal is replaced by --container-column, which the same head is already given in the stage form, and the lead took its own measure52ch
connect-bank, the email hint77.6ch640.field .hint had no measure. Six of the twenty fields carry a fragment and this one carries a paragraph, on the screen where a person decides whether to trust us. `form-field.css`, and at --type-meta the same token is 394px rather than 459: ch follows the font52ch
cancel-guide-blocked, a step60.8ch760the same rule as the first row52ch
history-trends-locked, the prose card59.5ch600the card's 560 against the system's 52ch, both deliberate and disagreeing, and open since stage 10 as a founder's question. `card.css`, and it is the first of the two answers it was given: 560 stays as the width of the CARD, the paragraph and the list items inside take the measure52ch
share-snapshot, the privacy paragraph58.3ch640a bare p inside .oncard, a one-off with no component and therefore no measure. `share-card.css`, where the eyebrow already reaches into the same block. A measure is not an owner: the one-off row stays open52ch
cancel-guide, the Pro gate's sentence77.3ch750The seventh, and it was uncovered by fixing the first. The sweep reports the worst leaf per page, and this one stood behind the 81.2ch step on the same screen. A chain rather than a rule anybody broke: the panel takes the screen's width, the gate takes the panel's, and nothing between the words and the shell had ever been asked how long a line should be. `panel.css`, five gates on five screens52ch
Two of the six carry a lesson the token cannot fix by itself. --container-text is 52ch, and ch is a unit of the element's own font: a 52ch cap set on a container at 16px does not give a 12px line inside it a 52ch measure, it gives it about 69. The story head is that exact shape, and it is the argument for putting the measure on the line rather than on the box around it. All seven are fixed, on the founder's word, and the pixels are accounted for both ways. At 360 the difference is zero on all 55 pages, box for box, which is the half of the promise that is not allowed to move and which holds by construction: every one of these caps is wider than a phone. At 1440, 16 pages and 137 boxes changed, and every changed WIDTH is one of the seven rules - 32 boxes, five selectors - while the other 105 are boxes that grew a line taller or moved down under one that did. Nothing else in the product moved.
And one more measure was restored the same day, by a rule change rather than by a fix of its own. The shell releases every direct child of the screen at the desktop point, and it did it at 0-2-0, so a component that declared its own measure at the same weight lost on source order: an organism is imported after an atom. Six elements had been repaired one at a time as somebody happened to measure them. Putting the blanket inside :where(), at specificity zero, found the seventh: dialog-sheet.css has said 480px, centred, under the 620 the flow column takes since stage 08, with the reason written on the rule, and the sheet had never once rendered it past the tablet point - it ran the full 588 column on all three of its screens. Zero boxes moved at 360; three pages and 49 boxes moved at 1440, every one of them that sheet. The change restores what the component already says rather than deciding anything new, and it is one line to revert.

Every row this stage opened, and where it went

The stage began with eight rows addressed to it, four inherited from stages 09 and 10 and four found by its own instruments. All eight are closed, and three of them closed as a decision rather than as a fix, which is the distinction worth keeping: a row that is answered "no, and here is the measurement" is not the same as one quietly dropped.

WhatStateWhere it closes
Seven prose blocks past the reading measure, aboveclosedfive component files: numbered steps, form field, card, panel, share card. Zero leaves past 52ch in the product now
38rem, a width literal in no register, in landing-story.cssclosednot registered but replaced, by the --container-column the same head is already given in the stage form. The product now holds no unregistered width number
.oncard and .lead are one-offs with no component, and both now have a measure declared by a neighbourclosed as a decisionand the answer is that neither is owed one. .oncard has a single host. .lead turned out to carry three meanings, not two: .muted.lead is a declared modifier of the muted line on 9 coloured pages and keeps the word, and bare .lead stands on exactly two pages in two different hosts with two different rules, which by rule 1 of the inventory is two host slots. Promoting a name three rules disagree about is how a collision becomes a component. The true fix is a rename, and a rename is markup behind the freeze
The focus order in the rail form: the rail's head takes focus first and its four destinations last, with the whole screen between themclosed as a decisionand the measurement is what closed it. Re-read across all 55 pages at 360 and at 1440: every page tabs bar, then content, then navigation, and not one page changes its order with width. So there is no width-dependent defect - there is one order, and it is content before navigation, which is the order a skip link exists to create artificially. Reordering the DOM would put four destinations in front of the first word on a phone. A skip link stays available and belongs to Voice first, because it is a new product string
The shell's blanket on .app > .screen > *, which had unbound six measures one at a timeclosedand closing it found the seventh. The named list this row asked for was tried and abandoned - it is fourteen selectors long and one member is a bare <div aria-hidden> that must widen - so both blankets went inside :where() instead, which is specificity zero: anything that declares a measure of its own now wins, anything silent still gets the pane. Zero boxes moved at 360; three pages moved at 1440, all of them the dialog sheet, which has declared 480px since stage 08 and had never once rendered it past the tablet point
The count-drift row: every per-component figure written by hand in forty placesclosed in partthe instrument arrived at stage 11 (counts.cjs) and its trigger was "when stage 12 stops adding screens". It has stopped: 0 per-component claims disagree with the corpus, and the width column, this table, the README and the two hubs are recounted. What is not closed is the row above it: the sentences on the component pages still describe a 28 or 32 page product
The component pages described a 28 or 32 page coloured corpus, 48 times across 24 filesclosedand the pass found a hole in the counter first: counts.cjs had been reading the CSS header alone, while the same sentence lives twice. It reads both copies now and found 18 pages disagreeing where the run had been saying zero. All 18 are recounted with their screen lists regenerated; of the other 28 occurrences, 19 were live claims and are recounted and 9 are stage records that now name the corpus they were measured on. The nine "checked rather than assumed" sweeps were re-run rather than re-typed (inert12.cjs, both corpora, 55 pages each) and every one still reads zero
Two pages in design/kit/ were in no registry, kit.html and shell.htmlclosedboth are in the System group of design/kit/_nav.js now, as "The screen template" and "The flat kit, stage 07". The count reads 85 of 85. The other half of kit.html's old row closed with it: a stand owns its chrome, which is the licence design/overview.html already carries in writing and this page uses too
Three things are left to the founder and none of them is a defect. A skip link would improve the focus order and it is a new product string, so it is Voice's before it is anybody else's. A rename is the only true fix for a class name that carries three meanings, and a rename is markup, which the freeze puts behind a decision written into the file it changes. And the dialog sheet's 480px, which this stage restored after eight stages of never rendering, is one line to revert if the wider sheet was the wanted look rather than the accident it appears to be.