/* about.css: the About page, and this page alone.
 *
 * It sits on top of site.css, which is the shared shell: the silk, the ramp,
 * the chrome, the footer. Nothing here restates a token; every colour below is
 * one of site.css's own names, so the no-hue rule and the measured contrast
 * floor recorded at the head of that file bind this page without being copied
 * into it. The only colour on the page is the two photographs, which is the
 * same trade the landing film makes with the jewelry renders.
 *
 * THE ANIMATION SYSTEM IS THE LANDING PAGE'S, MINUS THE FILM. about.js writes
 * exactly two numbers onto a tracked band per frame and every transform, fade,
 * lag and light below is derived from them here in calc():
 *
 *     --k   0 to 1, the band's way through its own reveal window
 *     --t   0 to 1, the band's whole traversal of the viewport, its top edge
 *           entering at the bottom to its bottom edge leaving at the top,
 *           which is what the photographs' parallax rides
 *
 * plus one number on the document element, --v, the smoothed scroll velocity
 * clipped to -1..1, which is the part that answers the reader's own hand: the
 * bench lamp swings on it, the plates lag on it, and both settle to nothing
 * the moment the thumb stops.
 *
 * Deriving in CSS rather than writing transforms from script is not tidiness.
 * It is what makes the whole page scrub identically in both directions with no
 * state to get out of step, it survives a reader landing mid-page on a
 * restored scroll position, and it keeps the per-frame cost on the compositor.
 *
 * EVERYTHING IS GATED ON html.js. With scripting off, or if about.js fails to
 * load (its onerror drops the class, the same guard index.html carries), no
 * element ever starts hidden and the page is ordinary prose with two
 * photographs in it, complete and in order. That is also what the printer
 * gets; see the print block at the foot of this file, whose selectors have to
 * match the html.js specificity or the page prints blank, which is the exact
 * mistake home.css records paying for.
 */

/* ------------------------------------------------------------- the column */

.ab {
  width: 100%;
  /* The page's own bottom space; the top comes from .page-top above it. */
  padding-bottom: clamp(2rem, 6vw, 4rem);
}

/* ONE BAND WIDTH FOR THE PAGE, AND IT IS NOT THE READING MEASURE. It was
   48rem, which put every band of this page inside a 768px column on a 1440px
   window with the copy hugging its left edge: a ribbon of text down the middle
   of a very large amount of nothing, which is most of what was wrong with the
   first version of this page.

   60rem is the width of the PAGE. The reading measure is set where reading
   happens instead, on .ab__lead (34rem) and .ab__p (36rem), which were already
   capped and are untouched: no line of body copy on this page got a character
   longer. What the extra 12rem is spent on is the four things that are not
   prose and were being squeezed by a limit that was never about them: the two
   photographs, the craft list's second column, the gauge, and the headings,
   which now have room to sit at the size they are set at. */
.ab__wrap {
  width: 100%;
  max-width: 60rem;
  margin: 0 auto;
  padding: 0 max(clamp(1.15rem, 5vw, 2rem), env(safe-area-inset-left))
    0 max(clamp(1.15rem, 5vw, 2rem), env(safe-area-inset-right));
}

/* THE GAP BETWEEN BANDS IS ONE DECLARATION, not a margin argued per section.
   Every band on this page is a <section> under .ab, so the rhythm is set here
   and the hairline below carries space under itself only; a rule with margin
   at both ends inside a section that already has a top margin doubles the gap
   at every divider and halves it everywhere else, which is what the first
   capture showed.

   THE FIGURE CAME DOWN A THIRD in this pass. At 9vw the gap between two bands
   was 130px on a 1440px window on top of the 2rem to 3rem of space each rule
   already carries under itself, so the page scrolled as a series of islands
   with a screen of silk between them. 5.5vw puts the join at about 79px there
   and leaves the two ends of the range where they were tuned: 2.25rem on a
   320px phone, where the space is doing real work between two full-width
   bands. */
.ab > section {
  margin-top: clamp(2.25rem, 5.5vw, 3.75rem);
}

/* The plates sit ON the hero rather than after it: they are the picture the
   headline just introduced, and a full band gap between the two would put them
   back where they started, a screen further down. Written as a child selector
   rather than a bare class because the rule it is answering is one, and a
   (0,1,0) class cannot overrule a (0,1,1) child selector that comes before it
   however far down the file it sits. */
.ab > .ab__plates {
  margin-top: clamp(1.5rem, 3.5vw, 2.5rem);
}

/* --------------------------------------------------------------- the lamp */

