/* Circa8 — a risograph publication that happens to be a website.
 *
 * The essays argue that a record is imperfect, hand-made, and held by a person.
 * A site that set them in a centred column of system sans on pure white would
 * be arguing the opposite in the only language a reader actually notices first.
 *
 * Riso is the right press for that argument, and not only because violet,
 * gray and black are literally its ink drums. A riso lays one colour at a
 * time and never lands them perfectly on top of each other. The wordmark here
 * is printed three times in three inks, a hair out of register — which is the
 * thesis of the whole publication compressed into six letters: the record does
 * not line up. Nothing is randomised at runtime. Every imperfection is fixed
 * and deliberate, the way a printer's would be.
 *
 * The palette is split soft/deep on purpose. Violet and warm gray at full
 * softness are beautiful as ink and illegible as text, so the soft values fill
 * shapes and the deep values carry type. Both deep values clear 4.5:1 on paper.
 *
 * Tan was cut from the atmosphere and demoted to a card-only "parchment" —
 * a wash of it behind everything read as heritage-editorial rather than
 * riso. A warm stone wash joins violet and iris as the third atmosphere
 * colour, and one ink, flamingo (barbie pink at 75% alpha, set to `lighten`
 * on the marks that are pure pop with nothing else underneath them), exists
 * purely as a pop: it fails text contrast on paper by design and is used
 * only on shapes, borders and glows that never carry a word.
 */

/* ----------------------------------------------------------- the versal */

/* Royal (freeware, fontspace.com/royal-font-f1637), self-hosted because it is
 * not on any CDN. Subset to A–Z and packed to woff2: 104 KB of TTF down to
 * 40 KB, which is most of what an ornamental outline costs and is not going to
 * get much cheaper. It loads on demand — `unicode-range` means a page whose
 * essay opens on a letter outside A–Z never fetches it at all.
 *
 * The face has no lowercase. That is not a gap to work around, it is what the
 * font is for, so the only rule that uses it forces uppercase rather than
 * letting a stray lowercase opener drop silently to a fallback serif.
 *
 * `swap`: the versal is one decorative letter. A blank first character while a
 * 40 KB file arrives is a worse first impression than a plain one. */
@font-face {
  font-family: "Royal";
  src: url("fonts/royal-caps.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0041-005A;
}

/* ------------------------------------------------------------------ ink */

:root {
  --paper:        #fdfbf5;   /* warm ivory — the canvas */
  --paper-deep:   #f0ebe0;   /* parchment — artifacts and cards only, never atmosphere */
  --paper-edge:   #dcd5c6;
  --ink:          #161318;   /* near-black */
  --ink-soft:     #46424b;
  --ink-faint:    #635c6b;   /* muted plum — rules and metadata. 6.2:1 on paper */

  --accent:       #5f3daf;   /* violet, at type weight — 7.3:1 on paper */
  --accent-soft:  #c7b3ea;   /* violet, as ink         — fills only */
  --alt:          #6a6258;   /* warm gray, at type weight — 5.8:1 */
  --alt-soft:     #cfc6b8;   /* warm gray, as ink */

  --marker:       #e9ff3d;   /* highlighter — same in both themes */
  /* Flamingo: a pop, not a fourth text colour. Deliberately fails text
   * contrast, which is the guardrail that keeps it off type. Used only
   * where nothing has to be read: hover rings, peak markers, glows. Same
   * hex in every theme, like the marker — a pop doesn't dim for dark mode. */
  --pop:          #e0218abf;
  --hl-pink:      #e0218abf;   /* margin-hl's flamingo — brighter on dark stock, see below */
  --select:       #3ee8ff;   /* text-selection highlight — baby highlighter blue, same hex in every theme */

  --wash-violet:  #e2d9f3;
  --wash-iris:    #d9d6ec;
  --wash-stone:   #eae4d6;
  --glass:        rgba(253, 251, 245, .72);   /* the masthead's ground, sticky */

  --display: "Fraunces", "Hoefler Text", Georgia, serif;
  --body:    "Newsreader", "Iowan Old Style", Georgia, serif;
  --mark:    "Courier Prime", "Courier New", monospace;

  --measure: 33rem;
  --rule: 1px solid var(--paper-edge);

  /* Fraunces carries a WONK axis, which is a typeface admitting it is drawn by
   * hand. Used sparingly — headlines only — it reads as character. Used on body
   * copy it reads as a novelty font. */
  --wonk-on:  "SOFT" 28, "WONK" 1;
  --wonk-off: "SOFT" 12, "WONK" 0;
}

/* Dark is not an inversion. Riso on black stock is its own thing: the inks go
 * luminous, parchment drops to a warm shadow reserved for cards, and the
 * paper wash becomes a glow behind the type rather than a tint on top of it. */
@media (prefers-color-scheme: dark) {
  :root {
    --paper:      #121016;
    --paper-deep: #1b1723;
    --paper-edge: #4a4159;
    --ink:        #efecf2;
    --ink-soft:   #b3adba;
    --ink-faint:  #8d8496;
    --accent:      #b79df0;
    --accent-soft: #43306e;
    --alt:         #a89e90;
    --alt-soft:    #4a4438;
    --marker:      #e9ff3d;
    --pop:         #e0218abf;
    --hl-pink:     #ff3fb4e6;
    --select:      #3ee8ff;
    --wash-violet: #241d33;
    --wash-iris:   #221f31;
    --wash-stone:  #262117;
    --glass:      rgba(18, 16, 22, .68);
  }
}

:root[data-theme="dark"] {
  --paper:      #121016;
  --paper-deep: #1b1723;
  --paper-edge: #4a4159;
  --ink:        #efecf2;
  --ink-soft:   #b3adba;
  --ink-faint:  #8d8496;
  --accent:      #b79df0;
  --accent-soft: #43306e;
  --alt:         #a89e90;
  --alt-soft:    #4a4438;
  --marker:      #e9ff3d;
  --pop:         #e0218abf;
  --hl-pink:     #ff3fb4e6;
  --select:      #3ee8ff;
  --wash-violet: #241d33;
  --wash-iris:   #221f31;
  --wash-stone:  #262117;
  --glass:      rgba(18, 16, 22, .68);
}

:root[data-theme="light"] {
  --paper:      #fdfbf5;
  --paper-deep: #f0ebe0;
  --paper-edge: #dcd5c6;
  --ink:        #161318;
  --ink-soft:   #46424b;
  --ink-faint:  #635c6b;
  --accent:      #5f3daf;
  --accent-soft: #c7b3ea;
  --alt:         #6a6258;
  --alt-soft:    #cfc6b8;
  --marker:      #e9ff3d;
  --pop:         #e0218abf;
  --hl-pink:     #e0218abf;
  --select:      #3ee8ff;
  --wash-violet: #e2d9f3;
  --wash-iris:   #d9d6ec;
  --wash-stone:  #eae4d6;
  --glass:      rgba(253, 251, 245, .72);
}

/* --------------------------------------------------------------- ground */

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

/* Three ink washes bleeding in over ivory stock — violet, iris, and a warm
 * stone, the third replacing a tan bleed that read as heritage-editorial
 * rather than riso. They live on `.washes`, not here — see the comment below.
 *
 * Kept deliberately pale. A violet-to-gray gradient at full strength is the
 * single fastest way to make a site look machine-generated; at this weight it
 * reads as ink soaking into stock, which is the opposite impression. */
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 1.1875rem;
  line-height: 1.62;
  font-variant-numeric: oldstyle-num;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* The washes live on their own fixed layer, not on `body`, for one reason:
 * the breathe below animates `transform` on a composited element instead of
 * repainting the page background. Overscanned by 6% so the drift never shows
 * an edge. The 60s cycle is at the threshold of perception on purpose — ink
 * settling, not a screensaver. Reduced-motion is handled by the global
 * animation kill-switch at the bottom of this file. */
.washes {
  position: fixed;
  inset: -6%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(66% 46% at 4% -6%,   var(--wash-violet) 0%, transparent 66%),
    radial-gradient(62% 42% at 100% 4%,  var(--wash-iris) 0%, transparent 62%),
    radial-gradient(70% 40% at 88% 92%,  var(--wash-violet) 0%, transparent 64%),
    linear-gradient(176deg, var(--paper) 0%, var(--paper) 44%, var(--wash-stone) 100%);
  animation: breathe 60s ease-in-out infinite alternate;
}

@keyframes breathe {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(0, -1.1%, 0) scale(1.03); }
}

