/* ==========================================================================
   aidenseo.com — base.css (v3, "tilde rail")
   Fixed 180px left rail (name / tilde nav / socials), content column to its
   right. Source Serif 4 for prose, IBM Plex Mono for nav + metadata.
   Warm cream ground, olive ink, green as the working accent, pink only for
   actions (share) and the rail tildes on hover. Links are ink on a green
   underline with no hover state. No shadows, no cards, no motion.
   ========================================================================== */

/* ---- Fonts: self-hosted Source Serif 4 (variable weight 200–900) ---------
   IBM Plex Mono (400/500) is loaded from Google Fonts in base.njk.          */
@font-face {
  font-family: "Source Serif 4";
  src: url("/assets/fonts/source-serif-4-regular-latin.woff2") format("woff2");
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Source Serif 4";
  src: url("/assets/fonts/source-serif-4-regular-latin-ext.woff2")
    format("woff2");
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Source Serif 4";
  src: url("/assets/fonts/source-serif-4-italic-latin.woff2") format("woff2");
  font-weight: 200 900;
  font-style: italic;
  font-display: swap;
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Source Serif 4";
  src: url("/assets/fonts/source-serif-4-italic-latin-ext.woff2")
    format("woff2");
  font-weight: 200 900;
  font-style: italic;
  font-display: swap;
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---- Design tokens ------------------------------------------------------- */
:root {
  --bg: oklch(0.99 0.002 95); /* near-white, a whisper of warmth */
  --ink: oklch(0.26 0.015 120); /* body text, dark olive-gray */
  --ink-muted: oklch(0.45 0.02 120); /* decks, captions */
  --green: oklch(0.52 0.09 152); /* nav links, dates */
  --green-muted: oklch(0.42 0.05 152); /* essay subtitles */
  /* Presence dots in 1v1. Weighted to match --green (5.2:1 vs 5.1:1 on --bg)
     so neither state shouts louder than the other. */
  --red: oklch(0.55 0.19 27);
  --pink: oklch(0.62 0.14 358); /* hover + active ONLY — signal color */
  /* Same signal, dark enough to carry small text: --pink is 3.8:1 on --bg,
     under the 4.5:1 AA floor. Decorative uses keep --pink. */
  --pink-text: oklch(0.56 0.14 358);
  --hairline: oklch(0.9 0.012 110); /* rules, rail border */
  --hairline-soft: oklch(0.92 0.01 110); /* row separators inside lists */
  --underline: oklch(0.82 0.05 152); /* green-tinted link underlines */

  --font-serif: "Source Serif 4", Georgia, "Times New Roman", serif;
  --font-mono: "IBM Plex Mono", "SF Mono", Menlo, Consolas, monospace;

  --rail-w: 224px;
  --content-max: 720px; /* text measure */
  --content-pad: 40px;
  --wrap-max: calc(var(--content-max) + 2 * var(--content-pad));

  --indent: 1.5em;
}

/* ---- Reset --------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
* {
  margin: 0;
}
html {
  -webkit-text-size-adjust: 100%;
  /* Reserve the scrollbar gutter on every page, not just /zetamac/ (where it
     kept the game column from sliding when a screen change grew the page past
     the viewport). Site-wide, it also stops the content column jumping sideways
     when you navigate from a short page like /about/ to a long one — on
     platforms with classic scrollbars a short page is `V` wide and a scrolling
     one `V - 15`, which moved the shared left edge by half that. No effect where
     scrollbars overlay (macOS, and headless Chromium — so this is the one change
     in this pass that a local probe cannot see). */
  scrollbar-gutter: stable;
}
/* Reachable by screen readers and by Tab, invisible on screen. */
.visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* Skip link: hidden until focused, then parked over the top-left corner so a
   keyboard user can jump the ten rail links. It has to outrank .rail (also
   z-index 10, and later in the DOM) or the rail paints the brand straight
   through the focused link. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 20;
}
/* A boxed link at top-left lands squarely on the rail's name, clipping its
   top few pixels and reading as a collision however the z-indexes are
   ordered. A full-width bar has nothing to half-cover. */
