/* ==========================================================================
   LH BARBERSHOP — SYSTEM
   Implements 05-design/design-system/design-system.md
   Mechanisms tracked in 05-design/prototypes/_designloop/bar.md
   ========================================================================== */

/* --- 1. Tokens ----------------------------------------------------------- */
:root{
  --paper:#F3F1EC;
  --paper-2:#E9E5DC;
  --ink:#101011;
  --ink-soft:#56534D;
  --rule:rgba(16,16,17,.15);
  --rule-invert:rgba(243,241,236,.18);
  --gold:#B08A2E;
  /* — THE FOUR INKS. The table above is the MATERIAL; these are the four JOBS
     it does, and they are what every primitive below reads. Stated as roles
     rather than as colours, a dark chapter is the same publication in its
     other ink instead of a second stylesheet — see § 1a. The light values are
     the page's own; the dark values are the HERO's own, lifted verbatim:
     .72 is what § 12 gave every secondary line on the plate and .42 is what
     § 12a gave the facts' index. Nothing here is a new colour. — */
  /* — THE GROUND, as a role. The four inks below say what the page WRITES
     with; this says what it writes ON. It exists because an opaque ground is
     structural on this page — § 13a hands one to every section under the
     pinned timeline, and § 15b's two occluders depend on theirs to cover the
     CTA plate — so "paper" was stated as a literal in three places that all
     meant "this section's own ground". A tonal chapter (§ 5c) restates this
     one property and those three follow it, exactly as a dark chapter
     restates the four inks and every primitive follows those. — */
  --ground:var(--paper);

  --ink-1:var(--ink);                  /* the statement — headings, keys      */
  --ink-2:var(--ink-soft);             /* the voice — body, eyebrow, captions */
  --ink-3:rgba(16,16,17,.52);          /* the index, the note                 */

  --font-display:"Archivo","Helvetica Neue",Helvetica,Arial,sans-serif;
  --font-serif:"Instrument Serif",Georgia,"Times New Roman",serif;

  /* — THE TWO AXES, and the whole of the voice. Archivo is ONE family read at
     the two ends of its width axis, and the hero already reads it both ways on
     one screen: the headline at wdth 62 / wght 900, the wordmark and the BOOK
     plate at wdth 125 / wght 700. That is the grammar — anything that SPEAKS
     is condensed, anything that NAMES is extended — and until now it was
     spelled out as literal numbers in § 12d and § 16 while every section
     heading in between quietly ran the label axis at display size. One family,
     two axes, three roles, stated once.
     DISPLAY and LABEL were already `:root` custom properties; they lived in
     § 16b's token block because the footer lockup was the first thing to need
     them by name. They are the site's, so they are declared here. — */
  --wdth-display:62;  --wght-display:900;   /* .display / .section-title / .title */
  --wdth-label:125;   --wght-label:700;     /* wordmark, buttons, notes           */
  --wdth-body:100;    --wght-body:400;      /* body, keys, captions               */
  --lh-display:.80;                         /* § 12d's leading, on the cap        */
  --track-display:-.005em;
  --track-lead:.38em;                       /* the hero's connective              */
  --track-label:.08em;                      /* buttons, notes, captions           */
  --track-mark:.16em;                       /* the wordmark alone                 */
  --t-lead:clamp(.6875rem,.92vw,1.0625rem); /* § 12d's connective size            */

  /* THE DISPLAY RAMP IS CUT FOR THE CONDENSED AXIS, and had to be re-cut when
     the axis moved. At wdth 125 a cap costs ~.72em of run; at wdth 62 it costs
     ~.44em. Left at 6vw a section heading would have kept its type size and
     lost a third of its ink — the same words, visibly quieter than before.
     Both steps are scaled by the width ratio so the heading holds the WEIGHT
     it held, which is what the eye actually reads. The hero is not on this
     ramp (§ 12d sizes its two payload rows against the plate, not against a
     token) and is unaffected either way. */
  --t-display:clamp(2.75rem,11.2vw,8.25rem);
  --t-section:clamp(2.125rem,8vw,6.25rem);
  --t-title:clamp(1.5rem,2.4vw,2.25rem);
  --t-sub:1.5rem;
  --t-body:1rem;
  --t-micro:.875rem;

  --s-1:4px;  --s-2:8px;  --s-3:16px; --s-4:24px;
  --s-5:40px; --s-6:64px; --s-7:104px; --s-8:168px;

  --gutter:clamp(1rem,2.4vw,2rem);
  --header-h:72px;

  /* the column grid, as one number. Every hairline layer and every footer grid
     counts from it, so they cannot drift apart (see § 3 and § 16). */
  --grid-cols:6;
  --col:calc((100% - (var(--gutter) * 2)) / var(--grid-cols));
  --bp-lap:900px;
  --bp-hand:640px;

  /* — THE INSET, and § 5a is nothing but the consequences of it. A gridline is
     a STRUCTURAL object. Type is not allowed to touch one: between the line
     and the first letter there is always one step, the same step, everywhere
     on the site.

         GRIDLINE -> INSET -> TYPOGRAPHY    never    GRIDLINE -> TYPOGRAPHY

     ONE NUMBER, BOTH AXES. A cell is bounded by lines on four sides and the
     type inside it stands the same distance off all four, so a caption under a
     hairline and a headline beside a column rule are visibly the same
     decision. It replaces the --s-3 and --s-4 the price table, the metadata
     band and the accordion were each spending, separately, on this one job.

     CUT AS A FRACTION OF A COLUMN, not of the page. 14px against a 179px
     column on a phone and 24px against a 310px column at 1920 is ~8% of the
     cell at both ends, which is what makes the relationship read as CONSTANT
     rather than as a margin that happens to grow with the viewport. */
  --inset:clamp(.875rem,1.25vw,1.5rem);

  /* The two page edges that follow. --pad-frame is where STRUCTURE sits: the
     outer gridline itself, and therefore every image boundary, divider, rule
     and panel edge. --pad-type is where COPY sits, one inset inside it.
     Nothing that holds words should spend a bare --gutter again. */
  --pad-frame:var(--gutter);
  --pad-type:calc(var(--gutter) + var(--inset));

  /* — THE EDITORIAL HAND-OFFS. § 04 of the brief asks for these four gaps to
     be one number each rather than whatever a stack happened to give them.
     They are the hero's: --gap-label is the step § 12d puts between a
     connective and the word under it, read at section scale. — */
  --gap-label:var(--s-4);      /* label  -> heading */
  --gap-lede:var(--s-4);       /* heading -> body   */
  --gap-act:var(--s-5);        /* body   -> CTA     */
  --gap-meta:var(--s-5);       /* body   -> metadata rule */

  /* — THE RULE'S FALL. § 12d's connective rule is not capped and does not
     stop: it runs the whole of its column and FADES, because "a line that ends
     at nothing reads as a measure, and the eye asks what measured it". That is
     the device, so it is the device everywhere — a ruled label runs its own
     column to the end and dissolves. The hero's own rule is brighter than this
     (it is part of the headline, and it dies into the photograph); a chapter
     label's rule is the site's hairline, the same one the band under it and
     the six column rules behind it are drawn in. — */
  --rule-fade:linear-gradient(90deg,var(--rule) 0,var(--rule) 55%,transparent 100%);

  /* — THE PHOTOGRAPHIC GRAMMAR (§ 08). Not one treatment forced onto every
     picture: one GRADE, so every photograph is developed the same way, and one
     directional VEIL for pictures that carry type or sit against ink. The hero
     keeps its own scrim (--hero-scrim, § 12) because that one has a second job
     — it has to reach solid ink under the facts band. — */
  --photo-grade:saturate(.94) contrast(1.03);
  --photo-veil:linear-gradient(180deg,rgba(16,16,17,.26) 0%,
    rgba(16,16,17,.03) 34%,rgba(16,16,17,.30) 100%);

  /* — THE CARTOGRAPHIC GRADES (§ 4b, § 20). The site's ONE picture we did not
     shoot and cannot art-direct: Google draws the map in its own palette —
     cyan sea, mint parks, white arterials — and the only lever a keyless
     <iframe> leaves us is a filter on the outside of it. (Google's own styled
     basemaps — Silver, night — are Maps JavaScript / Static API features and
     need a key and a billing account; the decision record carries the
     comparison this page was chosen from.)

     THERE ARE TWO GRADES NOW, 2026-09-14, and that is a deliberate break from
     the single --map-tone both maps used to share. The page shows the same
     place twice and the two showings were never doing the same job: § 20's
     plate is a SMALL, HELD object — one card inside a pinned chapter, read at
     arm's length — and § 4c is a FULL-BLEED ENVIRONMENT the reader arrives in.
     One tone across both made them one embed shown twice; two grades make them
     one place seen at two moments.

     WHICH GRADE GOES WHERE WAS SWAPPED after both were built and read on the
     page, and the reason is worth keeping. Night went to the small plate first,
     on the argument that the plate sits inside a pinned INK chapter and should
     not be the one lit rectangle in it. That argument was about the plate's
     NEIGHBOURS. What the page actually needed was for the grades to match the
     SCALE each map is read at: an inverted map is a low-key surface that wants
     room to be atmospheric, and a 650px card is not that room, while the
     duotone's ink→gold ramp is a lighting effect that needs a frame long
     enough for the ramp to travel. So night takes the full-bleed chapter and
     the duotone takes the card, and § 20's ink chapter is answered instead by
     what it was always answered by — the ink SHEET standing on the map. What still holds them together is that NEITHER is a colour
     map: both start at grayscale(1), so the pair is monochrome cartography in
     two keys rather than two palettes.

     --map-night — § 4b/§ 4c. Grayscale, then inverted: the landmass goes to
     ink and the road network comes up white through it, which is a real night
     map rather than a dimmed day one. Contrast comes DOWN and brightness with
     it, because inversion alone drives Google's white arterials to pure #fff
     and they then out-shout the display numeral standing beside them.

     --map-mono + --map-duo — § 20. Grayscale first, so none of Google's hues
     survive to fight ours, then ONE ink→gold ramp laid over it in
     mix-blend-mode:color, which supplies hue and saturation and leaves the
     cartography's own lightness — every road width, every label — untouched.
     Two-colour cartography in the page's own two colours. The ramp is a
     GRADIENT, not a flat tint, so the frame runs ink at the top and gold at
     the foot and the map is lit rather than stained.

     THE MARKER SURVIVES BOTH, and that was the thing to check before either
     chain was allowed to start at grayscale(1). The old --map-tone spent a
     whole hue-rotate stop carrying Google's red pin toward --gold on the
     argument that a full desaturation would take the pin down with the sea. It
     does take its COLOUR down. It does not take the pin down: the marker is a
     solid teardrop against cartography that is nowhere near its value, so what
     grayscale leaves is a legible marker in the map's own ink — mid-grey on
     the duotone, where the ramp then hands it the page's gold hue back, and
     near-white on the night map, which is the brightest object in that frame.
     Both were rendered and read before this was written.

     A MARKER OF OUR OWN WAS BUILT AND THROWN AWAY, and the reason is worth
     keeping: an embed centres on `ll` and marks `q`, so a drawn pin has to be
     placed at a pixel offset from the centre of the frame — and Google rounds
     that recentring by an amount that changes with the frame's own width
     (measured: +99px through +250px against a computed +261px, across four
     widths of the same plate). A pin placed by arithmetic therefore slides off
     the address as the window resizes, and one placed by measurement is only
     right at the width it was measured at. Google's own marker is attached to
     the map rather than to the frame, which is also what keeps it honest after
     § 4b's .map__catch hands the embed over and the reader pans it. — */
  --map-night:grayscale(1) invert(1) contrast(.86) brightness(.88);
  --map-mono:grayscale(1) contrast(1.35) brightness(1.06);
  --map-duo:linear-gradient(180deg,rgba(16,16,17,.30),rgba(200,162,74,.26));

  --d-fast:300ms;
  --d-move:500ms;
  --d-reveal:700ms;
  --ease-out:cubic-bezier(.22,1,.36,1);
}

/* --- 1a. Chapters — the same publication, in its other ink ----------------
   The site runs DARK -> LIGHT -> DARK, and the brief's § 09 is exact about
   what that must mean: a light section is the light MODE of one publication,
   not a second design. So the flip is four custom properties and nothing else.
   Every primitive in § 4 reads --ink-1/2/3 and --rule; a dark chapter restates
   those four and inherits every other decision unchanged.

   THE DARK VALUES ARE THE HERO'S, not new ones — see § 1. That is what makes
   this a promotion rather than a redesign: .72 was already on
   `.hero .eyebrow/.micro/.body`, .42 was already on `.fact__ix`, and both
   rules are now deleted because this block states them once for the whole
   chapter instead of once per element per section.

   WHO CLAIMS IT. The three ink grounds the page already has (the hero plate,
   the cuts timeline, the closing CTA) claim it by selector, so no markup has
   to remember. `.chapter-dark` is the portable name for anything new, and
   `.invert` is kept as the alias the timeline stretch was written against.
   The footer is here too, as of 2026-09-05. It used to tokenise its own ink end
   to end (--band-ink, --footer-ink) and read none of these, which is exactly
   how it drifted into being a second design; § 16 now claims the chapter like
   every other ink stretch and keeps only its ground and its focus ring.

   THE PROCESS PLATE'S SHEET IS HERE TOO, as of 2026-09-14 — the first claimant
   that is a PLATE rather than a stretch of page. It is the same case for the
   same reason: it turned over to ink, and the four tokens are the whole of what
   that has to mean. Every line inside it (§ 20's number, street, town and way
   out) already reads --ink-1/2 and --rule and needed no edit, which is the
   proof that this block is doing the work rather than a second palette. Note
   the claim stops AT the sheet: .ps__card's own outline and the aerial under it
   are outside, on paper, and stay on the page's light ink.

   § 4c's .locale__sheet is here for the same reason and on the same day — the
   page's two location plates are one component, so they turn over together or
   they are two designs again.

   IT LEFT AND CAME BACK, and the round trip is worth recording. When § 4c's map
   went night in the grade swap, the sheet was flipped to paper on the argument
   that a plate's job is to be the one hard object on the picture, so a plate on
   an ink map should be light. Rendered, that is a true reading of contrast and
   the wrong reading of the page: the address plate is the same object in both
   places, and a reader meeting it as black-on-white in one chapter and
   white-on-black in the other meets two. The ink is the constant, and it is the
   GROUND that is allowed to change under it. What carries the plate on a night
   map instead is its keyline — § 1a's --rule-invert, which is the only thing
   drawing its edge there, and is why the sheet keeps a hairline it barely needs
   on paper. Its map, its wash and its keyline are outside the sheet. */
.chapter-dark,.invert,.section--ink,.hero,.cta,.footer,.ps__sheet,.locale__sheet{
  --ink-1:var(--paper);
  --ink-2:rgba(243,241,236,.72);
  --ink-3:rgba(243,241,236,.42);
  --rule:var(--rule-invert);
}

/* --- 2. Reset ------------------------------------------------------------ */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body,h1,h2,h3,h4,p,figure,blockquote,dl,dd,ul,ol{margin:0}
ul,ol{padding:0;list-style:none}
img,svg,video{display:block;max-width:100%}
button,input,select,textarea{font:inherit;color:inherit}
a{color:inherit;text-decoration:none}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  background:var(--paper);
  color:var(--ink);
  font-family:var(--font-display);
  font-size:var(--t-body);
  font-weight:400;
  line-height:1.55;
  font-variation-settings:"wdth" 100,"wght" 400;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
  position:relative;
}

/* --- 3. Grid — M3: hairlines, full height, never interrupted -------------- */
.grid-rules{
  position:fixed;inset:0;
  z-index:0;pointer-events:none;
  display:grid;grid-template-columns:repeat(var(--grid-cols),1fr);
  padding-inline:var(--gutter);
  /* the column lines are orientation, not content — barely-there on purpose */
  opacity:.35;
}
.grid-rules span{border-left:1px solid var(--rule)}
.grid-rules span:last-child{border-right:1px solid var(--rule)}
/* Any opaque surface buries the fixed layer, so it is re-drawn in place there.
   Same gutter, same column count: the x-positions are identical by construction
   rather than by matching numbers. Used by the footer (§ 16). */
.grid-rules--inline{position:absolute}
@media (max-width:900px){
  :root{--grid-cols:2}
  /* two columns wants two spans. The other four would wrap into extra rows and
     leave the right-hand edge drawn on only the bottom third of the layer. */
  .grid-rules span:nth-child(n+3){display:none}
  .grid-rules span:nth-child(2){border-right:1px solid var(--rule)}
}
.page{position:relative;z-index:1}

/* --- 4. Typography ------------------------------------------------------- */
/* THE DISPLAY VOICE IS THE HERO'S. It was not before: § 12d set the headline
   at wdth 62 / wght 900 with .80 leading, § 16 set the footer lockup the same
   way, and every section heading between them ran wdth 125 / wght 700 at 1.0.
   Archivo's width axis is wide enough that those two settings do not read as
   one typeface — the top and the bottom of the page spoke a condensed
   grotesque and the middle spoke an extended one, which is the single largest
   reason the chapters read as separate sites. One axis, stated once, from the
   tokens § 1 now holds.
   The hero keeps its OWN sizes (§ 12d clamps each payload row against the
   plate) and its own tail tuning; what it hands the rest of the page is the
   face, the leading and the tracking. */
.display,.section-title,.title,.footer__mark{
  font-family:var(--font-display);
  font-weight:var(--wght-display);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display);
  text-transform:uppercase;
  letter-spacing:var(--track-display);
  line-height:var(--lh-display);
  color:var(--ink-1);
  overflow-wrap:break-word;
}
.display{font-size:var(--t-display)}
.section-title{font-size:var(--t-section)}
.title{font-size:var(--t-title)}
/* the sub-head is BODY voice at heading size, not a fourth display width —
   it introduces a list, it does not open a chapter */
.sub{font-size:var(--t-sub);font-weight:700;line-height:1.25;
  font-variation-settings:"wdth" var(--wdth-body),"wght" 700;color:var(--ink-1)}
.body{font-size:var(--t-body);line-height:1.55;max-width:62ch;color:var(--ink-2)}
.micro{font-size:var(--t-micro);line-height:1.4;color:var(--ink-2)}
.num{font-variant-numeric:tabular-nums}

/* THE LABEL VOICE — the other end of the axis, and the one the chrome already
   spoke. Anything that NAMES a thing at small size is set here: the wordmark,
   the menu trigger, every button label, and § 4a's note. Grouped so the six
   places that were each spelling out `"wdth" 125,"wght" 700` cannot drift. */
.label{
  font-family:var(--font-display);
  font-weight:var(--wght-label);
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label);
  text-transform:uppercase;letter-spacing:var(--track-label);
}

/* M5: the serif exists only as an eyebrow — <=14px, <=4 words */
.eyebrow{
  font-family:var(--font-serif);font-style:italic;
  font-size:var(--t-micro);line-height:1.4;color:var(--ink-2);
  display:block;margin-bottom:var(--gap-label);
}

/* inverted stretch — the ground and the ink. The four inks come from § 1a,
   which this name is one of the claimants of. */
.invert{background:var(--ink);color:var(--paper)}

/* --- 4a. Editorial primitives — the hero's devices, made portable ---------
   Four devices carry the hero's editorial voice, and until now all four were
   welded into § 12/§ 12a and could not be used by anything else. They are
   lifted here unchanged, so a chapter can open the way the hero opens without
   re-deciding anything.

     .eyebrow--ruled   A. the serif label with the hairline running off it
     .lead             B. the tracked hairline caps of "MORE THAN A"
     .rule             C. the structural hairline, on its own
     .meta-row/.meta   D. the numbered facts band, generalised

   THE LAW ON RULES (§ 04-B of the brief: "do not use decorative rules
   randomly"): a chapter gets ONE structural hairline. A chapter that opens
   with a label takes it as .eyebrow--ruled; a chapter that already carries a
   rule of its own — the timeline's year rail, the price table's dividers —
   does not draw a second one. */

/* A. THE RULED LABEL. § 12d's device, at section scale: the small line sets on
   the left and a hairline runs off it to the end of the column and fades
   (--rule-fade, § 1). The .9em of air is § 12d's: the rule is a separate object
   crossing the column, not an extension of the words, so it is let off the type
   rather than pulled back onto it. */
.eyebrow--ruled{display:flex;align-items:center}
.eyebrow--ruled i{flex:1 1 auto;height:1px;margin-left:.9em;
  background:var(--rule-fade)}

/* B. THE LEAD — the connective. Tracked to most of a letter apart at a tenth
   of the payload's cap, so it reads as a rule of small caps set above a word
   rather than as type that lost an argument with it. § 12d states the same
   numbers against the hero's own sizes; this is the portable form. */
.lead{
  font-family:var(--font-display);
  font-size:var(--t-lead);
  font-variation-settings:"wdth" var(--wdth-body),"wght" 300;
  font-weight:300;
  letter-spacing:var(--track-lead);
  text-transform:uppercase;
  line-height:1.1;
  color:var(--ink-2);
  display:block;
}

/* C. THE NOTE — small uppercase metadata (§ 04-D). One size, one tracking, one
   ink, one alignment, everywhere: date ranges, captions, prices-in-NZD lines.
   It is the LABEL voice at caption weight, which is what keeps a caption
   related to the button and the wordmark instead of to the body copy. */
.note{
  font-family:var(--font-display);
  font-size:var(--t-micro);
  font-variation-settings:"wdth" var(--wdth-body),"wght" var(--wght-body);
  font-weight:var(--wght-body);
  letter-spacing:var(--track-label);
  text-transform:uppercase;
  line-height:1.4;
  color:var(--ink-3);
  font-variant-numeric:tabular-nums;
}

/* D. THE STRUCTURAL HAIRLINE, as an element rather than a border — for the
   places a rule divides two things that are not each other's border box.
   NOT to be confused with the footer band's `.rule--top` / `.rule--v1` etc
   (§ 16b): those are SVG <path>s inside .band__rules, they never carry this
   bare class, and they are the frame's own geometry rather than this. */
.rule{display:block;height:1px;border:0;margin:0;background:var(--rule)}

/* D2. THE CHAPTER SEAM — the same hairline doing a different job: the BOUNDARY
   between two chapters, rather than a divider inside one. § 22 hands every
   section below the pinned timeline the same opaque paper, so the barber
   chapter currently arrives over the cuts with no edge at all — it simply
   appears. This draws that edge, and because it rides the incoming section's
   own box it travels up with it as the section rises over the pinned stage.
   It does not break § 4a's law: the barber still opens on its ONE structural
   hairline (.eyebrow--ruled); this line belongs to neither chapter.
   It is STRUCTURE, so it stops on the two outer gridlines at --pad-frame (§ 1:
   "every image boundary, divider, rule and panel edge") instead of bleeding
   past them the way the picture beside it does. */
.seam{grid-column:1 / -1;margin:0 var(--pad-frame)}

/* E. THE NUMBERED METADATA (§ 04-C). This is § 12a's facts band with the hero
   taken out of it. Two forms of one component:
     .meta-row          stacked  — a short column of numbered rows, hairline
                        between each. What a half-column can hold.
     .meta-row--band    across   — one band split into equal cells by vertical
                        hairlines. The hero's form, and what a full-bleed
                        section can hold.
   § 12a keeps only what is HERO about its band: the absolute position on the
   plate's foot, the settle, and the gutter margins. Everything you can see of
   it — the rules, the cells, the index, the key — is stated here and there
   only once. */
.meta-row{display:grid;border-top:1px solid var(--rule)}
.meta-row>*{min-width:0}
/* the same inset, on the block axis: a key stands off the hairline above it
   exactly as far as it stands off the column rule beside it (§ 5a) */
.meta,.fact{display:flex;align-items:center;
  padding-block:var(--inset);border-top:1px solid var(--rule)}
.meta:first-child,.fact:first-child{border-top:0}
.meta__inner,.fact__inner{display:flex;align-items:baseline;
  gap:var(--s-4);width:100%}
.meta__ix,.fact__ix{font-size:var(--t-micro);line-height:1.4;color:var(--ink-3);
  font-variant-numeric:tabular-nums;flex:none}
/* stated, not shouted: the statement is the size of the copy it follows, and
   the number beside it is what makes it one of three */
.meta__key,.fact__key{margin-top:0;font-size:var(--t-body);
  font-weight:var(--wght-body);line-height:1.35;letter-spacing:0;
  font-variation-settings:"wdth" var(--wdth-body),"wght" var(--wght-body);
  text-transform:none;color:var(--ink-1)}

.meta-row--band{grid-template-columns:repeat(3,1fr)}
.meta-row--band .meta,.meta-row--band .fact{
  min-height:0;border-top:0;border-left:1px solid var(--rule);
  padding:var(--inset)}
/* the first key sits on the page's own left margin, level with the heading
   above it, so the band starts on the same line the chapter does */
.meta-row--band .meta:first-child,.meta-row--band .fact:first-child{
  padding-left:0;border-left:0}
@media (max-width:640px){
  .meta-row--band{grid-template-columns:1fr}
  .meta-row--band .meta,.meta-row--band .fact{
    padding:var(--inset) 0;border-left:0;border-top:1px solid var(--rule)}
  .meta-row--band .meta:first-child,.meta-row--band .fact:first-child{border-top:0}
}

/* --- 5. Layout ----------------------------------------------------------- */
.shell{padding-inline:var(--pad-type)}

/* --- 5a. THE FIELD — gridline, inset, typography --------------------------
   § 3 draws six hairlines down the page and every chapter is composed against
   them. What was never stated is HOW: until now a .shell was padded by exactly
   one --gutter, which is the x of the first hairline, so every eyebrow, every
   headline and every paragraph on the site began ON a line rather than inside
   the cell the line opens. Read with the grid visible the page looked printed
   over its own structure instead of set into it.

   A FIELD is any box whose inline edges ARE gridlines. There are seven of them
   and they are the site's whole layout vocabulary — the shell, the hero's copy
   block, a split's body, the cuts head, the closing CTA, the stream's centre
   panel, and .field itself. All seven now hold --pad-type, so the relationship
   is a property of the CONTAINER and nothing inside one has to remember it.
   That is what makes this a site rule and not seven section rules, and it is
   why a new section inherits it by being built out of the same boxes rather
   than by being told about it.
   The menu panel is deliberately NOT one: it is opaque and buries the hairline
   layer for its whole height, so there is no line behind its type to stand off
   (menu.css § 4).

   STRUCTURE KEEPS THE LINE. The inset is for words. A price table's dividers,
   a tile grid's seams, the process stage's canvas edge and any full-bleed
   photograph are measured BY the grid and must land exactly on it — so they
   step back out with .to-grid, which is one negative inset and nothing else.
   Every use of it in the markup is a design statement: this object is drawn on
   the lines, not set between them.

   The grid stays a structural system rather than a box around every element:
   .to-grid is the way out, .field's spans are the way to occupy more than one
   cell, and neither of them is a special case bolted onto a section. */

/* the escape — a structural child of a field, returned to the line it is
   measured against. Inline only: a bleed is an x decision. */
.to-grid{margin-inline:calc(-1 * var(--inset))}
/* and the full bleed, for a surface that runs past the grid to the viewport */
.to-bleed{margin-inline:calc(-1 * var(--pad-type));
  width:calc(100% + var(--pad-type) * 2)}

/* --- 5b. THE FIELD, as a grid — copy that spans columns -------------------
   The rule above holds one block of copy inside one field. This holds SEVERAL
   against the six columns at once, for the compositions where the hierarchy is
   carried by width: a label on one column, a headline across four, a lede on
   two. The field's own edges are the outer gridlines (--gutter, so its tracks
   ARE § 3's columns by construction, never by a matching number), and every
   child takes the inset as a margin — so a .col-4 headline starts one inset
   inside the first line and ends one inset short of the fifth.
   Below --bp-lap the page grid is two columns (§ 3) and a span of three or
   four has nothing to mean, so every child takes the full field. */
.field{display:grid;grid-template-columns:repeat(var(--grid-cols),1fr);
  padding-inline:var(--pad-frame)}
.field>*{grid-column:1 / -1;margin-inline:var(--inset);min-width:0}
.field>.col-1{grid-column:span 1}
.field>.col-2{grid-column:span 2}
.field>.col-3{grid-column:span 3}
.field>.col-4{grid-column:span 4}
.field>.col-5{grid-column:span 5}
.field>.col-6{grid-column:span 6}
.field>.to-grid{margin-inline:0}
@media (max-width:900px){.field>*{grid-column:1 / -1}}
.section{padding-block:var(--s-7);position:relative}
.section--tall{padding-block:var(--s-8)}
/* --- 5c. THE INK CHAPTER — a light section, turned over ------------------
   § 1a already says what a dark chapter IS: four ink roles restated, and
   every primitive follows. It had no SECTION-level claimant — the six that
   existed are the hero, the cuts band, the CTA, the footer and two plates,
   each of which sets its own ground as part of its own composition. This is
   the portable one: a `.section` that turns over, ground and all.

   WHY IT EXISTS. The page runs DARK -> LIGHT -> DARK, and the light stretch
   between the cuts band and the closing CTA is three sections long — the
   barber, the menu, the reviews. All three opened the same way (serif
   eyebrow, ruled hairline, two-line condensed headline on columns 1-4, lede
   on 5-6) and all three stood on the same --paper. Composed, that is one
   continuous surface with three headlines printed on it: nothing told a
   reader a chapter ended, because nothing did.

   IT IS THE SAME PUBLICATION IN ITS OTHER INK, which is § 1a's whole claim
   and the reason this is not a second design. Not one type size, weight,
   width, gap or hairline moves. The eyebrow is the same serif italic, the
   headline the same condensed 62/900, the table the same table — read in
   paper on ink instead of ink on paper.

   THE GROUND IS THE ROLE, NOT A COLOUR. --ground (§ 1) is what § 13a's
   blanket and § 15b's two occluders paint, so restating it here turns the
   section over *and* keeps those three agreeing with it. The four inks come
   from § 1a by joining its selector list; this block adds only the ground.

   IT KEEPS THE HAIRLINES. An opaque panel buries § 3's FIXED layer — that is
   why .section--beige was taken off services.html #detail (§ 27). Both
   sections carrying this class already redraw the layer inline with
   .grid-rules--inline, so the six columns run through the chapter; and
   because § 1a hands the section --rule-invert, they are redrawn in the
   chapter's own hairline rather than the page's. A claimant WITHOUT an
   inline layer would be the § 27 mistake again.

   THE KEYLINE IS THE CHAPTER'S EDGE, drawn per section so a run of two gets
   an opening edge AND an internal seam. It is STRUCTURE, so it stops on the
   two outer gridlines at --pad-frame (§ 5a) rather than bleeding, and it
   takes --rule, which inverts with the chapter.

   AND THE FURNITURE TURNS WITH IT. The booking table (§ 9) and the review
   wall (§ 9c) name a ground of their own; left alone they would stay paper
   and the chapter would come out as white objects on ink. They read --ground
   instead, so the chapter turns over whole. .wall keeps --rule as its 1px
   lattice and inverts with everything else. */
.section--ink{--ground:var(--ink);background:var(--ground);color:var(--ink-1)}
.section--ink::before{content:"";position:absolute;top:0;
  left:var(--pad-frame);right:var(--pad-frame);height:1px;background:var(--rule)}
/* the chapter's own furniture, on the chapter's own ground. */
.section--ink .pricing,
.section--ink .wall__col,
.section--ink .wall-tile{background:var(--ground)}
/* the site's one tonal step, kept as the token it always was (§ 1). */
.section--beige{background:var(--paper-2)}
/* An opaque panel buries the fixed hairline layer (§3), so the band re-draws it
   in place — the same move the footer makes at §16. The rules sit at 0 and the
   band's own children are lifted to 1, or a positioned layer would paint over
   in-flow text. The price table's dividers already land on columns 3 and 5.
   Held as one rule for any section carrying its own inline layer: the pinned
   timeline (§ 12) gives every section after it an opaque ground, so #area and
   #process bury the fixed layer the same way the beige band does. */
:where(section):has(> .grid-rules--inline){position:relative}
:where(section) > .grid-rules--inline{inset:0;z-index:0}
:where(section):has(> .grid-rules--inline) > :not(.grid-rules){position:relative;z-index:1}
/* THE HAND-OFFS ARE ONE NUMBER EACH (§ 1). A stack used to add its own --s-4
   on top of the eyebrow's own margin-bottom, so label -> heading came out at
   40px while every other gap in the same column was 24 — the one hand-off the
   brief asks to be a token was the one that was two. The eyebrow's margin is
   --gap-label and the stack stands down after it. */
.stack>*+*{margin-top:var(--s-4)}
.stack>.eyebrow+*{margin-top:0}
/* body -> metadata is its own step: the rule under a paragraph needs more air
   than two paragraphs need between them, or the hairline reads as an underline */
.stack>.meta-row{margin-top:var(--gap-meta)}
.stack-tight>*+*{margin-top:var(--s-3)}

/* M4: 50/50 split, image bleeds to the viewport edge */
/* THE PHOTOGRAPHIC GRAMMAR (§ 1, § 08 of the brief). Not one treatment forced
   onto every picture — one GRADE, so every photograph on the site is developed
   the same way, and one directional VEIL on the pictures that bleed to an
   edge. The hero is where both were measured: its planes carry --hero-scrim,
   which is this veil with a second job (it has to reach solid ink under the
   facts band, § 12). A split's picture has no band to reach, so it takes the
   open form — held light through the upper third so the room keeps its detail,
   exactly as § 12 words it, and closing at the foot where the frame meets the
   page. That is what makes the barber's portrait and the hero read as two
   frames of one shoot rather than as two stock photographs. */
.split{display:grid;grid-template-columns:1fr 1fr;align-items:center;min-height:78svh}
.split>*{min-width:0}
/* THE PORTRAIT MAY NOT OUTGROW THE VIEWPORT. The picture is a stretched grid
   item with its intrinsic aspect intact, so past ~1300px the 1086x1448 file
   sizes the row taller than the screen and the chapter no longer opens as one
   frame. The cap is the ceiling; object-fit already carries the crop. */
.split__media{position:relative;align-self:stretch;min-height:66svh;
  max-block-size:calc(100svh - var(--header-h));overflow:hidden}
.split__media img{width:100%;height:100%;object-fit:cover;filter:var(--photo-grade)}
.split__media::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:var(--photo-veil)}
/* THE BODY IS A CELL, AND ITS TWO EDGES ARE NOT THE SAME LINE. A split cuts
   the page at 50%, which is the THIRD HAIRLINE at every width — gutter + 3
   columns is exactly W/2 by construction — so the body meets a gridline on the
   seam side and the page's outer gridline on the other. The seam takes one
   --inset; the outside takes --pad-type, because the outer line is itself one
   gutter in from the viewport. Padding the box symmetrically (which is what a
   single --pad-type did) put the copy a whole gutter off the seam and only
   looked composed from the outside — the failure this section made most
   visible, and the reason the rule is measured from LINES and not from edges. */
.split__body{padding-block:var(--s-7);padding-inline:var(--inset) var(--pad-type)}
.split--flip .split__body{padding-inline:var(--pad-type) var(--inset)}
.split--flip .split__media{order:2}
.split--flip .split__body{order:1}
@media (max-width:900px){
  .split{grid-template-columns:1fr;min-height:0}
  /* stacked, the picture and the copy share one screen: the cap leaves the
     column room to sit under it without the chapter running two viewports. */
  .split__media{min-height:40svh;max-block-size:46svh;order:1!important}
  /* stacked, the body spans the page and both its edges are outer lines */
  .split__body,.split--flip .split__body{
    order:2!important;padding:var(--s-5) var(--pad-type)}
}

/* --- 4b. THE MAP — an instrument, and now a GROUND ------------------------
   "Where to find us" used to end § 5's split: a column of copy on paper, a
   framed map in the other half, a seam down the middle. That composition
   bounded the map like a product, which is exactly what made it read as one.
   The map is the chapter's ground now — full bleed behind the whole section,
   with § 4c's sheet laid over it — and everything below is what the frame
   still withholds from it, for the one reason it always did: a map is an
   INSTRUMENT, not a print.

   NO CAMERA. § 14a's lens may drift across a photograph. It may not drift
   across a thing the reader is about to touch — the pin would sit a few
   pixels off the address by the time the cursor arrived, and an <iframe>
   repainting under a transform is the most expensive plane on the page to
   move. Withheld by simply not carrying data-cam; § 14a needs no switch.

   NO VEIL. § 4's ::after holds light through the upper third and closes at
   the foot, which is how a photograph meets the page. Over a map it only
   dims the street names somebody came here to read. What § 4c lays over it
   instead is FLAT and it WARMS rather than darkens — a wash, not a veil, and
   the distinction is the whole of why one is allowed here and the other is
   not.

   THE GRADE IS NIGHT, 2026-09-14 (second pass). The chain used to be a partial
   desaturation — Google's colour walked toward our paper but left in place, on
   the argument that grayscale(1) would take the red marker down with the cyan
   sea. § 1 answers that argument with what a render actually shows: grayscale
   takes the marker's COLOUR, not the marker, and inversion then stands it as a
   light teardrop clear of the road net. So the cartography is free to go fully
   monochrome, and § 1's --map-night inverts it: the landmass drops to ink and
   the road network comes up white through it.

   WHY THE FULL-BLEED MAP IS THE ONE THAT GOES NIGHT. An inverted map is a
   low-key surface — most of its area is near-black and its whole legibility
   lives in a thin white net of roads. That wants ROOM: at section scale the
   net has somewhere to run and the darkness reads as an environment the reader
   is standing in at night. The same grade on § 20's 650px card reads as a
   small dark rectangle, because there is not enough of it for the net to be
   anything but texture. § 20 takes the duotone for the mirror of that reason.

   WHAT THIS COSTS is the sheet's contrast, and § 4c does NOT pay it by turning
   the sheet over. The address plate is one object read twice on this page, so
   it stays ink in both places and its EDGE does the work here instead — § 1a
   carries the round trip that settled it, paper sheet included and rejected.

   THE EMBED IS KEYLESS. `output=embed` needs no API key and no billing
   account, so there is no quota for the map to exhaust and no credential in
   the markup to rotate or leak. The keyed Maps Embed API is a one-attribute
   swap if a key is ever issued; see the decision record. */
.map__canvas{
  position:absolute;inset:0;width:100%;height:100%;display:block;border:0;
  filter:var(--map-night);
  /* THE MAP DOES NOT EAT THE PAGE'S SCROLL. One finger dragged across a live
     embed pans the map instead of scrolling the document — on a phone that is
     a full-bleed trap two thirds of the way down a long page, and the reader
     has no way to know why the page stopped. The frame stays inert until it
     is asked for, and .map__catch below is the ask. */
  pointer-events:none;
}
[data-map-frame].is-live .map__canvas{pointer-events:auto}

/* THE ASK. A real <button> — so the map is reachable by keyboard and
   announces itself — laid over the frame at full size, because on a phone the
   whole picture is what a thumb lands on. It states its offer in § 4C's .note
   metadata standing in one corner rather than as a label across the middle:
   the map already reads as a map, and the only thing the reader needs told is
   that it will move. The plate is the page's own paper on the page's own
   hairline, so the invitation is set in the site's voice and not in a
   convention borrowed from somebody else's cookie banner. */
.map__catch{
  position:absolute;inset:0;z-index:3;
  /* TOP-RIGHT IS THE ONLY CORNER GOOGLE LEAVES US. Its own furniture claims
     the other three — the place card top-left, the logo and the satellite
     thumbnail bottom-left, attribution and the fullscreen control
     bottom-right — and a plate of ours overlapping any of them reads as a
     rendering fault rather than as an invitation. */
  display:flex;align-items:flex-start;justify-content:flex-end;
  margin:0;padding:var(--s-4);border:0;background:transparent;
  font:inherit;color:inherit;text-align:left;cursor:pointer;
  -webkit-appearance:none;appearance:none;
}
[data-map-frame].is-live .map__catch{display:none}
.map__catch .note{
  background:var(--paper);
  /* THE PAGE'S OWN RULE, RESTATED — the same case as .locale__sheet's, and for
     the same reason. § 4c inverts --rule for the section so the map's keyline
     can be seen on a night ground; this is a PAPER plate standing on that
     ground, so the inverted hairline disappears into it. Restated here rather
     than hard-coded so a re-tone of the page's ink still reaches it. */
  --rule:rgba(16,16,17,.15);
  box-shadow:inset 0 0 0 1px var(--rule);
  padding:.6em 1em;
  transition:box-shadow var(--d-fast) ease,color var(--d-fast) ease;
}
.map__catch:hover .note,
.map__catch:focus-visible .note{box-shadow:inset 0 0 0 1px var(--ink);color:var(--ink-1)}
/* stacked, the frame is shorter and the plate comes in with the brackets */
@media (max-width:900px){.map__catch{padding:var(--s-3)}}

/* --- 4c. THE LOCATION PANEL — the chapter the map is the ground of --------
   One section, three planes: the map (z 0), a flat warm wash over it (z 1),
   and one editorial sheet (z 4) standing on the page's own gridlines. The
   reader meets ONE object. That is the whole brief, and everything below is
   the arithmetic of it.

   A. THE ENVIRONMENTAL PALETTE IS SAMPLED, NOT INVENTED. The three colours
   below were read off a 1440px screenshot of this very embed, after § 4b's
   grade: #D8E3D7 is what its parks and its sea come out as, #F3EADD is what
   its arterials come out as, and the ground is the two mixed. So the chapter
   is not "greenish paper that goes with a map" — it is literally the map's
   own ink, which is what makes the embed stop reading as a foreign object
   sitting in our page. The hairline and the keyline are the page's own ink
   walked the same distance toward sage; nothing here invents a fourth colour.

   B. THE GRID IS THE PAGE'S. The section pads to --pad-frame and runs
   --grid-cols equal tracks, which is § 3's hairline layer by construction
   rather than by matching numbers, and the sheet takes the first three of
   them: its left edge on the page's outer gridline, its right edge on the
   middle rule — the same line § 5's seam used to fall on. § 5a then applies
   unchanged: the box is STRUCTURE so it lands ON the lines, the type inside
   it stands one --inset off them. What is NOT symmetric is the block axis:
   there are no horizontal rules for a top edge to land on, so the sheet
   spends --s-6 there and the air it buys is what lets the map run ABOVE and
   BELOW it as well as beside it.

   C. THE SHEET IS FLAT, ON PURPOSE. No backdrop-filter. A blurred pane over
   a live iframe re-blurs on every composite — any motion anywhere on the
   screen drags the whole page's frame rate down with it, and the closing
   CTA's probe measures exactly that cost. The map reads through this sheet
   because the alpha is .93, which costs one paint.

   D. NO INLINE HAIRLINE LAYER. #area used to re-draw § 3's column rules
   because the pinned timeline buries the fixed one under an opaque ground.
   A picture does not carry the column layer anywhere else on this site, and
   the whole section is a picture now. The grid is still legible — the sheet
   is standing on two of its lines. */
.locale{
  /* SAMPLED AGAIN, off the NIGHT embed (2026-09-14). The two values below used
     to be the sage and cream Google's own parks and arterials came out as under
     the old partial desaturation. That map is gone: --map-night inverts the
     cartography, so what this chapter's ground has to match is not paper at all
     — it is the ink the landmass resolves to, with the road net the one light
     thing standing on it. The method is unchanged and it is the whole point:
     the chapter is not "a dark section that goes with a map", it is the map's
     own ink, which is what stops the embed reading as a foreign object. */
  --locale-land:#1B1B1C;          /* sampled: the inverted landmass          */
  --locale-net:#C9C7C1;           /* sampled: the road net standing on it    */
  --locale-ground:#151516;        /* the chapter's ground, under the embed   */
  /* INK, matched to the process plate (§ 20), because the page's two location
     plates have to be one object read twice — and it STAYS ink under the night
     grade, which § 1a reasons at length after trying the other way round. On an
     ink map the plate's edge is carried by its keyline rather than by its
     value, so the hairline it barely needs on paper is doing the whole job
     here.
     THE ALPHA IS UNCHANGED at .93: the layering is the composition, and a sheet
     the map cannot be seen through is just a box standing on a map. */
  --locale-sheet:rgba(16,16,17,.93);
  --locale-wash:rgba(16,16,17,.20);
  /* THE HAIRLINE AND § 23c's KEYLINE INVERT WITH THE CHAPTER. They used to be
     the page's ink walked one hue toward the map; on an ink ground an ink rule
     is not a rule at all, so both are now the page's PAPER at the same two
     weights, walked the same one hue toward the net the map is drawn in. Same
     jobs, same weights, opposite ink. */
  --rule:rgba(201,199,193,.22);
  --fr-line:rgba(201,199,193,.30);
  position:relative;isolation:isolate;
  display:grid;grid-template-columns:repeat(var(--grid-cols),1fr);
  align-items:center;
  padding:var(--s-7) var(--pad-frame);
  min-height:78svh;
  background:var(--locale-ground);
}
/* .locale sets its own display, which outranks the UA's [hidden]{display:none}
   — the same case as .btn (§ above) and .lhb-cta. The chapter is held out of
   the page with `hidden` on the section, 2026-09-14; remove the attribute in
   index.html to bring it back, nothing else here changed. */
.locale[hidden]{display:none}
/* the ground. Bleeds the full section on both axes — this is the "edge to
   edge" the brief asks for, and it is why nothing here is a max-block-size. */
.locale__map{position:absolute;inset:0;z-index:0;overflow:hidden;
  background:var(--locale-ground)}
/* THE WASH (§ 4b: a wash, not a veil). Flat and DARK now, at a fifth: under
   --map-night the embed's inverted landmass comes back a touch hotter than the
   chapter's own ink, and this settles it onto the page's black without
   touching the legibility of the white road net drawn on it. Flat is the whole
   point — a gradient here would be § 4's photographic veil, which § 4b
   withholds from a map, and over an inverted map it would close over the one
   thin layer carrying every street name. */
.locale__map::after{content:"";position:absolute;inset:0;z-index:1;
  pointer-events:none;background:var(--locale-wash)}
/* § 23c's keyline, closing the band top and bottom the way it closes a
   picture. Above the wash, which would otherwise dim its own frame. */
.locale__map::before{content:"";position:absolute;inset:0;z-index:2;
  pointer-events:none;box-shadow:inset 0 0 0 1px var(--fr-line)}


.locale__sheet{
  position:relative;z-index:4;
  /* THREE TRACKS AT EVERY DESKTOP WIDTH, and a two-track version was tried
     and dropped. Both payload rows cap out around 1330px, so at 1920 a
     three-column sheet does run 928px of paper around 380px of street — but
     the sheet's right edge lands on the SHORELINE there, and two tracks buy
     a tidier lockup at the cost of a 330px field of open sea between the
     sheet and the coast. The air inside the sheet is the brief's; the gap
     outside it was nobody's. */
  grid-column:1 / span 3;
  background-color:var(--locale-sheet);
  /* THE OUTLINE IS PAPER, NOT A HAIRLINE, as of 2026-09-14. Every other plate
     on this site draws its edge with --rule, and this one did too: § 1a
     resolves it to --rule-invert (paper at .18), which is the right weight for
     a plate standing on PAPER, where the fill is already doing the separating.
     On the night map the fill does none of that work — ink on ink — so .18 was
     the whole of what said "this is an object", and it was not enough.

     So the edge is stated rather than inherited here, and it is the page's
     paper at full strength: the one line in the chapter that is drawn rather
     than implied, which is what makes the card read as a card laid on the map
     instead of a darker patch of it. It is the SAME 1px inset box-shadow —
     the weight and the mechanism are unchanged, and it still lands on the
     sheet's own edge, which § 4c stands on the page's gridlines. Only the
     value moved.

     IT DOES NOT TOUCH --rule, deliberately. The chapter's hairline inside the
     sheet (.locale__rule, § 4a's one structural rule) has to stay a soft
     divider between the address and the claim — a white line there would make
     two plates out of one. So this is its own token and nothing else reads it.

     THE PROCESS PLATE IS NOT FOLLOWING. Its sheet stands on a LIT duotone,
     where --rule at .18 is still doing its job; the two plates stay one
     component by keeping the same mechanism at the same weight, not by
     carrying the same value onto grounds that are opposites. */
  --locale-keyline:var(--paper);
  box-shadow:inset 0 0 0 1px var(--locale-keyline);
  padding:var(--s-6) var(--inset);
}
.locale__sheet .eyebrow{margin-bottom:var(--s-5)}
.locale__addr{margin:0}
/* the two payload rows. Both are the hero's display voice (.display /
   .section-title state that); what is retuned here is SIZE, which § 3 of
   probe-system leaves free precisely so a chapter can compose. The floors are
   lifted off the page's ramp so the numeral still DOMINATES on a phone — at
   390px the site's own --t-display bottoms out at 44px, which is a heading,
   not the oversized numeral this composition is built on. */
.locale__num{display:block;font-size:clamp(5rem,10vw,8.25rem)}
.locale__street{display:block;font-size:clamp(2.5rem,5.4vw,4.5rem)}
/* the suburb NAMES a place, so it is the extended axis (§ 4's grammar:
   anything that speaks is condensed, anything that names is extended) */
/* THIS IS THE PROCESS PLATE'S LOCKUP, AT SECTION SCALE. The page already
   carries this address in this order inside the booking journey (.ps__sheet —
   number, street, town, hairline, way out), and the two are one component read
   at two sizes rather than two designs of the same lines. So the suburb is BODY
   voice in sentence case, the way .ps__card-tn is — it is the one line in the
   group that is read rather than declaimed — and the claim and the link are
   both § 4a's .note, the way .ps__card-go is.

   THE CLAIM IS THE ONE LINE THE TWO DO NOT SHARE, as of 2026-09-14. It stands
   HERE because this chapter is the first place on the page that says what the
   room is; it left the process card because by the foot of step 03 the journey
   has already shown it. Same component, one line of difference, and the
   difference is the position in the read rather than a second design. */
.locale__place{margin:var(--s-3) 0 0;
  font-size:clamp(1rem,1.5vw,var(--t-title));line-height:1.2;color:var(--ink-2)}
/* the display face is set at .80 (§ 4's --lh-display) and these two lines are
   a LOCKUP, not a paragraph: the step between them is spacing, which the
   composition owns, rather than leading, which the system does. */
.locale__street{margin-top:var(--s-2)}
.locale__rule{margin-block:var(--s-5) var(--s-4)}
.locale__claim{margin:0}
/* the way out, and it is a UTILITY and not an act: the .note voice on a
   hairline underline that fills in on hover — the same understatement the
   closing plate's second door is set in (§ 23b's .cta__aside). */
.locale__link{
  display:inline-flex;align-items:baseline;gap:.5em;
  margin-top:var(--s-5);color:var(--ink-2);text-decoration:underline;
  text-decoration-color:var(--rule);text-decoration-thickness:1px;
  text-underline-offset:.35em;
  transition:text-decoration-color var(--d-fast) ease,color var(--d-fast) ease;
}
.locale__link:hover,.locale__link:focus-visible{
  color:var(--ink-1);text-decoration-color:currentColor}
.locale__link i{font-style:normal}

@media (max-width:900px){
  /* --grid-cols is 2 below this width (§ 3), so "the first three tracks" is
     the whole page. The sheet keeps both outer gridlines; the map keeps the
     air above and below it, which is where the layering still reads. */
  .locale{grid-template-columns:1fr;min-height:0;padding-block:var(--s-7)}
  .locale__sheet{grid-column:1 / -1;padding:var(--s-5) var(--inset)}
  .locale__sheet .eyebrow{margin-bottom:var(--s-4)}
}

/* --- 6. Header — M6 ------------------------------------------------------
   Two children now, not three: the booking plate lives beside this element
   rather than inside it. § 6a says why. */
.header{
  position:fixed;top:0;left:0;right:0;z-index:60;height:var(--header-h);
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  /* the bar's own border-bottom runs edge to edge — the bar is frame. What it
     HOLDS is type, so the trigger and the plate stand off the outer line by
     the same inset every headline under them does (§ 5a). */
  padding-inline:var(--pad-type);
  color:var(--paper);
  background:transparent;border-bottom:1px solid transparent;
  transition:background var(--d-fast) ease,border-color var(--d-fast) ease,color var(--d-fast) ease;
}
.header.is-stuck{background:var(--paper);color:var(--ink);border-bottom-color:var(--rule)}
.header__mark{justify-self:center;font-weight:var(--wght-label);
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label);
  font-size:1.0625rem;letter-spacing:var(--track-mark);
  text-transform:uppercase;white-space:nowrap}
/* --- 6a. The booking word, in the header's right slot ---------------------
   It is a CHILD of .header again. It was a glass .btn and a sibling, because a
   backdrop-filtered pane cannot survive the header's mix-blend-mode:exclusion
   group (menu.css § 7); it is now the word BOOK in a hairline square, so it
   takes the blend the way the trigger and the wordmark do and needs no ground
   of its own. Third grid item of three (menu.css § 2), justified to the end,
   which leaves the wordmark optically centred.

   The border is 1px — the same hairline as the trigger's bars and the column
   rules. That is the whole object: type, and one line around it. */
.header__book{
  justify-self:end;
  display:inline-flex;align-items:center;justify-content:center;
  min-height:36px;padding:0 var(--s-3);
  border:1px solid currentColor;
  color:inherit;text-decoration:none;white-space:nowrap;
  font-size:var(--t-micro);font-weight:var(--wght-label);
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label);
  text-transform:uppercase;letter-spacing:var(--track-label);
  transition:background var(--d-fast) ease,color var(--d-fast) ease,
             border-color var(--d-fast) ease;
}
/* Hover fills the square. Paper face / ink label are the header's own two
   values, so exclusion inverts the filled box against the ground exactly as it
   already inverts the label — over a photograph it reads paper, over the paper
   sections it reads ink. No third colour enters the blend (menu.css § 8). */
.header__book:hover,.header__book:focus-visible{
  background:var(--paper);color:var(--ink);border-color:var(--paper);
}
.header__book:active{opacity:.85}

/* The trigger label carries the wordmark's axes. Both are Archivo; what made
   them read as two faces is that this one inherited body's
   font-variation-settings ("wdth" 100,"wght" 400) — a low-level property that
   outranks font-weight. Same move as § 7's .btn and the .lhb-cta label, so the
   whole call-to-action voice is one face. Tracking stays per-element: .16em on
   the 17px mark against .08em at 14px is the same optical fit, not a second
   typeface. */
.menu-trigger{display:flex;align-items:center;gap:var(--s-2);background:none;border:0;
  cursor:pointer;min-height:44px;padding:0;font-size:var(--t-micro);
  font-weight:var(--wght-label);
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label);
  text-transform:uppercase;letter-spacing:var(--track-label)}
.menu-trigger__bars{display:grid;gap:4px;width:20px}
.menu-trigger__bars i{display:block;height:1px;background:currentColor}
@media (max-width:640px){
  .header__mark{font-size:.75rem;letter-spacing:.1em}
  .menu-trigger{font-size:0;gap:0}          /* bars only — the label is redundant at this width */
  .menu-trigger__bars{width:22px}
  .header__book{padding:0 var(--s-2)}
}

/* --- 7. Buttons — the pressed plate ---------------------------------------
   Every CTA is now one object: a square plate, bevelled by a 3px ring that
   holds a shadow at its head and a pool of light at its foot, with a gold
   sheen that only rises under the pointer. The geometry does not change —
   border-radius stays 0, the 44px minimum stays, the padding stays.

   TWO PALETTES, ONE PLATE
     .btn (default) / .btn--on-light   ink plate on paper   — dark on light
     .btn--invert   / .btn--on-dark    paper plate on ink   — light on dark
   Dark grounds (the hero, the closing CTA, the footer, the header before it
   sticks) claim the inverted palette on their own, so the modifier is only
   needed for a light plate that sits somewhere the CSS cannot see is dark.

   HOW THE THREE LAYERS STACK   the plate isolates, so both pseudo-elements
   sit at z-index:-1 — above the plate's own face, below its label. ::before
   is masked hollow, so the ring paints only outside the face; ::after is the
   sheen, clipped to the foot. Nothing paints over the type.

   TWO TIERS. This plate is the PRIMARY. The secondary is § 7a's .btn--glass,
   which is the footer booking cell's material carried onto the same box — it
   reuses this section's --btn-face / --btn-label palette and replaces both
   pseudo-elements, so read the two together before changing either. */

/* the foot light is a colour inside a gradient, and gradients do not
   interpolate — registering the property is what lets it fade. Browsers
   without @property simply switch it at once. */
@property --btn-foot{syntax:"<color>";inherits:true;initial-value:transparent}

/* .btn sets its own display, which outranks the UA's [hidden]{display:none}
   — so `hidden` on a button did nothing at all. It matters on the Work
   pane (§ 19), where an empty pane now lands in the middle of the screen
   and must not offer to book a look nobody has picked yet. */
.btn[hidden]{display:none}

.btn{
  /* ---- palette: dark on light ---- */
  --btn-face:var(--ink);
  --btn-label:var(--paper);
  --btn-edge:rgba(243,241,236,.16);
  --btn-edge-lit:rgba(176,138,46,.55);
  --btn-inset:
    inset 0 1px 1px rgba(255,255,255,.16),
    inset 0 2px 2px rgba(255,255,255,.11),
    inset 0 4px 4px rgba(255,255,255,.07),
    inset 0 8px 8px rgba(255,255,255,.045),
    inset 0 16px 16px rgba(255,255,255,.03);
  --btn-drop:
    0 -1px 1px rgba(16,16,17,.02),
    0 -2px 2px rgba(16,16,17,.03),
    0 -4px 4px rgba(16,16,17,.05),
    0 -8px 8px rgba(16,16,17,.06),
    0 -16px 16px rgba(16,16,17,.08);
  --btn-ring-head:rgba(16,16,17,.62);
  --btn-foot-rest:rgba(255,255,255,.14);     /* the foot light at rest */
  --btn-foot-lit:rgba(255,255,255,.72);      /* under the pointer */
  --btn-foot:var(--btn-foot-rest);
  --btn-glow:var(--gold);
  --btn-glow-soft:rgba(176,138,46,.5);
  --btn-label-glow:rgba(176,138,46,.55);
  --btn-sheen:0;
  --btn-bevel:3px;

  position:relative;isolation:isolate;
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;padding:0 var(--s-4);
  /* The wordmark's axes (§ 6). font-weight:700 alone never reached the face:
     font-variation-settings is a low-level property and body's inherited
     "wght" 400 outranked it, so every plate on the site was rendering regular
     at normal width. Declared here, the label matches the header lockup and
     the .lhb-cta pill. The footer's booking CTA is not a .btn — it is
     .band__cta, SVG lettering (§ 16) — and is deliberately left alone. */
  font-size:var(--t-micro);font-weight:var(--wght-label);text-transform:uppercase;
  letter-spacing:var(--track-label);
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label);
  border:1px solid var(--btn-edge);border-radius:0;cursor:pointer;
  background:var(--btn-face);color:var(--btn-label);
  box-shadow:var(--btn-inset),var(--btn-drop);
  transition:background var(--d-fast) ease,color var(--d-fast) ease,
             border-color var(--d-fast) ease,box-shadow var(--d-fast) ease,
             text-shadow var(--d-fast) ease;
}
/* a custom property inside transition-property invalidates the whole
   declaration where it is not understood, so the foot fade is added apart */
@supports (transition-property:--btn-foot){
  .btn{transition:background var(--d-fast) ease,color var(--d-fast) ease,
                  border-color var(--d-fast) ease,box-shadow var(--d-fast) ease,
                  text-shadow var(--d-fast) ease,--btn-foot var(--d-fast) ease}
}
/* the shared focus ring clears the bevel instead of landing on it */
.btn:focus-visible{outline-offset:5px}

/* the ring — hollow, so it can never wash over the face it surrounds */
@supports (mask-composite:exclude) or (-webkit-mask-composite:xor){
  .btn::before{
    content:"";position:absolute;z-index:-1;pointer-events:none;
    inset:calc(var(--btn-bevel) * -1);padding:var(--btn-bevel);
    background-image:linear-gradient(0deg,var(--btn-foot),var(--btn-ring-head));
    box-shadow:1px 1px 1px rgba(255,255,255,.12),-1px -1px 1px rgba(16,16,17,.12);
    -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
    mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
    -webkit-mask-composite:xor;mask-composite:exclude;
  }
}

/* the sheen — gold licking up from the foot of the plate, nothing at rest */
.btn::after{
  content:"";position:absolute;z-index:-1;inset:0;pointer-events:none;
  background-image:linear-gradient(0deg,#fff 0,var(--btn-glow) 4%,
                                   var(--btn-glow-soft) 9%,transparent 22%);
  opacity:var(--btn-sheen);
  transition:opacity var(--d-fast) ease,filter var(--d-fast) ease;
}

.btn:hover,
.btn:focus-visible{
  --btn-foot:var(--btn-foot-lit);
  --btn-sheen:1;
  border-color:var(--btn-edge-lit);
  text-shadow:0 0 12px var(--btn-label-glow);
}
.btn:active{--btn-foot:#fff;border-color:var(--btn-edge-lit)}
.btn:active::after{filter:brightness(1.6)}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.6;cursor:not-allowed;
  --btn-sheen:0;--btn-foot:var(--btn-foot-rest);text-shadow:none}

/* ---- palette: light on dark — a paper plate, lit the same way ------------
   The bevel cannot be white on white, so the head of the ring goes light and
   the plate takes its depth from ink at the foot instead. */
.btn--invert,.btn--on-dark,
.hero .btn,.cta .btn,.footer .btn{
  --btn-face:var(--paper);
  --btn-label:var(--ink);
  --btn-edge:rgba(16,16,17,.14);
  --btn-edge-lit:rgba(176,138,46,.8);
  --btn-inset:
    inset 0 1px 1px rgba(255,255,255,.9),
    inset 0 2px 4px rgba(255,255,255,.5),
    inset 0 -1px 1px rgba(16,16,17,.07),
    inset 0 -12px 16px rgba(16,16,17,.05);
  --btn-drop:
    0 -1px 1px rgba(16,16,17,.10),
    0 -2px 2px rgba(16,16,17,.10),
    0 -4px 6px rgba(16,16,17,.12),
    0 -8px 10px rgba(16,16,17,.14),
    0 -16px 20px rgba(16,16,17,.16);
  --btn-ring-head:rgba(243,241,236,.10);
  --btn-foot-rest:rgba(243,241,236,.22);
  --btn-foot-lit:rgba(255,255,255,.9);
  --btn-label-glow:rgba(176,138,46,.7);
}

/* The header used to flip this plate back to the light palette on .is-stuck.
   It no longer can, and no longer should: the booking plate is a sibling of
   the header (§ 6a), so it never carries .is-stuck — and menu.css § 7 keeps
   the header transparent on every state it has, stuck included, so there is
   no paper ground under it to flip for. One palette, the inverted one, at
   every scroll position. */

/* ---- 7a. the quiet plate, in glass — the SECONDARY CTA -------------------
   The footer's booking cell (§ 16b .band__cta) is the site's best button and
   it was a one-off. This is that object, made portable, and it is now what
   every secondary CTA is made of — the header's Book and "Follow along" on
   story.html. (The "Add more cuts" / "Clear" pair it also names went with the
   Ideal Look on 2026-09-12, and book.html itself on 2026-09-15.) Primary CTAs
   are untouched: the hero keeps .lhb-cta and the
   inline .btn keeps the pressed plate with its gold sheen. Two tiers, two
   materials, and the difference is now legible at a glance.

   WHAT WAS TAKEN OFF .band__cta — the numbers are its --fb-* tokens verbatim
     REFRACT   the pane reads the pixels behind it: blur 14 / saturate 1.18 /
               brightness 1.28 over ink. On the element itself, because
               backdrop-filter cannot live on a pseudo without also taking the
               pseudo's own paint into the filter.
     FROST     a RAMP, not a wash — transparent at the bottom-left, full at the
               top-right, on the 46deg light axis. This is what pins the light
               source, and it is the one thing that stops the pane reading as
               a flat translucent box.
     TOOTH     ground glass: one specular pass with the light, one weak return
               against it, and the same 170px turbulence tile the Work plate
               and the footer are cut from.
     TELLS     four inset shadows: lit top edge, shadowed foot, inner depth,
               and the hairline that IS the pane's edge (the border is held
               transparent so the hairline draws it and can fade with the rest).
     FLUTES    § 23's law: pitch is a LENGTH, not a count. --rib stays at the
               site's 22px, so a 110px header chip is cut from the same sheet
               as a 900px poster cell rather than being a scaled copy of it.
     HOVER     the glass CLEARS. Background goes solid, the label inverts, and
               all four glass layers fade to nothing over --d-fast. The
               backdrop-filter is written as the full identity chain on both
               sides — blur(0px) saturate(1) brightness(1), never `none` —
               because a filter list only interpolates against the same
               functions in the same order. `none` snaps.
     PRESS     opacity .85, and nothing else. Measured off the footer.

   WHAT DID NOT COME ACROSS, and why
     The growing underline and the sliding arrow. Both are typographic devices
     of the BOOK lockup, not of the material: --cta-rule-w-hover is literally
     the headline's ink width, and there is no headline on a 44px chip to
     measure one against. Carrying them would need a third paint layer and
     would put a rule inside a box whose foot is already drawn by --gb-foot.
     The material transferred; the poster's furniture stayed in the poster.

   TWO GROUNDS, ONE PANE. Default is the light tuning — an ink veil over
   paper, since paper frosted over paper is nothing. Dark grounds (hero, the
   closing CTA, the footer, the header before it sticks) claim the footer's
   own paper-over-ink numbers on their own, exactly as the pressed plate does.

   .btn--ghost is kept as an alias so no stray copy of the old name regresses. */

.btn--glass,
.btn--ghost,
.menu__lang-btn{
  /* ---- the sheet: light tuning (ink veil over paper) ---- */
  --gb-blur:14px;
  --gb-sat:1.18;
  --gb-bright:.96;                     /* darken, where the dark pane also darkens */
  --gb-frost-rgb:16,16,17;
  --gb-frost:.06;
  --gb-light:46deg;                    /* the site's light source: upper right */
  --gb-spec-hi:.20;                    /* specular with the light  */
  --gb-spec-lo:.08;                    /* the weak return against it */
  --gb-flute-hi:.30;                   /* paper: the specular does the work... */
  --gb-flute-lo:.16;                   /* ...and the seam whispers */
  --gb-tells:
    inset 0 1px 0 rgba(255,255,255,.55),
    inset 0 -1px 0 rgba(16,16,17,.10),
    inset 0 0 26px rgba(16,16,17,.05),
    inset 0 0 0 1px rgba(16,16,17,.13);
  --gb-glass:1;                        /* 1 at rest, 0 once cleared */

  /* PITCH. § 23 states the law: --rib is a LENGTH, so two PANES are cut from
     one sheet instead of being scaled copies. A 44px chip is not a pane. The
     footer cell is ~745px wide and carries ~34 ribs — a corduroy; the header
     chip is ~103px and the same 22px pitch gives it FOUR, which reads as
     stripes painted on a box, not as fluted glass. So the chip is cut finer,
     to land in the same ribs-per-eyeful the footer has. The departure is from
     the law's letter, in service of the thing the law exists to protect. */
  --rib:9px;
  --rib-edge:8%;

  /* ---- the plate's own knobs, withdrawn ---- */
  --btn-face:rgba(16,16,17,.05);       /* the body of the slab; the blur is the rest */
  --btn-label:var(--ink);
  --btn-edge:transparent;              /* the hairline tell draws the edge */

  /* .btn already carries both; the switch cell (menu.css § 9) does not, and
     without them the two pseudo-elements at z-index:-1 fall behind the ground
     they are supposed to be lying on. Stated here so the material is portable
     to anything, not just to a .btn. */
  position:relative;isolation:isolate;

  box-shadow:none;                     /* the plate's inset+drop; the pane casts nothing */
  backdrop-filter:blur(var(--gb-blur)) saturate(var(--gb-sat)) brightness(var(--gb-bright));
  -webkit-backdrop-filter:blur(var(--gb-blur)) saturate(var(--gb-sat)) brightness(var(--gb-bright));
  transition:background var(--d-fast) ease,color var(--d-fast) ease,
             border-color var(--d-fast) ease,opacity var(--d-fast) ease,
             backdrop-filter var(--d-fast) ease;
}

/* THE RIBS — on ::before, and the swap is deliberate. The mask that releases
   the flutes at both edges applies to the whole pseudo-element, so on ::after
   it would also cut the tells' inset shadows, and the hairline that draws the
   pane's edge would go missing at the two ends. Guarded by the same @supports
   as the plate's bevel ring, which this replaces: where masks are missing the
   ::before simply never generates, rather than painting unreleased stripes. */
@supports (mask-composite:exclude) or (-webkit-mask-composite:xor){
  .btn--glass::before,
  .btn--ghost::before,
  .menu__lang-btn::before{
    content:"";position:absolute;z-index:-1;pointer-events:none;
    inset:0;padding:0;
    box-shadow:none;
    opacity:var(--gb-glass);
    background-image:repeating-linear-gradient(90deg,
      rgba(255,255,255,0) 0,
      rgba(255,255,255,var(--gb-flute-hi)) calc(var(--rib) * .07),
      rgba(255,255,255,0) calc(var(--rib) * .34),
      rgba(16,16,17,0) calc(var(--rib) * .62),
      rgba(16,16,17,var(--gb-flute-lo)) calc(var(--rib) * .94),
      rgba(16,16,17,0) var(--rib));
    /* the shorthand, not mask-image: it resets the composite, clip and origin
       the plate's ring left behind (content-box + xor), which would otherwise
       inset the ribs by the border and punch a hole through them. */
    -webkit-mask:linear-gradient(90deg,transparent 0,#000 var(--rib-edge),
      #000 calc(100% - var(--rib-edge)),transparent 100%);
    mask:linear-gradient(90deg,transparent 0,#000 var(--rib-edge),
      #000 calc(100% - var(--rib-edge)),transparent 100%);
    transition:opacity var(--d-fast) var(--ease-out);
  }
}

/* FROST + TOOTH + TELLS — one layer, in the footer's paint order. */
.btn--glass::after,
.btn--ghost::after,
.menu__lang-btn::after{
  content:"";position:absolute;z-index:-1;inset:0;pointer-events:none;
  opacity:var(--gb-glass);
  filter:none;                         /* the plate's :active brightness pass */
  background-image:
    linear-gradient(var(--gb-light),
      rgba(var(--gb-frost-rgb),0) 0%,
      rgba(var(--gb-frost-rgb),calc(var(--gb-frost) * .4)) 46%,
      rgba(var(--gb-frost-rgb),var(--gb-frost)) 100%),
    linear-gradient(var(--gb-light),rgba(255,255,255,0) 66%,
      rgba(255,255,255,var(--gb-spec-hi)) 100%),
    linear-gradient(calc(var(--gb-light) + 180deg),
      rgba(255,255,255,var(--gb-spec-lo)) 0%,rgba(255,255,255,0) 26%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='170' height='170'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.86' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='170' height='170' filter='url(%23g)' opacity='.14'/%3E%3C/svg%3E");
  background-size:auto,auto,auto,var(--fb-tile) var(--fb-tile);
  box-shadow:var(--gb-tells);
  transition:opacity var(--d-fast) var(--ease-out);
}

/* ---- the clear ---------------------------------------------------------- */
.btn--glass:hover,.btn--glass:focus-visible,
.btn--ghost:hover,.btn--ghost:focus-visible{
  --gb-glass:0;
  --btn-face:var(--ink);--btn-label:var(--paper);
  border-color:transparent;
  text-shadow:none;                    /* the plate's gold label glow */
  backdrop-filter:blur(0px) saturate(1) brightness(1);
  -webkit-backdrop-filter:blur(0px) saturate(1) brightness(1);
}
.btn--glass:active,.btn--ghost:active{opacity:.85;border-color:transparent}
.btn--glass:active::after,.btn--ghost:active::after{filter:none}
.btn--glass:disabled,.btn--glass[aria-disabled="true"],
.btn--ghost:disabled,.btn--ghost[aria-disabled="true"]{--gb-glass:1}

/* ---- dark grounds: the footer's own numbers, unchanged ------------------ */
.btn--invert.btn--glass,.btn--on-dark.btn--glass,
.hero .btn--glass,.cta .btn--glass,.footer .btn--glass,
.btn--invert.btn--ghost,.btn--on-dark.btn--ghost,
.hero .btn--ghost,.cta .btn--ghost,.footer .btn--ghost,
.menu__lang-btn{                       /* the panel is --ink, like the footer */
  /* The footer's --fb-bright is 1.28 and it is doing nothing there: its
     backdrop is #101011, so 1.28x lands at #141416. Behind a header chip is a
     PHOTOGRAPH, and 1.28x on that is a white slab with a word on it. What
     makes the footer pane read as one dark ribbed object is its ink ground,
     not its brightness — so the chip carries the ink as its own face and the
     backdrop is sunk, not lifted. Same object, on any ground. */
  --gb-bright:.92;
  --gb-frost-rgb:243,241,236;
  --gb-frost:.075;
  --gb-spec-hi:.13;
  --gb-spec-lo:.06;
  --gb-flute-hi:.09;                   /* ink: the specular whispers... */
  --gb-flute-lo:.22;                   /* ...and the seam does the work */
  --gb-tells:
    inset 0 1px 0 rgba(255,255,255,.16),
    inset 0 -1px 0 rgba(16,16,17,.5),
    inset 0 0 26px rgba(243,241,236,.05),
    inset 0 0 0 1px rgba(255,255,255,.05);
  --btn-face:rgba(16,16,17,.55);
  --btn-label:var(--paper);
}
/* over dark ground the pane clears to paper, not ink — clearing to ink there
   is the button disappearing into the photograph. */
.hero .btn--glass:hover,.hero .btn--glass:focus-visible,
.cta .btn--glass:hover,.cta .btn--glass:focus-visible,
.footer .btn--glass:hover,.footer .btn--glass:focus-visible,
.btn--invert.btn--glass:hover,.btn--invert.btn--glass:focus-visible,
.btn--on-dark.btn--glass:hover,.btn--on-dark.btn--glass:focus-visible,
.hero .btn--ghost:hover,.hero .btn--ghost:focus-visible,
.cta .btn--ghost:hover,.cta .btn--ghost:focus-visible,
.footer .btn--ghost:hover,.footer .btn--ghost:focus-visible,
.btn--invert.btn--ghost:hover,.btn--invert.btn--ghost:focus-visible,
.btn--on-dark.btn--ghost:hover,.btn--on-dark.btn--ghost:focus-visible{
  --btn-face:var(--paper);--btn-label:var(--ink);
}

/* THE HEADER DOES NOT SWAP GROUND. The pressed plate above flips to the light
   tuning on .is-stuck because system.css:165 gives the stuck header a paper
   background — but menu.css § 7 overrides that back to transparent on every
   state the header has, stuck included. The chrome floats over whatever the
   page put there: a photograph on services.html, paper two sections later.
   So the pane keeps ONE tuning throughout — the dark slab, which is the
   footer's own object and is legible on both. A tuning that switched on
   .is-stuck would be switching on a ground that is not there. */

@media (prefers-reduced-motion:reduce){
  .btn,.btn::after,.btn--glass::before,.btn--ghost::before{transition:none}
}

/* --- 8. Menu overlay ----------------------------------------------------- */
.menu{position:fixed;inset:0;z-index:65;background:var(--paper);
  padding:var(--header-h) var(--gutter) var(--s-6);
  display:grid;grid-template-rows:1fr auto;
  opacity:0;visibility:hidden;
  transition:opacity var(--d-fast) ease,visibility var(--d-fast) ease}
.menu.is-open{opacity:1;visibility:visible}
.menu__list{align-self:center;display:grid;gap:var(--s-2)}
/* the overlay's nav is a typographic MONUMENT (design-system.md § 5) — it is
   set at display scale, so it takes the display voice like every other thing
   on this site that is set at display scale. § 07 of the design-system brief:
   one navigation, changing state; not a second header with its own type. */
.menu__list a{font-size:var(--t-section);font-weight:var(--wght-display);
  line-height:var(--lh-display);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display);
  text-transform:uppercase;letter-spacing:var(--track-display);color:var(--ink-soft);
  transition:color var(--d-fast) ease}
.menu__list a:hover,.menu__list a[aria-current="page"]{color:var(--ink)}
.menu__foot{display:flex;flex-wrap:wrap;gap:var(--s-5);justify-content:space-between;
  border-top:1px solid var(--rule);padding-top:var(--s-4)}

/* --- 9. Price table ------------------------------------------------------ */
.pricing{display:grid;grid-template-columns:repeat(3,1fr);border:1px solid var(--rule)}
.pricing>*{min-width:0}
/* every cell of the table is a cell of the system: its type stands one inset
   off the divider that opens it and off the hairline above it (§ 5a) */
.price-col{padding:var(--inset);border-left:1px solid var(--rule)}
.price-col:first-child{border-left:0}
.price-row{display:flex;justify-content:space-between;gap:var(--s-4);
  padding-block:var(--inset);border-top:1px solid var(--rule);font-variant-numeric:tabular-nums}
.price-row:first-of-type{border-top:0}
.price-row__price{font-weight:700}
/* THE MENU VARIANT — the home page's booking table (index.html #services).
   The same table, with the one difference that matters: every row IS the
   Fresha booking link, so a row is an <a> that keeps the table's own type
   rather than a link's — name left, price right, exactly as § 9 sets them.
   The plate (§ 9g) and its arithmetic (js/services.js) are shared unchanged;
   only the reveal differs, and that is behaviour, not layout. */
.pricing--menu .price-row{text-decoration:none;color:inherit}
@media (max-width:900px){
  .pricing{grid-template-columns:1fr}
  .price-col{border-left:0;border-top:1px solid var(--rule)}
  .price-col:first-child{border-top:0}
}

/* --- 9b. (was THE SERVICE INDEX) -----------------------------------------
   The home page's booking menu was set here as numbered full-width rows —
   index / name / duration / price / arrow, one per line. On 2026-09-15 it
   took the price table's layout instead (§ 9, .pricing--menu), so the two
   pages that list the same eight services list them in one grammar and this
   second one had nothing left to style. Removed rather than kept warm: the
   hover, plate and reveal rules that referenced it all live in § 9g, and
   they name .price-row only now. */

/* --- 9c. THE REVIEW WALL — proof that never stops arriving ----------------
   bar-flow.md M6 asked for attributed proof. It used to be six cards in one
   bordered grid, which said "here are six reviews". The shop has 264, and the
   argument is not any one of them — it is that they keep coming. So the six
   cards became a WALL: three columns of tiles running continuously past a
   window, alternating a photograph with a client's own words.

   THE SEPARATION IS THE STRUCTURE. Tiles do not butt together. Each stands a
   full gutter off the next (--wall-sep) with ONE hairline floating in the
   middle of that gutter — air, rule, air. This is the editorial device the
   rest of the site already uses vertically, turned on its side: a rule that
   SEPARATES rather than a rule that underlines. It is also what makes the
   alternation legible at a glance. A photograph and a review that share an
   edge read as one object with a line through it; a photograph and a review
   a gutter apart read as two things in a grid, which is what they are.
   The COLUMN rules stay 1px and hard, because those are the page's own
   gridlines (§ 3) and have to keep landing on them.

   WHY IT READS AS ONE OBJECT AND NOT AS THREE SLIDERS
   Every tile on the wall occupies the same height, and every column is driven
   at the same PIXELS PER SECOND — one tile per --wall-beat, in every column,
   in both directions. Identical speed is the whole trick: the eye reads a
   shared velocity as one surface moving, and reads three different velocities
   as three components that happen to be adjacent. The centre column runs the
   other way, which at a shared speed gives a relative motion of exactly 2x
   between neighbours — enough that the lattice is always re-dealing itself,
   never enough to look like it is racing.

   WHY THE SEAM IS INVISIBLE
   wall.js appends a second copy of each column's tiles and wraps the offset by
   exactly one copy, so the frame after the last tile is the first tile at the
   same offset with the same gutter under it. There is no reset to see.
   Tiles carry their gutter as their OWN margin for that reason: the track is
   then exactly two copies with no leftover, and one copy is a clean wrap.

   THE MOTION IS DRIVEN IN SCRIPT, not by an animation, because the reader can
   take hold of it — see wall.js. The stylesheet owns the measurements and the
   two states it can be in; the position itself belongs to the pointer.

   THE PHOTOGRAPHY takes the cuts band's grade (§ 13, grayscale + a touch of
   contrast) so a set shot across four years on four cameras reads as one
   commission, and so the only colour in the section is the gold the stars
   carry. The client's words are set in --ink-1, not the --ink-2 body voice:
   in this section the client IS the statement.
   The motion stops for `prefers-reduced-motion` and on a phone — see the foot
   of this section, where the wall resolves into the static grid it is made of. */
.wall{
  /* one tile, everywhere. The floor is set by the LONGEST quote on record at
     the narrowest three-column viewport, not by the picture. */
  --wall-tile:clamp(300px,20vw,330px);
  /* the gutter between two tiles in a column. Wide enough to be a DECISION —
     at a hairline's distance the eye reads a seam, at this one it reads a
     grid. */
  --wall-sep:clamp(22px,2vw,34px);
  --wall-beat:9s;            /* seconds for ONE tile to cross. ~35px/s: slower
                                than a read, so nothing is ever taken away.
                                wall.js mirrors this number — change both. */
  /* THE TONE, held constant across the focal plane below: only COLOUR is
     animated, never the development. grayscale(1) + this is the wall's resting
     grade, and grayscale(0) + this is the same photograph in colour — so the
     two ends of the ramp are one development, not two looks. Read by wall.js
     rather than restated there. */
  --wall-tone:saturate(.92) contrast(1.12) brightness(.94);
  /* THE AXIS, and this stylesheet is the ONLY authority on it. wall.js reads
     this property instead of keeping a breakpoint of its own, so the width at
     which the wall turns on its side is stated once, here, and the script
     cannot disagree with the layout it is driving.
       y     the desktop wall — three columns running past a window
       x     the phone's lanes — the same wall laid on its side (9c-i)
       none  a wall that has stopped, and is read as the grid it is made of  */
  --wall-axis:y;
  position:relative;
  display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:100%;
  gap:1px;background:var(--rule);border:1px solid var(--rule);
  /* a fraction of a tile, never a whole number of them — a window that showed
     exactly N tiles would read as a static grid between moves */
  height:min(calc((var(--wall-tile) + var(--wall-sep)) * 2.3),78vh);
  overflow:hidden}
.wall__col{position:relative;min-width:0;min-height:0;overflow:hidden;
  background:var(--paper)}
.wall__track{display:flex;flex-direction:column;will-change:transform}

/* --- the hold ------------------------------------------------------------
   The wall is draggable, so it says so: the column carries the grab cursor and
   takes the closed hand while it is held. Scoped to the widths where the wall
   actually moves — under 768 it is a static stack and a grab cursor there
   would be a promise nothing keeps.
   There is deliberately NO hover pause. A wall that stops whenever the pointer
   crosses it is a wall the reader cannot cross without changing it; now the
   pointer only changes it when the reader means it to, by taking hold. */
@media (min-width:768px) and (prefers-reduced-motion:no-preference){
  .wall.is-looped .wall__col{cursor:grab;touch-action:auto}
  .wall.is-dragging .wall__col{cursor:grabbing}
  /* only while a column is actually held — the quotes stay selectable the rest
     of the time, which matters for words that are somebody's own */
  .wall.is-dragging{user-select:none;-webkit-user-select:none}
}

/* --- the tile ------------------------------------------------------------
   Height is fixed and identical for both kinds, because the wrap arithmetic
   depends on it and because the checkerboard only reads if the bands line up.
   THE GUTTER IS THE TILE'S OWN MARGIN, not a `gap` on the track: n tiles then
   occupy exactly n x (tile + gutter) including the last one, so half the
   doubled track is exactly one copy. A `gap` is the classic bug here — n tiles
   have n-1 internal gaps, so one copy is a gutter short and the wall hitches
   once per cycle, forever. */
.wall-tile{margin:0 0 var(--wall-sep);flex:none;position:relative;
  height:var(--wall-tile);background:var(--paper)}
/* the rule, floating in the middle of the gutter below the tile. It is drawn
   from the tile rather than between tiles so that it wraps with them. */
.wall-tile::after{content:"";position:absolute;left:0;right:0;
  bottom:calc(-1 * var(--wall-sep) / 2);height:1px;background:var(--rule)}
/* THE GRADE is the cuts band's (§ 13) carried across, then deepened by a
   stop. The rail's photographs sit on an ink ground, which holds them; these
   sit on PAPER, with nothing behind them to push against, and at the rail's
   own contrast they float off the page instead of being set into it.
   object-position is high rather than centred because six of the seven are
   portraits in a portrait frame: cropped to a landscape tile from the middle,
   a face loses its forehead and keeps the chest.

   THE FOCAL PLANE — § 22's device (app.js paintFocus), stood up vertically.
   The grayscale here is the RESTING end of a ramp: wall.js hands every
   photograph a grayscale amount read off its own centre, so a band of colour
   is held across the middle of the wall that the columns travel THROUGH. It is
   a band, not an animation on the images — nothing is on a timer, nothing has a
   seam, and it is correct on the first paint and after any re-measure, because
   it is a pure function of the same offset the wrap is taken from.
   Only COLOUR moves. The horizontal rail (§ 13) also spends opacity and scale
   on this, because its frames float in a row with gaps between them and can
   afford to recede; these butt into a grid, and dimming one would punch a hole
   in the surface rather than push it back. § 22's rails are the vertical
   precedent and they animate colour alone for the same reason. */
.wall-tile--shot{overflow:hidden}
.wall-tile--shot img{display:block;width:100%;height:100%;object-fit:cover;
  object-position:50% 35%;
  filter:grayscale(1) var(--wall-tone);
  /* a held column must not hand the browser its own image-drag instead */
  -webkit-user-drag:none;user-select:none}
/* head / words / attribution — three zones, stated as three rows so the tile
   has a structure rather than a stack. The 1fr is what makes it feel
   unhurried: the quote sits directly under the head and the air falls to the
   FOOT, above the metadata, which is the reference's own vertical rhythm. */
.wall-tile--word{display:grid;grid-template-rows:auto 1fr auto;
  padding:var(--inset);gap:var(--s-4)}
.wall-tile__head{display:flex;align-items:center;gap:var(--s-3)}
/* a hairline SQUARE, not a circle — radius is 0 everywhere on this site but
   the book pill (design-system § 5) */
.wall-tile__avi{flex:none;width:38px;height:38px;border:1px solid var(--rule);
  display:grid;place-items:center;
  font-size:var(--t-micro);letter-spacing:var(--track-label);
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label);
  font-weight:var(--wght-label);color:var(--ink-1)}
.wall-tile__stars{font-size:var(--t-micro);letter-spacing:.25em;
  color:var(--gold);line-height:1}
.wall-tile__quote{margin:0;align-self:start}
.wall-tile__quote p{margin:0;color:var(--ink-1);
  font-size:clamp(.9375rem,1.05vw,1.125rem);line-height:1.5;
  max-width:32ch;text-wrap:pretty;
  /* the marks are drawn, not typed: the text node stays exactly the string the
     client left, so a quote copied off this page is still verbatim */
  text-indent:-.36em}
.wall-tile__quote p::before{content:"\201C"}
.wall-tile__quote p::after{content:"\201D"}
/* the attribution closes the tile against its own foot rule, so every review
   ends on the same line whatever length its words ran to */
.wall-tile__meta{margin:0;align-self:end;
  font-size:var(--t-micro);text-transform:uppercase;
  letter-spacing:var(--track-label);color:var(--ink-3);
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label);
  font-weight:var(--wght-label)}

/* --- when the wall stands still ------------------------------------------
   Reduced motion and the phone get the SAME answer, and it is not a degraded
   one: the wall resolves into the lattice it was made of — the static
   checkerboard, every review present and readable, nothing clipped. The second
   copy is the only thing that goes, along with the last gutter rule, which
   would otherwise be a line floating under the end of the section. */
/* NOTE ON THE STATIC STATES BELOW. The rail's `is-static` resolves to FULL
   COLOUR (§ 13: the grey is .is-marquee's alone) and this one
   deliberately does not. The rail's default is colour and grey is its
   de-emphasis, so with nothing travelling it gives every frame its default
   back. The wall's default is GREY — the monochrome foundation the section is
   composed on — and colour is the reward for crossing the band. Resolving to
   colour here would hand a reader who asked for stillness more emphasis than a
   reader who did not, which is backwards. Standing still means every
   photograph at the resting end of the ramp, which is the grade stated above,
   and the section keeps one identity in both states. */
@media (prefers-reduced-motion:reduce){
  .wall{--wall-axis:none;height:auto;grid-template-rows:auto}
  .wall__col{overflow:visible;cursor:auto}
  .wall__track{transform:none!important}
  .wall [data-wall-clone]{display:none}
  .wall__col:last-child .wall-tile:last-of-type{margin-bottom:0}
  .wall__col:last-child .wall-tile:last-of-type::after{display:none}
}
/* 767 and not the site's 640: the cut here is not the phone, it is the point
   where a THIRD of the viewport stops being a column. At 700px each column is
   221px wide, which still holds the longest review on record — but only just,
   and a tile that squares up like that has stopped being the reference's
   composition. 768 (a tablet held upright) is the narrowest width at which
   three columns are still three columns.

   WHAT THIS BLOCK IS NOW. It is the wall with NOTHING DRIVING IT — the stack a
   reader gets with script off, and the base the lane system in 9c-i is built
   on top of. It used to be the phone's whole answer; 9c-i is now that answer,
   and this is its floor. */
@media (max-width:767px){
  /* three columns of ~120px is not a column, it is a margin, so the grid
     collapses to ONE readable column. Four of the seven photographs stand down
     (data-phone) so an undriven stack stays the length of a section rather
     than the length of a page. */
  .wall{--wall-axis:none;grid-template-columns:1fr;grid-template-rows:auto;height:auto}
  .wall__col{overflow:visible;cursor:auto}
  /* scoped to the undriven wall: with 9c-i running, the transform IS the
     composition, and an !important here would win against the inline one
     wall.js writes and freeze the lanes at nothing */
  .wall:not(.is-looped) .wall__track{transform:none!important}
  .wall [data-phone="hide"]{display:none}
  /* freed from the wrap arithmetic, a tile can be its own size again */
  .wall-tile{height:auto}
  .wall-tile--shot{aspect-ratio:4/3}
  .wall-tile--word{min-height:calc(var(--wall-tile) * .72);gap:var(--s-3)}
  .wall__col:last-child .wall-tile:last-of-type{margin-bottom:0}
  .wall__col:last-child .wall-tile:last-of-type::after{display:none}
}

/* --- 9c-i. THE PHONE — the wall laid on its side -------------------------
   THE PROBLEM WITH THE STACK ABOVE. One column of ten tiles is honest, but it
   is a page and a half of scrolling to learn one thing, and it says "here are
   some reviews" — the exact sentence § 9c was built to stop saying. The
   argument is that they KEEP COMING, and on a phone that argument has nowhere
   to run vertically, because the page is already spending that axis.

   SO THE WALL TURNS NINETY DEGREES, AND THE AXES STOP FIGHTING. A phone has
   width it never uses and a scroll it always owns. Three columns become three
   LANES, each a band the full width of the section, each running the tiles it
   already held — the same alternation, the same 4 / 6 / 4 counts, the same
   pair of directions. Nothing about the composition is invented here; it is
   the same wall, read across instead of down.

   WHY THE COUNTER-MOTION SURVIVES THE ROTATION. Columns 1 and 3 rose and the
   centre fell; laid down, 1 and 3 travel LEFT and the centre travels RIGHT, so
   adjacent lanes still oppose and the lattice still re-deals itself. The
   4 / 6 / 4 counts do the same work they did standing up: three lanes of equal
   length on one clock resolve into a pattern inside two cycles, and these
   never arrive back at an arrangement the eye has already been shown.

   THE CARD IS DELIBERATELY WIDE — 78vw, capped. One card is fully in frame and
   the next is a third of the way in, which is the only cue a horizontal band
   needs: the reader is told the row continues without being told in words, and
   without an arrow, a dot row, or a gradient softening the edge. The hard clip
   is kept because it is the same clip the desktop wall uses; a fade here would
   be a second grammar for one idea.

   EVERY PHOTOGRAPH COMES BACK. data-phone stood four of them down because a
   stack pays for them in page length; a lane pays for them in PERIOD, which is
   the thing being bought. Fourteen tiles on three clocks is what makes the
   loop unreadable as a loop.

   AND THE FOCAL PLANE TURNS WITH IT — see wall.js. The band is not an
   animation on the pictures, it is a position on the lane, so rotating the
   wall rotates it for free: it is measured across the WIDTH now, one card
   deep, and a photograph comes up out of grey as it takes the middle of the
   lane and is honestly monochrome again before the next one arrives. It costs
   what it costs because the plane only ever writes to a photograph whose tint
   has actually moved, and here it is asked for a coarser step — two points of
   grayscale rather than a half — so one card crossing a lane is a few dozen
   style writes across eight seconds rather than one every frame. The resting
   end of the ramp is the grade this section is composed on, which is why a
   lane that is standing still, or motion that has been declined, looks
   exactly like the stylesheet with no script at all. */
@media (max-width:767px) and (prefers-reduced-motion:no-preference){
  .wall.is-looped{
    --wall-axis:x;
    /* ONE card is fully in frame and the next is entering, and the pair of
       numbers is chosen for a reason that is easy to miss: what must never
       happen is a lane showing a WHOLE number of cards. At exactly two, the
       row stops being a row and becomes a pair of cards sitting in a box, and
       every cue that the content continues is gone with it — the motion alone
       does not say "there is more", the cut-off card does.
       72vw holds the fraction near 1.2 across the phones, and the cap is what
       keeps it there at the top of the range: left at 336px, 767 lands on 2.05
       and the third card is an 18px sliver. At 380 it lands on 1.83. */
    --wall-card:min(72vw,380px);
    /* the lane, and with the card width it is also the photographs' aspect
       ratio — stated once, so every image in the section shares it and the
       rows read as rows. svh and not dvh: a lane that resized itself as the
       URL bar came and went would reflow three tracks mid-drift. The floor is
       the longest review on record at the narrowest card; the cap stops a
       tablet-tall viewport from spending a whole screen on one lane. */
    --wall-lane:clamp(186px,25svh,260px);
    --wall-sep:clamp(14px,3.6vw,20px);
    /* a shade quicker than the desktop wall's 9s, because the card crossing is
       a card-width and not a tile-height — same pixels per second to the eye */
    --wall-beat:8s;
    grid-template-columns:1fr;
    grid-template-rows:repeat(3,var(--wall-lane));
    height:auto;overflow:hidden}
  /* THE GESTURE, and it is the one thing the desktop wall refuses. There, a
     drag and a page scroll were the same movement and the thumb had to win.
     Here they are PERPENDICULAR: pan-y hands the browser every vertical
     intention untouched and leaves the horizontal one to wall.js, so a lane
     can be taken hold of on a phone without ever trapping a scroll. */
  .wall.is-looped .wall__col{overflow:hidden;cursor:auto;touch-action:pan-y}
  /* THE TRACK TAKES THE LANE'S HEIGHT, and this is the load-bearing line of
     the block. A flex row is width:max-content so that the doubled copy can be
     measured; left at height:auto it is also CONTENT-tall, and the cards below
     — which ask for 100% — resolve that against auto, size themselves to their
     own words, and hang out of the bottom of the lane where the overflow clips
     them. The attribution goes first and silently. The lane is a definite
     height (it is a grid row), so 100% here is definite and 100% below lands
     on it. */
  .wall.is-looped .wall__track{flex-direction:row;width:max-content;height:100%}
  /* the gutter is the tile's own margin on the travelling axis — same
     arithmetic as § 9c, turned: n tiles occupy exactly n x (card + gutter),
     so half the doubled track is exactly one copy and the wrap is clean */
  .wall.is-looped .wall-tile{width:var(--wall-card);height:100%;
    margin:0 var(--wall-sep) 0 0}
  /* and the hairline floats in the middle of that gutter, standing up */
  .wall.is-looped .wall-tile::after{left:auto;right:calc(-1 * var(--wall-sep) / 2);
    top:0;bottom:0;width:1px;height:auto}
  /* the still stack's 4/3 comes off, and the picture is cut to the LANE
     instead of setting its own box — § 9c's `height:100%` on the image is
     already waiting for it. The card width and the lane height are both
     tokens, so every photograph shares ONE aspect ratio at any given
     viewport, which is what makes three rows read as three rows. */
  .wall.is-looped .wall-tile--shot{aspect-ratio:auto}
  .wall.is-looped .wall-tile[data-phone="hide"]{display:block}
  /* the same four zones, tightened to a card: head, words, attribution. The
     quote keeps --ink-1 and the stars keep their gold — the hierarchy is the
     desktop tile's, at the size the card can hold. */
  /* THE INSET IS SET BY THE LONGEST REVIEW ON RECORD AT THE NARROWEST CARD,
     the same rule § 9c states for the desktop tile — only here it is the inset
     that gives rather than the tile, because the lane's floor is already fixed
     by the other end of the problem: three lanes have to be one screen on a
     568px-tall phone, and 186 x 3 is that screen. Measured at 320: Brent B's
     review is the longest of the seven and ran five lines 2.4px past a 16px
     inset. The house inset stands from 445px up, where the card is wide enough
     not to need the room. */
  .wall.is-looped .wall-tile--word{min-height:0;gap:var(--s-2);
    padding:clamp(12px,3.6vw,16px)}
  .wall.is-looped .wall-tile__avi{width:30px;height:30px;font-size:.75rem}
  .wall.is-looped .wall-tile__head{gap:var(--s-2)}
  .wall.is-looped .wall-tile__stars{font-size:.75rem;letter-spacing:.2em}
  .wall.is-looped .wall-tile__quote p{font-size:clamp(.8125rem,3.4vw,.9375rem);
    line-height:1.4;max-width:none}
  /* § 9c's claim about the attribution — "every review ends on the same line
     whatever length its words ran to" — is only true while the attribution is
     ONE line, and the longest of them ("Fresha client · Jun 2026 · Fresha") ran
     to two on every card narrower than a large phone. The label tracking is
     what pays: the house figure is set for a label standing alone in a rule,
     not for thirty-three characters closing a compact card. */
  .wall.is-looped .wall-tile__meta{font-size:.65rem;letter-spacing:.045em;
    /* three of the seven run to two lines below 430 even at this tracking —
       "Fresha client · May 2026 · Fresha" is thirty-three characters and the
       card is the card. Two lines is fine; an ORPHAN is not, and left to
       normal wrapping every one of them dropped a single trailing "Fresha"
       onto its own line. Balanced, the break falls at the middle separator and
       the attribution reads as two halves of one line rather than as a
       mistake. Browsers without it wrap as before. */
    text-wrap:balance}
  /* the undriven stack closes its last rule against the foot of the section.
     A lane has no last tile — the rule it would have dropped is the one that
     falls in the seam — so it is put back. */
  .wall.is-looped .wall__col:last-child .wall-tile:last-of-type::after{display:block}
  /* while a lane is held, the words stop being selectable, exactly as § 9c
     does for the pointer — a drag across somebody's own words should move the
     lane, not raise a selection handle */
  .wall.is-dragging{user-select:none;-webkit-user-select:none}
}

/* --- 9d. THE LOGISTICS ROWS — hours, address, booking ---------------------
   The reference closes on a working-hours table: day-range left, time right,
   one hairline per row, then the appointment line and the loudest ask
   (bar-flow.md M5, M7). LH already OWNS this component — § 4a's numbered
   metadata, stacked form — so the table is three .meta rows with one new
   job: a right-set value. No second row grammar is invented, and the
   component recurs on the page the way § 9 of the design system audits. */
.hrs{border-bottom:1px solid var(--rule)}
.hrs .meta{padding-inline:var(--inset)}
.hrs .meta__inner{align-items:baseline}
.hrs__val{margin:0 0 0 auto;text-align:right;flex:none;
  font-size:var(--t-body);line-height:1.35;color:var(--ink-1);
  font-variant-numeric:tabular-nums}
@media (max-width:640px){
  .hrs .meta__inner{flex-wrap:wrap;row-gap:var(--s-1)}
  /* the value drops under its key, aligned to the key's own left edge —
     width states the margin's complement so the pair spans exactly 100% */
  .hrs__val{margin-left:calc(var(--s-4) + 2ch);
    width:calc(100% - var(--s-4) - 2ch);text-align:left}
}

/* --- 9e. THE SHELF — product cards ----------------------------------------
   The reference's card (bar-flow.md M2): image, brand, name + price, size,
   and a BUY bar cut off by its own hairline at the card's foot. Four cells
   on the tile-grid's device — one bordered surface split by the hairline —
   with the photograph developed by the site's one grade (§ 11's .figure).
   The BUY bar is the row-link grammar at card scale: the whole bar is the
   link, top hairline, label voice. */
.shelf{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;
  background:var(--rule);border:1px solid var(--rule)}
.shelf>*{min-width:0}
.shelf-card{margin:0;background:var(--paper);display:flex;flex-direction:column}
/* one ground for all four shots: the placeholders arrive with four slightly
   different whites, so each is MULTIPLIED onto the site's own paper — white
   falls away, the object and its shadow survive, and the strip reads as one
   shoot on one sheet (P3 craft round 1). */
.shelf-card__img{margin:0;background:var(--paper)}
.shelf-card__img img{display:block;width:100%;height:auto;aspect-ratio:3/4;
  object-fit:cover;filter:var(--photo-grade);mix-blend-mode:multiply}
.shelf-card__body{padding:var(--inset);display:grid;gap:var(--s-1);flex:1 1 auto}
.shelf-card__brand{margin:0}
.shelf-card__row{display:flex;justify-content:space-between;gap:var(--s-4);
  align-items:baseline}
.shelf-card__name{font-size:var(--t-title);line-height:1;
  font-weight:var(--wght-display);text-transform:uppercase;
  letter-spacing:var(--track-display);color:var(--ink-1);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display)}
.shelf-card__price{font-size:var(--t-body);font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--ink-1);margin-left:auto}
/* the strip's footnote is a ROW OF THE FRAME, not a caption leaking under
   it: it spans the grid and the 1px gap draws its opening rule */
.shelf__note{grid-column:1 / -1;background:var(--paper);
  padding:var(--inset);margin:0}
.shelf-card__buy{display:block;text-align:center;text-decoration:none;
  padding:var(--s-3) var(--inset);border-top:1px solid var(--rule);
  font-size:var(--t-micro);text-transform:uppercase;
  letter-spacing:var(--track-label);color:var(--ink-1);
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label);
  font-weight:var(--wght-label);
  transition:background var(--d-fast) ease,color var(--d-fast) ease}
.shelf-card__buy:hover,.shelf-card__buy:focus-visible{
  background:var(--ink);color:var(--paper)}
@media (max-width:900px){.shelf{grid-template-columns:repeat(2,1fr)}}

/* --- 9f. THE DUO — two offers, one frame ----------------------------------
   The reference's gift-cards/memberships split (bar-flow.md M4): two equal
   panels in one bordered surface, each a centred column — parenthesised
   index, display title, short body, one action — divided by the hairline. */
.duo{display:grid;grid-template-columns:1fr 1fr;gap:1px;
  background:var(--rule);border:1px solid var(--rule)}
.duo>*{min-width:0}
.duo-panel{background:var(--paper);padding:var(--s-6) var(--inset);
  display:flex;flex-direction:column;align-items:center;text-align:center;
  gap:var(--s-4)}
.duo-panel__ix{font-size:var(--t-micro);color:var(--ink-3);
  font-variant-numeric:tabular-nums}
.duo-panel__title{margin:0;font-size:var(--t-title);line-height:1;
  font-weight:var(--wght-display);text-transform:uppercase;
  letter-spacing:var(--track-display);color:var(--ink-1);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display)}
.duo-panel__body{margin:0;font-size:var(--t-body);line-height:1.55;
  color:var(--ink-2);max-width:44ch}
.duo-panel .btn{margin-top:auto}

/* --- 9f-i. THE DUO PANELS STAND IN THE ROOM ------------------------------
   The two panels stopped being flat paper on 2026-09-14: each one now takes
   the studio photograph it is actually selling — (01) the workstation, (02)
   the chair held empty — as its ground.

   WHY THE TEXT MOVED TO THE LEFT EDGE. Both sources are shot the same way:
   a luminous, near-empty wall filling the left half, the subject anchored
   hard right. The old centred column put four inks straight over black
   leather, and the only way to rescue that is a heavy veil — which would
   have cost exactly what the photographs are here for (the warm, high-key
   exposure and the subject itself). Aligning the column to the start puts
   the type on the negative space the photographer already left for it, so
   the scrim below only has to WARM the wall to --paper and insure the one
   edge where the chair creeps in. Nothing else about the panels changed:
   same three elements, same order, same scale, same weights.

   THE SCRIM IS PAPER, NOT SHADOW. Every stop is rgba(243,241,236,x) —
   the site's own --paper. It lifts toward the type and clears to almost
   nothing over the subject, so the crop keeps its own exposure. No blur
   anywhere: a backdrop-filter here re-blurs on every composite and drags
   the whole page's frame rate down (07-operations/learnings).

   THE PANEL HOLDS AN ASPECT RATIO, and that is the whole trick. Left to
   min-height the panel got proportionally wider as the viewport grew, so
   cover's vertical crop deepened with it and the same object-position
   framed a different photograph at every width — at 1440 the room's framed
   print read whole, at 1920 it was a severed half-word. 16/9 pins it: the
   crop is IDENTICAL from 1101px to 2560px, wide or single-column, and the
   two panels always agree with each other. Only the phone re-frames, and
   it re-frames deliberately (below).

   WHICH WAY EACH CROP RUNS. 16/9 is wider than the sources' 3/2, so cover
   trims the HEIGHT and object-position tunes Y: (01) settles low enough to
   hold the counter, the brush, the LH tin and the towel in band; (02) holds
   the whole chair from headrest to footrest. The phone panel is 4/5 —
   taller than 3/2 — so cover trims the WIDTH there instead and the same
   property tunes X, pulled left onto the wall the type needs. */
/* NO overflow:hidden HERE, deliberately. The ground is absolutely positioned
   at inset:0, so there is nothing to clip — and clipping costs something real:
   `overflow:hidden` makes a grid item's automatic minimum size resolve to
   zero, so `aspect-ratio` stops being a preferred height and becomes a hard
   one. At 1200px the narrow column wrapped the body to five lines, the stack
   outgrew 16/9, and the button was silently cut in half by the panel's edge.
   With overflow visible the aspect ratio still sets the height in every normal
   case and simply yields when the type needs more. probe-duo-ground asserts
   that nothing is ever clipped, at every viewport. */
.duo-panel--shot{position:relative;isolation:isolate;
  align-items:flex-start;text-align:left;
  aspect-ratio:16/9;align-self:stretch;justify-self:stretch}
/* BOTH STRETCHES ARE LOAD-BEARING, and the reason is one exception in the
   spec, applied twice. A grid item's default `normal` alignment stretches it
   to fill its track — EXCEPT when the item has an aspect ratio, which these
   do. So when one panel's type wrapped to a taller stack than the other's
   (1440-1560px, where (02) ran a line longer), the shorter panel stayed at
   its 16/9 height while the row grew, and .duo's own --rule background showed
   through underneath as a grey band across the foot of the photograph.

   `align-self:stretch` fixes that — and on its own introduces the same bug
   rotated ninety degrees. With the height made definite by the stretch, the
   still-unstretched inline axis took ITS size from the aspect ratio instead
   of the track: 434px tall became 771px wide inside a 686px column, and the
   two panels overlapped each other by 84px. `justify-self:stretch` closes the
   other half. With both set, the track and the row decide the box, and the
   ratio does what it is actually here for — setting the row's floor through
   the item's intrinsic contribution when the type does not need more. */
/* AND NO EXPLICIT min-height EITHER, for the same reason. A floor of
   clamp(320px,24vw,400px) looked harmless and was not: any explicit min-height
   REPLACES min-height:auto, which is the thing that lets a grid item refuse to
   be smaller than its content. With it set, aspect-ratio was still a hard
   height and the stack simply hung out of the frame at 600, 640, 1320 and
   1360px — no longer clipped, but overlapping the panel edge, which is worse.
   16/9 is the floor in practice at every width the two-up survives; letting it
   go back to auto means the type wins on the rare width where it must. */
.duo-panel__ground{position:absolute;inset:0;z-index:0;display:block}
.duo-panel__ground img{width:100%;height:100%;object-fit:cover;display:block;
  filter:var(--photo-grade)}
/* (01) the gift box: its source is 1589x990, barely narrower than the 16/9
   panel, so the trim is slight and the frame sits nearly whole — 45% keeps
   BOTH the lid's top edge and the tray's bottom corner inside the crop.
   (02) the shop, with Leandro standing at the chair: 1672x941 is 16/9 to
   within a rounding error, so in the two-column layout cover has nothing to
   trim and the frame arrives exactly as it was shot. Centre here is not the
   default being left alone — it is the only crop that takes nothing. */
.duo-panel--shot:nth-child(1) .duo-panel__ground img{object-position:50% 45%}
.duo-panel--shot:nth-child(2) .duo-panel__ground img{object-position:50% 50%}
/* THE RAMP CLEARS RIGHT AFTER THE COLUMN, and that is a correction. It used
   to run .74 at 34% down to .20 at 62%, which sounds gentle and is not: the
   type stops at 36%, so the whole 36-62% stretch was scrim laid over
   photograph for no one's benefit. It reached the gift box's lid and the
   figure and greyed them by ~58 and ~48 of 255 — measured against the source
   files, not guessed. Holding .80 to 28% and clearing to .08 by 54% keeps
   every ink above 7:1 on the worst ground either photograph offers and drops
   the lift on both subjects to under 20. The scrim should end where the words
   do. */
.duo-panel__ground::after{content:"";position:absolute;inset:0;
  background:linear-gradient(100deg,
    rgba(243,241,236,.88) 0%,
    rgba(243,241,236,.80) 28%,
    rgba(243,241,236,.30) 42%,
    rgba(243,241,236,.08) 54%,
    rgba(243,241,236,.03) 100%)}
.duo-panel--shot>:not(.duo-panel__ground){position:relative;z-index:1}
/* THE COLUMN IS CAPPED TO THE CLEAN GROUND, and that cap is measured, not
   guessed: scanning each source for pixels darker than luma 110 puts the
   first subject pixel at x 0.37 (the gift box's lid) and x 0.50 (the chair).
   36% clears the earlier of the two, and both panels share one number so the
   pair reads as a pair. This cap is the reason no further veil is needed
   anywhere.

   36 and not 42: the cap bounds the element's BOX, but what has to clear the
   subject is the longest GLYPH LINE inside it, and a ragged-right paragraph
   puts that wherever the words fall. At 42% the box sat at x .418 and the
   first line landed at x .406 — past the lid at .37, and the body measured
   4.14:1 at 820px because of it. Cap the box below the subject and the rag
   can do what it likes. */
.duo-panel--shot .duo-panel__body{max-width:min(34ch,36%)}
.duo-panel--shot .duo-panel__title{max-width:36%}
/* .btn--line is a transparent bordered word (§ 9f below), and on these
   grounds it STAYS transparent. A --paper fill was tried and pulled: the
   scrim already brings both grounds to within a point of --paper by
   luminance (241 measured, against --paper's 241), so the fill added no
   contrast — all it did was flatten the travertine's grain and the wall's
   window-light into one dead rectangle, which is exactly the promotional-card
   look the brief rules out. The fill is kept only on the phone (below), where
   the column runs the full width and the button genuinely lands on the
   subject. */

/* The two-up is retired at 1300px — not 900, and not the 1100 this first
   used. A two-column panel below that is under ~660px wide, which leaves the
   36% column around 220px: the body wraps to five lines, the title breaks in
   two, and the composition stops being a photograph with a caption on it and
   becomes a cramped card. One full-width panel at those widths is both
   roomier and a better frame for a landscape photograph. */
@media (max-width:1400px){
  .duo{grid-template-columns:1fr}
  /* A FULL-WIDTH PANEL NEEDS ITS OWN RATIO. 16/9 is right for a half-width
     panel and absurd for a whole one: at 1360px it made a 1300x730 slab, the
     type a postage stamp in one corner of an empty travertine field. 12/5
     keeps a single-column panel in the proportion a landscape photograph
     actually wants — 1300x542 at the top of the range, and at the bottom the
     type simply outgrows it and the panel yields (see min-height above).
     And the button is unpinned here: `margin-top:auto` is right when the
     panel is close to the stack's natural height and wrong when it is twice
     it, leaving the action floating 200px under its own paragraph. */
  .duo-panel--shot{aspect-ratio:12/5}
  .duo-panel--shot .btn{margin-top:0}
  /* (02) TAKES 2/1, NOT 12/5, AND THE REASON IS A PERSON. In one column each
     panel is its own grid row, so the two are free to differ — and here they
     must. 12/5 leaves 74% of the source's height; Leandro stands from y .07
     to y .94, which is 87%. Any 12/5 crop therefore cuts either the top of
     his head or his feet. 2/1 leaves 89% and takes neither. (01) keeps 12/5:
     a box lying flat is a horizontal subject and a deeper panel would only
     add empty travertine. */
  .duo-panel--shot:nth-child(2){aspect-ratio:2/1}
  /* and centred with it: at 2/1 the 89% that survives is centred on the 87%
     the figure occupies, so the trim comes off the ceiling and the floor —
     the two parts of the room with nothing in them. */
  .duo-panel--shot:nth-child(2) .duo-panel__ground img{object-position:50% 50%}
}
/* No widening in the single-column range. An earlier revision relaxed the cap
   to 48% here on the reasoning that a full-width panel has room to spare — it
   does, but the constraint is not the panel, it is the photograph: the gift
   box's lid starts at x .37 whatever the panel is doing, and at 820px the 48%
   body ran onto it at 4.14:1. The cap belongs to the image, so it stays at
   42% at every width above the phone. */
/* THE PHONE PANEL is near-square: the crop turns horizontal, so
   object-position tunes X there. BOTH panels put their type at the FOOT and
   raise the scrim to meet it — and that is a change (02) earned when its
   photograph changed.

   The old (02) was an empty room: a wall across the top, a chair standing in
   the foot, so the type took the top band and the scrim fell from it. The new
   (02) is a portrait — Leandro standing at the chair, occupying y .07 to .94
   of the frame. A top-weighted scrim on that photograph lands on his face.
   Measured on a 10x10 luminance grid, the clean ground in this frame is the
   lit wall and the floor along the BOTTOM-LEFT (median 181-234 from y .55
   down, out to x .50), so the type goes there and the wash lands on the legs
   of his trousers and the base of the chair instead.

   (01) is a product shot with the same structure for a different reason: the
   box fills the frame top to bottom and the only clean ground is the
   travertine sweeping across the foot. Two photographs, one composition,
   arrived at separately.

   Worth remembering: the contrast probe cannot see this. It measures whether
   the type is readable, not whether the photograph survived — the first
   version of the phone layout passed every assertion and was washing a matte
   black gift box and its gold foil to flat grey. */
@media (max-width:560px){
  .duo-panel--shot{aspect-ratio:4/5;justify-content:flex-end;
    padding-top:var(--s-7)}
  .duo-panel--shot:nth-child(2){aspect-ratio:4/5}
  .duo-panel--shot .duo-panel__body,
  .duo-panel--shot .duo-panel__title{max-width:none}
  .duo-panel--shot .btn{margin-top:0}

  /* (01) holds the gold LH mark inside the frame; (02) holds the whole
     figure, arm included, out to the chair he is leaning on. */
  .duo-panel--shot:nth-child(1) .duo-panel__ground img{object-position:35% 50%}
  .duo-panel--shot:nth-child(2) .duo-panel__ground img{object-position:55% 50%}
  .duo-panel__ground::after{background:linear-gradient(180deg,
    rgba(243,241,236,.04) 0%,
    rgba(243,241,236,.08) 26%,
    rgba(243,241,236,.72) 46%,
    rgba(243,241,236,.90) 68%,
    rgba(243,241,236,.94) 100%)}

  /* THE BUTTON TAKES A GROUND, on the phone and nowhere else. This was tried
     on every viewport and pulled everywhere above 560px, where it added no
     contrast and only flattened the travertine's grain into a dead rectangle.
     On a 390px panel the arithmetic is different and it is not a matter of
     taste: the button is 63% of the panel's width, while the widest clean
     ground either photograph can offer at a crop that still shows its subject
     is about 50%. The label therefore lands on the subject at EVERY crop —
     measured at 3.41:1 without this. One opaque solid, rest state only;
     § 7's :hover inversion stands and is asserted. */
  .duo-panel--shot .btn--line{background:var(--paper)}
}

/* the DOC's ghost — § 5 Button as written: 1px --rule border, transparent
   ground, label voice, 44px target. .btn--ghost was annexed by the glass
   secondary (§ 7a, 2026-09-04) and now carries flutes and a bevel, which
   § 1 forbids anywhere the four inks rule; this states the written variant
   under its own name for the places a quiet bordered word is the right
   weight — the duo panels, where two glowing knobs would also spend gold
   past § 1's cap. */
.btn--line{background:transparent;border:1px solid var(--rule);
  color:var(--ink-1);box-shadow:none;filter:none;
  transition:border-color var(--d-fast) ease,background var(--d-fast) ease,
    color var(--d-fast) ease}
.btn--line::before,.btn--line::after{content:none}
.btn--line:hover,.btn--line:focus-visible{border-color:var(--ink);
  background:var(--ink);color:var(--paper);box-shadow:none}
.btn--line:active{box-shadow:none}

/* --- 9a. THE SECTION HEAD — headline beside its claim ---------------------
   The reference stamps one head lockup on every mid-page section: kicker,
   the chapter's one rule, then the display headline on the left with a short
   body column standing beside it (bar-flow.md M1). LH already owns every part
   of that — .eyebrow--ruled is the kicker-and-rule, .section-title is the
   headline — so this only states the pairing: title left, a capped body
   column right, top-aligned, both one inset inside their lines because the
   .shell they sit in already holds --pad-type. Below the lap breakpoint the
   pair stacks and the aside takes the heading's own hand-off.
   The lockup is § 5b's .field, escaped to the viewport with .to-bleed, so its
   tracks ARE the page's columns by construction: the title holds columns
   1–4 and the aside is SEATED ON THE FIFTH GRIDLINE, one inset inside it —
   not on a measurement of its own (P1 system round 1: a 36ch column put the
   aside's left edge on no line at all). */
.sec-head{row-gap:var(--gap-lede);align-items:start}
.sec-head__aside{font-size:var(--t-body);line-height:1.55;color:var(--ink-2)}
/* the column seats exist only where the six columns do — stated above the
   field's own ≤900 collapse, or a span of five would land in implicit
   columns off the canvas on a two-column page */
@media (min-width:901px){
  .sec-head .section-title{grid-column:1 / 5}
  .sec-head__aside{grid-column:5 / 7}
}
/* and the section FOOT — the close the reference lands as a band: the
   connective lead on the left, the one action on the right, one row. The
   footer's signature (§ 16) is this same composition at page scale. */
.sect-foot{display:flex;justify-content:space-between;align-items:center;
  gap:var(--s-5);flex-wrap:wrap}

/* --- 9g. THE SERVICE PLATE — the row, photographed ------------------------
   Two lists on this site name services in words: the home page's booking menu
   (§ 9b) and the services page's price table (§ 9). This is the picture that
   answers a row of either — hover it and that service's own work is laid on
   the list as an opaque print.

   It is deliberately NOT a tooltip beside the cursor. The print is PINNED TO
   A HAIRLINE the list already draws: on the price table it hangs from the top
   edge of the hovered row, in the next column along; on the booking menu it
   drops out of the line that row closes on. Either way its edge IS one of the
   list's own rules, which is the same argument § 5a makes about every other
   object here — structure keeps the line — and it is why the reveal reads as
   part of the drawing rather than a card floated over it.

   NOTHING HERE MOVES THE LIST. The plate is out of flow, and the only
   properties that change on a row are colour and opacity, so no row can
   change height and neither list can reflow under the cursor.

   Geometry is arithmetic, so it lives in assets/js/services.js: it measures
   the hovered row against its list and writes --svc-x / --svc-y as px, always
   landing the plate inside the list's own width so nothing can push the page
   sideways. Everything below is the look. In both tables the plate is the
   LAST child of its host on purpose — .price-col:first-child above carries
   the table's left edge and must keep counting from the real first child. */
.pricing{position:relative;--svc-w:clamp(152px,15.5vw,218px)}
/* THE MENU'S PRINT IS SMALLER, because its table is shorter. A print hangs
   from the top hairline of the row it answers, so the tallest one a table can
   carry is the room between its LAST row and the foot of the section the
   table stands in. The services page lists twelve services in columns of
   four with a whole split section beneath it; the home menu lists eight in
   columns of four, two and two, and the reviews begin a short paragraph
   later. At the services page's size the last row's print left this section
   and opened over the reviews — so the menu takes the size its own table can
   hold, and every row on the home page keeps the same one. */
.pricing--menu{--svc-w:clamp(148px,11vw,190px)}

.svc-plate{
  position:absolute;left:0;top:0;z-index:3;pointer-events:none;
  width:var(--svc-w);aspect-ratio:3/4;background:var(--ink);
  transform:translate3d(var(--svc-x,0),var(--svc-y,0),0) scale(.965);
  opacity:0;
  /* while it is down, only the fade is live: the plate is repositioned for
     the next row invisibly, so it never slides in from wherever it last was */
  transition:opacity 240ms ease}
.svc-plate.is-on{
  opacity:1;
  transform:translate3d(var(--svc-x,0),var(--svc-y,0),0) scale(1);
  /* row to row it travels rather than blinking — one print being moved */
  transition:transform 300ms var(--ease-out),opacity 260ms ease}
/* the list's own hairline, carried onto the print so it reads as part of the
   same drawing and not as a card floated over it */
.svc-plate::after{content:"";position:absolute;inset:0;pointer-events:none;
  box-shadow:inset 0 0 0 1px var(--rule-invert),
             0 2px 5px rgba(16,16,17,.10),0 18px 44px rgba(16,16,17,.22)}
/* every print of a list in one stack. Both the outgoing and the incoming
   image are already decoded, so moving down the list cross-dissolves instead
   of flickering through an empty plate. */
.svc-plate img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;opacity:0;transition:opacity 300ms ease;
  /* one grade for all of them, so a set shot over four years on four cameras
     reads as one commission rather than sixteen sources */
  filter:saturate(.92) contrast(1.04)}
.svc-plate img.is-on{opacity:1}

/* THE ROW, ACTIVE. A warm wash bled out to the rules the row is set one inset
   inside, the price in the one accent, and every other row let down to a
   whisper. All three are colour and opacity only, so the emphasis costs the
   list no height. One row type serves both tables (§ 9), so it is stated
   once. */
.price-row{position:relative;isolation:isolate;
  transition:opacity 260ms ease,color 260ms ease}
.price-row::before{content:"";position:absolute;z-index:-1;
  inset:0;background:var(--ink);opacity:0;transition:opacity 260ms ease;
  /* a row sits INSIDE .price-col's inset, so its plate bleeds back out by
     that inset to land on the column dividers */
  inset-inline:calc(-1 * var(--inset))}
.price-row__price{transition:color 260ms ease}
.is-plated .price-row{opacity:.42}
.is-plated .price-row.is-active{opacity:1;color:var(--ink-1)}

/* THE ROW, INVERTED. Same gesture as the header's book square (§ 6): the
   hairline object fills with its own ink on hover and the type flips to paper.
   The row's full-bleed ::before is that plate, and it is ink at one opacity
   only — a gold wash sharing the layer bled yellow through the fade-out.
   Hover is quoted at the plated specificity (0,3,0) so it outranks the
   let-down .is-plated rules on the row the cursor is actually on. */
.price-row:hover,.price-row:focus-visible,
.is-plated .price-row:hover,.is-plated .price-row:focus-visible{
  opacity:1;color:var(--ground)}
.price-row:hover::before,.price-row:focus-visible::before{
  background:var(--ink-1);opacity:1}
.price-row:hover .price-row__price,
.price-row:focus-visible .price-row__price{color:var(--ground)}

/* THE ROW, ACTIVE WITHOUT A POINTER. On a phone there is no cursor to carry
   the inversion down the menu, so the READING LINE carries it instead
   (services.js, BROWSE): scroll the list and the row crossing the line takes
   the inked plate, the paper type and its own photograph, then hands all
   three to the next row as it passes — the laptop's hover state, cascading,
   driven by the only gesture a phone spends on a list.

   It is the SAME declaration block as :hover above, re-quoted for .is-active,
   because a touch device never fires hover and must not be shown a lesser
   version of the state. Quoted at the plated specificity (0,3,0) and placed
   after the let-down rules so it outranks them on the row the line is on.

   The gate is the exact complement of services.js's `fine` query — a device
   that is not (hover AND fine pointer) — so the CSS lights up on precisely
   the devices the scroll driver is bound on, and on no others. Cross-fade
   only: every property here is colour or opacity, so the cascade still costs
   the list no height and nothing reflows under the thumb. */
@media (hover:none),(pointer:coarse){
  .pricing--menu.is-plated .price-row.is-active{opacity:1;color:var(--ground)}
  .pricing--menu.is-plated .price-row.is-active::before{
    background:var(--ink-1);opacity:1}
  .pricing--menu.is-plated .price-row.is-active .price-row__price{
    color:var(--ground)}
}

/* On one column there is no next column to grow into, so the print blooms out
   of the row's own edge — below it in the top half of the list, above it in
   the bottom half — and the name and price under the finger are never the
   thing it covers. */
@media (max-width:900px){
  .pricing{--svc-w:min(40vw,168px)}
}

/* AND AT --bp-hand THE PRINT GOES AWAY ENTIRELY. One narrow column is not a
   list a picture can stand beside: a plate wide enough to read spans most of
   the column and covers the rows either side of the one it answers, so the
   reveal stops being an aside to the table and becomes a thing laid over it.
   Both tables lose it together — the home page's booking menu and the
   services page's price table are one object (§ 9) and must not diverge on
   the phone. services.js raises no plate at all below this width (no hover,
   no reading line, no tap), and this is the same decision stated in CSS so a
   stale class from a resize can never leave one on screen. The rows go back
   to full strength: nothing is let down, because nothing is being answered. */
@media (max-width:640px){
  .svc-plate,.svc-plate.is-on{display:none}
  .is-plated .price-row,.is-plated .price-row.is-active{opacity:1}
}

/* Reduced motion keeps the answer and drops the travel: the plate cuts to its
   row and cross-fades, which is the information without the movement. */
@media (prefers-reduced-motion:reduce){
  .svc-plate,.svc-plate.is-on{
    transform:translate3d(var(--svc-x,0),var(--svc-y,0),0);
    transition:opacity 200ms ease}
}

/* --- 10. Accordion ------------------------------------------------------- */
.acc__item{border-top:1px solid var(--rule)}
.acc__item:last-child{border-bottom:1px solid var(--rule)}
.acc__trigger{width:100%;background:none;border:0;cursor:pointer;
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  padding-block:var(--inset);min-height:44px;text-align:left}
.acc__chev{transition:transform var(--d-fast) ease;flex:none}
.acc__item.is-open .acc__chev{transform:rotate(180deg)}
.acc__panel{overflow:hidden;height:0;transition:height 400ms var(--ease-out)}
.acc__inner{padding-bottom:var(--s-4)}

/* --- 11. Media ----------------------------------------------------------- */
.figure{position:relative;overflow:hidden}
.figure img{width:100%;height:auto;aspect-ratio:3/4;object-fit:cover;
  object-position:50% 28%;filter:var(--photo-grade)}
.tile-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--rule)}
.tile-grid>*{min-width:0}
@media (max-width:900px){.tile-grid{grid-template-columns:repeat(2,1fr)}}

/* --- 12. Hero — the first movement, not the first screen ------------------
   The hero is TWO blocks in ONE section: a 100svh plate, and the facts strip
   (§ 12a) that rides up over its foot. That structure is lifted wholesale from
   fabrica.framer.media, where the "Our clients" strip is likewise the hero
   section's last child rather than a section of its own — which is the entire
   reason the two never read as separate blocks there. Nothing hides the seam;
   there is no seam to hide.

   The surface is NOT lifted. fabrica states the join with a rounded, inset
   media card; LH has no rounded corners anywhere (design-system.md § Image:
   "radius is 0") and its images bleed to the edge (M4). So the join is carried
   by the two devices LH already owns — one continuous ink ground across both
   blocks, and a hairline (§ 12a) that crosses into the photograph's foot and
   slides out of it.

   .hero keeps the ink ground and the z-index that make it the opaque occluder
   the pinned cuts stage is uncovered from behind (§ 13a). The strip inherits
   both by living inside it, so the pin's composition is untouched: what used
   to scroll off the stage was a 100svh hero, and what scrolls off it now is a
   taller one. No arithmetic in § 13a or app.js reads the hero's height. */
.hero{position:relative;background:var(--hero-ink,var(--ink));
  display:flex;flex-direction:column;gap:0;
  padding-bottom:0;
  /* THE WHOLE HERO IS THE SCREENFUL, not the plate alone. The three facts are
     the last line of the composition and have to be READ as part of it, so
     they sit on the first screen rather than a scroll below it. The plate
     gives up the fixed 100svh it used to hold and flexes into whatever the
     strip leaves — so the strip lands on the floor of the viewport at every
     height, with no arithmetic anywhere that has to know how tall it is.
     Phones opt back out below: there, 100svh cannot hold both. */
  min-height:100svh;
  /* `clip`, and `hidden` only as the floor beneath it. overflow:hidden makes
     .hero a scroll container, and a descendant's `animation-timeline:view()`
     resolves against the nearest one — so the settle in § 12a would be timed
     against a box that never scrolls, and would never run. `clip` clips
     without establishing a scroll container. Declared in this order so an
     engine without `clip` keeps `hidden` and loses only the motion, which
     § 12a is written to survive. */
  overflow:hidden;overflow:clip}
.hero>*,.hero__body>*{min-width:0}
/* the plate is the screenful — everything §12 used to say about .hero */
/* The plate is still the whole screenful — the facts band does not take a
   slice of it, it lies ON its foot (§ 12a), where the scrim has already taken
   the photograph down to ink. That is what the reference does: one image
   holding the screen, and the three facts reading off its darkest edge. */
.hero__plate{position:relative;flex:1 1 auto;min-height:100svh;
  display:grid;align-items:center;
  /* OVERSCAN, at the TOP only. The photo planes are inset negatively by this
     much so the slowest of them can lag downward without ever dragging its own
     top edge into frame. Nothing lags upward (§ 12b), so the foot needs none —
     and the foot is worth keeping honest, because every extra pixel here is a
     taller box for object-fit:cover to fill, which magnifies the photograph
     and eats the negative space on the left the headline is composed into.
     It has only to clear the largest --pz-* below: 40px against a 34px lag. */
  --hero-over:40px;
  /* THE FOOT. How much of the plate's bottom the photograph does NOT reach.
     It exists to let the frame OPEN UP: object-fit:cover fills the box, so a
     box far taller in aspect than the 16:9 file (1440x900 is 1.60 against the
     photograph's 1.78) is paid for by cropping ~7% off each side — and what
     sits in that left 7% is the clipper standing on the counter, the one
     object in the room that says what the room is for. Shortening the box
     brings its aspect towards the file's and the side crop nearly vanishes:
     at 12% the clipper is back at 1280, 1440, 1680 and 1920, and 1024x768
     stops cutting the chair's right arm as well. The scrim below is taken to
     solid ink at exactly this height, so the plane's own bottom edge is never
     a visible line — it ends inside ink that was already there.
     Portrait resets it to 0: that crop is a different framing (§ 12c). */
  --hero-foot:0%;
  /* 1 - --hero-foot, as a bare number. The two MUST agree; it exists because
     the foreground's grade has to be re-mapped onto the plate's own rows
     (§ 12e B2) and calc cannot take the complement of a percentage. Restated
     wherever --hero-foot is. */
  --hero-foot-rest:1;
  /* the three parallax offsets, in px, written by app.js. Declared here so the
     layers below have a defined transform before a single scroll event, and so
     the reduced-motion rule can flatten all three in one place. */
  --pz-bg:0px; --pz-tx:0px; --pz-fg:0px;
  /* THE GRADE, declared once because it has to be spent TWICE — on the room
     and, through his own alpha, on him. The background and the foreground are
     the same photograph; grading one and not the other is an exposure mismatch
     between two copies of one frame, and that mismatch is what reads as a
     cutout pasted on a plate. Held light through the upper two thirds so the
     room keeps its detail: crushed, a shallow depth of field stops reading as
     distance and starts reading as blur. */
  --hero-scrim:linear-gradient(180deg,
    rgba(16,16,17,var(--hero-s1,.36)) 0%,
    rgba(16,16,17,var(--hero-s2,.10)) 34%,
    rgba(16,16,17,var(--hero-s3,.74)) 79%,
    rgba(16,16,17,var(--hero-s4,.62)) 90%,
    rgba(16,16,17,var(--hero-s5,.70)) 100%);
  /* the cutout, again, as a mask — § 12b's ::after grades him through it */
  --hero-fg-src:url("../img/hero-foreground.webp");
  /* the plate owns its own stacking context, so the z-indexes below are read
     against each other and never against .hero's own z-index:1 (§ 13a). */
  isolation:isolate;
  /* THE PLANES ARE CLIPPED BY THE PLATE, not by the section. The overscan and
     the parallax both size the photo planes OUTSIDE this box — up by
     --hero-over at the top, and DOWN by --pz-bg (up to 56px) as the page
     scrolls, since every plane lags downward (§ 12b). .hero's own clip used to
     be the only thing catching that, which is exact while the plate IS the
     section — but on a phone the strip returns to flow and the section runs
     --s-6 + the band taller than the plate, so the background's lagging foot
     poked out BELOW the plate's floor, past the last stop of the scrim, and
     hung a lit, hard-edged sliver of photograph on the ink between the plate
     and the facts. Clipping here puts the cut back inside the scrim's solid
     ink (96% portrait, 88% landscape), which is where § 12b says every plane's
     bottom edge ends. `clip`, not `hidden`, for § 12's reason: hidden would
     make the plate a scroll container and the reveals inside it would time
     their view() ranges against a box that never scrolls. */
  overflow:clip}

/* --- 12b. The three planes ------------------------------------------------
   Background photo, then the type, then the same photograph's subject cut out
   and laid back over his own copy — so the headline is occluded by the man
   rather than by a gradient pretending to be one.

   REGISTRATION is the whole trick, and it is free: hero-foreground.webp is
   1672x941 and hero-mobile-foreground.webp is 941x1672, the exact frames of
   the two backgrounds. Give both planes the same box and the same
   object-fit/object-position and the cutout lands on top of its own twin at
   every viewport shape, with no per-breakpoint nudging to drift out of tune.

   WHY EVERY PLANE LAGS DOWNWARD, none upward. The background still contains
   Leandro, so any relative motion between the planes risks his two copies
   separating into a visible double edge. They cannot: the foreground lags
   LEAST, so it always sits higher up the plate than its background twin, and
   because a seated body widens as it descends, the cutout's silhouette at any
   given row is the wider one and swallows the twin behind it. Lagging least
   also means the foreground never lifts its own foot off the bottom of the
   plate. Depth still reads, because depth is relative motion: against the
   background's 56px the subject's 8px is the fastest thing on screen. */
.hero__bg,.hero__fg{position:absolute;left:0;right:0;
  bottom:var(--hero-foot);
  top:calc(-1 * var(--hero-over));
  pointer-events:none}
.hero__bg{z-index:0;transform:translate3d(0,var(--pz-bg),0)}
.hero__fg{z-index:3;transform:translate3d(0,var(--pz-fg),0)}
/* the transform is on the wrapper and the drop-shadow on the img inside it, so
   scrolling re-composites a cached layer instead of re-running the filter. */
.hero__bg,.hero__fg{will-change:transform}
/* <picture> is inline by default; blocking it out keeps the img's height:100%
   resolving against .hero__bg and kills the baseline gap under the line box. */
.hero__bg picture,.hero__fg picture{display:block;width:100%;height:100%}
/* 32% was framed for the old 3:4 plate. Neither new plate crops vertically at
   the aspects most visitors are on — the 16:9 file only past ~16:9, the 9:16
   file only past ~9:16 — so this value is a no-op nearly everywhere and exists
   for the wide end: at 21:9 a 32% offset takes the crown of Leandro's head
   off, and 14% holds it with ~40px to spare. Horizontal stays centred, which
   keeps him right-of-frame and the headline on the dark left. */
.hero__bg img,.hero__fg img{display:block;width:100%;height:100%;
  object-fit:cover;object-position:50% 14%}

/* NO CAST SHADOW, and no brightness trim. Both were here and both were the
   tell. His shadow is already IN the photograph — he was lit in that room —
   so a drop-shadow does not deepen it, it draws a second one around his
   outline that the scene never had: a dark halo tracing hair, shoulder and
   chair arm, which is the classic sign of a subject pasted onto a plate. The
   two planes hold identical pixels in perfect registration, so with the grade
   below applied to both there is no edge left to hide, and nothing to soften.
   Depth is carried by the parallax and by the grade, not by a fake shadow.

   THE GRADE, through his own alpha. The scrim passes UNDER this plane, so
   without this he stands at full exposure in a room that has been taken down
   — the same frame, lit two ways. Masking the identical gradient with the
   cutout itself lands it on him exactly as it lands on the wall behind him.
   The mask has to be sized and placed like the img it grades (cover, 50% 14%)
   or the grade slides off the man. The gradient is anchored to the BOTTOM and
   sized short by the overscan, because this box is taller than the plate at
   the top and the ramp has to line up with the plate's own. */
.hero__fg::after{content:"";position:absolute;inset:0;
  background-image:var(--hero-scrim);
  background-size:100% calc(100% - var(--hero-over));
  background-position:0 100%;background-repeat:no-repeat;
  -webkit-mask-image:var(--hero-fg-src);mask-image:var(--hero-fg-src);
  -webkit-mask-size:cover;mask-size:cover;
  -webkit-mask-position:50% 14%;mask-position:50% 14%;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}

/* THE FOOT. The scrim below resolves the plate into solid ink, but it passes
   UNDER this plane, so without a mask the cutout would stay lit all the way
   down and hang his legs over the ink the facts strip is drawn on. Fading him
   out just above the plate's floor hands the last of him back to the graded
   background, which holds the identical pixels — so the handover has nothing
   to show. The overscan is all at the top, so this box's floor IS the plate's
   floor and the stops need no correction term. The ramp has to be SPENT before
   the scrim's own last stops are — he cannot still be lit where the plate has
   gone to ink — and before the facts band that now lies on that foot. */
.hero__fg{
  -webkit-mask-image:linear-gradient(180deg,#000 68%,rgba(0,0,0,0) 96%);
  mask-image:linear-gradient(180deg,#000 68%,rgba(0,0,0,0) 96%)}

/* THE SCRIM, lifted off .hero__bg and onto the plate. It has to keep the
   plate's own box: on the background it would now ride the overscan and the
   parallax, and its last stop — the one that has to be exactly the plate's
   floor — would land 64px below it and leave the seam short of solid.
   Above the photograph, below the type, below the subject, so it grades the
   room and buys the headline its contrast without dulling either.
   The last stop is SOLID ink, and it lands at 88% — below the button, so the
   three tuned stops above it are untouched. Ending the ramp at .82 left the
   photograph stopping at a visible edge, and an edge is exactly what this
   sequence must not have: the plate has to resolve into the same ground the
   facts strip and the cuts stage are drawn on, so that all three read as one
   surface the page slides over rather than three stacked blocks. */
.hero__plate::after{content:"";position:absolute;inset:0;z-index:1;
  pointer-events:none;
  background:var(--hero-scrim)}
.hero__body{position:relative;z-index:2;transform:translate3d(0,var(--pz-tx),0);
  padding:0 var(--pad-type) var(--s-7);width:100%;color:var(--paper)}
/* the spacing these two used to carry as inline style. It has to live here so
   § 12c can hand both of them to the floor of the plate on a phone — an inline
   margin would outrank the margin-top:auto that does it. */
/* --s-4, not --s-5. The headline below takes 2.4x the cap it used to and has
   to find that height somewhere; 16px off each of these two is 32px the block
   gets back, and at this scale the copy reads closer to the headline as a
   consequence rather than in spite of it. */
.hero__copy{margin-top:var(--s-4);
  /* the connectives' voice, not the body's. Family was never the difference —
     § 12d's leads are Archivo like everything else; what set them apart was the
     axis pair, wdth 100 / wght 300 against the body's 400. This row takes that
     same light cut so the block under the headline reads as one hand. The
     TRACKING does not come with it: .38em is a device for two- and three-word
     grammar rows, and at 62ch of sentence it stops being readable, so this row
     keeps its normal letterspacing. Opacity moves with the weight — 300 at the
     .72 § 12 hands the hero body is a line that disappears into the
     photograph; .88 puts back what the lighter stem takes away. */
  font-variation-settings:"wdth" 100,"wght" 300;
  font-weight:300}
.hero__actions{margin-top:var(--s-4);display:flex;gap:var(--s-3);flex-wrap:wrap}

/* --- 12e. INTEGRATION — the plate and the chapters under it, one page ------
   The problem this answers: the plate is a dark photograph and everything
   below it is paper ruled by hairline columns, so the page opens as two
   documents. Two independent dials, switched from `data-hero-blend` on <body>
   (`off` | `columns` | `light` | `both`), so the two can be judged apart and
   together rather than as one irreversible edit.

   A. COLUMNS — structure carried through, not tone.
   § 3 draws the page's six verticals `fixed` at z 0, and the plate is opaque,
   so they are buried for the whole first screenful and then appear from
   nowhere at #cuts. This redraws them INSIDE the plate exactly as the footer
   (§ 16) and the cuts stage (§ 13c) already do: same --gutter, same
   --grid-cols, so the x-positions are identical by construction, and the
   visitor's eye tracks one set of columns from the top of the page down.
   Drawn on the paper-side rule (--rule-invert) because the ground here is ink.
   The layer sits ABOVE the scrim and BELOW the foreground plane, so Leandro
   occludes the columns the same way he occludes the headline — the grid reads
   as being in the room rather than printed on the glass. It fades in from the
   top so it does not butt the header's own edge. */
.hero__plate>.grid-rules--inline{
  position:absolute;inset:0;z-index:2;
  /* the fixed layer's own alpha is set for paper; on a graded photograph .18
     disappears through the lit third, so the hero states its own value. */
  --rule:var(--hero-rule,rgba(243,241,236,.16));
  opacity:0;transition:opacity .4s var(--ease,ease)}
[data-hero-blend="columns"] .hero__plate>.grid-rules--inline,
[data-hero-blend="both"]    .hero__plate>.grid-rules--inline{
  opacity:1;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 16%,#000 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 16%,#000 100%)}
/* the type keeps its own plane above them (both are z 2 siblings, and the
   body is later in source), and § 12b's foreground at z 3 still cuts them. */

/* THE RIGHT-HAND THREE STOP AT THE INK LINE. Columns 4, 5 and 6 are the ones
   that cross the room rather than the negative space, so they spend most of
   their run occluded by Leandro and by the chair — and then re-emerge as three
   short stubs on the solid ink below him, between the plate's floor and the
   facts strip. Read top to bottom the effect is a line that gives up and then
   starts again, which is the opposite of what the columns are here to do.
   They are cut at 88% instead: --hero-foot, the row the scrim reaches solid
   ink at, so each one ends where the photograph does rather than at an
   arbitrary height. The ramp above it is short, and starts at 82% — below the
   gap between the chair's arm and his shoulder, where the sixth column reads
   clean against the wall and is the whole reason this trims the foot instead
   of shortening the lines outright.
   Continuity across the seam is carried by 1, 2 and 3, which run the full
   height down the negative space the headline is composed into and meet
   #cuts' own verticals unbroken. Portrait draws two columns (§ 3), so this
   never applies there. */
.hero__plate>.grid-rules--inline span:nth-child(n+4){
  -webkit-mask-image:linear-gradient(180deg,#000 82%,rgba(0,0,0,0) 88%);
  mask-image:linear-gradient(180deg,#000 82%,rgba(0,0,0,0) 88%)}

/* HE HAS TO OCCLUDE THEM ALL THE WAY DOWN — and the reason he did not is a
   grade mismatch, not a masking problem.
   § 12b grades the two planes through one scrim so they cannot come apart.
   They do not come apart in COLOUR, but they do in HEIGHT: the plate's ::after
   spans the plate, reaching solid ink at 88% of it, while the foreground's
   ::after is sized `100% - --hero-over` inside a box that also runs
   --hero-foot short at the bottom — so the SAME gradient is squeezed into 88%
   of the rows and reaches solid ink at 70% of the plate instead of 88%. Through
   that 18% band the man is painted darker than the plate he stands on, and
   § 12b's long fade-out is what hides it: he is dissolved before the mismatch
   can be read. That fade is also, unavoidably, what let a column line through
   his legs — a plane that is fading out cannot occlude anything.
   So the fade is not tightened here, it is retired. Re-mapping the foreground's
   grade onto the plate's own rows — same height, same origin — makes his ink
   and the plate's ink the same value at every row, which means the plane can
   simply END at the bottom of its box with a hard edge: at that row (88% of
   the plate, --hero-foot) both he and the plate are solid --hero-ink, so there
   is nothing for the cut to show. Fully opaque the whole way down, so the
   columns are occluded by the man for every row he occupies.
   The portrait crop needs no re-mapping — --hero-foot is 0 there, so the
   gradient already covers the plate exactly — and --hero-foot-rest:1 makes
   this the same rule rather than an exception to it. */
/* LANDSCAPE ONLY, and the portrait exclusion is not a tidiness rule — § 12c
   spends that fade on something else. There the plate has no foot
   (--hero-foot:0), so the grades already agree and there is no mismatch to
   correct; what the fade does instead is UNCOVER the plate's floor, which is
   where § 12c sends the supporting copy and the CTA. Retiring it there buries
   the Book button under an opaque cutout. The two columns a phone draws sit on
   the gutters, nearly clear of him, so there is little for him to occlude
   anyway — portrait keeps § 12b's fade exactly as tuned. */
@media not all and (max-aspect-ratio:1/1){
  [data-hero-blend="columns"] .hero__fg::after,
  [data-hero-blend="both"]    .hero__fg::after{
    background-size:100% calc((100% - var(--hero-over)) / var(--hero-foot-rest));
    background-position:0 var(--hero-over)}
  [data-hero-blend="columns"] .hero__fg,
  [data-hero-blend="both"]    .hero__fg{
    -webkit-mask-image:none;mask-image:none}
}

/* B. LIGHT — the plate's value raised toward the paper it hands off to.
   Not a lighter photograph: the same grade, spent less. The three scrim stops
   come down by about a third each, which gives the room back its highlights
   through the upper two thirds.
   The wash on top is haze, not a highlight — a low paper tint over the upper
   third only, well clear of the headline's block, so the room opens up
   without touching the contrast the type was tuned against.

   THE FOOT STAYS ON --ink, AND THAT IS A REVERSAL. This block used to move the
   plate's two solid stops off pure #101011 onto a warm near-black mixed toward
   --paper, on the argument that the seam at the bottom of the plate is a step
   from black to cream and warming the black shortens that step in hue as well
   as in value. The argument is sound about the SEAM and wrong about the
   PLATE. What sits directly above the foot is a graded photograph of a dark
   room, and its own shadows are neutral; a warm floor under them does not read
   as the photograph resolving into ink, it reads as a second, browner surface
   the photograph has been laid on — soot. That is a cost paid across the whole
   first screen to smooth one join, and the join is not even the one it looks
   like: on a phone the facts strip lies between the plate and the paper, so
   the warm value was buying a transition that another 40px of ink and a whole
   band of type already stand in front of.
   So the plate's foot is the page's ink at every breakpoint, and the three
   scrim stops above — which are what actually lighten the room — are
   untouched. This is now the only reason --hero-ink still exists: it is the
   name the ramp and the ground both read, so they cannot drift apart the way
   § 12c's ramp did. Kept as a token rather than folded into --ink for exactly
   that reason. */
[data-hero-blend="light"],[data-hero-blend="both"]{
  --hero-ink:var(--ink);
  --hero-s1:.15; --hero-s2:.02; --hero-s3:.34;
  --hero-s4:.56; --hero-s5:.80}
[data-hero-blend="light"] .hero__plate::before,
[data-hero-blend="both"]  .hero__plate::before{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(180deg,rgba(243,241,236,.16) 0%,
    rgba(243,241,236,.07) 24%,transparent 52%)}

/* `.hero .eyebrow,.hero .micro,.hero .body{color:rgba(243,241,236,.72)}` stood
   here and is gone: § 1a claims .hero as a dark chapter and hands those three
   the same .72, as --ink-2, along with every other primitive on the plate. The
   value is this rule's — it was promoted, not replaced. */
/* the subhead is the exception, and has to be stated AFTER § 4's .body: the
   two weigh the same, so source order is the only thing that decides. See
   § 12 .hero__copy for why the weight moved. */
.hero .hero__copy{color:rgba(243,241,236,.88)}
/* --- 12d. The headline — four rows, two of them grammar -------------------
   THE STRUCTURE, and the whole of the redesign. The sentence is
   "Distinctly yours. The modern cut." — Leandro's own direction, landed
   2026-09-11 over "More than a cut. It's a standard."; the record is
   07-operations/decisions/2026-09-11-hero-headline-modern-cut.md. The device
   below is the one this block was built for and did not move: the two
   connectives do not share the payload words' size. "Distinctly" and "The"
   are grammar — nobody has to READ them at 180px, they only have to be
   there — so they drop to a tracked hairline and hand the entire scale to
   YOURS. and MODERN CUT., the only two words in the line that carry the claim.
   Two rows became four, and the payload came out 2.4x bigger inside a block
   that grew by half, because it stopped paying full size for words that were
   never the point. The eyebrow went with them: it was a third small tracked
   line stacked on two more, and the first connective now holds that slot.

   THE FACE is Archivo at wdth 62 / wght 900 — the display voice the footer
   lockup already speaks (§ 16, --wdth-display / --wght-display), not a new
   one. It is also the widest part of the deviation from the reference, which
   is set in a face narrower than Archivo's axis reaches: its CUT. runs 1.41
   ink-widths per cap against Archivo's 2.36 at wdth 62. Squeezing the rest of
   the way with textLength, as § 16 legitimately does, would cost this block
   its word reveal, its ramp and its selectable text, and buy a glyph
   distortion at 200px that nobody asked for. The extra width is load-bearing
   HERE in any case: a truly ultra-condensed STANDARD. would need a ~230px cap
   to reach him, and the plate has no such height to spend. Archivo's
   proportion is precisely what lets the line reach him at a cap this
   composition can afford.

   THE TWO SIZES are the reference's own device, kept. Each payload row is
   scaled toward a common right edge rather than to a common type size — which
   is why YOURS., six glyphs against eleven, is set LARGER than MODERN CUT.
   The reference runs that ratio at 1.59; this runs it at 1.38 (it ran 1.27
   under the old sentence), because row B came down .833 to hold its right
   edge and row A did not move. It stays under 1.59 for the reason it always
   did — the rows here are taller (see THE FACE) and 1.59 drives the block up
   through the header at 900px of viewport — and note the rewrite moves the
   block the SAFE way, since only the lower row shrank.

   THE OVERLAP survives both rewrites, and is still the point (§ 12b) — but it
   has changed rows. The two-row composition put both lines across him; this
   one puts only the lower payload there, because the upper row is short and
   stops well left of him — see THE SHORT ROW below. It is also WHY the long
   half of the sentence has to be the last row rather than the first: the
   reverse order needs YOURS. at 20.4vw to reach him, which inverts the two
   sizes and drives the block through the header. The note is in index.html. Sampling hero-foreground.webp's own alpha down the plate, his edge
   holds 62–63% of the viewport through the top third, then walks left: 58% at
   37% down, 56% at 42%, 52% at 50%, 48% at 58% — flat to within a point from
   1280 through 1920. MODERN CUT. lands in the 41–57% band, where he is at about
   52%, and the size below runs its tail to ~58% so the last glyph and a half
   go behind him — the same tail STANDARD. had, held across a longer word by
   the .833 in THE SIZES rather than by moving the row. The rule from the old block is unchanged and still the test:
   the word has to SURVIVE the occlusion for the occlusion to read as one. */
.hero__head{display:block}

/* the two payload rows. Each carries its own --hw because the two are not one
   type step (see THE TWO SIZES) — a shared size cannot make this composition. */
.hero .hero__head .hero__line{
  display:block;white-space:nowrap;
  /* the axis, from § 1. These four numbers were measured HERE and are now the
     site's — .display, .section-title and .title all read the same tokens, so
     a section heading is this headline's smaller sibling rather than a
     different typeface at a smaller size. */
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display);
  font-weight:var(--wght-display);
  letter-spacing:var(--track-display);
  /* leading pulled under the em box and onto the cap: at .82 the four rows
     read as one stacked block rather than four adjacent lines. */
  line-height:var(--lh-display);
  font-size:var(--hw);
  /* the photograph is behind the type at its lightest here, so the words carry
     their own edge rather than leaning on the scrim to make one. */
  text-shadow:0 .012em .035em rgba(16,16,17,.5);
}
/* THE SIZES ARE CLAMPED IN BOTH AXES, and the vh term is not a nicety. This
   block is 2.4x the cap it replaced, and a headline that only answers to WIDTH
   walks straight through the header on a short laptop: at 1440x780 — a 900px
   panel minus browser chrome, which is what people are actually on — the
   vw-only size put the first row 11px ABOVE the header's floor. min() hands
   the smaller of the two to every shape, so the composition is bounded by
   whichever of the plate's dimensions is scarce. The pair stays in proportion
   because both terms carry the same 1.15 ratio (see THE TWO SIZES).

   THE HEIGHT TERM IS NOT A FLAT vh, and cannot be. Everything under the
   headline — copy, button, and the padding that holds the block off the facts
   strip — is a FIXED 264px at every viewport, so the h1's share of a short
   plate is squeezed far harder than a linear vh would model. --hw-fit takes
   that overhead out first and divides what is left: 100svh less a constant,
   over the 1.72 payload-ems the pair adds up to. The constant is the whole of
   the tuning and it reads directly, because 1.72 x .581 is 1: the block comes
   out at (svh - C + 73) tall, the body at (svh - C + 337), and the body is
   centred, so the gap left under the 72px header is a FLAT (C - 337)/2 - 72 at
   every height the term binds at. C = 500 buys ~10px of it.

   C IS BOUNDED BELOW BY THE OVERLAP, not by taste, and 1280x800 is the case
   that sets it. He stands at ~52% there, so the lower payload needs 9.8vw to
   reach him at all and the vw term gives 11.0; the height term must therefore
   stay ABOVE the reach threshold at 800 or it takes the occlusion away, which
   is (800 - C) x .581 x .833 > 125 => C < 542. (Under the old sentence the
   same bound read C < 509 — the .833 bought 33px of headroom, so C is now
   further inside its bound than when it was tuned.) The first draft ran C = 529 for a
   24px gap and dev/probe-hero-layers.mjs failed 1280x800 on exactly this —
   0/2 rows opaque, tail alpha 69. 500 clears the bound by 5px of type.

   It binds only where it should: at 1440x780, 1366x768 and 1280x720 it is the
   smaller term and pulls the block back off the header; at 1280x800, 1440x900
   and 1920x1080 the vw term binds and the composition is the one measured in
   THE OVERLAP above. */
:root{--hw-fit:calc((100svh - 500px) * .581)}
/* the upper clamp is load-bearing, not a tidy bound: a fixed cap makes the row
   a smaller fraction of an ever-wider viewport and walks the overlap back down
   rather than letting it run off him. 21rem holds 13.2vw out to 2560. */
/* Row A is UNCHANGED by the 2026-09-11 rewrite and that is deliberate: it
   sets the block's top edge and its cap, so leaving it alone means the
   headline still starts exactly where it was measured to start. YOURS. is
   six glyphs against CUT.'s four, so the row simply runs further right —
   28.4vw to 44.3vw at 1440 — which is still short of him and still an open
   row (see THE SHORT ROW).
   Row B is retuned to hold its right edge across a longer word. MODERN CUT.
   measures 4.975 ems of ink to STANDARD.'s 4.141 (Archivo at wdth 62 /
   wght 900, tracked; measured at 1440 against the live face), so the size
   comes down by that same 4.141/4.975 = .833 and the tail lands where
   STANDARD.'s did — 58.1vw at 1440, inside the 41–57% band THE OVERLAP
   measured. Every number here is the old one times .833: 13.2 -> 11.0vw,
   the fit multiplier 1 -> .833, 21 -> 17.5rem, 2 -> 1.7rem. The occlusion
   is therefore not re-argued, it is preserved by construction. */
.hero__line--a{--hw:clamp(2.5rem,min(15.2vw,var(--hw-fit) * 1.15),24.2rem)}
.hero__line--b{--hw:clamp(1.7rem,min(11vw,var(--hw-fit) * .833),17.5rem)}

/* THE ROW'S BOX IS ITS INK, not the column, and that is a measurement contract
   rather than a nicety. .mask>* is display:block, and a Range over a block's
   contents hands back LINE boxes — full width, every time. Both probes read the
   tail of this row out of exactly those rects (dev/probe-hero-layers.mjs § 3
   ranges .hero__line and samples the cutout's alpha at right - 3), so a block
   inner span would have them sampling the far edge of the viewport and calling
   whatever they found there the occlusion. inline-block shrink-wraps it back
   onto the word. */
.hero .hero__head .hero__line>span{display:inline-block}

/* THE SHORT ROW is left open, and that is the decision rather than the default.
   YOURS. is six glyphs where MODERN CUT. is eleven, so the two rows end
   ~14vw apart and the block's right edge is a step rather than a ragged one.
   (Under the old sentence that step was ~30vw; the rewrite closes it by half,
   which is the one thing it improves for free.) The reference fills that step with a torn paper tab carrying the LH
   monogram; that was built, then replaced with LH's own straight razor, then
   removed outright on Joshua's call — the object was not the vibe, and neither
   version of it was. Nothing of either is left in the tree; the razor survives
   only as ~/Desktop/blade-hero.webp, and the record is in
   07-operations/decisions/2026-09-04-hero-headline-four-rows.md.
   WHAT CARRIES THE ROW INSTEAD is the rule on the connective under it. It runs
   to 47vw, which is between CUT.'s 25 and STANDARD.'s 57, so the three ranked
   edges read as a deliberate stair down the right of the block rather than as
   one long line and one that stopped early. That is why the rule's max-width is
   a tuned number and not "the rest of the row" — it is now the only thing
   holding the middle of that step, and § 12d has nothing else to lose.
   Do NOT re-close this row by growing row A toward row B: the pair is
   already at the ceiling the plate can spend (see THE SIZES), and the ratio
   that would square the two edges is 1.59, which is the reference's and which
   drives the block up through the header at 900px of viewport. */

/* THE CONNECTIVES. A tenth of the payload's cap and tracked to most of a
   letter apart, so they read as a rule of small caps set above the word rather
   than as type that lost an argument with it. Weight 300 against 900 is the
   other half of that: at the same tracking a 700 lead competes. */
/* § 4a's .lead is this rule, made portable. The connective keeps its own
   colour — .92 rather than the chapter's .72 — because it is part of the
   headline here, not a secondary line beside one. */
.hero .hero__head .hero__lead{
  display:block;
  font-size:var(--t-lead);
  font-variation-settings:"wdth" var(--wdth-body),"wght" 300;
  font-weight:300;
  letter-spacing:var(--track-lead);
  line-height:1.1;
  color:rgba(243,241,236,.92);
  margin-bottom:.95em;
}
/* the gap the other way — payload baseline down to the next lead's cap. It is
   set in the LEAD's em, not the payload's, so it holds its proportion to the
   small line it belongs to rather than swinging with the big one. */
.hero .hero__head .hero__line + .hero__lead{margin-top:1.9em}

/* the rule. It is the reference's, and it does one job: it carries the eye
   from a six-character line across to where STANDARD. is about to start, so
   the short row is not a hole.

   IT NOW RUNS THE WHOLE PLATE, and that is the reference's own move rather
   than a longer version of the old one. Capped at 47vw the rule STOPPED, in
   open air, a third of the way short of him — a line that ends at nothing
   reads as a measure, and the eye asks what measured it. Run gutter to gutter
   it ends on HIM: the body sits at z 2 and the cutout at z 3 (§ 12b), so the
   rule simply travels back into the photograph and is taken by his arm, which
   is the same occlusion STANDARD. gets one row below and buys the connective
   row a share of the composition's one real depth cue. The old worry — that a
   rule outrunning the word beneath it would read as the longer line — is spent
   by the same fact: past his edge there is no rule to compare, because there
   is no rule visible.

   The right end is pulled through the body's gutter so it reaches the actual
   edge of the plate. .hero clips (§ 12), so the bleed costs no scroll. */
.hero .hero__lead--ruled>span{display:flex;align-items:center;max-width:none}
.hero .hero__lead--ruled i{flex:1 1 auto;height:1px;
  /* THE GAP OFF THE COPY. This used to be -.24em, pulling the rule back onto
     the letterspace hanging off the A so the gap was "the gap and not the gap
     plus .38em". At 47vw that was right — the rule was an extension of the
     words. Running full width it is a separate object crossing the plate, and
     it has to be let off the type: .95em here plus the .38em of tracking is
     the reference's ~1.33em of air between the A and the line's first pixel. */
  margin-left:.95em;
  margin-right:calc(-1 * var(--pad-type));
  /* THE STOPS CLIMB, and they climb because the h1 above them falls. § 12d's
     --hero-ramp masks the WHOLE headline, rows and rule alike, from full light
     at 40% down to a .24 floor at 62% — which is right for type receding into
     the plate and wrong for this line, whose whole job is to still be lit at
     the moment he takes it. Measured off the reference, that rule holds a flat
     ~150 of luminance from the A across to his arm and then simply stops;
     masked and unclimbed, ours was down to 78 by 45% of the run and read as a
     line that had petered out rather than one that had gone behind him.
     So these stops are the ramp's inverse, converted into the <i>'s own box —
     which starts after the gap and ends a gutter past the h1's right edge:
     .56 held flat while the ramp is flat, then rising through .65 to 1 by the
     point the ramp has spent itself. The product of the two is a constant .56
     out to his edge — the alpha measured off the reference. Past him the
     climb has nowhere left to go, so the last stops let go, and they let go
     EARLY — 0 by 72% of the run. Not tidiness: at 1440 his shoulder sits well
     inside the plate and a rule still carrying the ramp's .24 floor past it
     surfaced again in the dark to his right as a short floating segment with
     no line attached to it. The fall has to be spent while he is still over
     the top of it, at every width — 72% is behind him at 390, 1440 and 1672
     alike. */
  background:linear-gradient(90deg,rgba(243,241,236,.56) 0,
    rgba(243,241,236,.56) 36%,rgba(243,241,236,.65) 44%,
    rgba(243,241,236,1) 51%,rgba(243,241,236,1) 60%,
    rgba(243,241,236,0) 72%)}

/* THE RAMP is the depth signal, and it re-solves against the new tails. Type
   does not simply stop at his edge — it loses light as it travels back, so the
   last word is already receding before he takes it and the eye reads two
   planes rather than one cut line. The mask is on the h1, one box gutter to
   gutter, so every row dims at the same x and the fall lines up with HIM
   rather than with each row's own length. The stops moved LEFT with the rows:
   the old fall (full light to 50%) was set for a line that ran to 72vw, and
   against a tail that now ends at ~59% it would spend the whole ramp past the
   word. Full light to 40%, the fall carried through his edge at 48–56%, and a
   floor at .24 — a floor, not zero, so the tail is still faintly there in the
   sliver where he does not cover it. He is the occluder (§ 12b); this only
   lights the approach. */
.hero .hero__body .display{
  letter-spacing:-.005em;
  --hero-ramp:linear-gradient(90deg,#000 0,#000 40%,rgba(0,0,0,.86) 48%,
    rgba(0,0,0,.52) 55%,rgba(0,0,0,.24) 62%,rgba(0,0,0,.24) 100%);
  -webkit-mask-image:var(--hero-ramp);mask-image:var(--hero-ramp)}

/* THE HEADLINE BLOCK IS SET IN INK, and it is the one exception to § 1a's
   claim on .hero. The chapter rule is right about the plate — the ground is a
   photograph over --ink and everything else on it (eyebrow, facts, CTA) still
   reads the inverted trio. But the top of the plate is the LIGHT part of that
   photograph, and paper-coloured type across it was the same value as the room
   behind it. So the four rows, the connectives, the rule and the subhead are
   restated on the page's own ink here rather than the chapter's, scoped to the
   headline block so nothing else on the plate moves with them.
   These have to come after § 4's .display and after § 12's .hero__copy — same
   weight, source order decides. */
.hero .hero__head{--ink-1:var(--ink);color:var(--ink)}
.hero .hero__head .hero__lead{color:rgba(16,16,17,.92)}
.hero .hero__lead--ruled i{
  background:linear-gradient(90deg,rgba(16,16,17,.56) 0,
    rgba(16,16,17,.56) 36%,rgba(16,16,17,.65) 44%,
    rgba(16,16,17,1) 51%,rgba(16,16,17,1) 60%,
    rgba(16,16,17,0) 72%)}
.hero .hero__copy{color:rgba(16,16,17,.88)}

/* the reveal, staggered down the four rows. M7 still: upward, once, never
   loops — this only spaces the four so they arrive as a sentence being set
   rather than as one block dropping in. */
.hero__head>*:nth-child(2)>*{transition-delay:90ms}
.hero__head>*:nth-child(3)>*{transition-delay:180ms}
.hero__head>*:nth-child(4)>*{transition-delay:250ms}

.hero__meta{display:flex;flex-wrap:wrap;gap:var(--s-3) var(--s-5);
  border-top:1px solid rgba(243,241,236,.28);padding-top:var(--s-3);margin-top:var(--s-5)}
@media (max-width:640px){.hero__meta{gap:var(--s-2) var(--s-4)}}

/* --- 12c. The mobile composition -----------------------------------------
   Not the desktop one scaled down — a different arrangement of the same three
   planes, because the 9:16 crop puts him across the WHOLE width. Measured off
   hero-mobile-foreground.webp's own alpha at 390x844, the plate has exactly
   two places type can go: the strip above his crown (clear to ~130px down),
   and a channel of about 220px beside his head and shoulder, from ~160 to
   ~280px down. From 480px down he covers the full width of the plate, so the
   foot — where the desktop composition puts its headline — cannot hold a line
   of type at all.

   So the block splits at the seam the file itself draws. The eyebrow and the
   headline go to the TOP, into that channel, where the foreground is at full
   opacity and the occlusion is as hard as the desktop's: his head and his
   shoulder take the tails of the lines. The supporting copy and the button go
   to the FOOT, onto ink, below the height the foreground is faded out at.
   Same three planes, same order, same real occlusion — a different plan.
   Amplitudes step down with the viewport in app.js; the seam is this one. */
@media (max-aspect-ratio:1/1){
  .hero__plate{--hero-over:24px;--hero-foot:0;--hero-foot-rest:1;align-items:stretch}
  .hero__body{display:flex;flex-direction:column;
    padding-top:calc(var(--header-h) + var(--s-4))}
  /* THE SPLIT NOW FALLS BETWEEN THE COPY AND THE BUTTON, not above the copy.
     margin-top:auto is still the whole of it — one item takes the slack and
     everything before it stays where the flow put it — it has simply moved
     down one element. The supporting line reads as the headline's second
     breath, sitting under it on the same left edge as every row above; the
     button alone takes the floor, where the scrim has already reached ink for
     it. No wrapper element either way, and so still no second copy of the
     hero's content to hold in step with the first. */
  .hero__body>.body{margin-top:var(--s-4)}
  .hero__actions{margin-top:auto}
  /* THE COPY IS BESIDE HIM NOW, and the channel is the one this section
     measured off the cutout's own alpha: ~250px of clear ground on the line
     under the headline at 390, closing to ~180px forty pixels lower as his
     shoulder comes across, and to ~125px by the height the button still sits
     at. So the line takes a channel-width measure and a step down in size — at
     the body's 16px the first sentence alone needs 275px of run and no row on
     this plate has it. § 12d's ramp is not the answer here: the headline can
     afford to be handed to him mid-word because the two words that carry the
     claim have already been read, and a sentence cannot.
     Measured with dev/probe-hero-copy.mjs — every line clears the silhouette
     by 20px or more at 360, 390, 414 and 430, and the portrait tablet, whose
     channel is 460px wide, keeps the authored two-line break at the 15px cap.
     The <br> stays: it is the break between the two sentences, not a fit, and
     balance evens the rag inside each of them — without it the first sentence
     runs long and drops "space." on a line of its own. Where balance is not
     supported the wrap is the plain one, which was measured too and clears. */
  .hero__copy{max-width:46vw;font-size:clamp(12px,3.3vw,15px);line-height:1.45;
    text-wrap:balance}
  /* THE WRAP RULE IS GONE, and so is the reason for it. It existed because the
     old headline was two multi-word lines that had to be allowed to break
     somewhere. § 12d's rows are ONE WORD each, and .display carries
     overflow-wrap:break-word — so "normal" here would let the browser break
     STANDARD. across two lines mid-word rather than leave it long. Every row
     stays nowrap and is sized to fit instead.
     The <br> added on 2026-09-11 does not reopen this: it is an AUTHORED
     break at a space, not permission to wrap, and nowrap still holds every
     rendered line together — a <br> breaks regardless of white-space, which
     is exactly why it was chosen over relaxing this rule.
     Worth recording: the four-row structure is not new to this breakpoint,
     only to the design. At 60vw the old column already wrapped to MORE THAN /
     A CUT. / IT'S A / STANDARD. — which is exactly the composition § 12d now
     sets on purpose at every width, and set here first by accident. */
  .hero__line{white-space:nowrap}

  /* THE COLUMN IS STILL MEASURED OFF HIM, not off the page: across every
     portrait shape the mobile cutout's edge beside his head sits at 54–55vw —
     215px of 390, 415px of 768. What is gone is the 68vw cap, because against
     § 12d it is no longer a column, it is a CLIP: each row is its own .mask
     (overflow:hidden) and a nowrap word wider than that box would be cut by
     the BOX rather than by him — the one thing § 12b must never let happen.
     The rows carry their own width instead, sized so STANDARD. lands near
     76vw, a deliberate 20vw behind his head, and CUT. with its tab stops near
     50vw, just short of him — the same two jobs the desktop rows do.
     No --hw-fit here. That term models the desktop plate, where a fixed 264px
     of copy, button and padding sits UNDER the headline and competes with it;
     portrait pushes all of it to the floor with margin-top:auto, so the only
     ceiling the headline has is the channel beside his head. A flat vh cap is
     the right model for that, and 26vh keeps the block inside the channel at
     every portrait shape (172px of 844 at 390, 294px of 1024 at 768). */
  /* ROW B IS SIZED OFF THE OCCLUSION HERE, not off the desktop's .833, and
     that is the whole of the phone's share of the 2026-09-11 rewrite. The
     portrait plate hands the row to him ~14vw sooner than the desktop plate
     does, so STANDARD.'s old 18.6vw carried across to MODERN CUT. buries the
     noun: the tail lands at 83vw against a 54–55vw edge and the line reads
     "THE MODERN". A tail of "RD." is completed by the eye; a missing second
     word is not. The row therefore comes down to 10.7vw, where its ink ends
     at 237px of 390 — about 25px, a glyph and a quarter, past the edge — so
     what he takes is the full stop and the edge of the T, which is the
     desktop's "last glyph and a half" read at phone scale. THIS NUMBER IS
     BOUNDED ON BOTH SIDES: below it the row clears him entirely and § 12b's
     one depth cue is gone from the phone; above it the noun starts going.
     WHAT WAS TRIED FIRST, and is recorded so it is not tried again: breaking
     the row at the space (MODERN / CUT.) keeps the type big and reads well —
     but it adds a payload row, and § 12c's channel NARROWS going down, so a
     taller headline pushes the copy into less room rather than more. It put
     the copy's third line 29px inside his arm at 360 and 390, and the three
     places it could be bought back from (the payload, the connective gap, the
     copy's own margin) together returned half of what it owed.
     A UNIFORM SENTENCE IS THE OTHER THING THIS BUYS: every breakpoint now
     sets the same four rows in the same order, so there is one composition to
     reason about rather than a phone variant of it.
     The pair now runs at 2.0 rather than the desktop's 1.38 — wider than the
     reference's 1.59, and deliberate: on a 390px plate the short row is the
     only one with room to be the size the composition wants, and the long row
     is bounded by his edge rather than by taste. */
  .hero__line--a{--hw:clamp(1.75rem,min(21.4vw,29.9vh),13.8rem)}
  .hero__line--b{--hw:clamp(.9rem,min(10.7vw,15vh),6.9rem)}
  .hero .hero__head .hero__lead{letter-spacing:.3em}
  /* the ramp re-solves against the full-width box the h1 now has, and against
     tails that end at 50 and 76vw rather than at the old column's 85%. */
  .hero .hero__body .display{max-width:none;
    --hero-ramp:linear-gradient(90deg,#000 0,#000 48%,rgba(0,0,0,.76) 57%,
      rgba(0,0,0,.4) 66%,rgba(0,0,0,.22) 76%,rgba(0,0,0,.22) 100%)}
  /* The copy and the button land on his legs here, not on room, and the foot
     of the foreground has handed those legs back to the background plane —
     so the scrim has to be the thing that darkens them. It reaches ink far
     earlier than the desktop ramp, which only ever had to clear a floor.

     THE TWO SOLID STOPS ARE THE TOKEN, not a literal, and that is the whole
     of the seam. The plate's floor and .hero's own background are meant to be
     one colour by construction — .hero paints --hero-ink, and every ramp that
     resolves into it is supposed to end on the same name. This one spelled its
     floor as a literal `var(--ink)` instead, so when § 12e B moved --hero-ink
     off pure #101011 the ramp did not come with it: on a phone (where the
     strip leaves the plate and sits in flow below it) the gradient finished at
     #101011 one pixel above a ground of #201E1A, a hard step in three channels
     that reads as a rule nobody drew. Both sides read ONE custom property now,
     so the ramp ends on whatever the foot is set to and cannot drift from it
     again — which is the fix regardless of what that value is. § 12e B has
     since taken the foot back to --ink, so today they meet at #101011.
     The three translucent stops above are NOT touched — they are the phone
     crop's own grading, tuned against his legs rather than against a floor. */
  .hero__plate{
    --hero-scrim:linear-gradient(180deg,rgba(16,16,17,.36) 0%,rgba(16,16,17,.10) 28%,
      rgba(16,16,17,.42) 66%,rgba(16,16,17,.68) 88%,
      var(--hero-ink,var(--ink)) 98%,var(--hero-ink,var(--ink)) 100%);
    --hero-fg-src:url("../img/hero-mobile-foreground.webp")}
  /* The foreground's foot, lifted to just under his hands: his legs go back to
     the background plane, which holds the identical pixels, so the copy and
     the button get ink to sit on. Everything above it is untouched. */
  .hero__fg{
    -webkit-mask-image:linear-gradient(180deg,#000 54%,rgba(0,0,0,0) 72%);
    mask-image:linear-gradient(180deg,#000 54%,rgba(0,0,0,0) 72%)}
}

/* Phones step back out of the single-screen hero. 100svh cannot hold the
   plate's composition AND the strip on a 390x844 screen, and shrinking the
   plate to make it fit would take back the channel § 12c just found for the
   headline. The strip returns below the fold here — which is also the only
   place its settle (§ 12a) can play, that animation being driven by an entry
   range an already-visible strip has nothing left of. */
@media (max-aspect-ratio:1/1) and (max-width:699px){
  /* --s-5 above and below. On every other width the band lies ON the plate's
     foot and owns no gap at all; here it is the ONE place in the page where
     that join is spent as empty ink, so the figure has to be the smallest one
     that still reads as a step rather than a stack. It has come down twice:
     --s-8/--s-7 read as a block stranded a screen from the composition it
     belongs to, and --s-6 still left the three keys floating in a field of
     ink with no edge near enough to measure them against — 128px of empty
     ground for 46px of type. 40 is the step the page already spends between a
     heading and the block under it, and against a strip that is now 46px tall
     it is proportionate to what it separates instead of dwarfing it. */
  .hero{min-height:0;gap:var(--s-5);padding-bottom:var(--s-5)}
  .hero__plate{flex:0 0 auto;min-height:100svh}
  /* THE BUTTON SPANS THE COLUMN, and only here. At the plate floor it is the
     only object on its line, and on a phone a pill measured to its label sits
     two-thirds of the way across and reads as one that was cropped rather than
     one that was sized — a thumb wants the whole width too. It stops at this
     seam because the portrait TABLET's column is 731px, and a 52px pill drawn
     that wide stops being a button and becomes a bar; there it keeps
     cta-button.css's measured fluid width.
     Full width wants the label centred with it, which means dropping BOTH of
     --hero's horizontal paddings: the left inset and the run reserved for the
     knob — the knob is positioned absolutely (cta-button.css § knob) and takes
     no part in the flex line, so nothing has to be held clear of it. flex:none
     is what holds the measured width everywhere else; width:100% is read as
     this item's flex base, so the two do not fight. */
  .hero__actions{width:100%}
  .hero__actions .lhb-cta--hero{width:100%;min-width:0;
    justify-content:center;padding-left:0;padding-right:0}
}
/* A portrait TABLET keeps the single screen, so it keeps the facts band lying
   on the plate's foot — and § 12c has just sent the copy and the button to
   that same foot. The body has to clear the band's own height, which the
   landscape composition never needs because there the content is centred. */
@media (max-aspect-ratio:1/1) and (min-width:700px){
  .hero__body{padding-bottom:calc(var(--s-8) + var(--s-3))}
  /* A portrait TABLET is not a big phone. Its crop puts his head high and
     narrow — the silhouette's edge is out at 67vw across the headline's first
     row, against 55vw on a phone — so the phone's column stops 25–35px short
     of him and the composition loses its occlusion entirely. Measured at
     768x1024: the long row needs ~531px to reach him, which is 60px type, and
     the column then had to stay under the ~615px that MORE THAN A would take
     or the line stopped breaking where it should.
     BOTH OF THOSE ARE GONE with § 12d. Nothing wraps any more, so there is no
     break to protect; and the column cap is now a clip rather than a measure,
     for the same reason it went in § 12c. What the measurement still buys is
     the check: § 12c's 18.6vw runs STANDARD. to ~78vw at 768, which clears his
     67vw by 11 — so the phone's sizes reach him here too, and this block needs
     to do nothing but take the old cap back off. */
  .hero .hero__body .display{max-width:none}
}

/* --- 12a. The facts strip — the hero's second movement --------------------
   Three standing facts, treated as an editorial band: no counters, no plus
   signs, nothing that animates a number. fabrica's client row supplies the
   spatial rhythm (a small label row, a wide gap, then one flat grid of equal
   cells at a hairline-thin gutter) and LH supplies the grammar.

   THE BAND — .tile-grid's device (§ 11) inverted for ink: one surface split by
   1px seams, not three cards floating on a ground. At three columns inside the
   same gutter the seams fall on the 2nd and 4th lines of the six-column grid
   (§ 3), so the strip is measured BY the page's rules rather than against
   them, and the top hairline is the same rule .hero__meta and .tl__years draw.
   ------------------------------------------------------------------------- */
/* ON THE PLATE'S FOOT, not under it. Absolute here so the band costs the hero
   no height at all: the section stays exactly one screen tall, and the strip
   still lands on the floor of the viewport. Above the foreground plane (z 3),
   because at this height the composition is ink and the facts are text to be
   read, not something for him to stand in front of. Phones put it back into
   flow below — see § 12c. */
.facts{position:absolute;left:0;right:0;bottom:0;z-index:4;
  padding-bottom:var(--s-4);
  /* THE ROW'S OWN CONTRAST, now that it has no ink band to read off.
     § 12e's foot is retired (--hero-foot:0) — the photograph runs to the
     bottom of the plate and the scrim's tail is a vignette rather than a
     floor, so the keys can land on grey trousers or on lit chrome. One soft
     drop of the plate's own ink under the type carries them there without
     putting the band back: it is legible at the glyph, invisible at arm's
     length, and costs the composition nothing. */
  text-shadow:0 1px 12px rgba(16,16,17,.65),0 0 3px rgba(16,16,17,.5)}
/* Phones put the band back into flow below the plate (§ 12c) — which is also
   the only place its settle has an entry range left to play. This override has
   to sit AFTER the rule above, not with the rest of § 12c: a media query adds
   no specificity, so up there the absolute positioning would simply win. */
@media (max-aspect-ratio:1/1) and (max-width:699px){
  .facts{position:static;padding-bottom:0}
}
/* PHONES ONLY. Read across on one rule, the three numbered keys ARE the answer
   to where/how/how long — the label above them says the question a second time
   and costs the strip a line of the first screen it is trying to share with the
   plate. Stacked (§ below) the keys are a short column with no line to be read
   across, and the label is the heading that column needs, so it comes back
   there. The section's aria-label carries the same three words at every width,
   so nothing is lost to a screen reader by hiding it here. */
.facts__top{display:none;margin-bottom:var(--s-5)}
/* the label carries its own rule, so it is as wide as the words are and not as
   wide as the page — the band's own full-width rule is the one below it */
/* EVERYTHING YOU CAN SEE OF THIS BAND IS NOW § 4a's .meta-row, and the markup
   says so: .facts__row also carries `.meta-row .meta-row--band`. The hairline
   top, the transparent cells, the vertical seams, the one-line-high rows, the
   baseline pair and both inks are stated there once — which is what lets the
   About chapter carry the same three numbered facts without a second
   implementation of them, and what § 04-C of the brief means by a reusable
   metadata component. Two things came across as tokens rather than as
   literals: --rule-invert is now --rule (§ 1a sets it to exactly that inside a
   dark chapter), and the .62 on the band's eyebrow is now --ink-2's .72 — one
   step of contrast back on a label that only shows on phones.
   WHAT IS LEFT HERE is what is HERO about the band: where it sits, how wide
   its grid is, and the scroll cue, which is not a fact. */
.facts__top .eyebrow{margin-bottom:0;display:inline-block;
  border-bottom:1px solid var(--rule);padding-bottom:var(--s-2)}
/* THE ROW IS ONE LINE, NOT A BAND. Four objects on one axis, laid out by a
   single centre-aligned flex row: the mark, the hairline that runs off it, the
   three keys, the cue. Everything vertical is resolved by `align-items:center`
   on THIS row — nothing in the foot carries a top offset, a translate, or a
   nudge, which is what lets the mark's rule and the keys share an exact Y
   instead of an approximate one.

   ONE SIZE AND ONE TRACKING for the whole foot, taken from the cue's label
   rather than invented: at .6875rem/.22em the keys, the slashes and the word
   SCROLL are the same voice, which is what makes the foot read as one
   component and not as a strip with a cue parked on the end of it. */
.facts__row{
  --foot-size:.6875rem;
  --foot-track:.22em;
  display:flex;align-items:center;
  gap:var(--s-5);
  margin-inline:var(--pad-type);
}

/* --- the mark and its connective ------------------------------------------
   The mark is set in the site's serif, italic, at a weight the row can carry:
   a monogram, not a third wordmark. It takes the row's slack (flex:1) so the
   keys are pushed off the left margin and the rule between them is as long as
   the composition has room for — the reference's line is a MEASURE of the
   empty corner, so it has to be elastic rather than a fixed length. */
.facts__mark{display:flex;align-items:center;flex:1 1 auto;min-width:0}
.facts__mono{
  font-family:var(--font-serif);
  font-style:italic;
  font-size:clamp(1.75rem,2.6vw,2.5rem);
  line-height:1;
  letter-spacing:.02em;
  color:rgba(243,241,236,.86);
  flex:none;
  /* the serif's italic sits high in its box; this puts the monogram's own
     optical centre on the row's centre line rather than its em box's */
  transform:translateY(-.04em);
}
/* THE CONNECTIVE, and it is § 12d's rule verbatim — not a second kind of line.
   Same 1px, same .95em of air off the type it leaves, same flat cream the
   ruled lead holds across the plate (.56 is the alpha § 12d measured off the
   reference and spends its whole gradient holding). What differs is only its
   run: this one is bounded by the row's own gap instead of bleeding past the
   gutter, because the object it is travelling toward is the first key and not
   the edge of the plate.
   Its Y is not set here and must not be: it is a flex child of a
   center-aligned row, so the browser resolves it onto the same centre line the
   keys sit on. That is the alignment, computed rather than eyeballed. */
.facts__rule{
  flex:1 1 auto;height:1px;margin-left:.95em;
  background:rgba(243,241,236,.56);
}

/* --- the keys -------------------------------------------------------------
   No numbers, no cells, no seams. Three statements read across one line and
   divided by a slash, which is punctuation the line already owns rather than
   furniture added to it. The slash is a step quieter than the keys so the
   three read as the content and it reads as the join. */
.facts__list{display:flex;align-items:center;gap:var(--s-5);
  list-style:none;margin:0;padding:0;flex:none;min-width:0}
.facts__list .fact{display:flex;align-items:center;padding:0;border:0;min-width:0}
.facts__list .fact + .fact::before{
  content:"/";margin-right:var(--s-5);
  font-size:var(--foot-size);line-height:1;
  color:rgba(243,241,236,.34);
}
.facts__list .fact__key{
  margin:0;
  font-family:var(--font-display);
  font-size:var(--foot-size);
  line-height:1;
  letter-spacing:var(--foot-track);
  text-transform:uppercase;
  font-variation-settings:"wdth" var(--wdth-body),"wght" 400;
  font-weight:400;
  color:rgba(243,241,236,.78);
  white-space:nowrap;
}

/* --- the scroll cue -------------------------------------------------------
   Bottom-right of the first screen, on the row's own centre line.

   ITS DIVIDER IS A FREE-STANDING HAIRLINE. There are no cells left in the foot
   to take a border, and the reference draws it as a short vertical line set
   clear of the row on both sides — an object beside the cue, not an edge of
   it. Neutral, on --rule: with the numbered cells and their seams gone this is
   the only vertical in the composition, and in gold it read as an accent mark
   rather than as structure. The gold stays where it belongs, on the arrow.

   IT LOOPS — and the loop is borrowed, not invented. M7's "upward, once,
   never loops" is a rule about REVEALS: the page states a thing and then holds
   it. A scroll cue is not a reveal, it is a standing instruction, and it has
   to keep saying the same thing for as long as the reader has not acted on it.
   So the exception is deliberate and it is spent once, here.

   What keeps it from being the generic bouncing arrow is that the gesture is
   already on the page: it is the CTA knob's chevron pass (cta-button.css
   § chevrons) turned a quarter-turn to point down and driven by a keyframe
   loop instead of :hover. Every measured value comes across intact — chevron a
   enters from 11.8 back at opacity 0, chevron b drifts the 4.8 it drifts, both
   land together, the stroke lifts from cold to warm, and the timing is the
   button's own 480ms in / 370ms out on its 0.45,0,0.35,1 curve.
   The reader meets the button's language before they have hovered a button. */
/* margin-left:auto, not a spacer element: the cue holds the right margin
   whether or not the mark is there to take the row's slack (it is dropped
   under 900, below). One rule instead of a second flexible object. */
.facts__cue{display:flex;align-items:center;gap:var(--s-4);flex:none;
  margin-left:auto}
.facts__cue::before{content:"";flex:none;width:1px;height:44px;
  background:var(--rule)}
/* the chevrons lead and the word follows: the pair travels down, and the label
   is what it travels toward */
.scroll-cue{display:flex;flex-direction:column;align-items:center;gap:var(--s-2)}
/* the gold is the arrow's alone — the label stays in the row's voice, at the
   size and tracking the keys beside it are set in */
/* 15x19 renders the 17.4-unit box at 0.862, which puts the measured 1.7 stroke
   at 1.47px — the knob's chevrons scaled up, not re-weighted. The pair carries
   roughly the mass the single stem-and-head arrow carried at 11x26.
   overflow:visible because chevron a spends the first frames of every cycle
   above the box; system.css § 1's img,svg{max-width:100%} does not bite here,
   the box is smaller than its slot. */
.scroll-cue__arrow{width:15px;height:19px;flex:none;overflow:visible}

/* THE CHEVRONS — cta-button.css § chevrons, verbatim, pointed down.
   The stroke colours are the row's, not the knob's: the knob runs a neutral
   #6c6c6c to a warm #ffedc4, and the same cold-to-warm step in this section's
   gold is .40 alpha to the cream. Gold is still the arrow's alone. */
.scroll-cue__chev{
  fill:none;stroke:var(--cue-cold,rgba(199,166,110,.40));
  stroke-width:1.7;stroke-linecap:butt;stroke-linejoin:miter;
  animation:cue-chev 2000ms cubic-bezier(.45,0,.35,1) infinite;
}
/* a is the one that ARRIVES: hidden, 11.8 back along the travel. b is already
   there and only drifts its 4.8. Both live on the element as custom properties
   so one keyframe block drives the pair. */
.scroll-cue__chev--a{--cue-from:-11.8px;--cue-from-op:0}
.scroll-cue__chev--b{--cue-from:-4.8px;--cue-from-op:1}

/* 2000ms = 480 in, 620 landed, 370 out, 530 held back — the button's measured
   pair, with the dwell a loop needs on either side of it. The percentages are
   those milliseconds; they are not round numbers and should not be rounded. */
@keyframes cue-chev{
  0%    {transform:translateY(var(--cue-from));opacity:var(--cue-from-op);
         stroke:var(--cue-cold,rgba(199,166,110,.40));animation-timing-function:cubic-bezier(.45,0,.35,1)}
  24%   {transform:translateY(0);opacity:1;stroke:var(--cue-warm,rgba(255,237,196,.95))}
  55%   {transform:translateY(0);opacity:1;stroke:var(--cue-warm,rgba(255,237,196,.95));
         animation-timing-function:cubic-bezier(.45,0,.35,1)}
  73.5% {transform:translateY(var(--cue-from));opacity:var(--cue-from-op);
         stroke:var(--cue-cold,rgba(199,166,110,.40))}
  100%  {transform:translateY(var(--cue-from));opacity:var(--cue-from-op);
         stroke:var(--cue-cold,rgba(199,166,110,.40))}
}
/* Landed and still — the affordance is the shape, so the shape stays and only
   the loop goes. Same contract cta-button.css keeps at the foot of § 1. */
@media (prefers-reduced-motion:reduce){
  .scroll-cue__chev{animation:none;transform:none;opacity:1;
    stroke:var(--cue-warm,rgba(199,166,110,.92))}
}

/* --- the cue, laid on its side (§ 13, the work) ---------------------------
   The same object pointed LEFT instead of down, for the one affordance on the
   site that is not "keep scrolling": the marquee, which the reader drags. It
   is the device and not a copy of it — same stroke, same 1.7 weight, same
   2000ms with the button's 480/620/370/530 inside it, same cold-to-warm step.
   Three things change and all three are the axis:

   THE GLYPHS are the vertical pair transposed (x,y -> y,x) and then mirrored
   in x, which is what turns "points down" into "points left" without
   re-drawing a single number. The box transposes with them: 22 x 17.4 where
   the vertical one is 17.4 x 22, rendered at the same 0.862 so the stroke is
   the same 1.47px on screen.

   THE TRAVEL is translateX, and --cue-from goes POSITIVE, because "behind" is
   now to the right: chevron a still arrives from 11.8 back at opacity 0 and b
   still drifts its 4.8, they are simply drifting the other way along another
   axis. One keyframe block per axis; the two share everything else.

   THE ORDER is row-reverse, so the label sits to the LEFT of the pair and the
   chevrons travel toward their own caption exactly as they do when the pair
   points down (§ 12a). The DOM keeps arrow-then-label, because that is the
   reading order the vertical cue has and the direction is a layout fact. */
.scroll-cue--x{flex-direction:row-reverse}
.scroll-cue--x .scroll-cue__arrow{width:19px;height:15px}
.scroll-cue--x .scroll-cue__chev{animation-name:cue-chev-x}
.scroll-cue--x .scroll-cue__chev--a{--cue-from:11.8px}
.scroll-cue--x .scroll-cue__chev--b{--cue-from:4.8px}
@keyframes cue-chev-x{
  0%    {transform:translateX(var(--cue-from));opacity:var(--cue-from-op);
         stroke:var(--cue-cold,rgba(199,166,110,.40));animation-timing-function:cubic-bezier(.45,0,.35,1)}
  24%   {transform:translateX(0);opacity:1;stroke:var(--cue-warm,rgba(255,237,196,.95))}
  55%   {transform:translateX(0);opacity:1;stroke:var(--cue-warm,rgba(255,237,196,.95));
         animation-timing-function:cubic-bezier(.45,0,.35,1)}
  73.5% {transform:translateX(var(--cue-from));opacity:var(--cue-from-op);
         stroke:var(--cue-cold,rgba(199,166,110,.40))}
  100%  {transform:translateX(var(--cue-from));opacity:var(--cue-from-op);
         stroke:var(--cue-cold,rgba(199,166,110,.40))}
}
.scroll-cue__label{font-size:var(--foot-size,.6875rem);line-height:1;
  letter-spacing:var(--foot-track,.22em);
  text-transform:uppercase;color:var(--cue-label,rgba(243,241,236,.52));
  font-variation-settings:"wdth" var(--wdth-body),"wght" 400;font-weight:400}

/* NARROW LANDSCAPE. The row gives up its air one step at a time before it
   gives up an object: the gaps close first, and only then does the mark go. */
@media (max-width:1100px){
  .facts__row{gap:var(--s-4)}
  .facts__list{gap:var(--s-4)}
  .facts__list .fact + .fact::before{margin-right:var(--s-4)}
}
/* THE CORNER RUNS OUT BEFORE THE TYPE DOES. Between this seam and the phone
   seam the three keys and the cue together are wider than the row, and the
   mark is the object in the foot that carries no words — so it is the one that
   goes, rule and all, rather than the line being broken or the keys being set
   smaller than the cue's own label. The gaps close one step with it. */
@media (max-width:900px){
  .facts__mark{display:none}
  .facts__row{gap:var(--s-3)}
  .facts__list{gap:var(--s-3)}
  .facts__list .fact + .fact::before{margin-right:var(--s-3)}
}

@media (max-width:640px){
  /* THE ROW BECOMES A COLUMN. Below this seam the strip leaves the plate's
     foot and sits in flow (§ 12c), where there is no line to be read across —
     the three keys are a short list, the slashes have nothing to divide, and
     the mark has no corner to sit in. */
  .facts__row{flex-direction:column;align-items:center;gap:var(--s-3)}
  .facts__mark{display:none}
  /* --s-2 BETWEEN THE KEYS, and it is a measurement rather than a preference.
     Each key is one 10px line of caps; at --s-3 the column ran 26px of rhythm
     for 10px of type, which is 2.6 line-heights — three separate statements
     that happen to be near each other, not a list. 8 puts the rhythm at 18px,
     the same 1.8 the tracked label voice reads at everywhere else on the
     page, and the three lines cohere into the one block the row is at every
     other width. Tighter than this and the .22em tracking starts fighting
     the leading: the eye reads down the column instead of across the line. */
  .facts__list{flex-direction:column;align-items:center;gap:var(--s-2);width:100%}
  .facts__list .fact{justify-content:center;width:100%}
  .facts__list .fact + .fact::before{display:none}
  .facts__list .fact__key{white-space:normal;text-align:center}
  /* THE LABEL SITS ON ITS ROWS. Stacked, the three keys are a short column,
     and a --s-5 hand-off above them is wider than the rows themselves — the
     label floats free of the list it names. --s-3 holds it ONE step clear of
     the column while staying under the --s-5 that now separates the whole
     strip from the plate: the label has to belong to the keys more than the
     keys belong to the photograph, and that only reads if the inner gap is
     the smaller of the two. */
  /* CENTRED, AND THE RULE CENTRES WITH IT. The eyebrow keeps its
     inline-block + border-bottom from the base rule, so the hairline is still
     as wide as the words are — text-align on the block is what puts that
     shrink-wrapped unit on the column's centre line rather than its left
     edge. width:100% is what makes "centred" mean the page's measure and not
     the label's own box. */
  .facts__top{display:block;margin-bottom:var(--s-3);width:100%;text-align:center}
}
/* PHONES DROP THE CUE. Below this seam the row leaves the plate's foot and
   sits below the fold (§ 12c) — a "scroll" pointing down from there is being
   read by someone who has already done it. */
@media (max-width:699px){
  .facts__cue{display:none}
}

/* THE SETTLE — the strip is laid out one --s-8 below the plate and then lifted
   back INTO it, so it enters the viewport already crossing the photograph's
   foot and lands as the reader keeps scrolling. Because it travels DOWN while
   the page travels up, it moves slower than the page: the strip reads as held
   by the hero and released by it, which is the whole of fabrica's effect
   (its own strip runs translateY(-290px) scale(0.95) -> none over the same
   range). The lift is clamped under the plate's --s-7 body padding at every
   width, so the band crosses ink and never the headline or the button.

   Driven by the strip's OWN entry range — no scroll handler, no observer,
   nothing that can arrive a frame late, matching § 13a's rule for this page.
   Withdrawn wholesale where view() timelines or motion preferences are
   missing: the strip then simply sits where it is laid out, and the
   composition is complete without it. */
.facts{--facts-lift:clamp(148px,18vw,264px)}
/* PHONES: the lift is measured off the gap, and the gap is now --s-5. 148px of
   travel against 40px of ink would start the strip 108px above the plate's
   floor and land it across the button, which is the one thing this animation
   is written never to cross. The gap plus one --s-3 is the same figure the
   wide clamp draws: the band enters just INSIDE the plate's foot, crossing the
   photograph the way fabrica's does, and closes the gap as it lands — and 56
   against the body's --s-7 padding leaves --s-6 of ink between it and the
   button, one step more clearance than the --s-6 gap bought. It is written as
   the gap plus a step rather than as 56 so the two cannot drift apart the next
   time the join is tuned. After the base value, since a media query adds no
   specificity. */
@media (max-aspect-ratio:1/1) and (max-width:699px){
  .facts{--facts-lift:calc(var(--s-5) + var(--s-3))}
}
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .facts{animation:facts-settle linear both;
      animation-timeline:view();
      animation-range:entry 0% entry 100%}
  }
}
@keyframes facts-settle{
  from{transform:translate3d(0,calc(var(--facts-lift) * -1),0) scale(.955)}
  to{transform:none}
}


/* --- 12f. The proof rail — the foot's other voice --------------------------
   Markup and the reasoning for the CONTENT live beside it in index.html. This
   block owns the FORM, and the form is one sentence: the rail is the same
   component the three facts are, given a viewport and a clock.

   WHAT IT REUSES AND DOES NOT RESTATE. --foot-size and --foot-track are set
   once on .facts__row and every label in here inherits them, so the rail is
   audibly the same voice as PARAPARAUMU BEACH, NZ rather than a trust bar
   parked beside it. The dividers are .facts__cue::before's hairline verbatim
   (1px on --rule, 44px, standing clear of the row on both sides — an object
   beside the type, not an edge of it). The counts are .ev__num's voice at foot
   scale: the site sets a FIGURE in the serif, which is also what puts 5.4K in
   the same family as the LH monogram four objects to its left. Nothing in this
   section introduces a colour, a size, a radius or an easing.

   WHY THERE IS NOW A RULE ACROSS THE FOOT. Until the rail there were four
   objects on the line and the corner was mostly empty, so the line WAS the
   edge — nothing had to be drawn to say where the foot began. With the rail in
   it the foot carries eleven objects, and the reference answers that with the
   page's own hairline run at --pad-frame: structure's edge, the same 1px the
   six column rules behind it are drawn in. It is the ONLY thing added to the
   composition — see THE CORNER below for the one thing taken away.
   ------------------------------------------------------------------------- */

/* THE FOOT'S TOP EDGE. At --pad-frame because it is STRUCTURE (§ 1: the outer
   gridline itself, and therefore every rule), while the row inside it sits at
   --pad-type, one inset in. The strip is bottom-anchored and absolute above
   the phone seam, so the padding this rule needs grows the box UPWARD into ink
   the composition was already spending — the row does not move and the hero
   does not get taller. */
.facts{padding-top:var(--s-3)}
.facts::before{
  content:"";position:absolute;
  left:var(--pad-frame);right:var(--pad-frame);top:0;height:1px;
  background:var(--rule);
  pointer-events:none;
}

/* THE CORNER IS NO LONGER EMPTY, so the thing that measured it goes. § 12a's
   connective is "a MEASURE of the empty corner", elastic on purpose because
   the reference's line is as long as the composition has room for. The rail is
   what has the room now, and two elastic objects on one row would be two
   claims on the same slack. So above the one-line seam the mark shrinks to its
   monogram and the rail takes the flex — and below it, where the rail wraps to
   a line of its own and the corner is empty again, the rule comes straight
   back with no second implementation of it. :has() rather than a class: the
   rule's presence is a fact about what else is on the row. */
@media (min-width:1361px){
  .facts__row:has(.proof) .facts__mark{flex:none}
  .facts__row:has(.proof) .facts__rule{display:none}
}

/* THE DIVIDER BEFORE THE METADATA, and it is the cue's, not the rail's. It has
   to be OUTSIDE the marquee: everything inside the rail travels and dissolves
   into the mask, and the join between the proof and the three facts is
   structure, which stands still. A flex child of .facts__list, so the list's
   own --s-5 gap sets its air on both sides and nothing carries a margin. It
   belongs to the one-line composition alone: below the seam the rail has its
   own line and there is no join left to mark — and the 17px it costs the
   metadata line is exactly what pushed that line into a second wrap at 768. */
@media (min-width:1361px){
  .facts__row:has(.proof) .facts__list::before{
    content:"";flex:none;width:1px;height:44px;background:var(--rule);
  }
}

/* --- the slot and the window ----------------------------------------------
   TWO BOXES FOR TWO JOBS (index.html says the same thing at the markup). The
   SLOT is the flex item: on the one-line composition it is the only elastic
   object left on the row and takes whatever the mark, the keys and the cue
   have not; below the seam it claims a whole line. The WINDOW is how much of
   that line the rail occupies, and it is the thing the reader grabs.

   min-width:0 on both because a flex item's floor is its content and the
   content here is 750px of nowrap type — without it the row would push the cue
   off the plate at every width.

   IT FADES AT BOTH ENDS rather than cutting. § 1's --rule-fade states the
   site's position on this: "a line that ends at nothing reads as a measure,
   and the eye asks what measured it". A hard clip on moving type reads as a
   broken container; the same dissolve reads as a rail that continues past the
   frame, which is what it does. --s-3 is the shortest fade that hides a glyph
   entering — half a cap height would strobe.

   touch-action:pan-y IS THE WHOLE OF THE TOUCH CONTRACT. The page scrolls
   vertically and the rail scrolls horizontally, so the two gestures are
   declared on different axes and the browser arbitrates: a finger going down
   is the page's and never reaches this file, a finger going sideways is the
   rail's and never scrolls the page. That is why this component can take touch
   where the review wall (§ 9c) deliberately does not — the wall's drag is
   vertical, which is the page's own axis, and a gesture with two owners has
   no honest arbitration.

   user-select and the dragstart it implies: five of the objects in here are
   photographs, and an <img> is draggable by default. Left alone, the browser's
   own image-drag would hijack the gesture on the very object the reader is
   most likely to grab. */
.proof{
  --proof-fade:var(--s-3);
  --proof-av:32px;
  flex:1 1 auto;min-width:0;
  display:flex;
}
.proof__win{
  flex:1 1 auto;min-width:0;
  overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 var(--proof-fade),
    #000 calc(100% - var(--proof-fade)),transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 var(--proof-fade),
    #000 calc(100% - var(--proof-fade)),transparent 100%);
  touch-action:pan-y;
  -webkit-user-select:none;user-select:none;
  -webkit-tap-highlight-color:transparent;
}
/* the cursor is the only affordance the rail carries, so it says both halves
   of what the reader can do — and it only appears once proof.js has the copies
   up, because before that there is nothing to scroll to. */
.proof.is-looped .proof__win{cursor:grab}
.proof.is-held .proof__win{cursor:grabbing}
.proof__win img{-webkit-user-drag:none;user-drag:none}

/* THE SEAM, and it is the wall's idea (§ 9c) laid on its side: a seamless loop
   is not an animation problem, it is modular arithmetic. The track holds the
   set --proof-copies times and proof.js wraps one offset into ONE copy's outer
   width — so the frame after the wrap is the frame before it, to the pixel,
   whatever the reader has just done to it: drifted, dragged, thrown, or dragged
   backwards past its own start. There is no "end" to reach.

   NOTHING HERE ANIMATES, AND THAT IS THE POINT. This was a CSS keyframe pair
   on a percentage transform, which was cheaper and perfectly smooth — but a
   CSS animation owns `transform` outright, and the moment the reader takes hold
   of the rail there are two authorities on where it is and the handover costs a
   visible jump in both directions. § 9c settled this for the wall and the same
   answer holds here: the offset is ONE NUMBER, the clock adds to it, the
   pointer adds to it, a throw adds to it and decays. Nothing hands over,
   because nothing was ever held twice. The stylesheet owns the box; proof.js
   owns the transform, and only while .is-looped says the copies are up.

   The set's trailing margin equals its inner gap, so the rhythm across the
   seam is the rhythm inside the set. Do not put the gap on the track. */
.proof__track{display:flex;width:max-content}
.proof.is-looped .proof__track{will-change:transform}
.proof__set{display:flex;align-items:center;flex:none;
  gap:var(--s-4);margin-inline-end:var(--s-4)}

/* --- the objects on it ----------------------------------------------------
   One row each, content then hairline. No gap on the item: the divider and the
   content want different air (--s-4 against --s-3), and a flex gap cannot tell
   them apart. */
.proof__item{display:flex;align-items:center;flex:none}
.proof__item::after{
  content:"";flex:none;width:1px;height:44px;background:var(--rule);
  margin-inline-start:var(--s-4);
}

/* 1. THE FACES. --proof-av is the diameter and -.12 of it is the overlap, which
   is the reference measured rather than a guess: its discs are 37px on a 34px
   pitch, a tenth of a face. Enough that the five read as one cluster, little
   enough that all five are still five faces — a third of a disc, which is what
   a stacked-avatar component usually spends, hides the first two outright. The ring is the row's own hairline
   one step up (.32 against --rule's .18) because it has to survive being drawn
   at 30px over a photograph; anything heavier turns the cluster into five
   buttons. Later faces stand in front of earlier ones, which is DOM order and
   needs no z-index.

   NOTHING HERE CAN SHIFT. The box is stated on the element, the images carry
   their intrinsic 128x128, and object-fit does the crop — so the cluster is
   145px wide and 32px tall before the first byte lands, and stays 145x32
   whether the images arrive, arrive late, or never arrive at all. */
.proof__faces{display:flex;align-items:center;flex:none}
.proof__faces img{
  width:var(--proof-av);height:var(--proof-av);flex:none;
  border-radius:50%;object-fit:cover;
  border:1px solid rgba(243,241,236,.46);
  background:var(--ink);
}
.proof__faces img + img{margin-left:calc(var(--proof-av) * -.12)}
.proof__claim{
  margin-left:var(--s-3);
  font-family:var(--font-display);
  font-size:var(--foot-size,.6875rem);
  line-height:1.55;
  letter-spacing:var(--foot-track,.22em);
  text-transform:uppercase;
  font-variation-settings:"wdth" var(--wdth-body),"wght" 400;
  font-weight:400;
  color:rgba(243,241,236,.88);
  white-space:nowrap;
}

/* 2-5. THE FOUR PLATFORM BLOCKS. Three rows in a column — what it is, what it
   scored, how many. --s-1 between them and not --s-2: each row is one 10px
   line, and at 8px the three stop being one block and start being three notes
   that happen to be stacked. The stack is wrapped so the item stays a ROW and
   the hairline above can be its last child; a column item would stack its own
   divider underneath itself.

   THREE ROWS WHATEVER A PLATFORM PUBLISHES. TikTok carries two figures and
   takes two blocks for them rather than five rows in one — a five-row block
   would stand half again as tall as everything else on the line, the 44px
   hairline beside it would stop being the tallest thing in the row, and the
   whole foot would have to grow to hold it. Two figures cost WIDTH instead,
   which is the one thing a marquee already has an answer for. index.html says
   why the label is repeated rather than shared. */
.proof__stack{display:flex;flex-direction:column;align-items:flex-start;
  gap:var(--s-1)}
.proof__plat,.proof__note{
  font-family:var(--font-display);
  font-size:var(--foot-size,.6875rem);
  line-height:1;
  letter-spacing:var(--foot-track,.22em);
  text-transform:uppercase;
  font-variation-settings:"wdth" var(--wdth-body),"wght" 400;
  font-weight:400;
  white-space:nowrap;
}
/* the platform NAMES the block and the note reads it, so the name is the
   quieter of the two — .52 is the scroll cue's label ink, .78 the three keys'.
   Both are already on this row; neither is new. */
.proof__plat{color:rgba(243,241,236,.52)}
.proof__note{color:rgba(243,241,236,.78)}

/* THE STARS. Drawn, not set: ★ resolves to a different shape and a different
   weight in every fallback family, and at 10px that difference is the whole
   mark. One path, five stars, outer radius 5 to inner 2 — the classic 0.40
   waist, which stays sharp at this size where the softer 0.48 turns to
   pentagons. Height is the measurement; the 62-unit box gives the width. */
.proof__stars{display:block;height:10px;width:62px;flex:none;
  fill:rgba(243,241,236,.92)}

/* THE COUNT is .ev__num's voice (§ 13f) at foot scale — the site sets a figure
   in the serif, and here that also puts it in the same family as the LH
   monogram at the other end of the line. .86 leading is that block's, so the
   numeral occupies its cap height and the three rows close up the way the
   reference's do. */
.proof__figure{display:flex;align-items:center;gap:var(--s-2)}
.proof__count{
  font-family:var(--font-serif);
  font-size:clamp(1.0625rem,1.32vw,1.3125rem);
  line-height:.86;
  letter-spacing:.005em;
  color:var(--ink-1);
  font-variant-numeric:lining-nums tabular-nums;
  white-space:nowrap;
}

/* THE MARKS. Both are the real ones and both are monochrome by construction —
   currentColor, inherited from the count beside them, at one alpha step below
   it. Instagram is stroked at 1.7 on a 24 box, which lands at ~1.1px rendered:
   the row's hairline weight, so the glyph reads as drawn in the same hand as
   the dividers. TikTok is the one filled mark; its own artwork has no stroked
   form, and a hollowed version of it is not the mark. Neither carries a brand
   colour, a gradient or a plate. */
.proof__icon{flex:none;width:15px;height:15px;
  color:rgba(243,241,236,.80)}
.proof__icon--ig{fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.proof__icon--ig .proof__icon-dot{fill:currentColor;stroke:none}
.proof__icon--tt{fill:currentColor}

/* --- how it gives way -----------------------------------------------------
   ONE LINE while the corner can hold one, then a LINE OF ITS OWN — never a
   cramped version of the first. Below the seam the rail takes the whole width
   and goes to the top of the foot, and the three facts and the cue keep the
   line they have always had underneath it.

   1360 IS MEASURED, NOT PICKED. The corner is whatever the mark, the keys, the
   cue and their four --s-5 gaps leave, which at 1440 is 371px and falls a
   pixel of viewport at a time from there; the rail's first object — 145px of
   faces, --s-3, and the 131px the claim sets in — is 292px of that, and 1361
   is the last width where the corner still measures that much. Below it the
   rail would open mid-cluster, which is a cropped component rather than a
   compressed one.
   So that is where it stops sharing the line. Above it the reader gets the
   reference's composition; below it they get the same objects in the same
   order with the whole width to read them across. The hierarchy the reference sets
   left-to-right (faces -> claim -> Fresha -> platforms) is the order the
   marquee carries, so it survives the fold intact: nothing is dropped, the
   rail just reads it over time instead of all at once.

   order:-1 and a full basis, not a second block of markup — the slot is one
   flex item that stops sharing the line.

   THE LINE IS ITS OWN; THE WIDTH IS NOT. The slot keeps flex-basis:100%
   because that is the only thing that reliably breaks a flex line: narrow the
   ITEM and the LH mark — whose own hypothetical width is the 37px of its
   monogram — simply comes up and sits beside it. So the slot takes the line
   and the WINDOW takes the right-hand 62% of it, flush with the row's right
   edge and with the keys' own edge below. 62% is the smallest fraction that
   still opens on a whole object at 768 (436px against the cluster-and-claim's
   292), and the empty third it leaves on the left is not empty for long — the
   mark's connective rule runs across it on the line underneath. */
@media (max-width:1360px){
  .facts__row{flex-wrap:wrap;row-gap:var(--s-3)}
  /* width:100% as well as the basis, and it is load-bearing: at ≤640 the row
     is a COLUMN (§ 12a), where flex-basis is the height and the slot's width
     would fall back to shrink-to-fit — which for a marquee is the whole track,
     forty-odd thousand pixels of it. */
  .proof{flex:0 0 100%;order:-1;width:100%;justify-content:flex-end}
  .proof__win{flex:0 1 auto;width:clamp(260px,62%,100%)}
}

/* THE PHONE COLUMN (§ 12a). The row is already a column here and the rail is
   already its first child by order, so nothing has to be re-laid out — it only
   has to stop pretending it is beside anything. The divider that joined it to
   the metadata goes with the line they shared. This is also the one width
   where the marquee stops being a courtesy: 760px of evidence in 358px of
   screen has no static arrangement that is not a list. */
@media (max-width:640px){
  .proof{--proof-av:26px;--proof-fade:var(--s-3)}
  /* the phone column has no right-hand side to sit on — 62% of 315px is a
     letterbox. The window takes the column, the way every other object in the
     stack does. */
  .proof__win{width:100%}
  /* .facts__top is a row item now (index.html), and the row's own --s-3 column
     gap is the exact step § 12a bought with this margin — so the margin goes
     and the measurement it stood for stays. */
  .facts__top{margin-bottom:0}
}

/* --- 13. SCROLL TIMELINE -------------------------------------------------
   THE SIZING (one frame, three numbers, all measured off this stage):

     --tl-furniture  everything in the stage that is NOT a frame: the header
                     clearance and bottom padding, the two row gaps, the year
                     scale, the head, and the caption line hanging under each
                     frame. 100svh minus this IS the band a frame may fill.
     --tl-cap        the most of the screen's WIDTH one frame may take. This is
                     the dominance dial — it is what makes the phone show one
                     image and the laptop show one with its neighbours cropped.
     --tl-crop       the narrowest a frame may get. A tall band would otherwise
                     stretch a 3:4 portrait into a slot, so the frame's own
                     ratio is free to travel from 3/4 down to this floor and no
                     further; the photo cover-crops into whatever it lands on.

   --tl-band is the height that falls out of the first and third: fill the band,
   unless filling it would crop past the floor. Width is 3/4 of that, capped. */
/* The ground is the page's own paper — the same background The barber
   (§ .split) and What we do (§ .section) are set on, so the cuts band reads as
   part of that light run rather than an ink chapter cut into it. .tl is
   therefore no longer in § 1a's dark-chapter list: --ink-1/2/3 and --rule stay
   the light-ground values, which is what the head, the captions, the year
   scale and both hairlines read. */
.tl{position:relative;z-index:0;background:var(--paper);color:var(--ink);
  /* the emerge, § 13b — declared here, at the head of § 13, so the furniture
     media queries below can retune --cuts-rise like any other dial. */
  --cuts-rise:clamp(12px,1.8vh,24px);
  --cuts-scale:.955;
  --ease-emerge:cubic-bezier(.5,0,.35,1);
  /* + the rail's top hairline row and its gap (§ the rule below).
     BACK TO 22rem, AND THAT IS THE POINT OF § 13f LIVING IN THE HEAD. As its
     own row the evidence cost 5.5rem of furniture, and --tl-band is 100svh
     minus this number — so every pixel of it came straight off the
     photographs. Inside the head lockup it costs only the difference between
     its own height and the headline's, which is a few px, because it is
     spending dead space that was already reserved and empty. The rail is the
     size it was before the evidence existed. */
  /* THE FIGURE'S SIZE AND THE GAP UNDER IT ARE TOKENS ON THE SECTION, not on
     the panel that uses them, because three separate objects in three separate
     columns are positioned off them: the count's own arrows, the quote marks
     two columns to the left, and the scroll chevrons one to the right. Held on
     .ev they were invisible to the other two. .86 is the numeral's own
     line-height, so half the figure's height is --ev-num * .43. */
  --ev-num:clamp(2.5rem,4.6vw,4rem);
  --stat-gap:clamp(var(--s-2),.9vh,var(--s-3));
  --tl-furniture:22rem;
  --tl-cap:40vw;
  --tl-crop:.66;
  --tl-band:max(200px,min(calc(100svh - var(--tl-furniture)),
                          calc(var(--tl-cap) / var(--tl-crop))))}
/* The stage is the WINDOW — where the band is laid out, and what clips the
   strip that runs through it. It was sticky, for a pin that no longer exists
   (§ 13a); it is an ordinary 100svh box on the page now. The height stays
   definite and stays exactly one screen, because .tl__rise solves its grid
   against it and the frames read their own height off that grid — a band with
   an auto height would size itself from the photographs it is supposed to be
   sizing. The band itself is still .tl__rise and not this box, because the
   emerge (§ 13b) rides it. */
.tl__stage{position:relative;height:100svh;overflow:hidden}

.tl__rise{height:100%;
  display:grid;grid-template-rows:auto minmax(0,1fr) auto auto;gap:var(--s-4);
  /* the header is fixed and turns opaque once stuck, so the top of the band is
     cleared past it — the frames start immediately under the nav, not behind. */
  padding-block:calc(var(--header-h) + var(--s-3)) var(--s-5)}
.tl__rise>*{min-height:0;min-width:0}
.tl__head{padding-inline:var(--pad-type);display:flex;justify-content:space-between;
  align-items:flex-end;gap:var(--s-3) var(--s-4);flex-wrap:wrap}
/* THE HEAD IS NOW A THREE-OBJECT LINE (§ 13f). It was headline | cue with a
   space-between gap doing nothing in the middle; the evidence now occupies
   that gap and is held against the cue. The headline keeps its intrinsic width
   and the slack collapses to its right, which is why the evidence needs no
   width of its own — it takes what the headline leaves and no more. */
/* THE LAST 24px OF RAIL, BOUGHT BACK FROM PADDING INSTEAD OF FROM PHOTOGRAPHS.
   The evidence is ~84px tall and the headline beside it is ~60px, so as the
   tallest object on the line it sets the head's height and the rail pays the
   24px difference. It does not have to: .tl__rise already ends on --s-5 (40px)
   of bottom padding, which is air below the whole lockup and not a measure of
   anything. Letting the evidence bleed 12px into it at each end takes the head
   back to the height the headline alone would have set, and the rail back to
   the size it was before § 13f existed. The bleed is half the padding it eats
   into, so the lockup still clears the foot of the stage. */
.tl__head>.ev{align-self:flex-end;margin-block:-12px}

/* THE LINE HOLDS THREE OBJECTS, NOT FOUR — and the fourth is the claim.
   Measured at 1440 the head has 1340px of measure: the headline takes 371,
   the evidence needs 712 at its own minimum (the quote will not set under
   ~30ch without becoming a paragraph), the cue takes 83, and the gaps take 48.
   That is 1214 and it fits. Add the claim's 225 and it is 1439 against 1340 —
   so with the claim present the evidence wraps to a second line at every width
   below about 1700, which is the layout this change exists to undo.

   THE CLAIM IS ALSO THE RIGHT THING TO DROP, independently of the arithmetic.
   "Real people. Sharper versions." is our sentence about the work, sitting
   where the evidence now puts a CLIENT'S sentence about the work. They are the
   same move — words beside the headline asserting the cut is good — and the
   quote is the stronger of the two because it is not ours. Keeping both says
   it twice and lets the weaker one break the line.
   It is hidden rather than deleted, and it comes back on its own below 1200
   where the evidence takes its own line and the space is free again. */
@media (min-width:1200px){
  /* scoped to the head so it outranks § 13e's own .tl__claim display, which is
     declared later in the file — a media query buys no specificity */
  .tl__head .tl__claim{display:none}

  /* THE HEAD IS THE PAGE GRID, NOT A FLEX ROW THAT HAPPENS TO FIT.
     § 1 already states the law this needs: --pad-frame is where STRUCTURE sits
     — "the outer gridline itself, and therefore every image boundary, divider,
     rule and panel edge" — and --pad-type is where COPY sits, one --inset
     inside it. A space-between flex row obeys neither: it distributes slack,
     so every edge in the band lands wherever the content happened to end. At
     1440 that put the panel divider at x865 with grid lines at 720 and 949.
     Declared as the same repeat(--grid-cols,1fr) the hairline layer is drawn
     with, inside the same --pad-frame, the two cannot drift: the band's edges
     ARE the grid's lines, by construction rather than by measurement. */
  .tl__head{
    display:grid;
    grid-template-columns:repeat(var(--grid-cols),1fr);
    padding-inline:var(--pad-frame);
    align-items:end;
    gap:0}
  /* THE ROW IS SIX COLUMNS, AND EVERY OBJECT ON IT OWNS A WHOLE NUMBER OF
     THEM (2026-09-11). Before this the composition was headline(1-2) ·
     count(3) · quote(4-5) · cue(6), and three of those four only LOOKED
     placed: the cue shrank to its content and sat wherever it ended, the
     quote was bounded on its left by a rule and on its right by nothing, and
     the count's column had no edges at all. Stated as spans of the same
     repeat(--grid-cols,1fr) the hairline layer is drawn with, each object's
     boundaries ARE grid lines.

         1 – 2      3 – 4        5        6
         REAL PEOPLE  quote     count   scroll

     copy steps one inset inside the frame it is measured against — the same
     --inset every other column of type on the page is set in. */
  .tl__head>.tl__title{grid-column:1 / 3;padding-left:var(--inset)}
  .tl__head>.ev{grid-column:3 / 6}
  .tl__head>.tl__corner{grid-column:6 / 7}

  /* ONE SET OF VERTICAL BOUNDARIES FOR THE WHOLE ROW. The three right-hand
     cells stretch to the row's height and take the same 12px bleed (§ 13f's,
     which buys the rail back its 24px), so the rules they draw start and stop
     on exactly the same two horizontal lines. Without the shared stretch each
     border would be as tall as whatever type happened to sit behind it, and
     the row would read as three boxes of three different heights rather than
     as one band divided. */
  .tl__head>.ev,
  .tl__head>.tl__corner{align-self:stretch;margin-block:-12px}

  /* AND ONE FOOT LINE. Each cell then settles its content on the BOTTOM of the
     row, giving the bleed back as padding — so the quote's attribution, the
     count's label and the word SCROLL all close on the same horizontal as the
     headline's own box, which is the axis .tl__head has always aligned on
     (`align-items:end`). Centred in their cells instead, the three lines
     landed 849 / 868 / 871 at 1440: a 22px spread that reads as three separate
     components that happen to sit side by side, which is the thing this
     composition exists to undo. --ev-foot is the bleed, named once so the
     three cells cannot be given different values. */
  .tl__head{
    --ev-foot:12px;
    /* the count's label, as a size the axis can be computed from. It was
       stated on .ev__label; held here it is visible to --row-axis below, and
       the label simply reads it back. */
    --ev-label-size:.6875rem;
    --ev-label-track:.17em;
    /* THE ROW'S OTHER HORIZONTAL. The foot line closes the row; this one runs
       through the middle of it, and it is the axis the count's FIGURE is
       centred on — the largest object on the row and so the one the eye already
       reads as the centre. Measured up from the bottom of the stretched cell,
       which all three right-hand cells share exactly (same stretch, same -12px
       bleed), so one length places objects in three different columns:
           the foot bleed + the label + its gap + half the figure
       Everything in it is a token, so the axis follows the figure when the
       figure is re-sized and cannot be left behind. */
    --row-axis:calc(var(--ev-foot) + var(--ev-label-size) * 1.2
                    + var(--stat-gap) + var(--ev-num) * .43)}
  .tl__head .ev__panel--quote,
  .tl__head .ev__panel--stat{align-content:end;padding-bottom:var(--ev-foot)}
  .tl__head>.tl__corner{align-items:flex-end;padding-bottom:var(--ev-foot)}

  /* Three page columns, three equal tracks, zero gap — so the band's internal
     boundary at 2/3 is the page's own line between column 4 and column 5. */
  /* margin-inline:0 undoes § 13f's auto margin. In the flex row that margin
     was what held the band against the cue; as a grid item it instead absorbs
     the track's spare width and floats the band off its own column line —
     invisible at 1440, where the content happens to fill the span exactly, and
     39px out at 1920, where it does not. Placement does the holding now. */
  .tl__head .ev{grid-template-columns:repeat(3,1fr);gap:0;margin-inline:0;
    align-items:stretch}

  /* --- columns 3–4 : the voice ----------------------------------------- */
  /* BOXED BY THE GRID, NOT BY A BOX. Both of the quote's edges are drawn now,
     and both land on a page line because the track does — the left on the line
     between columns 2 and 3, the right on the line between 4 and 5, which is
     also the count's left edge. Nothing is added to make this a panel: it is
     two hairlines of the same weight as the grid's own, standing exactly where
     the grid already stands, and the block between them is inset the same
     --inset on BOTH sides so the quote sits centred in its two columns rather
     than hung off one of them. */
  .tl__head .ev__panel--quote{grid-column:1 / 3;
    border-left:1px solid var(--rule);
    border-right:1px solid var(--rule);
    padding-inline:var(--inset);
    /* A MARK AT EACH EDGE, THE TEXT CENTRED BETWEEN THEM. The two marks are
       the same glyph at the same size, so the 1fr they bracket is centred on
       the column pair's own axis by construction — and the stack centred in
       that track is therefore centred on the axis too, with no arithmetic and
       nothing to drift. The stack is a one-cell grid holding every quote at
       once (§ 13f), so its width is the WIDEST quote's and does not change as
       the panel re-sets: the marks hold still while the words change. */
    grid-template-columns:auto minmax(0,1fr) auto}
  .tl__head .ev__panel--quote>.ev__stack--quote{grid-column:2;grid-row:1;
    justify-self:center}
  /* THE MARKS ARE SET LIKE THE FIGURE BESIDE THEM. Same serif, same --ev-num
     the count's numeral is set at, so the two columns read as one pair of
     objects at one scale rather than as a big number next to small punctuation.
     What they do NOT take is the numeral's ink: the figure is --ink-1 and the
     marks are --gold (declared with the mark itself, below), which is what
     keeps punctuation at 4rem from reading louder than the sentence it
     encloses while still matching it in size. */
  /* ON THE ROW'S AXIS, AND STAYING IN THE GRID TO GET THERE. The marks hold
     their two `auto` tracks — that is what reserves their width and keeps the
     text from crowding them at 1200, where the column pair has only 25px of
     air either side — so the vertical is done with a transform, which moves
     ink without moving layout.

     THE OFFSET IS MEASURED FROM THE GLYPH, NOT THE BOX. A quote mark occupies
     only the top quarter of its em box; centring the BOX on the axis would
     leave the ink well above it. --mark-ink is where this serif puts the
     middle of the commas, as a fraction of the em, read off rendered pixels at
     4x — and the closing mark sits .058em lower in its box than the opening
     one, which no amount of correct box alignment fixes. Two constants, both
     properties of the typeface rather than of the grid: if the display serif
     is ever changed, these are the numbers to re-measure. */
  .tl__head .ev__panel--quote>.ev__mark{
    display:block;
    font-size:var(--ev-num);
    align-self:end;
    --mark-ink:.157em;
    transform:translateY(calc(var(--ev-foot) + var(--ev-num)
                              - var(--mark-ink) - var(--row-axis)))}
  .tl__head .ev__panel--quote>.ev__mark--close{--mark-ink:.215em}
  .tl__head .ev__panel--quote>.ev__mark--open{grid-column:1;grid-row:1;
    justify-self:start}
  .tl__head .ev__panel--quote>.ev__mark--close{grid-column:3;grid-row:1;
    justify-self:end}
  /* centred type, because the block is centred. The attribution keeps its own
     row on the stack's floor (`1fr auto`, below) so it cannot move when the
     quote above it runs to one line or three — only its axis changes, from the
     column's right edge to the column's centre. */
  .tl__head .ev__panel--quote .ev__quote{text-align:center}
  .tl__head .ev__stack--quote .ev__item{justify-items:center}
  .tl__head .ev__stack--quote .ev__cite{justify-self:center}
  /* AND THE QUOTE SITS ON THE MARKS' AXIS, not at the top of the slack. The
     1fr row is as tall as the LONGEST quote in the set (the stack reserves
     that height by construction), so a one-line review left on the row's top
     edge opened a hole the height of a line between itself and its name, with
     the two marks stranded below the sentence they are supposed to bracket.
     Centred in the row, a short quote lands on --row-axis — the same axis the
     marks' ink is carried on — so the pair reads as punctuation flanking the
     line at every length, and the name stays where § 13f pinned it: on the
     panel's floor, level with the count's foot beside it. */
  .tl__head .ev__stack--quote .ev__quote{align-self:center}

  /* --- column 5 : the count -------------------------------------------- */
  /* THE ARROWS COME OUT OF THE FLOW. In a flex row they were taking 88px of a
     229px column off the top, leaving the label 141px to set in — which is why
     "YEARS BEHIND THE CHAIR" was breaking over two lines under a numeral that
     had room to spare. Held absolute against the column's own edges, the type
     underneath gets the whole column and the arrows still sit exactly where
     the reference puts them: one at each end, the numeral centred between.
     The column is a grid rather than a block so `align-content` can centre the
     stack in the stretched cell, on the same axis the quote is centred on. */
  .tl__head .ev__panel--stat{grid-column:3;position:relative;
    display:grid;grid-template-columns:minmax(0,1fr)}
  /* one cell, three occupants. The arrows were absolute against the PANEL,
     which was only ever right while the panel centred its content — settled on
     the foot line the panel's middle is no longer the figure's middle. Sharing
     the stack's cell they are positioned against the STACK instead: held to its
     top edge and then dropped half a figure (--ev-num * .43, the numeral's own
     line-height halved), so they sit on the figure's axis whatever the label
     under it does. Still out of the label's way — the type underneath gets the
     whole column, which is what took "YEARS BEHIND THE CHAIR" off two lines. */
  .tl__head .ev__panel--stat>.ev__stack{width:100%}
  .tl__head .ev__panel--stat>.ev__arrow{position:absolute;margin:0;
    bottom:var(--row-axis);transform:translateY(50%)}
  .tl__head .ev__panel--stat>.ev__arrow:first-child{left:0}
  .tl__head .ev__panel--stat>.ev__arrow:last-child{right:0}
  /* one line each, now that there is a column's worth of room for them.
     Stated as the label's own --foot-track rather than as a letter-spacing, so
     the text-indent that optically re-centres the word block (§ 13f) moves with
     it — declared separately the two drift, and the label sits half a track
     left of the numeral it belongs to. */
  .tl__head .ev__label{--foot-size:var(--ev-label-size);
    --foot-track:var(--ev-label-track);white-space:nowrap}
  /* the free-standing divider has nothing left to divide: every boundary on
     this row is a column edge now. */
  .tl__head .ev__divide{display:none}

  /* --- column 6 : the cue ---------------------------------------------- */
  /* THE CUE GETS A COLUMN INSTEAD OF A CORNER. It used to be `justify-self:end`
     — an object shrunk to its content, floating in column 6 with a 36px stub of
     hairline in front of it that belonged to no line on the page. The cell
     stretches now, its left border IS the line between columns 5 and 6 (and so
     the count's right edge), and the cue is held against the far right by the
     auto margin — tucked into the column's own inset, on the same axis as the
     headline's at the other end of the row. */
  .tl__head>.tl__corner{border-left:1px solid var(--rule);
    padding-inline:var(--inset);
    /* CENTRED IN ITS COLUMN, not held against the page edge. Tucked right it
       read as an object parked in the margin; centred it is the sixth member of
       the row, set on its column's own axis exactly as the count is set on the
       fifth and the quote on the third and fourth. It keeps the foot line — the
       word SCROLL still closes on the same horizontal as the headline, the
       attribution and the count's label. */
    justify-content:center}
  /* the stub is retired here: the column line does its job, and drawing both
     would put two verticals within 150px of each other saying one thing. */
  .tl__head .tl__cue::before{display:none}

  /* THE CHEVRONS LEAVE THE CUE'S COLUMN TO JOIN THE ROW'S AXIS. In flow they
     sat wherever the gap above the word SCROLL happened to put them, which was
     19px above the axis the arrows and the marks are on. Held against the
     corner at --row-axis they land on it exactly, and the word stays where it
     was — on the foot line, bottom of the cell — so the cue still reads as the
     pair travelling toward its own caption (§ 12a), just with the travel
     measured by the row instead of by a gap. The animation is untouched: it
     transforms the two <path>s, never the <svg> this rule moves. */
  .tl__head>.tl__corner{position:relative}
  .tl__head .tl__cue .scroll-cue__arrow{position:absolute;
    left:50%;bottom:var(--row-axis);
    /* 1.6px, and it is the pair's ink rather than its box that is being
       placed. The two chevrons are drawn in the UPPER part of a 0 0 17.4 22
       viewBox (y 5 to 20.1) and the lower one's miter carries another ~1.2
       units past that, so the ink the eye reads centres .584 of the way down a
       19px box instead of .5 — 1.6px below the box's own middle. Centring the
       box would sit the chevrons that far under the arrows and the marks. */
    transform:translate(-50%,calc(50% - 1.6px))}
  /* ...and the sideways pair takes no nudge at all: transposed, its ink runs
     y 0.9 to 16.5 in a 17.4 box, which is centred. The offset above is the
     vertical glyph's asymmetry and nothing else. */
  .tl__head .tl__cue .scroll-cue--x .scroll-cue__arrow{transform:translate(-50%,50%)}
}

/* THE LONGEST LABEL SETS THE STEP. A sixth of 1200px is 190px of column, and
   "APPOINTMENTS COMPLETED" — 22 characters at .6875rem on a .17em track —
   measures 205. It overflowed before this row was rebuilt too; what is new is
   that the column has drawn edges now, so the overflow is no longer invisible,
   it runs straight through the rule between the count and the quote. One step
   down in size and track takes the widest label to 177 and leaves 13px of air
   inside the narrowest desktop column, still on one line. Above 1366 there is
   room for the full setting and nothing changes. */
@media (min-width:1200px) and (max-width:1365px){
  /* on the head, not on the label, because --row-axis is computed from it */
  .tl__head{--ev-label-size:.625rem;--ev-label-track:.13em}
}

/* Below 1200 the line cannot hold the evidence AND the headline, so the
   evidence stops trying: it takes a line of its own under the head, which is
   where it sat before this change and where the claim rejoins it. Stated as a
   breakpoint rather than left to flex-wrap because a wrapped flex item keeps
   its auto margin and would sit hard right on its own line, which reads as a
   mistake rather than as a decision. */
@media (max-width:1199px){
  /* order:1 sends it to the END of the flex order, so the head's first line is
     the headline and the cue exactly as it was before § 13f — space-between,
     one at each margin — and the evidence takes the line below. Without it the
     evidence's 100% basis wraps immediately after the headline and strands the
     cue alone on a third line, bottom-left, reading as a mistake. */
  .tl__head>.ev{flex:1 1 100%;margin-left:0;align-self:stretch;order:1;
    margin-block:0;
    border-top:1px solid var(--rule);padding-block:var(--s-3) 0}
  /* the head has two lines now, so the stage owes the rail that height back */
  .tl{--tl-furniture:27.5rem}
  /* ...unless the second line is not in the head at all (§ 13g). .has-ev-band
     is set by evidence.js only once the evidence has actually been moved to
     the standing band, so the 5.5rem is handed back on the one condition that
     makes it free. With no JS the class never appears and the number above
     stands, which is the layout this file has always described. */
  html.has-ev-band .tl{--tl-furniture:22rem}
}

/* the section title is furniture here, not the subject — capped well under
   --t-section so the photographs keep the height it would otherwise take.
   Its ink is § 1a's (the section is now set on paper like the two below it),
   so the explicit --paper that was stated here is gone; only the size is this
   section's decision, and the cap
   is re-cut by the width ratio § 1 applies to the whole display ramp. */
/* THE CEILING MOVED, THE RAMP DID NOT (2026-09-11). 5.2vw is unchanged — it is
   still a slower ramp than --t-section's 8vw, so this headline is still the
   smaller of the two at every width they are both set at. What changed is where
   it stops: 4.75rem bound from 1462px up, which meant the headline froze while
   the two columns under it kept growing — 386px of type in 570px of column at
   1920, a block visibly not filling the space it had been given. Released to
   --t-section's own ceiling it tracks its columns to 1923 and fills ~88% of
   them at every desktop width, which is the measure the rest of the row is set
   to. The ~19px of head this buys at the widest screens comes out of the slack
   already sitting in the rail's row (27-36px at every size measured), not out
   of a photograph: --tl-band is a fixed height and the 1fr row it sits in is
   larger than it needs to be. */
.tl__head .section-title{font-size:clamp(2.75rem,5.2vw,6.25rem)}
/* the head's own eyebrow rule is retired with the eyebrow: the chapter's label
   lives on the rail now (.tl__rule above). Kept as a no-op guard would be dead
   weight, so it is simply gone. */

/* --- 13e. The headline lockup -------------------------------------------
   THE CLAIM SITS BESIDE THE TITLE, NOT UNDER IT. "Recent cuts" is two short
   words in a condensed display face and it never filled the line it was given;
   the eyebrow above it has gone up to the rail, which would have left it more
   alone still. § 4a's .lead — the hero's tracked hairline caps — takes the
   space, set as the two statements the reference sets rather than as one
   sentence.

   ALIGNED ON BASELINES, NOT ON BOXES. `align-items:baseline` on this row puts
   the LAST line of the claim on the headline's baseline, which is the axis the
   eye actually reads the pair along. `flex-end` would have matched the two
   boxes' bottoms instead, and the display face's descender depth would have
   left the caps floating a few px high — the kind of misalignment that is
   invisible to a measurement and obvious to a reader. */
.tl__title{display:flex;align-items:stretch;gap:var(--s-4);
  flex-wrap:wrap;min-width:0}
.tl__title .section-title{flex:none}
/* the claim keeps the .lead's own size, weight and .38em track (§ 4a B); what
   is stated here is only that its two lines set tight enough to read as a
   block beside a headline, and that they never wrap into a third. --ink-2 is
   the .lead's already — restated only so the pair is legible as one decision
   with the keys in the corner below. */
/* RETIRED 2026-09-11, by request: "Sharper versions." is not set at any width.
   It was already hidden from 1200 up — § 13f dropped it so the evidence could
   have the line, on the argument that a client's sentence about the work beats
   ours standing next to it. Asked to go on tablet and phone as well, that
   reasoning simply finishes: the head is headline and cue, and the voice on the
   line (§ 13i) or on the plate (§ 13g) is the claim.
   The rule is kept rather than deleted, and so is the markup, because the line
   is COPY and retiring it is an editorial decision that should be one word to
   reverse — `display:none` back to `flex` — rather than a re-write. */
.tl__claim{display:none}
.tl__claim--set{margin:0;line-height:1;color:var(--ink-2);white-space:nowrap;
  display:flex;flex-direction:column;justify-content:center;gap:.5em}

/* --- 13c. The hairline columns, redrawn on the stage ----------------------
   § 3 draws the grid once, `fixed`, at z 0 — and every opaque chapter buries
   it. #intro already answers that with .grid-rules--inline; this section could
   not, because its layer is not a direct child of a <section> and § 3's
   `:where(section) > .grid-rules--inline` therefore never reaches it. Stated
   here instead, for the stage: same gutter, same --grid-cols, same 1px on
   --rule, so the verticals land on the page's own x-positions by construction.
   ON THE STAGE, NOT ON .tl__rise. The rise is the thing that scales and
   emerges (§ 13b); columns that scale with it would read as an artefact of the
   animation rather than as the page's structure. Held by the stage they stand
   still, full-height, and the band moves past them — which is the whole point
   of a grid. The stage clips (overflow:hidden), so they end exactly at the
   window's edges. */
.tl__stage>.grid-rules--inline{position:absolute;inset:0;z-index:0}
.tl__stage>:not(.grid-rules){position:relative;z-index:1}

/* --- 13d. The corner — § 12a's foot, in this chapter's ink ----------------
   The plate closes on a line of metadata (the keys, a slash, a free-standing
   vertical hairline, the cue) and the first chapter under it now closes its
   head on the same one. Everything that makes that line what it is comes
   across as the same two dials § 12a declares — one size and one tracking for
   the keys, the slash and the word SCROLL together — so the corner reads as
   one component and not as a caption with an arrow parked beside it.
   What is NOT inherited is ink: the plate is ink and this chapter is paper, so
   the cream literals are replaced by the tokens § 1a already resolves per
   chapter (--ink-1/2, --rule). The gold on the chevrons is unchanged; it is
   the arrow's alone at both ends. */
.tl__corner{
  --foot-size:.6875rem;
  --foot-track:.22em;
  --cue-label:var(--ink-3);
  /* THE LOOP INVERTS, THE GESTURE DOES NOT. On the plate the pass runs cold
     gold -> warm cream, because arriving means getting BRIGHTER against ink.
     On paper that same warm cream is the ground, so the arrival would read as
     the chevron disappearing. The two ends are tokens now, and this chapter
     states the same journey the only way a light ground can: cold gold ->
     the site's --gold at full. Timing, travel, opacity and easing are
     untouched — only which end of the contrast the arrival sits on. */
  --cue-cold:rgba(176,138,46,.34);
  --cue-warm:var(--gold);
  display:flex;align-items:center;gap:var(--s-4);flex:none}
.tl__keys{display:flex;align-items:center;gap:var(--s-4);
  list-style:none;margin:0;padding:0;min-width:0}
.tl__keys .fact{display:flex;align-items:center;padding:0;border:0;min-width:0}
/* punctuation, not furniture — the same slash the plate's keys are divided by,
   a step quieter than the keys so the join never competes with the content */
.tl__keys .fact + .fact::before{
  content:"/";margin-right:var(--s-4);
  font-size:var(--foot-size);line-height:1;
  color:var(--ink-3)}
.tl__keys .fact__key{
  margin:0;
  font-family:var(--font-display);
  font-size:var(--foot-size);
  line-height:1;
  letter-spacing:var(--foot-track);
  text-transform:uppercase;
  font-variation-settings:"wdth" var(--wdth-body),"wght" 400;
  font-weight:400;
  color:var(--ink-2);
  white-space:nowrap}
/* the divider is § 12a's: a short vertical hairline set clear of the row on
   both sides, an object beside the cue rather than an edge of it. Shorter here
   (36 to the plate's 44) because it is measured against a two-line head, not
   against the full foot of a screen. */
.tl__cue{display:flex;align-items:center;gap:var(--s-4);flex:none}
.tl__cue::before{content:"";flex:none;width:1px;height:36px;background:var(--rule)}

/* THE WINDOW IS A SCROLLER UNTIL SCRIPT SAYS OTHERWISE. With no JS at all the
   reader gets a plain horizontal scroller carrying the whole set at full
   colour — which is a complete reading of this chapter, and was not true while
   the window was `overflow:hidden` waiting for a scroll handler that never
   came. .is-marquee, set by app.js, is what closes it and hands the strip to
   the drift. */
.tl__viewport{overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;
  align-self:stretch;width:100%;display:flex;align-items:flex-start}
/* THE MARQUEE'S WINDOW.
   touch-action:pan-y is the one rule that keeps the drag honest on a phone:
   the browser keeps VERTICAL panning for the page and hands the horizontal to
   us, so a reader who means to scroll past this section always can, and a
   reader who means to drag the reel never scrolls the page by accident.
   cursor:grab is stated and will usually not be seen — barber-pole-cursor.css
   takes every cursor on the site for the drawn pole. It is here for the path
   where that cursor is down, and because the affordance is a fact about this
   element whether or not the site chooses to draw it. The word DRAG in the
   corner (§ 13, .tl__cue) is what actually carries it. */
.tl.is-marquee .tl__viewport{overflow:hidden;touch-action:pan-y;cursor:grab;
  user-select:none;-webkit-user-select:none}
.tl.is-marquee.is-dragging .tl__viewport{cursor:grabbing}
/* the focus ring is the keyboard's only sign that the arrows do anything here,
   so it is drawn inside the window where it cannot be clipped by the overflow */
.tl__viewport:focus-visible{outline:2px solid var(--gold);outline-offset:-3px}
.tl__track{display:flex;gap:var(--s-4);padding-inline:var(--gutter);
  will-change:transform;align-items:flex-start}
/* THE GUTTER GOES WHEN THE STRIP LOOPS. The inset put the first photograph's
   edge on the grid's first line, which is the right answer for a row that has
   a first photograph. A marquee has none — and a trailing inset is a gap the
   width of the gutter opening at the seam every time the position wraps. */
.tl.is-marquee .tl__track{padding-inline:0}
.tl-frame{flex:none;position:relative;
  transition:opacity var(--d-fast) ease,transform var(--d-move) var(--ease-out);
  height:var(--tl-band);width:min(var(--tl-cap),calc(var(--tl-band) * .75))}
.tl-frame img{width:100%;height:100%;aspect-ratio:auto;object-fit:cover}
/* A photograph is a draggable object to the browser and a native image drag
   aborts ours mid-gesture. The pointer is taken off the image rather than the
   frame so the press still lands on the viewport, which is what listens. */
.tl.is-marquee .tl-frame img{pointer-events:none;-webkit-user-drag:none}
/* THE RESTING STATE, and it is the marquee's alone: the far end of the focal
   plane's own curve (app.js — TL_DIM, TL_SHRINK), so the frame script has not
   painted yet is already the frame script would paint at the edge of the
   strip. Without script this is not the composition at all — the no-JS
   scroller above shows the set at full colour, which is the honest floor. */
.tl.is-marquee .tl-frame{opacity:.22;transform:scale(.94)}
.tl.is-marquee .tl-frame img{filter:grayscale(1) contrast(1.05)}
/* THE BAND, MEASURED RATHER THAN ESTIMATED. --tl-band above is arithmetic on
   --tl-furniture, and --tl-furniture is a guess at everything in the stage that
   is not a frame. It was accurate when each frame carried a caption line; those
   went, and the number did not, so the guess has been ~55px too generous ever
   since and the frames have been sitting in a row taller than themselves with
   the slack falling out at the bottom.
   The row is already the exact answer — grid solves `minmax(0,1fr)` against the
   real head, scale, rule and padding — so the viewport becomes a size container
   and the frame reads ITS height off it. 100cqh is what 100svh minus the
   furniture actually came to; nothing is left over.
   The two rules are unchanged, only their input is: the crop floor still caps
   the height at --tl-cap / --tl-crop (a tall screen still refuses to stretch a
   portrait past the floor, and the leftover falls under the frames as before),
   and the width is still three quarters of the height under the --tl-cap
   dominance cap. cqh cannot resolve a percentage through .tl__track's auto
   height, so the height is stated in cqh too rather than as 100%.
   Scoped off .is-static: that path puts the stage back in flow with an auto
   height, and a size container with no definite height from outside would
   contain itself to nothing. The declarations above stay as its sizing, and as
   the floor for an engine without container units. */
@supports (height:100cqh){
  .tl:not(.is-static) .tl__viewport{container-type:size}
  .tl:not(.is-static) .tl-frame{
    height:min(100cqh,calc(var(--tl-cap) / var(--tl-crop)));
    width:min(var(--tl-cap),75cqh)}
}
/* THE FOCAL PLANE — the shout's rails (§ 22), turned on their side.
   Colour here is not a state one frame is in and its neighbours are not: it is
   a band of colour held across the middle of the stage that the reel travels
   THROUGH. The frame on the centre line comes out of grayscale into the site's
   own grade at full, the ones either side of it are part-way out, and the
   extremities are honestly monochrome — so a pass reads as a ramp with one
   peak rather than as three brightness steps. The amount is per-frame and a
   function of where each photograph currently IS, which no selector can ask,
   so script paints filter/opacity/scale off the same smootherstep the rails
   use (app.js § the focal plane). The rules above are the resting state and
   the no-script state; the classes below still carry the ACTIVE frame for the
   year scale and for anything that needs to name it, not a look of their own.
   Deliberately no transition while script drives: the amount is already a
   continuous function of position, and a transition on top of it would be a
   second, slower clock fighting the first. */
.tl.is-marquee .tl-frame{transition:none}
/* THE RAIL'S TOP EDGE IS NOW THE CHAPTER'S LABEL. Same line, same weight, same
   colour, same gutter-to-gutter span, one grid gap of air off the frames — the
   only change is that it starts AFTER the words instead of at the margin,
   because § 4a's .eyebrow--ruled is the device for exactly this and the markup
   now carries it. The height:1px is gone with it: the box is a line of type
   now, and the hairline is the <i> inside it, laid on the type's own centre by
   `align-items:center` rather than by an offset. --tl-furniture is unchanged;
   the row grew by the difference between 1px and one --t-micro line, which the
   media queries below absorb along with the head's other tuning.
   The rule's ink is § 4a's --rule-fade, not the flat --rule the bare line
   carried: a hairline that runs OFF a label is a connective and reads as one
   only if it lets go at the far end. That is the same fade the About and How
   it works chapters open on, so all three now open identically. */
/* the label is TYPE and takes the inset; the frames below it are structure and
   keep the line (.tl__track, --gutter). That difference is the system, seen in
   one section: the rail's words start inside the cell the first photograph's
   edge is drawn on. */
.tl__rule{margin-inline:var(--pad-type);margin-bottom:0}
.tl__scale{padding-inline:var(--gutter)}
/* The rail's foot, and nothing else. It used to be a flex row of year markers
   sitting on a border-top; with the markers gone the border IS the element, so
   the padding that used to hold type off the line is gone with them and the box
   is the 1px it draws. Left as a border rather than rewritten as a height so it
   stays demonstrably the same hairline .tl__rule opens the band with. */
/* THE PLAYHEAD IS GONE WITH THE SWEEP. .tl__bar was a gold fill reporting how
   far through the track the scroll had carried the rail. A strip that loops has
   no "how far through" — the number it would report is the phase of a cycle,
   which is not a position in anything the reader is trying to reach. A progress
   rule that cannot progress is a second, wrong clock. Removed 2026-09-15. */
.tl__years{border-top:1px solid var(--rule)}

/* --- 13f. The evidence — the record, read out ------------------------------
   Two panels on one row: the COUNT, a free-standing vertical hairline, the
   VOICE. It is the composition § 12a already uses at the foot of the plate
   (keys, rule, cue) opened out to full width and given the serif — the one
   place on this page where a NUMBER is the subject rather than a caption.

   WHY THE STACK IS A GRID. Every item in a sequence is laid in the SAME grid
   cell (grid-area 1/1), so the box is as tall as the tallest quote and as wide
   as the widest numeral from the first paint. Nothing reflows when the content
   changes — the arrows never shuffle sideways as 265 becomes 2,156, and the
   rule never jumps as a two-line quote becomes a three-line one. This is the
   whole reason the panels can cross-fade at all: a fade between boxes of
   different sizes is a fade plus a layout shift, and the eye reads the shift.

   VISIBILITY, NOT DISPLAY. Hidden items carry visibility:hidden rather than
   display:none, which takes them out of the accessibility tree as cleanly but
   leaves them in the grid, still contributing their height. display:none would
   collapse the box to whichever item happens to be showing.

   THE MOTION IS ONE GESTURE, TWICE. Type enters from BELOW and leaves UPWARD,
   so a change reads as a line being re-set rather than a slide arriving: the
   entrance takes --d-reveal on --ease-out (the site's own reveal, 700ms) and
   the exit takes --d-fast (300ms) on an ease-in, because leaving should be
   quicker than arriving or the panel feels reluctant. Nothing here is a new
   curve — both tokens are § 1's. */
.ev{
  --ev-gap:clamp(var(--s-4),3.4vw,var(--s-6));
  /* THE HANDOVER, AS A DELAY RATHER THAN AS A TIMER. The incoming item is made
     current the instant the change is decided; it simply does not START for one
     --d-fast, which is exactly how long the outgoing item takes to leave. Doing
     it here instead of in a setTimeout means the DOM always has exactly one
     .is-current — there is no 300ms window where the row's state says nothing
     is showing, which is a lie about the panel and a trap for anything reading
     it (a probe, a screen reader, the next person). Same picture, honest state. */
  --ev-enter:var(--d-fast);
  display:grid;
  /* THE VOICE LEADS (2026-09-11). It was count | rule | voice; the head's
     composition now reads headline · quote · count · cue, and the DOM was
     re-ordered to match it rather than re-ordered by grid placement — the
     reading order and the visual order are the same sentence at every width.
     So the flexible track is first here and the count's intrinsic one last. */
  grid-template-columns:minmax(0,1fr) 1px auto;
  align-items:center;
  gap:var(--ev-gap);
  /* IT IS A MEMBER OF THE HEAD LOCKUP, NOT A ROW UNDER IT. The head is a
     space-between flex of headline and cue, and between them sat an empty half
     of the line — the headline is two short words in a condensed face and has
     never filled its measure. The evidence goes there, pushed hard against the
     cue by the auto margin, so the line reads headline … evidence · scroll.
     Two things follow from that. It costs the stage almost nothing, because
     the height was already reserved by the headline beside it. And it no
     longer needs a rule of its own: the head sits under the rail's foot
     hairline already, and a second line here would have drawn a box around a
     lockup that is meant to read as one line of type.
     ALIGNED ON THE BASELINE-ISH FOOT, not the box: `flex-end` matches the
     head's own align-items so the label and the cue's word sit on one axis. */
  flex:0 1 auto;min-width:0;margin-left:auto}

.ev__panel{display:flex;align-items:center;min-width:0}
.ev__panel--stat{gap:clamp(var(--s-2),1.2vw,var(--s-3));flex:none}
 /* mark and text, side by side; the text column then stacks the quote over
    its attribution, which is what puts the name bottom-right of the column. */
.ev__panel--quote{display:grid;grid-template-columns:auto minmax(0,1fr);
  column-gap:var(--s-3);align-items:start}
.ev__panel--quote>.ev__mark{grid-column:1;grid-row:1}
.ev__panel--quote>.ev__stack--quote{grid-column:2;grid-row:1}

/* the divider — § 12a's free-standing rule, stood on its end. It is a full
   height line rather than a centred stub because it is dividing two BLOCKS of
   type here, not two words on one line. */
.ev__divide{display:block;width:1px;align-self:stretch;background:var(--rule);
  min-height:2.5em}

/* --- the stack ---------------------------------------------------------- */
.ev__stack{display:grid;list-style:none;margin:0;padding:0;min-width:0}
.ev__item{grid-area:1/1;min-width:0;
  opacity:0;visibility:hidden;
  transition:visibility 0s linear var(--d-fast)}
.ev__item.is-current,.ev__item.is-leaving{opacity:1;visibility:visible;
  transition:visibility 0s}

/* THE TWO LINES DO NOT MOVE TOGETHER. The label trails the numeral by 90ms
   and the attribution trails the quote by the same, which is under a tenth of
   a second — far too short to read as a sequence, just long enough that the
   pair arrives as a composition being set rather than as one block sliding.
   It is the difference between typesetting and a transition. */
.ev__stack--stat .ev__item{display:flex;flex-direction:column;align-items:center;
  gap:var(--stat-gap)}
/* THE ATTRIBUTION IS ITS OWN OBJECT NOW, not the quote's last line. Two rows:
   the quote sets from the column's left edge, the name is pushed to its right
   edge — the corner nearest the scroll cue, which is the one place on this line
   where a small piece of type can sit without competing with anything. The two
   are related by the measure they share (the name's right edge and the quote's
   are the same line) rather than by sitting in the same paragraph. */
.ev__stack--quote .ev__item{display:grid;grid-template-rows:auto auto;
  row-gap:clamp(var(--s-2),.9vh,var(--s-3));justify-items:start}
.ev__stack--quote .ev__quote{grid-row:1}
.ev__stack--quote .ev__cite{grid-row:2;justify-self:end}
.ev__item>*{margin:0;opacity:0;transform:translateY(.3em)}
.ev__item>:nth-child(2){--ev-d:90ms}
.ev__item.is-current>*{opacity:1;transform:none;
  transition:opacity var(--d-reveal) var(--ease-out) calc(var(--ev-enter) + var(--ev-d,0ms)),
             transform var(--d-reveal) var(--ease-out) calc(var(--ev-enter) + var(--ev-d,0ms))}
/* --ev-dx is the EXIT delay, separate from --ev-d's entrance delay. They have
   to be separate for the quote panel: entering, the quote leads and the name
   follows it in; leaving, the name goes first and the quote holds longest.
   One dial for both would force the pair to leave in the order they arrived,
   which reads as a loop rather than as a line being struck and re-set. */
.ev__item.is-leaving>*{opacity:0;transform:translateY(-.26em);
  transition:opacity var(--d-fast) ease-in var(--ev-dx,var(--ev-d,0ms)),
             transform var(--d-fast) ease-in var(--ev-dx,var(--ev-d,0ms))}

/* --- the numeral -------------------------------------------------------- */
/* The serif, at the one size on this page where it is the subject. Lining,
   tabular figures: a proportional 1 would let 2,156 and 617 sit on visibly
   different centres as they swap, which on a centred stack reads as a wobble. */
.ev__num{
  font-family:var(--font-serif);
  font-size:var(--ev-num);
  line-height:.86;
  letter-spacing:.005em;
  color:var(--ink-1);
  font-variant-numeric:lining-nums tabular-nums;
  white-space:nowrap;
  display:block}
.ev__label{
  font-family:var(--font-display);
  font-size:var(--foot-size,.6875rem);
  line-height:1.2;
  letter-spacing:var(--foot-track,.22em);
  text-transform:uppercase;
  font-variation-settings:"wdth" var(--wdth-body),"wght" 400;
  color:var(--ink-3);
  text-align:center;
  /* the tracking is on the right of the last letter too; pulled back so the
     word block sits optically centred under the numeral rather than left of it */
  text-indent:var(--foot-track,.22em)}

/* PER-DIGIT, WHEN THE SCRIPT HAS SPLIT IT. evidence.js wraps each character of
   the numeral in .ev__d and numbers it; each then carries the same gesture 44ms
   behind its neighbour. At that interval the digits are not read as arriving
   one by one — the whole numeral still lands as one object — but the edge of
   the movement is soft instead of flat, which is what makes a cross-fade look
   set rather than switched. The unsplit numeral (no JS) animates as one block
   through the rule above, so the fallback is the same gesture without the
   refinement. */
.ev__num.is-split{opacity:1;transform:none;transition:none}
.ev__num.is-split>.ev__d{display:inline-block;opacity:0;transform:translateY(.3em)}
.ev__item.is-current .ev__num.is-split>.ev__d{opacity:1;transform:none;
  transition:opacity var(--d-reveal) var(--ease-out) calc(var(--ev-enter) + var(--i,0) * 44ms),
             transform var(--d-reveal) var(--ease-out) calc(var(--ev-enter) + var(--i,0) * 44ms)}
.ev__item.is-leaving .ev__num.is-split>.ev__d{opacity:0;transform:translateY(-.26em);
  transition:opacity var(--d-fast) ease-in calc(var(--i,0) * 22ms),
             transform var(--d-fast) ease-in calc(var(--i,0) * 22ms)}

/* --- the choreography, quote and attribution -----------------------------
   THREE BEATS IN, THREE BEATS OUT, AND NOT IN THE SAME ORDER.

   IN   quote rises and fades up          at --ev-enter
        the rule draws, left to right     at --ev-enter + 260ms
        the name arrives from the right   at --ev-enter + 400ms
   OUT  the name leaves to the right      at 0
        the rule closes, right to left    at 40ms
        the quote goes up last            at 120ms

   The reversal is the point. The pair reads as a sentence being SET and then
   STRUCK, not as a card flipping: entering, the quote leads because it is the
   content and the name is its footnote; leaving, the footnote goes first,
   because a quote whose attribution has already gone is visibly finished.

   THE NAME MOVES SIDEWAYS, THE QUOTE MOVES UP. Two axes, so the eye reads two
   objects with a relationship rather than one block sliding. The name's axis is
   the one it is aligned on — pushed to the column's right edge, it arrives from
   the right and leaves the same way, which makes the alignment look chosen
   rather than like the end of a line.

   THE RULE IS THE HINGE. It draws toward the name before the name exists and
   closes away from it after the name has gone, so the two are never both still
   for the length of the sequence. It is the beat that makes the pair read as
   choreographed rather than merely staggered. */
.ev__stack--quote .ev__quote{--ev-d:0ms;--ev-dx:120ms}
.ev__stack--quote .ev__cite{--ev-d:400ms;--ev-dx:0ms}
.ev__stack--quote .ev__cite{transform:translateX(.7em)}
.ev__stack--quote .ev__item.is-current .ev__cite{transform:none}
.ev__stack--quote .ev__item.is-leaving .ev__cite{transform:translateX(.7em)}
.ev__item.is-current .ev__cite::before{transform:scaleX(1);
  transition:transform 420ms var(--ease-out) calc(var(--ev-enter) + 260ms)}
.ev__item.is-leaving .ev__cite::before{transform:scaleX(0);transform-origin:100% 50%;
  transition:transform 200ms ease-in 40ms}

/* THE NAME SITS ON THE FLOOR OF THE COLUMN, not directly under the quote.
   1fr on the first row gives the quote the slack, so the attribution is pinned
   to the bottom edge whether the quote above it runs to one line or three. A
   name that floated up and down with the length of the quote would move every
   time the panel changed — the one piece of type on this line that must not,
   because it is aligned to the scroll cue beside it. */
.ev__stack--quote .ev__item{grid-template-rows:1fr auto}

/* --- the arrows --------------------------------------------------------- */
/* Real controls. 44px of target around a 10px glyph (the touch floor), drawn
   as nothing but the chevron so the row stays type and rules. Gold on arrival
   is the same journey the scroll cue makes at both ends of this chapter. */
.ev__arrow{
  flex:none;display:grid;place-items:center;
  width:44px;height:44px;margin:-11px;
  padding:0;border:0;background:none;cursor:pointer;
  color:var(--gold);
  transition:color var(--d-fast) var(--ease-out);
  -webkit-tap-highlight-color:transparent}
.ev__arrow svg{width:10px;height:16px;overflow:visible}
.ev__arrow path{stroke:currentColor;stroke-width:1.25;
  stroke-linecap:square;stroke-linejoin:miter;fill:none;
  vector-effect:non-scaling-stroke}
/* THE HOVER HAD TO MOVE, because the rest state took the colour it used to
   arrive on. It cannot go the other way either — the cue's cold end is the same
   gold at .34 alpha, which is 1.3:1 on paper and reads as a disabled control
   rather than a quiet one. So the arrival DEEPENS the metal instead of
   switching it: gold carried 22% toward the page's ink, which is 4.2:1 and
   still unmistakably the same colour. Same gesture, same family, one direction
   further along it. */
@media (hover:hover) and (pointer:fine){
  .ev__arrow:hover{color:color-mix(in srgb,var(--gold) 78%,var(--ink))}
}
.ev__arrow:focus-visible{outline:1px solid var(--gold);outline-offset:2px;
  color:color-mix(in srgb,var(--gold) 78%,var(--ink))}

/* --- the quote ---------------------------------------------------------- */
/* The opening mark is a display object, not punctuation: it is the reference's
   own device and it is set large, quiet and hung OUTSIDE the text block so the
   quote's first line starts on the panel's left edge rather than indented
   behind it. Its own line-height is killed so it cannot add height to a row
   that is measured in vh. */
/* GOLD, THE SAME GOLD THE CHEVRONS ARE DRAWN IN (2026-09-11). The row's three
   non-type objects — the marks, the count's arrows, the cue's chevrons — now
   share an axis (§ 13f), and sharing an axis while wearing two different inks
   read as a coincidence rather than as a set. --gold is exactly what the cue
   already arrives on: .tl__corner's --cue-warm IS var(--gold), and its cold end
   is the same hue at .34. One metal across the three columns. */
.ev__mark{
  flex:none;
  font-family:var(--font-serif);
  font-size:clamp(2rem,3vw,2.75rem);
  line-height:1;
  color:var(--gold);
  transform:translateY(-.06em)}
/* the closing half belongs to the centred two-column composition only (§ 13f,
   >=1200). Stacked under the headline the quote sets from the left margin, and
   a mark closing a left-aligned block is a mark floating in the slack after the
   shortest line. */
.ev__mark--close{display:none}
.ev__quote{
  font-family:var(--font-serif);
  font-size:clamp(1.0625rem,1.5vw,1.375rem);
  line-height:1.42;
  letter-spacing:.004em;
  color:var(--ink-1);
  /* the editorial measure — under the 65–75ch ceiling so a quote is read in
     two or three short lines, the way the reference sets it */
  max-width:34ch;
  text-wrap:pretty}
.ev__cite{
  font-family:var(--font-display);
  font-size:var(--foot-size,.6875rem);
  line-height:1.2;
  letter-spacing:var(--foot-track,.22em);
  text-transform:uppercase;
  font-variation-settings:"wdth" var(--wdth-body),"wght" 400;
  color:var(--ink-3)}
/* THE EM DASH IS DRAWN, NOT SET. The reference puts a dash before the name;
   here it is a hairline of the same weight as every other rule on the page,
   which makes the attribution a small object with a leading rule — the same
   construction the scroll cue uses two columns to its right (§ 13d), so the
   two read as siblings across the line instead of as type and furniture.
   It is also the thing the choreography needs: a dash cannot be drawn, and a
   rule can, so the sequence has a middle beat to land on. */
.ev__cite{display:flex;align-items:center;gap:var(--s-3);white-space:nowrap}
.ev__cite::before{content:"";flex:none;
  width:clamp(18px,2.4vw,40px);height:1px;background:var(--rule);
  transform:scaleX(0);transform-origin:0 50%}

/* --- the evidence, at the smaller screens -------------------------------- */
/* The panels stop being a row before the quote's measure collapses. Side by
   side, a 34ch quote under ~560px of column would set to five or six lines and
   the band would be taller stacked-as-a-row than it is as two rows. */
@media (max-width:900px){
  /* the evidence gives up the line and wraps under the headline — the head is
     already flex-wrap, so this is the margin releasing rather than a layout */
  /* the panels stop being a row: the quote's measure would collapse */
  .ev{grid-template-columns:minmax(0,1fr);gap:clamp(var(--s-3),1.6vh,var(--s-4))}
  /* the divider was dividing two columns; between two rows it would be a
     horizontal line one row below the one this band already sits on */
  .ev__divide{display:none}
  .ev__panel--stat{justify-content:flex-start}
  .ev__quote{max-width:52ch}
  .tl{--ev-num:clamp(2.25rem,7vw,3rem)}
}
@media (max-width:640px){
  .ev{padding-block:var(--s-3) 0}
  .tl{--ev-num:2.125rem}
  .ev__quote{font-size:1rem;line-height:1.38}
  .ev__mark{font-size:1.75rem}
  .ev__label,.ev__cite{letter-spacing:.16em}
}

/* MOTION DECLINED. The sequence does not advance on its own here — evidence.js
   never starts its clock (a panel that re-sets itself IS motion, however
   quietly it fades) and the arrows swap instantly instead. The reader still
   reaches every figure and every quote; nothing on the row moves unless they
   ask it to. */
@media (prefers-reduced-motion:reduce){
  .ev__item>*,
  .ev__item.is-current>*,
  .ev__item.is-leaving>*,
  .ev__num.is-split>.ev__d,
  .ev__item.is-current .ev__num.is-split>.ev__d{
    transition:none;transform:none}
  .ev__item.is-leaving{opacity:0;visibility:hidden}
  .ev__item.is-current>*,
  .ev__item.is-current .ev__num.is-split>.ev__d{opacity:1}
}

/* --- 13g. THE RECORD — the evidence, standing on its own ground ------------
   Below 1200 the evidence is not part of the cuts head at all. evidence.js
   moves the one [data-evidence] node out of the pinned stage and into this
   plate (see the file's own note), which sits in ordinary flow immediately
   after the section — so it is the plate that rises into the space the stage
   vacates as it releases (§ 13a). The reader finishes the reel and lands on
   what the reel adds up to.

   WHAT IT BUYS THE PHOTOGRAPHS. Measured on a 390x844 phone: the rail's
   viewport was 454px of 844 (54%) with 200px of head under it (24%). With the
   head back to one line the grid's 1fr row solves to ~611 and the frame takes
   605 of it — 72% of the screen, and a photograph 33% larger in area. Nothing
   was added to the page to get it; a footnote stopped being printed inside the
   chapter's one full-screen window.

   THE PLATE'S GRAMMAR IS THE SECTION'S ABOVE IT. Same paper, same inline
   hairline columns, and it opens on the same .eyebrow--ruled device — "The
   work" closes, "The record" opens. One line of type with a rule running off
   it is this page's way of naming a chapter, and this is a chapter now.
   --------------------------------------------------------------------------- */
.evb{position:relative;background:var(--paper);color:var(--ink);
  padding-block:clamp(var(--s-5),7vh,var(--s-7)) clamp(var(--s-6),9vh,var(--s-8))}
/* the mount is empty and the section is `hidden` until script fills it, and
   `hidden` has to beat the display above — an attribute the browser sets no
   layout for is worth more than a class here, so it is stated rather than
   relied on. */
.evb[hidden]{display:none}
.evb>.grid-rules--inline{position:absolute;inset:0;z-index:0}
.evb>:not(.grid-rules){position:relative;z-index:1}
.evb__inner{padding-inline:var(--pad-type);display:grid;gap:var(--s-5)}
.evb__rule{margin-bottom:0}

/* --- the evidence, as a plate rather than as a row ------------------------
   THE PLATE STYLES WHAT STANDS ON IT, BY LOCATION. Every rule here is scoped
   to .evb and not to a class on the evidence itself, because from 2026-09-11
   the two panels do not travel together: below 720 the voice and the count are
   both on this plate; from 720 to 1199 the voice goes back up into the pinned
   head (§ 13i) and only the count stands here. A class on the node would have
   to be right about which panels it currently held. The plate cannot be wrong
   about what is inside it.

   THE FIGURE'S SIZE IS DECLARED ON THE PLATE, and it has to be: --ev-num is a
   token on .tl (§ 13), because three objects in the cuts head are positioned
   off it. Outside that section it does not resolve at all and font-size falls
   back to inherited — which is how the ledger's numerals first came out at body
   size. It is stated on .evb rather than on .ev for the same reason as above:
   at 720 the .ev container is not on this plate and the ledger still is. */
.evb{--ev-num:clamp(2.25rem,7.5vw,3.25rem)}
.evb .ev{grid-template-columns:minmax(0,1fr);align-items:start;
  gap:var(--s-5);margin-left:0;flex:none;padding-block:0}
.evb .ev__divide{display:none}

/* THE ATTRIBUTION FOLLOWS THE QUOTE, it does not sit at the foot of the box.
   § 13f pushes the name to the bottom of a 1fr row so the two panels' feet
   line up across the head's row. There is no second panel beside it here, so
   the same rule only opens a hole between a one-line review and its name —
   and the hole is as tall as the longest quote in the set, because the stack
   reserves that height by construction. */
.evb .ev__stack--quote .ev__item{grid-template-rows:auto auto;
  row-gap:var(--s-3)}

/* THE VOICE KEEPS ITS CLOCK, and it is the only thing on this plate that
   moves. A quote is read serially — five at once is a wall of reviews, which
   is a different and weaker claim than one review the page keeps re-setting.
   It gets the size the head could never give it: a whole plate's measure. */
.evb .ev__panel--quote{align-items:start}
.evb .ev__quote{font-size:clamp(1.0625rem,4.4vw,1.375rem);line-height:1.42;
  max-width:34ch}
.evb .ev__mark{font-size:clamp(2rem,8vw,2.75rem)}

/* THE COUNT DOES NOT CYCLE HERE — every verified figure is laid out at once,
   and evidence.js never starts its clock in this home. That is not a
   concession to the small screen; it is what the composition is for. In the
   head, one cell of a row can only carry a record by re-setting itself. Here
   there is width for the whole thing, and a grid that also cycled would be a
   grid whose cells changed under the reader.

   The stack is laid `grid-auto-flow:row` and every item is released from the
   shared 1/1 cell it shares in the head. Their opacity/transform resting state
   (§ 13f) is overridden wholesale, digits included: nothing here is mid-
   gesture, because nothing here is arriving. */
.evb .ev__panel--stat{display:block;flex:none;width:100%}
.evb .ev__stack--stat{grid-auto-flow:row;gap:0;width:100%}
.evb .ev__stack--stat .ev__item{grid-area:auto;visibility:visible;opacity:1;
  flex-direction:row;align-items:baseline;justify-content:space-between;
  gap:var(--s-4);text-align:left;
  border-top:1px solid var(--rule);padding-block:var(--s-3);transition:none}
.evb .ev__stack--stat .ev__item:last-child{border-bottom:1px solid var(--rule)}
.evb .ev__stack--stat .ev__item>*,
.evb .ev__stack--stat .ev__num.is-split>.ev__d{opacity:1;transform:none;
  transition:none}
.evb .ev__stack--stat .ev__label{text-align:right}
/* the figure leads the line and the label closes it, so the numerals stack on
   one left edge and read down as a column of counts rather than as five
   centred captions. */
.evb .ev__num{line-height:.86}

/* THE UNVERIFIED FIGURE IS NOT LAID OUT HERE. In the head it is one item of a
   sequence and shows for 4.6s in every 27; laid out it would stand on the
   plate permanently, which is a louder claim than the source supports — and
   this one has no source at all (UNVERIFIED.md: no Google Business Profile
   located 2026-09-11, and the Maps card that does exist says 52, not 55).
   It stays in the DOM, and in the head's cycle, exactly as it was: this rule
   withholds it from the one composition that would publish it as settled fact. */
.evb .ev__stack--stat .ev__item[data-unverified]{display:none}

/* --- 13i. THE HEAD AT TABLET — headline, voice, scroll -------------------
   From 720 to 1199 the VOICE goes back up into the pinned viewport and takes
   the line between the headline and the scroll cue, which is the composition
   § 13f built for the desktop head. The COUNT does not go with it: five
   figures need a plate (§ 13g/h) and one review needs a line, and the whole
   point of splitting the two panels across two homes is that they are
   different KINDS of statement and always were.

   WHY THIS IS NOT THE 1200 GRID, RE-USED. Above 1200 the head is
   `repeat(var(--grid-cols),1fr)` and every object owns whole columns — that
   works because --grid-cols is 6 up there. At 900 and below the page drops to
   TWO columns (§ 3), so `grid-column:3 / 6` is not a line that exists on a
   768 iPad; it would silently create implicit tracks and the row's edges would
   stop being the page's edges, which is the one thing the six-column rule was
   written to guarantee. So this range states its own three tracks —
   intrinsic · slack · intrinsic — and draws the two rules itself. The reading
   is the desktop's; the arithmetic is honest about the grid underneath it.

   WHAT IT COSTS THE RAIL, STATED. A quote set in ~26ch runs to three lines at
   768 where it runs to two at 1200, so the head is ~30px taller here than a
   headline alone would make it, and --tl-band is 100svh minus the head. That
   is the trade the composition asks for and it is still nowhere near the
   layout this started from: the rail was 553px (54%) at 768 before the record
   left the stage, and it is ~700px (68%) with the voice back on the line. */
@media (min-width:720px) and (max-width:1199px){
  .tl__head{
    display:grid;
    grid-template-columns:auto minmax(0,1fr) auto;
    grid-template-rows:auto;
    padding-inline:var(--pad-frame);
    align-items:end;
    gap:0}
  .tl__head>.tl__title{grid-column:1;padding-left:var(--inset);
    align-self:end}
  /* § 13f's own placement, undone: below 1200 that rule gives the evidence a
     100% basis and a line of its own, which is exactly what this range no
     longer wants. */
  .tl__head>.ev{grid-column:2;flex:none;width:auto;order:0;
    align-self:stretch;margin-block:-12px;margin-left:0;
    border-top:0;padding-block:0}
  .tl__head>.tl__corner{grid-column:3;align-self:stretch;margin-block:-12px}

  /* THE TWO RULES ARE THE OBJECTS' OWN EDGES, drawn by the cells that need
     them rather than as free-standing elements — the same device the 1200 row
     uses, and the same 12px bleed at each end, so the pair of verticals starts
     and stops on one line. */
  .tl__head>.ev{border-left:1px solid var(--rule);padding-left:var(--ev-gap)}
  .tl__head>.tl__corner{border-left:1px solid var(--rule);
    padding-left:var(--s-4);align-items:flex-end;
    padding-bottom:var(--ev-foot)}
  .tl__head .tl__cue::before{display:none}

  /* the ev is one panel wide here, so its three-track head layout and its
     divider are both wrong: the voice takes the whole cell. */
  .tl__head .ev{grid-template-columns:minmax(0,1fr);gap:0;margin-inline:0;
    align-items:end}
  .tl__head .ev__divide{display:none}
  /* THE THREE PIECES OF TYPE CLOSE ON ONE LINE. The evidence and the corner
     both take § 13f's 12px bleed — it is what buys the rail its last 24px —
     which means their BOXES end 12px below the head's own foot. Left alone the
     headline closed at 984, the attribution at 988 and the cue's word at 996: a
     12px stagger that reads as three things that happened to stop near each
     other. The same --ev-foot device the 1200 row uses puts the type back on
     the head's foot line while the boxes keep their bleed. */
  .tl__head{--ev-foot:12px}
  .tl__head .ev__panel--quote{align-content:end;padding-bottom:var(--ev-foot)}
  /* the closing mark goes. It is a pair only where the quote is CENTRED
     between two column edges (the 1200 row); set left against a rule, a mark
     closing it on the right would be punctuating a line that has no right
     edge of its own. § 13f already makes exactly this call below 1200. */
  .tl__head .ev__mark--close{display:none}
  .tl__head .ev__mark--open{font-size:1.75rem;align-self:start}
  .tl__head .ev__quote{font-size:1rem;line-height:1.4}
  .tl__head .ev__cite{justify-self:end}
  /* the count is not here — it is on the plate below (§ 13g) */
  .tl__head .ev__panel--stat{display:none}
}

/* --- 13h. THE FIGURE'S ARRIVAL — five moments, not one ripple -------------
   The ask: an epic moment for each figure on a phone. The obstacle was not the
   animation, it was the ARITHMETIC, and it is worth writing down because it is
   the reason this section changes the composition and not just the timings.

   Measured at 390x844: five ledger rows, 64px each, 320px of ledger on an
   844px screen. Every figure is on the screen at once. Any trigger — one
   observer, five observers, a view() timeline — fires them 64px of scroll
   apart, which at a normal phone flick is about 64ms. Five figures 64ms apart
   is one ripple with a soft edge. It is not five moments, and no easing curve
   can make it into five.

   A MOMENT NEEDS SCROLL TO HAPPEN IN. So on a phone each figure is given a
   band of its own — ~28svh, the numeral at poster scale inside it — and the
   ledger becomes ~140svh of reading. Now consecutive figures are ~236px of
   scroll apart: a beat at flick speed, a held pause at reading speed. The
   figure is the only thing on the screen that is arriving.

   THE GESTURE IS § 13f's, IN § 13f's ORDER, WITH ONE INVERSION.
   The quote panel's choreography is already three beats — content, rule,
   footnote — and this is the same three:

       1. the rule draws, left to right        at 0
       2. the numeral rises, digit by digit    at 180ms, 70ms apart
       3. the label arrives from the left      at 520ms

   THE INVERSION IS THE RULE GOING FIRST, and it is forced by what the rule IS
   here. In the quote it is a hinge between two pieces of type and the content
   leads it. In the ledger it is the row's own top edge — the line that says a
   figure starts here — so it cannot arrive after the figure it contains. Drawn
   first, it reads as the band opening; drawn last it would read as a box being
   closed around something already standing in it.

   70ms BETWEEN DIGITS, NOT § 13f's 44. That interval was chosen so the numeral
   would land as ONE object while the panel cross-faded — the edge soft, not the
   digits counted. At poster scale the digits are separate objects whether the
   stylesheet likes it or not, and the cascade is the thing worth watching:
   2,156 arrives in four beats. Same gesture, read at a size where it can be.

   ONCE, UPWARD, AND NEVER AGAIN — § 14's M7. Not scrubbed to the scroll: a
   scrubbed numeral un-sets itself on the way back up, and a number that
   un-counts reads as a bug, not as a parallax.

   THE RESTING STATE IS VISIBLE. Every rule below is scoped to .ev--reveal,
   which evidence.js sets on the stack only once it has an IntersectionObserver
   to take the figures out of it again. No script, no class, no hidden figures —
   the ledger simply stands, complete, exactly as § 13g lays it out.
   ------------------------------------------------------------------------- */

/* --- the phone composition: one figure, one band ------------------------- */
@media (max-width:719px){
  /* POSTER SCALE. § 13g's 3.25rem ceiling is a ledger's figure — sized to sit
     in a row opposite its label. A figure that owns a band is the largest type
     on this page after the hero, and 22vw puts 2,156 across the full measure
     at 390 with the gutter intact. */
  .evb{--ev-num:clamp(4rem,22vw,7rem)}
  /* the band. min-height rather than height: a two-line label at 320px must be
     able to make its own row taller rather than be cropped by a round number. */
  .evb .ev__stack--stat .ev__item{
    flex-direction:column;align-items:flex-start;justify-content:center;
    min-height:28svh;gap:var(--s-3);padding-block:var(--s-5);
    text-align:left}
  /* the label moves under the numeral and on to its left edge, so the two are
     one lockup on one axis. text-indent is § 13f's compensation for the
     tracking on the last letter of a CENTRED label; left-aligned it would push
     the whole line off the numeral's stem by a quarter of a character. */
  .evb .ev__stack--stat .ev__label{text-align:left;text-indent:0}
}

/* --- the three beats ------------------------------------------------------
   Scoped to .ev--reveal (the stack, set by script). The specificity here is
   deliberate and not accidental: § 13f's resting states are written against
   .ev__item.is-current, five classes deep, because in the head exactly one item
   is current and the rest are hidden. In the band every item is laid out and
   the FIRST one still carries .is-current from the markup — so a rule that did
   not out-rank it would leave figure one standing while the other four
   animated. Every pair below is written :not(.is-in) / .is-in at six classes
   for that reason. */
.ev--reveal .ev__item{position:relative;border-top:0}
.ev--reveal .ev__item:last-child{border-bottom:0}

/* 1. THE RULE. A border cannot be drawn — its extent is not animatable — so the
   row's top edge becomes a pseudo-element laid on the same line, scaled from
   its left end. Same 1px, same --rule, same gutter-to-gutter span: what changes
   is that it now arrives. The last row's closing rule is the ::after, drawn
   with the label so the band shuts at the end of its own sentence. */
.ev--reveal .ev__item::before,
.ev--reveal .ev__item:last-child::after{content:"";position:absolute;
  left:0;right:0;height:1px;background:var(--rule);
  transform:scaleX(0);transform-origin:0 50%}
.ev--reveal .ev__item::before{top:0}
.ev--reveal .ev__item:last-child::after{bottom:0}
.ev--reveal .ev__item.is-in::before{transform:scaleX(1);
  transition:transform var(--d-reveal) var(--ease-out)}
.ev--reveal .ev__item.is-in:last-child::after{transform:scaleX(1);
  transition:transform var(--d-reveal) var(--ease-out) var(--fig-label)}

/* 2. THE NUMERAL, out of a mask. § 14's .word-mask device, applied to a figure:
   the digits start a whole line-height below the box and rise into it, so the
   numeral is not faded in — it is SET, the way type is set. The clip line has
   to fall just under the baseline or the digits' feet are cut while they
   travel, hence the .1em of padding pulled straight back out again in margin:
   the mask is deeper than the box without the box being taller. */
.ev--reveal .ev__num{overflow:hidden;display:block;
  padding-bottom:.1em;margin-bottom:-.1em}
.ev--reveal .ev__item:not(.is-in) .ev__num.is-split>.ev__d,
.ev--reveal .ev__item:not(.is-in) .ev__num:not(.is-split){
  opacity:1;transform:translateY(115%);transition:none}
.ev--reveal .ev__item.is-in .ev__num.is-split>.ev__d,
.ev--reveal .ev__item.is-in .ev__num:not(.is-split){
  opacity:1;transform:none;
  transition:transform var(--d-reveal) var(--ease-out)
             calc(var(--fig-lead) + var(--i,0) * var(--fig-step))}

/* 3. THE LABEL arrives along the edge it is aligned to, not up the axis the
   numeral just came up. Two objects on two axes read as a composition being
   set; two objects on one axis read as a block sliding. It is § 13f's own
   reasoning for the attribution, mirrored — that name is pushed right and
   arrives from the right, this label is set left and arrives from the left. */
.ev--reveal .ev__item:not(.is-in) .ev__label{opacity:0;
  transform:translateX(-.7em);transition:none}
.ev--reveal .ev__item.is-in .ev__label{opacity:1;transform:none;
  transition:opacity var(--d-reveal) var(--ease-out) var(--fig-label),
             transform var(--d-reveal) var(--ease-out) var(--fig-label)}

/* the dials, in one place. --fig-row is the extra delay a row takes from its
   POSITION in the ledger rather than from the scroll, and on a phone it is
   zero: there the scroll has already separated the figures and a second
   stagger on top would delay a figure the reader is looking straight at. */
.ev--reveal{--fig-lead:180ms;--fig-step:70ms;--fig-label:520ms}

/* A TABLET GETS THE STAGGER THE SCROLL CANNOT GIVE IT. From 720 up the ledger
   is § 13g's compact rows in the right-hand column of a two-column plate, and
   there all five really are 64px apart — the arithmetic at the top of this
   section, unfixed, because the composition that fixes it is a phone
   composition. So the ripple is made deliberate instead: each row takes 110ms
   from its index, set by script as --row, and the five read as a cascade down
   the column rather than as one flash. */
@media (min-width:720px) and (max-width:1199px){
  .ev--reveal .ev__item{
    --fig-row:calc(var(--row,0) * 110ms);
    --fig-lead:calc(180ms + var(--fig-row));
    --fig-label:calc(520ms + var(--fig-row))}
  .ev--reveal .ev__item.is-in::before{
    transition-delay:var(--fig-row)}
}

/* MOTION DECLINED — the ledger stands complete and nothing arrives. This is
   not the same as § 13f's decline: there the row keeps its arrows because a
   sequence still has to be steppable. Here there is no sequence and nothing to
   reach, so stillness costs the reader exactly nothing. evidence.js also
   declines to install the observer at all, so .is-in is handed to every row up
   front rather than left to a scroll that would do nothing. */
@media (prefers-reduced-motion:reduce){
  .ev--reveal .ev__item::before,
  .ev--reveal .ev__item:last-child::after,
  .ev--reveal .ev__item .ev__num.is-split>.ev__d,
  .ev--reveal .ev__item .ev__num:not(.is-split),
  .ev--reveal .ev__item .ev__label{
    opacity:1;transform:none;transition:none}
  .ev--reveal .ev__item::before,
  .ev--reveal .ev__item:last-child::after{transform:scaleX(1)}
}

/* THE ARROWS GO WITH THE CLOCK. They are the control for a sequence and there
   is no sequence on this plate. Left in place they would be two 44px targets
   that do nothing, in the one region of the page a thumb actually lands on —
   a control that does not control is worse than no control. */
.evb .ev__arrow{display:none}

/* The dominance dial, per screen. Only --tl-cap, --tl-crop and the furniture
   the smaller head leaves behind change — the sizing rule above is one rule at
   every width. A phone gets one photograph and the next one's edge; a tablet
   one and a sliver; a laptop one with its neighbours cropped at the frame. */
/* Portrait tablets are wider than the hand breakpoint but just as tall as they
   are narrow, so they are read as tablets here — at 40vw a 1024x1366 iPad would
   hit the crop floor and leave a void under the frames. */
@media (max-width:900px),(max-width:1100px) and (orientation:portrait){
  .tl{--tl-furniture:33rem;--tl-cap:70vw;--tl-crop:.64}
  .tl__head .section-title{font-size:clamp(1.5rem,4vw,2rem)}
  /* the claim drops below the headline rather than beside it — at this width
     the two side by side would push the corner onto a third row */
  .tl__title{align-items:flex-start;flex-direction:column;gap:var(--s-2)}
}
@media (max-width:640px){
  .tl{--tl-furniture:30rem;--tl-cap:90vw;--tl-crop:.60}
  /* THE RECLAIMED HEIGHT BECOMES PHOTOGRAPH, NOT AIR (§ 13g). With the record
     standing on its own plate the grid's 1fr row on a 390x844 phone solves to
     ~611px where it solved to 454. The frame reads that row directly (100cqh
     above), so it would take all of it — except that the CROP FLOOR caps the
     height at --tl-cap / --tl-crop, and at 90vw/.60 that is 585px. The band
     would grow to 585 and 26px would fall out under the frames as slack.
     .58 puts the ceiling at 605 and the slack back inside the photograph. It
     is a 2.5% tighter crop of a 3:4 portrait than the page already takes and
     the widths are untouched: the frame is still 90vw, so the next
     photograph's edge is exactly as visible as it was.
     --tl-furniture drops with it. It is the floor for an engine with no
     container units, and on that path the row really is 5.5rem shorter now. */
  html.has-ev-band .tl{--tl-furniture:21rem;--tl-crop:.58}
  .tl__rise{gap:var(--s-3);padding-block:calc(var(--header-h) + var(--s-2)) var(--s-4)}
  .tl__track{gap:var(--s-3)}
  .tl__head{gap:var(--s-2) var(--s-3)}
  /* the corner closes its gaps before it drops anything, § 12a's order */
  .tl__corner{gap:var(--s-3)}
  .tl__keys{gap:var(--s-3)}
  .tl__keys .fact + .fact::before{margin-right:var(--s-3)}
  .tl__head .section-title{font-size:1.5rem}
}
/* THE EVIDENCE IS STILL THE FIRST THING OFF A SHORT SCREEN (§ 13f), but it no
   longer takes --tl-furniture with it. In the head lockup the row buys no
   budget of its own; on a short screen it is dropped because the head has to
   wrap it onto a second line there and the photographs would pay for the wrap,
   not because the figure was ever reserved. The photographs are this chapter;
   the evidence is its footnote, and a footnote is what you cut. */
@media (max-height:700px){
  /* SCOPED TO THE HEAD (2026-09-11, § 13g). This was `.ev{display:none}` and it
     was the right call while the evidence had nowhere else to be: on a 667px
     screen the head's second line was ~150px of a band that only had 434px, so
     the footnote was cut and the photographs kept the height.
     It is the WRONG call once the evidence stands on its own plate below the
     section. There it costs the rail nothing at all, and a small phone — the
     shortest screen, the one that could see least of the record — was the one
     viewport being shown none of it. Every figure and every review was simply
     absent for a 375x667 reader. Scoping the rule to .tl__head keeps the
     original decision exactly where it still applies (no JS, evidence still in
     the stage) and withdraws it where it no longer does. */
  .tl__head .ev{display:none}
}

/* A phone on its side: the band is shorter than the furniture wants to be. The
   year scale is the one part of the stage the frames already say themselves
   (the captions carry the dates), so it is what goes. */
@media (max-height:560px){
  .tl{--tl-furniture:11.5rem;
    /* the foot padding this leaves is --s-3, so the rise starts inside it */
    --cuts-rise:8px}
  .tl__rise{gap:var(--s-2);padding-block:calc(var(--header-h) + var(--s-2)) var(--s-3)}
  .tl__scale{display:none}
  /* the band is shorter than the furniture wants. The label cannot go — it is
     the rail's top edge now, and dropping it would open the band at the top.
     The claim can: it is the one thing here the photographs restate. */
  .tl__claim{display:none}
  /* a phone on its side has no room for an affordance beside a headline, and
     the reader is already scrolling by the time this band arrives */
  .tl__cue{display:none}
  .tl__head .section-title{font-size:1.25rem}
}
/* --- 13a. The band in flow, and what the pin left behind ------------------
   THIS SECTION USED TO PIN. The stage was fixed to the viewport's foot behind
   the page, the hero above it was the opaque occluder that uncovered it, and
   .tl stood in flow as a stand-in 100vh + the track's travel tall so that
   vertical scroll could be the rail's transport. Built to
   workflows/ui-ux-design/implement-scroll-reveal-pin.md, measured to 1px at
   eight viewports, and correct — for a rail that had no other way to move.

   THE RAIL HAS ITS OWN CLOCK NOW (app.js — the marquee): it drifts, and the
   reader drags it. The pin's whole cargo has left, and what remains of a pin
   with no cargo is a section that spends four thousand pixels of the reader's
   scroll delivering one screen. So the pin is gone, and with it the fixed
   stage, the release, the landing sentinel and the scripted stand-in height.
   Removed 2026-09-15; see 07-operations/decisions/2026-09-15-the-work-marquee.md.

   WHAT IS LEFT is an ordinary 100svh band on the page's own paper, in flow,
   costing exactly the screen it occupies.

   THE SIBLING CONTRACT SURVIVES, and it is the one part of § 13a that was never
   about pinning. Every section below this one is lifted a layer and handed a
   ground if it has none — which is what lets § 23's CTA stand-in sit UNDER its
   own neighbours at z-index 0, and what keeps a section that forgot to declare
   a background from showing the band through itself. It was written as
   `.tl.is-pinned ~ *`, which meant it only held once script had decided to pin;
   it is `.tl ~ *` now and holds unconditionally. One class shallower, so every
   doubled class downstream written to outrank it still does, by more than it
   did. :where() keeps the ground at zero specificity so each section's own
   still wins.
   ------------------------------------------------------------------------- */
.tl ~ *{position:relative;z-index:1}
:where(.tl ~ *){background:var(--ground)}
/* Above the band, so the hero's cast (§ 13b) falls ON the photographs rather
   than being painted under them. */
.hero{z-index:1}

/* --- 13b. The seam — the hero's cast, and the band that rises into it -----
   § 13a keeps the hero one layer above this band. That is a z-order, and a
   z-order on its own reads as two flat plates: the band does not arrive from
   BEHIND the hero, it is merely painted after it. Two devices give the seam
   its depth, and BOTH OUTLIVED THE PIN — they were never its machinery. The
   pin gave them a reveal to happen during; the section entering the viewport
   gives them the same one, over the same distance, for nothing.

   1. THE CAST — the hero's own box-shadow, so it is emitted by the edge that
   casts it and travels with that edge exactly. No element, no observation, and
   nothing to keep registered: a full-width shadow under a full-width plate.

   Six layers, not one. A single shadow has one falloff and reads as a card's;
   stacking them lets the density be steep where the two planes nearly touch
   and long where the air is, which is what a deep soft cast actually does:

       below the hero's foot   0    .13×   .26×   .38×   .64×   .90×  of --cast
       black it lays down     .78    .46    .31    .21    .10    .03

   Every layer's offset clears half its own blur, so the whole cast lies BELOW
   the hero's foot and none of it leaks up over the plate, and the deepest
   layer's offset plus half its blur is exactly 1, so --cast is not a scale the
   numbers are struck from — it IS how far down the cast reaches. It is read
   off the viewport's height because the cast's job is a proportion of the
   screen, not a number of pixels; a hand gets a shorter one so it does not
   shade half a photograph.

   What makes it visible is not the ink — hero and stage are the same #101011,
   and black on black is nothing. It is the photographs and the paper-white
   type underneath: the frames rise up THROUGH the cast and out of it, which is
   the whole reading. It stays under reduced motion, being depth and not
   motion, and it lifts off by itself — the cast reaches --cast below the
   hero's foot, and at contain 0% that foot is the top of the screen.

   2. THE EMERGE — the band starts fractionally small and low and settles into
   place across the reveal, so the stage is not a still photograph being
   unveiled but a plane coming forward as the hero clears it.

   Anchored at the FOOT (transform-origin:50% 100%), for a reason the numbers
   force: the reveal uncovers this stage from the bottom of the screen upward,
   so the foot is the first band the reader sees and the one place the motion
   must not push content out of the window. Scaled about its foot, the band's
   top edge starts ~4.5vh low and rises while its bottom edge stays put; the
   only downward travel is --cuts-rise, kept under the stage's own foot padding
   at every size so the progress rule never leaves the screen.

   Slow by construction, not by duration: the range is the reveal itself, one
   whole viewport of scroll, and the easing spends it — a soft onset, the
   travel through the middle, and a long settle onto exactly scale 1. No
   opacity, no rotation, no overshoot; the section's own frames own the fades.

   Driven off the section's own view() timeline over `entry 0% entry 100%` —
   the whole of the band sliding up into the viewport, which for a 100svh
   section is exactly the one screen of scroll the pin's reveal used to be. It
   used to run `entry 0% -> contain 0%` off the stand-in, which was the same
   distance measured against a box 100vh + travel tall; with the stand-in gone
   the `contain` phase of a full-screen subject collapses to a single point and
   would leave the range with no length at all, so it is stated in entry alone.
   It is the ONE animation this section now runs, so it could ride either box;
   it stays on .tl__rise, which is the laid-out band and the thing that should
   be seen coming forward. Once, on the way in, and never again — § 14's M7,
   which the strip inside it is the page's one exception to.
   Withdrawn wholesale without view() timelines or under reduced motion: the
   band then sits where it is laid out, complete.
   ------------------------------------------------------------------------- */
.hero{--cast:clamp(200px,26vh,340px);
  box-shadow:
    0 calc(var(--cast) * .010) calc(var(--cast) * .015) rgba(0,0,0,.40),
    0 calc(var(--cast) * .045) calc(var(--cast) * .075) rgba(0,0,0,.34),
    0 calc(var(--cast) * .110) calc(var(--cast) * .170) rgba(0,0,0,.30),
    0 calc(var(--cast) * .220) calc(var(--cast) * .330) rgba(0,0,0,.26),
    0 calc(var(--cast) * .400) calc(var(--cast) * .600) rgba(0,0,0,.20),
    0 calc(var(--cast) * .620) calc(var(--cast) * .760) rgba(0,0,0,.13)}
@media (max-width:640px){.hero{--cast:clamp(150px,20vh,230px)}}

@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .tl{view-timeline-name:--cuts}
    .tl .tl__rise{
      transform-origin:50% 100%;
      will-change:transform;
      animation:cuts-emerge var(--ease-emerge) both;
      animation-timeline:--cuts;
      animation-range:entry 0% entry 100%}
    /* reduced motion is already excluded above; this is the other decline —
       script found the set too short to loop and put the section back to a
       plain scroller, where a band that rises into place is a flourish on a
       control */
    .tl.is-static .tl__rise{animation:none;transform:none}
  }
}
@keyframes cuts-emerge{
  from{transform:translate3d(0,var(--cuts-rise),0) scale(var(--cuts-scale))}
  to{transform:none}
}

/* THE DECLINE — reduced motion, or a set too short to be worth looping. A
   plain horizontal strip in flow, scrolled by the reader's own scroller, with
   the photographs at full colour and full size. The frames need no resetting
   here: the dim, the grey and the shrink all live on .is-marquee now (§ 13
   above), so this path never puts them on in the first place. The window is
   already a scroller by default for the same reason.
   The stage gives up its screen-height window — nothing is being clipped any
   more — and the track gives up whatever transform script last left on it. */
.tl.is-static .tl__stage{position:static;height:auto;animation:none;transform:none}
/* the row is exactly the frames' height here, so the caption hanging under each
   one needs the scroller to carry its line — nothing else reserves it. */
.tl.is-static .tl__track{padding-bottom:calc(var(--t-micro) * 1.4 + var(--s-2))}
.tl.is-static .tl__track{transform:none!important}

/* --- 14. Motion — M7: upward, once, 300–700ms, never loops ---------------- */
.mask{overflow:hidden;display:block}
.mask>*{display:block;transform:translateY(105%);transition:transform var(--d-reveal) var(--ease-out)}
.is-in .mask>*,.mask.is-in>*{transform:translateY(0)}
.word-mask{display:inline-block;overflow:hidden;vertical-align:bottom}
.word-mask>span{display:inline-block;transform:translateY(105%);transition:transform var(--d-reveal) var(--ease-out)}
.is-in .word-mask>span{transform:translateY(0)}
.reveal{opacity:0;transform:translateY(24px);
  transition:opacity var(--d-fast) ease,transform var(--d-move) var(--ease-out)}
.reveal.is-in,.is-in .reveal{opacity:1;transform:none}
[data-stagger="1"]{transition-delay:60ms}
[data-stagger="2"]{transition-delay:120ms}
[data-stagger="3"]{transition-delay:180ms}
[data-stagger="4"]{transition-delay:240ms}

/* --- 14a. THE CHAPTER CAMERA — § 12b's depth, spent on ONE plane -----------
   The hero composes THREE planes against each other. A .split and the closing
   plate have one photograph each, so the depth here is not between layers: it
   is between the photograph and the FRAME it is set into. The frame is nailed
   to the grid — its hairline, its glass corners, its veil, the ink over it —
   and the picture inside it lags DOWNWARD as the chapter passes the screen.
   The distance between the two is the camera, and it is the same reading § 12b
   gets from the hero: the middle of the page is no longer a stack of blocks
   that arrive and then hold still, it is a lens travelling past them.

   THE DIRECTION IS § 12b'S AND MUST STAY. Every plane on this site lags
   downward. A chapter that lagged upward would be a SECOND camera, and two
   cameras read as an effect where one reads as a lens.

   THE AMPLITUDE IS SMALL AND SPENT SLOWLY, for § 12b's reason: the whole
   travel is spent over the frame's entire passage through the viewport, so at
   reading speed nothing here is ever seen moving — it is only ever seen having
   moved. app.js clamps it inside --cam-over.

   THE OVERSCAN IS WHAT MAKES IT SAFE. The plane is grown by --cam-over at both
   ends and pulled back by that much at the top, so an offset inside --cam-over
   can never expose the frame's own edge. Both frames already clip (§ 4's
   .split__media, § 17's .cta), so no new clip is introduced here.

   TWO CLASSES ON PURPOSE. The plane's height has to outrank `.split__media img`
   and `.cta__bg img`, which give it 100% of a box it is now deliberately taller
   than; object-fit, the grade and the veil are still theirs. */
[data-cam]{--cam:0px;--cam-over:34px}
[data-cam] [data-cam-plane]{
  position:absolute;left:0;width:100%;
  top:calc(-1 * var(--cam-over));height:calc(100% + 2 * var(--cam-over));
  transform:translate3d(0,var(--cam),0);will-change:transform}
/* The seam the hero steps its own amplitude down on (§ 12b) and the width the
   splits stack at (§ 4) are the same idea: one screen, one picture, less room
   for the lens to travel in. */
@media (max-width:900px){[data-cam]{--cam-over:20px}}

/* --- 14b. THE READING POSITION ---------------------------------------------
   Not a reveal, and it does not obey § 14's "upward, once" law — it is a live
   READOUT, and since the cuts rail's playhead went with its sweep (§ 13, the
   marquee) it is the ONLY thing on the site that reports a position. That is
   the right number of them: a readout has to be reporting progress through
   something the reader is trying to get to the end of, and the only such thing
   here is the document. It adds no furniture:
   the line it fills is the header's own bottom border (§ 6), already drawn at
   1px in --rule, and --read is how far down the document the reader has come.
   It appears with the ground, because over the dark hero that border is
   transparent and there is no line to fill. */
.header::after{content:"";position:absolute;left:0;bottom:-1px;height:1px;
  width:100%;background:var(--gold);
  transform:scaleX(var(--read,0));transform-origin:0 50%;
  opacity:0;transition:opacity var(--d-fast) ease;pointer-events:none}
.header.is-stuck::after{opacity:1}

/* --- 15. Focus + reduced motion ------------------------------------------ */
:where(a,button,[tabindex]):focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.skip{position:absolute;left:-9999px;top:0;z-index:100;background:var(--ink);color:var(--paper);padding:var(--s-2) var(--s-3)}
.skip:focus{left:var(--pad-type);top:var(--s-2)}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:1ms!important;transition-duration:1ms!important}
  .mask>*,.word-mask>span,.reveal{transform:none!important;opacity:1!important}
  /* The hero's three planes settle flat (§ 12b). Zeroing the offsets rather
     than the transforms leaves translate3d in place, so the layers keep their
     own compositor layer and the composition — which is the depth, and stays —
     is identical to frame one of the moving version. app.js declines to run
     the parallax at all under this query; this is the belt to that brace, and
     it wins over the inline properties it would have written because an
     !important author rule outranks an inline declaration. */
  .hero__plate{--pz-bg:0px!important;--pz-tx:0px!important;--pz-fg:0px!important}
  /* § 14a settles flat for the same reason and in the same way: the offset is
     zeroed, not the transform, so each plane keeps its own compositor layer and
     the composition is identical to frame one of the moving version. app.js
     declines to run the camera at all under this query. */
  [data-cam]{--cam:0px!important}
}
html.shot *,html.shot *::before,html.shot *::after{transition-duration:0ms!important;animation:none!important}
html.shot .mask>*,html.shot .word-mask>span{transform:none!important}
html.shot .reveal{opacity:1!important;transform:none!important}
/* § 27's marker and its rules layer fade on .is-in rather than on .reveal */
html.shot .pay__n,html.shot .pay__rules{opacity:1!important}

/* ==========================================================================
   16. FOOTER — the colophon
   Supersedes the poster of 05-design/_outputs/footer-lockup-design-spec.md
   (the old § 16 + § 16b). Decision:
   07-operations/decisions/2026-09-05-footer-as-colophon.md

   WHAT WAS HERE. A 1325 x 998 poster: a drawn rectangle opened at one corner,
   LH set at ~970px, a ribbed glass BOOK slab over half the frame, a 184px
   arrow, and a scroll-driven glide — 1494px of footer at 1440, and by some
   distance the loudest object on a site whose entire argument is restraint.
   Nothing else on the page draws its own frame, spends a display line above
   --t-section, or carries a second glass material. It read as a second design
   stapled to the end of the first.

   WHAT IT IS NOW. Grammar the page has already used, read once more at the
   foot of it. No new device is introduced here at all.

     THE DIRECTORY  three blocks on the six columns, two columns each — VISIT,
                    HOURS, FOLLOW. Label over body at one size; the hierarchy
                    is weight, case and tracking (the old § 16's rule, kept).
     THE SIGNATURE  the section foot, verbatim: a condensed display line on the
                    left at --t-section, the connective beside it, the page's
                    one remaining action on the right, all standing on one
                    structural hairline. It is the same object as § 13's
                    "RECENT CUTS / REAL PEOPLE. SHARPER VERSIONS. / JAN-AUG
                    2026", and the CTA is the site's own .lhb-cta rather than a
                    cell of glass that exists nowhere else.
     THE BAR        the legal line, on the type axis, under the second rule.

   A DARK CHAPTER, NOT AN ISLAND. § 1a's four inks claim the footer now, like
   every other ink stretch, so .lead, .note and the display voice read here
   without restating anything. The --band-* token island (48 custom properties
   for one component) is gone; what survives as --footer-* is the surface, the
   focus ring, and the site-wide tokens the old block happened to host.
   ONE SIZE       label and body still compute to --t-footer. The single size
                  jump is the signature, and it is a step already on § 2's
                  ramp — not a seventh size.
   NO BOXES       ink, paper, hairlines. Every border is 1px, every radius 0.
   ========================================================================== */
:root{
  /* — surface. The four INKS are § 1a's, claimed below; these two are the
       ground and the focus ring, which no chapter token covers. — */
  --footer-surface:var(--ink);
  --footer-focus:var(--paper);                 /* the site's gold ring is
                                                  invisible on this ground */

  /* — the label / body voice. Site-wide tokens that have always been declared
       from this block; they are not the footer's own. — */
  --track-label:.08em;
  --wght-label:700;
  --wght-body:400;
  --wdth-body:100;
  --radius-flat:0;
  --border-hair:1px;

  /* — type: one size — */
  --t-footer:var(--t-body);
  --lh-footer:2;                               /* 32px pitch @1440 */

  /* — rhythm — */
  --footer-pad-top:var(--s-7);
  --label-gap:var(--s-4);                      /* label -> its content */
  --para-gap:calc(var(--t-footer) * var(--lh-footer));   /* one blank line */
  --sign-gap:var(--s-7);                       /* directory -> signature rule */
  --bar-gap:var(--s-6);                        /* signature -> bar rule */

  /* — z — */
  --z-footer-bg:0; --z-footer-rules:1; --z-footer-content:2;
}

.footer{
  position:relative;z-index:1;
  padding:var(--footer-pad-top) var(--gutter) 0;
  background:var(--footer-surface);
  color:var(--ink-1);
  font-size:var(--t-footer);
}

/* The hairlines, re-drawn inside the opaque ground. An absolute child resolves
   against the footer's PADDING box, which spans the full width — so inset:0
   hands the layer the whole band and its own padding-inline puts the lines on
   exactly the global x-positions. Never inset it by the gutter twice. */
.footer > .grid-rules--inline{
  inset:0;
  z-index:var(--z-footer-rules);
}

/* --- the directory -------------------------------------------------------- */
/* The six columns, and its blocks are cells: they take the inset as a MARGIN
   rather than the field's padding (§ 5b), so VISIT begins one inset inside the
   outer line, HOURS one inset inside the third, FOLLOW one inside the fifth. */
.footer__dir{
  position:relative;z-index:var(--z-footer-content);
  display:grid;
  grid-template-columns:repeat(var(--grid-cols),1fr);
  row-gap:var(--s-6);
}
.footer__dir > *{min-width:0;margin-inline:var(--inset)}
.footer__block--visit{grid-column:1 / 3}
.footer__block--hours{grid-column:3 / 5}
.footer__block--follow{grid-column:5 / 7}

/* --- type ---------------------------------------------------------------- */
/* Label and body differ by weight, case and tracking. Never by size or colour. */
.footer__label{
  font-family:var(--font-display);
  font-size:var(--t-footer);
  font-weight:var(--wght-label);
  font-variation-settings:"wdth" var(--wdth-body),"wght" var(--wght-label);
  line-height:1.4;
  letter-spacing:var(--track-label);
  text-transform:uppercase;
  color:var(--ink-1);
  margin-bottom:var(--label-gap);
}
.footer__block p,
.footer__links a{
  font-size:var(--t-footer);
  font-weight:var(--wght-body);
  font-variation-settings:"wdth" var(--wdth-body),"wght" var(--wght-body);
  line-height:var(--lh-footer);
  letter-spacing:0;
  color:var(--ink-1);
  max-width:22ch;
}
.footer__block p + p{margin-top:var(--para-gap)}

/* each hours row is its own element, so it can refuse to wrap on its own.
   901-960px is where "Tue, Wed: 11AM - 7PM" reaches 78% of a two-column span. */
.footer__hours span{display:block;white-space:nowrap;font-variant-numeric:tabular-nums}

/* FOLLOW keeps the body pitch, so the four links line up row-for-row with the
   four HOURS rows beside them. The 44px target is taken as padding and given
   straight back as a negative margin, so the column still measures on the
   glyph rather than on the hit box. */
.footer__links a{
  display:block;
  width:max-content;
  padding-block:calc((44px - var(--t-footer) * var(--lh-footer)) / 2);
  margin-block:calc((44px - var(--t-footer) * var(--lh-footer)) / -2);
  /* the rest underline is the site's hairline, so the link is marked as a link
     without colour being the only thing that says so; hover promotes it to the
     ink rather than introducing a second one. */
  text-decoration:underline;
  text-decoration-color:var(--rule);
  text-decoration-thickness:var(--border-hair);
  text-underline-offset:.22em;
  transition:text-decoration-color var(--d-fast) var(--ease-out),
    transform var(--d-fast) var(--ease-out);
}
/* the reference's footer gesture, translated: its polaroids LIFT under the
   pointer. This colophon owns no pictures — the lift is what is adopted, on
   the links, small enough (3px) to read as a rise rather than a move. The
   site's reduced-motion block strips only reveal transforms, so this hover
   states its own refusal. */
.footer__links a:hover,
.footer__links a:focus-visible{text-decoration-color:currentColor;
  transform:translateY(-3px)}
.footer__links a:active{opacity:.7}
@media (prefers-reduced-motion:reduce){
  .footer__links a:hover,.footer__links a:focus-visible{transform:none}
}

/* --- the signature -------------------------------------------------------- */
/* One structural hairline, run the full field — gridline to gridline — with the
   type standing one inset inside it, which is § 5a's law and the reason the
   rule is this element's border rather than a child of it. */
.footer__sign{
  position:relative;z-index:var(--z-footer-content);
  margin-top:var(--sign-gap);
  padding:var(--s-5) var(--inset) 0;
  border-top:var(--border-hair) solid var(--rule);
  display:grid;
  grid-template-columns:1fr auto;
  align-items:end;
  column-gap:var(--s-6);
  row-gap:var(--gap-label);
}
/* CONNECTIVE OVER PAYLOAD — the hero's own order ("MORE THAN A" / "CUT."),
   and the reason the tagline is not parked beside the mark: at 901px there is
   no room beside it, and a lead that wraps mid-sentence stops reading as a
   rule of small caps. Two rows on the left, the action spanning both on the
   right, so the CTA sits on the mark's own baseline. */
.footer__tagline{grid-area:1 / 1}
.footer__mark{grid-area:2 / 1}
.footer__cta{grid-area:1 / 2 / span 2 / auto;align-self:end;justify-self:end}
/* The display voice comes from § 4 (the selector list carries .footer__mark);
   the only thing stated here is the step it takes on § 2's ramp. It does not
   wrap: at every width from 390 to 1920 the line is under half the field. */
.footer__mark{font-size:var(--t-section);white-space:nowrap}

/* --- the bar -------------------------------------------------------------- */
.footer__bar{
  position:relative;z-index:var(--z-footer-content);
  margin-top:var(--bar-gap);
  padding:var(--s-4) var(--inset) var(--s-5);
  border-top:var(--border-hair) solid var(--rule);
  display:flex;flex-wrap:wrap;align-items:center;
  justify-content:space-between;
  gap:var(--s-3) var(--s-5);
  font-family:var(--font-display);
  font-size:var(--t-micro);
  font-weight:var(--wght-body);
  font-variation-settings:"wdth" var(--wdth-body),"wght" var(--wght-body);
  letter-spacing:var(--track-label);
  text-transform:uppercase;
  color:var(--ink-3);
}
.bar__id{display:flex;align-items:center;gap:var(--s-4)}
/* right-set, at every width: the ident line opens the bar and the copyright
   closes it on the outer line, one inset in. § 5a governs left edges, and this
   line has none — its relationship to the grid is the right rule. */
.bar__legal{margin-inline-start:auto;text-align:right}
/* the hero's monogram (.facts__mono), at caption scale */
.footer__seal{
  font-family:var(--font-serif);
  font-style:italic;
  font-size:1.25rem;
  line-height:1;
  letter-spacing:.02em;
  text-transform:none;
  color:var(--ink-2);
  transform:translateY(-.04em);
}
.bar__sep{
  width:var(--border-hair);height:1.1em;
  background:var(--rule);
}

/* paper on ink — the site's gold focus ring is invisible here */
.footer :where(a,button,[tabindex]):focus-visible{
  outline:2px solid var(--footer-focus);outline-offset:3px;
}

/* work.html's roam surface is position:fixed over a document that does not
   scroll, so the footer has nowhere to sit. It stays in the markup and comes
   back with the fallback flow, which is the only state of that page that
   scrolls. */
.canvas-page .footer{display:none}
@media (prefers-reduced-motion:reduce){.canvas-page .footer{display:block}}

/* --- responsive ----------------------------------------------------------- */
/* At --bp-lap the grid is two columns (§ 3), so the directory pairs up and
   FOLLOW takes the full width under them; the signature unstacks into the
   reading order it has always had — name, connective, action. */
@media (max-width:900px){
  .footer{--footer-pad-top:var(--s-6);--sign-gap:var(--s-6);--bar-gap:var(--s-5)}
  .footer__block--visit{grid-column:1 / 2}
  .footer__block--hours{grid-column:2 / 3}
  .footer__block--follow{grid-column:1 / -1}
  .footer__sign{
    grid-template-columns:1fr;
    align-items:start;
    row-gap:var(--gap-label);
  }
  .footer__cta{grid-area:auto;justify-self:start;margin-top:var(--s-3)}
}
@media (max-width:640px){
  .footer{--t-footer:.9375rem;--lh-footer:1.85;--footer-pad-top:var(--s-5)}
  .footer__dir{row-gap:var(--s-5)}
  .footer__block--visit,.footer__block--hours{grid-column:1 / -1}
  .footer__bar{gap:var(--s-3)}
}

/* --- 17. CTA — M8: eyebrow / oversized headline / one button, centred ----- */
.cta{position:relative;min-height:92svh;display:grid;place-items:center;
  overflow:hidden;text-align:center;background:var(--ink);color:var(--paper)}
.cta__bg{position:absolute;inset:0}
.cta__bg img{width:100%;height:100%;object-fit:cover}
.cta__bg::after{content:"";position:absolute;inset:0;background:rgba(16,16,17,.5)}
.cta__body{position:relative;padding:var(--s-8) var(--pad-type);width:100%;
  display:grid;justify-items:center;gap:var(--s-5)}
/* the ink is § 1a's — .cta is a dark chapter. Only the closed-up margin is
   this composition's: the centred stack sets its own gap. */
.cta .eyebrow{margin-bottom:0}
/* headline that splits around a block of images */
.cta__split{display:grid;justify-items:center;gap:var(--s-4)}
.cta__plate{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;
  background:var(--rule-invert);max-width:min(420px,72vw);border:1px solid var(--rule-invert)}
.cta__plate img{width:100%;aspect-ratio:1;object-fit:cover;filter:grayscale(1)}
@media (max-width:640px){.cta{min-height:82svh}.cta__body{padding-block:var(--s-7)}}

/* --- 17a. THE CLOSING PLATE AS A HELD SCENE ------------------------------
   Decision: 07-operations/decisions/2026-09-11-cta-as-a-held-scene.md.
   Driven by assets/js/cta-scene.js; § 17 above still owns the composition.

   WHAT CHANGED. § 17 composed the plate and stopped there: the section
   arrived in flow, held still and left, one block among the others. It is the
   loudest slot on the page and it read like the quietest. This is the same
   composition given a PASSAGE instead of a position.

   THE SEQUENCE, and it is the whole idea:
     the review wall (§ 9c) covers the plate entirely   the plate is BEHIND it
     the wall clears upward                             the plate is UNCOVERED
     nothing moves, for --cta-hold                      the plate IS the scene
     the process section (§ 22) arrives over it         the plate is BURIED

   HOW IT IS BUILT — three boxes and two negative margins, and no observation
   anywhere. .cta-stage is a tall stand-in; .cta is position:sticky inside it
   at exactly one viewport; the stand-in is pulled UP under the wall by
   --cta-lead and its foot is pulled up under the process section by one
   viewport. The two neighbours carry z-index 2 and an opaque ground, the
   stand-in carries 1 — so each neighbour OCCLUDES the pinned plate instead of
   pushing it. Uncovering and covering are therefore free: they are the
   neighbours' own scroll, at page speed, 1:1, and nothing can arrive a frame
   late or drift out of step with the depth.

   THE ARITHMETIC. With lead L, hold D and a viewport V the stand-in is
   2V + L + D tall and gives back L + V in margin, so the plate costs the
   reader V + D of document — one screen for the scene and the hold. The pin
   then divides exactly: L of reveal, D of hold, V of cover, and the cover
   lands on the frame the pin lets go. THE COVER LEG MUST BE ONE VIEWPORT,
   which is why the foot is a constant and only the lead is a variable.

   WHY --cta-lead IS MEASURED. The lead is an overlap with the section above,
   so it can never be longer than that section has to give. cta-scene.js sets
   it to min(one viewport, the wall's height minus a head it always keeps) —
   one viewport on every display the wall outgrows, and gracefully less on the
   ones it does not.

   THE DEPTH is § 12b's and § 14a's law spent on one scene, and it obeys the
   same two rules: the planes never travel in opposite directions, and every
   one of them is IDENTITY AT THE HOLD. What § 17 draws is what the reader
   looks at; everything here is approach and departure. cta-scene.js writes
   two numbers — how far into the approach (--sc-n) and how far into the
   departure (--sc-p), 0..1, at most one of them ever non-zero — and every
   amplitude below is stated here, in the stylesheet, exactly as § 22 states
   its own clocks.

   THE DEPTH IS TRANSFORMS ONLY, AND THE FADE IS SPENT ON THE FOUR LINES.
   Until 2026-09-15 that was a hard constraint: the pane carried a refracting
   backdrop, and an opacity, a filter or a mask anywhere above it made the pane
   a backdrop root, which left the refraction nothing behind it to bend. The
   glass is gone (§ 23a) and the constraint with it — but the composition is
   what the reader is reading, so the fade stays on the four lines rather than
   on their host, which is the right place for it either way.

   THE INDIVIDUAL TRANSFORM PROPERTIES, on purpose. translate/scale compose
   with `transform` rather than replacing it, so the press edge in
   cta-button.css, § 14's .mask rise and § 14a's own --cam all keep the
   transform they already own and this section adds a second channel. */
.cta-stage{position:relative}

/* THE LEG — one viewport, named once, because three things have to agree on
   it exactly or the cover does not land on the frame the pin lets go: the
   plate's own height, the foot the stand-in gives back, and the two of them
   inside the stand-in's height. cta-scene.js reads it off the plate's own box
   rather than restating the number.

   AND IT IS lvh, NOT svh OR dvh. A phone's URL bar is the whole reason: svh is
   the viewport with the bar SHOWN, so a plate cut to it would leave a band of
   bare stand-in under the scene the moment the bar retracted; dvh would resize
   the pinned plate mid-scroll, which is a layout shift inside a transition.
   lvh is the tallest the viewport ever gets, so the plate is never shorter
   than the screen — with the bar showing it simply runs a little off the
   bottom, which costs air this composition has to spare. On a desktop the
   three units are the same number and this reads as 100vh. */
.cta-stage.is-staged{
  --cta-leg:100lvh;
  /* BELOW BOTH NEIGHBOURS, which is the whole mechanism. § 13a lifts every
     sibling below the cuts band to z-index 1 (`.tl ~ *`), so the
     stand-in is taken back DOWN to 0 rather than the neighbours being taken
     up — a lower floor cannot be outrun by a section added later. */
  z-index:0;
  /* the ink is the plate's own ground. The stand-in is never visible — the
     sticky plate covers the viewport across the whole pin, and the wall
     covers it either side — so this is the colour of a mistake, not of a
     surface, and it is ink so that a mistake would not flash paper. */
  background:var(--ink);
  height:calc(2 * var(--cta-leg) + var(--cta-lead,100lvh) + var(--cta-hold,44svh));
  margin-top:calc(-1 * var(--cta-lead,100lvh));
  margin-bottom:calc(-1 * var(--cta-leg));
}
/* THE TWO OCCLUDERS. Both already render on a ground (§ 13a hands one to every
   sibling below the cuts pin); it is restated here because this section's
   layering depends on it and may not inherit that from another section's
   decision. Both already redraw § 3's hairlines inline, which is the one
   thing an opaque ground would otherwise bury.
   IT IS --ground, NOT --paper. What these two need is OPACITY — a sheet the
   lit plate cannot show through — and not one particular colour, which is why
   stating the colour here was a bug the moment a section under the pin stopped
   being paper. The lead IS the review wall, and the wall is on § 5c's warm
   panel: hard-coded, this rule repainted it --paper at the doubled specificity
   and took the chapter's ground off it every time the scene wired up. Reading
   the role, each occluder covers in whatever its own section stands on. */
/* The doubled class is deliberate and is the only thing it is for: § 13a's
   § 13a's `.tl ~ *` would otherwise hold both of
   these at z-index 1, level with the stand-in, where paint order would fall
   back to tree order and the plate would come out IN FRONT of the wall. */
.is-cta-lead.is-cta-lead,
.is-cta-cover.is-cta-cover{position:relative;z-index:2;background:var(--ground)}
/* the edge that does the covering, given a thickness — the cast of a sheet
   passing in front of a lit scene. Static on purpose: each of these edges
   is only ever over the plate, so the cast is already exactly as long as
   the transition is and costs nothing per frame to drive. */
.is-cta-lead {box-shadow:0  34px 66px -18px rgba(16,16,17,.55)}
.is-cta-cover{box-shadow:0 -34px 66px -18px rgba(16,16,17,.50)}

.cta.is-scene{
  --sc-n:0;   /* 0..1 into the approach  — 1 is fully behind the wall    */
  --sc-p:0;   /* 0..1 into the departure — 1 is fully under the process  */
  --sc-d:calc(var(--sc-n) + var(--sc-p));   /* distance from the hold, 0..1 */
  /* HOW FAR ANY OF IT TRAVELS, as two multipliers rather than as sixteen
     numbers. Every amplitude below is written for the desktop frame and then
     scaled by these, so the narrow step at the foot of this section is two
     lines and not a second copy of the choreography. --sc-y is travel ACROSS
     the frame, --sc-z is travel THROUGH it. */
  --sc-y:1;--sc-z:1;
  position:sticky;top:0;
  height:var(--cta-leg,100lvh);min-height:0;
  /* NO HEADER COMPENSATION, and it is measured rather than assumed: § 6's
     header is `is-hidden` and translated off the top by the time the plate
     arrives, because the whole of this passage is travelled downward. The
     scene is therefore centred in the WHOLE viewport, which is what puts the
     plate at exactly the offset § 17's flow composition puts it at — the
     held frame and the old section are the same picture. Scrolling back up
     brings the header with it, and the plate's top edge clears it. */
  /* a long lens. The depth is meant to be felt at the edges of the frame,
     not read off the middle of it — a short perspective would throw the
     plate's own keyline out of square as it recedes. */
  perspective:1600px;perspective-origin:50% 46%;
}
/* perspective reaches DIRECT children only, and the plate is a child of the
   body, not of the section — so the body is the plate's own 3D context. It
   is otherwise untouched: the composition it carries is § 17's. */
.cta.is-scene .cta__body{padding-block:0;perspective:1600px;perspective-origin:50% 46%}

/* THE ROOM — the deepest plane, and the one § 14a's camera already rides.
   --cam stays app.js's: this transform is on the FRAME, so both the lag
   inside it and the plane-covers-frame margin § 14a's probe measures come
   through untouched. 1.07 is the overscan the push-back spends — at -76px
   through a 1600px lens the plane still renders 1.02 of its box, so the
   ink behind it can never show at an edge. */
.cta.is-scene .cta__bg{
  translate:0 calc((var(--sc-n) *  14px + var(--sc-p) * -22px) * var(--sc-y))
              calc((var(--sc-n) * -60px + var(--sc-p) * -76px) * var(--sc-z));
  scale:1.07;
}
/* the atmosphere: the scene is darkest at both ends of the passage and
   clearest at the hold. An alpha rather than an opacity, for § 23a's
   reason — this pseudo-element is what the plate's glass is bending. */
.cta.is-scene .cta__bg::after{background:rgba(16,16,17,calc(.5 + var(--sc-d) * .28))}

/* THE PLATE — the mid plane. Further in z than the room and further in y,
   which is the parallax itself: the nearer object travels the more. */
.cta.is-scene .cta__pane{
  translate:0 calc((var(--sc-n) *   26px + var(--sc-p) *  -64px) * var(--sc-y))
              calc((var(--sc-n) * -130px + var(--sc-p) * -170px) * var(--sc-z));
}
/* THE COMPOSITION — the front plane, and the only stagger in the scene:
   the four lines travel a little further the lower they sit, so the stack
   opens and closes rather than arriving as one board. */
.cta.is-scene .cta__pane>.eyebrow,
.cta.is-scene .cta__pane>.display,
.cta.is-scene .cta__pane>.lhb-cta,
.cta.is-scene .cta__pane>.cta__aside{opacity:calc(1 - var(--sc-d) * .66)}
.cta.is-scene .cta__pane>.eyebrow   {translate:0 calc((var(--sc-n) * 10px + var(--sc-p) * -24px) * var(--sc-y))}
.cta.is-scene .cta__pane>.display   {translate:0 calc((var(--sc-n) * 15px + var(--sc-p) * -34px) * var(--sc-y))}
.cta.is-scene .cta__pane>.lhb-cta   {translate:0 calc((var(--sc-n) * 20px + var(--sc-p) * -44px) * var(--sc-y))}
.cta.is-scene .cta__pane>.cta__aside{translate:0 calc((var(--sc-n) * 24px + var(--sc-p) * -52px) * var(--sc-y))}

/* THE NARROW STEP, at § 14a's own seam. A phone is not shown a different
   sequence — it is shown the same one at the amplitude its frame can carry.
   Two thirds of the travel across the frame, half of it through, because the
   plate occupies under half the screen there instead of four fifths and the
   same 26px would read as a lurch rather than as depth. The lens comes in
   with them: a 1600px perspective across a 330px plate is barely a
   perspective at all, and 1100px restores the same foreshortening per pixel
   of push-back that the desktop frame gets. */
@media (max-width:900px){
  .cta.is-scene{--sc-y:.62;--sc-z:.52;perspective:1100px}
  .cta.is-scene .cta__body{perspective:1100px}
}

/* § 15's law, restated for the scene. cta-scene.js declines to wire under the
   query at all, so nothing below should ever match — it is the belt to that
   brace, and it wins over the inline properties the script would have written
   because an !important author rule outranks an inline declaration. */
@media (prefers-reduced-motion:reduce){
  .cta-stage.is-staged{height:auto!important;margin:0!important;background:none!important}
  .cta.is-scene{position:static!important;height:auto!important;
    min-height:92svh!important;perspective:none!important}
  .cta.is-scene .cta__body{padding-block:var(--s-8)!important;perspective:none!important}
  .cta.is-scene .cta__bg::after{background:rgba(16,16,17,.5)!important}
  .cta.is-scene .cta__bg,.cta.is-scene .cta__pane,
  .cta.is-scene .cta__pane>*{translate:none!important;scale:none!important;opacity:1!important}
  .is-cta-lead,.is-cta-cover{box-shadow:none!important}
}

/* --- 18. Hero display sizing guard --------------------------------------- */
.hero .display,.cta .display{word-break:normal;hyphens:none}

/* ==========================================================================
   19. PORTFOLIO — the endless cut stream
   The page has no hero. The centre of the page is the focal point: the stream
   runs behind and around a fixed centre panel that states the question.

   THE IDEAL LOOK WALLET WAS HERE, and is gone (2026-09-12): a tile is a
   photograph you look at, not a control you collect from. See
   07-operations/decisions/2026-09-12-remove-ideal-look.md.
   ========================================================================== */
.pf{position:relative;background:var(--paper)}

/* the stream: continuous columns of cuts, each column drifting at its own rate */
.pf__stream{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s-3);
  padding:var(--s-6) var(--gutter);align-items:start}
.pf__col{display:grid;gap:var(--s-3)}
@media (max-width:1100px){.pf__stream{grid-template-columns:repeat(3,1fr)}.pf__col:nth-child(4){display:none}}
@media (max-width:720px){.pf__stream{grid-template-columns:repeat(2,1fr)}.pf__col:nth-child(3){display:none}}

/* a single cut.

   THE GROUND IS ON THE PICTURE. Its only job is to hold the tile's colour
   while the file is still loading, so it is painted on the <img> rather than
   on the box — which is where it landed when the frames were machined glass
   and the picture wore a rounded cut. The frame is § 23c's keyline now and the
   tile is square again, so either would work; it stays on the picture because
   that is the box whose size the placeholder should match.

   IT IS A <figure>, not a <button> (2026-09-12). The page is a portfolio you
   roam, not a thing you collect from, so margin:0 replaces the button reset
   that used to be here and nothing about the tile responds to a pointer. */
.cut{position:relative;overflow:hidden;background:transparent;
  margin:0;display:block;width:100%;
  -webkit-user-select:none;user-select:none}
.cut img{width:100%;height:auto;aspect-ratio:3/4;object-fit:cover;display:block;
  background:var(--paper-2);
  transition:filter var(--d-fast) ease}
/* NOTHING MOVES ON HOVER, and nothing happens on a press. A contact sheet is a
   flat surface; a print that swells under the pointer is a card, not a print.
   A cut has no states at all now that the selector is gone. */

/* the centre panel — the focal point of the page */
.pf__centre{position:relative;z-index:2;grid-column:1/-1;
  display:grid;place-items:center;text-align:center;
  padding:var(--s-8) var(--pad-type);background:var(--paper);
  border-block:1px solid var(--rule)}
.pf__centre .cta__body{padding:0}
/* § 4 gives .display --ink-1, and the stream is a light chapter, so the ink
   that used to be stated here is the ink it already has. */
.pf__centre .body{margin-inline:auto;text-align:center}



/* ==========================================================================
   20. PORTFOLIO CANVAS — omnidirectional infinite field
   Tiles wrap on both axes, so panning never reaches an edge. The question
   block sits dead centre on first paint and recedes once you start moving.
   Without JS the tiles fall back to a normal flow grid (see .pf__stream).
   ========================================================================== */
.canvas-page,.canvas-page body{height:100%}
.canvas-page{overflow:hidden}
.canvas{position:fixed;inset:0;overflow:hidden;background:transparent;
  cursor:grab;touch-action:none;z-index:1}
.canvas-page body{background:var(--paper)}
.canvas:active{cursor:grabbing}
.canvas__field{position:absolute;inset:0;will-change:transform}

.canvas .cut{position:absolute;top:0;left:0;will-change:transform;cursor:pointer}
.canvas-page .grid-rules{will-change:transform}

.canvas .cut img{aspect-ratio:3/4}

/* The centre question is gone — the Ideal Look pane (§ 19) is the focal
   point now, and the page's heading survives for the document outline
   only. Everything the plate carried, including its roam glass, moved to
   the pane; nothing here replaces it. */
.canvas__h1{position:absolute;left:-9999px;top:0;width:1px;height:1px;overflow:hidden}

/* roam hint */
.canvas__hint{position:fixed;left:50%;bottom:var(--s-5);transform:translateX(-50%);
  z-index:3;font-size:var(--t-micro);color:var(--ink-soft);
  display:flex;align-items:center;gap:var(--s-2);
  transition:opacity var(--d-fast) ease}
.canvas.is-roaming .canvas__hint{opacity:0}

@media (prefers-reduced-motion:reduce){
  .canvas{position:static;overflow:visible;cursor:auto}
  .canvas__field{position:static}
  .canvas .cut{position:static;width:auto}
  .canvas-page{overflow:auto}
}



/* ==========================================================================
   22. PROCESS — the booking, told as three chapters
   Implements 05-design/_outputs/process-section-spec.md; re-composed on
   2026-09-05 as a storyboard and on 2026-09-11 as a TRIPTYCH
   (07-operations/changelog/2026-09-11-booking-journey-triptych.md).
   WHAT → WHEN → READY, and every chapter is the same three cells on the
   page's six columns:
     RAIL        columns 1–2   where am I — the three steps, one lit
     ACTION      columns 3–5   the decision I am making right now
     ATMOSPHERE  column 6      the room, the chair, the barber
   Nine discrete states live in data attributes — `data-ch` on .ps names
   the active chapter, `data-s` (0–3) on each chapter names its pose — and
   process.js only ever decides WHICH state is current; every transition
   below runs on the site's clocks (M7), nothing is scrubbed by scroll.
   Markup ships fully resolved (flow layout, data-s="3"), so no-JS, reduced
   motion and shot mode all read three finished chapters.
   THE DESKTOP STORYBOARD IS NO LONGER PINNED (2026-09-15). It looks exactly
   as it did — .is-wired's stage, grid, frame and scenes are untouched — but
   it costs the reader one screen of scroll instead of a 3700px track, and the
   nine beats are stepped by the rail, the Next control and the scene rather
   than by the wheel. THE HANDSET DECK (.is-deck, 22a) IS UNCHANGED and still
   pinned; .ps:not(.is-wired) is the short-viewport and no-JS page, as before.
   ONE ACTIVE STEP, ALWAYS: .is-on is on exactly one .ps__ch, and the rail
   marker, the heading, the copy, the readout and the scene all key off it.
   GOLD CARRIES ONE MEANING HERE: the thing you selected — the step you are
   on in the rail, the row you pressed, the slot you hold. Never decoration.
   THE HANDOVER IS VERTICAL. A scene leaves upward and the next arrives from
   below; the atmosphere is wiped, not cut, by a mask that travels the same
   way — so 01 → 02 → 03 reads as one page changing its mind, not three.
   ========================================================================== */
.ps{position:relative}
.ps__stage{position:relative}
.ps__frame{display:none}

/* --- a chapter, in flow: number + heading → the scene → the copy ---------
   This is the handset sequence and the reduced-motion / no-JS page. The
   desktop storyboard re-seats the same three elements (below). */
.ps__ch{position:relative;display:grid;grid-template-columns:1fr;gap:var(--s-4);
  padding-block:var(--s-5) var(--s-6);border-top:1px solid var(--rule)}
.ps__ch>*{min-width:0}
.ps__ch--1{margin-top:var(--s-6)}

/* THE RAIL MARKER. A 2px bar ON the outer gridline (the stage is .to-grid, so
   its left edge is column rule 1): gold on the step you are on, ink on the
   ones behind you, nothing ahead. It is the section's progress readout, in
   the § 14b sense — a fill on a line the page has already drawn. */
.ps__txt{position:relative;padding-inline:var(--inset)}
.ps__txt::before{content:"";position:absolute;left:0;top:-1px;bottom:0;width:2px;
  background:var(--gold);transform:scaleY(0);transform-origin:top;
  transition:transform var(--d-move) var(--ease-out),background var(--d-fast) ease}
.ps__ch.is-on .ps__txt::before{transform:scaleY(1)}
.ps__ch.is-past .ps__txt::before{transform:scaleY(1);background:var(--ink)}

/* the step is a control: the whole block is the button */
.ps__head{margin:0;font-size:inherit}
.ps__go{display:block;width:100%;margin:0;padding:0;border:0;background:none;
  font:inherit;color:inherit;text-align:left;cursor:pointer;-webkit-appearance:none}
.ps__go:focus-visible{outline:2px solid var(--gold);outline-offset:6px}
/* the number is the site's index (§ 4a .meta__ix) — micro, tabular, --ink-3 —
   with the section's own model beside it (01 · WHAT), and the active one
   carries § 4a's ruled-label device: a hairline runs off it toward the
   canvas and fades, the step pointing at its own scene. */
.ps__n{display:flex;align-items:center;font-size:var(--t-micro);line-height:1.4;
  color:var(--ink-3);font-variant-numeric:tabular-nums;margin-bottom:var(--s-3);
  transition:color var(--d-fast) ease}
.ps__n-k{margin-left:.7em;text-transform:uppercase;letter-spacing:var(--track-label);
  font-size:var(--t-micro);color:var(--ink-3);transition:color var(--d-fast) ease}
.ps__n-k::before{content:"·";margin-right:.7em}
.ps__n i{flex:1 1 auto;height:1px;margin-left:.9em;background:var(--rule-fade);
  transform:scaleX(0);transform-origin:left;
  transition:transform var(--d-reveal) var(--ease-out)}
.ps__ch.is-on .ps__n{color:var(--ink-1)}
.ps__ch.is-on .ps__n-k{color:var(--gold)}
.ps__ch.is-on .ps__n i{transform:scaleX(1)}
/* the heading SPEAKS, so it is the display voice (§ 4), a step above the
   title size — the rail is the section's masthead. Inactive steps stand
   back in contrast and a touch in scale; never in size. */
.ps__h{display:block;font-family:var(--font-display);font-weight:var(--wght-display);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display);
  text-transform:uppercase;letter-spacing:var(--track-display);
  line-height:var(--lh-display);font-size:clamp(1.75rem,2.7vw,2.625rem);
  color:var(--ink-1);text-wrap:balance;
  opacity:.34;transform:scale(.96);transform-origin:left center;
  transition:opacity var(--d-fast) ease,transform var(--d-move) var(--ease-out)}
.ps__go:hover .ps__h{opacity:.7}
.ps__ch.is-on .ps__h,.ps__ch.is-on .ps__go:hover .ps__h{opacity:1;transform:none}

.ps__body{padding-inline:var(--inset);display:grid;grid-template-rows:1fr}
/* THE CLOSE — the Book button and its one micro line, kept as a single
   element so chapter 03 can hand the pinned grid one item for its last row.
   It stands on the rail's own inset in both layouts, like the headings and
   the copy above it. */
.ps__close{padding-inline:var(--inset)}
/* the caveat's step off the button, moved out of an inline style so § 22c can
   re-scale it with the rest of the handset composition */
.ps__close-note{margin-top:var(--s-3)}
/* the close's three handset-only marks (§ 22c). Off by default so the desktop
   rail, which closes under step 03, is untouched by them. */
.ps__close-tick,.ps__close-eb,.ps__close-rule{display:none}
.ps__body p{min-height:0;overflow:hidden;color:var(--ink-2);max-width:40ch}

/* --- the scene's frame: ACTION beside ATMOSPHERE ---------------------------
   One hairline box on the lines; inside it, the scene on three columns and
   the picture on the fourth, split on column rule 6 by construction. The
   frame is an OUTLINE, not a border: it paints on the box's own first pixel,
   so the type inside stands one inset off the column rule and not one inset
   and a border (§ 5a, and dev/probe-grid-inset.mjs). */
.ps__fig{position:relative;background:var(--paper);
  outline:1px solid var(--rule);outline-offset:-1px;
  display:grid;grid-template-columns:3fr 1fr}
.ps__fig>*{min-width:0}
.ps__scene{position:relative;min-width:0;min-height:0;display:grid;grid-template-rows:minmax(0,1fr)}

/* THE META ROW — a scene opens on this line: a key on the left, a value on
   the right, both in the note voice, on one hairline. It is the legend of a
   diagram, never prose — the plate dropped its row on 2026-09-12, leaving it
   to the ledger and the contact sheet, which are unreadable without one. */
.ps__meta{display:flex;justify-content:space-between;align-items:center;gap:var(--s-3);
  min-height:var(--s-6);padding:0 var(--inset);border-bottom:1px solid var(--rule)}
.ps__meta>*{min-width:0}
.ps__meta-r{text-align:right;color:var(--ink-3)}
.ps__meta b{font-weight:400;color:var(--ink-1)}

/* THE ATMOSPHERE. A photograph in the fourth cell, and nothing else — no
   label, no caption, no name. Same object in every chapter, so the only
   thing that changes between steps is what it shows, and the left column is
   the section's only voice (2026-09-12). */
.ps__atm{position:relative;margin:0;overflow:hidden;min-height:0;
  border-left:1px solid var(--rule);background:var(--ink)}
.ps__atm-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:var(--photo-grade);opacity:0;transition:opacity var(--d-reveal) ease}
.ps__atm-img.is-on{opacity:1}
.ps__atm::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(0deg,rgba(16,16,17,.62) 0%,rgba(16,16,17,0) 42%),var(--photo-veil)}

/* --- scene 01: the plate and the rows — choose one ------------------------
   The plate restates the pressed row at poster scale; the rows below it are
   the menu. Choosing is one tween: the plate fills to ink (the § 1a dark
   chapter), the pressed row takes the gold mark, the others quiet. */
.ps__scene--1{grid-template-rows:minmax(0,1fr) auto auto}
.ps-plate{position:relative;display:grid;grid-template-rows:minmax(0,1fr) auto;
  min-height:0;overflow:hidden;color:var(--ink-1);background:var(--paper);
  transition:background var(--d-move) var(--ease-out)}
.ps-plate.is-on{background:var(--ink)}
.ps-plate__body{display:grid;align-content:center;gap:var(--s-3);
  padding:var(--s-3) var(--inset);min-height:0}
.ps-plate__name{display:block;font-family:var(--font-display);font-weight:var(--wght-display);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display);
  text-transform:uppercase;letter-spacing:var(--track-display);
  line-height:var(--lh-display);color:var(--ink-1);text-wrap:balance;
  font-size:clamp(2.5rem,min(4.8vw,9.5svh),4.75rem)}
.ps-plate__foot{display:flex;align-items:end;padding:0 var(--inset) var(--inset)}
.ps-plate__sum{font-family:var(--font-display);font-weight:var(--wght-display);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display);
  letter-spacing:var(--track-display);line-height:var(--lh-display);color:var(--ink-1);
  font-size:clamp(2rem,3.2vw,3rem)}
.ps-plate__empty{position:absolute;left:var(--inset);bottom:var(--inset);
  transition:opacity var(--d-fast) ease}
/* browse: an empty plate. select: the content lands, the empty note goes. */
.ps-plate__body,.ps-plate__foot{opacity:0;transform:translateY(10px);
  transition:opacity var(--d-fast) ease,transform var(--d-move) var(--ease-out)}
.ps-plate.is-on .ps-plate__body,.ps-plate.is-on .ps-plate__foot{opacity:1;transform:none;
  transition-delay:120ms,120ms}
.ps-plate.is-on .ps-plate__empty{opacity:0}
/* a second choice: the words swap under one short fade (process.js § select) */
.ps-plate.is-swap .ps-plate__body,.ps-plate.is-swap .ps-plate__foot{opacity:0;transform:translateY(6px);
  transition-delay:0s,0s}

.ps-deck{display:flex;flex-direction:column;border-top:1px solid var(--rule)}
/* a row is a control: mark · name · price, on a hairline */
.ps-card{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;
  gap:var(--s-3);height:56px;padding:0 var(--inset);margin:0;text-align:left;
  background:transparent;color:var(--ink-1);border:0;border-top:1px solid var(--rule);
  border-radius:0;cursor:pointer;font:inherit;-webkit-appearance:none;
  transition:background var(--d-fast) ease,color var(--d-fast) ease,
    opacity var(--d-fast) ease,transform var(--d-move) var(--ease-out)}
.ps-card:first-child{border-top:0}
.ps-card>*{min-width:0}
.ps-card:focus-visible{outline:2px solid var(--gold);outline-offset:-4px}
.ps-card__mark{display:block;width:8px;height:8px;border:1px solid var(--ink-3);
  transition:background var(--d-fast) ease,border-color var(--d-fast) ease}
/* the name is the LABEL voice (§ 4) — the same face, width and tracking as
   the BOOK plate and the wordmark */
.ps-card__name{font-size:var(--t-micro);font-weight:var(--wght-label);
  text-transform:uppercase;letter-spacing:var(--track-label);white-space:nowrap;
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label)}
.ps-card__price{font-size:var(--t-body);color:var(--ink-2);min-width:2.6em;text-align:right}
.ps-card:not(.is-sel):hover{background:var(--paper-2)}
.ps-card:not(.is-sel):hover .ps-card__mark{border-color:var(--ink-1)}
/* the pressed row: gold mark, full ink */
.ps-card.is-sel .ps-card__mark{background:var(--gold);border-color:var(--gold)}
.ps-card.is-sel .ps-card__price{color:var(--ink-1)}
/* browse → select → resolve: the unchosen recede in two steps */
.ps__ch--1[data-s="2"] .ps-card:not(.is-sel){color:var(--ink-2)}
.ps__ch--1[data-s="3"] .ps-card:not(.is-sel){color:var(--ink-2);opacity:.55}
/* browse: the rows reveal upward, one by one (§ 14's stagger) */
.ps__ch--1[data-s="0"] .ps-card{opacity:0;transform:translateY(12px)}
.ps__ch--1 .ps-card:nth-child(2){transition-delay:0s,0s,0s,60ms}
.ps__ch--1 .ps-card:nth-child(3){transition-delay:0s,0s,0s,120ms}
.ps__ch--1 .ps-card:nth-child(4){transition-delay:0s,0s,0s,180ms}

/* the foot of the scene: the menu link and the inspiration aside, split on
   column rule 4. The aside is one serif line (eyebrow grammar, M5), never a
   stage: no number, no box. */
.ps__foot{position:relative;display:grid;grid-template-columns:1fr 2fr;
  border-top:1px solid var(--rule)}
.ps__foot>*{min-width:0}
.ps__foot::before{content:"";position:absolute;left:33.333%;top:0;bottom:0;width:1px;
  background:var(--rule)}
/* MARGINS, NOT PADDING: the inset has to move the type's own box off the
   line, or the block still begins on it (§ 5a, probe-grid-inset) */
.ps__all{margin:var(--s-3) var(--inset);align-self:center}
.ps__all a{border-bottom:1px solid var(--rule)}
.ps__all a:hover{color:var(--ink);border-color:var(--ink)}
.ps__aside{position:relative;margin:var(--s-3) var(--inset);display:flex;align-items:center;
  justify-content:flex-end;text-align:right}
.ps__aside a{font-family:var(--font-serif);font-style:italic;
  font-size:var(--t-body);line-height:1.4;color:var(--ink-2);
  border-bottom:1px solid var(--rule)}
.ps__aside a:hover{color:var(--ink);border-color:var(--ink)}

/* --- scene 02: the ledger — grammar, not data -----------------------------
   Eight columns, six rows, hairline seams (the 1px gap over a --rule ground
   IS the seam). open = a small ink square, taken = a dash, YOUR slot = the
   gold block — ONE CELL, always, whatever the service. It used to grow with
   the service and label itself "×2"; the number is gone and so is the
   arithmetic, because the block's job is to say THIS ONE and it says that by
   arriving and locking. No dates, no day names, no clock times, ever (law 5). */
/* THE LEDGER TOOK THE WHOLE FRAME, 2026-09-14. The "Availability" key row
   and the Open / Taken / One-client-at-a-time foot were both removed: the
   diagram already reads as open and taken slots, and the claim is said in
   the left column's body line. The grid is now one track, so the cells
   take the height those two rows held. */
.ps__matrix{position:relative;display:grid;grid-template-rows:minmax(0,1fr);min-height:0}
.ps__cells{display:grid;grid-template-columns:repeat(8,1fr);grid-auto-rows:1fr;
  gap:1px;background:var(--rule);min-height:0}
.ps__cells i{display:block;position:relative;background:var(--paper);min-height:0;aspect-ratio:1}
.ps__cells i::before{content:"";position:absolute;left:calc(50% - 7px);width:14px;top:50%;height:1px;
  background:var(--rule);transition:background var(--d-fast) ease,opacity var(--d-fast) ease}
.ps__cells i.on::before{left:calc(50% - 5px);top:calc(50% - 5px);width:10px;height:10px;background:var(--rule)}
/* illumination sweeps left→right, column by column — 8 delays, not 48 */
.ps__cells i:nth-child(8n+1)::before{transition-delay:0ms}
.ps__cells i:nth-child(8n+2)::before{transition-delay:50ms}
.ps__cells i:nth-child(8n+3)::before{transition-delay:100ms}
.ps__cells i:nth-child(8n+4)::before{transition-delay:150ms}
.ps__cells i:nth-child(8n+5)::before{transition-delay:200ms}
.ps__cells i:nth-child(8n+6)::before{transition-delay:250ms}
.ps__cells i:nth-child(8n+7)::before{transition-delay:300ms}
.ps__cells i:nth-child(8n+8)::before{transition-delay:350ms}
.ps__ch--2:not([data-s="0"]) .ps__cells i.on::before{background:var(--ink)}
.ps__ch--2[data-s="2"] .ps__cells i.pick::before,
.ps__ch--2[data-s="3"] .ps__cells i.pick::before{background:var(--gold)}
.ps__ch--2[data-s="2"] .ps__cells i:not(.pick)::before{opacity:.6}
.ps__ch--2[data-s="3"] .ps__cells i:not(.pick)::before{opacity:.3}
/* the legend: the two marks, drawn the way the cells draw them */
/* THE SLOT ARRIVES, AND THAT IS THE MESSAGE. It sits two columns over and
   one segment down until the select beat, then travels into position on the
   transform clock; the tag lands and a hairline frame locks around it on the
   resolve beat. With no readout to carry, this motion is the whole statement.
   process.js seats it on the pick cell; the inline % fallback covers no-JS. */
.ps__slot{position:absolute;z-index:2;background:var(--gold);
  opacity:0;transform:translate(-200%,100%);
  transition:opacity var(--d-fast) ease,transform var(--d-move) var(--ease-out),
    left var(--d-move) var(--ease-out),top var(--d-move) var(--ease-out),
    height var(--d-move) var(--ease-out)}
.ps__ch--2[data-s="2"] .ps__slot,.ps__ch--2[data-s="3"] .ps__slot{opacity:1;transform:none}
.ps__slot-tag{position:absolute;left:-1px;top:0;transform:translateY(-100%);
  background:var(--ink);color:var(--paper);padding:3px var(--s-2);white-space:nowrap;
  font-size:var(--t-micro);font-weight:var(--wght-label);text-transform:uppercase;
  letter-spacing:var(--track-label);
  font-variation-settings:"wdth" var(--wdth-label),"wght" var(--wght-label);
  opacity:0;transition:opacity var(--d-fast) ease}
/* locked in: a hairline frame settles around the block, the rest stands back */
.ps__lock{position:absolute;z-index:1;pointer-events:none;
  outline:1px solid var(--ink);outline-offset:6px;
  opacity:0;transform:scale(1.15);
  transition:opacity var(--d-move) ease,transform var(--d-move) var(--ease-out)}
.ps__ch--2[data-s="3"] .ps__lock{opacity:1;transform:none}
.ps__ch--2[data-s="3"] .ps__slot-tag{opacity:1;transition-delay:200ms}

/* --- scene 03: the arrival plate — where you turn up ----------------------
   This was a contact sheet of "your references": four columns by three rows of
   photographs, with a count set into the last cell like a plate number. Every
   claim on it was false once the Ideal Look was removed — the pictures are
   Leandro's, the visitor has no references, and nothing had been sent — and
   the chapter was the wrong beat besides. 01 and 02 are the booking; 03 is now
   its end, and says the one thing a by-appointment studio at a residential
   address has to say (decisions/2026-09-12-remove-ideal-look.md).

   THE LAW OF § 22 STILL HOLDS: grammar, not data. No booking reference, no
   date, no clock time — nothing here can contradict Fresha, exactly as the
   ledger above cannot. The street number keeps the display numeral the count
   used to wear, so the chapter keeps its weight in the rail.

   The beats are unchanged: the plate lifts in, the frame draws at 03, the gold
   pin marks the corner, the tag lands. Only what is framed has changed. */
/* THE PLATE TOOK THE WHOLE FRAME, 2026-09-14. The "Where / By appointment"
   key row above the card was removed — the card is an address on a map and
   says WHERE by being one, and "by appointment" is already said in the
   section foot and the facts band. One track, so the card takes that row. */
.ps__board{position:relative;display:grid;grid-template-rows:minmax(0,1fr);min-height:0}
.ps__card{position:relative;display:grid;align-content:center;min-height:0;
  gap:var(--s-1);margin:var(--inset);padding:var(--s-5) var(--s-4);
  outline:1px solid transparent;outline-offset:var(--s-2);
  transition:outline-color var(--d-move) ease}
.ps__card>*{min-width:0}
/* THE ADDRESS IS A POSTER, not a caption. The cell this sits in was built to
   hold six photographs, so type at label scale leaves it two-thirds empty —
   the plate has to be SET, at the scale the rail beside it is set. Number,
   street and town are one descending lockup in the display face, which is the
   grammar every other poster line on this site already uses. */
.ps__card-no,.ps__card-st{display:block;font-family:var(--font-display);
  font-weight:var(--wght-display);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display);
  letter-spacing:var(--track-display);line-height:.92;text-transform:uppercase;
  color:var(--ink-1)}
.ps__card-no{font-size:clamp(4rem,10vw,9.5rem)}
.ps__card-st{font-size:clamp(1.75rem,4.4vw,4rem)}
.ps__card-tn{display:block;margin-top:var(--s-2);
  font-size:clamp(1rem,1.5vw,var(--t-title));line-height:1.2;color:var(--ink-2)}
/* THE CLAIM LEFT THE CARD, 2026-09-14. "One chair. One client at a time." is
   the studio's one-line promise and it recurs all over this page by design —
   the About band's key, the shout's own headline, § 4c's find-us sheet. What
   made this instance different is that the SAME PINNED SEQUENCE had already
   said it twice before the reader reached the card: chapter 02's meta reads
   "One client at a time" and its body reads "One client in the room at a time.
   The hour is yours." The card then said it a third time, two beats later, as
   the foot of an address. Removed as a restatement, not as a retreat from the
   claim. The line's own hairline is not deleted with it — the rule belongs to
   the foot of the lockup, not to the sentence that happened to stand on it, so
   it moves down to .ps__card-go, which is the foot line now. */

/* --- the ground under the plate: the aerial ------------------------------
   The frame held nothing but paper until 2026-09-14 — the address was set at
   poster scale and the two thirds of the plate around it were empty. It
   carries the aerial of the address now, and the plate is an <a>: the last
   thing this booking asks of a stranger is to find a house on a residential
   street, and a roof with a pin on it is a better instruction than a number.

   A DIFFERENT INSTRUMENT FROM § 4b's. #area is a ROADMAP at z=16 — what part
   of the coast is this. This is a SATELLITE at z=19 — which door. Two views
   of one place, neither doing the other's job.

   THE TREATMENT IS INK AND GOLD, 2026-09-14. § 4b's map is a full-bleed
   environment and takes the night grade; this one is a CARD — one held object
   about 650px across, read at arm's length inside a pinned chapter — and the
   duotone is the grade that survives being small. --map-mono takes the
   cartography to grey on the iframe and --map-duo lays one ink→gold ramp over
   it in mix-blend-mode:color, which supplies hue and saturation and leaves
   every road width and label weight at its own value. The map is COLOURED, not
   covered; a flat tint at the same alpha would flatten the arterials into the
   landmass, which is the one thing a map somebody came here to read cannot
   afford. And the ramp has somewhere to go even here: the plate is nearly
   square, so ink at its head and gold at its foot is a full travel rather than
   a hint. Nothing is cropped out of it and no piece of Google's furniture is
   covered.

   THE INK CHAPTER IS ANSWERED BY THE SHEET, NOT BY THE MAP. This plate sits in
   a pinned ink sequence, and the first pass inverted the map on exactly that
   argument. But the thing that makes this a dark chapter is .ps__sheet — an
   opaque ink plate on the page's own hairline, standing over the map — and a
   lit ground is what a plate needs in order to BE a plate.

   THE IFRAME IS INERT. pointer-events stay off it for the same reason § 4b
   gives — an embed under a thumb eats the page's scroll — and here the whole
   plate is a link, so the map must never take the press. */
.ps__map{position:absolute;inset:0;z-index:0;overflow:hidden;
  /* THE FRAME'S OWN GROUND MATCHES WHAT THE MAP RESOLVES TO. The embed is
     seated late and is skipped entirely in shot mode, so this colour is what
     the plate reads as whenever the map is not there. Under the duotone the
     map resolves to the ramp's warm off-white, so that is what stands in for
     it — sampled off the graded embed, not --paper-2, which is a shade cooler
     and showed as a seam against the map when the map arrived. */
  background:#EBE5D8}
/* TWO GRADES, ONE HAND — see § 1. This map and § 4c's no longer share a tone,
   and the reasoning for the split is there; what matters here is that both
   start at grayscale(1), so the pair is one monochrome cartography in two keys
   rather than two palettes. Nothing is washed out of this one and nothing is
   laid across it: the map prints at full strength, Google's attribution along
   its foot with it, and the type is kept off it by the sheet instead. */
.ps__map-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;border:0;
  pointer-events:none;filter:var(--map-mono)}
/* THE RAMP (§ 1's --map-duo) — the second half of the duotone. --map-mono has
   already taken the embed to grey on the iframe; this puts the page's ink and
   gold back over it in mix-blend-mode:color, which takes hue and saturation
   from here and lightness from the map, so the cartography keeps every value
   the cartographer drew and only changes what colour it is drawn in. Ink at
   the head of the card, gold at its foot.

   IT BLENDS AGAINST THE MAP AND NOTHING ELSE. .ps__map is positioned with
   z-index 0, so it is a stacking context and the blend is sealed inside it: it
   reaches the iframe under it and can never reach the plate or the pinned
   chapter behind it. That containment is why the sheet over it stays a flat
   opaque ink plate rather than picking up the ramp. */
.ps__map::after{content:"";position:absolute;inset:0;z-index:1;
  pointer-events:none;background:var(--map-duo);mix-blend-mode:color}

/* every line of the lockup stands in front of the ground */
.ps__card>:not(.ps__map){position:relative;z-index:1}

/* THE SHEET. A map at full strength cannot carry black display type, and a
   map faded until it can is no longer the instrument § 4b prints. So the
   address is not ON the map — it is on a sheet of the page's own paper laid
   over it, hard-edged and square, the way every other plate on this site
   meets a picture (§ 12a's hero plate, § 4b's .map__catch). It takes the left
   of the frame: that is where the rail's type already is, and it leaves the
   right of the map — where `ll` stands the marker — to be read. */
/* INK, NOT PAPER, as of 2026-09-14. The sheet used to be the page's own paper
   laid on the map — correct, and quiet to the point of disappearing into a
   cartography that is itself pale cream and off-white. Turned over, it is the
   one hard object in the frame and the address reads as a plate rather than as
   a lighter patch of map. The flip is this one declaration plus § 1a's four
   tokens; the keyline below is --rule, which § 1a has already made
   --rule-invert by the time it resolves here. */
.ps__sheet{display:grid;gap:var(--s-1);justify-self:start;align-self:center;
  max-width:min(62%,44ch);background:var(--ink);
  box-shadow:inset 0 0 0 1px var(--rule);
  padding:var(--s-4) var(--s-4) var(--s-3);
  transition:box-shadow var(--d-fast) ease}
/* THE INVITATION is the sheet's last line — § 4C metadata standing under the
   note above it, not a second plate floating in a corner. */
.ps__card-go{display:block;margin-top:var(--s-3);padding-top:var(--s-2);
  border-top:1px solid var(--rule);color:var(--ink-2);
  transition:color var(--d-fast) ease}
/* THE STREET FITS THE SHEET. At the plate's own scale "SEAVIEW ROAD" is 4rem
   and breaks over two lines inside a sheet this wide, which splits the one
   lockup the chapter has. The numeral keeps its full size — it is the poster
   — and the street is set to the width it has. */
.ps__sheet .ps__card-st{font-size:clamp(1.5rem,2.5vw,2.4rem)}
.ps__card-go i{font-style:normal}
/* IN: the plate rises and settles, chapter 01's grammar echoed the way the
   scattered photographs used to echo it */
.ps__ch--3[data-s="0"] .ps__card,
.ps__ch--3[data-s="1"] .ps__card{opacity:.85;transform:translateY(10px)}
.ps__card{transition:outline-color var(--d-move) ease,opacity var(--d-fast) ease,
  transform var(--d-move) var(--ease-out)}
.ps__ch--3[data-s="3"] .ps__card{outline-color:var(--ink)}
.ps__card::after{content:"";position:absolute;top:calc(-1 * var(--s-2) - 4px);
  left:calc(-1 * var(--s-2) - 4px);width:7px;height:7px;
  background:var(--gold);opacity:0;transition:opacity var(--d-fast) ease}
.ps__ch--3[data-s="3"] .ps__card::after{opacity:1;transition-delay:200ms}

/* --- the plate is a link ------------------------------------------------
   Set like the plate it already was: no underline, no borrowed link colour.
   It announces itself the way the rest of the site does — the keyline it has
   worn since the frame drew goes gold, the ground comes up, and the
   invitation's hairline closes to ink. Focus is the same move, so a keyboard
   sees exactly what a cursor sees. */
.ps__card{color:inherit;text-decoration:none}
.ps__ch--3[data-s="3"] .ps__card:hover,
.ps__card:focus-visible{outline-color:var(--gold)}
/* the keyline comes up to full strength on hover. --ink-1, not the literal
   --ink it used to be: on an ink ground that line is the ground. */
.ps__card:hover .ps__sheet,
.ps__card:focus-visible .ps__sheet{box-shadow:inset 0 0 0 1px var(--ink-1)}
.ps__card:hover .ps__card-go,
.ps__card:focus-visible .ps__card-go{color:var(--ink-1)}

/* --- the CTA row — one primary action, per the hierarchy law -------------- */
.ps__cta{margin-top:var(--s-6);display:flex;gap:var(--s-3);flex-wrap:wrap}

/* --- handset: the deliberate sequence, 01 → scene → copy ------------------ */
@media (max-width:640px){
  .ps__ch{padding-block:var(--s-5)}
  .ps__fig{grid-template-columns:1fr}
  .ps__atm{aspect-ratio:3/2;border-left:0;border-top:1px solid var(--rule)}
  .ps-plate{min-height:280px}
  .ps-card{height:52px}
  .ps__meta-r{max-width:55%}
  .ps__foot{grid-template-columns:1fr}
  .ps__foot::before{display:none}
  .ps__aside{border-top:1px solid var(--rule);padding-top:var(--s-3);justify-content:flex-start;text-align:left}
  /* the tab must fit the narrow board: the connective's size (§ 4a .lead) */
  .ps__slot-tag{font-size:var(--t-lead);padding:3px var(--s-1)}
  /* THE SHEET TAKES THE FOOT, NOT THE LEFT. There is no left half on a plate
     this narrow — a sheet at 62% leaves the map a strip — so it spans the
     width and sits on the floor of the frame, with the map read above it,
     which is the order a thumb scrolls in anyway. The plate is given a floor
     of its own so that band is a band and not a seam: without it the sheet is
     the whole height of the card and the map is four hairlines around it. */
  .ps__sheet{max-width:none;justify-self:stretch;align-self:end}
}
/* IN FLOW — the handset, a tablet, or a desktop reading with reduced motion
   or no JS — the frame has no pinned height to hand down, so the scenes
   size themselves: the plate keeps a floor, the cells keep an aspect, and
   the sheet becomes three columns of 3:4 frames (the feature spans two by
   two, which is the same ratio) with the count as a strip beneath. */
.ps:not(.is-wired) .ps__cells{aspect-ratio:8/6}
/* the plate is type on a hairline and needs no reflow without JS: it simply
   stops being vertically centred in a box that no longer has a fixed height */
.ps:not(.is-wired) .ps__card{align-content:start}
@media (min-width:641px){
  .ps:not(.is-wired) .ps-plate{min-height:320px}
}
/* — except that since 2026-09-14 the plate is a map with a sheet on it, and
   in flow it has no height but the sheet's: the map would be four hairlines
   around the type. So the narrow plate takes a floor of its own and the sheet
   drops to the foot of it, which leaves a real band of map above.
   Stated here rather than in the handset block above because that block is
   read BEFORE this section's align-content, and the two are the same weight. */
@media (max-width:640px){
  .ps:not(.is-wired) .ps__card{align-content:end;min-height:clamp(400px,62svh,520px)}
}

/* --- 22a. THE DECK — the storyboard, dealt (handset + small tablet, JS) ----
   Below 901px the seven-row pinned grid has nowhere to go: the rail, the
   action and the atmosphere cannot stand side by side on a 390px screen, so
   the section used to unroll as three chapters in flow, ~3000px of scrolling
   with the story told three times over in miniature. It is a DECK now — the
   same chapters, as four wide cards stacked in a leaning pile inside a pinned
   stage. Scrolling LIFTS the front card up and tips it back off the top while
   the card behind rises and scales into focus: a deck that deals itself
   upward, one card per push, ending on the booking.

   THE TOTAL SCROLL IS UNCHANGED (~3000px on a 390x844 handset). What changes
   is that the reader is looking at ONE composition the whole way down instead
   of scrolling past three, which is the same argument the desktop storyboard
   makes — the section is one page changing its mind, not three screens.

   FOUR CARDS, THREE DEALS. 01 WHAT, 02 WHEN, 03 READY and the CLOSE. The
   track is divided into (cards - 1) segments, not `cards`, so the last card
   arrives centred at the end of the track and STAYS there — the deck resolves
   to the Book button rather than emptying itself onto a blank stage. The
   close is a direct child of the stage in this layout; process.js moves it
   there and puts it back on teardown, exactly as the desktop grid already
   treats it as a stage-level item (the chapters are display:contents there).
   AND IT IS A SMALLER PLATE. A chapter card is a composition — a heading, a
   scene, a room; the close is one button and the line that qualifies it, and
   at a chapter's height that is a button adrift in half a screen of paper.
   Cut to a third of the stage it is a plate, and it is small enough to sit
   hidden behind the pile until 03 deals away, so the way down shows three
   photographic card edges and the deck resolves onto this one thing.

   EVERY TRANSFORM IS WRITTEN BY process.js, not by this file: one normalised
   progress 0->1 read off the section's own rect drives translate + rotateX +
   scale on all four cards. CSS owns the box, the ground and the type; the
   nine beats inside a card still run on the site's own clocks (M7), never
   scrubbed. No JS, reduced motion and shot mode never get .is-deck and read
   the three finished chapters in flow, which is what they should see.

   THE CARD IS THE CHAPTER, UNCHANGED. Same three blocks in the same order —
   the step and its heading, the scene with the atmosphere under it, the one
   body line — fitted to a fixed box: the scene takes the slack and the
   photograph keeps a band of its own so the triptych's third cell survives
   the loss of its column. */
@media (max-width:900px) and (min-height:560px){
  /* THE TRACK. The pinned stage plus this much travel; three segments share
     it, so each card gets a deliberate push of the thumb rather than a flick.
     process.js reads the travel off this box (rect.height minus the stage),
     so nothing downstream restates the number. */
  .ps.is-deck{--ps-deal:clamp(1700px,270svh,2500px);
    height:calc(100svh - var(--header-h) + var(--ps-deal))}
  .ps.is-deck .ps__stage{position:sticky;top:var(--header-h);
    height:calc(100svh - var(--header-h));box-sizing:border-box;
    /* the deck is dealt in depth, so the stage is the camera; the cards leave
       under the header rather than over it, which is what clip buys */
    perspective:1100px;perspective-origin:50% 42%;
    overflow:hidden;overflow:clip}

  /* --- a card ------------------------------------------------------------
     Centred in the stage and moved only by process.js. transform-origin is
     the card's own FOOT: a card leaving tips back about the edge it is
     standing on, and a card waiting grows upward out of the one in front of
     it, which is how a real stack behaves. */
  .ps.is-deck .ps__ch,
  .ps.is-deck .ps__close{position:absolute;top:50%;left:50%;
    width:100%;height:min(78svh,660px);
    margin:0;padding:0;border-top:0;
    transform:translate(-50%,-50%);transform-origin:center bottom;
    will-change:transform;backface-visibility:hidden;
    background:var(--paper);outline:1px solid var(--rule);outline-offset:-1px;
    box-shadow:0 32px 64px -32px rgba(16,16,17,.45);
    overflow:hidden;overflow:clip}
  /* the pile order: 01 in front, the close at the back */
  .ps.is-deck .ps__ch--1{z-index:4}
  .ps.is-deck .ps__ch--2{z-index:3}
  .ps.is-deck .ps__ch--3{z-index:2}
  .ps.is-deck .ps__close{z-index:1}
  /* ONLY THE CARD IN FRONT IS TOUCHABLE. The rows, the map link and the Book
     button all exist in the stack at once; a thumb must never reach one that
     is 40% hidden behind the card it is actually reading. */
  .ps.is-deck .ps__ch,.ps.is-deck .ps__close{pointer-events:none}
  .ps.is-deck .is-front{pointer-events:auto}

  /* --- the card's interior: the chapter's own three blocks ----------------
     RE-ORDERED, AND THE ORDER IS LOAD-BEARING. In flow the body line closes a
     chapter; on a card it moves up under the heading and the frame takes the
     foot, so the block at the bottom of every card is the ATMOSPHERE. That is
     what the stack shows: about 30px of each waiting card stands below the one
     in front, and those slivers are now strips of the next chapters' rooms
     rather than the clipped second line of somebody else's sentence. The deck
     reads as a pile of photographs before it reads as anything else, and the
     copy still lands in the reading order a standfirst has anywhere else. */
  .ps.is-deck .ps__ch{display:grid;grid-template-columns:1fr;
    grid-template-rows:auto auto minmax(0,1fr);gap:0}
  .ps.is-deck .ps__txt{grid-row:1}
  .ps.is-deck .ps__body{grid-row:2}
  .ps.is-deck .ps__fig{grid-row:3}
  .ps.is-deck .ps__ch--1{margin-top:0}
  .ps.is-deck .ps__txt{padding:var(--s-4) var(--inset) var(--s-3)}
  /* the rail marker runs the full height of the card's left edge, on the
     card's own first pixel rather than one above it */
  .ps.is-deck .ps__txt::before{top:0}
  .ps.is-deck .ps__h{font-size:clamp(1.6rem,7.2vw,2.5rem)}
  .ps.is-deck .ps__body{padding:0 var(--inset) var(--s-4);align-content:center}
  .ps.is-deck .ps__body p{max-width:none}

  /* the frame is INSIDE the card now, so it gives up its own outline and
     keeps only the two seams that divide the card */
  .ps.is-deck .ps__fig{outline:0;background:transparent;
    grid-template-columns:1fr;grid-template-rows:minmax(0,1fr) auto;
    border-top:1px solid var(--rule)}
  /* THE ATMOSPHERE KEEPS A BAND. It has no fourth column to stand in at this
     width, so it takes a strip across the foot of the frame — the third cell
     of the triptych, turned on its side. A band, not a picture: it is the
     room the chapter happens in, and the scene above it is the subject. */
  /* DEEP ENOUGH TO BE THE WHOLE SLIVER, AND THEN SOME. Two floors set this.
     The hard one: each waiting card stands 5% of a card height lower than the
     one in front, so the third one back shows ~100px, and a band shallower
     than that would let a sliver cut into the scene above it — the stack
     would read as clipped chapters again. The soft one, and the reason it is
     21svh rather than the 15 it opened at (2026-09-14): at a letterbox depth
     the room is a texture, and the third cell of the triptych is a PICTURE.
     The scenes above it all size on `minmax(0,1fr)` or a centred aspect, so
     they give the height back without any of them restating a number. */
  .ps.is-deck .ps__atm{aspect-ratio:auto;height:clamp(140px,21svh,230px);
    border-left:0;border-top:1px solid var(--rule)}

  /* --- the scenes give up their flow floors ------------------------------
     Every "in flow, size yourself" rule above (the plate's min-height, the
     arrival card's, the cells' aspect) exists because a chapter in flow has
     no height handed to it. A card HAS one, so the scenes take it and the
     floors would only overflow the box. */
  .ps.is-deck .ps-plate{min-height:0}
  .ps.is-deck .ps-plate__name{font-size:clamp(1.9rem,8.5vw,3rem)}
  .ps.is-deck .ps-plate__sum{font-size:clamp(1.5rem,6vw,2.25rem)}
  .ps.is-deck .ps-card{height:50px}
  /* the foot goes back to one row: two columns is what it was built as, and
     the card cannot spend 90px restating a link and an aside on two lines */
  .ps.is-deck .ps__foot{grid-template-columns:1fr 1fr}
  .ps.is-deck .ps__foot::before{display:block;left:50%}
  .ps.is-deck .ps__all,.ps.is-deck .ps__aside{margin:var(--s-2) var(--inset)}
  .ps.is-deck .ps__aside{border-top:0;padding-top:0;
    justify-content:flex-end;text-align:right}
  .ps.is-deck .ps__aside a{font-size:var(--t-micro);line-height:1.3}
  /* the ledger keeps its own proportion and stands in the middle of the
     frame: stretched to a card's height its cells become tall rectangles and
     the marks inside them get lost, which is the one thing a diagram whose
     whole argument is "these are hours" cannot afford */
  .ps.is-deck .ps__cells{aspect-ratio:8/6;align-self:center;justify-self:center;
    /* sized by whichever of the two runs out first: on a tall card the width
       decides, on a short one (a small tablet turned sideways) the height
       does, and the diagram scales down instead of pushing the photograph
       out of the bottom of the card */
    height:100%;width:auto;max-width:100%;max-height:100%}
  .ps.is-deck .ps__card{min-height:0;align-content:end;margin:var(--s-3)}
  .ps.is-deck .ps__card-no{font-size:clamp(3rem,14vw,5rem)}
  /* the band is a letterbox, so the barber's own portrait has to be framed
     for it — centred on the subject, not on the middle of a standing figure */
  .ps.is-deck .ps__atm--who .ps__atm-img{object-position:50% 16%}


}

/* THE FOOT LEAVES THE PHONE'S CARD (2026-09-14). "All services →" and the
   italic "Need inspiration? Browse a few looks first." are the scene's
   supporting matter, not the decision: on a 390px card the two columns wrap
   to two lines each and spend ~90px — a sixth of the card — restating links
   that are one tap away in the menu, directly under the four rows that ARE
   the step. Hidden below 641px only; the tablet card is wide enough to carry
   the pair on one row, and the section in flow (reduced motion, ?shot, no-JS,
   a landscape handset) keeps it everywhere. */
@media (max-width:640px){
  .ps.is-deck .ps__foot{display:none}

  /* THE ADDRESS IS THE PLATE, AND THE PLATE IS THE CARD (2026-09-14).
     Chapter 03's arrival plate is an ink sheet standing on an aerial of the
     address, which is right at the scale the desktop storyboard reads it —
     one held object about 650px across. On a phone's card the same
     construction is a ~280px map showing a roof with an opaque block over
     most of it: four hairlines of cartography around the thing you are
     actually reading. So the map goes and the sheet takes the frame edge to
     edge — which also makes the deck's first and last chapters the SAME
     object, an ink plate filling its scene, read twice.

     THE EMBED IS NOT MERELY HIDDEN. process.js declines to seat its `src` at
     this width, so a phone never fetches a tile it cannot see — § 22's own
     rule that nothing third-party loads unasked, applied to a width where the
     answer changed. `Open in Google Maps` is still the plate's last line and
     the whole plate is still the link, so nothing is lost but the picture. */
  .ps.is-deck .ps__map{display:none}
  .ps.is-deck .ps__card{margin:0;padding:0;outline:0;
    display:grid;grid-template-rows:minmax(0,1fr)}
  /* the gold corner pin sits OUTSIDE the card's box; with no margin left to
     sit in it would only be clipped by the card's own edge */
  .ps.is-deck .ps__card::after{display:none}
  .ps.is-deck .ps__sheet{justify-self:stretch;align-self:stretch;max-width:none;
    align-content:center;box-shadow:none;padding:var(--s-4) var(--inset)}
  /* full width now, so the street is set to the plate rather than to a sheet
     that was taking 62% of one */
  .ps.is-deck .ps__sheet .ps__card-st{font-size:clamp(1.5rem,7.5vw,2.4rem)}
}

/* --- § 22c · THE CLOSE, RECOMPOSED FOR THE THUMB --------------------------
   THE LAST CARD IS THE ANSWER, SO IT IS BUILT LIKE ONE. Until 2026-09-14 this
   card was the button and its caveat, both pushed against the left inset of a
   300px plate: correct in content and mute in form. The whole section — three
   cards, nine beats — resolves here, and a reader arriving at it had nothing
   telling them they had arrived. It reads as ONE CENTRED COLUMN now, on an
   axis the three cards before it never use, which is the whole of the signal:
   every other card is a left-ranged chapter; this one is a plate.

   FOUR MARKS DOWN ONE AXIS, IN THE ORDER THEY ARE READ:
     the gold tick     a mark, not a word — the eye's entry to the column
     the LABEL         "Ready when you are" in the NOTE voice (§ 4c), the same
                       voice the button's own label speaks, at a third of its
                       weight, so tier 1 introduces tier 2 rather than
                       competing with it
     the gold rule     the label's floor; the button stands on the space below
     the BUTTON        the one action, at the card's full measure
     the CAVEAT        the quietest line on the card, under the thing it
                       qualifies, at --ink-2 (6.7:1 on paper)
   Nothing else. The tick and the rule are the only new ink and both are the
   accent the rail already uses on the active step.

   THE SPACE AROUND IT IS THE COMPOSITION, NOT THE LEFTOVER. Every gap is a
   clamp on svh, so the same picture compresses on a 560px stage instead of
   cropping, and the card itself takes a little under half the stage: the
   column sits inside a deep top and bottom margin, and that margin is what
   makes a 64px button read as the only thing on the screen.

   ≤900px, NOT ≤900 AND DEALT. The deck is off under 560px of height, and off
   entirely for reduced motion and for no-JS, where the close is chapter 03's
   last child in flow. Those readers get the same composition; only the card's
   fixed height and its centring in the stage are the deck's. */
@media (max-width:900px){
  .ps__close{
    --cl-tick:clamp(20px,3.3svh,30px);  /* the entry mark                     */
    --cl-g1:clamp(13px,2.2svh,20px);    /* tick   -> label                    */
    --cl-g2:clamp(10px,1.7svh,15px);    /* label  -> rule                     */
    --cl-g3:clamp(22px,4.4svh,40px);    /* rule   -> the button               */
    --cl-g4:clamp(15px,2.6svh,24px);    /* button -> the caveat               */
    display:grid;justify-items:center;text-align:center;
    padding-inline:var(--inset);
    /* IN FLOW ONLY. The deck's card has an edge to stand the column inside;
       the reduced-motion and no-JS reader has chapter 03's body line directly
       above it and nothing but the axis change to separate the two, so the
       column is given the step a card's padding would have given it. The
       deck's own `margin:0` is three classes and wins here. */
    margin-top:var(--s-6)}

  .ps__close-tick{display:block;width:1px;height:var(--cl-tick);
    margin-bottom:var(--cl-g1);opacity:.85;
    background:linear-gradient(180deg,transparent 0,var(--gold) 55%,var(--gold) 100%)}

  /* the NOTE voice opened up: .08em is a caption's tracking and this is a
     masthead's. --ink-2 rather than the note's own --ink-3, because .52 ink
     at this size is 3.7:1 and the label is the card's first word.
     text-indent pays back the trailing letter-space so the string is
     optically centred on the column rather than sitting a letter left. */
  .ps__close-eb{display:block;margin:0 0 var(--cl-g2);
    font-size:12.5px;letter-spacing:.26em;text-indent:.26em;color:var(--ink-2)}

  .ps__close-rule{display:block;width:36px;height:1px;margin-bottom:var(--cl-g3);
    background:linear-gradient(90deg,transparent 0,var(--gold) 28%,var(--gold) 72%,transparent 100%)}

  /* --- the button, at the card's own measure ------------------------------
     THE PILL IS SIZED BY THE CARD, NOT BY THE PAGE. The baseline object is a
     52px inline CTA (cta-button.css § the slim override) sitting in a row of
     copy; this is the terminal action of the whole section on a device held
     in one hand, and it is given the height that says so. The measured
     PROPORTIONS are held, not abandoned: the knob keeps the reference's ~0.72
     knob:height ratio and its inset scales with it, so the object is the same
     material one notch larger — never a different button.

     THE LABEL IS NOT FIXED COPY, SO THE PILL CANNOT BE A FIXED HEIGHT.
     process.js rewrites this one button to name the pressed service — "Book a
     taper fade + design — $75", "Reservar taper fade + diseño — $75" — and
     `.lhb-cta__label` is `white-space:nowrap` by measurement, so the longest
     of them drove the pill straight off the right edge of the card (observed
     at 390px, 2026-09-14). cta-button.css steps the type down to 11.4px under
     420px rather than claiming to solve this; on this card there is a better
     answer than a smaller word, because the card is a plate with room under
     the button. The label WRAPS and the pill grows to meet it: min-height
     holds the full height for every label that fits on one line, so the short
     states never move, and `text-wrap:balance` splits a long one into lines
     of similar length rather than orphaning the price. The longest string the
     section can produce — "Reservar taper fade + diseno — $75" — takes three
     lines and an 84px pill at 390, still clear of the caveat under it. That
     wrapping is what buys the type back: 15.6px at 390 where the page-wide
     ladder gives 11.4px, because this pill has somewhere to put the overflow
     and an inline one has not. Scoped here; the measured object is untouched
     everywhere else.

     touch-action so the tap fires on the tap, with no 300ms wait for a
     double that this card has nothing to do with. */
  /* THREE CLASSES, DELIBERATELY. cta-button.css is the LAST sheet in the head
     (index.html:19) precisely so a tie at equal specificity falls to the
     component, and its own ladder — `.lhb-cta--fluid .lhb-cta__label` under
     420px — is two classes. A two-class selector here would lose the tie and
     the label would still be 11.4px. `.ps` is always an ancestor: in flow the
     close is chapter 03's last child, and on the deck process.js moves it to
     .ps__stage, which is inside .ps either way. */
  .ps .ps__close .lhb-cta{
    --lhb-h:clamp(56px,7.4svh,66px);
    --lhb-knob:calc(var(--lhb-h) * .72);
    --lhb-knob-inset:calc(var(--lhb-h) * .13);
    --lhb-pad-left:clamp(20px,6vw,28px);
    --lhb-gap:clamp(14px,4vw,20px);
    width:100%;min-width:0;height:auto;min-height:var(--lhb-h);
    padding-block:12px;touch-action:manipulation}
  /* text-align because this is a <button>: the UA sheet centres its text, and
     a nowrap label never showed it — the line box was the width of the line.
     The moment it wraps, the measured left inset stops meaning anything. */
  .ps .ps__close .lhb-cta__label{font-size:clamp(13.5px,4vw,16px);letter-spacing:.16em;
    white-space:normal;text-wrap:balance;text-align:left;line-height:1.28;min-width:0}

  .ps__close .ps__cta{margin:0;width:100%;display:block}
  .ps__close-note{margin-top:var(--cl-g4);max-width:34ch;
    color:var(--ink-2);line-height:1.5;text-wrap:balance}
}

@media (max-width:900px) and (min-height:560px){
  /* --- the fourth card: the close ---------------------------------------
     The column above, centred in a plate that takes a little under half the
     stage. 34svh/300px held the old left-ranged pair; the composition needs
     the room and, more to the point, a card this reader has been dealt three
     times should resolve onto something with presence. It is still far
     shorter than a chapter card (78svh), so it stays hidden behind 03 until
     03 deals away — which is the deal this section was built on. */
  .ps.is-deck .ps__close{align-content:center;
    height:min(46svh,400px);padding:var(--s-4) var(--inset)}
}

/* --- wired: the storyboard (desktop, JS) ----------------------------------
   The stage is one screen tall and becomes a SEVEN-row grid: the three steps stack down
   the rail in column 1 (heading row, copy row, ×3), and row 7 is the close —
   the Book CTA and its one micro line — which takes the remaining height, so
   the canvas still runs the full stage. The frame and the three scenes share
   columns 2–3 across all seven rows. Chapters become display:contents so ONE
   markup serves both layouts; every selector keyed on .ps__ch still resolves,
   and .ps__close is the single grid item chapter 03 hands down for row 7. */
@media (min-width:901px){
  /* NO TRACK (2026-09-15). The section used to be the pinned stage plus
     `--ps-travel: clamp(2600px,330svh,3700px)` of scroll, and the wheel
     stepped the nine beats through it. The composition below is unchanged —
     it is the same stage, at the same height, holding the same seven-row
     grid — but it STANDS IN THE DOCUMENT: one screen of section, scrolled
     past like any other, with the story turned by the rail, the frame's Next
     control or the scene itself (process.js initBoard).
     THE STAGE KEEPS ITS OWN HEIGHT and the section takes it, so the box the
     grid resolves against is the one it had under the pin; the header offset
     leaves with the pin, because a block in flow is never under the header. */
  .ps.is-wired{height:auto}
  .ps.is-wired .ps__stage{position:relative;
    height:calc(100svh - var(--header-h));box-sizing:border-box;
    padding-block:var(--s-5);
    display:grid;grid-template-columns:2fr 3fr 1fr;
    grid-template-rows:auto auto auto auto auto auto minmax(0,1fr);
    overflow:hidden;overflow:clip}
  .ps.is-wired .ps__ch{display:contents}
  .ps.is-wired .ps__txt{grid-column:1;border-top:1px solid var(--rule);
    padding-block:var(--s-4) var(--s-3)}
  .ps.is-wired .ps__ch--1 .ps__txt{grid-row:1}
  .ps.is-wired .ps__ch--1 .ps__body{grid-row:2}
  .ps.is-wired .ps__ch--2 .ps__txt{grid-row:3}
  .ps.is-wired .ps__ch--2 .ps__body{grid-row:4}
  .ps.is-wired .ps__ch--3 .ps__txt{grid-row:5}
  .ps.is-wired .ps__ch--3 .ps__body{grid-row:6}
  /* THE CLOSE, UNDER THE THIRD STEP — AND THERE THE WHOLE TIME. Until
     2026-09-12 the one CTA waited below the whole pinned section, which on a
     laptop means a screenful of scrolling after the decision was made, with
     only the words "Ready — book a cut" in the frame head to promise it was
     coming. It now stands in the rail under step 03, in the space the three
     steps leave empty, and it does not wait for step 03 to appear: the one
     action the section exists for should never be a step you have to reach.
     The label still rewords itself as soon as a service is pressed. */
  .ps.is-wired .ps__close{grid-column:1;grid-row:7;align-self:start;
    margin-top:var(--s-4)}
  .ps.is-wired .ps__close .ps__cta{margin-top:0}
  /* the copy unfolds under the active heading and folds away under the rest */
  .ps.is-wired .ps__body{grid-column:1;grid-template-rows:0fr;opacity:0;
    transition:grid-template-rows var(--d-move) var(--ease-out),opacity var(--d-fast) ease}
  .ps.is-wired .ps__ch.is-on .ps__body{grid-template-rows:1fr;opacity:1;
    transition-delay:0s,160ms}
  .ps.is-wired .ps__body p{padding-bottom:var(--s-4)}

  /* THE FRAME: one hairline box seated on column rules 3 and 7, a head strip
     for the readout and the pointer affordance, and nothing else. It is
     pointer-transparent so the scene under it stays interactive. */
  .ps.is-wired .ps__frame{display:block;grid-column:2 / 4;grid-row:1 / 8;position:relative;
    z-index:3;border:1px solid var(--rule);pointer-events:none;min-height:0}
  .ps__frame::after{content:"";position:absolute;left:0;right:0;top:var(--s-6);height:1px;
    background:var(--rule)}
  .ps__count{position:absolute;left:var(--inset);top:0;height:var(--s-6);
    display:flex;align-items:center}
  .ps__count b{font-weight:400;color:var(--ink-1);margin-right:.4em}
  .ps__seg{position:absolute;left:calc(var(--inset) + 5.5em);top:0;height:var(--s-6);
    display:flex;align-items:center;gap:4px}
  .ps__seg i{display:block;width:28px;height:2px;background:var(--rule);
    transition:background var(--d-fast) ease}
  .ps[data-ch="1"] .ps__seg i:nth-child(1),
  .ps[data-ch="2"] .ps__seg i:nth-child(-n+2),
  .ps[data-ch="3"] .ps__seg i{background:var(--ink)}
  .ps__next{pointer-events:auto;position:absolute;right:0;top:0;height:var(--s-6);
    padding:0 var(--inset);display:flex;align-items:center;gap:.6em;
    border:0;border-left:1px solid var(--rule);background:none;cursor:pointer;
    color:var(--ink-2);
    transition:color var(--d-fast) ease,background var(--d-fast) ease}
  .ps__next:hover{color:var(--ink-1);background:var(--paper-2)}
  .ps__next-n{color:var(--ink-1)}
  .ps__next-arrow{display:inline-block;transition:transform var(--d-fast) ease}
  .ps__next:hover .ps__next-arrow{transform:translateX(4px)}
  /* ON THE LAST STEP THERE IS NOTHING LEFT TO POINT AT. The control used to
     read "Ready — book a cut" with the arrow turned down toward a CTA a
     screenful below; the CTA is now in the rail beside it, so the affordance
     simply retires rather than narrating the obvious. */
  .ps[data-ch="3"] .ps__next{display:none}

  /* THE SCENES share the cell under the frame. Each fig is the same two
     cells — action on three columns, atmosphere on the fourth — stacked in
     the same place, so the seam on column rule 6 never moves. Hidden scenes
     are visibility:hidden so nothing focusable hides in the stack. */
  .ps.is-wired .ps__fig{grid-column:2 / 4;grid-row:1 / 8;position:relative;z-index:1;
    outline:0;overflow:hidden;min-height:0;background:transparent;
    padding-top:calc(var(--s-6) + 1px);
    visibility:hidden;pointer-events:none;
    transition:visibility 0s var(--d-reveal)}
  .ps.is-wired .ps__ch.is-on .ps__fig{visibility:visible;pointer-events:auto;z-index:2;
    transition-delay:0s}
  /* THE HANDOVER. The action leaves upward and arrives from below; the
     atmosphere is wiped by a mask travelling the same way — the outgoing
     picture closes toward the top as the incoming one opens from the foot,
     one edge, one direction. Stepping back runs the same wipe downward. */
  .ps.is-wired .ps__scene{opacity:0;transform:translateY(28px);
    transition:opacity var(--d-move) var(--ease-out),transform var(--d-move) var(--ease-out)}
  .ps.is-wired .ps__ch.is-past .ps__scene{transform:translateY(-18px) scale(.985)}
  .ps.is-wired .ps__ch.is-on .ps__scene{opacity:1;transform:none;transition-delay:120ms,120ms}
  .ps.is-wired .ps__atm{background:transparent}
  .ps.is-wired .ps__atm-img.is-on{clip-path:inset(100% 0 0 0);
    transition:clip-path var(--d-reveal) var(--ease-out),opacity var(--d-reveal) ease}
  .ps.is-wired .ps__ch.is-past .ps__atm-img.is-on{clip-path:inset(0 0 100% 0)}
  .ps.is-wired .ps__ch.is-on .ps__atm-img.is-on{clip-path:inset(0);transition-delay:100ms,0s}
  /* the scene itself is the pointer affordance for the next step */
  .ps.is-wired:not([data-ch="3"]) .ps__ch.is-on .ps__fig{cursor:pointer}
  .ps.is-wired .ps__scene{min-height:0}
  .ps.is-wired .ps__cells i{aspect-ratio:auto}
}
/* a short desktop viewport: the plate keeps its poster, the rows tighten,
   the foot goes — the decision stays whole */
@media (min-width:901px) and (max-height:760px){
  .ps.is-wired .ps__stage{padding-block:var(--s-4)}
  .ps.is-wired .ps__txt{padding-block:var(--s-3) var(--s-2)}
  .ps.is-wired .ps-card{height:44px}
  .ps.is-wired .ps__foot{display:none}
  .ps.is-wired .ps__card-no{font-size:var(--t-title)}
  .ps.is-wired .ps__card{padding-block:var(--s-3)}
}

/* --- reduced motion: the resolved page, and no clocks --------------------- */
@media (prefers-reduced-motion:reduce){
  .ps *,.ps *::before,.ps *::after{transition:none!important;animation:none!important}
  .ps__h{transform:none}
}


/* ==========================================================================
   23. FLUTED GLASS — what is left of the sheet
   Reference: the ribbed-glass study (vertical flutes, lateral refraction, an
   edge that lets go before the frame).

   THE FIVE LAYERS ARE GONE, 2026-09-15. The sheet was cut for two panes: the
   Work plate, retired with the roaming plate it faced, and the closing CTA
   (§ 23a), which no longer carries glass at all. With no .glass__layer left
   in any document, `refract / frost / grain / flutes / sheen` and the
   --glass-* tokens that tuned them went with the panes they were written for.
   See 07-operations/decisions/ for the record; the code is in commit 89bc470.

   WHAT SURVIVES IS THE PITCH. § 23d's shout channel is still cut from this
   sheet — four fixed vertical strips with a moving stream behind them — and
   glass.js still builds its optic off these two tokens, so the painted rib
   and the bent rib can never drift apart.

   PITCH IS PHYSICAL. --rib is a length, not a count, so a 340px strip and a
   760px strip are cut from the same sheet rather than being scaled copies of
   each other.
   ========================================================================== */
/* THE SHEET. Declared once, at the root, because it is one sheet: § 23d's
   four channels and the footer square are all cut from it. Anything that
   overrides --rib is stating that it is a different piece of glass, and
   should say why. */
:root{
  --rib:34px;             /* pitch of one flute — the sheet, not the pane */
  --rib-edge:10%;         /* the flutes let go before the frame, both sides */
}

/* --- 23a. The closing CTA plate — A COLUMN, NOT A PANE ------------------
   THE GLASS IS OUT, 2026-09-15. From 2026-09-05 this plate stood on the Work
   plate's material: five refracting layers, cut at the sheet's rib pitch and
   inverted for ink, so the type was lifted off the photograph by a slab of
   fluted glass. From 2026-09-11 that slab was also HELD — in § 23c2's machined
   acrylic sheet, rails, corners and all. Both are gone.

   WHAT THE ASK STANDS ON NOW is the room itself: `.cta__bg`'s photograph under
   the ink scrim its ::after paints, which is exactly what § 17a's depth is
   already moving. The scrim is .5 at the hold and deepens to .78 at both ends
   of the passage, and that alone is what holds paper type legible on it — the
   contrast is drawn ON the picture rather than in a layer over it.

   WHAT THE ELEMENT IS FOR, AND WHY IT STAYS. It is the MEASURE: 760px, centred,
   with the block padding and row gap the composition is set in, and it is the
   mid plane § 17a parallaxes. None of that was ever the glass's job.

   AND THE LAST backdrop-filter ON THIS PAGE GOES WITH IT. The plate's
   refraction is the cost § 17a's two brakes and wall.js's `is-wall-over` trade
   were written to dodge — a backdrop-filter re-runs its blur on every COMPOSITE
   of its region, so anything moving over the plate paid for it. There is
   nothing left under the wall to re-blur. */
.cta__pane{
  position:relative;
  width:min(760px,100%);
  padding:var(--s-7) var(--s-6);
  display:grid;justify-items:center;gap:var(--s-5);
}
/* the quiet second door (M5, 2026-09-05): the plate's one button BOOKS, and
   the ideal-look funnel keeps this line instead — micro voice, hairline
   underline, pulled in tight under the button it defers to */
.cta__aside{margin:calc(-1 * var(--s-3)) 0 0}
.cta__aside a{color:inherit;text-decoration:underline;
  text-decoration-color:var(--rule-invert);text-decoration-thickness:1px;
  text-underline-offset:.22em;transition:text-decoration-color var(--d-fast) ease}
.cta__aside a:hover,.cta__aside a:focus-visible{text-decoration-color:currentColor}
@media (max-width:640px){
  .cta__pane{padding:var(--s-6) var(--s-4)}
}

/* THE PLATE FITS A SHORT WINDOW RATHER THAN LOSING ITS PIN ----------------
   cta-scene.js declines the whole passage where the plate cannot be read
   inside one pinned viewport — `innerHeight >= pane + AIR * 2` at eligible().
   That guard is right: a plate clipped by the fold is worse than a plate that
   simply scrolls. What was wrong is how easily it fired.

   THE DEAD BAND, MEASURED. The pane takes its height from its own type, and
   --t-display tops out at 8.25rem from about 1180px wide — so from there up
   the pane is a FLAT 720px whatever the window does, and the scene needs 768px
   of viewport to keep it. A browser window on a 13" laptop is routinely
   1248x760. Eight pixels short, and the whole passage is declined: the plate
   stops pinning, and the sections either side run over it on the way past.
   Below 1180 the pane shrinks with the type and the band closes on its own,
   which is why this was only ever seen on a wide, short window.

   SO THE PLATE GIVES, AND IT GIVES FROM THE PART THAT IS AIR. Its block
   padding and its row gap are generous because at full height they should be;
   asked for a short window they take svh instead, down to a floor that still
   reads as a plate and not as a box. The type is left alone until 660px, below
   which the headline is the only thing left to give.

   AND THE THRESHOLD IS THE REQUIREMENT ITSELF, not a round number near it. The
   pane is 720 and the guard wants 720 + 48, so 768px of viewport is exactly
   where the full-height plate stops fitting — at 768 it passes by a hair and
   nothing here applies; at 767 it cannot, and this does. A window tall enough
   to have been fine is left alone to the pixel, which is the difference
   between a floor and a restyle. */
@media (min-width:641px) and (max-height:767px){
  .cta__pane{
    padding-block:clamp(40px,7svh,var(--s-7));
    row-gap:clamp(18px,3.2svh,var(--s-5));
  }
}
@media (min-width:641px) and (max-height:660px){
  .cta__pane{--t-display:clamp(2.75rem,13svh,8.25rem)}
}

/* --- 23c. The EDITORIAL FRAME — a keyline, and the grid ------------------
   Replaces the machined acrylic sheet, 2026-09-05.

   WHAT WAS HERE, AND WHY IT WENT
   Every photograph on the site was held inside a sheet of § 23's glass: two
   polished rails, taken-down top and bottom edges, an inner hairline, four
   machined corners with a floating crescent outside each, a specular down
   each rail, a backdrop band that bent the picture sideways, and a mask that
   cut the PHOTOGRAPH ITSELF to the sheet's rounded outline. It was ~400 lines
   of JS geometry, one <svg> per picture and a ResizeObserver to keep ninety
   viewBoxes in pixel step — and the effect of all of it was that the frame
   was the first thing you saw and the picture the second. The corner radius
   took a bite out of every face on the site.

   THE ORDER THIS RESTORES, and it is the whole brief:
       1  the photograph
       2  the type
       3  the framing
   Nothing here is translucent, blurred, gradiented, rounded or lifted. No
   backdrop-filter, no spread on any shadow, no radius, no mask. The pictures
   are square again, at exactly the sizes they already were, and the carousel,
   the canvas roam and every breakpoint are untouched.

   THE FRAME IS ONE 1px KEYLINE, on the picture's own edge, in ink. It is
   legible on any photograph because it is a BOUNDARY and not a mark: the
   paper is always on one side of it. That is also why it can be this quiet —
   the grid (§ 3's hairline columns, § 11's 1px tile gaps, § 5b's fields) is
   already doing the architectural work, and the keyline only has to close the
   picture against it.

   THE REGISTRATION MARKS are the print device the brief asks for: four corner
   brackets, 1px, in the same ink, set OUTSIDE the keyline in the page's own
   margin — where a printer puts a crop mark. Outside, because a mark drawn ON
   a photograph is at the mercy of whatever is under it, and a frame that only
   reads on some of the pictures is not a system. So they are opt-in, on the
   hosts that do not clip and have air to spend. The reel is the one that
   does: each picture stands on open paper with a grid gap either side.

   WHY PSEUDO-ELEMENTS AND NOT AN ELEMENT. ::before is the keyline, ::after is
   the marks. No node, no observer, no script — and it survives a transform, a
   container query and a breakpoint without being told, which is the whole of
   what the ResizeObserver used to be for. glass.js hangs nothing on a picture
   any more.

   ONE PLACE, FOUR HOSTS. The list below is what the FRAMES manifest in
   glass.js used to be: the treatment stays a THEME rather than becoming a
   per-picture decision. The story tile-grid is deliberately NOT on it — § 11
   frames that grid with its own 1px gaps, and a keyline inside a gridded
   plate is the same line drawn twice. */
/* THE TOKENS GO ON THE HOST, NOT ON THE PSEUDO. ::before draws the keyline
   and ::after draws the marks, and a custom property set on one pseudo is
   invisible to the other — they are siblings, not ancestors. Declared here,
   both inherit, and a context retunes its whole frame in one place. */
.tl-frame,.split__media,.cut{
  /* the keyline. Ink, not --rule: --rule is for structure the eye reads
     THROUGH, and this line has to end a photograph. */
  --fr-line:rgba(16,16,17,.42);
  --fr-mark:var(--fr-line);      /* the brackets are the same ink, always */
  --fr-len:16px;                 /* one arm of one bracket */
  --fr-out:9px;                  /* how far the bracket stands off the corner */
}
.tl-frame::before,
.split__media::before,
.cut::before{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  box-shadow:inset 0 0 0 1px var(--fr-line);
}

/* THE FOUR CORNER REGISTRATION MARKS — eight 1px background layers on one
   pseudo, a horizontal and a vertical arm per corner, on a box grown --fr-out
   clear of the picture so the marks stand OFF the keyline rather than
   thickening it. `.is-reg` is the only switch; the host must leave ::after
   free and must not clip its overflow. */
.is-reg::after{
  content:"";position:absolute;z-index:1;pointer-events:none;
  inset:calc(var(--fr-out) * -1);
  background-repeat:no-repeat;
  background-image:
    linear-gradient(var(--fr-mark),var(--fr-mark)),linear-gradient(var(--fr-mark),var(--fr-mark)),
    linear-gradient(var(--fr-mark),var(--fr-mark)),linear-gradient(var(--fr-mark),var(--fr-mark)),
    linear-gradient(var(--fr-mark),var(--fr-mark)),linear-gradient(var(--fr-mark),var(--fr-mark)),
    linear-gradient(var(--fr-mark),var(--fr-mark)),linear-gradient(var(--fr-mark),var(--fr-mark));
  background-size:
    var(--fr-len) 1px,1px var(--fr-len),
    var(--fr-len) 1px,1px var(--fr-len),
    var(--fr-len) 1px,1px var(--fr-len),
    var(--fr-len) 1px,1px var(--fr-len);
  background-position:
    left 0 top 0,left 0 top 0,
    right 0 top 0,right 0 top 0,
    left 0 bottom 0,left 0 bottom 0,
    right 0 bottom 0,right 0 bottom 0;
}

/* ---- per host ---------------------------------------------------------- */
/* THE SPLIT FEATURE bleeds to the viewport on one side and is the largest
   picture on the site, so it takes the keyline alone at a touch more weight —
   a bracket half off the screen is not a registration mark. Above the veil
   (§ 5's ::after), which would otherwise dim its own frame. */
.split__media::before{--fr-line:rgba(16,16,17,.5);z-index:2}
/* A CONTACT-SHEET PRINT on the canvas: ~230px wide. The keyline is the whole
   frame here, at the weight the canvas's own hairlines are drawn. It used to
   sit at z-index 0 so the Ideal Look's claimed-state metal hairline could be
   drawn over it; there is no claimed state any more (2026-09-12), and the
   z-index is kept only because the print is painted on a moving field. */
.cut::before{--fr-line:rgba(16,16,17,.34);z-index:0}
/* THE CLOSING PANE IS NOT ON THIS LIST, and has not been since 2026-09-11 —
   first because it was held in § 23c2's machined sheet, and since 2026-09-15
   because it is not a framed object at all. It is a measure with type in it
   (§ 23a): no surface, no edge, nothing to close. It left the token lists
   above with the sheet. */


/* CLOSING THE STORY GRID. § 11 frames its tiles with 1px gaps on a --rule
   ground, which draws every INTERNAL edge and no external one, so the plate
   used to end on nothing. One inset outline closes it — no layout change, no
   second line anywhere, and the grid keeps doing the framing. */
.tile-grid{outline:1px solid var(--rule);outline-offset:-1px}

/* THE PICTURES ARE SQUARE AGAIN. Restated rather than assumed: the sheet used
   to hand each host its own outline and mask the picture to it, and the one
   thing this section must never do is put a radius back. */
.tl-frame img,.split__media img,.cut img,.figure img{border-radius:0}

/* the brackets come down with the pictures. --fr-out shrinks with them so the
   mark never eats the grid gap it is standing in. */
@media (max-width:900px){
  .tl-frame,.split__media,.cut{--fr-len:13px;--fr-out:7px}
}
@media (max-width:640px){
  .tl-frame,.split__media,.cut{--fr-len:11px;--fr-out:6px}
}


/* .pane-glass SURVIVES FOR § 23d ALONE. It once framed a picture with a
   masked band of refracting glass; the machined sheet took that job off it,
   and § 23c's keyline has since taken it off the sheet. What is left is the
   component the shout channel re-cuts into four fixed vertical strips — a
   moving stream behind fixed glass, which is a motion device and not a frame
   around a photograph. Its comments below are written for that use. The only
   .pane-glass elements in the document are the four authored in index.html;
   nothing hangs one, and glass.js hangs nothing at all. */

.pane-glass{
  /* ONE FLUTE DEEP by default. The band is not an arbitrary inset: it is the
     pitch of the sheet, so what frames the picture is a single rib of glass
     rather than a stripe that happens to have ribs in it. */
  --pane-band:var(--rib);
  --pane-blur:3px;
  --pane-sat:1.16;
  --pane-warp:brightness(1);   /* identity until glass.js supplies the optic */
  --pane-hi:.30;          /* specular down the lit face of each flute */
  --pane-lo:.20;          /* the shadowed seam between two flutes */
  /* z-index 0, not 1: it shares the painting stage with the captions and
     affordances its container already carries, so DOM order decides — and the
     glass is inserted before them. */
  position:absolute;inset:0;z-index:0;pointer-events:none;
  backdrop-filter:var(--pane-warp) blur(var(--pane-blur)) saturate(var(--pane-sat)) brightness(1.05);
  -webkit-backdrop-filter:var(--pane-warp) blur(var(--pane-blur)) saturate(var(--pane-sat)) brightness(1.05);
  /* the ground edge of the sheet, at the picture's own border */
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.22),
             inset 0 1px 0 rgba(255,255,255,.30);
  -webkit-mask-image:
    linear-gradient(90deg,#000 0,#000 calc(var(--pane-band) * .45),
      transparent var(--pane-band),transparent calc(100% - var(--pane-band)),
      #000 calc(100% - var(--pane-band) * .45),#000 100%),
    linear-gradient(180deg,#000 0,#000 calc(var(--pane-band) * .45),
      transparent var(--pane-band),transparent calc(100% - var(--pane-band)),
      #000 calc(100% - var(--pane-band) * .45),#000 100%);
  mask-image:
    linear-gradient(90deg,#000 0,#000 calc(var(--pane-band) * .45),
      transparent var(--pane-band),transparent calc(100% - var(--pane-band)),
      #000 calc(100% - var(--pane-band) * .45),#000 100%),
    linear-gradient(180deg,#000 0,#000 calc(var(--pane-band) * .45),
      transparent var(--pane-band),transparent calc(100% - var(--pane-band)),
      #000 calc(100% - var(--pane-band) * .45),#000 100%);
  -webkit-mask-composite:source-over;
  mask-composite:add;
}
/* the flutes in paint, exactly as § 23 — same pitch, same light. The parent's
   mask clips this too, so it never reaches the middle of the picture. */
.pane-glass::after{
  content:"";position:absolute;inset:0;
  background-image:repeating-linear-gradient(90deg,
    rgba(255,255,255,0) 0,
    rgba(255,255,255,var(--pane-hi)) calc(var(--rib) * .07),
    rgba(255,255,255,0) calc(var(--rib) * .34),
    rgba(16,16,17,0) calc(var(--rib) * .62),
    rgba(16,16,17,var(--pane-lo)) calc(var(--rib) * .94),
    rgba(16,16,17,0) var(--rib))}


/* --- 23c2. THE MACHINED SHEET — REMOVED, 2026-09-15 ----------------------
   The closing plate was held inside a machined sheet of clear acrylic: two
   polished rails, taken-down top and bottom edges, an inner hairline, four
   corners that STEPPED-ARC-STEPPED rather than rounding, a floating crescent
   outside each, a specular down each rail, and a mask (--gf-cut, .is-cut) that
   carved the plate's five glass layers to that outline. It was drawn as SVG
   because the corner is made of circles and a stretched box turns every circle
   into an ellipse, which is the border-radius look it existed not to be; one
   ResizeObserver kept the viewBox in the host's own pixels.

   It went out with the glass it was holding (§ 23a). There is no second host to
   move it to — the editorial-framing decision of 2026-09-05 put every
   photograph on § 23c's keyline and the plate was the one exception left.

   THE GEOMETRY IS NOT LOST. `06-build/dev/shots/glass-frame-geometry.md` traces
   the reference, and the thirteen parts, the shared <defs> and frameGeometry()
   are in glass.js at commit 89bc470. It was recovered once before, from the
   dropped stash e3603cb; this time it is in the history properly. */

/* --- 23d. The sheet as a CHANNEL — the shout rails run behind it ---------
   § 23c frames a picture that is standing still. This is the same sheet cut
   the other way: fixed vertical channels with a moving stream behind them.

   WHERE IT SITS   Each rail already sits inside a band closed by two
                   hairlines, with --rail-pad of air on each side. That air is
                   the gap, and there are four of them across the section.

   HOW DEEP        TWICE THE GAP. Half of each strip lands on the ground
                   between the hairline and the picture — the gap being filled
                   — and half lands on the picture, so the glass has something
                   to bend. Fill only the gap and there is nothing behind it
                   but flat paper, and flat paper refracted is flat paper.

   WHY FOUR NARROW STRIPS AND NOT TWO WIDE ONES
                   A backdrop filter is computed over the WHOLE element and
                   only then masked. Two band-wide panes meant filtering
                   227x1080 twice a frame in order to keep 32px of each — and
                   on a section whose streams never stop moving that measured
                   at 493ms a frame against 35ms without it. Four strips, each
                   only as wide as the glass actually is, filter a fifth of
                   the area. Geometry is the optimisation; nothing is given up.

   WHAT IT MUST NOT TOUCH   the hairlines. Every strip starts one pixel inside
                   the line it sits against — § 16's law 5, for the same
                   reason: a grid line bent by a backdrop filter is not a grid
                   line (M3).
   ========================================================================== */
.shout__glass{position:absolute;inset:0;z-index:0;pointer-events:none}
.shout__edge{
  --pane-band:min(var(--rib),calc(var(--rail-pad) * 2));
  /* PAPER, so the light is the other way round: § 23's sheet spends .11/.06 on
     paper and § 23a's spends .07/.13 on ink, and this channel has always run
     that relationship ~1.4x harder than the pane does, because a strip one
     flute wide has to state the rib in a fraction of the width. Same multiple,
     applied to the paper pair rather than the ink one. */
  --pane-hi:.16;          /* paper: the specular does the work... */
  --pane-lo:.08;          /* ...and the seam only closes the rib */
  /* the hairlines this channel sits against are drawn by .shout__rules and are
     not this element's to redraw, so the ground edge § 23c carries comes off */
  box-shadow:none;
  /* full height, exactly as the hairlines run — the stage's padding undone the
     same way .shout__rules span undoes it */
  top:calc(var(--stage-pt) * -1);bottom:calc(var(--stage-pb) * -1);
  left:auto;right:auto;                     /* .pane-glass's inset:0, unset */
  width:var(--pane-band);
  /* .pane-glass lifts what it refracts by 1.05 — a value cut for glass sitting
     on ink. On paper that is a blown edge next to the hairline, so the bend
     comes back to the sheet's own 1.02 (§ 23). Everything else is inherited. */
  backdrop-filter:var(--pane-warp) blur(var(--pane-blur)) saturate(var(--pane-sat)) brightness(1.02);
  -webkit-backdrop-filter:var(--pane-warp) blur(var(--pane-blur)) saturate(var(--pane-sat)) brightness(1.02);
}
/* one edge each, so a strip is solid against its own hairline and lets go as
   it crosses onto the photograph */
.shout__edge--lo,.shout__edge--ri{
  -webkit-mask-image:linear-gradient(90deg,#000 0,#000 45%,transparent 100%);
  mask-image:linear-gradient(90deg,#000 0,#000 45%,transparent 100%)}
.shout__edge--li,.shout__edge--ro{
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 55%,#000 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 55%,#000 100%)}

.shout__edge--lo{left:calc(var(--gutter) + 1px)}
.shout__edge--li{left:calc(var(--gutter) + var(--rail-band) - 1px - var(--pane-band))}
.shout__edge--ri{right:calc(var(--gutter) + var(--rail-band) - 1px - var(--pane-band))}
.shout__edge--ro{right:calc(var(--gutter) + 1px)}

/* Below 560px the band is gone: the rails move behind the headline and the
   section keeps only its two gutter lines (§ 22). No band, no channel. */
@media (max-width:560px){.shout__glass{display:none}}
/* --- 23b. Reduced motion -------------------------------------------------
   The flutes are a surface, not a movement, so they stay. Only the parallax
   goes, and that is decided in glass.js — nothing here needs to change. */

/* ==========================================================================
   22. THE SHOUT — pinned, and moving in two directions at once
   Reference: workflows/ui-ux-design/implement-scroll-reveal-pin.md, M7, M8

   THE IDEA    Photography moves upward. Typography echoes downward. Nothing
               else moves. Two rails and one headline produce the whole effect,
               which is why the geometry underneath stays this plain.

   THE COMPOSITION (M8 — eyebrow / oversized headline / one button, and the
               optional image block, which here is the two rails rather than a
               plate). The core is sized between the rails, not over them, so
               the type never sits on a photograph. Everything scales from
               --rail-w: the rails set their own width, the core takes what is
               left, and the headline is sized from the core it has to fit.

   NOTHING PINS The section is one screen tall and the reader scrolls straight
               through it into the hours and the footer. It held itself on a
               280svh runway once; the hold read as a wall at the end of a long
               page, so the runway is gone and the composition is simply the
               last screen of the chapter. The motion below is unaffected —
               it was never a function of the pin.

   THE MOTION  scroll VELOCITY, not scroll distance. Script publishes exactly
               three numbers on this section — --echo (0..1 energy), and the
               two rail offsets — and this sheet spends them. Attack is fast,
               release is long, so the composition has inertia: it collapses
               back to the resting state over about a second after the wheel
               stops rather than snapping.

   THE FOCUS   the one thing this sheet does NOT own. Colour in the rails is a
               band held across the middle of the stage that the photographs
               travel through: full colour on the frame at the centre line,
               falling off a smootherstep curve to full grey at the rail's
               extremities. That number is per-frame and depends on where each
               image currently IS, which no selector can ask, so script writes
               the grayscale amount onto each <img> from the same offset it
               takes --rail-l/-r from (app.js § the focal plane). The rule
               below is the resting state and the no-script state — the whole
               rail monochrome, exactly as it was.

   NEVER LOOPS (M7) The rails carry no animation. They are a pure function of
               how far the reader has scrolled, so at rest they are still. The
               seam is hidden by repetition and a modulo in script, not by an
               animation that runs on a timer.

   THE DECLINE no pin, no echo, static rails: under reduced motion and in shot
               mode. The section falls back to one clean 100svh composition,
               which is also what it is with no script at all.
   ========================================================================== */

.shout{
  /* the two given quantities — everything else is derived from them */
  --rail-w:clamp(40px,13vw,250px);
  --rail-air:clamp(8px,2.2vw,40px);         /* rail edge to the core's edge */
  /* the air between a hairline and the photographs it brackets. The section is
     opaque, so the global grid (§ 3) is buried here and re-drawn in place; the
     rails then sit INSIDE a band rather than on top of a line, which is why
     the band, not the rail, is what the outer hairline measures. */
  --rail-pad:clamp(6px,1.1vw,16px);
  --rail-band:calc(var(--rail-w) + 2 * var(--rail-pad));
  --core-w:calc(100% - 2 * (var(--rail-band) + var(--rail-air)));
  /* 16.8cqw is the FIT, measured not guessed: the lockup's longest line,
     "A SHOUT", sets 5.685em in Archivo at wdth 125 / wght 700 with this
     tracking, so 1/5.95 of the core is the largest the headline can be and
     still hold that line whole. There is deliberately no lower bound — a floor
     is exactly how type ends up wider than the column it was sized for, and a
     headline that wraps to four lines is a broken composition, not a small one.
     The other two terms are the height budget and a ceiling for wide screens. */
  --shout-type:min(16.8cqw,13svh,8.5rem);

  /* the echo — 6 layers, each one step further down than the last */
  --echo-step:.42em;
  --echo-rest:.10;                          /* the trail that survives at rest */

  /* published by script, spent here. Defaults are the resting state. */
  --echo:0;
  --rail-l:0px;
  --rail-r:0px;

  /* the stage's own padding, named so the hairline layer can undo it and run
     the full height of the screen (M3) instead of stopping at the text box */
  --stage-pt:calc(var(--header-h) + 4svh);
  --stage-pb:8svh;

  position:relative;
  height:100svh;                            /* one screen, scrolled through */
  /* PAPER, not ink. The closing ask is the last beat of the light chapter,
     not a second ink plate one screen after § 21's: #services, #area and
     #process all stand on paper, and the shout now closes on that same ground
     rather than turning the publication over immediately before the footer
     turns it over again. This is why .shout is deliberately not a claimant of
     § 1a — on paper it wants the root's four inks, so it states its ground and
     every primitive underneath inherits, exactly as those sections do. */
  background:var(--paper);color:var(--ink);
}
.shout__stage{
  position:relative;
  height:100svh;
  overflow:hidden;
  display:grid;place-items:center;
  /* bottom-weighted, because the echo hangs below the headline and the
     composition has to stay optically centred while it does */
  /* the trail now reserves its own band inside .shout__echo, so the stage no
     longer has to leave room underneath for an overhang */
  padding:var(--stage-pt) var(--gutter) var(--stage-pb);
}
/* --- the hairlines, continued through an opaque section ------------------- */
/* Four lines, not six: the two that the global grid already draws at the
   gutter, and the two that close the band on the core side. They are a
   separate layer from .shout__rails because the rails are masked top and
   bottom — a hairline that fades out is not a grid line (M3: full height,
   never interrupted). */
.shout__rules{position:absolute;inset:0;z-index:0;pointer-events:none}
.shout__rules span{
  position:absolute;top:calc(var(--stage-pt) * -1);bottom:calc(var(--stage-pb) * -1);
  width:var(--rail-band);
  border-inline:1px solid var(--rule);
}
/* one gutter in from the section edge — the same x the fixed layer (§ 3) draws
   its outermost hairline at, which is what makes this read as that line
   continuing rather than as a second line near it. */
.shout__rules span:first-child{left:var(--gutter)}
.shout__rules span:last-child{right:var(--gutter)}

/* --- the rails: two continuous streams, not a set of cards ---------------- */
.shout__rails{position:absolute;inset:0;z-index:0;pointer-events:none}
.shout__rail{
  position:absolute;top:0;bottom:0;
  width:var(--rail-w);overflow:hidden;
  /* the stream enters and leaves rather than starting and stopping */
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 12%,#000 88%,transparent 100%);
          mask-image:linear-gradient(to bottom,transparent 0,#000 12%,#000 88%,transparent 100%);
}
.shout__rail--l{left:calc(var(--gutter) + var(--rail-pad))}
.shout__rail--r{right:calc(var(--gutter) + var(--rail-pad))}
.shout__stream{
  display:grid;gap:1px;                     /* one hairline, so it reads as one strip */
  transform:translate3d(0,var(--rail-l),0);
}
.shout__rail--r .shout__stream{transform:translate3d(0,var(--rail-r),0)}
.shout__stream img{
  /* height:auto, or the width/height attributes' presentational hint wins and
     every frame is 480px tall whatever its column actually measures */
  width:100%;height:auto;aspect-ratio:4/5;
  object-fit:cover;object-position:50% 38%;
  /* the floor, and what the section looks like with no script: fully grey.
     Script raises individual frames off it by distance from the centre line —
     it re-states the contrast, so the two treatments stay one filter and the
     value lives in one place per file. Deliberately NO transition: the amount
     is already a continuous function of position, and a transition on top of
     it would be a second, slower clock fighting the first. */
  filter:grayscale(1) contrast(1.06);
}

/* --- the core: eyebrow, headline, one button ------------------------------ */
.shout__core{
  position:relative;z-index:1;
  width:min(100%,var(--core-w));
  container-type:inline-size;               /* the headline is sized from this */
  display:grid;justify-items:center;text-align:center;gap:3.4svh;
  /* the trail's reserved band (see .shout__echo) is declared here so the
     button below it can measure the same constant and step back into it */
  --echo-depth:calc(var(--shout-type) * 1.5);
}
.shout .eyebrow{
  /* no colour of its own any more: on paper the primitive's own --ink-2 (§ 4)
     is the right voice, and stating a second one here is how a chapter becomes
     a second design instead of the other ink of the same one (§ 1a). */
  margin-bottom:0;
  opacity:calc(1 - var(--echo) * .55);      /* the supporting matter recedes */
}

/* The trail hangs BELOW the headline, so the echo block reserves a fixed band
   for it and clips there: the ghosts dissolve inside their own zone instead of
   travelling on into the button. The band is a constant, so nothing in the
   composition moves as --echo rises — only what is visible inside it changes. */
.shout__echo{
  position:relative;z-index:0;display:block;width:100%;
  padding-bottom:var(--echo-depth);
  -webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - var(--echo-depth)),
                     rgba(0,0,0,.55) calc(100% - var(--echo-depth) * .55),transparent 100%);
          mask-image:linear-gradient(to bottom,#000 calc(100% - var(--echo-depth)),
                     rgba(0,0,0,.55) calc(100% - var(--echo-depth) * .55),transparent 100%);
  overflow:hidden;
}
.shout__line{
  font-family:var(--font-display);font-weight:var(--wght-display);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display);
  font-size:var(--shout-type);
  line-height:1;                            /* M1 — leading is exactly 1.0 */
  text-transform:uppercase;letter-spacing:-.025em;
  color:var(--ink);
  overflow-wrap:break-word;                 /* a guard, for a fallback face only */
}
/* Each duplicate is the same headline, one --i further down the trail. Crisp:
   no blur, no split, no colour — the only things that change are how far it
   has travelled, how much of it is left, and a scale so slight it reads as
   distance rather than as a transform. */
.shout__ghost{
  --travel:calc(var(--echo-rest) + (1 - var(--echo-rest)) * var(--echo));
  position:absolute;top:0;left:0;right:0;
  transform-origin:50% 0;
  transform:translate3d(0,calc(var(--i) * var(--echo-step) * var(--travel)),0)
            scale(calc(1 - var(--i) * .014 * var(--echo)));
  opacity:calc(max(0,.46 - var(--i) * .072) * (.18 + .82 * var(--echo)));
  will-change:transform,opacity;
}

/* the button is part of the composition, not a control on top of it; it steps
   back while the trail is passing through it and returns at rest */
.shout__cta{
  position:relative;z-index:2;                /* in front of the echo trail */
  display:flex;align-items:center;justify-content:center;text-align:center;
  width:clamp(240px,calc(var(--core-w) * .62),520px);max-width:100%;
  min-height:max(48px,5.2svh);padding-inline:var(--s-3);
  background:var(--ink);color:var(--paper);
  font-size:clamp(var(--t-micro),calc(var(--shout-type) * .12),1.125rem);
  font-weight:700;text-transform:uppercase;letter-spacing:.08em;line-height:1.1;
  opacity:calc(1 - var(--echo) * .72);
  transition:background var(--d-fast) ease,color var(--d-fast) ease;
}
.shout__cta:hover{background:var(--gold)}

/* Narrow: the rails give up width first, because the headline is the thing
   that cannot get smaller without losing the section. */
/* Narrow: a phone has no periphery. Below 560px there is not enough width to
   hold a rail, a gutter of air and an oversized headline side by side — one of
   the three has to give, and it cannot be the headline (M8: the type is the
   section). So the composition changes rather than shrinks: the rails widen
   back to a size at which a haircut is actually legible, move BEHIND the type,
   and drop to a tone that keeps large ink type at ~5.8:1 over them. The
   headline takes the full measure and goes back to two clean lines. */
@media (max-width:560px){
  .shout{
    --rail-w:clamp(88px,30vw,150px);
    --rail-air:0px;
    --core-w:100%;
    --echo-step:.38em;
  }
  .shout__rails{opacity:.42}
  /* the rails now sit under the headline, so a band drawn around them would
     rule straight through the type. The section keeps the two gutter lines
     only, which is what the global grid draws at this width anyway. */
  .shout__rules span{width:auto;left:var(--gutter);right:var(--gutter);
    border-inline:0;border-left:1px solid var(--rule)}
  .shout__rules span:last-child{border-left:0;border-right:1px solid var(--rule)}
  .shout{--stage-pt:calc(var(--header-h) + 3svh);--stage-pb:14svh}
  .shout__core{gap:2.6svh}
}

/* Wide: the band is exactly one column of the global grid, so the hairline on
   the core side is not a new line invented for this section — it is the grid's
   own second rule, drawn where it always was. The rail then takes what the
   column leaves after its air. Below 900px the grid drops to two columns
   (§ 3) and a column is far too wide to be a rail, so the band stays derived
   from the rail there. */
@media (min-width:901px){
  .shout{
    --rail-band:var(--col);
    --rail-w:calc(var(--col) - 2 * var(--rail-pad));
  }
}

@media (prefers-reduced-motion:reduce){
  .shout__cta,.shout .eyebrow{opacity:1}
}

/* ==========================================================================
   24. THE CREED — the words, and the faces behind them
   story.html #words, driven by assets/js/trail.js

   THE IDEA    One belief, set large and centred on empty paper, with the
               people it is about arriving only where the reader's hand goes.
               The section is deliberately over-tall and otherwise empty: the
               air IS the runway the trail needs, and at rest it is what makes
               the statement read as a held moment rather than a paragraph.

   THE FRAMES  Real cuts from the portfolio — the faces that have actually sat
               in the chair, which is what the sentence under them is about.
               They sit in the markup rather than being built by script, so a
               render without JS is the statement alone and nothing is ever
               fetched twice.

   THE MOTION  Distance, not time. Script lays a frame down each time the
               pointer has travelled a fixed fraction of a frame's own width,
               so the spacing is a property of the geometry and reads the same
               however fast the hand moves. Each frame then lives exactly three
               token durations (design-system.md § 6): 500ms in on the
               transform token, 500ms held, 700ms out on the reveal token. The
               entrance resolves upward, like every other reveal on the site.

   IN FRONT    The frames pass in FRONT of the type, which is the whole effect.
   OF THE TYPE Nothing is covered for longer than a glance — a frame is gone
               inside 1.7s — and nothing is covered at all unless a reader is
               actively moving a pointer through the section.

   THE DECLINE no trail: under reduced motion, without a pointer that hovers
               and can be aimed, and without script. What is left is one
               centred statement on paper, which is the resting state anyway.
   ========================================================================== */

.creed{
  display:grid;place-items:center;
  min-height:88svh;
  overflow:hidden;                 /* a frame clips on the section edge, never over it */
  /* A frame is one column of the site's own grid (§ 3), so the trail is
     measured against lines the reader can already see rather than a width
     invented for it. The floor holds a face legible where a column narrows
     faster than a head does. */
  --frame-w:max(132px,var(--col));
}

/* four of the six columns: wide enough that the statement never sets as a
   ribbon, narrow enough that the trail always has periphery to cross. */
.creed__core{position:relative;z-index:1;text-align:center;
  max-width:min(100%,calc(var(--col) * 4));margin-inline:auto}

/* the site's section voice, held to two lines by the <br> rather than by a
   measure — the break is editorial (the claim, then the thing being claimed
   about) and has to survive every width. */
.creed__quote{margin-inline:auto}
.creed__mission{
  /* one step above body and below the quote: this is the claim, not a caption,
     so it takes --t-sub and full ink. It gives that up first on narrow
     screens, where 24px over 34ch is a wall rather than a statement. */
  font-size:clamp(1.125rem,2.4vw,var(--t-sub));
  line-height:1.35;color:var(--ink);
  max-width:34ch;margin:var(--s-6) auto 0;
}
/* the statement runs to three paragraphs: only the first is set off from the
   headline, the rest sit a paragraph apart so they read as one voice. */
.creed__mission + .creed__mission{margin-top:var(--s-4)}
/* Leandro's own wordmark, signing the creed. The supplied artwork is square
   with roughly a fifth of its height as transparent air top and bottom, so it
   is sized by height and centred rather than given a width — the optical mark
   is what has to measure right, not its canvas. Kept small: it signs the
   statement, it does not head the page. */
.creed__sign{
  display:block;width:auto;
  /* Sized off the wordmark inside it, not off the file. "LEANDRO HERRERA" is
     only ~5% of the artwork's height, so anything under about 180px turns the
     name into grey mush — measured in the browser, not guessed. */
  height:clamp(180px,22vw,260px);
  margin:var(--s-5) auto 0;
  /* No blend mode and no plate: the file is already transparent-backed, and
     multiply would flatten the light L of the monogram into the paper — it
     can only darken, and that letter is meant to sit lighter than its ground. */
}
.creed__core .micro{margin-top:var(--s-3)}

.creed__trail{position:absolute;inset:0;z-index:2;pointer-events:none}
/* One transform carries all three jobs: where the frame was laid, its own
   centre as the origin of that position, and the tilt. Script writes the three
   customs and nothing else; the sheet keeps the arithmetic. */
.creed__frame{
  position:absolute;top:0;left:0;width:var(--frame-w);
  transform:translate(var(--x,0),var(--y,0)) translate(-50%,-50%) rotate(var(--tilt,0deg));
}
/* the inner element carries the life, so the frame's placement transform is
   never something the animation has to preserve. Resting state is nothing at
   all — before the first pass, and after the last one has left. */
.creed__frame img{
  /* height:auto or the markup's own height attribute — a presentation hint the
     aspect-ratio cannot outrank — holds every frame at the source's 480px.
     Same reason .figure img (§ 5) carries it. */
  width:100%;height:auto;aspect-ratio:3/4;object-fit:cover;object-position:50% 28%;
  opacity:0;
}

/* A trail is pointer work; a touch screen has no periphery to sweep and its
   only pointermove belongs to the scroll. Hidden rather than merely idle, so
   ten lazy images are never fetched on the connection least able to spare it. */
@media (hover:none),(pointer:coarse){
  .creed__trail{display:none}
}
@media (prefers-reduced-motion:reduce){
  .creed__trail{display:none}
}
@media (max-width:900px){
  /* two columns wide (§ 3), so a column is no longer a frame-sized quantity
     and the core would be twice the page. Both go back to a plain measure. */
  .creed{--frame-w:clamp(132px,26vw,220px);min-height:0}
  .creed__core{max-width:34ch}
}


/* ==========================================================================
   25. EMPTY — the reference line went with book.html
   § 25 was THE IDEAL LOOK: a dark room holding a selector that asked a visitor
   to pick Leandro's own photographs, off Leandro's own site, and send them
   back to him. It was removed on 2026-09-12 — with § 25u, § 26 (the look
   sheet), § 21 (carry-and-drop) and § 19's wallet — and one sentence replaced
   it: .bring, the reference line at the head of book.html.
   On 2026-09-15 book.html itself was removed. It was an interstitial between a
   visitor and Fresha, and every booking CTA on the site now goes straight to
   the calendar. .bring had no second instance anywhere, so the rules went with
   the page; the sentence is not lost — services.html #detail cell 03 ("You
   bring the reference") says the same thing at more length, and it is the one
   place the claim is made now.
   The number is kept so § 26 and § 27 do not shift under every changelog that
   cites them.
   07-operations/decisions/2026-09-15-fresha-is-the-only-booking-destination.md
   07-operations/decisions/2026-09-12-remove-ideal-look.md
   ========================================================================== */


/* ==========================================================================
   27. WHAT YOU ARE PAYING FOR — services.html #detail
   The client's reference, supplied 2026-09-12: one oversized condensed
   headline in the left column, a 2x2 information grid in the right, fine
   architectural rules, and the whitespace left alone.

   WHAT WAS HERE WAS NOTHING. The markup called .process__grid and .step; the
   process reduction of 2026-09-12 took both out of this stylesheet and left
   services.html still asking for them. The four points had been setting as an
   undifferentiated stack down the left of an otherwise empty band ever since,
   which is the "content sitting on an empty page" the brief describes.

   IT STANDS ON THE PAGE'S OWN PAPER. The section carried .section--beige — the
   site's one tonal band — and an opaque panel BURIES § 3's fixed hairline
   layer (see § 5), so the six lines stopped dead at the top of this section
   and picked up again below it. On --paper they run straight through, which is
   what the hero at the top of this same page does, and the band is now told
   apart from its neighbours by its structure rather than by its tone.
   The three rules below keep --rule at full strength against a page layer
   drawn at .35 of it, so the composition's own architecture reads ~3x the
   background texture rather than dissolving into it (probe-paying, check 9).
   Nothing else in the section moved: this is a ground change, not a re-cut.

   THE SPLIT IS 2 | 4 OF THE SIX COLUMNS, and it is the one number in the
   reference that moved. The reference divides at ~45%. On this page that falls
   half a column off every hairline it draws: at 3 | 3 the 2x2's own seam lands
   at 4.5 of 6, so the rule sits where § 5a forbids structure to sit AND the two
   cells of type either side of it stop being one --inset past a column rule —
   which is the pair of claims dev/probe-grid-inset.mjs measures on this page at
   four widths. 2 | 4 is the nearest split that keeps all three rules ON
   hairlines (0-2 and 4) and all four cells of type one inset past one. The
   right grid gains the width the left gives up, which the copy wanted anyway:
   03 is two sentences long and had a half-column to say them in.

   THE HEADLINE IS RE-CLAMPED, NOT RE-SIZED. Two columns is ~423px at 1440 and
   --t-section's 100px sets "WHAT YOU ARE" at 536. The SLOPE is re-cut so the
   headline fills its own column instead of the viewport, keeping --t-section's
   own floor and its own cap at either end — the same move § 13f already makes
   for .tl__head .section-title, for the same reason, at nearly the same rate
   (5.2vw there, 5.15vw here).

   THE NUMERALS ARE THE SERIF, at the size § 13f sets a figure at (.ev__num).
   The reference sets them in a light sans, which here would mean a fifth
   weight on Archivo's axis: design-system.md § 2 admits 900 / 700 / 400 and
   300-for-the-connective, and says a section may state no wdth or wght literal
   of its own. Instrument Serif is the site's existing answer to "a figure, as
   the subject", it is already the voice of the eyebrow two columns to the
   left, and set at 60px against a 900 condensed headline it reads lighter than
   any sans at 400 would. One family change, no new weight.
   ========================================================================== */
.pay{
  --pay-air:clamp(2rem,3.6vw,4rem);      /* the air a crossed rule needs       */
  --pay-num:clamp(2.75rem,4.4vw,4.25rem);/* § 13f's figure, at marker size     */
}
.pay__field{display:grid;grid-template-columns:repeat(var(--grid-cols),1fr)}

/* --- the editorial column ------------------------------------------------
   Not centred and not evenly spaced: the eyebrow and the headline sit at the
   top, the connective under them, and the sign-off is pushed to the foot by
   the grid beside it — which is the taller child and therefore the one that
   sets the row. That asymmetry is the composition; a justify-content would
   distribute it away. */
.pay__lede{grid-column:span 2;display:flex;flex-direction:column;
  align-items:flex-start;padding-inline:var(--inset)}
.pay__title{font-size:clamp(2.125rem,5.15vw,6.25rem)}
.pay__lead{margin-top:var(--s-5)}
.pay__sign{margin-top:auto;padding-top:var(--s-7);line-height:1.7}
/* A MARK, NOT A RULE. § 4a gives a chapter one structural hairline and this
   chapter has already spent three on the grid, so the sign-off's dash is the
   glyph rather than a fourth line — and it is out of the accessibility tree,
   because an em dash read aloud before a two-line sign-off is noise. */
.pay__dash{display:block;font-style:normal;margin-bottom:.75em;color:var(--ink-3)}

/* --- the 2x2 -------------------------------------------------------------- */
.pay__grid{grid-column:span 4;position:relative;
  display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr}
/* two equal rows, so the seam that crosses them is at exactly 50% and lands
   with the rules layer below rather than wherever 03's extra sentence put it */
.pay__cell{padding:var(--pay-top) var(--inset) var(--pay-bot);
  --pay-top:var(--pay-air);--pay-bot:var(--pay-air)}
/* :where() so the two row rules cost no specificity and the breakpoints below
   can restate the padding on the plain class */
.pay__cell:where(:nth-child(-n+2)){--pay-top:0px}
.pay__cell:where(:nth-child(n+3)){--pay-bot:0px}

.pay__n{
  display:block;
  font-family:var(--font-serif);
  font-size:var(--pay-num);
  line-height:.86;
  letter-spacing:.005em;
  color:var(--ink-1);
  /* lining, tabular — four markers read down a column, and a proportional 1
     would sit 01 and 04 on visibly different centres (§ 13f's reason) */
  font-variant-numeric:lining-nums tabular-nums;
  /* the marker settles a beat behind the words it numbers. It is inside the
     cell's own .reveal, so this is a second opacity on top of that one and not
     a second gesture: the number arrives, the fade does the arriving. */
  opacity:0;transition:opacity var(--d-reveal) var(--ease-out) 120ms;
}
.pay__cell.is-in .pay__n{opacity:1}
.pay__h{margin-top:var(--s-4)}
/* the reference's measure, not the cell's: a cell is ~423px at 1440 and 14px
   copy run to the rule would be 55 characters. Held at the reference's ~36, the
   trailing air inside each cell is the breathing room rather than an accident */
.pay__p{margin-top:var(--s-3);font-size:var(--t-micro);line-height:1.6;
  color:var(--ink-2);max-width:36ch}

/* --- the rules -----------------------------------------------------------
   THREE RULES, ONE OBJECT, AND NOT ON THE CELLS. A border on a .reveal cell
   rides that cell's 24px rise, so the structure would arrive sliding; on a
   layer of its own it holds still and fades in 240ms behind the words — the
   last step of the stagger ladder (§ 14), so the grid finishes drawing itself
   as the fourth cell lands. The vertical is the block's own left edge
   (hairline 2) and ::before is its midpoint (hairline 4); ::after is the only
   one of the three that is not a column rule, because it crosses them. */
.pay__rules{position:absolute;inset:0;pointer-events:none;
  border-left:1px solid var(--rule);
  opacity:0;transition:opacity var(--d-reveal) var(--ease-out) 240ms}
.pay__grid.is-in .pay__rules{opacity:1}
.pay__rules::before,.pay__rules::after{content:"";position:absolute;background:var(--rule)}
.pay__rules::before{left:50%;top:0;bottom:0;width:1px}
.pay__rules::after{left:0;right:0;top:50%;height:1px}

/* --- below the lap breakpoint --------------------------------------------
   The page grid is two columns (§ 3), so a span of two or four has nothing to
   mean and the two halves stack. The 2x2 survives — at 900px a cell is still
   ~350px of copy — and so does the seam crossing it; what goes is the rule
   down the left, which was the seam between two columns and now has no second
   column to be between. The headline returns to the site's own ramp: it has
   the full width here and no longer needs a slope cut for a third of one. */
@media (max-width:900px){
  .pay__lede,.pay__grid{grid-column:1 / -1}
  .pay__lede{padding-bottom:var(--s-6)}
  .pay__title{font-size:var(--t-section)}
  .pay__sign{padding-top:var(--s-6)}
  .pay__rules{border-left:0}
}

/* --- phone ----------------------------------------------------------------
   One vertical sequence. There is no second column to give, so the 2x2 becomes
   four items and the seam becomes the hairline between them — the numerals and
   the rules are the only structure left, which is the structure the reference
   keeps at this width. Not cards: no ground, no corner, no shadow, and the
   hairline runs the full width of the field rather than around anything. */
@media (max-width:640px){
  .pay__grid{grid-template-columns:1fr;grid-template-rows:none}
  .pay__rules{display:none}
  .pay__cell{--pay-top:var(--s-5);--pay-bot:var(--s-5)}
  .pay__cell:where(:not(:first-child)){border-top:1px solid var(--rule)}
  .pay__cell:where(:first-child){--pay-top:0px}
  .pay__cell:where(:last-child){--pay-bot:0px}
  /* the titles' breaks were cut for a half-column; at full width they read as
     one line each. The space before each <br> in the markup is what survives
     it being taken out — display:none on a <br> removes the break AND the word
     gap, which set "The consultationis free". */
  .pay__br{display:none}
  .pay__p{font-size:var(--t-body);max-width:none}
}
