@import url('./fonts.css?v=0827061221');   /* bumped with the rest; see HANDOVER */

/* ============================================================
   ARTIE ROMO MARTIN
   Design tokens. Palette sampled directly from the Let It Rain
   cover: wet stone, storm grey, lamp bronze, sunset gold, cream.
   One accent (amber). One radius (0). One theme (night).
   ============================================================ */

:root {
  /* --- ground --- */
  --abyss:      #06070a;
  --night:      #0b0d11;
  --storm:      #12151b;
  --ember-ink:  #17130f;

  /* --- ink on night --- */
  --cream:      #ece2d4;
  --haze:       #b9b1a6;   /* body copy, lifted from #a49b90: thin serif
                              strokes on a dark ground lose more than the
                              contrast ratio suggests */
  /* mist is the quietest text on the page and still carries real copy,
     so it is set at the lightest value that clears 4.5:1 on the darkest
     ground the page ever reaches, with headroom. */
  --mist:       #9c948a;   /* labels and captions, lifted with it */
  --hairline:   rgba(236, 226, 212, 0.14);
  --hairline-2: rgba(236, 226, 212, 0.07);

  /* --- the single accent --- */
  --amber:      #d79a5f;
  --amber-hi:   #edb277;
  --amber-dim:  rgba(215, 154, 95, 0.32);
  --live:       #7ac58f;   /* the only green on the site: a state */
  --paper:      #cdc3b5;   /* controls: cream, stepped back from white */

  /* --- type --- */
  --display: 'Cormorant Garamond', 'Iowan Old Style', Georgia, serif;
  --text:    'Newsreader', 'Iowan Old Style', Georgia, serif;
  --micro:   'Archivo', 'Helvetica Neue', Arial, sans-serif;

  /* --- corners ---
     Two values, and the split is by size rather than by whim.

     A 10px corner on a 40px button reads as rounded; the same 10px
     on a card five hundred pixels wide barely registers, which is
     why the book cards looked square-ish while the controls looked
     soft. Radius has to grow with the box for the curve to read as
     the same curve. So: controls take --radius, large surfaces take
     --radius-lg, and nothing picks a number of its own. */
  --radius:     10px;   /* buttons, chips, fields */
  --radius-lg:  20px;   /* cards, plates, images, panels */

  /* --- rhythm --- */
  --gutter:  clamp(1.5rem, 5vw, 5.5rem);
  --maxw:    84rem;
  --measure: 33rem;
  --bay:     clamp(5.5rem, 13vw, 11rem);   /* section padding */
  /* The masthead's height, as a number other things can read.

     Anything that has to clear the fixed bar — the hero's first
     line, the sticky book frame, the scroll offset for an anchor —
     used to carry its own copy of 72px, and on a phone those copies
     disagreed by enough that the book arrived 12px under the bar. */
  --bar:     72px;

  /* --- motion ------------------------------------------------
     One motion language for the whole site. Two easings, four
     durations, and nothing outside them.

     --exit is the workhorse: fast out of the gate, long settle.
     Everything the reader touches uses it. --inout is reserved
     for things that travel and return (drift, weather), where a
     symmetric curve is what reads as physical.

     Controls sit in the 140-220ms band. Anything slower than
     that on a hover or a press stops reading as feedback and
     starts reading as lag.
     ------------------------------------------------------------ */
  --ease:       cubic-bezier(0.22, 1, 0.36, 1);   /* exit  */
  --ease-soft:  cubic-bezier(0.4, 0, 0.2, 1);     /* inout */

  --t-press:    120ms;   /* the push itself                     */
  --t-control:  180ms;   /* hover, nav, links, form focus       */
  --t-panel:    320ms;   /* things that open, swap or slide     */
  --t-reveal:   620ms;   /* text and blocks entering the page   */

  --z-atmos: 0;
  --z-body:  10;
  --z-nav:   80;
  --z-grain: 90;
}

/* ============================================================
   RESET / BASE
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  min-height: 100dvh;
  background: var(--abyss);
  color: var(--cream);
  font-family: var(--text);
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  font-weight: 350;
  line-height: 1.72;
  font-optical-sizing: auto;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* height:auto is load bearing. The width/height attributes on every
   <img> reserve layout space against CLS, but without this they also
   act as presentational hints and lock the rendered height. */
img, picture, canvas, svg { display: block; max-width: 100%; }
img { height: auto; }
button, input { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }

::selection { background: var(--amber); color: var(--abyss); }

:focus-visible {
  outline: 1px solid var(--amber-hi);
  outline-offset: 4px;
}

.skip-link {
  position: fixed;
  top: 0; left: 50%;
  transform: translate(-50%, -110%);
  z-index: 200;
  padding: 0.9rem 1.6rem;
  background: var(--amber);
  color: var(--abyss);
  font-family: var(--micro);
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  transition: transform var(--t-control) var(--ease);
}
.skip-link:focus { transform: translate(-50%, 0); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ============================================================
   TYPOGRAPHY PRIMITIVES
   Display serif is used only at large sizes and never above 500.
   ============================================================ */

/* Tracking is optical, not fixed. A face set for book sizes opens up
   as it is enlarged, so the larger the size the tighter it is set.
   Cormorant in particular goes loose above about 3rem if left alone. */
.display {
  font-family: var(--display);
  font-weight: 300;
  line-height: 1.02;
  letter-spacing: -0.012em;
  text-wrap: balance;
  font-kerning: normal;
  font-variant-ligatures: common-ligatures contextual;
}

.d-1 { font-size: clamp(3.2rem, 12.5vw, 10.5rem); letter-spacing: -0.024em; }
.d-2 { font-size: clamp(2.6rem, 6.4vw, 5.4rem);  line-height: 1.06; letter-spacing: -0.018em; }
.d-3 { font-size: clamp(2rem, 3.9vw, 3.15rem);   line-height: 1.12; letter-spacing: -0.012em; }
.d-4 { font-size: clamp(1.5rem, 2.4vw, 2rem);    line-height: 1.2;  letter-spacing: -0.006em; }

.caps {
  font-family: var(--display);
  font-weight: 300;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

.lede {
  font-size: clamp(1.15rem, 1.02rem + 0.6vw, 1.45rem);
  line-height: 1.62;
  font-weight: 350;
  color: var(--haze);
  max-width: var(--measure);
  text-wrap: pretty;
  /* a larger optical size thins the strokes, which is what keeps a
     standfirst from reading as merely bigger body copy */
  font-variation-settings: 'opsz' 28;
}

.prose {
  max-width: var(--measure);
  font-weight: 350;
  color: var(--haze);
  text-wrap: pretty;
  font-variation-settings: 'opsz' 16;
  hanging-punctuation: first last;
}
.prose p + p { margin-top: 1.2em; }
.prose em { font-style: italic; color: var(--cream); }
.prose strong { font-weight: 400; color: var(--cream); }

/* the one micro-label style. Rationed: three on the page, maximum. */
.marker {
  display: block;
  font-family: var(--micro);
  font-weight: 500;
  font-size: 0.6875rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--amber);
}

.rule {
  height: 1px;
  border: 0;
  background: var(--hairline);
}

/* ============================================================
   LAYOUT
   ============================================================ */

.shell {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.bay { padding-block: var(--bay); position: relative; z-index: var(--z-body); }

main { position: relative; z-index: var(--z-body); }

/* every deep link has to clear the fixed masthead */
section[id], footer[id] { scroll-margin-top: 5.5rem; }

/* ============================================================
   ATMOSPHERE
   A fixed stack behind everything. Only opacity is animated, so
   the whole weather system costs one composited layer per plate.
   The page travels from cold storm to lamplight to a warm horizon;
   it never leaves the night.
   ============================================================ */

.atmosphere {
  position: fixed;
  inset: 0;
  z-index: var(--z-atmos);
  pointer-events: none;
  background: var(--abyss);
}

.atmosphere__plate {
  position: absolute;
  inset: 0;
  will-change: opacity;
}

/* cold storm, top of the page */
.atmosphere__plate--storm {
  opacity: 1;
  background:
    radial-gradient(120% 80% at 50% -10%, #1b2029 0%, transparent 62%),
    radial-gradient(90% 60% at 78% 22%, rgba(52, 62, 78, 0.5) 0%, transparent 70%),
    linear-gradient(180deg, #0a0d12 0%, #06070a 100%);
}

/* the lamp on the promenade */
.atmosphere__plate--lamp {
  opacity: 0;
  background:
    radial-gradient(70% 55% at 50% 62%, rgba(215, 154, 95, 0.26) 0%, transparent 66%),
    radial-gradient(100% 70% at 20% 30%, rgba(120, 84, 48, 0.22) 0%, transparent 68%),
    linear-gradient(180deg, #0d0b09 0%, #100c08 100%);
}

/* the break in the cloud */
.atmosphere__plate--dawn {
  opacity: 0;
  background:
    radial-gradient(80% 46% at 50% 108%, rgba(237, 178, 119, 0.34) 0%, transparent 68%),
    radial-gradient(60% 40% at 84% 88%, rgba(215, 154, 95, 0.2) 0%, transparent 70%),
    linear-gradient(180deg, #08090c 0%, #14100c 100%);
}

.atmosphere__rain {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  opacity: 0.85;
}

/* Film grain. Fixed and pointer-events-none so it never repaints
   with the scroll. */
/* Sits exactly on the viewport. The drift comes from shifting the
   tile rather than moving the element, because an oversized layer
   here is real horizontal overflow, and the blend has to stay on
   this element or it loses the page as its backdrop. */
.grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-grain);
  pointer-events: none;
  opacity: 0.42;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)' opacity='0.5'/%3E%3C/svg%3E");
  mix-blend-mode: soft-light;
  animation: grain-drift 6s steps(5) infinite;
}
@keyframes grain-drift {
  0%   { background-position:   0px   0px; }
  20%  { background-position: -13px   9px; }
  40%  { background-position:   9px -13px; }
  60%  { background-position:  -7px  -9px; }
  80%  { background-position:  13px   5px; }
  100% { background-position:   0px   0px; }
}

/* ============================================================
   REVEAL PRIMITIVES
   Not a blanket fade. Type rises from a clipped baseline the way
   a line of set metal lifts off the page; blocks drift a short
   distance. Everything is transform and opacity only.
   ============================================================ */

/* The reveal mask.

   The padding is not decoration. Display sizes here run at a 1.06
   line height, which makes the line box shorter than the font's own
   descender, and overflow:hidden then cuts the tail off any g, y or
   p on the last line. "one long season." lost the g. The box is
   given the descender back and the extra height is pulled off again
   with a negative margin, so nothing below it moves. */
.lift {
  display: block;
  overflow: hidden;
  padding-bottom: 0.16em;
  margin-bottom: -0.16em;
}
.lift > * {
  display: block;
  transform: translateY(105%);
  will-change: transform;
}

.drift { opacity: 0; transform: translateY(2.2rem); will-change: transform, opacity; }

/* The hero is never hidden by CSS.

   Everything else on the page starts hidden and is revealed by a
   scroll trigger, which is safe: if the trigger never fires the
   reader never got there either. The hero is different. It is the
   first thing on the page, and hiding it means a starved frame loop
   can withhold the whole first screen. It has happened twice.

   So the hero renders, and the arrival animates FROM a hidden state
   instead of TO a visible one. If the timeline never runs, nothing
   is lost but the choreography. */
.hero__lead .lift > * { transform: none; }
.hero__lead .drift { opacity: 1; transform: none; }

.js-off .lift > * { transform: none; }
.js-off .drift { opacity: 1; transform: none; }

/* Without the motion layer the turn cannot reveal anything, so the
   sequence unstacks into three plain sections. Otherwise the ISBN
   and the buy link, which live in the later panels, are simply not
   on the page. */
.js-off .object-seq { min-height: 0; }
.js-off .object-seq__frame { position: static; min-height: 0; padding-block: var(--bay); }
.js-off .book { margin-bottom: 3rem; }
.js-off .tome { margin-bottom: 0; }
.js-off .tome__ground { display: none; }
.js-off .turnstile { display: grid; gap: clamp(3rem, 6vw, 4.5rem); }
.js-off .turnstile__panel {
  grid-area: auto;
  opacity: 1;
  transform: none;
  pointer-events: auto;
  max-width: 40rem;
}
.js-off .turnstile__index { display: none; }

/* Without the motion layer the shelf cannot pan, so six of the ten
   cards would sit outside a clipped viewport with no way to reach
   them. It lies down and becomes a grid instead. */
.js-off .gallery { min-height: 0; display: block; }
.js-off .gallery__viewport { overflow: visible; -webkit-mask-image: none; mask-image: none; }
.js-off .gallery__track {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(2.5rem, 4vw, 3.5rem) clamp(1.5rem, 2.6vw, 2.6rem);
  width: auto;
  padding-inline: 0;
  transform: none;
}
.js-off .entry { width: auto; }
.js-off .gallery__rail { display: none; }

/* ============================================================
   NAVIGATION
   ============================================================ */

.masthead {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-nav);
  height: calc(var(--bar) + env(safe-area-inset-top));
  padding-top: env(safe-area-inset-top);
  display: flex;
  align-items: center;
  transition: background var(--t-panel) var(--ease), border-color var(--t-panel) var(--ease);
  border-bottom: 1px solid transparent;
}
/* The lifted backdrop is painted by a pseudo-element, and that is not
   a style preference.

   backdrop-filter — like filter, transform and perspective — makes an
   element a CONTAINING BLOCK for its position:fixed descendants. The
   mobile menu is a fixed panel with inset:0 that lives inside this
   header. The moment the header lifted, "the viewport" became the
   72px header box for that panel: it collapsed to 80px, the links
   spilled out of it, and the page showed through behind them. At the
   top of the home page the header is not lifted, so the menu opened
   correctly and the fault looked like it only appeared after
   scrolling. On the journal pages, where the header is lifted from
   the first frame, it was broken at every scroll position.

   On a pseudo-element the same blur paints identically and creates a
   containing block for nothing. */
.masthead::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: rgba(8, 9, 12, 0.72);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  transition: opacity var(--t-panel) var(--ease);
}
.masthead[data-lifted='true']::before { opacity: 1; }
.masthead[data-lifted='true'] {
  border-bottom-color: var(--hairline-2);
}

.masthead__inner {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
}

/* Newsreader, not Cormorant.

   Cormorant is a display face: it is drawn for 100px and its thin
   strokes go spindly and uneven long before 18px, which is what
   made the wordmark look wrong at either weight I tried. Newsreader
   is the same voice drawn for text sizes, and it is already the
   face the prose is set in. Letterspaced and set in caps it reads
   as an imprint rather than as a heading that shrank. */
.wordmark {
  font-family: var(--text);
  font-weight: 500;
  font-size: 0.98rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--cream);
}
.wordmark span { color: var(--amber); }

