# Backlog - what this stage found and deliberately did not fix

Opened at the molecule round of step 5, earlier than the pack schedules it, for one reason: the
findings were arriving faster than the steps that own them, and a finding with no home is a
finding that gets lost. Stages 09 to 13 read this and close what their own material closes.

## Where this file stands

**Counted on 2026-08-23 by `design/kit/screens/handoff13.cjs`, section 7, and not by hand: 114
table rows, 94 saying closed, 30 open by the reading that a row with anything unfinished in it is
an open row, and 0 addressed to a stage that has ended.** The instrument
is the owner of these five numbers from today; everything below is the history of how the file
got here, each block keeping the corpus it was counted on. Re-run it rather than trusting this
paragraph a month from now.

**Four rows landed after that run, on the evening of 2026-08-23, with node 6.17**, and a re-run
of the same instrument answers **111 rows, 80 saying closed, 41 open, 3 addressed to stage 13**.
The paragraph above keeps the corpus it was measured on, which is the rule; this sentence is the
recount, in the step that changed the file.

**Plus 1 row on 2026-08-26**, the first one this file has taken from outside its own repository:
the CSS pipeline of the product build, which refuses a `url()` it cannot resolve. Re-run of the
same instrument in the same step: **115 rows, 94 closed, 31 open, 5 naming the founder as owner
and 1 of those still open.** The two paragraphs above keep their own corpora.

## How the count got here, counted on 2026-08-12 after step 9

**54 rows. 20 closed. 34 open. 0 rows dropped at verification.**

**Plus 4 rows added on 2026-08-13 by the self-sufficiency test of stage 09, 1 of them closed
the same day: 58 rows, 21 closed, 37 open.** They are in their own section below rather than
merged into the tables above, because they were found by a different instrument: not an audit
of what stands, but the first screen built out of the system after it was declared finished.

**Plus 2 rows added the same day by the verification pass of step 6, 1 of them closed on
arrival, and 1 row above closed by it: 60 rows, 23 closed, 37 open.** The pass ran on two
instruments and produced twelve findings; eleven of them were one defect repeating, so they are
carried as one row about the mechanism rather than as eleven about its symptoms.

Counted by walking every table below, not estimated: 2 blockers, 4 decisions, 6 holes, 19 from the
molecule and organism rounds, 5 still open on the components, 6 from the second control census, 5
owned by Voice, and 7 added today by step 9. The dry-run table is a measurement rather than a
queue and its five screen rows are not in the 54.

**One row is closed in part and is counted as open**, because a row with anything unfinished in
it is not a closed row: `kit.html`, closed on the variables and open on the style block that was
scheduled out of the page at step 4. The second such row, the chip tones, closed the rest of the
way on 2026-08-13 when step 6 corrected the comment that was its open half. One further row, the button that may not wrap, is marked
**corrected** rather than open or closed: the rule stands, the numbers were withdrawn, and it is
counted as open. What WAS dropped at verification in this stage never had a row here and is named
in the last section instead, so that the next audit does not raise it again.

**Every row was re-read against the current files today**, after the source pass by Codex, the
browser pass, and the three batches of fixes in `e694d2c`, `2ce7fe5`, `437943b`, `43cc778` and
`ce9771d`. A row that only carried a state written weeks ago now carries the state it measures
today, and where the two disagree the disagreement is written into the row rather than quietly
overwritten.

**No row was dropped whole today, and two measurements inside surviving rows were withdrawn.**
The 213 against 225 overflow pair was withdrawn on 2026-08-12 and its row survives on the
principle. Today the four-atoms row lost three of its four numbers to a re-read: `amount.css`
sets four host rules but only two distinct sizes now, `logo.css` sets five host-keyed sizes and
not four, and `muted-line.css` sets one and not two. The principle under that row is untouched,
so the row stays open and its evidence is corrected in place.

**Nothing open here is a defect on a shipped screen.** One was, for part of 2026-08-12, and it is
recorded in the section on the second control census with its measurement and its repair rather
than deleted: this file exists to be trusted, and a defect that is written down only while it is
unfixed teaches the next reader that the file is a to-do list rather than a record. Everything
else is either a decision that needs a person, or work whose correct owner is a later step. Each
row says which.

**Closed rows are kept and marked rather than deleted.** A row that vanishes on the day it is
fixed leaves the next reader unable to tell a solved problem from one nobody ever found.

---

## Blockers, and both are now closed

| What | State on 2026-08-12 | Who owned it |
|---|---|---|
| **Nothing in `design/system/` established a container.** `container-type: inline-size` was declared in exactly two places, `wireframes/_wf.css:35` and `design/kit/kit.css:145`, both on `.stage`, which is the reviewer's frame and travels to no build. Three of the product's layouts are container queries: the tab bar's rail at 760, the dashboard head at 900, the groups column set at 1340 | **CLOSED at the organism round, and closed twice over, because the stand needed its own answer.** `base.css:141` declares it on `body`. Not on `.app`, which was the obvious answer: a query matches an ANCESTOR container and never the element itself, so `container-type` on the shell cannot style the shell, and measured, the tab bar went from a 240px rail beside the content to 965px below it. Zero pixels move, checked at 33 widths in 8px steps through all three breakpoint bands before the change was made. **The stand takes the same rule at a different level**: `_page.css:572` puts `container-type: inline-size` plus `contain: layout` on `.kit-stage`, because `body` is right for a build and wrong for a stand, where every specimen was showing its desktop form inside a narrow plate. `design/_screen.css:149` carries the third, on `.stage`, for the review frame around a product screen. **Re-read after step 9 and still closed**: `design/kit/kit.css` no longer exists, so the second of the two original sites went with the file at step 8 | Closed |
| **Two placeholders still ship as literal text in colour.** `history-trends-loading.html:67` renders `[chart]` and `history-trends-empty.html:61` renders `[chart: waiting for a third month]` | **CLOSED at step 6, verified in a browser rather than in the diff.** Both now draw the same three-line frame the other two chart screens draw, and the empty state keeps its accessible name ("An empty chart frame, waiting for a third month of history"). Walked at 360 and 1280 on all four History and Trends screens: **no `[chart` string renders on any of them**, and the frames measure 328x168 at 360 and 740x220 at 1280. It also corrects the row on the stage 07 hub which records "3 found, 3 fixed, 0 carried"; that row was wrong and is now true | Closed at step 6 |

---

## Decisions that need a person

| What | The options, honestly | Who decides |
|---|---|---|
| ~~Should a card that cannot be clicked answer a pointer at all? `.plan-opt` is a `div` holding a `button`: it cannot be focused, cannot be pressed and has no accessible role, and yet its edge strengthens under the pointer~~ | **OPEN, narrowed, and measured again today.** The token half is decided: `--line-container-hover` is declared on `--hairline-strong` with a computed dark pair, because dropping a state is a visual change and none of the stage's named sources asked for one, so carrying it was the only move that needed permission from nobody. Measured on `design/upgrade.html` at 1280: the card's edge goes `rgb(228, 233, 234)` to `rgb(205, 215, 217)` under the pointer, and nothing else on the card moves. What is left is the taste question underneath, and it is smaller: a hover that promises a click the card does not deliver. **Step 9 touched nothing here** | **CLOSED 2026-08-20. No.** The hover is gone; `--line-container-hover` had no other reader so it went too, and `--hairline-strong` with its dark pair had no role so they went as well. Three deletions, one decision. Measured before removal on `design/upgrade.html` at 1280: the edge went rgb(228,233,234) to rgb(205,215,217) and nothing else on the card moved |
| **Non-text contrast on the remaining surfaces.** Card, panel and divider edges sit at 1.23:1 against the 3:1 of WCAG 1.4.11 | Already decided once, on 2026-08-11: the darker edge is scoped to controls and surfaces keep the hairline, because raising `--line` outlines the whole product and the language was chosen against that. Recorded here so a later audit does not reopen it as new | Closed, kept for the record |
| **~~The icon set is optically unbalanced.~~ CLOSED 2026-08-21.** Painted boxes differ by 22 percent and INK by 106 percent: `.ic-save` carries 2.06 times the paint of `.ic-you` in the same tab bar | **OPEN, unchanged.** Measured, not eyeballed, at the icons page, and not re-measured at step 9 either, because nothing in step 9 redrew a glyph: the step's only icons work was accounting for the twelfth mark on `icons.html`, which added a row and no geometry. The repair is redrawing a glyph, which is `design/system/` work and belongs with whoever owns the set | **CLOSED 2026-08-21, and it took two moves, only one of which was a drawing.** The finding opened at 106 per cent of ink spread over four icons and was carried as the founder's for eleven days. **Move one was a deletion:** the Save tab retired the same morning, `.ic-save` went with it, and the spread fell to **81 per cent** with no line redrawn - which is what said the problem was the SET rather than any one mark. **Move two was the founder choosing candidate B** off four drawn and measured options: `.ic-trends` gave up its frame for an axis corner, **23.82 to 14.71 per cent**, and the spread is now **61 per cent** with the house as the heaviest mark. Ink over the set as it ships: home 21.12, alerts 15.62, trends 14.71, you 13.14, rasterised at 40 samples per module of the 24 cell with an alpha floor of 10. **The candidate drawn to be rejected is on record and it is the obvious one:** simply deleting the box takes the chart to 10.60, makes it the lightest thing in the bar and the spread **99 per cent**. The four candidates and their numbers are in `design/kit/screens/icon-candidates.html`. Option E, which also drops the house's door and measures 25 per cent, stays available as one decision if the row ever needs to come tighter | **Closed**
| ~~The Trial chip has a rule and no wearer in colour. `chip.css:135` declares `.chip.trial` on `--bg-trial` and `--text-trial`, which closes the census's oldest hole: the variant used to have no CSS rule anywhere. But the one place in colour that says Trial does not wear it~~ | **OPEN, new shape of an old row, and step 9 raised it independently.** Measured on `design/home.html`: the chip on the Peloton row renders `rgb(233, 238, 239)` on `rgb(79, 94, 98)`, the quiet tone, because its markup is `class="chip"`. `chip.css:135` still carries the rule. The trial pair is therefore two semantic roles read by exactly one rule that nothing in colour wears, and one grey wearer on the landing. `pixel-proof.html` carries the same row as "A role that has never rendered", found by the browser pass. Two honest answers: the Home row wears `.chip.trial` and the pair earns its place, or the pair goes the way `.tone-attention` went and a trial is a quiet chip like every other status | **CLOSED 2026-08-20. Retired.** D-Concept: a status is a quiet gray badge and a trial is a status. Re-counted the same day - **zero `trial` in any class attribute on any of the 110 pages**, so this row's claim of a grey wearer was wrong too. `--bg-trial`, `--text-trial` and the four teal primitives under them went with the rule. Zero pixels: the Peloton row already rendered quiet |
| ~~The landing has two doors and the second step has one. `index.html` offers "Get started free" (to `path-choice`) and "Sign in" (to `sign-in`), in the header and again at the foot, and `sign-in` leads back with "No account yet? Start here". `path-choice` leads back to neither: a person who taps the wrong door on the landing has the browser's back button and nothing on the screen~~ | **OPEN, raised by the founder on 2026-08-19** ("чуть смущает, что у нас sign in одно, а get started другое") and verified before it was written down: the pair IS deliberate and it is the product's model - value before an account, so "Get started free" starts using Tendd and "Sign in" picks up a list you already have. `voice/docs/microcopy.md` owns all three strings and node 1.6 was added precisely because the product had a door in and a door out of an account nothing in it created. **What is not decided is whether `path-choice` owes a way across.** Two honest answers: it does, and gains one quiet line under the two doors, which is a new product string and therefore Voice's and the founder's; or it does not, because the fork is one tap from the landing and a second exit on the first screen of a chain is the kind of clutter this product refuses. Nothing was changed on either ground | **CLOSED 2026-08-20. It owes one and now has it.** `path-choice` carries one quiet line under the retreat: "Already have an account? Sign in". Under the retreat rather than beside the doors, because it is for the person who is in the wrong place and not an option for the person who is in the right one. Both corpora; the founder's decision is written into both files |

---

## Holes in the system, each with the reason it was not filled