/* A printer's colour bar: the strip of ink chips a press operator leaves on
 * a proof to check each drum laid down. Five chips — violet, warm gray,
 * stone, flamingo, ink — running near the bottom margin like the sheet was
 * never trimmed. Solid colour, hard stops, no border: it is ink on the
 * stock, not a UI element. The chips take their values from the theme, so
 * on black stock they read as the luminous dark-drum set. Sits behind the
 * content plane, z-index -1, so it never competes with anything — it just
 * shows through the paper's own margin at every width, phone included. */
.colour-bar {
  position: fixed;
  right: 44px;
  bottom: 28px;
  z-index: -1;
  width: 11px;
  height: 92px;
  pointer-events: none;
  opacity: .8;
  background: linear-gradient(180deg,
    var(--accent-soft) 0 16px,    transparent 16px 19px,
    var(--alt-soft)    19px 35px, transparent 35px 38px,
    var(--wash-stone)   38px 54px, transparent 54px 57px,
    var(--pop)         57px 73px, transparent 73px 76px,
    var(--ink)         76px 92px);
}

@media (max-width: 900px) {
  .colour-bar { right: 24px; bottom: 18px; width: 9px; height: 78px; }
}

/* Riso stock has tooth, and riso ink sits on top of it rather than soaking in.
 * Without this the background is a smooth colour field and the whole conceit
 * collapses into "pastel gradient blog". Fixed, non-interactive, cheap. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: .34;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.86' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

@media (prefers-color-scheme: dark) {
  .grain { mix-blend-mode: screen; opacity: .11; }
}

:root[data-theme="dark"] .grain  { mix-blend-mode: screen;   opacity: .11; }
:root[data-theme="light"] .grain { mix-blend-mode: multiply; opacity: .34; }

/* Selecting text on the page — the one interaction still asking for a
 * genuinely bright baby-blue rather than the flamingo pop used everywhere
 * else. plus-lighter is the CSS name for additive/"linear dodge" blending —
 * it adds the highlight's channels onto whatever's underneath rather than
 * mixing, so the same hex reads true on both paper and ink stock. */
::selection { background: color-mix(in srgb, var(--select) 40%, transparent); color: var(--ink); text-shadow: none; mix-blend-mode: plus-lighter; }

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ------------------------------------------------------------- masthead */

/* Sticky and glassy: the wordmark and the Ledger link stay reachable while
 * an essay scrolls underneath, and the blur keeps text from running straight
 * under the bar unreadably rather than pretending the header is opaque paper
 * it isn't. */
.masthead {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
  overflow: hidden;
  --mh-pb: .7rem;
  padding: 1rem clamp(1.5rem, 5vw, 4.5rem) 0;
  border-bottom: var(--rule);
  background: var(--glass);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
}

.wordmark {
  position: relative;
  display: inline-block;
  text-decoration: none;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 700;
  font-size: clamp(1.7rem, 4.4vw, 2.6rem);
  letter-spacing: .14em;
  line-height: 1;
  margin-bottom: var(--mh-pb);
}

/* Out of register by a hair, the way a two-colour press drifts. This is the
 * one thing a visitor will remember, and it is the argument of the whole
 * publication compressed into six letters: the record does not line up. */
.wordmark-ink,
.wordmark-reg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transition: transform .5s cubic-bezier(.2,.7,.2,1);
}
/* Soft ink, not deep. A riso misregistration shows pale colour peeking out
 * from behind the black pass — deep values here just read as a drop shadow. */
.wordmark-ink  { color: var(--accent-soft); transform: translate(-.07em, .03em); }
.wordmark-reg  { color: var(--alt-soft);    transform: translate(.05em, -.025em); }
.wordmark-text { position: relative; display: block; color: var(--ink); }

/* The wordmark is an inline SVG now — "circa" in Italiana with the c8
 * monogram sitting in the 8's slot. currentColor throughout, so the three
 * press layers above tint it exactly as they tinted the text. Sized in em
 * so the clamp() on .wordmark's font-size still governs it. */
.wordmark svg { display: block; height: 1.1em; width: auto; }

.wordmark:hover .wordmark-ink { transform: translate(-.11em, .05em); }
.wordmark:hover .wordmark-reg { transform: translate(.085em, -.04em); }

.masthead-nav {
  display: flex;
  align-items: flex-end;
  gap: 1.1rem;
  font-family: var(--mark);
  font-size: .76rem;
  letter-spacing: .1em;
  text-transform: uppercase;
}

/* The nav link as the same document tab used on the index map: an outlined
 * chip, open on the bottom edge so it reads as sticking up out of the
 * masthead's own rule rather than a button floating in the bar. Sits flush
 * against that rule — everything else in the nav carries the masthead's
 * bottom padding as its own margin so the tab is the one thing touching it. */
.masthead-nav a {
  position: relative;
  text-decoration: none;
  white-space: nowrap;
  padding: .32rem .7rem .3rem;
  border: 1px solid var(--paper-edge);
  border-bottom: none;
  border-radius: .4rem .4rem 0 0;
  transform-origin: bottom center;
  transition: color .25s, border-color .25s, box-shadow .25s, background .25s,
    transform .2s cubic-bezier(.2, .8, .25, 1);
}
/* Home and Ledger read as one set of tabs — same size regardless of
 * label length, the way a real tab set is cut to one template. */
.tab-stack a {
  display: inline-block;
  min-width: 4.7rem;
  text-align: center;
}
.masthead-nav a.is-active {
  border-color: var(--accent);
  color: var(--ink);
  box-shadow: 0 0 1px var(--accent), 0 -2px 16px 1px var(--accent-soft);
}
.masthead-nav a:hover, .masthead-nav a:focus-visible {
  color: var(--pop);
  border-color: var(--pop);
  transform: scale(1.05);
}
.masthead-nav a.is-active:hover, .masthead-nav a.is-active:focus-visible {
  color: var(--ink);
  border-color: var(--accent);
  box-shadow: 0 0 1px var(--accent), 0 -2px 22px 3px var(--accent-soft);
}
/* The default focus ring is offset outward and square — on two tabs sitting
 * close together it bleeds across both and reads as broken. Inset it so it
 * hugs whichever tab actually has focus. */
.masthead-nav a:focus-visible { outline-offset: -3px; }

.tab-stack {
  display: flex;
  align-items: flex-end;
  gap: .4rem;
}

.nav-rule { width: 2.2rem; height: 1px; background: var(--paper-edge); margin-bottom: var(--mh-pb); }
.masthead-note { color: var(--ink-faint); font-style: italic; text-transform: none; letter-spacing: .04em; margin-bottom: var(--mh-pb); }

