/* ---------------------------------------------------------------------------
   The landing.

   A second stylesheet and not three thousand more lines in the first one. The
   sheet's own stylesheet is about ONE material — a one-bit ribbon and the
   chrome that has to stay legible over it — and this is a page: a column of
   type, a set of full-bleed breaks, and a scroll model none of the rest of the
   site has. Two subjects, two files, and the tokens are shared because they are
   declared on `:root` over there.

   THREE numbers drive everything below and all three are written by
   `home/section.js` once a frame:

     `--flow`   the page's signed scroll velocity, -1..1. At rest it is 0 and
                every expression that reads it collapses to nothing — which is
                why there is no `is-moving` class anywhere. The deformation IS
                the velocity.
     `--py`     per element: how far it has travelled off its own parallax
                factor. On `.px` only, and it is combined with `--ry` in ONE
                transform so the two never fight over the property.
     `--cap`    per ink block: the height of the arc its top and bottom edges
                are bowed by. The paper→ink boundary is the only straight line
                left on this page, so it is the one that gets bent.

   `--ry` is the entrance offset. It is a REGISTERED property (see `@property`
   below) because a custom property is a token, not a length, and an
   unregistered one cannot be interpolated — the entrance would be a jump.
--------------------------------------------------------------------------- */

@property --ry {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

/* The same argument one axis over. `--rx` is the entrance offset for the
   things that arrive from the side rather than from below — the foot's links,
   the rules under the questions — and it has to be registered for exactly the
   reason `--ry` does. */
@property --rx {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

/* --- THE DECK OF QUESTIONS, AS ONE NUMBER ----------------------------------
   `--fk` is which card of the FAQ is on screen, and it is registered so that
   the browser will INTERPOLATE it. That is the whole engine of that block: the
   track's travel, every card's lag and ink, and the carriage on the rail are
   all read off this one number on the same frame, so there is no second
   animation to fall out of step with the first. `inherits: true`, because the
   cards have to read a number written on the deck above them.

   `--fdx` is the thumb, in px, on the track only: the drag offset that rides
   on top of `--fk` while a finger owns the position, and springs back to zero
   when it lets go. Registered so that spring back is a transition rather than a
   jump, and `inherits: false` because a card must never inherit its deck's
   drag.

   PREFIXED, and the prefix is not decoration. `--k` is taken: the services
   column writes it per card as that card's own scale, and `styles.css` spends
   it on padding, gap and font size with `var(--k, 1)`. Registering a bare
   `--k` here would give it an initial value of 0 for the whole document — the
   fallback in those three rules would stop firing and that column would resolve
   to no padding, no gap and no type. `@property` is global, and this file is
   not the only one in the room. */
@property --fk {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

@property --fdx {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

/* --- the layer -------------------------------------------------------------
   Same shape the services column has, for the same reasons: a full-bleed ground
   of its own at `z-index: 5`, faded rather than displayed, and `inert` while it
   is not the thing on screen. */
.home {
  position: fixed;
  inset: 0;
  z-index: 5;
  background: var(--paper);
  color: var(--ink);
  opacity: 0;
  visibility: hidden;
  overflow: hidden;
  /* The layer owns every gesture. `none` and not `pan-y`: the page inside it is
     a transform, so letting the browser scroll anything here would scroll the
     document behind the sheet instead. */
  touch-action: none;
  overscroll-behavior: contain;
  transition:
    opacity 0.5s var(--ease-out),
    visibility 0s linear 0.5s;

  --flow: 0;
  --flow-abs: 0;
  /* The fallback until the layer has measured itself. `--dvh` is the window
     as it is RIGHT NOW, written onto <html> by `core/device.js` on the frame
     the address bar moves; `100vh` is the window as it would be with the
     browser chrome collapsed, which on a phone is a hundred-odd points taller
     than the layer actually is. Two frames of a page laid out against the wrong
     height is one visible jump on load. */
  --vh: var(--dvh, 100vh);

  /* The type scale of the landing, and it is not the sheet's. The chrome is set
     at the size of a label; this is a page that has to be read from a metre
     away and then from thirty centimetres. */
  --t-display: clamp(2.8rem, 7.4vw, 8.2rem);
  --t-title: clamp(1.9rem, 4.4vw, 4.1rem);
  --t-lead: clamp(1.05rem, 1.5vw, 1.55rem);
  --t-body: clamp(0.95rem, 1.05vw, 1.15rem);
  --col: min(1360px, 100% - var(--pad) * 2);

  /* How far a word is thrown by the arch, at full velocity and at the edge of
     its line. In em, so a headline the size of a building bends by the same
     proportion a caption does. */
  --arch: 0.34em;
}

body.is-home .home {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

/* One transform on one element. Everything on the page is inside it. */
.home__track {
  position: relative;
  width: 100%;
  will-change: transform;
  backface-visibility: hidden;
}

/* Where you are, down the right edge. The services column's rail, stood in the
   one section that is long enough to need it. */
.home__rail {
  position: absolute;
  top: 22vh;
  bottom: 22vh;
  right: calc(var(--pad-edge) * 0.7);
  width: var(--hair);
  background: currentColor;
  opacity: var(--rule-o);
  z-index: 4;
  pointer-events: none;
  /* It rode `difference` in white, which is what everything over the ribbon
     does — it had to survive a page that changed ground seven times on the way
     down. One ground now, so it is simply printed in the ink like every other
     hairline on the page. Difference over a full-bleed photograph is a coin
     toss, and two of the blocks this rail crosses are photographs. */
  color: var(--ink);
}

.home__rail > i {
  position: absolute;
  inset: 0;
  background: currentColor;
  transform-origin: 50% 0;
  transform: scaleY(0);
  opacity: 0.9;
}

/* --- parallax and entrance, in ONE transform -------------------------------
   `--py` is written by the frame loop, `--ry` by a class. Two properties, one
   `transform`, because two rules both setting `transform` on one element is the
   oldest bug in this kind of page: whichever cascades last wins outright and
   the other one silently does nothing. */
.px,
.rv {
  transform: translate3d(0, calc(var(--py, 0px) + var(--ry, 0px)), 0);
}

.rv {
  opacity: 0;
  --ry: 22px;
  transition:
    opacity 0.85s var(--ease-out),
    --ry 0.85s var(--ease-out);
}

.hb.is-in .rv {
  opacity: 1;
  --ry: 0px;
}

/* --- a block ---------------------------------------------------------------
   Two kinds and one rule between them — see the note at the head of
   `blocks.js`. A break is full bleed and is the only thing on screen; a clean
   block is a column of type. */
.hb {
  position: relative;
  width: 100%;
  /* The ground this block stands on, and the tone it prints in.

     The page has ONE ground and the tokens exist anyway, because a block can
     stand on something that is not it — a photograph, which has no polarity to
     follow (`.hb--reel`). Everything below that has an opinion about the ground
     reads these two rather than `--paper` / `--ink` directly, so a button that
     fills with its own foreground on hover is written once and is correct
     wherever it lands. */
  --bg: var(--paper);
  --fg: var(--ink);
}

/* --- the foot of the page --------------------------------------------------
   The gem, the close and the foot carry an OPAQUE ground, and it is the
   sheet's own.

   These three used to be literally black with literally white type, whichever
   way the dial was pointing, on the argument that a page should end the same
   way from both states of a setting. It reads as an argument and it was a bug:
   press the dial to the pale sheet and the landing ran white for two thirds of
   its length and then fell off a cliff into three screens of black — the exact
   full-screen crossing `:root` in styles.css exists to have removed, kept alive
   by three declarations at the bottom of one file. And it was not only this
   page: the reader who chose a white site got a white Work, a white Services, a
   white About, and a black ending to the one section that has an ending.

   One sheet means ONE sheet. The tokens below are the page's, the shaders
   inside these blocks are handed the same pair by `section.js`, and the ending
   is now made by what it always should have been made by — the gem assembling,
   the ring turning, the foot closing — rather than by the lights going out.

   The class stays, and it still means something: THIS BLOCK PAINTS ITS OWN
   OPAQUE GROUND. A break holding a shader has to, or the track shows through
   it. `#mount` in `section.js` no longer branches on it, because there is no
   longer a second pair for it to choose. */
.hb.is-ink {
  --bg: var(--paper);
  --fg: var(--ink);
  background: var(--bg);
  color: var(--fg);
}

.hb.is-break {
  min-height: var(--vh);
  display: grid;
  align-items: center;
  overflow: hidden;
}

.hb.is-clean {
  padding: clamp(90px, 15vh, 200px) 0;
}

/* --- ONE GROUND, and the seam between two blocks ----------------------------
   There were two grounds here and seven crossings between them, domed with
   `border-radius / var(--cap)` so the ink rose into the paper instead of
   cutting. The crossings are gone — the sheet is one tone now, and the reason
   is written at `:root` in styles.css — so what is left to draw is not an edge
   between two colours but the JOIN between two pressures.

   One hairline, full bleed, on the top edge of every block but the first.

   And it is not a static rule. `--seam` is written per block per frame by
   `section.js` out of the same two numbers `--cap` took — how close the seam is
   to the fold, and how fast the page is going — and it drives two things:

     the REACH: the rule opens from the middle of the screen outwards, so a
                boundary arrives as a line being drawn rather than as a line
                that was already there.
     the INK:   a THIRD of the sheet's hairline at rest, the whole of it at
                full arrival. A boundary you can find when you look for it and
                cannot see when you are reading. It used to be 6% and 22%
                written here; those two numbers were the page's heaviest rule
                and the page's second-lightest, both invented in this block.
                The reach is still this seam's own gesture — what it opens TO
                is `--rule-o`, like everything else drawn with a hairline.

   Costs no layout: `opacity` and `transform` on a pseudo-element, both
   paint/composite only, sixty times a second, on a page whose track is one
   transform. `--cap` could not say that — a border-radius that big forces the
   block to re-clip its children, which on a block holding a live canvas is the
   one thing that showed up in a profile. */
.hb {
  position: relative;
  z-index: 0;
}

.hb::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--hair);
  background: currentColor;
  opacity: calc(var(--rule-o) * (0.32 + 0.68 * var(--seam, 0)));
  transform: scaleX(calc(0.28 + 0.72 * var(--seam, 0)));
  transform-origin: 50% 50%;
  pointer-events: none;
  z-index: 3;
}

/* The head of the page is not a join. */
.hb:first-child::before { display: none; }

/* Nor is the inside of a full-bleed break that is holding a shader: a hairline
   across the top of a live canvas is a scratch on the lens. Breaks keep the
   seam only where they meet a column of type. */
.hb.is-break + .hb.is-break::before { opacity: calc(var(--rule-o) * (0.16 + 0.3 * var(--seam, 0))); }

/* A pinned block is tall and its child stands still inside it.

   `display: block`, and it is a FIX rather than a preference: `.is-break` is a
   grid that centres its child, and a pin whose child is centred inside a
   four-viewport block starts one and a half viewports down from where the
   arithmetic in `section.js` believes it is. The reel came up cut in half and
   the deck came up below the fold. A pinned block positions its own child; it
   must not also be a grid. */
/* `.hb.hb--pin` and not `.hb--pin`: the rule it has to beat is `.hb.is-break`,
   which is two classes, and a single-class rule loses to it outright however
   far down the file it sits. Written once with the wrong weight, the reel was
   still centred inside four viewports — its pin started 2239px down and the
   whole strip opened below the fold. Specificity is not a style question here;
   it is the difference between the block working and not. */
.hb.hb--pin {
  display: block;
}

.hb__pin {
  position: relative;
  height: var(--vh);
  overflow: hidden;
  will-change: transform;
}

/* Where a heavy component is mounted, when the budget allows. Never
   interactive: a landing where the wheel does something different depending on
   which shader it is over is a landing the reader cannot use. */
.hb__stage {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.hb__stage--behind {
  z-index: 0;
}

.hb__stage > * {
  display: block;
  width: 100%;
  height: 100%;
}

/* --- display type, and the arch --------------------------------------------
   `--u` is written per word by `section.js`: the word's centre as a fraction of
   its line. Squared about the middle it becomes `--bow` — negative in the
   centre of the line, positive at both ends — and the word is offset by that
   times the page's velocity. So the middle of a line lags its ends and a
   straight baseline becomes an arc, which is exactly what the reel's fragment
   shader does to its sampling coordinate and exactly what `--cap` does to the
   edge between the two grounds. One deformation, three implementations.

   The clip for the entrance is on the WORD and not on the line, because the
   line would clip the arch as well and the bend would be sheared off at the
   very moment it is largest. */
.hx {
  margin: 0;
  font-weight: 700;
  line-height: 0.94;
  letter-spacing: -0.035em;
  text-wrap: balance;
}

.hx__line {
  display: block;
}

.hx__w {
  display: inline-block;
  overflow: hidden;
  /* Descenders. An inline-block clipped to its own box eats the tail of a `y`.

     0.18em was measured against `.hx`'s own 0.94 line, and the room's headline
     sets the same words at 0.86 — a tighter line is a shorter box under the
     same baseline, so the tail of `playing.` came back three pixels short of
     clearing it. 0.26em is the tightest line on the page with the margin kept.
     Cancelled by the negative margin, so nothing below moves. */
  padding-bottom: 0.26em;
  margin-bottom: -0.26em;
  vertical-align: bottom;
  --d: calc(var(--u, 0.5) * 2 - 1);
  --bow: calc(var(--d) * var(--d) * 4 - 1.1);
  transform: translate3d(0, calc(var(--flow) * var(--bow) * var(--arch)), 0);
}

.hx__w > i {
  display: block;
  font-style: inherit;
  transform: translateY(112%);
  transition: transform 1.05s var(--ease-out);
  transition-delay: calc(var(--wi, 0) * 42ms);
}

.hb.is-in .hx__w > i {
  transform: translateY(0);
}

/* --- the column and its furniture ------------------------------------------ */

.hb__col {
  width: var(--col);
  margin: 0 auto;
  position: relative;
  z-index: 2;
}

/* THE LABEL AT THE HEAD OF A BLOCK — and nothing on THIS page draws one now.

   The landing had eight: "02 — The material", "08 — Questions", "10 — The end".
   All eight are deleted, builder and copy, and the reasoning is at the head of
   `home/content.js`.

   These four rules stay because ABOUT draws four of them, out of its own maker
   in `about/blocks.js`, and this file is where the class has always been
   declared. That page is one argument in four numbered movements — the number
   is the structure of the thing rather than a filing label stuck on the front
   of it — which is the distinction that decided which set went.

   Deleting the class along with the landing's calls would take About's head
   labels with it, silently, from a file About does not open. */
.hb__kicker {
  display: flex;
  align-items: center;
  gap: 0.9em;
  margin: 0 0 clamp(28px, 4vh, 56px);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0;
  --ry: 10px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.7s var(--ease-out),
    --ry 0.7s var(--ease-out);
}

.hb.is-in .hb__kicker {
  opacity: 0.62;
  --ry: 0px;
}

.hb__kicker-rule {
  display: block;
  width: clamp(28px, 4vw, 64px);
  height: var(--hair);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.9s var(--ease-out) 0.1s;
}

.hb.is-in .hb__kicker-rule {
  transform: scaleX(1);
}

.hb__body {
  max-width: 62ch;
  margin-top: clamp(24px, 3.4vh, 44px);
  font-size: var(--t-body);
  line-height: 1.6;
  opacity: 0;
  --ry: 16px;
  transition:
    opacity 0.85s var(--ease-out) 0.2s,
    --ry 0.85s var(--ease-out) 0.2s;
}

.hb__body p {
  margin: 0 0 1em;
}

.hb__body--two {
  max-width: 84ch;
  columns: 2;
  column-gap: clamp(28px, 4vw, 70px);
}

.hb.is-in .hb__body {
  opacity: 0.78;
  --ry: 0px;
}

/* --- the way on ------------------------------------------------------------
   One control shape for every "take me to that section" in the landing. */
.hb__cta {
  appearance: none;
  -webkit-appearance: none;
  margin-top: clamp(30px, 4.6vh, 60px);
  padding: 0 0 0.5em;
  border: 0;
  border-bottom: var(--hair) solid currentColor;
  background: none;
  color: inherit;
  font: inherit;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
  position: relative;
  opacity: 0;
  --ry: 12px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.7s var(--ease-out) 0.3s,
    --ry 0.7s var(--ease-out) 0.3s;
}

.hb.is-in .hb__cta {
  opacity: 1;
  --ry: 0px;
}

.hb__cta::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.5s var(--ease-out);
}

.hb__cta:hover::after,
.hb__cta:focus-visible::after {
  transform: scaleX(1);
}

.hb__cta:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 6px;
}

/* --- the five presses ------------------------------------------------------
   `hb__cta` above is the hairline-under-a-word control, and it is the right
   shape for navigation that belongs to a paragraph — a service row, a link in
   the foot. It was ALSO the shape of the five presses this page exists to
   receive, which meant the landing's calls to action looked exactly like the
   landing. A page where the thing it is FOR is drawn like the thing it is made
   of has no call to action at all, only more page.

   So those five — see the work, what we do, open the ribbon, open the column,
   open the wall, start a project — are `vs-button-chrome`: a transparent box
   whose entire body is a liquid-metal rim, filling with the theme's own ink on
   hover. Nothing else on this landing is, and that is what makes them read as
   the exits. The full argument is beside `chromeCta` in `blocks.js`.

   The row carries the block's entrance so the buttons themselves never have to
   know about `is-in`. */
.hb__act {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(10px, 1.2vw, 18px);
  /* Only as wide as the presses in it. A block-level flex row stretches to its
     column, and on the two pinned blocks that row is `pointer-events: auto`
     over a full-bleed canvas — an invisible strip across the whole screen that
     takes the pointer for a button sitting at one end of it. */
  width: fit-content;
  max-width: 100%;
  margin-top: clamp(26px, 4vh, 52px);
  opacity: 0;
  --ry: 14px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.8s var(--ease-out) 0.34s,
    --ry 0.8s var(--ease-out) 0.34s;
}

.hb.is-in .hb__act {
  opacity: 1;
  --ry: 0px;
}

/* The control, wired to the BLOCK's ground rather than to the sheet's.

   The plate it floods with is the block's foreground and the label on it the
   block's background, so one declaration is a white pill with black type on
   the ink blocks at the foot and a black pill with white type everywhere
   above — and it stays correct when the dial turns the whole sheet over. The
   component reads `--chrome-fill` / `--chrome-fill-ink` for exactly this. */
.xcta {
  --ctrl-h-lg: clamp(46px, 4.3vw, 56px);
  --ctrl-h-md: clamp(38px, 3.4vw, 46px);
  --ctrl-fs-lg: var(--t-micro);
  --ctrl-fs-md: var(--t-micro);
  --ctrl-px-lg: clamp(24px, 2.6vw, 38px);
  --ctrl-px-md: clamp(18px, 2vw, 28px);
  --chrome-fill: var(--fg);
  --chrome-fill-ink: var(--bg);
  /* The label at rest, and the focus ring. Both default to a near-white inside
     the component, which is invisible on a paper sheet. */
  --inp-text: var(--fg);
  --inp-accent: var(--fg);
  font-family: inherit;
  /* The rim is the border, and a rim on a moving page needs the same rigid box
     every other control here has. */
  flex: none;
}

/* The label, reached through the component's own `part`.

   Set on the host these two do nothing, and it is not a shadow-DOM boundary
   problem — `letter-spacing` and `text-transform` are inherited properties and
   they cross into a shadow root perfectly well. The control inside is a
   `<button>`, and the UA stylesheet declares `letter-spacing: normal` and
   `text-transform: none` on form elements, which beats inheritance outright.
   `::part` is an author rule on the element itself, so it wins.

   Every other press on this landing is set at caption size in caps with wide
   tracking. These are the five that matter most; they do not get to be the
   ones set in sentence case. */
.xcta::part(label) {
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* --- the first screen's furniture, AND THERE IS NONE LEFT -------------------
   `.hero__stamp` stood here first: the studio's filing — name, year, where it
   works — set in tracked-out micro-copy one gutter above the run's own kicker.
   It said what the chrome across the top of the page says, forty pixels lower.

   `.hero__cue` stood here second, and it has gone the same way: a hairline with
   a segment running down it and the word SCROLL, in the bottom right of the
   first screen. It was the last object standing in the open middle of the
   photograph, and it was the FOURTH thing on that screen telling a reader the
   same fact — the run's own tally counts six panels, the block's fraction reads
   `01 / 06`, and the strip cuts under the reader's hand the moment they move.
   An instruction the page has already demonstrated three times is not an
   instruction; it is a caption on a door that is already open.

   Deleted in the markup (`scrollCue` in `home/blocks.js`, and the append in
   `#build`), in the copy, and here — the block, the `.hb.is-first` switch, the
   `.home.is-moved` stand-down, the rail, the run and the `home-cue` keyframes.
   `--reel-runrule` outlived it: the run's own foot row is measured to that line
   now. See `.reel__row--b`. */

/* --- in motion: THE PROJECTION ROOM ----------------------------------------
   A dark room, three and a half viewports deep, with one screen standing in
   the middle of it and twelve pieces of the studio's own work threaded through
   that screen by the reader's own travel down the page.

   Everything below is the drawing of it. `home/showreel.js` owns the threading
   and `vs-anim-liquid-cut.js` owns the melt; this file owns the room.

   --- WHY IT IS A PIN, and it is the same argument the run at the top makes --
   The block used to be one screen the page went past, with a carousel in it
   running off a timer. Everything about that was competently made and none of
   it was the PAGE: the reader arrived, and then waited while a machine that
   had nothing to do with them dealt twelve pictures at its own pace.

   It is three and a half screens of block with one screen of room standing
   still inside it now. The reader's travel is the shuttle — see the head of
   `showreel.js` for the station arithmetic — so the block cannot be waited
   through and cannot be missed: every piece of work in it is a piece of the
   scroll. That is the shape the run at the top of this landing already had,
   and having it at both ends is what makes the page one object instead of a
   hero and a list of blocks.

   --- THE ROOM IS DARK ON BOTH SHEETS, and this is the second exception -----
   `:root` in `styles.css` states the rule this breaks and states it as a rule:
   one ground, top to bottom, the dial turns the whole of it over, and NOTHING
   is allowed to disagree. There has been exactly one documented exception —
   `.hb--reel`, which stands on a PHOTOGRAPH, and a photograph has no polarity
   to follow.

   This is the second and it is the same exception, one step further along.
   What the type in here stands on is not a picture, it is PROJECTED LIGHT: a
   screen throwing a beam into a volume of dust. Light has no polarity either,
   and it has something stronger — it only exists against dark. A projection
   room with the lights on is not a projection room, it is a monitor on a desk,
   and the entire block would be a rectangle of video with a caption under it.

   Two exceptions, both of them "a block that stands on something that is not
   the sheet", both written down where the declaration is. It is not a licence:
   a block that wants its own ground has to be able to finish that sentence.

   --- THE GEOMETRY IS SIX NUMBERS, DECLARED ONCE ---------------------------
   The screen's rectangle is spent in eleven places — four hairlines, the
   frame, the aperture, the two crop corners, the caption's baseline, and BOTH
   halves of the headline that crosses it. Written out eleven times it would be
   wrong within a week, and the failure would be a letter torn in half a
   pixel off the edge of the picture, which is the one artefact anybody would
   see. So it is `--mo-h`, `--mo-w`, `--mo-cy` and the three insets derived
   from them, and nothing in this block measures the screen any other way.

   `var(--vh)` and not `vh`: the pin's height is the LAYER's, written per
   resize by `section.js`, and on a phone with a collapsing address bar the
   window and that layer differ by enough to hang the caption on the picture.
   Same reason a pinned block's own height is written in px. */

.hb--motion {
  /* THE PAIR, pinned. See the exception note above. `#mount` reads these back
     off the block rather than being told, so the dust and the melt are painted
     for the room and not for the sheet — the mechanism is already there and it
     is the same one `.hb--reel` uses. */
  --fg: #ffffff;
  --bg: #07070a;
  color: var(--fg);

  /* THE THRESHOLD, and it is the price of the exception above.

     A block that pins its own dark pair is a hard rectangle of black cut into
     the sheet, and on the PALE sheet that is exactly the full-screen crossing
     `:root` in `styles.css` spent a whole revision removing — the eye
     re-adapts at the edge and the block reads as a different website for three
     and a half screens.

     So the room does not start at its own top edge. It is entered: a hand's
     width of SHADOW at each end, falling into the ground. That is what a room
     is — you cross a threshold to be inside one.

     `--mo-edge` and NOT `--paper`, and that is the one place this block
     refuses to track the sheet. The threshold used to be drawn out of the
     page's own ground, which on the pale sheet meant the two ends bled up into
     WHITE — a lit doorway at each end of a dark room, and the brightest thing
     in the block was the part of it that is supposed to be nothing. A room is
     entered out of the dark in both polarities, so the ends fall to black on
     either sheet: on the dark one it is the same soft deepening it always was,
     on the pale one it is now a shadow instead of a window.

     Two backgrounds and a colour under them rather than three elements: the
     block's own `::before` is the page's seam hairline and its pin's `::after`
     is the room's vignette, and a third layer for a gradient that composites
     perfectly well as a background image would be a third thing to keep in
     step with the first two. */
  --mo-edge: #000000;
  background-color: var(--bg);
  background-image:
    linear-gradient(to bottom, var(--mo-edge) 0%, transparent clamp(70px, 11vh, 190px)),
    linear-gradient(to top, var(--mo-edge) 0%, transparent clamp(70px, 11vh, 190px));

  /* The screen. Its HEIGHT is the free number and the width follows it at the
     clips' own four to three, so the twelve studies are shown whole — the
     correction the old strip made and the one thing about it that was right.
     Bounded on the viewport's width as well as its height, because a short
     wide screen would otherwise put the frame through both side rules. */
  --mo-h: clamp(200px, min(calc(var(--vh) * 0.56), 46vw), 640px);
  --mo-w: calc(var(--mo-h) * 4 / 3);
  /* A hair above the middle. The room is symmetric about its own vertical
     axis and NOT about its horizontal one: there is a label rule at the head
     and a caption plus a transport at the foot, so the mass below the screen
     is greater and the screen has to rise to meet it. Dead centre reads as
     sunk. */
  --mo-cy: calc(var(--vh) * 0.472);
  --mo-t: calc(var(--mo-cy) - var(--mo-h) / 2);
  --mo-b: calc(var(--vh) - var(--mo-cy) - var(--mo-h) / 2);
  --mo-x: calc(50% - var(--mo-w) / 2);
  /* Where the head rule and the foot rule sit, in from the room's own edges,
     and the air between a rule and the type that belongs to it. The same
     number top and bottom, for the reason the run's grid gives: a rule
     touching a line of type is an underline, a rule with air on both sides is
     a boundary, and these are boundaries. */
  --mo-band: clamp(58px, 9vh, 116px);
  --mo-air: clamp(12px, 1.9vh, 26px);
  /* THE BOX'S PADDING, and it is a flat twenty pixels on all four sides.

     The screen's own four rules used to be drawn one hair OUTSIDE the picture,
     which is a line beside a photograph — near enough to read as its border,
     far enough to read as a mis-registration. Twenty pixels is a MARGIN: the
     picture is standing inside a box rather than wearing one, which is what
     every other rule in this room is doing to what it encloses.

     Flat and not a clamp, because it is the one number in the block that is
     about the eye's distance from an edge rather than about the size of the
     room. A padding that grew with the viewport would be a hand's width on a
     wall and a hair on a laptop, and the relationship it draws — line, air,
     picture — would only be legible at one of them. */
  --mo-gap: 20px;

  /* Written per frame by the strip: how far through the piece's own station
     the reader is, and how far into the melt out of it. Declared here so every
     rule below has a value on the first frame, before anything has run. */
  --rs-p: 0;
  --rs-melt: 0;
}

/* THE ROOM'S VIGNETTE, and it is inside the pin rather than on the block.

   On the block it would be three and a half viewports of gradient with one
   viewport of room sliding through it, so the falloff would travel while the
   composition stood still — which is the exact artefact a pin exists to
   prevent. It closes the four corners down into the ground so the beam has
   somewhere to fall off to, and it is `--bg` rather than black because the
   room declares its own pair one rule up and nothing here should restate it. */
.hb--motion .hb__pin::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(
    132% 96% at 50% 46%,
    transparent 0%,
    transparent 16%,
    color-mix(in srgb, var(--bg) 30%, transparent) 40%,
    color-mix(in srgb, var(--bg) 66%, transparent) 62%,
    color-mix(in srgb, var(--bg) 90%, transparent) 78%,
    var(--bg) 92%
  );
}

/* QUIETER THAN IT HAS EVER BEEN, and that is the correction the room asked
   for the moment it existed.

   The instinct on a dark ground is to turn the atmosphere UP — there is more
   room for it, nothing is being read through it, so let it carry the block.
   Taken at 0.68 what it carried was the block: a four-times-size photograph at
   full contrast filling every pixel the screen was not on, arguing with the
   piece of work in the middle at exactly the same volume. The room stopped
   being a room and became a wall with a postcard pinned to it.

   A projection room is DARK. What is in the air is a fraction of the light
   coming off the screen, and the screen has to be the brightest thing in it by
   a wide margin or none of the rest of this — the spill, the bead on the melt,
   the crop marks, the type crossing the edge — has anything to be brighter
   than. A third is where the ghost is unmistakably there and unmistakably
   behind. */
.hb--motion .hb__stage--behind {
  opacity: 0.3;
}

/* --- the room's furniture ---------------------------------------------------
   Eight hairlines and a running mark, and not one of them is decoration.

   FOUR VERTICALS: the page's own column, and the screen's own two sides. Two
   pairs, mirror-symmetric about the same axis, which is what makes this a grid
   the screen is standing IN rather than a frame drawn around it — the screen's
   edges ARE two of the room's lines, so there is no second system.

   FOUR HORIZONTALS: the head rule, the screen's top and bottom, the foot rule.
   The same symmetry one axis over, and every piece of type in the block sits
   in one of the cells they make.

   The whole rank drifts with the page's own velocity. One transform on the
   container and not nine on the children: the children are already carrying
   their entrance in `transform`, and two rules setting one property is the
   oldest bug in a sheet like this. */
.mo__room {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  transform: translate3d(0, calc(var(--flow, 0) * -7px), 0);
}

.mo__v,
.mo__h {
  position: absolute;
  background: currentColor;
  opacity: 0;
  transition:
    opacity 0.9s var(--ease-out),
    transform 1.25s var(--ease-out);
}

.mo__v {
  top: 0;
  bottom: 0;
  width: var(--hair);
  transform: scaleY(0);
  transform-origin: 50% 0;
}

.mo__h {
  left: 0;
  right: 0;
  height: var(--hair);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

.mo__v--l { left: var(--pad); transition-delay: 0.06s; }
.mo__v--r { right: var(--pad); transition-delay: 0.06s; }
/* THE BOX, and it stands `--mo-gap` off the picture on every side.

   These four used to be drawn one hair outside the screen, which puts a line
   along the edge of a photograph — a border by another name, and near enough
   to read as one drawn badly. Twenty pixels of air turns the same four lines
   into a box the picture is standing in. See `--mo-gap`.

   The four are still FULL-LENGTH rules through the room rather than four
   segments: they are the room's grid, and a grid that stops at the corners of
   the thing it encloses is a frame. What marks the corners is `.mo__dot`. */
.mo__v--sl { left: calc(var(--mo-x) - var(--mo-gap) - var(--hair)); transition-delay: 0.2s; }
.mo__v--sr { right: calc(var(--mo-x) - var(--mo-gap) - var(--hair)); transition-delay: 0.2s; }

.mo__h--t { top: var(--mo-band); transition-delay: 0.14s; }
.mo__h--b { bottom: var(--mo-band); transition-delay: 0.14s; }
.mo__h--st { top: calc(var(--mo-t) - var(--mo-gap) - var(--hair)); transition-delay: 0.28s; }
.mo__h--sb { bottom: calc(var(--mo-b) - var(--mo-gap) - var(--hair)); transition-delay: 0.28s; }

.hb--motion.is-in .mo__v { transform: scaleY(1); }
.hb--motion.is-in .mo__h { transform: scaleX(1); }

/* The page's own column reads at the weight every rule on this site reads at.
   The screen's four sit UNDER that at rest and come up with the melt: the
   lines nearest the event are the ones that flare, so the room reacts to the
   picture instead of standing beside it. Same envelope as everything else the
   melt drives — see the note in `section.js` about four surfaces. */
.hb--motion.is-in .mo__v--l,
.hb--motion.is-in .mo__v--r,
.hb--motion.is-in .mo__h--t,
.hb--motion.is-in .mo__h--b { opacity: 0.2; }

.hb--motion.is-in .mo__v--sl,
.hb--motion.is-in .mo__v--sr,
.hb--motion.is-in .mo__h--st,
.hb--motion.is-in .mo__h--sb {
  opacity: calc(0.13 + var(--rs-melt) * 0.32);
  /* The flare is the melt's own curve and not a transition: `--rs-melt` is
     already a smoothed number written once a frame, and easing an eased value
     is a lag nobody asked for. The entrance keeps its transition; this
     overrides only while the block is in. */
  transition: transform 1.25s var(--ease-out);
}

/* --- the four corners of the box --------------------------------------------
   THE MARK WHERE TWO RULES CROSS, and it is one dot of three pixels.

   The inner pair of verticals and the inner pair of horizontals cross at
   exactly four points, and those four points are the corners of the box the
   screen stands in. Left unmarked they are an accident of two lines passing
   through each other; marked, they are corners, and the four rules read as one
   rectangle rather than as a lattice that happens to enclose a picture.

   THIS REPLACED THE CROP MARKS. There were four L-shaped registration brackets
   standing off the screen's corners — a hairline down and a hairline across at
   each. At the size this room draws them they stopped being a printer's mark
   and became HUD furniture: four bracket glyphs hovering a hand's width off a
   photograph, belonging to a viewfinder rather than to a drawing. One dot on a
   crossing says the same thing with a third of the ink and nothing to read.

   Square and not round. Everything in this room is ruled with straight
   hairlines; a circle would be the only curve in the drawing, and it would be
   three pixels wide, which is exactly the size at which a circle is a fuzzy
   square anyway.

   THE ARITHMETIC. A rule is `--hair` wide with its leading edge at
   `--mo-x - --mo-gap - --hair`, so its own CENTRE LINE is half a hair further
   in; the dot is 3px, so it comes back half of that to sit on the crossing
   rather than beside it. Same on both axes, off the same properties the rules
   themselves are placed from — one source for the position, so a dot cannot
   come out of register with the line it is marking. */
.mo__dot {
  position: absolute;
  width: 3px;
  height: 3px;
  /* ROUND, like the node on the run's frame upstairs — see `.joint` in
     `styles.css`. It was a three-pixel SQUARE, which at this size is a
     difference you feel rather than see: a square node has four corners of its
     own sitting on a corner, and the eye reads the pair of them as a rule that
     stopped short rather than as a crossing that was marked. Every crossing on
     this landing is marked the same way now. */
  border-radius: 50%;
  background: currentColor;
  opacity: 0;
  transform: scale(0.4);
  /* Behind nothing and over the rules: the crossing is the top of the stack in
     its own two square pixels or it is not a mark. */
  z-index: 1;
  transition:
    opacity 0.7s var(--ease-out) 0.62s,
    transform 0.7s var(--ease-out) 0.62s;
}

.mo__dot--tl,
.mo__dot--bl { left: calc(var(--mo-x) - var(--mo-gap) - var(--hair) / 2 - 1.5px); }
.mo__dot--tr,
.mo__dot--br { right: calc(var(--mo-x) - var(--mo-gap) - var(--hair) / 2 - 1.5px); }
.mo__dot--tl,
.mo__dot--tr { top: calc(var(--mo-t) - var(--mo-gap) - var(--hair) / 2 - 1.5px); }
.mo__dot--bl,
.mo__dot--br { bottom: calc(var(--mo-b) - var(--mo-gap) - var(--hair) / 2 - 1.5px); }

/* They arrive after the rules that make them — a corner cannot exist before
   its two lines do — and they flare with the melt on the same envelope the
   rules do, a shade brighter, because a corner is where the drawing is most
   itself. */
.hb--motion.is-in .mo__dot {
  opacity: calc(0.3 + var(--rs-melt) * 0.55);
  transform: none;
  transition: transform 0.7s var(--ease-out) 0.62s;
}

/* --- the two labels on the head rule ---------------------------------------
   Left says what this is. Right says how it is driven, which is the one place
   on this page a control is explained in words — the argument is beside
   `MOTION.hint` in `content.js`.

   Each carries a rule that runs from the label out to the room's edge, so the
   two of them are the head rule turning a corner rather than two words parked
   on top of it. */
.mo__head {
  position: absolute;
  left: var(--pad);
  right: var(--pad);
  top: var(--mo-band);
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(16px, 3vw, 48px);
  padding-top: var(--mo-air);
  pointer-events: none;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.mo__label,
.mo__hint {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.9em;
  min-width: 0;
  opacity: 0;
  --ry: 8px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.8s var(--ease-out) 0.55s,
    --ry 0.8s var(--ease-out) 0.55s;
}

.mo__label { flex: 0 1 auto; }
/* The hint is quieter than the label. One of them names the object and the
   other is an instruction; an instruction at the same weight as the thing it
   is about reads as the thing being called "scroll". */
.mo__hint { flex: 0 1 auto; opacity: 0; }

.hb--motion.is-in .mo__label { opacity: 0.62; --ry: 0px; }
.hb--motion.is-in .mo__hint { opacity: 0.34; --ry: 0px; }

.mo__label-rule,
.mo__hint-rule {
  flex: none;
  width: clamp(28px, 4vw, 74px);
  height: var(--hair);
  background: currentColor;
  opacity: 0.5;
  transform: scaleX(0);
  transition: transform 1.05s var(--ease-out) 0.62s;
}

.mo__label-rule { transform-origin: 0 50%; }
.mo__hint-rule { transform-origin: 100% 50%; }

.hb--motion.is-in .mo__label-rule,
.hb--motion.is-in .mo__hint-rule { transform: scaleX(1); }

/* --- the headline, and it crosses the screen -------------------------------
   Set at the size of the room, centred on the screen's own centre line, and
   drawn TWICE.

   The copy UNDER the screen is plain ink on the room. The copy OVER it is
   clipped to the screen's rectangle and rides `difference`, so inside the
   frame the words are the picture inverted and outside it they are white on
   black — one line of type running from the dark of the room INTO the moving
   work and out the other side.

   Two layers and not one clip-path with a hole in it, and the reason is that
   `inset()` has no complement: the outside copy would have to be a polygon
   with an even-odd rule, expressed in the same six numbers, recomputed at
   every breakpoint. Two elements and one rectangle is the version that cannot
   drift, and the drift here would be a letter torn half a pixel off the edge
   of the picture.

   `difference` and not `exclusion` or a knockout: it is the only blend that
   guarantees contrast against an unknown moving picture at every pixel, which
   is the whole problem — the twelve clips are white interfaces and black
   trading desks in the same strip. The layer contains nothing but glyphs, so
   the warning at `.hb--reel` about a blend applying to a whole subtree does
   not bite: there is no control inside it to come out inverted.

   The second copy is `aria-hidden` in the markup. Six words read twice is the
   whole accessibility cost of the trick, and it is not one worth paying. */
.mo__title {
  position: absolute;
  inset: 0;
  margin: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  text-align: center;
  /* Centred on `--mo-cy` and not on the pin. The padding is twice the offset
     between the two, so `align-content: center` resolves onto the screen's own
     axis — one declaration instead of a translate that would then have to be
     fought over by the arch. */
  padding-bottom: calc(var(--vh) - var(--mo-cy) * 2);
  pointer-events: none;
  /* ONE LINE, AND IT IS SIZED TO THE ROOM RATHER THAN TO THE SCREEN.

     The headline was two lines staggered in opposite directions, which made
     the block of type run diagonally through the picture — a composition that
     needed three and a half viewports of arrival to be read as one gesture. In
     one screen it read as a headline with a caption sitting inside the frame.

     It is one line now: a BAR of type at the size of the room, crossing the
     picture edge to edge. Room, picture, room — the whole move, said once.

     `vw` and nothing else, and the line does not wrap. The screen is bounded
     at 46vw of height-derived width, so a line held at a fixed share of the
     viewport overhangs it by the same margin at every size the room is drawn
     at — which is the one property this headline has to have. A `rem` floor
     would break that promise at exactly the widths where the screen is widest
     relative to the page. The cap is what stops it becoming a poster on a
     wall-sized monitor; past that the room simply has more air in it. */
  font-size: min(5.9vw, 7.4rem);
  line-height: 0.86;
  letter-spacing: -0.045em;
  --arch: 0.30em;
}

/* THE LINE DOES NOT WRAP, and that is what keeps it one line.

   `.hx__w` is an inline-block per word so each can be clipped and lifted on
   its own, which also means the browser is free to break between them. At the
   size above it never wants to on a desktop and always wants to on a phone,
   and a headline that is one line at one width and three at another is not the
   drawing this room is. Held on one line and scaled by `vw`, it is the same
   composition at every width. */
.mo__title .hx__line {
  white-space: nowrap;
}

.mo__title--out {
  z-index: 3;
  /* Held back a shade. This half of the word is standing on the room, and the
     room is dust: at full white the type is the brightest thing in the block
     and the eye goes to it rather than to the work. The half inside the frame
     is doing the loud version. */
  color: color-mix(in srgb, var(--fg) 88%, transparent);
}

.mo__title--in {
  z-index: 5;
  mix-blend-mode: difference;
  clip-path: inset(var(--mo-t) var(--mo-x) var(--mo-b) var(--mo-x));
}

/* --- the screen -------------------------------------------------------------
   `.rs` is the whole apparatus and it covers the pin, because two of its parts
   live in two different cells of the room: the frame is in the middle and the
   transport is on the foot rule. It swallows nothing — only the controls take
   the pointer back. */
.rs {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.rs__screen {
  position: absolute;
  top: var(--mo-t);
  left: var(--mo-x);
  width: var(--mo-w);
  height: var(--mo-h);
  /* The screen swells a hair while the picture is melting. Twelve thousandths
     — under two pixels at a laptop width — and it is the difference between a
     cut happening TO a screen and a screen doing the cutting. */
  transform: scale(calc(1 + var(--rs-melt) * 0.014));
}

/* THE SPILL. A beam has to land somewhere, and without this the frame is a
   rectangle of video pasted onto a black page: nothing in the room knows the
   screen is lit. It brightens with the melt on the same envelope as the rules
   and the dust, so the one event lights the whole volume.

   `closest-side` and a generous inset, so the falloff is the shape of the
   screen rather than a circle behind it. */
.rs__screen::before {
  content: "";
  position: absolute;
  inset: -22% -16%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(
    closest-side,
    color-mix(in srgb, var(--fg) 22%, transparent) 0%,
    color-mix(in srgb, var(--fg) 9%, transparent) 44%,
    transparent 76%
  );
  opacity: calc(0.5 + var(--rs-melt) * 0.5);
}

/* The aperture. The screen opens from its own centre line — a projector gate
   rather than a fade — on the same curve the room's rules run in on. */
.rs__frame {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  background: #000;
  clip-path: inset(50% 0 50% 0);
  transition: clip-path 1.15s var(--ease-out) 0.42s;
}

.hb--motion.is-in .rs__frame { clip-path: inset(0 0 0 0); }

/* --- the strip under the screen ---------------------------------------------
   THE FALLBACK, AND THE SOURCE OF THE PICTURES, and it is one thing doing both
   jobs on purpose.

   The canvas over this draws nothing at all until a frame has decoded — see
   the head of `vs-anim-liquid-cut.js` — so a reader with no WebGL2, a refused
   codec or `prefers-reduced-motion` is looking straight at these: twelve
   posters, one per station, with the filing under them and the transport
   working. Nothing branches. The canvas is transparent or it is not.

   Which is also why the strip is never hidden while the canvas is up. It could
   be — it is behind an opaque canvas for most of the block's life — and the
   frame it would be hidden on is the one where the shader has a context and
   the decoder has not caught up, which is a black screen for a beat. A video
   layer that is composited under an opaque one costs a compositor pass; a
   black screen costs the block. */
.rs__track {
  position: absolute;
  inset: 0;
  z-index: 1;
  margin: 0;
  padding: 0;
  list-style: none;
}

.rs__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
}

/* No transition and no travel. Everything that used to happen here — the clip
   rectangle, the counter-moving media box, the nine-slat shutter — is the
   canvas's job now, and a DOM cut running underneath an identical GL one is
   two transitions arguing about the same picture. */
.rs__slide[data-state="on"] { opacity: 1; }

.rs__media {
  position: absolute;
  inset: 0;
}

.rs__v {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* COLOUR AT REST. The strip is moving footage of the work — the one thing on
     the page with the least business being drained of what it is — and the
     shader over it lifts contrast without touching saturation for the same
     reason. The frame around a picture on this site may be one bit; the
     picture inside it never is. */
  filter: contrast(1.05) saturate(1.1);
}

/* The canvas. Over the posters, under nothing. */
.rs__gl {
  position: absolute;
  inset: 0;
  z-index: 2;
}

/* THE CROP MARKS ARE GONE. Four L-shaped registration brackets stood off the
   screen's four corners, a hairline down and a hairline across at each. What
   says where the two systems touch now is the box — four rules standing 20px
   off the picture with a dot on each crossing, `.mo__dot` above. The argument
   is written out there; the short version is that a bracket floating beside a
   photograph is a viewfinder, and a corner marked on a rule is a drawing. */

/* --- the caption under the screen ------------------------------------------ */

.mo__note {
  position: absolute;
  left: var(--pad);
  right: var(--pad);
  /* MEASURED FROM THE RULE AND NOT FROM THE PICTURE. The caption's air is the
     air between it and the line above it, and that line is `--mo-gap` below the
     screen now — see `.mo__h--sb`. Without the gap in this sum the note kept
     its distance from the photograph and lost two thirds of its distance from
     the rule it sits under, which reads as a caption stuck to a boundary. */
  top: calc(var(--mo-t) + var(--mo-h) + var(--mo-gap) + clamp(18px, 2.6vh, 40px));
  margin: 0;
  z-index: 5;
  text-align: center;
  pointer-events: none;
  font-size: var(--t-micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0;
  --ry: 10px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.9s var(--ease-out) 0.8s,
    --ry 0.9s var(--ease-out) 0.8s;
}

.hb--motion.is-in .mo__note { opacity: 0.46; --ry: 0px; }

/* --- the transport ----------------------------------------------------------
   One row on the foot rule: the filing of the piece on screen, the ruler
   between two arrows, and the count.

   THEY MOVE THE REEL. They used to move the PAGE — the picture was a function
   of the scroll, so a control that changed it without moving the reader would
   have been undone by the next notch of the wheel. The room is one screen now
   and the reel owns which piece is up, so pressing `07` puts `07` on the
   screen and leaves the reader standing where they are. Note 4 in
   `showreel.js` carries the whole of it.

   THREE COLUMNS, AND THE OUTER TWO ARE EQUAL. It was `1fr 2.2fr auto`, which
   made the ruler the elastic member and left the filing a third of the row:
   at a laptop width `Vuesax 4.0 · Design system` ran out of column and was
   ellipsed, which is a name being cut to make room for a control. The ruler
   takes a MEASURE of its own now — see `.rs__ruler` — and the two `1fr`
   columns either side of it are equal, so it is centred under a centred screen
   by the grid rather than by arithmetic, and the filing has half the row to
   set a name in. */
.rs__rail {
  position: absolute;
  left: var(--pad);
  right: var(--pad);
  bottom: var(--mo-band);
  padding-bottom: var(--mo-air);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: clamp(14px, 2.4vw, 44px);
  font-size: var(--t-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 0.85s var(--ease-out) 0.86s;
}

.hb--motion.is-in .rs__rail { opacity: 1; }

.rs__filing {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-weight: 500;
  min-width: 0;
}

/* THE FILING IS STRUCK, not swapped.

   The row is rewritten on the frame the picture crosses over — see `#caption`
   — and a line of type that is simply different next time you look is a line
   nobody notices changing. One class, added on the frame after it is removed,
   so the animation restarts even when the piece before it had the same
   discipline. It re-settles WITH the picture rather than after it. */
@keyframes rs-strike {
  from { opacity: 0; transform: translate3d(0, 6px, 0); }
  to { opacity: 1; transform: none; }
}

.rs__filing.is-struck { animation: rs-strike 0.52s var(--ease-out) both; }

.rs__name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rs__sep {
  flex: none;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.5;
}

.rs__kind {
  font-weight: 400;
  opacity: 0.55;
  white-space: nowrap;
}

.rs__tags {
  display: inline-flex;
  gap: 0.7em;
  opacity: 0.4;
  white-space: nowrap;
  margin-left: 0.3em;
}

.rs__tags i { font-style: normal; }

.rs__ruler {
  display: flex;
  align-items: center;
  gap: clamp(10px, 1.3vw, 22px);
  /* A MEASURE, and not whatever the row has left over.

     The ruler used to be the elastic column and took two thirds of the rail,
     which is a control the width of a paragraph: twelve segments across nine
     hundred pixels are nine hundred pixels of hairline saying one number. Held
     to a measure it is an INSTRUMENT sitting under the picture — and the width
     it is held to is a share of the screen above it rather than of the page,
     so the thing that indicates where you are in the reel is the size of the
     reel. */
  width: clamp(200px, calc(var(--mo-w) * 0.6), 470px);
}

/* THE ARROW IS THE RULE TURNING A CORNER, and that is the whole of the
   redesign of it.

   It was a circle with a glyph in it — twice, side by side, hard against the
   right-hand end of a row. Two pills at the end of a strip is the shape every
   carousel on the internet has, it says nothing about this block, and it put
   both directions on one side of the thing they steer.

   What is drawn now is a hairline running out of the ruler toward the room's
   edge with a chevron on its far end: the transport is the foot rule
   continuing, and the two directions stand at the two ends of the twelve marks
   they move through. Nothing is filled, nothing is round, and there is no
   glyph — a chevron drawn as two hairlines at the weight the whole room is
   ruled at belongs to the drawing rather than to a toolbar. */
.rs__arrow {
  position: relative;
  flex: none;
  width: clamp(30px, 3.4vw, 54px);
  height: 30px;
  padding: 0;
  border: 0;
  background: none;
  /* Load-bearing. The UA stylesheet gives a `<button>` `color: buttontext`,
     which is black, and it beats inheritance — so on the room's own ground the
     whole transport was black hairlines on black and the block appeared to
     have no controls at all. Anything here drawn in `currentColor` inside a
     form element has to say this. */
  color: inherit;
  cursor: pointer;
  pointer-events: auto;

  /* TWO NUMBERS, AND THE WHOLE ARROW IS WRITTEN FROM THEM.

     `--rs-tip` is the side of the square whose two visible edges are the
     chevron. `--rs-reach` is how far the arrow's FAR END is held back at rest,
     and letting go of that hold is the whole gesture. Both pieces of the
     drawing — the end of the rule and the point of the head — are expressed
     from the same pair below, which is what makes it impossible for the head
     to come away from its own rule. It came away before: the note over the
     head claimed a translate of half its own diagonal that was never written,
     so the chevron floated eight pixels clear of the line it was supposed to
     terminate, at every width the button takes. */
  --rs-tip: 11px;
  --rs-reach: 7px;

  /* Read at rest, not merely present. These stand over a room made of grain
     and they are the block's only two glyphs; at the weight a hairline takes
     on paper they were a set of controls the reader had to go looking for. */
  opacity: 0.7;
  transition: opacity 0.34s var(--ease-out);
}

.rs__arrow:hover,
.rs__arrow:focus-visible { opacity: 1; }

/* THE RULE, AT THE RULER'S OWN WEIGHT. Two device pixels and not one: the
   twelve graduations beside it are ruled at two — see `.rs__mark-bed` — and a
   transport drawn in two weights reads as two instruments parked side by side.
   The negative margin is the same correction the graduations take: `top: 50%`
   with no offset puts an even-numbered hairline half a pixel low, and half a
   pixel of step in a foot rule is a foot rule with a break in it. */
.rs__arrow-rule {
  position: absolute;
  top: 50%;
  height: calc(var(--hair) * 2);
  margin-top: calc(var(--hair) * -1);
  background: currentColor;
  transition: --rx 0.42s var(--ease-out);
}

/* The chevron, drawn as the two visible sides of a square turned forty-five
   degrees.

   Its POINT is what has to land on the end of the rule — not its corner and
   not its box. The point of a square rotated a half-turn off square stands
   `(√2 − 1) / 2` of a side beyond the box on the axis it is turned along,
   which is the 0.2071 below and the number the old note was reaching for. */
.rs__arrow-tip {
  position: absolute;
  top: 50%;
  width: var(--rs-tip);
  height: var(--rs-tip);
  border-top: calc(var(--hair) * 2) solid currentColor;
  border-right: calc(var(--hair) * 2) solid currentColor;
  transition: --rx 0.42s var(--ease-out);
}

/* THE FAR END, and both pieces of the arrow read the same expression for it:
   `--rs-reach` of hold, less however much of it `--rx` has given back. One
   quantity, spent twice, so the rule and the head cannot disagree about where
   the end of this arrow is — at any width, in any state, or at any point of
   the transition between them.

   `--rx` IS THE PAGE'S OWN REACH, and this is the second control on the site
   to spend it: the deck's arrows put it on their head — `.faq__arrow-head` —
   and it is registered as a `<length>` at the head of this sheet precisely so
   that it can be transitioned. Reusing it means the two arrows on this page
   move by the same named quantity on the same curve.

   IT IS AN INSET HERE AND A TRANSLATE THERE, and the difference is forced. The
   deck's arrow lets its head travel away from a rule that stays put, which it
   can afford because its head and its rule were never welded. Ours are: the
   whole defect being repaired is a chevron floating clear of its line, so the
   end of the RULE has to travel with the head — and no translate lengthens a
   rule. An inset moves both, exactly, from one number. */
.rs__arrow--prev .rs__arrow-rule {
  left: calc(var(--rs-reach) - var(--rx));
  right: 0;
}

.rs__arrow--next .rs__arrow-rule {
  left: 0;
  right: calc(var(--rs-reach) - var(--rx));
}

.rs__arrow--prev .rs__arrow-tip {
  left: calc(var(--rs-tip) * 0.2071 + var(--rs-reach) - var(--rx));
  transform: translateY(-50%) rotate(-135deg);
}

.rs__arrow--next .rs__arrow-tip {
  right: calc(var(--rs-tip) * 0.2071 + var(--rs-reach) - var(--rx));
  transform: translateY(-50%) rotate(45deg);
}

/* THE GESTURE, IN ONE RULE. The hold is released and the arrow reaches toward
   the room's edge: the control says which way it is about to take you before
   it takes you, and nothing it draws ever leaves the box it is drawn in. It
   used to be a `scaleX(1.5)` on the rule alone, which put sixteen pixels of
   hairline outside the button and straight through the head.

   `--rx` is registered `inherits: false` with an initial of nought, so it has
   to be set on the two elements that read it rather than on the button — and
   so the failure mode of a missing `--rs-reach` is an arrow that does not
   move, never an arrow that walks off its own control. */
.rs__arrow:hover .rs__arrow-rule,
.rs__arrow:hover .rs__arrow-tip,
.rs__arrow:focus-visible .rs__arrow-rule,
.rs__arrow:focus-visible .rs__arrow-tip { --rx: var(--rs-reach); }

/* --- the ruler ---------------------------------------------------------------
   TWELVE GRADUATIONS, AND ONE BAR TRAVELLING ACROSS THEM.

   It was one continuous hairline with the twelve standing behind it as
   invisible hit zones — the reel drawn as a single line, on the argument that
   twelve dashes make the eye count before it can read. That argument holds for
   twelve dashes each carrying their own progress fill, which is what it was
   written against. It does not hold for a row with nothing in it: an unbroken
   rule at a fifth, over a room made of grain, says neither how many pieces
   there are nor which one is up nor that it can be pressed at all. Three
   questions, no answers, and a reader looking at a smudge under a picture.

   So the twelve are DRAWN again, and the thing that was actually wrong with
   twelve is fixed instead: there is AIR between them, they hold no state of
   their own, and one bar crosses them.

     the twelve graduations   how many pieces, and that this row is a control
     the one under the hand   which one you are about to ask for
     the bar                  which one is up, and how far through it is

   Three drawings, three questions, and each of them answered by exactly one.
   The bar is a third heavier than a graduation and drawn at full ink, so it is
   never mistaken for the row it travels along.

   THE AIR IS THE POINT. Twelve units flush against each other are one unit
   twelve times as long, which is the row this replaces. The gap comes out of
   the SEGMENT and not out of the ruler's measure — see `--rs-seg` — so the
   flexbox that lays out the twelve and the arithmetic that places the bar are
   dividing the same width by the same twelve, and the bar cannot drift off the
   graduation it is standing on. */
.rs__marks {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  min-width: 0;
  /* Tall enough to be a target for a finger; the drawing is two pixels of it. */
  height: 26px;

  /* THE AIR BETWEEN TWO GRADUATIONS, and it is a SHARE OF THE SEGMENT rather
     than a length.

     What the eye reads in a row of repeated marks is the ratio of ink to air,
     not either of them — a five-pixel gap is generous between short marks and
     invisible between long ones, so a fixed number is a different row at every
     width the ruler takes. A quarter of the segment holds that ratio steady
     from a phone to a wide screen: unmistakably twelve things, still one
     instrument.

     Bounded at both ends because a share alone is not enough. Under three
     pixels the gap stops being a decision and becomes a rounding artefact;
     over seven the row starts reading as twelve dashes parked in a line, which
     is the failure the single continuous rule was written to escape. */
  --rs-gap: clamp(3px, calc(100% / var(--rs-n, 12) * 0.24), 7px);
  /* ONE GRADUATION, in the row's own arithmetic. Eleven gaps come out of the
     twelve segments, which is what the flexbox does with `gap` — so anything
     that has to land on a segment can be written from this and be exact. */
  --rs-seg: calc(
    (100% - (var(--rs-n, 12) - 1) * var(--rs-gap)) / var(--rs-n, 12)
  );
  gap: var(--rs-gap);
}

/* THE MEASURE THE TWELVE ARE RULED ON. One continuous hairline the width of
   the ruler, under everything, at a weight that is barely there.

   Twelve graduations with nothing between them are twelve dashes parked in a
   line — the failure the single rule was originally written to escape, and at
   a phone's width, where a segment is eight pixels and the air is three, it is
   the failure the row falls into first. A line UNDER them at less than half
   their weight binds them back into one instrument without ever competing with
   them: what is read is a ruled line carrying twelve graduations, which is
   what a ruler is, and the air between them is still air because the step from
   this to a graduation is more than double.

   It is the reel WHOLE — how many are left, before anything has been said
   about where in it the reader is. */
.rs__marks::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: calc(var(--hair) * 2);
  margin-top: calc(var(--hair) * -1);
  background: currentColor;
  pointer-events: none;
  opacity: 0.22;
}

/* THE BAR. One graduation wide, stepped to the piece that is up, filling as it
   plays, and drawn OVER the graduation's own hairline rather than instead of
   it — so the row never loses a unit while the bar is short.

   `--rs-i` and `--rs-n` are written by `showreel.js` — which segment, and how
   many there are — so the row divides itself by the DATA and this rule holds
   no count. `--rs-p` is how far through the station the clock has come.

   The floor under the fill is the load-bearing detail. At the instant of a cut
   `--rs-p` is nought, and a bar scaled to nothing is a bar that has vanished
   from a row whose whole job is to say where you are — the indicator would
   blink out twelve times a reel, once at each moment the reader most needs it.
   Twelve per cent is a NUB: present, unmistakably at the start of its segment,
   and growing from there. Twelve and not six because a twelfth of the ruler is
   about two dozen pixels wide, and six per cent of that is under two — a mark
   that has to be looked for is a mark that is not answering.

   The step is transitioned and the fill is not. Moving from one segment to the
   next is an event and wants an ease; the fill is a clock, and easing a clock
   is a bar that lags what it is measuring. */
.rs__marks::after {
  content: "";
  position: absolute;
  top: 50%;
  /* A third over the graduations it crosses. Same ink, more weight: the bar has
     to be the thing the eye lands on in a row of thirteen hairlines, and it
     cannot buy that with a colour this room does not have. */
  height: calc(var(--hair) * 3);
  margin-top: calc(var(--hair) * -1.5);
  background: currentColor;
  pointer-events: none;
  left: calc(var(--rs-i, 0) * (var(--rs-seg) + var(--rs-gap)));
  width: var(--rs-seg);
  transform: scaleX(calc(0.12 + 0.88 * var(--rs-p, 0)));
  transform-origin: 0 50%;
  opacity: 1;
  transition: left 0.44s var(--ease-out);
}

.rs__marks li { flex: 1 1 0; min-width: 0; }

.rs__mark {
  position: relative;
  display: block;
  width: 100%;
  height: 26px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;                       /* see `.rs__arrow` */
  cursor: pointer;
  pointer-events: auto;
}

/* THE GRADUATION. One per piece, the width of its own segment, and it is drawn
   AT REST — which is the change, and the reason the whole row now reads.

   It was nought until a pointer arrived. Twelve invisible marks under one
   continuous rule is a control that only exists once you have found it, and
   finding it means moving a pointer along a smudge to see whether anything
   answers. A transport is furniture: it has to say what it is while nobody is
   touching it.

   THE NUMBER IS MEASURED AGAINST THE GRAIN AND NOT AGAINST THE GROUND, and
   that is the whole of why it is this high.

   The block's ground is `#07070a`, and against a FLAT `#07070a` a fifth would
   already be visible and a third would be comfortable. It is not flat: the
   dust holds a ghost of the picture on screen and paints the whole room in
   speckle, and the bright end of that speckle reaches well past what a third
   of white comes out at. A line that measures three to one against the average
   of a noisy field is a line the field crosses, over and over, along its
   length — which is what the row actually looked like: not faint, INTERRUPTED.

   So the graduation is set above the grain's own ceiling rather than above the
   ground's floor, and it is two device pixels because one is a hairline the
   speckle simply eats. The rule this replaces was a fifth at one pixel and
   measured under two to one against the flat ground alone. */
.rs__mark-bed {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: calc(var(--hair) * 2);
  margin-top: calc(var(--hair) * -1);
  background: currentColor;
  opacity: 0.48;
  transition: opacity 0.26s var(--ease-out);
}

/* THE ONE WHOSE NAME IS UNDER THE SCREEN, between the resting weight and full.

   `is-on` follows the DOMINANT piece and the bar follows the STATION, and for
   the second half of every melt those are two different segments — the name has
   changed, the clock has not finished the one it is on. Drawn, that half second
   is the row handing over: the graduation lights on the piece that has arrived
   while the bar finishes the piece that is leaving. It has to sit above the
   ten that are neither and below the bar, or the handover is either invisible
   or a second bar. */
.rs__mark.is-on .rs__mark-bed { opacity: 0.74; }

/* AND THE ONE UNDER THE HAND, at full ink, because it is about to be asked
   for. The row answers before it is pressed, and the reel holds while a hand
   is on the rail — `#bind` in `showreel.js` — so this is a row that has
   stopped to be read.

   AFTER `is-on`, AND THAT IS THE POINT OF THE ORDER. The two selectors weigh
   the same, so the later one wins; written the other way round, running a
   pointer along the row would DIM the one segment that was already lit and the
   reader would watch the transport lose the piece it is playing at exactly the
   moment they went looking for it. */
.rs__mark:hover .rs__mark-bed,
.rs__mark:focus-visible .rs__mark-bed { opacity: 0.9; }

.rs__count {
  margin: 0;
  display: inline-flex;
  gap: 0.3em;
  justify-self: end;
  font-variant-numeric: tabular-nums;
}

.rs__count em {
  font-style: normal;
  opacity: 0.4;
}

.rs__mark:focus-visible,
.rs__arrow:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

/* --- 01 selected work: a cover, then the strip ------------------------------
   The block is five and a bit viewports — one of title card, four and a bit of
   run — and everything in it is drawn WHITE over a scrim.

   That last part is a change and it is the load-bearing one. The head used to
   ride `mix-blend-mode: difference`, which is a clever way to stay legible over
   an unknown picture and a bad way to have a colour: the type was a different
   grey on every panel, and a blend mode applies to an element's whole rendered
   subtree, so the chrome rim inside it came out inverted along with everything
   else. Plain white over a gradient of the block's own ground is one colour on
   every panel, and it lets the controls behave.

   --- THE GRID, and why the numbers are what they are -----------------------
   TWO hairlines, and they are the two edges of the page's own column. The two
   horizontals the page used to draw are gone — the strip under them draws its
   own pair and the block was printing four lines where the composition wants
   two; see `.reel__h`, which is what is left of them.

   `--reel-top` and `--reel-bot` are still the BAND: they are where the strip is
   told to compose, and the two rules it draws for itself are a tenth of that
   band in from each end. `--reel-air` is the gutter between a rule and the type
   nearest it. ONE number for that gutter, on both sides of both rules, which is
   the whole of "the lines and the text breathe": a rule pressed against a line
   of type reads as an underline, and rules at different distances read as an
   accident. Change `--reel-air` and every clearance in the block moves together.

   The head band carries a line more than the foot does, because this block
   stands FIRST: the block's own number, then the headline. That is why
   `--reel-top` has an `.is-first` value and `--reel-bot` does not.

   --- AND THE STRIP UNDER IT IS ON THE SAME GRID ----------------------------
   These four lines are not the only ones on screen. The run composes each panel
   with two hairlines and four pieces of type of its own, and it used to place
   them on ITS OWN thirds — which is a second grid that has never heard of this
   one, so at most viewports one of its rules was drawn straight through the
   headline and the work's name came up underneath the press.

   It is TOLD now. `#reelFrame` in `home/section.js` measures `.reel__h--t`,
   `.reel__h--b` and the column's inner margin off this block and hands the three
   fractions to the component, which composes strictly inside them. Which means
   the numbers here are the whole layout: move a band and the panel follows on
   the next resize, with nothing to keep in step by hand. */

.hb--reel {
  --reel-top: clamp(104px, 19vh, 220px);
  --reel-bot: clamp(112px, 20vh, 232px);
  --reel-air: clamp(16px, 2.2vh, 30px);
  /* How far past its band each wash of the scrim reaches, as a multiple of the
     band. It was the literal 1.7 written twice; it is a token because the head
     band GROWS when this block stands first, and a veil that grows with it
     would darken most of the photograph to pay for one extra line of type.

     It went UP when the scrim came down. The two moves are one move: a wash
     that is a third as dark has to be twice as long to still be a wash and not
     an edge. See `--reel-scrim-t` below and the curve on `.hb__pin::after`. */
  --reel-veil: 2.05;
  /* THE WEIGHT OF THE SCRIM, one number per band, and every stop of both
     gradients is a fraction of it.

     They were 0.8 and 0.84 at the outer edge with a single kink at the band —
     which is what made the thing read as a BLUR laid over the photograph
     rather than as light falling off. Two straight ramps meeting at an angle
     is a crease, and the eye finds a crease in a gradient instantly, however
     soft each half of it is.

     Both are down 60% and the kink is gone with them: the falloff below is
     eight stops on a curve, so there is no point along it where the rate
     changes abruptly. Less ink, spread further, shaped — the type still has
     its ground and the photograph is a photograph again to within a hand's
     width of the edge. */
  /* WHICH WAY UP THE PANEL UNDER THE PAGE'S TYPE IS: 1 on a photograph, 0 on
     paper, and every value between while a seam crosses the screen.

     Written once a frame by `section.js` off the strip's own position — see
     the note beside it — and spent HERE, in three places and no others: the
     weight of the scrim, the colour the furniture prints in, and the pair the
     one control on this block fills with.

     The run alternates a photograph the size of the screen with a sheet of
     paper. The block was written as though it were all photograph: a literal
     white over a literal black, on the argument that what is under it has no
     polarity to follow. That is true of half of it. On the other half the
     scrim was laying a grey wash across a white page to give white type a
     ground it should never have needed, which is a shadow doing a colour's
     job — and it is the one thing on this page you can see being done. */
  --reel-k: 1;
  --reel-scrim-t: calc(0.32 * var(--reel-k));
  --reel-scrim-b: calc(0.34 * var(--reel-k));
  /* Still literal, and still for the reason they always were: these two are
     READ BACK by `section.js` and handed to the shaders as hex, so they are
     the pair the PICTURES are painted for and they cannot become an
     expression. What turns over is the furniture standing on them, below. */
  --fg: #ffffff;
  --bg: #000000;
  color: color-mix(in srgb, var(--fg) calc(var(--reel-k) * 100%), var(--ink));
  /* AND THE HAIRLINE WEIGHT CROSSFADES WITH THE COLOUR IT IS PRINTED IN.

     The line above walks this block's furniture from white to the sheet's ink
     across the seam between a photograph and a plate. A rule is furniture, so
     it makes that crossing too — and a white rule and a black rule do not want
     the same alpha (see `--rule-light` / `--rule-dark` in `styles.css`). The
     weight is therefore mixed on exactly the number the colour is mixed on: at
     `--reel-k` 1 the frame is white over a photograph and takes the white
     weight, at 0 it is the sheet's ink on paper and takes the sheet's own.
     Nothing here names a number; it names which of the two ends it is at. */
  --rule-o: calc(var(--rule-sheet) + (var(--rule-light) - var(--rule-sheet)) * var(--reel-k));
  /* THE GROUND, and it matters now in a way it did not before.

     The block declares white type and declared no background, which was
     harmless eight screens down where whatever was above it had already
     painted. It opens the page now: for the frames between first paint and the
     run's first frame, white type on the pale sheet is white on white — the
     studio's name, the headline and the way out, all invisible, on the first
     thing anybody sees. The ink is what the photographs stand on, so it is
     what stands there while they load. */
  background: var(--bg);
}

/* The press, which turns over with everything else standing on the run.

   `.xcta` takes `--fg` for its label and its fill and `--bg` for the ink
   inside that fill — correct everywhere else on the page, and half wrong here
   for the same reason the block's own `color` was: on a plate panel the label
   is white type on a white page and the fill it grows on hover is white on
   white. Both are re-pointed at the same crossfade the rest of the furniture
   rides, so the rim reads on a photograph and on paper without the block
   having to know which one is under it this frame. */
.hb--reel .xcta {
  --chrome-fill: color-mix(in srgb, var(--fg) calc(var(--reel-k) * 100%), var(--ink));
  --chrome-fill-ink: color-mix(in srgb, var(--bg) calc(var(--reel-k) * 100%), var(--paper));
  --inp-text: color-mix(in srgb, var(--fg) calc(var(--reel-k) * 100%), var(--ink));
  --inp-accent: color-mix(in srgb, var(--fg) calc(var(--reel-k) * 100%), var(--ink));
}

/* Standing first, the head band holds the headline and has to clear the chrome
   above it, so it is deeper than the band the composition wants further down.

   IT USED TO BE THREE LINES AND `clamp(168px, 26vh, 296px)` — a head band a
   QUARTER of the screen deep, on the block whose whole argument is the
   photograph under it: the studio's filing, then the block's number and what the
   run is, then the headline, each of the first two saying something the screen
   was already saying louder. Both went, and the number comes down with them.

   The floor is what the top of the page costs rather than what the type does:
   the headline is bottom-aligned to the rule, so the band's depth is the only
   thing holding it clear of the wordmark and the dial, and `--pad * 2` is where
   everything else on this page starts.

   Scoped to `.is-first` and not to `.hb--reel`, for the same reason the cue is:
   this is a consequence of the block's POSITION. Move the run and the band comes
   back to the size the composition wants. */
.hb--reel.is-first {
  --reel-top: clamp(148px, 17.5vh, 192px);
  /* Pulled in with the growth, and it has to be: the veil is a multiple of the
     band, so at the standing figure a grown head band would reach half the
     screen and the block whose whole argument is the photographs would be
     showing mostly gradient. Same ratio to the block's own veil it always
     held — the two moved together when the scrim was lightened. */
  --reel-veil: 1.75;
}

/* THE CUE STOOD HERE, hung over `--reel-runrule` so it cleared the hairline the
   panel under it draws. The cue is gone — see the tombstone at the head of this
   file — and the two lines it was measured against are gone with it.

   `--reel-runrule` is not: it is the line the STRIP draws across its own foot,
   and it is now the line the block's own foot row addresses. Its opposite
   number, `--reel-toprule`, is written beside it by the same measure. Both are
   spent in `.reel__row--t` / `.reel__row--b`, and nowhere else. */

/* --- the title card, as a layer ------------------------------------------
   The two words were a screen of their own — one whole viewport the reader had
   to scroll past before a single piece of work appeared. They are printed ON
   the run now, which is what a title card is for.

   It only works because the band's output alpha is glyph coverage and nothing
   else (see the head of `vs-anim-pinch-type-band.js`): what lands on the
   photograph is ink in the shape of two words, not a second picture.

   HELD BACK, and this is the whole tuning. At full strength two words at the
   size of a wall are the composition and the work behind them is a texture,
   which is the wrong way round for the block whose entire argument is the
   photographs. At this weight they are a watermark the eye reads once and then
   looks past — a title card, not a poster.

   HELD BACK BY SIZE NOW, AND NOT BY GREY. The band was set at the component's
   own default and then dropped to a third of its strength to stop it taking the
   screen, which buys quiet by making white type into grey type over a
   photograph — a title card that reads as a smudge. The type is a third smaller
   instead (`MOUNTS.pinch`) and set in ONE colour rather than walked toward a
   mid-grey tint across the pin, so what is left can stand at half strength and
   still be white. Smaller and cleaner beats bigger and dimmer. */
.hb__stage--band {
  z-index: 2;
  /* Nought, and the resting value is on the block's own arrival below.

     It was written the other way round — the resting value here and a
     transition beside it — which is a fade with nothing to fade FROM: it was
     the first computed value the element ever had, no rule anywhere computed a
     different one, and a transition only runs on a change. The band was simply
     there. Hung on `is-in` it does what the note claims: the screen resolves
     as a photograph, and then as a photograph with a name on it. */
  opacity: 0;
  transition: opacity 1.6s var(--ease-out) 0.35s;
}

/* ONE, and the number has come up through 0.52 and 0.86 to get here.

   Every one of those was the same mistake in a different denomination: the card
   was held back by DILUTING it. An element at 0.86 carrying a window at 0.6 is
   two words printed at half strength, and half strength over a photograph is
   not quiet white type — it is the photograph, showing through the letters, in
   the shape of the letters. That is why the band came out the picture's own
   blue and read as a smudge rather than as ink.

   So the type is net: full value over a cover and inside a plate alike, in a
   colour named by the WINDOW rather than by this element — white on a
   photograph, the sheet's paper on a page — and with no grain on it
   (`REEL_BAND`, `MOUNTS.pinch`). What keeps it a watermark instead of a poster
   is the SIZE — 0.21 against the component's own default — and the MASK, which
   only lets it print where there is a picture to carry it. Both are honest
   levers, because both leave the colour alone.

   The nought above is still the entrance and this is still only its resting
   value: the screen resolves as a photograph, then as a photograph with a name
   on it. */
.hb--reel.is-in .hb__stage--band { opacity: 1; }

/* --- the head band carries the headline and nothing above it ---------------
   `.reel__cline` was here, absolutely positioned a double pad down from the top
   of the pin, holding the block's number one line above the headline. It is
   gone, and so is the `.reel__cmeta` that once sat at the other margin of the
   same line printing "Design engineering · 23 pieces · 2016 — 2026".

   Both went for the same reason and it is the reason the `.hero__stamp` above
   them went: every fact they carried is already on the screen they were printed
   over, said larger. The discipline is the band across the photograph set at
   the size of a wall; the number and the count are the `01 / 06` and its six
   marks two lines below. A head that repeats the three things nearest it is a
   caption for a reader who is assumed not to be looking.

   Nothing here reserved height — the line was out of flow — so the headline row
   sits exactly where it did, and the `.reel__h--t` rule is still the rule that
   closes this head. It is also still why there is no `.is-first` step on the
   `top`: there is nothing above the headline to step down under. */

/* --- the run -------------------------------------------------------------- */

/* The scrim. Only the two bands where the type is, and gone well before the
   middle, so the photograph keeps its whole middle. Literal black rather than
   `--bg` for the reason at the head of the block.

   AND ONLY OVER A PHOTOGRAPH. Both weights are a token times `--reel-k`, so on
   a plate panel — paper, with the picture an object on it — every stop of both
   gradients resolves to `rgb(0 0 0 / 0)` and the wash is not merely faint, it
   is not there. The type it was holding up is printed in the sheet's ink by
   then; see the head of the block.

   --- WHY EIGHT STOPS AND NOT THREE -----------------------------------------
   It was three: full weight at the edge, a little under half of it at the band,
   nothing at the end of the veil. Two straight ramps meeting at an angle — and
   an angle in a gradient is a LINE. The eye reads the crease across the picture
   before it reads anything the scrim was laid down to make readable, and what
   it reads there is a blurred rectangle sitting on a photograph rather than
   light falling off it.

   These eight are one curve, roughly the square of the distance left to run, so
   the rate of change never jumps: heavy where the type actually is, then most of
   the weight is gone by the halfway mark, then a long thin tail that reaches the
   end at nothing. There is no place along it to see an edge, because there is no
   edge.

   Every stop is `--reel-scrim-t` / `--reel-scrim-b` times a fraction, and the
   fractions are the SHAPE. Retune the weight of either band by moving its one
   token; the curve is unchanged and stays a curve. */
.hb--reel .hb__pin::after {
  content: "";
  position: absolute;
  inset: 0;
  /* 3, and not 2: the title-card band sits at 2 now, under this. The scrim is
     what keeps the type in the two bands readable over BOTH of them. */
  z-index: 3;
  pointer-events: none;
  background:
    linear-gradient(
      to bottom,
      rgb(0 0 0 / var(--reel-scrim-t)) 0%,
      rgb(0 0 0 / calc(var(--reel-scrim-t) * 0.9)) calc(var(--reel-top) * var(--reel-veil) * 0.12),
      rgb(0 0 0 / calc(var(--reel-scrim-t) * 0.76)) calc(var(--reel-top) * var(--reel-veil) * 0.25),
      rgb(0 0 0 / calc(var(--reel-scrim-t) * 0.58)) calc(var(--reel-top) * var(--reel-veil) * 0.4),
      rgb(0 0 0 / calc(var(--reel-scrim-t) * 0.41)) calc(var(--reel-top) * var(--reel-veil) * 0.55),
      rgb(0 0 0 / calc(var(--reel-scrim-t) * 0.24)) calc(var(--reel-top) * var(--reel-veil) * 0.7),
      rgb(0 0 0 / calc(var(--reel-scrim-t) * 0.1)) calc(var(--reel-top) * var(--reel-veil) * 0.85),
      transparent calc(var(--reel-top) * var(--reel-veil))
    ),
    linear-gradient(
      to top,
      rgb(0 0 0 / var(--reel-scrim-b)) 0%,
      rgb(0 0 0 / calc(var(--reel-scrim-b) * 0.9)) calc(var(--reel-bot) * var(--reel-veil) * 0.12),
      rgb(0 0 0 / calc(var(--reel-scrim-b) * 0.76)) calc(var(--reel-bot) * var(--reel-veil) * 0.25),
      rgb(0 0 0 / calc(var(--reel-scrim-b) * 0.58)) calc(var(--reel-bot) * var(--reel-veil) * 0.4),
      rgb(0 0 0 / calc(var(--reel-scrim-b) * 0.41)) calc(var(--reel-bot) * var(--reel-veil) * 0.55),
      rgb(0 0 0 / calc(var(--reel-scrim-b) * 0.24)) calc(var(--reel-bot) * var(--reel-veil) * 0.7),
      rgb(0 0 0 / calc(var(--reel-scrim-b) * 0.1)) calc(var(--reel-bot) * var(--reel-veil) * 0.85),
      transparent calc(var(--reel-bot) * var(--reel-veil))
    );
}

.reel__grid {
  position: absolute;
  inset: 0;
  left: 0;
  right: 0;
  width: var(--col);
  margin: 0 auto;
  z-index: 4;
  pointer-events: none;
}

/* The two edges of the column, drawn. They are what turns a full-bleed
   photograph into a page: everything in the block is inside them, and the
   picture runs past them on both sides. */
.reel__v {
  position: absolute;
  top: 0;
  bottom: 0;
  width: var(--hair);
  background: currentColor;
  opacity: var(--rule-o);
  transform: scaleY(0);
  transform-origin: 50% 0;
  transition: transform 1.2s var(--ease-out) 0.1s;
}

.hb--reel.is-in .reel__v { transform: scaleY(1); }
.reel__v--l { left: 0; }
.reel__v--r { right: 0; }

/* THE TWO HORIZONTALS ARE RAILS NOW, AND THEY DRAW NOTHING.

   They used to be ink: a rule across the head band and one across the foot,
   entering on `is-in` with a `scaleX`. The strip UNDER them draws its own pair
   a tenth of the band in from each end — see `#rules` and `#grid` in
   `vs-anim-liquid-reel.js` — so the first screen carried FOUR horizontals where
   the composition wants two, in two pairs sixty-odd pixels apart. Close enough
   to read as one line printed twice, far enough to read as a mistake, and the
   upper pair swept while the lower pair stood still. The plate treatment had
   already dropped its own copy for exactly this reason; this is the page making
   the same concession.

   THEY STAY IN THE MARKUP because they are the BAND, not the line. `#reelFrame`
   in `home/section.js` measures these two rectangles and hands the strip the
   fractions it composes inside; `--reel-top` / `--reel-bot` are a `clamp()`, and
   a custom property holding a `clamp()` cannot be read back resolved from
   script. So the rails are the one honest source of those two numbers, they keep
   their `--hair` height so the inner and outer edges stay exactly a hairline
   apart, and they simply have no colour. Nothing here animates, because nothing
   here is visible. */
.reel__h {
  position: absolute;
  left: 0;
  right: 0;
  height: var(--hair);
}

.reel__h--t { top: var(--reel-top); }
.reel__h--b { bottom: var(--reel-bot); }

/* THE FOUR CORNERS STOOD HERE, and they went with the lines they were on.

   A joint is the mark a draughtsman puts where two rules CROSS: it is what says
   the meeting was drawn rather than four hairlines happening to overlap. The two
   horizontals draw nothing now, so there are no crossings on this frame — and a
   node dropped where nothing meets is a speck of ink in the middle of a
   photograph.

   `.joint` in `styles.css` is untouched; the blocks that still rule a rectangle
   still use it. What is deleted is `.reel__joint` and its four corners, here and
   in `home/blocks.js`. The block keeps its two verticals, and a pair of parallel
   rules has no corner to mark. */

/* Where the reader is in the run, drawn ON the left rule rather than printed
   as a number beside it. `--pin` is the pin's own progress, written per frame
   by `section.js`. The count is still there for anyone who wants the digit;
   this is for everyone who does not. */
/* RECONSTRUCTED, and the note under this rule says why it had to be.

   The mark rides the left vertical rather than sitting beside it: `--hair`
   wide, so it is the same line the frame is drawn with, at a strength the rule
   itself never reaches. That is the whole idea — the reader's place in the run
   is not a second object on the frame, it is a stretch of the frame being
   pressed harder.

   It travels `100% - --run-h`, which puts its head on the top rule at nought
   and its foot on the bottom rule at one; anchored on `--pin` alone with no
   transition, because `--pin` is already a smoothed number written once a
   frame by `section.js` and easing an eased value is a lag nobody asked for.
   The one thing that IS eased is its arrival, and it arrives after the frame
   has finished drawing itself — a position marker on a frame that is still
   being built is a reading of nothing. */
.reel__run {
  --run-h: clamp(44px, 8vh, 104px);
  position: absolute;
  left: 0;
  width: var(--hair);
  height: var(--run-h);
  top: calc(var(--pin, 0) * (100% - var(--run-h)));
  background: currentColor;
  opacity: 0;
  transition: opacity 0.8s var(--ease-out) 1.15s;
}

.hb--reel.is-in .reel__run { opacity: 0.9; }

/* --- THE RUN'S TWO ROWS, AND THEY WERE LOST WITH THE BRACE ------------------
   Everything from here to the foot of the spread's head went with the missing
   `}` documented above: the two rows of furniture on the run, the count, the
   tally, the filing under the strip, and the spread's own head and lattice.
   The rules are rewritten rather than recovered, so read them as new work that
   had to land in an old shape — every measurement below is either read off a
   token this block already declares (`--reel-top`, `--reel-bot`, `--reel-air`,
   `--col`) or is the base the media branches at the foot of this file are
   written as overrides OF. Those branches are the specification: a base that
   disagreed with them would show up as a phone laid out better than a desktop.

   TWO ROWS, ONE INSIDE EACH BAND. The head band carries the headline and the
   reader's place in the run; the foot band carries the way in and the filing
   of whatever is on screen. Both are the page's own column, both are held off
   the frame's hairline by `--reel-air`, and both take a padding of their own so
   a word never stands ON one of the two verticals.

   THAT PADDING IS READ BACK. `#reelFrame` in `home/section.js` measures
   `getComputedStyle(row).paddingLeft` and hands it to the strip as `frame-x`,
   so the shader composes its panels inside the same margin the type stands on.
   It is a number this stylesheet owns and another file consumes — which is
   why it is written once, here, and never restated. */
.reel__row {
  position: absolute;
  left: 0;
  right: 0;
  width: var(--col);
  margin: 0 auto;
  /* THE INSET IS NAMED, because two things read it and they are not in the
     same rule. The row pads by it so a word never sits ON a vertical
     hairline, `#reelFrame` measures it back off the DOM to tell the strip
     where a word may start, and the narrow branch solves the headline's size
     against the column it leaves behind. Three readers, one declaration. */
  --reel-inset: clamp(14px, 1.6vw, 26px);
  padding: 0 var(--reel-inset);
  z-index: 5;
  display: flex;
  justify-content: space-between;
  gap: clamp(18px, 3vw, 56px);
}

/* SAT ON THE HEAD RULE FROM UNDERNEATH, AND ON THE FOOT RULE FROM ABOVE — the
   type in a band always addresses the line that band is drawn by.

   AND THE LINE IT ADDRESSES IS THE STRIP'S NOW. These two rows used to stand a
   gutter off `--reel-top` / `--reel-bot`, which is where the page drew its own
   pair of horizontals. The page draws no horizontals any more (see `.reel__h`),
   so a row measured to `--reel-top` is a row measured to a line nobody can see —
   and the line the reader CAN see, the strip's own, is a tenth of the band
   further in. Left as they were, the headline sat a hundred and twenty pixels
   clear of the only rule above it and the press sat a hundred and twenty clear
   of the only rule below: two holes in the photograph exactly the depth of the
   lines that were removed.

   `--reel-toprule` and `--reel-runrule` are those two lines, in px off the head
   and the foot of the pin, written by `#reelFrame` off the same rails and the
   same `REEL_RULE` the strip composes with. So `--reel-air` still means what it
   has always meant — the one gutter between a rule and the type nearest it —
   and it is now measured to the rule that is actually printed.

   THE FALLBACK IS THE OLD NUMBER, and it has to be: these are written by script
   on the first measure, and a row with no height for two frames is a headline
   that arrives from the top of the screen. Before the measure lands the rows sit
   where they always sat. */
.reel__row--t {
  top: 0;
  height: calc(var(--reel-toprule, var(--reel-top)) - var(--reel-air));
  align-items: flex-end;
}

.reel__row--b {
  bottom: 0;
  height: calc(var(--reel-runrule, var(--reel-bot)) - var(--reel-air));
  align-items: flex-start;
}

/* The press has a gutter above it everywhere else on this page, because
   everywhere else it follows a paragraph. Here it is one of two things sharing
   a band, and the two have to start on the same line — a control hanging half
   a gutter below the filing beside it reads as a second row that did not fit. */
.reel__row--b .hb__act {
  margin-top: 0;
}

/* --- THE HEADLINE, AND THE ONE NUMBER THE NARROW BRANCH SOLVES IT WITH ----
   `--reel-emw` is the advance width of the LONGEST of the two authored lines
   — "none of them a template." — in em, measured in the real face at the
   weight it is set at, tracking included: `measureText` reports advances
   only, and CSS then adds `letter-spacing` after every glyph including the
   last, so the -0.035em on this line is 24 characters' worth of width the
   canvas does not report.

   10.764 MEASURED, 11.4 DECLARED, and the six per cent between them is not
   rounding slack — it is a GUTTER. Solved on the measured number the line
   fills its column to within four pixels of the right-hand vertical, and a
   headline stopping four pixels off a rule has not been set to the measure,
   it has collided with it. Eighteen pixels reads as a margin. The slack pays
   for the rasteriser as well, which is the reason `.led__w` carries its own:
   a line computed to the last pixel rounds up about half the time, and a line
   one pixel too wide is a HARD-BROKEN line that soft-wraps — which is the one
   failure this whole equation exists to avoid.

   Declared here rather than in the branch that spends it, so the day somebody
   rewrites the second line in `content.js` there is one number to re-measure
   and it is next to the type it describes. The two lines are authored breaks
   — `heading()` splits on the newline — so this is a constant of the COPY and
   not of the viewport. */
.reel__title {
  --reel-emw: 11.4;
  font-size: clamp(1.5rem, 2.35vw, 2.7rem);
  max-width: 20ch;
}

/* --- THE HEAD BAND BELONGS TO THE HERO AND NOT TO THE RUN -------------------
   A pin holds its whole child still, and everything printed in the two bands
   was held with it: the headline, the count and the press stood in the same
   place for four and a half viewports while six pieces went past behind them.
   Held that long, they stop reading as the top of a page and start reading as
   fixed chrome — an interface bar over a picture rather than the head of a
   title card.

   THE WHOLE HEAD BAND LEAVES, AND THAT INCLUDES THE COUNT AND THE TALLY. They
   used to be held on the argument that a statement ABOUT the run — which piece
   is up — has to survive the run to say it. It does; it does not have to be
   said four times. The marker on the left rail is already the reader's place
   drawn against the length of the run, the filing in the foot band already
   names what is on screen, and the strip itself is the piece. A fraction and
   six marks parked over the reader's head for four viewports were a third and
   a fourth copy of one fact, and the copy that was hardest to ignore.

   So the head band is the hero's, entire: the sentence that opens the page and
   the reader's position in the run as of the first screen. Said once, on the
   screen they are true of, and gone. The press goes with them because it is
   the way out of the hero and not a control on the strip.

   THEY UNDO THE PIN, and undoing it is one subtraction. `--pinpx` is the exact
   push `section.js` writes into `.hb__pin` this frame; translating by its
   negative leaves them standing in the PAGE rather than in the frame, so they
   climb out of the top of the pin at the speed the wheel is turning and the
   block's own `overflow: hidden` takes them. No fade, no second curve, no
   progress of their own to fall out of step with the strip's — they are simply
   not pinned, which is what they should never have been.

   `var(--ry)` is kept in the sum because it is the press's ENTRANCE, and all
   three have to share the one transform property or the last one written wins.
   On the headline and the count it resolves to the registered `0px` and costs
   nothing — the count's own entrance is an opacity and does not touch this.

   Scoped to `.hb--reel.is-first` on purpose. `.hb__act` is the press on six
   blocks and `--pinpx` is written on every pinned block, so an unscoped rule
   would pull the room's press off its own screen as well. */
.hb--reel.is-first .reel__title,
.hb--reel.is-first .reel__tcol,
.hb--reel.is-first .reel__row--b .hb__act {
  transform: translate3d(0, calc(var(--ry) - var(--pinpx, 0px)), 0);
}

/* The count and the tally, stacked on the trailing edge of the head band and
   pushed apart so the fraction sits at the top of the band and the marks sit a
   gutter over the strip's own head rule — which is where its panels change, and
   which is the same line the headline opposite them is set to. */
.reel__tcol {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(10px, 1.4vh, 20px);
  opacity: 0;
  transition: opacity 0.85s var(--ease-out) 0.55s;
}

.hb--reel.is-in .reel__tcol { opacity: 1; }

.reel__count {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: 0.34em;
  font-size: var(--t-micro);
  letter-spacing: 0.14em;
  font-variant-numeric: tabular-nums;
}

.reel__count i {
  font-style: normal;
  opacity: 0.5;
}

.reel__count em {
  font-style: normal;
  opacity: 0.32;
}

/* The one digit that changes. Full strength against the total beside it, which
   is what makes a fraction read as a position rather than as a label. */
.reel__count-i {
  font-weight: 500;
  opacity: 1;
}

/* SIX MARKS, AND THEY ARE THE SAME HAIRLINE THE FRAME IS DRAWN WITH. A row of
   dots is a carousel; a row of rules is this page. They draw themselves in
   after the frame has landed, one after the next, and the one that is up is
   the one at full strength — no fill, no colour, no second shape. */
.reel__tally {
  display: flex;
  gap: clamp(5px, 0.6vw, 10px);
}

.reel__mark {
  display: block;
  width: clamp(14px, 1.9vw, 26px);
  height: var(--hair);
  background: currentColor;
  opacity: 0.3;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition:
    opacity 0.4s var(--ease-out),
    transform 0.55s var(--ease-out) calc(var(--i, 0) * 60ms + 0.55s);
}

.hb--reel.is-in .reel__mark { transform: none; }

.reel__mark.is-on { opacity: 1; }

/* WHAT IS ON THE SCREEN, NAMED — AND IT IS NOT PRINTED ANY MORE.

   `#reelFiling` in `section.js` still writes it on every cut of the strip: the
   name, a rule, the year, two chips. It used to be SET, on the trailing edge of
   the foot band opposite the press. It was the same four facts the panel itself
   prints, three hundred pixels lower and a size smaller — the work's name is
   already across the photograph at forty-eight px, and the year and the
   disciplines were beside it until they went for the same reason. A filing that
   repeats the thing it files is a caption on a caption.

   IT IS STILL IN THE ACCESSIBILITY TREE, and that is the whole of why the node
   survives. It carries `aria-live="polite"`, and a reader who cannot see the
   strip cut is the one reader for whom this was never a repeat: without it, six
   panels go past and nothing is announced. Hidden with the sheet's own sr-only
   recipe rather than `display: none`, because `display: none` takes it out of
   that tree as well and would leave the live region announcing into nothing.

   Absolutely positioned, so the row it is in stops reserving space for it and
   the press stands alone on the foot line. */
.reel__filing {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The filing's four parts were dressed here — the name at 500, a hairline
   `<i>` between it and the year because a middot at this size is a speck, the
   year on tabular figures, the chips at a lower strength. None of it is drawn
   any more: the parent is a one-pixel clip, and type inside a clip has no
   weight, no tracking and no rule between its halves.

   The `<i>` stays in the markup and is not restyled to nothing — an empty
   element with no text carries nothing into the announcement either way, and
   deleting it from `blocks.js` would put a bare `NameYear` in the live region
   with no separator in the DOM to fall back on. It is simply undressed. */

/* NOTE — THIS BRACE IS THE FILE'S SPINE, AND IT WAS MISSING.

   Without it `.reel__run` never closed, and CSS nesting is far too polite to
   say so: every rule from here to the end of the file — the disciplines board,
   the numbers, the projection room, the questions, the CLOSE and the foot,
   some two and a half thousand lines — was quietly re-read as a DESCENDANT of
   `.reel__run`, an `<i>` with no size inside one block of the reel. The
   selectors all still existed. None of them could ever match anything.

   That is what the close looked like when it was reported: a headline at the
   browser's own `h2` size, a paragraph at the browser's own measure, a column
   with no `align-content: end` under it and nothing holding it off the poster
   the ring stands on. Not a layout bug in the block — the block had no layout
   at all.

   Three `@property` registrations went with it (`--grow`, `--half`, `--serif`,
   and `--enter` further down), which is the second-order failure: an
   unregistered custom property cannot be transitioned, so several entrances
   below this line were not slow — they were instant, and read as pops.

   If the bottom half of this page ever goes plain again, run the brace count
   before reading a single selector. */
/* --- 05 the disciplines, AS THE SETTING ------------------------------------
   This block has been three things and the third one is the first that is not
   a picture of data.

   It was a mirrored lattice of eleven photographs, which argued half of the
   headline. Then it was the invoice drawn as a schedule: a column head with a
   week ruler in it, six ticks, a unit label, eleven full-width axes, eleven
   segments, twenty-two dimension serifs and two travelling verticals — around
   forty-five drawn objects, none of them printed above 46% of the ink. The
   concept was right and the REGISTER was wrong. An invoice rendered as a Gantt
   chart is a spreadsheet, and a spreadsheet is what a portfolio looks like
   when it has run out of typography.

   --- THE SETTING IS THE SCHEDULE -------------------------------------------
   The chart is gone and the type does its work. Eleven names, one weight, one
   ink, one left margin, and eleven sizes that NOBODY CHOSE:

       font-size = --fr × --measure / --emw

   `--fr` is the discipline's span over the longest span on the sheet, `--emw`
   is the width of its own name in em, and `--measure` is the column. Solve for
   the size and the name ENDS on the week it takes. The ragged right edge of
   this block is the chart, it is made of nothing but words, and it re-sets
   itself the day a discipline is added to the catalog.

   `--measure` is a LENGTH written by `home/section.js` off `offsetWidth`, and
   deliberately not `100cqw`: a container query on this sheet means layout
   containment on this sheet, and the keystone's photograph is the one thing in
   the block that has to be able to run past the column's own edge.

   --- TWO INKS ---------------------------------------------------------------
   `--fg` at full strength for every character, and `--led-tint` for the rules.
   There is no third value in this block and nothing here is dimmed to make it
   secondary — a caption is SMALL, which is what typography has for the
   purpose. The old sheet had eight opacities and read as unfinished rather
   than as restrained.

   --- THE RULE OPENS INTO THE PICTURE ---------------------------------------
   The one gesture kept from the invoice, because it was the one thing in there
   that worked. Under every name is a hairline exactly as long as the name, and
   under the hand it swells downward into a band of that discipline's own
   photograph at full colour — out to the LOW week, so the picture is what you
   will certainly spend and the type overhanging it is the range. The span
   numeral walks from the high mark to the low one while it opens, so the
   distance it travels is the estimate, and one terminator stands where it
   lands.

   ONE registered property does all of it — `--led-open`, 0 to 1 — and it is
   declared in ONE place for three sources: the pointer, the keyboard, and the
   scroll (`.is-at`, written by an observer on narrow screens, where there is
   no hand to hover with). Three drivers that cannot disagree about what open
   means, because there is only one rule that says it.

   NOTHING IN HERE TRANSITIONS `transform`. `--flow` is rewritten on the host
   sixty times a second and it rides the transform of every row; a transition
   on that property retargets every frame, which damps the page's own drift to
   nothing and leaves it rubber-banding for the length of the duration. The
   opening is a clip and two scales driven by a number, and the number is what
   is transitioned.

   --- THE PICTURES KEEP THEIR COLOUR ----------------------------------------
   In the band and out of it, on the pale sheet and on the dark one. The frame
   is one bit; the work inside it never is.
--------------------------------------------------------------------------- */

/* The open state, 0 at rest and 1 under a reader. It is inherited, so one
   declaration on the row moves the clip, both terminator scales and the walk
   of the numeral — and nothing else in the block reads it, so there is exactly
   one thing to change when the gesture changes. */
@property --led-open {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* The entrance, and it is separate from the open state on purpose: a row that
   is being hovered while the sheet is still arriving must not have its rule
   drawn twice from two different sources. */
@property --led-in {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

.hb--services {
  /* THE MONUMENT SIZE, DECLARED ONCE. Two objects in this block are set at the
     size of a wall — the horizon at the head and the count at the foot — and
     they are the same object seen twice: the block opens on the range and
     closes on the count. They were two clamps in two places in the old sheet
     and they had already drifted apart by a rem. */
  --led-mon: clamp(3.6rem, 11.5vw, 10rem);
  /* THERE IS NO TINT. This block has TWO inks and the second one is not a
     grey — it is a HAIRLINE. The rules are printed at full strength and they
     are one pixel tall, which is what makes them read as drawing rather than
     as a table that failed to load; the old sheet had eight opacities between
     0.12 and 0.46 and every one of them was a decision to make something
     harder to see instead of smaller. Hierarchy here is 1px against 152px. */
}

.led__title {
  font-size: var(--t-title);
  max-width: 14ch;
}

/* --- the head, and the horizon in it ---------------------------------------
   The headline on the leading edge, the longest span on the sheet at monument
   size on the trailing one, and the heaviest rule in the block under both.

   This is where the void was. The old head was a headline occupying the left
   half of the first screen and half a viewport of empty paper beside it, and
   the six ruler ticks that were supposed to be doing that work were 11px tall
   at 50% ink, three hundred pixels below. One numeral, at the size the claim
   deserves, in the space that was already paid for. */
.led__head {
  position: relative;
  display: grid;
  /* THREE TRACKS, and the middle one is the point. Two tracks made the head a
     headline and a numeral with half a screen of paper between them, and that
     paper was the first thing the block said. The claim now takes the width it
     needs, the gauge takes everything that is left, and the horizon stands at
     the end of it — so the empty half is spanned by the measurement it was
     always supposed to be about. The foot closes on the same three. */
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto;
  align-items: end;
  gap: clamp(20px, 3vw, 56px);
  padding-bottom: clamp(14px, 1.8vh, 26px);
}

/* THE HEAD RULE, and it is the heaviest line on the sheet — the only one in
   the block that is not a hairline. A ledger puts its strongest rule under the
   head and its second strongest above the total, and that hierarchy is the
   whole reason a sheet like this can be read at a glance. */
.led__head::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: max(var(--hair), 2px);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 1.05s var(--ease-out);
}

.hb.is-in .led__head::after { transform: none; }

/* --- THE GAUGE ---------------------------------------------------------------
   One dotted extension line and one terminator, run between the claim and the
   number that sizes it. It is the same three marks every rule in the index is
   built out of — track, cap, ink — so the head reads as the first line of the
   chart rather than as a title above one.

   IT DRAWS IN FROM THE HORIZON. `transform-origin: 100%`, so the line arrives
   travelling back from the numeral toward the sentence: the measurement is laid
   off the mark it is measured to, which is the order a hand draws it in. Every
   other rule on the sheet runs out from the left margin, and this one is the
   only object on the block whose zero is on the right — because the only thing
   it is fixed to is the horizon. */
.led__gauge {
  position: relative;
  display: block;
  align-self: end;
  min-width: 0;
  height: 1em;
  /* Landed on the cap-middle of the unit label beside it rather than on the
     head rule, so the dotted line runs INTO "WEEKS, END TO END" instead of
     doubling the heaviest rule in the block four pixels above it. */
  margin-bottom: 0.42em;
}

.led__gauge::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--hair);
  /* FULL INK, DIFFERENT TEXTURE. The block has two inks and neither of them is
     a grey; an extension line is told apart from a measurement by being broken,
     which is what a drawing has always used, and not by being printed fainter. */
  background: repeating-linear-gradient(
    90deg,
    currentColor 0 2px,
    transparent 2px 7px
  );
  transform: scaleX(0);
  transform-origin: 100% 50%;
  transition: transform 1s var(--ease-out) 0.34s;
}

.led__gauge::after {
  content: "";
  position: absolute;
  right: 0;
  /* ON the rule and hanging BELOW it — `top: 100%` is the box's bottom edge,
     which is where `::before` drew the line. Same direction as every cap in
     the index, so the head is drawn in the sheet's own hand. */
  top: 100%;
  width: var(--hair);
  height: 0.66em;
  background: currentColor;
  transform: scaleY(0);
  transform-origin: 50% 0;
  transition: transform 0.46s var(--ease-out) 1.06s;
}

.hb.is-in .led__gauge::before,
.hb.is-in .led__gauge::after { transform: none; }

.led__horizon {
  display: grid;
  justify-items: end;
  gap: 0.2em;
  text-align: right;
  min-width: 0;
}

/* The last surviving number of the ruler. Same face, same weight and the same
   crown correction as the count at the foot — see `.led__word` for why the
   padding pair is not optional under an 0.78 line box. */
.led__hz {
  display: block;
  overflow: hidden;
  padding-top: 0.14em;
  margin-top: -0.14em;
  padding-bottom: 0.1em;
  margin-bottom: -0.1em;
  font-style: normal;
  font-family: var(--face-display);
  font-weight: 500;
  font-size: var(--led-mon);
  line-height: 0.78;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}

.led__hz > b {
  display: block;
  font-weight: inherit;
  transform: translateY(112%);
  transition: transform 1.1s var(--ease-out) 0.18s;
}

.hb.is-in .led__hz > b { transform: none; }

/* The unit, printed once, under the number it belongs to. "WEEKS" under every
   tick was a ruler shouting its own name six times. */
.led__hzu {
  font-style: normal;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* --- THE RAIL, and it is where the eleven photographs went -----------------
   One object between the head and the index: a sheet with the block's own
   title on it at the size of a room and five of the eleven stills crossing it
   as plates in real perspective, shoved by the page's own scroll. It replaces
   eleven bands that each held one photograph rolled up inside one hairline —
   three viewports of ledger to be shown the work in single file. See the note
   beside `rail` in `blocks.js` and `MOUNTS.tilt` in `home/section.js`.

   IT REACHES ONE PAGE MARGIN PAST THE COLUMN ON EACH SIDE and no further, which
   is deliberately not full bleed. A break on this page is full bleed and is the
   only thing on screen; this is a clean block and the rail is an object IN a
   column of type. On a window the column has already filled the two margins are
   the whole of the difference and it reads as edge to edge anyway; on a wide
   one it stays an object with ground around it, which is the reading that keeps
   the block a page rather than a second break.

   NO `overflow` HERE. The stage inside is inset to the box and a canvas cannot
   paint outside itself, so there is nothing to clip — and this element is a
   SIBLING of the index below, so nothing about it can reach the note on
   `.led__body`. */
.led__rail {
  position: relative;
  /* --- FULL BLEED, AND THE OLD NEGATIVE MARGIN ONLY GOT HALF WAY THERE ------
     It was `calc(-1 * var(--pad))`, which cancels the column's own gutter and
     nothing else. `--col` is `min(1360px, 100% - --pad * 2)`, so past about
     1440 the CAP is what bounds the column and the rail came out 1440 wide in a
     1704 window — a hundred and thirty points of paper down each side.

     Two things were wrong with that, and they are the same thing seen from
     either end. The wall of type is a marquee: it is meant to run off both
     edges of the SCREEN, and stopping it short of them turns a sentence with no
     end into a sentence in a box. And the plates are authored to cross out of
     the rail — that overhang is what makes them read as objects in front of the
     wall rather than as pictures pasted onto it — so a rail inset from the
     screen let them break their own frame and then land in the margin, which
     reads as a layout fault rather than as depth.

     `50% - 50vw` is the column's centre line taken back out to the viewport's
     edge, so this resolves correctly whatever `--col` is doing above it — the
     cap, the percentage, or a value neither of them. Safe against a scrollbar
     because `.home` is a fixed layer at `overflow: hidden` and the document
     behind it does not scroll: there is no vertical bar for `100vw` to
     overshoot, and nothing here could scroll sideways if there were. */
  width: 100vw;
  margin: clamp(26px, 3.6vh, 52px) 0 0;
  margin-inline: calc(50% - 50vw);
  /* A landscape band and not a screen. The clamp's ceiling is a shade over half
     the viewport because what has to be true is that the head above it and the
     first names below it are on screen WITH it — a rail that filled the fold
     would be a break wearing a ledger's clothes. */
  height: clamp(300px, 52vh, 620px);
  /* The ground under a stage that has not been mounted yet, and under one that
     never will be because the reader has no WebGL2: paper, ruled top and
     bottom, which is what the rest of this sheet is made of. */
  background: var(--bg);
}

/* The two rules the rail stands between, drawn out from the left one after the
   other on the sheet's own stagger — the same entrance the eleven hairlines
   under the names have, at the scale of the object they are bracketing. */
.led__rail::before,
.led__rail::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: var(--hair);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
}

.led__rail::before {
  top: 0;
  transition: transform 1.05s var(--ease-out) 0.3s;
}

.led__rail::after {
  bottom: 0;
  transition: transform 1.05s var(--ease-out) 0.4s;
}

.hb.is-in .led__rail::before,
.hb.is-in .led__rail::after { transform: none; }

/* The stage itself is `.hb__stage` — absolute, inset 0, `pointer-events: none`
   by house rule. That last one is not a limitation here: the piece is driven by
   the PAGE's scroll, which it reads off its own box, and by a ghost pointer
   while no real one has arrived. Nothing about the rail asks the reader to
   discover that the wheel does something different over this rectangle. */
.led__rail-in { z-index: 1; }

/* --- the sheet -------------------------------------------------------------
   `--measure` is the zero-to-horizon of the whole block, in pixels, and every
   width and every type size below resolves against it. The authored value is
   the column's own arithmetic so the sheet is correct on the first paint;
   `#ledMeasure` in `home/section.js` replaces it with the measured width on
   every resize.

   --- AND IT IS TWO COLUMNS NOW ---------------------------------------------
   The equation is untouched — a name is still set at `--fr` of its measure
   divided by the width of the name in em, so it still ENDS on the week it takes
   and the ragged edge is still the chart. What changed is WHICH measure: half
   the sheet, so the eleven lines take a page instead of three viewports.

   `--col-w` is that half, declared once here and read by the size, by the width
   of the rule and by the width of the caption. The keystone takes it back to
   the whole sheet on itself — see `.led__item.is-open`.

   `align-items: end` is what makes it a two-column LEDGER rather than two lists
   standing next to each other: the rule under a name sits at the bottom of its
   item, so bottom-aligning a pair puts the two rules of a row on one line. The
   eleven different sizes then hang UP from a shared rule, which is the same
   drawing the single column made against a shared left margin.

   NO `overflow` AND NO `container-type` ON THIS ELEMENT OR ANY OF ITS
   ANCESTORS. Nothing in the index runs past the column any more, but the rule
   is kept because the reason it existed is a property of this page rather than
   of the old markup: `#ledMeasure` measures this element, and a container query
   here is layout containment on the sheet the whole block is solved against. */
/* SIXTY PIXELS OVER THE SHEET, WHICH IS THE SAME SIXTY THAT STANDS UNDER IT.
   It was `clamp(30px, 4vh, 58px)`: air scaled to the window, which is right
   for a ledger three viewports tall and wrong for one line of type standing
   between two fixed objects. The press below the line is a hard 60 — see
   `.hb--services .hb__act` — so a stand-off that read 30 on a short window and
   58 on a tall one put the name off its own axis at every height except one.
   Two equal numbers, and the line sits in the middle of them by construction
   rather than by luck. */
.led__body {
  list-style: none;
  margin: 60px 0 0;
  padding: 0;
  position: relative;
  --measure: min(1360px, 100vw - var(--pad) * 2);

  /* The gutter between the two columns, and the half-sheet that falls out of
     it. One subtraction, in one place: a second copy of it in a `width` is the
     copy that goes stale the day the gutter moves. */
  --led-gap: clamp(28px, 3.4vw, 64px);
  --col-w: calc((var(--measure) - var(--led-gap)) / 2);

  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--led-gap);
  /* A ROW GAP, DECLARED. There was none: the only air between one rule and the
     caption under it was `--led-gut`, which is a gutter INSIDE a row and sized
     for a caption — so the vertical rhythm of the sheet was whatever eleven
     different type sizes happened to leave behind, and it measured 127, 166,
     146 and 130 pixels down the same five rows. The gap is a decision now and
     the gutter went back to being the caption's. */
  row-gap: clamp(20px, 2.6vh, 40px);
  align-items: end;

  /* `--led-gut` STOOD HERE. It was the caption gutter, and the caption is
     gone — see the tombstone beside `.led__cap`. */
}

.led__item {
  position: relative;
  min-width: 0;

  /* --- THE SIZE, AND WHAT IT IS ALLOWED TO CARRY ---------------------------
     It was `--fr * --col-w / --emw`: solve the size so the name ENDS on the week
     it takes. The idea is good and the arithmetic had a lie in it, because that
     equation is driven by TWO variables and only one of them is a fact about the
     studio. `--fr` is the duration. `--emw` is how long the word happens to be.

     Eleven disciplines came out at 24, 26, 40, 46, 48, 52, 60, 63, 79 and 82
     pixels, and the pairs that landed beside each other were 24 against 60 and
     26 against 46. "Web Design & Development" is a ten-week job set at 24px
     because it is a long name; "App Development" is sixteen weeks at 60px
     because it is a short one. Nobody reads a 24px line beside a 60px line as
     "ten weeks against sixteen" — they read it as *this one matters less*. A
     scale contrast of six to one, carrying a ranking the reader cannot recover,
     is the whole reason this sheet looked unfinished.

     So the two jobs are split, and each goes to the instrument that can do it:

       THE TYPE CARRIES RANK.    A ramp on `--fr` alone — 0.035 to 0.128 of the
                                 column — so bigger IS longer, monotonically,
                                 across a range of 1.8 to 1 instead of 6 to 1.
                                 Eleven sizes, still none of them art-directed,
                                 still re-set the day a discipline is appended.
       THE RULE CARRIES THE WEEK. Exactly, to the pixel, against a horizon that
                                 is now drawn. See `.led__band`.

     And the second half of the `min()` is the only thing left of the old
     equation: the size at which the name exactly fills its column. It is a CAP
     now rather than the answer, so a long name is quietly held back to the
     measure instead of being shrunk to a caption by it. Two of the eleven ever
     reach it.

     `--col-w` and not `--measure`: the sheet is two columns, and the measure a
     name is solved against is the column it is standing in. The keystone puts
     `--measure` back on itself and is the only line that does. */
  --fs: min(
    calc(var(--col-w) * (0.035 + 0.093 * var(--fr, 1))),
    calc(var(--col-w) / var(--emw, 8))
  );

  /* THE BOW, and on a stack of type it runs DOWN. `--u` is the row's place in
     the list, the square about the middle turns it into a lag, and the two
     ends of the sheet trail its middle while the page is scrolled past — a
     stack of paper riffling rather than a table sliding. Same arithmetic as
     `.hx__w`, one axis over.

     THE TRAVEL IS A MULTIPLE OF THE LINE'S OWN SIZE, and that is the whole
     gain over the old sheet: at full velocity the 172px line swings about
     twenty-seven pixels and the 51px line swings about eight, so eleven lines
     of eleven different weights riffle like eleven sheets of paper of eleven
     different thicknesses instead of like eleven rows of one table.

     `var(--fs) * 0.055` and NOT `0.055em`, which is what this was and what it
     could not be. `em` on this element is the element's own font-size, and the
     row has none — the size lives on the name inside it. The `em` version
     resolved against the page's 16px on all eleven and bowed the whole stack
     by two and a half pixels, which is a bow nobody can see and a rule nobody
     would think to check.

     NO TRANSITION ON THIS PROPERTY. See the head of this block. */
  --d: calc(var(--u, 0.5) * 2 - 1);
  --bow: calc(var(--d) * var(--d) * 4 - 1.1);
  transform: translate3d(0, calc(var(--flow) * var(--bow) * var(--fs) * 0.055), 0);
}

/* --- the row ---------------------------------------------------------------
   One button per discipline: a caption, a name, and the rule the name's
   picture lives in. The row does not move, does not scale and has no ground of
   its own. There is no card here to lift off anything. */
.led__row {
  appearance: none;
  -webkit-appearance: none;
  display: block;
  width: 100%;
  margin: 0;
  /* NO PADDING. It was `--led-gut` at the top — the caption gutter — and the
     caption is gone, so the padding was holding a box that no longer has
     anything in it off a line that is now the whole row. */
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  position: relative;

  /* A row tabbed to on a page four viewports tall lands wherever the browser
     puts it, and where the browser puts it is often with its picture under the
     fold. Two words. */
  scroll-margin-block: 22vh;

  --led-open: 0;
  --led-in: 0;

  transition:
    --led-open 0.62s var(--ease-out),
    --led-in 1.05s var(--ease-out) calc(var(--i, 0) * 46ms + 120ms);
}

.hb.is-in .led__row { --led-in: 1; }

/* THE OPEN STATE, DECLARED ONCE, FOR THREE DRIVERS.

   The pointer, the keyboard, and — on a screen with no hand to hover with —
   the scroll, through `.is-at`, written by one IntersectionObserver in
   `home/section.js`. Written as one selector list rather than three rules
   because three rules is how a keyboard reader and a pointer end up disagreeing
   about what "open" means.

   `.is-open` is the keystone, and it is in this list because the studio
   retainer is open AT REST: the sheet is never a page about eleven
   photographs with no photograph on it. */
.led__row:hover,
.led__row:focus-visible,
.led__item.is-at > .led__row,
.led__item.is-open > .led__row {
  --led-open: 1;
}

.led__row:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
}

/* --- `.led__cap` AND `.led__no` STOOD HERE, AND THE ORDINAL WENT WITH THE
   LIST ------------------------------------------------------------------
   A caption box the width of the column with "01" set on its leading edge,
   standing in `--led-gut` of air above the name. It filed a row in an
   eleven-line ledger, and it filed correctly for as long as there was an 02
   under it. The index is one line now, so the number counted nothing and the
   gutter it stood in was fifty-five pixels of paper between the picture and
   the only sentence in the block.

   `--led-gut` went with it — see `.led__body` and the narrow query — because
   the gutter was the caption's and there is no caption. What holds the name
   off the rail now is the sheet's own `margin-top`, which is the one place a
   stand-off between two objects belongs. `blocks.js` says the same. */

/* --- `.led__span` STOOD HERE, AND THE SPAN WENT WITH THE SHEET ----------
   "4–10 WEEKS" on the trailing edge of every caption, parked on the high
   week and walking down to the low one as the picture opened — the range
   read as a distance travelled. It measured against the ten lines beside
   it, and the ten lines are gone: a duration with nothing to be longer or
   shorter THAN is a fact printed in a corner. The keystone never had one
   anyway — ONGOING is not a range — so what the block lost here is a
   caption that was already blank on the only row left standing.

   `--pl` and `--led-w` went out with it and with the rule below; nothing
   in the block measures a week any more. See `.led__band`. */

/* --- the name, which IS the measure -----------------------------------------
   The one equation. `--fr` of the measure divided by the width of the name in
   em is the size at which this name ends on its own week, and `--emw` is
   written from a canvas measurement of the real face in `blocks.js`.

   `nowrap` because a name that wrapped would be a name that stopped measuring
   anything, and the size is solved so it never has to. The window is clipped
   for the entrance, like every other line of type on this page. */
.led__w {
  display: block;
  overflow: hidden;
  /* THE MASK CLEARS THE GLYPH AT THE TOP AND MAKES ROOM FOR IT AT THE BOTTOM,
     and the two ends are deliberately not symmetrical. The top pair is the
     usual trick — paint box grown, layout box put back — because an 0.8 line
     is shorter than the face and the caps stand proud of it. The bottom is a
     PADDING WITH NO MARGIN CANCELLING IT: the descenders have to occupy real
     space, because what comes next is the rule this line is measured by, and a
     rule drawn through the tail of a "g" is a rule scratched onto the word. */
  padding-top: 0.1em;
  margin-top: -0.1em;
  /* THE PADDING IS THE FACE'S OWN DESCENT, so the bottom of the box IS the
     descender line and the band's margin below it is a pure optical stand-off.

     It was 0.18em, which is less than the descent, so the tail of a "g" ate
     into the gap the band was holding: measured on the built sheet, a name with
     a descender cleared its rule by 3 pixels and a name without one cleared it
     by 20. The box is identical for both — only the ink differs — which is
     exactly why the correction belongs here and not in the margin. */
  padding-bottom: 0.25em;
  font-size: var(--fs);
  font-weight: 700;
  line-height: 0.8;
  letter-spacing: -0.022em;
  white-space: nowrap;
  color: var(--fg);
}

.led__w > i {
  display: block;
  font-style: normal;
  transform: translateY(112%);
  transition: transform 1s var(--ease-out) calc(var(--i, 0) * 46ms + 180ms);
}

.hb.is-in .led__w > i { transform: none; }

/* --- `.led__band`, `.led__rule` AND THEIR HORIZON STOOD HERE ------------
   One hairline as long as the name, a broken extension line running the
   rest of the column, and a terminator standing on the far end of it. It
   was the drawing the whole sheet was read against, and it was right for
   as long as there were eleven of them stacked into one vertical.

   With the index down to the keystone there is one rule left, under one
   name, comparing itself to nothing. That is not a dimension — it is a
   border under a title, and a border is the one mark this block never
   had. The keystone closes on air instead. `blocks.js` no longer builds
   the element; `--led-band`, `--led-w` and `--pl` went with it. */

/* --- `.led__plate` AND `.led__shot` STOOD HERE, and the pictures they drew
       are on the rail above ---------------------------------------------------
   The photograph of each discipline was in the markup from the first frame at
   the band's full size, clipped to a closed seam lying on the rule, and the
   hover opened the clip. It was a good gesture and there were ELEVEN of them:
   the reader met the studio's work one picture at a time, at the pace of a
   list, over three viewports of paper.

   Five of them are on screen at once now, in perspective, on a sheet that is
   the block's own sentence — `.led__rail`. What is left down here is the
   DRAWING, which was always the argument: a hairline as long as the name and a
   terminator standing on the low week.

   The two rules are gone rather than left unused, and so is the one that stood
   in `.home.is-dark` holding a photograph back on a black ground. A selector
   with no element is a rule the next person has to prove is dead before they
   can touch anything near it. */

/* --- THE KEYSTONE TAKES THE WHOLE SHEET ------------------------------------
   The studio retainer is not a twelfth of a list, it is the other ten bought at
   once — so on a two-column index it spans both columns and solves its name
   against the WHOLE measure rather than half of it. One declaration does both:
   `--col-w` is what the size, the rule and the caption all read.

   It keeps naming itself out of the catalog. No rule here says which row it is;
   `is-open` is written by `blocks.js` off `keystone` in the seed. */
.led__item.is-open {
  grid-column: 1 / -1;
  --col-w: var(--measure);

}

/* --- AND IT STANDS ON THE BASELINE, NOT ON THE DESCENDER LINE -------------
   The name carries a quarter-em of padding under it — the face's own descent,
   put there so the rule that used to be drawn beneath the line could clear the
   tail of a "g". The rule is gone and the sheet is one line, so that padding is
   no longer a stand-off between two marks: it is forty pixels of nothing
   between the last word of the block and the press under it, at a size where
   forty pixels reads as a paragraph break. The layout box keeps it — the paint
   box is what the entrance mask is cut from — and the margin takes it back, so
   what stands under the line is the BASELINE.

   ON `.led__w` AND IN `em`, WHICH IS THE ONLY PLACE THE NUMBER IS TRUE. The
   obvious home for it is `.led__item`, where `--fs` is declared — and `--fs` is
   NOT the size this line is set at. The narrow query re-reads it through a
   `clamp()` with a floor and a ceiling, and at 375px the ceiling bites: the
   name comes out at 3.4rem against a `--fs` of eighty-odd. A correction solved
   from `--fs` over-shoots by exactly the difference and pulls the press up into
   the type. `em` here is the element's own font-size, which is whatever the
   clamp resolved to, so the padding and its cancellation are the same length at
   every width by construction. */
.led__item.is-open .led__w {
  margin-bottom: -0.25em;

  /* --- AND IT HANGS FROM THE CAP LINE, NOT FROM THE LINE BOX -------------
     The mirror of the margin above, and it exists for the same reason: the
     sixty pixels over this line have to be sixty pixels of VISIBLE air, and
     the top of the line box is not where the ink starts.

     Measured off Satoshi at 100px: the face's own ascent is 101 and its
     descent 24, so a 0.8 line box carries a half-leading of -22.5, the
     baseline falls 78.5 below the box top, and the cap stands 73 above the
     baseline — five and a half hundredths of an em of nothing between the
     top of the box and the top of an "F". It is 8px at the desktop size and
     2px on a phone, which is exactly the kind of number that makes a
     stand-off measured in pixels drift with the type size.

     `-0.155em` and not `-0.055em`: the 0.1em the rule above already spends
     cancelling the paint mask is still being spent. Both live in `em` on the
     element that carries the size, for the reason the margin below does. */
  margin-top: -0.155em;
}

/* --- AND THE LINE STANDS ON THE BLOCK'S OWN AXIS ---------------------------
   The left margin was the zero of a measure: eleven names hanging off one edge
   so the ragged right-hand end drew the chart. There is no chart — there is
   one sentence, under a full-bleed rail that is centred and over a press that
   is centred — and a single line flushed left under two centred objects is not
   a measure any more, it is one object out of the axis the other two agree on.

   On the row rather than on the name, because the row is the box the whole
   width of the sheet and the name is the window inside it. */
.led__item.is-open > .led__row { text-align: center; }

/* --- THE PRESS, SIXTY PIXELS UNDER THE KEYSTONE ---------------------------
   Every other block on the landing hangs its exit off the shared
   `clamp(26px, 4vh, 52px)` and should go on doing so — the measurement here
   is not a taste about buttons, it is the one stand-off left in a block that
   used to close on a ledger and now closes on a single line of type. Sixty
   pixels from the baseline of "Full Product Studio", which is what the
   negative margin above makes this number mean. Scoped to the block so it
   can never reach the reel, the ventures head or the CTA row. */
.hb--services .hb__act {
  margin-top: 60px;
  /* ON THE AXIS WITH THE LINE ABOVE IT. `.hb__act` is `width: fit-content`, so
     centring it is a margin and not a `justify-content` — the flex row is only
     as wide as the press in it, and the box is what has to move. */
  margin-inline: auto;
}

/* --- THE FOOT STOOD HERE, AND A SHEET WITH ONE LINE ON IT HAS NO TOTAL --
   A double rule, the count at monument size on the leading edge, eleven
   ticks drawn between, and "Disciplines / One invoice" on the trailing
   one — swapped under the hand for whichever discipline was being read.
   It was a ledger closing on its own sum, and a sum is the second reading
   of a list. There is no list.

   `.led__foot`, `.led__word`, `.led__tally`, `.led__sum`, `.led__sum-a`
   and `.led__sum-b` are gone with it, here, in the narrow query and in the
   reduced-motion block. `home/section.js` still LOOKS for the three it
   swapped — `ledWord`, `ledName`, `ledSpan` — and every lookup now comes
   back null, which `#total` guards on its first line. See `blocks.js`. */

/* --- THE QUOTE'S RULES STOOD HERE, and the block they styled is deleted ----
   `.hb--quote`, `.quote__inner`, `.quote__kicker`, `.quote__text`, `.quote__by`
   and `.quote__by-rule`: three lines at display size over a field of particles,
   on a screen of its own between the service list and the count.

   `blocks.js` says why it went. What is worth recording HERE is the one thing
   it took with it that was never its own: `.quote__text` spelled the display
   stack out by hand — `"PP Supply Sans", "Satoshi", Georgia, serif` — and it
   was the copy with the `Georgia` in it, which no other spelling of that stack
   on this site had. It was the last hand-written one on the landing. The name
   is `--face-display` in `:root` now and every user of it reads that. */

/* --- 05 the numbers --------------------------------------------------------

   THE BOX IS GONE, and that is the first half of why this block looked like a
   widget somebody dropped in rather than a paragraph of the page.

   It was one rule drawn straight across the head of the whole grid with a gap
   under it: a container lid, on the one page that spends nine other blocks
   refusing to draw containers. Four figures under one lid read as a WIDGET —
   a stats strip, the thing every template ships with. The same hairline moved
   onto each cell reads as four FIGURES, each standing on its own line, which
   is what they are: they do not add up to anything and they were never a
   table.

   That is also About's count, exactly, and deliberately: the two pages are one
   voice read at one distance. These four classes are shared between the two
   sheets — About declared the whole treatment locally and then had to type
   `border-top: 0` here to undo the lid it never wanted. It is the BASE now, so
   the undoing is gone with the thing it was undoing, and About keeps only what
   is genuinely its own: the `[ FIG. 01 ]` filing its lattice sets, and the arc.

   What is this page's own is below, under `#home`. */

.num__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  /* Row gap wider than column gap. The cells are stacked pairs of type, not
     boxes, so what separates two of them side by side is their rules ending;
     what separates two of them over each other is air, and it takes more. */
  gap: clamp(30px, 4vw, 72px) clamp(24px, 3vw, 56px);
  margin: clamp(30px, 5vh, 64px) 0 0;
}

/* One hairline per figure, in the page's soft line — the same weight the chips
   print at and the same one About's count stands on. */
.num__cell {
  border-top: var(--hair) solid var(--line-soft);
  padding-top: clamp(14px, 2.2vh, 26px);
  opacity: 0;
  --ry: 20px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.7s var(--ease-out),
    --ry 0.7s var(--ease-out);
}

.hb.is-in .num__cell { opacity: 1; --ry: 0px; }
.num__cell:nth-child(2) { transition-delay: 0.08s; }
.num__cell:nth-child(3) { transition-delay: 0.16s; }
.num__cell:nth-child(4) { transition-delay: 0.24s; }

/* THE DISPLAY FACE, and this is the second half of it.

   The figures were set in Satoshi Light — the READING face, at display size.
   That is what "basic" is, precisely: the body face enlarged is the default
   every page falls to when nobody decides anything, and a count is the one
   place on a landing where the type IS the content. The quote three blocks up
   is in the display face and so is every number on About; these were the
   holdout. */
.num__value {
  margin: 0;
  font-family: var(--face-display);
  font-size: clamp(3rem, 7vw, 7rem);
  font-weight: 400;
  line-height: 0.9;
  letter-spacing: -0.05em;
  font-variant-numeric: tabular-nums;
}

.num__label {
  margin: 0.7em 0 0;
  font-size: var(--t-micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.55;
  max-width: 18ch;
}

/* --- and what the LANDING's count does that About's does not ---------------
   Scoped to `#home` because these four classes are shared and About's count is
   composed against a lattice and an arc engine this page does not have. Every
   rule under here is a landing decision, not a general one. */

/* The rule DRAWS, rather than being found already drawn.

   It is a pseudo-element and not the border above, because a border cannot be
   scaled from nothing — and drawing is the page's own gesture: it is what the
   rule beside every kicker on this landing does, at the same duration and the
   same origin. Four figures whose lines are struck left-to-right in sequence
   are four figures being WRITTEN down. Four that fade in with their rules
   already full width are four things arriving. */
#home .num__cell {
  border-top: 0;
  position: relative;
}

#home .num__cell::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--hair);
  background: var(--line-soft);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.9s var(--ease-out) 0.1s;
}

#home .hb.is-in .num__cell::before { transform: scaleX(1); }

/* The same cascade the cells themselves are on, restated: `transition-delay`
   on the cell is the CELL's delay and a pseudo-element does not inherit it. */
#home .num__cell:nth-child(2)::before { transition-delay: 0.18s; }
#home .num__cell:nth-child(3)::before { transition-delay: 0.26s; }
#home .num__cell:nth-child(4)::before { transition-delay: 0.34s; }

/* Bigger, and the suffix is no longer part of the number.

   `10+` was one string, so the `+` was set at the size of a digit and tracked
   in with it — which makes a plus sign as loud as the ten it is qualifying and
   leaves that one cell reading a third wider than the three beside it. It is
   its own glyph now: a third the size, hung from the cap line, at the tone the
   count's own `/ 06` prints at. The figure is the figure; the plus is a note
   about the figure. */
#home .num__value {
  display: flex;
  align-items: flex-start;
  font-size: clamp(3.2rem, 7.8vw, 7.6rem);
  line-height: 0.84;
  letter-spacing: -0.035em;
}