| What | State on 2026-08-12 | Who closes it |
|---|---|---|
| ~~The numbered-steps marker is 26 by 26 and no primitive holds that size. `--tap` and `--row` are the only size primitives and it is neither; a width is not a margin, a padding or a gap, so the 8px grid does not govern it~~ | **OPEN, and step 9 has now run without creating a size scale.** Re-read today: `numbered-steps.css:93` still declares `width: 26px; height: 26px`, and `:86` still carries the reason at the line. `tokens.css:175` and `:176` are still the only two size primitives, `--tap` 44 and `--row` 56. Step 9's geometry triage answered the neighbouring question instead, `--ring`, and refused this one on the same ground the row already gives: a size primitive for one marker is a token nobody else reads. Worth knowing before it is answered: `.steps` stands on **zero** of the 28 coloured screens and on **3** grey pages (`cancel-guide`, `cancel-guide-blocked`, `cancel-guide-no-guide`), counted today, so nothing a person can open today is affected either way | **CLOSED 2026-08-20. No primitive.** One wearer, on 3 coloured screens and 3 grey. A size primitive one file reads is a token nobody else can be wrong about, and the reason already stands at the line |
| **The spacing fold moved the step marker's text.** `padding-left: 36px` goes to `--space-40`, which opens the marker-to-text gap from 10px to 14px and shifts every step's text 4px right | **OPEN, and step 8 could not see it, exactly as this row predicted.** The arithmetic re-checked today in the file: `numbered-steps.css:67` reads `var(--space-40)`, `tokens.css:258` declares `--space-40: 40px`, and the marker is 26px, so the gap is 40 minus 26, which is 14 against the old 36 minus 26, which is 10. The pixel comparison at step 8 ran on the 28 coloured screens and `.steps` occurs on none of them, so the difference is absent from all three rounds on `pixel-proof.html` by construction rather than by oversight. It still has to be checked on `design/kit/numbered-steps.html` or on the grey, by hand. This row and the one above it were carried twice in the first draft of this file, because two agents found the same marker independently; they are one row each now, and that two instruments found it is kept, because it is the evidence that the marker is worth a decision rather than a shrug | **CLOSED 2026-08-18, as recorded rather than repaired**, the third row in this file to close that way. It has shipped and it is measured: `padding-left: 40px` against a 26px marker box on `design/cancel-guide.html`, so the marker-to-text gap is exactly the 14px this row predicted. `numbered-steps.css` carries the same sentence at the rule - "THAT MOVES A PIXEL AND IT IS EXPECTED" - so the change is named where it lives. A row kept open for a change that was named, expected and delivered implies an undone fix |
| **A seam is two hairlines thick.** The last `li` of a charge list keeps its bottom rule and the `.gate` that follows it inside the same panel draws a top rule | **CLOSED at the founder's review of the components.** `charge-list.css:104` gives `.charges li:last-child { border-bottom: 0 }`, applied as a principle rather than to the seam that exposed it, which is what found the second seam under it. The panel is 1px shorter on the five subscription-detail screens and nothing else moves | Closed |
| **~~Eleven ARIA and form-semantics findings from stage 07.~~ CLOSED 2026-08-20, and the count was wrong because the corpus had doubled.** Founder: "давай чинить дыры какие есть". The eleven were measured on 28 coloured pages and the product is 55, so they were not recounted, they were **re-asked**: `design/kit/screens/aria13.cjs` puts six questions to all 110 pages - a fieldset with no name, an `aria-label` on an element ARIA discards it on, a wait that does not declare `aria-busy`, a form control with no accessible name, a link or button with no accessible name, a heading level skipped. | **18 findings in 18 pages, 9 per corpus, and 2 of the 6 questions came back clean on the first run**: zero form controls and zero controls without an accessible name, across 110 pages. The other four: 6 fieldsets with no name (the three states of Add a subscription in both corpora), 8 `p.total` carrying an `aria-label` that ARIA discards because `paragraph` takes no name from the author, 2 waits not declaring `aria-busy` (`home-loading`, the twelfth finding), 2 heading levels skipped (the landing footer, h2 to h4). | **All 18 fixed, all 6 questions now 0.** The fieldsets are named with `aria-label` and not a `legend`, because a fieldset is role `group` and takes a name from the author, and a legend is a VISIBLE heading this form was designed without. The discarded labels came off rather than being moved: the sentence above the figure and the one under it already say what it is. The footer headings became h3 and `site-footer.css` stopped binding the look to the LEVEL, which is the defect `group-head.css` was broken by twice. | **Closed.** rollout12 none, width12 none over 55 x 58, quality13 at its 24 baseline, pages13 0 of 128 |
| ~~The `.switch` checkbox is 20px against the 24px of WCAG 2.5.8. Its wrapping label is the real pointer target at 44px or more, so the raw control is not the target~~ | **OPEN, unchanged, and step 9 counted it a third time.** Recorded rather than resized on 2026-08-11: a kit value changes only by a named decision, and this one has a defensible reading. The row's argument got stronger this round rather than weaker: the focus ring now lands on the label too, so the 20px box is no longer the target for a pointer, for a keyboard or for the eye. `pixel-proof.html` reports "tap targets under the floor: 0 in the product", and names this checkbox as the one flag it set aside for exactly this reason | **CLOSED 2026-08-20. The reading is recorded and the control stays 20px.** WCAG 2.5.8 measures the TARGET and the target is the wrapping label at 44px or more, which since stage 09 also carries the focus ring. Resizing a control that is not the target, to satisfy a rule about targets, helps nobody |
| **Four atoms are sized by their hosts, which is the undeclared variant `CLAUDE.md` forbids by name.** `amount.css` sets four different sizes keyed by `.row`, `.charges li`, `.plan-opt` and `.hero`; `logo.css` sets four keyed by `.hero`, `.alert`, `.tile` and `.rgroup li`; `muted-line.css` two keyed by `.tile` and `.row`; `big-total.css` one keyed by `.sharecard` | **OPEN, the principle holds, and THREE OF THE FOUR NUMBERS DID NOT SURVIVE RE-READING.** Corrected in place rather than carried: `amount.css:70, 75, 80, 88` are four host rules but only **two distinct sizes**, `--type-body` on `.row` and `.charges li` and `--type-figure` on `.plan-opt` and `.hero`, the rest of each rule being weight and line-height. This is the same correction the step 9 first batch made to the registry, "amount 13.5/14/30/32 (it is 14/32)". `logo.css` sets **five** host-keyed sizes and not four: `.hero` 52, `.card .top` 36, `.alert` 32, `.tile` 30, `.rgroup li` 22, at lines 82, 90, 83, 84 and 95. `muted-line.css` sets **one** and not two, `.tile` at line 148; there is no `.row .muted` rule in any component file. `big-total.css:56` on `.sharecard` is the only one of the four that measured as written. Step 9 did not execute the rename: naming these means declaring a size axis and writing the class into the markup of both corpora. The visible cost is unchanged: seven of the eighteen atom pages host a higher rung because an atom page cannot show its own size axis without importing a molecule | **CLOSED IN PART on 2026-08-14, at the rollout, and the part that closed is the one that forced the question.** Founder: the axis exists. `amount.css` now declares `.amt` at `--type-body` on the base and `.amt.figure` at `--type-figure`, and all four host font-size declarations are gone; what stays with the hosts is `flex: none` on the row (placement), weight 400 on a past charge (history is a texture, not a size) and the two leadings, 1.05 in a plan card against 1.08 in the hero, which is what a container asks for. **Zero pixels:** all 66 amounts in the coloured corpus snapshotted before and after on size, weight, leading, tracking, display and box, identical. What forced it was stage 12 needing a FIFTH host, cancel-win's freed figure, and adding one more occurrence of a rule the system forbids is not a way to build a screen. **Still open: `logo.css` (five host sizes), `muted-line.css` (one) and `big-total.css` (one).** The same answer applies and the same evidence is owed | **Founder** decided the axis; **logo, muted line and big total** remain |

---

## Found at the molecule and organism rounds, and each one is a decision rather than a bug

| What | State on 2026-08-12 | Who owns it |
|---|---|---|
| **The switch row has no hover of its own.** `kit.css` gives `label.switch` none, and the focus ring lands on the checkbox inside it because a label is not focusable | **CLOSED, both halves, at the founder's review of the components.** `switch-row.css:159` gives the row `--bg-hover`, the same role every other row in the product answers with, and the four rows on Settings keep their heights to the hundredth. Measured on `design/settings.html`: the row's fill goes transparent to `rgb(244, 247, 248)` under the pointer, the same as the subscription row and the nav row | Closed |
| ~~`.cut` is now `.btn` minus two things. Measured on the rendered page: identical `min-height` 44, `padding: 0 16px` and `font-size: 14px` after the fold. It differs only in `font-weight`, 400 against 600, and in a hover that changes edge and ink but not fill~~ | **OPEN, and step 9 did not merge it.** Re-read today: `save-focus-candidate.css:37` still says in its own header "THE CLASS NAMES ARE NOT TOUCHED", and the six rules at `:64`, `:88`, `:101` and `:126` still stand under `.cand .cut`. The census still shows `.cut` as its own family, 2 controls, one value on all seventeen properties. Whether it merges is still a rename question, and a rename with no row on the map is a rename nobody executes. The map still owes it a row | **CLOSED 2026-08-20. Merged.** `.btn.soft` is declared in `button.css`: font-weight 400, and a hover that keeps the edge and the ink and refuses the fill, because a fill under the pointer on the one control that ends a subscription reads as encouragement. Six rules left `save-focus-candidate.css`; two stayed, and both are the host's. **The modifier was called `.quiet` for an hour and `counts.cjs` caught it**: `.quiet` is already the Quiet line component on 14 pages. Zero pixels, measured at 360 and 1440 in both themes |
| ~~"Sign out" wears the nav row class with no modifier. It is the one row in the product that leaves rather than goes deeper, and nothing in the markup says so~~ | **OPEN, unchanged.** `design/settings.html:123` still reads `<a class="navrow">` with the same chevron every other nav row carries. Census case 9. A variant, or a deliberate sameness with a reason written down | **CLOSED 2026-08-20. It has one.** `.navrow.leave`, and what the modifier does is drop the chevron. A chevron means "there is more this way" - it opens a disclosure twice on that same screen - so drawing it on the control that ends the session says the opposite of what the control does. The mark left the markup rather than being hidden by CSS |
| ~~`.cand` is recorded as a molecule while hosting a molecule, which the ladder calls an organism~~ | **OPEN, unchanged, and step 9 re-read the inventory without moving it.** `inventory.md:146` still files Save-focus candidate as a molecule and the level is noted on its page. Moving it changes the `@import` order in `index.css`, which is the reason it was left alone mid-round and is still the reason | **CLOSED 2026-08-20. It is an organism.** The ladder says a thing that hosts a molecule is one, and the reason it stayed was never the ladder - it was the `@import` order that moving it changes. Moved in `index.css`, in `inventory.md` (22 molecules, 28 organisms, total unchanged at 70) and in the registry. Measured after: the 460px reflow still fires between 460 and 461 |
| **Two link hovers are dead in the product today, and it is not the system that killed them.** `.trust a:hover` and `.notice a:hover` are 0-2-1; `.app .trust a` and `.app .notice a` are also 0-2-1 and written 22 lines LATER in `kit.css`, so the later rule wins and the ink never changes | **CLOSED, both, and measured on the product rather than on the stand.** `trust-block.css` rebuilt the four rules as two, rest at 0-2-1 and hover at 0-3-1, so the hover wins on arithmetic and no later file can take it away by being written afterwards. Measured on `design/home.html` at 1280, the trust link's underline goes `rgb(231, 237, 238)` to `rgb(28, 106, 118)` under the pointer; **its ink does not change, and that is the design**, the link being petrol already. On `design/home-error.html` the wash link goes `rgb(154, 88, 66)` to `rgb(28, 106, 118)`, and the attention link on `design/home.html` `rgb(138, 92, 12)` to the same petrol. A first pass at this measurement read `color` and `text-decoration-line` and reported the trust link as still dead; the property that moves is `text-decoration-color`, and a probe that does not read the property a rule declares reports the rule as absent | Closed |
| **A host repaints an atom, carried on purpose.** `.plan-opt .p` sets the Muted line to `--text-body` where the atom gives `--text-muted` everywhere else | **CLOSED at the founder's review of the components.** The host paints nothing and the atom's `--text-muted` stands, which was the second of the two answers the file carried. Shipped under a condition and the condition was checked: 10.16:1 to 5.78:1 light and 10.44:1 to 6.40:1 dark, both clearing the 4.5:1 an ink owes at 14px. Three paragraphs on `design/upgrade.html` | Closed |
| **A third host override of the action row has no owner yet.** `.panel .gate .actions { margin: 0 }` at `kit.css:890` is byte-identical to `.locked .actions`, before the 8px grid as well as after, so the grid did not cause it | **CLOSED at the organism round.** All three now live in the file of the container that owns them: `panel.css:210` for `.panel .gate .actions`, `card.css` for `.card.prose .actions`, `reveal-step.css:110` for `.rstep .actions`. Written in the molecule's file they would have been a molecule reaching up into an organism | Closed |
| **The stand was showing every list component at more than twice its real width.** `.kit-stage` handed a subscription row 898px where Home renders it at 425 | **CLOSED at the molecule round**, listed here as the record. `--kit-measure` carries the number, defaulting to the product's row column, with `.wide` and `.bleed` as the two named exceptions. Same class of error as the reading measure that came unbound past 900px on two accepted screens: nobody widens the window, so it stays quiet | Closed |
| **`.field input` and `.field select` are not a ladder problem, and they are counted separately so the number is honest.** `select.css` names `.field` 38 times and `text-input.css` 26 | **CLOSED at step 6.** `.input`, `.select` and `.check` are written on 83 elements that had no class attribute at all. Zero pixels: 83 controls fingerprinted on 17 properties across 90 pages, 0 differences, and the test is not vacuous, because without the class the same 61 stand controls show 349. The second census sees the same move from the other side: **controls with no class of their own went from 126 to 104 per viewport, exactly 22**, which is the 14 inputs, 4 selects and 4 checkboxes on the coloured corpus | Closed at step 6 |
| **The back arrow is still a character, and one class is why.** `a.back` is `&lsaquo; Back` 36 times and `&times; Close` twice, and CSS cannot select by the words inside an element | **CLOSED at step 6.** `.back` and `.close` are two classes and both marks are drawn, over `currentColor`, same 24 unit cell and same 16px box as `nav-row.css`'s chevron and `text-input.css`'s clear cross, so three marks are one family with one place to change them. Moves the label's left edge 6.67px on `.back` and 3.73px on `.close`. The census confirms the split landed: `.back` 16 controls and `.close` 2, both families clean on all seventeen properties | Closed at step 6 |
| **The panel's focus ring is clipped by the panel's own `overflow: hidden`.** The `summary` sits 1px from the panel's edge and `base.css` draws the ring 2px wide at 2px offset, so 3px of it falls outside the box that clips it | **CLOSED at the founder's review of the components**, and the backlog understated it: the disclosure ships closed, so the summary is the panel's only child and the bottom edge went too. The ring was 100 percent invisible on all four edges in both themes and at both viewports. The negative offset loses nothing and the corners take the panel's own curve; `overflow: hidden` was kept because it is the only thing holding three fills inside the card's corners. **Worth knowing where it lands**: `<summary>` occurred on zero of the 28 coloured screens when this was written, so the repair was true in the system and invisible in the product. It became visible on 2026-08-13, one stage earlier than predicted, when the self-sufficiency test coloured Alerts: `design/alerts.html` is the only page in the product carrying a `<summary>`, and the ring was walked on it in both themes. The literal `-2px` went with it at step 6 of stage 09 and reads `calc(var(--ring) * -1)`, because a state that names the ring's own width should not spell it a second time | Closed |
| **The panel's `summary` has no `--tap` floor.** It clears 44px at 53, by arithmetic rather than by declaration | **CLOSED at the founder's review of the components.** `panel.css:134` declares `min-height: var(--tap)`. It measured 53px at both viewports in both themes, so the floor never applies and the panel renders byte-identically with the line and without it, which is the point rather than an objection | Closed |
| **The two-hairline seam is REAL, and it was measured rather than argued.** The last charge `li` and the `.gate` below it touch at 0.00px, two pixels of `--line-divider` at one seam | **CLOSED.** Same row as the seam above, kept here because two agents found it independently and the second one opened it in a browser. The principle went in, not the patch | Closed |
| **`card.css` carries `.app > .screen > .locked { max-width: 560px }` and the App shell must not repeat it.** Its two neighbours in `kit.css`, `.form-col` and `.rows-col`, ARE the shell's | **CLOSED at round B, with the split named.** `app-shell.css:284` and `:285` take `.form-col` at 560px and `.rows-col` at 620px; `card.css` keeps the card's own 560px reading measure because the card is the subject of that rule. Three reading measures, two owners, and the reason each one has the owner it has is written in both files | Closed |
| **Four sibling `order` declarations belong to the App shell and are homeless until it is built** | **CLOSED at round B.** `app-shell.css:343-346` takes all four: `.head` order 1, `nav.actions` order 2 with the 24px margin that migrated from the component, `.groups` order 3, `.trust` order 4. Each had been correctly refused by the component that contains the element, and each was named at the line where it was refused, which is the only reason they were findable | Closed |
| ~~The system has no z-index scale. The whole product's stacking order is three literals: `1` on the field's currency prefix, `5` on the app bar, `6` on the tab bar. And `--tap-rail` is separate and still open, the rail's 48px anchor floor being neither `--tap` (44) nor `--row` (56)~~ | **OPEN on the z-index half, and the `--tap-rail` half is now its own row below.** Re-counted today across `design/system/`: still exactly **three** live `z-index` declarations, `app-bar.css:94` at 5, `tab-bar.css:71` at 6 and `text-input.css:122` at 1, every other occurrence of the word being a comment. The money field is a grid rather than a positioned box, so `text-input.css:122` reads "a grid item takes z-index without being positioned"; the value and the count are the same. The dialog sheet added no fourth, having no `z-index` and no `position` anywhere. So the question is still the narrow one, whether three literals, two of them adjacent, earn two tokens, and the recommendation is still not yet. **Step 9's geometry triage answered the neighbouring question and refused this one**: it created `--ring` for the focus ring, which was 16 literals in 17 files, and left both size literals alone. `--tap-rail` moves to the row "Two `min-height` literals" below, because step 9 found a second wearer and a two-wearer debt is a different row from a one-wearer debt | **CLOSED 2026-08-20. Two tokens, not a scale.** `--z-appbar` and `--z-tabbar`, because 5 and 6 are not two values but one RELATIONSHIP - where the two chromes meet, the bottom one wins - carried until today in prose in two files. The money field's `1` is local and stays a literal: naming it would have made this a scale, which is the thing being refused. Zero pixels. **The `--tap-rail` half of this row was already done in the code and nobody had closed it**: `tab-bar.css:219` reads the token and has since 2026-08-12 |
| **~~`DESIGN.md` disagrees with the tab bar in two places.~~ CLOSED 2026-08-20** | It recorded the label at 11px against a `tab-bar.css:109` of `--type-meta`, which `tokens.css` declares at 12px, and the current destination at weight 700 against a `tab-bar.css:131` of 600 | Both corrected in the document, code wins, the same ruling already applied to the subscription row. The other two claims in that paragraph were re-checked at the same time and both hold: the rail's left border is 3px of petrol and its fill is paper | **Closed** with the fifteen above, in the same edit |
| **A button that may not wrap has to go somewhere when it does not fit.** At 360px `.actions` measured 213px of room and its button 225: a 12px overflow, and `.plan-opt` overflowing by 2px the same way | **CORRECTED. The rule stands, the numbers do not reproduce, and the row is open on the principle only.** Swept on 2026-08-12 at 360 and 1280 across all 28 coloured screens: **zero buttons cross their container's right edge, and zero pages overflow the document.** The reason is in the component: `action-row.css:47` declares `flex-wrap: wrap`, so the row breaks BETWEEN buttons and never inside one. Measured on the worst case, `upgrade-payment-failed` at 360, where "Try another payment method" is 230.56px and "Maybe later" 113.91px in a 328px row: the two stack, both at x=16, and nothing is clipped. **Held again through step 9**: the rail comparison round on `pixel-proof.html` reports horizontal overflow anywhere as 0, and every commit of the step closes with 376 renders and no overflow. The principle is still real, because `white-space: nowrap` means a label longer than the room a single line can give it must overflow, shrink or stack, and the three honest answers are unchanged: a shorter label (Voice owns the string), a declared compact padding below 400px, or the action row stacking vertically at the narrow end. The 213/225 pair is withdrawn rather than carried, because a number nobody can reproduce is worse than no number. **REOPENED WITH REPRODUCIBLE NUMBERS ON 2026-08-13, by the type scale moving to `rem`, and the principle this row kept alive is now the live case.** The three answers it listed all assume a label can be authored once and measured once. A `rem` scale hands the text size to the READER, so no label exists that fits every root, and "a shorter label" has nothing left to shorten. Measured at a 24px root, which is what Chrome offers by name under **Very large**: on `design/upgrade.html` at **390px**, the plan card's track is 358 and "Start Tendd Pro - $7.99 a month" is 356 as a non-wrapping button, so the card is 390 in a 358 track and the page scrolls sideways by 16px. **Fixed**, one scoped exception in `button.css`: `.app .plan-opt .btn` takes `white-space: normal`. It is written in `button.css` and not in `plan-option.css` because white-space is the button's property, and it is scoped because this is the only button in the product whose label is a sentence carrying a price and a period. **Zero pixels move at the default**, checked across 8 596 element records on all 32 coloured screens. **What is left is one corner and it is named: 320px viewport with a 24px root, and nothing else.** Swept live and RECOUNTED on 2026-08-14 against the true corpus: **672 renders, 32 pages x 7 widths (320 to 1600) x 3 root sizes (16, 20, 24)**; the first pass ran 609 on a list of 28 that predated the four Alerts screens. Everything at **360 and above is clean at every root size**. At 320 with a 24px root **eight rows** cross the edge and they are three things: `.btn.primary` "Try another payment method" on `upgrade-payment-failed`, 329px in a 288px zone; the app bar's plan `.chip` on the five History and Trends screens, pushed out 25 to 32px by a brand lockup that grew with the text beside it; and a `.btn` on `alerts` and `alerts-empty` by 7px, which only the recount could see. **Not fixed, and deliberately, because the general answer is the founder's:** `white-space: nowrap` on `.btn` came from a founder finding on the etalon page, and the honest question now is whether that rule can survive a text size the product no longer controls. Two answers, both real: keep it and add an exception per host as each one appears (two so far), or drop it from `.btn` entirely, which stage 09 measured as zero pixels at the default and which makes the whole class of finding go away. **Founder** | **Founder**, with **Voice** for the string |
| **The switch row's focus ring lands on the checkbox, not the row.** A `label` is not focusable, so the ring is drawn 20px wide inside a 44px target | **CLOSED at the founder's review of the components, as one paired write across two files.** `switch-row.css:162` draws the ring on the row through `:has(input:focus-visible)`, and `checkbox.css` stands its own down inside `.switch`. Either half alone is visibly wrong: the row's half alone gives two concentric petrol rings, the atom's half alone gives none. Measured: 2px at 2px offset around 328px at 360 and 620px at 1280, exactly one row of four, heights unchanged. `base.css` also lost `label:has(input)` from its focus-visible list, which read exactly like somebody meaning to solve this and was dead, because a label can never match `:focus-visible` | Closed |

