feat: eyebrow 14px and one small-text floor; fix a site-wide reflow defect; stamp TTL; §4 bars the struck universal

Pouya's five rulings of 2026-08-31, after 64bce10. One commit, because AGENTS.md
entry (aj) covers all five and the review fixes interleave across them —
recorded here rather than left to be inferred.

1. THE EYEBROW, 13 -> 14px, at the one token definition. `--text-eyebrow` is
deliberately NOT an alias of `--text-sm` despite sharing its value: the two move
for different reasons, and aliasing would mean a change to body-meta type
silently moving every eyebrow. Sweep by rendering, 22 pages x 2 widths, 801 mono
instances: 441 carry `.eyebrow`, 420 now at 14px, 21 held at 11px. Zero of 801
below 4.5:1.

Header check, which Pouya asked for explicitly: nothing header-side consumes the
token, `--header-h` is 81px, and the sticky header measures exactly 81.00px at
every width from 66rem up with the CTA on `.header-inner`'s content edge. Nothing
overflowed, so nothing was improvised.

2. /contact/'s LABELS RAISED, AND THE FLOOR CLAIM REWRITTEN BECAUSE THE RULING'S
PREMISE WAS FALSE. The ruling asked docs/02 to record the tagline as "the ONLY
sub-14px text on the site". It is not: the `--text-xs` 12px rung has ten
declaration sites and 130 elements rendering at 12px. So docs/02 records the
claim that IS verified — `--text-2xs` has one consumer in the repository — and
enumerates the 12px rung as a separate treatment. Writing a false claim into the
spec because the conclusion was handed down is the move the rules forbid.

A third block moved that the ruling did not name: /bio/'s ten <h2>s and its strap
were copies of five of `.eyebrow`'s six declarations at 11px — the same escape the
footer headings were. docs/02 justified holding them by the one-page PDF
constraint; measured, `@media print` sets both to 7pt, so the screen size never
reached the PDF and that was never the reason.

Consolidating them DID change the printed sheet, because the missing sixth
declaration was `font-weight`: 500 grew the PDF 89,496 -> 91,151 bytes. Print
therefore freezes 400, the committed PDF stays byte-identical (10 differing
bytes, all /CreationDate and /ModDate), and unifying is Pouya's call.

3. THE REFLOW DEFECT WAS SITE-WIDE AND /bio/ WAS NOT THE BINDING CAUSE.
`white-space: nowrap` on `SiteHeader .brand-name` held all 22 pages 63px over at
320px/root 32. Eight cause-specific fixes, no `overflow-x` anywhere; all eight
produce byte-identical geometry at root 16. `Pill` is marked in docs/02 as the
backstop it is — the real cause was `PracticeCard`'s rem-based padding, and with
it clamped "Construction" goes from 94x220 in six two-character lines to 158x85 in
two.

Three instrument findings now in docs/02, each of which hid a real defect:
`break-word` does not reduce min-content and `anywhere` does;
`getBoundingClientRect()` reports border boxes, so an element sweep cannot see
text spilling outside its own box; and `mobile: true` emulation expands the
layout viewport, so `scrollWidth - innerWidth` reads 0 while the reader still
scrolls sideways.

4. TTL STAMPED `ENABLED` in §7, with `DISABLED` at first verification recorded
rather than overwritten. Q60 narrowed to its second half and OPEN: no record has
been watched to vanish, and `ENABLED` proves the setting, not the behaviour.
R19's sweep found three stale copies outside §7; all now defer to it. R19 itself
was not edited — it points at §7 rather than carrying state, which is the
property that made it work.

5. §4 GAINS THE STRUCK-UNIVERSAL ROW, citing the committed ontario.ca extract and
cross-referenced to `check:claims`'s `struck-universal-q39`. It bars the claim in
BOTH directions: the commercial half is Pouya's attributed position, not a
verified fact. The row immediately caught two places asserting it flatly —
`SiteHeader.astro` and §9 Q33 — both now attributed. `check:claims` unmodified;
still frozen.

REVIEW: adversarial-reviewer, two rounds, 16 findings, ALL ACCEPTED, NONE
DECLINED. claims-auditor did not run (D20). Eight of round 2's ten were defects
in round 1's own fixes.

Round 1's blocking finding was a defect in my own record: I wrote that every
reflow residual was zero at "root 16 and root 32, 286 measurements". Two ways of
simulating 200% text are NOT equivalent, because media-query `rem` resolves
against the DEFAULT font size, not the root element's. Under the method docs/02
itself prescribes the site is 944px over on 21 pages and 508px on / at 1280, and
304px at 1920 — nav clipped mid-word, Practice/Fees/Contact and the CTA
off-screen, WCAG 1.4.4 with loss of functionality. My 286 excluded exactly the
widths where the defect lives.

NOT FIXED, DELIBERATELY: the cause is `flex-wrap: nowrap` on `.nav-list` above
66rem, a locked step-1 decision, and the standing instruction is to stop and
report rather than improvise a header change. It is now a blocking item on
docs/06's cutover checklist, ticked only by ruling on it — "not by re-measuring
it with the method that reports zero".

Round 2 also caught: a 63,743-byte figure that was the `cmp -l` differing-byte
count rather than the 1,655-byte size delta; a lost-navigation list naming
Med-Arb, which is not in the masthead at any width, while omitting Contact; the
tagline's justification left recorded at 13px, where one clause of it is false at
14px (20px of document overflow at 1216 with a seventh nav item); my own label
raise reintroducing a 38px element overflow at the width just ruled on; two
copies of the eyebrow treatment left at weight 400; three stale residual tables;
and the min-content explanation duplicated six times in src/.

VERIFIED, exit statuses read directly, never through a pipe: build 0 (22 pages),
check 0 (0 errors/0 warnings/0 hints), check:claims 0, og:proof 0, check:intake 0,
lint 0, minifier tripwire clean, TODO in dist 0 with a source sanity check.
Overflow: 374 measurements over 22 pages, every one zero, row counts asserted
before reading, with positive controls (119px at width 200, 319px at root 64).
Lighthouse 0, run three times with identical category scores: perf 99 on / and
100 elsewhere, a11y 100, best practices 100, SEO 100 on all indexable pages,
CLS 0.000. / sits at LCP 2.03s against a 2.0s budget, unchanged by this work.