/* There is a lamp over every jeweller's bench, and there is one in the lid of
   the box the landing film opens. This is that lamp with the reader's hand on
   it: one layer that swings with the scroll and settles the moment the scroll
   does, so the page is lit by something being MOVED rather than by a gradient
   somebody painted in the middle of the screen.

   IT IS SHADE, NOT LIGHT, AND THAT IS ARITHMETIC RATHER THAN TASTE. The first
   cut was the finale's own pool, a neutral wash of --graphite at 6%, and it
   fails the contrast floor site.css measures. That floor is quoted at the
   silk's BRIGHTEST 24px patch, which composites to 83 of 255 at --velvet-a
   0.5, where --page-muted measures 4.65:1. Body text needs 4.5:1, so the
   largest ground the muted step can stand on is 85, which leaves two levels of
   headroom, and a 6% wash of #ECECEC spends nine: it lands the same copy at
   4.04:1. Solving for the alpha that clears 4.5 gives 1.3%, which is a light
   nobody can see.

   A real lamp is a pool with darkness around it, so the layer paints the
   DARKNESS: alpha 0 at the centre rising to 0.46 of --paper at the rim. Every
   pixel it touches gets DARKER, so no ground on this page is ever lighter than
   site.css measured it and every ratio can only improve. It is also the truer
   picture, because what a bench lamp actually does to a room is leave the rest
   of it dark.

   MEASURED off the shipped page at 1440x900, brightest 24px ground patch in a
   text-free strip, with the layer on and with it display:none, and quoted with
   --page-muted on it:

       top edge      84 -> 70   4.57:1 -> 5.70:1
       pool centre   63 -> 57   6.36:1 -> 6.97:1
       bottom edge   35 -> 28   9.49:1 -> 10.29:1

   The unlit top edge at 84 is the site-wide worst patch site.css records at 83
   and 4.65:1, which is the check that the strip was sampling the real thing.
   Every band gains. Re-measure the same way if the stops below are touched.

   THE LAYER IS TALLER THAN THE VIEWPORT AND MOVES BY TRANSFORM, which is the
   whole of its cost. Writing the gradient's own position per frame would
   repaint a full-screen layer sixty times a second on a phone; a static
   gradient on an over-tall promoted layer, translated, is a composite and
   nothing else. 180vh with 40vh of overhang at each end is what keeps the
   gradient's own outer edge off the frame at both ends of the swing.

   Neutral, and it has to be: the no-hue rule binds the interface, and this is
   interface. It is written in literal channels rather than a token for the
   same reason .ab__cap's veil is: a token cannot be given an alpha inside
   rgba(). */
.ab__lamp {
  position: fixed;
  top: -40vh;
  right: 0;
  left: 0;
  height: 180vh;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    82% 44% at 50% 50%,
    rgba(5, 5, 6, 0) 0%,
    rgba(5, 5, 6, 0.12) 52%,
    rgba(5, 5, 6, 0.46) 100%
  );
}

/* THE GROUND LADDER, RESTATED WHOLE FOR THIS PAGE so there is no tie to break.
   site.css puts the flat paper and the silk both at -2 and orders them by
   position in the document, which works while there are two layers and stops
   working the moment a third wants to sit between them. This page has three:
   the paper, the silk on it, and the lamp's shade over both. Each gets its own
   step, and all three stay behind everything that carries text. The silk's own
   painting is untouched, because velvet.js measures its host element and the
   host has not moved. Scoped by the sheet, which only this page loads. */
body::before {
  z-index: -3;
}

.velvet {
  z-index: -2;
}

/* --------------------------------------------------------------- the type */

.ab__title {
  margin: 0;
  font-family: var(--font-display);
  /* The same envelope the finale's closing line takes, so arriving here from
     that button is the same voice at the same size. */
  font-size: clamp(2.3rem, 7vw, 4.3rem);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: 0.01em;
  color: var(--page-fg);
}

/* A masked line: the box clips, the line inside it travels. This is the
   landing film's .bl in miniature, restated here under its own name because
   home.css is the landing page's sheet alone and this page does not load it.
   The padding and margin pair is what stops the mask shaving a descender while
   the line is still settling. */
.ab-l {
  display: block;
  overflow: hidden;
  padding-bottom: 0.08em;
  margin-bottom: -0.08em;
}

.ab-l__in {
  display: block;
}

.ab__lead {
  margin: clamp(1.25rem, 4vw, 1.75rem) 0 0;
  max-width: 34rem;
  font-family: var(--font-display);
  font-size: clamp(1.08rem, 3.2vw, 1.32rem);
  font-style: italic;
  line-height: 1.6;
  color: var(--page-muted);
}

.ab__h2 {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 5.2vw, 2.6rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: 0.01em;
  color: var(--page-fg);
  text-wrap: balance;
}