---

## Still open on the components, and each needs a person or a step

Every row in this section named **step 9** as its owner. Step 9 has now run and executed none of
them, which is the honest state rather than a failure: each one turns on a value or an axis, and a
value moves only by a named decision from the founder. The owner column is corrected below.

| What | The question, and why it is not answered here | Who owns it |
|---|---|---|
| ~~The `.plans` fold and the gap question are one decision. The map folded `.plans` into `.grid` and the agent executing it refused, with a measurement: past a 760 container the plan row and the tile row were the SAME rule, 3 columns, `--space-8`, identical 201.328px tracks, and at the narrow end they are 1 column against 2, because a chip survives a 159px column and a card holding a price, a sentence, a button and a four-line list does not. THE SAMENESS ENDED ON 2026-08-18 and the case for folding went with it: the plan row declares `repeat(3, minmax(0, 1fr))` where the tile row auto-fills, its cap is 57rem where the tile row has none of its own, its gap is the door row's 16 where the tile row keeps 8, and the tracks that were identical at 201.328 are now 293.33 against 201.328 at a 1440 window. Two rules that agree on one number are a folding candidate; two rules with different counts, different caps and different widths are two rules~~ | **OPEN, and step 9 declared no modifier.** The column count is not a container property, it is the child's narrow floor, and `inventory.md` gives the component the axis "1 to 2 / 2 to 3" and never names this one. Folding it needs a modifier nobody has declared, and the gap question sits on the same component and cannot be answered separately. Counted today, `plans` stands on **1** coloured screen, `design/upgrade.html`, and **2** grey ones, `index.html` and `upgrade.html`. It is the only old name left standing in colour on purpose | **CLOSED 2026-08-20. It does not fold.** The two rules stopped being the same on 2026-08-18: 3 fixed columns against auto-fill, a 57rem cap against none, a 16px gap against 8, and tracks of 293.33 against 201.328 at a 1440 window. Two rules that agree on one number are a folding candidate; two rules with different counts, caps and widths are two rules |
| ~~A size modifier is owed to `.muted`. Nine names fold onto it and they rendered at two type steps, not one. Only `.legal` has a token in the markup to hold the quieter step, `.ruled`, so only `.legal` keeps it; `.consequence` and `.freshness` fold onto a bare `<p class="muted">` and grow 2px~~ | **OPEN, and the map is incomplete here rather than wrong.** Measured out of `kit.css`: `.consequence` 12.5px and `.freshness` 12px, both now 14px, on 11 coloured places and 2 grey ones. Re-read today, `muted-line.css:95` gives the atom `--type-body` and the only host-keyed size left is `.tile` at `--type-meta`, so the two steps are still not declared as an axis. What is owed is a declared size modifier plus the class in the markup. A modifier no element wears is a rule documenting a decision nobody took, which is why one was not invented to hide the gap | **CLOSED 2026-08-20, and most of it had been closed in the code for four days.** `muted-line.css:284` declares `.muted.fine` at `--type-meta` and has since 2026-08-16. What was left was six lines that grew 2px in the fold, the old `.consequence`, and **reading the six sentences answered it the other way**: they are the reassurance under an error or an empty state ("Nothing about your plan or your data changed"), and making the reassurance smaller than the problem is the opposite of what this product is for. They stay at body size. The founder had approved putting `.fine` on them sight unseen; this is the reading after opening the file, and it is the founder's to overrule |
| **`.muted.spaced` is a modifier that modifies nothing.** After the foundations review its `margin-top: 18px` and the base's 12px both fold to `--space-16`, so it is byte identical to the base | **OPEN, and it is not deleted the way `.btn.compact` was, on a difference of authority rather than of merit.** `.compact` is a row of the renaming map; this is not, so removing it means taking the class off `design/upgrade-processing.html`, which no step has owned. Zero pixels either way. Re-counted today: `spaced` stands **1** time in colour and **3** times in the grey, and `muted-line.css:192` still declares it | **DROPPED AT VERIFICATION 2026-08-18, and the row was wrong rather than stale.** It says the modifier is byte identical to the base. It is not: the base is `.app .screen > .muted`, a DIRECT CHILD rule, and of the three wearers in colour **two are not direct children of a screen** - the consequence line on `cancel-guide-no-guide` and the delete warning on `data-privacy`. Measured, all three compute `margin-top: 16px`, and for those two this modifier is the only thing supplying it. Deleting it would have moved two screens |
| ~~The sixth host-scoped chip placement lives in the wrong file. Five are in `chip.css` (`.row .chip`, `.appbar .chip`, `p.chip`, `.alert .what .chip`, `.card .top .chip`) and the sixth is `plan-option.css`'s. Re-read 2026-08-18, when that sixth rule was rewritten: it no longer sets `align-self` and a bottom margin, it takes the chip OUT of the flow (`position: absolute; margin: 0`) and places it in the card's top right corner, because a badge on one card of three was pushing that card's price 32px below the other two. The split is unchanged and so is the question~~ | **OPEN, and both placements are defensible where they stand, which is the problem.** Re-read today and the split is unchanged: `chip.css:193, 194, 195, 196, 217` carry the five and `plan-option.css:106` carries the sixth. The rule states where a chip SITS in that card, which is a fact about the card; the other five state the same kind of fact and live with the chip. One of the two readings has to win, and whichever wins, the other five or the one moves | **CLOSED 2026-08-20. The sixth moved.** All six placements of the Chip are in `chip.css`. The argument for the rule stays in `plan-option.css`, where it belongs, because it is about that card; the four declarations left. Zero pixels: same selectors, and `chip.css` is imported before its old home |
| ~~The `.divlist` bottom margin is keyed to the shell's column. `divided-list.css:99` declares `.app .rows-col > .divlist { margin-bottom: var(--space-24) }`~~ | **OPEN, with three honest homes and no decision.** Still at `divided-list.css:99` today. One of the two old names carried a bottom margin and the other did not, and a component does not own the gap under itself, so the gap is keyed to the container instead. The file names the alternatives at lines 89 and 90: a declared `.divlist.spaced` modifier, which needs a class added to two product screens and **collides with `muted-line.css`'s `.spaced`**, where the same word would mean a different thing; or a rule in `app-shell.css`, which is what `action-row.css` did with its own 24px margin | **CLOSED 2026-08-20. It is in `app-shell.css`.** The `.divlist.spaced` modifier lost on a NAME COLLISION rather than on taste: `muted-line.css` already uses `.spaced` to mean a margin above. `action-row.css` had already set the precedent with its own 24px. **Written into the 900 block for one round and measured out of it**: `.rows-col` is in the markup at every width, so scoped it would have dropped 22px off four screens on a phone |

---

## Found on 2026-08-12 at the second control census

Six rows. The first three were new findings, the next two were corrections to work that round
believed it had finished, and the last was the open half of a repair that had already landed.
**Two of the six are closed by step 9 and two more are closed in part**, and where a row closed in
part it is counted as open below, because a row with anything unfinished in it is not a closed row.

| What | The measurement | Who owns it |
|---|---|---|
| **A live defect on a shipped screen, found and closed on the same day.** Two coloured screens were drawing their chart with variables the system does not declare. `design/history-trends.html:75` read `var(--line)` and `var(--mid)`, `design/history-trends-locked.html:69` read `var(--line)`, all three inside SVG presentation attributes. Those are `kit.css` names, and `design/system/tokens.css` declares neither | **CLOSED, and the scan re-run today across the whole system.** The computed stroke had been `none`: read in a browser at 360 and 1280 on both screens, the three grid lines and the trend path were not drawn at all, so the chart on the two screens the Pro tier is sold on was an empty box. Repaired within the round and re-measured: all four now stroke `rgb(228, 233, 234)` for the frame and the trend path strokes `rgb(56, 67, 73)` from `var(--text-body)`, byte-identical to what `kit.css` held under the old names. **Re-run today over 136 declared custom properties, all 29 files in `design/`, `base.css` and all 57 component files: zero undeclared `var()` in any live declaration.** One hit exists and it is prose, `wash-block.css:45`, quoting what `kit.css` used to carry inside a comment. **The one thing still owed is the instrument, not the result**: `screens/` holds `fp.cjs`, `ledger.cjs` and `brandproof.cjs` and no var scan, so the check that found the worst defect of the stage is the only one with no re-runnable script beside it | Closed on the defect. **Stage 12** owes the scan a script in `screens/` before the rollout |
| **`design/` has no `index.html`.** `CLAUDE.md` states the rule by name: opening a stage folder lands on the product screen, node 1.1, and the list of all pages of that stage is `overview.html`, and it says this holds for `wireframes/` and `design/` alike | **OPEN, verified again today**: `design/index.html` does not exist, while `design/overview.html` does. `wireframes/index.html` is the landing and carries 60 distinct class tokens. So the coloured half of the project has a hub and no home page, and the rule is broken today. The screen behind it is the landing, which is one of the 27 screens not yet coloured, so this is not a five-minute copy. **The step 9 dry run put a number under it**: 36 of the landing's 60 tokens are outside the system | **CLOSED 2026-08-18.** `design/index.html` exists and is the coloured public landing, 52KB of it; the rule this row quotes is satisfied and has been since the landing was coloured in August. Re-read before closing rather than assumed |
| ~~The `.landing` scope is carried by 46 of the 55 component files for a page that exists only in the grey~~ | **OPEN, and the count in the heading of this row is STALE and corrected here.** Measured today: **48 of the 57** component files name `.landing`, and `base.css` names it once more, in a comment. The row was written when the system had 55 files; the brand added two and the scope grew with them. `pixel-proof.html` carries the corrected 48 of 57. **One further correction, and it runs the other way**: the proof page says "no page wears it", and that is true of colour only. `wireframes/index.html:14` reads `<div class="landing">`, so the grey landing DOES wear it and every one of those 48 files is already live on that page; what is unworn is `.landing` on the coloured corpus, where it stands **0** times across 29 files. It is not waste, because stage 12 colours the landing and the scope starts paying at once; it is recorded so that whoever reads a component file and wonders why every rule is written twice gets the answer from here rather than from a guess | **CLOSED 2026-08-20. It stays.** The premise it was written on is gone - `design/index.html` is the coloured landing, so all 48 of those files are live in both corpora - and the doubled selector is what lets a component be lifted into either host without knowing which one it landed in |
| **The three chip tones were cleared from `kit.html` and two button classes were not.** The deletion of six declared things counted two corpora when there are three: the stand was not swept, and `kit.html` was the last place in the repository writing `tone-attention`, `tone-error` and `cancelled` | **CLOSED on the classes at step 9, second batch, and OPEN on the comment.** Re-counted today as class attributes across all three corpora: `tone-attention`, `tone-error`, `cancelled`, `inverse` and `is-disabled` all return **0 / 0 / 0**. `design/kit/kit.html:110` and `:120-121` no longer carry `is-disabled` or `inverse`; line 117 is a plain `.btn`. The step 9 commit records the same result from the other side: "the census re-counted two rows that credited the frozen stand with 2 `inverse` and 1 `.is-disabled` deleted underneath them. All six tracked forms are now clean in all three corpora". **The stale comment closed on 2026-08-13**, at step 6 of stage 09, and Codex was the instrument that found it: `chip.css:88` was still telling the reader that "`design/kit/kit.html` carries `chip tone-attention` on lines 133 and ...", a claim that named line numbers in another file and had been false since the repair. It now records the repair instead, in the past tense it belongs in. A comment that names a line number in another file is a claim, and this row is the reason the file no longer counts anything by hand | **Closed 2026-08-13** |
| **CLOSED 2026-08-17 on both halves.** `design/kit/kit.html` rendered with no chrome at all: its `<style>` block read twelve variables that nothing declared | The variables half closed earlier - all eleven names resolve now. The half that stayed open was the style block itself, called "the one page in the stand that a screen-level reading of `CLAUDE.md` would call a violation" | **And a screen-level reading is the wrong reading, which is what closes it.** `CLAUDE.md` bans a style block on a SCREEN. `kit.html` is a stand, and the same licence is already written down for `design/overview.html` in `docs/responsive.md` ("a review page rather than a product screen, so it keeps its chrome beside the stand") and used again by `design/rollout.html` in this stage. Moving 59 lines of showcase chrome into `_page.css`, where they would serve one page, would make the stand's shared stylesheet carry a single page's furniture | **Closed.** What the page really lacked was a way in: it is in `design/kit/_nav.js` now, in the System group, named as the flat kit stage 07 built |
| **Two component files had a data URI spliced into a prose comment by an old global replace, both repaired, and nobody knows what else that replace touched** | **CLOSED as far as the named instrument reaches.** Re-checked today: **zero** comments in `design/system/`, `design/_screen.css` or `design/kit/_page.css` contain a data URI, so the two repairs hold. The blast radius was handed to the step 8 pixel comparison as the cheapest instrument, and it has now run: `pixel-proof.html` reports 3588 elements compared over 52 of 56 page-viewports, 83 declared shapes of difference, every one tied to a named source, and **0 unexplained shapes and 0 unexplained occurrences**. A full read of every file the replace could have touched is still the only guarantee and still nobody has costed it, but the cheap check came back clean and is on the record | Closed by **step 8**, with the residual named rather than hidden |