/* Below ~640px the note wraps mid-word and the rule floats between two
 * ragged lines — clutter next to a masthead this small. Drop both and
 * leave the short nav link on its own. */
@media (max-width: 40rem) {
  .masthead-note, .nav-rule { display: none; }
}

.masthead--compact { padding-top: .85rem; --mh-pb: .6rem; }
.masthead--compact .wordmark { font-size: clamp(1.2rem, 2.6vw, 1.55rem); }

/* ---------------------------------------------------------- the landing */

.hero {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 20rem);
  gap: clamp(2rem, 6vw, 5rem);
  align-items: start;
  padding: clamp(2.5rem, 6vw, 5rem) clamp(1.5rem, 5vw, 4.5rem) clamp(1.5rem, 4vw, 3rem);
  max-width: 82rem;
}

.hero-slug {
  grid-column: 1 / -1;
  margin: 0 0 1.8rem;
  font-family: var(--mark);
  font-size: .78rem;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--accent);
}
.hero-slug::after {
  content: "";
  display: inline-block;
  width: 4.5rem;
  height: 1px;
  margin-left: 1rem;
  vertical-align: .32em;
  background: var(--accent);
}
/* A real timestamp rather than a mood-setting label — the nostalgia this
 * page wants comes from evidence like this, not from a filter. */
.hero-time {
  margin-left: .6rem;
  letter-spacing: .08em;
  color: var(--ink-faint);
}

.hero-story { max-width: var(--measure); }
.hero-story p { margin: 0 0 1.15em; }
.hero-story .beat {
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-style: italic;
  font-size: 1.5rem;
  line-height: 1.32;
  margin: 1.7rem 0;
  padding-left: 1.3rem;
  border-left: 2px solid var(--accent);
  color: var(--ink);
}

/* ------------------------------------------------------- illuminated cap */

/* The decoration is in the letterform, not around it — a plain letter inside a
 * decorated frame reads as a sticker, so the letter has to be the ornament.
 *
 * Every initial the corpus actually uses (A I M O S T Y) was set in five
 * candidate faces and compared before this one was picked. The true texturas
 * lost on identifiability, not on beauty: Unifraktur's S is a G to anyone who
 * is reading rather than admiring. */
.dropcap {
  position: relative;
  float: left;
  font-family: "Royal", "Luminari", serif;
  font-weight: 400;
  /* Royal has a cap height of 820/1000 against the previous face's ~700, so
   * the same font-size would set a visibly larger letter. Sized down to hold
   * the two-line drop the essays were already set to. The ornament's tail
   * carries about a fifth of a line past that on purpose — it hangs into the
   * float's margin rather than pushing the text further right. */
  font-size: 3.75em;
  line-height: .86;
  color: var(--ink);
  margin: .04em .1em -.04em 0;
  /* The face has no lowercase at all. An essay that happened to open on one
   * would fall through to the fallback serif mid-word; this makes that
   * impossible rather than unlikely. */
  text-transform: uppercase;
  /* Ornamental caps carry a lot of side bearing; this pulls the paragraph
   * back against the glyph so it does not read as a floating ornament. */
  padding-right: .02em;
}

/* Second pass, printed a hair off — the same misregistration as the wordmark,
 * so the one ornamental moment in a piece belongs to the same press. */
.dropcap::before {
  content: attr(data-letter);
  position: absolute;
  inset: 0;
  color: var(--accent-soft);
  transform: translate(-.035em, .028em);
  z-index: -1;
}

/* The letter is drawn in the SVG; this copy exists only so the sentence a
 * screen reader announces still begins with its first letter. */