.ab__p {
  margin: clamp(1rem, 3vw, 1.35rem) 0 0;
  max-width: 36rem;
  font-size: clamp(0.95rem, 2.6vw, 1.02rem);
  line-height: 1.75;
  color: var(--page-muted);
}

.ab__p strong {
  color: var(--page-fg);
  font-weight: 500;
}

/* THE TWO PARAGRAPHS SHARE A ROW ON A WIDE SCREEN. Stacked under a heading
   that runs the width of the band, a 36rem measure leaves the right half of a
   1440px window empty and makes the band twice as tall as its content: the
   reader scrolls past a column of text with a screen of silk beside it. Set
   side by side, each column is about 448px, which is 60 characters and inside
   the measure the same copy was already being read at.

   Two columns of body text is a bad idea for continuous prose, because the eye
   has to travel back up the page to start the second one. It is not
   continuous: these are two four-line paragraphs, so what the reader gets is
   two short blocks side by side rather than a page of newsprint.

   NO WRAPPER AND NO CHANGE OF STRUCTURE, which is what keeps the entrance
   arithmetic honest: the stagger counts DIRECT CHILDREN with nth-child, and a
   div around the paragraphs would recount every one of them. Auto-placement
   does the whole job from four flat children: the hairline takes row 1 across
   both columns, the heading row 2 across both, and the two paragraphs fall
   into row 3 side by side. Both keep their own top margin off the heading, so
   their first lines sit on one line. */
@media (min-width: 60em) {
  .ab__prose {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: clamp(2rem, 4vw, 3.25rem);
  }

  .ab__prose > .ab__rule,
  .ab__prose > .ab__h2 {
    grid-column: 1 / -1;
  }

  .ab__prose > .ab__p {
    max-width: none;
  }
}

/* A hairline that draws itself across the measure as its band arrives. It is
   scaled from the left rather than animated on width, so it costs a composite
   and never a layout. */
.ab__rule {
  height: 1px;
  /* 6vw put 86px under the hairline on a 1440px window, which lands on top of
     the band gap above it and reads as one enormous join rather than as two
     pieces of rhythm. 3.5vw is 50px there and leaves the phone end alone. */
  margin: 0 0 clamp(1.5rem, 3.5vw, 2.5rem);
  border: 0;
  background: var(--footer-rule);
  transform-origin: left center;
}

/* --------------------------------------------------------------- the hero */

/* THE HERO IS SIZED BY WHAT IS IN IT. It carried min-height: min(72svh, 40rem)
   with the copy centred in it, which on a 900px window reserved 648px for a
   headline and one paragraph that together set at about 260: the rest was a
   screen of silk between the title and the first photograph, and it is the
   single largest piece of the empty space this pass was asked to close. The
   padding is what sets the room now, and it is generous at the top and modest
   at the foot, because the thing under the hero is a pair of plates that
   belong to it. */
.ab__hero {
  padding-top: clamp(1.75rem, 6svh, 3.5rem);
  padding-bottom: clamp(0.5rem, 2svh, 1.25rem);
}

/* THE HEADLINE AND THE LINE UNDER IT STAY STACKED, and that was checked rather
   than assumed. Setting them side by side as a masthead is the obvious way to
   spend a wide frame and it was built and measured: "Twenty-three years" sets
   at about 600px at the title's own 4.3rem ceiling, and half of this page's
   60rem band after the gap is 480, so the first masked line WRAPPED, which
   breaks the mask (the line is meant to arrive as one travelling block) and
   left "years" alone on a line of its own. The two ways out are both worse
   than the problem: a 1.4fr split leaves the lead 240px and six lines deep,
   and cutting the title to fit makes the page's one loud voice quieter to buy
   a layout. Empty frame beside a headline is not the wasted space this pass
   was asked about; a screen of it under one was. */

/* -------------------------------------------------------------- the media */

/* A photograph on this page is a framed thing, not a bleed: a hairline, a 4px
   corner and a sunk ground, which is the card idiom site.css already uses for
   every panel on the site. The frame clips, and the image inside it is taller
   than the frame is, which is the room the parallax spends.

   The <figure> IS the frame rather than holding one, because a <figcaption>
   has to be the first or last child of its own figure: a clipping div in
   between would take the caption out of the figure it captions. */