.skip-link:focus {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  /* The rail's name starts at --content-pad, so anything shorter than this
     slices its ascenders instead of covering it. */
  min-height: calc(var(--content-pad) + 30px);
  display: flex;
  align-items: center;
  padding: 8px 20px;
  background: var(--bg);
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: 14px;
  text-decoration: none;
}
body {
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-serif);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.65;
  /* Do not set -webkit-font-smoothing: antialiased. It drops subpixel
     rendering for dark-on-light text, thinning the serif into gray. */
  text-rendering: optimizeLegibility;
}
img,
picture,
svg {
  display: block;
  max-width: 100%;
  height: auto;
}
h1,
h2,
h3,
h4 {
  line-height: 1.25;
  font-weight: 600;
}
code,
pre {
  font-family: var(--font-mono);
  font-size: 0.85em;
}
a {
  /* Ink text on a green-tinted rule — that's the whole treatment; hover
     changes nothing. */
  color: var(--ink);
  text-decoration-line: underline;
  text-decoration-color: var(--underline);
  text-decoration-thickness: 1px;
  text-decoration-skip-ink: none; /* hard strike: continuous line through descenders */
  text-underline-offset: 3px;
}
:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 2px;
}
/* The skip link's target is a container, not a control. Left to the rule
   above it draws a ring around the entire content column — 1117px tall on a
   desktop article, taller than the screen on a phone, and straight through
   the citation sidenotes. The jump itself is the feedback. */
main[tabindex="-1"]:focus,
main[tabindex="-1"]:focus-visible {
  outline: none;
}

/* ---- Layout: fixed rail + independently scrolling content ----------------
   The rail offset lives on body padding (not main margin) because on content
   pages main IS .wrap — margins would collide, and .wrap's auto centering
   must happen within the region right of the rail. */
body {
  padding-left: var(--rail-w);
}
.wrap {
  width: 100%;
  max-width: var(--wrap-max);
  /* Anchor the column left of center: a third of the free space on the
     left, the rest on the right. Floors at the rail on narrow windows. */
  margin-left: calc((100% - min(100%, var(--wrap-max))) / 3);
  margin-right: auto;
  padding: var(--content-pad) var(--content-pad) 96px;
}
/* Gallery pages (art) widen past the text measure to fit three columns;
   --wrap-max drives both the cap and the off-center margin math above. */
.wrap.wrap-gallery {
  --wrap-max: calc(940px + 2 * var(--content-pad));
}

/* ---- Rail ----------------------------------------------------------------
   Name on top, tilde nav below it, socials pinned to the bottom.            */
.rail {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--rail-w);
  border-right: 1px solid var(--hairline);
  padding: var(--content-pad) 24px 32px;
  /* One column, sized to the widest of the three blocks, centred in the rail,
     with the blocks left-aligned inside it. That is the original centred
     composition — the name does not move — but the tilde column is now a
     straight edge instead of a stagger.
     Flex cannot express this: `align-items: center` centres each block
     independently, and since they are different widths ("Aiden Seo" against
     "zetamac" against "mail") their tildes landed at 58px, 74px and 86px.
     `flex-start` straightens the edge but drags everything to the padding
     edge at 24px. A grid track sizes to the widest block, so centring the
     track keeps the group where it was and starting the items squares them. */
  display: grid;
  grid-template-rows: auto auto 1fr;
  justify-content: center;
  justify-items: start;
  overflow-y: auto;
  z-index: 10;
}
.rail-brand {
  font-family: var(--font-serif);
  font-size: 20px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: var(--ink);
  text-decoration: none;
}
.rail-nav {
  margin-top: 44px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}
/* Rail links are ink like everything else; the tilde is the only accent
   (green at rest, pink on hover) and the label never changes. The current
   page renders as a span, so it naturally sits out the hover. */