#home .num__d { font-variant-numeric: tabular-nums; }

#home .num__suffix {
  font-style: normal;
  font-size: 0.34em;
  line-height: 1;
  margin-top: 0.22em;
  margin-left: 0.08em;
  letter-spacing: 0;
  opacity: 0.38;
}

#home .num__label {
  margin-top: clamp(12px, 1.8vh, 22px);
  max-width: 15ch;
  line-height: 1.5;
  opacity: 0.5;
}

/* AND THE GRID'S TOP MARGIN GOES, on this page only.

   That margin is the air between the block's head label and the figures under
   it. About still has a head label — its four numbered movements — so the base
   keeps it. The landing's went with every other kicker on the page, and a
   margin measured from a thing that is not there any more is just the block
   starting sixty-four pixels lower than the seven around it. The figures ARE
   the block now; they start where the block starts. */
#home .hb--numbers .num__grid {
  margin-top: 0;
}

/* THE COLUMNS ARE NOT EQUAL, and the copy is why.

   Four identical columns is the other half of the widget look, and it is not
   even right for the material: the labels are 14, 19, 21 and 25 characters
   long, so on an even grid the longest wraps to three lines while the shortest
   sits on one, and the row of figures ends up standing on four labels of four
   different depths. The tracks are cut to the copy instead — the widest label
   gets the widest measure — and all four land on two lines.

   Above 900px only. Below it the grid is two-up and then one-up (see the two
   media queries at the foot of this file), where a measure cut for a four-wide
   row means nothing. */
