feat: build step 3 — /about/, minus its memberships; close Q40–Q43
Build and deploy / build-and-deploy (push) Failing after 6s
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:
co-authored by
Claude Opus 5
parent
165d259f5c
commit
c576b9a85f
@@ -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 →</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>
|
||||
@@ -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>
|
||||
|
||||
@@ -139,3 +139,47 @@ export function homeGraph(imageUrl?: string) {
|
||||
'@graph': [professionalServiceNode(imageUrl), personNode(imageUrl)],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* `/about/`'s graph — build step 3. This is where PERSON_ID actually resolves:
|
||||
* every other page references `/about/#person`, and until now nothing served it
|
||||
* from that URL.
|
||||
*
|
||||
* ONE NODE, AND THREE ADDITIONS WERE CONSIDERED AND DECLINED. Each is a
|
||||
* decision rather than an omission, recorded so the next reader does not
|
||||
* "complete" it:
|
||||
*
|
||||
* 1. `ProfilePage` as a wrapper, with `mainEntity` → Person. Accurate, and
|
||||
* Google documents it. Declined: docs/04's structured-data table lists
|
||||
* `Person` for this page and does not list `ProfilePage`, and a type not in
|
||||
* the spec is a deviation that needs a reason. The marginal gain is not
|
||||
* one. Revisit in docs/04, not here.
|
||||
* 2. `BreadcrumbList`. docs/04 requires it on "all nested pages" and says it
|
||||
* must MATCH VISIBLE BREADCRUMBS. `/about/` is one hop from the root, has
|
||||
* no visible breadcrumb, and the header nav marks it as current — so
|
||||
* emitting one would assert a navigation structure the page does not show.
|
||||
* Breadcrumbs begin at the two-level pages: `/practice/<area>/`, `/insights/<slug>/`.
|
||||
* 3. `memberOf` for the four memberships. Declined on R10 / **Q44** — and
|
||||
* the visible page reached the same answer one round later, which is worth
|
||||
* recording: `/about/` now publishes **no memberships group at all**,
|
||||
* because R10 is a prohibition on shipping such a page and the
|
||||
* re-confirmation was not obtained. So this field is not a stricter
|
||||
* standard than the page; it is the same one.
|
||||
*
|
||||
* Two earlier versions of this comment were wrong on the facts. They said
|
||||
* "the page publishes them visibly" (it does not, as of 2026-08-28) and
|
||||
* "all four renew yearly" — §4 records yearly renewal for **the OBA
|
||||
* sections and the CTF only** and says nothing about ADRIC or ADRIO. The
|
||||
* widened form had propagated to four files.
|
||||
*
|
||||
* The reason a machine-readable membership claim is worse than a visible
|
||||
* one stands regardless: a list on a page is corrected by editing the page,
|
||||
* while a scraped claim is cached and re-served by systems that never
|
||||
* re-read it. OCNI lapsed quietly once already. Add this when Q44 closes.
|
||||
*/
|
||||
export function aboutGraph(imageUrl?: string) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@graph': [personNode(imageUrl)],
|
||||
};
|
||||
}
|
||||
|
||||
+124
-19
@@ -56,12 +56,31 @@ export const CREDENTIALS = {
|
||||
'Stitt Feld Handy — negotiation and ADR workshop series',
|
||||
],
|
||||
languages: ['English', 'Farsi'],
|
||||
/** [verified 2026-08-26 — Pouya, AGENTS.md Q28 and the CTF addition of the
|
||||
* same date] — and FOR NOW. Both the OBA sections and the Canadian Tax
|
||||
* Foundation renew yearly, so every line below is a fact with a shelf life
|
||||
* (AGENTS.md R10). Re-confirm at renewal, and before any page that lists
|
||||
* memberships ships. NOT OCNI (lapsed) and NOT the Law Society — listing the
|
||||
* LSO implies licensure, which D13 bars. Do not add either. */
|
||||
/**
|
||||
* [verified 2026-08-26 — Pouya, AGENTS.md Q28 and the CTF addition of the same
|
||||
* date] — and FOR NOW.
|
||||
*
|
||||
* WHAT §4 ACTUALLY SAYS ABOUT RENEWAL, because a widened version of it reached
|
||||
* a public page. §4: *"Both the OBA sections and the CTF renew yearly."* It
|
||||
* says **nothing** about ADRIC's or ADRIO's renewal period. An earlier form of
|
||||
* this comment read "Both the OBA sections and the Canadian Tax Foundation
|
||||
* renew yearly, so every line below is a fact with a shelf life", which is two
|
||||
* claims joined by a "so" that does not follow — and the widened form
|
||||
* ("all four renew annually") then propagated into `schema.ts`, into
|
||||
* `/about/`, and into §9 Q44. Exactly the SES-DKIM duplication shape: the copy
|
||||
* that goes stale is the one nobody re-reads, and this copy became public copy.
|
||||
*
|
||||
* **NOT PUBLISHED AS OF 2026-08-28 — R10 / Q44.** R10 requires a
|
||||
* re-confirmation *"before any page listing memberships ships"*, `/about/` is
|
||||
* that page, and the re-confirmation is a fact only Pouya holds. It was not
|
||||
* obtained, so `/about/` ships its Credentials section WITHOUT a memberships
|
||||
* group and carries a `TODO(pouya)`. Do not render this array on a public page
|
||||
* until Q44 closes.
|
||||
*
|
||||
* NOT OCNI (lapsed — §4: "not current, do not publish") and NOT the Law
|
||||
* Society: listing the LSO implies licensure, which D13 bars. Do not add
|
||||
* either.
|
||||
*/
|
||||
memberships: [
|
||||
'ADR Institute of Canada (ADRIC)',
|
||||
'ADR Institute of Ontario (ADRIO)',
|
||||
@@ -114,6 +133,33 @@ export const ROLE = {
|
||||
],
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* THE Q41(a) SENTENCE. It lives here for the reason `ROLE` above lives here:
|
||||
* *"these are the two where the wording IS the compliance."*
|
||||
*
|
||||
* It was hand-typed into `/` and then into `/about/`, and **the two copies had
|
||||
* already diverged** — `/` used a comma ("one side, a working engineering
|
||||
* practice on the other"), `/about/` used full stops — within the same session
|
||||
* that wrote both. This is the sentence Q41(a) makes responsible for making the
|
||||
* licence implication *"impossible rather than merely absent"*, so a silent
|
||||
* divergence in it is the highest-consequence drift on the site.
|
||||
*
|
||||
* Pouya's ruling, 2026-08-27, kept because it is the finding rather than the fix:
|
||||
*
|
||||
* "The implication test applies everywhere, not just to labels. Prose has more
|
||||
* room, so it is easier to satisfy: state the asymmetry explicitly rather than
|
||||
* relying on a parallel construction to carry it."
|
||||
*
|
||||
* Deleting the parallel is only half of it — a reader supplies the missing
|
||||
* symmetry from silence, and for the legal half the missing half is a licence.
|
||||
* Naming that half **training** is what forecloses it. Do not tidy this into a
|
||||
* parallel, do not shorten it to fit a layout, and do not retype it into a page.
|
||||
*/
|
||||
export const ASYMMETRY_LINE =
|
||||
'The two halves are not the same kind of thing, and the asymmetry is the ' +
|
||||
'honest part. A law degree on one side. A working engineering practice on ' +
|
||||
'the other. One is training I hold. The other is work I still do.';
|
||||
|
||||
/** The three credential slots. Never matter counts — AGENTS.md §4. */
|
||||
export const CREDENTIAL_ROW = [
|
||||
{ value: 'Q.Med', label: 'ADRIC / ADRIO designation' },
|
||||
@@ -233,7 +279,15 @@ export const FEES = {
|
||||
documentsOnlyComplex: 9500, // flat
|
||||
// No tribunal-secretary rate — removed by Pouya 2026-08-26.
|
||||
},
|
||||
// ENE, settlement counsel, dispute-system design, pre-dispute technical advisory
|
||||
/**
|
||||
* THREE services at this rate, not four. Q42 CLOSED 2026-08-27 by Pouya:
|
||||
* early neutral evaluation, dispute-system design, and pre-dispute technical
|
||||
* advisory each gained a §4 Offerings row; **settlement counsel was REMOVED**
|
||||
* — his words: *"Settlement counsel acts FOR a party in negotiation. That is
|
||||
* a partisan role, and putting it on a site that (a) sells neutrality and
|
||||
* (b) asserts no licensure under D13 is wrong twice over."* Do not restore
|
||||
* it, and do not price it.
|
||||
*/
|
||||
hourly: 500, // [verified 2026-08-26]
|
||||
cancellation: [
|
||||
{ window: 'More than 30 days before', fee: 'No fee. Disbursements only.' },
|
||||
@@ -313,19 +367,38 @@ export const PRACTICE_AREAS = [
|
||||
name: 'Insurance, SABS & LAT',
|
||||
chip: 'Insurance',
|
||||
/**
|
||||
* "LAT pre-hearing mediation" was flagged by `claims-auditor` 2026-08-27:
|
||||
* `docs/01` §`/practice/insurance/` lists that phrase as a SEARCH INTENT,
|
||||
* not as an offering, and as a service blurb it reads as conducting
|
||||
* mediation inside the LAT's own statutory process — which is not what a
|
||||
* privately retained neutral is appointed to do.
|
||||
* Q41(c) CLOSED 2026-08-27 — and the verification changed the wording again.
|
||||
*
|
||||
* Reworded to the offering that is actually rowed in §4 (mediation) applied
|
||||
* to the subject matter that is actually verified (insurance / SABS).
|
||||
* Q41 asks Pouya to confirm the intent before the page ships at step 5.
|
||||
* `LAT pre-hearing mediation` (a SEARCH INTENT in `docs/01`, never an
|
||||
* offering) must never be published. Pouya's ruling: *"imprecise and must
|
||||
* not imply appointment by the tribunal. Verify against LAT's own materials
|
||||
* how its case-conference process is conducted and who conducts it."*
|
||||
*
|
||||
* Verified 2026-08-28 against the LAT Rules and the LAT-AABS process page,
|
||||
* both extracted into `docs/reference/lat-case-conference.md`:
|
||||
*
|
||||
* - Rule 2.4: *"'Case Conference' has the same meaning as 'Pre-Hearing
|
||||
* Conference' as defined in the SPPA."* **"Pre-hearing" is the
|
||||
* Tribunal's own label**, and what it names is a case conference.
|
||||
* - Rule 14.3: a **Member** presides, and is then disqualified from the
|
||||
* hearing panel. Rule 14.6: parties must attend. The neutral is the
|
||||
* Tribunal's, and a privately retained one cannot be appointed to it.
|
||||
* - The Rules contain **zero** occurrences of `mediat` or `arbitrat`
|
||||
* (0 in 66,593 characters). The concept is not in them.
|
||||
*
|
||||
* The interim read "private mediation of matters before the LAT", which is
|
||||
* ambiguous in the one word that matters: `before` reads as *pending at* as
|
||||
* easily as *prior to*. Replaced with the temporal frame the Tribunal's own
|
||||
* page endorses — *"you may want to consider negotiation or mediation
|
||||
* services... before filing at the LAT-AABS, and continuing... after a
|
||||
* claim has been filed."*
|
||||
*
|
||||
* `/practice/insurance/` at step 5 must say the mediation is PRIVATE and is
|
||||
* not the Tribunal's case conference.
|
||||
*/
|
||||
blurb:
|
||||
'Accident benefits and SABS entitlement, MIG disputes, and private ' +
|
||||
'mediation of matters before the LAT.',
|
||||
'mediation alongside a LAT application, before filing or after.',
|
||||
},
|
||||
{
|
||||
slug: 'shareholder',
|
||||
@@ -392,9 +465,25 @@ export type _SlugCoverage = _AssertNever<_SlugsWithoutAnArea>;
|
||||
*
|
||||
* TIMINGS ARE `docs/01` §`/process/`'s, verbatim: "confidential intake (day 0) ·
|
||||
* engagement and framing (1–7) · pre-session exchange (7–21) · the session
|
||||
* (21–30) · binding conclusion (30+)". `docs/03` §Process requires them REAL
|
||||
* rather than illustrative, so they are not softened to "typically" — and they
|
||||
* are not invented either. **They have no §4 row; that is Q43.**
|
||||
* (21–30) · binding conclusion (30+)".
|
||||
*
|
||||
* **Q43 CLOSED 2026-08-27, and the ruling went the other way from this
|
||||
* comment's previous reasoning.** It read: *"`docs/03` §Process requires them
|
||||
* REAL rather than illustrative, so they are not softened to 'typically'."*
|
||||
* Pouya ruled that the five timings are **service commitments, the same class
|
||||
* as Q27's response time** — not facts about him, so they need framing rather
|
||||
* than a Verified row:
|
||||
*
|
||||
* "Present them as the TYPICAL shape of an engagement, explicitly not a
|
||||
* guarantee: mediation timing depends on party and counsel availability,
|
||||
* which he does not control. Published as typical, they are honest and
|
||||
* useful; published as commitments, the first matter that slips makes the
|
||||
* page false."
|
||||
*
|
||||
* The NUMBERS ARE UNCHANGED — softening was never the fix, and inventing them
|
||||
* was never on. What changed is that they now ship with `PROCESS_FRAMING`
|
||||
* below, which is **not optional**: any page rendering these steps renders it
|
||||
* too. `docs/03` §Process has been amended to record the override.
|
||||
*
|
||||
* Step 5 is labelled from the spec but its body says what actually concludes —
|
||||
* minutes of settlement in a mediation, an award where the process is arbitral.
|
||||
@@ -435,6 +524,22 @@ export const PROCESS = [
|
||||
},
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* THE FRAMING THAT MAKES THE TIMINGS PUBLISHABLE — Q43, Pouya 2026-08-27.
|
||||
*
|
||||
* Not decoration and not a disclaimer bolted on: it is the condition on which
|
||||
* the five numbers above may appear at all. *"Published as typical, they are
|
||||
* honest and useful; published as commitments, the first matter that slips
|
||||
* makes the page false."*
|
||||
*
|
||||
* RENDER IT ADJACENT TO THE STEPS, on every page that renders them — `/` now,
|
||||
* `/process/` at step 6. A reader who sees `Days 21–30` and not this sentence
|
||||
* has read a commitment. First person, per `docs/03` §Voice.
|
||||
*/
|
||||
export const PROCESS_FRAMING =
|
||||
'This is the typical shape of an engagement, not a commitment. Timing ' +
|
||||
'depends on party and counsel availability, which I do not control.';
|
||||
|
||||
/** Seven items is the ceiling before a nav stops being scannable. */
|
||||
export const PRIMARY_NAV = [
|
||||
{ href: '/about/', label: 'About' },
|
||||
|
||||
@@ -13,17 +13,44 @@ import SiteHeader from '../components/SiteHeader.astro';
|
||||
import SiteFooter from '../components/SiteFooter.astro';
|
||||
import { SITE } from '../data/site';
|
||||
|
||||
export type Props = SeoProps;
|
||||
/**
|
||||
* `preloadSerifItalic` — OPT-IN, AND IT IS A PER-PAGE DECISION SITTING IN A
|
||||
* SHARED LAYOUT, WHICH IS WHY IT NEEDED A PROP.
|
||||
*
|
||||
* The italic face is preloaded because `/`'s `<h1>` contains
|
||||
* `<em class="it">the room</em>` and a serif-to-fallback swap inside a 96px
|
||||
* headline moves the whole last line. That reasoning is `/`-specific and the
|
||||
* preload was unconditional.
|
||||
*
|
||||
* Measured on `/about/` (resource timing, cache disabled, 390x844 DPR 2):
|
||||
* `instrument-serif-latin-400-italic.woff2` fetched as a `link` at **22,428 B**,
|
||||
* while an enumeration of every element's computed `fontFamily|fontStyle` on
|
||||
* that page returns `Instrument Serif|normal`, `Geist|normal`,
|
||||
* `Geist Mono|normal`, `Geist|italic` — **no `Instrument Serif|italic`.** Total
|
||||
* page transfer is ~162 KB, so it was ~14% of the page, preloaded ahead of the
|
||||
* faces that actually render.
|
||||
*
|
||||
* OPT-IN RATHER THAN OPT-OUT: forgetting to opt in costs one line of reflow on a
|
||||
* page that has an italic headline; forgetting to opt out costs 22 KB on the
|
||||
* critical path of a page that does not. Fifteen pages remain, so the default
|
||||
* is the one whose failure is cosmetic. docs/02 allows one italic phrase per
|
||||
* headline, so set it wherever a headline uses `.it`.
|
||||
*/
|
||||
export type Props = SeoProps & { preloadSerifItalic?: boolean };
|
||||
|
||||
// SITE.locale is `en_CA` — Open Graph's underscore form. The lang attribute
|
||||
// takes the BCP 47 hyphen form. One source, two spellings, no second constant.
|
||||
const lang = SITE.locale.replace('_', '-');
|
||||
|
||||
// Destructured OFF the props before the spread below, so it does not reach
|
||||
// <SEO>, which would reject it.
|
||||
const { preloadSerifItalic = false, ...seo } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang={lang}>
|
||||
<head>
|
||||
<SEO {...Astro.props} />
|
||||
<SEO {...seo} />
|
||||
|
||||
{
|
||||
/* No SVG favicon. The mark is a shaded ribbon, not flat vector paths, so
|
||||
@@ -74,13 +101,17 @@ const lang = SITE.locale.replace('_', '-');
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/instrument-serif-latin-400-italic.woff2?v=1"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
{
|
||||
preloadSerifItalic && (
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/instrument-serif-latin-400-italic.woff2?v=1"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
/* No script tag. Not "no framework", not "minimal JS" — none.
|
||||
|
||||
@@ -0,0 +1,971 @@
|
||||
---
|
||||
/**
|
||||
* `/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 → §The arc
|
||||
* 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 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 —
|
||||
* AGENTS.md Q45. No such file exists, 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 there are Pouya's.
|
||||
*
|
||||
* 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 IS NOT ON THIS PAGE. R10 is written as a
|
||||
* prohibition — re-confirm *before* any page listing memberships ships — and
|
||||
* `/about/` is the page it names. The re-confirmation is a fact only Pouya holds
|
||||
* and was not obtained, so the group is withheld and a `TODO(pouya)` sits on
|
||||
* CREDENTIAL_GROUPS below with the exact question. §4 is NOT re-stamped:
|
||||
* nothing was re-checked. Q44.
|
||||
*
|
||||
* A first version of this page published all four and disclosed the gap instead.
|
||||
* Both review agents rejected that; the reasoning is on CREDENTIAL_GROUPS.
|
||||
*/
|
||||
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 Pill from '../components/Pill.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,
|
||||
PORTRAIT,
|
||||
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 designation line — docs/01 item 1. Assembled from constants so it cannot
|
||||
* drift from §4, and ordered held-first.
|
||||
*
|
||||
* Q.Arb IS NOT IN IT, deliberately. It is not held (§4: "Describe as newly
|
||||
* commenced, never as held or nearing completion"), and a designation line is
|
||||
* precisely a list of things held. The arc section states the stage plainly,
|
||||
* which is what §4's paired-disclosure condition requires — this page offers
|
||||
* arbitration, so the stage appears on this page and not only in the footer.
|
||||
*/
|
||||
const designationLine = [
|
||||
'Mediator',
|
||||
CREDENTIALS.designations[0],
|
||||
CREDENTIALS.education[0],
|
||||
];
|
||||
|
||||
/**
|
||||
* The credentialing arc — docs/01 item 4, and docs/03: "the credentialing
|
||||
* pathway from Q.Med through Q.Arb to C.Med-Arb is stated openly as in
|
||||
* progress. The brief treats that arc as part of the story rather than
|
||||
* something to obscure."
|
||||
*
|
||||
* `state` is the load-bearing column. "Commenced August 2026" is §4's exact
|
||||
* wording and the ONLY permitted wording — docs/03: not "in progress", because
|
||||
* the weaker form drifts toward "nearly complete", which §4 Forbidden bars
|
||||
* outright.
|
||||
*/
|
||||
/*
|
||||
* FIVE CLAIMS CAME OUT OF THIS BLOCK, and the first was the worst thing in the
|
||||
* step-3 diff. BOTH review agents found it independently, which is the strongest
|
||||
* signal this loop produces.
|
||||
*
|
||||
* 1. ⚠️ "arbitral appointments are not gated behind it, which is why I accept
|
||||
* them now" — **the false universal Q39 struck, on a public page.**
|
||||
* Unscoped ("arbitral", not commercial), asserted as flat fact in the first
|
||||
* person, and it publishes a proposition of Ontario law that §4 holds only
|
||||
* in scoped form and deliberately does NOT stamp `[verified]`. Family
|
||||
* arbitration is an arbitral appointment and it IS gated
|
||||
* (`docs/reference/ontario-family-arbitration-training.md`). Q39 swept
|
||||
* three instances of this universal on 2026-08-27; this was the fourth and
|
||||
* the first outside a comment. §4 requires the STAGE be stated — never the
|
||||
* register's gating rationale. Deleted rather than rescoped: this page has
|
||||
* no business carrying the argument at all.
|
||||
* 2. "on the same institutional pathway" and
|
||||
* 3. "Three designations on one institutional pathway" (the section lede) —
|
||||
* §4 attaches ADRIC / ADRIO to **Q.Med only**. Neither the Q.Arb row nor
|
||||
* the C.Med-Arb row names a body.
|
||||
* 4. "The senior hybrid designation" — a ranking claim about a third party's
|
||||
* credential structure, with no row and no source.
|
||||
* 5. The expansions — "Qualified Mediator", "Qualified Arbitrator",
|
||||
* "Chartered Mediator-Arbitrator". Flagged as being in §11 Glossary but
|
||||
* not in §4 Verified.
|
||||
*
|
||||
* ⚠️ ITEM 5 WAS REMOVED AND IS NOW RESTORED, AND IT IS THE ONE PLACE THIS
|
||||
* SESSION WENT AGAINST A REVIEW FINDING. The reason is a SECOND finding, from
|
||||
* the next audit pass, and it is a consistency point rather than a claim point:
|
||||
* this page also publishes "Provincial Offences Act", "Statutory Accident
|
||||
* Benefits Schedule" (as SABS) and "the ADR Institute of Canada and the ADR
|
||||
* Institute of Ontario" — every one of them a §11 Glossary expansion, on exactly
|
||||
* the ground the designation names were struck. *"One standard or the other."*
|
||||
*
|
||||
* The standard chosen is: **§11 Glossary is the source for DEFINITIONAL
|
||||
* expansions** — what an abbreviation stands for — while §4 Verified remains the
|
||||
* only source for claims ABOUT POUYA. Expanding `Q.Med` says nothing about him;
|
||||
* "he holds it" is the claim, and that has a row. The alternative standard would
|
||||
* have required stripping POA, SABS and the institute names from the prose and
|
||||
* `recognizedBy` from the JSON-LD, which makes the page materially worse for a
|
||||
* reader who does not already know the acronyms, in exchange for no reduction in
|
||||
* risk. **Q46 asks Pouya to ratify that standard** and it is the only thing
|
||||
* holding it up; if he declines, all four classes come out together.
|
||||
*
|
||||
* Sourcing them externally was tried first and failed: `adric.ca/designations/`
|
||||
* redirects to `/designations-cee/` and its HTML contains **zero** occurrences
|
||||
* of "Q.Med", "Qualified Mediator" or "Chartered Mediator" in 114,985 bytes —
|
||||
* navigation only, body assembled client-side. So R14 cannot be met from the
|
||||
* obvious URL, which is why this rests on §11 and on Q46 rather than on a
|
||||
* committed extract.
|
||||
*/
|
||||
const ARC = [
|
||||
{
|
||||
name: 'Q.Med',
|
||||
state: 'Held',
|
||||
/* NOT "the designation I mediate under". That imported a PERMISSION framing
|
||||
onto what §4 records as a voluntary credential, in a register that says
|
||||
no designation is required to be appointed as a mediator — the
|
||||
credential-as-licence slip §4 says produced a wrong answer twice. */
|
||||
body:
|
||||
'Qualified Mediator, held through the ADR Institute of Canada and the ' +
|
||||
'ADR Institute of Ontario. The designation I hold as a mediator today.',
|
||||
},
|
||||
{
|
||||
name: 'Q.Arb',
|
||||
state: 'Commenced August 2026',
|
||||
body:
|
||||
'Qualified Arbitrator. Newly commenced — not held, and not nearing ' +
|
||||
'completion.',
|
||||
},
|
||||
{
|
||||
name: 'C.Med-Arb',
|
||||
state: 'The endpoint',
|
||||
body:
|
||||
'Chartered Mediator-Arbitrator. The designation this practice is built ' +
|
||||
'toward.',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* - Any licence status, in either direction. §4 records it `[unestablished]`.
|
||||
*
|
||||
* TODO(pouya): Are ADRIC, ADRIO, the three OBA sections (Construction &
|
||||
* Infrastructure, ADR, Civil Litigation) and the Canadian Tax Foundation all
|
||||
* current TODAY, and in which month does each renew? — AGENTS.md Q44.
|
||||
*
|
||||
* ⚠️ MEMBERSHIPS ARE DELIBERATELY NOT RENDERED, AND THIS IS A REVERSAL.
|
||||
*
|
||||
* The first version of this page published all four on §4's 2026-08-26 stamp and
|
||||
* disclosed the outstanding re-confirmation in a note, in this comment, in
|
||||
* `schema.ts`, in §9 Q44 and on the cutover checklist. Both review agents
|
||||
* rejected that, and they are right. §12 **R10** is written as a PROHIBITION —
|
||||
* *"Re-confirm at each renewal, **and before any page listing memberships
|
||||
* ships** — `/about/` at build step 3 is the first one that will"* — and
|
||||
* documenting a prohibition is not discharging it. `CLAUDE.md` gives the
|
||||
* procedure for a fact you do not have, and it is this one: leave
|
||||
* `TODO(pouya)`, log the question, let the gap be visible. *"A build that fails
|
||||
* on an unanswered question is a correct build."*
|
||||
*
|
||||
* The alternative was to ship them and call it disclosed. It was taken once and
|
||||
* is recorded here as the decision it was, not as an oversight — and it came
|
||||
* with a second defect on top: the note asserted *"Memberships are renewed
|
||||
* annually and are listed as current"*, which (a) warranted currency the
|
||||
* register cannot vouch for and (b) widened §4, which records yearly renewal for
|
||||
* the **OBA sections and the CTF only** and says nothing about ADRIC or ADRIO.
|
||||
*
|
||||
* OCNI is the precedent and it is in §4: a membership lapsed, quietly, and the
|
||||
* register now reads "not current, do not publish". Nothing tells you when.
|
||||
*
|
||||
* The other three groups ship. Restoring this one is one array entry, the moment
|
||||
* Q44 closes — and re-stamp §4 and `CREDENTIALS.memberships` that day.
|
||||
*/
|
||||
const CREDENTIAL_GROUPS = [
|
||||
{ title: 'Designations', items: CREDENTIALS.designations },
|
||||
{ title: 'Education', items: CREDENTIALS.education },
|
||||
{ title: 'Certifications', items: CREDENTIALS.certifications },
|
||||
];
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="About · Pouya Lajevardi · Mediator, Q.Med · Toronto"
|
||||
description="Pouya Lajevardi, JD, Q.Med — a Toronto mediator who also practises as a machine-learning and infrastructure engineer. Credentials, background, designations."
|
||||
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. */
|
||||
}
|
||||
<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 commercial disputes and I
|
||||
accept arbitration appointments in commercial matters; the Q.Arb
|
||||
pathway commenced in August 2026. 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), the Q.Arb pathway commenced August 2026,
|
||||
C.Med-Arb as the goal, 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>
|
||||
<p>
|
||||
Mediation is where they meet. I hold the Q.Med designation through the
|
||||
ADR Institute of Canada and the ADR Institute of Ontario, 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>
|
||||
<p>
|
||||
My Q.Arb pathway commenced in August 2026, and C.Med-Arb is the
|
||||
designation I am working toward. I state the stage openly because an
|
||||
appointing body will establish it anyway, and because a reader can do
|
||||
more with the fact than with a hedge. I would rather say where I am on
|
||||
the arc than leave it to be inferred.
|
||||
</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. */
|
||||
}
|
||||
<p>I run SML Company Ltd alongside both.</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, which is
|
||||
the form `docs/03` actually sanctions on `/for-parties/` (*"the
|
||||
mediator is not your lawyer"* — role, not capacity) and the only one
|
||||
that asserts nothing and denies nothing. The underlying question is
|
||||
R1's. */
|
||||
}
|
||||
<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>
|
||||
|
||||
{/* ---- 4. The credentialing arc -------------------------------------- */}
|
||||
{
|
||||
/* SECTION 4 BEFORE SECTION 3, and the reorder is deliberate. docs/01 lists
|
||||
credentials (item 3) then the arc (item 4). The arc is the part a reader
|
||||
is likely to have a question about — it is the thing this practice is
|
||||
candid about that others are not — and burying it under a scannable list
|
||||
of things already held reads as a footnote to them. §4's paired-disclosure
|
||||
condition also wants the stage stated where the offering is made, and the
|
||||
offering is made in the narrative directly above. The list follows. */
|
||||
}
|
||||
<section class="section section-inverse arc-section reveal">
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<SectionHeading
|
||||
eyebrow="The arc"
|
||||
level={2}
|
||||
lede="Three designations. Two of them are ahead of me, and saying so is the point."
|
||||
>
|
||||
<span slot="heading">Where the credentials sit.</span>
|
||||
</SectionHeading>
|
||||
</div>
|
||||
|
||||
{
|
||||
/* `role="list"` RESTORED, AND THE REASON I REMOVED IT WAS WRONG ABOUT
|
||||
ARIA. The comment here claimed that on an <ol> the role "re-announces
|
||||
an ordered list as an unordered one". It does not: **both <ul> and <ol>
|
||||
map to the `list` role**, so `role="list"` on an <ol> is a no-op for
|
||||
ordering, not a downgrade. What it is actually for is the WebKit
|
||||
heuristic that strips list semantics from a list with
|
||||
`list-style-type: none` — and `.arc` sets exactly that.
|
||||
|
||||
It also left the two <ol>s on this two-page site DISAGREEING, with
|
||||
`/`'s `.process-strip` keeping the role. That is the state that gets
|
||||
copied seventeen times.
|
||||
|
||||
Not verified here: whether WebKit's heuristic covers <ol> as well as
|
||||
<ul>. There is no Safari instrument in this environment, so the role
|
||||
stays on the precautionary side, which costs nothing. Chrome's AX tree
|
||||
exposes `.arc` as `list` with three `listitem` children either way. */
|
||||
}
|
||||
<ol class="arc" role="list">
|
||||
{
|
||||
ARC.map((stage) => (
|
||||
<li class="arc-item">
|
||||
<h3 class="arc-name">{stage.name}</h3>
|
||||
<p class="arc-state">
|
||||
<Pill>{stage.state}</Pill>
|
||||
</p>
|
||||
<p class="arc-body">{stage.body}</p>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ---- 3. Credentials, structured ------------------------------------ */}
|
||||
<section class="section section-alt 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 stacked on top of an undischarged R10 — the reminder
|
||||
whose entire purpose is that no such warranty be made without a
|
||||
re-confirmation. The memberships group is now off the page
|
||||
(Q44), so the sentence has nothing left to warrant either.
|
||||
(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. */
|
||||
|
||||
/* --- 4. The arc ------------------------------------------------------ */
|
||||
|
||||
.arc {
|
||||
display: grid;
|
||||
/* `min(18rem, 100%)` rather than a bare 18rem floor: a bare floor cannot
|
||||
shrink below itself and overflows at a large default font size. The
|
||||
credential row on `/` is the measured instance of that mistake. */
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
|
||||
gap: var(--space-6);
|
||||
/* NO `padding: 0` OR `list-style: none` HERE — `global.css`'s
|
||||
`ul[role='list'], ol[role='list']` reset already supplies both, and this
|
||||
block was re-implementing it by hand. Two rules for one job, and the
|
||||
hand-written copy is the one that drifts. `margin: 0` also comes from the
|
||||
global `* { margin: 0 }` reset. */
|
||||
}
|
||||
.arc-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
padding-block-start: var(--space-4);
|
||||
border-block-start: 1px solid var(--line-dark);
|
||||
}
|
||||
/* <h3>, not <p>. These are the headings of the three arc items and they set
|
||||
at --text-2xl serif, so marking them up as paragraphs was the fake-heading
|
||||
pattern: a screen-reader user got no heading navigation for the one section
|
||||
on this page a reader is most likely to jump to. `ProcessStep` on `/` uses
|
||||
<h3> for exactly this shape. Outline stays h1 -> h2 -> h3 with no skips. */
|
||||
.arc-name {
|
||||
font-family: var(--font-serif);
|
||||
font-size: var(--text-2xl);
|
||||
line-height: var(--leading-tight);
|
||||
}
|
||||
.arc-state {
|
||||
margin: 0;
|
||||
}
|
||||
.arc-body {
|
||||
margin: 0;
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
/* NOT --text-secondary. On an inverse ground `--ink-soft` measures ~1.4:1
|
||||
against `--ink` — the inherited `--text-inverse` (cream, 16.81:1) is what
|
||||
carries body copy here, so the colour is deliberately left alone rather
|
||||
than set to a token that is correct only on cream. */
|
||||
}
|
||||
|
||||
/* --- 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);
|
||||
color: var(--text-secondary);
|
||||
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>
|
||||
+258
-92
@@ -29,6 +29,7 @@
|
||||
import { Picture, getImage } from 'astro:assets';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import Button from '../components/Button.astro';
|
||||
import ContactBand from '../components/ContactBand.astro';
|
||||
import CredentialRow from '../components/CredentialRow.astro';
|
||||
import Eyebrow from '../components/Eyebrow.astro';
|
||||
import InfinityMark from '../components/InfinityMark.astro';
|
||||
@@ -39,12 +40,13 @@ import portrait from '../assets/pouya-lajevardi.jpg';
|
||||
import ogDefault from '../assets/og-portrait.jpg';
|
||||
import { homeGraph } from '../data/schema';
|
||||
import {
|
||||
CONTACT,
|
||||
ASYMMETRY_LINE,
|
||||
CREDENTIAL_ROW,
|
||||
CREDENTIAL_ROW_ARB,
|
||||
PRACTICE_AREAS,
|
||||
PORTRAIT,
|
||||
PROCESS,
|
||||
PROCESS_FRAMING,
|
||||
SITE,
|
||||
} from '../data/site';
|
||||
|
||||
@@ -87,6 +89,7 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
description="Commercial mediation and arbitration in Toronto. Construction, technology, energy, insurance and shareholder disputes, read as contracts and as engineering."
|
||||
imageAlt={PORTRAIT.alt}
|
||||
jsonLd={graph}
|
||||
preloadSerifItalic
|
||||
>
|
||||
{/* ---- 1. Hero ------------------------------------------------------- */}
|
||||
<section class="hero">
|
||||
@@ -125,21 +128,35 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
below uses.
|
||||
|
||||
(b) "facts most neutrals take on faith" is a COMPARATIVE assertion
|
||||
about a population of third parties. It comes from `docs/03`'s core
|
||||
positioning statement, so it is approved copy — but `docs/03`'s own
|
||||
compliance checklist requires "any comparative claim is factual and
|
||||
verifiable", and §4 has no row for it. Replaced with a
|
||||
non-comparative distinction that is checkable and says the same
|
||||
thing: documents rather than pleadings. Q41 asks Pouya whether the
|
||||
original may be restored; changing approved copy back is his call,
|
||||
and shipping an unverifiable comparative in the meantime is not. */
|
||||
about a population of third parties, and **Q41(b) CLOSED 2026-08-27:
|
||||
it is not restored, and the reason is not only compliance.** Pouya:
|
||||
*"That is an unverifiable empirical claim about other practitioners,
|
||||
and comparative claims must be factual and verifiable. It is also
|
||||
weaker copy: assert his capability, not the field's incapability."*
|
||||
It is struck from `docs/03`'s core positioning statement too — the
|
||||
approved-copy defence is gone, because the approved copy changed.
|
||||
|
||||
His replacement wording is used verbatim: *"built for disputes that
|
||||
turn on the contract, the code, and the engineering documents"*. The
|
||||
interim ("the documents rather than the pleadings") is also gone; it
|
||||
said nothing about other neutrals but it still worked by contrast.
|
||||
|
||||
ON THE ECHO OF THE HEADLINE, because it is deliberate and one edit
|
||||
from being reversed if he reads it as a stumble. The `<h1>` ends
|
||||
"the contract, the code, and the room"; this sentence re-runs the
|
||||
triad and swaps the third term for "the engineering documents". Two
|
||||
of three words repeat forty words apart. Read as a rhyme it does the
|
||||
work of the whole positioning statement in one move; read as an
|
||||
oversight it looks careless. Judged the first, flagged as the
|
||||
second. */
|
||||
}
|
||||
<p class="hero-lede">
|
||||
I mediate commercial disputes from Toronto, and I accept arbitration
|
||||
appointments. I also practise as a machine-learning and infrastructure
|
||||
engineer, so the matters I take are the ones that turn on the
|
||||
documents rather than the pleadings: the change order, the model card,
|
||||
the System Impact Assessment, and the regulatory overlay around them.
|
||||
I mediate commercial disputes from Toronto, and I accept commercial
|
||||
arbitration appointments. I also practise as a machine-learning and
|
||||
infrastructure engineer, so the matters I take are the ones that turn
|
||||
on the contract, the code, and the engineering documents: the change
|
||||
order, the model card, the System Impact Assessment, and the
|
||||
regulatory overlay around them.
|
||||
</p>
|
||||
|
||||
<div class="hero-cta">
|
||||
@@ -152,9 +169,39 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
|
||||
{
|
||||
/* `widths` + `sizes` rather than `densities`: the portrait is fluid, and
|
||||
a density ladder would size it from one assumed CSS width. The largest
|
||||
real render is ~476px (content 1280 - 96 gutter - 64 gap, x 0.425),
|
||||
so 960 is the 2x ceiling.
|
||||
a density ladder would size it from one assumed CSS width.
|
||||
|
||||
⚠️ THE OLD CEILING ARGUMENT WAS WRONG, AND IT WAS WRONG BY IGNORING
|
||||
THE RANGE WHERE THIS IMAGE IS WIDEST. It read: "The largest real
|
||||
render is ~476px (content 1280 - 96 gutter - 64 gap, x 0.425), so 960
|
||||
is the 2x ceiling." That arithmetic describes the TWO-COLUMN layout,
|
||||
which only engages at 66rem. Below 66rem the hero is a single column
|
||||
and the portrait is the full content width. Measured, both pages:
|
||||
|
||||
viewport slot DPR-2 needs picked result
|
||||
640 592 1184 760/960 1.23-1.56x upscale
|
||||
768 672 1344 960 1.40x
|
||||
900 804 1608 960 1.68x
|
||||
1024 928 1856 960 1.93x
|
||||
|
||||
At 768/DPR-1 a 760w file EXISTS and is not chosen, so part of the loss
|
||||
was purely the wrong `sizes` (60vw declared against a ~88vw slot).
|
||||
Reconfirmed with the HTTP cache cleared — an earlier probe reported a
|
||||
2.81x OVERSIZED fetch at 390/DPR-1 which was a cache artefact, not a
|
||||
defect.
|
||||
|
||||
TWO FIXES, BOTH REQUIRED. (1) `.hero-portrait` is capped at 30rem in
|
||||
the single-column range, so the widest real slot is 480 CSS px
|
||||
everywhere — which makes 960 exactly right for DPR 2 rather than
|
||||
accidentally short. (2) A 1440 rung, because 480 x 3 = 1440 and the
|
||||
desktop slot at DPR 3 already needed 1287-1428; the ≥66rem range was
|
||||
upscaling ~1.34-1.49x at DPR 3 before this and nobody had measured it.
|
||||
The master is 1600, so 1440 exists.
|
||||
|
||||
THE CAP CHANGES HOW THIS PAGE LOOKS between 640px and 1055px — the
|
||||
portrait was 592-928px wide there and is now 480. That is a visible
|
||||
design change to a reviewed page, made on payload grounds; raising the
|
||||
cap is a one-line change but the ladder has to grow with it.
|
||||
|
||||
`width` AND `height` ARE PASSED ALONGSIDE `widths`, AND THAT IS NOT
|
||||
REDUNDANT. With `widths` alone, Astro emits the UNTOUCHED 1600px
|
||||
@@ -168,24 +215,79 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
349kB" for every variant either way.
|
||||
|
||||
What a browser actually takes is the AVIF: 5.6 / 7.3 / 11.1 / 14.8 /
|
||||
21.5 kB across the five widths [measured 2026-08-27].
|
||||
21.5 kB across the first five widths [measured 2026-08-27]; 1080 and
|
||||
1440 are added 2026-08-28 for DPR 3 (26.6 / 48.8 kB).
|
||||
|
||||
eager + fetchpriority=high because this is the LCP candidate on the
|
||||
page docs/04 budgets hardest. */
|
||||
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 — 0 upscaling across 11
|
||||
real device profiles on both pages.
|
||||
|
||||
TWO RESIDUAL OVER-FETCHES, LEFT DELIBERATELY, so neither reads as an
|
||||
oversight later. **320@2** needs 544 and takes 640 (1.18x): there is no
|
||||
rung between 480 and 640, and 480 would be a 1.13x UPSCALE, so the
|
||||
oversize is the better half of that trade. **1056@2 needs 760.4 and
|
||||
takes 960 (1.26x, +6.8 KB)** — a knife-edge, and worth stating because
|
||||
it looks like a `sizes` error and is not: the declared `36vw` is
|
||||
accurate to the measured 36.0% track, and 380.2 x 2 = 760.4 misses the
|
||||
760 rung by four tenths of a pixel. Declaring 35vw to duck under it
|
||||
would make `sizes` less truthful across the whole band in exchange for
|
||||
a 0.05% upscale at this width. The declaration stays honest and one
|
||||
viewport over-fetches.
|
||||
|
||||
⚠️ `fetchpriority="high"` IS GONE, AND THIS PAGE HAD IT WHILE
|
||||
`/about/` WITHHELD IT ON THE IDENTICAL MEASUREMENT. The old comment
|
||||
read "eager + fetchpriority=high because this is the LCP candidate on
|
||||
the page docs/04 budgets hardest" — true only from 768px up. Measured,
|
||||
cache cleared per device:
|
||||
|
||||
320x568 @2 portrait visible 0px LCP — 11,058 B
|
||||
360x780 @3 portrait visible 0px LCP — 21,526 B
|
||||
390x844 @3 portrait visible 0px LCP P.hero-lede 27,594 B
|
||||
430x932 @3 portrait visible 30px LCP P.hero-lede 48,799 B
|
||||
1280x900 @1 portrait visible 595px LCP IMG.portrait 7,257 B
|
||||
|
||||
So on mobile — the axis the ≥95 budget is actually measured on — it
|
||||
promoted 27-49 KB of image the reader cannot see above the Geist face
|
||||
that paints the real LCP element. `/about/` already withheld it for
|
||||
exactly this reason and this page did the opposite; the inconsistency
|
||||
is the finding.
|
||||
|
||||
`loading="eager"` STAYS: the portrait is the LCP element from 768px up,
|
||||
and eager costs nothing where it is off-screen. If the desktop LCP ever
|
||||
needs protecting explicitly, the right instrument is
|
||||
`<link rel="preload" imagesrcset imagesizes>` in <head>, which honours
|
||||
`sizes` and therefore self-cancels on phones — not a blanket attribute
|
||||
that cannot. */
|
||||
}
|
||||
<div class="hero-portrait">
|
||||
<Picture
|
||||
src={portrait}
|
||||
width={960}
|
||||
height={960}
|
||||
widths={[380, 480, 640, 760, 960]}
|
||||
sizes="(min-width: 80rem) 476px, (min-width: 66rem) 42vw, (min-width: 40rem) 60vw, 92vw"
|
||||
widths={[380, 480, 640, 760, 960, 1080, 1200, 1440]}
|
||||
sizes="(min-width: 80rem) 476px, (min-width: 66rem) 36vw, (min-width: 33rem) 480px, calc(100vw - 3rem)"
|
||||
formats={['avif', 'webp']}
|
||||
fallbackFormat="jpeg"
|
||||
alt={PORTRAIT.alt}
|
||||
loading="eager"
|
||||
decoding="sync"
|
||||
fetchpriority="high"
|
||||
class="portrait-img"
|
||||
/>
|
||||
</div>
|
||||
@@ -234,26 +336,62 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
as "the 'two directions at once' argument — law and engineering
|
||||
converging on the same dispute", so the ARGUMENT is unchanged and
|
||||
still delivered; only the construction that carried the implication
|
||||
is gone. Whether Q37's reasoning formally extends to prose is
|
||||
Pouya's to say — Q41. */
|
||||
is gone.
|
||||
|
||||
**Q41(a) CLOSED 2026-08-27: Q37's reasoning DOES extend to prose,
|
||||
and prose has to do more than avoid the parallel.** Pouya: *"The
|
||||
implication test applies everywhere, not just to labels. Prose has
|
||||
more room, so it is easier to satisfy: state the asymmetry
|
||||
explicitly rather than relying on a parallel construction to carry
|
||||
it."* Avoiding the pair was therefore only half the fix — a reader
|
||||
can still supply the missing symmetry from silence. So the second
|
||||
paragraph now names both halves for what they are: training on one
|
||||
side, current work on the other. "Training I hold" is the opposite
|
||||
of a licence claim, which is the point of saying it out loud.
|
||||
|
||||
`docs/01`'s and `docs/03`'s own phrase "law and engineering" is the
|
||||
struck construction; both now carry a note not to lift it into copy.
|
||||
The argument it names is Pouya's and stands. */
|
||||
}
|
||||
<div class="prose approach-prose">
|
||||
<p>
|
||||
Any dispute I take has two readings: what the documents say, and
|
||||
what the engineering says. They are not blended here. They run at
|
||||
the same time.
|
||||
Any dispute I take gets read twice: once against the documents, and
|
||||
once as engineering. The two readings are not blended here. They run
|
||||
at the same time.
|
||||
</p>
|
||||
{
|
||||
/* FROM A CONSTANT, NOT TYPED. It was typed here and then typed
|
||||
again on `/about/`, and the two copies had ALREADY diverged inside
|
||||
one session — a comma here, full stops there. Q41(a) makes this
|
||||
the sentence responsible for foreclosing the licence implication,
|
||||
so it is the worst string on the site to let drift. See
|
||||
ASYMMETRY_LINE in src/data/site.ts. */
|
||||
}
|
||||
<p>{ASYMMETRY_LINE}</p>
|
||||
{
|
||||
/* TWO COMPARATIVES CAME OUT OF THESE PARAGRAPHS ON 2026-08-28, and
|
||||
both had SURVIVED the sweep that closed Q41(b) the day before:
|
||||
|
||||
"The second half of each pair usually arrives as a separate
|
||||
expert report." — an empirical claim about how disputes are
|
||||
usually run, i.e. about a population of other matters.
|
||||
"it is why the technical half is not something a party has to
|
||||
commission and wait for" — the same claim in counterfactual
|
||||
form, which is harder to spot and says more.
|
||||
|
||||
Pouya's ruling on Q41(b) is the test: *"assert his capability, not
|
||||
the field's incapability."* Both worked by asserting the field's.
|
||||
What replaces them says only what he does, which is the stronger
|
||||
claim anyway — and it is shorter. */
|
||||
}
|
||||
<p>
|
||||
A construction claim is a contract question and a scheduling
|
||||
question. A software dispute is a licence question and an
|
||||
architecture question. A grid connection is a regulatory question
|
||||
and a load question. The second half of each pair usually arrives as
|
||||
a separate expert report.
|
||||
and a load question.
|
||||
</p>
|
||||
<p>
|
||||
I do both readings myself. That is the whole of it, and it is why
|
||||
the technical half is not something a party has to commission and
|
||||
wait for.
|
||||
I read both halves of each pair myself. That is the whole of it.
|
||||
</p>
|
||||
<p class="approach-metaphor">
|
||||
My mark is an infinity loop, and it is the argument in one line:
|
||||
@@ -419,14 +557,21 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
|
||||
{
|
||||
/* THIS SAID "All six areas, and what else is offered", which asserted
|
||||
offerings beyond the six. The only candidates — early neutral
|
||||
evaluation, settlement counsel, dispute-system design, pre-dispute
|
||||
technical advisory (`docs/01` §`/practice/`) — have NO row in §4
|
||||
"Offered now", and §4 states: "An offering may not be published until
|
||||
it has a row." `claims-auditor` called this the Med-Arb-in-the-footer
|
||||
defect in a new place, and it is exactly that. Reworded to what is
|
||||
rowed; the four candidates need rows before `/practice/` can offer
|
||||
them at step 5. */
|
||||
offerings beyond the six, none of which had a §4 row.
|
||||
|
||||
**Q42 CLOSED 2026-08-27, and one of the four candidates came out.**
|
||||
Early neutral evaluation, dispute-system design and pre-dispute
|
||||
technical advisory now have Offerings rows. **Settlement counsel does
|
||||
not, and never will** — Pouya struck it as his own error in `docs/01`:
|
||||
*"Settlement counsel acts FOR a party in negotiation. That is a
|
||||
partisan role, and putting it on a site that (a) sells neutrality and
|
||||
(b) asserts no licensure under D13 is wrong twice over."*
|
||||
|
||||
THIS LINE STILL READS "All six practice areas" and that is unchanged
|
||||
on purpose. The three rowed processes are `/practice/`'s "also
|
||||
offered" strip at step 5, not a claim `/` makes in a link label — a
|
||||
six-card grid followed by "and what else is offered" is the
|
||||
Med-Arb-in-the-footer shape whether or not the rows exist. */
|
||||
}
|
||||
<p class="areas-more">
|
||||
<a href="/practice/">All six practice areas →</a>
|
||||
@@ -441,7 +586,7 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
<SectionHeading
|
||||
eyebrow="How it runs"
|
||||
level={2}
|
||||
lede="Five steps, from the first call to the conclusion. Counsel want the shape of the downside before they commit a client's day."
|
||||
lede="Five steps, from the first call to the conclusion — including what happens if the matter does not settle."
|
||||
>
|
||||
<span slot="heading">From first call to conclusion.</span>
|
||||
</SectionHeading>
|
||||
@@ -457,6 +602,17 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
}
|
||||
</ol>
|
||||
|
||||
{
|
||||
/* NOT OPTIONAL, AND NOT A DISCLAIMER — Q43, Pouya 2026-08-27. It is the
|
||||
condition on which the five timings may be published at all: *"Published
|
||||
as typical, they are honest and useful; published as commitments, the
|
||||
first matter that slips makes the page false."* It sits directly under
|
||||
the numbers rather than in the section lede above them, because a
|
||||
reader who scans the strip and skips the lede has read a commitment.
|
||||
`/process/` renders the same constant at step 6. */
|
||||
}
|
||||
<p class="process-framing">{PROCESS_FRAMING}</p>
|
||||
|
||||
<p class="process-more">
|
||||
<a href="/process/">What happens if the matter does not settle →</a
|
||||
>
|
||||
@@ -468,28 +624,13 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
|
||||
{/* ---- 8. Contact band ---------------------------------------------- */}
|
||||
{
|
||||
/* docs/01 item 8 is "Intake CTA and booking link". THERE IS 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. */
|
||||
/* A COMPONENT SINCE 2026-08-28. It was ~20 lines of markup plus ~20 of CSS
|
||||
here and the same again on `/about/`, and the two had already drifted
|
||||
(`.contact-body` at 52ch here, 46ch there) inside the session that wrote
|
||||
the second one. Seventeen pages remain. docs/01 item 8's missing booking
|
||||
link is documented in the component, once. */
|
||||
}
|
||||
<section class="section section-accent contact-band">
|
||||
<div class="wrap contact-inner">
|
||||
<div>
|
||||
<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 →</Button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<ContactBand />
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
@@ -536,6 +677,15 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--bg-raised);
|
||||
/* THE CAP IS THE FIX FOR THE UPSCALE, not a style preference. Uncapped, the
|
||||
single-column slot ran to 928 CSS px at a 1024px viewport, which is 1856
|
||||
device px at DPR 2 against a 960w ceiling — 1.93x. 30rem makes 480 the
|
||||
widest real slot on any page, so the ladder's 960 covers DPR 2 exactly
|
||||
and the new 1440 rung covers DPR 3. See the <Picture> comment above.
|
||||
`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;
|
||||
}
|
||||
/* The <picture> wrapper is the box that gets sized, NOT the <img> — the exact
|
||||
defect CLAUDE.md records for <Button> and then for <Picture> inside
|
||||
@@ -555,6 +705,13 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
}
|
||||
|
||||
@media (min-width: 66rem) {
|
||||
.hero-portrait {
|
||||
/* Above 66rem the grid track is already 390-476px, so the cap is inert —
|
||||
released anyway so the track, not this number, governs the two-column
|
||||
layout. Keeping it would silently become the constraint if the track
|
||||
ever widened. */
|
||||
max-inline-size: none;
|
||||
}
|
||||
.hero {
|
||||
padding-block: var(--space-9);
|
||||
}
|
||||
@@ -581,10 +738,17 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
gap: var(--space-7);
|
||||
align-items: center;
|
||||
}
|
||||
/* NO `display: flex; gap` ANY MORE, AND THAT IS A CORRECTION TO A FIX.
|
||||
global.css gained `:where(.prose) > p + p { margin-block-start }` on
|
||||
2026-08-28 because a bare `.prose` block had no paragraph spacing at all.
|
||||
The new rule's comment asserted "with `:where()` the flex container's gap
|
||||
governs and this contributes nothing" — **false, and it was measured false
|
||||
immediately after being written.** `:where()` controls SPECIFICITY, not
|
||||
whether a declaration applies: nothing here was overriding the margin, so
|
||||
flex `gap` and the margin both applied and this block's paragraph gaps went
|
||||
**24px -> 48px**. Spacing now comes from the one global rule, which is the
|
||||
point of having it. */
|
||||
.approach-prose {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-5);
|
||||
margin-block-start: var(--space-6);
|
||||
font-size: var(--text-lg);
|
||||
line-height: var(--leading-relaxed);
|
||||
@@ -614,9 +778,10 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
|
||||
/* --- 4. Two practices ---------------------------------------------- */
|
||||
|
||||
.section-head {
|
||||
margin-block-end: var(--space-7);
|
||||
}
|
||||
/* 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). */
|
||||
.pair {
|
||||
display: grid;
|
||||
gap: var(--space-5);
|
||||
@@ -752,29 +917,30 @@ const graph = homeGraph(new URL(ldImage.src, Astro.site).href);
|
||||
gap: var(--space-5);
|
||||
margin: 0;
|
||||
}
|
||||
/* --space-5 rather than --space-6: this belongs to the strip above it, not to
|
||||
the link below.
|
||||
|
||||
/* --- 8. Contact band ----------------------------------------------- */
|
||||
THE RATIO IN THIS COMMENT WAS WRONG WHEN FIRST WRITTEN. It said
|
||||
"--text-meta on cream measures 3.07:1". It does not: `#6e6359` on `#faf7f2`
|
||||
measures **5.47:1** and passes AA. 3.07:1 is `--muted` on **ink**, which is
|
||||
exactly what `tokens.css` says and what this comment misread. So the
|
||||
*reason* given was false even though the *choice* is right —
|
||||
--text-secondary (10.76-11.75:1) is correct here because this sentence is a
|
||||
CONDITION on the numbers above it, not metadata about them, and a condition
|
||||
has to read like body copy. Corrected 2026-08-28 on
|
||||
`adversarial-reviewer`'s measurement. */
|
||||
.process-framing {
|
||||
margin-block-start: var(--space-5);
|
||||
max-inline-size: var(--width-prose);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.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 {
|
||||
max-inline-size: 52ch;
|
||||
}
|
||||
.contact-action {
|
||||
/* `flex: none` is `0 0 auto`, so the button could not shrink below its
|
||||
max-content width — 425px at a 200% default font size, in a 342px
|
||||
container. `0 1 auto` plus `min-inline-size: 0` lets it. It still never
|
||||
shrinks at a normal font size, because there is room. */
|
||||
flex: 0 1 auto;
|
||||
min-inline-size: 0;
|
||||
}
|
||||
/* 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>
|
||||
|
||||
@@ -283,6 +283,44 @@ a:hover {
|
||||
.prose p {
|
||||
max-inline-size: var(--width-prose);
|
||||
}
|
||||
/* `.prose` HAD NO PARAGRAPH SPACING, AND NOTHING ANYWHERE SUPPLIED IT.
|
||||
The reset above sets `* { margin: 0 }`, so a bare `.prose` with two <p>
|
||||
children rendered them as one block. Measured on `/about/` §Language:
|
||||
**gap between paragraph 1 and paragraph 2 = 0.0px** — "…history arrive
|
||||
together." running straight into "Working in the parties' own language…", on
|
||||
screen and in the printed PDF.
|
||||
|
||||
It survived step 2 because BOTH of `/`'s prose blocks supply their own
|
||||
spacing: `.approach-prose` uses `display:flex; gap`, and `.bio-prose` on
|
||||
`/about/` has its own `> p + p`. So the only two call sites happened to opt
|
||||
out of the defect. The rule belongs HERE, where `.prose` lives, or it has to
|
||||
be remembered on all fifteen remaining pages.
|
||||
|
||||
`:where()` KEEPS THE SPECIFICITY AT ZERO so a component's own rule for the
|
||||
SAME PROPERTY wins without `!important`.
|
||||
|
||||
⚠️ IT DOES NOT PROTECT AGAINST A FLEX `gap`, AND THIS COMMENT ONCE CLAIMED IT
|
||||
DID — "Verified: with `:where()` the flex container's gap governs and this
|
||||
contributes nothing." That was false and was measured false minutes later:
|
||||
`:where()` lowers SPECIFICITY, which only matters when two rules set the same
|
||||
property. A flex `gap` is a different property, so gap and margin both apply
|
||||
and add. `/`'s `.approach-prose` went **24px -> 48px** on the strength of that
|
||||
sentence. It has been converted to use this rule instead of a `gap`, and
|
||||
`.bio-prose`'s duplicate `> p + p` was removed for the same reason. If a
|
||||
future block needs different spacing, override `margin-block-start` — do not
|
||||
reach for `gap`. */
|
||||
:where(.prose) > p + p {
|
||||
margin-block-start: var(--space-5);
|
||||
}
|
||||
/* THE PAGE-OWNED WRAPPER FOR `SectionHeading`, and it lives here because it was
|
||||
byte-identical in two pages with seventeen to come. It exists only because a
|
||||
parent cannot style a child component's root (`CLAUDE.md`; measured on
|
||||
`SectionHeading`), so every page that uses a section heading needs a wrapper
|
||||
it owns — which means every page needs this rule. Same argument that extracted
|
||||
`ContactBand`, applied to a rule instead of a component. */
|
||||
.section-head {
|
||||
margin-block-end: var(--space-7);
|
||||
}
|
||||
.section {
|
||||
padding-block: var(--section-y);
|
||||
}
|
||||
@@ -491,4 +529,76 @@ hr {
|
||||
.section {
|
||||
padding-block: var(--space-5);
|
||||
}
|
||||
|
||||
/* THE INVERSE GROUNDS HAD TO BE NEUTRALISED AND WERE NOT — and this block's
|
||||
own heading says why it matters: the About page is printed by people
|
||||
evaluating an appointment.
|
||||
|
||||
`print-color-adjust` defaults to `economy`, so a UA drops the BACKGROUND
|
||||
and keeps the text. Chrome's default print dialog has "Background graphics"
|
||||
unchecked, so `.section-inverse` and `.section-accent` printed cream text
|
||||
on white paper. Measured with `Page.printToPDF`, `printBackground: false`,
|
||||
rasterised at 150 dpi: the dominant glyph colour across the whole arc block
|
||||
was **rgb(166,164,161) — 2.49:1 against white**, and that grey is Chrome's
|
||||
own legibility fudge. The DECLARED colour is cream at ~1.04:1, which is
|
||||
what a UA without that fudge renders. With `printBackground: true` the
|
||||
pages are correct, which is what isolates the cause.
|
||||
|
||||
What vanished was the Q.Med / Q.Arb / C.Med-Arb progression on `/about/` —
|
||||
§4's paired disclosure — plus the contact band. `!important` because the
|
||||
rules being overridden are class-level and these must win regardless of
|
||||
which section variant a future page uses. */
|
||||
/* TOKENS FIRST, THEN CLASSES — and the token half is the part that works.
|
||||
A class-by-class version of this block shipped first and MISSED TWO
|
||||
ELEMENTS, both measured under print-media emulation: `.approach-metaphor`
|
||||
on `/` and `.btn-gold` on both pages stayed at `rgb(226,200,154)` —
|
||||
gold-l, which is **1.62:1 against white paper** once the ground is dropped.
|
||||
One is the paragraph carrying the infinity-mark argument; the other is the
|
||||
call to action. Enumerating class names cannot work here: `--text-inverse-2`
|
||||
is consumed by page-scoped and component-scoped rules this file has never
|
||||
heard of, and there will be seventeen more pages of them.
|
||||
|
||||
Custom properties INHERIT, and that is the one mechanism that crosses
|
||||
Astro's component-scope boundary (see `Pill.astro`). Redefining the three
|
||||
inverse tokens on the section itself therefore reaches every descendant
|
||||
rule, including ones written after this block. */
|
||||
.section-inverse,
|
||||
.section-accent {
|
||||
background: transparent !important;
|
||||
color: #000 !important;
|
||||
--text-inverse: #000;
|
||||
--text-inverse-2: #000;
|
||||
--pill-fg: #000;
|
||||
--pill-border: #000;
|
||||
--rule: #000;
|
||||
}
|
||||
/* The belt-and-braces half. These four set a colour LITERALLY rather than
|
||||
through a token, so the inheritance above does not reach them. */
|
||||
.section-inverse .eyebrow,
|
||||
.section-accent .eyebrow,
|
||||
.section-inverse .lede,
|
||||
.section-accent .lede {
|
||||
color: #000 !important;
|
||||
}
|
||||
/* The dot is a background, not text, so it does not follow `color`. */
|
||||
.section-inverse .eyebrow .dot,
|
||||
.section-accent .eyebrow .dot {
|
||||
background: #000 !important;
|
||||
}
|
||||
/* EVERY BUTTON, NOT JUST THE ONES ON AN INVERSE GROUND. Two of the three
|
||||
variants set light text on a coloured background of their own
|
||||
(`.btn-primary` cream-on-maroon, `.btn-gold` gold-on-ink), and a UA at
|
||||
`print-color-adjust: economy` drops the background and keeps the text.
|
||||
Measured against white paper: `.btn-gold` 1.62:1, `.btn-primary` **1.07:1**.
|
||||
|
||||
The first version of this rule was scoped to `.section-accent .btn-gold`,
|
||||
which fixed the contact band and left the HERO CTA on `/` unreadable —
|
||||
`.btn-primary` sits on cream, inside no inverse section at all, so nothing
|
||||
in this block reached it. A print sweep of all 89 visible text elements
|
||||
found it; the class-scoped version had passed its own narrower check. */
|
||||
.btn {
|
||||
background: transparent !important;
|
||||
color: #000 !important;
|
||||
border-color: #000 !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user