.nav-link,
.social-link {
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  white-space: nowrap;
  color: var(--ink);
  text-decoration: none;
}
.nav-link {
  text-transform: lowercase; /* socials keep their case: "X", not "x" */
}
.rail-brand::before,
.nav-link::before,
.social-link::before {
  content: "~ " / ""; /* decorative tilde; empty alt so it isn't read aloud */
  color: var(--green);
}
a.rail-brand:hover::before,
a.nav-link:hover::before,
.social-link:hover::before {
  color: var(--pink);
}
/* You are here. The current item renders as a <span>, so it already sits out the
   hover — but a span and an <a> computed to exactly the same ink, the same
   weight and the same green tilde, which meant the rail encoded the current page
   for assistive tech and then showed everyone else nothing. The item stops
   looking like an affordance instead: its tilde drops the accent and its label
   takes the weight. Keyed on [aria-current] rather than on the element, so the
   visual state cannot drift away from the accessible one. */
.rail-brand[aria-current="page"],
.nav-link[aria-current="page"] {
  font-weight: 500;
}
.rail-brand[aria-current="page"]::before,
.nav-link[aria-current="page"]::before {
  color: var(--ink-muted);
}
.rail-social {
  margin-top: auto;
  padding-top: 32px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}

/* Below the rail: it collapses to a horizontal top bar — name on its own
   line, then the same tilde links (and socials) flowing in wrapped rows.

   The switch is at 1024px, not 720px, because that is the width where the
   two layouts give the same 720px measure. The rail costs 224px plus twice
   --content-pad, so at 720px it took the column from 679px down to 416px and
   it did not climb back until ~983px: making the window wider made the text
   narrower, and the whole 720-983px band — iPad portrait, half-screen laptop
   windows — read worse than one pixel below it. At 1024 the column is 720px
   either way, so it now only ever grows. */
@media (max-width: 1023.98px) {
  /* Keeps --wrap-max (content-max + 2 * pad) in step with the 20px the bar
     layout actually uses, so the measure still caps at --content-max
     instead of overshooting it by 40px. */
  :root {
    --content-pad: 20px;
  }
  .rail {
    position: static;
    width: auto;
    border-right: 0;
    border-bottom: 1px solid var(--hairline);
    padding: 20px 20px 16px;
    /* Back to flex for the bar, and drop the rail's centred grid track —
       inherited, `justify-content: center` would centre each wrapped row of
       links instead of running them from the left edge. */
    display: flex;
    justify-content: flex-start;
    flex-flow: row wrap;
    align-items: baseline;
    column-gap: 18px;
    row-gap: 8px;
    overflow: visible;
  }
  /* margin-right: 100% forces the wrap, exactly as flex-basis: 100% did, but
     leaves the anchor sized to its text. At 100% the link's box was the full bar
     width, so 243px of empty header right of the name navigated home. */
  .rail-brand {
    flex: 0 0 auto;
    margin-right: 100%;
    margin-bottom: 2px;
  }
  /* The nav links flow into the bar; the socials do not. With both dissolved,
     all nine ran together at one 18px gap and a row read "zetamac ig in X mail"
     — the primary/secondary split that 443px of rail whitespace states on
     desktop, deleted. The socials keep their own line instead, so the bar says
     the same three things the rail does: name, nav, socials. */
  .rail-nav {
    display: contents;
  }
  .rail-social {
    flex: 0 0 100%;
    flex-flow: row wrap;
    align-items: baseline;
    column-gap: 18px;
    row-gap: 8px;
    margin-top: 0; /* the desktop rail's `auto` would centre it in the row */
    padding-top: 0;
  }
  body {
    padding-left: 0;
  }
  .wrap {
    padding: 28px var(--content-pad) 72px;
  }
}

/* ---- Prose --------------------------------------------------------------- */
/* One page title, one step beneath it. /archive/ and /about/ used to hand-write
   theirs a step smaller (28px) with their own margins (26px and 30px), and
   /zetamac/ a third (18px) — so the same relationship, page title to first
   block, was set five different ways across six pages. It is 30px/24px
   everywhere now. Two exceptions, both different relationships and both
   deliberate: the home page's 48px, because a hero earns a bigger step, and the
   20px on .article-header below, which is the gap under a dateline hairline. */
