Skip to content

site: Factory — a sixth design, a Defense Factory replica in SSCSB's own terms - #59

Merged
p4gs merged 32 commits into
mainfrom
design/defense-factory-r1
Sep 14, 2026
Merged

p4gs merged 32 commits into
mainfrom
design/defense-factory-r1

Conversation

@p4gs

@p4gs p4gs commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Factory — a sixth design, drawn in the Defense Factory's system with SSCSB's substance

Adds factory to the design registry (/_d/factory/): a direct replica of OpenAI's Defense Factory page as a system — its measured motion, its diagram grammar, its type and tint scale — with every diagram re-drawn for the problems and solutions in this project's scope. No copy, imagery or branding from the reference is used; the motion was measured from the reference frame by frame and re-implemented, not lifted.

This PR ships the state after three measure-and-judge rounds plus the first group of the final targeted pass, so it can be reviewed live. The rest of that pass (diagram connectors, honesty copy, polish) is in progress on the same branch and lands as a follow-up PR.

What it is

  • Aperture hero on a native animation-timeline (progress = scrollY / innerHeight), gated behind @supports (animation-timeline: scroll()); the no-support and prefers-reduced-motion branches both paint the settled end state (0 animations under reduced motion at both widths — measured).
  • Sticky pill nav with the reference's 0.25 s cross-fade, a scroll-spy that leads the heading, and mobile auto-scroll of the active pill; the same nav on the methodology page and every repo sheet.
  • Scroll-traced SVG figures (pathLength = 1, dash-offset bound to a 0.30 × vh band ending at figure-centre = viewport-centre), an autoplaying loop (2.5 s per stage, offset-path packet, dotted rings), a toggletip, a radio segmented control with no sliding thumb, and a static bar chart — all data-bound to scoring.ts, reclassify.ts, checks.ts, threats.ts and trust.ts, so the diagrams change when the registry does.
  • Two font families (Inter + JetBrains Mono, Google Fonts), nothing bold, colour encodes one thing, red only for a real fail.

Honesty additions (found by the security-reader lens, applied Factory-side)

  • A fifth verdict state, not in this record, on chip, key, phase note, table band and figures — ten registry controls that a v1 record holds no row for were previously folded into "out of scope", which moved two counts on one sheet in the flattering direction.
  • Every hover-only fact is visible text on a phone; no fact lives only in a title.
  • Still to land in the follow-up: the two-state split of the absent wording (absent everywhere vs absent from the scan but present in the signed local record), the coverage clause stating the absent controls are outside both published numbers, and the attack-group summaries' full denominators.

Validation so far

  • Three measure-and-judge rounds: an independent measurement agent re-running every proposed test at 1440 and 390 in real Chrome, then four judges (craft, mobile-UX, fidelity-to-reference with the reference frames side by side, security-reader). Round-3 scores: security 4/4/4, fidelity 4/4/4, mobile 4/4/4 at both widths; craft 4/4/3. All four: "ship with the listed fixes" — this PR carries the first group of those fixes.
  • Aperture within 0.044 px of the reference's measured table; loop cadence 2507–2512 ms; 350 scrub frames with monotone travel; 0 tap targets under 44 px; scrollWidth === clientWidth on all eight page/width pairs; fonts loaded, no serif face registered.
  • The other five designs are byte-identical to main outside the switcher block (regression diff re-run on this exact commit).

Gates on this commit

bun test 888 pass / 0 fail / 1 skip · tsc --noEmit 0 · bun run build 0 · forbidden-string greps 0 (no workstation paths, no "nobody could answer/check", no reference copy).

Left for follow-ups (shared copy and data, deliberately not touched in a design PR)

  • ATTACK_CLASSES A3/A8 address the maintainer in the second person on a page a consumer of the record reads.
  • Local-lane evidence strings carry absolute workstation paths; Factory redacts at render time, the class wants an upstream fix.
  • methodology-local.ts:55 "sit outside every denominator" contradicts the printed coverage formula; glossary.ts:46 "nobody could answer this check" — Factory overrides both in its own copy.
  • Seven registry controls appear in no published record; the page says so and does not guess why.
  • The five other designs' methodology pages still say "About a dozen" class-C checks (there are 16).

🤖 Generated with Claude Code

https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm

p4gs and others added 30 commits September 13, 2026 13:45
A faithful replica of the reference's design SYSTEM — its shapes, type scale,
chrome, section rhythm, diagram grammar and motion — with every word and every
diagram drawn from SSCSB's own data.

The pieces, and where each number came from:

* The hero aperture copies the mechanism rather than the look: `timeline-scope`
  on the stage, `view-timeline-name` on a 100svh response panel inset to exactly
  one viewport, `animation-range: entry-crossing exit-crossing 0%`, two bars at
  `calc(50% + 1px)` translating -/+100% -> 0 and two clipped halves translating
  -/+50% -> 0 on `cubic-bezier(0.333333, 0, 0.666667, 1)`, plus the two
  `step-end` tracks that fire together at progress 1. That makes progress equal
  `scrollY / innerHeight` over the first viewport and nothing else — which is
  only true while the opening panel is exactly `100svh - header` and the header
  is exactly the height it subtracts. The header's 64px is therefore load-bearing
  and is set from the same custom property the panel reads.

* Every diagram is rendered FROM the data. Nine bars are
  `controlsDefending(A1..A9).length`; the two the accent marks are computed —
  they are the groups where more than half the defending checks are ones only a
  maintainer's own machine can answer, which is A3 and A6 today and will follow
  the registry if that changes. The maze carries `PHASES.length` checkpoints on a
  serpentine generated from that count, with walls hashed deterministically and
  every edge the route crosses removed, so the route is always a legal path
  through it. Region count is `PHASES.length`, node chips and explorer cards are
  `CONTROL_REGISTRY` grouped by phase, and every question is `CHECK_QUESTIONS`.
  Nothing holds a hand-typed copy of the taxonomy.

* Nothing fades in on scroll and nothing counts up. A whole-document sweep of
  the reference found exactly two animated components and 22 transitions, all of
  them hover/focus/state affordances; three attempts to make its figures count
  measured zero mutations across a 3.1s scroll-in. The restraint is the design.

* Three branches, and the SETTLED state is the default: `MOTION_CSS` paints the
  finished frame unconditionally and only then turns motion on inside
  `@supports (animation-timeline: scroll())` + `prefers-reduced-motion:
  no-preference`. A browser without scroll-driven animation, a reader who asked
  for less motion and a printer all get the black response panel with its
  complete headline, the traces fully drawn with every checkpoint reached, and
  the loop standing still with its moving parts gone.

* The switcher is in the colophon, in document flow. Bulletin measured a fixed
  one painting over body text at some scroll position on every page, twice over,
  and reserving clearance at the END of a document cannot protect its MIDDLE.

Two deliberate departures from the reference, both because this is a directory
and not a launch page: its top-right search slot ships at `opacity: 0` and ours
is visible (drawn in mid-grey on no ground, so it reads on the white chapters
and on the black block alike — a fixed element cannot ask what is under it), and
its red is not used at all, because on this site red means fail.

Two canaries bumped rather than softened, as their own comments instruct:
`test/home.test.ts` and `test/layout.test.ts` each name the designs they cover.