@media (min-width: 900px) {
  #home .num__grid {
    grid-template-columns: 1fr 1.2fr 0.86fr 1.04fr;
  }
}

/* --- 08 the ventures deck --------------------------------------------------- */

/* --- the band -------------------------------------------------------------
   The deck was a pinned viewport at the top of the page. It is a BANNER now:
   full bleed across, a fraction of a screen down, with the block's clean
   padding as the air above and below it. The page runs straight past — nothing
   in here takes the wheel, and since `section.js` stopped writing `flow` to
   the component, nothing in here is driven by the wheel either. It loops on
   its own idle cruise. See `ventures()` in `blocks.js`. */
.vent__band {
  position: relative;
  width: 100%;
  /* The one number in the block that had to be chosen rather than derived.

     The deck's whole lattice is sized off WIDTH — a card is a fraction of the
     canvas across and its own ratio down — so a band that is full bleed and
     short crowds six cards and a word into a third of the room they were drawn
     for. Two thirds of the viewport is where the composition still reads as
     itself and the block still reads as a band the page goes past rather than
     a screen the page stops at. */
  height: clamp(440px, 64vh, 800px);
  overflow: hidden;
}

.vent__head {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0 var(--pad) calc(var(--pad) * 2);
  z-index: 3;
  width: var(--col);
  margin: 0 auto;
  pointer-events: none;
}