.versal-text {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (max-width: 34rem) {
  .dropcap { font-size: 4.3em; }
}

/* The reader's own note in the margin — set in the typewriter face, tipped a
 * degree, the way something added afterwards always is. */
.margin-note {
  position: relative;
  font-family: var(--mark);
  font-size: 1rem;
  line-height: 1.75;
  margin-top: 3.2rem;
}
.margin-mark { display: block; color: var(--ink-faint); margin-bottom: .45rem; }

/* The annotation is highlighted, not framed: marker ink laid over each line,
 * `box-decoration-break: clone` so every wrapped line gets its own strip.
 * Flamingo, additively blended (plus-lighter) instead of "lighten" — on dark
 * stock, lighten only shows a colour where it already beats the near-black
 * backdrop channel-for-channel, which read as a dim, muddy rose. Addition
 * shows the full colour regardless of what's underneath, which also means a
 * selection dragged across a strip mixes the two highlight colours instead
 * of one occluding the other. --hl-pink (not --pop) so this can run brighter
 * on dark stock without also blowing out --pop's other uses — hover rings,
 * peak markers, glows — which stay at the deliberately-contrast-failing
 * flamingo. Ink follows the theme rather than staying fixed, since the wash
 * itself is translucent enough to need real contrast from the text on top
 * of it. */
.margin-hl {
  background: color-mix(in srgb, var(--hl-pink) 40%, transparent);
  mix-blend-mode: plus-lighter;
  color: var(--ink);
  padding: .12em .4em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.declaration {
  position: relative;
  z-index: 2;
  border-top: var(--rule);
  padding: clamp(1.75rem, 4.5vw, 3rem) clamp(1.5rem, 5vw, 4.5rem) clamp(2rem, 5vw, 3.5rem);
  max-width: 82rem;
}
.declaration-line {
  margin: 0;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 600;
  font-size: clamp(2.5rem, 8.5vw, 6rem);
  line-height: .96;
  letter-spacing: -.022em;
  text-wrap: balance;
  max-width: 82rem;
}
.declaration-sub {
  margin: 1.2rem 0 0;
  max-width: 30rem;
  font-family: var(--body);
  font-size: 1.15rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.tuesday {
  position: relative;
  z-index: 2;
  background: color-mix(in srgb, var(--wash-stone) 55%, var(--paper) 45%);
  border-top: var(--rule);
  border-bottom: var(--rule);
  padding: clamp(2rem, 5vw, 3.5rem) clamp(1.5rem, 5vw, 4.5rem);
}
/* Text on the left, the Diff beside it on the right — the proof needs to
 * sit next to the claim, not underneath it three scrolls later. The Diff
 * column runs the full left-column height (spanning both grid rows) so the
 * spec rows tuck in below the explanation while the document stays one
 * unbroken piece beside them. Collapses to a single stacked column below
 * 60rem, in head → diff → frame order, matching the desktop reading path. */
.tuesday-inner {
  max-width: 82rem;
  display: grid;
  grid-template-columns: minmax(0, .85fr) minmax(0, 1.25fr);
  grid-template-areas: "head diff" "frame diff";
  column-gap: clamp(2.5rem, 5vw, 4rem);
  row-gap: 2rem;
  align-items: start;
}
.tuesday-head { grid-area: head; }
.tuesday-frame { grid-area: frame; }

@media (max-width: 60rem) {
  .tuesday-inner {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "head" "diff" "frame";
    row-gap: 2.2rem;
  }
}
.tuesday h2 {
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 500;
  font-size: clamp(1.9rem, 4.6vw, 3rem);
  line-height: 1.08;
  margin: .3rem 0 1rem;
  letter-spacing: -.015em;
}
.tuesday .lede {
  max-width: var(--measure);
  margin: 0 0 1.6rem;
  font-size: 1.24rem;
  color: var(--ink-soft);
}

/* The one breadcrumb of product clarity on the page. A first-time visitor
 * could otherwise read Circa8 as purely editorial; this is the single line
 * that says the rows below are a real interface rather than a mood board —
 * without turning into a features section. The flamingo rule is the pop
 * getting its one placement on the homepage, not the copy shouting it. */
.spec-tag {
  max-width: var(--measure);
  margin: 0 0 2.4rem;
  padding-left: 1rem;
  border-left: 2px solid var(--pop);
  font-family: var(--body);
  font-style: italic;
  font-size: 1.04rem;
  color: var(--ink-soft);
}

.ledger { border-top: 1px solid var(--paper-edge); }
.ledger-row {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: 1.5rem;
  padding: .95rem 0;
  border-bottom: 1px solid var(--paper-edge);
  align-items: baseline;
}
.ledger-k {
  font-family: var(--mark);
  font-size: .74rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--accent);
}
/* This is the specification, not a caption — it carries the sentence that
 * does the most work on the page, so it gets full ink rather than the
 * softened tone body copy uses. */
.ledger-v { font-size: 1.08rem; color: var(--ink); }

.doc-lede {
  margin: 2.2rem 0 1rem;
  font-family: var(--body);
  font-style: italic;
  font-size: 1.02rem;
  color: var(--ink-soft);
}

/* The one piece of actual product on the page — deliberately not a card.
 * Flat paper, square corners, a hairline edge and the same fixed hand-set
 * tilt the ledger's own nodes carry, so it reads as a sheet that exists
 * rather than a UI panel describing one.
 *
 * The paper is 60% opaque rather than solid — it lets whatever is behind it
 * show through a little, the way an actual sheet of paper does, and it means
 * the card settles into dark mode instead of sitting on it as a stark white
 * rectangle. Text still uses the page's own ink variables, so contrast holds
 * in both themes rather than being tuned for one and hoped for the other. */
.doc {
  max-width: 36rem;
  margin: 0 0 .5rem;
  padding: 1.7rem 1.9rem 1.9rem;
  background: color-mix(in srgb, var(--paper) 60%, transparent);
  border: 1px solid var(--paper-edge);
  box-shadow: 0 16px 36px -18px rgba(0,0,0,.55);
  transform: rotate(-.4deg);
}
/* This is the page's product proof, not one more editorial fixture — it
 * gets its own grid area beside the explanation (see .tuesday-inner) and a
 * scale bump to match: bigger padding, a wider cap, and every internal
 * size up roughly a step, so it reads as a document worth pausing on
 * rather than a specimen filed next to the copy that describes it. Still
 * capped, still paper, still tilted the same amount — bigger, not louder. */
.doc--diff {
  grid-area: diff;
  max-width: 48rem;
  padding: 2.1rem 2.4rem 2.4rem;
  align-self: start;
}

.doc-head {
  margin: 0 0 1.5rem;
  padding-bottom: 1.1rem;
  border-bottom: 1px solid var(--paper-edge);
}
.doc-diffid {
  margin: 0 0 .55rem;
  font-family: var(--mark);
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.doc-title {
  margin: 0;
  font-family: var(--mark);
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink);
}
.doc-span {
  margin: .35rem 0 0;
  font-family: var(--mark);
  font-size: .8rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
  color: var(--ink-soft);
}

.doc-when {
  margin: 0 0 .65rem;
  font-family: var(--mark);
  font-size: .78rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--accent);
}
/* Plain, not quoted — everything on this card is Circa8's own statement
 * about the record, not the person's exact words, so the italic "this is
 * what they said" voice used on the other card doesn't apply here. */
.doc-word {
  margin: 0 0 .55rem;
  font-family: var(--body);
  font-size: 1.1rem;
  line-height: 1.46;
  color: var(--ink);
}
.doc-src {
  margin: .55rem 0 0;
  font-family: var(--mark);
  font-size: .72rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: .03em;
  color: var(--ink-soft);
}

/* An outcome already on the record, not a dramatic beat — the weight comes
 * from being set like a stamped verdict (mono, tracked, capitalised),
 * never from size or colour. */
.doc-outcome {
  margin: .25rem 0 .65rem;
  font-family: var(--mark);
  font-weight: 700;
  font-size: .88rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink);
}
/* The repeated variables get the one spot of accent colour on the card —
 * they're the point of the whole comparison, so they earn it. Still type,
 * not a pill or a badge. */
.doc-vars {
  margin: .25rem 0 .65rem;
  font-family: var(--mark);
  font-weight: 600;
  font-size: .9rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--accent);
}

.doc-diff {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 2.2rem;
  padding: 1.7rem 0;
  border-bottom: 1px solid var(--paper-edge);
}
.doc-diff-col:last-child {
  padding-left: 2.2rem;
  border-left: 1px solid var(--paper-edge);
}

/* The comparison the whole card exists to set up — same two-column grid as
 * the diff above it, deliberately larger and quieter, with room on both
 * sides so it reads as the thing being demonstrated rather than one more
 * row of evidence. No italics, no quotation marks: a fact placed next to
 * another fact is the whole argument. */
.doc-pivot {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 2.2rem;
  padding: 2.1rem 0;
}
.doc-pivot > div:last-child {
  padding-left: 2.2rem;
  border-left: 1px solid var(--paper-edge);
}
.doc-pivot-line {
  margin: 0;
  font-family: var(--body);
  font-size: 1.32rem;
  line-height: 1.4;
  color: var(--ink);
}

/* Reads as a document's own footer — a count, not a claim. Same visual
 * register as the provenance lines above it, just centred under a rule,
 * the way a page number or a record count sits at the foot of a ledger. */
.doc-tally {
  margin: 0;
  padding-top: 1.1rem;
  border-top: 1px solid var(--paper-edge);
  text-align: center;
  font-family: var(--mark);
  font-size: .74rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: .05em;
  color: var(--ink-soft);
}

@media (max-width: 34rem) {
  .doc-diff, .doc-pivot {
    grid-template-columns: 1fr;
    gap: 1.3rem 0;
  }
  .doc-diff-col:last-child,
  .doc-pivot > div:last-child {
    padding-left: 0;
    border-left: 0;
    padding-top: 1.3rem;
    border-top: 1px solid var(--paper-edge);
  }
}

/* Three attempts at a map (hub-and-spoke, orbit, jittered orbit) all read as
 * a diagram describing an archive rather than as the archive itself. A
 * table of contents doesn't have that problem — it's what a record's own
 * index has always looked like. The heading stays quiet on purpose: this
 * section is a continuation of the essay above it, not a new pitch. */
.toc-section {
  position: relative;
  z-index: 2;
  padding: clamp(.75rem, 1.8vw, 1.1rem) clamp(1.5rem, 5vw, 4.5rem) clamp(2rem, 4.5vw, 3rem);
  max-width: 82rem;
}
.toc-head {
  margin-bottom: .6rem;
  text-align: left;
}
.toc-head h2 {
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 500;
  font-size: clamp(1.1rem, 1.9vw, 1.3rem);
  margin: 0;
  letter-spacing: -.01em;
  color: var(--ink-soft);
}
.toc-head p {
  margin: .3rem 0 0;
  font-size: .96rem;
  color: var(--ink-faint);
  font-style: italic;
}

.toc { max-width: none; border-top: 1px solid var(--paper-edge); }
/* ~35% more room per row than the first pass — three editorial categories
 * standing beside the product proof need enough weight to still read as
 * substantial, not three compressed nav links trailing after it. */