bun test 705 pass / 0 fail / 1 skip. tsc 0. build 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
The existing suite already runs against Factory, so the vocabulary ban, the
honesty copy, the link integrity and the readability ceilings are covered. What
it cannot see is the half of this design that is CSS and markup structure — and
each of those failures is silent:

* A motion branch that stops matching does not throw. `@supports not
  (animation-timeline: scroll())` and the reduced-motion block are the two
  branches almost nobody renders, and both are the ones that must paint the
  SETTLED state. If either quietly stopped, a reader who asked for less motion
  would get a blank aperture and an undrawn diagram, and no test that renders
  HTML would notice.
* An `animation-timeline` declaration that escapes the `@supports` block is
  inert where it would have been honoured and invalid nowhere. It fails by
  doing nothing.
* A diagram that stops being rendered FROM the data keeps rendering. Nine bars,
  six regions, six checkpoints and 54 cards are all derived; a refactor that
  froze any of them into a literal would still produce a page that looks right
  today and goes stale silently.

RED FIRST, PROVEN RATHER THAN CLAIMED. The design was written before its test
file, so "red first" was demonstrated by mutation instead of by order: each
motion-branch and derived-diagram assertion was re-run against a source with
that one property deliberately broken, and each went red on exactly the test
that names it — an `animation-timeline` moved outside the branch, the
reduced-motion block's trace rules deleted, an aperture keyframe renamed, the
`matchMedia` check moved after the observer, the bar heights frozen to a
literal, the region list sliced to five, and the loop's `[data-running]` gate
removed. Seven mutations, seven reds, recorded under
MEMORY/WORK/sscsb-factory-design/build/red-first-mutations.txt.

Assertions are two-sided where an old form could linger and COUNTED where a
partial match would pass: a `.filter` whose predicate stops matching returns an
empty array, and an empty array satisfies "none of these are wrong".

bun test 744 pass / 0 fail / 1 skip (39 of them new). tsc 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
… them found

Self-validation at 1440x900 and 390x844 through headless Chrome with the
rendering lifecycle asserted live (visibility visible, 63 rAF ticks/s). Every
defect below rendered, passed the suite, and threw nothing.

THE APERTURE WAS 13px OUT AT 1440. Its progress equals scrollY / innerHeight
only while the opening panel is exactly one viewport minus the header, and the
search control's chips wrapped to a second row — so the response panel sat at
document y 913 instead of 900 and the whole range shifted down the page. The
table now matches the brief to the decimal at 390 and at 0 / 50 / 100 % at 1440.
Its 25 % and 75 % rows disagree by ~7px, and THE BRIEF'S ROWS ARE THE WRONG
ONES: both are annotated "interpolated between samples" and were interpolated
LINEARLY, while the ease is cubic-bezier(0.333333, 0, 0.666667, 1) whose y(t) is
exactly 3t^2 - 2t^3. Against the brief's own 50px sample series — a real
measurement — smoothstep puts scrollY 225 at -608.3 and 675 at -112.7, which is
what this renders.

WHAT ONLY A BROWSER COULD SAY:

* "repositorycan" at 390, where the stylesheet hides the headline's break and
  the two text nodes closed up.
* Seven tap targets under 44px, including two the FIRST probe could not see:
  the segmented options are label-wrapped, so they carry no [for] and the
  selector list never reached them. The probe was widened; the gap had survived
  three rounds.
* The triangle's local-lane node rendered solid. fill="none" is an SVG
  presentation attribute and ANY CSS fill rule beats it — three branches set
  one. The test caught the fix and was itself pinning the inert attribute.
* The loop's card type is sized in cqw with no container, so those units
  resolved against the VIEWPORT and every clamp pinned to its maximum: an 18px
  title in a 164px circle.
* The resting ring was a single dot. stroke-dasharray: 0,1 renders whatever
  pathLength says, and at pathLength="1" the gap is the whole circumference.
* The 390 rail's return packet survived BOTH fallback branches on specificity,
  so a reduced-motion reader got a static dot parked on the rail.
* The loop's centre pulse pushed 9px of document past the right edge at 390.
* An open toggletip widened the document — 6px at 1440, 17px at 390. No
  closed-state probe can see that; it took opening all 109 in turn and watching
  scrollWidth.
* The taxonomy explainer rendered white-on-white inside the black chapter. The
  shared components take their palette from bridge tokens and followed them
  faithfully onto the wrong ground: headings survived, every control id and
  incident line went pale-grey-on-pale-grey.
* Two of the eight methodology pills landed their headings under the sticky nav,
  because those sections are rendered by SHARED modules under their own class
  and a per-class scroll-margin never reached them.
* The listing side-scrolled at 390 where the spec asks for the card grammar.

All twelve are pinned two-sided in test/factory.test.ts. The reference's 40px
pill and segmented controls are deliberately 44px here — this site's shared
layer holds every other control to that floor, and the extra 4px is padding, not
type. Recorded as an escalation rather than a silent divergence.

Reduced motion re-measured at BOTH widths, which closes the brief's own §6 gap:
getAnimations() 0, the black panel with its complete headline, traces drawn with
9 of 9 checkpoints reached, the loop static with every moving part gone, and the
pill still tracking with its transition collapsed to 1e-06s.

bun test 756 pass / 0 fail / 1 skip. tsc 0. build 0. 0 tap targets under 44px and
scrollWidth === clientWidth on all eight pages.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
A1 · The opening panel now holds exactly kicker → headline → context line →
ONE pill-shaped search input on the page axis → the arrow. The "already
listed" chips are stripped from the shared control's output rather than the
control being re-implemented, so home-shared.ts stays the single source of
the ids filter.js binds; the strip throws if that block ever moves. They were
measured doing real damage — wrapping to a second row at 1440 and pushing the
opening panel 13px over one viewport, which moves the whole aperture range
down the page, and descending as a three-step staircase at 390.

The panel's container is the 1120 wrap now, not the 1376 media frame, and the
display scale is cut to 8.2vw so the longest line fits inside it: line 1 was
measured at 1182px hanging 29px past a 1120 container on each side. Kicker and
headline both balance, and six pixels of TRAILING PADDING optically centres
the display line — padding and not margin, because the element box must not
move or the probe comparing the five centres measures the shim.

A2 · The fixed search affordance is a plate at ≥768 — opaque white, hairline,
the reference's 1px shadow — instead of a transparent disc that read as a
smudge on whatever it covered. At ≤767 it is REMOVED: every route it offers is
already on the page, and 44px of fixed chrome is the most expensive square on
a 390px screen.

A3 · The explorer's segmented control pins at 72px (12 + 48 + 12) so it and the
chapter nav stack as two clean rows, which is what the reference's own frame
shows; below the nav's z-index, so an overlap resolves the only way it may. It
does not pin at all at ≤767. Every focusable diagram control without an id
gains the same 88px scroll-margin the id rule already gives the rest, so
keyboard focus can never park a control under the bar.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
A4 + E2 · `nav.fy-chapters` returned null on both secondary pages when the
measurement pass looked, so a reader who learned the navigation on the home
page lost it the moment they opened a listing — on the longest page the site
serves, where the controls table alone runs to 54 rows.