.ab__frame {
  margin: 0;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--footer-rule);
  border-radius: 4px;
  background: var(--paper-raised);
  /* aspect-ratio rather than a height, so the box is known before the file
     lands and nothing on the page moves when it does.

     ONE ASPECT FOR BOTH PLATES, and that is what makes them a pair rather than
     two photographs that happen to be adjacent. They used to be cut 614/780
     and 900/1040, which is a 4% difference in shape at two different widths on
     two different bands: side by side those two tops and two feet would not
     line up and the eye would read the mismatch before it read either picture.
     4/5 is the crop both sources give up least to. The frame holds the image
     at 116% of its own height (see below), so the box the file is fitted into
     is 4 : 5.8, which is 0.690 against the portrait's own 0.671 and the
     bench's 0.750: the portrait loses 18px of 685 off its height and the bench
     40px of 500 off its width, at the sizes these are drawn at. Neither cut
     reaches anything in either picture. */
  aspect-ratio: 4 / 5;
}

/* <picture> is an inline box laying out around an absolutely positioned child,
   which is to say around nothing; blocking it keeps the frame's own line box
   from reserving a strut under the image. */
.ab__frame picture {
  display: block;
}

.ab__frame img {
  display: block;
  position: absolute;
  /* 116% of the frame, seated by its own centre: the 16% is what the parallax
     translate has to travel through, so the frame can never show its own
     ground at either end of the move. */
  left: 0;
  top: 50%;
  width: 100%;
  height: 116%;
  object-fit: cover;
  transform: translate3d(0, -50%, 0);
}

/* The caption is seated on the frame's own foot rather than hung under it, so
   the photograph reads as a plate with a line on it. The wash is what makes
   the line legible over whatever the photograph happens to be doing down
   there: at 0.86 of #0a0a0b the brightest pixel in either picture composites
   under 45, where --page-muted clears 8:1. */
.ab__cap {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  padding: clamp(2.5rem, 8vw, 4rem) clamp(0.9rem, 3vw, 1.35rem)
    clamp(0.75rem, 2.5vw, 1.1rem);
  font-family: var(--font-mono);
  font-size: clamp(0.56rem, 1.9vw, 0.64rem);
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  text-transform: uppercase;
  color: var(--page-muted);
  background: linear-gradient(
    rgba(5, 5, 6, 0) 0%,
    rgba(5, 5, 6, 0.55) 46%,
    rgba(10, 10, 11, 0.86) 100%
  );
}

/* -------------------------------------------------------------- the pair */

/* THE TWO PHOTOGRAPHS ARE ONE OBJECT. The page used to stand the portrait
   alone and centred on its own band, capped at 27rem, and then hang the bench
   half-width beside the craft list a whole prose section further down: two
   pictures of the same man, at two sizes, doing two different jobs, with 850px
   of silk and four paragraphs between them. Together they are a diptych, and
   the caption rail under each is the only label either needs.

   THE PAIR SPLITS AT 34em AND NOT SOONER. Side by side on a 320px screen each
   plate is about 137px wide, which is a contact sheet rather than a portrait
   of somebody; stacked, the gap is a quarter of what it is between two bands,
   so they still read as one block being scrolled past rather than as two
   pictures that turned up separately. From 34em each plate is 250px and up,
   which is the size the old single portrait was drawn at on a phone. */
.ab__plates {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(0.6rem, 2vw, 0.9rem);
  align-items: start;
}

@media (min-width: 34em) {
  .ab__plates {
    grid-template-columns: 1fr 1fr;
  }
}

/* The second plate is set down a little from the first, which is how two
   prints actually lie when somebody has put them on a bench, and which carries
   the eye across the pair instead of stopping it on a seam. Only where there
   is room for it: below 60em the two plates are already narrow and a drop just
   costs height. It is a margin rather than a transform because the frames'
   images are the one thing on this page already carrying a transform, and a
   second one on the frame would give its own clip something to do. */
@media (min-width: 60em) {
  .ab__plates > :nth-child(2) {
    margin-top: clamp(1.5rem, 4vw, 3rem);
  }
}

/* --------------------------------------------------------------- the four */

/* TWO COLUMNS FROM 48em, which is what the bench photograph used to occupy.
   The four entries hung in single file beside a sticky plate; with the plate
   up in the pair, a single file down a 60rem band is four short paragraphs
   with 500px of nothing to the right of each, and the band is twice as tall as
   it needs to be. In two columns the list is a ruled table of what the bench
   does, which is the shape the information actually has: four peers, not a
   ranking. */
.ab__craft {
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  column-gap: clamp(1.5rem, 4vw, 2.75rem);
  margin: clamp(1.5rem, 4vw, 2.25rem) 0 0;
  padding: 0;
  list-style: none;
  counter-reset: craft;
}

@media (min-width: 48em) {
  .ab__craft {
    grid-template-columns: 1fr 1fr;
  }
}

/* The divider is a pseudo-element rather than a border, because a border
   cannot be drawn: this one scales from the left as its own entry arrives, on
   the same easing as the gauge's graduations and the rules between the bands,
   so every hairline on the page behaves like the same hairline. */
