/* Self-hosted fonts — Fraunces (display) + Newsreader (text), latin subset, variable
   weight, served same-origin: faster, private (no Google call), keeps the CSP strict.
   Re-fetch from the Google Fonts css2 API (Chrome UA) if a face ever changes. */
@font-face{font-family:'Fraunces';font-style:normal;font-weight:400 500;font-display:swap;src:url(fonts/fraunces.woff2) format('woff2');}
@font-face{font-family:'Newsreader';font-style:normal;font-weight:400 500;font-display:swap;src:url(fonts/newsreader.woff2) format('woff2');}
@font-face{font-family:'Newsreader';font-style:italic;font-weight:400;font-display:swap;src:url(fonts/newsreader-italic.woff2) format('woff2');}
:root{
  --paper:#f5f2ea; --ink:#211e19; --muted:#5f594e; --line:#ddd6c8;
  /* MAT SYSTEM — one framing language with two GLOBAL dials (weight + bottom-bias).
     Each surface sets its own --ms (side mat, tuned to ~4.5% of that image's width);
     the foot derives as --ms x --mat-foot. Pure % can't track height-capped images in
     CSS, so sizes are per-surface; width-pinned wrappers (figs) use true %. */
  --mat:#fcfbf8; --key:rgba(33,30,25,0.18); --key-soft:rgba(33,30,25,0.15);
  --tile:#e7e1d4; --card:#efe9dd; --card-hover:#e9e1d0; --wall:#6f6450;   /* warm surfaces + the dimmed loupe wall (gallery dusk) */
  --mat-scale:1;     /* GLOBAL mat weight — 1.0 ≈ 5% (measured). Turn this one knob (e.g. 1.1 ≈ 5.5%). */
  --mat-foot:1.33;   /* foot ÷ side (bottom-weighting) */
  --gutter:52px;     /* the page's side inset — ONE definition, consumed by .wrap's padding and
                        the splash's photo-width cap --pw. A token because the same inset was
                        once hand-doubled in two places and drifted (#175); since #181 it feeds
                        widths only, never a reserve height. Overridden for phones below. */
  --serif:'Fraunces', Georgia, serif;
  --text:'Newsreader', Georgia, serif;
  --sans:-apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
}
.sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;}
/* Control-contrast rule: every interactive affordance (dots, arrows, loupe close, links) sits at >=3:1 on the paper — tune control tones here, not per component. */
*{box-sizing:border-box;}
body{margin:0; background:var(--paper); color:var(--ink); font-family:var(--text);
  font-size:19px; line-height:1.6; -webkit-font-smoothing:antialiased;}
img{display:block;}
a:focus-visible, button:focus-visible, [tabindex]:focus-visible{outline:3px solid var(--ink); outline-offset:3px; border-radius:2px;}
/* ...but never while the visitor is using a pointer: this site moves focus by script
   (chapter turns, loupe/menu open+close) and WebKit paints the keyboard ring for that
   even on a finger tap. focus.js records the modality; focus still MOVES, only the
   paint is withheld. Beats every ring rule below it on specificity — keep it here.
   BOTH pseudo-classes: measured in WebKit, closing the loupe by tap leaves a 1px UA
   ring around the PHOTOGRAPH via plain :focus, which a :focus-visible rule never sees. */
:root[data-modality="pointer"] :focus, :root[data-modality="pointer"] :focus-visible{outline:none;}
.wrap{max-width:1400px; margin:0 auto; padding:30px var(--gutter) 64px;}
/* Short pages (404, sparse indexes): pin the footer to the viewport bottom.
   The ::after spacer absorbs free space BEFORE the footer (order 98 < 99), so
   the footer keeps its normal 72px rhythm on tall pages (spacer collapses to
   zero) and sits at the bottom on short ones. Only direct children become
   flex items — page internals are untouched. */
.wrap{min-height:100vh; min-height:100svh; display:flex; flex-direction:column;}  /* vh first = the pre-15.4-Safari fallback */
.wrap::after{content:''; flex:1; order:98;}
.wrap > footer{order:99;}

header{display:flex; align-items:baseline; justify-content:space-between;
  padding-bottom:18px; border-bottom:1px solid var(--line);}
/* The wordmark is a deliberate LOGOTYPE in Newsreader (the calm text serif), not Fraunces —
   Fraunces' display J read too characterful at mark size. The photoapp "Copy matted" signature
   uses the same Newsreader, so the mark is identical on the web + in prints. consistency.cjs guards it. */
.mark{font-family:var(--text); font-size:25px; font-weight:500; letter-spacing:.01em; text-decoration:none; color:var(--ink);}
.mark small{font-family:var(--sans); font-size:12px; font-weight:400; color:var(--muted); letter-spacing:.16em; text-transform:uppercase; margin-left:9px;}
nav{font-family:var(--sans); font-size:16px; display:flex; gap:22px;}
nav a{color:var(--ink); text-decoration:none; letter-spacing:.01em; padding:13px 1px; margin:-7px 0; display:inline-block;}  /* ~44px touch height on iPad (review) */
nav a:hover{text-decoration:underline; text-underline-offset:5px; text-decoration-thickness:1px;}
nav a.on{color:var(--ink); text-decoration:underline; text-underline-offset:5px; text-decoration-thickness:1px;}

/* prose links — on-palette (ink + underline), never the browser default blue: 404, essays, About, future copy */
.page-head a, .work-intro a, .essay a, .spread .text-page a, .land-text a{color:var(--ink); text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px;}
.page-head a:hover, .work-intro a:hover, .essay a:hover, .spread .text-page a:hover, .land-text a:hover{color:var(--muted);}