The sheet gets six pills over its real sections (the record · phases · what
ran · defences · who ran it · all checks) and the directory three (search ·
listings · the columns). Both lists are fixed rather than derived because both
pages render exactly those sections unconditionally; the sheet's conditional
panels (#merge, #coverage) are reachable from the section above them and would
otherwise make a pill that sometimes points at nothing. Every id is on a
container that already existed — nothing was invented to hang an anchor on.

`fy-chapters-tight` is the secondary variant: the reference's 160px of air is
right when the nav arrives after a whole black block, and a hole when it
arrives two elements into a directory.

Also, one line away and inside the same element: the dead `DIR_EMPTY_SCRIPT`
placeholder (§B16 / Max-13) is deleted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
A5 · The fifty-four typed cards were right and orphaned — a phase picker with
nothing on the page saying where the checks come from or what they produce.
The reference draws this component as a left-to-right flow (Inputs, a dotted
working viewport, Outputs) on a dotted-grid stage, and that shape happens to be
the true one here: three evidence sources feed the checks, and the checks
produce one record.

Inputs are the three sources typed by evidence class, so a tint on an input
card means what the same tint means on a check card two columns right. The
Record column takes the reference's emphasis type — white on an ink hairline —
and deliberately carries NO data-reference-type: it is an output, not an
evidence class, and the four tints encode the class and nothing else.

The key moves above the panel and reads as dots plus one dotted-square glyph
rather than four filled swatches.

Two geometry facts found by measuring rather than by reading:

  · The heads cannot live inside the bands. A band centres its content against
    the tallest column on the stage, so "Inputs" and "Record" landed hundreds
    of pixels below "Checks". They are the outer grid's own first row now.
  · Each band is its own two-column grid with align-content: center, which is
    what makes the wire's row exactly as tall as the column beside it — and
    that is what lets the bracket's arms at 16/50/84 % land on the three input
    cards' centres. Stretched to the grid row instead, those percentages come
    from the 54-card column.

The flow takes the media frame rather than the prose wrap: at 1120 the checks
column is one card column too narrow, which doubles the middle band's height
and drags the columns that centre against it down with it. Measured after:
grid 1326, bands 851, cards 2 × 396, all three column centres on y 12003.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
… boxes

A6 · The reference's grid is one tall narrow column, two stacked regions in a
wide middle, two stacked narrow ones on the right and a short full-width strip
across the bottom. Ours is now the same shape, and WHICH phase lands where is
computed from its control count — the two largest take the wide middle, the
smallest the short strip. Measured at 1440: narrow-left 310 × 1172, wide-middle
674 × 604 over 674 × 552, narrow-right 310 stacked, full-width 1326 × 264. At
390 it collapses to one column in PHASES order, which is source order, so every
slot rule is dropped rather than re-pointed.

Each phase now nests three deep: the region holds its plainly-answerable chips,
then a stacked card whose dashed 1…n group holds the checks only a maintainer's
machine can answer. That is the reference's own offset stacked-card edge, and
its 6px overhang sits inside a 16px region padding so it can never reach the
document edge.

The regions lose their hue (§D11's first clause, landed here because A6
rebuilds the same element): four tints exist on this page and they encode the
EVIDENCE CLASS. A phase is not an evidence class, so a tinted region spent the
same four colours on a second meaning — and the green one spent a verdict
colour on decoration. The one tint left inside a region is the class-C group's
violet, which is the class encoding again.

The P1…P6 badges are gone from the region corners. Chip icons carry
`data-cls` and take their colour from the class, so the glyph is what says
where an answer could come from — five classes, five distinct glyphs, asserted.

And the region note (§D14 / Max-6): "13 checks belong to this phase", never
"run here" — the scanner does not walk a phase. On the sheet, where the chips
carry verdicts, it splits into "· N answered · M with no answer", because
"13 checks" alone above a column of NO ANSWER chips reads as a contradiction.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
A7 + D8 · It rendered the same three panels three times, word for word, with a
fourth in the third lane: ninety per cent of the figure's area spent saying one
thing three ways, and at a glance it read as a rendering fault. Worse for the
contract, it emphasised the LOCAL lane — the weakest of the three — while
drawing lanes 1 and 2 identically, which erases the only difference that
matters between an outside scan and a signed CI scan. A reader left with "local
sees most, and the first two are interchangeable," which is the trust ordering
backwards.

Now: what every lane can read is stated ONCE in a shared row above them, with a
hairline under it saying it is the premise; then each lane shows only its
delta — nothing more, a signature, the sixteen checks no outside scan reaches.
The SIGNED CI lane takes the 2px #0285ff frame and a real `signed` marker on
it (real DOM text, not a CSS ::before — generated content is exactly what a
DOM-render capture drops). The local lane's one extra card takes the dashed
weaker treatment the +local badge wears everywhere else.

The closing line is "Two of the three can be checked by anyone. The third can
only be asserted by the maintainer, and is weighted accordingly."

And the arithmetic closes on the figure that invites it. 26 + 6 + 4 + 16 = 52
against a metric seven hundred pixels up that says 54; the two class-M controls
were dropped with no note. The footnote now reads "+ 2 about the tool — never
counted. 52 of 54 are scored", and a test adds the rendered counts and requires
them to equal it, so the footnote cannot drift from the panels.

The L-shaped label→group hairline brackets are restored, and drop out when the
row stacks rather than pointing across a gutter that no longer exists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
A8 · The route serpentined over a field of random wall stubs: it never turned a
corner the walls made, and the six checkpoints stacked on two edges because
that is where a serpentine's legs end. Now a seeded depth-first carve makes a
PERFECT maze over the reference's own 12 × 8 lattice of 40-unit cells — every
cell reachable, exactly one path between any two — and the route is that path,
which makes it a property of the maze rather than a line drawn on top of one.
Measured: 22 straight runs, 21 turns, 6 true axis reversals, 77 interior walls
plus the border, 39 cells of road. The checkpoints sit at real arc fractions
(points are one per 40-unit step, so an index into them IS the fraction), first
and last at the ends, minimum gap 0.21.

D5 · And what the figure DEPICTS changes, which outranks how it is drawn. The
scanner does not walk a route: it reads a clone and a settings API, once, and
produces every verdict from that one read. So the road is the path CODE takes,
from a commit to a published package, the checkpoints are where the standard
set's checks sit along it, and the beat reads "One record covers the whole
lifecycle." — with the T1–T7 reconciliation the home page never carried. The
figure's accessible name said "a single route through 6 checkpoints, one per
phase of the scan"; it now says what it draws.

The metric that read "6 — phases a scan walks, in order" reads "6 — phases the
54 checks are grouped into". A test scoped to the beat fails on walks, passes,
in order, or none is skipped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…ls that fit

A9 · The root scrolls smoothly, so a click on pill 04 sent the page THROUGH 02
and 03 and the spy dutifully lit each one on the way — a 250ms cross-fade fired
three times, which reads as the nav having a seizure. A click now marks its own
target at once and the spy is ignored until the scroll settles: `scrollend`
where the event exists, a 700ms fallback where it does not, and the flag is
released either way so one click cannot stop the nav tracking forever.

Over a dark section the nav inverts — #000 on a #333 border with white text —
written by the spy from whether the active section is inside `.fy-dark`, the
same class the black block itself is painted by. The reference never shows a
white pill on black, and a 1px #e6e6e6 hairline on #000 is simply not there.

The methodology's eight labels measured 1230px against the reference's 728, to
within 37px of the fixed search affordance, and stopped reading as one control.
They are Protocol · Threats · Scorecard · Evidence · Formula · Grades · Local ·
Changelog now; the section headings still carry the full titles, which is where
a label belongs once it has to be read rather than recognised.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…clipped

A10 · Connectors are ARCS. The reference's dotted connectors curve outward
around the ring, and a straight chord between two discs on a circle reads as a
different diagram. One quadratic does all five edges: the connector box is
already rotated to its chord's angle, and local −y after that rotation IS the
outward normal, so no per-edge sign is needed. The travelling packet rides the
same path, so an active edge differs from a resting one in colour and opacity
and in nothing else.

The centre is a bounded 220px circle — dot pattern plus a hairline ring — which
is what makes five cards read as orbiting something rather than as five cards
with texture between them. Its halo is circular and stays circular: a radial
gradient on a square paints a disc with four lit corners.

The brief's measured values are restored where nothing forced the change:
process margins −64/−112, connector base opacity 0.75, no opacity on the
resting ring, and the card title/body clamps back to 20/15.

Which surfaced a real defect. `padding: 7%` on a 193px card resolves against
its CONTAINING BLOCK — the 840px process box — so it computed to 58.6px a side
and left 75px of text width inside a 193px circle; "One commit, one snapshot"
wrapped onto FOUR lines. A 26px length gives the inscribed square of the
circle, and the centre's 8% padding had the same bug (84px of text in a 218px
disc). Measured after: every caption 1–2 line-boxes at 1440, store title 172px.