.nav { display: flex; align-items: center; gap: clamp(1.4rem, 2.6vw, 2.6rem); }
.nav a {
  font-family: var(--micro);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--haze);
  padding-block: 0.4rem;
  position: relative;
  transition: color var(--t-control) var(--ease);
}
.nav a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--amber);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform var(--t-control) var(--ease);
}
.nav a:hover, .nav a[aria-current='page'] { color: var(--cream); }
.nav a:hover::after, .nav a[aria-current='page']::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* The one action a QR arrival is looking for, reachable from any
   point on any page. Amber against the grey nav items so it is found
   by scanning rather than by reading. */
/* the same capsule as the buttons, at nav scale. It sits beside
   them on every screen, and a square nav chip next to a row of
   capsules reads as an oversight rather than a distinction. */
/* `.nav a` is (0,1,1) and this was (0,1,0), so the colour below
   never applied and the one call in the header rendered in haze:
   the least readable thing on the page was the thing being asked
   for. Matching the anchor specificity fixes it at the source. */
.nav a.nav-cta {
  padding: 0.5rem 1.05rem;
  border: 1px solid var(--amber-dim);
  border-radius: var(--radius);
  background: rgba(215, 154, 95, 0.1);
  color: var(--amber-hi);
  transition: background var(--t-control) var(--ease), color var(--t-control) var(--ease),
              border-color var(--t-control) var(--ease);
}
.nav-cta::after { display: none; }
.nav a.nav-cta:hover,
.nav a.nav-cta:focus-visible {
  background: var(--amber);
  border-color: var(--amber);
  color: #191510;
}

.nav-toggle { display: none; }

/* ============================================================
   BUTTONS
   The header chip, at page scale.

   The one control that worked was the small amber chip in the
   masthead, so it is now the pattern rather than an exception: a
   faint amber wash, an amber hairline, an amber label in the micro
   face, and the site's one corner radius. Everything below is that
   chip, set larger because these are the actions of a page rather
   than of a nav bar.
   ============================================================ */

.btn {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  padding: 0.92rem 1.75rem;

  font-family: var(--micro);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  line-height: 1.25;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;

  border: 1px solid var(--amber-dim);
  border-radius: var(--radius);
  background: rgba(215, 154, 95, 0.1);
  color: var(--amber-hi);

  transition: background var(--t-control) var(--ease),
              border-color var(--t-control) var(--ease),
              color var(--t-control) var(--ease);
}
.btn:hover,
.btn:focus-visible {
  background: var(--amber);
  border-color: var(--amber);
  color: #191510;
}
.btn:active { background: var(--amber-hi); border-color: var(--amber-hi); }

/* ---- the light passes across it ---------------------------------
   On a page lit by weather, the thing that says a surface can be
   pressed is light moving over it, not a shadow under it.
   ---------------------------------------------------------------- */
.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  transform: translateX(-102%) skewX(-16deg);
  background: linear-gradient(90deg,
    transparent 0%, rgba(255, 244, 228, 0.22) 50%, transparent 100%);
  transition: transform var(--t-reveal) var(--ease);
}
.btn:hover::before,
.btn:focus-visible::before { transform: translateX(102%) skewX(-16deg); }

/* ---- touch ------------------------------------------------------
   There is no hover on a phone, so every one of these controls was
   completely inert to a finger: the sweep and the colour change
   only ever fired for a pointer. On a touch device the press does
   the same work.
   ------------------------------------------------------------------ */
/* Driven by press() in main.js rather than by :active, which a
   touch does not reliably raise. One attribute, styled here. */
.btn[data-pressed='true'] {
  background: var(--amber);
  border-color: var(--amber);
  color: #191510;
}
.btn--quiet[data-pressed='true'] {
  background: var(--cream);
  border-color: var(--cream);
  color: #14110d;
}
.social__link[data-pressed='true'],
.nav a.nav-cta[data-pressed='true'] {
  background: var(--amber);
  border-color: var(--amber);
  color: #191510;
}
.totop[data-pressed='true'] { border-color: var(--amber); }

/* ---- the secondary ----------------------------------------------
   The same rectangle with the fill taken out. It fills on approach,
   which is where the primary already is: the pair share one idea and
   are a step apart in it.
   ---------------------------------------------------------------- */
.btn--quiet {
  background: rgba(236, 226, 212, 0.035);
  border-color: rgba(236, 226, 212, 0.22);
  color: var(--haze);
}
.btn--quiet:hover,
.btn--quiet:focus-visible {
  background: rgba(236, 226, 212, 0.1);
  border-color: var(--cream);
  color: var(--cream);
}
.btn--quiet:active { background: rgba(236, 226, 212, 0.16); }
.btn--quiet::before {
  background: linear-gradient(90deg, transparent, rgba(236, 226, 212, 0.16), transparent);
}

/* the give button is the one call on its screen, so it is set one
   step larger */
.btn--give { font-size: 0.8rem; padding: 1.05rem 2.1rem; }

@media (prefers-reduced-motion: reduce) {
  .btn--go::after { transition: none; }
}

/* Every control answers the pointer twice: once on approach, once
   on press. The press is 1px and 120ms, which is below conscious
   notice and is exactly why it reads as a physical object rather
   than a rectangle changing colour. */
.btn:active,
.tlink:active,
.nav-cta:active { transform: translateY(1px); }

/* ============================================================
   TACTILE FEEDBACK
   ============================================================ */

/* the shelf tiles carry their own approach; see .tile below */

/* journal entries: the whole row is the target, so the whole row moves */
.entry {
  transition: background var(--t-control) var(--ease);
}
.entry__title,
.entry__meta,
.entry__stand {
  transition: color var(--t-control) var(--ease), transform var(--t-control) var(--ease);
}
.entry:hover { background: rgba(236, 226, 212, 0.022); }
.entry:hover .entry__title,
.entry:hover .entry__meta,
.entry:hover .entry__stand { transform: translateX(4px); }
.entry:active .entry__title { transform: translateX(4px) translateY(1px); }

/* footer and column links shift rather than merely recolour */
.foot__col a {
  display: inline-block;
  transition: color var(--t-control) var(--ease), transform var(--t-control) var(--ease);
}
.foot__col a:hover { transform: translateX(3px); }

/* the pager at the foot of an article */
.pager__side { transition: background var(--t-control) var(--ease); }
.pager__side:hover { background: rgba(236, 226, 212, 0.022); }

/* the form answers focus with the accent, and the button with a press */
.signup button:active { transform: translateY(1px); }

/* an underlined text link, the site's tertiary action */
.tlink {
  display: inline-block;
  font-family: var(--micro);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--cream);
  padding-bottom: 0.35rem;
  background-image: linear-gradient(var(--amber), var(--amber));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 1px;
  transition: color var(--t-control) var(--ease), background-size var(--t-control) var(--ease);
}
.tlink:hover { color: var(--amber-hi); background-size: 40% 1px; }

/* ============================================================
   HERO
   Two columns that each carry their weight: everything the reader
   has to act on stacks in one block on the left, the object owns
   the right. No element is left floating in a corner of its own.
   ============================================================ */

.hero {
  position: relative;
  min-height: 100dvh;
  display: grid;
  align-items: center;
  padding-block: clamp(6rem, 12vh, 8rem) clamp(3rem, 7vh, 5rem);
  z-index: var(--z-body);
}