/* mobile menu button + overlay (button shown only on narrow screens) */
.menu-btn{display:none; align-items:center; gap:8px; background:none; border:0; cursor:pointer; color:var(--ink); font-family:var(--sans); font-size:14px; letter-spacing:.01em; padding:13px 8px; min-height:44px;}  /* 44px hit floor (a11y review) */
.menu-btn svg{display:block;}
.menu-overlay{position:fixed; inset:0; z-index:60; background:var(--paper); display:none; flex-direction:column; padding:24px 26px 40px;}
.menu-overlay.open{display:flex;}
.menu-top{display:flex; justify-content:space-between; align-items:center; padding-bottom:18px; margin-bottom:28px; border-bottom:1px solid var(--line);}
.menu-mark{font-family:var(--text); font-size:23px; font-weight:500; color:var(--ink);}  /* Newsreader logotype — see .mark */
.menu-close{background:none; border:0; font:300 36px/1 var(--serif); color:var(--ink); cursor:pointer; padding:4px 10px 8px; min-width:44px; min-height:44px;}  /* 44px hit floor (a11y review) */
.menu-overlay a{font-family:var(--serif); font-size:30px; font-weight:500; color:var(--ink); text-decoration:none; padding:18px 2px; border-bottom:1px solid var(--line);}
.menu-overlay a.on{text-decoration:underline; text-underline-offset:8px; text-decoration-thickness:1px;}  /* you-are-here = the nav underline, never pale colour (a11y review) */
.menu-btn:focus-visible, .menu-close:focus-visible, .menu-overlay a:focus-visible{outline:3px solid var(--ink); outline-offset:3px;}

footer{display:flex; justify-content:space-between; font-family:var(--sans); font-size:12px;
  color:var(--muted); letter-spacing:.02em; margin-top:72px; padding-top:18px; border-top:1px solid var(--line);}
footer a{color:var(--muted); text-decoration:underline; text-underline-offset:3px; display:inline-block; padding:8px 2px; margin:-8px -2px;}  /* padded hit area (review) */

/* Home splash — one random pool image, contain-sized (never cropped — portrait or landscape sized
   to the viewport), slim-framed; splash.js fades it in. The image clicks into the loupe (loupe.js).

   THE min-height IS THE DESIGN, NOT A LAYOUT DETAIL — do not remove it (#175). It is what makes
   entering the home page land on a SINGLE PHOTOGRAPH: it reserves the first screen so the doors
   below always start past the fold, whatever shape the browser happened to pick — at EVERY width
   since #181; the phone is not an exception (see the media query below). Take it away and the
   doors join the arrival on any screen too tall for the photograph to fill, and the home page
   stops being a shop window and becomes an index. splash.cjs asserts this now; it did not before,
   which is exactly how the reserve got deleted once already.

   --splash-top is the reserve's one input, and its two values are NOT the same quantity. The
   phone's 97px IS the header block above the splash, exactly (22 wrap padding + 63 header + 12
   margin) — ZERO lift, because on a small screen the reserve is also the photograph's height
   budget and every lifted pixel shrinks the picture (on a phone held landscape the cap IS the
   reserve). Its doors then land 64px past the fold: the .doors own margin. The desktop's 150px
   is the header block (113 measured: 30 + 59 + 24) PLUS 37px of lift — #175's shipped constant,
   kept so big screens render byte-identically — putting the desktop doors at 64-37=27px past
   the fold. Re-tuning after a header change: preserve the LIFT, not a formula — re-measure the
   block, then desktop = block + 37, phone = block; the clearance follows as the doors margin
   minus the lift. splash.cjs asserts both: zero lift on phones (equality) and never-negative
   lift everywhere (the wordmark wraps below ~360px and outgrows 97 — open issue #183).
   --reserve is "the rest of the first screen"; the image cap subtracts the mat from it, so a
   height-bound frame lands exactly on the reserve (also asserted).

   It also earns its keep twice over: being CSS, it holds the space at first layout, before
   splash.js has picked anything — so the doors never jump when the photograph arrives. The frame
   fits inside it by construction rather than by luck: the image's height cap below is the reserve
   minus the mat, so the two track each other at every --sf and the box never grows.

   place-items:START center, NOT center. Centring was the one thing actually wrong here: it
   split the surplus into equal bands above and below, framing a landscape photograph in 244px of
   nothing on a tablet held upright and reading as a fault. Top-aligned, the photograph sits under
   the header where the eye expects it and the surplus simply trails below. */
.splash{--sf:clamp(9px,1.3vmin,16px); --matv:calc(var(--sf) + var(--sf) * var(--mat-foot)); --pw:calc(min(1400px,100vw) - 2*var(--gutter) - 2*var(--sf)); --splash-top:150px; --reserve:calc(100svh - var(--splash-top)); position:relative; min-height:var(--reserve); display:grid; place-items:start center; margin-top:24px;}
.splash .frame{background:var(--mat); padding:var(--sf) var(--sf) calc(var(--sf) * var(--mat-foot)); outline:1px solid var(--key); box-shadow:0 8px 34px rgba(33,30,25,.14); line-height:0; opacity:0; transition:opacity .45s ease;}
/* The height cap is the RESERVE MINUS THE MAT, not a hardcoded number, so the frame lands exactly
   on the reserve at any --sf. It used to be `100svh - 180px`, which allowed 30px for a mat that is
   ~27px at 1440x900 — but --sf is clamp(9px,1.3vmin,16px), so above ~990px of vmin it clamps to
   16px and the mat grows to ~37px. Measured: at 2560x1440 an upright frame came to 1297 against a
   1290 reserve, so the box grew 7px for upright picks only and the doors moved when the photograph
   landed. Derived from the reserve, that cannot happen at any viewport. */