E1 · At 390 the rail inherited all of that desktop geometry and nothing reset
it: the store's title rendered SIX of its twenty-eight pixels with zero dark
pixels in its glyph box, card 05 was cut by 63px and every card lost 10.7px of
its right edge. The stage's `overflow: clip` is what hid it. The compact block
now undoes the geometry explicitly, the halo — a property of a disc — goes
where the store is a card, and the clip goes with it. Measured at 390:
scrollWidth === clientWidth and scrollHeight === clientHeight on the stage, all
five cards and the store and its title fully inside it.

There is no hardcoded `.fy-compact` any more (Max-10): the rail is a property
of the viewport, so the media query is the only thing that knows about it.

D9 · The centre said "the signed record" — a universal two of the four trust
kinds do not have: an external scan produces an unsigned record, and so does a
CI run without id-token. It says `the record` / `scan-record.json · signed when
the lane can sign it` now, and the lead says the same. The caption "Shared
context" named nothing a reader can point at and was stranded 25px below the
stage border; it is the centre's third line, reading "One record. Every stage
adds to it."

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
A11 / Max M1 · `--fy-window-viewport-height` was referenced by the aperture's
`view-timeline-inset` and defined by nothing. An undefined custom property is
invalid at computed-value time, so the inset silently fell back to `auto` — and
`auto` happens to coincide with the intended inset at exactly one viewport
height, which is why it looked correct in the one place anyone measured it. It
is defined on `.fy-track` now and the response panel's min-height reads the
SAME property at both widths, so the panel and the timeline's inset cannot be
changed apart.

The guard is class-level rather than a check for that one name: every
`var(--fy-…)` in the stylesheet without a fallback must be defined somewhere —
in the CSS, by the script's `setProperty`, or as an inline style a renderer
writes. Proven red by deleting the definition.

Max M2 · The loop and the traced figures were gated on `@supports
(animation-timeline: scroll())`. Only the APERTURE uses a scroll timeline. In
an engine with keyframes and JS but no scroll-timeline support — every WebKit
before 26, every in-app web view — the loop's rings and packets were
`display: none` while its state machine kept flipping stages, and the traces
drew with every checkpoint already lit because the `[data-reached="false"]`
pending rules were unreachable. It fails by doing nothing, in the browsers
least likely to be the ones it was tested in.

The traces' pending rules and the whole loop block are in a plain
`@media (prefers-reduced-motion: no-preference)` now, which is how the
reference gates both. The `@supports not` branch paints the aperture's settled
state and nothing else — its `display: none` on the loop's moving parts turned
a missing CSS feature into a dead diagram. Reduced motion still settles all
three, because all three are what a reader asking for less motion is asking
about. Proven red by moving the gate back inside the supports block.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…they open

A12 · The sort control keeps its id and its element — filter.js binds
`#dir-sort`, and a design may restyle a control but never replace one — and
wears the design's chrome instead of the platform's: appearance none, a 999px
pill on a hairline, 44px, a focus ring. Its chevron and the checkbox's tick are
BACKGROUND IMAGES rather than generated content, because generated content is
exactly what a DOM-render capture drops, and a control whose only affordance
vanishes from every screenshot is a control nobody can review. The coverage
checkbox is a bordered 20px square in the design's radius with 10px to its
label, and the listing count has its own right-aligned cell at the end of the
bar instead of crowding two controls it has nothing to do with.

A13 · The repository name was ink-black with no underline: on a directory whose
whole job is to be opened, the primary target was the one thing on the row that
did not say it was one. It is blue and underlined now, like every other link on
the site, with a `View record →` affordance under it at 96 × 44. The card's
phase bars take the sheet's geometry — measured, the bar stack is now exactly
the card's content width at both widths (211.1 at 1440, 316 at 390) instead of
being boxed to 190px in a table cell.

The MERGE pill's centre sat 27px below the top of the three-line text it marks,
because the shared 44px summary rule centres a summary vertically — right for
the one-line case, wrong here. Fixed on specificity rather than order, since
that rule is declared later; re-measured, the pill's top and the summary's top
are now the same y.

E4 · Five standalone links measured 17px tall — "Install the Action →", "How
that is checked →", "Search, or request one →", the repository URL and "scan
run". Each is the whole content of its own block, so the WCAG 2.5.8 inline
exception covers none of them and this site holds every other control to 44.
Links inside running prose keep the exception and are deliberately untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
… whole voice

D1 · The response panel said "— and what nobody could check." That is FALSE,
and this site is the thing that disproves it: a class-C check is answerable by
exactly one party, and the local lane exists so that party can answer it. It
reads "— and what only its maintainer could." now, which is the same sentence
shape and a true one, and the supporting line that carries the rule is
unchanged.

The caveat is also above the fold. The opening panel's context line is "Every
listing here is a public record of one scan of one commit — what it could
prove, and what it could not", so a reader who never scrolls past the first
viewport has still met the claim the whole directory rests on.

(One occurrence survives on the directory and the sheet: the SHARED plain gloss
of `unverified` in glossary.ts reads "nobody could check this". Shared copy,
out of this design's scope, recorded rather than reworded. The test is scoped
to the pages Factory writes.)

§B2's font half · Source Serif 4 is gone from the head and from every rule that
read it — the pull-quote and the inline term glosses. A third family, with an
italic axis, requested on every page of the site so that two paragraphs could
be set in it. The test is two-sided: gone from the head AND gone from the CSS,
because a dropped @font-face with a live `font-family` falls back to Georgia
and the page still looks deliberate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…e visible