/* The scrim, and this block cannot do without one.

   Everything the deck deals is a FULL-COLOUR product screenshot and half of
   them are nearly white — which is the whole point of the block, and it is
   also why white type laid straight over it disappears. It did before, eight
   screens down, where the reader met it mid-scroll on cards that were already
   moving; standing first it is the opening sentence of the site printed in
   white on white.

   So: the block's own ground, heavy at the two edges where the furniture and
   the copy are and gone by a quarter of the way in, so the deck keeps its
   whole middle — which is where the word is. Two gradients rather than one,
   because the head at the foot needs three times the cover the filing at the
   top does and a single symmetric wash would either drown the top or fail the
   bottom.

   The stops are further up the band than they were: the wash was tuned when
   this was a full pinned VIEWPORT, and the head's own height did not shrink
   when the block became a band two thirds that tall — so a wash written as a
   percentage covered proportionally less of the copy standing in it.

   `z-index: 2` puts it over the stage at `1` and under `.vent__head` at `3`.
   The first-screen furniture is no longer in this block at all — the run opens
   on the reel now, and the filing and the cue went with the position. */
.vent__band::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    linear-gradient(
      to bottom,
      var(--bg) 0%,
      color-mix(in srgb, var(--bg) 62%, transparent) 9%,
      transparent 22%
    ),
    linear-gradient(
      to top,
      var(--bg) 0%,
      color-mix(in srgb, var(--bg) 86%, transparent) 22%,
      color-mix(in srgb, var(--bg) 44%, transparent) 44%,
      transparent 68%
    );
}