.splash .frame img{width:auto; height:auto; max-width:var(--pw); max-height:calc(var(--reserve) - var(--matv));}
/* THE SAME TENET AT EVERY WIDTH (#181). A phone briefly had its own reserve — cut to the
   photograph's worst-case shape so the doors could join the arrival (#175's "option C, revisit
   if there is an issue") — and the revisit, on the device, killed it: a peeking door tile reads
   as clutter fighting the photograph (at worst the blue Bubbles door under a monochrome pick),
   where empty mat below a top-aligned photograph reads as a gallery wall — the idiom already
   approved on iPad portrait; a landscape pick on a tall phone leaves more of it, seen and
   approved on-device. One photograph on arrival, everywhere; a phone photograph is small
   because width dictates it (we never crop), and the calm below it is the composition, not a
   fault.

   So the only thing a narrow screen changes is the top offset — the header block exactly, see
   --splash-top above — and the tighter margin feeding it. Retiring the derived reserve also
   erases #177's 600-760px band: with one rule there is no boundary at which the design changes
   character, so an iPad mini (744) or a split-view pane simply gets the tenet. The reserve
   still holds the space at first layout, before splash.js has picked (0.42 CLS was the
   measured cost of not doing so, on the device Core Web Vitals weights hardest), and splash.cjs
   asserts box == reserve for the pool's extremes at every viewport in ARRIVAL. */
@media (max-width:760px){ .splash{--splash-top:97px; margin-top:12px;} }
@media (prefers-reduced-motion:reduce){ .splash .frame{transition:none;} }

/* loupe / lightbox — a matted print, floated with space on a dimmed ground */
.lightbox{position:fixed; inset:0; z-index:50; display:none; align-items:center; justify-content:center; background:var(--wall); padding:4vmin; cursor:zoom-out;}
.lightbox.open{display:flex;}
/* loupe mat: CSS fallback (portrait-tuned); loupe.js overrides --ms to exactly 5% of the rendered image so BOTH orientations match the page */
.lb-mat{display:inline-block; background:var(--mat); box-sizing:border-box; --ms:calc(clamp(16px,1.7vw,26px) * var(--mat-scale)); padding:var(--ms) var(--ms) calc(var(--ms) * var(--mat-foot)); outline:1px solid var(--key); cursor:default;}
.lb-mat img{display:block; max-width:72vw; max-height:72vh; width:auto; height:auto;}
.lb-close{position:fixed; top:16px; right:22px; z-index:51; width:46px; height:46px; padding:0 0 3px; border:0; border-radius:50%; background:rgba(245,242,234,0.14); color:var(--paper); font:300 28px/1 var(--serif); cursor:pointer; transition:background .2s;}
.lb-close:hover{background:rgba(245,242,234,0.26);}
.lb-close:focus-visible{outline:3px solid var(--paper); outline-offset:3px;}  /* paper on the dusk wall (5.2:1) — ink measured 2.86:1 (a11y review) */
.intro{text-align:center; color:var(--muted); font-size:22px; line-height:1.5; max-width:30rem; margin:34px auto 0;}

.doors{display:grid; grid-template-columns:repeat(3,1fr); gap:26px; margin-top:64px;}
.door{cursor:pointer; text-decoration:none; color:var(--ink); display:block;}
.door .pic{aspect-ratio:3/4; overflow:hidden; background:var(--tile); border:1px solid var(--key-soft);}
.door .pic picture, .folio .pic picture{display:block; width:100%; height:100%;}  /* a <Plate> wrapper must fill the cover tile so the img height:100% + object-fit:cover resolves */
.door .pic img{width:100%; height:100%; object-fit:cover; transition:transform 1.4s ease;}
.door:hover .pic img{transform:scale(1.03);}
.door .name{font-family:var(--serif); font-size:22px; font-weight:500; margin:14px 0 2px;}
.door .sub{font-family:var(--sans); font-size:13px; color:var(--muted);}

.kicker{font-family:var(--sans); font-size:12px; font-weight:400; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); margin:0;}  /* weight+margin normalized: kickers may be h1s now (a11y review) */
.page-head{text-align:center; max-width:38rem; margin:46px auto 42px;}
.page-head .kicker{display:block; margin-bottom:16px;}
.page-head h1{font-family:var(--serif); font-weight:500; font-size:34px; margin:16px 0 18px; letter-spacing:.01em;}
.page-head .statement{color:var(--muted); font-size:22px; line-height:1.6;}  /* 22px reading floor (review) */

.essay{max-width:40rem; margin:0 auto;}
.essay .kicker{display:block; margin:46px 0 14px;}
.essay h1{font-family:var(--serif); font-weight:500; font-size:34px; line-height:1.18; margin:0 0 12px;}
.essay .meta{font-family:var(--sans); font-size:13px; color:var(--muted); letter-spacing:.03em; margin-bottom:32px;}
.essay-hero{margin:0 0 30px;}
.essay-hero img{width:100%;}
/* TWO LEVELS, because one was never enough for a long essay. `h2` is a SECTION of the essay; `h3`
   is a step within one, and the only level that takes a generated number.
   h2 moves 23px → 27px. At 23 against 22px body it was a heading by FACE alone (Fraunces against
   Newsreader) with one point of size behind it — enough while it was the only level, but with a
   second level below it the top one has to have somewhere to be. h3 inherits exactly the 23px h2
   has read at on the live site for months, so the new level is the known-good one and the change
   is the promotion of h2, not an untested size. */