.ab__craft li {
  counter-increment: craft;
  position: relative;
  padding: clamp(1.1rem, 3vw, 1.5rem) 0;
}

.ab__craft li::before,
.ab__craft li::after {
  content: "";
  position: absolute;
  right: 0;
  left: 0;
  height: 1px;
  background: var(--footer-rule);
  transform-origin: left center;
}

.ab__craft li::before {
  top: 0;
}

/* THE CLOSING RULE BELONGS TO WHICHEVER ENTRIES ARE ON THE LAST ROW, and in
   two columns that is two of them, not one. Every entry carries a bottom rule
   that is switched off unless it is on that row, which is one declaration
   block and two selectors rather than the same six properties written twice.
   Drawn per entry rather than once across the list, so it breaks at the column
   gap exactly as the top rules do: a continuous line under two broken ones
   reads as a mistake. And it can never double with the rule below it, because
   an entry that has a neighbour under it is not on the last row. */
.ab__craft li::after {
  bottom: 0;
  display: none;
}

.ab__craft li:last-child::after {
  display: block;
}

@media (min-width: 48em) {
  .ab__craft li:nth-last-child(-n + 2)::after {
    display: block;
  }
}

/* The number is the mono face at the smallest size on the page, hung on the
   same line as the title rather than in a gutter: a gutter costs 3rem of a
   320px screen and buys nothing a colour could not. */
.ab__craft h3 {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 3.6vw, 1.45rem);
  font-weight: 400;
  line-height: 1.25;
  color: var(--page-fg);
}

.ab__craft h3::before {
  content: counter(craft, decimal-leading-zero);
  flex: none;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  color: var(--pewter-deep);
}

.ab__craft p {
  margin: 0.6rem 0 0;
  /* Clears the number and its gap so the copy hangs under the title, not under
     the digits. */
  padding-left: calc(0.62rem * 2 + 0.75rem);
  font-size: clamp(0.9rem, 2.5vw, 0.97rem);
  line-height: 1.7;
  color: var(--page-muted);
}

/* -------------------------------------------------------------- the gauge */

/* Twenty-three graduations, every fifth one longer and the last one longest,
   which is how a ring gauge, a caliper and a mandrel are all marked. It is a
   figure the page has already said in words, taken rather than stated.

   flex with a gap and 1fr columns, so the scale is exactly as wide as the
   measure at every viewport and the spacing between marks is arithmetic
   instead of a guess. The ink stays 1px however wide the column gets, because
   a graduation that thickens with the screen is a stripe: each item IS the
   column and the mark is a hairline gradient drawn down the middle of it,
   which is also what lets the tick scale from its foot without the layout
   noticing. */
/* CAPPED, BECAUSE A SCALE IS A DENSITY. The graduations divide whatever width
   they are given, so on the widened band the twenty-three of them came out
   39px apart on a 1440px window, which is not a rule with marks on it, it is a
   row of separate strokes. 30rem holds them at about 21px, which is inside the
   30px the old 48rem band gave them and close to the spacing on the thing this
   is a picture of. */
.ab__gauge {
  margin: 0;
  max-width: 30rem;
}

.ab__scale {
  display: flex;
  align-items: flex-end;
  gap: clamp(0.35rem, 1.6vw, 0.75rem);
  height: clamp(3rem, 9vw, 4.5rem);
}

.ab__scale span {
  flex: 1 1 0;
  height: 34%;
  background-image: linear-gradient(
    to right,
    transparent calc(50% - 0.5px),
    var(--pewter-deep) calc(50% - 0.5px),
    var(--pewter-deep) calc(50% + 0.5px),
    transparent calc(50% + 0.5px)
  );
  transform-origin: bottom center;
}

/* 01, 06, 11, 16 and 21 are the fives of a scale that starts at one. */
.ab__scale span:nth-child(5n + 1) {
  height: 62%;
  background-image: linear-gradient(
    to right,
    transparent calc(50% - 0.5px),
    var(--page-muted) calc(50% - 0.5px),
    var(--page-muted) calc(50% + 0.5px),
    transparent calc(50% + 0.5px)
  );
}

/* And the twenty-third is the count itself, so it is the tallest and the
   brightest thing on the rule. */
.ab__scale span:last-child {
  height: 100%;
  background-image: linear-gradient(
    to right,
    transparent calc(50% - 0.5px),
    var(--page-fg) calc(50% - 0.5px),
    var(--page-fg) calc(50% + 0.5px),
    transparent calc(50% + 0.5px)
  );
}

