feat: build step 3 — /about/, minus its memberships; close Q40–Q43
Build and deploy / build-and-deploy (push) Failing after 6s

Applies Pouya's rulings on Q42, Q41(a)(b)(c), Q43 and Q40, then builds
`/about/` — six of docs/01's seven items.

`/about/` ships WITHOUT a memberships group. R10 is a prohibition on
shipping a page that lists memberships before they are re-confirmed; the
re-confirmation is a fact only Pouya holds and was not obtained. The
first version published all four and disclosed the gap in five places
instead; both review agents rejected that. Q44 carries the question.

Rulings:
- Q42 — ENE, dispute-system design and pre-dispute technical advisory
  rowed; settlement counsel struck as a partisan role. The strike
  exposed a hole in the offering test, which now states the prior
  question it was missing.
- Q41(a) — Q37 reaches prose, and prose is held to a higher bar. The
  sentence is now one constant, ASYMMETRY_LINE, because two hand-typed
  copies had already diverged inside one session.
- Q41(b) — not restored; the implication turned out to be in three
  places, two of which survived the sweep that closed it.
- Q41(c) — verified against the LAT's own Rules and extracted into
  docs/reference/lat-case-conference.md. Rule 2.4 makes "Pre-Hearing
  Conference" the Tribunal's own term for a case conference; the Rules
  contain zero occurrences of `mediat` in 66,593 characters.
- Q43 — the timings are service commitments; PROCESS_FRAMING renders
  adjacent to them, not in a lede above.
- Q40 — bundled to step 7 as R15, blocking cutover.

Four review passes, 43 findings, nine of them defects in their own
predecessors' fixes. The worst was mine: the false universal Q39 struck
reached a public page. Also fixed a portrait ladder that upscaled 1.93x
at 1024/DPR2 on BOTH pages — the shipped home page included — because
its 960 ceiling was derived from the layout range where the image is
narrowest.

Verified: check/lint/build/audit clean; 0 upscaling across 11 device
profiles; 0 overflow and 0 over-wide elements at 13 widths; 0 contrast
failures across 127 and 88 painted pairs; 0 print failures against white
paper; reveal 0 hidden under reduced-motion and print; zero JavaScript.
Lighthouse NOT RUN — tool unavailable until step 7 (R11). HTML validator
NOT RUN.

