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
+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>
|
||||
|
||||
Reference in New Issue
Block a user