/* U.N.N.A.M.E.D. exported 23 August 2026 */
/* ============================================================
   U.N.N.A.M.E.D. — THE SHARED SURFACE
   unnamed.css · extracted 10 August 2026

   Every page on this site wears the same chrome: the tokens, the type,
   the header, the footer, and the three floating objects (the colour
   picker, the asterisk, Ask). Until today that chrome existed TWICE,
   once inside index.html and once inside future.html, and the pages had
   already drifted — the header clock's reel sat at 1.05em on one page
   and 1.34em on the other, which is how this file came to exist.

   THE RULE FROM HERE: if a thing appears on more than one page, it lives
   in this file and nowhere else. A page-specific value is expressed as a
   TOKEN this file declares and the page overrides, never as a second
   copy of the rule.

   WHAT STAYS IN A PAGE: its own layout, its own figures, and its own
   token overrides. future.html keeps --beat, --cov and the mosaic
   tokens; index.html keeps its grid and its hero.

   WHERE THE TWO PAGES DISAGREED, future.html won (at Manny's direction:
   "I like where we netted out on the future page"). The four decisions:
     · the clock reel is 1.34em, with a matching line-height, so a digit
       sits optically level with the wordmark beside it
     · the time takes tabular numerals, so the seconds do not shuffle
       the line as they count
     · the Ask pill's size is a TOKEN (--ask-w / --ask-h) because the two
       pages genuinely wanted different sizes — index runs it large, the
       thematic page runs it small. One rule, two values.
     · focus and hover selectors are the union of both pages, so a
       control that exists on either page is covered here.
   ============================================================ */

/* ---------- the tokens ----------
   Two colours at a time, plus what the page derives from them. Anything
   a page needs on top of these it declares itself. */
:root {
  --bg:#FFFFFF; --fg:#000000;
  --swap:.45s ease;             /* one duration for every colour change */
  --bar:60px;                   /* the header's real height, set live by JS */
  --reel:1.34em;                /* the clock's rolling line box */
  --gut:clamp(16px, 1.4vw, 60px);
  --col:calc(var(--gut) * 2.5);
  /* THE FLOATING SET IS ONE OBJECT, ALIGNED TO THE PAGE (at Manny's
     direction, 13 Aug — "left align the two floating functions on the
     left to the U.N.N.A.M.E.D. wordmark... and ensure those two are also
     aligned to the middle with the Ask button"). Two tokens carry the
     whole law: --float is the control's size, --fgap the picker-to-
     asterisk PITCH (centre to centre). Both left edges land on --gut,
     the header's own padding, so the pair shares the wordmark's edge;
     and the pair STRADDLES the Ask pill's horizon, half a pitch each
     side, so the three objects read as one composition. Change the
     pitch here and the pair re-centres itself — never offset one
     control against the other by hand. */
  --float:clamp(48px, 3.9vw, 56px);
  --fgap:clamp(72px, 6vw, 86px);
  /* THE ASK PILL IS SIZED BY TOKEN, not by page. index.html wants it
     large and the thematic pages want it small; the rule below is one
     rule either way. Override these two in the page, never #ask-bug. */
  --ask-w:clamp(96px, 7.8vw, 112px);
  --ask-h:clamp(180px, 15vw, 216px);
  /* ---------- THE TOUCH AND READING TOKENS ----------
     Both pages size their phone and tablet behaviour from these three, so a
     decision made once holds on every page (Manny, 10 Aug: "ensure they share
     the same tokens").
       --tap     the fingertip minimum. 44px is the platform guidance on both
                 iOS and Android and it is not a style choice.
       --hit     the height of a link's INVISIBLE hit area. It is 34 and not 44
                 on purpose: this site's links are one line of 19-23px type,
                 often stacked at a 36px pitch, and an area taller than the
                 pitch overlaps its neighbour — a bigger target that hits the
                 wrong link is worse than a small one.
       --measure the longest comfortable line. Capped in the tablet band, where
                 a single column across 800px otherwise runs to 75 characters
                 and the eye loses its place on the return sweep. 56ch and not
                 62: `ch` is the width of a zero, which in Helvetica Bold is
                 wider than the average lowercase letter, so 62ch measured 69
                 actual characters. Set from what was counted, not from the
                 unit's promise, and 52 after counting again on the page. */
  --tap:44px;
  --hit:34px;
  --measure:52ch;
}

/* ---------- HOW THE TYPE SETS ITSELF ----------
   `pretty` asks the browser to avoid the widow it would otherwise leave — the
   last line of a paragraph carrying one short word — by adjusting the lines
   above it. `balance` evens the lines of a short display block instead, which
   is what a two or three line heading wants and what a paragraph does not.
   Neither is supported everywhere yet and neither is load-bearing: where they
   are ignored the type sets as it always did, and `knitWidows` in unnamed.js
   binds the last two words of every paragraph as a backstop. Belt and braces,
   because a widow on a 350px column is not a subtlety — it is a line with one
   word on it. */
p, q, cite, .say, .d, .pcell p, .f-lead div, blockquote {
  text-wrap:pretty;
}
h1, h2, .pin, .ttl, .t-pin, .plabel, figcaption {
  text-wrap:balance;
}
/* A PHRASE THAT IS ONE THING KEEPS ITS OWN LINE (16 Aug, at Manny's direction -
   "on the home page move systems to the previous line, Gen AI systems").

   IT HAS TO BE AN ELEMENT, not a non-breaking space. `knitWidows` rebuilds the
   LAST text node of every paragraph from its stripped text on every run, so an
   interior nbsp written into the markup is wiped the first time the page
   resizes. A span is walked past, survives every run, and says in the markup
   what it means: these words are a term, not three words that happen to be
   adjacent. Same instrument `.cx` already uses for a client's name. */
.nb { white-space:nowrap; }