.page-title,
.poem-title {
  font-size: 30px;
  font-weight: 600;
  line-height: 1.16;
  margin-bottom: 24px;
}

/* ---- Article header (title, deck, hairline, dateline + share) ------------ */
.article-header {
  margin-bottom: 20px;
}
.article-header .page-title,
.article-header .poem-title {
  margin-bottom: 0; /* the header wrapper owns the gap below */
}
/* .article-header p, not .article-deck: the deck is a <p> inside .page, and
   `.page p` ties on specificity and wins on source order — so the deck
   rendered at body size with a stray 1.1rem below it instead of the 19px
   step-up and the 12px header rhythm. Same trap ul.work-list documents. */
.article-header p.article-deck {
  margin-top: 12px;
  margin-bottom: 0;
  font-size: 19px;
  line-height: 1.45;
  font-weight: 300;
  color: var(--green-muted);
}
.article-figure {
  margin-top: 20px;
  max-width: 34rem;
}
.article-figure img {
  width: 100%;
}
.article-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  margin-top: 12px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--green);
}
.article-updated {
  margin-left: 1rem;
}
/* The dateline + share row (and its hairline) always runs the full column
   width, header image or not — one consistent length on every article.
   Poems are exempt: narrow poem layouts cap it to their own measure. */
.share-button {
  font: inherit;
  background: none;
  border: 0;
  padding: 0;
  color: var(--pink-text); /* an action, so it earns the signal color at rest */
  cursor: pointer;
}

.page p,
.poem-body p {
  margin-bottom: 1.1rem;
}
.page p {
  /* Full column width — body text runs to the same right edge as the
     dateline rule and share button. */
  font-size: 16px;
  text-wrap: pretty;
}
.page figcaption {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--green);
}
.page > h2,
.page > h3 {
  margin-top: 28px;
  margin-bottom: 0.75rem;
}
.page > h2 {
  font-size: 21px;
}
.page > h3 {
  font-size: 18px;
}
.page ul,
.page ol {
  margin-bottom: 1.1rem;
  padding-left: var(--indent);
}
.page li {
  margin-bottom: 0.35rem;
  font-size: 16px;
}
em {
  font-style: italic;
}

/* ---- Work lists: mono date gutter / serif title grid --------------------- */
.work-section {
  margin-bottom: 40px;
}
.work-section-title {
  font-size: 20px;
  font-weight: 600;
  margin-bottom: 12px;
}
/* ul.work-list, not .work-list: these lists live inside .page, and `.page ul`
   is the more specific selector — it would otherwise win and re-indent them. */
ul.work-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
/* Both tracks are tokens because /archive/ reuses them for the same gutter and
   the hanging rule below collapses one of them while keeping the other as the
   visual gap. 60px holds "07-27-26" — 8 chars of 12px mono is 57.6px, and a
   fallback face's overshoot is absorbed by the gap. The gap is one --indent at
   the 16px list size (1.5 × 16 = 24), so the date stands off its title by the
   same step the prose uses to indent. */
.work-item {
  --date-col: 60px;
  --date-gap: 24px;
  display: grid;
  grid-template-columns: var(--date-col) 1fr;
  column-gap: var(--date-gap);
  align-items: baseline;
}
.work-item + .work-item {
  margin-top: 6px;
}
.work-date {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--green);
  /* The hyphens are break opportunities, so a couple of pixels of squeeze
     would fold "07-27-26" onto two lines and break the baseline it shares
     with the title. It overhangs into the gap instead. */
  white-space: nowrap;
  /* Flush to the gutter's INNER edge, so every date stands exactly --date-gap
     off its own title whatever its length: /archive/ prints "07-27" (its year
     is the group heading), two characters shorter than the home page's
     "07-27-26", and left-aligned it would open a 24px hole the other lists
     don't have. justify-self rather than text-align because it also sizes the
     box to the text — which is what lets the collapsed track below hang it. */
  justify-self: end;
}
.work-title {
  grid-column: 2; /* undated items still align with dated ones */
  font-size: 16px;
  line-height: 1.45;
}
/* a.work-title is itself the grid item, so it stretches across the whole 1fr
   column: on /archive/ that boxed 279px of text in a 566px focus ring. Sizing
   it to its content keeps the ring on the words; long titles still wrap at
   the column edge. */