D2 (which supersedes A14) · The track spanned pass + fail + gap + unverified,
so a phase with five answered checks and two nobody could answer drew a green
bar filled to 71.4 % — and printed 100% at the end of it. A sighted reader had
no legend, no count and no label for the hatch: they either read the bar as a
proportion and concluded the number was inflated, or read the number and
concluded the bar meant nothing. And the hatch WAS the unanswered set, drawn as
the empty tail of a pass-green progress bar — the visual grammar of shortfall,
which is exactly the third state rendered as a deficit that threats.ts forbids.

The track is the ANSWERED set now, its green segment is the phase's own percent
and its complement is what did not pass, so the bar's width IS its number.
Measured live at both widths on the fixture sheet: pass width 100% against a
printed 100%, on all five rows that have answers.

The sentence that explained it existed, written well, in an `aria-label` — the
one place no sighted desktop reader and no touch reader will ever meet it. It
is a line of its own under each bar now, in its own words: "100% of 5 answered
· 2 no answer (not counted)". One three-state legend sits above the stack on
the sheet, and deliberately not once per card on the directory, where the
page-level key already names the same three states.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
D3 · A local-lane verdict now reads WEAKER at the row. `commit-signing`
rendered a green PASS identical to `secrets`; the header said `+local 6` and
the panel said beautifully that a workstation record is weaker than an
authenticated scan — but the row is the only place a reader decides whether to
trust an individual control, and there was no way on the page to find out WHICH
six greens were the repository owner asserting his own posture on his own
laptop. Every chip and every evidence-table row the local lane resolved is
dashed and says `local`, and the word links to the panel that says what that
signature proves. Only a COUNTABLE verdict can be marked: a "no answer" chip
labelled locally-resolved would be two contradictory claims in one chip.

`+local 6` was uncountable — six what, and which six — with the sentence that
answered it in a `title`. It reads `+6 from a local signed record` and is an
anchor to the rows it names.

D4 · The contradiction badge is in the hero, before any figure. types.ts
requires it "on the listing row AND on the detail page" precisely because it is
the most interesting fact the directory holds about a repository, and it was on
the detail page — in the seventh section, below every number a reader acts on.
A visitor arriving from a shared link read A+ / 100% / not provisional and
left. "The grade is not provisional" is qualified in the same breath where the
sources disagreed, with its own link to #merge. The wording is words
everywhere: "DIFFERENT COMMIT — the maintainer's local record describes …, this
listing scans …", never MERGE and never ≠, which are a mathematician's notation
standing in for the thing readers came to find out.

D10 · Both figures name what they are a fraction OF. 87.1% on a page whose home
had just taught "54 checks in the standard set" reads as ~47 answered; the true
figure was 27 of 31 in scope, off by a factor of 1.7 in the flattering
direction, and the strings 31 and 27 appeared ZERO times in the rendered page.
The counts are derived from the rows the table below actually shows.

D12 · `no answer` and `info` were fourteen units of grey apart, differing on one
channel plus the word. `no answer` is dashed with a struck label; `info` loses
its pill entirely, because it is not a verdict and a pill is what a verdict
looks like here. (The three-state legend landed with D2.)

The fixture for all of this is new and deliberate: the default one carries no
local lane and no contradiction, so every claim above would have passed
vacuously against it — the two facts this design is most obliged to surface are
exactly the two it has never had.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
D6 · The accent singled out A3 and A6 — the two groups defended mostly by
checks only a maintainer's machine can answer. The claim checks out against the
data, and THE CHART COULD NOT SHOW IT: A8 is also 5 and grey, A7 is also 8 and
grey, so the emphasis rule was underivable from anything plotted and a reader
had to take the caption's word for it. Each bar is two segments now —
answerable from outside, and only on a maintainer's own machine — so the fact
is readable off the figure, with no bar singled out and the brand accent out of
a chart about risk entirely. The hatch is a 3-of-7 duty cycle at 42% white,
which averages the 0.18 the second fill wants and delivers it AS a texture, so
the two segments survive greyscale. The caption describes what is drawn and
carries a key for the two fills.

D7 · The triangle asserted three relationships that do not exist: that lane 1
relates to lane 2 as lane 2 relates to lane 3, that the set is closed, and that
there is an edge from the weakest lane back to the strongest. What this site
believes is an ORDERING, and an ordering is a line. It is an open path of
exactly two segments down a three-rung ladder now — the project's own signed
build, a scan from outside, a maintainer's own machine — with the hollow node
at the weak end and the words "strongest" and "weakest" at the edges. The
headline is "Three ways a record gets made, and they do not carry equal
weight.", which also retires the second-person line that addressed a maintainer
on a page a consumer of someone else's record is reading (D15).

Measured after moving the rank words: at x 40 "strongest" ran straight through
the first node, so the ladder starts at 120 and the words sit outside it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
D11 · The green tint pair is gone entirely — it existed only to colour a phase
region, which is not an evidence class, so a verdict hue was being spent on
decoration. A test now walks every remaining use of the pass hue and requires
its selector to be a verdict, a grade ring or a key. `meta` takes a DOTTED
border instead of a fifth shade: "about the tool, never counted" is not a fifth
kind of evidence, it is the absence of one, and dotted is how this page already
says "not a thing that was scored". And `scored before v2` was painted amber —
this page's `gap` colour, which says something went wrong, when what happened
is that the rules are versioned and this record names its version. It is
neutral grey and reads "scored under methodology v1 →".

D13 · threats.ts is explicit that `sourced: "primary"` is a claim about THIS
SITE — that someone here opened the CVE record, the government alert or the
project's own write-up — and it exists because one incident once wore the mark
by pattern-match rather than because anybody fetched it. The methodology page
made that claim and marked its exception; the home page rendered the same
citations with no tier claim and no slot for the mark, so the first time a
class's lead incident was `reported` it would have cited journalism as fact,
silently, on the higher-traffic page. The claim is above the A1–A9 list now,
and the renderer FAILS CLOSED: an incident whose tier is missing or
unrecognised renders no incident line at all.

D14 · The explorer's panel note was the last "run in this phase" on the site.

D16 · Local-lane evidence carries absolute workstation paths onto a public page
— a real name, and the shape of a machine nobody asked to have described.
`redactHome` rewrites `/Users/<name>/` and `/home/<name>/` to `~/` before
display, proven through a record rather than by reading the renderer. The
scanner's own output and the sweep of the other five designs stay Remaining
Work in the ISA; this is the render-time floor while that is pending.

D15's half that this design owns landed with D7 (the ranked figure's headline).
The A3/A8 second-person lines are shared ATTACK_CLASSES data and stay out of
scope, as the ISA records.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
A15 / Max-3 · The CSS comment claimed "0.2 s out the way it came — side-aware"
and shipped only the enter keyframe, while the script set `hidden`
synchronously: a 40 ms stroboscopic series over 0–240 ms found the panel gone
at EVERY sample, delay 0 included. It vanished in one frame, which is exactly
what a 0.4 s entrance makes conspicuous. There is a real `fy-tip-exit` now, it
leaves by the offset it arrived on, and the script hides the panel on
`animationend` — with a 400 ms belt for an engine that never fires one, and a
state check so a panel re-opened while a close is pending cannot be hidden by
it. Under reduced motion it closes at once.

