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:
co-authored by
Claude Opus 5
parent
0f7595b602
commit
fce89d46eb
+371
-66
@@ -145,12 +145,21 @@ alias of it; see the eyebrow rule below.
|
||||
consumer of the 11 px token. `--text-xs` (12 px) is a separate rung and is
|
||||
enumerated below.
|
||||
**The exception, and why it is deliberate rather than residual:** the tagline is
|
||||
ornamental and layout-constrained. 13 px already took the header to 83.4 px while
|
||||
`--header-h` is 81 px, which `global.css` feeds into `scroll-padding-top`; and
|
||||
with a seventh nav item the CTA lands past `.header-inner`'s content edge at
|
||||
1216, 1240, 1280 and 1440 — it does not resolve as the viewport widens. Insights
|
||||
is that seventh item. Both measurements are in `SiteHeader.astro`, and document
|
||||
overflow is 0 in every one of those cases, so no page-level check can see it.
|
||||
ornamental and layout-constrained. ⚠️ **THE COST WAS RE-MEASURED ON 2026-09-01
|
||||
AND IT IS A DIFFERENT COST NOW — the masthead may wrap, so what used to overflow
|
||||
invisibly is visible instead.** At `--text-eyebrow` (14 px) the header stands at
|
||||
**144.98 px** rather than 81 px — at 1216 px with six nav items, and at **every
|
||||
width from 1216 px up** with a seventh (measured at 1216 / 1240 / 1280 / 1360 /
|
||||
1440 / 1600 / 1760 / 1920) — because the row wraps. *(This listed four widths
|
||||
ending at 1440 for one revision; that list was carried over from the superseded
|
||||
CTA-past-the-edge measurement, which really did stop at 1440.)* **Document overflow
|
||||
is 0 and the CTA sits exactly on `.header-inner`'s content edge in every one of
|
||||
those cases**, where before the fix they were 20 px of overflow at 1216 px, 4 px
|
||||
at 1280 px, and the CTA 67.8 / 43.8 / 51.8 / 51.8 px past the content edge. So
|
||||
the two costs this bullet used to cite are **gone**, replaced by one that is
|
||||
larger and easier to see: 64 px of header height on every page. *(13 px took the
|
||||
header to 83.4 px; that figure was taken before the masthead could wrap.)*
|
||||
Insights is that seventh item. The measurements are in `SiteHeader.astro`.
|
||||
**Three blocks moved to `--text-eyebrow` on 2026-08-31 and their carve-outs are
|
||||
superseded.** (a) `/contact/`'s `<label>`/`<legend>` set — *raise, do not
|
||||
ratify*, superseding the "accepted, not ratified" note this bullet carried for
|
||||
@@ -171,8 +180,17 @@ alias of it; see the eyebrow rule below.
|
||||
size. *(This read "63,743 bytes" for one revision. That is the `cmp -l`
|
||||
differing-byte count, not the size delta: the content stream is Flate-compressed,
|
||||
so a one-property change scrambles most of the file. Two figures, and the wrong
|
||||
one answered a question nobody asked.)* That freeze is a held decision, not a
|
||||
design position — see `bio.astro`.
|
||||
one answered a question nobody asked.)* ⚠️ **THAT FREEZE IS NOW A RULED
|
||||
CONSTRAINT, 2026-09-01, NOT A DECISION AWAITING POUYA: the circulated PDF's
|
||||
typography changes only when its CONTENT is deliberately revised, never as a
|
||||
side effect of a screen refactor.** So the print block keeps 400 whatever the
|
||||
screen does, and `.eyebrow`'s 500 stops at the `@media print` boundary. **The
|
||||
reason it is a rule and not a preference** is that the PDF is the one artefact
|
||||
this project's apparatus sees only when someone re-renders it (`AGENTS.md`
|
||||
R16): a refactor that silently re-typesets it changes a document already in a
|
||||
reader's hands, and the diff that would have shown it is a 1,655-byte
|
||||
compressed blob nobody reads. A screen change that reaches print is therefore
|
||||
a defect by construction, not a judgement call — see `bio.astro`.
|
||||
**`--text-xs` (12 px) IS A SEPARATE RUNG AND IT WAS NOT IN SCOPE ON 2026-08-31.
|
||||
Ten declaration sites, all of them:** `Pill`, `Breadcrumbs`, `CredentialRow`,
|
||||
`DefinitionGrid`, `ArticleCard`, `ProcessStep` (two), `insights/[...slug].astro`,
|
||||
@@ -371,70 +389,247 @@ Not a polish pass. A build requirement.
|
||||
`documentElement.scrollWidth - documentElement.clientWidth`, plus an
|
||||
enumeration of every element wider than `clientWidth` to name the offender.
|
||||
|
||||
⚠️ **THE NARROW-WIDTH RESIDUALS ARE 0. THE 1280 px AND 1920 px ONES ARE NOT, AND
|
||||
THIS PARAGRAPH CLAIMED THEY WERE.** Pouya ruled the narrow case a defect on
|
||||
2026-08-31 — *"the 152/112/82 px overflows at 320/360/390 with 32 px root text
|
||||
violate WCAG 1.4.10 and are not accepted"* — and that case is fixed. The wide
|
||||
case is a **different cause**, is **not** fixed, and is now stated correctly
|
||||
rather than reported as zero.
|
||||
✅ **THE LARGE-TEXT NAV OVERFLOW IS FIXED, 2026-09-01.** Pouya reopened the
|
||||
step-1 header decision for the purpose — *"its record attributed the residual to
|
||||
the wrong cause and characterized a measured 944 px functional failure as a
|
||||
robustness margin"* — and ruled *fix, do not accept*. What follows is the
|
||||
current state. The narrow case was ruled a defect on 2026-08-31 (*"the
|
||||
152/112/82 px overflows at 320/360/390 with 32 px root text violate WCAG 1.4.10
|
||||
and are not accepted"*) and fixed then.
|
||||
|
||||
**TWO METHODS OF SIMULATING 200% TEXT, AND THEY ARE NOT EQUIVALENT. This is the
|
||||
whole of the error.** Every earlier number in this section came from the command
|
||||
below — `documentElement.style.fontSize = '32px'`. A later sweep used Chrome's
|
||||
`Page.setFontSizes({standard: 32})` instead and reported 0 everywhere, and the
|
||||
prose generalised that to *"at root 16 px and root 32 px"*.
|
||||
**THREE MECHANISMS ENLARGE TEXT, AND THEY ARE NOT EQUIVALENT — this is the
|
||||
measurement the whole record turned on.** Every figure in this section before
|
||||
2026-09-01 came from `documentElement.style.fontSize = '32px'`. A later sweep
|
||||
used Chrome's `Page.setFontSizes({standard: 32})`, reported 0 everywhere, and
|
||||
the prose generalised that zero. All three are re-measured below.
|
||||
|
||||
| | media query `66rem` resolves to | header above 1056 px | 1280 px, root 32 |
|
||||
|---|---|---|---|
|
||||
| `documentElement.style.fontSize='32px'` | **1056 px** — matches | `sticky`, `flex-wrap: nowrap` | **944 px over** |
|
||||
| `Page.setFontSizes({standard:32})` | **2112 px** — never matches | `relative`, `flex-wrap: wrap` | **0** |
|
||||
| mechanism | `rem` in a media query | `rem`/`em` in a PROPERTY | `getComputedStyle` root | sees the enlargement? |
|
||||
|---|---|---|---|---|
|
||||
| `documentElement.style.fontSize='32px'` (root style) | **16 px → `66rem` = 1056 px** | 32 px | 32 px | media query **no**, property **yes** |
|
||||
| `Page.setFontSizes({standard:32})` (the reader's *default font size*) | 32 px → `66rem` = **2112 px** | 32 px | 32 px | **yes, both** — the breakpoint moves with the type |
|
||||
| `--blink-settings=minimumFontSize=32` (the reader's *minimum font size*) | 16 px → `66rem` = **1056 px** | **16 px** | **32 px** | **NOTHING IN CSS SEES IT** |
|
||||
|
||||
In a media query `rem` resolves against the **default** font size, not the root
|
||||
element's. So raising the *default* moves the breakpoints along with the type and
|
||||
the desktop nav is never reached; setting the *root* doubles the type while
|
||||
leaving the breakpoints at 16 px, which is the layout the desktop nav was
|
||||
measured in. **Both are real settings, and there is a THIRD mechanism which is
|
||||
the one a reader actually reaches.** Chrome exposes "Font size" — the default,
|
||||
the second row, which moves the breakpoints along with the type and overflows
|
||||
nothing — and, separately, **"Minimum font size", which FLOORS computed sizes
|
||||
rather than scaling them and leaves media queries alone.** Measured at 1280 px
|
||||
with `--blink-settings=minimumFontSize=32`: the `66rem` query matches, the nav is
|
||||
`sticky` / `nowrap`, `.brand-name` computes to **32 px (floored, not scaled to
|
||||
42.6 px)**, and the document is over by **830 px**. So the failure is reachable,
|
||||
and **830 px is the reachable figure while 944 px is the `documentElement`-root
|
||||
one — they are not the same measurement and must not be quoted as one.** The two
|
||||
rows below are the two simulations, not the three mechanisms.
|
||||
*(Row 1's media-query cell read "32 px → 2112 px" for one revision, which
|
||||
contradicted its own verdict column, the paragraph below it, and measurement —
|
||||
and it deleted the one number that explains why the failure is reachable at all.
|
||||
If `66rem` really resolved to 2112 px under root scaling, the 66 rem desktop
|
||||
masthead would never be reached at 1280 px and the 944 px overflow could not
|
||||
happen. Found by `adversarial-reviewer`.)*
|
||||
|
||||
**The corrected figures, `documentElement.style.fontSize='32px'`, 22 pages:**
|
||||
Two consequences, both measured 2026-09-01 and both load-bearing:
|
||||
|
||||
| width | pages over | overflow |
|
||||
1. **In a media query `rem` resolves against the browser's DEFAULT font size,
|
||||
not the root element's.** So raising the *default* moves the breakpoints
|
||||
along with the type and the desktop masthead is never reached; setting the
|
||||
*root* doubles the type and leaves the breakpoints where they were, which is
|
||||
the layout the desktop nav was measured in. **A container query is the one
|
||||
construct that differs** — `@container` `rem`/`em` DO resolve against the
|
||||
root element, measured — which is why the sticky gate below is a property and
|
||||
not a query.
|
||||
2. ⚠️ **UNDER A MINIMUM-FONT-SIZE SETTING, `getComputedStyle(el).fontSize` AND
|
||||
THE `em` UNIT DISAGREE.** Blink floors the *reported computed* font size to
|
||||
32 px while `em` and `rem` keep resolving at 16 px — so `scroll-padding-top:
|
||||
6.0625em` computes to **97 px, not 194 px**, a `66em`-wide box measures
|
||||
**1056 px, not 2112 px**, and `min(0px, calc(100vw - 66em))` is **0px**.
|
||||
Media queries, container queries and length units are all blind to it.
|
||||
**Only used-value layout — wrapping — responds**, which is why wrapping had
|
||||
to be the mechanism and why one residual below cannot be closed in CSS.
|
||||
|
||||
**Before and after, 22 pages × 16 widths (320 → 1920 px) = 352 page-widths per
|
||||
setting, document overflow `documentElement.scrollWidth −
|
||||
documentElement.clientWidth`. Both columns measured on the SAME grid**, from a
|
||||
build of the previous commit and a build of the working tree, by the same probe:
|
||||
|
||||
| text setting | before | after |
|
||||
|---|---|---|
|
||||
| 320 / 360 / 390 / 414 / 640 / 1024 | **0 of 22** | **0** — the ruled targets, fixed |
|
||||
| 1280 | 22 of 22 | **944 px** on 21 pages, **508 px** on `/` |
|
||||
| 1920 | 21 of 22 | **304 px** (all but `/`) |
|
||||
| default (root 16) | 0 of 352 | **0 of 352** |
|
||||
| root style 32 px | **175 of 352** | **0 of 352** |
|
||||
| default font size 32 | 0 of 352 | **0 of 352** |
|
||||
| minimum font size 32 | **219 of 352** | **88 of 352** — all of it the FOOTER, see the residual below |
|
||||
|
||||
Root 16 is 0 at every width. The old **944 px** figure is **unchanged**; 602 px
|
||||
became 508 px on `/` only because `.brand-name`'s `nowrap` went.
|
||||
**The wide-width figures that were the defect, root style 32 px, and what they
|
||||
are now.** Every one is 0 of 22 pages after the fix, and the nav items and the
|
||||
CTA are on-screen at every width:
|
||||
|
||||
⚠️ **AND AT 1280 px IT IS NOT A MARGIN — IT IS UNREACHABLE NAVIGATION.** The
|
||||
furthest element is `div.header-cta` at **2224 px** against a 1280 px viewport;
|
||||
the nav clips mid-word after "Arbitration", and **Practice (left 1378), Fees
|
||||
(1556), Contact (1666) and the header CTA are entirely off-screen** behind a
|
||||
horizontal scrollbar — including the route to the inquiry form. *(This named
|
||||
"Med-Arb" for one revision. Med-Arb is in `SECONDARY_NAV` and is not in the
|
||||
masthead at any width; the item actually lost, and the worst of them, is
|
||||
Contact.)* That is
|
||||
WCAG 1.4.4 with loss of functionality. The paragraph struck here called it *"a
|
||||
robustness margin rather than a failure. Revisit if a real reader hits it."*
|
||||
| width | before | after |
|
||||
|---|---|---|
|
||||
| 1056 | 649 px, 22 of 22 | **0** |
|
||||
| 1100 | 606 px, 22 of 22 | **0** |
|
||||
| 1216 | 928 px / 491 px on `/`, 22 of 22 | **0** |
|
||||
| 1240 | 904 px / 468 px on `/` | **0** |
|
||||
| 1280 | **944 px** / 508 px on `/` | **0** |
|
||||
| 1440 | 784 px / 351 px on `/` | **0** |
|
||||
| 1600 | 624 px / 193 px on `/` | **0** |
|
||||
| 1920 | 304 px, 21 of 22 | **0** |
|
||||
|
||||
**THE CAUSE AT 1280 px IS `flex-wrap: nowrap` ON `.nav-list` ABOVE 66 rem, AND
|
||||
THAT IS A LOCKED STEP-1 DECISION — SO IT IS AN OPEN DECISION FOR POUYA, NOT A
|
||||
FIX MADE HERE.** His instruction with the eyebrow ruling was explicit: *"if
|
||||
anything header-side does overflow, stop and report rather than improvising a
|
||||
layout change."* The options are an `em`-based breakpoint so the header drops to
|
||||
its stacked layout when the type grows, or letting the nav wrap above 66 rem and
|
||||
giving up the constant 81 px `--header-h`. Both change the desktop masthead.
|
||||
Carried on `docs/06`'s cutover checklist so it cannot ship unresolved.
|
||||
*(Only 1280 and 1920 were in the earlier record, so it never showed that the
|
||||
failure ran the whole width of the sticky range. The same run under minimum font
|
||||
size was 414 / 370 / 854 / 830 / 830 / 750 / 670 / 510 px, of which the record
|
||||
carried only the 830.)*
|
||||
|
||||
⚠️ **AND IT WAS NOT A MARGIN — IT WAS UNREACHABLE NAVIGATION.** The furthest
|
||||
element was `div.header-cta` at **2224 px** against a 1280 px viewport; the nav
|
||||
clipped mid-word after "Arbitration", and **Practice, Fees, Contact and the
|
||||
header CTA sat entirely off-screen** behind a horizontal scrollbar — the route
|
||||
to the inquiry form among them. WCAG 1.4.4 with loss of functionality. The
|
||||
paragraph struck here called it *"a robustness margin rather than a failure.
|
||||
Revisit if a real reader hits it."*
|
||||
|
||||
**THE FIX IS TWO DECLARATIONS, AND THE SECOND ONE IS THERE BECAUSE THE FIRST
|
||||
ONE HAS A CONSEQUENCE.**
|
||||
|
||||
**(1) The masthead may wrap.** `flex-wrap: nowrap` is gone from `.header-inner`
|
||||
and from `.nav-list` above 66 rem, along with the dead `flex: none` beside it.
|
||||
|
||||
⚠️ **WHICH OF THE TWO WAS THE CAUSE IS NOW MEASURED, AND THIS PARAGRAPH FIRST
|
||||
GOT IT WRONG.** It said *"that pair, not any one declaration, was the cause"*.
|
||||
Restoring each declaration alone on the fixed build, root 32, `/about/` —
|
||||
document overflow / number of nav links and CTA off-screen:
|
||||
|
||||
| restored above 66 rem | 1056 | 1216 | 1280 | 1440 | 1920 |
|
||||
|---|---|---|---|---|---|
|
||||
| neither (shipped) | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
|
||||
| `.nav-list { flex-wrap: nowrap }` only | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
|
||||
| `.header-inner { flex-wrap: nowrap }` only | 0/0 | 273/1 | 209/1 | 49/1 | 0/0 |
|
||||
| both (= the state before) | 649/3 | 928/5 | 944/5 | 784/4 | 304/1 |
|
||||
|
||||
**`.header-inner`'s `nowrap` was necessary and sufficient.** And the second row
|
||||
is the one that matters for honesty: **`.nav-list`'s removal is inert** — byte-
|
||||
identical to shipped at every width, under a 200 % root size *and* under minimum
|
||||
font size, **with six nav items and with seven**. It is kept removed for two
|
||||
reasons that are not "it fixed the overflow": the `nowrap` prohibition in
|
||||
`SiteHeader.astro` would otherwise contradict a `nowrap` still sitting in the
|
||||
file, and the override only ever re-stated `nowrap` over a base rule that already
|
||||
wraps. Found by `adversarial-reviewer`; the mechanism sentence about min-content
|
||||
was true and was not the binding constraint. *(`flex` is a flex-ITEM property and `.nav`
|
||||
computes `display: block`, measured at 1056 / 1280 / 1920 px — so `flex: none`
|
||||
set `flex-shrink: 0` on something that is not a flex item and had **no effect**.
|
||||
Removing it leaves `.nav`'s and `.nav-list`'s geometry byte-identical at all
|
||||
three widths; the only trace was the computed `flex-shrink`, 0 → 1. Its comment
|
||||
claimed it was what stopped the nav being squeezed, which it never was.)*
|
||||
**It cost nothing at any normal size: 0 geometry
|
||||
differences across 22 pages × 16 widths (352 page-widths)**, header exactly **81.00 px** at every
|
||||
width from 1056 px up, brand on one line, nav on one line, CTA exactly on
|
||||
`.header-inner`'s content edge (gap 0.00 px) — **and the same with a seventh nav
|
||||
item injected**, at 1024 / 1047 / 1056 / 1071 / 1100 / 1150 / 1200 / 1216 / 1240
|
||||
/ 1280 / 1440 / 1920 px, where all seven share one line from 1056 px up. So the
|
||||
`nowrap` was not load-bearing at any width above the breakpoint; the 66 rem
|
||||
breakpoint is what keeps the row intact, and it already accounts for seven items.
|
||||
|
||||
**(2) The masthead is sticky only while it is one row**, and this half took two
|
||||
attempts — the first one shipped a regression, which is recorded because the
|
||||
reason it failed is the useful part.
|
||||
|
||||
Wrapping makes the header **taller** at enlarged text: **244.59–350.86 px** at a
|
||||
200 % root size against 81 px normally. A sticky box that size covers `#main`
|
||||
after "Skip to content", so stickiness has to go wherever the header can exceed
|
||||
`scroll-padding-top`. The gate is a **property**, not a query, because in a
|
||||
property `rem` tracks the root element.
|
||||
|
||||
⚠️ **ROUND 1 WROTE `min(0px, calc(100vw - 66rem))` AND IT WAS WRONG IN TWO WAYS
|
||||
THAT ONLY AN INTERMEDIATE ROOT SIZE EXPOSES.** Both were found by
|
||||
`adversarial-reviewer` and independently reproduced: **20 of 300**
|
||||
(page × viewport × root) points left `#main` behind the header, 10–69 px, across
|
||||
roots 18–30 — while root 16 and root 32, the only two sizes the first sweep
|
||||
measured, were both clean. **WCAG 1.4.4 is a requirement up TO 200 %, not AT
|
||||
200 %**, and a two-point sweep cannot see a diagonal band.
|
||||
|
||||
1. **It RAMPED where it needed to STEP.** One pixel inside the threshold the term
|
||||
lifts the header by one pixel — but the header has already gained a whole row,
|
||||
so it still covered `#main`. Wrapping is a step function; the response has to
|
||||
be one too. Hence the `* 100000` saturation, bounded by `-100vh`.
|
||||
2. **It used the wrong threshold in the wider band.** The tagline appears at
|
||||
76 rem, and with it the one-row masthead fits only from **1207 px = 75.4 rem**
|
||||
— the step-1 binary search already recorded that number. Gating the wide band
|
||||
on 66 rem therefore fired far too late. **`/` measured clean throughout, and
|
||||
that is what identified the driver:** `/` is the one page that suppresses the
|
||||
masthead tagline.
|
||||
|
||||
⚠️ **AND ROUND 2 FOUND THAT THE GATE WAS ONE-SIDED — it could only fire when the
|
||||
root was LARGER than the default.** Chrome's "Font size" control has **five**
|
||||
presets (Very small 9 px, Small 12, Medium 16, Large 20, Very large 24) and the
|
||||
sweeps had used 16 and 32 — 32 is not one of them. At **9 px the masthead wraps
|
||||
for the opposite reason**: `--width-content` is `80rem`, so the content column
|
||||
shrinks to 720 px while the header's `min-inline-size: 44px` / `min-block-size:
|
||||
44px` floors do not, and the row wraps at **every** viewport width. The header
|
||||
stood at **120 px against a 54.56 px `scroll-padding-top`** — **65 px of `#main`
|
||||
behind it on the 21 pages that render the tagline**, at 1056 through 1920 px.
|
||||
`/` measured 12 px, and that 12 px is **pre-existing**: the previous build is 67 px
|
||||
against the same 54.56 px offset on all 22 pages. So the wrap fix widened a
|
||||
pre-existing 12 px defect to 65 px, and the second term closes both.
|
||||
|
||||
**The shipped form is two bands, each gating on the width ITS layout requires,
|
||||
and two terms, each catching one direction:**
|
||||
|
||||
```css
|
||||
@media (min-width: 66rem) { .site-header {
|
||||
position: sticky;
|
||||
inset-block-start: clamp(-100vh,
|
||||
min(calc((100vw - 66rem) * 100000), calc((1rem - 16px) * 100000)), 0px); } }
|
||||
@media (min-width: 76rem) { .site-header {
|
||||
inset-block-start: clamp(-100vh,
|
||||
min(calc((100vw - 76rem) * 100000), calc((1rem - 16px) * 100000)), 0px); } }
|
||||
```
|
||||
|
||||
`1rem - 16px` is negative exactly when the root is below the CSS initial font
|
||||
size — the 16 px the whole rem scale is built on — and it also catches the
|
||||
root-style equivalent, where the previous form left the header sticky at 69 px
|
||||
against a 60.63 px offset at root 10. **The cost, measured and accepted:** a
|
||||
reader whose default text size is "Small" (12 px) loses the sticky header, where
|
||||
it currently works — but only just: the header is 73 px against a 72.75 px offset,
|
||||
a **0.25 px** margin. Trading a 0.25 px margin for a provable gate is the right
|
||||
way round.
|
||||
|
||||
Both thresholds are the existing layout breakpoints, and both sit just above the
|
||||
measured one-row fit width (1047 px ≈ 65.4 rem without the tagline, 1207 px ≈
|
||||
75.4 rem with it), so the gate fires **at or before** the wrap rather than after
|
||||
it. **At every normal size both evaluate to exactly `0px`** — a matched
|
||||
`min-width: 66rem` guarantees `100vw ≥ 1056 px` and a matched `min-width: 76rem`
|
||||
guarantees `100vw ≥ 1216 px`, because `100vw` counts a classic scrollbar and the
|
||||
query width does not, so the term can only be more positive.
|
||||
|
||||
**Measured after: 0.00 px of `#main` covered — 0 of 300 grid points across roots
|
||||
16–32 × viewports 1056–1920 on three pages, 0 of 176 rows at the default size and
|
||||
at a 200 % root size over all 22 pages, 0 of 144 cases sitting directly on the
|
||||
two thresholds, and 0 of 198 at the SEAM where the two bands meet** (viewports
|
||||
1180–1260 × roots 16–32). ⚠️ **`covered: 0` is also what a non-sticky header
|
||||
gives, so the seam was checked the other way round as well:** at the default size
|
||||
across 1180 / 1200 / 1210 / 1215 / 1216 / 1217 / 1220 / 1240 / 1260 / 1440 /
|
||||
1920 px the computed `inset-block-start` is `0px` and the header is still pinned
|
||||
at top 0.00 when scrolled — stickiness is preserved, not quietly lost. ⚠️ **Saturation leaves a transition band, and it is
|
||||
arithmetic rather than measured:** a finite factor means the response is only
|
||||
a near-step, so the band is about (header − `scroll-padding-top`) / factor ≈
|
||||
**0.0007 px** of viewport width. CDP takes integer viewport widths, so that band
|
||||
was not driven directly — it is bounded by the arithmetic, not by a probe.
|
||||
|
||||
*(Lightning CSS folds the factor into the units and emits
|
||||
`clamp(-100vh,10000000vw - 6600000rem,0px)`. Verified equivalent by measurement,
|
||||
not by reading: the computed `inset-block-start` is `0px` at every sticky width at
|
||||
the default size, and the header pins at top 0.00.)*
|
||||
|
||||
⚠️ **"AN OFFSET TOO SHORT CANNOT ARISE" WAS WRITTEN HERE AND IN TWO SHIPPED
|
||||
COMMENTS, AND IT IS FALSE. THE EXCEPTIONS ARE ENUMERATED RATHER THAN WAVED AT**,
|
||||
because a comment that says "cannot" is the kind that stops the next reader
|
||||
looking. The gate handles **root-relative** enlargement in both directions.
|
||||
What it cannot see, measured, header height against computed
|
||||
`scroll-padding-top`, header `top` = `0px` in every row:
|
||||
|
||||
| condition | header | offset | short by | status |
|
||||
|---|---|---|---|---|
|
||||
| `minimumFontSize=32` | 164.58–270.56 px | 97 px | **68–174 px** | live, unfixable in CSS |
|
||||
| fallback font metrics + 7th nav item, **default** text size, 1056–1091 px | 141 px | 97 px | **44 px** | latent on `showInsights` |
|
||||
| `defaultFontSize=9` | 120 px | 54.56 px | ~~65 px~~ | **fixed** by the `1rem - 16px` term |
|
||||
| root-style 10 px | 69 px | 60.63 px | ~~8 px~~ | **fixed** by the same term |
|
||||
|
||||
**`--header-h` IS A FLOOR, NOT A CONSTANT**, and that is the sentence that
|
||||
had to change. It is the header's height at the **default** text size; above the
|
||||
default the masthead is deliberately taller, and declaration (2) is what makes
|
||||
that safe — where the gate can see the header exceed the token it is not sticky,
|
||||
so a `scroll-padding-top` that is too *generous* is harmless. ⚠️ **It does NOT
|
||||
make a short offset impossible: the two cases the gate cannot see are tabulated
|
||||
above.** The token previously asserted "one constant 81 px across
|
||||
every width where it is sticky", and that sentence is what made a 944 px
|
||||
overflow read as settled.
|
||||
|
||||
**What the narrow-width fix DID establish, and it is worth keeping separate.**
|
||||
The narrow residual had been charged to that same `flex-wrap: nowrap` — and for
|
||||
@@ -447,11 +642,15 @@ Not a polish pass. A build requirement.
|
||||
defended forever**, because the reason given is one nobody wants to re-open —
|
||||
which is exactly what happened for four days.
|
||||
|
||||
**The eight narrow-width fixes — no `overflow-x` was added anywhere.** Seven are
|
||||
cause-specific; the eighth is marked as the backstop it is:
|
||||
**TEN FIXES ACROSS THE TWO PASSES — no `overflow-x` was added anywhere.** Eight
|
||||
are narrow-width (2026-08-31); the last two are the large-text nav (2026-09-01).
|
||||
All but one are cause-specific; the exception is marked as the backstop it is:
|
||||
|
||||
| Element | Was | 320 px | Cause |
|
||||
|---|---|---|---|
|
||||
| `SiteHeader .header-inner` | `flex-wrap: nowrap` removed above 66 rem | **944 px** at 1280 px, root 32 | **Necessary and sufficient, measured.** A `nowrap` flex line cannot break, so the brand / nav / CTA row could not reflow at all |
|
||||
| `SiteHeader .nav-list` | `flex-wrap: nowrap` and a dead `flex: none` removed above 66 rem | **0 px — inert** | Kept removed so the `nowrap` prohibition is not contradicted by a `nowrap` in the same file. Byte-identical to shipped at every width and every text setting, six items and seven |
|
||||
| `SiteHeader .site-header` | `inset-block-start: 0` → a saturating `clamp()` gate, in **two** bands (66 rem, and 76 rem where the tagline shows) | *(consequence of the row above)* | Wrapping makes the header 244.59–350.86 px at root 32; sticky at that height covered `#main` by up to 157 px after the skip link. **Round 1's single-band `min()` ramped instead of stepping and left 20 of 300 grid points covered by 10–69 px** |
|
||||
| `SiteHeader .brand-name` | `white-space: nowrap` removed | **63 px, all 22 pages** | Two words glued into one unbreakable box |
|
||||
| `/bio/ .rates-list` | `overflow-wrap: anywhere` | **152 px** | `https://adr.smlcompany.ca/fees/` has no break opportunity, and the single grid track it sized stretched all five rows to 424 px |
|
||||
| `/ .hero-h` | `overflow-wrap: anywhere` | 36 px | "contract," at 104 px held `.hero-copy` open |
|
||||
@@ -475,7 +674,37 @@ Not a polish pass. A build requirement.
|
||||
card, escaping its own rounded border. Two lines is the better of those. The
|
||||
clamp does not change any normal size: 10vw holds 32 px from 320 px up.
|
||||
|
||||
**Three instrument findings, and each one hid a real defect:**
|
||||
**Six instrument findings, and each one hid a real defect. The last three are
|
||||
from the 2026-09-01 pass and all three produced a CLEAN-LOOKING result:**
|
||||
|
||||
4. ⚠️ **A UNIFORM PASS FROM A TAUTOLOGY.** The skip-link probe reported
|
||||
`0.0 px of #main covered` on **946 of 946** page-widths, before and after,
|
||||
under every mechanism. `global.css` sets `scroll-behavior: smooth`, which
|
||||
makes `scrollIntoView()` **asynchronous**, so a synchronous read afterwards
|
||||
measures `scrollY = 0`; and at `scrollY = 0` the target sits exactly at the
|
||||
header's bottom edge, so `covered` is 0 **by construction**. Pass
|
||||
`behavior: 'instant'` and assert that `scrollY` equals its predicted value —
|
||||
once fixed the same probe reported 10–26 px covered on 22 of 22 pages, a
|
||||
pre-existing defect the tautology had hidden.
|
||||
5. ⚠️ **A RUNNING TRANSITION MAKES A PROPERTY READ RETURN THE OLD VALUE.**
|
||||
Forcing reduced motion — by CDP emulation *or* by
|
||||
`--force-prefers-reduced-motion` — made `documentElement.style.fontSize =
|
||||
'32px'` read back as **16 px**, while the inline `style` attribute plainly
|
||||
said `font-size: 32px`. This codebase's reduced-motion block sets
|
||||
`transition-duration: 0.01ms !important` on `*`, which starts a transition on
|
||||
**every** property change, and a synchronous `getComputedStyle` then returns
|
||||
the transition's *start* value. The instrument now injects
|
||||
`transition:none;animation:none` instead. Same family as *a running
|
||||
transition outranks an `!important` author declaration*.
|
||||
6. **A DOM-INJECTED ELEMENT RECEIVES NO SCOPED STYLES.** Astro scopes component
|
||||
CSS with a `data-astro-cid-*` attribute, so the hand-built
|
||||
`<a class="nav-link">` used to test a seventh nav item got **none** of
|
||||
`.nav-link`'s rules — no `min-inline-size`, no `min-block-size`, no
|
||||
`font-size` — and measured narrower and shorter than a real item, which is
|
||||
the direction that makes a width test pass. **Clone a real node** and assert
|
||||
its computed styles match a sibling's.
|
||||
|
||||
The original three:
|
||||
|
||||
1. **`overflow-wrap: break-word` permits a break at layout time but does not
|
||||
reduce min-content size** — `anywhere` does. Five of the fixes are this
|
||||
@@ -498,6 +727,82 @@ Not a polish pass. A build requirement.
|
||||
`documentElement.scrollWidth - documentElement.clientWidth`, which is correct
|
||||
in both modes.
|
||||
|
||||
⚠️ **THE ONE RESIDUAL THAT REMAINS, AND IT IS THE FOOTER RATHER THAN THE
|
||||
HEADER.** Under Chrome's **minimum font size** only, document overflow remains at
|
||||
four widths — **14 px at 320, 38 px at 1024, 30 px at 1056, 19 px at 1100**, on
|
||||
all 22 pages, falling to 0 from 1200 px up. Every instance is
|
||||
`SiteFooter`: `info@smlcompany.ca` has no break opportunity and needs **310 px**
|
||||
inside a 224–243 px footer-contact column, and at 320 px the `.footer-col` box
|
||||
itself measures 310 px inside a 272 px content area. **The FOOTER'S OWN overflow is
|
||||
byte-identical before and after** — `div.footer-grid` 78 px at 1056 and 67 px at
|
||||
1100, `footer.site-footer` 30 px and 19 px, unchanged — and the 320 px and
|
||||
1024 px *document* figures are unchanged too, at 14 and 38. ⚠️ **The 1056 px and
|
||||
1100 px document figures DID change, 414 → 30 and 370 → 19, and this sentence
|
||||
claimed all four were identical.** They changed because the header stopped
|
||||
contributing its own 414 px and 370 px, not because the footer moved; a document
|
||||
figure and a footer contribution are different measurements and only the second
|
||||
is unchanged. Found by `adversarial-reviewer`. It is **out of the scope Pouya set** for this step ("the header and
|
||||
its consumers"), so it was measured and left rather than improvised: the
|
||||
1024–1100 px half looks like one `overflow-wrap: anywhere` on the footer contact
|
||||
links, the 320 px half is a column-sizing question and is not the same fix.
|
||||
Not header-side: **no header element extends the document at any width under any
|
||||
mechanism**, and the nav items and CTA are on-screen in **1408 of 1408**
|
||||
measurements (four text settings × 22 pages × 16 widths). *(This said "946 of
|
||||
946" for one revision. 946 is a different sweep — the four runs made with the
|
||||
tautological skip probe in instrument finding 4 — and reusing it here for the
|
||||
final build was the two-similar-totals mistake this section keeps warning
|
||||
about.)* *(The Practice dropdown panel's contents do overflow their own box
|
||||
by 84 px under this setting, but the panel sits inside a **closed** `<details>`
|
||||
and never extends the document — checked by taking the furthest-right box rather
|
||||
than the largest content overflow.)*
|
||||
|
||||
⚠️ **AND ONE THAT CANNOT BE CLOSED IN CSS AT ALL.** Under minimum font size the
|
||||
sticky gate above cannot fire — **both** of its terms evaluate to `0` because
|
||||
`rem` still resolves at 16 px there, so `100vw - 66rem` is positive and
|
||||
`1rem - 16px` is exactly zero — and the header stays sticky at
|
||||
**164.58–270.56 px** and the skip link lands with **68–174 px of `#main` behind
|
||||
it**, on 22 of 22 pages from 1056 px up. It was **10–26 px before**, so this pass
|
||||
widened it. *(These read "228–271 px" and "132–174 px" for one revision; both
|
||||
omitted the single-row band — 68 px at 1200 px and on `/` from 1216 px up.
|
||||
`adversarial-reviewer` measured the widths the first sweep had not.)* The trade is deliberate and is the right way round: what it bought is the
|
||||
removal of **830 px of horizontal overflow with four nav items and the CTA
|
||||
unreachable** under that same setting. Vertical offset leaves every target
|
||||
reachable by scrolling; the horizontal case did not. **No media query, container
|
||||
query or length unit can detect this mechanism** (see the table above), the only
|
||||
candidates left are a fitted magic number tied to the current nav item count or
|
||||
a normal-settings behaviour change, and Pouya's constraint was to stop and report
|
||||
rather than ship either. Carried on `docs/06`'s cutover checklist.
|
||||
|
||||
⚠️ **AND ONE MORE LATENT CASE, AT THE DEFAULT TEXT SIZE — NO READER SETTING
|
||||
INVOLVED: FALLBACK FONT METRICS PLUS THE SEVENTH NAV ITEM.** With every `.woff2`
|
||||
blocked — what a reader on the Slow 4G profile `docs/04` budgets against sees
|
||||
first, since the fonts are `font-display: swap` — and a seventh nav item cloned
|
||||
from a real node, the header measures **141 px at every width from 1056 to
|
||||
1091 px**, and 81 px from 1092 px up. With the webfonts loaded it is 81 px at all
|
||||
of them. **Two consequences, and the record first carried only the smaller one:**
|
||||
|
||||
1. **A 60 px collapse** when Geist swaps in — a CLS contribution on all 22 pages,
|
||||
against the CLS < 0.05 budget. **Larger, not new:** the previous build gives
|
||||
86.59–86.97 px in the same band, so a ~5.6 px shift already existed.
|
||||
2. ⚠️ **AND 44 px OF `#main` BEHIND THE STICKY HEADER** after the skip link,
|
||||
because 141 px exceeds the 97 px `scroll-padding-top`. **This one IS new** —
|
||||
the previous build's 86.97 px stayed under 97 px, so it covered **0**. Framing
|
||||
this case as "a CLS contribution" understated it, and a reader ruling on a
|
||||
layout shift would weigh it differently from one ruling on the skip link.
|
||||
|
||||
*(The band was recorded as "1056 / 1064 / 1072 / 1084 … 81 px from 1092 up",
|
||||
which reads as though 1086–1090 were checked and clear; they are all 141 px. It
|
||||
is a contiguous **36 px** band, 1056–1091. Both errors found by
|
||||
`adversarial-reviewer`.)*
|
||||
|
||||
**It is latent, not shipped:** with six items there is no wrap at any width, and
|
||||
`showInsights` turns the seventh on only at two published articles. **Not fixed
|
||||
here, and deliberately:** the two candidates are raising the desktop breakpoint
|
||||
past 1091 px, which changes the layout at normal settings in that band, or giving
|
||||
Geist a metric-matched `size-adjust` fallback, which is a font-stack change well
|
||||
outside "the header and its consumers". Both are Pouya's call; carried on
|
||||
`docs/06`.
|
||||
|
||||
**What is still latent, measured and not a defect today:** `.hero-h` has **16
|
||||
scoped rules, one per page, and only 2 carry `overflow-wrap: anywhere`** (`/` and
|
||||
`/about/`). The other 14 measure 0 because their headlines' longest words are
|
||||
|
||||
+69
-15
@@ -534,21 +534,75 @@ Then invalidate `/*`.
|
||||
- [ ] Favicon set complete
|
||||
- [ ] Tested on iOS Safari, Android Chrome, desktop Safari/Chrome/Firefox
|
||||
- [ ] Tested at 320 px and at 200% zoom
|
||||
- [ ] ⚠️ **THE 200%-DEFAULT-TEXT RESIDUAL AT 1280 px AND 1920 px IS RULED ON,
|
||||
EITHER WAY. BLOCKING, AND IT IS A DECISION RATHER THAN A TASK.** With
|
||||
`documentElement.style.fontSize = '32px'` the site is over by **944 px on 21
|
||||
pages and 508 px on `/` at 1280 px**, and **304 px on 21 pages at 1920 px**
|
||||
— and it is not a scroll margin: the nav clips mid-word and **Practice, Fees,
|
||||
Contact and the header CTA are entirely off-screen — including the route to
|
||||
the inquiry form**, WCAG 1.4.4 with loss of functionality. The cause is `flex-wrap: nowrap` on `.nav-list` above
|
||||
66 rem, a **locked step-1 header decision**, so no fix was improvised — see
|
||||
`docs/02` §Reflow for the measurement, the two non-equivalent ways of
|
||||
simulating 200% text, and the two candidate fixes. **This item is ticked by
|
||||
Pouya either fixing it or recording it as knowingly accepted with the
|
||||
measured consequence — not by re-measuring it with the method that reports
|
||||
zero.** The narrow widths (320/360/390/414/640/1024) are fixed and are 0.
|
||||
Raised by `adversarial-reviewer`, 2026-08-31, against a record that had
|
||||
declared the whole thing fixed
|
||||
- [x] ✅ **THE 200%-TEXT NAV OVERFLOW IS FIXED, 2026-09-01 — THIS ITEM IS
|
||||
DISCHARGED.** Pouya reopened the step-1 header decision and ruled *fix, do
|
||||
not accept*. `flex-wrap: nowrap` is gone from `.header-inner` (the necessary
|
||||
and sufficient one, measured) and from `.nav-list`, and the masthead is
|
||||
sticky only while it is one row — a saturating `clamp()` gate in two bands,
|
||||
66 rem and 76 rem. Measured across 22 pages × 16 widths, before and after on
|
||||
the same grid: **root-style 32 px went from 175 of 352 page-widths over to
|
||||
0**; minimum font size from 219 to 88, all of the remainder being the footer;
|
||||
default font size stays 0. Nav items and CTA on-screen in **1408 of 1408**
|
||||
measurements. The shipped rendering at the default size is
|
||||
**geometry-identical — 0 differences on 352 page-widths**, header 81.00 px,
|
||||
CTA on the content edge, with six nav items and with a seventh.
|
||||
⚠️ **AND IT WAS VERIFIED ACROSS THE INTERMEDIATE ROOT SIZES, WHICH IS WHAT
|
||||
ROUND 1 DID NOT DO.** WCAG 1.4.4 is a requirement up TO 200 %, not AT 200 %:
|
||||
round 1 sampled root 16 and root 32 only, both clean, and shipped a gate that
|
||||
left `#main` behind the header at **20 of 300** grid points across roots
|
||||
18–30. **And round 2 found the gate was one-sided**: Chrome's "Font size" has
|
||||
five presets (9 / 12 / 16 / 20 / 24 px) and only 16 had been swept, so at
|
||||
**9 px the masthead wrapped for the opposite reason** — the `80rem` content
|
||||
cap shrinks while the header's px minimums do not — leaving **65 px of `#main`
|
||||
behind the header** on 21 of 22 pages at every width. A second gate term
|
||||
(`1rem - 16px`) closes it, and closes a **pre-existing 12 px** shortfall on
|
||||
all 22 pages at that setting as well. Now **0 failing across 762 points**:
|
||||
roots 9–15 (70), roots 16–32 (300), the band seam (198), both thresholds
|
||||
(144) and all five default-font-size presets (50). Both findings by
|
||||
`adversarial-reviewer`; `docs/02` §Reflow carries all of it
|
||||
- [ ] ⚠️ **TWO REFLOW RESIDUALS SURVIVE, BOTH UNDER CHROME'S MINIMUM-FONT-SIZE
|
||||
SETTING ONLY, AND BOTH NEED A RULING RATHER THAN A RE-MEASUREMENT.**
|
||||
*(a) The footer, and it is OUT OF the scope Pouya set for the header step.*
|
||||
Document overflow of **14 px at 320, 38 px at 1024, 30 px at 1056 and 19 px
|
||||
at 1100 CSS px**, on all 22 pages, 0 from 1200 px up. Cause:
|
||||
`info@smlcompany.ca` has no break opportunity and needs 310 px inside a
|
||||
224–243 px footer-contact column; at 320 px the `.footer-col` box itself is
|
||||
310 px in a 272 px content area. **Pre-existing: the footer's OWN overflow is
|
||||
byte-identical before and after** (`div.footer-grid` 78 px at 1056, 67 px at
|
||||
1100). The 1056/1100 *document* figures did change, 414 → 30 and 370 → 19,
|
||||
because the header stopped contributing its own — a document figure and a
|
||||
footer contribution are different measurements.
|
||||
*(b) The skip link, and it CANNOT be fixed in CSS.* The header stays sticky
|
||||
at **164.58–270.56 px** under that setting, so "Skip to content" lands with
|
||||
**68–174 px of `#main` behind the header** on 22 of 22 pages from 1056 px
|
||||
up — it was 10–26 px before, so the header fix widened it, deliberately, to
|
||||
buy the removal of 830 px of horizontal overflow with four nav items and the
|
||||
CTA unreachable. **Nothing in CSS can detect that mechanism**: media queries,
|
||||
container queries AND length units all keep resolving `rem`/`em` at 16 px
|
||||
while `getComputedStyle` reports 32 px (measured; `docs/02` §Reflow). The
|
||||
remaining candidates are a fitted constant tied to the nav item count, or a
|
||||
behaviour change at normal settings. **Ticked by Pouya fixing (a) and ruling
|
||||
on (b) — not by re-measuring with a method that reports zero**
|
||||
|
||||
- [ ] ⚠️ **THE SEVENTH NAV ITEM PLUS FALLBACK FONT METRICS — A LAYOUT SHIFT *AND*
|
||||
A SKIP-LINK FAILURE, AT THE DEFAULT TEXT SIZE, ARMING ITSELF WHEN THE SECOND
|
||||
INSIGHTS ARTICLE PUBLISHES.** No reader setting is involved: with every
|
||||
`.woff2` blocked — what a reader on the Slow 4G profile `docs/04` budgets
|
||||
against sees first, since the fonts are `font-display: swap` — and a seventh
|
||||
nav item present, the header measures **141 px at every width from 1056 to
|
||||
1091 px** (a contiguous **36 px** band) and 81 px from 1092 px up; with the
|
||||
webfonts loaded it is 81 px throughout. Two consequences: **(i)** a **60 px
|
||||
collapse** when Geist swaps in, on all 22 pages, against the CLS < 0.05
|
||||
budget — *larger, not new*, since the previous build shifts ~5.6 px in the
|
||||
same band; and **(ii) 44 px of `#main` behind the sticky header** after "Skip
|
||||
to content", because 141 px exceeds the 97 px `scroll-padding-top` — **this
|
||||
half IS new**, the previous build's 86.97 px stayed under 97 px and covered 0.
|
||||
**Latent, not shipped:** six items never wrap, and `showInsights` turns the
|
||||
seventh on at two published articles. Two candidate fixes, both Pouya's call
|
||||
because both are outside "the header and its consumers": raise the desktop
|
||||
breakpoint past 1091 px, which changes the normal-settings layout in that
|
||||
band, or give Geist a metric-matched `size-adjust` fallback. Raised by
|
||||
`adversarial-reviewer`, 2026-09-01; `docs/02` §Reflow carries the measurements
|
||||
|
||||
**Infrastructure**
|
||||
- [ ] S3 versioning enabled
|
||||
|
||||
Reference in New Issue
Block a user