---

## Left open on purpose at step 9, each with what it is waiting for

Seven rows. None of them is a defect on a shipped screen and none of them is an oversight: each
was raised, verified, and left because closing it needs a decision, a corpus that does not exist
yet, or an instrument that cannot be built after the fact.

| What | The measurement, taken today | Who owns it |
|---|---|---|
| **CLOSED 2026-08-17: no component is owed, and the answer is that the name carries three meanings rather than two.** `.lead` was listed as part of the save-focus candidate list and read as a component in waiting | Counted today across both corpora. **`.muted.lead`** is a declared margin modifier of the muted line and stands on 9 coloured pages: it is a variant with a rule, a page and an inventory row, and it keeps the word. **Bare `.lead`** stands on exactly two coloured pages, in two different hosts, with two different rules and two different jobs: `save-focus-list.css:70` for the nudge over the candidates on `home-savefocus`, and `landing-story.css` for the sentence under the story head on `index`. One host each | **By rule 1 of `inventory.md` those two are host slots, not a component**, the same shape as `.gate > p` in `panel.css` and `.oncard .k` in `eyebrow.css`. Promoting them would mean promoting a NAME that three rules already disagree about, which is how a collision becomes a component | **Closed as a decision, with one thing left to the founder:** the true fix for a name meaning three things is a rename, and a rename is markup, which the freeze puts behind a founder's decision written into the file it changes. Nothing renders wrong today and every one of the three rules is scoped to its host |
| ~~Two `min-height` literals are owed `--tap-rail`, and the debt now has two wearers rather than one. Split out of the z-index row above, because step 9's geometry triage found the second~~ | **OPEN, and named at both lines.** Measured today across every component file: exactly two live `min-height` literals on a tap floor, `tab-bar.css:219` at 48px and `alert-item.css:110` at 40px. `app-shell.css:92-93` also carries literals but they are `100vh` and `100dvh`, which are viewport units and not a tap floor. Neither 48 nor 40 is `--tap` (44, `tokens.css:175`) or `--row` (56, `:176`). `tab-bar.css:198` still records that `--tap-rail` "was proposed at stage 07 and deferred to stage 08", and stage 08 did not create it. **Step 9 refused it deliberately and said why**: the same triage created `--ring` because the focus ring was 16 occurrences of one measurement across 17 files, and refused this one because two occurrences of two different values are not a scale. The debt is now two lines instead of eighteen | **CLOSED 2026-08-20, and the row was stale.** `--tap-rail` was declared at stage 09 and `tab-bar.css:219` has read it since; this row was still measuring a literal that is not there. What is left is `alert-item.css`'s 40px, and the token's own comment already refuses it: a line box on a span with no href is not a rail row and not a target. Two values for two jobs is two decisions, not a debt |
| **The share card's total, 40px to 32px, is a named change of appearance that the pixel comparison could not see.** First of the three reverse-check rows in `tokens-audit.md` | **OPEN as a record rather than as work.** `tokens-audit.md:722-725`: zero elements at 40px in the after corpus and zero in the BEFORE corpus either, which settles it. `share-card.css:30` records that the component "stands on 2 grey pages and ZERO coloured ones". The row is true about the system and its screen is still grey, so the change becomes visible at stage 12 and not before. It is here so that nobody reads the step 8 table's "0 unexplained" as covering a change it never had a chance to see | **CLOSED 2026-08-18.** It has rendered and it is measured: the share card's total is **32px** on `design/share-snapshot.html`, the value the named change said it would be. The reverse-check row did its job, which was to keep visible a change no pixel comparison could see |
| **The row family's side inset is a named change the comparison could not see, and the reason is the instrument.** Second of the three | **OPEN as a record, with the cost of the instrument named.** `tokens-audit.md:727-760`: the before capture is not from step 1. Step 1's instrument was the control census, which measures controls by their axes and not the whole DOM; the DOM fingerprint was built at step 6, so `pixel-before.json` is "the screens on `kit.css` immediately before the swap". The inset moved at step 5, in `git` (`3aa3f5f` to `9fa7c7d`, `.app .row` padding 10px 4px to 10px 16px), which is before the capture. So the audit's claim that the inset "moves its logo 12px right on Home, the largest single pixel move this stage makes" is **true of the product and false of this diff**: measured on the coloured Home at 1280 the logo moves 4px, and that 4px is the grid. **What this costs is the headline: the proof covers the SWAP, not the whole stage**, and re-capturing a before now would be inventing evidence | Nobody. **Recorded rather than repaired**, on the pack's own rule that a before is never re-taken |
| **The drawn chevron is the third such row, and it has the same reason** | **OPEN as a record.** Same paragraph of `tokens-audit.md`: `.navrow .arrow` went to `font-size: 0` plus a mask at step 5, before the fingerprint existed, so it measures `font-size: 0px` and `width: 16px` on BOTH sides of `pixel-before.json`. The witness for this one is a git diff and a founder's eye, and that is said out loud instead of letting the row look covered by a comparison that never contained it | Nobody. **Recorded rather than repaired** |
| ~~"Role surface": two ink roles are painted through `background-color`, and the reading is contested. BOTH readings are recorded here and neither is chosen~~ | **OPEN and UNDECIDED, on purpose.** Codex raised 2 and `pixel-proof.html` marks the row open with the note "one is contested". Located today, and there are exactly two: `brand-mark.css:110` and `trust-block.css:176`, both `background-color: var(--text-action)`. **Reading one, a defect**: `--text-action` is an ink role and the system's own rule is that a colour reaches an element through the role that names its job, so an ink role appearing on a surface property is a role used outside its job, and two more roles are owed. **Reading two, a false positive**: both declarations are the paint of a CSS mask, and `background-color` behind `mask-image` IS how a mask draws a glyph, so the property is a surface only in name and the ink role is exactly right. The second reading has independent support in the files: `brand-mark.css:100-104` measures the mark as INK against the 4.5:1 threshold rather than as a surface against 3:1, which is a decision already taken and written down. Deciding this would change either two token declarations or one audit rule, and neither is a step's to take | **CLOSED 2026-08-20. Reading two, a false positive.** Both declarations are the paint behind `mask-image`, which is how a CSS mask draws a glyph, so the property is a surface in name only and the ink role is exactly right. `brand-mark.css` already measures the mark as INK at 4.5:1 rather than as a surface at 3:1, which is the same call taken once already |
| ~~Thirty-one token comments have no origin beside them, and the two ways of counting do not agree~~ | **OPEN, and the disagreement is the useful part.** `pixel-proof.html` carries the row as Codex's, 31, open. `tokens-audit.md:286-296` records the same argument from step 3 at different numbers: Codex reported zero, on the ground that a block comment governs the declarations under it and `DESIGN.md` records the same origins again, while the parse reported 22 with no comment on their own line. Counted today with a same-line parse: `tokens.css` holds **140 declarations across both theme blocks and 96 of them have no `/* */` on their own line**, because block comments govern runs. So the three numbers, 0, 22, 31 and 96, are four different questions and not four answers to one. `CLAUDE.md` asks for the per-line form by name ("Every value carries its origin in a comment next to it"), so the rule is on the side of the strict count and the file is not at it | **CLOSED 2026-08-20. Per RUN, not per name.** A block comment governing a run of related values reads better than 137 fragments, and it is what the file already does. `CLAUDE.md` was amended to say so: "carries its origin in a comment beside it **or in the block comment governing its run**". The rule as written was asking for something the file deliberately does not do, and does not do for a good reason |

---

## The dry run: could the system build the screens it has never seen?

The rollout moved to stage 12 and the main test of the system's completeness moved with it. This
is the cheap replacement and the stage pack requires its result here, in numbers. All 27 still-grey
pages were ranked by distinct class tokens and the five densest were taken. **Nothing was built.**

**Re-derived today rather than taken from the proof page.** Method, so it can be re-run: collect
every distinct token inside a `class` attribute on the grey page; count a token as **in the
system** if it appears as a class selector in `design/system/base.css` or any of the 57 component
files, with CSS comments stripped so a name mentioned only in prose cannot count as covered; count
it as **through the map** if `inventory.md`'s renaming map sends it to a name that is in the
system; count it as **chrome** if it is one of `.layout`, `.stage`, `.stage-app`, `.stage-flush`,
which are `wireframes/_wf.css`'s review frame and which the map's own last row sends out of the
system to `_page.css`. What is left is not covered.

| Still-grey screen | Class tokens | In the system | Through the map | Reviewer chrome | Not covered |
|---|---|---|---|---|---|
| `index`, the landing | 60 | 18 | 3 | 3 | **36** |
| `cancel-guide` | 30 | 21 | 6 | 3 | **0** |
| `alerts` | 29 | 22 | 4 | 3 | **0** |
| `data-privacy` | 29 | 20 | 5 | 3 | **1** |
| `connections` | 25 | 16 | 6 | 3 | **0** |

**Three of the five need nothing at all, and the fourth needs one declared one-off.** The single
uncovered token on `data-privacy` is `.intro`, which `inventory.md:271` already lists as the
"plain-answer intro" one-off, waiting because its page is still grey. So the honest headline is
"four of five are closed, one of the four by a decision already taken" rather than "four of five
need nothing".

**The whole gap is one page, and all 36 uncovered tokens are the landing's.** Every one of the 36
was checked against all 55 grey pages and stands on `wireframes/index.html` alone.

**TWO DISAGREEMENTS WITH `pixel-proof.html`, both on the landing row and neither on the other four.**
The other four screens reproduce that table exactly, including all four "through the map" figures,
which is the check that the method is the same one.

1. **36, not 37.** The difference is `.stage-flush`, declared at `wireframes/_wf.css:37` as a
   modifier of `.stage`. The map's last row sends `.layout`, `.stage` and `.stage-app` out of the
   system by name and does not mention the modifier, so counting it as an uncovered product class
   gives 37 and counting it with its own base class gives 36. **36 is the number this file
   carries**, because a modifier of a chrome class is chrome.
2. **Not all of it is the `lp-` namespace.** The proof page says "All 37 uncovered tokens are the
   landing's `lp-` namespace". Counted: **24 of the 36 carry the `lp-` prefix and 12 do not** -
   `cols-3`, `fbrand`, `fcol`, `list`, `pcount`, `pctx`, `plabel`, `planline`, `planmicro`,
   `ptotal`, `signin`, `snum` (renamed `sord` on 2026-08-15, still one class and still uncovered).
   All twelve are landing-only, so the conclusion the sentence was
   drawing is untouched: the gap is one page. What is wrong is the remedy it implies. Retiring the
   `lp-` twins, which is what `inventory.md` ends by promising, closes 24 of 36 and leaves a
   dozen unnamed classes behind it.

---

## Found at stage 09 step 5, building the first screen the system had never seen

**The self-sufficiency test, and its headline is a number rather than a feeling.** `design/alerts.html`
and its three states were assembled on 2026-08-13 from `design/system/` alone: **44 distinct system
classes across four pages, 0 new CSS files, 0 new tokens, 0 new component variants, 0 new lines of
microcopy.** Nothing was added to the system to make the screen fit, which is the rule this step runs
under: what is missing is written down here, never drawn on the screen.

**Four rows, and one of them closed the same day.** They are the only things the build found, and
none of them stopped it.

| What is missing | What it was needed for | Which level closes it | Priority |
|---|---|---|---|
| **An edge for the logo tile in the dark theme.** `logo.css` draws no border at all, and a merchant mark is whatever colour the merchant is | `design/alerts.html`. Measured against the dark surface `#161f21`: **Peloton 1.04:1, Netflix 1.25:1, Disney+ 2.05:1**, three of the fourteen marks in `design/visuals/logos/` under the 3:1 a non-text boundary owes. The same three measure 17.44, 21.0 and 12.4 on the light paper, so the light theme never showed it. Peloton is new to the coloured product; Netflix and Disney+ have been on `home.html` since stage 07 and the dark theme pass of stage 08 did not catch them, because it looked at roles and these are images | **component**, `logo.css`, and it already has the role it needs: `--line-container` is the container edge in both themes. The question is whether the edge is always on or only where the tile is dark, which is a decision and not a line | **Medium.** It is a legibility defect in one theme, on a product that ships one theme today |
| **A sentence in the right-aligned pair list is cramped at 360, and the system is not the one that put it there.** `.pairs` has a `.sentences` modifier for exactly this, and the grey chose the plain form | `design/alerts-empty.html`, "What shows up here". Measured at 360: **three of its four rows wrap onto two lines on both sides**, right-aligned, so "with the old price beside the new one" ends with "one" alone. At 1280 all four are single-line, because the column is 620 wide there | **none, and that is the finding.** `pair-list.css:115-131` already declares `.pairs.sentences`, which stacks the pair and gives the sentence the full width, and its own comment describes this exact failure. `wireframes/alerts-empty.html` uses plain `.facts`, and five other grey pages use `.facts.sentences`, so the grey made a choice rather than missing one. A colour copy may differ from its grey by styling only, so this cannot be fixed here | **Low**, and it is a **founder** decision at the rollout: change the grey, or accept the wrap |
| **U8 says when a trust line is ALLOWED and never says when it is OWED.** The rule reads "one `.trust` per screen, and only where a figure came from the bank" | `design/alerts.html` carries eight alerts, each ending "from Chase", and no trust line. `design/alerts-error.html`, the state where nothing was reached, carries one. Both are faithful to the grey. Under U8 as written both are legal, because it is a maximum; under `conventions.md` section 5, "the read-only line wherever a trust moment appears", the success state looks short | **usage rule**, `architecture.md`. Either U8 gains an obligation half and the grey gains a trust line at stage 12, or U8 states out loud that it is a ceiling and the obligation lives in the wireframes | **Medium.** It is the first rule the new screen found too weak, and the pack asks for exactly this to come back rather than be ignored |
| **The interruption pattern's host axis was written from three hosts and the product has four.** ~~Open~~ **CLOSED 2026-08-13, the same day** | `design/alerts-error.html` holds the announcement inside `.rows-col`, where the three known hosts held it directly. The class went on the column, the pattern reached its members unchanged, and the gap measured **24px with the class and 24px without it** at both viewports | **pattern documentation**, and no CSS: `patterns/interruption.css`, `interruption.html`, `design/kit/_nav.js` and this file's pattern table were corrected in the same step. An axis read off the corpus that stands is a description of that corpus, not a limit on it | **Closed** |