/* The four overrides that used to be here forced the kicker, the rule, the
   headline and the paragraph to be simply UP, because a pinned block is read
   from the inside and there is no arrival to hang an entrance on. There is one
   now: this is a band the reader scrolls onto like every other clean block, so
   the shared `.hb.is-in` entrance does the work and only the paragraph needs a
   rule of its own — `.vent__body` is not `.hb__body` and never inherited one. */
.vent__body {
  opacity: 0;
  --ry: 12px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.8s var(--ease-out) 0.18s,
    --ry 0.8s var(--ease-out) 0.18s;
}

.hb--ventures.is-in .vent__body { opacity: 0.72; --ry: 0px; }

/* The way out of the deck. The head is `pointer-events: none` so the wall
   behind it can still be read; the one control in it has to opt back in.

   It used to force `opacity: 1` and `--ry: 0` as well, which skipped the
   shared entrance for the same reason the four rules above did. The block
   arrives now, so the press arrives with it. */
.vent__head .hb__act {
  pointer-events: auto;
  margin-top: clamp(18px, 2.4vh, 32px);
}

.vent__title {
  font-size: clamp(1.6rem, 3.2vw, 3rem);
  max-width: 14ch;
  --arch: 0.28em;
}

.vent__body {
  max-width: 48ch;
  margin: clamp(18px, 2.4vh, 30px) 0 0;
  font-size: var(--t-body);
  line-height: 1.55;
}

/* --- AND THE GEM'S TWO STOOD HERE -----------------------------------------
   `.hb--gem` and `.gem__note`. The block is deleted; `blocks.js` says why. */

/* --- the questions ---------------------------------------------------------
   --- THE MARKUP IS ON ABOUT AND THE STYLESHEET IS STILL HERE ---------------
   The deck was the fourth block of this landing and it is the HERO of About
   now — `about/ask.js` builds it, `about/ask-deck.js` turns it, and
   `about/ask-screen.js` prints the room it stands on. What did NOT move is
   everything below this line, and that is deliberate rather than lazy.

   Every stylesheet in this project is loaded on every page — see the head of
   `index.html` — and these selectors are all unprefixed `.faq__*`, so they
   apply wherever the markup is. Moving nine hundred lines to `styles.about.css`
   would change not one pixel and would cost the one thing this block has going
   for it: the notes below are the notes that were written while it was drawn,
   against the four ways one number is spent, and a file move rewrites the
   blame on every one of them for no reader.

   What About adds on top is in `styles.about.css` under `.hb--ask`: a room a
   fifth taller, the clock the deck now turns itself on, and the liquid the
   turn is made of. That file OVERRIDES this one; it does not restate it. If a
   rule below and a rule over there disagree about the same declaration, this
   one is the block and that one is the hero.

   --- AND THE BLOCK ITSELF --------------------------------------------------
   Nothing in it is a colour, a border, a shadow or a box. The deck is
   made of the same two things the rest of the page is made of — type and
   hairlines — and every state it has is one of those two moving. There is no
   card edge and no panel: a "card" here is a question set large, one vertical
   rule, and an answer standing beside it.

   --- ONE NUMBER, SPENT FOUR WAYS --------------------------------------------
   `--fk` on `.faq__deck` is the index of the card on screen. It is a registered
   property, so it INTERPOLATES, and it is the only thing this block animates.
   Four separate things read it on the same frame:

     the track travels    `translateX(--fk * -100% / --n)`
     each card lags       `--dk: --i - --fk`, spent as a counter-slide
     each card's ink      the same `--dk`, spent as opacity
     the carriage slides  `translateX(--fk * 100%)` along the rail

   Which means there is no second number to keep in step with the first. The
   failure mode of every set of carousel dots ever written is that the slide is
   at 3 and the dot is at 2, because two animations were started from two
   places and one of them was interrupted. Interrupt this one anywhere — press
   an arrow mid-travel, throw the deck with a thumb — and all four things are
   still describing the same position, because they ARE the same position.

   The travel is a percentage of the track's own width divided by `--n`, which
   `blocks.js` writes onto the deck. So the deck needs no pixel measurement at
   all to be in the right place: it is correct at any width, on the first
   frame, before any script has read a rectangle. The one thing measured in
   `about/ask-deck.js` is the height of the tallest card, and that is a different claim —
   see the note there.

   --- WHAT REPLACED THE ACCORDION, AND WHAT SURVIVED IT ---------------------
   Gone: the row, the plus-that-becomes-a-minus, the panel that measured its
   own height, the sheet held above its own box, `--near` on the five rows that
   were not the one. All of it was in service of a list that changed the page's
   height when you read it.

   Kept, because the GESTURE survived the furniture: `--j` and `--jr` on the
   stack inside an answer, the ledger built out of hairlines that draw
   themselves from the left, and the way out under a paragraph drawn as a
   hairline rather than as the chrome button. Those were never about the
   accordion; they were about how an answer is read.
--------------------------------------------------------------------------- */

/* --- the head band ----------------------------------------------------------
   The headline on the left, and on the right the two things a deck owes the
   reader before they touch it: where they are in the set, and the controls
   that move them. A carousel that says neither is a carousel the reader has to
   discover by accident. */
.faq__head {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: clamp(20px, 4vw, 72px);
  padding-bottom: clamp(20px, 2.8vh, 38px);
}

/* The rule under the band, drawn from the left as the block is crossed. Same
   material and the same arrival the old list's head rule had — it is the one
   line in the block that has no row of its own to be drawn by. */
.faq__head::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: var(--hair);
  background: currentColor;
  opacity: 0.1;
  transform: scaleX(calc(0.3 + 0.7 * var(--q, 1)));
  transform-origin: 0 50%;
  transition: transform 0.9s var(--ease-out);
}

.faq__title {
  font-size: var(--t-title);
  max-width: 15ch;
}

/* THE COLUMN ARRIVES AS A SEQUENCE AND NOT AS A LUMP.

   It used to fade and rise as one element, which is one transition and reads as
   one object — a grey rectangle that appears — while the headline beside it is
   melting word by word. The two halves of the band were arriving in two
   different languages.

   Three things stand in here and they are three different KINDS of thing: a
   sentence, a count, and a pair of controls. So they come in as three, in the
   order the eye uses them, behind the headline's own melt: the note at 0.22,
   the count at 0.34, the arrows at 0.46. The gaps are an eighth of a second,
   which is under the tenth-of-a-second floor where a stagger stops reading as
   sequence and starts reading as lag, and over the frame where three delays are
   indistinguishable from none.

   THE ARROWS COME FROM THE SIDE and the other two from below, and that is not
   variety for its own sake. `--rx` is the offset this sheet gives things that
   arrive along their own axis — the foot's links do it, the rules under the
   questions do it — and a pair of horizontal arrows sliding up is a pair of
   arrows pointing the wrong way for a tenth of a second.

   THE CUE IS NOT IN THE SEQUENCE, it only holds the two that are. Nesting a
   fade inside a fade multiplies them, and the note's resting 0.4 read through a
   parent still at a third of its own would have arrived at 0.13 — a value the
   sheet never asked for, on the way to one it did.

   Each of the three carries its entrance in its OWN rule below rather than in a
   second set up here. This block used to declare `.faq__meta`'s and it worked
   because nothing declared it twice; three of these selectors are declared
   again forty lines down, at the same specificity and later in the file, so an
   entrance written here would be silently overwritten by the resting opacity
   that follows it. Same rule as everywhere else in this sheet: one selector,
   one place. */
.faq__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: clamp(14px, 1.8vh, 22px);
  text-align: right;
}

/* Not uppercase and not tracked out. The micro-copy on this page is filing —
   labels, counts, keys in a ledger — and this is a SENTENCE. Setting it in the
   same tracked-out capitals would file it as furniture, which is the one thing
   it is not. */
.faq__note {
  margin: 0;
  max-width: 30ch;
  font-size: var(--t-micro);
  line-height: 1.55;
  opacity: 0;
  --ry: 14px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.7s var(--ease-out) 0.22s,
    --ry 0.7s var(--ease-out) 0.22s;
}

/* Layout only. See the note over `.faq__meta`. */
.faq__cue {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.4vw, 30px);
}

.faq__count {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  font-size: var(--t-micro);
  letter-spacing: 0.14em;
  font-variant-numeric: tabular-nums;
  opacity: 0;
  --ry: 12px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.6s var(--ease-out) 0.34s,
    --ry 0.6s var(--ease-out) 0.34s;
}

.faq__count-n { opacity: 0.9; }
.faq__count-of { opacity: 0.3; }

/* The slash, as a hairline leaning over rather than as a `/`. The glyph in
   this face is a different weight from the digits either side of it and it
   sits on a different part of the body; a line does not have opinions. */
.faq__count-slash {
  display: block;
  width: var(--hair);
  height: 1.1em;
  background: currentColor;
  opacity: 0.22;
  transform: rotate(20deg);
}

.faq__arrows {
  display: flex;
  align-items: center;
  gap: clamp(8px, 1vw, 14px);
  opacity: 0;
  --rx: 10px;
  transform: translate3d(var(--rx), 0, 0);
  transition:
    opacity 0.6s var(--ease-out) 0.46s,
    --rx 0.6s var(--ease-out) 0.46s;
}

/* The three, arriving. `.faq__cue` is not here: it never left. */
.hb.is-in .faq__note { opacity: 0.4; --ry: 0px; }
.hb.is-in .faq__count { opacity: 1; --ry: 0px; }
.hb.is-in .faq__arrows { opacity: 1; --rx: 0px; }

/* --- the two arrows ---------------------------------------------------------
   The same object as the arrow under a `go` link — a hairline with a head on
   it, built out of the one pixel the whole block is built out of — at the size
   of a control rather than at the size of a word. The previous arrow is the
   next arrow turned over: one shape, declared once, so the two can never drift
   apart by a pixel of head or a degree of angle. */
.faq__arrow {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  position: relative;
  display: block;
  width: clamp(46px, 4.4vw, 62px);
  height: clamp(30px, 3.4vh, 40px);
  padding: 0;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.4s var(--ease-out);
}

.faq__arrow.is-prev { transform: scaleX(-1); }

.faq__arrow:hover,
.faq__arrow:focus-visible { opacity: 1; }

/* The end of the deck, said by the control rather than by nothing happening.
   A carousel whose arrow still looks pressable at the last card is a carousel
   that answers a press with silence. */
.faq__arrow:disabled {
  opacity: 0.14;
  cursor: default;
}

.faq__arrow-rule {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: var(--hair);
  background: currentColor;
  transform-origin: 0 50%;
  scale: var(--sc, 0.72) 1;
  transition: scale 0.5s var(--ease-out);
}

.faq__arrow:hover .faq__arrow-rule,
.faq__arrow:focus-visible .faq__arrow-rule { --sc: 1; }

.faq__arrow-head {
  position: absolute;
  right: 0;
  top: 50%;
  width: 0.5em;
  height: 0.5em;
  border-top: var(--hair) solid currentColor;
  border-right: var(--hair) solid currentColor;
  transform: translate3d(var(--rx, 0px), -50%, 0) rotate(45deg);
  transform-origin: 100% 50%;
  transition: --rx 0.45s var(--ease-out);
}

.faq__arrow:hover .faq__arrow-head,
.faq__arrow:focus-visible .faq__arrow-head { --rx: 5px; }

.faq__arrow:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
}

/* --- the deck ---------------------------------------------------------------
   `--fk` lives here and is transitioned here, once, for everything below. The
   two classes that switch it off are the two moments where a transition is a
   lie: the first paint, where there is nothing to travel from, and a drag,
   where the position is a finger and a finger does not ease.

   `--room` is the height of the photograph the whole block stands on, and it is
   declared here rather than on the room because two other things are measured
   against it — the ask band inside every card, and the mark in the corner. One
   number, three readers, and a block that is never half a room out of true. */
.faq__deck {
  --fk: 0;
  --room: clamp(210px, 34vh, 400px);
  /* --- THE ROOM'S OWN PAIR, PINNED, and it is the THIRD documented exception
     to one ground on this page.

     `.hb--reel` pins its pair because it stands on a PHOTOGRAPH and a
     photograph has no polarity to follow. `.hb--motion` pins its pair because
     what the type in it stands on is PROJECTED LIGHT, and light only exists
     against dark. The rule beside both of those says the licence is not a
     licence: a block that wants its own ground has to be able to finish the
     sentence.

     This one finishes it. What the question stands on is a photograph UNDER
     INK — and ink on a picture is dark on any sheet, because taking light off a
     photograph is the only thing a line screen has ever done. Drawn in the dark
     sheet's own ink, which is white, the screen did not veil the picture: it
     deleted it, and printed the question in near-black on top of the hole. The
     screen has a direction and the dial does not get to reverse it.

     TWO NAMES AND NOT `--fg` / `--bg`, and that is the second half. Those two
     sort the sheet's pair by ROLE, and the roles swap when the dial turns; the
     room needs them sorted by LIGHTNESS and needs that to hold on both sheets.
     So the veil is the ink and the mark is the paper, always, and every one of
     the four things in the room — the canvas, the question, its filing, the
     mark in the corner — reads these rather than picking one of the sheet's own
     and being wrong half the time.

     AND IT IS ONLY THE ROOM. Everything under it — the answer, the ledger, the
     way out, the rail, the foot — is on the sheet's own paper in the sheet's
     own ink and turns over with it, which is most of the block. The exception
     is four hundred pixels of photograph, not a page. */
  --veil: #0a0a0c;
  --mark: #ffffff;
  position: relative;
  margin-top: clamp(26px, 3.6vh, 50px);
  transition: --fk 0.86s var(--ease-out);
}

.faq__deck.is-instant,
.faq__deck.is-dragging { transition: none; }

/* --- THE ROOM ---------------------------------------------------------------
   A stack of eight photographs, under one line screen, with eight questions
   sliding across it. One plate per question, and the stack DISSOLVES rather
   than travels — see `.faq__shot` below.

   It is a SIBLING of the viewport and not a child of any card, which is the
   whole reason the block reads as one object: the deck travels and the ground
   does not move with it. Same relationship a window has with what is behind it,
   which is the relationship this site draws everywhere it can.

   No border, no radius, no shadow. The room's edges are where the photograph
   stops, and a picture with a box drawn round it is a picture in a frame — this
   one is printed ON the sheet. */
.faq__room {
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--room);
  overflow: hidden;
  z-index: 0;
  /* The veil. A canvas cannot say `currentColor`, so `ask-screen.js` reads this
     off the element every twentieth frame — which is how the dial turns the
     screen over with the rest of the page without that file knowing there is a
     dial. See the pair on `.faq__deck`. */
  color: var(--veil);
  background: var(--bg);
}

/* THE PICTURE KEEPS ITS COLOUR — the site's rule and not this block's. What is
   over it is a screen, which is a way of PRINTING a photograph rather than a
   way of editing one: at the top of the room by the eighth question there is
   nothing over the picture at all.

   It drifts against the room under the page's own velocity, the way every
   photograph in a window on this site does, and the scale is what hides the
   travel. NOTHING TRAVELS SIDEWAYS. There are eight of these stacked in the
   one rectangle now — one per question — and the deck turning does not move
   any of them: it dissolves the stack. The room is still a fixed ground and
   the question still slides across it, which is the relationship the block is
   built on and the reason the plates are not simply dealt to the cards.

   --- THE STACK IS A DISSOLVE AND NOT A CROSSFADE, and the difference is the
   whole rule here. A crossfade is both plates at a half and neither of them
   opaque, which over the room's own ground washes the pair out to a grey that
   is not in either photograph — a picture at 50% is not half a picture, it is a
   different, weaker one. So `1 - --dk` is CLAMPED at the top: every plate the
   reader has already passed stands at full ink under the stack, and only the
   plate being ARRIVED AT fades, over a solid one. Two plates, one impression,
   and at every position between two questions the room is honestly printing
   both — which is what a press does when it has not finished changing plates,
   and is the only kind of blend this block has any business making.

   Read straight off `--fk`, so there is no second number and nothing to fall
   out of step with the travel, the ink on the cards or the carriage on the
   rail. Under `prefers-reduced-motion` the deck's transition is gone, `--fk` is
   an integer at every moment, and this resolves to exactly one opaque plate:
   the dissolve is not switched off anywhere, it simply has nothing to cross. */
.faq__shot {
  --dk: calc(var(--i) - var(--fk));
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* WHERE TO CUT A PORTRAIT INTO A 3.4:1 BAND, and it is per plate — see `at`
     in `ASK_SHOTS`. The fallback is not a house default that plates opt out of;
     it is what the room falls back to if a plate ever arrives without its
     number, and 42 is the figure the block was drawn at when there was one
     picture. `ask-screen.js` reads `--at` off this element and cuts its grid to
     the same band, which is the whole reason this is a custom property and not
     an `object-position` shorthand written twice. */
  object-position: 50% var(--at, 42%);
  opacity: clamp(0, calc(1 - var(--dk)), 1);
  /* The arriving plate comes in a hair over size and settles onto the one it is
     covering. `max()` so it is only ever the plate on its way IN: a plate the
     reader has passed is under an opaque one and any scale on it is fill nobody
     will ever see. Two and a bit percent, which is under the 6% the drift is
     already hiding — it is there to make the change read as a plate being laid
     down rather than as a frame dropping. */
  transform: translate3d(0, calc(var(--flow) * -2.4%), 0)
    scale(calc(1.06 + max(0, var(--dk)) * 0.024));
  filter: contrast(1.04) saturate(1.02);
  user-select: none;
}

/* The screen itself. `mix-blend-mode: normal` on purpose and stated by absence:
   the ink is opaque, the paper between the rules is the photograph, and that is
   what a line screen IS. A multiply or a darken here would make it a filter
   over a picture rather than a picture made of lines. */
.faq__screen {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  color: var(--veil);
}

/* The filing mark, in the corner the question does not stand in. It names the
   technique and, beside it, says the one thing about this block nobody
   discovers by accident: the screen opens as you ask.

   BOTTOM RIGHT, and the reason is the screen's own floor. It is set in paper
   over a photograph, and up in the top corner it was landing on whatever that
   corner happened to be — which on this frame is a bright sky. The foot of the
   room never opens past two thirds closed, so the bottom is the one band of
   this picture that is guaranteed to be dark on any photograph the belt ever
   hands it. The question stands in the other end of that same band.

   Which is the block paying for its own legibility out of its own material,
   rather than out of a gradient laid over the top. */
