Steps 7a through 10 as one authorised run. Nothing deployed (D11).
7a Lighthouse returns as `lighthouse@13.4.1` + `chrome-launcher`, NOT
`@lhci/cli`. AGENTS.md §7's advisory attribution was wrong: the carriers
were @lhci/cli's own `tmp` and @puppeteer/browsers' `extract-zip`, not
Lighthouse, which audits clean. A deliberate deviation from R11's literal
trigger, recorded with what it costs. Local gate; CI has no Chrome.
7b OG card generator (satori + sharp) discharges R15 — 20 typed cards plus
per-article cards; the portrait stays on / and /about/ by Q40. Insights
plumbing: ArticleCard, Prose, the index, the article route, articleGraph,
and /'s section 7. Card copy is constrained structurally because text in a
JPEG cannot be grepped by check:claims: every headline IS its page's <h1>,
enforced by `npm run og:proof`.
7c Five drafted launch articles, draft: true / reviewedByPouya: false. An
independent compliance audit returned 76 findings and 57 unsourced
assertions; all blocking and should-fix applied.
8 /contact/, the intake form, and backend/intake/ (undeployed). Plain HTML
POST to a same-origin /api/intake with a 303 redirect, so the form works
with zero JavaScript. docs/05 records three deliberate deviations.
9 /fees/ on Q59's ruling — overtime runs from the session cap, and the
reservation point ships adjacent to the rate. One-page PDF bio discharges
R16; /bio/ is its source, so the circulated artefact stays inside the
review apparatus.
10 /legal/privacy/ and /legal/terms/, written to the backend as built. Three
of the policy's statements are derived and cannot drift.
Also: /about/'s inverse credentials band (approved at step 6); Q59 closed;
R15 and R16 discharged; and a fix to shipped copy — /practice/energy/ asserted
the absence of a regulation the source extract says must not be asserted.
Review: adversarial-reviewer, two rounds (D20/D19). Round 1 returned 16
findings including two blocking — an invisible ghost button on /fees/ at
1.00:1 that Lighthouse scored 100, and a privacy policy that named one data
processor when there are two. All 16 acted on.
Lighthouse, 22 pages, mobile: performance 99-100, accessibility 100,
best practices 100, SEO 100 on every indexable page, CLS 0.000.
AGENTS.md entry (ah) has the detail, including four of my own verification
commands that were wrong and what each of them nearly caused.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
956 lines
48 KiB
Plaintext
956 lines
48 KiB
Plaintext
---
|
||
/**
|
||
* `/about/` — Biography and credentials. Build step 3 (docs/01 §Build order):
|
||
* "the credential spine everything else references."
|
||
*
|
||
* JOB (docs/01 §`/about/`): "be the page an appointing body or opposing counsel
|
||
* reads before agreeing to an appointment. This page carries the verifiable
|
||
* record."
|
||
*
|
||
* SECTIONS, against docs/01's seven-item outline:
|
||
* 1 Portrait, name, designation line → the hero
|
||
* 2 Narrative biography, 400–600 words → §Background
|
||
* 3 Credentials, structured → §Credentials
|
||
* 4 The credentialing arc → REMOVED 2026-08-29, see below
|
||
* 5 Languages and cross-cultural → §Language
|
||
* 6 Speaking and publications → OMITTED, per the spec itself
|
||
* 7 Person JSON-LD + PDF bio → JSON-LD ships; the PDF does not
|
||
*
|
||
* ⚠️ ITEM 4 IS GONE, AND IT WAS DELETED RATHER THAN REWRITTEN. Pouya, 2026-08-29:
|
||
* *"Two held designations, no journey… An arc invites 'where are you on it';
|
||
* two designations don't."* Q.Arb is held and C.Med-Arb is off the site
|
||
* entirely, so the section had no subject left — three pills reading Held,
|
||
* Held, and nothing. What it used to carry now sits in item 3, which is a list
|
||
* of held things and was always the right shape for it. **Do not restore it
|
||
* from docs/01's seven-item outline**; that item is struck there too.
|
||
*
|
||
* ITEM 6 IS OMITTED ON THE SPEC'S OWN INSTRUCTION, not by oversight: "Omit the
|
||
* section entirely until there is something in it. An empty 'Speaking' heading
|
||
* is worse than no heading." Nothing to list.
|
||
*
|
||
* ITEM 7'S PDF IS NOT SHIPPED, and the omission is stated rather than silent.
|
||
* **Q45 CLOSED 2026-08-28 — deferred by Pouya to build step 9, alongside
|
||
* `/fees/`, and tracked as AGENTS.md §12 R16.** His reasoning: it is a derived
|
||
* artefact, so building it before this page and the fee card are final means
|
||
* building it twice. No such file exists yet, and a link to a missing file on
|
||
* the page an appointing body reads is worse than its absence.
|
||
*
|
||
* It is also not a formatting job: a one-page bio is a credential document
|
||
* circulated DETACHED from the site, where no build and no reviewer ever
|
||
* re-checks it. Two decisions travel with R16 and are NOT settled by the
|
||
* deferral — generated-at-build vs authored once, and whether it carries
|
||
* anything the site does not.
|
||
*
|
||
* This paragraph also comes out at step 9 (R16), which now lists this file.
|
||
* *(It pointed at Q45 after Q45 closed; `adversarial-reviewer` found that
|
||
* `grep -rn R16 src` returned nothing at all.)*
|
||
*
|
||
* THE PARENT/CHILD SCOPE TRAP, because this page uses <SectionHeading> four
|
||
* times. A parent CANNOT style a child component's root element — the rule
|
||
* compiles against the parent's cid and silently never matches. Every heading
|
||
* below is wrapped in a page-owned <div class="section-head">. Not defensive
|
||
* boilerplate: it is the fourth-instance defect CLAUDE.md records, and the
|
||
* components have had their `class` props deleted so passing one is a build
|
||
* error rather than a silent no-op.
|
||
*
|
||
* R10 / Q44 — THE MEMBERSHIPS GROUP SHIPS, AND IT CARRIES NO CURRENCY
|
||
* WARRANTY. Q44 closed 2026-08-28: Pouya re-confirmed all four as current, so
|
||
* R10's prohibition is discharged and the group renders. Two conditions came
|
||
* with that ruling and both are load-bearing on this page:
|
||
*
|
||
* 1. **Nothing here promises the memberships stay current.** The struck
|
||
* sentence ("Memberships are renewed annually and are listed as current")
|
||
* stays struck and nothing replaces it. His words: *"List the memberships;
|
||
* promise nothing about their future state."* The list is a snapshot, and
|
||
* only §4's stamp says when it was taken.
|
||
* 2. He declined renewal-date tracking, so **R10 no longer fires on a date —
|
||
* it fires on an event**: re-confirm before any cutover or major
|
||
* republish. That is why R10 stays live with the group shipped.
|
||
*
|
||
* The page held all three positions in three days — published-with-a-disclosure
|
||
* (rejected by both review agents), withheld-behind-a-TODO (correct while R10
|
||
* was undischarged), now published-with-no-warranty. The middle state was not a
|
||
* detour; it is what "a build that fails on an unanswered question is a correct
|
||
* build" looks like from the inside.
|
||
*/
|
||
import { Picture, getImage } from 'astro:assets';
|
||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||
import ContactBand from '../components/ContactBand.astro';
|
||
import Eyebrow from '../components/Eyebrow.astro';
|
||
import SectionHeading from '../components/SectionHeading.astro';
|
||
import portrait from '../assets/pouya-lajevardi.jpg';
|
||
import ogDefault from '../assets/og-portrait.jpg';
|
||
import { aboutGraph } from '../data/schema';
|
||
import {
|
||
ASYMMETRY_LINE,
|
||
CREDENTIALS,
|
||
DESIGNATIONS_HELD_LINE,
|
||
PORTRAIT,
|
||
PRACTICE_AREAS,
|
||
ROLE,
|
||
SITE,
|
||
} from '../data/site';
|
||
|
||
/**
|
||
* `image` ON THE Person NODE — docs/04 lists it, and this page is where
|
||
* PERSON_ID (`/about/#person`) actually resolves.
|
||
*
|
||
* AN EARLIER VERSION OF THIS PAGE OMITTED IT and argued at length that it could
|
||
* not be supplied: "SEO.astro computes that URL internally and does not expose
|
||
* it." That reasoning talked itself into the wrong answer — the URL does not
|
||
* have to come from SEO.astro. `/` derives it in six lines with `getImage()`,
|
||
* and the same six lines work here. The result was two documents asserting the
|
||
* same `@id` with different property sets, which is worse than either choice
|
||
* made deliberately.
|
||
*
|
||
* EXACTLY the transform SEO.astro applies to the same source (jpeg, 1200x630),
|
||
* so Astro's asset cache returns the same hashed file rather than emitting a
|
||
* second copy for the crawler. JPEG on purpose: link-preview and structured-data
|
||
* consumers are not browsers and several still do not decode WebP, let alone
|
||
* AVIF.
|
||
*
|
||
* The withdrawn reasoning follows, because it is a good example of a comment
|
||
* arguing for a defect.
|
||
*
|
||
* NO `getImage()` CALL FOR THE JSON-LD IMAGE, unlike `/`.
|
||
*
|
||
* `/` generates the 1200x630 jpeg so the Person node can carry an absolute
|
||
* `image` URL, and it works because that page renders the ProfessionalService
|
||
* node too. Here the Person node is the whole graph, and SEO.astro already emits
|
||
* exactly the same transform of exactly the same source as `og:image`. Calling
|
||
* `getImage()` again would return the same cached asset — so this is not about
|
||
* duplicate files, it is about a second place that has to be kept in step with
|
||
* SEO.astro's transform. It is passed the URL by the layout instead.
|
||
*
|
||
* Except it cannot be: `SEO.astro` computes that URL internally and does not
|
||
* expose it. So the node ships WITHOUT `image` on this page and WITH it on `/`,
|
||
* which is a real inconsistency in a field docs/04 lists for the Person node.
|
||
* Both resolve to the same @id, so a crawler joining the two documents gets the
|
||
* image either way — but that is a hope about crawler behaviour, not a fact.
|
||
* Recorded rather than papered over; the fix is for SEO.astro to expose the URL
|
||
* it already computes, which is a component change and not a page change.
|
||
*/
|
||
const ldImage = await getImage({
|
||
src: ogDefault,
|
||
format: 'jpeg',
|
||
width: 1200,
|
||
height: 630,
|
||
});
|
||
const graph = aboutGraph(new URL(ldImage.src, Astro.site).href);
|
||
|
||
/* THE BIOGRAPHY ENUMERATES THE PRACTICE AREAS IN PROSE, which means it is the
|
||
one place on the site that names them without rendering `PRACTICE_AREAS`.
|
||
Card labels ("Insurance, SABS & LAT") do not read as prose, and a joined list
|
||
of them in a paragraph was worse than the duplication. So the count is
|
||
asserted instead: add a seventh area and this throws, which is the prompt to
|
||
rewrite the sentence. Same mechanism as the ground-alternation check in
|
||
`practice/[slug].astro`. */
|
||
/* `: number`, NOT the inferred literal. `PRACTICE_AREAS` is `as const`, so
|
||
`.length` is the literal type `6`; with `AREAS_NAMED_IN_BIO` also literal `6`,
|
||
adding a seventh area makes the comparison `7 !== 6` — non-overlapping
|
||
literals — and `astro check` fails first with `ts(2367) This comparison
|
||
appears to be unintentional`, which reads like lint noise. Both deploy paths
|
||
run `npm run check` BEFORE the build, so the implementer would never see the
|
||
message below, and the message is the whole point of the guard. Widening the
|
||
left side keeps the comparison live at runtime. Measured by
|
||
`adversarial-reviewer` 2026-08-30. */
|
||
const AREAS_NAMED_IN_BIO: number = 6;
|
||
if (PRACTICE_AREAS.length !== AREAS_NAMED_IN_BIO) {
|
||
throw new Error(
|
||
`/about/'s biography names ${AREAS_NAMED_IN_BIO} practice areas in prose, ` +
|
||
`but PRACTICE_AREAS now has ${PRACTICE_AREAS.length}. Rewrite the ` +
|
||
`"I accept appointments in six areas" paragraph, then update this count.`,
|
||
);
|
||
}
|
||
|
||
/**
|
||
* The designation line — docs/01 item 1. Assembled from constants so it cannot
|
||
* drift from §4.
|
||
*
|
||
* BOTH DESIGNATIONS, because a designation line is a list of things held and
|
||
* as of 2026-08-29 Q.Arb is one of them. It was excluded while it was a stage.
|
||
* The issuing bodies repeat across the two entries; that is what the strings in
|
||
* `CREDENTIALS.designations` say, and spelling them once across a joined pair
|
||
* would be this file re-typing §4 rather than rendering it.
|
||
*/
|
||
const designationLine = [
|
||
'Mediator',
|
||
...CREDENTIALS.designations,
|
||
CREDENTIALS.education[0],
|
||
];
|
||
|
||
/**
|
||
* The structured credentials — docs/01 item 3: "designations, education,
|
||
* certifications, memberships. Every line from AGENTS.md §4 Verified."
|
||
*
|
||
* LANGUAGES ARE NOT A GROUP HERE, and that is a heading decision rather than an
|
||
* omission: docs/01 item 5 gives them a section of their own, and having both
|
||
* an h3 "Languages" group and an h2 "Language" section would put the same two
|
||
* facts in the accessibility tree twice under near-identical names. The section
|
||
* wins because it carries the cross-cultural half, which a list cannot.
|
||
*
|
||
* NOT PRESENT, AND EACH IS A §4 DIRECTIVE RATHER THAN A GAP:
|
||
* - The Law Society. Listing it implies licensure, which D13 bars. §4:
|
||
* "Excluded deliberately, not by oversight."
|
||
* - OCNI. Not current (§4), so it is not published. **It is the precedent** —
|
||
* a membership lapsed quietly and the register now reads "not current, do
|
||
* not publish". Nothing told anyone when.
|
||
* - Any licence status, in either direction. §4 records it `[unestablished]`.
|
||
*
|
||
* ⚠️ MEMBERSHIPS RENDER, AND THE PAGE SAYS NOTHING ABOUT THEIR FUTURE STATE.
|
||
* That second clause is the whole ruling, so it is written here beside the
|
||
* array rather than in a change log nobody reads at edit time.
|
||
*
|
||
* **Q44 closed 2026-08-28.** Pouya re-confirmed all four as current — ADRIC,
|
||
* ADRIO, the three OBA sections, and the Canadian Tax Foundation — which
|
||
* discharges R10's prohibition and puts the group back on the page. §4 is
|
||
* re-stamped `[verified 2026-08-28 — Pouya]`.
|
||
*
|
||
* **DO NOT ADD A CURRENCY SENTENCE.** Not "renewed annually", not "current as
|
||
* of", not "listed as current", not a stamp date in the markup. His ruling is
|
||
* explicit: *"List the memberships; promise nothing about their future state.
|
||
* The list is a snapshot, and only §4's stamp says when it was taken."* The
|
||
* struck sentence is quoted in the comment further down this file with the
|
||
* three defects it carried; the second of those is now the live one.
|
||
*
|
||
* **WHY THE ABSENCE OF A WARRANTY IS THE HONEST FORM AND NOT AN EVASION.**
|
||
* Pouya declined renewal-date tracking. Without renewal months there is no date
|
||
* on which anyone could re-check, so any sentence asserting continued currency
|
||
* would be asserting something nothing in this repo can support — which is the
|
||
* OCNI shape exactly: §4 carries OCNI as lapsed and unpublishable, and that was
|
||
* found roughly a year late. R10 therefore fires on an **event** now (before any
|
||
* cutover or major republish), not on a date. That is recorded in §12 R10 with
|
||
* this reasoning attached, because a stamp with no trigger behind it goes stale
|
||
* silently.
|
||
*
|
||
* "Canadian Tax Foundation" ships WITHOUT a "(CTF)" abbreviation, unlike ADRIC
|
||
* and ADRIO. Pouya's ruling wrote "(CTF)" when listing what to confirm, but the
|
||
* abbreviation appears nowhere else on this site — ADRIC and ADRIO earn their
|
||
* parentheses because "Q.Med (ADRIC / ADRIO)" uses them in the Designations
|
||
* group two entries above. An unused abbreviation is noise, so it is omitted;
|
||
* one word to reverse if he wants it.
|
||
*
|
||
* `memberOf` IS EMITTED FROM THIS PAGE'S JSON-LD (Q53), AND FROM A DIFFERENT
|
||
* ARRAY. `src/data/schema.ts` builds it from `MEMBERSHIP_ORGS`, not from the
|
||
* `CREDENTIALS.memberships` this group renders: the graph names the bodies, the
|
||
* page also names the OBA's three sections. **Only their length is enforced, by
|
||
* a compile-time assertion — content parity is manual.** So editing this list
|
||
* means editing that one. They are the same §4 row seen twice.
|
||
*/
|
||
const CREDENTIAL_GROUPS = [
|
||
{ title: 'Designations', items: CREDENTIALS.designations },
|
||
{ title: 'Education', items: CREDENTIALS.education },
|
||
{ title: 'Certifications', items: CREDENTIALS.certifications },
|
||
{ title: 'Memberships', items: CREDENTIALS.memberships },
|
||
];
|
||
---
|
||
|
||
<BaseLayout
|
||
title="About · Pouya Lajevardi · Mediator, Q.Med, Q.Arb · Toronto"
|
||
description="Pouya Lajevardi, JD, Q.Med, Q.Arb — a Toronto mediator who also practises as a machine-learning and infrastructure engineer. Credentials and background."
|
||
ogType="profile"
|
||
imageAlt={PORTRAIT.alt}
|
||
jsonLd={graph}
|
||
>
|
||
{/* ---- 1. Hero: portrait, name, designation line --------------------- */}
|
||
<section class="hero">
|
||
<div class="wrap hero-inner">
|
||
<div class="hero-copy">
|
||
<Eyebrow dot>About</Eyebrow>
|
||
|
||
{
|
||
/* THE H1 IS THE NAME, not a headline, and that is docs/01 item 1
|
||
("Portrait, name, designation line") agreeing with the search intent
|
||
it records for this page (`"Pouya Lajevardi"`, `Pouya Lajevardi
|
||
mediator`). The masthead carries the name as a brand mark; a bio page
|
||
needs it as the document's subject. */
|
||
}
|
||
<h1 class="display hero-h">{SITE.name}</h1>
|
||
|
||
{
|
||
/* EACH SEPARATOR IS INSIDE THE SPAN IT PRECEDES, not a sibling of it.
|
||
As siblings the flex container wrapped between them, leaving an
|
||
orphaned "·" at the end of line 1 at 390px. The NON-BREAKING SPACE
|
||
after the glyph is what keeps it attached — an earlier version used
|
||
`white-space: nowrap` on the whole item instead, which fixed the
|
||
orphan and broke reflow at a 200% default font size. */
|
||
}
|
||
<p class="designation">
|
||
{
|
||
designationLine.map((part, i) => (
|
||
<span class="designation-part">
|
||
{i > 0 && (
|
||
<span class="sep" aria-hidden="true">
|
||
{'·\u00A0'}
|
||
</span>
|
||
)}
|
||
{part}
|
||
</span>
|
||
))
|
||
}
|
||
</p>
|
||
|
||
{
|
||
/* THE ROLE LINE, and it is the single highest-risk sentence on this
|
||
page. Both strings come from `ROLE` in src/data/site.ts rather than
|
||
being typed here, for the reason that file gives: "these are the two
|
||
where the wording IS the compliance."
|
||
|
||
D13: the approved phrasing is "active litigation exposure", NEVER
|
||
"practice" in this context. The boutique is never named (D16). The
|
||
matter types are §4 verbatim and must not be extended without a row.
|
||
|
||
EXPLICITLY INTERIM — AGENTS.md R1, surfaced again 2026-08-28
|
||
precisely because this page is where the framing now does its
|
||
heaviest work.
|
||
|
||
THE MEDIATION HALF CARRIES NO SCOPE, AND THE ASYMMETRY IS
|
||
DELIBERATE — Q56, ruled 2026-08-30. Arbitration is scoped commercial
|
||
because of a legal gate (Q39: family arbitration in Ontario requires
|
||
prescribed training). Mediation has no such gate and §4's row is
|
||
unscoped. Do not restore "commercial" to the mediation half to make
|
||
the two read as a pair. */
|
||
}
|
||
<p class="hero-lede">
|
||
I am {ROLE.title} at {ROLE.at}, with {ROLE.litigationLine} across{' '}
|
||
{ROLE.litigationAreas.slice(0, -1).join(', ')} and{' '}
|
||
{ROLE.litigationAreas.at(-1)}. I mediate. I accept arbitration
|
||
appointments in commercial matters. I also work as a machine-learning
|
||
and infrastructure engineer.
|
||
</p>
|
||
</div>
|
||
|
||
{
|
||
/* `widths` + `sizes` rather than `densities`, because the portrait is
|
||
fluid and a density ladder would size it from one assumed CSS width.
|
||
`width` and `height` are passed ALONGSIDE `widths` — without them Astro
|
||
declares the untouched 1600px master as the <img src> fallback, which
|
||
is the defect `/`'s comment records at 254,626 bytes.
|
||
|
||
⚠️ THIS COMMENT PREVIOUSLY MADE FOUR CLAIMS AND THREE WERE MEASURABLY
|
||
FALSE. Recorded rather than quietly replaced, because the false ones
|
||
were the confident ones.
|
||
|
||
(a) "Same ladder as `/`'s hero and the same reasoning" — true, and
|
||
that was the problem: `/`'s reasoning derived a 960 ceiling from
|
||
the TWO-COLUMN layout, which only engages at 66rem. Below 66rem
|
||
the hero is one column and the portrait is the full content
|
||
width. Measured: 592px at a 640 viewport, 672 at 768, 804 at 900,
|
||
928 at 1024 — needing 1184-1856 device px at DPR 2 against a 960
|
||
ceiling. **1.40x upscale at 768/DPR2, 1.93x at 1024/DPR2**, on
|
||
this page and on `/`. At 768/DPR1 a 760w file exists and is not
|
||
chosen, so part of the loss was purely a wrong `sizes` (52vw
|
||
declared against an ~88vw slot). Fixed two ways: `.hero-portrait`
|
||
is capped at 30rem below 66rem so the widest real slot is 480 CSS
|
||
px, which makes 960 exactly right for DPR 2; and a 1440 rung
|
||
covers DPR 3, which the ≥66rem range had also been missing
|
||
(429px x 3 = 1287 against 960).
|
||
|
||
AND THE 1440 RUNG OVERSHOT, SO 1080 EXISTS TO CORRECT IT. Adding 1440
|
||
for DPR 3 removed a 1.07x upscale at 390/DPR3 and replaced it with a
|
||
**48,799 B fetch where the old one was 21,526 B** — +27 KB on a phone,
|
||
to fix a 7% softness nobody can see. A browser takes the smallest
|
||
candidate at or above what it needs, and with no rung between 960 and
|
||
1440 the only choices were "slightly soft" or "+27 KB". 1080 makes
|
||
1026 (390 x DPR 3) exact and cheap. This was a defect in the fix for
|
||
the defect above, found by measuring the fix rather than the source.
|
||
|
||
AND 1080 ALONE MISSED THE TWO LARGEST CURRENT PHONES, WHICH
|
||
MAKES THIS THE THIRD ITERATION OF THIS LADDER. 1080 was tuned to
|
||
390 CSS px x DPR 3 (= 1026), and `sizes` resolves to
|
||
`calc(100vw - 3rem)` up to 528px, so every phone wider than 390
|
||
overshoots to the next rung: iPhone 14 Plus (428@3, needs 1140)
|
||
and 15/16 Pro Max (430@3, needs 1146) both took **1440 —
|
||
48,799 B**, against 27,594 for the device the rung was tuned
|
||
for. +21,205 B, 13% of page weight. A 1200 rung closes it at
|
||
1.05x. Measured after: no rung more than 1.06x oversized on the
|
||
phone axis, and still no upscaling anywhere.
|
||
(b) "The LCP element on this page is the <h1>" — **false.**
|
||
`PerformanceObserver` at 1280x900: LCP element is
|
||
`IMG.portrait-img`, size 229,679; the <h1> box is 51,484, 4.5x
|
||
smaller. So the portrait IS the LCP element at desktop widths.
|
||
(c) "two words of 96px serif" — **false.** `.hero-h` sets
|
||
`--text-5xl`, which computes to **76px**. 96px is `--text-6xl`,
|
||
which is what `/` uses.
|
||
(d) "above the fold at every width" — **false.** Portrait top vs
|
||
viewport height: 782 vs 568 at 320, 752 vs 640 at 360 — entirely
|
||
below the fold at both, i.e. **0 visible px** at the two widths
|
||
`docs/02` names explicitly. 120px visible at 390x844.
|
||
|
||
SO WHY IS IT STILL `eager` AND NOT `fetchpriority="high"`? Because (b)
|
||
and (d) pull in opposite directions and the split is real. Re-measured
|
||
AFTER the cap, since the cap changes the element's size and therefore
|
||
the LCP candidate (`PerformanceObserver`, cache cleared per sample):
|
||
|
||
390x844 LCP = P.hero-lede 93,411
|
||
768x1024 LCP = IMG.portrait-img 168,161
|
||
1280x900 LCP = IMG.portrait-img 229,679
|
||
|
||
So the portrait is the LCP element from **768px up** — not "~1056px
|
||
up", which is what this comment said before the cap was measured — and
|
||
at phone widths LCP is the hero lede, a font-dependent text paint the
|
||
preloaded Geist already covers. `loading` and `fetchpriority` cannot be
|
||
conditioned on viewport. `eager` serves the tablet-and-desktop LCP;
|
||
`fetchpriority="high"` is withheld because at 320-360, where the image
|
||
is entirely off-screen, it would outrank that text paint. For
|
||
reference, `/` differs at 768 (LCP = H1.display, 135,289) because its
|
||
headline is a four-line sentence rather than a two-word name. */
|
||
}
|
||
<div class="hero-portrait">
|
||
<Picture
|
||
src={portrait}
|
||
width={960}
|
||
height={960}
|
||
widths={[380, 480, 640, 760, 960, 1080, 1200, 1440]}
|
||
sizes="(min-width: 80rem) 429px, (min-width: 66rem) 33vw, (min-width: 33rem) 480px, calc(100vw - 3rem)"
|
||
formats={['avif', 'webp']}
|
||
fallbackFormat="jpeg"
|
||
alt={PORTRAIT.alt}
|
||
loading="eager"
|
||
decoding="sync"
|
||
class="portrait-img"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* ---- 2. Narrative biography ---------------------------------------- */}
|
||
<section class="section bio reveal">
|
||
<div class="wrap">
|
||
<div class="section-head">
|
||
<SectionHeading eyebrow="Background" level={2}>
|
||
<span slot="heading">Two directions, one file.</span>
|
||
</SectionHeading>
|
||
</div>
|
||
|
||
{
|
||
/* 400–600 WORDS, docs/03: "Tell the three tracks as one arc, not three
|
||
lists." Measured, not estimated — see the word-count assertion in the
|
||
verify step of this session's Change Log entry.
|
||
|
||
Q41(a) IS APPLIED THROUGHOUT AND THIS IS THE FIRST PAGE WRITTEN UNDER
|
||
IT. Pouya's ruling, 2026-08-27: the implication test reaches prose, and
|
||
prose is held to a HIGHER bar — "state the asymmetry explicitly rather
|
||
than relying on a parallel construction to carry it." So the fourth
|
||
paragraph names which half is training and which is work, in as many
|
||
words. Avoiding the noun pair "law and engineering" is not sufficient
|
||
on its own: a reader can supply the missing symmetry from silence, and
|
||
for the legal half the missing half is a licence.
|
||
|
||
EVERY CLAIM TRACES TO §4 Verified: the JD, the boutique role, active
|
||
litigation exposure and its four matter types, Q.Med, multiple
|
||
completed sole mediations, arbitration appointments (§4 Offerings,
|
||
scoped to commercial), Q.Arb, engineering practice, SML Company Ltd,
|
||
Farsi, Iranian-Canadian. Nothing here asserts or implies licensure. */
|
||
}
|
||
<div class="prose bio-prose">
|
||
<p>
|
||
I came to dispute resolution from two directions, and I still work in
|
||
both.
|
||
</p>
|
||
<p>
|
||
The first is law. I hold a JD from Bond University, and I am{' '}
|
||
{ROLE.title} at {ROLE.at}. That role gives me {ROLE.litigationLine} — personal
|
||
injury, construction, regulatory matters under the Provincial Offences Act,
|
||
and accident benefits under the SABS. What that exposure is actually worth
|
||
in a mediation is unglamorous: I have seen how these files get built. Which
|
||
productions turn out to be thin. Where expert reports talk past each other
|
||
rather than disagree. Which issues resolve once someone puts the documents
|
||
in order, and which ones never will.
|
||
</p>
|
||
<p>
|
||
The second is engineering. I work as a machine-learning and
|
||
infrastructure engineer. That is current practice, not a former career
|
||
and not an interest: I read code, model documentation, deployment
|
||
topology, and the operational records that show what a system did
|
||
rather than what a specification said it would do.
|
||
</p>
|
||
{
|
||
/* FROM A CONSTANT — ASYMMETRY_LINE in src/data/site.ts. It was typed
|
||
here and separately on `/`, and the two copies had already diverged
|
||
(full stops here, a comma there) inside the session that wrote both.
|
||
Q41(a) makes this the sentence responsible for foreclosing the
|
||
licence implication, so it is the worst string on the site to let
|
||
drift. */
|
||
}
|
||
<p>{ASYMMETRY_LINE}</p>
|
||
{
|
||
/* A SECOND PARAGRAPH HERE WAS CANDOUR ABOUT AN INCOMPLETE CREDENTIAL
|
||
— "I would rather say where I am on the arc". Q.Arb is held; do not
|
||
write another. `check:claims` q-arb-as-a-stage catches the form. */
|
||
}
|
||
<p>
|
||
Mediation is where they meet. {DESIGNATIONS_HELD_LINE}, and I have
|
||
completed multiple sole mediations. I accept arbitration appointments
|
||
in commercial matters — as sole arbitrator, as a party-appointed
|
||
arbitrator, and in co-arbitration. Where a matter turns on a technical
|
||
question, I read the technical material myself.
|
||
</p>
|
||
{
|
||
/* BOTH ADDITIONS TO THIS SENTENCE CAME BACK OUT. §4 verifies exactly
|
||
one relation — *"Operator of SML Company Ltd. alongside the
|
||
practice"* — and that is now all it says.
|
||
|
||
"It is not a law firm and does not hold itself out as one" — a
|
||
negative REGULATORY statement with no row, attached to the one
|
||
§4 row carrying an express caution against being read together
|
||
with the licence row *"into an implication that neither row
|
||
makes."* Added to be helpful; it touches exactly what §4 says
|
||
not to touch.
|
||
"the company through which the engineering work is done" — a
|
||
corporate-structure claim. §4 verifies operation alongside the
|
||
practice, the jurisdiction of incorporation and the place of
|
||
business — not which work runs through which vehicle. */
|
||
}
|
||
{
|
||
/* §4 VERBATIM: "Operator of SML Company Ltd. **alongside the
|
||
practice**." This read "alongside both", where "both" resolved to the
|
||
law track and the engineering track — a concurrency claim §4 does not
|
||
make, flagged by `claims-auditor` 2026-08-28. Two neighbouring
|
||
additions were deleted from this section for the same reason (see
|
||
below), and the wording had propagated from docs/03, now corrected
|
||
too. NOT a corporate-structure claim either — see schema.ts on why
|
||
`worksFor` is withheld. */
|
||
}
|
||
<p>I run SML Company Ltd alongside the practice.</p>
|
||
{
|
||
/* ⚠️ NO SCOPE CLAIM AND NO COVERAGE CLAIM. §4 has no row for the
|
||
subject matter of his ADR work, and `/practice/insurance/` ships
|
||
"Disputes between an insured person and an insurer" — an
|
||
individual-versus-insurer SABS dispute is not commercial, so this
|
||
paragraph must not declare the six areas a commercial class.
|
||
What Q35 grants is the frame used here: an area may be named where
|
||
he can competently accept an appointment, framed as positioning
|
||
rather than claimed history. "I accept appointments in" is that
|
||
frame; "the subject matter runs across" is not, because §Background
|
||
is where a reader defaults to reading history. */
|
||
}
|
||
<p>
|
||
I accept appointments in six areas: construction and infrastructure;
|
||
technology, AI and data; energy and the grid; insurance and accident
|
||
benefits; shareholder and family-business disputes; and cross-border
|
||
matters. What those have in common is a record somebody has to read
|
||
closely, and a dispute that turns on what is actually in it.
|
||
</p>
|
||
<p>
|
||
I have also completed the Kompass Arbitration Certificate Program and
|
||
the Stitt Feld Handy negotiation and ADR workshop sequence. Neither is
|
||
a designation, and I name them precisely for that reason: process
|
||
training is the easiest thing in this field to assert loosely, so it
|
||
is worth stating exactly what it was.
|
||
</p>
|
||
{
|
||
/* THE NEUTRALITY LINE. It is a disclaimer and it earns its place:
|
||
docs/03 requires the equivalent on `/for-parties/`, and this is the
|
||
page an appointing body reads. It also states the negative of the
|
||
implication §4 Forbidden bars — "acts for clients", "represents
|
||
parties" — which is a stronger position than merely never asserting
|
||
it. Q42's reasoning is the same reasoning: Pouya struck settlement
|
||
counsel because a partisan role "undercuts the brand's central
|
||
claim". This sentence is that claim, stated.
|
||
|
||
The family-law exclusion is NOT here. Pouya scoped it to
|
||
`/practice/shareholder/` — "One sentence, not a section" — and
|
||
widening it to this page is his call, not an implementer's. */
|
||
}
|
||
{
|
||
/* THIS SENTENCE HAS NOW BEEN WRONG IN BOTH DIRECTIONS, WHICH IS WHY
|
||
THE THIRD VERSION AVOIDS THE AXIS ALTOGETHER.
|
||
|
||
"I do not give legal advice" — flagged because "do not" describes
|
||
an ELECTION, and an election implies the entitlement to choose.
|
||
"I cannot give legal advice" — flagged on the next pass because
|
||
"cannot" is a DENIAL of entitlement, and §4 on licence status
|
||
is explicit: *"Do not assert it, do not deny it, do not infer
|
||
it from anything else here."*
|
||
|
||
Both readings are correct and they point in opposite directions,
|
||
because both sentences make a claim about CAPACITY. So this one does
|
||
not: it states the ROLE and its consequence for the reader, and it is
|
||
the only form that asserts nothing and denies nothing. The underlying
|
||
question is R1's.
|
||
|
||
AN EARLIER VERSION OF THIS COMMENT CITED `docs/03`'s
|
||
`/for-parties/` directive as the sanctioned example — *"the mediator
|
||
is not your lawyer" — role, not capacity*. That was wrong twice:
|
||
"not your lawyer" PRESUPPOSES lawyer status, which §4 Forbidden bars
|
||
of Pouya and D13 bars by implication; and the half of that sentence
|
||
this comment did not quote was "cannot give you legal advice", which
|
||
is the denial the comment itself says was struck twelve lines above.
|
||
`docs/03` has since been corrected. Do not re-import an example from
|
||
a spec without reading the whole sentence it sits in. */
|
||
}
|
||
<p>
|
||
I act as a neutral. I do not act for a party in a matter I take, and
|
||
each party should have their own legal advice.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* ---- 3b. The one-page PDF ------------------------------------------- */}
|
||
{
|
||
/* ✅ **R16 / Q45 DISCHARGED — build step 9.** `docs/01` §`/about/` item 7 has
|
||
carried a pending note since step 3: the PDF did not exist, and a link to a
|
||
file that does not exist is a broken link on the page an appointing body
|
||
reads. It exists now, it is committed, and this is the link.
|
||
**`/bio/` is the source and the PDF is a rendering of it** — so every line
|
||
of the document circulated with an appointment proposal is on a page that
|
||
`check:claims`, the adversarial review and the cutover claims pass all see.
|
||
That was R16's actual objection: *"a PDF circulated with an appointment
|
||
proposal is read once, by the reader who matters most, and never seen by a
|
||
reviewer again."*
|
||
It carries NOTHING the site does not — R16's second open sub-decision, and
|
||
the answer that avoids the §4 question it flagged. No matter list, no
|
||
referees, no figure that is not on `/fees/`. */
|
||
}
|
||
<section class="section bio-download reveal">
|
||
<div class="wrap">
|
||
<p class="download-line">
|
||
<a href="/pouya-lajevardi-bio.pdf" download>
|
||
Download a one-page PDF of this record
|
||
</a>
|
||
<span class="download-note">
|
||
— designations, education, memberships, the processes offered and the
|
||
rates, on one sheet. The same page is at <a href="/bio/">/bio/</a>.
|
||
</span>
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
{/* ---- 3. Credentials, structured ------------------------------------ */}
|
||
{
|
||
/* ⚠️ `section-inverse`, NOT `section-alt` — approved by Pouya at build step
|
||
6 and applied at step 7b. The arc section struck on 2026-08-29 was this
|
||
page's only dark band, so removing it left `/about/` with four cream
|
||
sections and the accent contact band, and the alternating rhythm
|
||
`docs/02` sets went with it.
|
||
Exactly ONE rule had to change — `.cred-title`. The measured ratios are on
|
||
that rule below, which is where a future editor changing a colour will be
|
||
looking. Everything else inherits cream from `.section-inverse`. */
|
||
}
|
||
<section class="section section-inverse creds reveal">
|
||
<div class="wrap">
|
||
<div class="section-head">
|
||
<SectionHeading eyebrow="Credentials" level={2}>
|
||
<span slot="heading">The verifiable record.</span>
|
||
</SectionHeading>
|
||
</div>
|
||
|
||
<div class="cred-grid">
|
||
{
|
||
CREDENTIAL_GROUPS.map((group) => (
|
||
<div class="cred-group">
|
||
<h3 class="cred-title">{group.title}</h3>
|
||
<ul class="cred-items" role="list">
|
||
{group.items.map((item) => (
|
||
<li>{item}</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
))
|
||
}
|
||
</div>
|
||
|
||
{
|
||
/* THIS NOTE IS GONE, AND IT CARRIED THREE SEPARATE DEFECTS. It read:
|
||
"Memberships are renewed annually and are listed as current. Nothing
|
||
above asserts a licence to practise law, in either direction."
|
||
|
||
(a) "renewed annually" WIDENED §4, which records yearly renewal for
|
||
the OBA sections and the CTF only and says nothing about ADRIC
|
||
or ADRIO. The widened form had already propagated to four places.
|
||
(b) "listed as current" was an affirmative public WARRANTY of
|
||
currency. **THIS IS NOW THE OPERATIVE DEFECT.** Q44 closed and
|
||
the group is back on the page, so the sentence has something to
|
||
warrant again — and it still must not return. Pouya, 2026-08-28:
|
||
*"NO CURRENCY WARRANTY on the page. The sentence you struck
|
||
stays struck, and nothing may replace it."* He declined renewal
|
||
tracking, which is exactly why the warranty cannot be made:
|
||
there is no date behind it. R10 fires on an event instead.
|
||
(c) "Nothing above asserts a licence to practise law, in either
|
||
direction" READS AS A DENIAL. §4 on licence status: "Do not
|
||
assert it, do not deny it, do not infer it from anything else
|
||
here." It was also the only sentence on the site that raised
|
||
licensure at all, on the page where R1 says the D13 framing is
|
||
already doing its heaviest work — and no spec asked for it.
|
||
|
||
Both review agents flagged (c) independently and escalated it to
|
||
Pouya rather than rewriting it. That is the right destination: R1. */
|
||
}
|
||
</div>
|
||
</section>
|
||
|
||
{/* ---- 5. Language and cross-cultural practice ----------------------- */}
|
||
<section class="section language reveal">
|
||
<div class="wrap">
|
||
<div class="section-head">
|
||
<SectionHeading eyebrow="Language" level={2}>
|
||
<span slot="heading">English and Farsi, without an interpreter.</span>
|
||
</SectionHeading>
|
||
</div>
|
||
|
||
{
|
||
/* §4 verifies "Bilingual English and Farsi" and "Iranian-Canadian;
|
||
cross-cultural fluency with diaspora business communities".
|
||
|
||
NO QUANTITY AND NO COMPARATIVE. An earlier draft opened "many of the
|
||
disputes I am best placed to take", which carries a count I do not
|
||
have and the token "best" — which §4 Forbidden bars as a superlative
|
||
and which a forbidden-terms sweep would flag on sight. Rewritten to a
|
||
claim about the work: some disputes are not separable from the
|
||
relationship, and this is what working in the parties' own language
|
||
changes. Nothing about other neutrals — Q41(b). */
|
||
}
|
||
<div class="prose">
|
||
<p>
|
||
I mediate in English and in Farsi. I am Iranian-Canadian, and some
|
||
commercial disputes are not separable from the relationship between
|
||
the parties — family-held companies and diaspora businesses in
|
||
particular, where the commercial disagreement and a much longer
|
||
history arrive together.
|
||
</p>
|
||
<p>
|
||
Working in the parties' own language, with no interpreter in the room,
|
||
changes what gets said and how early it gets said. It removes a layer
|
||
between a party and their own account of events.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* ---- Contact band --------------------------------------------------- */}
|
||
<ContactBand />
|
||
</BaseLayout>
|
||
|
||
<style>
|
||
/* --- 1. Hero -------------------------------------------------------- */
|
||
|
||
.hero {
|
||
padding-block: var(--space-8) var(--space-9);
|
||
}
|
||
.hero-inner {
|
||
display: grid;
|
||
gap: var(--space-7);
|
||
align-items: center;
|
||
}
|
||
.hero-copy {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-5);
|
||
}
|
||
.hero-h {
|
||
/* --text-5xl, not --text-6xl. `/`'s headline is a sentence and needs the
|
||
display ceiling; this is a two-word name, and at 96px it sets 15
|
||
characters across a line that then has nothing to balance against. */
|
||
font-size: var(--text-5xl);
|
||
/* `anywhere`, NOT `break-word`. The type scale is rem-based, so at a 200%
|
||
DEFAULT FONT SIZE this heading computes to 88px and "Lajevardi" — one
|
||
unbreakable 9-character word — is wider than the 224px content box at a
|
||
320px viewport. `break-word` permits a break at layout time but does NOT
|
||
reduce min-content size, so it would not have helped; `anywhere` does.
|
||
It has no effect at any normal size: a word only breaks when it cannot
|
||
fit. Breaking a name mid-word is ugly and it is better than a reader at
|
||
200% zoom losing the page. WCAG 1.4.4 / 1.4.10. */
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.designation {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
/* THE GAP CARRIES THE SEPARATOR'S SPACING, not a margin on .sep. The
|
||
separator is aria-hidden, so its box must not be what a sighted reader
|
||
depends on for rhythm while a screen-reader user gets nothing — with
|
||
`gap` the spacing survives the element being ignored. */
|
||
gap: var(--space-1) var(--space-3);
|
||
align-items: baseline;
|
||
font-family: var(--font-mono);
|
||
font-size: var(--text-sm);
|
||
letter-spacing: var(--tracking-tight);
|
||
color: var(--text-secondary);
|
||
}
|
||
/* NO `white-space: nowrap` — IT WAS HERE AND IT BROKE REFLOW. Gluing the whole
|
||
item together made "Q.Med (ADRIC / ADRIO)" unbreakable, and at a 200%
|
||
DEFAULT FONT SIZE that pushed this page to 108px of overflow at 320px
|
||
against `/`'s accepted 63. The separator does not need the whole item to be
|
||
unbreakable — it only needs to stay attached to the FIRST word, which the
|
||
non-breaking space in the markup does. WCAG 1.4.10. */
|
||
.designation-part {
|
||
display: inline;
|
||
}
|
||
.designation .sep {
|
||
/* NOT `var(--rule)`. Gold `#c9a876` on cream measures **2.10:1** — the one
|
||
hard constraint in `docs/02` and `tokens.css`, which say in terms that
|
||
gold is never a text colour on cream. An in-browser audit of all 100
|
||
text-bearing elements on this page returned exactly two failures and both
|
||
were this span. `aria-hidden` does not dispose of it: these separators are
|
||
the only thing dividing three credential items, and at 2.10:1 they are
|
||
invisible, so the line reads as a run-on. That is a legibility failure
|
||
before it is a rule breach. `--text-meta` measures 5.47:1 on cream and
|
||
still recedes from the 11.75:1 text beside it. */
|
||
color: var(--text-meta);
|
||
}
|
||
.hero-lede {
|
||
max-inline-size: var(--width-prose);
|
||
font-size: var(--text-lg);
|
||
line-height: var(--leading-relaxed);
|
||
color: var(--text-secondary);
|
||
}
|
||
/* `<Picture>` emits an <img> wrapped in a <picture>, and the <picture> is the
|
||
box the grid sizes — the `class` prop lands on the <img>, which is the
|
||
defect InfinityMark.astro records for its own flex sizing.
|
||
|
||
THIS COMMENT CLAIMED "the <picture> WRAPPER carries no cid, so it needs
|
||
`:global()`". **That is false.** Read from the built HTML: the emitted markup
|
||
is `<picture data-astro-cid-ta2fbyqs="true">` — Astro DOES propagate the
|
||
page's scope attribute to both elements for `astro:assets` components (it
|
||
does not for ordinary user components, which is the real rule CLAUDE.md
|
||
records). Decisive corroboration in the shipped CSS: InfinityMark's BARE
|
||
`picture` selector compiles to `picture[data-astro-cid-usztftas]` and
|
||
demonstrably works. So `:global()` was unnecessary and merely broader than
|
||
intended — it would also have matched a <picture> inside any child
|
||
component placed here. Plain selector, correct comment.
|
||
|
||
4/5 rather than 1/1 — the master is square, and 4/5 is the crop `/` uses.
|
||
One portrait treatment across the site rather than two. */
|
||
.hero-portrait {
|
||
aspect-ratio: 4 / 5;
|
||
overflow: hidden;
|
||
border-radius: var(--radius-lg);
|
||
background: var(--bg-raised);
|
||
/* THE CAP IS THE UPSCALE FIX, not a style preference — see the <Picture>
|
||
comment. Uncapped, this slot ran to 928 CSS px at a 1024px viewport, which
|
||
is 1856 device px at DPR 2 against a 960w ceiling. `margin-inline: auto`
|
||
because a 480px box in a 928px column would otherwise sit hard against
|
||
the inline start. */
|
||
max-inline-size: 30rem;
|
||
margin-inline: auto;
|
||
}
|
||
.hero-portrait picture {
|
||
display: block;
|
||
block-size: 100%;
|
||
}
|
||
.portrait-img {
|
||
inline-size: 100%;
|
||
block-size: 100%;
|
||
object-fit: cover;
|
||
/* Above centre: the head sits in the upper half of a square crop. */
|
||
object-position: 50% 22%;
|
||
}
|
||
|
||
@media (min-width: 66rem) {
|
||
.hero-portrait {
|
||
/* Released above 66rem: the grid track is already 390-476px, so the cap
|
||
is inert — and leaving it in place would silently become the constraint
|
||
if the track ever widened. The track governs here, not this number. */
|
||
max-inline-size: none;
|
||
}
|
||
.hero-inner {
|
||
/* 1fr / 0.62fr — the portrait is smaller than `/`'s 0.72 because this
|
||
hero's copy block is a name plus two short lines and the picture would
|
||
otherwise dominate a page whose subject is the text. */
|
||
grid-template-columns: 1fr 0.62fr;
|
||
gap: var(--space-8);
|
||
}
|
||
}
|
||
|
||
/* --- 2. Background --------------------------------------------------- */
|
||
|
||
/* NO `.section-head` RULE — it moved to `global.css`, where `.prose` lives.
|
||
It was byte-identical in both pages, and every one of the seventeen
|
||
remaining pages needs it for the same reason (a parent cannot style a child
|
||
component's root, so the wrapper must be page-owned). */
|
||
/* NO `> p + p` RULE HERE. It was the only paragraph-spacing rule in this
|
||
file and it is now redundant: global.css owns `.prose` paragraph spacing as
|
||
of 2026-08-28, which is what stopped §Language below from rendering its two
|
||
paragraphs as one block. Two rules setting the same property to the same
|
||
value is one rule that will eventually disagree. */
|
||
.bio-prose {
|
||
font-size: var(--text-lg);
|
||
line-height: var(--leading-relaxed);
|
||
}
|
||
/* NO `.bio-prose i` RULE. It styled a statute-name italic that the bio no
|
||
longer uses: `Geist` ships NO italic face (every Geist `@font-face` is
|
||
`font-style: normal`), so the `<i>` rendered as SYNTHETIC OBLIQUE, and the
|
||
serif italic that would have set it properly is no longer preloaded on this
|
||
page. "Provincial Offences Act" is set in roman. If a statute name ever
|
||
needs italics here, load a face for it first. */
|
||
|
||
/* --- 3b. The one-page PDF ------------------------------------------- */
|
||
|
||
/* A quiet band between the bio and the credentials, not a call to action: the
|
||
reader an appointing body sends here is looking for the record, and a
|
||
download button styled like the contact CTA would compete with it. */
|
||
.bio-download {
|
||
padding-block: var(--space-7);
|
||
border-block: 1px solid var(--border);
|
||
}
|
||
.download-line {
|
||
max-inline-size: var(--width-prose);
|
||
font-size: var(--text-base);
|
||
line-height: var(--leading-body);
|
||
}
|
||
.download-note {
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
/* --- 3. Credentials -------------------------------------------------- */
|
||
|
||
.cred-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
|
||
gap: var(--space-7) var(--space-6);
|
||
}
|
||
/* THESE ARE HEADINGS AND THEY WERE DRESSED AS EYEBROWS. The previous rule set
|
||
11px / Geist Mono / 0.18em / uppercase / --text-meta, which is `docs/02`'s
|
||
eyebrow specification exactly — and `docs/02` says in terms: "An eyebrow is
|
||
not a heading and never carries the <h*>." `Eyebrow.astro` restates it.
|
||
|
||
The measurable consequence was worse than the rule breach: at 11px these
|
||
<h3>s were SMALLER than the 12px eyebrow above them and 5px smaller than
|
||
the 16px list items they head, so "MEMBERSHIPS" was the least legible text
|
||
on the page an appointing body reads.
|
||
|
||
Resolved by keeping the <h3> — these genuinely are the headings for their
|
||
lists, and the heading navigation is worth more than the styling — and
|
||
dropping the eyebrow treatment. The gold rule stays: gold is sanctioned for
|
||
dividers, and a 1px border is not text. */
|
||
.cred-title {
|
||
font-size: var(--text-base);
|
||
font-weight: var(--weight-medium);
|
||
letter-spacing: var(--tracking-tight);
|
||
/* THE ONE COLOUR THAT HAD TO MOVE WITH THE BAND. This was
|
||
`--text-secondary`, which is `--ink-soft` — **1.43:1** on the ink ground
|
||
this section now has, i.e. worse than the gold-on-cream 2.10:1 this
|
||
project treats as the defect that must never ship. `--text-inverse-2` is
|
||
gold-l: 11.09:1 on ink (docs/02). The list items below inherit cream from
|
||
`.section-inverse` at 16.81:1 and are untouched.
|
||
The gold border is a 1px divider, which tokens.css sanctions gold for on
|
||
any ground, and gold on ink measures 8.00:1 regardless. */
|
||
color: var(--text-inverse-2);
|
||
padding-block-end: var(--space-3);
|
||
border-block-end: 1px solid var(--rule);
|
||
}
|
||
.cred-items {
|
||
margin: var(--space-4) 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-3);
|
||
font-size: var(--text-base);
|
||
line-height: var(--leading-snug);
|
||
}
|
||
/* NO `.cred-note` RULE. The note it styled was removed — three defects in one
|
||
sentence, recorded in the markup above — and the rule went with it rather
|
||
than shipping as dead CSS to every visitor. Noted so the next reader does
|
||
not re-add a note to fill a rule that no longer exists. */
|
||
|
||
/* NO CONTACT-BAND RULES HERE. `.contact-inner`, `.contact-h`, `.contact-body`
|
||
and `.contact-action` moved to `ContactBand.astro` with the markup they
|
||
style. They were left behind after the extraction — dead CSS shipping to
|
||
every visitor, and worse, the **46ch / 52ch divergence the extraction
|
||
existed to end was still sitting on disk in both pages**, so the next reader
|
||
would have found two different values and no rendered difference. Deleted
|
||
2026-08-28. */
|
||
</style>
|