fix: header reflows under enlarged text; reopen the step-1 nowrap decision

The step-1 header decision is formally reopened (AGENTS.md (ak)): its record
attributed the residual to the wrong cause and read a measured 944px functional
failure as a robustness margin.

Mechanism — wrapping, plus a gated sticky offset:
- `flex-wrap: nowrap` removed from `.header-inner` (measured necessary AND
  sufficient) and from `.nav-list` (measured inert; removed so the prohibition
  is not contradicted by a `nowrap` in the same file), with a dead `flex: none`.
- `inset-block-start` becomes a two-band, two-term saturating clamp() so the
  header is sticky only while the masthead is one row. A media query cannot
  express this: its `rem` resolves against the browser DEFAULT font size, a
  property's against the root element. The second term catches a root BELOW the
  default, where the 80rem content cap shrinks faster than the header's px
  minimums; without it 65px of `#main` sat behind the header at 9px.
- Wrapping is the only mechanism that reflows under all THREE enlargement paths,
  because Chrome's minimum-font-size setting is invisible to @media, to
  @container and to every length unit.

Measured, 22 pages x 16 widths = 352 page-widths per method:
  root-style 32px        175/352 -> 0/352   (1280px: 944px overflow -> 0)
  minimum-font-size 32   219/352 -> 88/352  (residual is the footer email)
  default-font-size 32     0/352 -> 0/352
  default (root 16)        0/352 -> 0/352
Nav items and CTA on-screen in 1408/1408. A further 762 points across roots
9-32, both thresholds, the band seam and all five Chrome presets: 0 failing.

Normal-settings identity: 0 differences on 352 page-widths across six metrics,
with six nav items and with a seventh injected. Header 81.00px at all eight
widths >= 1056, CTA gap 0.00px. Lighthouse: 22 pages, no category below 95,
CLS 0.000 on every page.

--header-h is reworded as a FLOOR, not a constant; value unchanged.

Also in this step, per ruling:
- /bio/ print `font-weight` frozen at 400 — the circulated PDF's typography
  changes only when its content is deliberately revised, never as a side effect
  of a screen refactor. Declaration byte-identical; the constraint is recorded.
- CLAUDE.md: the two-simulation rule for enlarged text, the zsh
  no-word-splitting rule, the third (minimum-font-size) mechanism, and
  "state the grid with the count".

Two rounds of adversarial-reviewer, eleven findings, all resolved; round 2's
blocking finding was a defect in round 1's own fix. Two suggested fixes declined
with reasons in (ak). claims-auditor deliberately not run — D20.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
This commit is contained in:
Pouya Lajevardi
2026-09-01 12:34:50 -04:00
co-authored by Claude Opus 5
parent 0f7595b602
commit fce89d46eb
8 changed files with 731 additions and 125 deletions
+35
View File
@@ -273,6 +273,15 @@ argument and the comparison never ran. Prefer `git grep`, quote or array-expand
anything you pass as flags, and re-check any result whose shape is "uniformly
bad".
**zsh does not word-split parameter expansions; a loop over `$VAR` runs ONCE —
use command substitution or arrays, and assert the iteration count.** *Pouya's
convention, 2026-09-01.* `for p in $PAGES` iterates one item, not twenty-two,
and `node probe.mjs 320,360 $P` measures one page — both of which then report
"max=0, nonzero=0" and read as a clean sweep. `$(cat file)` and `${=VAR}` do
split; `"${(@f)VAR}"` splits on newlines. **The fix is not remembering which:
assert the count before reading the result** — a probe that says how many rows it
measured cannot silently measure one.
**And re-check "uniformly GOOD" too — that is the dangerous half.** *Added
2026-08-30; sharpened on Pouya's instruction 2026-08-31, as "the sharpest
instrument finding yet".* The same `set -- $pair` loop recurred while confirming
@@ -387,6 +396,32 @@ So before acting on a number: say what it is a number *of*; confirm the command
actually ran and read its exit status; and check it against a second method that
cannot fail the same way — the bytes on disk, a screenshot, a hit test.
**Two simulations of 200% text are not equivalent: media-query `rem` resolves
against the browser DEFAULT font size, not the root element — measure under both
methods before declaring a reflow result.** *Pouya's convention, 2026-09-01.*
Raising the default moves the breakpoints along with the type, so the desktop
layout is never reached and nothing overflows; setting `documentElement.style
.fontSize` doubles the type and leaves the breakpoints where they were, which is
the layout the desktop nav was measured in. One of those reported **0** while the
other reported **944 px** on the same 22 pages, and the prose generalised the
zero.
⚠️ **AND THERE ARE THREE MECHANISMS, NOT TWO — the third is the one that defeats
every CSS conditional.** Measured 2026-09-01: Chrome's **"Minimum font size"**
*floors* computed font sizes instead of scaling them, so text enlarges while
`rem` keeps resolving at 16 px. A media query does not see it, and neither does a
container query — `@container` `rem`/`em` DO track the root element (that is the
one real difference from `@media`, and it is measured), but under a minimum-font-
size setting they still resolve at 16 px while `getComputedStyle` reports 32 px.
**So no query fires, and only used-value layout — wrapping — reflows.** That path
was the worst of the three: on one grid of 22 pages × 16 widths, **219 of 352
page-widths overflowed** against root-style's **175**, and it was the only one
failing at 320 px and 1024 px. **Always state the grid with the count** — two
sweeps in that session quoted totals of 220, 330 and 352 for the same claim, and
side by side they read as contradictions rather than as different width lists.
Measure all three mechanisms; treat a clean result from one as evidence about
that one.
**And a grep that matches is not a finding until you read what it matched.**
A case-insensitive sweep for `LSO` hit `I aLSO practise`; a superlative sweep for
`leading` hit `the pLEADINGs`. Both on the same page on the same day. Print the