.toc-row {
  padding: 1.9rem 0;
  border-bottom: 1px solid var(--paper-edge);
}
.toc-name {
  display: flex;
  align-items: baseline;
  gap: .8rem;
  margin: 0 0 1.05rem;
  text-decoration: none;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 600;
  font-size: clamp(1.4rem, 2.8vw, 1.75rem);
  color: var(--ink);
}
.toc-num {
  font-family: var(--mark);
  font-weight: 400;
  font-size: 1rem;
  color: var(--accent);
}
.toc-name:hover, .toc-name:focus-visible { color: var(--accent); }

.toc-titles {
  display: flex;
  flex-wrap: wrap;
  gap: .65rem 1.9rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.toc-titles li {
  font-family: var(--body);
  font-style: italic;
  font-size: 1.02rem;
  color: var(--ink-soft);
}
.toc-titles a {
  text-decoration: none;
  color: inherit;
  border-bottom: 1px solid transparent;
  transition: color .2s, border-color .2s;
}
.toc-titles a:hover, .toc-titles a:focus-visible {
  color: var(--ink);
  border-color: var(--ink-faint);
}

/* --------------------------------------------------------- writing index */

/* The page's own h1 — the masthead nav link now carries the visible
 * document-tab treatment (see .masthead-nav a), so this stays in the DOM
 * for screen readers and SEO without drawing a second tab on the page. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.deck {
  font-family: var(--body);
  font-style: italic;
  font-size: 1.24rem;
  line-height: 1.45;
  color: var(--ink-soft);
  max-width: 34rem;
  margin: 0;
}

/* ------------------------------------------------------------- category */
.cat-head { max-width: 54rem; margin: 0 auto; padding: clamp(3rem, 8vw, 6rem) clamp(1.5rem, 5vw, 4rem) 2rem; }
.cat-head .kicker a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--paper-edge); }
.cat-head .kicker a:hover, .cat-head .kicker a:focus-visible { color: var(--accent); border-color: var(--accent); }
.cat-head h1 { margin: .6rem 0 .8rem; }

.cat-list { max-width: 54rem; margin: 0 auto; padding: 0 clamp(1.5rem, 5vw, 4rem) 6rem; border-top: 1px solid var(--paper-edge); }
.cat-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .2rem 1.5rem;
  padding: 1.4rem 0;
  border-bottom: 1px solid var(--paper-edge);
  text-decoration: none;
  color: var(--ink);
}
.cat-row-title {
  grid-column: 1;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-size: 1.2rem;
  font-weight: 500;
}
.cat-row-deck { grid-column: 1; color: var(--ink-soft); font-style: italic; }
.cat-row-meta {
  grid-column: 2;
  grid-row: 1 / 3;
  align-self: center;
  font-family: var(--mark);
  font-size: .7rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.cat-row:hover .cat-row-title, .cat-row:focus-visible .cat-row-title { color: var(--accent); }

/* ------------------------------------------------------------------ map */
/* The ledger index is a graph again — three hubs, twenty-one nodes — but
 * without drawn connectors this time. A curve from a hub to each of its
 * nodes reads fine at three or four nodes per cluster; at seven or eight it
 * starts to look tangled no matter how gently it's bowed. Three vertical
 * dividers do the same job a bundle of curves did (mark which nodes belong
 * to which argument) without getting more cluttered as a cluster grows.
 *
 * Coordinates come from the build script, which holds one hand-placed table.
 * The canvas is taller than it used to be for exactly one reason: the same
 * hand-placed percentages spread further apart in absolute pixels on a
 * taller canvas, which is the cheapest way to buy a cluster more breathing
 * room without hand-tuning every coordinate in it. */

.map {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 92rem;
  margin: 0 auto;
  margin-top: clamp(1.5rem, 3vw, 2.5rem);
  padding: clamp(.75rem, 2vw, 1.25rem) clamp(1.5rem, 5vw, 4rem) clamp(3rem, 7vw, 5rem);
  height: clamp(46rem, 92vh, 68rem);
}
.cluster { display: contents; }

/* A soft colour field behind each cluster — the tidying move that matters
 * most. Without it the shapes read as one undifferentiated field; with it,
 * the eye groups a hub with its own nodes before reading a single title. */
.hub-halo {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: 24rem;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, var(--accent-soft) 0%, transparent 70%);
  opacity: .22;
  filter: blur(8px);
  pointer-events: none;
}