.ab__scale-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin: 0.85rem 0 0;
  font-family: var(--font-mono);
  font-size: clamp(0.5rem, 1.8vw, 0.58rem);
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  color: var(--pewter-deep);
  /* Old-style figures would drop the 01 below the baseline the 23 sits on. */
  font-variant-numeric: lining-nums;
  font-feature-settings: "lnum" 1, "onum" 0;
}

.ab__scale-name {
  color: var(--page-muted);
}

/* -------------------------------------------------------------- the close */

/* No panel and no box. The finale on the landing page is already a centred
   card of closing words with these two pills in it, and a second one here
   would be the same screen twice; this is the last band of an article, so it
   sets like every band above it and the pills simply follow the paragraph. */
.ab__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem 0.8rem;
  margin-top: clamp(1.5rem, 4vw, 2.25rem);
}

/* The finale's two pills, restated here because home.css is the landing page's
   sheet alone and this page does not load it. Same geometry, same caps
   correction split across both pads; see --caps-drop in site.css for why a
   one-sided pad delivers half the movement. */
.ab__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: calc(0.68rem + var(--caps-drop)) clamp(1.3rem, 4vw, 1.9rem)
    calc(0.68rem - var(--caps-drop));
  border: 1px solid var(--pewter-dim);
  border-radius: 999px;
  color: var(--page-fg);
  font-family: var(--font-body);
  font-size: clamp(0.64rem, 2.2vw, 0.72rem);
  font-weight: 400;
  white-space: nowrap;
  text-decoration: none;
  background: var(--pewter-wash);
  transition: border-color 0.25s ease, background-color 0.25s ease,
    color 0.25s ease, opacity 0.25s ease, transform 0.25s ease;
}

.ab__btn:hover,
.ab__btn:focus-visible {
  border-color: var(--page-fg);
  background-color: rgba(236, 236, 236, 0.12);
}

.ab__btn:active {
  transform: translateY(1px);
}

.ab__btn--book {
  border-color: transparent;
  background: var(--page-fg);
  color: #0a0a0b;
}

.ab__btn--book:hover,
.ab__btn--book:focus-visible {
  border-color: transparent;
  background-color: #ffffff;
  opacity: 0.92;
}

.ab__call {
  margin: clamp(1rem, 3vw, 1.4rem) 0 0;
  max-width: 36rem;
  font-size: clamp(0.82rem, 2.4vw, 0.9rem);
  line-height: 1.6;
  color: var(--page-muted);
}

.ab__call a {
  color: var(--page-fg);
  text-decoration-color: var(--footer-rule);
  text-underline-offset: 0.22em;
  /* The non-breaking hyphens in the markup keep the digits together; this
     keeps the two numbers off each other's lines on a 320px screen. */
  white-space: nowrap;
}

.ab__call a:hover,
.ab__call a:focus-visible {
  text-decoration-color: currentColor;
}

/* site.css draws focus rings in --pewter, which is right on the flat silk and
   a step quiet over the lamp's own pool; this page restates them at the
   foreground value for the same reason home.css does. */
.ab :focus-visible {
  outline: 2px solid var(--page-fg);
  outline-offset: 3px;
}

/* =========================================================================
   MOTION
   Everything below this line is gated on html.js, so it exists only for a
   reader whose browser is running about.js. Above the line the page is
   finished and static.
   ========================================================================= */

/* The two numbers about.js writes, defaulted here so a band it has not reached
   yet computes its finished state rather than a blank one. --k is overwritten
   to 0 by the motion gate below. */
html.js [data-r] {
  --k: 1;
  --t: 0.5;
}

/* Written on the document element by about.js, defaulted on the same element
   here so a frame that has not run yet computes a still page rather than an
   invalid one. Not `:root` under `html.js`: html IS :root, so that selector
   asks for a root inside a root and matches nothing. */
html.js {
  --v: 0;
}