.hero__stage {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.04fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  width: 100%;
  /* The same measure as every other section.

     It used to be 92rem, which is wider than a 1440 screen, so the
     hero fell back to the raw gutter and started 48px left of the
     masthead, the about section, the collection and the journal.
     One left edge for the whole site is worth more than a poster
     that is slightly wider than the page under it. */
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.hero__lead { position: relative; z-index: 3; }

.hero__marker { margin-bottom: clamp(1.1rem, 2.4vh, 1.7rem); }

/* sized to very nearly fill its column, which is what closes the
   gap that opens up when the title is set too small for its space */
.hero__title {
  margin: 0;
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(3.1rem, 11vw, 11rem);
  line-height: 0.9;
  /* letterspaced caps carry a trailing space after the last glyph.
     Half of it comes back so the block reads as optically centred on
     its own measure rather than sitting a hair left. */
  letter-spacing: 0.035em;
  padding-right: 0.035em;
  text-transform: uppercase;
  color: var(--cream);
  font-kerning: normal;
}

/* Caps sit inside a left sidebearing that the gutter does not know
   about, so at this size the L reads as indented against everything
   stacked beneath it. Pulling it back aligns the type, not the box. */
.hero__word { margin-left: -0.045em; }

.hero__word { display: block; white-space: nowrap; }
/* the gradient lives on the transformed child, not the clipping
   parent, or the text clip loses the glyphs to their own layer */
.hero__word--accent > span {
  color: transparent;
  background: linear-gradient(176deg, #fae2c1 4%, var(--amber-hi) 40%, #b0763f 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

/* The pair. Both copies are the same .tome used in the sequence, so
   the spine reads identically here and there. Sizes are absolute
   lengths rather than percentages because translateZ, which builds
   the third dimension, cannot take a percentage. */
.hero__books {
  --front-w: clamp(8.5rem, 22vw, 19rem);
  --back-w: calc(var(--front-w) * 0.88);

  position: relative;
  z-index: 2;
  width: calc(var(--front-w) * 1.66);
  /* The box is centred against the text column, but what the eye
     centres is the ink, and the ink is not the box: the front copy is
     pinned to the top of the container while the container reserved
     room underneath it, which floated the whole pair high. Bound to
     the covers instead, and the two columns read level. */
  height: calc(var(--front-w) * 1.75);
  margin-inline: auto 0;
}

.hero__book { position: absolute; }
.hero__book .tome { margin-bottom: 0; will-change: transform; }

/* the back copy, further away and turned to show its back board */
.hero__book--back {
  left: 0;
  top: 17%;
  rotate: -14deg;
  z-index: 1;
}
.hero__book--back .tome { --cover-w: var(--back-w); }
.hero__book--back .tome__body { --ry: 203deg; --rx: -5deg; }
.hero__book--back .tome__shade { --shade: 0.26; }

/* the front copy, nearer, with a sliver of spine showing */
.hero__book--front {
  right: 0;
  top: 0;
  rotate: 3deg;
  z-index: 2;
}
.hero__book--front .tome { --cover-w: var(--front-w); }
.hero__book--front .tome__body { --ry: 20deg; --rx: -5deg; }

/* the pair casts onto the page rather than onto nothing */
.hero__book .tome::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 96%;
  width: 78%;
  height: 3.5rem;
  transform: translateX(-50%);
  background: radial-gradient(50% 50% at 50% 50%, rgba(0, 0, 0, 0.72), transparent 72%);
  filter: blur(10px);
  pointer-events: none;
  z-index: -1;
}

.hero__say {
  margin-top: clamp(1.5rem, 3vh, 2.2rem);
  max-width: 26rem;
  font-size: clamp(1.02rem, 0.96rem + 0.35vw, 1.2rem);
  line-height: 1.6;
  font-weight: 350;
  color: var(--haze);
}

.hero__actions {
  margin-top: clamp(1.9rem, 4vh, 2.8rem);
  display: flex;
  flex-wrap: wrap;
  /* 2.6rem was set when each control carried its own underline and
     two rules close together read as one broken line. These are
     solid boxes now, and a pair of related actions sits closer. */
  gap: 0.75rem 0.9rem;
  align-items: center;
}

/* ============================================================
   THE OBJECT  (pinned book sequence)
   Front.png and Back.png are photographic renders of the same
   physical book from opposite three quarter angles. The turn
   pivots each plate on the spine and swaps plates at the edge on
   moment, so the artwork is never squashed or reinterpreted.
   ============================================================ */

/* the runway. The frame sticks for the length of the turn, which
   is cheaper and far more predictable than a pinned spacer. */
.object-seq {
  position: relative;
  z-index: var(--z-body);
  min-height: 320vh;
}

.object-seq__frame {
  position: sticky;
  top: 0;
  min-height: 100dvh;
  display: grid;
  align-items: center;
  grid-template-columns: 1fr;
  gap: clamp(2rem, 5vw, 4rem);
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding: clamp(5rem, 10vh, 7rem) var(--gutter) clamp(3rem, 7vh, 5rem);
}

@media (min-width: 62rem) {
  .object-seq__frame {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    gap: clamp(3rem, 7vw, 7rem);
  }
}

.book { position: relative; display: grid; place-items: center; }

/* ---- the book itself -------------------------------------------
   Six faces of a real cuboid at the book's true proportions. One
   definition, used by the hero pair and by the turning copy, so the
   object is the same object everywhere on the site.

   COVER  5.5 x 8.5 in         -> w/h 0.64513
   SPINE  250 pages of cream stock at 0.0025 in a page
          = 0.625 in           -> 0.11364 of the cover width

   Change SPINE-K alone and all six faces, both in the hero and in
   the sequence, follow it.
   ---------------------------------------------------------------- */

.tome {
  --spine-k: 0.11364;
  --cover-w: clamp(11rem, 25vw, 19rem);
  --cover-h: calc(var(--cover-w) / 0.64513);
  --spine-w: calc(var(--cover-w) * var(--spine-k));
  --ry: 22deg;
  --rx: -4deg;

  position: relative;
  width: var(--cover-w);
  height: var(--cover-h);
  perspective: 2400px;
  perspective-origin: 50% 44%;
  margin-bottom: clamp(2.5rem, 7vh, 5rem);
}

.tome__body {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(var(--rx)) rotateY(var(--ry));
}

.tome__face {
  position: absolute;
  overflow: hidden;
  backface-visibility: hidden;
}
.tome__face img { width: 100%; height: 100%; object-fit: fill; }

/* real paperbacks are not razor sharp at the corners, and the tiny
   radius is what stops the object reading as a CG box */
.tome__cover { inset: 0; border-radius: 2px; }

/* the crease where the board meets the spine. The back face is
   mirrored by its own 180 degree rotation, so the same local edge
   lands on the spine side for both boards. */
.tome__cover::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(90deg,
    rgba(0, 0, 0, 0.42) 0,
    rgba(0, 0, 0, 0.12) 2.5%,
    rgba(0, 0, 0, 0) 7%);
}
.tome__face--front { transform: translateZ(calc(var(--spine-w) / 2)); }
.tome__face--back  { transform: rotateY(180deg) translateZ(calc(var(--spine-w) / 2)); }

/* the two long edges: spine on the left, fore edge on the right */
.tome__face--spine,
.tome__face--fore {
  top: 0;
  height: 100%;
  width: var(--spine-w);
  left: 50%;
  margin-left: calc(var(--spine-w) / -2);
}
.tome__face--spine { transform: rotateY(-90deg) translateZ(calc(var(--cover-w) / 2)); }
.tome__face--fore  { transform: rotateY(90deg)  translateZ(calc(var(--cover-w) / 2)); }

/* head and tail */
.tome__face--head,
.tome__face--foot {
  left: 0;
  width: 100%;
  height: var(--spine-w);
  top: 50%;
  margin-top: calc(var(--spine-w) / -2);
}
.tome__face--head { transform: rotateX(90deg)  translateZ(calc(var(--cover-h) / 2)); }
.tome__face--foot { transform: rotateX(-90deg) translateZ(calc(var(--cover-h) / 2)); }

/* The page block. 225 leaves cannot be drawn at this size, so the
   striation is pitched to read as many rather than to be counted. */
.tome__face--fore,
.tome__face--head,
.tome__face--foot {
  background-color: #ded3bb;
  background-blend-mode: multiply, normal;
}
.tome__face--fore {
  background-image:
    repeating-linear-gradient(90deg,
      rgba(103, 86, 58, 0.30) 0 0.6px,
      rgba(255, 252, 243, 0.55) 0.6px 1.5px,
      rgba(163, 145, 111, 0.20) 1.5px 2.2px),
    linear-gradient(90deg, #a99a7c 0%, #f2ead8 24%, #e0d5bb 66%, #9d8f72 100%);
}
.tome__face--head,
.tome__face--foot {
  background-image:
    repeating-linear-gradient(0deg,
      rgba(103, 86, 58, 0.28) 0 0.6px,
      rgba(255, 252, 243, 0.5) 0.6px 1.5px,
      rgba(163, 145, 111, 0.18) 1.5px 2.2px),
    linear-gradient(180deg, #a99a7c 0%, #ece3ce 30%, #d6cab0 70%, #988b6f 100%);
}
.tome__face--head { filter: brightness(1.12); }
.tome__face--foot { filter: brightness(0.7); }

/* per face lambert term, written by the turn */
.tome__shade {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: #05070b;
  opacity: var(--shade, 0.12);
}

/* one soft highlight travelling across the boards as they turn */
.tome__gloss {
  position: absolute;
  inset: 0;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: var(--gloss, 0.3);
  background: linear-gradient(104deg,
    rgba(255, 243, 222, 0) 30%,
    rgba(255, 243, 222, 0.26) 46%,
    rgba(255, 243, 222, 0.06) 55%,
    rgba(255, 243, 222, 0) 70%);
  background-size: 300% 100%;
  background-position: 110% 0;
}

/* the light pooling on wet stone, straight off the cover */
.tome__ground {
  position: absolute;
  left: 50%;
  top: calc(100% - 0.4rem);
  width: 62%;
  height: 4rem;
  transform: translateX(-50%);
  background:
    radial-gradient(52% 44% at 50% 20%, rgba(0, 0, 0, 0.82), transparent 74%),
    radial-gradient(68% 72% at 50% 46%, rgba(215, 154, 95, 0.18), transparent 76%);
  filter: blur(8px);
  pointer-events: none;
}

.turnstile { position: relative; display: grid; }
.turnstile__panel {
  grid-area: 1 / 1;
  opacity: 0;
  transform: translateY(1.6rem);
  transition: opacity var(--t-panel) var(--ease), transform var(--t-panel) var(--ease);
  pointer-events: none;
}
.turnstile__panel[data-active='true'] {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.turnstile__panel h2, .turnstile__panel h3 { margin-bottom: 1.4rem; }
.turnstile__panel .prose { margin-bottom: 2rem; }

.turnstile__index {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 2.2rem;
  font-family: var(--micro);
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mist);
}
.turnstile__index i {
  display: block;
  width: 2.4rem; height: 1px;
  background: var(--hairline);
  position: relative;
  overflow: hidden;
}
.turnstile__index i::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--amber);
  transform: scaleX(var(--turn, 0));
  transform-origin: left;
}

/* colophon: the real, printed facts of the book */
.colophon {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: 1.6rem 2rem;
  margin-top: 2.4rem;
  padding-top: 1.8rem;
  border-top: 1px solid var(--hairline-2);
}
.colophon div { display: grid; gap: 0.4rem; }
.colophon dt {
  font-family: var(--micro);
  font-size: 0.62rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mist);
}
.colophon dd {
  font-size: 0.98rem;
  font-variant-numeric: tabular-nums;
  font-weight: 300;
  color: var(--cream);
  line-height: 1.4;
}

/* ============================================================
   PASSAGE  (full width typographic band, the book's own words)
   ============================================================ */

.passage { position: relative; z-index: var(--z-body); text-align: center; }
.passage__inner {
  max-width: 46rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.passage blockquote {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(1.9rem, 4.3vw, 3.6rem);
  line-height: 1.24;
  letter-spacing: -0.005em;
  color: var(--cream);
  text-wrap: balance;
}
.passage blockquote em { font-style: italic; color: var(--amber-hi); }
.passage figcaption {
  margin-top: 2.4rem;
  font-family: var(--micro);
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mist);
}

/* word by word reveal, driven by scroll position */
.passage .w {
  transition: color var(--t-panel) var(--ease), opacity var(--t-panel) var(--ease);
  color: rgba(236, 226, 212, 0.16);
}
.passage .w[data-lit='true'] { color: var(--cream); }
.js-off .passage .w { color: var(--cream); }

/* ============================================================
   ABOUT  (asymmetric split, portrait bleeding to the page edge)
   ============================================================ */

.about { position: relative; z-index: var(--z-body); }

.about__grid {
  display: grid;
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: center;
}

@media (min-width: 58rem) {
  .about__grid {
    grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
    gap: clamp(3rem, 8vw, 7rem);
  }
  .about__portrait { margin-left: calc(var(--gutter) * -1); }
  .about__text { padding-top: clamp(2rem, 6vw, 5rem); }
}

.about__portrait {
  position: relative;
  will-change: transform;
}
/* Luminosity over a warm plate turns the supplied colour headshot
   into a duotone that belongs to the night, and the mask lets the
   photograph dissolve into the page instead of ending on a cut.

   Two gradients intersected, not one. The vertical fade alone left
   the left, right and top edges as hard rectangular cuts, so the
   portrait read as a photograph pasted onto the page rather than
   something surfacing out of it. The horizontal pass softens the
   sides; the corners get both, which is why they go first, exactly
   as they would in a darkroom. */