html {
  font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight:700;
  font-size:clamp(20px, 2.1vw, 30px);   /* 30px at a 1440 window, and 30 is the ceiling */
  line-height:1.05;
  letter-spacing:-0.02em;
  scroll-behavior:smooth;
  /* the same guard on the root: overflow propagates from whichever of html
     or body is not `visible`, and stating it in both places means the page
     does not depend on which one wins. y stays free. */
  overflow-x:hidden;
  overflow-x:clip;
  /* the pacing. modules are the snap areas, and a module is taller than
     the glass, so the scroll runs free inside one and catches at the seam.
     the 16:9 panels carry their own stop, which is what makes the catch
     land every two panels rather than once per chapter. */
  /* ---------- PROXIMITY, NOT MANDATORY ----------
     (16 Aug, at Manny's report - "on Position with the paragraph, if I flick
     swiftly upward it takes me REALLY high up the page, it doesn't feel
     natural".)

     MANDATORY MEANS THE SCROLL IS NOT ALLOWED TO REST BETWEEN TWO POINTS. Every
     module is a snap area aligned to its START, and a chapter is around 6700px
     tall, so from the middle of one the nearest point going UP is that
     chapter's own crown - thousands of pixels away. The browser is not
     overshooting when it flies there. It is obeying: it has to land on a point,
     that is the point, and `scroll-behavior: smooth` animates the whole trip.
     The taller the section, the further the throw, which is why it reads as a
     bug in the chapters and nowhere else.

     MEASURED before the change, headless and with no trackpad inertia at all:
     a 900px upward wheel from mid-chapter travelled 1239. The snap took 339 of
     that on a gesture with no momentum behind it. A real flick carries
     thousands.

     PROXIMITY KEEPS THE CATCH AND DROPS THE COMPULSION. Release near a seam and
     it still lands on the seam, which is the whole pacing figure and it is
     unchanged. Release in the middle of a chapter and the page simply stays
     where it was put. Nothing loses its snap point; the scroll just stops being
     obliged to reach one.

     THIS IS NOT THE CHANGE THAT WAS REVERTED. That one took `scroll-snap-align`
     off the chapters, which deleted their catch entirely and made the pages feel
     loose. Every align and every stop is untouched here. */
  scroll-snap-type:y proximity;
  -webkit-font-smoothing:antialiased;
}
/* ON A TOUCH SCREEN THE CATCH IS SCRIPTED, NOT SNAPPED (7 Oct, at Kir's
   direction: "keep Manny's idea, just make the scroll smooth"). Any snap area
   puts an iPhone's scroller on UIKit's fast deceleration, so a flick on a case
   page coasted a fifth of one on the index, and `scroll-snap-stop` stopped
   half the throws dead on a seam. Here the throw is free and the same seams
   draw the page in once it has come to rest near one: shared/unnamed.js, ON A
   TOUCH SCREEN THE PAGE LANDS ON A CHAPTER. Every align and stop stays on the
   chapters, for the mouse and the trackpad. */
@media (hover:none) and (pointer:coarse) {
  html { scroll-snap-type:none; }
}


body {
  background:var(--bg); color:var(--fg);
  -webkit-tap-highlight-color:transparent;
  /* ---------- THE PAGE NEVER SCROLLS SIDEWAYS ----------
     (18 Aug, at Manny's report: "can you please ensure there isn't any weird
     horizontal scrolls".) MEASURED across eight pages and five widths: the
     document's scrollWidth ran past its clientWidth on glome at 390, on
     nike-diamond at 390 and 768, and the culprit was one element every time.

     `.m-clu .card .face` carries a 108px transparent border as halo room for
     the deal blur (see the essay on it in each project page). That border is
     LAYOUT, not paint, so a card cast flush to the right margin — `.cc { right:0 }`,
     and `.v2 .cb { right:0 }` — puts a 108px transparent box past the page edge.
     The picture never gets there: `background-clip:padding-box` keeps it inside.
     Only the empty border does, and only sometimes, because the cards drift.

     `overflow-x:hidden` here was already the intended guard and it was not
     enough. A hidden overflow still makes a SCROLL CONTAINER: the box cannot be
     dragged by the user in most engines, but scrollWidth still exceeds
     clientWidth, script and focus can still move it, and iOS will still
     rubber-band it. `clip` makes no scroll container at all, so there is
     nothing to scroll by any means. `hidden` stays first as the fallback for
     engines that predate `clip` (Safari under 16); they ignore the second
     declaration and keep the old behaviour, which is what they have today.

     THE HALO IS NOT TOUCHED. It was chosen to be immune to a compositor fault
     that cannot be reproduced in this repo's harness, and it is measured at
     three times the deal blur's real reach. The overflow it creates is
     transparent, so clipping it costs nothing that is visible. */
  overflow-x:hidden;
  overflow-x:clip;
}

body, header, #contact { transition:background-color var(--swap), color var(--swap); }

::selection { background:var(--fg); color:var(--bg); }

a { color:inherit; text-decoration:none; }

a:hover, a:focus-visible {
  background:none; color:inherit; outline:none;
  text-decoration:underline;
  text-decoration-thickness:.07em;
  text-underline-offset:.14em;
}

#mark:hover, #mark:focus-visible { text-decoration:none; }

header {
  position:fixed; left:0; right:0; top:0; z-index:10;
  background:color-mix(in srgb, var(--bg) 28%, transparent);
  backdrop-filter:blur(20px) saturate(1.2);
  -webkit-backdrop-filter:blur(20px) saturate(1.2);
  padding:calc(var(--gut) * .7) var(--gut);
  display:flex; justify-content:space-between; align-items:center;
  flex-wrap:wrap; gap:0 32px;
}

#mark { display:inline-block; }

#mark:hover { background:none; color:var(--fg); }

.hright { display:flex; gap:clamp(14px, 1.6vw, 44px); align-items:center; min-width:0; }

#navword { flex:none; }

#cityclock .cw { display:inline-block; height:var(--reel); overflow:hidden; text-align:center; }

#cityclock .reel { display:block; transform:translateY(0); transition:transform .5s cubic-bezier(.22,1,.36,1); }

#cityclock .rl { display:block; height:var(--reel); line-height:var(--reel); }

#cityclock { display:flex; gap:.5em; justify-content:flex-end; }

#cc-city { display:inline-flex; justify-content:flex-end; }

#cc-time { display:inline-flex; font-variant-numeric:tabular-nums; }

#navword { overflow:hidden; line-height:1.34; }

#navword a { display:block; transform:translateY(0); }

#navword a.out { transition:none; transform:translateY(115%); }

#navword a.in { transition:transform .5s cubic-bezier(.19,1,.22,1); transform:translateY(0); }

.scw { display:inline-block; white-space:nowrap; }

.sc  { display:inline-block; text-align:center; }

.rv { opacity:0; overflow:visible; transition:opacity .8s ease; }

.rv.on { opacity:1; }

.rvb { opacity:0; filter:blur(16px); transform:translateY(14px);
       transition:opacity .7s ease, filter .9s cubic-bezier(.2,.7,.3,1), transform .8s cubic-bezier(.2,.7,.3,1); }

.rvb.on { opacity:1; filter:blur(0); transform:none; }

#ask-bug:hover, #ask-bug:focus-visible,
#bug-core:hover, #bug-core:focus-visible,
#idx-bug:hover, #idx-bug:focus-visible,
#bug-ring .tick:hover, #bug-ring .tick:focus-visible { text-decoration:none; }

#bug-core { transition:background-color var(--swap), color var(--swap), transform .3s cubic-bezier(.22,1,.36,1); }