@media (prefers-reduced-motion: no-preference) {
  /* A tracked band starts at nothing and is brought in by its own --k. The
     smoothstep is applied to the staggered value rather than to --k itself, so
     each child gets the site's ease rather than the linear number the scroll
     delivers, which is the same arithmetic the beats run.

     THE LAST STAGGER STEP HAS TO FINISH INSIDE THE WINDOW, which is one
     inequality and is the whole reason these two numbers are 0.09 and 0.6.
     --k is clamped at 1 by construction, so a child whose ramp is written
     (k - offset) / span only ever completes if offset + span <= 1. The first
     cut was 0.1 and 0.66, and the deepest child on the page is the fifth
     (--i 4, the phone line at the foot of the close), whose ramp therefore
     needed k = 1.06: it settled at 0.977 opacity and STAYED there for the rest
     of the visit, on the last line of the page, where a reader has stopped
     scrolling and is looking straight at it. Found by scrolling the whole page
     in a headless pass and then listing every element left under full opacity,
     which is the only way this shows up: it is not a wrong-looking frame, it
     is a frame two percent short for ever. 0.09 x 4 + 0.6 = 0.96, and the
     craft list below carries the same check. Re-derive both if a band ever
     gains a sixth child. */
  html.js [data-r] {
    --k: 0;
  }

  html.js [data-r] > * {
    --i: 0;
    --s: clamp(0, calc((var(--k) - var(--i) * 0.09) / 0.6), 1);
    --ss: calc(var(--s) * var(--s) * (3 - 2 * var(--s)));
    opacity: var(--ss);
    transform: translate3d(0, calc((1 - var(--ss)) * 30px), 0);
  }

  html.js [data-r] > *:nth-child(2) {
    --i: 1;
  }
  html.js [data-r] > *:nth-child(3) {
    --i: 2;
  }
  html.js [data-r] > *:nth-child(4) {
    --i: 3;
  }
  html.js [data-r] > *:nth-child(5) {
    --i: 4;
  }

  /* A CONTAINER THAT STAGGERS ITS OWN CHILDREN STOPS ANIMATING ITSELF. Both of
     these are direct children of a tracked band, so the generic rule above
     would fade and lift the whole block while the rules below fade and lift
     every row inside it: two easings multiplied is slower and mushier than
     either one, and the stagger is the part worth reading. */
  html.js [data-r] > .ab__craft,
  html.js [data-r] > .ab__gauge {
    opacity: 1;
    transform: none;
  }

  /* The four craft entries, staggered by their own index in the list. Same
     inequality as above: 0.1 + 0.1 x 3 + 0.55 = 0.95, inside the window. */
  html.js [data-r] .ab__craft li {
    --j: 0;
    --sj: clamp(0, calc((var(--k) - 0.1 - var(--j) * 0.1) / 0.55), 1);
    --sjj: calc(var(--sj) * var(--sj) * (3 - 2 * var(--sj)));
    opacity: var(--sjj);
    transform: translate3d(0, calc((1 - var(--sjj)) * 22px), 0);
  }

  html.js [data-r] .ab__craft li:nth-child(2) {
    --j: 1;
  }
  html.js [data-r] .ab__craft li:nth-child(3) {
    --j: 2;
  }
  html.js [data-r] .ab__craft li:nth-child(4) {
    --j: 3;
  }

  /* EVERY HAIRLINE ON THIS PAGE IS DRAWN, NOT FADED: the rules between the
     bands, the dividers in the list and the graduations on the gauge all scale
     from one end as their band arrives, so the page reads as something being
     ruled rather than something appearing. Scale is a composite; width and
     height are a layout. */
  html.js [data-r] .ab__rule {
    transform: scaleX(var(--ss, 1));
  }

  html.js [data-r] .ab__craft li::before,
  html.js [data-r] .ab__craft li::after {
    transform: scaleX(var(--sjj));
  }

  /* THE GAUGE STANDS UP LEFT TO RIGHT. Each graduation scales from its own
     foot, so it grows out of the baseline the way a mark is stepped off a
     rule; --n is the tick's index, written in the markup exactly the way
     index.html writes --i onto the landing film's lines, and 0.021 per tick
     spends about half the band's window walking the twenty-three of them with
     the other half left for each one to finish in. */
  html.js [data-r] .ab__scale span {
    --sn: clamp(0, calc((var(--k) - 0.1 - var(--n) * 0.021) / 0.3), 1);
    --snn: calc(var(--sn) * var(--sn) * (3 - 2 * var(--sn)));
    transform: scaleY(var(--snn));
    opacity: var(--snn);
  }

  /* The label under it waits for the last mark rather than arriving with the
     first, so the reader reads the scale and then what it counts. */
  html.js [data-r] .ab__scale-label {
    --sc: clamp(0, calc((var(--k) - 0.6) / 0.3), 1);
    opacity: calc(var(--sc) * var(--sc) * (3 - 2 * var(--sc)));
  }

  /* THE PARALLAX AND THE LAG, which are the two things that make this page
     answer the scroll rather than merely notice it.

     --t is the frame's whole traversal of the viewport, so the image inside it
     travels its own 16% of spare height across a full screen of scrolling and
     is dead centre when the frame is. --v is the smoothed scroll velocity,
     clipped to -1..1 by about.js, and it drags the image a further 14px
     against the direction of travel while the reader is actually moving,
     settling to nothing the moment they stop. Both are transforms on a
     promoted element, so a frame costs a composite and nothing else.

     The two are summed into one translate because a second transform on the
     frame would give its own clip something to do. */
  html.js .ab__frame img {
    transform: translate3d(
      0,
      calc(-50% + (0.5 - var(--t)) * 16% + var(--v) * 14px),
      0
    );
    will-change: transform;
  }

  /* The caption lags the picture it sits on by a little less, so the plate
     reads as one thing moving at two depths rather than as a label stuck to a
     photograph. */
  html.js .ab__cap {
    transform: translate3d(0, calc(var(--v) * 6px), 0);
  }

  /* The lamp swings against the scroll and settles when it stops. 16vh is the
     largest throw at which the gradient's own outer edge never reaches the
     frame, given the 40vh of overhang the layer carries at each end. At full
     tilt the leading edge of the viewport sits at about 35% shade and the
     trailing edge at about 10%, which is a differential the eye reads as a
     light being carried and which never touches a pixel of type. */
  html.js .ab__lamp {
    transform: translate3d(0, calc(var(--v) * 16vh), 0);
    will-change: transform;
  }

  /* The hero's own entrance belongs to a clock, not to the scroll: it is
     already at the top of the viewport when the page loads, so a scroll-driven
     window would compute 1 on the first frame and the page would open with the
     entrance already spent. Everything below the hero belongs to the scroll. */
  html.js .ab__hero > * {
    opacity: 0;
    animation: ab-in 1100ms var(--ease-out) forwards;
  }

  /* Two children now, the title and the line under it. The title takes none of
     this (it is masked; see below), so the only delay that is spent is the
     lead's, and it is spent AFTER the second masked line has finished
     travelling: 240ms + 1 x 120ms of delay plus 1200ms of rise puts the
     title's foot down at 1560ms, and 900ms is far enough behind it that the
     lead arrives under a headline that has stopped moving. */
  html.js .ab__hero > *:nth-child(1) {
    animation-delay: 100ms;
  }
  html.js .ab__hero > *:nth-child(2) {
    animation-delay: 900ms;
  }

  /* The title is two masked lines, so it takes the rise rather than the fade
     its siblings take: the whole point of a mask is that the line is not
     visible early and dim, it is not there at all. */
  html.js .ab__hero > .ab__title {
    opacity: 1;
    animation: none;
  }

  html.js .ab__hero .ab-l .ab-l__in {
    animation: ab-rise 1200ms var(--ease-out) backwards;
    animation-delay: calc(240ms + var(--i) * 120ms);
  }

  @keyframes ab-in {
    from {
      opacity: 0;
      transform: translate3d(0, 34px, 0);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }

  @keyframes ab-rise {
    from {
      transform: translateY(112%);
    }
    to {
      transform: none;
    }
  }

}

/* --- Print ---------------------------------------------------------------
 *
 * site.css's print block flips the ramp to ink on paper for the whole site, so
 * nothing here restates a colour. What this block has to do is undo the
 * motion, and the selectors MUST carry html.js: they are answering rules whose
 * own selectors carry it, and a bare class here loses the specificity fight
 * and prints the page at opacity zero. That is the exact mistake home.css
 * records paying for, so it is written the same way on the way in.
 *
 * The photographs stay. They are the page: a reader printing this is printing
 * who makes the ring.
 */
@media print {
  html.js .ab__hero > *,
  html.js .ab__hero .ab-l__in,
  html.js [data-r] > *,
  html.js [data-r] .ab__craft li,
  html.js [data-r] .ab__scale span,
  html.js [data-r] .ab__scale-label,
  html.js .ab__frame img,
  html.js .ab__cap {
    opacity: 1;
    transform: none;
    animation: none;
  }

  html.js [data-r] .ab__rule,
  html.js [data-r] .ab__craft li::before,
  html.js [data-r] .ab__craft li::after {
    transform: none;
  }

  /* A frame that clips a 116% image is showing three quarters of a photograph
     once the transform is gone; on paper it is simply the picture. */
  html.js .ab__frame img {
    position: static;
    height: auto;
  }

  .ab__frame {
    aspect-ratio: auto;
    background: none;
  }

  .ab__cap {
    position: static;
    padding: 0.5rem 0 0;
    background: none;
  }

  /* A lit pool is a thing a screen does; paper is already the light. */
  .ab__lamp,
  .ab__actions {
    display: none;
  }

  /* On paper the two plates are two photographs one after the other, at their
     own shapes, because the grid's whole argument is about a screen's width
     and the drop on the second one is a composition inside a viewport. The
     rules that draw them are answered above; this is only the layout. */
  .ab__plates {
    display: block;
  }

  .ab__plates > :nth-child(2) {
    margin-top: 1rem;
  }

  .ab__craft {
    display: block;
  }
}