.about__portrait img {
  width: 100%;
  filter: grayscale(1) contrast(1.08) brightness(0.94);
  mix-blend-mode: luminosity;

  --feather-v: linear-gradient(180deg,
      transparent 0%, rgba(0,0,0,0.6) 5%, #000 14%,
      #000 52%, rgba(0,0,0,0.55) 78%, transparent 99%);
  --feather-h: linear-gradient(90deg,
      transparent 0%, rgba(0,0,0,0.6) 4%, #000 13%,
      #000 87%, rgba(0,0,0,0.6) 96%, transparent 100%);

  mask-image: var(--feather-v), var(--feather-h);
  mask-composite: intersect;
  -webkit-mask-image: var(--feather-v), var(--feather-h);
  -webkit-mask-composite: source-in;
}
.about__portrait::before {
  content: '';
  position: absolute;
  inset: -12% -14% -20% -10%;
  background: radial-gradient(58% 48% at 46% 40%, rgba(215, 154, 95, 0.3), transparent 72%);
  filter: blur(8px);
  z-index: -1;
}

/* The name holds one line.

   Sized against its own column rather than the viewport, because the
   column stops growing at --maxw while the viewport does not: a vw
   value that fits at 1440 is far too large at 1920. Measured, the
   name needs 13.2% of the column width to sit on one line, and that
   figure holds at every width because it is a property of the words
   and the typeface, not of the screen.

   Breaking to two lines pushed everything under it down, which is
   what left the social row hanging below the bottom of the portrait. */
.about__text { container-type: inline-size; }

.about__name {
  font-size: min(5.4rem, 13.2cqw);
  white-space: nowrap;
  margin-bottom: 1.8rem;
  color: var(--cream);
}
.about__sign {
  display: block;
  margin-top: 2.4rem;
  font-family: var(--display);
  font-style: italic;
  font-size: 1.4rem;
  color: var(--amber);
}

/* ---- elsewhere ---------------------------------------------------
   Two ways to reach him, drawn rather than imported. The icons are
   the same hairline weight as the rules on this page, and they sit
   in the same rounded surface as every other panel, so the row reads
   as part of the site rather than as a widget dropped into it.
   ------------------------------------------------------------------ */

.social {
  display: flex;
  gap: 0.7rem;
  margin-top: 2.2rem;
  padding: 0;
  list-style: none;
}

/* The same amber chip as every control on the site. A hairline
   border with a haze icon was invisible against this ground; if a
   thing is a way to reach him it should look like one. */
.social__link {
  display: grid;
  place-items: center;
  width: 2.85rem;
  height: 2.85rem;
  border: 1px solid var(--amber-dim);
  border-radius: var(--radius);
  background: rgba(215, 154, 95, 0.1);
  color: var(--amber-hi);
  transition: color var(--t-control) var(--ease),
              border-color var(--t-control) var(--ease),
              background var(--t-control) var(--ease),
              transform var(--t-control) var(--ease);
}
.social__link svg { width: 1.2rem; height: 1.2rem; display: block; }

.social__link:hover,
.social__link:focus-visible {
  color: #191510;
  border-color: var(--amber);
  background: var(--amber);
  transform: translateY(-2px);
}
.social__link:active { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .social__link, .social__link:hover { transition: none; transform: none; }
}

/* ============================================================
   COLLECTION  (editorial index, not a card grid)
   ============================================================ */

.collection { position: relative; z-index: var(--z-body); }

.collection__head {
  display: grid;
  gap: 1.4rem;
  max-width: 44rem;
  margin-bottom: clamp(3rem, 7vw, 5.5rem);
}

/* ---- the shelf --------------------------------------------------
   Five books, one per row, each in its own body and each arriving
   on its own scroll beat. The published one carries the object and
   the button; the four still being written carry a blank cover at
   the same proportion, because that is honestly what exists.

   The radius is 14px and stops there. Enough that the panel reads
   as a made thing, not so much that a poetry collection starts
   looking like an app.
   ---------------------------------------------------------------- */

.shelf {
  display: grid;
  gap: clamp(1rem, 1.8vw, 1.6rem);
  /* it is an <ol>. The browser's own 40px list indent was pushing
     every card that far right of the heading above it. */
  margin: 0;
  padding: 0;
  list-style: none;
  /* the cards rock forward out of a tilt as they arrive, which needs
     a camera to be a tilt rather than a squash */
  perspective: 1600px;
  perspective-origin: 50% 40%;
}

.tile {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(1.4rem, 3vw, 2.6rem);
  padding: clamp(1.5rem, 2.6vw, 2.2rem);
  border: 1px solid var(--hairline-2);
  border-radius: var(--radius-lg);
  background: linear-gradient(165deg, rgba(236, 226, 212, 0.04), rgba(236, 226, 212, 0.012));
  transition: border-color var(--t-panel) var(--ease),
              background var(--t-panel) var(--ease);
}
@media (min-width: 40rem) {
  .tile { flex-direction: row; align-items: center; }
}
.tile:hover {
  border-color: var(--amber-dim);
  background: linear-gradient(165deg, rgba(236, 226, 212, 0.065), rgba(236, 226, 212, 0.02));
}

/* the light that sits along the top edge of a lit object */
.tile::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(236, 226, 212, 0.16), transparent);
}

.tile--quiet { background: rgba(236, 226, 212, 0.012); }
.tile--quiet .tile__title { color: var(--haze); }

/* the published one is given the warmth and the room */
.tile--lead {
  border-color: var(--amber-dim);
  background:
    radial-gradient(64% 120% at 14% 50%, rgba(215, 154, 95, 0.12), transparent 68%),
    linear-gradient(165deg, rgba(236, 226, 212, 0.06), rgba(236, 226, 212, 0.015));
}

/* ---- the object ---- */

/* One cover size for all five. The published book used to get a
   larger one, which made its card taller than the rest and the
   column read as five different objects. Its standing is carried by
   the warm border, the glow, the real artwork and the button; it
   does not also need to be bigger. */
.tile__art {
  flex: 0 0 auto;
  width: clamp(8.5rem, 22vw, 12.5rem);
  align-self: center;
}

.tile__art img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 44px -18px rgba(0, 0, 0, 0.95);
  transition: transform var(--t-reveal) var(--ease);
}
/* Direct child only. The published book nests two faces inside a
   .turn, and each carries its own rotation; a blanket transform on
   every img in here cancelled the back face and the flip showed an
   empty slot. */
.tile:hover .tile__art > img,
.tile:hover .cover { transform: translateY(-4px) rotate(-0.8deg); }

/* ---- turning the book over --------------------------------------
   The card holds both faces of the same object and shows the back
   on approach. It is its own camera: the shelf already has a
   perspective for the arrival, and a nested 3D object needs one of
   its own or it flattens into a horizontal squash.

   Only the published book does this. The other four have no back
   cover to show, and inventing one would be a lie about a book that
   does not exist yet.
   ---------------------------------------------------------------- */

.tile--lead .tile__art { perspective: 1400px; }

.turn {
  position: relative;
  transform-style: preserve-3d;
  transition: transform 720ms var(--ease);
}
.tile--lead:hover .turn,
.tile--lead:focus-within .turn { transform: rotateY(180deg); }

.turn__face {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 44px -18px rgba(0, 0, 0, 0.95);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
/* the back sits in the same cell, already turned away */
.turn__face--back {
  position: absolute;
  inset: 0;
  transform: rotateY(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .turn, .tile--lead:hover .turn { transition: none; transform: none; }
}

/* ---- a cover that does not exist yet ----------------------------
   Not invented artwork and not a cropped reuse of the one cover
   that does exist. The same 5.5 x 8.5 object, typeset, waiting.
   ---------------------------------------------------------------- */

.cover--blank {
  position: relative;
  display: grid;
  grid-template-rows: 1fr auto auto;
  justify-items: center;
  gap: 0.9rem;
  aspect-ratio: 0.64513;
  padding: 1.4rem 0.9rem 1.1rem;
  border-radius: var(--radius-lg);
  border: 1px solid var(--hairline-2);
  /* warm board, not cool. var(--storm) alone read blue against a
     page lit by a sunset, so the cover looked like a different
     object from the one beside it. */
  background:
    linear-gradient(118deg, rgba(236, 226, 212, 0.06) 0%, rgba(236, 226, 212, 0.016) 46%, rgba(0, 0, 0, 0.2) 100%),
    linear-gradient(180deg, #221b14 0%, #191410 100%);
  box-shadow: 0 20px 44px -18px rgba(0, 0, 0, 0.95);
  transition: transform var(--t-reveal) var(--ease), border-color var(--t-panel) var(--ease);
}
.tile:hover .cover--blank { border-color: var(--amber-dim); }

/* the bound edge */
.cover__spine {
  position: absolute;
  inset: 0 auto 0 0;
  width: 7%;
  border-right: 1px solid rgba(0, 0, 0, 0.45);
  background: linear-gradient(90deg, rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.08));
}

.cover__title {
  align-self: end;
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(0.95rem, 1.5vw, 1.2rem);
  line-height: 1.18;
  text-align: center;
  text-wrap: balance;
  color: var(--haze);
  padding-inline: 0.4rem;
}
.cover__title--none { font-style: italic; color: var(--mist); }

.cover__mark {
  width: 1.6rem;
  height: 1px;
  background: var(--amber-dim);
}

.cover__by {
  font-family: var(--micro);
  font-size: 0.48rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mist);
  text-align: center;
}

/* On a full width row the status belongs in the opposite corner.
   Left in the text column it left the right half of every card
   empty, which made a five row shelf read as five near empty
   boxes. */
@media (min-width: 52rem) {
  .tile { position: relative; }
  .tile > .tile__body > .pill {
    position: absolute;
    top: clamp(1.5rem, 2.6vw, 2.2rem);
    right: clamp(1.5rem, 2.6vw, 2.2rem);
    margin: 0;
  }
}

/* ---- the words ---- */

.tile__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.8rem;
  max-width: 42rem;
}

.tile__num {
  font-family: var(--micro);
  font-size: 0.62rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mist);
}

.tile__title {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(1.6rem, 1.2rem + 1.3vw, 2.4rem);
  line-height: 1.12;
  letter-spacing: -0.012em;
  color: var(--cream);
}
.tile--lead .tile__title { font-size: clamp(2rem, 1.4rem + 2.2vw, 3.2rem); }
.tile__title .untitled { color: var(--mist); font-style: italic; }

.tile__note {
  font-size: 0.99rem;
  line-height: 1.65;
  font-weight: 350;
  color: var(--haze);
  text-wrap: pretty;
}
.tile--lead .tile__note { font-size: clamp(1rem, 0.96rem + 0.28vw, 1.14rem); }

.tile__act { margin-top: 0.8rem; }

/* ---- the status pill ---- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: rgba(236, 226, 212, 0.03);
  font-family: var(--micro);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mist);
  white-space: nowrap;
}

/* the one status on the list that is a fact rather than a promise */
.pill--live {
  color: var(--amber-hi);
  border-color: var(--amber-dim);
  background: rgba(215, 154, 95, 0.09);
}
/* The dot is the one green on the site. Amber is the house colour
   and is used for emphasis everywhere, so an amber dot read as more
   emphasis rather than as a state. Green reads as live. */
.pill--live::before {
  content: '';
  width: 0.4rem; height: 0.4rem;
  border-radius: 50%;
  background: var(--live);
  animation: pip 2.6s var(--ease-soft) infinite;
}
@keyframes pip {
  0%, 100% { opacity: 1;    box-shadow: 0 0 0 0 rgba(122, 197, 143, 0.55); }
  50%      { opacity: 0.55; box-shadow: 0 0 0 5px rgba(122, 197, 143, 0); }
}

/* ---- the arrival ------------------------------------------------
   Each card is held back until it is asked for. The resting state
   lives here rather than in the script so that a card is never
   briefly visible before the motion layer takes hold; the script
   lifts .shelf--armed once it is ready, and a page without GSAP
   never arms it and shows all five outright.
   ---------------------------------------------------------------- */

.shelf--armed .tile {
  opacity: 0;
  transform: translate3d(0, 120px, 0) scale(0.92);
  transform-origin: 50% 100%;
  will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {
  /* stacked in one cell and faded, these six were a blank space here.
     Laid out as a list they say the same thing without moving. */
  .destinations {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4em 1em;
    height: auto;
  }
  .destinations__word {
    position: static;
    font-size: clamp(1.1rem, 2vw, 1.5rem);
    opacity: 1 !important;
    transform: none !important;
  }
  .destinations::after { display: none; }

  .pill--live::before { animation: none; }
  .tile, .tile:hover, .tile__art img, .cover--blank { transition: none; transform: none; }
  .shelf--armed .tile { opacity: 1; transform: none; }
}

/* ============================================================
   GIVE
   The one section that asks for something, so it is the one section
   that stops. The frame is held while the six destinations pass
   through, then releases.
   ============================================================ */

/* An ordinary section again.

   It used to run 230vh so the six destinations could be dragged past
   by the scrollbar. They now count themselves off on a timer, so the
   runway was 130vh of empty scrolling holding nothing. */
.give {
  position: relative;
  z-index: var(--z-body);
  padding-block: var(--bay);
}

.give__frame {
  position: relative;
  display: flex;
  align-items: center;
  overflow: hidden;
}
/* The warm wash spans the section, padding included.

   It used to sit on the inner frame, which was sticky and a full
   screen tall, so it always covered the viewport. The frame is now
   only as tall as its content, and inset:0 on it painted a band
   across the middle of the section with hard edges above and below
   it. That seam is what read as wrong spacing: the gap was the
   site's usual 176px, but the background changed colour halfway
   through it. */
.give::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(58% 62% at 50% 88%, rgba(215, 154, 95, 0.14), transparent 72%),
    linear-gradient(180deg, rgba(23, 19, 15, 0) 0%, rgba(23, 19, 15, 0.5) 55%, rgba(23, 19, 15, 0) 100%);
}
.give__frame { position: relative; z-index: 1; }