.essay h2{font-family:var(--serif); font-weight:500; font-size:27px; margin:46px 0 12px;}
.essay h3{font-family:var(--serif); font-weight:500; font-size:23px; margin:32px 0 8px;}
/* About's in-body headings are SECTION LABELS, not reading subheads — they
   speak in the site's kicker voice (small letterspaced caps, like the "ABOUT"
   above them), not the essays' serif h2. One point of size over the body just
   reads as bold text. */
.about-body h2{font-family:var(--sans); font-weight:400; font-size:12px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); margin:46px 0 16px;}
.essay p{font-size:22px; line-height:1.68; margin:0 0 1.1em;}

/* LISTS IN READING PROSE — two kinds, and markdown already tells them apart.
   `1.` is a SEQUENCE: numerals hang in the page gutter so the item text keeps ONE left edge with
   the prose around it (browser default shoves every item 40.7px right — measured, tests/lists.cjs).
   `- ` is a SET: each item opens with its own term, which IS the handle, so a bullet beside it
   would be a second handle doing nothing.

   The marker sits in a real grid column aligned on the BASELINE, not a box nudged into the margin.
   Absolute positioning pins a box TOP, so the numeral's baseline moved every time its size changed
   and size and position could never both be right; baseline alignment holds at 0px delta at any
   size, with no magic offset to go stale when the type scale moves. Item spacing is declared here
   rather than inherited from the <p> margin each item happens to contain. */
/* --list-marker, NOT --gutter: --gutter is the PAGE's side inset (:root, above), and its comment
   says it exists as one definition because that inset was once hand-doubled and drifted. Reusing
   the name here would shadow it for everything inside a list — silently, since nothing errors. */
/* A LIST DOES NOT FLOW AROUND A FLOAT. Prose wrapping a floated figure is the point of the float;
   a list wrapping one is not — a `- ` set gives up its bullet and its indent so the aligned column
   of terms IS the structure, and a float puts the first term in a different column from the rest.
   Caught the first time a real essay put a list under a side figure: item one sat 420px right of
   items two through five. `clear` on both sides, because a right float leaves the left edge intact
   but shortens the first lines, which ruins the same scan. */
.essay ol, .essay ul{list-style:none; margin:0 0 1.1em; padding:0; clear:both;}
/* A LIST IN READING PROSE IS PROSE, and sets at the prose size whichever way it was written.
   Markdown emits two shapes: items on consecutive lines leave bare text in the <li>, items with a
   blank line between them wrap each in a <p>. Only `.essay p` carried the 22px, so the first shape
   fell through to the site's 19px base — the same convention rendering 14% smaller because of how
   many times the author pressed Return. Nobody chose 19; it is what inheritance handed it. Sizing
   the <li> makes the two shapes identical to the pixel, and the blank lines go back to being an
   authoring convenience the reader cannot detect.
   It also protects the numeral: `.essay ol > li::before` is 20px to match the cap height of 22px
   text beside it, and at 19px that alignment was quietly wrong. */
.essay li{font-size:22px; line-height:1.68;}
/* Margin zeroed so the <p> markdown puts inside a loose item cannot fight the item spacing
   declared below — but only on the FIRST paragraph. An item that genuinely runs to two paragraphs
   welded them together at 0px, tighter INSIDE the item than between items, which reads backwards. */
.essay li > p{margin:0;}
.essay li > p + p{margin-top:.7em;}
/* THE TWO KINDS SPACE DIFFERENTLY, because they are read differently.
   A SEQUENCE is a run of full sentences you read one after another, and each wants the air a
   paragraph gets. A SET is scanned, not read through: the items cohere as one block, and its own
   term is what you are looking for. Item spacing that matches the paragraph margin renders a set
   as three paragraphs — which is exactly what shipped first time round, at 1.15em against `.essay
   p`'s 1.1em, a difference of 0.05em that no one can see. */
.essay ol > li{margin-bottom:1.15em;}
.essay ul > li{margin-bottom:.45em;}
/* AN INTRODUCED LIST BELONGS TO ITS INTRODUCTION. Markdown demands a blank line after the colon
   before it will start a list at all — that is its syntax and the author cannot avoid it — but a
   full paragraph gap then sets the sentence as far from its own list as from an unrelated one.
   Halve it, so "…has three components:" and the three components read as one block. Selecting the
   PREVIOUS sibling needs :has(); a browser without it simply keeps the old 1.1em, which is the
   current design rather than a broken one. */
.essay p:has(+ ol), .essay p:has(+ ul){margin-bottom:.5em;}
.essay ol{counter-reset:item; --list-marker:2.4rem;}
.essay ol > li{display:grid; grid-template-columns:var(--list-marker) 1fr; align-items:baseline;
  margin-left:calc(-1 * var(--list-marker)); counter-increment:item;}
/* 20px matches the cap height of the body text beside it. It would want ~17px against a small-caps
   lead-in, which tops out far lower — the numeral has to match whatever the first line starts with. */
.essay ol > li::before{content:counter(item); justify-self:end; padding-right:.75em;
  font-family:var(--serif); font-weight:500; font-size:20px; color:var(--muted);
  font-variant-numeric:tabular-nums;}
/* 760px, the LAYOUT breakpoint where .wrap drops the page gutter to 22px — not the 767px
   responsive-image breakpoint. This rule hangs markers into that gutter, so it must flip with it;
   at 767 there was a 761-767 band with a 52px gutter and nothing hanging into it. */