#bug-core::after { transition:background-color var(--swap), inset .3s cubic-bezier(.22,1,.36,1); }

#bug-ring { transition:background-color var(--swap), color var(--swap); }

#idx-bug, #idx-bug:hover, #idx-bug:focus, #idx-bug:focus-visible,
#idx-note, #idx-note:hover { text-decoration:none; }

#idx-note {
  flex:0 0 auto;
  white-space:nowrap;          /* one line. the pill only ever grows sideways */
  opacity:0; padding-right:1.1em;
  transition:opacity .35s ease;
}

#ask {
  position:fixed; inset:0; z-index:40;
  background:color-mix(in srgb, var(--bg) 22%, transparent);
  backdrop-filter:blur(16px) saturate(1.25);
  -webkit-backdrop-filter:blur(16px) saturate(1.25);
  display:flex; align-items:flex-start; justify-content:center;
  animation:haze .35s ease;
}

#ask[hidden] { display:none; }

body.asking #ask-bug { opacity:0; pointer-events:none; transform:scale(.6); }

#ask-close {
  position:fixed; z-index:2;
  top:14px; right:20px;
  color:var(--fg);
  text-underline-offset:.14em;
}

#ask-close:hover, #ask-close:focus-visible {
  background:none; color:var(--fg);
  text-decoration:underline; text-decoration-thickness:.07em;
}

#ask-sheet {
  width:100%; max-width:1100px;
  padding:22vh 20px 20px;
  display:flex; flex-direction:column; gap:28px;
  max-height:100svh;
}

#ask-sheet > * { pointer-events:auto; }

#ask-input {
  width:100%;
  background:none; color:var(--fg);
  caret-color:var(--fg);
  outline:none;
}

#ask-input::placeholder { color:var(--fg); opacity:1; }

#ask-out { overflow-y:auto; white-space:pre-wrap; }

#ask-out:empty { display:none; }

#ask-bug {
  position:fixed; z-index:31;
  left:auto; right:clamp(20px, 2.2vw, 64px); top:calc(50% + var(--bar) / 2);
  /* fixed standard size at Manny's direction — the pill does not grow with
     the window; the clamp floor only lets it shrink on small screens */
  /* SIZED BY TOKEN so one rule serves both pages (index runs the pill
     large, the thematic pages small) — override --ask-w/--ask-h in the
     page, never this rule. */
  width:var(--ask-w);
  height:var(--ask-h);
  margin:calc(var(--ask-h) / -2) 0 0 0;   /* stays on the horizon */
  border-radius:9999px;
  display:grid; place-items:center;
  color:var(--fg);                      /* the theme ink */
  background:color-mix(in srgb, var(--bg) 46%, transparent);
  backdrop-filter:blur(24px) saturate(1.7);
  -webkit-backdrop-filter:blur(24px) saturate(1.7);
  cursor:grab;
  touch-action:none;
  transition:transform .35s cubic-bezier(.22,1,.36,1),
             backdrop-filter .35s ease,
             background-color var(--swap), color var(--swap),
             opacity .35s ease;
  animation:arrive-pill 1.1s cubic-bezier(.19,1,.22,1) backwards, lens 9s ease-in-out 1.1s infinite;
}

#ask-bug:hover, #ask-bug:focus-visible {
  background:color-mix(in srgb, var(--bg) 46%, transparent);
  color:var(--fg);
  backdrop-filter:blur(34px) saturate(2);
  -webkit-backdrop-filter:blur(34px) saturate(2);
}

#ask-bug.drag { cursor:grabbing; animation:none; transform:scale(.95); }

body.at-contact #ask-bug {
  opacity:0; pointer-events:none;
  transition:opacity .3s ease;
}

body.at-contact #bug-ring {
  background:color-mix(in srgb, color-mix(in srgb, var(--fg) 20%, var(--bg)) 30%, transparent);
}

#bug {
  position:fixed; z-index:30;
  /* THE PAIR STRADDLES THE ASK PILL'S HORIZON. That horizon is half a bar
     below the middle of the viewport (the Ventures dots' own line — a frame
     hanging under the bar), and the picker used to sit ON it with the
     asterisk a whole pitch below, which put the PAIR's centre half a pitch
     low against the pill. It rides half a pitch high now and the asterisk
     half a pitch low, so the group is centred on the same line the pill is.
     Its left edge is the gutter — the header's padding, so the wordmark's
     edge — which is what the +half-a-control does: the box carries a
     centring margin of -size/2, so `left` names the CENTRE. */
  left:calc(var(--gut) + var(--float) / 2);
  top:calc(50% + var(--bar) / 2 - var(--fgap) / 2);
  /* THE PICKER DOES NOT SCALE WITH THE PAGE (at Manny's direction, 11 Aug —
     "across index and future the colour picker shouldn't scale proportionally
     to the page, it doesn't look good"). Same law as the Ask pill: the ceiling
     is the 1440 rendering (3.9vw there = 56px), the floor still lets a small
     glass shrink it. On a 2600 display the wheel was a 380px object with 63px
     swatches — furniture, not a control. */
  width:var(--float);
  height:var(--float);
  margin:calc(var(--float) / -2) 0 0 calc(var(--float) / -2);
  touch-action:none;
  /* backwards, not both. a forwards fill keeps holding opacity:1 after the
     animation ends, and an animated value outranks a normal declaration, so
     the stand-down over the footer could never take. both keyframe sets end
     on the element's own resting state, so nothing is lost by releasing. */
  animation:arrive-bug 1s cubic-bezier(.19,1,.22,1) backwards;
}

#bug-core {
  position:absolute; inset:0;
  width:100%; height:100%;
  border-radius:50%;
  /* the same glass as the ask pill and the mark, so all three read as one set */
  background:color-mix(in srgb, var(--bg) 46%, transparent);
  backdrop-filter:blur(24px) saturate(1.7);
  -webkit-backdrop-filter:blur(24px) saturate(1.7);
  cursor:grab;
  padding:0;
  touch-action:none;          /* or a drag on a phone scrolls the page instead */
  transition:transform .3s cubic-bezier(.22,1,.36,1), background-color var(--swap);
}

#bug-core::after {                     /* the eye: current color */
  content:"";
  position:absolute; inset:32%;
  border-radius:50%;
  /* ink now, not the ground. on a solid body the eye had to be the ground
     colour to show; on glass that would leave nothing to see. */
  background:var(--fg);
  transition:inset .3s cubic-bezier(.22,1,.36,1), background-color var(--swap);
}

#bug-core:hover, #bug-core:focus-visible {
  background:color-mix(in srgb, var(--bg) 46%, transparent); transform:scale(1.08); }

#bug.open #bug-core { transform:scale(.62); }

#bug.open #bug-core::after { inset:22%; }