.give__inner {
  position: relative;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ---- two columns -------------------------------------------------
   The argument reads on the left; the campaign sits on the right.
   One column each, and the panel is given a fixed measure so a piece
   of third party markup can never decide how wide this section is.
   ------------------------------------------------------------------ */
.give__grid {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4.5rem);
  align-items: center;
}
@media (min-width: 62rem) {
  .give__grid { grid-template-columns: minmax(0, 1fr) 25rem; }
}
@media (min-width: 78rem) {
  .give__grid { grid-template-columns: minmax(0, 1fr) 27rem; }
}

.give__words { max-width: 34rem; }

/* the panel keeps the site's corner and a hairline, so it reads as an
   inset belonging to this page rather than a page from another one */
.give__side {
  display: grid;
  gap: 1.1rem;
  align-content: center;
}

.give__panel {
  width: 100%;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--hairline);
  background: rgba(236, 226, 212, 0.03);
  box-shadow: 0 24px 60px -30px rgba(0, 0, 0, 0.9);
}
.give__panel .gfm-embed { display: block; line-height: 0; }
.give__panel iframe { display: block; width: 100% !important; border: 0; }

/* below the split the two stack, and the words centre again so a
   phone reads it as one column rather than a broken layout */
/* The stacked version of the split. Half of this used to live here
   and did nothing: `.destinations` and `.assure` are both declared
   further down this file at the same specificity, so source order
   handed them back their left alignment and the section read as
   centred type with two left aligned strays in it. The rules that
   lost are now in the mobile block at the foot of the stylesheet,
   after every base rule they have to beat. */
@media (max-width: 61.99rem) {
  .give__words { max-width: 40rem; margin-inline: auto; text-align: center; }
  .give__side { max-width: 25rem; margin-inline: auto; }
  .give__note { margin-inline: auto; }
}

.give__marker { margin-bottom: clamp(1.2rem, 2.6vh, 1.8rem); }
/* The headline is set to its column, not to the viewport.

   It used to run the full width of a centred section. In the left
   half of a split it wraps to four lines at the old size, and the
   column then stands taller than the screen holding it, which clips
   the first line under the masthead. */
.give__title {
  color: var(--cream);
  margin-bottom: 1.4rem;
}
@media (min-width: 62rem) {
  /* Sized down from the site's d-2 because the column is half as wide,
     but not this far down: at 3.5rem it was smaller than the single
     destination word underneath it, and a section heading that loses
     to its own subheading reads as a mistake. */
  .give__title { font-size: clamp(3rem, 4.4vw, 4.4rem); }
}

.give__say {
  max-width: 34rem;
  font-size: clamp(1.1rem, 1.02rem + 0.4vw, 1.32rem);
  line-height: 1.65;
  font-weight: 350;
  color: var(--haze);
  text-wrap: pretty;
}
.give__say em { font-style: italic; color: var(--cream); }

/* ---- the six ----------------------------------------------------
   One line, one place, counted off as the reader is held. They stack
   in the same cell so the block never changes height and nothing
   below it moves while the sequence runs.
   ---------------------------------------------------------------- */

.destinations {
  position: relative;
  display: grid;
  align-items: center;
  justify-items: start;
  height: clamp(3.6rem, 8vh, 5.2rem);
  margin-block: clamp(1.5rem, 3.4vh, 2.4rem);
}

.destinations__word {
  grid-area: 1 / 1;
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(1.9rem, 4vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.018em;
  color: var(--cream);
  white-space: nowrap;

  opacity: 0;
  transform: translateY(0.6em);
  /* slower and further than a scrubbed reveal wanted. The scrollbar
     used to supply the sense of movement; now the word has to carry
     it on its own. */
  transition: opacity var(--t-reveal) var(--ease), transform var(--t-reveal) var(--ease);
}
.destinations__word[data-on='true'] {
  opacity: 1;
  transform: none;
  /* waits for the last one to clear */
  transition-delay: 170ms;
}
/* leaving is quicker than arriving. With both at the same speed the
   outgoing word was still legible while the next one arrived, and two
   destinations were readable at once in the same spot. */
.destinations__word[data-on='past'],
.destinations__word[data-on='false'] {
  transition-duration: 230ms;
  transition-delay: 0ms;
}
/* the one that has just passed leaves upward, so the sequence reads
   as a list being counted rather than a word being swapped */
.destinations__word[data-on='past'] {
  opacity: 0;
  transform: translateY(-0.6em);
}

/* How far through the six.

   A full-width track with a fill inside it, rather than a bar that
   scales. The old rule centred itself twice, once with translateX
   and again with a negative margin, so the whole thing sat a full
   width to the left and appeared to run out at the middle of the
   section. Painting the fill as a gradient also means the unfilled
   remainder stays visible, which is what makes it read as a
   progress line rather than a growing underline. */
.destinations::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -1.1rem;
  width: clamp(7rem, 16vw, 13rem);
  height: 1px;
  background:
    linear-gradient(90deg,
      var(--amber) 0 calc(var(--counted, 0) * 100%),
      rgba(215, 154, 95, 0.16) calc(var(--counted, 0) * 100%) 100%);
  transition: background 260ms var(--ease-soft);
}

.give__act { margin-top: clamp(2.4rem, 5vh, 3.6rem); }

/* When GoFundMe's panel renders, this button is a second door to the
   same room and it goes away. When the panel is blocked, which ad
   blockers do routinely to embeds from donation platforms, it is the
   only way to give and it stays. Set by give() in main.js. */
.give__act[data-spare='true'] .btn { display: none; }

/* the one button the whole section exists for, so it is allowed to
   be the largest control on the site */
/* .btn--give is set with the other button variants, above */

/* This line was set as fine print, which was right when it read
   "online giving is not open yet" and had to apologise quietly. Now it
   carries the most persuasive fact on the page, so it is set to be
   read: upright rather than italic, larger, and in the reading cream
   instead of the grey used for asides. */
/* The one piece of arithmetic on the page, given a body.

   Three things were wrong with the first version. As an inline-block
   holding a sentence that wraps, the box stretched to the full column
   and left dead space to the right of the short second line. The
   sentence was set in the reading face at a light weight, which is
   drawn for long paragraphs and goes weak in a small panel. And the
   amount, the only number that matters here, was the same size as the
   words around it.

   Now: a fixed measure so both lines fill it, the figure set large in
   the display face, and the sentence in the interface face, which is
   drawn to stay crisp at this size. */
.give__note {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  width: fit-content;
  max-width: 23rem;
  margin-bottom: 1.7rem;
  padding: 0.95rem 1.3rem;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: rgba(236, 226, 212, 0.04);
  font-style: normal;
}

/* The figure is set in the interface face at a true bold, not in the
   display face at a hairline. Cormorant's old style numerals are
   beautiful and completely wrong for a price: they are drawn to sit
   inside a sentence, and this number has to be read at a glance.

   font-synthesis is switched off deliberately. If the real 700 ever
   goes missing from the file the number will simply render light
   rather than being faked into a smear, which is a failure you can
   see and fix. */
.give__note b {
  flex: 0 0 auto;
  font-family: var(--micro);
  font-weight: 700;
  font-synthesis-weight: none;
  font-size: 2.1rem;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--amber-hi);
}

.give__note span {
  font-family: var(--micro);
  font-weight: 400;
  font-size: 0.82rem;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--cream);
}

/* the apology it reverts to when no campaign is connected */
.give__note:not([data-live='true']) {
  display: block;
  max-width: 30rem;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--text);
  font-size: 0.95rem;
  font-style: italic;
  font-weight: 350;
  color: var(--haze);
  margin: 1.4rem 0 0;
}
/* ---- the tally ---------------------------------------------------
   The same rule the destinations counter uses, carrying a different
   measurement. It sits under the button rather than beside it, quiet
   enough that it reports progress without becoming a second ask.
   ---------------------------------------------------------------- */

.tally {
  width: 100%;
  max-width: 20rem;
  margin: clamp(1.8rem, 4vh, 2.6rem) auto 0;
}
.tally[hidden] { display: none; }

.tally__bar {
  height: 2px;
  border-radius: 2px;
  background: rgba(236, 226, 212, 0.12);
  overflow: hidden;
}
.tally__bar i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 2px;
  background: var(--amber);
  transition: width var(--t-reveal) var(--ease);
}

.tally__line {
  margin-top: 0.85rem;
  font-family: var(--micro);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mist);
  font-variant-numeric: tabular-nums;
}
.tally__line b { font-weight: 500; color: var(--amber-hi); }
.tally__dot { margin-inline: 0.35rem; opacity: 0.6; }

/* ---- the assurance ----------------------------------------------
   Small on purpose. It is not persuasion, it is the removal of a
   doubt: the reader has already decided they might give, and this
   answers the last question standing in the way. Set at caption
   size so it reassures without becoming another pitch.
   ---------------------------------------------------------------- */

.assure {
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 0.55rem;
  width: 100%;
  margin: 0;

  font-family: var(--micro);
  font-size: 0.72rem;
  line-height: 1.55;
  letter-spacing: 0.02em;
  color: var(--mist);
  text-align: left;
}

.assure__mark {
  flex: 0 0 auto;
  width: 1.05rem;
  height: 1.05rem;
  margin-top: 0.06rem;
  color: var(--amber);
}

.assure a {
  color: var(--haze);
  padding-bottom: 1px;
  background-image: linear-gradient(var(--amber-dim), var(--amber-dim));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 1px;
  transition: color var(--t-control) var(--ease), background-size var(--t-control) var(--ease);
}
.assure a:hover { color: var(--amber-hi); background-size: 100% 2px; }

@media (max-width: 47.99rem) {
  /* min-height: 200vh used to sit here. It was the runway the six
     destinations needed back when they were dragged past the reader
     by the scrollbar. They have counted themselves off on a timer
     since, and the runway stayed: 1688px of section holding 962px of
     content, so the page ran on for most of a screen after the
     shield line with nothing in it. */

  /* The box has to be a fixed height or the section jumps as the six
     words swap through it, but 5.5rem was more than twice the height
     of the word standing in it. The word sat centred with 28px of
     nothing under it, then the rule 18px below that: 46px between a
     word and its own underline, against 29px between the word and
     the paragraph above. That is the uneven gap.

     Now the box is the line box plus a hair, so the rule's own
     offset is the whole distance, and the margins carry the spacing
     that used to leak out of the box. */
  .destinations {
    height: 2.3rem;
    margin-block: 1.8rem 2.7rem;
  }
  .destinations::after { bottom: -0.85rem; }
}

/* ============================================================
   JOURNAL  (staggered editorial contents)
   ============================================================ */

.journal { position: relative; z-index: var(--z-body); }

.journal__head {
  display: grid;
  gap: 1.4rem;
  max-width: 44rem;
  margin-bottom: clamp(3rem, 7vw, 5rem);
}

/* ---- the gallery ------------------------------------------------
   Ten entries travelling left to right while the section holds still.
   The wrapper is pinned and the track is scrubbed, so vertical scroll
   becomes horizontal travel: the reading gesture for a shelf.
   ---------------------------------------------------------------- */

.gallery { position: relative; }

.gallery__viewport {
  overflow: hidden;
  width: 100%;
}

.gallery__track {
  display: flex;
  /* wider than it was. Four cards in a 1200px viewport with 37px
     between them read as traffic rather than as a shelf. */
  gap: clamp(2.4rem, 4vw, 3.75rem);
  width: max-content;
  /* No leading indent. The viewport already sits on the shell's
     content edge, so any padding here pushed the first card out of
     line with the heading above it. */
  padding-inline: 0;
}

.entry {
  flex: 0 0 auto;
  width: clamp(17rem, 25vw, 24rem);
  display: block;
  transition: background var(--t-control) var(--ease);
}

.entry__plate {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  /* wide rather than tall: these are landscape photographs, and a
     portrait crop was throwing away the part of the frame that
     carries the picture */
  aspect-ratio: 4 / 3;
  background: var(--storm);
  margin-bottom: 1.2rem;
}
.entry__plate img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* the plate leans in as the card is approached, which is the only
     hover the gallery needs */
  transition: transform var(--t-panel) var(--ease), filter var(--t-panel) var(--ease);
  filter: brightness(0.86) saturate(0.94);
}
.entry:hover .entry__plate img,
.entry:focus-visible .entry__plate img {
  transform: scale(1.045);
  filter: brightness(1) saturate(1);
}

/* a hairline that draws itself under the card being considered */
.entry__plate::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--amber);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-panel) var(--ease);
}
.entry:hover .entry__plate::after,
.entry:focus-visible .entry__plate::after { transform: scaleX(1); }

/* A card with no artwork yet. Not a grey box and not a spinner: the
   kicker, set large, in the space the picture will occupy. It reads as
   a typographic decision, and it goes on reading that way if the
   picture never arrives. */
.entry__plate--type {
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: linear-gradient(160deg, rgba(236,226,212,0.05), rgba(236,226,212,0.015));
  border: 1px solid var(--hairline-2);
}
.entry__plate--type span {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  letter-spacing: -0.012em;
  color: var(--haze);
  text-align: center;
  text-wrap: balance;
}
.entry--typeset:hover .entry__plate--type { border-color: var(--amber-dim); }
.entry__plate--type::after { display: none; }

.entry__body { display: block; }

.entry__meta {
  display: flex;
  gap: 1.1rem;
  align-items: center;
  margin-bottom: 0.9rem;
  font-family: var(--micro);
  font-size: 0.62rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mist);
}
.entry__meta b { color: var(--amber); font-weight: 500; }

.entry__title {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(1.4rem, 1.9vw, 1.75rem);
  line-height: 1.16;
  letter-spacing: -0.012em;
  color: var(--cream);
  margin-bottom: 0.6rem;
  transition: color var(--t-control) var(--ease);
  text-wrap: balance;
}
.entry:hover .entry__title { color: var(--amber-hi); }

.entry__stand {
  /* Held to two lines. Some standfirsts ran to two and some to
     three, so the bottom of the row was ragged and the whole shelf
     read as clutter. Every card now occupies the same height. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 0.95rem;
  line-height: 1.6;
  font-weight: 350;
  /* the card standfirst is read, not skimmed past, so it takes the
     body grey rather than the caption grey */
  color: var(--haze);
  text-wrap: pretty;
}

/* how far there is left to travel */
.gallery__rail {
  position: relative;
  height: 1px;
  margin: clamp(1.6rem, 3vh, 2.4rem) var(--gutter) 0;
  background: var(--hairline-2);
}
.gallery__rail i {
  position: absolute;
  inset: 0 auto 0 0;
  width: 100%;
  background: var(--amber);
  transform: scaleX(var(--travelled, 0));
  transform-origin: left;
}

.journal__more { margin-top: clamp(3rem, 6vw, 4.5rem); }

/* ---- how the shelf behaves at each size -------------------------

   Wide: the shelf is pinned and panned. It is centred in its held
   frame and masked at both edges so it reads as continuing past the
   window rather than stopping at it.

   Narrow: a pinned horizontal pan fights the one gesture a phone
   actually has. The shelf lies down and becomes a list.
   ---------------------------------------------------------------- */

@media (min-width: 54rem) {
  /* The runway has to sit on the sticky element's own parent, not on
     the section. A sticky child can only travel as far as its parent
     box is tall, and .shell hugs its content.

     Named, not selected by position. `.journal > .shell` also matched
     the tail that carries the closing link, which handed it 320vh of
     empty runway and opened a three-screen hole above the footer. */
  .journal__runway { min-height: 320vh; }

  /* The stage is what holds, and it holds the title with the shelf.

     Sticking the shelf alone looked correct in the stylesheet and
     wrong on the page: the shelf carries min-height:100dvh and
     centres its contents, so before it reached the top of the screen
     its cards sat half a viewport below the title. A heading with a
     void under it. Held together, the two arrive as one composition
     and the gap cannot open. */
  .journal__stage {
    position: sticky;
    top: 0;
    height: 100dvh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(1.4rem, 2.6vh, 2.4rem);
  }
  .journal__stage .journal__head { margin-bottom: 0; }
  /* The link belongs to the held composition. Sitting it outside the
     runway meant it appeared below whatever empty space was left at
     the bottom of a centred full-height stage, and that space grows
     with the window: a tall screen opened a gap most of a screen
     deep between the shelf and the way out of it. */
  .journal__stage .journal__more { margin-top: 0; }

  /* Inside a box of fixed height, width has to answer to height too.
     21vw alone overflowed the stage on every laptop shorter than
     982px, and an overflowing centred box loses the top of the
     heading and the bottom of the shelf at the same time. */
  .journal__stage .entry { width: clamp(15rem, min(25vw, 40vh), 24rem); }

  /* Both edges fade, but the leading one only once the shelf is
     moving.

     A fixed leading fade washed out the left of the first card and
     made the shelf look like it began somewhere short of the
     heading. A fade that is not there until the first card starts
     to leave gets both: aligned at rest, and cards that dissolve
     out rather than being cut off. --lead is driven by the scroll
     progress in gallery(). */
  .gallery__viewport {
    --lead: 0%;
    --tail: 94%;
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--lead), #000 var(--tail), transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0, #000 var(--lead), #000 var(--tail), transparent 100%);
  }

  /* the index page is a shelf you can see all of at once */
  /* An explicit count rather than auto-fill. auto-fill depends on the
     reader's root font size, which is not something to leave a page
     composition resting on: at 20px default it silently drops a
     column and the index reads twice as large as intended. */
  .contents--index {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(2.8rem, 4.5vw, 4rem) clamp(1.6rem, 2.6vw, 2.6rem);
    border-top: 0;
  }
  .contents--index .entry { width: auto; }
}

@media (min-width: 76rem) {
  .contents--index { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* A short desktop window has no room to spare inside a stage that
   cannot grow. Give back the height in the order it is least missed:
   the gaps first, then the card size, then the standfirst. */
@media (min-width: 54rem) and (max-height: 52rem) {
  .journal__stage { gap: clamp(1rem, 2.1vh, 1.7rem); }
  .journal__stage .entry { width: clamp(14rem, min(25vw, 37vh), 24rem); }
  .journal__stage .journal__head { gap: 0.9rem; }
}
@media (min-width: 54rem) and (max-height: 47rem) {
  .journal__stage .journal__head .lede { display: none; }
}

@media (max-width: 53.99rem) {
  /* Three, not ten.

     The shelf lies down into a list at this width, and ten cards is
     then a very long scroll through the same shape over and over
     before the page continues. Three is a taste; the link under
     them is where the archive lives. Nothing is hidden from the
     reader, it is one tap further on. */
  .gallery__track .entry:nth-child(n+4) { display: none; }

  .gallery { min-height: 0; }
  .gallery__viewport { overflow: visible; }
  .gallery__track {
    flex-direction: column;
    width: auto;
    padding-inline: 0;
    gap: clamp(2.5rem, 7vw, 3.5rem);
    transform: none !important;
  }
  .gallery__rail { display: none; }
  .entry { width: auto; }
  .entry__plate { aspect-ratio: 16 / 10; }
  .contents--index {
    display: grid;
    gap: clamp(2.5rem, 7vw, 3.5rem);
    border-top: 0;
  }
}

/* ============================================================
   INVITATION  (newsletter)
   ============================================================ */

.invitation { position: relative; z-index: var(--z-body); }

/* Aligned at the top, not the bottom.

   Bottom aligning made sense when the form was a single underlined
   line: it sat on the same baseline as the last line of the prose.
   The form is a field, a button and a note now, and bottom aligning
   a taller column pushed its first control above the headline it
   belongs to, so the eye met the input before the invitation. */
.invitation__grid {
  display: grid;
  gap: clamp(2.2rem, 5vw, 4rem);
  align-items: start;
  padding-top: clamp(3rem, 6vw, 4.5rem);
  border-top: 1px solid var(--hairline);
}
@media (min-width: 56rem) {
  .invitation__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr); }
}

/* A form heading, not a statement.

   This and the closing line below it were both d-3 and both landed at
   exactly 50px, left aligned, one above the other, with 112px between
   them. Two headings of identical rank in the same column read as
   congestion: nothing tells the eye which one is the point. This one
   is functional and steps down; the line underneath is the site's last
   word and keeps its size. */
.invitation__title {
  font-size: clamp(1.65rem, 1.3rem + 1.3vw, 2.5rem);
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--cream);
  margin-bottom: 1.2rem;
}

.signup { display: grid; gap: 1rem; }

/* Off screen rather than display:none. Some bots skip anything that is
   not rendered, and a field they skip catches nothing. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
/* ---- the field ---------------------------------------------------
   This was an underlined line of text with a bare amber word at the
   end of it. It read as a caption rather than as a form: on a phone
   the word had 35px and the input had 291, and the two together
   looked like one broken control.

   A real field and a real button, stacked, at every width. The field
   carries the site's own surface, hairline and corner, and the
   button IS the site's button — .btn in the markup rather than a
   second thing styled to resemble it, so it inherits the amber chip,
   the light that passes across it and the press.
   ------------------------------------------------------------------ */
.signup__field {
  display: grid;
  gap: 0.85rem;
}

/* Stacked is right on a phone, where a full width target is worth
   more than the proportion. Given a real column it is not: an amber
   chip stretched to 540px is a lot of very low contrast area, and it
   stops reading as a button and starts reading as a bar. Side by
   side from here, with the field taking the slack. */
@media (min-width: 48rem) {
  .signup__field {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: stretch;
    gap: 0.7rem;
  }
  .signup button { width: auto; padding-inline: 2.1rem; }
}
.signup__field input {
  width: 100%;
  min-height: 3.25rem;
  padding: 0.85rem 1.1rem;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: rgba(236, 226, 212, 0.03);
  outline: none;
  font-family: var(--text);
  /* Set larger than the body copy around it, not smaller.

     16px is only the floor — below it iOS zooms the page on focus and
     leaves the reader zoomed in. But a form field is the one place on
     the site where somebody has to read back what they themselves
     typed, letter by letter, to check it, and at 16.8px an address
     full of dots and @ was hard work for anyone who needs reading
     glasses. */
  font-size: 1.18rem;
  font-weight: 300;
  color: var(--cream);
  letter-spacing: 0.01em;
  transition: border-color var(--t-control) var(--ease),
              background var(--t-control) var(--ease);
}
.signup__field input::placeholder { color: var(--mist); opacity: 1; }
.signup__field input:focus {
  border-color: var(--amber);
  background: rgba(236, 226, 212, 0.055);
}
.signup button { width: 100%; }
.signup__help {
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--haze);
}
.signup__status {
  font-family: var(--micro);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  color: var(--amber-hi);
  min-height: 1.2rem;
}
/* It reserves a row so the note below it does not jump when a message
   arrives. With nothing to say it should not also reserve the gap. */
.signup__status:empty { min-height: 0; margin-top: -0.4rem; }
.signup__status[data-tone='error'] { color: #e8a08a; }

/* ============================================================
   FOOTER
   ============================================================ */

.foot {
  position: relative;
  z-index: var(--z-body);
  padding-block: clamp(4rem, 8vw, 6rem) 2.5rem;
}
/* The gap between the invitation above and this grid used to be an
   inline style on the element, at up to 11rem. Both blocks are inside
   the same footer and belong to each other; 11rem of nothing read as
   the page having ended twice. */
.foot__grid {
  display: grid;
  gap: clamp(2.2rem, 5vw, 3.5rem);
  margin-top: clamp(4rem, 8vw, 7rem);
  padding-bottom: 3rem;
}

/* Two link columns that keep their proportion, and the closing line
   taking the measure it was set for. The link columns are narrower
   than the 0.7fr they had, because "Email" and "The journal" do not
   need 200px and the space belongs to the sentence on the left. */
@media (min-width: 50rem) {
  .foot__grid { grid-template-columns: minmax(0, 1.45fr) repeat(2, minmax(0, 0.6fr)); }
}

.foot h2 { color: var(--cream); margin-bottom: 1.2rem; }
.foot__say h2 { max-width: 22ch; }

/* ---- the column labels ------------------------------------------
   These were mist: the value this site uses for things the reader is
   not meant to stop on. Set one step quieter than the links beneath
   them, they read as the least important thing in their own column,
   and the column had no top edge at all.

   Amber, over a hairline, is the same treatment the section kickers
   get further up the page — "THE COLLECTION", "GIVE A BOOK". A label
   in this typeface at this colour already means "what follows is a
   list of one kind of thing", and the footer now says it too.
   ------------------------------------------------------------------ */
.foot__col h3 {
  font-family: var(--micro);
  font-weight: 500;
  font-size: 0.6875rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--amber);
  padding-bottom: 0.85rem;
  margin-bottom: 1.15rem;
  border-bottom: 1px solid var(--hairline-2);
}