@media (max-width:760px){
  /* A FIXED marker column, not auto: auto sizes to each numeral, so item 1 and item 24 started
     their text at different x and a 24-item list showed a ragged left edge (caught by lists.cjs).
     Re-point the variable rather than restating the columns, so the width has one spelling. */
  .essay ol > li{--list-marker:1.8rem; margin-left:0;}
  /* Left-aligned here, not right: with no gutter the numerals ARE the block's left edge, and
     anchoring them to the prose margin stops the list floating inset on the narrowest screen. */
  .essay ol > li::before{justify-self:start; padding-right:.5em;}
}

/* NUMBERED STEPS — opt in per essay (`numberedSections` in frontmatter), and only where the order
   carries meaning. The number is GENERATED here, never typed into the heading. Same face and size
   as the heading, muted: a step number is part of the TITLE — you would say it aloud — unlike a
   list numeral, which is a reference label and belongs out in the margin. The nbsp keeps the number
   married to the first word if the heading wraps.

   It numbers `h3` and never `h2`, which is what makes the switch usable: per-ESSAY numbering was
   too coarse the moment a real essay had a numbered RUN inside a larger structure — five scenarios,
   then a section on the downsides that is a peer of the whole run rather than a sixth scenario. An
   essay can now opt in and still carry unnumbered sections. Both directions are asserted in
   tests/lists.cjs: an opted-in h3 must number, and an h2 must not, even there. */
/* Each SECTION owns the numbering, and starts it over. Without that the counter ran the length of
   the essay, so the first step under a second `h2` came out as 4 — reading as a fourth member of
   the run above it rather than the first of its own. Caught by rendering the movements essay's real
   structure: five scenarios, then a section on the downsides carrying steps of its own.
   The reset lives ONLY on the section, never also on the article: with both, the article's counter
   wins for the h2's following siblings and the restart silently does nothing (measured — the step
   still read 4). Steps that appear before any section heading are fine, because `counter-increment`
   creates the counter itself when nothing has reset one. */
.essay.numbered h2{counter-reset:section;}
.essay.numbered h3{counter-increment:section;}
.essay.numbered h3::before{content:counter(section) ".\00a0"; color:var(--muted);
  font-variant-numeric:tabular-nums;}

/* THE LEAD-IN. Emphasis on this site is not weight: both @font-face blocks cap the axis at 400-500,
   so `strong` at 600/700/800 all render identically to 500 — a 2.8% width step from body text,
   invisible (measured). Rather than import a bold and with it a memo's voice, `strong` marks the
   opening term of a list item in small capitals: they sit at lowercase height so the line's rhythm
   is undisturbed, and they stay calm at nine words where full capitals shout. Weight 500 is the
   axis ceiling, and buys back the stroke the browser loses synthesising small caps from scaled
   capitals — the subset carries no true smcp feature. */
.essay strong{font-weight:500; font-variant-caps:small-caps; text-transform:lowercase;
  letter-spacing:.05em; font-size:1.06em;}
/* Drop caps are OPT-IN (`cap`), granted at build only when the paragraph can
   carry one: enough text to wrap the 3-line initial, and no all-stem I/J
   opener (src/lib/dropcap.js). On About the cap sits on the practice
   paragraph — where the reading starts — never on the standfirst lead. */
@supports (initial-letter:3) or (-webkit-initial-letter:3){
.essay p.lead.cap::first-letter, .text-page.cap .standfirst + p::first-letter, .land-text.cap .standfirst + p::first-letter, .about-body.cap > h2:first-child + p::first-letter, .about-body.cap > p:first-child::first-letter{-webkit-initial-letter:3; initial-letter:3; font-family:var(--serif); font-weight:500; margin-right:0.14em; color:var(--ink);}
}  /* @supports: browsers without initial-letter (Firefox) get NO cap styling, not a half-styled letter (closing review) */
.pull, .essay p.pull{font-family:var(--serif); font-style:italic; font-size:25px; line-height:1.4;  /* the .essay p selector out-specified .pull — pulls rendered at body size (review) */
  border-top:1px solid var(--line); border-bottom:1px solid var(--line); padding:24px 0; margin:34px 0; text-align:center;}
.essay .essay-note{font-style:italic; color:var(--muted); font-size:17px; margin-top:34px;}  /* essay end-note (provenance) */
.essay .contact{margin-top:30px;}  /* About: space above the get-in-touch line */

.field-kicker{display:flex; justify-content:space-between; font-family:var(--sans); font-size:12px;
  letter-spacing:.1em; text-transform:uppercase; color:var(--muted); margin:40px 0 16px;}
.field-kicker a{color:var(--muted); text-decoration:none;}  /* Days entry back-link */
.spread{display:grid; grid-template-columns:54% 1fr; grid-template-rows:auto 1fr; grid-template-areas:"img text" "alt text"; align-items:start; border:1px solid var(--line); background:var(--paper);}  /* rows auto 1fr: the alternative sits under the plate, not text-length-dependent (review) */
.spread .img-page{grid-area:img; padding:38px; text-align:center;}
.spread .img-page img{display:block; width:100%; height:auto;}
.spread .text-page{grid-area:text; grid-row:1 / -1; align-self:stretch; padding:38px 40px; border-left:1px solid var(--line);}  /* span + stretch: the gutter hairline runs the full spread (review) */
.spread h1{font-family:var(--serif); font-weight:500; font-size:29px; line-height:1.15; margin:0 0 6px;}  /* heading leading, not the inherited 1.6 (review) */
.spread .field-meta{font-family:var(--sans); font-size:14px; letter-spacing:.05em; color:var(--muted); margin-bottom:22px;}  /* dates are content here (review) */
.spread p{font-size:22px; line-height:1.66; margin:0 0 1em;}
.img-page .caption{font-family:var(--sans); font-size:12px; letter-spacing:.04em; color:var(--muted); margin-top:11px;}
.spread .alt{grid-area:alt; padding:0 38px 36px; text-align:center;}
.spread .alt .lab{font-family:var(--sans); font-size:11px; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); margin-bottom:9px;}
.spread .alt img{width:min(100%,360px); height:auto; margin-left:auto; margin-right:auto; box-sizing:border-box; --ms:calc(16px * var(--mat-scale)); border-style:solid; border-color:var(--mat); border-width:var(--ms) var(--ms) calc(var(--ms) * var(--mat-foot)); outline:1px solid var(--key);}
.spread .alt .note{font-family:var(--text); font-style:italic; font-size:15px; color:var(--muted); line-height:1.45; margin:9px auto 0; max-width:34ch;}
.tech{font-family:var(--sans); font-size:12px; color:var(--muted); margin-top:16px; letter-spacing:.02em;}
.field-nav{display:flex; justify-content:space-between; font-family:var(--sans); font-size:13px; color:var(--muted); margin-top:18px;}