#bug.drag #bug-core { cursor:grabbing; transform:scale(.9); }

#bug.pulse #bug-core { animation:beat 2.6s ease-in-out infinite; }

#bug-ring {
  position:absolute; left:50%; top:50%;
  width:clamp(180px, 15vw, 216px); height:clamp(180px, 15vw, 216px);
  margin:calc(clamp(180px, 15vw, 216px) / -2) 0 0 calc(clamp(180px, 15vw, 216px) / -2);
  /* the swatches are sized in em, and the inherited size is the page's
     viewport-scaled type — pinned here to the same 1440 ceiling, or the
     dots keep growing after the ring has stopped */
  font-size:clamp(17px, 2.1vw, 30px);
  border-radius:50%;
  backdrop-filter:blur(24px) saturate(1.7);
  -webkit-backdrop-filter:blur(24px) saturate(1.7);
  background:color-mix(in srgb, color-mix(in srgb, var(--bg) 20%, var(--fg)) 30%, transparent);
  animation:bloom .52s cubic-bezier(.34, 1.4, .5, 1);   /* past one, then settles */
  transform-origin:50% 50%;
}

#bug-ring[hidden] { display:none; }

#bug-ring.shut { animation:wilt .26s cubic-bezier(.4, 0, .7, .2) forwards; pointer-events:none; }

#bug-ring .tick { animation:seat .42s cubic-bezier(.34, 1.5, .5, 1) backwards; }

#bug-ring .tick:nth-child(1) { animation-delay:.05s }

#bug-ring .tick:nth-child(2) { animation-delay:.09s }

#bug-ring .tick:nth-child(3) { animation-delay:.13s }

#bug-ring .tick:nth-child(4) { animation-delay:.17s }

#bug-ring .tick:nth-child(5) { animation-delay:.21s }

#bug-ring .tick:nth-child(6) { animation-delay:.25s }

#bug-ring.shut .tick { animation:none; }

#bug-ring .tick {                      /* the six house colors, seated on the ring */
  position:absolute; width:1.15em; height:1.15em;
  border-radius:50%;
  margin:-0.575em 0 0 -0.575em;
  cursor:pointer;
  transition:transform .18s cubic-bezier(.22,1,.36,1);
}

#bug-ring .tick:hover, #bug-ring .tick:focus-visible { transform:scale(1.5); background:none; }

.f-inner { padding:120px var(--gut) 14px; }

.f-lead { display:grid; grid-template-columns:repeat(4, 1fr); gap:0 var(--col); }

.f-lead > div:first-child { grid-column:1 / span 2; }

.f-lead > div:last-child  { grid-column:3 / span 2; }

.f-social { display:flex; flex-direction:column; grid-column:1; }

.f-cities [data-clock] { font-variant-numeric:tabular-nums; }

.f-cities {
  display:grid; grid-template-columns:repeat(4, 1fr);
  gap:32px var(--col);
  margin:120px 0;
}

.f-cities > div:nth-child(odd)  { grid-column:1; }

.f-cities > div:nth-child(even) { grid-column:2; }

.f-base {
  display:grid; grid-template-columns:repeat(4, 1fr);
  gap:10px var(--col);
  align-items:end;
  margin-top:64px;
  padding-bottom:0;
}

.f-base > div:nth-child(2) { grid-column:2 / span 2; }

.f-base > div:last-child { grid-column:4; justify-self:end; text-align:right; }

/* ---------- the same chrome, at other sizes ---------- */

@media (prefers-reduced-motion: reduce) {
  .rvb { opacity:1; filter:none; transform:none; }
}

@media (max-width:600px) {
  #idx-note { padding-right:.7em; }
}

/* ---------- THE PHONE'S OWN EDGES ----------
   (mobile pass, at Manny's direction — "prioritise legibility and nativeness".)
   Both pages ship `viewport-fit=cover`, which is what lets the wash run under the
   notch and into the corners — and which also puts the three floating controls
   inside the home indicator's gesture strip, where a tap is the system's before it
   is ours. Nothing in either page read `env(safe-area-inset-*)`, so this is the
   whole of it: the fixed chrome sits above the indicator and inside the notch, and
   on a device with no insets every `env()` falls back to 0 and the layout is
   exactly what it was. The gutter takes the left and right insets too, which is
   what a phone in landscape needs and what the audit's 844x390 pass could not see
   (the harness reports no insets at all). */
:root {
  --safe-b:env(safe-area-inset-bottom, 0px);
  --safe-l:env(safe-area-inset-left, 0px);
  --safe-r:env(safe-area-inset-right, 0px);
  /* THE FLOOR UNDER SAFARI'S TOOLBAR (6 Oct, iOS 26). --deep is how far down a
     pinned frame reaches, --floor how much of it lies below the glass: the
     type laid over a frame stands that much higher, so the toolbar never sits
     on it, and the frame hands that much back in the flow, so it pins for as
     long as it always did. Off an iPhone a frame keeps the small viewport it
     always had and the floor is nought, so desktop and Android lay out
     exactly as before; on an iPhone both are set in UNDER SAFARI'S BARS,
     ALL OF IT, below. */
  --deep:100svh;
  --floor:0px;
}
header { padding-left:calc(var(--gut) + var(--safe-l)); padding-right:calc(var(--gut) + var(--safe-r)); }
footer .f-inner { padding-left:calc(var(--gut) + var(--safe-l)); padding-right:calc(var(--gut) + var(--safe-r)); }
/* ---------- AND THE PAGE STANDS IN THE SAME COLUMN AS ITS CHROME ----------
   (16 Aug, at Manny's report - "can you align the Overview title to
   U.N.N.A.M.E.D.".)

   THE TWO RULES ABOVE WERE THE ONLY THINGS ON THE SITE THAT KNEW ABOUT THE
   NOTCH. Header and footer inset themselves by the safe area; every page-edge
   rule inside `main` - the chapter label, the grid, the voice, the walls -
   sets `var(--gut)` and nothing else. Where the inset is zero the two agree to
   the pixel, which is every window this repo can open and is why the fault
   read as imaginary from here. Where it is not, the wordmark steps right and
   the whole page stays put.

   MEASURED, by forcing the variable at 1440 wide:

       safe-l 0    wordmark 20.2   Overview 20.2   delta   0
       safe-l 24   wordmark 44.2   Overview 20.2   delta -24
       safe-l 47   wordmark 67.2   Overview 20.2   delta -47

   Exactly the inset, every time, which is the whole of it. A notched display
   in fullscreen reports one; this container reports zero.

   THE INSET GOES ON `main`, ONCE, rather than onto the label that was noticed.
   Every page-edge rule inside keeps `var(--gut)` and keeps its relations, and
   the page as a whole moves into the same column the header and footer already
   stand in. Fixing the label alone would have aligned it to the wordmark and
   broken it against the grid directly underneath it, which is a second fault
   bought with the first. `--gut` is not touched because it is a GAP as well as
   a margin: `.m-row` uses it for both, and an inset folded into it would push
   the columns apart as well as the page in. */