.work-title {
  justify-self: start;
}
/* Hang the date in the margin so titles start on the page's one left edge — the
   same line as the section heading above them and, on the home page, the
   headline above that. In the column, the two tracks indent every title 84px
   past everything else on the page.

   The date's TRACK collapses to nothing and the date hangs out of it, rather
   than the whole row being pulled left by a negative margin. Both land the
   title on the content edge, but only this one leaves the row's own box there.
   A negative margin moves the box, and so moves everything drawn on it: on
   /archive/ it started each row's hairline 84px left of the year heading above
   it, while the year-group rules still began at the content edge — two rule
   origins on one page. With the track collapsed the row still spans exactly the
   text column, so its hairline does too, and the 1fr title track still ends at
   the column's right edge, keeping titles on the prose measure.

   Listed per list, never as bare .work-item: /archive/ rows carry .work-item
   too (that class is what excludes them from pretext), and the hang is only
   safe where the row's left neighbour is margin.

   Only where the hang has somewhere to go. The rail is fixed at --rail-w and
   .wrap's left margin is a third of the free space, so the clearance between
   the rail's hairline and the date is (V − 1024)/3 + 40 − 84, which reaches a
   comfortable 24px at V = 1228. Below 1230px the date keeps a real column,
   because it overflows LEFT — a direction browsers never give a scrollbar, so
   instead of being scrolled to it would silently slide under the rail, which
   has no background of its own to hide behind. */
@media (min-width: 1230px) {
  .work-list .work-item,
  .archive-list .work-item {
    grid-template-columns: 0 1fr;
    column-gap: 0;
  }
  .work-list .work-date,
  .archive-list .work-date {
    margin-right: var(--date-gap); /* the gap the collapsed track no longer holds */
  }
}
/* On a 320px phone the gutter's 84px is most of a third of a 280px column, and
   nothing is hanging at that width to earn it. The date still needs its 60px,
   so the squeeze comes off the indent. One rule for every list, now that the
   archive's rows share these tokens instead of narrowing a year rail and a date
   column of their own. */
@media (max-width: 419.98px) {
  .work-item {
    --date-gap: 12px;
  }
}

/* ---- Archive: year headings over dated entry rows ------------------------
   The year used to be a 64px rail to the LEFT of the rows, which meant no row
   could reach the content edge — every title sat 88px in from the page's own
   H1, and the date another 66px past that. The year is a heading over its
   group now, like every other section heading on the site, which frees the
   rows to hang their dates in the same gutter .work-list uses: shared
   --date-col and --date-gap, one gutter site-wide.
   Rows keep the .work-item class so pretext leaves their grids alone. */
.archive-group + .archive-group {
  margin-top: 20px;
  border-top: 1px solid var(--hairline);
  padding-top: 20px;
}
.archive-year {
  margin-bottom: 12px; /* matches .work-section-title's margin-bottom */
  font-size: 17px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--ink);
}
.archive .archive-list {
  /* outweigh the .page ul indent + margin */
  list-style: none;
  margin: 0;
  padding: 0;
}
/* No per-row rule, and no track overrides: the shared --date-col / --date-gap
   ARE the gutter, and the shared 6px row rhythm IS the spacing — an archive row
   is a /works/ row under a year heading, so it should look like one.
   A row hairline used to sit under each entry, which drew a second faint
   horizontal line ~10px below the title's own link underline: one stopping at
   the end of the words, one running the full measure. Two near-identical rules
   per row read as a rendering fault rather than as structure, and the hanging
   date sat above a line that didn't reach it. The link underline is already a
   line per row; the year-group rules are already the page's divisions. */
.archive-list .work-title {
  font-size: 15.5px;
}