@media (max-width:760px){
  .wrap{--gutter:22px; padding:22px var(--gutter) 44px;}
  header{align-items:center; position:sticky; top:0; z-index:40; background:var(--paper);}
  nav{display:none;}
  .menu-btn{display:flex;}
  .doors{grid-template-columns:1fr; gap:30px;}
  .page-head h1, .essay h1{font-size:30px;}
}

.work-intro{text-align:center; max-width:34rem; margin:44px auto 38px;}
.work-intro .kicker{display:block; margin-bottom:14px;}
.work-intro p{color:var(--muted); font-size:22px; line-height:1.5; margin:0;}  /* 22px reading floor (review) */
/* The Days feed link — deliberately QUIET (footer-note register, not a call to
   action): the audience that wants it knows the word, everyone else reads past. */
.feed-link{display:inline-block; margin-top:6px; padding:6px 6px; font-family:var(--sans); font-size:13px; color:var(--muted); letter-spacing:.02em;}  /* padded hit area (review) */
.work-index{display:grid; grid-template-columns:repeat(2,1fr); gap:40px 30px; max-width:1050px; margin-left:auto; margin-right:auto;}
.folio{text-decoration:none; color:var(--ink); display:block;}
.folio .pic{aspect-ratio:3/4; overflow:hidden; background:var(--tile); border:1px solid var(--key-soft);}
.folio .pic img{width:100%; height:100%; object-fit:cover; transition:transform 1.6s ease;}
.folio:hover .pic img{transform:scale(1.04);}
.folio .name{font-family:var(--serif); font-size:24px; font-weight:500; margin:16px 0 3px;}
.folio .desc{font-family:var(--sans); font-size:13px; color:var(--muted); line-height:1.5;}
.crumb{font-family:var(--sans); font-size:15px; color:var(--muted); text-decoration:none; display:inline-block; padding:8px 6px; margin:-2px -6px 0;}  /* 15px + padded hit area (review) */
@media (max-width:760px){ .work-index{grid-template-columns:1fr; gap:34px;} }

.spread .standfirst{font-family:var(--text); font-style:italic; font-size:22px; line-height:1.45; color:var(--ink); margin:0 0 20px; max-width:32ch;}

.field-archive{margin-top:6px; max-width:1050px; margin-left:auto; margin-right:auto;}  /* the ledger gets a measure — matches .work-index (review) */
.year{font-family:var(--serif); font-weight:500; font-size:15px; letter-spacing:.03em; color:var(--muted); border-bottom:1px solid var(--line); padding-bottom:8px; margin:42px 0 4px;}
.entry{display:flex; gap:22px; align-items:flex-start; text-decoration:none; color:var(--ink); padding:22px 2px; border-bottom:1px solid var(--line);}
.entry .ethumb{flex:0 0 88px;}  /* DESIGN RULE (Jon, 2026-08-15): the archive is a LEDGER — index thumbs are cues, the ENTRY is where the picture leads. Keep list-scale; imagine 250 entries. */
.entry .ethumb img{width:88px; height:110px; object-fit:cover; display:block;}
.entry .ebody{flex:1;}
.entry .etitle{font-family:var(--serif); font-size:21px; font-weight:500; margin:0 0 5px;}
.entry .etake{font-family:var(--text); font-style:italic; font-size:19px; color:var(--ink); line-height:1.45; margin:0 0 9px;}  /* the choosing line, up from 17 (review) — ledger stays compact */
.entry .emeta{font-family:var(--sans); font-size:13px; letter-spacing:.05em; color:var(--muted); text-transform:uppercase;}  /* dates are content here (review) */
.entry .eno{flex:0 0 auto; font-family:var(--sans); font-size:12px; color:var(--muted); letter-spacing:.05em; padding-top:3px;}
.entry:hover .etitle{text-decoration:underline; text-underline-offset:3px;}
@media (max-width:760px){ .entry .ethumb{flex:0 0 66px;} .entry .ethumb img{width:66px; height:84px;} .entry .eno{display:none;} }