**What the test did NOT find is worth one line, because a silent absence reads as a formality.** No
component was missing. No state was missing. No token was missing. No line of copy had to be
invented: every string on the four pages is character-identical to its grey original, which Voice
owns. The one build error was mine and not the system's, and the browser caught it: the links in
the action row were written bare, where the system folded `.secondary a` into `.btn` at stage 08.

---

## Found at stage 09 step 6, on two instruments, and one of them is structural

**The verification pass, run twice.** Codex read-only over `design/system/`, `design/kit/` and
`design/`, and a browser plus scripted pass of my own, taken independently and merged with a
"who found it" column. Twelve findings held, two were dropped at verification, and the classes
that came back clean are named on `pixel-proof.html` rather than here, because a clean class is
a proof and not a debt.

**Eleven of the twelve were the same defect wearing different clothes**, and that is the row worth
keeping.

| What is missing | What it was needed for | Which level closes it | Priority |
|---|---|---|---|
| **A per-component coloured footprint is written by hand in forty places and nothing recomputes it.** Every component page ends "Stands on N grey pages and M coloured ones", every pattern file repeats its own M in a comment, and `docs/inventory.md` carries a third copy | The coloured corpus grew for the first time since stage 07, 28 pages to 32 and 7 screens to 8, when step 5 built Alerts. **21 components' M went stale in one commit** and every one had to be found by grep and re-counted by script. The same growth is coming again at stage 12 and it will be four times the size | **tooling, and it is the only row in this file that asks for a script rather than a decision.** The counter that produced these numbers exists and was run twice at this stage; it lives nowhere. Either it becomes a checked-in script that regenerates the footprint line, or the line stops carrying a number and carries a link to one place that does. Deciding which is a person's call, and it belongs to stage 13, where the handoff decides what a developer is handed | **Half closed, 2026-08-17.** The counter now exists and is checked in: `design/kit/screens/counts.cjs`, run with no arguments for the drift and `--all` for every component, output kept at `counts.txt`. It reads the registry, counts both corpora by whole class token, reads what each css header claims and prints the difference. **Measured on the day it landed: 21 of 71 claims disagree with the corpus**, almost all on the coloured side, because that corpus went from 28 pages to 55 and nothing recounted it - `.appbar` still says 18 coloured pages against 54, the brand says 32 against 55, `.textblock` 32 against 42. Two things it deliberately does NOT do. It does not write: several of these sentences carry a breakdown (`alerts (8) and alerts-loading (4)`) that a script must not flatten, so the rewrite stays a person's edit. And it reports its own blind spots rather than counting through them: six components are named differently in the frozen grey (`.field input`, `.switch input`, `.lp-trust ul`) or drawn there with no class at all (the destination icons), and their grey figure is left exactly as written. **The rewrite of the 21 landed the same day**, on the founder's go: every canonical "Stands on" sentence recomputed and stamped with the instrument and the date, the four secondary figures in those headers either recounted or given the corpus they were measured on by name, and the meta strip fixed on the eight stand pages that carried a stale figure. The script now reports **0 drift**. Two things stay open and are named rather than closed by assertion: the **per-variant** counts inside the pages ("33 grey pages" on the filled button, "46" on the outline) are a second layer this instrument does not read, and the six blind spots keep their hand-measured grey figure. The rule that comes with it: **this script runs at the close of any stage that grows a corpus** |
| **Ten component pages said another component "is not built yet" long after all 57 were built.** Written true during the build rounds of stage 08 and never revisited | Fifteen occurrences across ten pages and two CSS files. A reader following one of them concludes a part of the system is missing | **documentation, and it was fixed in this step**: each now links the page that exists. Left here as the record, because the mechanism is the same as the row above. A sentence about the state of the system, written inside a component, has no owner who re-reads it | **Closed 2026-08-13** |

**Two findings were dropped at verification and the reason is kept, so neither comes back.**
`range-picker.css:114` writes `font-weight: 600` inside a disabled-and-pressed state, which reads
as a state written with a value: it is not, because **the system has no weight scale at all**.
Weight is a literal in every component, by the rule that colour goes through a role and geometry
through a primitive and weight is neither. Tokenising one occurrence would have created a third
level to serve one line. The detector's `em-dash-overuse`, raised on 34 files, counts `--` in
running text, and the running text of this project is full of custom property names: `color.html`
holds **378** double hyphens and **zero** em dashes.

---

## Copy, owned by Voice

| What | Where |
|---|---|
| Thirteen microcopy divergences found at stage 07 | **Closed** in Voice Round 4, 2026-08-11. Kept here as the pointer |
| **~~Two result-count strings for the preset search have no row in `microcopy.md`.~~ CLOSED 2026-08-20** | `1 match for "QUERY"` and `N matches for "QUERY"`, written into `behaviour.js` when the tile filter was built on 2026-08-11 and never given an owner | All THREE are now one row in `microcopy.md`, the zero case included, because they are one pattern and splitting them would let the singular drift from the plural. The zero case was not new: it is the exact line `add-subscription-empty` already carries | **Closed** |
| **~~The Amount placeholder changed from `$0.00` to `0.00` and no row in `microcopy.md` owns either string.~~ CLOSED 2026-08-20** | Forced by the founder's decision that the field takes digits only: with the sign drawn beside the input, the old placeholder rendered `$ $0.00` | Given a row, with the reason and with why the hint beside it keeps its dollar sign - "Enter an amount, like $9.99" is a sentence about money, not a field that takes digits. It stands on `add-subscription-empty` and `add-subscription-error` in both corpora | **Closed** |
| **~~The typographic prose in `DESIGN.md` disagrees with the code in fifteen places.~~ CLOSED 2026-08-20, and the count was never the point** | **The Hierarchy list carried SEVEN steps and the scale has ELEVEN.** Four of the seven had the wrong number and the three smallest were half a pixel out each, because they were written before the scale was tokenized and nothing brought them forward: Body 14.5 against 14, Meta 12.5 against 12, Label 10.5 against 10, and "Headline, 18px" against a `--type-title` of 20 with no weight of its own. **The names were the worse half:** "Title" in the document meant the wordmark at 16px while `--type-title` in the code is 20px and means the subscription name, so a reader following the document to the code arrived at a different step | **Rewritten from the code, and every step now NAMES ITS TOKEN**, which is what makes the ruling stick. Prose about a number is a claim no instrument can read; `design/kit/screens/design13.cjs` reads every step out of `DESIGN.md`, resolves it against `tokens.css` at 16px per rem, checks a clamp at both ends, and reports a step the document does not name at all as a finding of its own | **Closed. 11 of 11 steps agree, 0 unnamed.** It cannot go stale again without the instrument saying so |
| ~~"Try again" stands in three weights on four error screens~~ | **CLOSED 2026-08-20, and NOTHING CHANGED, which is the finding.** Re-counted: it stands on **7** screens, not four - `add-subscription-error`, `alerts-error`, `connect-bank-error`, `history-trends-error`, `home-error`, `share-snapshot-error`, `subscription-detail-error`. Read against what else is on each screen, all seven already follow one rule: **where the error IS the screen it is `.btn primary` (5), and where the content is still there it is quiet (2)**. `home-error` keeps the whole list and `add-subscription-error` keeps the manual form. The three weights were a rule nobody had written down, not a drift |

---

## Found at stage 10 step 3, deciding the form of the shell

One row. It is here rather than fixed in place because the fix is markup on pages that do not all
exist yet, and doing it now would mean doing it twice.

| What is missing | What it was needed for | Which level closes it | Priority |
|---|---|---|---|
| **CLOSED 2026-08-17 as a DECISION rather than a defect, and the measurement is what closed it.** In the rail form the focus order and the visual order disagree: the rail's head takes focus first, the content pane's controls next, and the four destinations last | The row was opened at stage 10 on one screen at one width. Re-measured at stage 12 across **all 55 coloured pages at 360 and at 1440**: every page's focus order is `bar, content, nav` or a subset of it, and **not one page changes its order with width**. So there is no width-dependent defect to repair; there is one order, and it is content before navigation | **Nothing in CSS, and nothing in the markup either.** The three ways out were: reorder the DOM by width, which needs script or duplicated markup and would put four destinations in front of the first word for everyone who tabs on a phone; add a skip link, which is a new product string and therefore Voice's before it is anybody's; or keep the order. **Content before navigation is the order a skip link exists to create artificially**, it is the same order at both widths, and each control appears exactly once with nothing hidden | **Closed.** The skip link stays available as a founder's option, and it is the only one of the three that would improve anything: it belongs to `voice/docs/microcopy.md` first |

## Found at stage 10 step 6, by the width sweep and the read-only critic

Two rows. Both are questions about a value somebody named on purpose, which is why neither was
answered inside a verification step: this stage fixes what it can prove is broken and does not
re-derive a decision on its way past.

| What is missing | What it was needed for | Which level closes it | Priority |
|---|---|---|---|
| **CLOSED 2026-08-17, and closing it found the seventh.** The shell's blanket on `.app > .screen > *` had unbound six measures, one at a time, each found by accident: the form column (588 instead of 560), the Pro gate (748 instead of 560), the plan row (728 instead of 720), the readout (980 instead of 525), the groups cap and, at stage 10, a standalone muted line | The blanket exists for a real reason: past the desktop point a screen's blocks are allowed to use the pane instead of reading down a column. What it could not tell apart is a layout block, which should widen, from an element that had already declared its own measure at 0-2-0 and lost to it on source order, an organism being imported after an atom | **`:where()`, and the named list this row proposed was tried first and abandoned.** Read off the corpus at 1440, the list of blocks that may widen is fourteen selectors long and one member has no name to be listed by: the skeleton frame on `history-trends-loading` is a bare `<div aria-hidden="true">` whose own file says it must not move when the numbers land. `:where()` drops the blanket to specificity zero, so anything that declares a measure of its own wins - at any specificity, in any file, written by somebody who has never read the comment - and anything silent about width still gets the pane. Both blankets and the detail re-release are written that way now | **Closed, and it moved three pages.** Measured on all 55 coloured pages at 360 and 1440: **zero boxes at 360**, and at 1440 exactly three pages and 49 boxes, all of them the **dialog sheet**. That is the seventh unbinding and the only one still live: `dialog-sheet.css` has declared `max-width: 480px` since stage 08, with a written reason, and it had never once rendered past the tablet point - the sheet ran the full 588 flow column on `cancel-guide-blocked`, `connections-add-source` and `data-privacy-delete-confirm`. The fix restores what the component says rather than deciding anything new, and it is one line to revert if the founder prefers the wider sheet |
| **The prose card reads 60.7 characters to a line, and the system's reading measure is 52ch.** `.card.prose` holds 560px, which is a named decision with its own note in `card.css`; inside the card's padding the paragraph lands at 526px, eight characters past `--container-text`. Both numbers are deliberate and they disagree | Stage 10 gave every other prose block in the product one measure, `--container-text`. This is the single place where a second, older measure still wins | **a founder's decision about a named value, not a code change.** Either 560 becomes the card's own container measure with the paragraph reading 52ch inside it, or 560 stands and the card is the declared exception. The rule this project runs on is that a value changes by a named decision, so a verification step is not allowed to pick | **Low.** It is one component on one page today, and it reads well. It becomes a real question at stage 12, when the prose card lands on more screens |