.map-edges {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* pathLength normalises every curve to 100 units, so one dash rule draws them
 * all regardless of actual length — otherwise each edge needs its own measured
 * dasharray and the short ones finish while the long ones have barely begun. */
.edge {
  fill: none;
  stroke: var(--ink-soft);
  stroke-width: 1.8;
  stroke-linecap: round;
  opacity: 0;
  animation: edge-in .5s ease-out forwards;
  animation-delay: var(--d, 0s);
}
@keyframes edge-in {
  to { opacity: .7; }
}

.hub, .node {
  position: absolute;
  left: var(--x);
  top: var(--y);
  animation: pop .6s cubic-bezier(.2,.8,.25,1) backwards;
  animation-delay: var(--d, 0s);
}

@keyframes pop {
  from { opacity: 0; transform: translate(-50%, -50%) scale(.86) rotate(0deg); }
}

/* Smaller than the first pass, and oval rather than round — an ellipse reads
 * less like a fourth essay and more like a label stamped onto the canvas,
 * which is closer to what a hub actually is here. */
.hub {
  transform: translate(-50%, -50%);
  width: clamp(8.5rem, 10vw, 11.5rem);
  aspect-ratio: 1.75 / 1;
  display: grid;
  place-content: center;
  text-align: center;
  gap: .3rem;
  padding: .9rem 1.1rem;
  text-decoration: none;
  z-index: 1;
  border-radius: 50%;
  background-color: color-mix(in srgb, var(--accent-soft) 40%, var(--paper) 60%);
  background-image: radial-gradient(
    color-mix(in srgb, var(--ink) 11%, transparent) 1px, transparent 1.4px);
  background-size: 9px 9px;
  color: var(--ink);
  box-shadow: 0 0 0 1px var(--paper-edge), 0 10px 30px -20px rgba(0,0,0,.6);
  scroll-margin-top: 6rem;
  transition: transform .3s cubic-bezier(.2,.8,.25,1), box-shadow .3s;
}
/* Every hub is the same violet — a parent node reads as one kind of thing
 * regardless of which section it heads. */

.hub:hover, .hub:focus-visible {
  transform: translate(-50%, -50%) scale(1.045);
  box-shadow: 0 0 0 1px var(--pop), 0 14px 32px -18px rgba(0,0,0,.55);
}

/* Monospace, not the display serif every node title already uses — a hub
 * needed to read as a different kind of object at a glance, not just a
 * smaller one. Courier Prime is already in the type system (see --mark, used
 * for every meta line and label) so this borrows an existing voice rather
 * than adding a fourth typeface. */
.hub-num {
  font-family: var(--mark);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: .02em;
  opacity: .72;
}
.hub-name {
  font-family: var(--mark);
  font-weight: 400;
  font-size: .68rem;
  line-height: 1.3;
  letter-spacing: .07em;
  text-transform: uppercase;
  text-wrap: balance;
}

.node {
  transform: translate(-50%, -50%) rotate(var(--tilt, 0deg));
  width: clamp(10.5rem, 13vw, 14rem);
  padding: 1.15rem 1.3rem 1rem;
  background-color: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  text-decoration: none;
  overflow: hidden;
  transition: transform .32s cubic-bezier(.2,.8,.25,1), box-shadow .32s, border-color .32s;
}
/* Essay nodes are the destination, so their hover carries the most life on
 * the map: a lift instead of just a scale, the flamingo pop instead of the
 * structural violet, and the title's own underline drawing in — a cue that
 * survives even for a reader who has motion reduced, since the border and
 * lift alone already read as "this is the thing to click". */
.node:hover, .node:focus-visible {
  transform: translate(-50%, -50%) translateY(-.3rem) rotate(0deg) scale(1.06);
  border-color: var(--pop);
  box-shadow: 0 18px 36px -20px rgba(0,0,0,.5), 0 0 0 1px var(--pop);
  z-index: 3;
}
.node:hover .node-title::after, .node:focus-visible .node-title::after {
  transform: scaleX(1);
}

.node-title {
  display: block;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 500;
  font-size: 1.02rem;
  line-height: 1.16;
  letter-spacing: -.01em;
  text-wrap: balance;
  position: relative;
  padding-bottom: .2rem;
}
.node-title::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--pop);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .3s cubic-bezier(.2,.7,.2,1);
}
.node-meta {
  display: block;
  margin-top: .6rem;
  font-family: var(--mark);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Shapes are assigned in the build table rather than derived, because each one
 * has to hold a real title at a readable size. That rules out most of the
 * shapes reached for first — a circle is lovely and cannot set "The Beliefs You
 * Quietly Stopped Believing" without dropping to 9px.
 *
 * Each shape now also carries its own faint background pattern — a few
 * percent of ink mixed into the parchment, never more, so it reads as tooth
 * in the stock rather than decoration. A dot grid, a diagonal hatch, a
 * cross-hatch, concentric rings, straight ruling: five textures for five
 * silhouettes is one more way the "map babies" read as individuals rather
 * than five copies of one template card. */
.node--squircle {
  border-radius: 1.5rem;
  background-image: radial-gradient(
    color-mix(in srgb, var(--ink) 9%, transparent) 1px, transparent 1.4px);
  background-size: 10px 10px;
}
.node--pill {
  border-radius: 999px;
  padding: 1.3rem 1.7rem 1.2rem;
  background-image: repeating-linear-gradient(
    45deg, color-mix(in srgb, var(--ink) 7%, transparent) 0 1px, transparent 1px 9px);
}
.node--leaf {
  border-radius: 2.4rem .3rem 2.4rem .3rem;
  background-image:
    repeating-linear-gradient(45deg,  color-mix(in srgb, var(--ink) 6%, transparent) 0 1px, transparent 1px 10px),
    repeating-linear-gradient(-45deg, color-mix(in srgb, var(--ink) 6%, transparent) 0 1px, transparent 1px 10px);
}

/* An oval, not a circle — every title on the map sets at the same size (see
 * .node-title), so the shape has to give a long title enough width on its
 * own rather than the text shrinking to fit a perfect circle. */
.node--oval {
  width: clamp(11.5rem, 14vw, 15rem);
  aspect-ratio: 1.55 / 1;
  border-radius: 50%;
  display: grid;
  place-content: center;
  text-align: center;
  padding: 1.3rem 1.7rem;
  background-image: repeating-radial-gradient(
    circle at center, color-mix(in srgb, var(--ink) 8%, transparent) 0 1px, transparent 1px 10px);
}

/* clip-path cuts the border off with it, so this shape carries its edge as an
 * inset ring instead. */
.node--cut {
  border: 0;
  box-shadow: inset 0 0 0 1px var(--paper-edge);
  clip-path: polygon(0 0, calc(100% - 1.3rem) 0, 100% 1.3rem, 100% 100%, 1.3rem 100%, 0 calc(100% - 1.3rem));
  background-image: repeating-linear-gradient(
    var(--tilt, 0deg), color-mix(in srgb, var(--ink) 7%, transparent) 0 1px, transparent 1px 11px);
}
.node--cut:hover, .node--cut:focus-visible { box-shadow: inset 0 0 0 1px var(--pop); }

.stack {
  position: relative;
  z-index: 2;
  padding: clamp(2.5rem, 6vw, 4rem) clamp(1.5rem, 5vw, 4.5rem);
  max-width: 82rem;
  scroll-margin-top: 2rem;
}
.stack + .stack { border-top: var(--rule); }

.stack-head { max-width: 40rem; margin-bottom: 2.4rem; }
.stack-num {
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-size: 1.4rem;
  color: var(--accent);
  margin: 0 0 .2rem;
}
.stack-head h2 {
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 500;
  font-size: clamp(1.7rem, 4.4vw, 2.6rem);
  line-height: 1.1;
  margin: 0 0 .7rem;
  letter-spacing: -.015em;
}
.stack-blurb { margin: 0; color: var(--ink-soft); font-size: 1.04rem; }

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: 1.6rem;
}

/* Laid on the page by hand. The tilt comes from the slug, so it never moves
 * between builds — a card that reshuffles on refresh reads as a bug, not as a
 * person having placed it. */