.prev-link{display:inline-block; font-family:var(--sans); font-size:15px; color:var(--muted); text-decoration:none; padding:8px 6px; margin:34px -6px 0;}  /* 15px + padded hit area (review) */
.next-entry{display:flex; align-items:center; gap:30px; text-decoration:none; color:var(--ink); background:var(--card); padding:26px 30px; margin-top:18px; transition:background .3s;}
.next-entry:hover{background:var(--card-hover);}
.next-entry .nthumb{flex:0 0 190px;}
.next-entry .nthumb img{width:190px; height:138px; object-fit:cover; display:block;}
.next-entry .nbody{flex:1; min-width:0;}
.next-entry .nkick{font-family:var(--sans); font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); margin-bottom:8px;}
.next-entry .ntitle{font-family:var(--serif); font-size:29px; font-weight:500; line-height:1.12; margin:0 0 6px;}
.next-entry .ntake{font-family:var(--text); font-style:italic; font-size:18px; color:var(--muted); line-height:1.45;}
.next-entry .narrow{flex:0 0 auto; font-family:var(--serif); font-size:40px; color:var(--muted); transition:transform .3s, color .3s;}
.next-entry:hover .ntitle{text-decoration:underline; text-underline-offset:3px;}
.next-entry:hover .narrow{transform:translateX(6px); color:var(--ink);}
@media (max-width:640px){ .next-entry{gap:18px; padding:20px;} .next-entry .nthumb{flex:0 0 110px;} .next-entry .nthumb img{width:110px; height:84px;} .next-entry .ntitle{font-size:23px;} .next-entry .ntake{font-size:16px;} .next-entry .narrow{display:none;} }

.land-hero img{width:100%; display:block;}
.land-hero .caption{font-family:var(--sans); font-size:12px; letter-spacing:.04em; color:var(--muted); margin-top:11px; text-align:center;}
.land-body{max-width:40rem; margin:42px auto 0;}
.land-text{flex:1; min-width:0;}
.land-text h1{font-family:var(--serif); font-weight:500; font-size:29px; line-height:1.15; margin:0 0 6px;}  /* heading leading, not the inherited 1.6 (review) */
.land-text .field-meta{font-family:var(--sans); font-size:14px; letter-spacing:.05em; color:var(--muted); margin-bottom:18px;}  /* dates are content here (review) */
.land-text .standfirst{font-family:var(--text); font-style:italic; font-size:22px; line-height:1.45; color:var(--ink); margin:0 0 20px; max-width:34ch;}
.land-text p{font-size:22px; line-height:1.66; margin:0 0 1em;}
.land-aside{margin-top:42px; text-align:center;}
.land-aside .lab{font-family:var(--sans); font-size:11px; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); margin-bottom:9px;}
.land-aside img{width:min(100%,360px); height:auto; margin-left:auto; margin-right:auto; display:block; box-sizing:border-box; --ms:calc(16px * var(--mat-scale)); border-style:solid; border-color:var(--mat); border-width:var(--ms) var(--ms) calc(var(--ms) * var(--mat-foot)); outline:1px solid var(--key);}
.land-aside .note{font-family:var(--text); font-style:italic; font-size:15px; color:var(--muted); line-height:1.45; margin:9px auto 0; max-width:34ch;}
@media (max-width:880px){
  .spread{grid-template-columns:1fr; grid-template-areas:"img" "text" "alt";}
  .spread .img-page{padding:22px 22px 0;}
  .spread .text-page{grid-row:auto; align-self:auto; padding:24px; border-left:0; border-top:1px solid var(--line);}  /* RESET the wide template's full-height span — it painted the words over the plate when stacked (closing review) */
  .spread .alt{padding:24px; border-top:1px solid var(--line);}
}

.portfolio-seq{margin-top:6px;}
.portfolio-seq img{cursor:zoom-in;}

.seed{display:flex; justify-content:center; margin:0 0 44px;}
.seed img{max-width:560px; max-height:730px; width:auto; height:auto; display:block; box-sizing:border-box; --ms:calc(26px * var(--mat-scale)); border-style:solid; border-color:var(--mat); border-width:var(--ms) var(--ms) calc(var(--ms) * var(--mat-foot)); outline:1px solid var(--key);}
.pair{display:flex; gap:clamp(24px,2.8vw,48px); align-items:flex-start; margin:0 0 58px;}
.pair img{min-width:0; width:100%; height:auto; display:block;}
@media (max-width:640px){ .seed img{max-width:100%; --ms:calc(16px * var(--mat-scale));} .pair{flex-direction:column; gap:18px; margin-bottom:30px; align-items:stretch;} }

.gmat{--mx:calc(clamp(15px,1.65vw,23px) * var(--mat-scale)); background:var(--mat); box-sizing:border-box; padding:var(--mx) var(--mx) calc(var(--mx) * var(--mat-foot)); outline:1px solid var(--key); flex-shrink:1; flex-basis:calc(2 * var(--mx)); min-width:0;}
.gmat img{width:100%; display:block;}

/* solo feature — a single image standing alone, large, capped to the viewport */
.feature{display:flex; justify-content:center; margin:0 0 clamp(40px,6vh,84px);}
.feature img{display:block; max-width:100%; max-height:80vh; width:auto; height:auto; box-sizing:border-box; --ms:calc(clamp(24px,2.8vw,46px) * var(--mat-scale)); border-style:solid; border-color:var(--mat); border-width:var(--ms) var(--ms) calc(var(--ms) * var(--mat-foot)); outline:1px solid var(--key);}
/* large plates — tuned per representative width so all land on ~4.5% side / ~6% foot */
.land-hero img{--ms:calc(clamp(28px,3vw,44px) * var(--mat-scale)); border-style:solid; border-color:var(--mat); border-width:var(--ms) var(--ms) calc(var(--ms) * var(--mat-foot)); outline:1px solid var(--key);}
.essay-hero img{--ms:calc(clamp(22px,2.5vw,38px) * var(--mat-scale)); border-style:solid; border-color:var(--mat); border-width:var(--ms) var(--ms) calc(var(--ms) * var(--mat-foot)); outline:1px solid var(--key);}
.spread .img-page img{--ms:calc(clamp(17px,1.75vw,25px) * var(--mat-scale)); border-style:solid; border-color:var(--mat); border-width:var(--ms) var(--ms) calc(var(--ms) * var(--mat-foot)); outline:1px solid var(--key);}
@media (max-width:640px){ .essay-hero img, .spread .img-page img, .land-hero img{--ms:calc(16px * var(--mat-scale));} }