Max-4 · `role="dialog"` with focus left on the trigger behind it is a dialog a
keyboard reader is standing outside of — the close button, the body and the
links were all downstream of a tab order that had not moved. The panel carries
`tabindex="-1"` and takes focus on open, with `preventScroll` so a panel
anchored below the fold cannot jump the page; Escape still returns focus to the
trigger.

Measured live at 1440: on open, `document.activeElement === panel` and
`scrollWidth === clientWidth`; on close, `animation-name: fy-tip-exit` at 0.2s
with opacity 1 → 0.69 → 0.39 → 0.12 still in layout, then hidden.

Also Max-11's phantom allowlist entry: the restraint sweep allowed
`var(--popover-enter-animation-name)`, a name nothing in this tree ever
animated — an allowlist entry that could only ever hide a real one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
D17 · Every `title=` and `aria-label=` in the design tree was enumerated and
classified: 34 source sites (10 titles, 24 accessible names), rendering 32
titles and 392 labels across the four fixture pages and 62 / 400 across the
built tree.

Ten carried a QUALIFYING fact — a threshold, a denominator, who could answer,
or the word "weaker" — and are surfaced or removed:

  · the `provisional` chip's "Evidence coverage below 75%" title is gone; the
    threshold is in the coverage note and the directory's terms note;
  · the four lane chips lose their titles, and `laneKey()` prints trust.ts's own
    four sentences under the listing — including the one saying the local lane
    is WEAKER than the action lane, which is the single most important
    qualifier the directory carries and which lived on hover;
  · the phase bar's per-segment counts are gone from `title` and printed under
    the bar (D2);
  · the chart's aria-label clause, both traced figures' labels and the phase
    row's label are each mirrored word-for-word on the visible layer.

Twenty-four are accessible names that assert nothing — Close, Read on, the nav
and region names, `grade A+`, the six phase-name labels (each also in the row's
own accessible name and on the sheet's phase bands), `Find a repository` (which
already had a visually-hidden twin) and `sscsb verify raw outcome` (glossed
visibly by the section it sits in).

One `title` on a Factory page is written by a SHARED module — the `reported`
chip's gloss in threats-shared.ts. Out of this design's file scope, and its
fact is stated in the same section by the sourcing sentence above the list, so
the title duplicates rather than hides. Asserted as such.

The test enumerates every distinct title value across the four pages, so a new
one cannot appear without this list being revisited.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
The browser pass found two things no HTML test could have.

The compact loop card was `position: static`, so its active and arrival rings —
absolutely positioned at inset −1px — took their containing block from the
whole loop. Measured at 390 they were 310 × 686 and painted a blue box round
the entire rail for the 120 ms of the release animation. A still screenshot
caught it; a geometry probe of the CARDS never would have, because the cards
were fine.

Making it `relative` then made it OBEY the desktop rule's circle percentages —
`inset-block-start: 40.729%` is ignored by a static box and honoured by a
relative one — which scattered all five cards and pushed the document to 575px
wide at 390. Both are reset now, and both numbers are in the test's comment.

And `.fy-row-lane`, the `local` link D3 added to each evidence row, measured
51.2 × 32 at 390: the card restack blockifies it out of the WCAG 2.5.8 inline
exception, which is exactly why it passed at 1440 and failed at 390. It reaches
the floor. Re-measured: 0 targets under 44px on all four pages at both widths.

§B17 / Max-9 · One comment at the prose-margin clamp, recording that Factory
sits at ~4.7 of its 5.0 for a structural reason — 54 questions rendered as
cards plus 54 more in toggletips pull its home and repo means down, widening
the gap from the other end — and that a red here on Factory ALONE points at
the shared methodology copy, not at Factory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
… content