.faq__room-mark {
  position: absolute;
  right: clamp(18px, 2.4vw, 40px);
  /* UNDER the question's baseline and not level with it. The ask is padded off
     the foot of the room by the same figure this used to be, so a one-line
     question and this mark came out on one line — two different kinds of thing
     sharing a baseline, which reads as a caption that has been knocked into the
     headline. Hugging the plate's own edge it is unmistakably furniture. */
  bottom: clamp(9px, 1.1vh, 15px);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.8em;
  pointer-events: none;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mark);
  filter: drop-shadow(0 1px 3px rgb(0 0 0 / 0.55));
  opacity: 0;
  transition: opacity 0.8s var(--ease-out) 0.5s;
}

.hb.is-in .faq__room-mark { opacity: 0.82; }

.faq__room-rule {
  display: block;
  width: clamp(18px, 3vw, 44px);
  height: var(--hair);
  background: currentColor;
  opacity: 0.6;
}

/* The instruction is quieter than the label: one is what the picture IS, the
   other is what it does, and a reader only needs the second one once. */
.faq__room-open { opacity: 0.7; }

/* --- the track --------------------------------------------------------------
   THE WIDTH IS DECLARED AND THE COLUMNS ARE DERIVED FROM IT, and it has to be
   that way round. The obvious form is `grid-auto-columns: 100%` — eight columns
   each as wide as the viewport — and it lays out perfectly and breaks the
   travel: grid columns OVERFLOW their container rather than widening it, so the
   track's own border box stays one viewport wide, and a transform of `-100%`
   divided by eight moves the deck an eighth of a card.

   So the box is eight viewports wide by declaration, and a column is an eighth
   of the box. Now `-100% / --n` is exactly one card at any width, and the whole
   travel still needs no measurement in pixels — which is the point. */
.faq__view {
  position: relative;
  z-index: 1;
  overflow: hidden;
  /* Height in px, written by `about/ask-deck.js`: the tallest card's, so the block is one
     height at all eight positions and nothing below it on the page ever moves.
     `auto` until the first measure, which is what a reader with no script and
     a reader on the first frame both get — the first card, at its own size. */
}

.faq__track {
  display: grid;
  grid-auto-flow: column;
  width: calc(var(--n) * 100%);
  grid-auto-columns: calc(100% / var(--n));
  align-items: start;
  height: 100%;
  --fdx: 0px;
  transform: translate3d(calc(var(--fk) * -100% / var(--n) + var(--fdx)), 0, 0);
  /* Only the thumb's offset is transitioned here — `--fk` is transitioned on
     the deck and arrives through it. This is the spring back from a drag that
     did not travel far enough to turn the card. */
  transition: --fdx 0.5s var(--ease-out);
}

.faq__deck.is-instant .faq__track,
.faq__deck.is-dragging .faq__track { transition: none; }

/* --- one card ---------------------------------------------------------------
   `--dk` is how far this card is from the one on screen, signed, and `--ak` is
   the same distance without a sign — spelled with `max()` rather than `abs()`
   because `abs()` is younger than the browsers this has to be right in.

   Two things read it. The INK, so the card being left fades as it goes and the
   card arriving inks up as it lands, which is what stops the travel reading as
   a strip of paper being pulled sideways. And the LAG: the content slides a
   little against its own card, so a card arrives from further away than the
   track actually travelled. Same relationship the photograph has with the room
   it is standing in — the window moves and what is behind it does not. */
.faq__card {
  --dk: calc(var(--i) - var(--fk));
  --ak: max(var(--dk), calc(-1 * var(--dk)));
  grid-row: 1;
  min-width: 0;
  opacity: clamp(0, calc(1.08 - var(--ak) * 1.16), 1);
}

/* TWO BANDS AND NOT TWO COLUMNS. The ask stands the full height of the room and
   the answer stands under it on paper — see `faqCard()` in blocks.js for why
   the block turned a quarter when it grew a ground. */
.faq__card-in {
  display: grid;
  grid-template-rows: var(--room) auto;
  align-items: start;
  padding-bottom: clamp(16px, 2.4vh, 30px);
  transform: translate3d(calc(var(--dk) * 8%), 0, 0);
}

/* --- the ask, standing in the photograph ------------------------------------
   The band that covers the room, with the filing and the question hung at its
   foot — which is the part of the screen that never opens, so there is always
   ink under this type. See the note on the floor in `ask-screen.js`.

   Everything in here is set in PAPER, and it is the only type on this landing
   that is: it is printed on a photograph, and a photograph has no polarity to
   follow.

   AND IT CARRIES NO SHADOW, because it does not need one and the one it had was
   costing it. There were two — a wide one to seat the type on a bright crop and
   a tight one to keep the counters open — written on the assumption that the
   thing behind this type is a photograph. It is not. `ask-screen.js` holds a
   floor of ink across the foot of the picture for ever: the density is a
   function of HEIGHT as well as of `--fk`, the room opens upward out of the
   press, and the band the question stands in never opens at all. Read off the
   rendered canvas under the question that floor measures 0.001 mean luminance
   and 0.0056 at its single brightest pixel — white type on it is 20.5:1 mean
   and 18.9:1 at the worst pixel in the band, against the 4.5 an eye is owed.

   So the pair was four times redundant, and a `drop-shadow` is not free: it is
   a filter, so the whole band is rasterised to an offscreen buffer and the
   glyphs come back with a dark halo the size of the blur baked into their
   edges. At a headline's weight, over a screen made of hairlines, that halo is
   what the eye reads as smudge — it does not seat the type, it softens it. The
   ink under this type is the screen's own floor, drawn out of the block's one
   material, and that was always the argument. */
.faq__ask {
  position: relative;
  z-index: 1;
  min-width: 0;
  height: var(--room);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: clamp(18px, 2.6vh, 34px) clamp(18px, 2.4vw, 40px);
  color: var(--mark);
}

/* The filing leads, the way it did on the row this replaced: smallest thing on
   the card, first thing the eye lands on, and the same two digits the rail
   under the deck is printing. */
.faq__num {
  display: block;
  font-size: var(--t-micro);
  letter-spacing: 0.16em;
  font-variant-numeric: tabular-nums;
  opacity: 0.8;
  margin-bottom: clamp(8px, 1.2vh, 16px);
}

/* A display heading and not a label. The question is the largest type in the
   block — it is the only thing on screen a reader has to find — and it arches
   with the page's velocity like every other headline here. */
.faq__q {
  font-size: clamp(1.5rem, 3.4vw, 3.1rem);
  max-width: 17ch;
  --arch: 0.2em;
}

/* --- the answer, on the sheet's own paper -----------------------------------
   Two columns: what we say, and the four facts. The rule between them is the
   one that used to stand between the question and the answer — same element,
   same gesture, one place down the page, because the join it is drawing is now
   between the prose and the ledger. */
.faq__ans {
  position: relative;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1.28fr) minmax(0, 1fr);
  gap: clamp(26px, 4.4vw, 78px);
  align-items: start;
  padding-top: clamp(22px, 3.2vh, 44px);
}

.faq__say {
  min-width: 0;
}

/* --- the rule between the halves --------------------------------------------
   An element and not a border, because a border cannot be drawn from one end.
   It grows down out of the top of the ledger as the card comes up and retreats
   into it as the card leaves, so the two halves of the answer are legibly
   JOINED by the line rather than separated by it.

   Placed off the grid's own second column with a negative half-gap, which is
   the one number in this block written twice — here and on `.faq__ans`. Halved
   by hand rather than by a custom property because `clamp()` inside `calc()`
   inside a `var()` chain is a sentence nobody can read at a glance, and this
   one has to stay legible. */
.faq__spine {
  position: absolute;
  left: calc(56.2% - clamp(13px, 2.2vw, 39px));
  top: clamp(22px, 3.2vh, 44px);
  bottom: 0.5em;
  width: var(--hair);
  background: currentColor;
  opacity: 0.14;
  transform: scaleY(0);
  transform-origin: 50% 0;
  transition: transform 0.4s var(--ease-snap);
}

.hb.is-in .faq__card.is-live .faq__spine {
  transform: scaleY(1);
  transition: transform 0.95s var(--ease-out) 0.16s;
}

/* --- and the stack inside an answer -----------------------------------------
   IN, from the top, in reading order: `--j`. OUT, from the bottom, in the
   order a hand puts paper back: `--jr`. Both are written by `blocks.js`,
   because the stack is not the same shape on every card — five of the eight
   have a link — and `nth-child` would have to be re-derived every time the
   copy moves.

   The exit is its own state and not the entrance with the numbers reversed.
   Going in, a part arrives from BELOW its place, which is how type settles.
   Coming out it leaves UPWARDS, back under the question it came from, and it
   goes faster: the reader has already turned the card, and a dismissal that
   takes as long as a reveal is a dismissal that is arguing.

   The exit is also the RESTING state, which is the one thing that looks odd
   written down and is right: a card that has never been on screen and a card
   that has just left are the same thing to a reader who cannot see either of
   them, and a third class to tell them apart would exist only to be correct in
   the dark. */
.faq__step {
  opacity: 0;
  --ry: -14px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.26s var(--ease-snap),
    --ry 0.32s var(--ease-snap);
  transition-delay: calc(var(--jr, 0) * 38ms);
}

.hb.is-in .faq__card.is-live .faq__step {
  opacity: var(--op, 1);
  --ry: 0px;
  transition:
    opacity 0.55s var(--ease-out),
    --ry 0.72s var(--ease-out);
  transition-delay: calc(150ms + var(--j, 0) * 82ms);
}

/* --- the three registers ----------------------------------------------------
   The sentence, the ledger, the inconvenient part. They are set at three
   different weights of ink on purpose: the answer is what you read, the ledger
   is what you came for, and the note is what you get if you are still here.
   `--op` and not a hard `opacity`, because the entrance owns that property —
   see `.faq__step`. */
/* The measure came in with the column. The lead used to have the wider half of
   the card to itself; it now shares the sheet with the ledger beside it, and a
   paragraph that runs past about sixty characters at this size is a paragraph
   the eye loses the start of. */
.faq__lead {
  --op: 0.82;
  margin: 0;
  max-width: 54ch;
  font-size: var(--t-body);
  line-height: 1.65;
}

.faq__more {
  --op: 0.48;
  margin: clamp(18px, 2.4vh, 28px) 0 0;
  max-width: 52ch;
  font-size: var(--t-body);
  line-height: 1.65;
}

/* --- the ledger -------------------------------------------------------------
   A `dl` and not a table: these are pairs, and a table would claim the columns
   mean something ACROSS the rows, which they do not. "Terms / fixed price" has
   no relationship to "Site / six to ten weeks" beyond standing under the same
   question.

   Every pair carries its own hairline and every hairline is drawn from the
   left, one after the next, off `--f`. That is the one flourish in the block
   and it is the block's own gesture rather than a new one: the rail below is
   built out of graduations that arrive, and the ledger is that rail turned on
   its side. */
.faq__facts {
  margin: clamp(20px, 2.8vh, 32px) 0 0;
  max-width: 44rem;
}

.faq__fact {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 12.5em) minmax(0, 1fr);
  gap: clamp(10px, 1.6vw, 28px);
  align-items: baseline;
  padding: clamp(8px, 1.15vh, 14px) 0;
}

.faq__fact-rule {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--hair);
  background: currentColor;
  opacity: 0.14;
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
  /* Un-drawn the way it was drawn: back into the left margin, quickly, so a
     card that is leaving empties rather than merely slides away full. */
  transition: transform 0.3s var(--ease-snap);
}

.hb.is-in .faq__card.is-live .faq__fact-rule {
  transform: scaleX(1);
  transition: transform 0.85s var(--ease-out);
  transition-delay: calc(300ms + var(--f, 0) * 78ms);
}

.faq__fact dt {
  font-size: var(--t-micro);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.5;
  opacity: 0.44;
}

.faq__fact dd {
  margin: 0;
  font-size: clamp(0.92rem, 1.1vw, 1.06rem);
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  opacity: 0.85;
}

/* --- the way out of an answer -----------------------------------------------
   A hairline under a word, and deliberately NOT the chrome button: the note
   beside `chromeCta` in `blocks.js` divides the landing's controls into the
   five presses the page exists for and the navigation that happens inside a
   reading. A link at the foot of an answer is the second kind. It belongs to
   the paragraph it is standing in, and drawing it as the first kind would put
   ten calls to action on a page that has five. */
.faq__go {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.75em;
  margin: clamp(22px, 2.8vh, 34px) 0 0;
  padding-bottom: 0.62em;
  color: inherit;
  text-decoration: none;
  font-size: var(--t-micro);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
}

.faq__go-rule {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--hair);
  background: currentColor;
  opacity: 0.26;
  transform: scaleX(1);
  transform-origin: 0 50%;
  transition: opacity 0.4s var(--ease-out);
}

.faq__go:hover .faq__go-rule,
.faq__go:focus-visible .faq__go-rule { opacity: 0.9; }

/* The arrow is a hairline with a head on it, built out of the same one pixel
   everything else in the block is built out of. It travels on `--rx`, which is
   the same offset the two arrows in the head band take on hover — one gesture,
   two places, so the block has a hand rather than a set of effects. */
.faq__go-arrow {
  position: relative;
  display: block;
  width: 1.5em;
  height: var(--hair);
  background: currentColor;
  opacity: 0.7;
  transform: translate3d(var(--rx, 0px), 0, 0);
  transition:
    opacity 0.4s var(--ease-out),
    --rx 0.45s var(--ease-out);
}

.faq__go-arrow::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  width: 0.42em;
  height: 0.42em;
  border-top: var(--hair) solid currentColor;
  border-right: var(--hair) solid currentColor;
  transform: translate(0, -50%) rotate(45deg);
  transform-origin: 100% 50%;
}

.faq__go:hover .faq__go-arrow,
.faq__go:focus-visible .faq__go-arrow { opacity: 1; --rx: 8px; }

.faq__go:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 5px;
}

/* --- the rail ---------------------------------------------------------------
   Eight numbers hung under one hairline, each with a graduation dropping from
   it, and a short segment of full ink riding along underneath. It is a ruler,
   and it is a ruler on purpose: a row of dots says "there are eight of these",
   a ruler says "you are here, and here is how far the rest of it goes".

   The carriage rides `--fk` like everything else on the page, so it is never
   out of step with the track — there is no second animation to interrupt. And
   the ticks are the arch: a row of eight objects lagged off `--u` by the
   page's own velocity, which is the same figure the headline words run across
   a line and the same one the old list ran down a column. */
.faq__rail {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  margin-top: clamp(22px, 2.8vh, 36px);
}

.faq__rail-line {
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--hair);
  background: currentColor;
  opacity: 0.1;
  transform: scaleX(calc(0.3 + 0.7 * var(--q, 1)));
  transform-origin: 0 50%;
  transition: transform 0.9s var(--ease-out);
  pointer-events: none;
}

.faq__rail-car {
  position: absolute;
  left: 0;
  top: 0;
  width: calc(100% / var(--n));
  height: var(--hair);
  background: currentColor;
  opacity: 0.8;
  transform: translate3d(calc(var(--fk) * 100%), 0, 0);
  pointer-events: none;
}

.faq__tick {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  position: relative;
  display: block;
  text-align: left;
  padding: clamp(13px, 1.7vh, 20px) 0 0;
  cursor: pointer;
  --d: calc(var(--u, 0.5) * 2 - 1);
  --bow: calc(var(--d) * var(--d) * 4 - 1.1);
  transform: translate3d(0, calc(var(--flow) * var(--bow) * 0.45em), 0);
}

.faq__tick-n {
  display: block;
  font-size: var(--t-micro);
  letter-spacing: 0.14em;
  font-variant-numeric: tabular-nums;
  opacity: 0.24;
  transition: opacity 0.45s var(--ease-out);
}

.faq__tick:hover .faq__tick-n,
.faq__tick:focus-visible .faq__tick-n { opacity: 0.66; }

.faq__tick[aria-selected="true"] .faq__tick-n { opacity: 1; }

/* The graduation. Three lengths and three inks, and between them they are the
   difference between eight buttons and a scale: at rest a mark you can find
   when you look for it, under the hand a full one, and at the position you are
   at the longest line in the block. */
.faq__tick-m {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--hair);
  height: clamp(7px, 1vh, 11px);
  background: currentColor;
  opacity: 0.16;
  transform-origin: 50% 0;
  scale: 1 0.5;
  transition:
    opacity 0.4s var(--ease-out),
    scale 0.5s var(--ease-out);
}

.faq__tick:hover .faq__tick-m,
.faq__tick:focus-visible .faq__tick-m { opacity: 0.5; scale: 1 1; }

.faq__tick[aria-selected="true"] .faq__tick-m { opacity: 0.85; scale: 1 1.5; }

/* --- the question on the tick -----------------------------------------------
   In the control rather than beside it, so the tab's accessible name is "03,
   Who actually does the work?" and not "03". It is never `display: none` and
   never `visibility: hidden` for exactly that reason — both would take it out
   of the accessibility tree and leave eight controls called nothing.

   On a wide screen with a pointer it also SURFACES, above the rail, which is
   what turns eight numbers into an index somebody can use without turning
   eight cards to find out what is on them. */
.faq__tick-q {
  position: absolute;
  left: 0;
  bottom: calc(100% + clamp(9px, 1.2vh, 14px));
  white-space: nowrap;
  font-size: var(--t-micro);
  letter-spacing: 0.01em;
  opacity: 0;
  pointer-events: none;
  transform: translate3d(0, 5px, 0);
  transition:
    opacity 0.3s var(--ease-out),
    transform 0.3s var(--ease-out);
}

/* The last three read back into the rail rather than off the edge of it. */
.faq__tick:nth-last-child(-n + 3) .faq__tick-q {
  left: auto;
  right: 0;
}

@media (min-width: 861px) and (hover: hover) {
  .faq__tick:hover .faq__tick-q,
  .faq__tick:focus-visible .faq__tick-q {
    opacity: 0.6;
    transform: none;
  }
}

.faq__tick:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
}

/* --- the foot of the deck ---------------------------------------------------
   Eight answers that end on the eighth answer end on a full stop, and the
   reader who turned all eight without finding theirs is the most qualified
   press on the page. It arrives after the deck has. */
.faq__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: clamp(10px, 2vw, 28px);
  margin-top: clamp(28px, 4vh, 56px);
  opacity: 0;
  --ry: 16px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.7s var(--ease-out),
    --ry 0.7s var(--ease-out);
  transition-delay: 0.52s;
}

.hb.is-in .faq__foot { opacity: 0.9; --ry: 0px; }

.faq__foot-t {
  font-size: var(--t-micro);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.44;
}

/* The link in the foot has already arrived with the row it sits in, so it must
   not run the answer stack's entrance on top of it. */
.faq__foot .faq__go { margin-top: 0; }

/* --- and none of this for a reader who asked for none -----------------------
   Scoped to the block, next to what it is undoing. The deck still WORKS under
   this setting — every control turns it, every card is reachable — it simply
   arrives rather than travels. `--fk` is written the same way it always is and
   the transition on it is what goes, which is the difference between a
   carousel that respects the preference and one that quietly stops being a
   carousel. */
@media (prefers-reduced-motion: reduce) {
  .faq__deck,
  .faq__track { transition: none; }
  .faq__card-in { transform: none; }
  .faq__tick { transform: none; }
  .faq__step,
  .hb.is-in .faq__card.is-live .faq__step {
    opacity: var(--op, 1);
    transform: none;
    transition: none;
    transition-delay: 0s;
  }
  /* Still off screen when the card is: the viewport clips, so a stack at full
     ink on a card at `--dk: 3` is a stack nobody can see. What this avoids is
     four fades and a slide firing on a reader who asked for none. */
  .faq__spine,
  .hb.is-in .faq__card.is-live .faq__spine,
  .faq__fact-rule,
  .hb.is-in .faq__card.is-live .faq__fact-rule {
    transform: none;
    transition: none;
    transition-delay: 0s;
  }
  .faq__foot { transition-delay: 0s; }
  /* THE ROOM STAYS, AND THE SCREEN STAYS DRAWN.

     Neither is motion in the sense this query is about. The photograph is the
     block's ground and the line screen over it is how the block is PRINTED —
     take it away and the questions are standing on a bare picture with nothing
     under them, which is a legibility failure rather than a calmer page.

     What goes is the drift. `ask-screen.js` reads this query itself and never
     advances its phase under it, so the screen is a still print that redraws
     only when the deck is turned. This is the other half: the photograph's own
     travel against the room, which rides the page's velocity like every window
     on this site and is the one thing here that moves without being asked. */
  .faq__shot { transform: scale(1.06); }
}

/* --- 11 the close ----------------------------------------------------------
   THE OBJECT AND THE WORDS ARE NOT ON THE SAME SHELF ANY MORE.

   --- what was wrong with it ------------------------------------------------
   The stage was full bleed, and a full-bleed stage is not a big picture — it
   is a picture with no edges to compose against. The ring's poster is set to
   sixty per cent of the CANVAS height, so the canvas being the whole block
   meant the words MAKE / SOMETHING / WORTH / KEEPING were four lines the
   height of a wall, and the bottom two of them landed exactly where the column
   stands. `align-content: end` had already pushed the column down to get it
   off the middle of the ring; all that did was walk it into the poster's last
   line instead. Two pieces of type, both at full ink, both centred, one behind
   the other. It was reported as the close "hitting" its own text, and that is
   precisely what it was.

   --- what it is now --------------------------------------------------------
   Two rows. The object gets the first and the words get the second, and they
   cannot reach each other because a grid row is a floor.

   THE STAGE IS 1.6, AND THAT RATIO IS NOT A TASTE. The ring's shader frames
   itself against a 1.6 reference box — `k = min(x / 1.6, y)` — so at exactly
   that ratio `k` IS the element's height, and the ring's diameter becomes a
   fixed fraction of one number. `--ring-h` is that number, and it is the whole
   size of the object: the tube, the poster it bends and the air around both
   scale together off it, which is what keeps the glass as FULL of type at the
   small size as it was at the large one. Sixty-five per cent of the screen,
   where the old full-bleed stage was a hundred: the object is a bit over a
   third smaller, which is what was asked for, and it is smaller by being
   FRAMED rather than by being pushed back.

   The stage may still be taller than its row, and that is deliberate — it is
   centred in it, so what overflows is the poster's paper margin and never the
   ring, which lives inside the middle 78% of its own frame. The overflow is
   paper on paper: the canvas is filled with the block's OWN measured `--bg`
   (see `#mount` in `section.js`), so there is no rectangle to see. */
/* --- AND THE CLOSE IS THE ONE BLOCK WITH NO SEAM ON EITHER SIDE OF IT --------
   Every block on this page but the first draws a hairline across its own top
   edge — see `.hb::before`. The rule already makes one exception, for a break
   that follows a break, and the reason it gives is the reason for this one:
   "a hairline across the top of a live canvas is a scratch on the lens". The
   close follows the questions, which are a column of type and not a break, so
   it was getting the full-strength seam — a line straight across the paper the
   ring floats on, a hand's width above the tube.

   And the foot below it draws its own, which is a second line closing the
   object in from underneath.

   Both are gone, and only around this one block: the seam is drawn by a
   pseudo-element on `.hb`, so a `display: none` here removes exactly two lines
   from the sheet and every other join on the page keeps its. What the close is
   composed of is a ring, a sentence and a sheet of paper with nothing else on
   it. */
.hb--cta::before,
.hb--cta + .hb--footer::before { display: none; }

.hb--cta {
  /* Row one is the object and it takes what is left; row two is the column and
     it takes what it needs. `minmax(0, 1fr)` and not `1fr`, or the stage's own
     min-content would stop the row ever shrinking on a short screen. */
  grid-template-rows: minmax(0, 1fr) auto;
  grid-template-columns: minmax(0, 1fr);
  align-content: stretch;
  align-items: center;

  /* The one number that is the size of the object. */
  --ring-h: calc(var(--vh) * 0.65);
}

/* Short screens: the column below is close to a fixed number of pixels tall,
   so on a laptop in landscape it is the ring that has to give, not the words.
   Two steps rather than a formula — the shader's own framing is already doing
   the continuous part. */
@media (max-height: 660px) { .hb--cta { --ring-h: calc(var(--vh) * 0.58); } }
@media (max-height: 540px) { .hb--cta { --ring-h: calc(var(--vh) * 0.40); } }

.hb--cta .hb__stage--behind {
  /* STILL ABSOLUTE — but absolute INTO THE FIRST ROW rather than over the
     whole block, which is the one thing that makes this work.

     An absolutely positioned child of a grid container can be assigned a grid
     area, and then that area is its containing block. So the stage is centred
     in row one and cannot reach row two, while still being out of flow: it
     contributes nothing to track sizing, which means it can be TALLER than its
     row without pushing the block past a viewport. As an in-flow item it did
     exactly that — `1fr` in a `min-height` container resolves to the item's
     own height, and the close came out 25px taller than the screen.

     BOTH LINES, ON BOTH AXES. `grid-row: 1` is `1 / auto`, and an `auto` end
     line on an out-of-flow child sends the containing block back to the grid
     container's padding box — silently, and it looks exactly like the rule not
     being read at all. `1 / 2` is the difference between the stage living in
     the first row and the stage living over the whole block. */
  grid-row: 1 / 2;
  grid-column: 1 / 2;
  inset: auto;
  justify-self: center;
  align-self: center;
  aspect-ratio: 1.6;
  height: var(--ring-h);
  /* On a narrow screen the width binds first and the ratio takes the height
     down with it — which is also what the shader does at that aspect, so the
     two agree instead of fighting over who is cropping. */
  /* On a phone the WIDTH is what binds — the shader's 1.6 reference box means
     a portrait host can never give the ring more than about half its width —
     so every pixel of it is worth having and there is no margin to keep: the
     frame is filled with the block's own paper, so its edges are not edges. */
  max-width: 100%;
}

.cta__inner {
  grid-row: 2;
  width: var(--col);
  margin: 0 auto;
  position: relative;
  z-index: 2;
  padding-bottom: clamp(48px, 8vh, 110px);
  /* 46ch WAS A PARAGRAPH'S COLUMN and there is no paragraph in here any more.
     The cap existed to keep the headline over a body of text set at the same
     width — take the text away and it is just a display line held to a third
     of the width of the object it is standing under. 58ch is the widest this
     sentence can be set and still break where it has always broken: "Tell us
     what is / not working.", two lines, balanced. */
  max-width: min(58ch, var(--col));
  /* THE CLOSE IS THE ONE BLOCK ON THIS PAGE SET ON ITS CENTRE LINE, and it is
     the only one that has earned it: everything above is a reading, and a
     reading is left-aligned because a reader's eye needs a fixed margin to
     return to. This is not a reading. It is a sentence and a press standing on
     a ring that has been centred in the block since the day it was mounted —
     the type was hanging off the left edge of an object whose whole shape is
     radial. One axis for the three of them. */
  text-align: center;
}

/* Bigger than it was, and the ring paying for it is the point: an object a
   third smaller leaves the block with room for the sentence it exists to ask,
   and a closing headline set two steps under every other headline on the page
   reads as a caption for the ring rather than as the last thing the page says.

   Still short of `--t-title`. The measure here is 46ch — it is a paragraph's
   column, not a display column — and the page's title size would take this
   sentence to four lines inside it.

   AND IT IS TWO FIFTHS BIGGER THAN THAT NOW — every term of the clamp times
   1.4, so the floor, the slope and the ceiling all move together and the
   sentence scales the same way at every width instead of hitting its cap
   sooner. 3.4rem became 4.76rem: at the measure it is set in, "Tell us what
   is" is 458px of a 503px column, which is the SAME two-line break it had at
   the small size — the line that follows would need another 84px to fit and
   there is nowhere for it to get them. A closing headline that re-wrapped
   when it grew would be a different composition; this is the same one, read
   from closer. */
.cta__title {
  /* UP AGAIN, and the ring is the argument. The close is one sentence over a
     1370px object now, and a headline set to 500px under it reads as a caption
     for the ring rather than as the last thing the page says — which is the
     exact failure the old comment above described, arrived at from the other
     side. Same clamp shape, every term scaled: floor, slope and ceiling move
     together, so the sentence grows the same way at every width. */
  font-size: clamp(2.6rem, 5.4vw, 6.2rem);
  /* More bend under the same velocity than the rest of the page. The close is
     the one headline the reader arrives at with the wheel still turning. */
  --arch: 0.32em;
}

/* --- the words arriving -----------------------------------------------------
   The page's own entrance is a word rising out of a clipped box, one word
   every 42ms. That is right and it is the same gesture in every block; this
   one is the LAST of the eleven, so it is allowed to take longer over it.

   Three differences, and each is one property:

   the SWING. The word does not slide up, it swings up — it starts three
   degrees off and lands level, pivoting on its own bottom left corner. The
   mask is what makes that read: what you see is the far corner of the word
   clearing the edge before the near one does, which is a page of type being
   SET rather than a row of blocks being moved.

   the STAGGER, at 88ms and rising: `--wi` is the word's index, so "working."
   lands three quarters of a second after "Tell". Long enough to be a sentence
   being spoken rather than a line being revealed.

   the SETTLE, at 1.3s on the house curve, which is nearly all tail. */
/* --- AND THE SWING IS GONE, because this headline MELTS now ----------------
   What stood here was the block's own entrance: the word did not slide up, it
   swung up, starting three degrees off `--d` and pivoting on the outer corner
   of its row so the two halves of a centred line leaned towards each other and
   landed level together. 118%, 1.3s, 88ms a word.

   It is deleted rather than scoped off, and the deletion is the point: the
   swing and the melt want opposite things from the same element. The swing is
   READ THROUGH THE MASK — the whole gesture is a corner clearing an edge, and
   there is no gesture at all without `.hx__w`'s `overflow: hidden`. The melt
   needs that mask gone, because what it has to show is a blur half the size of
   the type spreading past the word's own box until it welds to its neighbour.
   Keeping both would have been a mask that clips the effect it is wrapping.

   Where the entrance lives now: `.hx--morph` at the foot of this file, and
   `hx--morph` on the heading in `blocks.js`. The 88ms stagger survived the move
   — `--melt-stag` is 90 — because that number was never about the swing. It is
   how long a reader takes to read one word of a closing sentence. */

/* The rule under the headline, drawn after the last word lands.

   Same idiom as `.hb__kicker-rule` and the same reason: this page is made of
   hairlines, and a block whose headline just floats over its paragraph is the
   one place on the landing where two pieces of type are left to settle their
   own relationship. A pseudo-element, so nothing is added to the document for
   a line. */
.cta__title::after {
  content: "";
  display: block;
  /* A FULL-MEASURE RULE UNDER A CENTRED HEADLINE IS A BOX LID. It was drawn
     the width of the column because it was separating two things — a headline
     and the paragraph that answered it — and there is nothing under it now but
     air and a button. So it stops being a divider and becomes a MARK: a short
     hairline on the centre line, the same one printed everywhere else on the
     page, opening from its own middle in both directions at once. */
  width: clamp(60px, 6.5vw, 108px);
  height: var(--hair);
  margin: clamp(24px, 3.2vh, 42px) auto 0;
  background: currentColor;
  opacity: 0.28;
  transform: scaleX(0);
  transform-origin: 50% 50%;
  transition: transform 1.25s var(--ease-out) 0.52s;
}