main { padding-left:var(--safe-l); padding-right:var(--safe-r); }

@media (max-width:600px) {
  #bug { left:calc(16px + var(--safe-l)); top:auto; bottom:calc(18px + var(--safe-b)); margin:0; width:44px; height:44px; }
  #bug-core { width:44px; height:44px; }
  #bug-core::after { inset:14px; }
  #bug-ring { width:184px; height:184px; margin:-92px 0 0 -92px; }
  #bug-ring .tick { width:30px; height:30px; margin:-15px 0 0 -15px; }
  #ask-bug {
      right:calc(16px + var(--safe-r)); top:auto; bottom:calc(18px + var(--safe-b)); margin:0;
      width:62px; height:62px; border-radius:50%;
    }
  /* (the asterisk's phone position lives in each page: both declare `#idx-bug`
     outside a media query, and a page rule beats a shared one on source order
     whatever media query it sits in. The insets are tokens, so the decision is
     still made once even where the declaration cannot be.) */
  #ask-bug, #bug-ring, #idx-bug, #bug-core { backdrop-filter:blur(12px) saturate(1.4); -webkit-backdrop-filter:blur(12px) saturate(1.4); }
  #ask-bug:hover, #ask-bug:focus-visible { backdrop-filter:blur(12px) saturate(1.4); -webkit-backdrop-filter:blur(12px) saturate(1.4); }
  #ask { backdrop-filter:blur(10px) saturate(1.2); -webkit-backdrop-filter:blur(10px) saturate(1.2); }
  #ask-sheet { padding:16vh 16px 16px; gap:20px; }
  #ask-close { top:12px; right:16px; }
  #bug-ring .tick, #bug-core, #ask-bug, #idx-bug { min-height:0; }
}

@media (prefers-reduced-motion: reduce) {
  #bug-core, #bug-ring, #bug-ring .tick { animation:none !important; transition:none !important; }
  #ask-bug, #bug { animation:none !important; }
}

/* ---------- THE TABLET CEILING IS 1024, NOT 960 ----------
   (mobile pass, at Manny's direction — "don't forget about tablet too".) Every
   one of these blocks used to stop at 960, which left an iPad Pro in portrait —
   exactly 1024 wide, touch pointer, tablet battery — being served the desktop
   layout and the desktop film budget. The JS switch (`TABLET` in unnamed.js) and
   these queries now agree on one number, so a decision made for tablets actually
   reaches the biggest tablet. */
@media (max-width:1024px) {
  /* THE CITIES STAY IN TWO COLUMNS (at Manny's direction — "I like the footer with
     the cities in two columns from index.html more, adopt that format"). They used
     to collapse to one long stack here, which reads as a list of offices rather
     than as a studio in five places, and it made the footer twice as tall on the
     glass where height costs the most. Three in the first column and two in the
     second, which fills the width at every size from a phone up. index.html had
     worked this way since its own audit; the rule belongs here, where both pages
     get it and the footer is the same object on each. */
  .f-cities { grid-template-columns:1fr 1fr; gap:26px 20px; margin:88px 0; }
  .f-cities > div:nth-child(-n+3) { grid-column:1; }
  .f-cities > div:nth-child(n+4)  { grid-column:2; grid-row:auto; }
  .f-cities > div:nth-child(4) { grid-row:1; }
  .f-cities > div:nth-child(5) { grid-row:2; }
  .f-lead { grid-template-columns:1fr; gap:1.05em 0; }
  .f-lead > div:first-child, .f-lead > div:last-child { grid-column:1; }
}

/* ---------- THE PHONE RULES, WITH ENOUGH SPECIFICITY TO SURVIVE ----------
   These live here so both pages set identically, and the selectors carry a
   deliberate extra step — `html:root` rather than `html`, `body header` rather
   than `header` — for a cascade reason that cost real time to find. A page's own
   <style> loads AFTER this file, so a plain base rule in the page (`html {
   font-size:clamp(...) }`) beats a media-query rule here on source order alone,
   no matter that the query is narrower. Removing the pages' duplicate copies of
   these rules therefore un-set them: index went back to 20px type on a phone and
   its header wrapped onto two lines at 375px wide. One extra step of specificity
   makes the shared rule win, and a page that genuinely wants something else can
   still say so inside its own media query. */
@media (max-width:600px) {
  html:root { font-size:17px; line-height:1.12; }
  :root:root { --beat:64px; }
  body header { padding:12px calc(16px + var(--safe-r)) 12px calc(16px + var(--safe-l)); }
  body #cityclock { display:none; }
  body footer .f-inner { padding:72px calc(16px + var(--safe-r)) calc(12px + var(--safe-b)) calc(16px + var(--safe-l)); }
  footer .f-cities { margin:56px 0; gap:20px 24px; }
  footer .f-base { grid-template-columns:1fr; gap:14px; margin-top:48px; }
  footer .f-base > div:nth-child(2), footer .f-base > div:last-child {
      grid-column:1; justify-self:start; text-align:left;
    }
}

@media (pointer: coarse) {
  /* THE CHAPTER WORD'S TARGET IS ITS OWN BOX, not a pseudo-element: `#navword`
     clips to its line box so the word can roll over on a section change, and
     that clip ate the invisible area every time. Padding the link grows the line
     box and the clip with it, so the roll still reads and the target is real. */
  #navword a { padding-block:7px; }
  /* AND THE BAR DOES NOT GROW FOR IT. The padding above is what makes the
     chapter word tappable; left alone it also made the header 14px taller on a
     phone, which is glass taken from the page to hold a target. The bar gives
     the padding back from its own, so the target grows INSIDE the bar and the
     bar measures what it always did (47px on a phone). */
  body header { padding-block:5px; }
  header #mark, #navword a, .f-lead a, .f-social a { position:relative; }
  header #mark::after, #navword a::after, .f-lead a::after {
      content:""; position:absolute; left:0; right:0; top:50%;
      height:var(--hit); transform:translateY(-50%);
    }
  .f-social { line-height:2.1; }
  .f-social a::after {
      content:""; position:absolute; left:0; right:0; top:50%;
      height:var(--hit); transform:translateY(-50%);
    }
  /* A STANDALONE LINK ANYWHERE ELSE gets the same treatment, once. Both pages
     had grown their own version of this — one page painted hit areas with
     `::after`, the other pushed `min-height:44px` onto every `a` and then had
     to exempt a dozen selectors from it, which is how a 21px inline link ended
     up 44px tall and pushed its own line apart. The rule is here now, it is
     scoped to touch, and it costs the layout nothing because a hit area is not
     a box: it is a pseudo-element centred on the type. */
  a[class*="f-"], #contact a, .d-row a, .pcell a[href],
  [role="button"]:not(button) { position:relative; }
  a[class*="f-"]::after, #contact a::after,
  [role="button"]:not(button)::after {
      content:""; position:absolute; left:0; right:0; top:50%;
      height:var(--hit); transform:translateY(-50%);
    }
}