R1. The mobile rules `.fy-flow-head, .fy-flow-band { grid-column: 1 }` are
(0,1,0) and lost to the desktop `.fy-flow-head[data-flow-head="in"]` family at
(0,2,0), which nothing overrides — so the three-column track survived the media
query and computed to `0px 186px 130px` at 390: "Inputs" broke to one character
per line inside a zero-width track, its cards overflowed their own column,
"Checks" landed on top of them (the site's only text overlap), and "Record" sat
2186px above the card it names. The document never widened, which is why only a
computed-style probe found it. Matching the attribute makes the mobile rules
(0,2,0) too, and the later block wins.

Also here, same figure: the stage's border is dotted like every other stroke in
it, and the three bands top-anchor at 1440 so a head sits on its own content
rather than naming a column that starts a third of a screen below it. Nothing is
capped — the figure's point is that it holds every check.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…t all

R2. The sheet stated two different out-of-scope counts and attached the bigger
one to the coverage claim: `21 of the 54 … out of scope` against the table's
`11 out-of-scope checks are greyed`. The gap is exactly the ten registry
controls the record holds no row for — binary-artifacts, webhooks,
dependency-pinning, socket-firewall-ci, publish-targets, trusted-publishing,
publish-tokens, publish-provenance, dist-manifests, maintainer-mfa — folded into
"out of scope", which is a claim about a repository that nothing measured. It ran
in the flattering direction: those ten sit outside the coverage denominator, and
counting them as unanswered would put coverage under the 75% floor, so "clears
the floor" and "the grade is not provisional" both rested on the guess.

So `not in this record` is a state: on the chip (dotted, muted id, the words),
in the key, in the phase note, as its own band in the All-controls table so "see
the table" is true, and in the figures sentence as three counts that each come
from the rows supporting them and sum to the registry. The phase note is a
partition now — it left four of thirteen chips unaccounted — and `phaseBars`
says which kind of empty a bar is: "no rows in this record" is the record saying
nothing, "no checks in scope" is a decision it made. P6 asserted the second while
the table said the first, for the same phase.

Also: the hatch is gone (it had become a legend for itself), the key draws the
real pills, and the directory card prints the denominator its percentage is of.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…se sentences

R3. `info` was rendered 33 times on a sheet and named in none of the three keys
on the page, while the figure above it said the same thing in plain English. The
chip says `out of scope` now, every state that can render appears in every key —
drawn as the page actually draws it, real pills rather than swatches that matched
nothing — `gap` is amber everywhere (the table key painted it red), and the
explorer's Record card promises the words the sheet actually prints.

R4. Red means `did not pass` on this site; the contradiction family was painted
in it, on listings with zero fails and grade A+, which is this ISA's own
anti-claim. Amber is taken (it is `gap`), so the badge, the panel and the row
take the neutral ink hairline the directory row already used for the same fact.
`conflictBadge` returned on a stale commit BEFORE it tested contradictions, so a
listing carrying both swallowed the one `types.ts` requires; it renders both.
The `reported` source-tier chip stops wearing the gap amber.

R5. "outside every denominator" is false under the site's own printed formula —
an unanswered in-scope check is inside the coverage denominator, which is exactly
why coverage reads 90.9% and not 100%. And the glossary's "nobody could answer
this check" is the construction the hero was corrected away from, false on a
sheet where eight of them were answered by the maintainer; Factory renders its
own wording on all four pages, including inside one shared block it renders
whole — by rewriting the output with a loud guard, never the shared module.

R6. The re-verify links now say the signed bytes still carry the paths this page
redacts; the row stops printing "vs" between two identical grades; and the home
metric reconciles 16 in the standard set with the ten or eleven in scope.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…loop is a loop

S1. The chapter rail was pinned to 278px inside a 344px column and inset ~34px
from every other element on the page. Three costs, all at 390: one pill less of
capacity on the two longest pages; a visibly indented left edge; and — because
the opaque plate was NARROWER than the measure — a line passing under it stayed
visible on BOTH sides, so "ar … an" and "ings" survived beside the chrome as word
fragments, which reads as a rendering fault rather than as chrome. It spans the
gutters now and clips at the screen edge, as the reference's own 390 rail does.

S2. Five methodology tables are 560-640px inside a 344px window — including the
one carrying all 54 checks, their questions and the groups each defends. Every
question was cut mid-word at the viewport edge and the third column was off
screen, with no fade, no shadow and no scrollbar to say so; the document never
widens, which is why every overflow probe stayed green. They take the design's
own edge mask, a focus ring, a tab stop and a visible cue.

S3. The compact rail was five stacked cards with no connector and no return, on
the chapter called "The loop" — and the 36px gutter the reference fills with its
return line was reserved and empty, so the rail also read as accidentally
off-centre. The cards and connectors live in two sibling containers, so both are
flattened into the rail's grid and ordered from a property the renderer derives;
the fifth edge is drawn as the return bracket in that gutter, the step number
moves into a left gutter beside the title, and the padding is symmetric — 37px
each side, measured.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…stop staircasing

S4. Four centred wrapped rows — the explorer KEY, the maze legend, JUMP TO PHASE
and the footer switcher — each captured their first item with the run-in label
and then landed every wrapped line on a different left edge. For a key that
defeats the one thing a key exists to do. The label takes its own line and the
items align to one edge.

S5. Every chart label was SVG text, so its RENDERED size was whatever the viewBox
scaled to: 16px declared measured ~12px at 1440 and the A1-A9 row was dropped
outright at 390, leaving nine unlabelled heights on a bare axis with the list
that decodes them several screens away. The labels are HTML now, in real CSS
pixels, positioned from the same plot geometry the bars are drawn from — bar and
id centres agree within 0.4px, measured — the ids are mono like every other
identifier on this site, and the chart gains the one centred axis word the
reference has. The mobile stretch moves from a transform to the wrap's own
aspect-ratio, so the drawing stretches and the type does not.

S6. Same fix on the ranked figure, where the three names ARE the content: ~8-9px
at 390 in light grey on black, and the middle label printed across the trace.
They are HTML at 14px, placed off the line's own corridor — the line descends, so
"above, centred" is clear for the first node and crosses it for the other two —
and the weakest node finally gets the dash D7 asked for alongside its hollow.

S7. A nested chip keeps its id in one piece: the class-C group gives back its
indent and the pills wrap instead of the identifier.

S8. Counters go 1-up at 390, the hero kicker drops the separator the break was
landing on, and the 1-to-n counter gets its own nowrap cell against a two-word
label — it was splitting across two lines at BOTH widths, which read as two
unrelated figures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…ure looks like one

T1. The reference's single most-cited choice is that its 126.7px display line is
weight 400 and nothing on the page is heavier; this tree had no rule enforcing
it, so 94 of 94 strong elements rendered at 700 — five bold runs in eight lines
of the honesty-rule paragraph, and at 390 a bold lead wrapping across two lines
is the first thing the eye lands on. Measured after: 0 elements over 500 on any
of the four pages, the shared layer's own six 600/700 weights included.

T2. The pull-quote loses its left rule and indent — which took ~60px off a 344px
measure at 390 — for the reference's centred sans with an em-dash attribution,
and the chart beat drops its third caption-size paragraph for h2 plus one body
block, with the key kept as a legend rather than a register.

T3. Eighteen expandable regions looked inert. The A1-A9 lead says "Open one to
see which checks defend it" directly above nine rows whose only open-state signal
was a colour change, and the nine threat summaries rendered as subheadings —
the shared layer sets display:inline-flex on every summary for its tap floor,
and that suppresses the ::marker pseudo in this engine. A phone has no hover and
no cursor to fall back on, so that was the whole affordance. Both get a marker
whose transform changes with the state, and the summaries get cursor: pointer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
…ft rail

T4. Round 1's E4 gave standalone links their 44px with inline-flex, which
inflates a shared LINE BOX — so on the sheet's metadata line the repository URL
and "scan run" rendered ~12px below the baseline of the text they sit inline
with, and "scan run" dropped onto a line of its own starting mid-column. It is
the first thing the eye catches in the sheet's first viewport, and it is round-1
m3's fault relocated. The hit area is an absolutely positioned pseudo now:
measured 0px spread across every line of that row, at both widths. Every "·"
becomes a ::before on the item that FOLLOWS it, bound to it by a non-breaking
space — measured 0 stranded separators on all four pages at both widths, where
there were eight. Every trailing arrow is bound to its last word.

T5. The `local` marker in the verdict column reached 44px by growing into a
dashed ~46px CIRCLE beside a 20px PASS pill — twice its neighbour's height, a
shape that appears nowhere else on the site, and a second wrong copy of a
component the phase chips already draw right. The element is the 44px target;
the drawn pill is a pseudo at the neighbour's height. The cell's contents are
wrapped so the 390 card restack cannot blockify the link out of the inline
exception, and `raw: pass` stops breaking.

T6. The grade ring was a flex sibling of the title, so the title, badges,
contradiction notice, metadata and version pill sat 75px right of the rail the
breadcrumb, the ring, both figures, the legend and P1-P6 share. Stacking it puts
all of them on one rail at every width — a margin-hang runs out of margin below
1272px, which is a fix that only works where it was measured.

T7. The headline breaks after "What each" so the two lines are closer in length.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
T8 the search field is a 999px pill on both pages — 8px is this design's DIAGRAM
radius and the hero's own pill rule was losing the cascade to the override block.
T9 the wordmark is Inter 500 at -0.02em rather than wide-tracked mono, which read
as a terminal prompt. T10 methodology paragraphs are spaced at 1.5x their line
(the gap was narrower than the leading inside them), the glosses lose the italic
for the sans at a muted colour, the run-in "Checks that defend it" label hangs
its wrapped list at the list's own left edge, and the `reported` chip is a pill.
T11 the overview counts get a fixed right-aligned numeral cell, so "checks" stops
landing at four different x positions down one column of panels. T12 the maze
legend is a three-column grid (two wrapped rows, five different left edges).
T13 the sheet's cards narrow to the measure they hold — every paragraph stopped
at ~928px inside a frame running to 1280. T14 pass and gap take the tint/border
register where they are tints, with the full-strength hue kept for the word, so
contrast goes up; red stays the fail state and stays nowhere else. T16 the nav
gets the plate's own shadow, so the segmented control passing under it on its way
out reads as passing under something. T17 the first pill target on each page
clears the nav even on the landing where the nav has not reached its sticky
offset yet — it measured 0.0px. T18 the navigating guard's 700ms belt is armed
only where `scrollend` does not exist: armed unconditionally it fired FIRST on
any jump longer than 700ms, which is why only the longest page lit a third pill.
T19/T20 land with S6 and T1.

Also §B's last three: print keeps the opening panel (it carries the page's only
h1), the checks count on an A1-A9 row is no longer aria-hidden, and
`data-window-in-view` is written by an observer rather than hardcoded, so the
aperture's `will-change` is conditional as the reference's is.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
21 new cases, and the file goes 858 -> 879. Each one pins a defect this round
closed rather than the shape of the code that closed it: the three verdict keys
are asserted as the SAME SET of words drawn with the real pills; the compact
rail's interleave order is read off the rendered markup so a sixth stage would
rail itself; the chart's id row is asserted against the plot's own column
arithmetic rather than a copy of it; the navigating belt is asserted absent
where `scrollend` exists.