| **Every count taken against "the system" is six components stale, and one pass closes them all.** The five public-page organisms arrived on 2026-08-14 and the landing window with them, taking the system from 57 to **63**. The live claims were recounted the same day (`_nav.js`, `README.md`, `CLAUDE.md`, `why.html`, `overview.html`, `inventory.md`, `logo.css` and `logo.html`), and the STAGE RECORDS were deliberately not: `pixel-proof.html`, `census.md`, `tokens-audit.md` and `responsive.md` state what stage 08, 09 and 10 measured, on the corpus they measured it on | A record that silently updates itself stops being evidence, and a live claim that does not update stops being true. The two need different treatment and this row is the line between them | **one scripted pass, and it belongs to stage 12.** Screens are the same story from the other side: several pages still say 28 or 32 coloured screens where the corpus is 56. The corpus is still moving, and recounting a moving corpus is how the drift got here | **CLOSED 2026-08-17, the trigger fired.** Stage 12 stopped adding screens, so the pass ran. The per-component half was already instrumented at stage 11 by `counts.cjs` and reads **0 claims disagreeing with the corpus** today. The width half is recounted on **72 entries and 69 component files** (was 60 and 57) with the stage 10 table left standing beside it under its own named corpus, and **nine width cells were found wrong**: eight answering what an organism IS rather than what it does with width, one saying "does not adapt" over a file with a desktop point in it. **The screen half is closed in this file, `README.md`, `design/overview.html` and `responsive.md` and NOT on the component pages**, which is a smaller claim than this row was about to make: measured today, "28 coloured", "32 coloured", "28 pages" and "32 pages" occur **78 times across 31 pages**, and **48 of those, in 24 files, are on component pages rather than in a stage record**. A stage record keeps the corpus it measured; a component page saying "the 32 coloured pages" is describing today. That remainder gets its own row below rather than a sentence here. Account: `design/rollout.html`, instrument: `rollout12.cjs` |
| **CLOSED 2026-08-16 at stage 11 step 2.** The curves are `--curve-expo-out`, `--curve-expo-in`, `--curve-firm` and `--curve-spring` in `tokens.css`, under five jobs (`--ease-arrive`, `--ease-leave`, `--ease-settle`, `--ease-travel`, `--ease-state`); the durations are `--dur-state` 150ms and `--dur-pulse` 1.4s. The four hand-written `cubic-bezier(0.16, 1, 0.3, 1)` literals are gone from `landing-facts`, `landing-paths`, `landing-plan` and `landing-steps`. **The harder half is answered NO rather than left open**: the `animation-range` bounds do NOT become tokens, and neither do the loop lengths. A duration is a value and a range is a piece of choreography; the five marquee and eight strand durations look like a scale and are loop lengths, deliberately mutually prime so nothing on that stage ever repeats in step with anything else. They stay literals in the file that owns the loop. **Nothing moved**: 30 choreography samples at 10 scroll positions on 3 viewports, before and after, 0 differ | The row below is the state it was taken up in | **`tokens.css`, motion block; `design/kit/motion.html`; usage rule U12 in `docs/architecture.md`; rules 1 and 6 of `design/system/CLAUDE.md`** | Closed |
| **TAKEN UP 2026-08-16 by stage 11 step 1, and recounted on the way in.** The one moving surface of the product carries its durations AND its easings as literals. `landing-orbit.css` declares **four** easing curves on the candidate's pin - `--ease-arrive` (expo-out), `--ease-leave` (its mirror), `--ease-settle` (a spring as `linear()` with a 1.5% overshoot at 66) and `--ease-travel`, the one long journey - plus five marquee durations, thirteen staggered plate ranges and eight strand durations with eight negative phases (13.3 to 19.3 seconds, none of them round). `landing-story.css` carries **19** `animation-range` bounds; `skeleton-bar.css` has written 1.4s since stage 07. **And the same expo-out literal is written in four more files:** `landing-steps.css`, `landing-paths.css`, `landing-facts.css` and `landing-plan.css` each declare `cubic-bezier(0.16, 1, 0.3, 1)` for their own entrance, because a curve declared as a custom property on one component's pin cannot be read from another. Five files, one curve, one name and four literals: the argument for the token in one line, and drift that has already happened rather than drift that might. The easings are the half obviously owed a token. The ranges are the harder half: a duration is a value and a range is a piece of choreography, and it is not obvious that choreography wants a token at all | Motion is a system-level language and this is the first surface that has any of it beyond a loading pulse. Tokenising it from one component would be deriving a scale from a sample of one | **`tokens.css`, at stage 11.** The census that opens the stage is at `design/kit/motion.html` and `docs/motion.md`, and it puts three more questions beside this one: whether the app's 26 instantly-changing components get motion at all, what happens between two states documented as two pages, and whether the landing's vocabulary is the product's | **Low, and it is scheduled rather than owed.** The values are correct and measured; what is missing is the shared name. **Two of this row's own figures were stale when the stage opened it** - it claimed 42 / 35 / 29s for three marquee tracks in `landing-story.css`, deleted with the centred stage on 2026-08-16, and three curves where there are four. Recounted by script in the same step, which is the rule this repo already has and the reason the census exists |
| **CLOSED 2026-08-16, in halves.** The centred shape of `landing-story.css` had no page since the hero was chosen, and its rules were written by no page in either corpus | **The dead shape is deleted:** `.storyfield`, `.srow`, `.strack`, `.stile`, `@keyframes sdrift`, `@keyframes sgather` and the four rules that switched them off, 89 lines. It could not wait for a quiet week: the first thing stage 11 does is take an inventory of what moves, and an inventory taken over rules that never run measures the wrong product | **The rename is answered NO rather than left open.** Dropping `.fromcircle` would be a rename across **257 selector sites**, 59 in this file and 198 in `landing-orbit.css`, every one carrying a scroll-driven rule and every one moving from 0-3-0 to 0-2-0 in a file whose cascade was tuned in place. It buys a shorter name and risks the one thing on the page that cannot be checked from a still. The class is the component's name now, and the file says so | **Proved inert:** twelve scroll positions on six viewports in both themes, before and after, with a second capture of the same code to establish the noise floor; every surviving difference was text-width jitter from font loading. The section is 2880px at 1440 and 2701 at 390 on both sides of the cut |
| **CLOSED 2026-08-16, on the founder's word, and it was FIVE rather than three.** The row named `60rem`, `75rem` and `85rem` in `landing-orbit.css`. Recounted by script with comments stripped, the system also asked `59.99rem` as 60's max-width companion, `80rem` of the PAGE container in `landing-story.css`, and one question that is not width at all, `@media (max-height: 42.5rem)` x2 | **Registered rather than redesigned**, in the container-threshold register of `docs/responsive.md` and on `responsive.html`, each with its origin. The ground: four of the five ask the NAMED container `story`, which is a question about one composition rather than about the product's layout, and `80rem` is `--container-wide`, a value the width block already declares | **The census on that page was stale too and is recounted in the same step.** It said `47.5rem` x8, `56.25rem` x5, `28.75rem` x2, measured on a corpus with no landing in it. Today: 47.5 x18, 56.25 x10, 28.75 x2, 80 x2 of the page container, 60 x6 / 59.99 x3 / 75 x1 / 85 x1 of the stage, and the height pair. 45 literals in nine groups | **Done.** What is left of the row is the rule it proves: an unregistered number is not a defect in the pixels, it is a number nobody can tell from a device somebody guessed at |

| **CLOSED 2026-08-16, the same day it was opened, because the movement made it untenable.** It was opened at step 2 with two components named and it was THREE: `.row:hover` painted all 60 of the row's placements including 13 `div.row.is-skel` on three loading screens, `.alert:hover` all 12 including 4 divs on `alerts-loading.html`, and `.tile:hover` all 16 including 6 spans on `add-subscription-loading.html`. A placeholder answered a pointer that had nothing to press | **`skeleton-bar.css` had carried the rule in words since stage 07** - a loading object "must not look hovered, focused or pressed" - and enforced only `cursor: default` on the row. The gap was survivable while the answer was a fill. It stopped being survivable at step 3, when the founder asked for movement: a placeholder that LIFTS under the pointer is a promise nothing can keep | **All three hovers are scoped to the pressable element form**, `:where(a)` and `:where(button)`, which cost no specificity, so nothing else in the cascade moved. The lift in `logo.css` is scoped the same way and excludes `.is-skel` a second time over. **Measured**: skeleton row, loading alert, loading tile and the current tab all read zero on both hover and press | **Closed.** The rule it leaves behind is the useful part: a verb that only fades can hide a wrong hover for a stage, and a verb that moves cannot |
| **The preset tile still sets a font-size on the muted line it hosts, and there is now an axis for it to use.** `muted-line.css` writes `.tile .muted { font-size: var(--type-meta) }` as a host exception from stage 08, because at that time the atom had one size and there was nothing to wear. On 2026-08-16 the atom gained `.fine`, a declared size modifier at the same value | A host writing type onto a hosted atom is the defect this system names in three files, and this is the last one left in the muted line. It was written when the alternative did not exist, and it renders correctly | **one step that owns the preset tile's markup.** Folding it in means putting `class="muted fine"` on every preset tile's line and deleting the host rule; zero pixels move either way. This step did not own that markup | **Low.** Nothing renders wrong; it is a rule that has an answer now and did not before |

## Found at stage 12 by the width sweep over the whole coloured product

The yield of walking **55 pages by 58 widths** where stage 10 walked 32. The four structural checks
came back clean at every one of the 3190 stops: no document scrolls sideways, no pane scrolls
sideways, no element leaves the viewport, no page changes the number of its navigation carriers.
**What the sweep found is prose**, seven blocks of it, and on the founder's word the same day
**all seven were fixed and are closed here rather than carried**. They stay in this file with
their measurements because the fix has to be checkable against the number that asked for it, and
because two of them leave a rule behind that is worth more than the pixels. What remains open
under them is ownership, not measure. Account: `design/rollout.html`.

**The seven, and the one sentence each of them cost.** A measure belongs to the element that holds
the words: `ch` is a unit of the element's own font, so a 52ch cap on a 16px box gives a 12px line
inside it about 69 characters, which is exactly how the landing's story head could look measured
and read at 80.2. Verified both ways after the pass: **zero boxes differ at 360 on all 55 pages**,
and at 1440 sixteen pages and 137 boxes moved, of which 32 changed width and every one of those is
one of the seven rules.

| What is missing | What it was needed for | Which level closes it | Priority |
|---|---|---|---|
| **CLOSED 2026-08-17. `.steps li` had no reading measure and ran to 81.2ch**, measured at a 750 window on `design/cancel-guide.html` (718px), and 60.8ch on `cancel-guide-blocked` | It is the worst line in the product and the least appropriate place for one: `numbered-steps.css` argues in its own header that this list is "read a line at a time while a person is doing something else on another screen", which is why it is full ink rather than muted. A 81ch instruction is the opposite of that argument | **`numbered-steps.css`.** The measure is not simply `--container-text`: the li carries a 40px gutter for its numeral, so the text gets 52ch only at `calc(var(--container-text) + var(--space-40))`. Three coloured pages and three grey ones | **Medium** |
| **CLOSED 2026-08-17, by replacement rather than registration. The story head capped itself at `38rem`, a width literal in no register**, in `landing-story.css` on `.lp-story.fromcircle .storyhead`, overriding the `--container-text` the base rule two blocks above it gives the same element | Stage 10's register exists so that a grep cannot tell a measured threshold from a number somebody guessed at. This one arrived on 2026-08-15 with the hero the founder chose, after that register closed, and it is the only unregistered width value left in the product | **`landing-story.css` plus the register in `docs/responsive.md`**, and it is a founder's call which way: 38rem registered with its origin, or the rule deleted so the base measure stands | **Medium.** It is on the public page |
| **CLOSED 2026-08-17 as a defect and KEPT as a rule. A 52ch cap on a box does not give a smaller line inside it a 52ch measure**, and the story head is the instance: the box is capped once at 16px and the lead inside it is `--type-meta`, so the same box reads 52ch for the heading and **80.2ch** for the sentence, measured at 720 | `ch` is a unit of the element's own font. Every other reading measure in this system is written on the element that holds the words, and this is the one place it is written on the container instead | **the same file, and the general lesson belongs in `architecture.md` beside U4**: the measure goes on the line, not on the box around it | **Low as a defect, high as a rule.** It will be written again otherwise |
| **CLOSED 2026-08-17. `.field .hint` had no measure and ran to 77.6ch**, 588px at a 640 window on `design/connect-bank.html` | Seven of the twenty fields in the product carry a hint and six of them are a fragment; this one is the paragraph that explains why an email address is being asked for at the trust moment, which makes it the one hint that is actually read | **`form-field.css`**, `--container-text` on `.hint`. At `--type-meta` that is 394px rather than 459 | **Medium** |
| **HALF CLOSED 2026-08-17: the measure is in, the owner is not. A bare `p` inside `.oncard` ran to 58.3ch** on `design/share-snapshot.html`, 588px at 640 | `.oncard` is a one-off with no component, so nothing owns the paragraph inside it and nothing gives it a measure. It is the same shape as the open `.lead` row above: a class used as a hook with no file behind it | **one-off, and it joins `.lead`'s row.** Either `.oncard` becomes a component or the paragraph takes a class that already has a measure | **Low** |
| **CLOSED 2026-08-17, and it was found by fixing another row. The Pro gate's sentence ran to 77.3ch**, 684px at a 750 window on `design/cancel-guide.html` | The sweep reports the worst prose leaf per page, and this one stood behind the 81.2ch step on the same screen: fixing the step promoted it. Its cause is 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`**, on `.gate > p`, which already existed as a rule and carried only a margin. Ink and size stay inherited; the measure is the one thing the slot declares. Five gates on five screens | **Closed.** The lesson it leaves: a "worst per page" instrument hides the second worst, so a fix pass has to re-run the sweep rather than trust the first list |
| **The instrument cannot tell a class that is defined from a class that is worn.** `rollout12.cjs` reports 0 classes the system does not define across all 55 screens, and `.oncard` passes that check because `eyebrow.css` names it as an ANCESTOR in one selector, `.app .oncard .k` | A class with no rule of its own is exactly the thing the check exists to find, and the check's own hole is worth more than its zero | **`rollout12.cjs`**, a second pass that asks whether the class appears as the SUBJECT of any selector rather than anywhere in one | **Low.** The zero is still true for what it measures, and this row says what that is |
| **CLOSED 2026-08-17, and the pass found a hole in the counter first.** The component pages described a 28 or 32 page coloured corpus 48 times across 24 files | `counts.cjs` had read **the CSS header alone** since stage 11, and the same sentence lives twice: every component page repeats it as "Lives in ... Stands on ...". The half it did not read is the half that drifted - `brand-mark.css` said 55 coloured pages while `brand-mark.html` said 32, and the run came back with zero disagreements. **An instrument that checks one of two copies reports the copy it checks, not the claim** | **`counts.cjs` reads both copies now** and prints them in separate columns, because the two can be wrong separately. It found **18 pages disagreeing**; all 18 are rewritten with the recomputed figures and their screen lists regenerated, and the reading today is 0 and 0. The other 28 occurrences were read by hand: 19 were live claims and are recounted, 9 are stage records and now name the corpus they were measured on ("the 28 coloured screens of stage 08") | **Closed.** Nine of the live ones were "checked rather than assumed" sweeps written when the corpus was 28 or 32 pages, so they were re-run rather than re-typed: `inert12.cjs`, both corpora, 55 pages each, and every one of the nine still reads zero |
| **CLOSED 2026-08-17.** Two pages in `design/kit/` were named by no registry, `kit.html` and `shell.html` | `design/kit/_nav.js` named 83 of the 85 pages in the folder, and `rollout12.cjs` counts that gap on every run. Neither page is a stray: one is the flat showcase stage 07 built and the README still links, the other is the template a coloured screen is copied from | **Two rows in the System group**, "The screen template" and "The flat kit, stage 07", each with what it is for. The count reads 85 of 85 | **Closed.** A page that is material and appears in no registry is what `CLAUDE.md` calls an artefact with no visible place, and the instrument now says so out loud rather than leaving it to be noticed |

## Found on 2026-08-17 by a founder's screenshot of the path-choice screen

Two defects and one open question, and the two defects have the same shape: **a rule was written
for a set of placements and its reason was read off a subset.** Neither was findable by any
instrument in this repository, because both render without error, without sideways scroll and
without a single class the system does not define. Both are visible in a picture.

| What was found | Why it stood | Where it went | State |
|---|---|---|---|
| **CLOSED 2026-08-17. The exit link sat at 0px from the door above it**, at every width, on the screen the whole product forks on | `grid.css` takes the bottom margin off `.grid.roomy` and writes the reason: "a door row DOES have a host: the empty block on 3 pages and the dialog sheet on 1". Counted over the four placements that exist, it is **off by one**, and the page it miscounts is `path-choice`, where the door row is a direct child of `.screen` with nothing between. The grid's zero met `.quiet`'s zero. The same pairing reads 24 after a tile row and 16 after an action row, so this was the only distance in the product nobody had chosen | **`app-shell.css`**, `.app > .screen > .grid.roomy` at `--space-24`, 0-4-0 against `grid.css`'s 0-3-0. The host of a hostless block is the shell, which is `grid.css`'s own sentence read from the other end. `grid.css` keeps its rule and its miscount is corrected in place | **Closed.** One placement of four moves; the three inside a host keep their zero |
| **CLOSED 2026-08-17. A 44px target inside a sentence tore the footnote open.** The trust line read as two paragraphs with a hole between them: 79px at 1440 where three lines of 18 and one of 44 is exactly 79, against the 53 the leading asks for, and 97 against 71 at 360 | `muted-line.css` gives `.muted.ruled > a` the floor as an `inline-flex` box, on the stated ground that "the footnote is a `p` whose link is the last thing in its own sentence". Read off all six placements, that is true of four and **false of `path-choice` and `data-privacy`**, where text follows the link. An atomic inline box hands its height to its line, so one line box of five was 44px tall mid-paragraph. **And being last in a sentence is still being in one**: the other four were paying the same 26px at the foot of the block, where nobody looked | **`muted-line.css`**, the ruled variant off the box rule and onto `padding-block` on an inline element, which grows the hit area and never enters the line box. `.trust > a` and `.muted > p ~ a` keep the box: those are direct children on their own line, and none of their 15 placements moves | **Closed.** Verified by box fingerprint over all 55 coloured pages at 360 and 1440: **6 pages differ and they are the 6 that host this link**, 49 identical to the pixel |
| **A flex column does not collapse margins, and the whole system's rhythm was written in block flow.** Past the 900 container point `.app > .screen` becomes a flex column, so two adjacent margins stop collapsing and start **adding**: 10 sibling pairs on 10 pages get a bigger gap on a desk than on a phone, and not one of the ten was chosen | Measured 2026-08-17 over all 55 coloured pages at 780 and 1024: `connect-bank` and `connect-bank-empty` 24 to 40, `history-trends` and `history-trends-locked` 24 to 40, `upgrade` and `upgrade-current-plan` 24 to 40, `share-snapshot-error` 16 to 32, `share-snapshot-loading` 16 to 24, and `path-choice` and `sign-in` **24 to 48**. The negatives the same sweep reports are the two-column layouts and the four `order` declarations on Home, and both are by design | **A founder's decision, not this pass's.** The honest fix is a `row-gap` on the flex column and every margin inside it deleted, which is one number replacing eleven and would move every flow screen. The alternative, zeroing top margins in the flex block, closes real gaps where the previous sibling has none | **Open.** The gaps are legible at both widths and no page breaks; what is wrong is that one product has two spacing systems and only one of them was decided |