/* ---------- THE TABLET BAND ----------
   A tablet is the size that gets forgotten: too wide for the phone rules, too
   narrow for the desktop grid. Its failure is always the same one — a single
   column of copy running the full width of the glass. The measure is capped
   here, and only here, so a paragraph keeps a line length the eye can track
   and the page keeps its left edge. */
@media (min-width:601px) and (max-width:1024px) {
  p, .say, q, cite, .pcell p, .f-lead div, blockquote { max-width:var(--measure); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior:auto; scroll-snap-type:none; }
  .rv { opacity:1 !important; transition:none !important; }
  #navword a { transform:none !important; transition:none !important; }
  #cityclock .reel { transition:none !important; transform:none !important; }
}


/* ==========================================================================
   THE LIBRARY IS A ROOM YOU CAN WALK INTO
   (17 Aug, at Manny's direction, in three passes: "a button to trigger a crazy
   cool gallery of ALL the files as one giant grid", then "you lost a lot of the
   attributes of the rest of the website, the presence of the wash, the rounded
   corners... I don't like the idea that I have to click to see the image", then
   "just index the 4x5s and make the interaction a lot more interesting, and have
   the wash have some sort of interplay with the images, for example if I choose
   an image it swaps the background".)

   IT IS ONE SHAPE NOW, AND THAT IS WHAT MAKES IT A ROOM. Mixed spans made a
   mosaic, and a mosaic is a layout. 227 files are 4:5, which is the shape this
   library actually stands in — the walls, the marks, the portrait berths — so a
   wall of nothing but 4:5 reads as a rack of prints rather than a grid of
   assorted boxes, and the only thing left varying is the work.

   THE IRREGULARITY COMES FROM SIZE AND FROM HOLES, not from shape. One tile in
   twelve is dealt at double, and one cell in eighteen is left empty for the wash
   to come through. Both are placed by a hash off the index, so the same wall
   comes back on every open rather than reshuffling under a reader halfway down.

   AND CHOOSING A PICTURE CHANGES THE LIGHT. The site's `#amb` puts a blurred
   picture behind the page; here the chosen frame becomes that picture, on two
   stacked plates that crossfade, the blob field recedes, the room's own ground
   moves to a colour out of the frame, and the rest of the wall dims and desats
   so what is left lit is the thing being looked at. It is the site's own move,
   pointed at whatever the reader picks.
   ========================================================================== */
#room {
  position:fixed; inset:0; z-index:90;
  background:var(--room-ink, #08080a); color:#fff;
  display:flex; flex-direction:column;
  opacity:0; pointer-events:none;
  transition:opacity .42s ease, background 1.1s ease;
}
#room.up { opacity:1; pointer-events:auto; }
#room[hidden] { display:none; }

/* ---------- the wash ----------
   two layers. the blob field is the library's own colours drifting, and it is
   what the room breathes on its own. the ambient plates are the chosen picture,
   blurred the way `#amb` blurs one, and they take over when something is picked. */
#room-wash { position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none;
  animation:roomturn 240s linear infinite; }
@keyframes roomturn { from { filter:hue-rotate(0deg); } to { filter:hue-rotate(360deg); } }
#room-wash i {
  position:absolute; display:block; border-radius:50%;
  width:52vmax; height:52vmax; margin:-26vmax 0 0 -26vmax;
  filter:blur(90px) saturate(1.35);
  opacity:.42; mix-blend-mode:screen;
  will-change:transform, opacity;
  transition:opacity .9s ease;
  animation:roomdrift var(--t, 48s) cubic-bezier(.45,0,.55,1) infinite alternate;
  animation-delay:var(--dl, 0s);
}
@keyframes roomdrift {
  from { transform:translate3d(var(--x0), var(--y0), 0) scale(var(--s0, 1)); }
  to   { transform:translate3d(var(--x1), var(--y1), 0) scale(var(--s1, 1.25)); }
}
#room-amb { position:absolute; inset:0; z-index:1; pointer-events:none; }
#room-amb i {
  position:absolute; inset:-10%; display:block;
  background-size:cover; background-position:center; background-repeat:no-repeat;
  filter:blur(70px) saturate(1.3);
  transform:scale(1.1);
  opacity:0; transition:opacity 1s ease;
  will-change:opacity;
}
#room-amb i.on { opacity:.6; }
/* the field steps back so the chosen picture is the light in the room */
#room.picked #room-wash i { opacity:.1; }

#room-bar {
  position:relative; z-index:5; flex:0 0 auto;
  display:flex; align-items:baseline; gap:18px;
  padding:calc(14px + var(--safe-t, 0px)) calc(var(--gut) + var(--safe-r, 0px)) 12px calc(var(--gut) + var(--safe-l, 0px));
  font-size:11px; letter-spacing:.06em; text-transform:uppercase;
  background:linear-gradient(#00000099, #00000000);
  backdrop-filter:blur(14px) saturate(1.3); -webkit-backdrop-filter:blur(14px) saturate(1.3);
}
#room-bar b { font-weight:600; }
#room-said {
  margin-left:auto; text-transform:none; letter-spacing:.01em; font-size:11px;
  opacity:.62; text-align:right; white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; max-width:44vw;
}
#room-sorts { display:flex; gap:12px; }
#room-sorts button, #room-shut {
  appearance:none; background:none; border:0; padding:0; cursor:pointer;
  color:inherit; font:inherit; letter-spacing:inherit; text-transform:inherit;
  opacity:.42; transition:opacity .2s ease;
}
#room-shut { opacity:.62; }
#room-sorts button:hover, #room-sorts button:focus-visible, #room-shut:hover { opacity:1; outline:none; }
#room-sorts button[aria-pressed="true"] { opacity:1; text-decoration:underline; text-underline-offset:3px; }

/* ---------- the wall ----------
   a fixed square cell, and every tile spans 4 by 5 of them, so the shape is the
   library's shape to the pixel and `cover` has nothing to crop. no `dense` would
   leave whole quarters empty; dense packs it and the holes stay where the hash
   put them. */