Two are written as counts on purpose. The heavy-weight check asserts SIX
declarations over 500 rather than none: the concatenated sheet still contains
the shared layer's own six, which this design covers with an override rather
than rewriting a shared module — a seventh means the layer grew one the override
does not reach, and the browser probe (0 elements over 500 on all four pages) is
what actually closes the claim. The phase-name check counts the rendered key
rows, because a title attribute never renders at all on a touch device and the
directory is the one page that draws the bars without the names.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
p4gs and others added 2 commits September 13, 2026 19:51
… numbers

Self-check in real Chrome through VerifyViewport (lifecycle live), against a
Bun.serve of site/dist at 1440 and 390, all four pages.

1 · The `scored under methodology v1` pill could not share a baseline with the
line it sat in — a 44px pill among 21px text runs, its label centred inside it,
sitting ~12px below everything beside it. That is round-1 m3 surviving T4's fix
for the LINKS on the same line; §B10 already allowed the alternative, so the
pill takes its own row.

2 · The explorer's Record card lost its full stop in the sepList refactor. It is
in the 390 capture and invisible to every assertion that was looking for the
words rather than the sentence.

3 · The first pill on EVERY page landed with 0.0px of clearance, and a 124px
scroll-margin — tried first — measured 0.0px unchanged: 88px is the nav's PINNED
offset, and on a page's first target the nav has not reached it, so it is still
in flow directly above the section and its bottom IS the section's top, at any
scroll-margin. The clearance had to be real air under the rail. 24px, and every
pill of every page now clears by 23.6-28.5px.

Two instrument facts are recorded in BUILD-NOTES because they produced false
failures first: a tap-target probe must credit an absolutely positioned pseudo
(the only way to give an inline link 44px without inflating the line box it
shares), and an overlap probe must exclude the visually-hidden 1x1 idiom as well
as closed <details> — overflow:hidden clips painting, not a descendant's
geometry, so a hidden THEAD's cells still report real rects.

Evidence under fix2/: every probe and the driver, plus captures at both widths.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
Round 3's four judges and the measurement pass converged on one sentence, and
these are its four shapes.

THE ID AWAY FROM ITS CHIP. `.fy-node-label { overflow-wrap: anywhere }` was
global while the repair that confines a break to a real hyphen — `flex-wrap:
wrap` on the chip plus `overflow-wrap: normal` on the label — lived only inside
the 767px block. A narrow region at 1440 squeezes a chip exactly the way a 390
viewport does, so the five-across Distribution & publishing band set
`publish-provenance` as `publis` / `h-` / `proven`: not a control id, and not
searchable. Both declarations are unconditional now. The squeeze itself is
round 2's new verdict word — `NOT IN THIS RECORD` is the longest in the set and
carries `flex: 0 0 auto`, so it never yields and the label absorbed all of it.

THE COMMA AWAY FROM ITS ID. T10 made the run-in checks list a two-column grid so
the label would hang it. In a grid each `<code>` is an item and each ", " between
them an ANONYMOUS item, so auto-placement dealt nine ids into column 2 and nine
bare commas into column 1 — measured at x=320 against ids at 499.4 (1440) and
x=24 against 203.4 (390), up to 81 on a fully expanded page, as the payoff of the
one interaction that section's copy advertises. The label takes its own line
instead: ordinary inline layout is back in charge of the separators, the ids get
the whole measure, and home's copy and methodology's `.tx-class-controls` — which
hung two different ways, each wrong — are one behaviour. `white-space: nowrap` on
the code then keeps `branch-protection`, `pr-template`, `maintainer-mfa` and
`publish-provenance` whole at 390.

THE MIDDOT AWAY FROM ITS ITEM. T4 moved every separator to a leading `::before`
bound by a non-breaking space, which is why no line ends on one — measured 0
across four pages. Two cases survive it and CSS can express neither: the dot
still strands at a line END when the item it introduces is an atomic inline-block
that wraps whole (the directory card's "… 30 of 33 in scope ·" with its coverage
pill below), and nothing anywhere stops a line BEGINNING on one, which is what
the fix traded into — the sheet's first viewport at 390 and the colophon both
open lines on a middot. Whether a separator sits at a line boundary is a question
only layout can answer, so the page's own script asks it after layout and again
on resize, and marks the ones that do. The mark HIDES THE GLYPH rather than
removing the box: `visibility` changes no geometry at all, so marking a separator
can never move the wrap that produced it, and the pass cannot oscillate against
its own effect. Re-run on `document.fonts.ready`, because web fonts land after
first layout and change every one of these measurements.

THE BADGE AWAY FROM ITS PILL. A 999px radius on a two-line box is an ellipse:
`DIFFERENT COMMIT` broke between its words and set a ~60px egg beside the 17px
sentence it labels, on the directory's primary row, once per listing, at both
widths — while the sheet hero draws the same fact correctly as an 8px box.
`nowrap`, and `flex: 0 0 auto` so `summary`'s inline-flex cannot squeeze it under
its own label.

Also, and each one declaration:

- 45 OF 54 DISCLOSURES SAID NOTHING ABOUT OPENING. Round 2 gave `.tx-details
  summary` a rotating chevron and recorded that `.fy-merge summary` and
  `.fy-table summary` already had one. Read against the built CSS they did not:
  they had `cursor: pointer` and then `display: inline-flex`, which is itself
  what suppresses the `::marker`. So the 41 `evidence (n)` rows on a sheet were
  caption-coloured text that happened to be a button — on a page whose whole
  argument is that the evidence is there to be opened, and on a phone with no
  hover and no cursor to fall back on. Same glyph, same rotation, same three
  declarations, so the site has one disclosure. The 44px target is untouched.
- `.fy-swipe { display: block }` is an author rule and beats the UA's `[hidden]
  { display: none }`, so the script's own `cue.hidden = max <= 1` did nothing and
  three methodology tables that fit their window advertised a swipe that cannot
  move. A false affordance is worse on touch than none.
- The drawn `local` pill is 18px, the height of the `.fy-oc-pass` chip it is
  meant to pair with, not 22.
- Home's `reported` mark takes methodology's radius, padding and spacing, so the
  two instances are the same object rather than a pill and a rectangle — and the
  double word space in front of it (8px of its own on top of the 8px
  `.fy-incident-when` supplies) is gone.
- `hyphens: none` on the pull-quote.

Three round-1/round-2 assertions pinned the forms this replaces and are updated
two-sided rather than deleted. The new ones are in "round 3 — a token never
breaks away from what belongs to it", including a count-based guard so a new
`<summary>` family cannot appear without the chevron rule being revisited.

888 pass / 0 fail / 1 pre-existing skip; tsc 0; build 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
@p4gs
p4gs merged commit 7ba092f into main Sep 14, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant