/* ============================================================
   EDITORIAL — the locked article template
   Authority: _design/editorial-contract.md. Every colour and
   type value below is measured, not chosen; if you change one,
   change the contract too or they drift apart.

   WHY THIS IS A SHARED STYLESHEET AND SLOT 4 PAGES ARE NOT
   Each Slot 4 project "is its own website" — different colours,
   type and imagery per project — so those pages inline their
   own <style> and share only a structure. Editorial is the
   opposite: it is a masthead. Every piece must look like the
   same publication, so a fix belongs in one file, not four.
   Per-piece variation is the token flip at the bottom, and
   nothing else.

   THE GROUND IS SLATE, NOT BLACK, AND THAT IS LOAD-BEARING
   His Gathering of Nations frames run 27-72% dark pixels
   (median mean-luminance ~85). On a pure-black page they lose
   their edge and become floating highlights with no boundary.
   #152632 frames every photograph without drawing a border,
   and reads cool against imagery that averages warm browns
   (#5c4946, #442f33, #765b42), which makes the warmth advance.
   ============================================================ */

.article {
  /* dark ground — ratios against #152632, all measured */
  --ground:      #152632;
  --paper:       #f2efe9;  /* 13.50  display type            */
  --body:        #d8d3c9;  /* 10.39  prose                   */
  --muted:       #a9a196;  /*  6.07  captions, credits       */
  --accent:      #d07e14;  /*  4.93  HIS masthead orange     */
  --accent-lift: #e39a3d;  /*  6.61  accent over photography */
  --dim:         #7d8a93;  /*  4.37  LARGE TEXT ONLY         */
  --rule:        #2a3f4e;  /*  1.42  HAIRLINES ONLY          */

  --display: "Libre Baskerville", Baskerville, "Times New Roman", serif;
  --mono: "Geist Mono", "SF Mono", ui-monospace, monospace;

  /* Nelson, measured: 24px / 36px / 480px. 24 is bigger than
     instinct suggests — most sites set 17 and read as a blog.
     The size IS the luxury; do not shrink it to fit more in. */
  --size-body: 24px;
  --lh-body: 1.5;
  /* MEASURED, not inherited from the reference. The contract recorded the
     reference's 480px column and inferred "~55ch" from it — that inference
     was wrong for this face. Nelson runs macOS Baskerville; Libre Baskerville
     is materially wider, and 480px at 24px sets only ~36 characters, which
     reads choppy and ragged. 44rem (704px) probes at 54 characters, inside
     the comfortable band for a serif at this size. Contract updated to match. */
  --measure: 44rem;

  --gap: clamp(3rem, 7vw, 7rem);

  /* HEIGHT-CAPPED ON PURPOSE — an Editorial-only reversal of the
     "full bleed means full bleed" rule, made at the user's request
     on 2026-08-15: "The images are so large that the constant scroll
     to see some of them is a turnoff to keep going."
     That earlier instruction was given about Slot 4 project pages,
     where the images sit inside a reader column and a 4:3 frame is
     ~1080px once. Here every frame is viewport-WIDE, so a portrait
     rendered 2160px tall and cost two full screens of scrolling
     each, eight times in a row. Capping the window is what makes
     the piece readable; the parallax above is what keeps the frame
     feeling alive rather than merely cropped. */
  --frame-h: 90svh;

  background: var(--ground);
  color: var(--body);
  font-family: var(--display);
  font-weight: 400;
}

/* ---------- reset-ish ---------- */
.article *, .article *::before, .article *::after { box-sizing: border-box; }
.article img, .article video { display: block; max-width: 100%; }

/* ============================================================
   COVER — A SEQUENCE, NOT A STILL

   The first build cropped the cover to fill the viewport, which
   showed a DETAIL of a curated frame and called it a cover. The
   photographs are edits he composed; the cover now opens with the
   whole frame and only fills the screen once the reader commits
   to scrolling.

     scroll 0     image contained, whole, on the ground colour
     scrolling    scales up toward full bleed
     ~65% through ombre fades in and the titling arrives
     100%         a full-bleed cover; scrolling on enters the story

   Driven from the same rAF as everything else, no library. With
   JavaScript off, `.cover` never gets `is-live` and every element
   below renders in its FINAL state — filled, scrimmed, titled —
   because a legible cover matters more than the choreography.
   ============================================================ */
.cover {
  position: relative;
  height: 190svh;              /* the scroll distance for the sequence */
}
.cover__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
}

.cover__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--focal, 50% 50%);
}
/* Only once script is driving it does the image start contained. The
   static fallback stays object-fit:cover above, so a no-JS cover is
   the composed one rather than a small picture floating in a field. */
.cover.is-live .cover__media {
  object-fit: contain;
  object-position: 50% 50%;
  transform: scale(var(--cover-scale, 1));
  transform-origin: var(--focal, 50% 50%);
  will-change: transform;
}

/* The type sits directly on the photograph, so it needs its own
   ground. A scrim weighted to the bottom-left keeps faces and
   regalia in the upper frame untouched — the same corner-weighted
   logic as the Karmal card asset, done in CSS because here the
   photograph is shared with the index and must not be baked. */
.cover__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, rgb(21 38 50 / .92) 0%, rgb(21 38 50 / .55) 28%,
                    rgb(21 38 50 / 0) 62%),
    linear-gradient(to right, rgb(21 38 50 / .55) 0%, rgb(21 38 50 / 0) 55%);
}

.cover__in {
  position: relative;
  padding: var(--gap);
  padding-bottom: clamp(4rem, 10vh, 8rem);
  max-width: 60rem;
}
.cover.is-live .cover__scrim,
.cover.is-live .cover__in { opacity: var(--cover-type, 1); }

.cover__kicker {
  font-family: var(--mono);
  font-size: .75rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent-lift);
  margin: 0 0 1.5rem;
}

/* Display sets at 400, never 700. Libre Baskerville ships no bold
   italic (400/400i/700 only, no variable axis — verified against the
   Google Fonts API), so a 700 headline cannot carry the one-italic-
   word move below. The reference's own h1 computes 400. Large serif
   at regular weight IS the look; reaching for bold is the instinct
   to resist. */
.cover__title {
  font-weight: 400;
  font-size: clamp(2.75rem, 7vw, 6rem);
  line-height: 1.04;
  letter-spacing: -.015em;
  color: var(--paper);
  margin: 0;
  text-wrap: balance;
}
.cover__title em { font-style: italic; }

.cover__meta {
  font-family: var(--mono);
  font-size: .8125rem;
  line-height: 1.7;
  letter-spacing: .04em;
  color: var(--muted);
  margin: 2rem 0 0;
}
.cover__meta b { color: var(--body); font-weight: 400; }

/* ============================================================
   FOUR-CORNER FRAME + SCROLL PROGRESS
   The site's existing mono-label framing DNA, carried across so
   Editorial reads as the same house as the project pages.
   ============================================================ */
.frame {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 40;
  font-family: var(--mono);
  font-size: .6875rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
  mix-blend-mode: difference;
}
.frame > * { position: absolute; padding: 1.25rem 1.5rem; }
.frame__tl { top: 0; left: 0; }
.frame__tr { top: 0; right: 0; }
.frame__bl { bottom: 0; left: 0; }
.frame__br { bottom: 0; right: 0; }

/* Set by script; the static fallback is a dash, never a stale 0%. */
.frame__br::after { content: attr(data-progress); }

@media (max-width: 720px) {
  /* The frame goes entirely on a phone. There is no gutter at this width
     for a fixed label to live in, so every corner sits directly on top of
     the prose — mix-blend-mode keeps it legible but it still reads as two
     things printed over each other. Nothing is lost: the issue tag is on
     the cover and the credit is in the footer. A first attempt hid only
     two corners and kept the progress readout, which left exactly the same
     collision in two places instead of four. */
  .frame { display: none; }
}

/* ============================================================
   BODY
   ============================================================ */
.article__body {
  position: relative;
  padding: clamp(5rem, 14vh, 10rem) 0;
  display: flex;
  flex-direction: column;
  gap: var(--gap);
}

/* Prose is OFFSET, not centred. Centring a 480px column in a
   1440px viewport reads as a template; the offset reads as a
   page that was set. Nelson does the same. */
.prose {
  width: min(var(--measure), calc(100% - 3rem));
  margin-inline: clamp(1.5rem, 14vw, 15rem) auto;
  font-size: var(--size-body);
  line-height: var(--lh-body);
  color: var(--body);
}
/* Both rules are specificity (0,1,0) so source order decides and the second
   wins where it applies. Do NOT rewrite the first as `.prose p` — that is
   (0,1,1), it beats the sibling rule regardless of order, and every
   paragraph in a passage loses its spacing while still looking like it
   should have some. */
.prose > * { margin: 0; }
.prose > * + * { margin-top: 1.5em; }
.prose em { font-style: italic; }
.prose a { color: var(--accent); text-underline-offset: .2em; }

/* The opening paragraph carries the standfirst weight. */
.prose--lead {
  width: min(52rem, calc(100% - 3rem));
  font-size: clamp(1.375rem, 2.1vw, 1.75rem);
  line-height: 1.42;
  color: var(--paper);
}

/* A section marker, for the chaptered parts. */
.mark {
  font-family: var(--mono);
  font-size: .75rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--accent);
  width: min(var(--measure), calc(100% - 3rem));
  margin-inline: clamp(1.5rem, 14vw, 15rem) auto;
}
.mark::before {
  content: "";
  display: block;
  width: 3rem;
  border-top: 1px solid var(--rule);
  margin-bottom: 1.25rem;
}

/* ============================================================
   FRAMES — sizes are MEASURED, not chosen

   Every photograph is drawn at a size he set, and that size is
   recorded in the PDF. tools/pdf-image-rects.swift reads the
   transformation matrix in force at each image draw, which IS the
   placement rectangle, and the build writes it in as --pw / --ph.
   Four passes were rejected for picking heights out of the air;
   there was never anything to pick.

   His whole vocabulary is three sizes:
     full-page bleed  ~100% x 116%  -> 100vw x 100vh, covering
     full-width band  ~100% x  52%  -> 100vw x  52vh
     inset             41-93% x 44-64%  -> kept as measured

   A page clips what overflows it, so nothing here exceeds one
   screen in either axis. `object-fit: cover` on a frame whose
   proportions came from the source is not a crop we invented —
   it is the crop he drew.
   ============================================================ */

.bleed { margin: 0; width: 100%; }

.bleed figcaption {
  font-family: var(--mono);
  font-size: .75rem;
  line-height: 1.6;
  letter-spacing: .04em;
  color: var(--muted);
  padding: .875rem clamp(1.5rem, 5vw, 5rem) 0;
  max-width: 44rem;
}

/* wrap, or the figcaption becomes a flex SIBLING of the picture and
   steals width from it — measured at 61% of the viewport on a frame
   that had been told to be 100vw. */
.bleed--set { display: flex; flex-wrap: wrap; }
.bleed--set > figcaption { flex: 1 0 100%; }
.bleed--set[data-align="start"]  { justify-content: flex-start; }
.bleed--set[data-align="end"]    { justify-content: flex-end; }
.bleed--set[data-align="center"] { justify-content: center; }

/* NO CROPPING, AT ITS FULL WIDTH. User, 2026-08-16.
   The frame keeps its width — --pw, which is 100vw for a full-bleed or band
   frame — and takes whatever height that width implies for THIS photograph.
   Everything that could crop is gone: the fixed height, the overflow clip,
   and object-fit/object-position. There is nothing left to cut the picture
   against, so the frame is always whole and always as wide as it ever was. */
.win {
  position: relative;
  width: var(--pw, 100vw);
  max-width: 100%;
  will-change: transform;
  transform: translate3d(0, var(--shift, 0px), 0);
}
.win img, .win video {
  display: block;
  width: 100%;
  height: auto;
}

/* FULL-PAGE FRAME WITH A PASSAGE OVER IT — his page 3.
   The picture fills the screen and the writing sits in a translucent
   panel down one side, which is only legible because the panel is a
   flat ground rather than a gradient: a gradient is weakest exactly
   where the type starts. */
.bleed--over { position: relative; display: block; }
.bleed--over .win { width: 100vw; }
.bleed--over .side {
  position: absolute;
  top: 0; right: 0;
  width: min(34%, 30rem);
  height: 100%;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.1em;
  padding: clamp(2rem, 4vh, 4rem) clamp(1.5rem, 2.5vw, 2.75rem);
  background: rgb(12 20 27 / .66);
  backdrop-filter: blur(1px);
  color: #eae6de;
  font-size: clamp(.9375rem, 1.05vw, 1.0625rem);
  line-height: 1.6;
}
.bleed--over .side > * { margin: 0; }
.bleed--over .side__cap {
  font-family: var(--mono); font-size: .6875rem; line-height: 1.7;
  letter-spacing: .04em; color: var(--muted); margin-top: .5rem !important;
}
.bleed--over > figcaption { display: none; }