.foot__col ul { list-style: none; padding: 0; display: grid; gap: 0.55rem; }

/* Lifted from haze to cream, and given the same underline that grows
   under every other text link on the site, so a footer link looks
   like something to press rather than like small print. */
.foot__col a {
  display: inline-block;
  font-size: 1rem;
  font-weight: 300;
  color: var(--cream);
  padding-bottom: 2px;
  background-image: linear-gradient(var(--amber), var(--amber));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition: color var(--t-control) var(--ease),
              background-size var(--t-control) var(--ease);
}
.foot__col a:hover,
.foot__col a:focus-visible {
  color: var(--amber-hi);
  background-size: 100% 1px;
}

.foot__base {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  justify-content: space-between;
  align-items: center;
  padding-top: 2rem;
  border-top: 1px solid var(--hairline-2);
  font-family: var(--micro);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  color: var(--mist);
  font-variant-numeric: tabular-nums;
}
/* The credit is a signature. It was set at mist on a dark ground,
   which is the value used for things a reader is not meant to stop
   on, and it disappeared. */
.foot__base a {
  color: var(--cream);
  padding-bottom: 0.15rem;
  background-image: linear-gradient(var(--amber-dim), var(--amber-dim));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 1px;
  transition: color var(--t-control) var(--ease), background-size var(--t-control) var(--ease);
}
.foot__base a:hover { color: var(--amber-hi); background-size: 100% 2px; }
.foot__base > span:last-child { color: var(--haze); }
.foot__base a:hover { color: var(--amber-hi); }

/* ============================================================
   MOBILE NAVIGATION
   ============================================================ */

@media (max-width: 52rem) {
  /* the panel is fixed and later in the DOM, so the wordmark and the
     close control have to be lifted above it or they vanish when open */
  .wordmark, .nav-toggle { position: relative; z-index: 2; }

  .nav-toggle {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 40px; height: 40px;
    margin-right: -8px;
    background: none;
    border: 0;
    cursor: pointer;
    padding: 0 8px;
  }
  .nav-toggle span {
    display: block;
    height: 1px;
    background: var(--cream);
    transition: transform var(--t-control) var(--ease), opacity var(--t-press) var(--ease);
  }
  .nav-toggle[aria-expanded='true'] span:nth-child(1) { transform: translateY(3px) rotate(45deg); }
  .nav-toggle[aria-expanded='true'] span:nth-child(2) { opacity: 0; }
  .nav-toggle[aria-expanded='true'] span:nth-child(3) { transform: translateY(-3px) rotate(-45deg); }

  .nav {
    position: fixed;
    inset: 0;
    z-index: 1;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 0;
    padding: 5rem var(--gutter) 0;
    background: rgba(6, 7, 10, 0.97);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--t-panel) var(--ease), visibility var(--t-panel);
  }
  .nav[data-open='true'] { opacity: 1; visibility: visible; }
  /* in the panel it is the one button among a list of links, so it
     keeps the button voice rather than inheriting the display serif */
  .nav a.nav-cta {
    align-self: stretch;
    margin-top: 1.8rem;
    padding: 1.05rem 1.2rem;
    font-family: var(--micro);
    font-weight: 500;
    font-size: 0.78rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    text-align: center;
  }

  .nav a {
    font-family: var(--display);
    font-size: 2.1rem;
    letter-spacing: 0.02em;
    text-transform: none;
    color: var(--cream);
    padding-block: 0.55rem;
  }
  .nav a::after { display: none; }
}

/* ============================================================
   RESPONSIVE RECOMPOSITION
   ============================================================ */

@media (max-width: 61.99rem) {
  .object-seq { min-height: 280vh; }
  .object-seq__frame { align-content: center; gap: clamp(1.5rem, 4vh, 2.5rem); }
  /* --cover-w, not width.

     Setting `width` here resized the box and left every face inside
     it still built from --cover-w, which stayed at its 11rem floor.
     The boards came out 179.4px wide while the fore edge and spine
     were still translated to ±88px, half of 176. The cream page
     block therefore sat 1.7px inside the boards, and as the book
     turned that gap showed as a hard pale line down the edge and a
     spine that did not meet its own covers.

     One custom property drives all six faces. Change it and the
     object stays an object. */
  .tome { --cover-w: min(14.5rem, 46vw); width: var(--cover-w); }
  .turnstile { min-height: 19rem; }
  .turnstile__index { margin-bottom: 1.4rem; }
  .turnstile__panel .prose { margin-bottom: 1.4rem; }
  .colophon { margin-top: 1.6rem; padding-top: 1.4rem; gap: 1rem 1.5rem; }
}

/* the two column hero holds together down to about 990px; below
   that both columns go narrow at once and the frame goes hollow, so
   the stacked composition takes over earlier than the usual phone
   breakpoint. */
@media (max-width: 61.99rem) {
  /* Recomposed, not shrunk. One column, and the pair slots between
     the title and the copy so the object still arrives before the
     reader is asked to do anything. display:contents dissolves the
     lead block so its children can be ordered around the books. */
  .hero { padding-block: 5rem 3rem; }
  .hero__stage { grid-template-columns: minmax(0, 1fr); gap: 0; justify-items: start; }
  .hero__lead { display: contents; }
  .hero__marker { order: 1; margin-bottom: 1.1rem; }
  .hero__title {
    order: 2;
    font-size: clamp(3.2rem, 13vw, 6.5rem);
    letter-spacing: 0.03em;
    line-height: 0.94;
  }
  /* the cluster derives its box from the front copy, so the size is
     set there rather than on the container */
  .hero__books {
    order: 3;
    --front-w: min(16rem, 52vw);
    justify-self: center;
    margin: -0.4rem auto 0;
  }
  .hero__book--back { top: 19%; rotate: -12deg; }
  .hero__say { order: 4; max-width: 26rem; }
  .hero__actions { order: 5; }

  .contents > :nth-child(even) { transform: none; }
  .about__portrait { margin-inline: calc(var(--gutter) * -1); }
  /* the glow may only overhang vertically here. The portrait is
     already full bleed at this width, so the -14% horizontal reach
     had nothing left to reach into and pushed 55px of horizontal
     scroll onto every phone. */
  .about__portrait::before { inset-inline: 0; }
  .grain { opacity: 0.3; }
}

@media (max-width: 30rem) {
  /* the wordmark shares the bar with a menu button here, and at the
     desktop size it was taking nearly two thirds of the width */
  .wordmark { font-size: 0.8rem; letter-spacing: 0.16em; }
  .btn { padding: 0.95rem 1.6rem; font-size: 0.74rem; letter-spacing: 0.15em; }
  /* only on a phone, where a full width target is worth more than
     the proportion. On a tablet it just reads as a stretched box. */
  .hero__actions { width: 100%; }
  .hero__actions .btn { flex: 1 1 auto; }
}

/* ============================================================
   REDUCED MOTION
   Everything collapses to a still, legible page. No canvas, no
   pinning, no drift.
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .lift > * { transform: none !important; }
  .drift { opacity: 1 !important; transform: none !important; }
  .atmosphere__rain { display: none; }
  .grain { animation: none; }
  .passage .w { color: var(--cream) !important; }
  /* The turn becomes three plain sections: the object, then each
     panel in reading order. Nothing sticks, nothing overlays. */
  .object-seq { min-height: 0; }
  .object-seq__frame {
    position: static;
    min-height: 0;
    grid-template-columns: minmax(0, 1fr);
    padding-block: var(--bay);
  }
  .book { margin-bottom: 3rem; }
  .tome { margin-bottom: 0; }
  .tome__ground { display: none; }
  .turnstile { display: grid; gap: clamp(3rem, 6vw, 4.5rem); }
  .turnstile__panel {
    opacity: 1 !important;
    transform: none !important;
    grid-area: auto;
    pointer-events: auto;
    max-width: 40rem;
  }
  .turnstile__index { display: none; }

  /* the header cannot rely on a scroll driven background here */
  .masthead {
    background: rgba(8, 9, 12, 0.94);
    border-bottom-color: var(--hairline-2);
  }

  /* a settled version of the weather rather than a cold one */
  .atmosphere__plate--lamp { opacity: 0.5; }
  .atmosphere__plate--dawn { opacity: 0.22; }
  .tome__gloss { display: none; }
}

/* ============================================================
   BACK TO TOP
   Bottom right, out of the way of the reading column, and silent
   until it has something to offer. The ring reports progress
   through the page, so the control answers "how much is left"
   as well as "take me back".
   ============================================================ */

.totop {
  position: fixed;
  right: clamp(1rem, 2.5vw, 2rem);
  bottom: clamp(1rem, 2.5vw, 2rem);
  z-index: 60;

  display: grid;
  place-items: center;
  width: 3.1rem;
  height: 3.1rem;
  padding: 0;
  cursor: pointer;
  border: 0;
  border-radius: 50%;
  background: rgba(12, 10, 8, 0.72);
  -webkit-backdrop-filter: blur(9px) saturate(1.1);
  backdrop-filter: blur(9px) saturate(1.1);
  box-shadow: 0 6px 26px -8px rgba(0, 0, 0, 0.8);

  opacity: 0;
  transform: translateY(12px) scale(0.9);
  transition: opacity var(--t-panel) var(--ease),
              transform var(--t-panel) var(--ease),
              box-shadow var(--t-panel) var(--ease);
}
.totop[hidden] { display: none; }
.totop[data-shown='true'] { opacity: 1; transform: none; }

.totop:hover {
  box-shadow: 0 0 30px -6px rgba(215, 154, 95, 0.6);
}
.totop:active { transform: translateY(1px); }

/* the ring */
.totop__ring {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  transform: rotate(-90deg);          /* start the fill at twelve o'clock */
  overflow: visible;
}
.totop__ring circle {
  fill: none;
  stroke-width: 1.5;
}
.totop__track { stroke: rgba(236, 226, 212, 0.14); }
.totop__fill {
  stroke: var(--amber);
  stroke-linecap: round;
  /* 2 * pi * r, r = 20 */
  stroke-dasharray: 125.66;
  stroke-dashoffset: calc(125.66 - (125.66 * var(--read, 0)));
  transition: stroke-dashoffset 120ms linear;
}

/* the arrow, drawn rather than typed so it matches the hairlines */
.totop__arrow {
  position: relative;
  width: 0.55rem; height: 0.55rem;
  margin-top: 2px;
  border-top: 1.5px solid var(--cream);
  border-left: 1.5px solid var(--cream);
  transform: rotate(45deg);
  transition: border-color var(--t-control) var(--ease), transform var(--t-control) var(--ease);
}
.totop:hover .totop__arrow {
  border-color: var(--amber-hi);
  transform: rotate(45deg) translate(2px, 2px);
}

@media (prefers-reduced-motion: reduce) {
  .totop { transition: opacity var(--t-control) linear; transform: none; }
  .totop[data-shown='true'] { transform: none; }
  .totop__fill { transition: none; }
  .totop:hover .totop__arrow { transform: rotate(45deg); }
}

/* ============================================================
   THE CURTAIN
   The name, then the site.

   Visible by default rather than revealed by script. A cover that
   only appears once JavaScript says so produces a flash of the page
   underneath it, and the whole point is that the name is the first
   thing. The risk of that choice is a cover that never leaves, so
   curtain() in main.js removes it on every failure path there is:
   no GSAP, reduced motion, a hidden tab, an already seen session,
   and a wall clock failsafe. A page with no script at all hides it
   outright, from the noscript block in the markup.
   ============================================================ */

.curtain {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: grid;
  place-items: center;
  background: var(--abyss);
}