Opens Q44 (memberships), Q45 (PDF bio), Q46 (offering-test gating; the
glossary standard), Q47 (jobTitle without worksFor). Adds R15.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0148NztQskLKKApP5SzAA78e
This commit is contained in:
Pouya Lajevardi
2026-08-28 12:10:41 -04:00
co-authored by Claude Opus 5
parent 165d259f5c
commit c576b9a85f
17 changed files with 2603 additions and 147 deletions
+97
View File
@@ -0,0 +1,97 @@
---
/**
* The conversion band — maroon ground, one heading, one CTA. `docs/01` puts it
* at the foot of `/` (item 8) and it belongs at the foot of every page that
* wants an appointment enquiry.
*
* EXTRACTED 2026-08-28 ON `adversarial-reviewer`'S FINDING, and the finding was
* not "this is duplicated" but "this is duplicated AND HAS ALREADY DRIFTED."
* `/` and `/about/` carried identical markup and ~20 identical lines of CSS,
* except `.contact-body` — `52ch` on `/`, `46ch` plus a `line-height` on
* `/about/`. Two call sites, one already divergent, seventeen pages to come.
*
* NO PROPS AND NO SLOTS AT ALL, AND THAT IS A CORRECTION MADE ON REVIEW.
* This shipped with `eyebrow?`, `cta?` and a named `heading` slot, all
* defaulted, and **not one of the two call sites overrode any of them** — the
* exact pattern this repo has already deleted twice with the reasons written
* into the source: `Eyebrow.astro` (*"`tag?: 'p' | 'span'` had zero call sites,
* so its `<span>` branch was unreachable code"*) and `SectionHeading` (*"two
* mechanisms for one job… One way in."*). The header even argued against
* `title`/`body` props and then added `eyebrow`/`cta`. Strings are inlined; add
* a prop when a second call site actually needs one.
*
* The empty `Props` guard stays, though, and it is not decoration: without it
* an Astro component's props widen to `any` and `<ContactBand class="x" />`
* compiles clean while matching nothing — the parent-scope defect `CLAUDE.md`
* records four times, and the one `Pill` was caught by. Verified by probe.
*
* THE `<h2>` IS FIXED AT LEVEL 2 rather than taken as a prop. Every page that
* uses this band has an `<h1>` of its own and top-level sections at `<h2>`, so a
* configurable level here is a way to skip a heading level by accident. If a
* page ever needs otherwise, that is a spec question, not a prop.
*
* `CONTACT.responseTime` is rendered from the constant, never typed: §4 records
* it as **a public commitment** that *"must read identically on `/contact/`, in
* the inquirer confirmation email, and in any bio."*
*/
import Button from './Button.astro';
import Eyebrow from './Eyebrow.astro';
import { CONTACT } from '../data/site';
interface Props {
children?: unknown;
}
const _props: Props = Astro.props;
void _props;
---
{
/* NO BOOKING LINK, and that is not an omission: booking is parked
(AGENTS.md R6) and `CONTACT.bookingUrl` is null, so `/contact/` ships the
intake form with a reserved slot for an embed. Stated rather than silently
dropped, on every page that renders this band. */
}
<section class="section section-accent contact-band">
<div class="wrap contact-inner">
<div class="contact-copy">
<Eyebrow dot>Next step</Eyebrow>
<h2 class="display contact-h">Start with a call.</h2>
<p class="contact-body">
Tell me the shape of the matter and who is involved, and I will tell you
whether I am the right neutral for it. {CONTACT.responseTime}
</p>
</div>
<div class="contact-action">
<Button href="/contact/" variant="gold"
>Request a consultation &rarr;</Button
>
</div>
</div>
</section>
<style>
.contact-inner {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: var(--space-6);
}
.contact-h {
margin-block: var(--space-4) var(--space-4);
font-size: var(--text-4xl);
}
.contact-body {
/* 52ch, which is `/`'s value. The two call sites had drifted to 52ch and
46ch; 52 is the one that shipped first and was reviewed. */
max-inline-size: 52ch;
line-height: var(--leading-body);
}
.contact-action {
/* `0 1 auto` + `min-inline-size: 0`, NOT `flex: none`. `none` is `0 0 auto`,
which refuses to shrink below max-content and pushed the band into
overflow at 320px. This lets the button wrap instead. Measured on `/`. */
flex: 0 1 auto;
min-inline-size: 0;
}
</style>
+64 -5
View File
@@ -15,11 +15,53 @@
* :global(), no wrapper div, and no rule that silently does nothing.
*/
/**
* No props. A `class?: string` was declared and never passed, and a parent
* cannot reach this root regardless — see SectionHeading for the measurement
* and CLAUDE.md for the rule. The custom-property hooks below are how an
* ancestor influences it.
* NO PROPS — AND THE EMPTY `Props` INTERFACE IS LOAD-BEARING, NOT DECORATION.
*
* A `class?: string` was declared here once, was never passed, and a parent
* cannot reach this root regardless — see SectionHeading for the measurement and
* CLAUDE.md for the rule. It was deleted, and the deletion was written up as
* "passing one is now a build error". **It was not.** With frontmatter
* containing only comments, an Astro component's props widen to `any`, so
* `<Pill class="chip">` compiled with **zero** errors, matched nothing, and let
* the flex or grid child absorb the difference — silently.
*
* Measured by probe page, `<Eyebrow class>`, `<Pill class>`,
* `<SectionHeading class>`, `<Button bogus>`: `astro check` reported **3 errors
* — Eyebrow, SectionHeading, Button. Nothing for Pill.** Adding the three lines
* below takes the same probe to **4 errors, 0 hints** (and no `ts(6196)`,
* because the interface is referenced by the destructure below).
*
* CLAUDE.md names `Pill` as the next place the parent-scope defect will happen.
* The guard that was documented as protecting it was absent on exactly it.
*
* The custom-property hooks in the style block are how an ancestor influences
* this component: custom properties inherit, which is the one mechanism that
* legitimately crosses the boundary.
*/
/**
* `children` DECLARED, NOTHING ELSE. Getting to this line took two wrong turns
* and both are worth recording, because each looked correct:
*
* - `interface Props {}` — rejected by eslint
* (`@typescript-eslint/no-empty-object-type`), and it would have been the
* wrong tool anyway: `{}` in TypeScript means "any non-nullish value", not
* "no properties".
* - `Record<string, never>` — passes eslint and does reject `class`, but it
* also rejects `children`, so it broke the two REAL call sites
* (`PracticeCard.astro:34` and `/about/`'s arc) while the probe page went
* green on the thing it was testing. A fix that satisfies its own test and
* breaks production is exactly what `/build` Phase 4 warns about.
*
* Slot content arrives as `children`, so `children` is the one permitted
* property and every other prop is an error. Verified by probe: all six
* components now reject `class`, and `<Pill>text</Pill>` compiles. Deleting
* this re-disables checking at every call site.
*/
interface Props {
children?: unknown;
}
const _props: Props = Astro.props;
void _props;
---
<span class="pill"><slot /></span>
@@ -39,6 +81,23 @@
line-height: 1.4;
text-transform: uppercase;
color: var(--pill-fg, var(--text-meta));
white-space: nowrap;
/* `nowrap` UNTIL 2026-08-28, AND IT WAS FINE UNTIL A PILL HAD FOUR WORDS.
`/`'s six pills are one or two words (longest "Cross-cultural").
`/about/` ships `Commenced August 2026`, and at a 200% DEFAULT FONT SIZE
(root 32px — a real browser setting, not page zoom) that pill measured
382.6px wide with its right edge at 430.6 in a 390px viewport:
**41px of document overflow at 390, 111px at 320.** Injecting
`white-space: normal` took 390 to **0** and 320 to **63**, 63 being the
header residual docs/02 already accepts. WCAG 1.4.10 Reflow.
`normal` costs nothing at default size — a pill only wraps when it cannot
fit, which is exactly when wrapping is the right answer.
WHAT IT LOOKS LIKE AT THE EXTREME, recorded so it is not later read as a
new bug: at 320px with root at 32px, `Commenced August 2026` renders
**224 x 119px** inside `border-radius: 999px` — a three-line stadium. It
is ungainly and it is legible, in-viewport, and the alternative was
111px of document overflow. */
white-space: normal;
}
</style>