The zsh `$VAR` word-split trap fired twice more and both times read as a clean
pass; caught only by asserting row counts first. Two of my own instruments were
wrong before they were right: a `grep -F '0.875rem'` state check that could never
match because Lightning CSS writes `.875rem`, and a probe whose inline
`!important` was outranked by a running CSS transition.

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-08-31 15:44:13 -04:00
co-authored by Claude Opus 5
parent 64bce105f8
commit 0f7595b602
15 changed files with 534 additions and 120 deletions
+124 -3
View File
@@ -652,6 +652,7 @@ cannot vouch for.
| The word **"lawyer"** used of Pouya; "called to the bar"; "licensed"; "my law practice"; "my litigation practice"; any post-nominal implying a licence | D13. The site asserts the JD and nothing further |
| Any phrasing that *implies* entitlement to practise law without saying so — "acts for clients", "represents parties", "my clients", "legal advice" | Same rule. Implication is the risk, not just the word. Describe the role factually instead: *Director of Firm Operations at a Toronto litigation and ADR boutique*, and the matter types worked on |
| **Q.Arb described as commenced, in progress, a pathway, or not yet held** | **It is HELD** `[verified 2026-08-29 — Pouya]`. Publish `Q.Arb (ADRIC / ADRIO)`. **The acquisition date is recorded in §4 and is NOT published** — and it is itself contradicted on the record and deliberately left that way (Q55, closed unresolved 2026-08-30). `npm run check:claims` `q-arb-as-a-stage` enforces the stage words and a date near the designation, on `dist/` |
| **"Anyone may be appointed an arbitrator in Ontario"; "nothing in law gates the role"; "no licence / designation is required to arbitrate" — and every paraphrase of that universal** | ⚠️ **THE UNIVERSAL IS FALSE, IT WAS IN THIS REGISTER FOR A DAY, AND IT REACHED A PUBLIC PAGE ONCE. Ruled a Forbidden row by Pouya, 2026-08-31.** Q39: this register carried *"Anyone may be appointed an arbitrator in Ontario. Nothing in law gates the role behind a designation"* and reasoned from it. Pouya checked the counter-example rather than defending it — *"My assertion was FALSE as a universal"* — and the source is committed at **`docs/reference/ontario-family-arbitration-training.md`** (`https://www.ontario.ca/page/training-family-arbitrators`, retrieved **2026-08-27**, per R14: the extract is in the repository so a reviewer can check the claim rather than trust it). That page states prescribed training for Ontario **family** arbitrators — 14 hours on screening for domestic violence and power imbalances, 30 hours of Ontario family law for arbitrators who are not members of a Canadian bar, and 10 hours over any two-year period ongoing. ⚠️ **THE SCOPED REPLACEMENT IS COMMERCIAL ARBITRATION AND IT IS POUYA'S ATTRIBUTED POSITION, NOT A VERIFIED FACT.** The source establishes the family half **directly** and the commercial half only by **absence** — a page about family arbitrators is not authority for what commercial arbitrators need — so §4 Offerings records the commercial half attributed to him and deliberately unstamped. **This row therefore bars the claim in BOTH directions:** do not publish the no-gate universal, and do not publish its converse either. This repository does not conclude a proposition of law. **Enforced mechanically as `struck-universal-q39` in `scripts/check-claims.mjs`**, which is where the bar lived alone — with no §4 row and no checklist line — from 2026-08-29 until this row existed; that gap was surfaced to Pouya in build step 10 and this is the side he fixed it on. Nothing on the site turns on the gated activity: he has confirmed he does not accept family arbitration under the *Family Law Act* (§4 Offerings, scope exclusion). **Swept four times before it was caught; recall is not evidence** `[verified 2026-08-27 — ontario.ca, committed extract]` |
| ~~Q.Arb described as held, imminent, or "nearly complete"~~ | **STRUCK 2026-08-29 — Q.ARB IS HELD.** This row was correct from 2026-08-26 to 2026-08-29 and is now exactly backwards. **What replaces it is the opposite bar:** Q.Arb described as *commenced*, *in progress*, *a pathway*, or *not yet held* — see the Verified row. The publishable form is `Q.Arb (ADRIC / ADRIO)`; the acquisition date is recorded in §4 and is **not** published |
### The substitution principle
@@ -721,7 +722,7 @@ the audience it targets. Revisit at month 1218. `[verified 2026-08-25 — dec
| ACM certificate | `arn:aws:acm:us-east-1:327082975128:certificate/2b6d5bdf-6790-430c-9b82-c00ab66e6d87` — ISSUED `[verified 2026-08-26]` |
| Intake API | `adr-intake-api`, HTTP API `4tl0m5igkj`, endpoint `https://4tl0m5igkj.execute-api.ca-central-1.amazonaws.com` `[verified 2026-08-26]` |
| Intake Lambda | `adr-intake-handler`, `nodejs24.x` `[verified 2026-08-26]` |
| Intake table | `adr-intake-submissions` (DynamoDB, ca-central-1) `[verified 2026-08-26]`. ⚠️ **TTL WAS `DISABLED` AT FIRST VERIFICATION Pouya ran `describe-time-to-live` on 2026-08-31 and it returned `DISABLED`.** `/legal/privacy/` has stated since build step 10 that a record is *"deleted automatically by the database rather than by someone remembering to do it"* after 24 months, so **that promise was unbacked from the moment it was written, and it stays unbacked in this record until a test record is confirmed deleted** — the handler wrote the attribute and nothing on the table consumed it. This is the Q22 shape on a public privacy commitment rather than on a deploy control: a documented mechanism that did not exist. **Pouya initiated enabling the same day with `AttributeName=ttl`** ``[assumed — initiated 2026-08-31 on his report; the status was NOT re-read to this record, and DynamoDB reports `ENABLING` for a period before it settles]``**, which is the attribute the handler writes** — `backend/intake/handler.mjs` writes `ttl` as a Number in **epoch seconds** at **24 months** (`RETENTION_MONTHS = 24`, added to `getUTCMonth()`), matching `docs/05` §Retention and the `ttl` row of its item table `[verified 2026-08-31 — read from the handler, not recalled]`. ⚠️ **`ENABLED` IS NOT YET STAMPED HERE, DELIBERATELY.** Enabling was initiated, not confirmed to this record, and DynamoDB reports `ENABLING` for a period before it settles — so the status must be re-run and stamped. **And `ENABLED` would prove the setting, not the behaviour:** `docs/06`'s cutover item requires a test record written with a near-future `ttl` and confirmed to disappear, and it is not ticked by reading this row or the handler code |
| Intake table | `adr-intake-submissions` (DynamoDB, ca-central-1) `[verified 2026-08-26]`. **TTL IS `ENABLED`, `AttributeName: ttl`** ``[verified 2026-08-31 — Pouya ran `describe-time-to-live` and read `TimeToLiveStatus: ENABLED`]``. The handler side matches: `backend/intake/handler.mjs` writes `ttl` as a Number in **epoch seconds** at **24 months** (`RETENTION_MONTHS = 24`, added to `getUTCMonth()`), which is `docs/05` §Retention and the `ttl` row of its item table `[verified 2026-08-31 — read from the handler, not recalled]`. ⚠️ **IT WAS `DISABLED` AT FIRST VERIFICATION EARLIER THE SAME DAY, AND THAT IS RECORDED RATHER THAN OVERWRITTEN.** Pouya ran `describe-time-to-live` on **2026-08-31** and it returned `DISABLED`; he enabled it on **2026-08-31** and re-read `ENABLED` the same day. `/legal/privacy/` has stated since build step 10 that a record is *"deleted automatically by the database rather than by someone remembering to do it"* after 24 months, so **that promise was unbacked from the day it was written until the day it was enabled** — the handler wrote the attribute and nothing on the table consumed it. This is the Q22 shape on a public privacy commitment rather than on a deploy control: a documented mechanism that did not exist. ⚠️ **`ENABLED` PROVES THE SETTING, NOT THE BEHAVIOUR, AND THE BEHAVIOUR IS STILL UNPROVEN — §9 Q60 STAYS OPEN.** No record has been written with a near-future `ttl` and watched to disappear. `docs/06`'s cutover checklist carries that test as a blocking item, it is not ticked by reading this row or the handler code, and §12 R19 keeps it surfacing until a deletion has actually been observed |
| SES identities | Domain `smlcompany.ca` **verified for sending** `[verified 2026-08-26]`; addresses `info@`, `intake@`, `adr@` |
| SES account | **Production access GRANTED** — out of the sandbox in `ca-central-1`, confirmed by AWS in writing and effective immediately `[verified 2026-08-26 — Q19 closed]`. Mail now reaches unverified recipients, so the inquirer confirmation in D18 works |
| Mail hosting | **Google Workspace** — MX `1 smtp.google.com`; `google._domainkey` present, so Google DKIM is configured `[verified 2026-08-26 — DNS query]` |
@@ -772,7 +773,7 @@ Nothing below can be invented. Each needs an answer from Pouya.
| # | Question | Blocks |
|---|---|---|
| **Q60** | ⚠️ **IS DYNAMODB TTL `ENABLED` ON THE INTAKE TABLE, AND HAS A TEST RECORD BEEN OBSERVED TO DISAPPEAR?** Two halves, and neither substitutes for the other. `/legal/privacy/` does not merely publish a retention *period* — it asserts a **mechanism**: *"the record is deleted automatically by the database rather than by someone remembering to do it"*. `describe-time-to-live` returned **`DISABLED`** when Pouya first ran it (2026-08-31; §7 holds the status and its stamp), so the mechanism was not running from the day that sentence was written. **The handler's side is already verified and is not what is being asked** — it writes `ttl` as a Number in epoch seconds at 24 months, which is the attribute name being enabled; writing the attribute is not the mechanism. **Do not answer this from the handler code, and do not answer it from `ENABLED` alone:** `ENABLED` proves the setting, and only a record written with a near-future `ttl` and watched to vanish proves the behaviour. `TODO(pouya)` sits on the retention section of `src/pages/legal/privacy.astro`; `docs/06`'s cutover checklist carries both halves; §12 R19 keeps it surfacing. **Why this is a numbered question and not only a checklist line:** `CLAUDE.md` requires a `TODO(pouya)` plus a §9 row when a page needs a fact the repository does not have, and this page needs one — a cutover checklist fires once, at cutover, and §9 is what a person editing this page reads. Raised by `adversarial-reviewer` round 2, 2026-08-31 | **`/legal/privacy/` going public.** Nothing else — no other page states the mechanism, verified by sweeping `dist/` for the retention vocabulary and reading each hit in context |
| **Q60** | ⚠️ **HAS A TEST RECORD BEEN OBSERVED TO DISAPPEAR FROM THE INTAKE TABLE?** **Half one closed 2026-08-31: TTL is `ENABLED` with `AttributeName: ttl`, verified by command — §7 holds that status and this row does not restate it.** The question is now the second half alone, and it was never the smaller half. `/legal/privacy/` does not merely publish a retention *period* — it asserts a **mechanism**: *"the record is deleted automatically by the database rather than by someone remembering to do it"*. **`ENABLED` proves the setting; only a record written with a near-future `ttl` and watched to vanish proves the behaviour.** Two things this may NOT be answered from: the handler code, which writes the attribute and nothing more (that side is verified and is not what is being asked); and the table setting, which is what was just confirmed. ⚠️ **AND THE FIRST HALF IS THE REASON TO TRUST THE SECOND LESS, NOT MORE:** `describe-time-to-live` returned **`DISABLED`** when Pouya first ran it on 2026-08-31, so the sentence above was published against a mechanism that was not running, and nothing in the repo, the build or AWS reported it. A setting that was off for as long as nobody looked is not evidence that the behaviour now works. `TODO(pouya)` sits on the retention section of `src/pages/legal/privacy.astro`; `docs/06`'s cutover checklist carries the test as blocking; §12 R19 keeps it surfacing. **Why this is a numbered question and not only a checklist line:** `CLAUDE.md` requires a `TODO(pouya)` plus a §9 row when a page needs a fact the repository does not have, and this page needs one — a cutover checklist fires once, at cutover, and §9 is what a person editing this page reads. Raised by `adversarial-reviewer` round 2, 2026-08-31 | **`/legal/privacy/` going public.** Nothing else — no other page states the mechanism, verified by sweeping `dist/` for the retention vocabulary and reading each hit in context |
| ~~Q59~~ | ✅ **RULED AND CLOSED 2026-08-31 — Pouya. OVERTIME RUNS FROM THE SESSION CAP**: the fourth hour of a half day, the seventh of a full day. Not the billed envelope. `/fees/` shipped at build step 9 on this ruling and `docs/07` carries it in full. ⚠️ **THIS ROW NAMED A CONSTANT THAT NO LONGER EXISTS**`FEES.mediation.overtimeStartsAfterSessionHours` was deleted the same day as dead data: nothing read it, so reversing it would have changed nothing and failed nothing, which is Q22's shape at constant scope. **Where the ruling actually lives:** the trigger is rendered on `/fees/` from `halfDay.hours` / `fullDay.hours`, and `FEES.mediation.reservation` carries the half that publishes as prose. Found by `adversarial-reviewer` round 2 — §9 is what a later implementer reads to find where a ruling is recorded, so pointing it at a deleted identifier is the same defect one layer up. ⚠️ **AND THE RULING CAME WITH A SECOND HALF THAT ANSWERS THE ARITHMETIC ANOMALY THIS ROW EXISTED TO ESCALATE, WHICH THE TRIGGER ALONE COULD NOT.** His words: *"a full day reserves the day; half-day overtime is subject to availability."* **The full-day fee buys the DAY, not six hours of it.** Read as a price comparison the table below says the full-day rate is never the cheaper choice; read knowing what each fee reserves, the $2,000-narrowing-to-$500 spread is the price of certainty rather than a defect. The sentence is `FEES.mediation.reservation` and it publishes **adjacent to the overtime row**, not as a footnote — the same structural rule as `PROCESS_FRAMING` beside the five timings under Q43, because a reader who takes the number and skips the framing has read a different offer. **THE ANOMALY IS NOT CLOSED AND STAYS ON §12 R5.** The gap is in D14's own figures — the half-to-full step is $2,000 against $1,500 for three hours of overtime — and the reservation point explains what it buys without removing it; the spread is largest at three to five hours, which is the band a half-day booking actually overruns into. `docs/07` §Recorded dissent carries the table for the 12-month review. **The original question, kept because the shape of it is the lesson.** *Where does the overtime hour start?* `docs/07`'s card carried *"Overtime, per hour — $500"* and had never said what it was overtime **to**. Q58's ruling settled the two allowances and did not reach this; Q15Q17's answer records the rate with no trigger. The two candidates were the session cap (3 h / 6 h) and the billed envelope (5 h / 9 h), and this repository was barred from picking one — a fee term is a fact we do not have, and `CLAUDE.md`'s rule for that is a question, not an inference. **It cost two strikes to hold that line:** a first pass at `docs/07`'s Q58 note asserted the session cap as applied fact and `adversarial-reviewer` struck it in the change set that wrote it; a round-1 fix then published the $500 rate on `/for-parties/` beside an unambiguous *"up to 3 hours"*, which **defines the trigger by adjacency** — nothing else on the page is a quantity it can attach to — and round 2 struck that too. Both strikes were right, and the ruling supplied the value they were waiting for | ~~`/fees/`, `/for-parties/`~~ — both now unblocked and shipped |
| ~~Q58~~ | **RULED 2026-08-31 — `hours` IS THE SESSION, AND THE AMBIGUITY WAS IN `docs/07` RATHER THAN IN ANY COPY. Pouya owned it in terms:** *"the ambiguity is mine… My `docs/07` wording said "up to 3.5 h, including 2 h preparation", which is genuinely unclear: 3.5 was meant as the TOTAL time committed, of which 2 is preparation — leaving 1.5 hours in the room. Your arithmetic caught it: if prep sat inside, 3.5 and 7 wouldn't be exactly 2×, because preparation doesn't scale with session length. The intended reading is the market's, and my wording obscured it."* **THE CORRECTED CARD, in his words:** *"Half day — up to 3 hours of session. Fee includes up to 2 hours of preparation. $2,000. Full day — up to 6 hours of session. Fee includes up to 3 hours of preparation. $4,000."* His reason for 3 and 6: *"the market convention — Patey and Zuber both publish "all or part of 3 hours" and "all or part of 6 hours", and those were the comparables the rate was set against. Selling 1.5 hours of room time as a half day would be an outlier nobody would recognise."* ⚠️ **ONE PROVENANCE NOTE, and it is R14's rule rather than a doubt about the ruling:** `docs/07`'s committed extract records Patey and Zuber at **3 h** and **6 h** but **does not carry the phrase "all or part of"** — so `docs/07` cites the hours, not the phrase, and the phrase is not attributed to them anywhere in the repository. The hours corroborate the ruling on their own, and ADR Chambers' roster rate in the same table is the clearest corroboration of the *shape*: *"one half hour of preparation time per party **and** up to three hours of mediation"* — preparation counted separately from a three-hour session. **APPLIED:** `docs/07`'s two card rows and its §All parameters confirmed (which prescribed the flat *"including 2 hours"*, the form `/for-parties/` then shipped); `FEES.mediation.*.hours` 3.5 → 3 and 7 → 6 with the semantics in the constant's doc comment; `/for-parties/` now states the session length interpolated from the constant and the preparation allowance **as a cap**. **The preparation allowance is CAPPED and must be published as capped***"including **up to** 2 hours"*, never the flat form and never "preparation included". **`/fees/` is UNBLOCKED for build step 9.** **The question as raised is preserved below.** **DOES `hours` IN THE MEDIATION RATE CARD MEAN THE LENGTH OF THE DAY, OR THE BILLED ENVELOPE INCLUDING PREPARATION?** `docs/07-fees.md` reads *"Half day — **up to 3.5 h, including 2 h preparation**"* and *"Full day — up to 7 h, including 3 h preparation"*. Taken at face value, 3.5 is the whole billed envelope and the **time in the room is 1.5 h** for a half day and **4 h** for a full day. **Against that reading:** 3.5 and 7 are exactly 2×, which they would not be if preparation sat inside them (1.5 vs 4 is not 2×). So either the card's wording is wrong in the one document that is the authority on money, or `FEES.mediation.*.hours` in `src/data/site.ts` does not mean what a page would naturally publish it as. **This was one sentence from shipping.** A draft of `/for-parties/` answered *"What happens on the day?"* with *"A half day is about 3.5 hours"* — the envelope presented as the day, to the reader least able to check it. The sentence was removed rather than resolved by inference; the page now says only that you book a half day or a full day. **What is needed:** one line from Pouya saying which the 3.5 and 7 are. Then `docs/07`'s two rows or `site.ts`'s field gains the correction, and the semantics go in the constant's doc comment (a warning is there now). **`/fees/` at build step 9 publishes this table and cannot be built without the answer.** Raised by `adversarial-reviewer`, 2026-08-30 | **Nothing.** No page stated a duration while the question was open — the one draft sentence that did was removed rather than reconciled, which is why the ruling had nothing to correct in public copy |
| ~~Q57~~ | **CLOSED 2026-08-31 — NO SEVENTH UNDERTAKING, AND THE PAGE IS COMPLETE AS IT STANDS.** Pouya: *"`/process/` stating when conflicts are run and what the check needs is complete. A reader assumes the outcome, and the obvious undertaking ("if a conflict is found I decline") adds nothing a reader doesn't already infer. Your restraint was right — §4's gate held. Record it closed rather than open, so it stops appearing in the live list."* **So this is a closure, not a deferral:** the answer is that the page says nothing further, which was one of the two outcomes the question named. §4 gains no seventh conduct undertaking and `CONDUCT_UNDERTAKINGS` still holds six. **APPLIED:** the `TODO(pouya)` is removed from `src/pages/process.astro` §Conflicts and replaced with the ruling, so a later reader finds the decision where the question was rather than an open marker; the file header's *"see the TODO below"* is corrected to cite this closure. `src/` now carries **zero** live `TODO(pouya)` markers. **The question as raised is preserved below.** **WHAT HAPPENS WHEN A CONFLICTS CHECK TURNS SOMETHING UP?** `/process/` §Conflicts ships saying **when** the check runs (the intake call, before anything is agreed) and **what it needs** (full legal names of the parties, the parent or affiliate actually behind the dispute, counsel on each side). It stops there, and the stop is deliberate: **any sentence naming the outcome is a SEVENTH conduct undertaking**, and §4's gate for that class is one line — *"an undertaking may be published only where Pouya has made it in terms. Not 'would obviously agree to', not 'follows from the process' — said."* *"If a conflict appears I decline the appointment"* is exactly what that gate refuses to let this repository infer, however obvious it looks. **What is needed:** one sentence from Pouya, in his words, or a decision that the page says nothing further. `TODO(pouya)` sits at `src/pages/process.astro` §Conflicts. Raised at build step 6, 2026-08-30 | **Nothing.** The section shipped accurate and unchanged; what closed is whether anything more was owed |
@@ -812,7 +813,7 @@ Nothing below can be invented. Each needs an answer from Pouya.
| ~~Q36-orig~~ | **Is co-arbitration work available now?** Asserted in three specs — `docs/01` §`/arbitration/`, `docs/03` §Voice as model copy, and `docs/07-fees.md`, which instructs *"The page must say so"* — and **it is in no §4 row.** §4 records the Q.Arb pathway as commenced August 2026 and nothing about what is available in the meantime. The sentence is offered under the heading *"Name the limits"*, so the unverified half travels inside advice about honesty, which is how it survived. All three files now cite this question; none has been deleted, because the *shape* of the sentence is right and only its second clause is unsourced. Raised by `claims-auditor`, 2026-08-26 | `/arbitration/` (step 4) and `/fees/` (step 9) |
| ~~Q37~~ | **ANSWERED 2026-08-27 — the label changes.** `JD + ML` is now labelled **"Legal training and engineering practice"**, not "Law and engineering". Pouya's reasoning, and it is the finding rather than the fix: *"The parallel was doing the implying — a degree and a practice under one noun. The asymmetry is the honest part."* A degree is not a practice, and a label that renders them as two instances of one thing invites the reader to supply the missing symmetry — which for "Law" is precisely the licensure implication D13 bars. The new label is longer and less tidy, deliberately. `docs/03`'s credential-row table is amended, and the `TODO(pouya)` is gone from `src/data/site.ts`. Original question follows | — |
| ~~Q37-orig~~ | **Is "Law and engineering" the right label under `JD + ML`?** §4's substitution principle supplies the three credential-row **values** (`Q.Med` / `JD + ML` / `EN · FA`); the labels come from `docs/03` and are not themselves registered. `claims-auditor` reads this one as a D13 implication risk: "engineering" is shorthand for a **verified practising** claim, so the parallel invites "Law" to be read the same way, and D13 bars implication as hard as assertion. Left as written — it is approved copy and changing approved copy is not an implementer's call — with a `TODO(pouya)` on it in `src/data/site.ts`. Nothing renders it yet. Raised by `claims-auditor`, 2026-08-26 | The credential row on `/` (step 2) and `/about/` (step 3) |
| ~~Q33~~ | **ANSWERED 2026-08-26 — the question rested on a false premise.** It assumed the constraint on describing arbitration was about *entitlement*. ADR designations are voluntary credentials, not licences, and **commercial arbitral appointment in Ontario is not gated behind a designation** so the constraint was always positional, never legal, and it was Pouya's to answer. *(This row said "anyone may be appointed an arbitrator in Ontario" until 2026-08-27. **Pouya checked it and it is false as a universal** — family arbitration is gated; see Q39. Corrected in place, because §9 is Current Truth. The Change Log entries that quote the original stand unedited, which is where the history lives.)* He accepts arbitration appointments now. `Mediation · Arbitration · Toronto` is **restored to the masthead one-liner**, and ~~`docs/04`'s specified home `<title>` stands as written~~**AMENDED 2026-08-28.** The literal is replaced by a composition of `SITE.name` + `SITE.tagline`, so the masthead constants and the title cannot drift; the shipped string is `Pouya Lajevardi · Mediation · Arbitration · Toronto`. Q33's substance is untouched — the unscoped masthead form is still ratified — only the instruction to keep a hand-typed literal is See **§4 Offerings**, added for exactly this class of statement. The original question text follows for the record | — |
| ~~Q33~~ | **ANSWERED 2026-08-26 — the question rested on a false premise.** It assumed the constraint on describing arbitration was about *entitlement*. ADR designations are voluntary credentials rather than licences, and **on Pouya's stated basis, which §4 Offerings records attributed to him and deliberately unstamped**commercial arbitral appointment in Ontario is not gated behind a designation, so the constraint was always positional rather than legal, and it was his to answer. ⚠️ **STATED AS HIS POSITION, NOT AS FACT:** §4 Forbidden's `struck-universal-q39` row bars the class claim about arbitral gating **in both directions**, and this repository does not conclude a proposition of law. *(This row said "anyone may be appointed an arbitrator in Ontario" until 2026-08-27. **Pouya checked it and it is false as a universal** — family arbitration is gated; see Q39. Corrected in place, because §9 is Current Truth. The Change Log entries that quote the original stand unedited, which is where the history lives.)* He accepts arbitration appointments now. `Mediation · Arbitration · Toronto` is **restored to the masthead one-liner**, and ~~`docs/04`'s specified home `<title>` stands as written~~**AMENDED 2026-08-28.** The literal is replaced by a composition of `SITE.name` + `SITE.tagline`, so the masthead constants and the title cannot drift; the shipped string is `Pouya Lajevardi · Mediation · Arbitration · Toronto`. Q33's substance is untouched — the unscoped masthead form is still ratified — only the instruction to keep a hand-typed literal is struck. See **§4 Offerings**, added for exactly this class of statement. The original question text follows for the record | — |
| ~~Q33-orig~~ | **How is arbitration described in site-wide copy before Q.Arb is conferred?** Not a wording question — a positioning one, and it recurs on every page. §4 records the Q.Arb pathway as **commenced August 2026**, "never as held or nearing completion". `SITE.tagline` reads `Mediation · Arbitration · Toronto`, and `docs/01` specifies that exact string as the **Home hero eyebrow** — where the hero copy beneath it can qualify it. Build step 1 promoted it to a masthead line under Pouya's name on every page, where nothing qualifies it and it reads as a designation strip; `claims-auditor` flagged it and **it has been removed from the header**. But the question it exposed is still open and lands at build step 2: does the hero eyebrow keep `Arbitration` as specified, qualify it, or hold it until designation? `/arbitration/` (step 4) is separately specced to state the position plainly, which is the honest model — the issue is the one-line forms that have no room to. **Second pass, same day:** the removal from the header did not sweep the proof sheet, where the same string was **hand-typed** as a type specimen and rendered ten times at up to 96px. Both defects fixed — the specimen is now a neutral pangram — and the lesson is the sweep rule again: a removal is a `grep`, not a memory. The nav item `Arbitration` also survives, deliberately; that is Q35. Raised by `claims-auditor`, 2026-08-26 | The Home hero (step 2); `docs/04`'s specified home `<title>`; any bio or one-line descriptor |
| **Q34** | **Do §2's measurements of the previous build still hold, and were they measured against the deployed file?** Two of `claims-auditor`'s findings were unverified old-site assertions published in copy, which sent Claude Code to measure the live URL on 2026-08-26. What came back does not straightforwardly match §2. **Reproduced:** the response is a single **2,206,032-byte** file, and `<title>` is still `SML Company · Dispute Resolution`. **Could not be reproduced from the served HTML:** there are **zero** `base64,` occurrences anywhere in it — §2 records "two logo PNGs ~1 MB, base64-inlined" — and no reference to `unpkg` or a React development build. **Why this is a question and not a correction:** the served document is a *bundler harness*. 2,210,921 bytes of it are inline `<script>`, the real `<head>` sits JSON-escaped inside a `<script type="__bundler/template">`, and the application is loaded from **nine UUID-named script files that were not fetched**. The logos and the CDN references may well be in those bundles. So §2 may be right, may be stale, or may have been measured against the local source rather than the deployment — this record cannot tell which. **Why it matters beyond tidiness:** `docs/02` and `CLAUDE.md` both cite the base64-logo figure as the *reason* for a standing convention, and §12 R8 exists because unverified premises propagate. Nobody should quote these numbers in public copy until the question closes. Raised by Claude Code, 2026-08-26 | Nothing structural. It governs whether §2's figures may be cited as fact |
| ~~Q39~~ | **ANSWERED 2026-08-27 — and the answer is that the register's premise was FALSE.** Pouya checked it rather than defending it. His words: *"My assertion was FALSE as a universal."* **(a) Is it gated? YES.** Family arbitrators in Ontario must complete prescribed training — 14 hours on screening for domestic violence and power imbalances, 30 hours of Ontario family law for arbitrators outside the Ontario or another Canadian bar, and 10 hours ongoing per two-year period. **Sourced, and the source is now in the repo** per R14: `docs/reference/ontario-family-arbitration-training.md`, retrieved 2026-08-27 with provenance. **(b) Does the offering extend to it? NO** — and that is what actually disposes of the question. Pouya does not do family arbitration, so it is a **scope exclusion, not a gating problem**, and the prescribed-training requirement never bears on this practice. `Shareholder & Family Business` means **commercial disputes among family shareholders** — shareholder and partnership disputes, co-founder breakdowns, business succession — and its page must state plainly that family law matters are not accepted. *One sentence, not a section: Pouya's instruction is that it earns its place only by saving a wasted intake call.* **(c) One honest limit on the source.** The extract establishes the family gate directly and the commercial half only by absence — it neither mentions nor excludes commercial arbitration. §4 therefore carries *"commercial arbitration in Ontario requires no licence and no designation"* as **Pouya's stated position, unstamped**, not as a sourced fact. Do not let the footnote promote it. **Also swept:** the false universal survived in three live places outside the paragraph that was corrected — §4's NOT-NEGOTIABLE boundary bullet, §9's Q33 closure row, and a comment in `SiteHeader.astro`. All three fixed; the `grep` and its output are pasted in **Change Log entry (w), §Sweeps**. *(`adversarial-reviewer` flagged this citation while it still pointed at nothing: the entry was written at Phase 6, after review, so for the duration of the review a §9 row asserted in the present tense that evidence "is in the Change Log" before it was. Fair catch. The row now names the entry, and the reviewer independently re-ran the sweep and confirmed it was genuinely complete.)* Original question follows | — |
@@ -937,6 +938,126 @@ never being raised again.
# Change Log
## 2026-08-31 (aj) — The eyebrow goes to 14 px and the small-text floor becomes real; the reflow defect turns out to be site-wide and its record turns out to be wrong twice; TTL is stamped `ENABLED`; §4 gains the struck-universal row
**Prior commit:** `64bce10` — entry (ai)'s twelve-file tree (eyebrow 12 → 13 px, TTL confirmed and gated), committed as-is on Pouya's instruction. It is a deliberate departure from one-logical-change-per-commit: entry (ai) covers both items and splitting it would have meant rewriting the record rather than moving it.
**Scope:** Pouya's five rulings of 2026-08-31, after (ai) shipped.
---
### 1. `--text-eyebrow` 13 → 14 px, from the one definition
`0.8125rem``0.875rem` in `tokens.css`. One edit site; `global.css`'s `.eyebrow` already pointed at the token and did not change.
**14 px is the same value as `--text-sm`, and the token is deliberately NOT an alias of it.** The eyebrow and body-meta type move for different reasons, and aliasing would mean a future change to meta text silently moving every eyebrow. The "13 px is not a rung on the scale" reasoning is retired: at 14 px it *is* that rung.
**Rendered sweep, 22 pages × 2 widths, 801 mono uppercase-or-tracked instances measured over CDP.** 441 elements carry `.eyebrow`: **420 at 14 px, 21 held at 11 px.** Every `.eyebrow` instance moved. Contrast on all four grounds unchanged and passing — gold-l on ink 11.09, meta on cream 5.47, meta on cream-2 5.01, gold-l on maroon 8.11 — and **zero of 801 below 4.5 : 1**.
**Header check, which is what Pouya asked for explicitly.** Nothing header-side consumes `--text-eyebrow`: `git grep 'text-eyebrow' -- src` returns the token definition and `global.css` only. `--header-h` computes to `5.0625rem` = **81 px**, and the sticky header measures **exactly 81.00 px** at 1056 / 1200 / 1216 / 1240 / 1280 / 1440 / 1600, with the CTA landing exactly on `.header-inner`'s content edge. Nothing overflowed, so nothing was improvised.
### 2. `/contact/`'s labels raised, and the floor claim rewritten because the ruling's premise was false
`label`/`legend` and the direct-contact `<dt>`s → `--text-eyebrow`. `--text-2xs` now has **one consumer in the whole repository**`git grep 'var(--text-2xs)' -- src` returns `SiteHeader.astro:211` and nothing else.
⚠️ **THE RULING ASKED FOR A RECORD THAT IS NOT TRUE, AND IT IS NOT WHAT WAS WRITTEN.** *"Record in docs/02 that it is now the ONLY sub-14px text on the site."* Measured: the `--text-xs` (12 px) rung has **ten declaration sites** and **130 elements carrying their own text** render at 12 px across the 22 pages. So `docs/02` records the claim that *is* verified — one consumer of the 11 px token — and enumerates the 12 px rung as a separate treatment that was not in scope. Softening a false claim into a defensible one is the move this project forbids; the answer was to write the true one.
**A third block moved that the ruling did not name, and the reason it was held turned out not to apply.** `/bio/`'s ten `<h2>`s and its strap were **copies of five of `.eyebrow`'s six declarations at 11 px** — mono, `0.18em`, uppercase, `--text-meta`: the eyebrow treatment, hand-typed, the same escape the footer's column headings were in (ai). `docs/02` justified the carve-out by `npm run bio:pdf` holding the sheet to one page. **Measured: the `@media print` block sets both to 7 pt, so the screen size never reached the PDF.** They now carry the class.
⚠️ **AND CONSOLIDATING THEM CHANGED THE PRINTED SHEET, WHICH IS WHY PRINT FREEZES `font-weight: 400`.** The missing sixth declaration was `font-weight`; taking `.eyebrow`'s 500 grew the PDF **89,496 → 91,151 bytes**. That is a visual change to the artefact circulated with an appointment proposal, from a ruling that was about size — so print keeps 400, the committed PDF stays byte-identical, and **whether to unify is Pouya's call**. With the freeze in place `npm run bio:pdf` renders **89,496 bytes, 1 page**, and `cmp -l` against the committed file gives **exactly 10 differing bytes, all inside `/CreationDate` and `/ModDate`**. Reverted; `git status public/` is clean.
### 3. The reflow defect was site-wide, not `/bio/`'s — and `/bio/` was not even the binding cause
Pouya ruled the 152/112/82 px overflows a WCAG defect to be fixed at the cause, never with `overflow-x: hidden`. **The sweep he asked for found the siblings on all 21 other pages.**
| Element | Fix | 320 px contribution | Cause |
|---|---|---|---|
| `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/` sized a single-column grid track; all five rate rows stretched to 424 px |
| `/ .hero-h` | `overflow-wrap: anywhere` | 36 px | One word at 104 px held `.hero-copy` open |
| `/ .feature-body` | `overflow-wrap: anywhere` | 26 px | "party-appointed" at 202 px |
| `CredentialRow .credential-label` | `overflow-wrap: anywhere` | 38 px | "designation" needs 143 px in an 88 px track |
| `SiteFooter .footer-brand` | `flex-wrap: wrap` | 12 px | Flex item at `min-width: auto` cannot shrink below "Lajevardi" |
| `PracticeCard .card` | `padding` clamped | *(the cause under `Pill`)* | rem-based `--space-6` is 64 px a side at root 32 |
| `Pill` | `overflow-wrap: anywhere` | 33 px → 1 px | **Backstop, not cause** — a one-word pill cannot wrap at a space that is not there |
`flex-wrap: wrap` on the footer brand rather than `anywhere`, because it reflows without hyphenating a person's name. **All eight produce byte-identical geometry at root 16** — the clamp holds 32 px from 320 px up, and the brand name is one line at all 16 widths.
**Three instrument findings, each of which hid a real defect, now in `docs/02` §Reflow:** `overflow-wrap: break-word` does not reduce min-content and `anywhere` does (five of the fixes are this distinction); `getBoundingClientRect()` reports **border boxes**, so an element sweep cannot see text spilling outside its own box — which is how a 38 px document overflow had an invisible offender; and under `mobile: true` emulation the **layout viewport expands to fit the content**, so `scrollWidth innerWidth` reads 0 at a requested 320 while the reader still scrolls sideways. The honest formula is `scrollWidth clientWidth`.
### 4. TTL stamped `ENABLED`; Q60 stays open on the half that matters
§7 carries `TimeToLiveStatus: ENABLED`, `AttributeName: ttl` `[verified 2026-08-31]`, and records that it was `DISABLED` at first verification the same day — the promise on `/legal/privacy/` was unbacked from the day it was written until the day it was enabled. **Q60 is narrowed to its second half and stays OPEN:** no record has been written with a near-future `ttl` and watched to vanish. `ENABLED` proves the setting, not the behaviour.
**R19's sweep found two stale copies outside §7 and a third that two review rounds surfaced.** `docs/05:376` said *"THE SETTING WAS MEASURED OFF"*; `privacy.astro`'s `TODO(pouya)` said *"returned `DISABLED`"*; and `privacy.astro:39` still said the promise *"depends on a control nobody has verified"*. All three now carry the constraint and defer to §7. **R19 itself was deliberately not edited** — it points at §7 and Q60 rather than carrying state, which is the property that made it work.
### 5. §4 gains the struck-universal row, and the sweep it demands found two files stating the claim flatly
A Forbidden row for *"anyone may be appointed an arbitrator in Ontario"* / *"nothing in law gates the role"* and paraphrases, citing `docs/reference/ontario-family-arbitration-training.md` (ontario.ca, retrieved 2026-08-27, the 14 / 30 / 10-hour figures) and cross-referenced to `check:claims`'s `struck-universal-q39`. **The row bars the claim in both directions**: the commercial half is Pouya's attributed position, not a verified fact, because a page about family arbitrators is not authority for what commercial arbitrators need. `docs/03`'s checklist gains a citing line — it could not have one before, because an item there cites a §4 row rather than restating a bar.
⚠️ **AND THE ROW IMMEDIATELY CAUGHT TWO PLACES ASSERTING IT AS FACT:** `SiteHeader.astro`'s brand comment and **§9 Q33**. Both now attribute it. §4 Offerings was already careful (*"His stated basis:"*); these two were not. `check:claims` was **not** modified — it stays frozen.
---
### Adversarial review — two rounds, 16 findings, all accepted, none declined
`adversarial-reviewer` only. `claims-auditor` did not run — **D20**.
**Round 1, six findings. One blocking, and it was a defect in my own record.** I had written into `docs/02` that every reflow residual was *"0 px on all 22 pages… at root 16 px and root 32 px — 286 measurements, every one zero"*, including that the old 602/944 px figures at 1280 were *"0 too"*. **They are not.**
⚠️ **THE CAUSE IS THAT TWO WAYS OF SIMULATING 200% TEXT ARE NOT EQUIVALENT, AND I GENERALISED ONE WITHOUT SAYING WHICH.** In a media query `rem` resolves against the **default** font size, not the root element's:
| method | `66rem` resolves to | nav above 1056 px | 1280 px, root 32 |
|---|---|---|---|
| `documentElement.style.fontSize='32px'` — the command `docs/02` itself prescribes | **1056 px**, matches | `sticky`, `flex-wrap: nowrap` | **944 px over** on 21 pages, 508 on `/` |
| `Page.setFontSizes({standard:32})` — what I used | **2112 px**, never matches | `relative`, `wrap` | **0** |
My 286 was 176 + 110, and the 110 covered five widths that **excluded 1024/1280/1920 at root 32** — precisely where the defect lives. The prose then generalised it. Re-measured under the prescribed method: **0 at 320/360/390/414/640/1024; 944 px on 21 pages and 508 px on `/` at 1280; 304 px on 21 pages at 1920.**
**It is not a scroll margin.** The farthest element is `div.header-cta` at **2224 px** against a 1280 px viewport; the nav clips mid-word after "Arbitration" and **Practice, Fees, Contact and the header CTA are off-screen** — including the route to the inquiry form. WCAG 1.4.4 with loss of functionality. **And there is a third mechanism which is the reachable one:** Chrome's "Minimum font size" *floors* computed sizes and leaves media queries alone — measured **830 px** over at 1280.
**NOT FIXED, AND DELIBERATELY SO.** The cause is `flex-wrap: nowrap` on `.nav-list` above 66 rem, a locked step-1 decision, and Pouya's standing instruction with the eyebrow ruling was *"if anything header-side does overflow, stop and report rather than improvising a layout change."* It is now a **blocking item on `docs/06`'s cutover checklist**, ticked only by him fixing it or accepting it with the measured consequence — *"not by re-measuring it with the method that reports zero."*
**The other five round-1 findings, all mine:** the "exactly one exception" floor claim was falsified thirty lines below in the same document; `global.css` sets `break-word` on **`p` and** `h1``h6`, so the premise I wrote for `.feature-body`'s fix was false while the fix was right; two stale TTL statements survived my own R19 sweep; `Pill`'s fix was symptom-specific, the real cause being `PracticeCard`'s rem-based padding; and 107 of 129 added `src/` lines were comment prose, with each of seven measurements stored twice.
**Round 2, ten findings, and eight were defects in round 1's own fixes** — the ratio D19's two-round cap exists for. The material ones:
- **`docs/02` said the print change was "63,743 bytes".** 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. The delta is **1,655**. Two figures and I quoted the one answering a question nobody asked.
- **The lost-navigation list named "Med-Arb", which is in `SECONDARY_NAV` and is not in the masthead at any width.** The item actually lost, and the worst of them, is **Contact**. Corrected in `docs/02` and in the `docs/06` blocker, which is ticked on the measured consequence.
- ⚠️ **Raising the eyebrow to 14 px made the tagline's own justification stale, and one clause of it false.** `SiteHeader.astro` recorded the constraint at 13 px. Re-measured at the live 14 px: header **84.98 px** against `--header-h` 81; CTA past the content edge **1.3 px at 1216 with the six items shipping today** and **67.8 / 43.8 / 51.8 / 51.8 px** with a seventh; and **20 px of document overflow at 1216, 4 px at 1280** — so *"document overflow is 0 in every one of those cases, so a page-level check cannot see this"* was true at 13 px and false at 14. At 11 px all three are zero with six items and with seven.
- **My own item-2 fix reintroduced an overflow at the width Pouya had just ruled on.** Moving `/contact/`'s labels 11 → 14 px put "Firm or organisation" **38 px outside its own box** at 320 px / root 32 — invisible to a document-level check, and found by the very instrument finding this change set added to `docs/02`. Fixed with `overflow-wrap: anywhere`.
- **The same move left two copies of the eyebrow treatment rendering at weight 400** under a `p.eyebrow` of identical size and colour, while the rewrite had deleted the sentence that legitimised the duplication. `font-weight` added to both; `docs/02` carries a **named** carve-out — they keep `--text-secondary` (11.75 : 1) rather than `.eyebrow`'s `--text-meta` (5.47 : 1) because a form label is operative text.
- **Three residual tables and an element-sweep sentence in `docs/02` still read as current** while the section below them said the residuals were zero. Annotated as dated history; the sweep sentence's own predicate now returns **0 elements**.
- **`CredentialRow.astro:60` still asserted the false constraint** with the correction 46 lines below it. Corrected in place — the track cannot overflow, its content can.
- **The `break-word`/min-content explanation appeared six times in `src/`.** Reduced to one-line citations of `docs/02` §Reflow; `git grep 'min-content' -- src` now returns **two hits, both predating this session** (`about.astro:751`, `index.astro:850`), which are deliberately left rather than widening the diff.
**Declined: none, in either round.**
### Verified — commands run, exit statuses read directly, never through a pipe
`npm run build` **0** (22 pages) · `npm run check` **0** (0 errors / 0 warnings / 0 hints, read as three lines) · `npm run check:claims` **0** · `npm run og:proof` **0** · `npm run check:intake` **0** · `npm run lint` **0** · minifier tripwire `grep -rE 'animation:[^;}]*(scroll\(\)|view\(\))' dist --include='*.css'` exit **1**, no output · `TODO` in built HTML **0**, with a source sanity check confirming the marker exists so the probe is not vacuous.
**Overflow, this pass: 374 measurements over 22 pages, every one zero**, row counts asserted before reading — 132 at `rootstyle` root 32 (320/360/390/414/640/1024), 132 at `default32` root 32, 110 at root 16 (320/390/640/1280/1920). **Positive controls**, because a uniform pass is the result you were hoping for and therefore ends the check: the same probe reports 119 px at width 200 and 319 px at root 64.
⚠️ **THE `$VAR` WORD-SPLIT TRAP FIRED TWICE MORE AND BOTH TIMES READ AS A CLEAN PASS.** zsh does not word-split parameter expansions, so `node probe.mjs 320 $PAGES` measured **one** page and printed "max=0, nonzero=0" at every width. Command substitution `$(cat file)` does split; parameter expansion does not. Caught only by asserting the row count first — which is now how every sweep in this entry is written.
**Two of my own instruments were wrong before they were right, and both failed in the safe direction only by luck.** A state assertion `grep -rqF '0.875rem' dist` could never match, because Lightning CSS writes `.875rem` — it would have read "BEFORE" forever. And a tagline probe reported the forced size not taking: an inline `!important` was being outranked by a **running CSS transition**, which sits above `!important` author declarations in the cascade. Reading the value it actually got, rather than asserting equality and stopping, is what found it.
`npm run lighthouse` **0** — 22 pages, no category below 95: perf 99 on `/` and 100 on the other 21, a11y 100 (with `prefers-reduced-motion` forced), best practices 100, SEO 100 on all 16 indexable pages and `69n` on the six deliberate `noindex`, CLS **0.000** everywhere. Run three times across this change set with identical category scores; LCP varies ±0.08 s between runs, and `/` sits at **2.03 s** against `docs/04`'s 2.0 s — unchanged by this work, and the script reports LCP rather than asserting it.
### Open, and what needs Pouya
- ⚠️ **The 1280 px / 1920 px reflow residual is his ruling to make** — fix the desktop nav or accept it with the measured consequence. Blocking on `docs/06`.
- **Q60** — the TTL test record. `ENABLED` is stamped; the behaviour is unproven and `/legal/privacy/` carries a `TODO(pouya)`.
- **`/bio/`'s print `font-weight` freeze** — unify to 500 and re-render the PDF, or leave it.
- **`.hero-h` has 16 scoped rules and 2 carry `anywhere`.** The other 14 measure 0 today. The single-source fix — `global.css`'s `h1``h6` rule from `break-word` to `anywhere`, deleting both overrides — has a site-wide blast radius on intrinsic sizing and was not measured, so it is recorded rather than done.
- **§12 R1 surfaced**, unchanged. R18's seven dated extracts, R10's memberships, R17's 2026-11-26 key rotation, R9's unconfirmed SES subscription and R5's fee arithmetic all remain live.
- **R11's general half was not exercised** — this was a fix session, not a build-order phase boundary, so no dependency pins were re-checked.
**Not committed.** 15 files.
---
## 2026-08-31 (ai) — The eyebrow goes to 13 px from one definition, and the sweep by rendering found one escaped copy of it and one deliberate override; DynamoDB TTL was `DISABLED` while `/legal/privacy/` promised automatic deletion
**Type:** design-system change (site-wide) + an operational fact recorded + a one-line handler correction.
+230 -45
View File
@@ -99,7 +99,8 @@ Geist 400.
**Scale.** Fluid, `clamp()`, `1.25` ratio at the small end widening to `1.333` at
the display end. Tokens `--text-2xs` through `--text-6xl` in `tokens.css`, plus
`--text-eyebrow` at 13 px — see the eyebrow rule below for why it is not a rung.
`--text-eyebrow` at 14 px — the same value as `--text-sm` and deliberately not an
alias of it; see the eyebrow rule below.
**Rules.**
@@ -108,31 +109,84 @@ the display end. Tokens `--text-2xs` through `--text-6xl` in `tokens.css`, plus
- Display line-height `0.95``1.05`; letter-spacing `-0.02em`.
- Body line-height `1.6`. Measure capped at `68ch` — the old site ran full-bleed
paragraphs at 1400 px, which is unreadable.
- Eyebrows: mono, **13 px** (`--text-eyebrow`), `0.18em` tracking, uppercase,
always paired with a real heading. **13 px is not a rung on the scale** — it
sits between `--text-xs` and `--text-sm` because uppercase mono at this
tracking reads smaller than it measures, and at 12 px it was being lost
against the display type (Pouya, 2026-08-31; it read 1112 px until then).
There is **one definition**, `.eyebrow` in `global.css`; the footer's column
headings carry that class rather than repeating it. An eyebrow is not a
heading and never carries the `<h*>` — a real heading may carry the class.
**"One definition" means one definition of the 13 px eyebrow.** The four 11 px
blocks in the next bullet repeat the mono/`0.18em`/uppercase treatment
deliberately and are not covered by it.
- `--text-2xs` (11 px) is the compressed-label floor, not the eyebrow floor.
**Three carve-outs, and they are deliberate rather than incidental** — do not
read this line as licence for an 11 px label anywhere else. (a) The header
tagline, and **it is load-bearing today, not at some future step:** 13 px takes
the header to 83.4 px while `--header-h` is 81 px, which `global.css` feeds
into `scroll-padding-top`. A seventh nav item is the second reason and that one
does arrive with Insights. Both measurements are in `SiteHeader.astro`. (b) The `/bio/` print sheet, which
`npm run bio:pdf` holds to one page. (c) On `/contact/`, both the
`<label>`/`<legend>` set and the direct-contact `<dl>` terms.
⚠️ **(c) is the weak one and is flagged as such.** 11 px uppercase mono
measures 5.47 : 1 and breaches no floor in this document or in WCAG, but it
now sits two steps below the eyebrow on the page that collects inquiries, for
no reason beyond it having been written that way. Raised with Pouya
2026-08-31; it is an accepted size, not a ratified one.
- Eyebrows: mono, **14 px** (`--text-eyebrow`), `0.18em` tracking, uppercase,
weight 500, always paired with a real heading. Pouya raised it **12 → 13 → 14 px
on 2026-08-31**, in two passes, because uppercase mono at this tracking reads
smaller than it measures and 13 px was still getting lost against the display
type. There is **one definition**, `.eyebrow` in `global.css`. An eyebrow is not
a heading and never carries the `<h*>` — but a real heading may carry the class,
and **fourteen do, in two groups**: the footer's four column headings
(`SiteFooter.astro`) and `/bio/`'s ten block headings.
⚠️ **"ONE DEFINITION" HAS ONE CARVE-OUT AND IT IS NAMED RATHER THAN IMPLIED:**
`/contact/`'s `<label>`/`<legend>` set and its direct-contact `<dt>`s repeat the
treatment instead of taking the class, because they need `--text-secondary`
(11.75 : 1) rather than `.eyebrow`'s `--text-meta` (5.47 : 1) — a form label is
operative text. **Everything else about them matches, `font-weight` included**;
without that they rendered at 400 beneath a `p.eyebrow` of the same size and
colour, which is the divergence a carve-out is meant to bound rather than hide.
Do not widen this to any other element.
**14 px is the same value as `--text-sm`, and `--text-eyebrow` is deliberately
not an alias of it.** The two move for different reasons; aliasing would mean a
future change to body-meta type silently moved every eyebrow on the site.
(This bullet said "13 px is not a rung on the scale — it sits between
`--text-xs` and `--text-sm`" for one revision. At 14 px it is that rung.)
- **THE 11 px FLOOR HAS EXACTLY ONE CONSUMER, AND THAT IS THE CLAIM THAT IS
TRUE.** Pouya's ruling, 2026-08-31: *"form labels are operative text, and the
site should have exactly one small-text floor."* `--text-2xs` (11 px) now has
**one consumer in `src/`** — the header tagline (`git grep 'var(--text-2xs)' --
src` returns one hit, `SiteHeader.astro`; 21 rendered instances, every one
`span.eyebrow.brand-tagline`) — and nothing else may use it without a
measurement recorded beside it.
⚠️ **THIS BULLET SAID "THE SMALL-TEXT FLOOR IS 14 px, AND THERE IS EXACTLY ONE
EXCEPTION" FOR ONE REVISION, AND THAT WAS FALSE** — falsified by this document
thirty lines below, which concedes a 12 px family. The ruling asked for the
record that the tagline is the only sub-14 px text on the site; **it is not, so
that is not what is recorded here.** What is true is the sentence above: one
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.
**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
one revision. (b) `/contact/`'s direct-contact `<dl>` terms, which are label
text on the same page and would otherwise have been left a step behind the
labels beside them. (c) `/bio/`'s ten block headings and its strap, which were
**copies of five of `.eyebrow`'s six declarations, at 11 px** — the same escape
the footer's column headings turned out to be — and now carry the class. Not
byte-for-byte: the `font-size` differed, and **the missing sixth was
`font-weight`**, which is precisely the one the print block now has to freeze.
⚠️ **(c)'s carve-out was justified by a reason that did not apply.** This bullet
said the `/bio/` sheet was held at 11 px because `npm run bio:pdf` holds it to
one page. Measured 2026-08-31: the `@media print` block sets both elements to
**7 pt**, so the screen size never reached the PDF and the one-page constraint
was never what kept them small. **Print does now freeze `font-weight: 400`**,
because taking `.eyebrow`'s 500 changed the printed sheet — the PDF grows from
**89,496 to 91,151 bytes, +1,655** — and the ruling that moved them was about
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`.
**`--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`,
and **`/bio/`'s two — `.fine` and `.sheet-contact p`**. The first eight are mono
and uppercase or tracked at `0.06em`, a third of the eyebrow's tracking, which is
what makes them a different treatment. **`/bio/`'s two are neither uppercase nor
tracked**, so they are not in that family at all — they are simply 12 px text,
and an earlier draft of this bullet defined the exception in a way that excluded
them and therefore missed them.
**Rendered count, and say what it is a number of:** at 1280 px / root 16 across
all 22 pages, **130 elements carrying their own text compute to 12 px** (149 if
elements that merely inherit the size are counted). `ArticleCard` and the article
meta contribute **zero** today — no article is published, so neither renders.
Every instance measures ≥ 5.01 : 1.
- Italic display (`.it`) is the one flourish the design allows. One italic phrase
per headline, at most.
- Never skip a heading level. `<h1>` once per page.
@@ -251,6 +305,12 @@ Not a polish pass. A build requirement.
32 px, a real accessibility setting and not page zoom — `/` measured **234 px**
of overflow at 390. Brought down in three measured steps:
⚠️ **THE THREE TABLES BELOW ARE A HISTORY OF HOW THE NUMBER CAME DOWN, NOT THE
CURRENT STATE.** Every residual in their final rows — the 3 px, the 23 px and the
63 px — went to **0 on 2026-08-31**, on all 22 pages at 320 / 360 / 390 / 414 /
640 / 1024 px. The current state is §Reflow below, which also carries what is
still NOT zero at 1280 px and 1920 px. Read the rows as dated steps.
| Fix | 390 px | 320 px |
|---|---|---|
| as first built | 234 px | 304 px |
@@ -280,10 +340,13 @@ Not a polish pass. A build requirement.
| `/med-arb/` | **0 px** | 23 px | 63 px |
At the **default** root size all three are **0 px at every width measured**.
The element-level sweep at 320/root-32, predicate `right > clientWidth`,
names **three** `PracticeCard` chips on `/mediation/` — Construction,
Technology, Shareholder — plus the header and footer brand elements, which
appear on all five pages. *(This read "six" for one pass. Six is the count of
⚠️ **THE ELEMENT-LEVEL SWEEP BELOW IS ALSO HISTORICAL.** It read: *"the
element-level sweep at 320/root-32, predicate `right > clientWidth`, names three
`PracticeCard` chips on `/mediation/` — Construction, Technology, Shareholder —
plus the header and footer brand elements, which appear on all five pages."*
Re-run with that exact predicate on 2026-08-31: **0 elements**, on all five pages
at 320 / 360 / 390 with root 32. The chips were fixed at their cause
(`PracticeCard`'s padding) and the brand elements at theirs; see §Reflow. *(This read "six" for one pass. Six is the count of
`article.card` with `scrollWidth > clientWidth`, a different predicate on a
different element; `adversarial-reviewer` re-ran it and no predicate yields
six chips. A number in this table has to be re-runnable, which is the whole
@@ -299,27 +362,149 @@ Not a polish pass. A build requirement.
single unbreakable 9-character name ("Lajevardi") exceeds the 224 px content
box at 320 px; `overflow-wrap: anywhere` is the only remedy that reduces
min-content size. `/about/` now measures equal to or better than `/` at all
three widths, and its 320/360 residual is the same header decision.
three widths. *(Its 320/360 residual was recorded here as "the same header
decision" until 2026-08-31, when that attribution turned out to be wrong and the
residual went to 0 — see below.)*
Command, so the numbers are re-runnable rather than quoted: headless Chrome
over the built `dist`, `document.documentElement.style.fontSize = '32px'`, then
`documentElement.scrollWidth - documentElement.clientWidth`, plus an
enumeration of every element wider than `clientWidth` to name the offender.
Two things worth keeping. **`overflow-wrap: break-word` permits a break at
layout time but does not reduce min-content size** — `anywhere` does, and that
distinction was the whole of one of those fixes. And 1280 px stays over, from the header's
deliberate `flex-wrap: nowrap` above 66 rem plus `white-space: nowrap` on the
brand name — **602 px on `/` and 944 px on every other page**, because
`SiteHeader` gates the tagline on `!isHome`, so the masthead is wider
everywhere except home. *(This read a flat "602 px" until 2026-08-28. That
figure was measured on `/` and generalised; `/about/` was already 944 when it
was added to this table, and step 4's three pages are 944 too. Found by
`adversarial-reviewer`.)* The 320 px residual is the same header
plus the display headline's 104 px floor. Undoing either re-opens the measured
step-1 header decision, so they stand. All of this is beyond what this floor
requires — page zoom is clean — so it is a robustness margin rather than a
failure. Revisit if a real reader hits it.
⚠️ **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.
**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"*.
| | 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** |
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.
**The corrected figures, `documentElement.style.fontSize='32px'`, 22 pages:**
| width | pages over | overflow |
|---|---|---|
| 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 `/`) |
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.
⚠️ **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."*
**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.
**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
the narrow case that attribution was **wrong**. The cause there was
`white-space: nowrap` on `.brand-name`, a different declaration in the same
component, and removing it **changes nothing at any real size**: the brand name
is one line at all 16 widths at root 16, the sticky header still measures exactly
81 px at every width from 66 rem up, and the CTA still lands exactly on
`.header-inner`'s content edge. **A residual defended by the wrong cause is
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:
| Element | Was | 320 px | Cause |
|---|---|---|---|
| `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 |
| `/ .feature-body` | `overflow-wrap: anywhere` | 26 px | "party-appointed" at 202 px |
| `CredentialRow .credential-label` | `overflow-wrap: anywhere` | 38 px | "designation" needs 143 px in an 88 px track |
| `SiteFooter .footer-brand` | `flex-wrap: wrap` | 12 px | Flex item at `min-width: auto` cannot shrink below "Lajevardi" |
| `PracticeCard .card` | `padding` clamped | *(the cause under `Pill`)* | The space scale is rem-based, so `--space-6` is 64 px a side at root 32 — 128 px of padding in a ~224 px box |
| `Pill` | `overflow-wrap: anywhere` | 33 px → **1 px** | ⚠️ **SYMPTOM, NOT CAUSE.** A one-word pill cannot wrap at a space that is not there |
`flex-wrap: wrap` on the footer brand rather than `anywhere`, because it reflows
without hyphenating a person's name.
⚠️ **`Pill`'s `anywhere` IS A BACKSTOP AND THE TRADE IS RECORDED RATHER THAN
HIDDEN.** It removes the min-content floor of a shrink-to-fit `inline-flex` box,
so the pill collapses to whatever its parent gives it. The real cause was
`PracticeCard`'s padding: with it clamped, "Construction" at root 32 / 320 px
went from **94 × 220 px broken into six two-character lines** to **158 × 85 px on
two lines**, and root 16 is unchanged at 121 × 27 px on one line. **`anywhere` is
still load-bearing** — removing it leaves 1 px of document overflow on `/`,
`/mediation/` and `/practice/`, and renders the pill 240 px wide inside a 224 px
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:**
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
distinction. `global.css:166` sets `break-word` on **`p` AND `h1``h6`** —
*(this line said "`h1``h6` and nothing else, so body copy inherits no
wrapping at all", which was wrong in a way that mattered: `.feature-body`
**is** a `<p>` and already had `break-word`, so the premise given for its fix
was false while the fix itself was right)*. Nothing else gets one, so a
`<span>`, `<li>`, `<dd>` or `<a>` inherits no wrapping.
2. **`getBoundingClientRect()` reports BORDER BOXES, so an element sweep cannot
see text spilling outside its own box.** `.credential-label`'s words ran 86 px
past an 88 px cell while every element's rect stayed inside the viewport —
the document was 38 px over and the offender was invisible to the predicate
this table's own command uses. Read `scrollWidth - clientWidth` per element as
well as per document.
3. **Under `Emulation.setDeviceMetricsOverride({mobile: true})` the LAYOUT
viewport expands to fit the content**, so `innerWidth` becomes 472 at a
requested 320 and `scrollWidth - innerWidth` reads **0** while the reader is
still scrolling sideways. The honest formula is
`documentElement.scrollWidth - documentElement.clientWidth`, which is correct
in both modes.
**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
shorter. The single-source fix would be to change `global.css`'s `h1``h6` rule
from `break-word` to `anywhere` and delete both overrides; that is a design-system
change with a site-wide blast radius on intrinsic sizing and it has not been
measured, so it is recorded here rather than done.
- **Measure the elements, not only the page.** A document-level overflow check
passes while a flex child absorbs the deficit by being crushed — that is how
+9
View File
@@ -537,6 +537,15 @@ Not both halves of the rule.**
long-term designation goal* row, which carries the reasoning and the
deliberate deviation from the strategy brief. `check:claims`
`c-med-arb-struck` sweeps `dist/`
- [ ] **A class statement about what Ontario law does or does not gate behind an
arbitral designation** — decided by §4 Forbidden's *"Anyone may be appointed
an arbitrator in Ontario" / "nothing in law gates the role"* row, which
carries the committed source and the reason the scoped replacement is
**attributed to Pouya rather than stamped**. It bars the claim in **both**
directions; this repository does not conclude a proposition of law.
`check:claims` `struck-universal-q39` sweeps `dist/`. *(Added 2026-08-31,
when Pouya ruled the §4 row into existence: this line could not exist before
it, because an item here cites a row rather than restating a bar.)*
- [ ] **A firm, a team, or offices that do not exist** — §4 Forbidden rows the
specific false artefacts (*"Since 2009"*, *"sixteen years"*, the London
and New York offices, the company number, and the fictitious founder);
+1 -1
View File
@@ -373,7 +373,7 @@ Plausible or Fathom, cookieless, no consent banner.
- [x] **Honeypot live.** ⚠️ **The timing check is NOT implemented** — see deviation 1 above; it is unimplementable on a CDN-cached static page and would be a control that does nothing
- [ ] **Rate limit configured** — API Gateway throttling, 5 requests / 5 minutes per source IP. Not expressible in handler code; not done
- [x] **The form's own protection is the `Origin` check, not CORS** — see deviation 2. CORS on the endpoint still to be restricted for scripted calls
- [ ] **TTL set and verified by test record.** ⚠️ **THIS ONE BACKS A PUBLISHED PROMISE, AND THE SETTING WAS MEASURED OFF ON 2026-08-31.** `/legal/privacy/` states that records are deleted automatically after 24 months. The handler writes the `ttl` attribute — epoch seconds, 24 months, confirmed against this spec `[verified 2026-08-31]`but `describe-time-to-live` returned **`DISABLED`** when Pouya first ran it, so the page promised a mechanism that was **not running**, from the day it was written. Enabling was initiated the same day with `AttributeName=ttl`; §7 records the state and deliberately does not yet stamp `ENABLED`. **The test record is what closes this, not the status:** `ENABLED` proves the setting, a record written with a near-future `ttl` and observed to vanish proves the behaviour
- [ ] **TTL set and verified by test record.** ⚠️ **THIS ONE BACKS A PUBLISHED PROMISE.** `/legal/privacy/` states that records are deleted automatically after 24 months, and it asserts the **mechanism**, not only the period. The handler writes the `ttl` attribute — epoch seconds, 24 months, confirmed against this spec `[verified 2026-08-31]`and **writing the attribute is not the mechanism**: TTL must also be enabled on the table, which is a table setting the code cannot see. **`AGENTS.md` §7 holds that status and its stamp; this line does not restate it** — it restated it once, went stale within the day, and had to be pulled back (§12 R19). **The test record is what closes this item, not the status:** `ENABLED` proves the setting, a record written with a near-future `ttl` and observed to vanish proves the behaviour. Tracked as §9 Q60
- [ ] KMS customer-managed key and PITR enabled. **Neither is claimed on `/legal/privacy/`** — the page says "encrypted at rest", which is true of every DynamoDB table unconditionally, and does not mention either of these because §7 does not verify them
- [ ] Both emails send; SPF/DKIM/DMARC aligned; inbox-tested, not spam-tested
- [ ] DLQ and CloudWatch alarm configured. The handler writes to DynamoDB **before** sending mail, so a replay cannot lose a submission
+15
View File
@@ -534,6 +534,21 @@ 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
**Infrastructure**
- [ ] S3 versioning enabled
+9 -4
View File
@@ -57,10 +57,12 @@ const { slots } = Astro.props;
measured-sounding comment that was false is this project's own named
failure mode.
`minmax(0, 1fr)` cannot overflow at any width or any root font size,
which also retires the `min(11rem, 100%)` guard this line briefly
`minmax(0, 1fr)` means the TRACK cannot overflow at any width or any root
font size, which retires the `min(11rem, 100%)` guard this line briefly
carried — that guard was fixing the overflow symptom of a floor that
should not have been there. */
should not have been there. ⚠️ **THE TRACK IS NOT THE CONTENT:** a label's
own words can overflow the track, and at a 200% default font size they
did. That is why `.credential-label` below carries `overflow-wrap`. */
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--space-6) var(--space-5);
margin: 0;
@@ -102,7 +104,10 @@ const { slots } = Astro.props;
deliberately long (AGENTS.md Q37) — it wraps to two lines at every width
and must not be prevented from doing so. Do not add `white-space: nowrap`
here, and do not shorten the label to make the row tidier: the asymmetry
is the honest part. */
is the honest part. ⚠️ `overflow-wrap: anywhere` is LOAD-BEARING here, not
cosmetic — `anywhere`, not `break-word`: see `docs/02` §Reflow, instrument
finding 1. */
text-wrap: pretty;
overflow-wrap: anywhere;
}
</style>
+6 -1
View File
@@ -85,7 +85,12 @@ void _props;
200% default font size (root 32px) overflowed the document by 111px at
320px — WCAG 1.4.10 Reflow. Measured 2026-08-28; no pill on the site is
that long today, which is exactly why this is easy to delete and must not
be. `normal` costs nothing at default size. */
be. `normal` costs nothing at default size.
⚠️ `normal` ALONE IS NOT ENOUGH — a one-word pill cannot wrap at a space
that is not there. `anywhere`, not `break-word`, and this is the BACKSTOP
rather than the cause: `docs/02` §Reflow, instrument finding 1 and the
`Pill` row. */
white-space: normal;
overflow-wrap: anywhere;
}
</style>
+7 -1
View File
@@ -48,7 +48,13 @@ const H = `h${level}` as 'h2' | 'h3';
flex-direction: column;
align-items: flex-start;
gap: var(--space-4);
padding: var(--space-6);
/* CLAMPED, not a flat --space-6, for the reason `.feature` on `/` carries:
the space scale is rem-based, so `2rem` is 64 px a side at a 200% default
font size — 128 px of padding inside a ~224 px content box, which left
~96 px for the whole card column and was the real cause of a `Pill`
overflowing. The 10vw term holds it at 32 px on every viewport from 320 px
up and lets it collapse only when the rem is doubled. */
padding: clamp(var(--space-4), 10vw, var(--space-6));
background: var(--bg);
border: 1px solid var(--border);
border-block-start: 2px solid var(--rule);
+6
View File
@@ -171,8 +171,14 @@ const aboutLinks = [
border-block-end: 1px solid var(--rule);
}
/* `flex-wrap: wrap` is the reflow fix, chosen OVER `overflow-wrap: anywhere` on
the name: the name is a flex item at `min-width: auto` and cannot shrink
below "Lajevardi", and wrapping the flex line breaks no word where
`anywhere` would have hyphenated a person's name. No effect at any normal
size. `docs/02` §Reflow carries the measurement. */
.footer-brand {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-3);
min-block-size: 44px;
+26 -14
View File
@@ -158,10 +158,15 @@ const isHome = path === '/';
Pouya's name read as a held capability, and §4 then recorded Q.Arb as
merely commenced. Q33 answered that the same day, and the premise was
wrong twice over — §4 now records Q.Arb as HELD, and even before it did:
ADR designations are voluntary credentials, not licences, and COMMERCIAL
arbitral appointment in Ontario is not gated behind a designation — so the
constraint was always positional, never legal, and Pouya accepts sole,
party-appointed and co-arbitration work today. See §4 Offerings.
ADR designations are voluntary credentials rather than licences, and on
**Pouya's stated position, which §4 Offerings records attributed to him and
deliberately unstamped**, commercial arbitral appointment in Ontario is not
gated behind a designation — so the constraint was always positional rather
than legal, and he accepts sole, party-appointed and co-arbitration work
today. ⚠️ **STATED AS HIS POSITION, NOT AS FACT, AND THAT IS REQUIRED:** §4
Forbidden bars the class claim about arbitral gating **in both directions**,
and this repository does not conclude a proposition of law. See §4 Offerings
and the `struck-universal-q39` row.
SCOPED 2026-08-27 (Q39). This comment said "Anyone may be appointed an
arbitrator in Ontario", which Pouya checked and found FALSE as a universal:
@@ -199,24 +204,31 @@ const isHome = path === '/';
same words as the hero eyebrow (docs/01). */
.brand-tagline {
display: none;
/* HELD AT 11px, BELOW THE `.eyebrow` THIS ELEMENT CARRIES. Two reasons, both
measured 2026-08-31 at 13px. (1) `--header-h` is 81px and the header goes
to 83.4px — `global.css` drives `scroll-padding-top` from that token. (2)
With a seventh nav item the CTA lands past `.header-inner`'s content edge
by 42px at 1216, 18px at 1240 and 26px at 1280 and 1440 — it does not
resolve as the viewport widens. Insights is that item; `showInsights`
turns it on at two published articles. ⚠️ Document overflow is 0 in every
one of those cases: flex lets the CTA sit past the padding edge without
growing the document, so a page-level check cannot see this. */
/* HELD AT 11px, BELOW THE `.eyebrow` THIS ELEMENT CARRIES. Measured at the
LIVE token value, 14px, on 2026-08-31 — earlier figures here were taken at
13px and understated all three costs. (1) `--header-h` is 81px and the
header goes to 84.98px; `global.css` drives `scroll-padding-top` from that
token. (2) The CTA lands past `.header-inner`'s content edge — 1.3px at
1216 with the SIX nav items shipping today, and 67.8 / 43.8 / 51.8 / 51.8px
at 1216 / 1240 / 1280 / 1440 with a seventh. It does not resolve as the
viewport widens. Insights is that item; `showInsights` turns it on at two
published articles. (3) ⚠️ AND AT 14px IT DOES GROW THE DOCUMENT: 20px of
overflow at 1216 and 4px at 1280 with seven items. At 13px it did not,
which is why this comment used to say a page-level check could not see it.
At 11px all three are zero, with six items and with seven. */
font-size: var(--text-2xs);
white-space: nowrap;
}
/* ⚠️ NEVER `white-space: nowrap` HERE. It was, until 2026-08-31, and because the
masthead is on all 22 pages that one declaration was the site's binding
reflow defect at a 200% default font size. The name is two words and takes
two lines when it has to; at every normal size it never wraps. WCAG 1.4.4 /
1.4.10; `docs/02` §Reflow carries the measurement. */
.brand-name {
font-family: var(--font-serif);
font-size: var(--text-xl);
line-height: var(--leading-tight);
letter-spacing: var(--tracking-tight);
white-space: nowrap;
color: var(--text);
}
.brand:hover .brand-name {
+35 -21
View File
@@ -119,12 +119,12 @@ const PROCESSES = [
</div>
<h1 class="sheet-name">{SITE.name}</h1>
<p class="sheet-desigs">{CREDENTIALS.designations.join(' · ')}</p>
<p class="sheet-strap">{SITE.tagline}</p>
<p class="eyebrow sheet-strap">{SITE.tagline}</p>
</header>
<div class="sheet-grid">
<section class="block block-wide">
<h2>The practice</h2>
<h2 class="eyebrow">The practice</h2>
<p>
{
/* ⚠️ NO LEADING SCOPE. This sentence read "I act as a neutral in
@@ -152,26 +152,26 @@ const PROCESSES = [
</section>
<section class="block">
<h2>Designations</h2>
<h2 class="eyebrow">Designations</h2>
<ul role="list">
{CREDENTIALS.designations.map((d) => <li>{d}</li>)}
</ul>
<h2>Education</h2>
<h2 class="eyebrow">Education</h2>
<ul role="list">
{CREDENTIALS.education.map((d) => <li>{d}</li>)}
</ul>
<h2>Certifications</h2>
<h2 class="eyebrow">Certifications</h2>
<ul role="list">
{CREDENTIALS.certifications.map((d) => <li>{d}</li>)}
</ul>
</section>
<section class="block">
<h2>Memberships</h2>
<h2 class="eyebrow">Memberships</h2>
<ul role="list">
{CREDENTIALS.memberships.map((d) => <li>{d}</li>)}
</ul>
<h2>Languages</h2>
<h2 class="eyebrow">Languages</h2>
<ul role="list">
<li>
{CREDENTIALS.languages.join(' and ')}, without an interpreter
@@ -180,14 +180,14 @@ const PROCESSES = [
</section>
<section class="block">
<h2>Processes</h2>
<h2 class="eyebrow">Processes</h2>
<ul role="list">
{PROCESSES.map((p) => <li>{p}</li>)}
</ul>
</section>
<section class="block">
<h2>Subject matter</h2>
<h2 class="eyebrow">Subject matter</h2>
<ul role="list">
{PRACTICE_AREAS.map((area) => <li>{area.name}</li>)}
</ul>
@@ -197,7 +197,7 @@ const PROCESSES = [
</section>
<section class="block block-wide">
<h2>Rates</h2>
<h2 class="eyebrow">Rates</h2>
<ul role="list" class="rates-list">
<li>
Half day, up to {halfDay.hours} hours of session — {
@@ -228,7 +228,7 @@ const PROCESSES = [
</section>
<section class="block block-wide sheet-contact">
<h2>Contact</h2>
<h2 class="eyebrow">Contact</h2>
<p>
{CONTACT.email} · {CONTACT.phoneFallback} · {CONTACT.location}
<br />
@@ -271,13 +271,13 @@ const PROCESSES = [
letter-spacing: var(--tracking-wide);
color: var(--accent);
}
/* Type comes from the global `.eyebrow` class on the element; only the margin is
here. This rule and `.block h2` below were byte-for-byte copies of
`.eyebrow`'s declarations at `--text-2xs` — the same escape the footer's
column headings were. The print block below sets both to 7pt, so the screen
size never reached the PDF. */
.sheet-strap {
margin-block-start: var(--space-2);
font-family: var(--font-mono);
font-size: var(--text-2xs);
letter-spacing: var(--tracking-eyebrow);
text-transform: uppercase;
color: var(--text-meta);
}
.sheet-grid {
@@ -290,12 +290,8 @@ const PROCESSES = [
grid-column: 1 / -1;
}
/* See `.sheet-strap` above: type from `.eyebrow`, only the rule under it here. */
.block h2 {
font-family: var(--font-mono);
font-size: var(--text-2xs);
letter-spacing: var(--tracking-eyebrow);
text-transform: uppercase;
color: var(--text-meta);
padding-block-end: var(--space-2);
border-block-end: 1px solid var(--border);
}
@@ -320,8 +316,13 @@ const PROCESSES = [
.block p + p {
margin-block-start: var(--space-3);
}
/* `anywhere`, and the cause is ONE STRING: the last row prints the fee-card URL,
which has no break opportunity and sized this single-column grid track. The
URL has to stay — printed sheet, the reader cannot click it. `docs/02`
§Reflow, instrument finding 1. */
.rates-list {
max-inline-size: none;
overflow-wrap: anywhere;
}
.fine {
font-size: var(--text-xs);
@@ -367,6 +368,19 @@ const PROCESSES = [
.sheet-head {
padding-block-end: 8pt;
}
/* ⚠️ `font-weight` IS FROZEN AT 400, AND FROZEN RATHER THAN CHOSEN. These
elements carried no weight of their own before 2026-08-31 and rendered at
400; taking `.eyebrow` gave them its 500, which changed the PRINTED sheet —
a visual change to the artefact circulated with an appointment proposal,
from a ruling that was about SIZE. `docs/02` §Accessibility floor carries
the byte figures. So print keeps 400
and the committed PDF stays byte-identical; screen takes the 500 every other
eyebrow has. ⚠️ POUYA'S CALL WHETHER TO UNIFY: if he does, this declaration
goes and `npm run bio:pdf` must be re-run and re-committed. */
.block h2,
.sheet-strap {
font-weight: var(--weight-normal);
}
.block h2 {
font-size: 7pt;
padding-block-end: 3pt;
+22 -5
View File
@@ -323,18 +323,22 @@ const hintId = (name: string) => `${name}-hint`;
color: var(--text-secondary);
}
/* The direct-contact block. A `<dl>` because each row is genuinely a
term and its value, which is also what lets the labels stay legible at
small sizes without a heading level. */
/* The direct-contact block. A `<dl>` because each row is genuinely a term and
its value, which is what gives the labels their semantics without spending a
heading level on them. */
.direct {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
gap: var(--space-5);
margin-block-start: var(--space-8);
}
/* `--text-eyebrow`, not `--text-2xs`: these `<dt>`s are label text on the page
that collects inquiries, so they move with the `<label>`s below rather than
sitting a step behind them. Pouya's ruling, 2026-08-31. */
.direct dt {
font-family: var(--font-mono);
font-size: var(--text-2xs);
font-size: var(--text-eyebrow);
font-weight: var(--weight-medium);
letter-spacing: var(--tracking-eyebrow);
text-transform: uppercase;
color: var(--text-meta);
@@ -343,6 +347,10 @@ const hintId = (name: string) => `${name}-hint`;
margin-block-start: var(--space-2);
font-size: var(--text-base);
line-height: var(--leading-snug);
/* The email address has no break opportunity and overflowed at a 200% default
font size. It must stay selectable and correct, so it breaks rather than
being truncated. `docs/02` §Reflow carries the measurement. */
overflow-wrap: anywhere;
}
/* The no-retainer sentence, set larger than the paragraphs under it. On an
@@ -374,13 +382,22 @@ const hintId = (name: string) => `${name}-hint`;
gap: var(--space-2);
}
/* NOT the `.eyebrow` class, deliberately: `--text-secondary` (11.75:1) rather
than `.eyebrow`'s `--text-meta` (5.47:1), because a form label is operative
text. Everything else matches it, `font-weight` included — without that these
rendered at 400 under a `p.eyebrow` of the same size and colour.
`overflow-wrap` because at a 200% default font size "Firm or organisation"
ran 38px outside its own box at 320px: `docs/02` §Reflow, instrument
finding 2. */
label,
legend {
font-family: var(--font-mono);
font-size: var(--text-2xs);
font-size: var(--text-eyebrow);
font-weight: var(--weight-medium);
letter-spacing: var(--tracking-eyebrow);
text-transform: uppercase;
color: var(--text-secondary);
overflow-wrap: anywhere;
}
/* Maroon on cream is 12.29:1, so the asterisk is legible — but it is
`aria-hidden` and paired with a visually-hidden "(required)", because
+7
View File
@@ -687,6 +687,9 @@ const latest = (await getCollection('insights', ({ data }) => !data.draft))
runs four lines; `text-wrap: balance` (global.css) keeps them even. */
font-size: var(--text-6xl);
max-inline-size: 22ch;
/* `anywhere`, not `break-word` — one word here held the whole hero column
open. `docs/02` §Reflow, instrument finding 1. */
overflow-wrap: anywhere;
}
.hero-lede {
max-inline-size: var(--width-prose);
@@ -875,6 +878,10 @@ const latest = (await getCollection('insights', ({ data }) => !data.draft))
flex: 1 1 auto;
max-inline-size: 46ch;
color: var(--text-secondary);
/* `anywhere`, not the `break-word` `global.css` already gives this `<p>` —
one token held this card's grid track open. `docs/02` §Reflow, instrument
finding 1. */
overflow-wrap: anywhere;
}
.feature-arrow {
font-size: var(--text-xl);
+22 -18
View File
@@ -27,23 +27,25 @@
*
* ⚠️ **WHAT THIS PAGE DELIBERATELY DOES NOT CLAIM, AND THE OMISSIONS ARE THE
* POINT.** docs/05 specifies a customer-managed KMS key, point-in-time recovery,
* and DynamoDB TTL. `AGENTS.md` §7 verifies the table's name and region and
* **does not verify any of those three as enabled**. So:
* and DynamoDB TTL. `AGENTS.md` §7 is the register for whether each of the three
* is enabled, and **this comment does not restate what it says** — it did once,
* went stale within the day, and had to be pulled back (§12 R19). So:
*
* - "Encrypted at rest" IS stated, because DynamoDB encrypts every table at
* rest unconditionally — it is true whether or not the customer-managed key
* in docs/05 has been configured.
* - The customer-managed key and point-in-time recovery are NOT mentioned.
* Neither is a fact a reader needs, and neither is verified.
* - **Automatic deletion IS stated, and it is the one promise on this page
* that depends on a control nobody has verified.** The handler writes the
* `ttl` attribute, and ⚠️ **writing the attribute is not the mechanism** —
* TTL must be ENABLED on the table, and **§7 records whether it is.**
* docs/05's definition of done carries "TTL set and verified by test
* record" and `docs/06`'s cutover checklist names this page as what that
* item is protecting. **Both halves — `ENABLED` by command, and a test
* record confirmed deleted — before this page is public.** See the
* TODO(pouya) on the retention section below, and §9 Q60.
* - **Automatic deletion IS stated, and it asserts a MECHANISM rather than only
* a period** — the one promise here whose truth lives entirely outside this
* repository. The handler writes the `ttl` attribute, and ⚠️ **writing the
* attribute is not the mechanism**: TTL must also be enabled on the table,
* which §7 records — **and the setting being on still does not prove a
* record is ever deleted.** Only a record written with a near-future `ttl`
* and watched to vanish proves that. docs/05's definition of done carries
* "TTL set and verified by test record" and `docs/06`'s cutover checklist
* names this page as what that item protects. **Both halves before this page
* is public.** See the TODO(pouya) on the retention section below, and §9 Q60.
*
* ⚠️ **NO LICENSURE CLAIM AND NO ANSWER TO THE CAPACITY QUESTION.** A privacy
* policy is where "legal advice" phrasing arrives by convention. §4 records
@@ -209,13 +211,15 @@ const COLLECTED = INTAKE_FIELDS.map((field) => field.label);
<h2>How long it is kept</h2>
{
/* TODO(pouya): is DynamoDB TTL `ENABLED` on the intake table, and has a
test record been written with a near-future `ttl` and observed to
disappear? AGENTS.md §9 Q60. The sentence below asserts a mechanism,
not just a period — `describe-time-to-live` returned `DISABLED` on
2026-08-31 — and it must not go public until both halves are
confirmed. §7 records the status; do not answer this from the
handler code, which only writes the attribute. */
/* TODO(pouya): has a test record been written to the intake table with a
near-future `ttl` and OBSERVED TO DISAPPEAR? AGENTS.md §9 Q60. The
sentence below asserts a MECHANISM, not just a period, and the
setting being on does not prove the mechanism runs. The table
setting is confirmed — §7 holds that status and this comment does
not restate it, because it did restate it once and went stale within
the day (§12 R19). Do not answer this from the handler code, which
only writes the attribute. This page must not go public until a
deletion has actually been seen. */
}
<p>
<strong>{RETENTION_MONTHS} months from the date you send it</strong>,
+15 -7
View File
@@ -64,15 +64,23 @@
/* Fluid scale, 360px → 1600px viewport. Ratio widens toward the display
end (1.25 → 1.333) so headlines scale harder than body copy.
--text-2xs is the 11px floor for a COMPRESSED label — the header tagline
and the print sheet. It stopped being the eyebrow floor on 2026-08-31.
Added 2026-08-27: SiteHeader wrote `0.6875rem` as a literal, step-1
review finding #7. */
--text-2xs HAS EXACTLY ONE CONSUMER: the header tagline in SiteHeader, held
there for a measured layout reason recorded beside it. It is the smallest
type on the site and nothing else may use it without a measurement — the
three other places that did (the /contact/ labels, the /contact/ dl terms,
the /bio/ sheet) all moved to --text-eyebrow on 2026-08-31 because none of
them had a reason beyond having been written that way. Added 2026-08-27:
SiteHeader wrote `0.6875rem` as a literal, step-1 review finding #7. */
--text-2xs: 0.6875rem; /* 11 — label */
--text-xs: 0.75rem; /* 12 — legal */
/* Not a rung on the ladder above: 13px sits between xs and sm because
uppercase mono at 0.18em reads smaller than it measures. Pouya, 2026-08-31. */
--text-eyebrow: 0.8125rem; /* 13 — eyebrow */
/* THE SAME VALUE AS `--text-sm` BELOW, AND DELIBERATELY NOT AN ALIAS OF IT.
Uppercase mono at 0.18em reads smaller than it measures, so the eyebrow
needs the top of the small range rather than a rung of its own — but it and
body-meta type move for different reasons, and aliasing would mean a future
change to meta text silently moved every eyebrow on the site. Pouya raised
it 12 -> 13 -> 14 px on 2026-08-31; 13 px was still getting lost against the
display type. */
--text-eyebrow: 0.875rem; /* 14 — eyebrow */
--text-sm: 0.875rem; /* 14 — meta */
--text-base: 1rem; /* 16 — body */
--text-lg: clamp(1.0625rem, 0.99rem + 0.32vw, 1.1875rem); /* 17→19 */