/* ---- About: serif section headings (matching .work-section-title) --------
   The measure lives on the section, not on the paragraph, so the rule that
   divides two sections is exactly as long as the text it divides. It used to run
   the full 720px column over a 415px paragraph — a rule 1.73x the length of what
   it headed, on a page whose sections are one line each.
   In rem, not ch: `ch` resolves against each element's own font-size, and the
   section (17px) and its paragraph (16px) were both given `50ch` and landed
   21.9px apart. */
.about-section {
  max-width: 26rem;
}
.about-section + .about-section {
  margin-top: 30px;
  border-top: 1px solid var(--hairline);
  padding-top: 30px;
}
.section-label {
  font-size: 20px;
  font-weight: 600;
  color: var(--ink);
}
.about-section p {
  margin-top: 12px; /* matches .work-section-title's margin-bottom */
  line-height: 1.6;
}
.file-note {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--green);
}

.poem-body {
  font-size: 17px;
  line-height: 1.85;
}
/* Poem lines are authored with <br>. When one is too long for the measure it
   wraps, and a flush-left runover is indistinguishable from a line the poet
   wrote — worse in the poems that indent, where the runover lands further
   left than the line it continues and inverts the shape.
   text-indent only ever hangs a block's FIRST line, so poem-lines.mjs boxes
   each <br>-separated line to hang its own runover. Lines that fit are
   untouched: the indent only shows once a line wraps. */
.poem-line {
  display: block;
  padding-left: 1.5em;
  text-indent: -1.5em;
}

/* Poem: You Hold It — prose poem set in a narrow 3in column (from v1) */
.poem-you-hold-it .poem-title,
.poem-you-hold-it .poem-body,
.poem-you-hold-it .article-meta {
  max-width: 3in;
}
.poem-you-hold-it .poem-body {
  font-size: 20px;
}
.poem-you-hold-it .poem-body p {
  margin-bottom: 0;
}
.poem-you-hold-it .poem-body p + p {
  margin-top: 1em;
}

/* poem indentation hooks (used by a few poems) */
.poem-body .tab {
  display: inline;
}
.poem-body .tab::before {
  content: "";
  display: inline-block;
}
.poem-body .tab-1::before {
  width: var(--indent);
}
.poem-body .tab-1-5::before {
  width: calc(var(--indent) * 2);
}
.poem-body .tab-2::before {
  width: calc(var(--indent) * 3);
}

/* ---- Home ---------------------------------------------------------------- */
.home-intro {
  font-size: 33px;
  font-weight: 500;
  line-height: 1.22;
  color: var(--ink);
}
.home-recent {
  margin-top: 48px;
}
@media (max-width: 719.98px) {
  .home-intro {
    font-size: 27px;
  }
}

/* Pretext-driven line layout (src/assets/js/pretext-layout.mjs).
   .work-item rows are grid layouts and are excluded from pretext. */
html.pretext-pending .page p,
html.pretext-pending .page li:not(.work-item) {
  visibility: hidden;
}
/* The home intro is the largest thing on the page, so hiding it until the
   module has parsed and laid out shows a hole where the headline goes —
   ~220ms of one on a slow phone, which reads as a flicker on every reload.
   From 1024px up the column is pinned at --content-max, so the intro's
   tight-fit width is a constant and pretext reproduces the browser's own
   line breaks exactly (checked every 64px from 1024 to 1920). There is
   nothing to hide there: it can paint with the page and be re-boxed into
   .pretext-line spans with no visible change.
   Below 1024px the column tracks the viewport, the tight-fit rebalances the
   rag, and the breaks genuinely do differ at some widths (576px and 704px
   among them) — so keep hiding it there rather than trade a late headline
   for a visible re-wrap. */
@media (max-width: 1023.98px) {
  html.pretext-pending .home-intro {
    visibility: hidden;
  }
}
.pretext-layout {
  display: flow-root; /* not flex: li must stay display: list-item for markers */
  max-width: 100%;
}
li.pretext-layout {
  display: list-item;
}
.pretext-line {
  display: block;
  white-space: nowrap;
}