/* the page does not move while it is covered */
body.is-curtained { overflow: hidden; }

.curtain__inner {
  display: grid;
  justify-items: center;
  gap: clamp(1.1rem, 2.4vw, 1.8rem);
  padding-inline: var(--gutter);
}

.curtain__name {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: 0.32em;

  font-family: var(--display);
  font-weight: 300;
  /* One line, always. The size is driven by the viewport rather
     than by a fixed floor, so the name scales down to fit a phone
     instead of breaking across two lines. */
  font-size: clamp(1.3rem, 6.6vw, 4.4rem);
  line-height: 1.06;
  letter-spacing: 0.06em;
  white-space: nowrap;
  text-transform: uppercase;
  color: var(--cream);
  text-align: center;
}
/* each word rides in from behind its own edge */
.curtain__name .m {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
.curtain__name .m > span { display: inline-block; }

.curtain__rule {
  width: clamp(5rem, 14vw, 11rem);
  height: 1px;
  background: var(--amber);
  transform: scaleX(0);
  transform-origin: 50% 50%;
}

@media (prefers-reduced-motion: reduce) {
  .curtain { display: none; }
}

/* ============================================================
   THE PHONE
   ============================================================
   Most of this site's readers arrive by pointing a camera at the
   QR code printed in the back of the book, which means most of
   them arrive here, on a screen about 390 points wide held in one
   hand.

   Everything above this line is written mobile first, so the phone
   has always been getting the base rules and the wide screen has
   been getting the considered ones. That is backwards for this
   audience. This block is the phone's own layer: it runs last, so
   it settles every argument with a base rule declared further up,
   and it is the only place a phone specific decision is made.

   Three rules held throughout:

     NOTHING TOUCHES THE BAR. The masthead is fixed and 72px tall,
     and --bar is that number. Anything that scrolls under it gets
     a scrim; anything that stops under it gets clearance.

     NOTHING IS SMALLER THAN THE FINGER. 44px is the floor for a
     target, and there were controls here at 32.

     NOTHING IS CENTRED BY HALVES. A centred section centres all
     of itself, including the parts that read as captions.
   ============================================================ */

@media (max-width: 61.99rem) {

  /* ---- the bar --------------------------------------------------
     It had no ground under it until the reader scrolled far enough
     to trigger the lifted state, so on the first screen the wordmark
     sat directly on the hero and the two read as one block. Worse,
     mid page it laid caps over running text with nothing between.

     A scrim rather than a second solid bar: opaque under the type,
     gone by the bottom edge, so the page still passes under it
     rather than stopping at a line. It hands over to the real
     backdrop as soon as the lifted state arrives.
     -------------------------------------------------------------- */
  .masthead::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(180deg,
      rgba(8, 9, 12, 0.94) 0%,
      rgba(8, 9, 12, 0.78) 52%,
      rgba(8, 9, 12, 0) 100%);
    transition: opacity var(--t-panel) var(--ease);
  }
  .masthead[data-lifted='true']::after { opacity: 0; }

  /* ---- the first screen ----------------------------------------
     5rem of padding against a 72px bar left 8px between the two.
     The marker now starts a clear line below it. */
  .hero { padding-block: calc(var(--bar) + 2.6rem) 3rem; }

  /* ---- give -----------------------------------------------------
     The rules that source order took away. The section centres, so
     the destination word and the shield line centre with it. */
  .destinations { justify-items: center; }
  .destinations::after { left: 50%; transform: translateX(-50%); }
  .give__note { width: 100%; max-width: 26rem; }

  /* The shield above the line rather than beside it.

     justify-content alone did nothing visible: the sentence wraps to
     two lines and fills the column, so centring the flex row centred
     a box that was already full width and the text stayed hard left
     under a centred section. Stacked, the mark reads as a seal on the
     statement and the statement centres with everything else. */
  .assure {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.5rem;
    max-width: 27rem;
    margin-inline: auto;
    text-wrap: pretty;
  }
  .assure__mark { width: 1.15rem; height: 1.15rem; margin-top: 0; }

  /* GoFundMe's medium card is a lot shorter than the large one, so
     the panel no longer needs a 25rem cap to keep it in proportion
     with the argument above it. */
  .give__side { max-width: 27rem; }

  /* ---- the object ----------------------------------------------
     The frame is sticky at top:0, so its own padding is the only
     thing holding the book off the bar. clamp(5rem, 10vh, 7rem)
     resolved to 84px against a 72px bar: the book stopped 12px
     under it, which is the collision the reader sees as the book
     arriving late and then jumping.
     -------------------------------------------------------------- */
  .tome { margin-bottom: clamp(1.2rem, 3vh, 2rem); }
}

/* Only where the frame is actually sticky. Under reduced motion the
   sequence is three plain sections and takes the ordinary bay. */
@media (max-width: 61.99rem) and (prefers-reduced-motion: no-preference) {
  .object-seq__frame {
    padding-top: calc(var(--bar) + 1.4rem);
    padding-bottom: calc(1.6rem + env(safe-area-inset-bottom));
  }
}

@media (max-width: 47.99rem) {

  /* ---- the object, on a phone ----------------------------------
     The frame is sticky and one screen tall, so everything in it
     has to be legible at the same instant: the book, the heading,
     the paragraph and the button. It came to 938px in an 844px
     viewport, and the last 94px of every panel sat permanently
     below the fold while the book turned above it. The reader could
     watch the animation or finish the sentence, never both.

     The book gives up the most because it has already been seen:
     the hero opens with two copies of it at nearly twice this size.
     Here it is the thing being turned, not the thing being shown.
     -------------------------------------------------------------- */
  .tome { --cover-w: min(10.5rem, 38vw); }

  /* d-2 is a poster size. In a 342px column it set this heading in
     four lines of 46px and took a third of the screen before the
     first word of the paragraph. */
  .turnstile__panel h2 {
    font-size: clamp(1.7rem, 7.2vw, 2.3rem);
    line-height: 1.14;
    letter-spacing: -0.008em;
  }
  .turnstile__panel h2,
  .turnstile__panel h3 { margin-bottom: 1rem; }
  .turnstile__panel .prose p { font-size: 0.98rem; line-height: 1.62; }
  .turnstile__panel .prose { margin-bottom: 1.2rem; }
  .turnstile__panel p[style] { margin-top: 1.4rem !important; }
  .turnstile { min-height: 17rem; }
  .colophon { margin-top: 1.2rem; padding-top: 1.1rem; gap: 0.8rem 1.4rem; }

  /* ---- the portrait ---------------------------------------------
     It bled the full 390px and stood 374px tall, so the face alone
     was half a screen and the reader met it before the name. The
     feather is a proportion of the image, so it survives being
     smaller; the photograph just stops being furniture.
     -------------------------------------------------------------- */
  .about__portrait { width: min(76%, 19rem); margin-inline: auto; }
  /* with room either side again, the glow can reach sideways the way
     it does on a wide screen without pushing the page out */
  .about__portrait::before { inset-inline: -8%; }

  /* Measured, the gaps either side of "Faith. Perseverance. Hope."
     were 38px and 35px — near enough equal on paper, and visibly
     not equal on the page. The paragraph above sets at a 1.72 line
     height and carries about 9px of leading below its last line;
     the social row underneath is a bordered box that starts exactly
     where it says it does. So the real distances were 54px above
     and 48px below.

     Six pixels off the top closes it. This is an optical correction,
     which is why the number is not round. */
  .about__sign { margin-top: 2rem; }

  /* ---- targets --------------------------------------------------
     44px is the floor. What was here: buttons at 40, the newsletter
     field at 38, and Join at 35 by 32.
     -------------------------------------------------------------- */
  .btn { font-size: 0.78rem; }

  /* a footer link at 27px is a link you aim at */
  .foot__col ul { gap: 0.25rem; }
  .foot__col a { font-size: 1.02rem; }
  /* Read and Reach side by side rather than one under the other.

     Stacked, six links at a comfortable touch height ran to most of
     a screen on their own and the footer became a third of the page.
     Beside each other they read as what they are — two short lists —
     and the whole block is half as tall. The closing line above them
     keeps the full width. */
  .foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.6rem 1.5rem; }
  .foot__grid > :first-child { grid-column: 1 / -1; }
  .foot__col h3 { font-size: 0.72rem; letter-spacing: 0.18em; }
  .foot__col a { font-size: 0.95rem; }
  .foot__base { font-size: 0.78rem; gap: 0.6rem 1.5rem; letter-spacing: 0.06em; }

  /* Room under the last line for the button that floats over it.
     The scroll-to-top control is fixed 16px off the bottom and is
     50px across, so at the very end of the page it was sitting on
     top of the credit. 40px of footer padding could not clear it. */
  .foot { padding-bottom: calc(5.5rem + env(safe-area-inset-bottom)); }

  /* the status line reserves a row whether or not it has anything to
     say, which opened a hole between Join and the note under it */
  .signup { gap: 0.8rem; }
  .signup__status:empty { min-height: 0; }

  /* ---- micro type -----------------------------------------------
     The interface face was running at 9.9 to 11.5px here. These are
     labels, not fine print, and a phone is held further from the eye
     than a laptop screen is. */
  .colophon dt { font-size: 0.75rem; }
  .entry__meta { font-size: 0.76rem; letter-spacing: 0.16em; }
  .tlink { font-size: 0.76rem; }
  .turnstile__index { font-size: 0.74rem; }
  .assure { font-size: 0.78rem; }
  .marker, .give__marker { font-size: 0.76rem; }

}

/* A short phone. The sticky frame has to hold a book, a heading, a
   paragraph and a button on one screen, and below about 730px the
   printed facts are what gives. They are reference, not argument,
   and the button beneath them goes to the page that carries them. */
@media (max-width: 47.99rem) and (max-height: 46rem) {
  .colophon { display: none; }
  .tome { --cover-w: min(8.5rem, 32vw); }
  .turnstile__panel h2 { font-size: clamp(1.5rem, 6.4vw, 2rem); }
}

/* ============================================================
   ANY FINGER
   ============================================================
   Target sizes belong to the input device, not to the width of
   the window. These were written into the phone block first, and
   an iPad in portrait — 768px, one pixel outside it — got none of
   them: a 27px wordmark, a 40px menu button, seven footer links at
   27px, all being aimed at with a thumb.

   `pointer: coarse` is the actual question being asked. It is true
   on a phone, on a tablet, and on a touchscreen laptop, and false
   for a mouse at any width, which is why the desktop keeps the
   tighter spacing it was designed with.

   44px is the floor. Inline links inside running prose are exempt:
   the GoFundMe link in the shield line and the credit in the footer
   sit in sentences, and padding them to 44 would break the leading
   of the paragraph around them for a target nobody hunts for.
   ============================================================ */

@media (pointer: coarse) {
  .btn { min-height: 3rem; padding-block: 1rem; }
  .nav-cta { min-height: 2.75rem; display: inline-flex; align-items: center; }

  /* the wordmark was a 22px strip of text inside a 72px bar, so most
     of what looks like the logo was not the link */
  .wordmark { display: inline-flex; align-items: center; min-height: 44px; }
  .nav-toggle { width: 44px; height: 44px; margin-right: -10px; }

  .tlink { display: inline-block; padding-block: 0.8rem; }
  .foot__col ul { gap: 0.25rem; }
  .foot__col a { display: inline-block; padding-block: 0.6rem; }

  /* The horizontal nav, which a tablet in landscape still gets.

     Padded evenly rather than only at the top, so the label stays
     optically centred in the bar. The underline is drawn on ::after
     at bottom:0, so it is lifted by the same amount the padding
     grew and ends up exactly where it sat before. */
  .nav a { padding-block: 0.78rem; }
  .nav a::after { bottom: 0.38rem; }
  .foot__base a { display: inline-block; padding-block: 0.35rem; }
  .signup button { min-height: 3rem; }

  /* 16px from the bottom edge put this inside the home indicator's
     gesture area, where a tap swipes the reader out of the page */
  .totop {
    bottom: calc(1rem + env(safe-area-inset-bottom));
    right: calc(1rem + env(safe-area-inset-right));
  }

  /* nothing here has a cursor to be approached by, and a hover state
     left standing after a tap is a control that looks stuck on */
  .btn:hover, .btn--quiet:hover, .tlink:hover,
  .entry:hover .entry__plate img { transition-duration: var(--t-press); }
}