.card {
  display: block;
  position: relative;
  padding: 1.5rem 1.4rem 1.3rem;
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  text-decoration: none;
  transform: rotate(var(--tilt, 0deg));
  transition: transform .35s cubic-bezier(.2,.7,.2,1), box-shadow .35s, border-color .35s;
}
.card:hover {
  transform: rotate(0deg) translateY(-4px);
  border-color: var(--ink-faint);
  box-shadow: 0 14px 30px -18px rgba(0,0,0,.5);
}
.card h3 {
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 500;
  font-size: 1.3rem;
  line-height: 1.16;
  margin: 0 0 .6rem;
  letter-spacing: -.01em;
}
.card-deck {
  margin: 0 0 1rem;
  font-size: .96rem;
  line-height: 1.5;
  color: var(--ink-soft);
  font-style: italic;
}
.card-meta {
  margin: 0;
  font-family: var(--mark);
  font-size: .72rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --------------------------------------------------------------- essay */

.essay { position: relative; z-index: 2; }

.essay-head {
  padding: clamp(3rem, 8vw, 5.5rem) clamp(1.5rem, 5vw, 4.5rem) 2.5rem;
  max-width: 52rem;
}

.kicker {
  display: flex;
  align-items: baseline;
  gap: .7rem;
  margin: 0 0 1.4rem;
  font-family: var(--mark);
  font-size: .76rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.kicker-num { color: var(--accent); font-size: .95rem; letter-spacing: .1em; }

.essay-head h1 {
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 600;
  font-size: clamp(2.3rem, 6.6vw, 4rem);
  line-height: 1.02;
  letter-spacing: -.025em;
  margin: 0 0 1.1rem;
  text-wrap: balance;
}

/* Reading time and word count are information a reader actually uses to
 * decide whether to start — not decoration, so they read a size up from the
 * kicker and byline-dot with stronger contrast than the merely decorative
 * metadata around them. */
.byline {
  margin: 1.8rem 0 0;
  font-family: var(--mark);
  font-size: .84rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  display: flex;
  gap: .7rem;
  flex-wrap: wrap;
}
.byline-dot { color: var(--paper-edge); }

/* The text column sits left of centre with a wide margin on the right. That
 * margin is not wasted space — plates and pull quotes hang into it, which is
 * how a printed page has always handled apparatus. Centring the column would
 * be tidier and would look like every other site. */
/* The head, the body and the closing mark share one max width and one left
 * edge, so the kicker, the headline and the first line of prose all sit on the
 * same vertical. On a very wide display the whole block centres rather than
 * staying pinned left with an ever-growing dead margin. */
.essay-head,
.essay-body,
.essay-end { max-width: 84rem; margin-inline: auto; }

.essay-body {
  display: grid;
  grid-template-columns:
    [edge-start] clamp(1.5rem, 5vw, 4.5rem)
    [text-start] minmax(0, var(--measure))
    [text-end] minmax(0, 22rem)
    [edge-end] minmax(1.5rem, 1fr);
  row-gap: 0;
}
.essay-body > * { grid-column: text; }

.essay-body p { margin: 0 0 1.15em; }

.essay-body h2 {
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 500;
  font-size: clamp(1.4rem, 3.2vw, 1.85rem);
  line-height: 1.16;
  letter-spacing: -.012em;
  margin: 2.9rem 0 1rem;
  position: relative;
}
/* A printer's mark in the gutter rather than a numeral in the heading. */
.essay-body h2::before {
  content: "";
  position: absolute;
  left: -1.6rem;
  top: .72em;
  width: .7rem;
  height: 1px;
  background: var(--accent);
}

.essay-body strong { font-weight: 600; }
.essay-body em { font-style: italic; }

/* ------------------------------------------------------------ apparatus */

/* A pull quote earns its size — it is the one moment on the page where type
 * is allowed to be loud. The oversized opening mark does most of the work:
 * classic editorial drama, not a decoration bolted on afterward. */
.pull {
  position: relative;
  grid-column: text-end / edge-end;
  align-self: start;
  margin: .5rem 0 2rem 3.4rem;
  padding: 0 0 0 1.6rem;
  border-left: 3px solid var(--accent);
}
.pull::before {
  content: "\201C";
  display: block;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-size: 3.6rem;
  line-height: .5;
  color: var(--accent-soft);
  margin: 0 0 .5rem -.12rem;
}
.pull p {
  margin: 0;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 500;
  font-size: 1.68rem;
  line-height: 1.2;
  letter-spacing: -.015em;
  text-wrap: pretty;
}
.pull-cite {
  display: block;
  margin-top: .9rem;
  font-family: var(--mark);
  font-size: .74rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* The inline variant breaks the text column instead of riding beside it —
 * a second, louder register for a pull quote so not every essay reads the
 * same rhythm of prose-then-margin-note. Centered, full measure, the
 * opening mark scaled up to a full display size. */
.pull--inline {
  grid-column: text;
  margin: 3.2rem 0 3.4rem;
  padding: 0;
  border-left: 0;
  text-align: center;
}
.pull--inline::before {
  font-size: 5.4rem;
  line-height: .4;
  margin: 0 auto .1rem;
}
.pull--inline p {
  max-width: 31rem;
  margin: 0 auto;
  font-size: clamp(1.78rem, 1.2rem + 2.4vw, 2.4rem);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -.018em;
}
.pull--inline .pull-cite { margin-top: 1.15rem; }

/* A plate holds to the text column's own width rather than reaching into the
 * pull quote's margin the way it used to. The two kinds of apparatus used to
 * share a column and crowd each other whenever a figure landed near a quote;
 * now a plate is always exactly as wide as the prose above it, and a pull
 * quote is always a slim marginal note — two shapes a reader tells apart at
 * a glance instead of two grey boxes stacked in the same lane. */
.plate {
  grid-column: text;
  margin: 3.4rem 0 3.6rem;
  padding: 1.8rem 1.8rem 1.4rem;
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
}
.plate-art svg { width: 100%; height: auto; display: block; }

.plate-bar {
  fill: var(--accent);
  opacity: .82;
  transform-origin: center;
  animation: plate-in .7s cubic-bezier(.2,.7,.2,1) backwards;
  animation-delay: calc(var(--i) * .08s);
}
@keyframes plate-in {
  from { opacity: 0; transform: scaleX(.4); }
  to   { opacity: .82; transform: scaleX(1); }
}

/* Vertical growth for column charts (camera-roll density, the decay grid).
 * transform-box: fill-box makes "bottom" mean the shape's own bottom edge
 * rather than the SVG viewport's — without it every column would grow from
 * the same point regardless of where it sits. */
.plate-col {
  transform-box: fill-box;
  transform-origin: bottom;
  animation: plate-col-in .6s cubic-bezier(.2,.7,.2,1) backwards;
  animation-delay: calc(var(--i) * .05s);
}
@keyframes plate-col-in {
  from { opacity: 0; transform: scaleY(.35); }
  to   { opacity: 1;  transform: scaleY(1); }
}

.plate-dot {
  fill: var(--ink-faint);
  animation: plate-dot-in .4s cubic-bezier(.2,.7,.2,1) backwards;
  animation-delay: calc(var(--i, 0) * .06s);
}
/* The pop's other placement: the one point in each chart that the essay
 * actually talks about — a song's spike, the two days that got rewritten —
 * gets the flamingo instead of the structural violet the rest of the figure
 * uses, so "this is the moment" reads the same way across every plate. */
.plate-dot--peak { fill: var(--pop); mix-blend-mode: lighten; }
@keyframes plate-dot-in {
  from { opacity: 0; transform: scale(.4); transform-box: fill-box; transform-origin: center; }
  to   { opacity: 1; transform: scale(1); }
}

.plate-line { stroke-linecap: round; stroke-linejoin: round; }

/* The reader's note in the margin of a plate, not a chart label — set in the
 * same typewriter face and italic tilt as .margin-note, so an annotation
 * inside a figure reads as the same voice as one beside the prose. */
.plate-annot {
  fill: var(--accent);
  font-family: "Courier Prime", monospace;
  font-size: 11px;
  font-style: italic;
}

.plate-label {
  fill: var(--ink-soft);
  font-family: "Courier Prime", monospace;
  font-size: 12px;
}
.plate-rule { stroke: var(--paper-edge); stroke-width: 1; }

.plate-art--fpo {
  border: 1px dashed var(--ink-faint);
  padding: 2.2rem 1.6rem;
  text-align: center;
  background-image: repeating-linear-gradient(
    -45deg, transparent 0 11px, rgba(140,128,110,.07) 11px 22px);
}
.fpo-mark {
  margin: 0 0 .8rem;
  font-family: var(--mark);
  font-size: .72rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--accent);
}
.fpo-brief {
  margin: 0 auto;
  max-width: 32rem;
  font-size: .97rem;
  line-height: 1.55;
  color: var(--ink-soft);
  font-style: italic;
}

/* The belief-diff plate: five convictions set like a changed file, because a
 * stated belief is a line either present or not. */
.diff {
  font-family: var(--mark);
  font-size: .92rem;
  line-height: 2;
}
.diff-line { margin: 0; display: flex; gap: .8rem; }
.diff-mark { width: 1em; flex: none; text-align: center; }
.diff-line--add { color: var(--accent); }
.diff-line--remove {
  color: var(--ink-faint);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--ink-faint);
}
.diff-line--same { color: var(--ink-soft); opacity: .82; }

/* The four-readers plate: one set of evidence, four cards. Marked items carry
 * full weight; everything a given reader skimmed past sits faded beside it,
 * so the difference between readers is visible without a legend. */
.readers {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
}
.readers-card {
  padding: 1rem 1.1rem;
  background: var(--paper);
  border: 1px solid var(--paper-edge);
}
.readers-name {
  margin: 0 0 .6rem;
  font-family: var(--mark);
  font-size: .7rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--accent);
}
.readers-marks {
  list-style: none;
  margin: 0 0 .8rem;
  padding: 0;
  font-size: .86rem;
  line-height: 1.55;
}
.readers-marks li { color: var(--ink-faint); opacity: .5; }
.readers-marks li.is-marked { color: var(--ink); opacity: 1; font-weight: 600; }
.readers-line {
  margin: 0;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-style: italic;
  font-size: .98rem;
  line-height: 1.32;
  color: var(--ink-soft);
}
@media (max-width: 30rem) {
  .readers { grid-template-columns: 1fr; }
}

.plate figcaption {
  margin-top: 1.1rem;
  padding-top: .85rem;
  border-top: 1px solid var(--paper-edge);
  font-size: .92rem;
  line-height: 1.5;
  color: var(--ink-soft);
}
.plate-number {
  font-family: var(--mark);
  font-size: .72rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-right: .7rem;
}

/* The printer's square that closes a piece. */
.essay-end {
  margin: 3rem auto 0;
  padding-left: clamp(1.5rem, 5vw, 4.5rem);
  max-width: calc(var(--measure) + 9rem);
  color: var(--accent);
  font-size: .7rem;
}

/* Rules drawn as borders rather than as a 1px grid gap over a coloured
 * backdrop. The gap trick needs every cell filled with a flat colour, and a
 * flat fill over a gradient shows up as a patch. */
.jumps {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  border-top: var(--rule);
  margin-top: clamp(3rem, 7vw, 5rem);
}
.jump {
  display: block;
  padding: 1.9rem clamp(1.5rem, 5vw, 4.5rem);
  border-right: 1px solid var(--paper-edge);
  text-decoration: none;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-size: 1.22rem;
  line-height: 1.2;
  transition: background .3s;
}
.jump:last-child { border-right: 0; }
.jump:hover { background: color-mix(in srgb, var(--ink) 6%, var(--paper)); }
.jump span {
  display: block;
  margin-bottom: .5rem;
  font-family: var(--mark);
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
}
.jump--next { text-align: right; }

/* ------------------------------------------------------------- colophon */

.footer {
  position: relative;
  z-index: 2;
  padding: 2.2rem clamp(1.5rem, 5vw, 4.5rem);
  border-top: var(--rule);
  background: var(--paper);
}

.footer-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.4rem 2.5rem;
}

.footer-mark {
  margin: 0;
  font-family: var(--display);
  font-variation-settings: var(--wonk-on);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: .04em;
}
.footer-logo {
  display: block;
  height: 2.5rem;
  width: auto;
  margin-bottom: .45rem;
  color: var(--ink);
}
.footer-mark span {
  display: block;
  margin-top: .15rem;
  font-family: var(--mark);
  font-size: .72rem;
  font-weight: 400;
  letter-spacing: .09em;
  color: var(--ink-faint);
}

.footer-nav {
  display: flex;
  gap: 1.6rem;
  font-family: var(--mark);
  font-size: .84rem;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.footer-nav a {
  position: relative;
  text-decoration: none;
  padding-bottom: .18em;
  border-bottom: 1px solid var(--ink-faint);
  transition: color .25s;
}
.footer-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 100%;
  bottom: -1px;
  height: 1px;
  background: var(--pop);
  mix-blend-mode: lighten;
  transition: right .3s cubic-bezier(.2,.8,.25,1);
}
.footer-nav a:hover, .footer-nav a:focus-visible { color: var(--pop); }
.footer-nav a:hover::after, .footer-nav a:focus-visible::after { right: 0; }

/* ---------------------------------------------------------- page load */

.hero-slug, .hero-story, .margin-note, .declaration-line, .declaration-sub,
.essay-head .kicker, .essay-head h1, .essay-head .deck, .essay-head .byline {
  animation: settle .85s cubic-bezier(.2,.7,.2,1) backwards;
}
.hero-story        { animation-delay: .07s; }
.margin-note       { animation-delay: .18s; }
.declaration-line  { animation-delay: .12s; }
.declaration-sub   { animation-delay: .2s; }
.essay-head h1     { animation-delay: .06s; }
.essay-head .deck  { animation-delay: .13s; }
.essay-head .byline{ animation-delay: .2s; }

@keyframes settle {
  from { opacity: 0; transform: translateY(.7rem); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .card { transform: none; }
}

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

/* A mind map needs two dimensions. Below tablet width there is only one, so
 * it drops back to normal flow — the DOM is already ordered hub, its nodes,
 * next hub, so the grouping survives with nothing left to draw. */
@media (max-width: 62rem) {
  .map {
    height: auto;
    display: flex;
    flex-direction: column;
    gap: 2rem;
    max-width: 34rem;
  }
  .hub-halo, .map-edges { display: none; }

  /* No dividers on a single column, but a cluster is still a cluster — a
   * plain rule down the left of the hub and its own nodes, with a dot at
   * each node, reads as the same "hub, then its essays" grouping the
   * desktop map draws with a vertical seam between arguments. */
  .cluster {
    display: flex;
    flex-direction: column;
    gap: .9rem;
    position: relative;
    padding-left: 1.3rem;
  }
  .cluster::before {
    content: "";
    position: absolute;
    left: 0;
    top: 1.7rem;
    bottom: 1.7rem;
    width: 1px;
    background: var(--ink-faint);
  }

  .hub, .node {
    position: relative;
    left: auto;
    top: auto;
    transform: none;
    width: 100%;
    aspect-ratio: auto;
    animation: none;
  }
  .node { display: block; }
  .hub {
    border-radius: 1.2rem;
    text-align: left;
    place-content: start;
    padding: 1rem 1.3rem;
  }
  .hub-name { font-size: .85rem; }
  .hub:hover, .hub:focus-visible { transform: none; }

  .node { text-align: left; place-content: start; padding: 1.1rem 1.3rem; }
  .node::before {
    content: "";
    position: absolute;
    left: -1.33rem;
    top: 50%;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--ink-faint);
    transform: translateY(-50%);
  }
  .node:hover, .node:focus-visible { transform: none; }
  .node--oval { border-radius: 1.2rem; padding: 1.1rem 1.3rem; }
  .node--cut { clip-path: none; }
}

@media (max-width: 60rem) {
  .hero { grid-template-columns: minmax(0, 1fr); }
  .margin-note { margin-top: 1.5rem; }

  .essay-body {
    grid-template-columns:
      [edge-start] clamp(1.25rem, 5vw, 2rem)
      [text-start] minmax(0, 1fr)
      [text-end edge-end] clamp(1.25rem, 5vw, 2rem);
  }
  .essay-body > * { grid-column: text-start / text-end; }
  .pull, .plate { grid-column: text-start / text-end; }
  .pull { margin: 1.8rem 0 2rem; }
  .essay-body h2::before { display: none; }
  .plate { padding: 1.2rem 1rem 1rem; margin: 2.4rem 0 2.6rem; }
  .readers { grid-template-columns: 1fr; }
  .ledger-row { grid-template-columns: minmax(0, 1fr); gap: .3rem; }
}

@media (max-width: 34rem) {
  body { font-size: 1.09rem; }
  .dropcap { font-size: 3.4em; }
  .jump--next { text-align: left; }
}

/* --------------------------------------------------------------- print */
/* A publication that cannot be printed is not really a publication. */

@media print {
  .grain, .washes, .colour-bar, .masthead-nav, .jumps { display: none; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .essay-body { display: block; max-width: 34em; }
  .pull, .plate { break-inside: avoid; margin: 1.4em 0; }
  .plate-art--fpo { display: none; }
  a { text-decoration: none; }
  .footer { background: none; }
}