/* Print: no rail, no fixed-px nowrap rows — let the paper reflow. */
@media print {
  .rail {
    display: none;
  }
  body {
    padding-left: 0;
  }
  .pretext-line {
    display: inline;
    white-space: normal;
    height: auto !important;
    line-height: inherit !important;
  }
  html.pretext-pending .home-intro,
  html.pretext-pending .page p,
  html.pretext-pending .page li:not(.work-item) {
    visibility: visible;
  }
}

/* ---- Art gallery --------------------------------------------------------- */
.art-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  /* dense: when the column count drops and a wide piece wraps to its own
     row, later pieces backfill the hole it leaves instead of gapping. */
  grid-auto-flow: dense;
  gap: 28px 20px;
}
.art-item--wide {
  grid-column: span 2;
}
.art-item img {
  width: 100%;
}
.art-item figcaption {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink);
}
.art-meta {
  color: var(--green);
}

/* Two 230px tracks plus the 20px gap need 480px of content column. Below that
   auto-fill lays a single track, and a spanning item conjures an implicit
   `auto` column instead — which is how 720px rendered a 230px piece beside a
   166px one. Drop the span wherever the second track cannot exist.
   Content width is the viewport minus the rail and the column's own padding,
   so the two thresholds are 520px without the rail and 784px with it. */
@media (max-width: 520px), (min-width: 720px) and (max-width: 783.98px) {
  .art-item--wide {
    grid-column: auto;
  }
}

/* ---- Citation sidenotes ---------------------------------------------------
   JS (citation-sidenotes.mjs, bundled into pretext-layout.js) turns literal
   [n] markers into superscript toggles and moves the ## Notes list into
   per-marker sidenotes. Citation blocks opt out of pretext layout via
   data-pretext="off". With the left rail there is no left gutter, so wide
   screens float every note into the right margin. */
.citation-ref {
  font-size: 0.7em;
  line-height: 0; /* keep the superscript from expanding the line box */
  vertical-align: super;
}
.citation-toggle {
  color: var(--green);
  font-weight: 600;
  cursor: pointer;
}
.citation-checkbox {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
}
/* The checkbox is visually hidden, so surface keyboard focus on the [n] label.
   It is the sup's NEXT SIBLING, not a descendant — citation-sidenotes.mjs
   emits <sup class="citation-ref">…</sup><input class="citation-checkbox">,
   so this has to be :has(+ …) or the focus ring never renders at all. */
.citation-ref:has(+ .citation-checkbox:focus-visible) .citation-toggle {
  outline: 2px solid var(--green);
  outline-offset: 2px;
}
.citation-sidenote {
  display: none;
  /* Three steps under .page p's 16px. A note is apparatus, not prose, and at
     14px it read as body text that happened to sit in the margin — close
     enough to the column to invite comparison rather than to be skipped. */
  font-size: 13px;
  line-height: 1.5;
  overflow-wrap: break-word;
  /* The note is injected wherever its marker sits, so it inherits that host's
     type: on /its-funny/ note [1] lands in a <cite> and came out italic while
     note [2], in a <p>, stayed roman. Notes are their own register. */
  font-style: normal;
  font-weight: 400;
}
.citation-note-number {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--green);
  font-weight: 600;
}

/* Narrow: tapping the marker opens the note inline at the marker. */
@media (max-width: 1279.98px) {
  .citation-checkbox:checked + .citation-sidenote {
    display: block;
    margin: 0.5rem 0;
    padding-left: var(--indent);
  }
}

/* Wide: notes float into the right margin nearest their marker. The content
   column sits a third into the leftover space, so two thirds of it is a
   usable right gutter (plus the column's own right padding). */
@media (min-width: 1280px) {
  .has-citation-sidenotes {
    --citation-gutter: calc(
      (100vw - var(--rail-w) - var(--wrap-max)) * 2 / 3 + var(--content-pad)
    );
    --citation-width: min(15rem, calc(var(--citation-gutter) - 3rem));
    --citation-offset: calc(var(--citation-width) + 2rem);
  }
  .citation-sidenote {
    display: block;
    width: var(--citation-width);
    margin-bottom: 1.5rem;
    float: right;
    clear: right;
    margin-right: calc(-1 * var(--citation-offset));
  }
}