/* A frame that carries a passage beside it keeps its measured width
   and gives the remainder to the writing. */
.bleed--withtext { align-items: center; gap: clamp(2rem, 4vw, 4rem);
                   padding-right: clamp(1.5rem, 5vw, 5rem); }
.bleed--withtext { flex-wrap: nowrap; }
.bleed--withtext .win { flex: 0 0 auto; }
.bleed--withtext .side {
  flex: 1 1 0;
  max-width: 34rem;
  font-size: clamp(1rem, 1.2vw, 1.1875rem);
  line-height: 1.62;
  color: var(--body);
}
.bleed--withtext .side > * { margin: 0; }
.bleed--withtext .side > * + * { margin-top: 1.1em; }
.bleed--withtext .side__cap {
  font-family: var(--mono); font-size: .75rem; line-height: 1.7;
  letter-spacing: .04em; color: var(--muted);
  margin-top: 2rem !important; max-width: 26rem;
}
.bleed--withtext .side__cap::before {
  content: ""; display: block; width: 2.5rem;
  border-top: 1px solid var(--rule); margin-bottom: 1rem;
}

/* Two frames that belong together, at their measured sizes. */
.bleed--pair .plate { display: flex; gap: .75rem; align-items: flex-start; }
.bleed--pair .win { width: auto; flex: 1 1 0; }

/* A PAIR WHERE EACH FRAME CAPTIONS ITSELF.
   The cell, not the picture, is the flex child — the same shape as the
   figcaption fix on .bleed--set, and for the same reason: a caption that is
   a flex SIBLING of the picture steals width from it. Cells top-align, so a
   caption sits directly under its own frame however tall its neighbour is. */
.bleed--pair-cells .pcell { flex: 1 1 0; display: flex; flex-direction: column; min-width: 0; }
.bleed--pair-cells .pcell .win { width: 100%; flex: 0 0 auto; }
/* Flush with the frame's own left edge. The pair runs edge to edge, so the
   inset the prose captions carry would push this off its picture. */
.bleed--pair-cells .pcell > figcaption { padding-inline: 0; }

/* A PHOTOGRAPH SOMEONE ELSE SHOT CARRIES THEIR NAME ON THE FRAME.
   Set small and vertical up the frame's left edge, the way a print credit
   runs along a gutter. It is deliberately NOT in the figcaption: a caption
   is the story's voice and a credit is a rights statement, and merging them
   makes the photographer read as part of the reporting. */
.win .credit {
  position: absolute;
  left: .5rem;
  bottom: .625rem;
  z-index: 2;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--mono);
  font-size: 9px;
  line-height: 1;
  letter-spacing: .1em;
  color: #fff;
  /* the frames these sit on are photographs, so the name needs to hold on
     whatever tone happens to be in that corner */
  text-shadow: 0 1px 3px rgb(0 0 0 / .6);
  white-space: nowrap;
  pointer-events: none;
}

@media (max-width: 980px) {
  .bleed--set { flex-direction: column; }
  .bleed--over { display: flex; }
  .bleed--over .side {
    position: static; width: 100%; height: auto; background: none;
    backdrop-filter: none; color: var(--body); padding-inline: 1.5rem;
    font-size: var(--size-body); line-height: var(--lh-body);
  }
  .bleed--withtext { padding-right: 0; }
  .bleed--withtext .win { width: 100vw !important; }
  .bleed--withtext .side {
    max-width: none;
    width: min(var(--measure), calc(100% - 3rem));
    margin-inline: clamp(1.5rem, 8vw, 8rem) auto;
    font-size: var(--size-body); line-height: var(--lh-body);
  }
  .win { width: 100vw !important; }
  .bleed--pair .plate { flex-direction: column; }
  /* A CELL'S FRAME FILLS ITS CELL, NOT THE VIEWPORT.
     The blanket .win rule above is 100vw, which an ordinary pair absorbs
     because `flex: 1 1 0` lets the frame shrink below it. A cell's frame
     is `flex: 0 0 auto` and cannot, so it overhung the right edge by the
     plate's own inset - measured 398px of picture in a 390px viewport
     against 374px for every pair already shipping. */
  /* AND THE CELL MUST STRETCH TO THE PLATE'S WIDTH.
     Stacked, the plate is a COLUMN flex, so `align-items: flex-start`
     governs width and shrink-wraps each cell to its content - which
     leaves `width: 100%` on the frame inside resolving against nothing
     and falling back to the picture's own size. Measured 259px for the
     portrait beside 374px for the map, on the same edge. */
  .bleed--pair-cells .pcell { align-self: stretch; }
  .bleed--pair-cells .pcell .win { width: 100% !important; }
}

