Level 3. The sheet an invoked screen arrives as. 337 dialogs on 105 painted screens, three of them in the markup of nearly every screen and closed, which is why this component is at the centre of the scope defect the system keeps paying for. Measured 2026-08-08 in a browser, every dialog forced open, at 390 and 1280.
This section drew nothing at all, at either width, from 2026-08-08 until
2026-08-09. The rebuild that took it from 9 elements to 39 sliced the sheet out of the
product whole, and in the product a sheet is SHUT until a script opens it: a
<dialog> with no open attribute is
display:none. So the page carried 39 correct elements inside a box the browser
never painted, and the sweep that declared parity counted them and passed, because an element
count reads a shut dialog exactly as it reads an open one. It is the same trap as the SVG
with no fill and the link with no colour: a missing value is a value.
The sheet the product opens, with its head, its close and its body, 39
elements. Two things differ from the product and this page already said both: it is
open rather than showModal(), so there is no top layer and no
backdrop, and .tk-dlg pins it into the flow.
Every painted screen carries the sign-in sheet, the deposit sheet and the
How-it-works sheet in its markup, shut. That is 337 dialogs across 105 screens, and it is the
single fact that explains a defect this repository has now met three times: a
<dialog> sits outside the .app-case wrapper, so every rule
scoped to that class stops at the dialog's edge.
526 of the 559 buttons that stand in a dialog in this product are in a dialog
that carries no app-case. The chip's amount face was drawn by the browser for
exactly that reason until 2026-08-08, and the field beside it was right the whole time because
the input file had at least named the second place its control
stands. They are closed, so nothing rendered wrong; a closed dialog is where a defect
waits.
Naming the second place turned out to be the smaller mistake, and the field stopped doing it later the same day. Its face is a class now, so it needs no dialog at all, and this page no longer explains why the specimen on the input page stands in one. A container may say where a control sits and may not say what it looks like, which is the same sentence the close disc four sections down is an example of.
THE WRAPPER IS GONE, 2026-08-08, and the count is why. 415 selectors in
36 files opened with .app-case, 31 per cent of everything the system
declares. Measured across all 106 painted screens with every dialog forced open, it changed
the outcome for 13 of 375 selector tails: eight are one file drawing a page differently
from a sheet on purpose, and five were this defect. It matched zero elements of the course
chrome it was put there to protect. The account and the before-and-after are in
docs/decisions.md.
The two controls at the foot of that body are ANCHORS, and that is a rule
of this file rather than a detail of the markup. On a standalone overlay page the same control
navigates instead of opening the next sheet, so the control is an <a href>:
14 of them, on nine screens. An anchor decorates its text by default, which is what
dialog.app-dialog .sheet-body>a is for, and this page shipped that body without
a single one of them until 2026-08-11, so the rule had no stand anywhere in the kit. A
specimen with one element missing looks finished.
And the reason it never showed is the sharpest example of a skin paying for
another skin's markup in this system. The three worst placements are the sign-in sheet's
provider rows, and those pages also wore .outcome-dialog, which carries a blanket
a{text-decoration:none}. So the underline was killed by a rule about a WIN overlay,
on a page that is a sign-in, and the day the two skins were separated it would have appeared on
109 screens at once.
THE ANCHOR RULE NEXT TO IT WAS OVERWRITING THE SHAPE OF EVERY CONTROL IN A
SHEET, and it took a forced focus state to see it. Backlog 111.
dialog.app-dialog a:focus-visible{border-radius:var(--radius-6)} was written for a
text link: an outline follows its element's border-radius, so the only way to round
a ring is to round the element, and at (0,2,1) this one beat every control that is an anchor.
Measured under a forced :focus-visible across the 17 overlay pages, 88 anchors
changed radius and 48 of them had a shape of their own: 17 close discs went from 100px to
6px, a circle becoming a cornered square, plus 17 .hiw-full blocks and 14
.btn blocks from 10px to 6px. (.hiw-full was the sheet's link out to the
full guide and went with the 2026-08-14 rebuild; the reading is kept as taken.) The disc is the worst because showModal()
autofocuses the first focusable child, so that is the first paint of every one of those
sheets. Contrast is unaffected either way, 3.53:1. It is the shape that jumps.
The fix is one condition and not a list of the three components it was
overwriting. It is a:not([class]) now, which is the rule's own sentence: an
anchor with no component on it has no shape, so the ring gets one. The 40 anchors it was written
for carry no class at all, which is what the measurement found. A list of three would fail in the
wrong direction, silently overwriting the fourth shaped anchor to arrive; this fails in the right
one, because a new component brings a class and keeps its shape with no edit here. After:
40 anchors still take the corner and all 48 controls keep theirs, with the control proving
the rule fires at all, 2,706 anchors read and the ring changing on 2,706.
A body shares its head's horizontal padding, and three of the four did not. Backlog 110, measured over all four sheets in both themes at 390 and 1280 and identical at every one. The bare body sits at 20 under a head at 20 since backlog 104 and the outcome body at 20 under 20, but the sign-in body sat at 16 under 20 and the how-it-works body at 20 under 24: a 4px step between a title and the content beneath it on 221 of 348 sheets, and not the same 4px twice. Neither was an optical correction, because an optical correction is one value applied on purpose and these were two values arrived at by accident: a small sheet padding all four sides equally, and the generic body value standing under a hero that pads 24 because a hero is bigger. All four step +0px now. The vertical padding and the gap are left alone on purpose: an edge is shared with another element and has to agree, a gap is between siblings of one body and says how dense that body's content is. Four bodies at 8, 12, 12 and 16 is the open half of that row, and it is filed rather than guessed at.
| kind | placements | at 390 | at 1280 |
|---|---|---|---|
| plain sheet | 105 or so per screen family | 349x678 | 420x685 |
.signin-dialog | 109 | 349x427 | 410x423 |
| How it works | 105 | 349x854 | 464x764 |
.outcome-dialog | 6 | the win and loss overlays, which recolour the reconcile box | |
The frame clips its own corners, so it cannot also be the scroll container. The sheet is bounded by the viewport and its BODY scrolls. Every dialog in the product follows that split, and the How-it-works sheet writes it a second time for its own body for the same reason.
The How-it-works sheet had a file of its own until 2026-08-11, and that file
held three things at once: an explainer BLOCK, the standalone PAGE the block is the content
of, and ten rules about being a dialog. The ten are here now and the other thirty-four are
components/hiw.css. What decided the cut is that the file was one block with two
hosts, which is also why this was the last level in the kit that had to be declared instead of
counted: the arithmetic could not read a component nobody could name.
| what the rule decides | selector | rules | elements |
|---|---|---|---|
| this sheet is wider than the other three, and it clips | .app-dialog.hiw-dialog | 1 | 105 |
| the frame clips, so the BODY is the scroll container | :modal .hiw-steps | 1 | 105 |
| gone 2026-08-15: the body's own padding and column | .hiw-body | 0 | 0 |
| gone 2026-08-15: the brass link out, its arrow, its hover and its press | .hiw-full | 0 | 0 |
The link out was the sharpest of the three tests the cut was made with, and
the sheet it argued about no longer exists. .hiw-full could not stand on the page
at all, because the page IS the full guide, and a rule that can only ever fire inside a sheet
belongs to the sheet whatever the block around it is called. That reasoning was right and its
subject was rebuilt on 2026-08-14: the sheet is three steps with a picture each and the third one
is a way IN, so there is no link out to own. The other two tests still hold, and they are why the
two surviving rules are here: eighteen rules draw the explainer in a sheet at DOM depth 3 and on
the page at depth 9, so they are the block's; sixteen more are a grid, a 900 rail and a sticky
side column, so they are a screen's shell.
Eight of the ten rules were still standing on 2026-08-15, drawing nothing,
and the ninth was the one that mattered. .hiw-body was the SCROLL container, and
when the sheet was rebuilt nothing took the job: measured with the sheet open and its entrance
animation finished, at 320 x 480 the sheet capped at 441 against 562 of content, so 121px
stood outside the clip and scrollTop read back 0, with the whole step
navigation below the cut. 40px at 360 x 568, nothing at 360 x 640 and above. The rule names
.hiw-steps now, which is what this sheet's body is called. A frame that clips
cannot also be the scroll container, and this sheet had lost its half of that pair.
It is provably inert, and the proof is a zero that is known not to be an empty question. The move carries these rules three slots earlier past exactly two files. Every element of all 160 linked pages was asked which rules of the old file and which of those two both match it AND write a common longhand: not one pair, at any specificity. The same instrument pointed at this file returns 427 candidate pairs, which is how the zero is read as a zero rather than as a selector that matched nothing.
.sheet-close here and a second name in the How-it-works file were
the same button written twice, and this file's own copy was written twice again, once for
the outcome overlay and once for everything else. All three agreed on the 32px box, the pill,
the scrim and the edge, which is what says they were one control rather than three
decisions.
It is .icon-btn.icon-btn-photo now: the atom every icon-only press
in the product is, wearing the face for a photograph. What is left here is where it sits,
12 in from the top right corner above the head. A region may say where an icon button sits and
may not say what it looks like.
And its press does NOT take the system's held ground, which is the sharpest statement of a rule this system has: a state answers to the GROUND it stands on, not to the family it belongs to. The disc is a fixed dark scrim in both themes carrying a white mark; the product's pressed ground is a graphite in the Vault and a chalk in daylight, and neither of them is a step down from black.
Measured: 32x32 with a mouse and 32x32 with a finger. It is one of the four named exclusions from the touch floor, and 333 placements makes it the largest of them.
67 declarations of it, and the colour is declared on the class rather than inside the containers named further down. Without that, it inherits body text and reads as loudly as the sentence it qualifies, which is exactly what happened to the fine print under a multi-outcome list once the screens stopped carrying their own copy of the cascade.
A component has to say what it is wherever it stands. Same sentence as the yes/no pair's missing colour and the 992 links that rendered in the browser's blue: a missing value is a value.
A backdrop, a sheet and a grab handle sat in this file. 17 wireframes carry
them and no painted screen and no stand ever has, because the paint expresses an invoked
screen as a centred dialog. They survived a colour sweep only because it counted the grey tree's
files as markup, and this file cannot reach the grey tree at all: the wireframes carry
their own inline grey-box CSS and link nothing from components/.
The page says four kinds and the product ships five.
.outcome-dialog stands on 6 screens, the win and loss cluster, and it had
never been drawn in the kit. It is the only dialog in the product that opens BY ITSELF: a script
at the foot of those screens calls showModal() on load, because the person did not
ask to see it, the market resolved and the product is telling them.
It also carries a second class, .loss-dialog or
.win-dialog, which is where the outcome colour comes from. Shown loading, from
loss-loading.html, because that is the state a person actually meets first.
Its second class is where the outcome goes, and both faces stand here because the pair is the point: the same sheet, the same head, the same close, and one value apart. Green and red are outcome semantics in this system and nothing else, so this is the one dialog in the product allowed to carry either.
Neither .win-dialog nor .loss-dialog was
named in this file's own class list until 2026-08-10. Both were styled here and claimed by
no component header anywhere, which is why no sweep could ask whether the kit showed them. Both
are declared now, and the second was found by the declaration.
Its own width query: 640 DESK. Below DESK a modal sheet is the whole width of the window, bottom-anchored, 88dvh tall, with the top two corners rounded and the bottom edge gone: a phone sheet. From DESK up it is a centred capped card. This is the one component whose share of the window changes at every width by design, from 0.92 at 360 to 0.29 at 1440, because a sheet is capped and a window is not.
A dialog is outside the app wrapper, so every scope has to name it. That is not a preference, it is where the browser puts the element.
The frame clips and the body scrolls. One box cannot do both.
A state answers to the ground it stands on. A control on a fixed black scrim does not press the way a control on graphite presses.
Never scope a rule to a wrapper the product's own regions stand outside of. 526 of 559 dialog buttons were not covered, and neither was the footer or the bottom nav. The wrapper is gone from 415 selectors and the one file that kept it means the PAGE by it, on purpose, and says so.
Never write the close disc again. Two files and three rules agreed on every value, which is what says it was one control all along.
Never keep the grey tree's frame here. This file cannot reach the grey tree, so a rule kept for it is a rule kept for nobody.
The level and its reason: the organisms
page. The stylesheet: components/dialog.css. The close disc:
icon button. The field inside it: input. The
chips beside the field: quick amounts. The long sheet:
how it works. The reassurance boxes:
notice.