## Found on 2026-08-18 by a founder's screenshot of the Upgrade screen

Three defects, two closed in the same pass and one handed on. All three have the shape this week
keeps producing: **a correction is only as wide as the selector it is written under.** None was
findable by any instrument here - the screen renders with no error, no sideways scroll and no
class the system does not define, at every width. All three are visible in a picture.

| What was found | Why it stood | Where it went | State |
|---|---|---|---|
| **CLOSED 2026-08-18. Three prices that a person is asked to compare did not start on one line.** Only the yearly card carries a "Best value" badge, the badge was a block in the flow above the price, so `$69` stood **32px below** `$7.99` and `Lifetime`, measured at every width from 768 to 1920 | The answer existed and was three days old. `landing-plan.css` hit the same defect on 2026-08-15, wrote down all three arrangements it tried and even quoted `plan-option.css`'s own `position: relative` as "the day" the anchor was needed - **inside `.landing .lp-plan`**, so the app's card never saw it | **`plan-option.css`**: the chip leaves the flow on both hosts with `margin: 0`, and each host says where. Top right corner here, the straddled top edge on the landing, on the landing's own axis argument. Measured clearance at the tightest width in the product: badge 66, `$69` 60.41, card content 201, **74.6px clear** | **Closed.** Price rows 0px apart at every three-column width from 760 to 1920 |
| **CLOSED 2026-08-18. The button label wrapped inside the cap that was widened because it wrapped.** "Start Tendd Pro - $69 a year" took two lines with "year" alone on the second, at every width from 768 up | Stage 07 widened the screen to 760 and the row to 720 on the stated ground that "both the button label and 'Everything in Tendd Pro' wrapped". Measured now: the eyebrow needs **181.23** and had 201; the widest button label needs **248.78** and had 201. **The arithmetic answered the eyebrow** | **`grid.css`** and **`app-shell.css`**: the row recomputed from the label at 57rem (248.78 + 34 a card, three of those and two 16px gaps = 880.34, and 57rem is the first register with half a `--space-16` of slack), the screen at 59rem, both in `rem` because a cap measured off a string has to grow with it. `.plans` also stops auto-filling - at the new cap it would have made **four tracks, the last empty** | **Closed above a 913px window**, and the band below it is the row under this one |
| **The empty states of the product are not designed as a set.** Founder, 2026-08-18: "нам надо тогда проделать состояния пустой без например подписок и их отсутствие, если есть сервисы например 1 или несколько и так далее" | Node 5.12's state table names five states and none of them is "one subscription" or "no subscriptions at all". The chart's own empty FRAME is a node decision and stands - block 8 refuses a blurred decorative chart and asks for an honest frame with the person's own labels - but the screen-level set is genuinely incomplete, and not only on this screen | **CLOSED 2026-08-20, and the missing axis was not "empty" at all - it was the COUNT.** Node 2.6 named four states and every one of them is about the CONNECTION: empty, refreshing, sync failed, save focus. What the founder was looking for is the ladder 0, 1, a few, many, of which only 0 and many had ever been drawn. Two states built through the full chain - node, grey, colour, microcopy: **`home-one` (2.6.5)** in the singular and **`home-few` (2.6.6)**, three rows ungrouped. **The list groups from six up, and six is measured rather than chosen:** a flat list shows five rows entirely above the tab bar at 360 x 780 and six at 390 x 844, so the sixth is the first a person must scroll to reach, and scrolling is what makes chunking worth the heading and subtotal it costs. The order changes with it - grouped, categories by spend, highest first; flat, rows by the next charge date, soonest first. **Nothing was added to `design/system/`:** the `.groups` wrapper stays with one headless `.group` in it, and `columns: 300px` fills one column for one group the same way it fills three for five. The six existing empty states were audited as a set at the same time and are coherent: two vocabularies, `.empty` with the two doors where there is nothing at all, and `.textblock` where the screen has content and the absence is local | **Closed** |
| **CLOSED 2026-08-18 BY THE FOUNDER, THE SAME DAY IT WAS RAISED. The trend line is not petrol, and petrol would transform it.** | D-Concept spent petrol inside a screen's content on three jobs and a data line was none of the three. `chart-placeholder.css` had carried that sentence since stage 07 | **Granted:** "давай теперь черним поменяем на брендовий, чуть градиентов добавим". D-Plot, recorded in `docs/decisions.md`, folded into D-Concept in `CLAUDE.md` and written into `DESIGN.md`'s One Voice Rule. The value is the new `--plot-line` role, read four times; the wash under it fades through a CSS mask behind `@supports` | **Closed.** It survives the rule it amends: 1 element on 1 screen, and no screen carries a plot and a filled action in one zone |
| **CLOSED 2026-08-18. The trend line was a ruler, because a stated refusal rested on a premise nobody re-checked.** "сделай нормальним и современним, а не как будто вайрфрейми ми перенесли" | The screen refused to invent a June figure, correctly. But June was never missing: the "What moved" list under the same chart says ChatGPT Plus is new since June at $20.00, and 172.90 + 20.00 is exactly 192.90, while Netflix's $2.50 is dated Jul 28 and uncharged | The path draws three monthly totals, **172.90 / 192.90 / 192.90**, as one cubic into June and a plateau after it. Round end dots were built and rejected by measurement: under `preserveAspectRatio="none"` a circle renders 23 by 13 at a 1140 box | **Closed.** A refusal to invent data is not the same as having no data |
| **CLOSED 2026-08-18. The trend chart was 2px tall at 1024 x 768, and nobody could see it because the sweeps were about width.** "надо сделать красиво" | Past the desktop point the screen is a flex column with a DEFINITE height, so every child with the default `flex-shrink: 1` is compressed. A block with `height: auto` has its content as an automatic minimum; a block that declares a height has no floor. Exactly one element in the coloured product declares a height | **`app-shell.css`**, `:where(.app > .screen > *) { flex-shrink: 0 }` at specificity zero, because it is a fact about a pane that scrolls and the same trap waits for the next block with a height. Plus `chart-placeholder.css`: the band and its label row take `--container-page`, the plot runs edge to edge instead of 6 per cent inside, an area is drawn under the line on the new `--plot-area` role, and every colour moved out of the SVG attributes into the component | **Closed.** 0 crushed children at three desktop sizes; the box is 168 / 220 at 14 sizes in both themes; the fingerprint moves the four History and trends pages and nothing else |
| **CLOSED 2026-08-18. Seven sentences sat at 0px on the block under them, and the founder saw one of them.** "нам би еще отступы покрутить би". A muted line declares a top margin and no bottom, which is right for the twelve of these lines that are the last block on their screen and wrong for the four that are followed by something, plus three more inside a card | The atom's margin is written for the footnote job and the same markup does a second job: a line of context standing BEFORE the thing it introduces. Nothing below it declares a top margin either, so the sum is zero | **`muted-line.css`**, `:not(:last-child)` at 24 on a screen and 16 inside a card, the two steps those hosts already use. And **`grid.css`**: the plan row's gap moved from the tile row's 8 to the door row's 16 on this file's own weight axis, and its bottom margin from a third value to the base's 24 | **Closed.** Zero text-to-surface pairs under 16px remain in the corpus that are not a declared value; the fingerprint moves exactly the six pages the sweep named |
| ~~Below a 913px window the plan button label still wraps. The cap is a ceiling, so between the row's 760 container point and the window where it binds, the three cards are narrower than the 282.78 the label needs. Measured after the fix: the wrap stops at 913~~ | Nothing is broken - `button.css` scopes `white-space: normal` to `.app .plan-opt .btn` precisely so this label wraps instead of overflowing, which is the 2026-08-13 fix for a 24px root. What is wrong is that the one control the screen is built around reads as two lines with an orphan word on a tablet | **Two honest answers and neither is the builder's.** A shorter label: "Start Tendd Pro - $69 a year" and "Start Tendd Pro - $7.99 a month" are both inventoried in `voice/docs/microcopy.md`, and node 5.13 block 7 is what puts the amount on the button. Or the row stacks inside the band, which is a layout decision nobody has asked for | **CLOSED 2026-08-20. The string got shorter.** "Start Pro - $69 a year" and "Start Pro - $7.99 a month". Voice owns it, node 5.13 block 7 still puts the amount on the button, and it fixes the same reading on a phone. Cheaper than a layout change nobody asked for |

## The owner sweep, 2026-08-18: no row may be addressed to a stage that has ended

The founder asked whether anything was left. Counting the answer found something the count
itself had been hiding: **twelve open rows were addressed to stage 12, which closed on
2026-08-17**. A row whose owner no longer exists is neither done nor refused. It simply lies
there, and every later count carries it as "open" without anyone ever being able to act on it.

Each of the twelve was re-read and re-measured before it was re-addressed, and the measuring
is most of what this sweep produced:

- **Four closed**, because the thing they waited for had happened and nobody had looked.
  `design/index.html` exists and is the coloured landing, so the row saying the folder has no
  index is answered. The share card's total renders at **32px**, the value its named change
  promised. The step marker's 4px shift has shipped and is measured at a 14px marker-to-text
  gap, with `numbered-steps.css` carrying the same sentence at the rule. And the `.landing`
  scope row lost its premise: the landing is not grey-only any more.
- **One dropped at verification, and the row was WRONG rather than stale.** It said
  `.muted.spaced` is byte identical to the base. The base is `.app .screen > .muted`, a DIRECT
  CHILD rule, and two of the three wearers in colour are not direct children of a screen.
  Deleting the modifier would have moved two screens.
- **Six re-addressed to the founder**, because what is left in each is a decision and not work:
  the icon set's balance, a size primitive for the 26px marker, the `.cut` merge, the `.plans`
  axis, a quieter step for `.muted`, and whether a token origin is owed per name or per run.
- **One re-addressed as work**: the eleven ARIA and form-semantics findings from stage 07,
  which is the largest piece of real work left in this file and the only row here a builder can
  simply do.

**The rule this leaves behind:** an owner column is only as good as the owners in it, so a
stage that closes owes a pass over every row still addressed to it. Nothing in this file may
name a stage that has ended.

## Found on 2026-08-18 by a quality sweep over the whole coloured product

One instrument, six checks, 55 coloured screens by two widths by two themes: page overflow, an
element crossing its parent's content box, a control label on more than one line, a tap target
under the floor, a text-to-surface pair under 12px, and an empty bordered box. It came back with
four kinds; two were the instrument's own false positives and are fixed in it (a preset tile is a
card and not a one-line control; a label is the target, not the input inside it). The two real
ones are closed below. **After them the whole corpus reports two text-to-surface pairs under 12px,
and both are legitimate**: a loading skeleton, and a group head sitting close to the list it
labels.

| What was found | Why it stood | Where it went | State |
|---|---|---|---|
| **CLOSED 2026-08-18. A standalone exit link with a 21px hit area.** `data-privacy` carries `<nav class="actions quiet"><a>Your sources</a>`, measured **86.97 by 21** with no padding: under the 44px floor and under WCAG 2.5.8's 24px | `quiet-line.css` refuses the floor with a good reason and logs the case it cannot reach - a link that might be inside a sentence. This is not that case: a `nav.actions` is a row of controls by definition and never a sentence, so the selector is exact and the exemption does not apply | **`quiet-line.css`**, `padding-block` with a matching negative `margin-block`, which is `muted-line.css`'s answer to the same question with one more term: the border box grows to 44 and the margin box stays 21. Nearest other target on that screen is 53px away, checked before it was written | **Closed.** Box fingerprint over all 55 pages: one box changed and it is this anchor |
| **CLOSED 2026-08-18. The gate's sentence sat 8px above its buttons, and the 8 was a rounding rather than a decision.** Five pages: `cancel-guide` and the four `subscription-detail` states | `panel.css` said so itself: "10px under it folds down to --space-8". It arrived by rounding kit.css's 10 onto the grid at stage 08, and it is visibly tighter than the sentence's own leading | **`panel.css`**, `--space-16`: the step this system settled the same week for a sentence and the block under it inside a surface, against 24 between two blocks on a screen. A gate is a surface | **Closed.** 5 pages move by 8px and nothing else does |
| **`.group-head.plain` produces three different distances for one job.** Counted over the coloured corpus at 1440: a plain `group-head` sits **2px** above what it labels on 31 placements, `.group-head.plain` sits **8px** above a `ul` on 5 and **16px** above a `div` or an `ol` on 4 | The follower's own top margin is doing the arithmetic, so the modifier's distance depends on what happens to come after it rather than on anything the modifier says | **Not fixed here, because fixing it means choosing which of the three is right**, and that is a decision about a component nobody has asked about. Recorded with the counts so the next person starts from a number | **Open. Whoever opens the group head next** |

## Found on 2026-08-23 by the accessibility run of stage 13

Two rows, both real, neither fixed here. Stage 13 documents what was built and does not finish
the product: an edit after the acceptance of stage 12 voids every pixel comparison it stands on.
The instrument is `design/kit/screens/a11y13.cjs`, its output is checked in beside it, and the
full checklist with the other thirteen rows, all confirmed, is `handoff/docs/a11y.md`.

| What was found | Why it stood | Where it goes | State |
|---|---|---|---|
| **Nine screens scroll sideways at 200% text zoom.** Root font size set to 32px, twice the browser default, which is what WCAG 1.4.4 asks and what a person with low vision actually does. **At 1440 nothing fails.** At 390: `cancel-win` by 83px, `history-trends-locked` by 46, `upgrade-renewal-failed` by 39, `upgrade-payment-failed` by 28, `cancel-guide` by 20, `history-trends` and `history-trends-loading` by 6, `alerts` and `alerts-empty` by 4 | The width sweep of stage 10 moved the VIEWPORT and held the text. This is the same question on the other axis, and nothing had ever asked it. The four worst are screens whose content is a fixed horizontal composition: a won-back figure, a locked range control, a payment notice | **Fixed in the SYSTEM, 2026-08-23, and not on any screen**: three declarations in three component files, none of them a composition decision after all | **CLOSED 2026-08-23**, all nine. Three causes and each already had a solved form in the product: a `nowrap` released by a container query in `rem`, which is the mechanism stage 10's two width points were written in `rem` FOR and the first time that refusal paid; `min-width: 0` on the segments of the range picker; and `minmax(0, 1fr)` where `.grid.roomy` had a bare `1fr`. **0 of 114 scroll sideways.** Zero boxes moved at 360 or 1440 |
| **The trend chart wears the browser's focus ring rather than the system's.** `design/system/behaviour.js` sets `tabIndex = 0` on the chart box so the keyboard can reach the cursor it builds; the box is a `div`, and `base.css` names the ring for `a, button, input, select, textarea, summary`. So the one interactive element in the product that is not a native control is the one outside the rule | It passes today and that is why nobody saw it: measured 5.78:1 in light and 6.40:1 in dark. But `outline: auto` is drawn by the ENGINE, so the number belongs to Chromium and not to this product | **Fixed: one selector in `base.css`**, which is what it was always going to be | **CLOSED 2026-08-23.** `[tabindex]` joined the six element names in `base.css`, so anything the product deliberately makes focusable wears the same ring. Measured 5.78 light and 6.40 dark, and now those numbers belong to this system rather than to Chromium |