/* ============================================================
   PULL-QUOTE
   RESERVED FOR HIS SUBJECTS' VOICES, NOT HIS NARRATION.
   This is an editorial rule with a design consequence, not a
   style: the visual language here is borrowed from a body of
   work that was criticised for speaking over its subjects, and
   the fix is to let them hold the largest type on the page.
   See _design/editorial-contract.md §5.
   ============================================================ */
.quote {
  width: min(46rem, calc(100% - 3rem));
  margin-inline: clamp(1.5rem, 14vw, 15rem) auto;
  padding: 0;
  border: 0;
}
.quote blockquote {
  margin: 0;
  font-size: clamp(1.75rem, 3.4vw, 2.75rem);
  line-height: 1.24;
  color: var(--paper);
  text-wrap: pretty;
}
.quote cite {
  display: block;
  margin-top: 1.75rem;
  font-family: var(--mono);
  font-style: normal;
  font-size: .75rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent);
}

/* ============================================================
   AUDIO — a third-party player embed (Spotify), not a photograph.
   Same column measure as a pull-quote. Width is fluid; height is
   the platform's own generated dimension — see build-article.js.
   ============================================================ */
.audio {
  width: min(46rem, calc(100% - 3rem));
  margin-inline: clamp(1.5rem, 14vw, 15rem) auto;
}
.audio__frame { display: block; width: 100%; height: 102px; border: 0; }
.audio figcaption {
  font-family: var(--mono);
  font-size: .75rem;
  line-height: 1.6;
  letter-spacing: .04em;
  color: var(--muted);
  padding-top: .875rem;
}

/* ============================================================
   VIDEO
   Default is silent texture: a still that happens to move.
   One real film per piece gets controls and sound.
   ============================================================ */
.film video { width: 100%; height: auto; background: #000; }

/* THE PENDING STATE — the footage is shot but not yet cut, so the
   page holds the space rather than pretending the slot does not
   exist. It renders the poster photograph with a mono plate over
   it, so it reads as a designed placeholder and not as a broken
   embed. build-article.js prints every pending slot on each run,
   and drops this class the moment a real file is named. */
.film--pending { position: relative; }
.film--pending::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgb(21 38 50 / .58);
  pointer-events: none;
}
.film__plate {
  position: absolute;
  z-index: 2;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  font-family: var(--mono);
  font-size: .75rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--paper);
  padding: 1.5rem 2rem;
  border: 1px solid rgb(242 239 233 / .34);
  background: rgb(21 38 50 / .5);
  backdrop-filter: blur(2px);
  max-width: min(30rem, 82vw);
}
.film__plate b { display: block; color: var(--accent-lift); font-weight: 400; }
.film__plate span {
  display: block;
  margin-top: .75rem;
  letter-spacing: .04em;
  text-transform: none;
  color: var(--muted);
  font-size: .6875rem;
  line-height: 1.6;
}

/* ============================================================
   CLICK TO EXPAND — every in-body photograph, at the largest
   derivative already on the page. Never the cover sequence, never
   a video (its own controls already do that job) — scripts/editorial.js
   binds this to .win img only. A native <dialog> gets Escape and
   light-dismiss for free, so there is no focus trap to write by hand.
   ============================================================ */