.hb--cta.is-in .cta__title::after { transform: scaleX(1); }



.cta__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(18px, 3vw, 40px);
  margin-top: clamp(28px, 4vh, 52px);
  opacity: 0;
  --ry: 14px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.8s var(--ease-out) 0.72s,
    --ry 0.8s var(--ease-out) 0.72s;
}

.hb--cta.is-in .cta__row { opacity: 1; --ry: 0px; }

/* The close's press is the chrome control, and it is the biggest one on the
   page — a thicker rim and the large size, because it is the last thing the
   landing asks for and the only one that leads to a form.

   The row already carries the entrance, so the button's own row must not add a
   second one on top of it. */
.cta__row .hb__act {
  margin-top: 0;
  opacity: 1;
  --ry: 0px;
  transition: none;
}

/* THE ROW HAS ONE THING IN IT NOW, so it is a row in name only — what it is
   is the third object on the block's centre line. Scoped, because About's door
   still uses `.cta__row` for what it was built as: a press and an address side
   by side, hard against that page's left margin.

   The gap over it grows a step. The rule above it is 40px of hairline instead
   of a full column and the paragraph that used to hold the two apart is gone;
   at the old margin the button crowded a mark it is not attached to. */
.hb--cta .cta__row {
  justify-content: center;
  margin-top: clamp(34px, 5vh, 66px);
}

.cta__mail {
  color: inherit;
  font-size: var(--t-body);
  text-decoration: none;
  border-bottom: var(--hair) solid currentColor;
  padding-bottom: 0.2em;
  opacity: 0.7;
}

.cta__mail:hover { opacity: 1; }


/* --- and none of the above on a reader who asked for none -------------------
   The rule, the blur and the address's line are three entrances on one block.
   Under a reduced-motion request they are three states, arrived at without
   travelling.

   IT WAS FOUR. The headline's swing was the first name in this list and it is
   not here any more, because the headline does not swing — it melts, and the
   melt stills itself where it is declared. See `.hx--morph` at the foot of this
   file. A selector left here for an entrance that no longer exists is a rule
   that looks like it is doing the work. */
@media (prefers-reduced-motion: reduce) {
  .cta__title::after,
  /* UNSCOPED, and deliberately: About's door is built out of this same class
     and this is the only rule on the site that stills it. */
  .cta__row {
    transition: none;
  }
}

/* --- 10 the foot -----------------------------------------------------------
   The last block of the run, and it is the tenth. It used to be the only one
   with no number on it, which is why it never read as part of the page: every
   other block says where it is before it says anything else.

   --- WHAT WAS WRONG WITH IT ------------------------------------------------
   Two short link columns hard against the left margin, an address the size of
   a caption hard against the right, and a third of the page of nothing between
   them. That void was the layout telling on itself: `space-between` with two
   children is not a composition, it is an absence of one, and it put the ONE
   thing down here the page wants pressed — the address — last in the reading
   order and smallest on the screen.

   It is a twelve-column grid now and the order is reversed. The address leads,
   left, at display size, under the block's own number. The lists answer it
   from the right at five rows each, which is what closes the line. Nothing in
   the middle is empty because there is no middle any more.

   --- `--enter`, and why it is registered ------------------------------------
   The wordmark is a shadow-DOM component and a shadow tree cannot see
   `.is-in` on an ancestor. It can see an inherited CUSTOM PROPERTY, because
   those cross the boundary — so the block's arrival is published as a number
   here, once, and the component's own stylesheet reads it. That is the same
   contract every other entrance on this page has with `is-in`, stated in the
   one currency a component can be handed.

   Registered rather than left as a token because it is arithmetic on the other
   side of the boundary, and a `<number>` the engine has parsed is cheaper to
   substitute into nine `calc()`s than a string it has to re-parse. */
@property --enter {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* --- the end of the sheet, and the strip it may not stand in ----------------
   THIS RULE USED TO LOSE. It was written `.hb--footer`, one class, against
   `.hb.is-clean` — two classes, and the footer carries both — so neither of
   its paddings was ever applied and the block paid the clean block's 15vh at
   both ends. Two hundred pixels of nothing under the copyright line at a desk,
   which is the white band this change was opened to remove.

   Fixing the specificity alone would have made it worse in a way that is worth
   stating, because the number it was trying to apply was itself wrong: 56px of
   bottom padding under a bottom rail that is SIXTY pixels tall. The copyright
   line would have been printed underneath the row of social marks.

   So the reserve is not a number here at all, it is `--floor` — the rail's own
   footprint, declared once in styles.css where the control that sets its height
   reads it too, with the phone's home indicator already inside it. Whatever
   that strip measures, the sheet stops that far above the bottom of the window
   plus a gap of its own, at every width, on every display. The overlap this
   guards against cannot be reintroduced by editing one of the two, because
   there are no longer two.

   THE TOP IS DELIBERATELY NOT HERE. `.hb.is-clean` owns it and has been drawing
   it the whole time; naming it again would be a second declaration of a
   distance that is already right, and the seam above the foot is measured from
   what that rule says. */
.hb--footer.is-clean {
  padding-bottom: calc(var(--floor) + clamp(22px, 2.4vh, 34px));
}

.hb--footer {
  --enter: 0;
}

.hb--footer.is-in { --enter: 1; }

.foot {
  width: var(--col);
  margin: 0 auto;
}

/* --- the head of the foot: address left, lists right ----------------------- */
.foot__top {
  position: relative;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: clamp(18px, 2.6vw, 44px);
  row-gap: clamp(46px, 7vh, 88px);
  align-items: start;
  padding-bottom: clamp(58px, 9vh, 120px);
}

/* The rule under the head, drawn from the middle out rather than from the left
   — the seam at the top of every block on this page opens the same way, and
   this is the only other full-width hairline in the foot. Absolutely
   positioned, so it is not a grid item and does not have to be told which
   columns it spans. */
.foot__top::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: var(--hair);
  background: currentColor;
  opacity: var(--rule-o);
  transform: scaleX(0);
  transform-origin: 50% 50%;
  transition: transform 1.2s var(--ease-out) 0.24s;
}

.hb--footer.is-in .foot__top::after { transform: scaleX(1); }

.foot__say { grid-column: 1 / span 6; }
.foot__cols { grid-column: 8 / span 5; }

.foot__cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(26px, 4vw, 70px);
}

.foot__col-title {
  margin: 0 0 1.6em;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0;
  --ry: 12px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.7s var(--ease-out),
    --ry 0.7s var(--ease-out);
  transition-delay: calc(var(--ci, 0) * 90ms);
}

.hb--footer.is-in .foot__col-title { opacity: 0.4; --ry: 0px; }

/* The rule under a column title. It is the same mark the kicker at the head of
   every block carries, turned into a heading rule — so the two lists read as
   filed rather than as stacked, which is what they were missing when they sat
   in the corner with nothing around them. */
.foot__col-title::after {
  content: "";
  display: block;
  height: var(--hair);
  margin-top: 0.9em;
  background: currentColor;
  opacity: var(--rule-o);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.9s var(--ease-out) calc(0.14s + var(--ci, 0) * 90ms);
}

.hb--footer.is-in .foot__col-title::after { transform: scaleX(1); }

.foot__col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.62em;
}

/* Every row arrives on its own beat, and the beat is a function of BOTH the
   column it is in and its place down that column — so the foot resolves as a
   diagonal rather than as two lists that happened to start together. */
.foot__col li {
  opacity: 0;
  --rx: -14px;
  transform: translate3d(var(--rx), 0, 0);
  transition:
    opacity 0.65s var(--ease-out),
    --rx 0.75s var(--ease-out);
  transition-delay: calc(120ms + var(--ci, 0) * 90ms + var(--i, 0) * 55ms);
}

.hb--footer.is-in .foot__col li { opacity: 1; --rx: 0px; }

/* --- a link, and what it does when it is pointed at -------------------------
   The rule at the left grows out of nothing and the word steps aside to make
   room for it. Two moves, one gesture, and neither of them is a colour: a link
   that answers by getting brighter has told you it is a link, and a link that
   MOVES has told you it is a control.

   The gap is the width the rule will need, held open from the start, so the
   row's box never changes size and the list cannot reflow under the pointer.
   `row-reverse` is what puts the rule to the LEFT of the word while leaving the
   word first in the markup, where a screen reader wants it. */
.foot__link {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  padding: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}

.foot__link {
  font-size: var(--t-body);
  display: inline-flex;
  flex-direction: row-reverse;
  align-items: center;
  justify-content: flex-end;
  gap: 0.62em;
  opacity: 0.66;
  transition: opacity 0.35s var(--ease-out);
}

.foot__link:hover,
.foot__link:focus-visible { opacity: 1; }

.foot__link-rule {
  display: block;
  width: 1.5em;
  height: var(--hair);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.5s var(--ease-out);
}

.foot__link:hover .foot__link-rule,
.foot__link:focus-visible .foot__link-rule { transform: scaleX(1); }

/* --- a label that changes by moving -----------------------------------------
   Two copies of the word in a box the height of one of them. On hover the pair
   travels up by exactly that height: the word you were reading leaves and an
   identical one arrives from below. Nothing has faded, nothing has recoloured,
   and the row has answered.

   `overflow: hidden` on a box sized in `em` clips descenders, so the box is
   given the descender back as padding and takes the same amount off its own
   bottom margin — the same trick `.hx__w` uses for exactly the same reason. */
.sl {
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: 0.16em;
  margin-bottom: -0.16em;
}

.sl__t {
  display: block;
  font-style: inherit;
  transform: translate3d(0, var(--ry, 0px), 0);
  transition: --ry 0.5s var(--ease-out);
}

.sl__t--ghost {
  position: absolute;
  left: 0;
  top: 0;
  --ry: 100%;
}

.foot__link:hover .sl__t,
.foot__link:focus-visible .sl__t,
.foot__mail:hover .sl__t,
.foot__mail:focus-visible .sl__t { --ry: -100%; }

.foot__link:hover .sl__t--ghost,
.foot__link:focus-visible .sl__t--ghost,
.foot__mail:hover .sl__t--ghost,
.foot__mail:focus-visible .sl__t--ghost { --ry: 0%; }

/* --- the address ------------------------------------------------------------
   The one thing in the foot the page actually wants pressed, so it is the one
   thing set at a size a reader can hit from a metre away — and it is now set
   at a size that says so from across the room as well. It was `2.2vw` in the
   corner opposite two lists; it leads the block at nearly half again that,
   which is the difference between a footer that has contact details in it and
   a footer that is asking.

   The rule under it is an element rather than a border for the reason every
   other rule on this page is: it is redrawn on hover, and a border cannot be
   redrawn. */
.foot__say { min-width: 0; }

.foot__mail {
  display: inline-block;
  position: relative;
  max-width: 100%;
  color: inherit;
  text-decoration: none;
  font-size: clamp(1.5rem, 3.15vw, 3rem);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.03em;
  padding-bottom: 0.16em;
  opacity: 0;
  --ry: 18px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.8s var(--ease-out) 0.26s,
    --ry 0.8s var(--ease-out) 0.26s;
}

.hb--footer.is-in .foot__mail { opacity: 1; --ry: 0px; }

.foot__mail-rule {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--hair);
  background: currentColor;
  opacity: var(--rule-o);
  transform-origin: 0 50%;
  transition: opacity 0.4s var(--ease-out);
}

/* Out to the right and back in from the left. The rule is REDRAWN rather than
   lit, which is the same claim the word above it makes by sliding — and it is
   a keyframe rather than a transition because a transition cannot pass through
   a state, and the whole of this is the state in the middle where the line is
   not there. */
.foot__mail:hover .foot__mail-rule,
.foot__mail:focus-visible .foot__mail-rule {
  opacity: 1;
  animation: foot-rule 0.62s var(--ease-out);
}

@keyframes foot-rule {
  0%   { transform: scaleX(1); transform-origin: 100% 50%; }
  49%  { transform: scaleX(0); transform-origin: 100% 50%; }
  50%  { transform: scaleX(0); transform-origin: 0 50%; }
  100% { transform: scaleX(1); transform-origin: 0 50%; }
}

.foot__say-note {
  margin: 1.6em 0 0;
  max-width: 38ch;
  font-size: var(--t-body);
  line-height: 1.65;
  opacity: 0;
  --ry: 12px;
  transform: translate3d(0, var(--ry), 0);
  transition:
    opacity 0.8s var(--ease-out) 0.42s,
    --ry 0.8s var(--ease-out) 0.42s;
}

.hb--footer.is-in .foot__say-note { opacity: 0.5; --ry: 0px; }

/* --- the wordmark -----------------------------------------------------------
   The last thing, the only thing down here allowed to be loud, and now the
   only piece of type on this page with a MATERIAL.

   It has been three things. It was one block under a single `skewY`, and a
   skew is one affine transform — every letter leans by the same amount, which
   is a picture of a rigid page. It became nine spans carrying the headlines'
   own bow, which fixed the rigidity and left the real problem: the studio's
   name was a black slab the width of the page that did nothing.

   It is `<vs-anim-weight-wave>` now. Each letter springs toward a sine target
   offset by its own index, so the WEIGHT of the word travels through it left
   to right — wider and faster while the page is moving, settling while it is
   not, which is the argument every other curve on this landing makes. The
   physics are in the component; the two numbers below are the look, and they
   are here because this is where the rest of the foot is.

   `--ww-swell` is what a crest is worth in stroke. On Satoshi Medium at this
   size it is the difference between the Medium and the Black, drawn rather
   than swapped — see the head of `vs-anim-weight-wave.js` for why an optical
   weight beats a `wght` axis on a page set in five static files.
   `--ww-lift` is the hair of rise that comes with it, so a crest is a letter
   leaning out of the page and not only a letter getting fatter.

   The lean and the bow are unchanged and they are not in this file any more:
   they read `--q` and `--flow` from here, through the shadow boundary, and are
   drawn inside the component. One deformation, still. */
.foot__mark {
  display: block;
  font-size: clamp(3.2rem, 19.6vw, 19.8rem);
  font-weight: 500;
  line-height: 0.84;
  letter-spacing: -0.05em;
  margin: clamp(10px, 2vh, 26px) 0 0;
  --ww-swell: 0.05em;
  --ww-lift: 0.022em;
  /* NO INSET FOR THE STROKE, and that is deliberate. Half of a crest's stroke
     is drawn outside the glyph's own box — about eight pixels at the widest
     this ever renders — so the ink of the word reaches a fraction past the
     column at the top of a wave. Padding it in would fix an overflow that does
     not exist (the page's own margin is twenty times that) at the cost of the
     one thing the wordmark has to get right at rest: its left edge is the
     column's left edge, the same as every other line in the foot. */
}

/* --- the last row on the page -----------------------------------------------
   Two runs of the same type at the same size: the colophon on the left, the
   copyright on the right, and nothing between them but the column gap.

   THE ALIGNMENT IS A BASELINE AND IT IS THE LAST ONE. `end` — which is what
   this row used to say — aligns the BOXES, and two boxes of type only share a
   bottom edge when their descenders happen to agree; it is why the copyright
   sat a pixel and a half below the colophon it was supposed to be level with,
   and why it fell apart entirely the moment either side wrapped. A baseline is
   the line the letters actually stand on, which is the thing an eye reads two
   runs of type against.

   `last` rather than plain `baseline` is the wrap case, and it is the whole
   reason this is safe at every width: with one line each the two are identical,
   and if the colophon breaks over two lines at some middle width the copyright
   sits on the LAST of them — level with the bottom of the block rather than
   floating at the top of it. The `end` above is the fallback for an engine that
   does not know the keyword; it is what this row did before and it is never
   worse than that. */
.foot__bottom {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  align-items: last baseline;
  column-gap: clamp(24px, 5vw, 80px);
  row-gap: 22px;
  margin-top: clamp(30px, 5vh, 60px);
  padding-top: clamp(20px, 3vh, 32px);
  position: relative;
}

/* The last line on the page, drawn from the left as the block resolves. */
.foot__bottom::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--hair);
  background: currentColor;
  opacity: var(--rule-o);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 1.1s var(--ease-out) 0.3s;
}

.hb--footer.is-in .foot__bottom::before { transform: scaleX(1); }

/* NO MEASURE ON THIS ONE, and it is the only paragraph on the page without.

   `max-width: 56ch` is the right instruction for a paragraph a reader READS —
   the foot's note carries it, every body run on the site carries it — and it
   was the wrong one here. This is not a paragraph, it is a COLOPHON: one run
   of filing set at the smallest size on the sheet, sharing a rule with the
   copyright line opposite it. Held to 56 characters it broke over two lines
   against a one-line legal notice, and no alignment can rescue two blocks of
   different depth sitting on the same rule — the row read as a mistake at
   every width wide enough to have avoided it.

   Unbounded, the grid column is the measure: the track is `minmax(0, 1fr)`
   against the legal line's own width, which is a hair over a thousand pixels
   at a desk and holds the whole sentence on one line. Below that it wraps, and
   the `last baseline` on the row is what keeps the two agreeing when it does —
   see `.foot__bottom`. */
/* THE ONLY TWO RUNS ON THE PAGE THAT WERE SMALL AND FAINT AND LIGHT AT ONCE,
   which is why they were the only two that fell apart.

   Measured on the light sheet they were black at four tenths — rgb(153) on
   paper, 2.85:1 — set at `--t-micro`, which resolves to 10.4px at a desk, in
   the face's 400. Every other quiet run in this footer holds back on ONE of
   those axes and not three: the column titles are the same size at the same
   opacity but they are 500 and set in caps with tracking, the links and the
   note are half again the size. Four tenths is the sheet's quiet register and
   it is right everywhere it is spent on type big enough or heavy enough to
   carry it. Here it was spent on the smallest, lightest line on the site.

   AND THE LANDING IS A COMPOSITED LAYER, WHICH IS THE OTHER HALF OF IT. `.home`
   is `position: fixed` and `#homeTrack` carries `will-change: transform`, so the
   whole page is one texture the compositor translates rather than a document it
   repaints — and `flow.js` writes that translate to two decimals. A layer parked
   on a fractional pixel is sampled bilinearly on the way to the screen: half a
   pixel of blur laid over everything. On the wordmark it is nothing. On a 400
   stem at ten pixels and a sixth of the ink of the text beside it, it is the
   difference between type and a smudge — which is what it looked like, and why
   it read as though something were bending it. Nothing is: there is no filter,
   no rotation, no perspective and no radius anywhere in the ancestor chain.

   So both axes give a little. 0.55 puts them at 4.76:1, over the 4.5 an eye is
   entitled to at this size, and the 500 is the same weight the column titles
   opposite them already carry — a stem that survives half a pixel of resample
   where a hairline does not. The size does not move: `--t-micro` is shared with
   the run's own counter and four other places, and this row is not the place to
   redefine the sheet's smallest register. */
.foot__colophon {
  margin: 0;
  font-size: var(--t-micro);
  font-weight: 500;
  line-height: 1.7;
  opacity: 0.55;
}

.foot__legal {
  margin: 0;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.12em;
  line-height: 1.7;
  text-align: right;
  opacity: 0.55;
}

/* --- THE WAY BACK UP STOOD HERE, and it has gone ----------------------------
   Sixty lines: a label, and an arrow drawn as a hairline stem with two more
   rotated across its head. The arrow was the tell. Every other mark on this
   page is a RULE — a line that grows, a line that slides, a line that scales
   from its middle — and this one was a rule bent into a picture of something,
   which is the one thing the drawing on this site never does. Set beside the
   type it was also the only glyph on the sheet that had to be aimed at.

   And the page did not need it. The wordmark in the crown, HOME in the menu
   and the index all mean `goTo("home")` and all three are on screen while the
   foot is being read; a fourth door in the corner of the last block is a
   control that exists because the block had room, not because a reader was
   ever stuck. See `footer()` in blocks.js — the markup went with it, and so
   did its string in content.js. Deleted rather than commented out: a rule with
   no element left to match is a rule the next reader has to prove is dead. */

/* --- the dark sheet ---------------------------------------------------------
   `--ink` and `--paper` swap on the invert, and everything above follows them
   without knowing. These four do not, because they are the places where a
   photograph or a mixed colour is involved and a swap of two tokens cannot
   reason about a photograph. `section.js` puts the class on. */
/* --- THE LEDGER'S PICTURES STOOD HERE TOO, and they are on the rail --------
   One rule held eleven photographs back a little on a black ground and let each
   one take it back the moment it was chosen, driven by `--led-open` so that the
   theme could not fall out of step with the gesture.

   The eleven bands are gone — see the tombstone beside `.led__plate` — and the
   photographs are inside a WebGL sheet now, where the same job is done by two
   hex attributes handed to the shader from `MOUNTS.tilt`: the block's measured
   ground and the ink printed on it, read back off the cascade every time the
   dial turns the page over. A shader cannot read a class, and it does not have
   to: it is told what it is being painted onto.

   WHAT IS UNCHANGED IS THE RULE ITSELF. The frame is one bit; the work inside
   it is not, on either half of the theme. The plates on the rail are in full
   colour on the pale sheet and on the dark one. */

/* --- the shell, while the landing is up -------------------------------------
   The same trade the services column makes: the readings OF the ribbon step
   aside, the shell stays. Walking into a section is not leaving the site, and
   the way to the next one has to be on screen in this one. */
body.is-home .creed,
body.is-home .lockup__slot,
body.is-home .gauge,
body.is-home .plates,
body.is-home .probe,
body.is-home .tag {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.32s var(--ease-out);
}

/* The shell, and the panes it is read through. The landing is a full-bleed
   ground at `5`, so the panes have to come up with the shell or the correction
   sits under the thing it is correcting.

   The glass EDGE is deliberately not in this list. It is the boundary of the
   ribbon's run — the place where a strip that goes on forever leaves the
   screen — and this page has an end of its own. Brought up here it is a
   two-hundred-pixel curtain of ground colour across the foot of every block,
   over type that is trying to be read. See the note beside `.glassedge` in
   styles.css. */
body.is-home .chrome,
body.is-home .crown,
body.is-home .chromeglass {
  z-index: 6;
}

/* --- narrow ---------------------------------------------------------------- */

@media (max-width: 1000px) {
  /* THE FOOT UNSTACKS ITS TWO HALVES, and it does it here rather than at 620
     because the address is the thing that sets the floor: at `3.15vw` on a
     six-column cell, `hola@lusaxweb.com` starts breaking mid-word somewhere
     under a thousand pixels, and a broken address is worse than a stacked one.
     The order is unchanged — asking first, filing second — so the reading of
     the block survives the column count. */
  .foot__say,
  .foot__cols { grid-column: 1 / -1; }
  .foot__mail { font-size: clamp(1.5rem, 5.2vw, 2.6rem); }
  .hb__body--two { columns: 1; }
  /* THE ROOM BRINGS ITS RULES IN. Below a thousand the head rule and the foot
     rule at 9vh leave the transport and the caption crowding the screen, so
     the two bands come down and the screen comes down with them — the whole
     composition is measured off these, so one number moves the block. */
  .hb--motion {
    --mo-band: clamp(48px, 7vh, 84px);
    --mo-h: clamp(180px, min(calc(var(--vh) * 0.4), 46vw), 400px);
  }
}

@media (max-width: 860px) {
  .home {
    --arch: 0.24em;
  }
  /* THE SETTING HOLDS, AND THE HORIZON LIES DOWN.

     The one thing this block cannot give up at any width is the equation: the
     name is set at the size that makes it end on its own week. It survives
     untouched here, because it is written against `--measure` and `--measure`
     is whatever the column happens to be — a 620px sheet sets the eleven at
     620px worth of type and the ragged edge means exactly what it meant at
     1360.

     What folds is the HEAD. Two tracks with a monument numeral in the second
     one is a headline squeezed into 40% of a phone; the horizon drops under
     the headline and reads as a stamp on the head rule instead of as a
     monument beside it.

     AND THE PICTURES ARE DRIVEN BY THE SCROLL. There is no hand to hover with
     here, so a sheet whose photographs only exist under a pointer is a sheet
     with no photographs in it. `.is-at` — written by the observer in
     `home/section.js` onto whichever line is crossing the middle of the
     screen — is in the SAME selector list as `:hover` and `:focus-visible`
     further up, so the row opens exactly as it does on a desktop: the picture
     unrolls out of its own rule and the numeral walks from the high week to
     the low one, as the reader scrolls past. It is the desktop's one gesture,
     handed to a thumb, rather than a resting state standing in for it. */
  /* ONE COLUMN, and the measure goes back to the whole sheet with it. Two
     columns of names on a 375px screen is two columns of nothing: the equation
     would be solving eleven lines against 150 pixels. `--col-w` is the one
     place that has to change, because the size, the rule and the caption all
     read it — and the keystone's own override stays true, because half of one
     column and the whole of it are the same length here. */
  .led__body {
    grid-template-columns: minmax(0, 1fr);
    --col-w: var(--measure);
    /* Eleven rows on one column instead of five on two, so the gap between them
       is paid eleven times and is sized for that. */
    row-gap: clamp(10px, 2.2vw, 20px);
  }

  /* And the rail lies down. It is a band across a column of type, so on a
     screen a quarter of the width it was drawn for it keeps its PROPORTION
     rather than its height — a 52vh rail on a phone is most of the fold, which
     is a break, and this block is not one. */
  .led__rail {
    height: clamp(220px, 46vw, 380px);
  }

  .led__head {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: clamp(14px, 4vw, 26px);
  }

  /* THE TWO GAUGES GO, AND THEY GO BECAUSE THERE IS NO VOID TO SPAN. Both exist
     to make a horizontal distance mean something on a head and a foot that were
     half a screen wide. At this width the claim and its number are stacked and
     touching, and a dimension line drawn across two hundred pixels of a phone is
     a decoration of a measurement rather than one. */
  .led__gauge { display: none; }

  .led__horizon {
    grid-column: 1;
    justify-items: start;
    text-align: left;
    /* The two lines lie down beside each other on the rule rather than
       stacking, because the numeral is a stamp at this width and not a
       monument: a figure at 30vw over a caption is a second headline. */
    grid-auto-flow: column;
    align-items: baseline;
    gap: 0.7em;
  }

  .led__hz {
    font-size: clamp(2.6rem, 15vw, 5rem);
  }

  /* AND THE EQUATION GETS A FLOOR, which is the one thing it cannot have on a
     desktop and must have here.

     The measure at 375px is 311px, and 311px divided by the em width of "Web
     Design & Development" is TWELVE PIXELS. The setting is still exactly right
     and it is unreadable, which is the answer a chart gives when it is asked
     to be typography on a screen a quarter of the width it was solved for.

     So the size is floored and THE MEASURE MOVES DOWN ONE OBJECT. The rule
     under each name is still `--fr` of the column and the caption still stands
     on its end, so the ragged edge — which is the whole argument — is drawn by
     the rules rather than by the type. Six of the eleven still set
     proportionally and keep the ramp; the ones that would go under twenty
     pixels stop at twenty and let their own rule say the week for them.

     The floor is a `max()` inside the `clamp()` rather than a second rule, so
     there is still exactly one place in this stylesheet where a name is given
     a size. */
  .led__w {
    font-size: clamp(1.2rem, max(var(--fs), 5.4vw), 3.4rem);
  }

  /* --- the deck, on one column ------------------------------------------
     The card's two columns become one and the rule between them LIES DOWN
     across the top of the answer. Same object turned a quarter, which is
     cheaper to read than a second layout sharing nothing with the first — and
     it means the entrance is still one hairline being drawn, in the one
     direction there is room for it. */
  .faq__card-in {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(20px, 3.4vh, 30px);
  }
  .faq__spine {
    left: 0;
    right: 0;
    top: calc(-1 * clamp(10px, 1.7vh, 15px));
    bottom: auto;
    width: auto;
    height: var(--hair);
    transform: scaleX(0);
    transform-origin: 0 50%;
  }
  .hb.is-in .faq__card.is-live .faq__spine { transform: scaleX(1); }
  /* The head band stacks. A headline beside a count and two arrows at this
     width is a headline four words wide with a gutter for company. */
  .faq__head { grid-template-columns: minmax(0, 1fr); }
  .faq__meta { align-items: flex-start; text-align: left; }
  /* The ledger folds. A key column of 12.5em against a value column on a
     360px screen is two words a line in both of them. */
  .faq__fact { grid-template-columns: minmax(0, 1fr); gap: 0.2em; }
  /* Eight numbers across a phone is eight numbers with nothing between them.
     They come down a size and lose most of their tracking; the graduations do
     not, because at this width the ruler is doing more of the work than the
     digits on it are. */
  .faq__tick-n { font-size: 0.62rem; letter-spacing: 0.05em; }
  /* And the question on the tick is CLIPPED rather than merely transparent.
     It never surfaces at this width — the rule that shows it is behind
     `min-width: 861px` and a real pointer — and a nowrap sentence pinned to a
     43px control is three hundred pixels sticking out of the block. The layer
     clips, so nothing scrolls sideways, but the deck reports a scroll width it
     does not have and a browser that decides to reveal a focused control can
     still chase it off the edge.

     The sr-only recipe and not `display: none`: this is the accessible NAME of
     the control. Hide it properly and the rail is eight buttons called "01". */
  .faq__tick-q {
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }
  /* The run's four cells hold four things across one column, and at this width
     the column is not wide enough for the pair in either row. The two that go
     are the two that repeat something: the chips are on every card in the
     ribbon this block opens, and the bands come down with them. */
  .hb--reel {
    --reel-top: clamp(86px, 15vh, 150px);
    --reel-bot: clamp(92px, 16vh, 160px);
    --reel-air: clamp(12px, 1.8vh, 22px);
  }
  /* Two lines in the head band, at this width — the block's own number and the
     headline, and the headline wraps sooner here, which is why the growth is
     proportionally larger than it is on a wide screen. It was THREE and the
     number was `clamp(150px, 24vh, 226px)`; the studio's filing went and took a
     line and a gutter of the photograph back with it. */
  .hb--reel.is-first { --reel-top: clamp(152px, 17vh, 190px); }
  /* `.reel__tags { display: none }` stood here, and it was a width problem for a
     line of type this block no longer prints. The filing is an sr-only live
     region now, and a live region is the same length at every viewport — hiding
     a third of it under 860px would announce less to the one reader it is for. */
  .reel__title { font-size: clamp(1.15rem, 3.4vw, 1.6rem); max-width: 15ch; }
  .reel__mark { width: clamp(11px, 1.5vw, 16px); }
  /* Taller here than the fraction of the viewport a banner wants, and it has
     to be: the head is absolutely positioned along the foot of the band and
     the band clips its own overflow, but the copy in that head — a kicker, a
     headline, a paragraph at 48ch and a press — does not get shorter when the
     band does. It gets LONGER, because it wraps sooner. A band scaled off the
     viewport alone was cutting the top off its own paragraph. This is the copy
     as the floor and the viewport as the ceiling. */
  .vent__band { height: clamp(520px, 88vh, 760px); }
  /* The chips go with the service list's — at this width the filing is a name
     and a discipline, and two more words push the arrows off the row. */
  .rs__tags { display: none; }
  /* Tall enough that the WIDTH, and not the height, is what bounds the screen:
     on a phone the column is 350 or so and a 4:3 frame wants 262 of height to
     fill it. Below that the screen is a stamp in the middle of its own room. */
  .hb--motion { --mo-h: clamp(160px, min(calc(var(--vh) * 0.34), 58vw), 330px); }
  /* THE BOX CLOSES IN. Twenty pixels of air either side of a screen that is
     most of the width of the page is twenty pixels the picture does not have,
     and the box's own rules end up standing on the page's column. Ten is still
     air rather than a border at this size — the eye reads the gap, which is the
     whole job of it — and it gives the picture forty back. */
  .hb--motion { --mo-gap: 10px; }
  /* The hint goes. At this width it was taking the right-hand half of the head
     rule to name what is already playing under it. */
  .mo__hint { display: none; }
  /* AND THE HEADLINE GOES UP, which is the opposite of what a narrow branch
     usually does to type — for a reason that is arithmetic rather than taste.

     The screen is bounded on its own HEIGHT and capped at 330, so between 620
     and 860 it stops shrinking with the page while a headline set from `vw`
     keeps going: at 768 the line came out 437 wide against a 440 screen and
     landed wholly INSIDE the picture, which is the one thing this headline
     must never do. The line is about 9.7 characters of cap width; 7.8vw keeps
     it a clear head wider than the frame at every width in this branch and
     still a good margin inside the page's own column rules. */
  .mo__title { font-size: 7.8vw; }
}