#room-grid {
  position:relative; z-index:2;
  flex:1 1 auto; overflow:auto; overscroll-behavior:contain;
  display:grid; gap:var(--rgap, 6px);
  grid-template-columns:repeat(auto-fill, var(--cell, 40px));
  grid-auto-rows:var(--cell, 40px);
  grid-auto-flow:row dense;
  justify-content:center;
  padding:6px calc(var(--gut) + var(--safe-r, 0px)) calc(30px + var(--safe-b, 0px)) calc(var(--gut) + var(--safe-l, 0px));
  align-content:start;
}
.rtile {
  appearance:none; border:0; padding:0; margin:0; cursor:pointer;
  position:relative; display:block; overflow:hidden;
  border-radius:17px;
  background-color:#222; background-size:cover; background-position:center;
  background-repeat:no-repeat;
  grid-column:span var(--cx, 4); grid-row:span var(--cy, 5);
  opacity:0; transform:scale(.9);
  transition:opacity .6s ease var(--d, 0ms), transform .6s cubic-bezier(.2,.8,.2,1) var(--d, 0ms),
             filter .5s ease, box-shadow .4s ease;
}
#room.up .rtile { opacity:1; transform:none; }
/* a picture develops rather than appearing: a background-image cannot be
   transitioned, so the tile is lifted a fraction and comes back down as it lands */
.rtile.lit { transition:opacity .6s ease var(--d, 0ms), transform .5s cubic-bezier(.2,.8,.2,1),
                        filter .5s ease, box-shadow .4s ease;
             /* BACKWARDS, NEVER `both`. A filled-forwards animation keeps its
                last frame applied, and an animation beats a normal declaration
                in the cascade — so `to { filter:none }` locked `filter` on every
                lit tile and the picked state's dimming was silently ignored for
                the whole wall. Backwards holds the from-state before it starts
                and lets go at the end, which is all this needed. */
             animation:roomland .55s cubic-bezier(.2,.8,.2,1) backwards; }
@keyframes roomland {
  from { filter:brightness(1.35) saturate(.6); transform:scale(1.03); }
  to   { filter:none; transform:none; }
}
.rtile > video {
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; border-radius:inherit; pointer-events:none;
}
.rtile:hover, .rtile:focus-visible {
  outline:none; z-index:4;
  transform:scale(1.045);
  box-shadow:0 18px 60px rgba(0,0,0,.55);
}
/* the one that was chosen, and the wall it is standing in front of */
/* THE LIGHTS GO DOWN ON THE REST OF THE WALL. Dimming by opacity alone let the
   room's ground come through every tile, so a wall of light frames went PALE
   instead of dark, which reads as washed out rather than unlit. Brightness is
   the property that means what this needs to mean. */
#room.picked .rtile { filter:brightness(.26) saturate(.45); }
#room.picked .rtile.chosen {
  opacity:1; filter:none; z-index:6;
  transform:scale(1.16);
  box-shadow:0 40px 120px rgba(0,0,0,.66);
}
#room.picked .rtile:hover { filter:brightness(.62) saturate(.8); }
.rgap { grid-column:span var(--cx, 4); grid-row:span var(--cy, 5); pointer-events:none; border-radius:17px; }