.win img { cursor: zoom-in; }
.lightbox {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  max-width: 100vw;
  max-height: 100vh;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox::backdrop { background: rgb(10 16 20 / .94); }
.lightbox img {
  display: block;
  max-width: 92vw;
  max-height: 92vh;
  width: auto;
  height: auto;
  cursor: zoom-out;
}

/* ============================================================
   FOOT — prev/next is load-bearing here, not decoration.
   Sunflowers publishes as three linked parts of 367-725 words
   each (the user's decision); without chaptering they are three
   orphans and Part 3 especially cannot stand alone.
   ============================================================ */
.foot {
  border-top: 1px solid var(--rule);
  padding: clamp(3rem, 8vh, 6rem) clamp(1.5rem, 14vw, 15rem);
  display: grid;
  gap: 3rem;
}
.foot__nav { display: flex; flex-wrap: wrap; gap: 2rem 4rem; }
.foot__nav a { text-decoration: none; color: var(--body); max-width: 22rem; }
.foot__nav a:hover .foot__title { color: var(--accent); }
.foot__dir {
  font-family: var(--mono);
  font-size: .6875rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
  display: block;
  margin-bottom: .625rem;
}
.foot__title { font-size: 1.375rem; line-height: 1.3; transition: color .2s; }
.foot__credit {
  font-family: var(--mono);
  font-size: .75rem;
  line-height: 1.8;
  letter-spacing: .04em;
  color: var(--muted);
}
/* The separator between the two footer links. Faint on purpose — it is
   punctuation, not a third link. */
.foot__sep{color:var(--ink-fnt);padding:0 .15em}

.foot__credit a { color: var(--accent); }

/* ============================================================
   REVEALS — hidden by SCRIPT, never by CSS, so a CDN failure or
   a JS error degrades to a fully readable page rather than a
   blank one. Same rule as the Slot 4 pages.
   ============================================================ */
.is-armed .reveal { opacity: 0; transform: translateY(18px); }
.reveal { transition: opacity .9s ease, transform .9s ease; }
.reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .is-armed .reveal, .reveal { opacity: 1 !important; transform: none !important; }
  .reveal { transition: none; }
}

/* ============================================================
   PAPER FLIP — Bastimento
   Its imagery is daylight and spans the widest range in the set
   (mean-luminance 27 to 214). Same token-flip pattern proven on
   Danforth and Gaijin.

   THE `color` RE-DECLARATION BELOW IS NOT OPTIONAL.
   body{color:var(--body)} resolves --body in the BODY's scope —
   the dark value — and descendants inherit that already-computed
   colour, not a re-resolved var(). Without an explicit colour
   here, every element with no colour rule of its own keeps the
   dark theme. That is exactly the bug that put pale-green
   headings on Gaijin's paper reader.

   AND THE ACCENT CHANGES. #d07e14 measures 2.60:1 on this ground
   — decorative only, unusable as text. #8a5005 is the identical
   hue (33.8 degrees) deepened until it passes at 5.38:1.
   ============================================================ */
.article--paper {
  --ground:      #efe9df;
  --paper:       #151a1e;  /* 14.51 */
  --body:        #3f4a52;  /*  7.52 */
  --muted:       #5f6971;  /*  4.64 */
  --accent:      #8a5005;  /*  5.38 */
  --accent-lift: #8a5005;
  --dim:         #6b7278;  /*  4.04  large text only */
  --rule:        #dcd5c8;  /*  1.21  hairline only   */

  background: var(--ground);
  color: var(--body);      /* <- the line that must not be removed */
}
.article--paper .cover__scrim {
  background:
    linear-gradient(to top, rgb(21 26 30 / .82) 0%, rgb(21 26 30 / .42) 30%,
                    rgb(21 26 30 / 0) 64%),
    linear-gradient(to right, rgb(21 26 30 / .5) 0%, rgb(21 26 30 / 0) 55%);
}
/* The cover type still sits on a photograph, so it stays light
   even though the article below it is paper. */
.article--paper .cover__title { color: #f2efe9; }
.article--paper .cover__meta { color: #cfc8bc; }
.article--paper .cover__meta b { color: #f2efe9; }
.article--paper .cover__kicker { color: #e39a3d; }
.article--paper .film--pending::after { background: rgb(21 26 30 / .55); }

@media (max-width: 720px) {
  .article { --size-body: 20px; --frame-h: 70svh; }
  /* No sticky sequence on a phone: 190svh of scroll before the story
     starts is a wall, and the whole point of the cap above is fewer
     screens between the reader and the writing. */
  .cover { height: 100svh; }
  .cover__stage { position: relative; }
  .cover.is-live .cover__media { object-fit: cover; transform: none; }
  .cover.is-live .cover__scrim,
  .cover.is-live .cover__in { opacity: 1; }
  .prose, .mark, .quote { margin-inline: 1.5rem auto; }
  .foot { padding-inline: 1.5rem; }
}