/* --- short screens ----------------------------------------------------------
   The strip block has a kicker, a title, a note, a frame, a filing strip and a
   row of marks, and on a laptop at 680 pixels tall that is more than a screen.
   Keyed on HEIGHT, because that is the axis that ran out; the width branches
   above are a different problem. The type comes down and the frame comes down;
   the note goes, because a strip that is playing explains itself.

   The FIRST screen is the run, and on a short screen it is the block with the
   most furniture in it: two bands of type over a photograph, and a head band
   carrying three lines instead of one. Both bands come in, and the head band's
   first-screen growth comes in with them — otherwise the two of them take half
   the viewport and the photograph is a letterbox. */
@media (min-width: 1001px) and (max-height: 780px) {
  .hb--motion {
    --mo-band: clamp(42px, 7.5vh, 72px);
    --mo-air: clamp(9px, 1.5vh, 16px);
    --mo-h: clamp(170px, min(calc(var(--vh) * 0.44), 34vw), 340px);
  }
  /* THE CAPTION STAYS AT THIS HEIGHT, and that is a correction.

     It used to go with the rest of the short-screen branch, at 780 — which is
     every 720p laptop in the world, and 720p is not a short screen for this
     block: the room measures its screen at 46% of the layer, so at 690 of
     usable height there are still ninety pixels of air between the picture and
     the transport and the line sits in the middle of them.

     Where it genuinely does not fit is under 640, and that is its own branch
     below. The caption is the one line in the room that says what the twelve
     pieces ARE; losing it should cost a real shortage of screen, not a laptop
     lid.
     */
  /* And the headline comes down with the screen. It still has to CROSS the
     frame — that is the whole move — so it comes down by less than the screen
     does, and the overhang survives. */
  .mo__title { font-size: clamp(2.2rem, 7.4vw, 5.4rem); }
}

/* --- the run's two bands, on a short screen --------------------------------
   Keyed on HEIGHT ALONE, and that is the correction.

   This lived inside the branch above, which is `min-width: 1001px` — and every
   other rule in that branch is a wide-screen problem. The run's is not. A
   landscape phone at 812x375 is short and NARROW, so it fell through the crack
   and got the desktop band with a headline set at `15ch`: the block's own line
   ran 65..81 and the first line of the headline ran 66..138, one printed
   through the other. The 861–1000 strip fell through the same crack from the
   other side.

   Both bands come in, because on a short screen a band that is a fraction of
   the viewport takes a third of it and the photograph is a letterbox. The
   gutter comes in with them: it is a fraction of the viewport too, and it
   should stay in proportion to the band it separates. */
@media (max-height: 780px) {
  .hb--reel {
    --reel-top: clamp(84px, 15vh, 132px);
    --reel-bot: clamp(88px, 16vh, 142px);
    --reel-air: clamp(12px, 1.7vh, 22px);
  }

  /* The head band carries two lines when the block stands first, and on a short
     screen they do not fit in the band the composition wants.

     The band GROWS and the headline FOLDS. What does NOT happen is the stack
     moving up: the shell's wordmark sits at `--pad-edge` on the left and the
     dial and the way to Contact sit at `--pad-edge` on the right, and
     `--pad-edge` is `--pad * 0.6` — so lifting the block's line toward the top
     of the screen walks it straight into the chrome. That is the one kind of
     change this page is not allowed to make: a fix for the first screen that
     costs the header.

     It was THREE lines and `clamp(176px, 27vh, 218px)`. The studio's filing
     stood above the block's number saying what the chrome forty pixels higher
     already says, and on the shortest screen on the site it was costing a
     quarter of the photograph. Both went.

     `26ch` gives the headline back the two lines it is written with. It is cut
     to `15ch` under `max-width: 860px` — right for a headline standing alone
     in a column, wrong for one that has to clear a line above it. */
  .hb--reel.is-first {
    --reel-top: clamp(150px, 19vh, 168px);
  }
  .hb--reel.is-first .reel__title { max-width: 26ch; }

  /* Same argument as the width branch: the head's copy is the floor. */
  .vent__band { height: clamp(480px, 92vh, 640px); }
}

/* --- the projection room, on a genuinely short screen -----------------------
   Six hundred and forty is where the room actually runs out, and it is a long
   way below where the rest of this page's short-screen branch sits.

   The block is a pin: it holds ONE viewport of composition however tall the
   viewport is, and the composition is a head rule, a screen, a caption and a
   transport. The screen is measured as a fraction of the layer, so it shrinks
   with the room and never runs into anything. What does not shrink is the
   three rows of type — a rule of type is a rule of type at 400 pixels of
   height — and under about 640 the caption is the row that has to go, because
   the other two are the block's filing and the block's controls.

   Keyed on HEIGHT ALONE and not on width. A landscape phone at 812x375 is the
   case this is for, and every width branch in this file would miss it. */
@media (max-height: 640px) {
  .hb--motion {
    --mo-band: clamp(34px, 8vh, 60px);
    --mo-air: clamp(8px, 1.4vh, 14px);
    --mo-h: clamp(140px, min(calc(var(--vh) * 0.46), 34vw), 300px);
  }
  .mo__note { display: none; }
  /* And the box closes in with the room. See the note at `--mo-gap` in the 860
     branch: at a picture this size twenty pixels of stand-off is a quarter of
     the air the composition has left. */
  .hb--motion { --mo-gap: 10px; }
}

@media (max-width: 620px) {
  /* One column of lists, not two. Two columns of five rows in half a phone is
     a link a thumb cannot separate from the one beside it, and the pair of
     them only ever existed to close a line that is not there at this width. */
  .foot__cols { grid-template-columns: minmax(0, 1fr); }
  /* The colophon and the legal line stack. There is no room on a phone for two
     runs of type on one rule, and the baseline the row is aligned on at a desk
     is not a thing a single-column grid has — so the copyright comes back to
     the left margin under the colophon and reads as the next line of the same
     filing rather than as a run flushed to an edge nothing else is flushed to. */
  .foot__bottom { grid-template-columns: minmax(0, 1fr); }
  .foot__legal { text-align: left; }
  .num__grid { grid-template-columns: repeat(2, 1fr); }
  /* Twelve marks across a phone are twelve two-pixel hairlines nobody can
     press. The row stays anyway, as a READOUT rather than as a control — the
     thumb on the page is the transport at this width and the marks are how far
     through the reel that thumb has come. What goes is their ability to take a
     press they cannot receive accurately. */
  .rs__mark { pointer-events: none; height: 16px; }
  .rs__kind { display: none; }
  .rs__sep { display: none; }
  .mo__note { display: none; }
  /* One line of filing and a count on a phone: the ruler takes the width it
     needs and the two ends of the row keep theirs. */
  .rs__rail {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: clamp(8px, 1.4vh, 14px);
  }
  /* Full width on its own row, and the measure comes off: at this width the
     ruler IS the row rather than an instrument sitting in one. */
  .rs__ruler { grid-column: 1 / -1; grid-row: 2; width: auto; }
  .rs__marks { height: 16px; }
  .hb--motion {
    --mo-band: clamp(40px, 6vh, 66px);
    --mo-air: clamp(9px, 1.4vh, 16px);
    /* THE SCREEN COMES DOWN AND THE HEADLINE GOES UP, and the two moves are
       one move.

       The whole composition is a line of type crossing the edge of a picture,
       and on a phone it stops crossing: the frame is nearly the width of the
       device and the type is at its floor, so both lines land wholly inside
       the picture and what is left is a caption sitting on a video. The block
       loses its idea at the width most people will read it at.

       So the frame gives up a quarter of its width — it is still the largest
       thing on the screen by a distance — and the headline is set from the
       VIEWPORT rather than off the rem floor the rest of the page uses. Both
       lines clear both rules again. */
    --mo-h: clamp(150px, min(calc(var(--vh) * 0.30), 52vw), 300px);
  }
  /* The headline still crosses, and on a phone it has to do it on one line of
     twenty-three characters. 9.2vw is the largest size that line can take and
     stay inside the page's own two column rules — a hair over 330 of type in
     354 of column at 375 — which puts about forty pixels of it on the room at
     each end of a 260-wide screen. Any smaller and the line lands inside the
     picture; any larger and it crosses the one rule in this drawing that
     nothing may cross. */
  .mo__title { font-size: 9.2vw; }
  .xcta { --ctrl-px-lg: clamp(20px, 5vw, 30px); }
  /* The room's own column comes in to the page's edge padding at this width;
     the two side rules would otherwise stand a thumb's width inside a screen
     that is already the whole of it. */
  .hb--motion .mo__v--l { left: var(--pad-edge); }
  .hb--motion .mo__v--r { right: var(--pad-edge); }
  /* The run's foot was a press and a filing on one line, and on a phone the
     line was not long enough for both plus a year, so `.reel__year` and
     `.reel__sep` came out here. The foot is the press alone now and the filing
     is sr-only, so there is no line to run out of — and see the note under the
     860px branch for why nothing is dropped from it at any width. */
  /* Six marks and a fraction is one indicator too many at this width, and the
     marks are the one that reads without being read. */
  .reel__count { display: none; }
  .reel__tcol { justify-content: flex-end; }
  /* And the head band comes back down, because the crown is the only thing the
     headline has to clear here and the wide branch's floor is sized for one
     wrapping at 15ch in a column twice this one's width. The taller branch that
     goes with the column-solved headline is the query below this one — this is
     the floor a SHORT screen keeps, where a deeper band is half the viewport. */
  .hb--reel.is-first { --reel-top: clamp(122px, 16vh, 156px); }
}

/* --- the phone, WITH ROOM ABOVE THE FOLD TO SPEND ---------------------------
   Everything here is one decision — the headline is set from the column rather
   than off a rem floor — and it is keyed on HEIGHT as well as width for the
   reason `@media (max-height: 640px)` further up gives: a landscape phone is a
   620-wide screen that is 320 TALL, and a head band deep enough to stand a
   twenty-eight pixel headline in is more than half of it. Under 560 the branch
   above keeps its own floor and its own type, which is what that geometry can
   actually hold.

   560 and not 640: 320x568 is the shortest screen this site is built for and it
   is a PORTRAIT one — it has the room, it is simply narrow, and the division
   below hands it twenty-three pixels rather than eighteen. The landscape cases
   all sit under it. */
@media (max-width: 620px) and (min-height: 560px) {
  /* --- THE HEADLINE IS SET FROM THE COLUMN HERE, NOT FROM THE REM FLOOR -----
     At 375 the wide branch resolved to its own floor — 1.15rem, eighteen
     pixels — and eighteen pixels is a caption. The sentence that opens the page
     was the smallest type on the first screen, under a wordmark set larger than
     it, over a photograph the height of the viewport. The floor was never a
     decision about size; it was what a `clamp()` written for a two-column
     desktop lands on when the vw term collapses.

     SO THE SIZE IS SOLVED AND NOT CHOSEN, the same way every name on the
     ledger is: the two lines are AUTHORED breaks — `heading()` splits on the
     newline in `content.js` — so the longest of them is a constant, and the
     largest size at which a constant fits a column is one division:

         font-size = (column - both insets) / --reel-emw

     Twenty-eight pixels at 375 against eighteen, which is half again, and the
     ceiling is the measure rather than a taste: the divisor already holds the
     gutter the line needs off the right-hand rule, and past it a break written
     by hand starts soft-wrapping inside itself. `100vw` and not `--col`,
     which is `min(1360px, 100% - ...)` — a percentage inside a custom property
     resolves against the element that SPENDS it, and spent here that percentage
     is the column already, so the row's width would be subtracted twice.

     `2.2rem` caps it, for the top of this branch: at 620 the division wants
     fifty pixels, which is a headline sized for a column and not for the band
     it is standing in.

     --- AND THE TALLY GOES ABOVE IT, which is what buys the width ------------
     The head band was a row: headline on the leading edge, count and tally on
     the trailing one, both pushed to the foot of the band. Bottom-aligned, the
     ninety pixels the tally holds are taken off the LAST line of the headline —
     and the last line is the long one. Stacked, the marks keep the right-hand
     track they were already in, the headline gets the whole column, and the
     band grows by the one pixel the marks are tall. The count is already out
     at this width, so what moves is six hairlines. */
  .hb--reel.is-first .reel__row--t {
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-end;
    gap: clamp(10px, 1.4vh, 16px);
  }
  .hb--reel.is-first .reel__tcol { order: -1; }
  .hb--reel.is-first .reel__title {
    max-width: none;
    font-size: min(
      2.2rem,
      calc((100vw - var(--pad) * 2 - var(--reel-inset) * 2) / var(--reel-emw))
    );
  }
  /* AND THE BAND TAKES THE HEADLINE'S NEW HEIGHT, because that is what it is
     for. The depth of this band is the one thing holding the first line of the
     page clear of the crown, and the headline standing in it is now half again
     as tall. 122 was measured against eighteen-pixel type; at twenty-eight it
     leaves sixteen pixels between the wordmark and the top of the sentence on a
     667-tall screen, which reads as a collision. 140 puts thirty back.

     It is paid for out of the photograph, and that is the right pocket: the
     picture is four fifths of the screen either way, and the sentence is the
     only thing on it that has to be READ. */
  .hb--reel.is-first { --reel-top: clamp(140px, 17vh, 168px); }
}

/* --- reduced motion ---------------------------------------------------------
   The entrances collapse with everything else in the sheet's own stylesheet.
   What has to be turned off HERE is the two things that are not transitions:
   the arch, and the reach of the seam — which is driven by a velocity reduced
   motion has already pinned to zero, but the per-frame recalculation is still
   worth not paying for.

   IT WAS THREE. The cue's `home-cue` keyframes were the loudest of them — the
   one loop on the landing that ran forever — and they are gone with the cue. */
@media (prefers-reduced-motion: reduce) {
  .hx__w { transform: none; }
  .foot__mark { transform: none; }
  .hb::before { transform: none; }
  /* The room never plays a clip under this setting — see `showreel.js` — so
     what is left is twelve posters, one per station, with the filing under
     them and the transport working. The canvas over them draws nothing,
     because nothing decoded; this is the second lock, for a reader who turns
     the setting on after the page has loaded. */
  .rs__frame { clip-path: none; }
  .rs__screen { transform: none; }
  .rs__filing.is-struck { animation: none; }
  /* THE BAR STEPS RATHER THAN TRAVELS, and it has to be said HERE because the
     sheet's global cannot say it. `styles.css` flattens every transition on
     the page with `* { transition-duration: 0.01ms }` — and `*` matches
     elements, which a pseudo-element is not. The arrow's two pieces are real
     `<i>`s and are already covered; the bar is `.rs__marks::after` and would
     have gone on easing across the ruler under a setting that asked it not to.

     Nothing else about the transport changes: twelve graduations, a bar on the
     piece that is up, two arrows that answer. It stops sliding, that is all. */
  .rs__marks::after { transition: none; }
  .mo__room { transform: none; }
}

/* --- the page leans, and every list in it bends ----------------------------
   One deformation, applied everywhere it was not.

   The arch was on the headlines and on nothing else: `--flow` times a squared
   distance from the middle of a line, so the ends of a title lag its centre
   while the page is running. It is the same arithmetic the reel's shader puts
   on its sampling coordinate and the same one the seam puts on its reach, and
   for eight blocks it was the only thing on the page doing it — a headline
   that bent over a column of type that did not, which reads as a headline with
   an effect on it rather than as a page made of one material.

   Two shapes here and they are the whole of it.

   THE LEAN is the column. Type that is being scrolled past is type on a
   surface that is moving, and a surface that is moving is not square to the
   reader. A few tenths of a degree of shear plus ten pixels of lag, both off
   the signed velocity, and both gone the instant the page stops — this is a
   claim about travel, not a style.

   THE BOW is the lists. A list is a line of type stood on end: `--u` is the
   row's place down it, squared about the middle to give `--bow`, negative in
   the centre and positive at both ends. The rows at the two ends of a list lag
   the rows in the middle, so a stack of rows riffles like paper instead of
   sliding like a table. The questions already do this; these are the other two
   lists on the page, and they now do it with the same two lines of arithmetic.

   Cost is one composited transform per element on a page whose track is
   already one transform, and none of them reads layout. */

.hb__col,
.cta__inner,
.foot {
  transform:
    translate3d(0, calc(var(--flow) * -9px), 0)
    skewY(calc(var(--flow) * 0.3deg));
  transform-origin: 50% 50%;
}

/* Not the pinned blocks. Their child is already carrying a transform written
   per frame by `section.js`, and a second one on the same box is two things
   arguing about where the panel is. */
.hb__pin .hb__col {
  transform: none;
}

/* The label at the head of a block travels furthest, because it is the
   smallest thing in the column and the one furthest from the reading position
   — the same reason the number on a question leads its row. */
.hb__kicker {
  transform: translate3d(calc(var(--flow) * -14px), var(--ry), 0);
}

/* --- the disciplines --------------------------------------------------------
   The bow lives beside the block — see `.led__item`. It is the same two lines
   the questions bend by, on the same axis they bend on: the ledger is a list
   read DOWN, so `--u` is a row's place in it and the lag it buys is vertical.
   It has to be vertical, and the reason is written up there: the shared left
   margin is the zero of the measure, so a line that travelled sideways under
   the scroll would be a line that had walked off its own reading. */

/* --- and none of it on a reader who asked for none -------------------------
   `--flow` is zero at rest, so every rule above already resolves to nothing on
   a still page. This is for the reader who has asked for no motion at all: the
   page still moves under them — it is a scroll — but it does not bend while it
   does. */
@media (prefers-reduced-motion: reduce) {
  .hb__col,
  .cta__inner,
  .foot,
  .hb__kicker,
  .led__item,
  /* The wordmark's own letters are inside a shadow tree and cannot be named
     from here — the component turns its own lean, bow and wave off under the
     same query. This is the HOST, which is what carries the parallax. */
  .foot__mark,
  .faq__card,
  .faq__tick {
    transform: none;
  }
  /* --- and the setting arrives set ----------------------------------------
     The block's entrance is not a transform and so is not caught by anything
     above it: eleven rules running out from the left edge, eleven terminators
     rising, eleven names lifting into their windows and two monuments rolling
     up behind their masks, each on its own delay. It is the most motion on
     this page that is made of no movement at all, which is exactly the kind a
     reader who asked for none did not ask for.

     So the sheet is simply THERE. `--led-in` is pinned to 1, which is the
     whole of it: every rule at its measured length, every terminator standing,
     every caption printed. Three declarations, because the entrance was built
     to run off one number.

     WHAT SURVIVES IS THE OPENING, and it survives on purpose. It is a clip
     unrolling on one photograph under a hand that is deliberately on it, it
     happens once, and it happens because somebody asked for it — it is also
     this block's only way of showing a picture at all.

     AND `.led__item` STAYS IN THE `transform: none` LIST ABOVE. Its transform
     is the bow and nothing else; the opening rides `--led-open`, which is a
     number, not a transform. The day somebody moves the opening onto a
     transform, this block goes silent here without anything failing. */
  .led__row { --led-in: 1; }
  .led__w > i { transform: none; }
  .led__head::after { transform: none; }
  .led__hz > b { transform: none; }
  /* The head's gauge and the foot's tally are drawn rather than moved, so the
     still sheet has to put BOTH of their instruments back: the two that scale
     and the one that is walked across by a clip. A ruler left at
     `inset(0 100% 0 0)` is a ruler with no ticks on it. */
  .led__gauge::before,
  .led__gauge::after { transform: none; }
}


/* --- THE MELT ---------------------------------------------------------------
   Four headlines on this page do not slide in from under a mask. They arrive as
   ONE BLOB and pull apart into words.

   The trick is the gooey threshold the whole site is built on — the gate melts
   the mark into the wordmark with it, the sentence over the run melts one half
   into the other with it, and the filter itself is mounted once in `melt.js`.
   A feColorMatrix multiplies alpha by twenty-five and subtracts nine, so every
   in-between alpha snaps hard to 0 or to 1. Blur a word under that and the blur
   comes back as a crisp outline: it does not read as soft, it reads as THICK.
   Blur two words that are close enough to overlap and the threshold welds them
   — one shape that stretches, necks, and separates as the blur comes off.

   There is no clock in here. No rAF, no observer of its own, no JavaScript at
   all beyond the two numbers `#arch()` was already in position to write. The
   entrance is two CSS animations hung off `is-in`, which is the class every
   other entrance on this landing already hangs off.

   --- WHY IT IS TWO ANIMATIONS AND NOT ONE ----------------------------------
   Because the threshold is a liability the moment the words stop moving.

   An alpha threshold has no opinion about antialiasing. Every soft edge on a
   glyph is rounded to fully on or fully off, so type standing still under it
   comes out chunky and about a half-weight heavy — which is invisible while the
   words are blurred and welded, and obvious the second they are legible. The
   filter is only wanted for the stretch where two words actually overlap.

   So the WORDS carry the melt and the LINE carries the threshold, and the line
   drops it, on `step-end`, at the frame its own last word goes crisp. The still
   frame is drawn by the normal text rasteriser, which is the one thing that has
   ever drawn type well.

   --- WHY THE THRESHOLD IS ON THE LINE AND NOT ON THE HEADING ---------------
   Because the threshold welds whatever is inside the element carrying it, in
   every direction. Run over a whole two-line title, a blur of nearly half the
   size of the type closes a 0.94 line gap without trying, and the second line
   arrives fused to the underside of the first: one smear rather than two lines
   of words melting. Per line, the weld is horizontal — which is the direction
   the words are being read in.

   That is what `--w0` and `--wn` are for, and why they are measured in
   `#arch()` rather than counted in `blocks.js`: the browser breaks the line,
   not the builder. `text-wrap: balance` over a `vw` size means a heading is
   four words on one line at one width and two and two at another. See the note
   in `home/section.js`.

   --- THE FOUR --------------------------------------------------------------
   The reel's title over the screen, the question at the head of the deck, the
   question ON the card, and the last thing the page says. Everything else on
   the landing keeps the slide. A page where every headline melts is a page
   where the melt is the house style rather than the four moments it marks. */

.hx--morph {
  /* THE PEAK BLUR, AND IT IS MEASURED RATHER THAN CHOSEN.

     A ratio of the type and not a pixel count, because the four headlines this
     runs on are set from 1.5rem to 6.2rem and one number in pixels cannot be
     right for both ends of that.

     The value comes off the threshold. At 25a - 9 an alpha has to reach about
     0.36 to survive the cut, and a Gaussian on a letterform spends alpha far
     faster than it does on a solid mark — which is why the gate can blur its
     logo by 26px and a headline cannot. Rendered at 99px in Satoshi 700 and
     counted, the ink a word puts down under the threshold goes:

         blur  0px   1.00 × crisp        (the still frame)
         blur  6px   1.25 ×              (the swell — the letters weld)
         blur  8px   1.26 ×              (the most blob it is ever going to be)
         blur 14px   1.06 ×
         blur 18px   0.70 ×              (a mass, barely a word)
         blur 24px   0.04 ×              (all but gone)
         blur 28px   0.00 ×              (GONE — nothing survives the cut)

     So the entire effect lives between nought and about a fifth of the type,
     and everything past that is not a stronger melt, it is an erasure. 0.19em
     starts the word as a welded mass at a bit over half its ink, carries it
     through the swell at 8px where two neighbours are one shape, and lands it
     crisp. Forty-six pixels — which is what this was set at in the playground,
     where the threshold was gated OFF for the deep frames — puts every word on
     the last line of that table. */
  --melt-blur: 0.19em;
  /* One word every 90ms. It is the closing headline's old swing stagger, kept
     across the change of gesture because 88ms was never about the swing — it is
     how long a reader takes over one word of a sentence being spoken. */
  --melt-stag: 90ms;
  /* How long one word takes to come out of the blur. Longer than the page's
     1.05s slide, because a shape resolving reads slower than a shape arriving:
     the last third of this is almost entirely the final two pixels of blur
     coming off, and that is the part that looks like setting rather than
     fading. */
  --melt-word: 1.15s;
}

/* THE MASK GOES, and this is the load-bearing line of the whole block.

   `.hx__w` is clipped to its own box so a word can be lifted into view from
   under an edge — the page's own entrance. The melt needs the opposite: what it
   has to show is a blur half the size of the type spreading well past the
   word's box until it reaches the word beside it. Clipped to the line, the
   effect is cut off exactly where it is doing its work.

   The descender padding stays. It is cancelled by its own negative margin and
   costs nothing, and the day one of these four goes back to a mask it has to be
   there or the tail of a `y` is eaten. */
.hx--morph .hx__w {
  overflow: visible;
}

/* AND THE SLIDE GOES WITH IT. There is nothing left for a transition to do —
   every property the entrance touches is driven by keyframes below, and a
   transition still declared here would fight the animation for `transform` on
   the frame the animation is removed. */
.hx--morph .hx__w > i {
  transform: none;
  transition: none;
  opacity: 0;
}

/* A QUESTION ON A CARD THAT HAS BEEN TURNED PAST IS SIMPLY THERE.

   The eight cards are stacked in one grid cell and faded by their distance from
   `--fk`, so seven of them are at opacity 0 with their type fully laid out.
   Without this the resting state above would hold, and a card LEAVING would
   drop its question to nothing in one frame while the card itself was still a
   tenth visible — a question blinking out from under a photograph that is still
   fading. Settled is the default; the melt is what the live card gets on top of
   it. */
.hb.is-in .faq__card .faq__q.hx--morph .hx__w > i {
  opacity: 1;
}

/* --- the melt itself, and the two things that trigger it -------------------
   Everything on the landing melts when its BLOCK arrives. The eight questions
   melt when their CARD does — one at a time, as the deck is turned — which is
   the only place on this page where the same entrance has two clocks. Hence the
   `:not(.faq__q)` on the first selector: without it all eight would melt at
   once, seven of them behind a card at opacity 0, and the reader would turn the
   deck to find every question already set. */
.hb.is-in .hx--morph:not(.faq__q) .hx__w > i,
.hb.is-in .faq__card.is-live .faq__q.hx--morph .hx__w > i {
  animation: hx-melt var(--melt-word) var(--ease-out) both;
  animation-delay: calc(var(--wi, 0) * var(--melt-stag));
}

/* The threshold, lifted the frame the line's last word goes crisp.

   `--w0` is where this line's first word sits in the heading's own cascade and
   `--wn` is how many words are on it, so the gate opens with the line's first
   word and closes on its last — which on a two-line title means the second line
   is still welded while the first is already being read cleanly. */
.hb.is-in .hx--morph:not(.faq__q) .hx__line,
.hb.is-in .faq__card.is-live .faq__q.hx--morph .hx__line {
  animation: hx-melt-gate
    calc((var(--wn, 1) - 1) * var(--melt-stag) + var(--melt-word))
    step-end both;
  animation-delay: calc(var(--w0, 0) * var(--melt-stag));
}

/* OPACITY IS SPENT IN THE FIRST SIXTH, and that is not a taste decision.

   Under a 25a - 9 threshold there is no such thing as a faint word: a shape is
   either over the cut or it is gone. Written as a linear fade the word would
   spend two thirds of its entrance under the floor and then appear, which is a
   pop with a blur on it. Lifted to full ink at 16% the opacity stops being part
   of the effect at all and the BLUR alone decides what the word is shaped like
   — which is what the gooey trick actually is.

   The scale is the other half of the pull-apart: at 0.88 the words start closer
   to each other than they end, so the blob does not merely sharpen, it
   stretches. */
@keyframes hx-melt {
  0% {
    opacity: 0;
    filter: blur(var(--melt-blur));
    transform: scale(0.88);
  }
  /* THE PLATEAU, and it is the frame the whole thing is for.

     The blur is HELD at its peak while the opacity comes up, so what the reader
     sees first is a finished blob rather than a word that is already resolving.
     Without it the house easing — which is 80% travelled at a third of the time
     — had every word most of the way to crisp before it was visible at all, and
     six words each arrived as their own tidy shape. Two neighbours are only ever
     one shape while BOTH are near the peak, and 15% of 1.15s is 172ms against a
     90ms stagger: the word behind is still at two thirds of its blur when the
     next one lands at full, the two spreads overlap across the word space, and
     the threshold welds them. That is the bridge. */
  15% {
    opacity: 1;
    filter: blur(var(--melt-blur));
    transform: scale(0.9);
  }
  100% {
    opacity: 1;
    filter: blur(0);
    transform: scale(1);
  }
}

/* Two keyframes and `step-end`, so the reference is HELD to the end and then
   dropped rather than interpolated. `url()` and `none` do not interpolate: a
   discrete change left to the browser lands at the midpoint, which would take
   the threshold away while the words are still lying on top of each other —
   the one frame the whole effect exists for. */
@keyframes hx-melt-gate {
  0% { filter: url(#meltThreshold); }
  100% { filter: none; }
}

/* A melt is a shape moving. Under a reduced-motion request the four headlines
   are simply set — full ink, no blur, no threshold, no travel. Stilled here and
   not in the sheet's own reduced-motion block because these are animations
   rather than transitions, and that block turns off transitions. */
@media (prefers-reduced-motion: reduce) {
  .hx--morph .hx__w > i,
  .hb.is-in .hx--morph .hx__w > i,
  .hb.is-in .faq__card.is-live .faq__q.hx--morph .hx__w > i {
    animation: none;
    opacity: 1;
    filter: none;
    transform: none;
  }
  .hb.is-in .hx--morph .hx__line,
  .hb.is-in .faq__card.is-live .faq__q.hx--morph .hx__line {
    animation: none;
    filter: none;
  }
}
