Files
adr-sml/src/pages/bio.astro
T
Pouya LajevardiandClaude Opus 5 0f7595b602 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
2026-08-31 15:44:13 -04:00

410 lines
15 KiB
Plaintext

---
/**
* `/bio/` — the one-page bio, and the SOURCE of the PDF. Build step 9.
* Discharges `AGENTS.md` R16 / Q45.
*
* ⚠️ **R16 LEFT TWO DECISIONS OPEN AND BOTH ARE TAKEN HERE, UNDER STANDING
* AUTHORISATION. Read them before changing anything.**
*
* **(a) Generated at build, or authored once as a designed artefact?** Neither,
* exactly — and the third option is better than both. The bio is a PAGE in this
* repository, so every line of it is reviewed by the same apparatus that reviews
* every other page: `astro check`, `npm run check:claims` on the built HTML, the
* adversarial review, and the cutover claims pass. The PDF is then RENDERED from
* this page by `npm run bio:pdf`, deterministically, with no new dependency —
* `chrome-launcher` is already a devDependency because Lighthouse needs it.
*
* That answers R16's actual worry, which was never about tooling: *"It is the
* one artefact class this project's review apparatus cannot reach. A web page is
* re-reviewed by every audit; a PDF circulated with an appointment proposal is
* read once, by the reader who matters most, and never seen by a reviewer
* again."* Making the PDF a rendering of a reviewed page puts it back inside the
* apparatus. **It is not generated during `astro build`** — CI has no Chrome, and
* a build step that cannot run in CI is the Q22 shape again.
*
* **(b) Does it carry anything the site does not? NO.** Every line here renders
* from the same constants as the pages: `CREDENTIALS`, `ROLE`, `BOUTIQUE`,
* `PRACTICE_AREAS`, `FEES`, `CONTACT`. R16 flagged that both open sub-decisions
* were "each a §4 question of its own, and the matter list would collide with §4
* Forbidden directly" — so the answer that avoids both is a bio that adds
* nothing. No matter list, no referees, no figure that is not on `/fees/`. The
* fee summary IS here, because R16's own reasoning says an appointment proposal
* needs the rate card as much as the bio, and every figure in it is `/fees/`'s.
*
* `noindex`, and excluded from the sitemap in `astro.config.mjs`: it is a
* condensed duplicate of `/about/` and `/fees/`, and two URLs competing on the
* same content is the one thing `docs/04` is most concerned with.
*
* PRINT LAYOUT. `global.css`'s `@media print` block already hides the header,
* the footer and the skip link, neutralises the inverse grounds, and disables
* the reveal — all of it added because `/about/` is printed by people evaluating
* an appointment. This page adds only what makes it fit ONE sheet, and
* `scripts/bio-pdf.mjs` ASSERTS the page count rather than trusting it.
*/
import BaseLayout from '../layouts/BaseLayout.astro';
import Eyebrow from '../components/Eyebrow.astro';
import { getImage } from 'astro:assets';
import ogDefault from '../assets/og-portrait.jpg';
import { pageGraph } from '../data/schema';
import {
BOUTIQUE,
CONTACT,
CREDENTIALS,
FEES,
PRACTICE_AREAS,
ROLE,
SITE,
} from '../data/site';
const ldImage = await getImage({
src: ogDefault,
format: 'jpeg',
width: 1200,
height: 630,
});
const graph = pageGraph(new URL(ldImage.src, Astro.site).href);
const money = (amount: number) =>
new Intl.NumberFormat('en-CA', {
style: 'currency',
currency: FEES.currency,
maximumFractionDigits: 0,
}).format(amount);
const { halfDay, fullDay } = FEES.mediation;
/* The processes, each with a §4 Offerings row. Arbitration is scoped commercial
because Q39's gate is a legal one; mediation is unscoped because it has no
such gate (Q56). The asymmetry is designed — do not tidy it. */
const PROCESSES = [
'Mediation — sole mediator',
'Commercial arbitration — sole, party-appointed, co-arbitration',
'Med-arb — mediation converting to binding arbitration, agreed in advance',
'Early neutral evaluation — delivered to both parties together',
'Dispute-system design',
'Pre-dispute technical advisory',
];
---
<BaseLayout
title="One-Page Bio · Pouya Lajevardi · Mediator · Toronto"
description="A one-page record for circulation with an appointment proposal: designations, education, memberships, the processes offered, the practice areas, and the rates."
jsonLd={graph}
noindex
>
<section class="section bio-sheet">
<div class="wrap">
{
/* The download sits above the sheet and is `.no-print`, so the printed
copy does not carry a link to itself. */
}
<p class="no-print sheet-note">
This page is the source of the one-page PDF.
<a href="/pouya-lajevardi-bio.pdf" download>Download the PDF</a>, or
print this page.
</p>
<header class="sheet-head">
{
/* THE EYEBROW IS `.no-print`, AND IT IS HERE BECAUSE `og:proof` ASKED
FOR IT. That check compares each card's eyebrow against its page's
first `.eyebrow`, and this page had none — so the card said "Bio"
against nothing. The options were to weaken the check or to give the
page the element every other page has; weakening a check to match an
artefact is how a control stops controlling. On paper the sheet leads
with the name, so the eyebrow prints away. */
}
<div class="no-print">
<Eyebrow dot>Bio</Eyebrow>
</div>
<h1 class="sheet-name">{SITE.name}</h1>
<p class="sheet-desigs">{CREDENTIALS.designations.join(' · ')}</p>
<p class="eyebrow sheet-strap">{SITE.tagline}</p>
</header>
<div class="sheet-grid">
<section class="block block-wide">
<h2 class="eyebrow">The practice</h2>
<p>
{
/* ⚠️ NO LEADING SCOPE. This sentence read "I act as a neutral in
commercial disputes — as a mediator, as an arbitrator in
commercial matters, and in med-arb…", and the leading clause
scoped ALL THREE, mediation included. §4's mediation row is
unscoped deliberately (Q56) and says in terms: "do not scope it
on a page." It is the `/practice/` shape exactly — the two words
never appear in the same element, so no proximity grep reaches
it — and it was found by reading the rendered PDF. The scope
belongs on the arbitration clause alone, where Q39's legal gate
puts it. */
}
I act as a neutral — as a mediator, as an arbitrator in commercial matters,
and in med-arb where the parties want one neutral across both phases.
I read the contract and the technical record underneath it rather than
either side's summary of them.
</p>
<p>
I am {ROLE.title} at {BOUTIQUE}, with {ROLE.litigationLine} across
{' '}{ROLE.litigationAreas.join(', ')} matters. I am also a practising
machine-learning and infrastructure engineer, which is what lets me work
through a technical record at first hand.
</p>
</section>
<section class="block">
<h2 class="eyebrow">Designations</h2>
<ul role="list">
{CREDENTIALS.designations.map((d) => <li>{d}</li>)}
</ul>
<h2 class="eyebrow">Education</h2>
<ul role="list">
{CREDENTIALS.education.map((d) => <li>{d}</li>)}
</ul>
<h2 class="eyebrow">Certifications</h2>
<ul role="list">
{CREDENTIALS.certifications.map((d) => <li>{d}</li>)}
</ul>
</section>
<section class="block">
<h2 class="eyebrow">Memberships</h2>
<ul role="list">
{CREDENTIALS.memberships.map((d) => <li>{d}</li>)}
</ul>
<h2 class="eyebrow">Languages</h2>
<ul role="list">
<li>
{CREDENTIALS.languages.join(' and ')}, without an interpreter
</li>
</ul>
</section>
<section class="block">
<h2 class="eyebrow">Processes</h2>
<ul role="list">
{PROCESSES.map((p) => <li>{p}</li>)}
</ul>
</section>
<section class="block">
<h2 class="eyebrow">Subject matter</h2>
<ul role="list">
{PRACTICE_AREAS.map((area) => <li>{area.name}</li>)}
</ul>
<p class="fine">
Family arbitration under the <em>Family Law Act</em> is not offered.
</p>
</section>
<section class="block block-wide">
<h2 class="eyebrow">Rates</h2>
<ul role="list" class="rates-list">
<li>
Half day, up to {halfDay.hours} hours of session — {
money(halfDay.amount)
}. Fee includes up to {halfDay.prepIncluded} hours of preparation.
</li>
<li>
Full day, up to {fullDay.hours} hours of session — {
money(fullDay.amount)
}. Fee includes up to {fullDay.prepIncluded} hours of preparation.
</li>
<li>
Each party beyond two — {money(FEES.mediation.additionalParty)}.
Overtime beyond the session hours the fee covers —
{' '}{money(FEES.mediation.overtimePerHour)} an hour.
{' '}{FEES.mediation.reservation}
</li>
<li>
Arbitration — {money(FEES.arbitration.perHour)} an hour,
{' '}{money(FEES.arbitration.hearingDay)} a hearing day, or a flat fee
for documents-only and expedited references.
</li>
<li>
{FEES.taxNote} The full card, the cancellation schedule and the terms
are published at {SITE.url}/fees/.
</li>
</ul>
</section>
<section class="block block-wide sheet-contact">
<h2 class="eyebrow">Contact</h2>
<p>
{CONTACT.email} · {CONTACT.phoneFallback} · {CONTACT.location}
<br />
{CONTACT.responseTime} · {SITE.url} · {CONTACT.linkedin}
</p>
</section>
</div>
</div>
</section>
</BaseLayout>
<style>
.sheet-note {
margin-block-end: var(--space-7);
font-size: var(--text-sm);
color: var(--text-meta);
}
.bio-sheet {
padding-block: var(--space-8);
}
.sheet-head {
display: flex;
flex-direction: column;
gap: var(--space-3);
padding-block-end: var(--space-5);
border-block-end: 2px solid var(--rule);
}
.sheet-name {
font-family: var(--font-serif);
font-size: var(--text-4xl);
line-height: var(--leading-tight);
letter-spacing: var(--tracking-tight);
}
.sheet-desigs {
margin-block-start: var(--space-3);
font-family: var(--font-mono);
font-size: var(--text-sm);
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);
}
.sheet-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
gap: var(--space-6);
margin-block-start: var(--space-6);
}
.block-wide {
grid-column: 1 / -1;
}
/* See `.sheet-strap` above: type from `.eyebrow`, only the rule under it here. */
.block h2 {
padding-block-end: var(--space-2);
border-block-end: 1px solid var(--border);
}
.block h2:not(:first-child) {
margin-block-start: var(--space-5);
}
.block ul {
/* `global.css` strips the marker and padding from `ul[role='list']`. */
margin-block-start: var(--space-3);
display: grid;
gap: var(--space-2);
font-size: var(--text-sm);
line-height: var(--leading-snug);
}
.block p {
margin-block-start: var(--space-3);
font-size: var(--text-sm);
line-height: var(--leading-body);
color: var(--text-secondary);
max-inline-size: var(--width-prose);
}
.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);
}
.sheet-contact p {
font-family: var(--font-mono);
font-size: var(--text-xs);
line-height: var(--leading-relaxed);
max-inline-size: none;
}
/* --- One sheet of paper ------------------------------------------------ */
/* `global.css`'s print block already hides the header, footer and skip link,
neutralises the inverse grounds and disables the reveal. This is only what
makes the content FIT, and `scripts/bio-pdf.mjs` asserts the page count
rather than this comment claiming it. */
@media print {
.bio-sheet {
padding-block: 0;
}
.wrap {
max-inline-size: none;
padding-inline: 0;
}
.sheet-grid {
/* Two fixed columns rather than auto-fit: on paper there is no viewport
to fit to, and a print UA resolves `auto-fit` against the sheet width
inconsistently. */
grid-template-columns: 1fr 1fr;
gap: 10pt 18pt;
margin-block-start: 10pt;
}
.sheet-name {
font-size: 22pt;
}
.sheet-desigs {
font-size: 9pt;
}
.sheet-strap {
font-size: 7pt;
}
.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;
}
.block h2:not(:first-child) {
margin-block-start: 9pt;
}
.block ul,
.block p {
margin-block-start: 5pt;
font-size: 8.5pt;
line-height: 1.35;
}
.block ul {
gap: 2pt;
}
.sheet-contact p {
font-size: 8pt;
}
/* A block must not be split across a page break — on a one-sheet document
that would mean a second sheet carrying two lines. */
.block {
break-inside: avoid;
}
}
</style>