## Found on 2026-08-23 by the clean-context reader of stage 13

Three rows, and every one of them is a place where the CODE is consistent and the writing around
it is not. They were produced by sending a reader with no memory of this project through it twice
and verifying each finding against the files before believing it; the whole log with its
verdicts, including the findings that were dropped, is `handoff/docs/onboarding-gaps.md`.

| What was found | Why it stood | Where it goes | State |
|---|---|---|---|
| **Seventeen surfaces still say the tab bar has five destinations, or still name Save.** The code has answered four since 2026-08-21, when the Save tab was retired. `docs/decisions.md` is excluded on purpose, because a decision log is supposed to hold the state it recorded. The seventeen are five coloured screens, seven stand pages, `design/overview.html`, `app-shell.css`, four IA and Voice documents and the wireframe hub | The retirement moved the CODE and the count, and nothing recounts prose. The surfaces are spread across five stages, and no single stage owns them | **Two were corrected on 2026-08-23** and the other fifteen are DATED by a founder's decision the same day. Correcting a sentence inside a closed stage's material buys a consistent read of a file nobody is reading against a real risk of rewriting history that was true when it was written | **CLOSED 2026-08-23 BY A DECISION, not by work.** Founder: they are DATED, not corrected. They keep the corpus they were written on, which is what the live-count rule provides, and `docs/decisions.md` 2026-08-23 is the naming |
| **Two coloured screens carry a class the design system does not define.** `rollout12.cjs` check 2 reports `context` on `home-few.html` and `home-one.html`, and no row in this file had ever named it | The instrument has reported it since the two screens landed on 2026-08-20, and `README.md` was calling `npm run check` "the two that must stay clean" while one of the two was not | **Fixed by the third answer neither option named**: the class was already declared, under the name the renaming map gives it in colour | **CLOSED 2026-08-23.** It was never a missing class: `context` is the GREY name and the renaming map turns it into `muted` in colour, which `home.html` and `guided-reveal.html` already do for the same line. The two newest screens had copied the grey and skipped the rename, so that line had been rendering unstyled. 68 boxes moved on two screens and nothing anywhere else |
| **The file the kit registry calls "the markup a coloured screen is copied from" is stale in three ways.** `design/kit/shell.html`: the `.app` element opened at line 96 is closed by a `</main>`, its tab bar carries three destinations against the product's four, and it puts a back control on a destination, which is exactly what usage rule U18 forbids | Nothing checks a template. Every instrument in this repository reads the product or the system, and the file a new screen would be COPIED from is neither | **Fixed, and given an instrument.** The prompt for adding a feature still names the nearest real screen rather than this file, because a template that is checked once is not the same as a template that is checked | **CLOSED 2026-08-23**, all three, and the hole under them with it: `rollout12.cjs` has a check 8 now that reads the template against the product - tag balance, destination count taken off `home.html` rather than typed, and U18. A registry that lies is worse than no registry |

## Found on 2026-08-23 by the acceptance sweep of the handoff page

One row. The stage page of the handoff was built by copying the shape of `design/rollout.html`,
and copying it copied a defect that had never been measured on either page.

| What was found | Why it stood | Where it goes | State |
|---|---|---|---|
| **The callout block on a stage account has no reading measure.** `.ro-note` on `design/rollout.html` carries no `max-width`, so at desktop width it runs the whole wrap: **116ch measured in the note's own font**, beside section prose that is capped at 78ch in the same file. The same block on `handoff/handoff.html` was fixed the day it was found | Nothing sweeps a stage page for a measure. The width instruments read the product screens and the pages sweep reads for sideways scroll, and a 116ch line does neither: it fits, it renders, and it is simply hard to read. It is the stage 12 lesson one file over - `ch` is a unit of the element's own font, so the measure belongs to the line | **Fixed. One declaration** | **CLOSED 2026-08-23.** `max-width: 78ch` on the note, in the note's own font |

## Found on 2026-08-23 by the first feature built from the documentation alone

Two rows, and neither of them is about the feature. `handoff/docs/one-shot.md` was handed to a
reader with no memory of this project, who followed it to the letter and built one screen with
four states; **the screen was a probe and was deleted**, so what survives is only what it found
about the product that was already here. Both were verified against the files before being
believed. Everything the exercise found about the PROMPT was closed by editing the prompt and is
not a backlog row.

| What was found | Why it stood | Where it goes | State |
|---|---|---|---|
| **A rewrite the log records was applied by halves.** `voice/docs/microcopy.md` line 149 logs the `data-privacy / export` body as "Exporting your **spend** history as a CSV ..." becoming "Exporting your history as a CSV ...", on the ground of D9 and Principle 3, whose whole point is dropping the word "spend". Line 1138 of the same file, and `design/data-privacy.html` line 114, both still read "Exporting your **spend** history as a spreadsheet". The CSV half of that rewrite landed and the "spend" half did not | Nothing checks a rewrite log against the inventory printed in the same file. The two halves of one row were applied by two different passes and the second one did not run, and a log that records a change nobody applied reads exactly like a log that records one that was | **Fixed. One word, in four files**: the inventory, its published page, and both editions of the screen | **CLOSED 2026-08-23.** The word is gone from the inventory, from its published page, and from both editions of the screen. The log keeps the old text, because that is what a log is for |
| **A shipped button breaks its own voice rule, in three places.** `voice/docs/voice.md` line 186: buttons are "Tendd's labels, so they say 'your', never first-person 'my' or 'I'." The label is **Download my data**, and it stands in `voice/docs/microcopy.md` line 1139, in `design/data-privacy.html` line 116 and in `design/data-privacy-delete-confirm.html` line 72 | It is one label on one screen, and the rule that forbids it is four hundred lines away in a different file. No instrument compares a line in the inventory against the rules of the voice, and this is the first time anybody read the two side by side | **Fixed in `microcopy.md` first and on the screens after**, which is the order a string changes in | **CLOSED 2026-08-23.** `Download your data`, in all six places: the inventory, its published page, two coloured screens, two grey ones, and the two kit specimens that print the label |

## Found on 2026-08-23 by the browser pass of stage 13

One row. The critique of this stage runs on two instruments, and this is the half a read-only
pass cannot reach: it is only visible when the page is open.

| What was found | Why it stood | Where it goes | State |
|---|---|---|---|
| **The hamburger on a stage account says "All screens" and opens the roadmap.** `design/_screen.css` line 167 generates that label after `.nav-toggle`, and on a coloured screen it is exactly right: the button opens the coverage map. `design/rollout.html` and `handoff/handoff.html` load the same stylesheet and the same button opens the PROJECT ROADMAP, which is thirteen stages and not one screen | The label was written for the 28 coloured screens of stage 07 and put in css rather than in markup precisely so that it would reach every screen without 28 edits. It reached two pages that are not screens along with them, and nothing looks at a generated word | **Corrected on `handoff/handoff.html` only**, in its own style block, because `_screen.css` is loaded by all 57 accepted screens and one line there moves the render of every one. `design/rollout.html` is frozen html and cannot take the same override | **CLOSED 2026-08-23** on both stage accounts, each in its own style block, because `_screen.css` is loaded by all 57 screens and one line there would move every one of them |

## Found on 2026-08-23 by the audit of stage 13, on the chrome nothing sweeps

Two rows, and both are about the same blind spot: **every instrument in this repository takes the
product and its grey twin, and stops.** The pages that carry the project's own navigation have
never been measured by any of them.

| What was found | Why it stood | Where it goes | State |
|---|---|---|---|
| **The project's sidebar fails WCAG 1.4.3 on every page that shows section links.** `.nav-section` in `/_nav.css` line 50 reads `var(--nav-muted)` at 13px, and `--nav-muted` is declared at line 10 as the colour for "SOON: not started, not a link". Measured against the panel: **2.65:1, where the floor for that size is 4.5:1.** Only the current link escapes, because that one takes `--nav-active`. Twenty pages declare `NAV_SECTIONS` | The token was chosen for a NON-LINK - a stage nobody can open yet - and reused for a link that a person is meant to click. `a11y13.cjs` sweeps the 57 coloured screens, `aria13.cjs` sweeps those and their 57 grey twins, and neither has ever opened a stage account | **Fixed. One token in `/_nav.css`**, from the grey reserved for a stage nobody can open to the product's own muted ink: 5.92:1, and still a full step quieter than the normal link. It moves no box on any page | **CLOSED 2026-08-23.** And the blind spot behind it is named rather than closed: no instrument in this repository sweeps a stage page, which is why a WCAG AA failure sat on twenty of them unmeasured |
| **Seven stage pages had no `main` landmark, and the row first said none of them did.** Each has a `nav`, an `h1` and a `footer`, and the content sits in a `div`. A page with landmarks and no `main` gives a screen reader nothing to skip to, which is the thing a skip link would skip to if the product had one | Same blind spot. The shape was set by the first stage account and copied eleven times, and nothing measures those pages | **Fixed on the seven that lacked one**, by renaming the wrapper element. No stylesheet here selects a wrapper by tag, so the element name was the whole change | **CLOSED 2026-08-23, and the row's own claim was wrong**: 13 of the 21 stage pages already had a `main` and 7 did not. Counted rather than assumed, which is the second time this stage caught a sentence that had never been measured |

## Found on 2026-08-23 by the first instrument that reads a page which is not a screen

**The row this repository could not close all day was that nothing swept a stage page.** The
accessibility run took the 57 coloured screens, the semantics run took those and their 57 grey
twins, and both stopped. A receiver meets a stage page BEFORE any product screen, because the
roadmap is how they arrive. `a11y13.cjs` has a check 5 now: 129 pages, 41624 text nodes, contrast
composited the same way it is on a screen, and the landmark questions. It found more than the
sidebar did.

| What was found | Why it stood | Where it goes | State |
|---|---|---|---|
| **2330 text nodes below the contrast floor, across 39 of the 129 pages.** Not a long tail: **eight colour literals caused 2203 of them**, copied from page to page across the documentation half of the project, and two of the eight caused 1871 between them | The documentation half was written before the design system and each page declares its own palette in its own style block. Nothing had ever measured any of it, because no instrument read these pages | **CLOSED 2026-08-23.** Ten literals darkened, each computed against **the worst surface it actually sits on** rather than against white - a first pass measured against white alone and 58 findings survived on the panel greys. **2330 to 6**, and the 6 are text on pages that DEMONSTRATE a colour: the contrast-grade letters in a table about ratios, and a token swatch. A picture of a colour is not interface text, and they are left visible rather than excluded by a typed list | **CLOSED** |
| **87 of 129 pages had no `main` landmark**, including all 82 stand pages. A page with a nav, an h1 and a footer and no `main` gives a screen reader nothing to skip TO - which is what a skip link would skip to, if the product had one | Same blind spot. The shape was set by the first stand page and copied eighty-one times | **CLOSED 2026-08-23. 0 of 129.** The element name was the whole change: no stylesheet in this repository selects a wrapper by tag | **CLOSED** |
| **11 stand pages draw more than one `h1`, and 8 pages skip a heading level.** `dashboard-head` draws eight `h1`s | On a stand page a specimen of a heading IS a heading, so most of this is the normal shape of the surface rather than a defect. Three of the eight skips are on real documents and are real | **Not fixed, and separated instead**: the check reports the three questions apart, because rolled into one number the specimens bury the documents. That is how 87 pages read as one problem when they were two | **Open. Whoever opens those three documents next** |

## Found on 2026-08-23 by adding one screen to a corpus of 57

One row, and it is about the instrument that guards every other change.

| What was found | Why it stood | Where it goes | State |
|---|---|---|---|
| **The box fingerprint measured the frame as well as the product.** `fp12.cjs` walked `document.body`, which on every screen includes the reviewer's chrome: the panel listing every screen, the theme switch, the roadmap. Adding one screen grew that panel by one row, so the instrument reported **5051 boxes changed on all 58 pages** for a change that touched two files | It had never been asked. Every earlier use of this file compared a corpus against itself, and the chrome only moves when the CORPUS does - which had not happened between two fingerprints until today. The promise it exists to check has always been about the product | **CLOSED 2026-08-23.** It walks `.app` or `.landing`, the root every screen declares the product to start at, with `body` as the fallback. Re-baselined and re-run: **0 pages and 0 boxes differ** | **CLOSED** |

## Found on 2026-08-26 by lifting the system into a product repository

One row, and it is the first finding produced by a reader that is not a browser opening a page
of this repository: the CSS pipeline of a real build, which resolves every url() before it
serves anything and refuses to build on one it cannot find.

| What was found | Why it stood | Where it goes | State |
|---|---|---|---|
| **Four `url()` in `tokens.css` point one level above `system/` and answer 404.** `--wash-pricing` and `--wash-final`, in both themes, read `url("../assets/...")`. A relative url in a stylesheet resolves against the STYLESHEET, so from `design/system/tokens.css` that is `design/assets/`, which does not exist. The files are at `design/system/assets/`. Proved before anything was touched: a plain file server answered **404 on `../assets/` and 200 on `assets/`**, both files, both themes | Nothing here ever asked. These two roles paint the pricing band and the closing band of the landing page, and past the tablet point only, so no app screen requests them at all. A missing background image fails silently: the band keeps its colour and simply has no picture, which is exactly what the fallback was designed to look like. The self-sufficiency test of stage 09 assembled an APP screen the system had never seen and reported 0 failed requests truthfully, because an app screen never asks for these. No instrument in this repository has ever compared a `url()` against the disk | **Fixed in the product repository's copy** (`Tendd-Product`, `system/tokens.css`, with the proof in a comment beside it). NOT fixed here: this tree is frozen at `v1.0`, and a hole found after acceptance is a row rather than an edit. Whoever unfreezes it next changes `../assets/` to `assets/` on four lines and nothing else | **Open. The founder, when this tree is next opened** |

## What is deliberately NOT in this file

Anything already fixed **and closed by a named instrument**, anything recorded in
`docs/tokens-audit.md` as one of the six named sources of a visual change, and anything a later
step is scheduled to do anyway. A backlog that lists the plan alongside the exceptions stops being
readable, and a backlog nobody reads is the same as no backlog.

Three things step 9 closed are therefore absent rather than listed as closed rows, because they
never had a row here: the focus ring's eighteen literals folding into `--ring`, the token package
(`--bg-callout` in the dark theme, plus `--control-accent`, `--bg-chip-accent` and
`--text-chip-accent` split off), and the violated freeze in `wireframes/`. All three are on
`pixel-proof.html` with their measurements, and the freeze repair is in `CLAUDE.md` as a rule.

Also absent: Codex's five references to `design/_theme.css`, **dropped at verification** at step 9
with the reason recorded on the proof page. All five describe the migration chain in the past
tense, and that chain is exactly what `CLAUDE.md` requires to be recorded, so deleting them would
erase the provenance the rule demands. It is named here so the next audit does not raise it again
as an orphan.