.vp-series{font-family:var(--sans); font-weight:400; font-size:13px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); margin:52px 0 0; padding-bottom:10px; border-bottom:1px solid var(--line);}  /* the kicker voice — series headers must not read as another entry title (review) */
.vp-series .sub{font-family:var(--sans); font-size:12px; letter-spacing:.06em; color:var(--muted); margin-left:12px; text-transform:uppercase;}

.essay.article{max-width:52rem;}
.fig{margin:34px 0; clear:both;}
.fig .matw{display:block; box-sizing:border-box; background:var(--mat); border:1px solid var(--key); --ms:calc(4.5% * var(--mat-scale)); padding:var(--ms) var(--ms) calc(var(--ms) * var(--mat-foot));}
.fig .matw img{width:100%; display:block;}
.fig .cap{font-family:var(--sans); font-size:13px; color:var(--muted); margin-top:9px; letter-spacing:.02em; text-align:center;}
.fig-right{float:right; width:46%; margin:8px 0 22px 38px; clear:none;}
.fig-left{float:left; width:46%; margin:8px 38px 22px 0; clear:none;}
.fig-wide{width:min(66rem,92vw); margin-left:50%; transform:translateX(-50%);}
.fig-full{width:min(86rem,94vw); margin-left:50%; transform:translateX(-50%);}
.fig:not(.fig-left):not(.fig-right):not(.fig-plate) .matw{max-width:min(100%, 105vh); margin-left:auto; margin-right:auto;}

.about-portrait{margin:6px 0 38px; width:min(54rem,92vw); margin-left:50%; transform:translateX(-50%);}
.about-portrait img{display:block; width:100%; height:auto; border:1px solid var(--key-soft);}
/* A HEADING DOES NOT FLOW AROUND A FLOAT — the `.essay ol/ul` tenet one level up. Set beside a
   float, a long heading wraps at the float's edge and finishes at the column's: two left edges in
   one heading, and a generated step numeral starting mid-page (caught live on portfolio-thinking's
   step 2 beside a left figure, Aug 2026). h2 has cleared since #500; h3 arrived with the second
   reading level and missed it. Consequence for placement: a floated figure decorates the text
   BELOW it, so it goes just AFTER the heading of the section it dresses, never just before it. */
.article h2, .article h3{clear:both;}
@media (max-width:640px){
  .fig-right, .fig-left{float:none; width:100%; margin:28px 0;}
  .fig-wide, .fig-full{width:100%; margin:28px 0 34px; transform:none; margin-left:0;}
}

.spread .img-page img, .essay-hero img, .fig-wide img, .land-hero img{max-height:82vh; width:auto; max-width:100%; height:auto; display:block; margin-left:auto; margin-right:auto;}
@media (max-height:900px){ .pair{max-width:1080px; margin-left:auto; margin-right:auto;} }

.fig-plate{clear:both; text-align:center; max-width:min(56vh,540px); margin-left:auto; margin-right:auto;}

/* Chaptered portfolios — a multi-part Work shows ONE chapter at a time; chapters.js pages between
   them (title arrows + a foot "next"). Single-chapter Works never emit `.acts`, so are untouched. */
.act{display:none;}
.act.active{display:block;}
.act-head{text-align:center; max-width:38rem; margin:6px auto 30px;}
.act-of{display:block; font-family:var(--sans); font-size:12px; letter-spacing:.22em; color:var(--muted); margin-bottom:12px;}
.act-title-row{display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:12px;}
.act-title{font-family:var(--serif); font-weight:500; font-size:27px; letter-spacing:.01em; margin:0;}
.act-nav-slot{display:flex; align-items:center;}
.act-title-row > .act-nav-slot:first-child{justify-content:flex-end;}
.act-title-row > .act-nav-slot:last-child{justify-content:flex-start;}
.act-nav{background:none; border:0; cursor:pointer; font:300 34px/1 var(--serif); color:var(--muted); padding:5px 8px; min-width:44px; min-height:44px; transition:color .2s, transform .2s;}  /* 44px hit floor (a11y review) */
.act-nav:hover{color:var(--ink);}
.act-title-row > .act-nav-slot:first-child .act-nav:hover{transform:translateX(-3px);}
.act-title-row > .act-nav-slot:last-child .act-nav:hover{transform:translateX(3px);}
.act-intro{color:var(--muted); font-size:20px; line-height:1.6; margin:16px auto 0; max-width:34rem;}
.act-intro--quote{font-style:italic;}
.act-foot{display:flex; justify-content:center; margin:6px 0;}
.act-foot-next{display:inline-flex; align-items:baseline; gap:12px; background:none; border:0; border-top:1px solid var(--line); cursor:pointer; color:var(--ink); padding:22px 16px 0; transition:color .2s;}
.act-foot-next .afn-lab{font-family:var(--sans); font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted);}
.act-foot-next .afn-title{font-family:var(--serif); font-size:22px; font-weight:500;}
.act-foot-next .afn-arrow{font-family:var(--serif); font-size:26px; color:var(--muted); transition:transform .2s, color .2s;}
.act-foot-next:hover .afn-arrow{transform:translateX(4px); color:var(--ink);}
@media (max-width:640px){ .act-title{font-size:23px;} .act-nav{font-size:30px;} }

@media (prefers-reduced-motion: reduce){
  *{transition:none !important; animation:none !important;}
  .door:hover .pic img, .folio:hover .pic img{transform:none;}
}