@media (max-width:1024px) { #room-grid { --cell:32px; --rgap:5px; } .rtile, .rgap { border-radius:14px; } }
@media (max-width:600px)  { #room-grid { --cell:26px; --rgap:4px; } .rtile, .rgap { border-radius:11px; }
                            #room-said { max-width:36vw; } #room-wash i { opacity:.3; filter:blur(60px) saturate(1.3); } }
@media (prefers-reduced-motion: reduce) {
  #room, .rtile { transition:none !important; }
  .rtile { opacity:1; transform:none; }
  .rtile.lit { animation:none; }
  .rtile:hover, .rtile:focus-visible, #room.picked .rtile.chosen { transform:none; }
  #room-wash, #room-wash i { animation:none; }
}

/* ---------- PICTURES LOAD AS THEY APPROACH (6 Oct) ----------
   Every berth is written with its picture inline, so a case page used to ask
   for all of them the moment it was built: 58 files and 54 MB of stills on
   Nike Diamond before the first scroll. The inline url stays exactly where it
   is, because the page's own code reads it back out of the style (shapeOf,
   the rotators, standIn, srcOf). This rule only withholds the PAINT, and a
   background-image that loses the cascade is never fetched. shared/unnamed.js
   adds `un-seen` to each berth a screen and a half before it reaches the
   glass, which lifts the rule for that one berth. No `un-lazy` on the root
   (no IntersectionObserver) means no rule, and everything loads as before. */
html.un-lazy :is(.box, .face, .mtile, .pic)[style*="background-image"]:not(.un-seen) {
  background-image:none !important;
}

/* ---------- SQUIRCLE CORNERS, NOT ON PAPER ----------
   The clips (unnamed.js, SQUIRCLE CORNERS) are paths drawn for the screen's
   sizes; a printed page lays the boxes out again at its own, so print keeps
   the plain round corner underneath. */
@media print {
  .box, .wtile, .mtile, .card .face, #faces > i, #wk-card, #idx-bug { clip-path:none !important; }
}

/* ---------- UNDER SAFARI'S BARS, ALL OF IT (6 Oct, iOS 26) ----------
   (at Kir's direction — "the content has to go under the toolbar and the
   status bar everywhere".)

   SAFARI PAINTS ONLY THE DOCUMENT'S OWN SCROLL UNDER ITS BARS. Whatever rides
   the page's scroll is drawn there; a fixed layer, or anything sticky to the
   window, is cut at the viewport's edge, and every pinned frame on this site
   is sticky to the window. MEASURED in the iOS 26.5 simulator, frame by frame
   off a screen recording of the same flicks:
     sticky to the window ............ still, but cut at both bars
     pinned by script ................ reaches the edges, jumps up to 25px
     pinned by a scroll timeline ..... reaches the edges, jumps 25px each
                                       time the toolbar folds or unfolds
     sticky inside a scroller ........ reaches the edges, 0px in 541 frames
   So on an iPhone the page scrolls inside <body>, and the document itself
   is held a spill's height down (shared/unnamed.js), which puts <body>'s own
   scroll under the status bar and under the toolbar. AAEL's gallery holds its
   page the same way. Every sticky rule on the site keeps its numbers: sticky
   measures from inside the scroller's padding, and the padding IS the spill.

   WHAT IT COSTS. The document never moves, so Safari's toolbar stays out
   instead of folding to a pill (the page shows through it), and a tap on the
   clock no longer runs back to the top.

   iPHONE ONLY. `-webkit-touch-callout` exists in WebKit on iOS and nowhere
   else; a touch-only screen at most 500pt across in portrait (device-width,
   which iOS reports the same in both orientations) is a phone, not an iPad;
   `100lvh` keeps out the iOS that predates the viewport units this is built
   on; `screen` keeps print to the plain page. Android's bars work
   differently, and a phone there keeps the page exactly as it was. The same
   test is repeated in shared/unnamed.js (SPILL) and in each page's <head>.

   NOT IN THE PARALLAX PROTOTYPE (8 Oct, at Kir's direction: "use the parallax
   principle we used on my portfolio: we know the screen height and the
   animation the block should play, we adapt the fragment to the screen
   height, we scroll, and the content goes under the bars top and bottom; the
   toolbar should fold into the pill"). A page whose <head> puts `px` on the
   root lets the document scroll on an iPhone (nike-diamond.html, THE PARALLAX
   PROTOTYPE), because <body>'s throws lose their momentum, so every rule here
   that builds the <body> scroller asks for a root without it. `:where()` adds
   nothing to a selector's weight, so on every page without `px` the cascade
   is exactly what it was. The voices' cut (A PINNED VOICE IS NOT A TOOLBAR)
   holds in both: a document scrolling under Safari's bars is where Safari
   looks for the page's own bars to begin with. */
@supports (-webkit-touch-callout:none) and (height:100lvh) {
  @media screen and (hover:none) and (pointer:coarse) and (max-device-width:500px) {
    :root:where(:not(.px)) {
      --spill:120px;                       /* past the status bar, past the toolbar */
      --deep:calc(100lvh + var(--spill));  /* the floor of a pinned frame */
      --floor:calc(var(--deep) - 100svh);  /* the part of it under the toolbar */
      overflow:hidden;                     /* held still, never dragged */
      height:calc(100lvh + 2 * var(--spill));
      scroll-behavior:auto; scroll-snap-type:none;
    }
    :root:where(:not(.px)) body {
      height:100%;
      margin:0;                            /* the scroller is the whole screen (work.html
                                              has no reset, and kept the UA's 8px) */
      box-sizing:border-box;               /* its padding is inside the screen, not added */
      overflow-x:hidden; overflow-y:auto;  /* `clip` cannot pair with a scrolling axis */
      overscroll-behavior:contain;
      padding-top:var(--spill);
      /* the last line stops at the glass's floor (not off --deep, which
         reduced motion takes back) */
      padding-bottom:calc(100lvh + var(--spill) - 100dvh);
      scroll-padding-top:var(--spill);
      scroll-padding-bottom:calc(100lvh + var(--spill) - 100dvh);
      scroll-behavior:smooth;
      scroll-snap-type:none;               /* the chapters' catch is scripted on a touch screen (ON A TOUCH SCREEN THE CATCH IS SCRIPTED, above) */
    }
    /* THE FOOTER RUNS ON UNDER THE TOOLBAR (7 Oct, at Kir's report on the last
       screen). <body> keeps a toolbar's worth of padding below the last line so
       nothing ends under the toolbar, and that strip was the page's own colour
       under the black footer. The footer takes the strip into its own box: the
       margin hands it back, the padding keeps the type where it was. */
    :root:where(:not(.px)) #contact {
      margin-bottom:calc(-1px - (100lvh + var(--spill) - 100dvh));
      padding-bottom:calc(1px + (100lvh + var(--spill) - 100dvh));
    }
    /* THE HEADER'S GLASS RUNS UP UNDER THE CLOCK. A fixed header is never drawn
       under the status bar, and its box on the top edge turns the status bar
       into a flat fill. So here the header is not fixed. It stands on the held
       document (absolute: <body> is not positioned, so <body>'s scroll does not
       carry it), a spill down, exactly where the fixed one stood, and Safari
       draws it like any other page content.
       The glass is a layer of its own that reaches a spill further up. It
       cannot be the header's own box: grown upward, that box made Safari stop
       drawing the page under the toolbar (measured). The header keeps its size,
       so --bar is unchanged. */
    :root:where(:not(.px)) header {
      position:absolute; top:var(--spill);
      background:none;
      -webkit-backdrop-filter:none; backdrop-filter:none;
    }
    /* and it lets a drag through: off the document's scroll, the header would
       otherwise be a strip that no swipe can scroll. Its links still take taps. */
    :root:where(:not(.px)) header { pointer-events:none; }
    :root:where(:not(.px)) header a { pointer-events:auto; }
    :root:where(:not(.px)) header::before {
      content:""; position:absolute; z-index:-1;
      left:0; right:0; bottom:0; top:calc(-1 * var(--spill));
      background:color-mix(in srgb, var(--bg) 28%, transparent);
      -webkit-backdrop-filter:blur(20px) saturate(1.2); backdrop-filter:blur(20px) saturate(1.2);
      transition:background-color var(--swap);
      pointer-events:none;
    }
    /* A PINNED VOICE IS NOT A TOOLBAR (7 Oct, at Kir's report from the
       previews: "the picture is cut under the status bar", "blocks at the
       bottom start to be cut").

       Safari 26 looks for the page's own bars: a fixed or sticky box at
       least 80% of the screen wide, short, within a few px of the top or
       bottom edge. When it finds one, it stops drawing the page under its
       status bar and toolbar and fills them flat, and it keeps that fill
       after the box has gone. The pinned voices (.say-pin, on future.html,
       the case pages and wall_template) are sticky and 92% wide, so the
       moment one passed an edge on a flick, both bars went flat for the
       rest of the chapter: white on top, a dark band under the toolbar.
       The pinned frames and the washes are screen-tall, and Safari does not
       take those for bars.

       So the sticky box itself is cut to 76% of its width, 70-73% of the
       screen. The voice inside is widened by the same factor, so every line
       still wraps and sits exactly where it did; nothing reads the pin's
       width. Measured in the iOS 26.5 simulator on Nike Diamond and future:
       the flicks that used to flatten both bars now leave the page drawn
       under them, all the way down. */
    :root .say-pin { width:calc((100% - 2 * var(--gut)) * .76); }
    :root .say-pin .say { width:calc(100% / .76); }
    @media (min-width:601px) {
      /* a phone on its side lays the voice out at 60% (the lead) or 40% of
         the measure, flush right: the same, in the narrowed box's terms */
      :root .say-pin .say { width:calc(60% / .76); margin-left:calc(40% / .76); }
      :root .say-pin:not(.lead) .say { width:calc(40% / .76); margin-left:calc(60% / .76); }
    }
  }
  @media screen and (hover:none) and (pointer:coarse) and (max-device-width:500px) and (prefers-reduced-motion:reduce) {
    /* nothing pins under reduced motion, so no frame reaches under the bars */
    :root:where(:not(.px)) { --deep:100svh; --floor:0px; }
    :root:where(:not(.px)) body { scroll-behavior:auto; scroll-snap-type:none; }
  }
}
