/* =====================================================================
   tom bona — DARKROOM  ·  site.css  (single stylesheet, mobile first)
   A near-black bench. On it: paper slips, masking tape, typed index
   cards, white-bordered prints, contact sheets of cut film.
   Film appears ONLY on contact-sheet frames (one slim sprocket edge).
   Photos: greyscale + slight contrast lift, nothing on top of them.

    1  Tokens
    2  Fonts: metric-matched system fallbacks
    3  Base + the bench
    4  Skip link
    5  Header, dropdowns, phone menu sheet
    6  Page frame + page head (scrawl title, tape)
    7  Paper (long stories): headings, text, lists, links, names
    8  Print (single photo with caption)
    9  Contact sheet + shots (galleries, home strip)
   10  Prints grid (photos landing)
   11  Video facade + videos grid
   12  Old-only index card
   13  Blog post
   14  Gig archive: year nav, month cards, rows
   15  Table fallback, blockquote
   16  Home hero
   17  Story layout (bio, credits, news …)
   18  Footer sponsor slip
   19  Lightbox
   20  Motion
   ===================================================================== */

/* ---------- 1. TOKENS ---------- */
:root {
  /* greys only: every value has R = G = B */
  --g-film:   #0a0a0a;   /* film frames, video boxes, logo chips */
  --g-bench:  #121212;   /* the bench (page ground) */
  --g-lit:    #1c1c1c;   /* lamp-lit centre of the bench */
  --g-rule:   #383838;   /* hairlines on the bench */
  --g-pencil: #6b6b6b;   /* pencil on paper */
  --g-holes:  #a6a6a6;   /* sprocket holes */
  --g-focus:  #858585;   /* focus tint behind a split web address on paper: 3.0:1 against the paper */
  --g-rule-p: #c2c2c2;   /* dashed rules on paper */
  --g-old:    #d0d0d0;   /* older index card */
  --g-tape:   #c8c8c8;   /* masking tape */
  --g-sheet:  #d6d6d6;   /* contact-sheet paper */
  --g-paper:  #e8e8e8;   /* reading paper, slips, cards */
  --g-print:  #f4f4f4;   /* print borders, display type */

  --ink:       #161616;  /* on paper 14.9:1, on old card 11.6:1 */
  --ink-soft:  #333333;  /* on paper 10.3:1, on old card 8.2:1 */
  --chalk:     #e6e6e6;  /* on bench 15.0:1 */
  --chalk-soft:#bdbdbd;  /* on bench 10.0:1 */
  --tape-rgba: rgba(200, 200, 200, .9);

  /* type */
  --f-marker: "Caveat Brush", "Marker Fallback", "Segoe Print", "Comic Sans MS", cursive;
  --f-type:   "Special Elite", "Type Fallback", "Courier New", monospace;
  --f-mono:   "Courier Prime", "Mono Fallback", "Courier New", monospace;
  --f-body:   "Source Serif 4", "Serif Fallback", Georgia, "Times New Roman", serif;

  --fs-body:  clamp(1.0625rem, 1rem + .25vw, 1.1875rem);   /* 17–19px */
  --fs-ui:    1rem;                                         /* 16px mono */
  --fs-ui-l:  1.125rem;
  --fs-h2:    clamp(1.375rem, 1.24rem + .6vw, 1.625rem);       /* 22–26px: section headings */
  --fs-h3:    clamp(1.1875rem, 1.12rem + .3vw, 1.3125rem);     /* 19–21px: titles and labels (always above body) */
  --fs-sect:  clamp(1.75rem, 1.5rem + 1vw, 2.125rem);          /* 28–34px: section dividers */
  --fs-page:  clamp(3.5rem, 2.2rem + 5.5vw, 6.5rem);        /* page title scrawl */
  --fs-name:  clamp(3.75rem, 1.6rem + 6vw, 7rem);           /* home name scrawl */

  /* space */
  --s-1: .375rem; --s-2: .75rem; --s-3: 1.25rem; --s-4: 2rem; --s-5: 3.25rem; --s-6: 5rem;
  --gutter: clamp(16px, 4.5vw, 56px);
  --hdr-h: 64px;
  --measure: 62ch;
  --inset-text: 16px;   /* phones (< 600px): every line of text in <main> sits at least this far from the screen edge */
  --pad-phone: 8px;     /* phones: side padding of an edge-to-edge sheet (pictures may run this close to the edge) */

  /* texture tiles (static, rasterised once by the browser) */
  --tx-grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  1.4 0 0 0 -.8'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  --tx-paper: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='p'><feTurbulence type='fractalNoise' baseFrequency='.7' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 -.22 .13'/></filter><rect width='100%25' height='100%25' filter='url(%23p)'/></svg>");
  --tx-holes: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='10'><rect x='5' y='2' width='10' height='6' rx='1.5' fill='%23a6a6a6'/></svg>");
  --tx-scrawl: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 10' preserveAspectRatio='none'><path d='M2 6C40 3 80 8 120 4S180 6 198 3' fill='none' stroke='%23f4f4f4' stroke-width='3' stroke-linecap='round' vector-effect='non-scaling-stroke'/></svg>");
  --tx-pencil: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 10' preserveAspectRatio='none'><path d='M2 6C40 3 80 8 120 4S180 7 196 3' fill='none' stroke='%236b6b6b' stroke-width='2.2' stroke-linecap='round' vector-effect='non-scaling-stroke'/></svg>");

  --shadow-lift: 0 1px 0 rgba(0,0,0,.5), 0 10px 22px -8px rgba(0,0,0,.75);
  --shadow-slip: 0 2px 6px rgba(0,0,0,.55);
  --tape-clip: polygon(0 8%, 6% 0, 94% 5%, 100% 0, 98% 92%, 92% 100%, 4% 95%, 0 100%);
}

/* ---------- 2. FONTS: metric-matched local fallbacks (shown until Google Fonts swap in) ---------- */
@font-face { font-family: "Serif Fallback"; src: local("Georgia"); size-adjust: 96%; ascent-override: 98%; descent-override: 30%; }
@font-face { font-family: "Mono Fallback";  src: local("Courier New"); size-adjust: 100%; }
@font-face { font-family: "Type Fallback";  src: local("Courier New"); size-adjust: 102%; }
@font-face { font-family: "Marker Fallback"; src: local("Segoe Print"), local("Comic Sans MS"); size-adjust: 82%; }

/* ---------- 3. BASE + THE BENCH ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%; text-size-adjust: 100%; background: var(--g-bench); -webkit-tap-highlight-color: transparent;
  scroll-padding-top: calc(var(--hdr-h) + 16px);   /* anchors and focused items never land under the sticky header */
}
button { -webkit-appearance: none; appearance: none; }
html.menu-open { overflow: hidden; }
body {
  margin: 0; min-height: 100vh;
  color: var(--chalk);
  font: 400 var(--fs-body)/1.6 var(--f-body);
  background: radial-gradient(1400px 900px at 50% 0, var(--g-lit) 0%, var(--g-bench) 70%) no-repeat, var(--g-bench);
  position: relative; isolation: isolate;
}
body::before { /* fine static grain over the bench only (behind everything) */
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: var(--tx-grain); opacity: .12;
}
img { max-width: 100%; height: auto; }
a { color: inherit; }
/* focus: a light ring on the bench, an ink ring on anything paper (the ring is drawn on the paper around the item) */
:focus-visible { outline: 3px solid var(--g-print); outline-offset: 3px; }
.paper :focus-visible, .slip :focus-visible, .sub :focus-visible, .hero-links :focus-visible, .sponsor :focus-visible,
.old :focus-visible, .month :focus-visible, .sheet :focus-visible, .lightbox :focus-visible { outline-color: var(--ink); }
.month > summary:focus-visible { outline-offset: -5px; }   /* the summary fills the card: keep the ring on the paper */
.vh { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ---------- 4. SKIP LINK ---------- */
.skip {
  position: absolute; left: 12px; top: -100px; z-index: 100;
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 1rem;
  font: 400 var(--fs-ui)/1 var(--f-mono); color: var(--ink); background: var(--g-paper);
}
.skip:focus { top: 10px; }   /* light ring: it is drawn on the dark header */

/* ---------- 5. HEADER, DROPDOWNS, PHONE MENU SHEET ---------- */
.hdr {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  min-height: var(--hdr-h); padding: 0 var(--gutter);
  padding-inline: max(var(--gutter), calc((100% - 1360px) / 2 + var(--gutter)));   /* inner edges line up with .page's max width */
  background: var(--g-bench); border-bottom: 1px solid var(--g-rule);
}
.wordmark {
  display: inline-flex; align-items: center; min-height: 44px;
  font: 400 clamp(1.75rem, 1.5rem + .8vw, 2.125rem)/1 var(--f-marker);
  color: var(--g-print); text-decoration: none; white-space: nowrap;
}
.nav { margin-left: auto; display: flex; align-items: center; }
.nav ul { list-style: none; margin: 0; padding: 0; }
.menu-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
  min-height: 44px; min-width: 44px; padding: 0 1rem;
  font: 400 var(--fs-ui)/1 var(--f-mono); color: var(--chalk);
  background: none; border: 1px solid #484848; cursor: pointer;
}
.menu-btn::after, .dd-btn::after { /* wordless caret */
  content: ""; width: 0; height: 0; margin-top: 4px;
  border: 5px solid transparent; border-top-color: currentColor;
}
.menu-btn[aria-expanded="true"]::after { /* drawn × */
  width: 14px; height: 14px; margin: 0; border: 0;
  background:
    linear-gradient(45deg, transparent 43%, currentColor 43% 57%, transparent 57%),
    linear-gradient(-45deg, transparent 43%, currentColor 43% 57%, transparent 57%);
}
.dd-btn {
  display: inline-flex; align-items: center; gap: .5rem; cursor: pointer;
  background: none; border: 0; margin: 0; color: inherit; font: inherit; text-align: left;
}
.dd-btn[aria-expanded="true"]::after { transform: rotate(180deg); margin-top: -4px; }
.sub { display: none; }
.sub.is-open { display: block; }

/* phone + tablet: Menu button opens a full-height sheet; groups expand in place */
@media (max-width: 899.98px) {
  .navpanel {
    display: none;
    position: fixed; left: 0; right: 0; top: var(--hdr-h); bottom: 0; z-index: 49;
    overflow-y: auto; overscroll-behavior: contain;
    padding: 12px var(--gutter) 32px;
    background: rgba(8, 8, 8, .97);
  }
  .navpanel.is-open { display: block; }
  .navlist :focus-visible { outline-color: var(--ink); outline-offset: -3px; }
  .nav .navlist {   /* .nav prefix beats the generic `.nav ul` reset */
    max-width: 30rem; margin-left: auto;
    padding: 4px 20px 12px; color: var(--ink);
    background: var(--tx-paper), var(--g-paper); box-shadow: var(--shadow-lift);
  }
  .navlist > li { border-bottom: 1px dashed var(--g-rule-p); }
  .navlist > li:last-child { border-bottom: 0; }
  .navlist a, .navlist .dd-btn {
    display: flex; align-items: center; width: 100%; min-height: 48px;
    font: 400 var(--fs-ui-l)/1.25 var(--f-mono); color: var(--ink); text-decoration: none;
  }
  .navlist .dd-btn { justify-content: space-between; padding: 0; }
  .nav .navlist .sub { padding: 0 0 8px 1.25rem; }
  .navlist .sub a { min-height: 44px; font-size: var(--fs-ui); }
  .navlist [aria-current="page"] {
    font-weight: 700;
    text-decoration: underline; text-decoration-thickness: 2px; text-decoration-color: var(--g-pencil); text-underline-offset: 6px;
  }
  .navlist .dd-btn.is-current { font-weight: 700; }
}

/* desktop: one line, dropdowns are small index cards */
@media (min-width: 900px) {
  .menu-btn { display: none; }
  .navlist { display: flex; align-items: center; gap: clamp(0px, .4vw, 8px); }
  .navlist > li { position: relative; }
  .navlist > li > a, .navlist .dd-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
    min-height: 44px; min-width: 44px; padding: 0 clamp(.35rem, .8vw, .75rem);
    font: 400 var(--fs-ui)/1 var(--f-mono); color: var(--chalk); text-decoration: none; white-space: nowrap;
  }
  .navlist .dd-btn::after { border-width: 4px; border-top-color: var(--g-holes); }
  .navlist > li > a:hover, .navlist .dd-btn:hover, .navlist .dd-btn[aria-expanded="true"] { color: #ffffff; }
  .navlist > li > a[aria-current="page"], .navlist .dd-btn.is-current { /* grease-pencil underline */
    background: var(--tx-scrawl) center calc(100% - 5px) / calc(100% - 1rem) 6px no-repeat;
  }
  .navlist .dd-btn.is-current[aria-expanded="true"] { background: none; }   /* open card shows the current page itself; no stroke hovering over the card edge */
  .sub {
    position: absolute; top: calc(50% + var(--hdr-h) / 2 + 6px); left: 0; z-index: 60;   /* starts 6px below the header's bottom rule (the item is centred in the header), never across it */
    min-width: 13rem; padding: .375rem 0;
    background: var(--tx-paper), var(--g-paper);
    box-shadow: 0 1px 0 rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.35), 0 18px 30px -4px rgba(0,0,0,.9);   /* a clean dark edge over what it covers */
  }
  .navlist > li:nth-last-child(-n+2) .sub { left: auto; right: 0; }
  .sub a {
    display: flex; align-items: center; min-height: 44px; padding: 0 1.15rem;
    font: 400 var(--fs-ui)/1.2 var(--f-mono); color: var(--ink); text-decoration: none; white-space: nowrap;
  }
  .sub li + li a { border-top: 1px dashed var(--g-rule-p); }
  .sub a:hover { text-decoration: underline; text-underline-offset: 4px; }
  .sub a[aria-current="page"] { font-weight: 700; text-decoration: underline; text-decoration-thickness: 2px; text-decoration-color: var(--g-pencil); text-underline-offset: 4px; }
}

/* static header demos (components.html only) */
.demo-headers { padding: var(--s-4) 0; display: grid; gap: var(--s-4); }
@media (min-width: 900px) { .demo-headers { padding-bottom: 270px; } }   /* room for the open demo dropdown */
.hdr--demo { position: relative; z-index: 40; }

/* ---------- 6. PAGE FRAME + PAGE HEAD ---------- */
main { display: block; }
.page {
  max-width: 1360px; margin: 0 auto;
  padding: var(--s-4) var(--gutter) var(--s-5);
}
/* gallery pages (contact sheets, the photos landing) may use a wider page (render.GALLERY_PAGE_MAX) */
.wide:has(> .sheet-wrap, > .prints) { max-width: 1456px; }
/* galleries, the photos landing and videos, from 600px, in BOTH regimes: the page is ONE centred column that hugs
   its content (title, then the sheet / prints / video sheet, then the old-only tab), so the title starts exactly on
   the sheet's left edge at every width. The column may run to 16px from the screen edge (render.SIDE): a sheet
   wider than the page borrows the gutters instead of shrinking its photos. The header does not move. */
@media (min-width: 600px) {
  .wide:has(> .sheet-wrap, > .videos, > .prints) {
    display: grid; grid-template-columns: minmax(0, max-content); justify-content: center; align-content: start;
    padding-inline: 16px;
  }
}
.scrawl { margin: 0; font: 400 var(--fs-page)/.95 var(--f-marker); color: var(--g-print); }
.pagehead { margin-bottom: var(--s-5); }
.wide > .pagehead { margin-bottom: 4rem; }   /* the title stroke sits 16px+ clear of the tape tabs of the sheet / month card below */
.pagehead:has(+ .paper) { margin-bottom: 4rem; }   /* the title stroke must sit well clear of the paper's tape tabs (never nearly touching) */
/* page title (and a gig page's second year): tilted, with ONE grease-pencil stroke under its last line, as long as
   that line (the text is one inline box, .hl; an absolutely positioned child of an inline box is placed against its
   first fragment's start and its last fragment's end, so left:0 / right:0 / bottom:0 is exactly the last line). A
   title that wraps never gets a stack of stroked lines; its lines sit at the title's normal spacing. */
.pagehead .scrawl, .months + .scrawl { width: fit-content; max-width: 100%; transform: rotate(-2deg); transform-origin: 0 0;   /* pivot at the top-left: no line of a tilted title ever leans past the page gutter */ padding-right: .1em; line-height: 1.32; text-wrap: balance; }
/* long titles (set by length at build time) are smaller, so they sit on one line from 1024px */
.pagehead .scrawl--long { font-size: clamp(3rem, 1.9rem + 4.2vw, 5.5rem); }
.pagehead .scrawl--longer { font-size: clamp(2.5rem, 1.5rem + 3.1vw, 3.75rem); }
.pagehead .scrawl > .hl, .months + .scrawl > .hl { position: relative; padding-bottom: .26em; }   /* .26em: the stroke clears descenders */
.pagehead .scrawl > .hl::after, .months + .scrawl > .hl::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 9px;
  background: var(--tx-scrawl) 0 0 / 100% 100% no-repeat;
}
.tape {
  display: table; margin: var(--s-4) 0 0; max-width: 100%;
  padding: .5rem 1.25rem .45rem;
  font: 400 var(--fs-ui)/1.4 var(--f-mono); color: var(--ink);
  background: var(--tx-paper), var(--g-tape);
  clip-path: polygon(0 6%, 3% 0, 6% 8%, 9% 1%, 92% 0, 95% 8%, 98% 2%, 100% 10%, 99% 94%, 96% 100%, 93% 92%, 90% 99%, 7% 100%, 4% 93%, 1% 99%);
  transform: rotate(-1.2deg);
}
.pagehead > .tape:first-child { margin: 0 0 var(--s-3); } /* a tape above the title (gig years: "calendar") */
/* a tape under a title: its top sits 16px+ below the title's tilted stroke at every width (the stroke dips at its
   left end, the tape at its right) */
.pagehead > .scrawl + .tape { margin-top: clamp(2.25rem, 1.5rem + 2vw, 3.5rem); }
.slip {
  display: inline-block; margin: 0; padding: .45rem .85rem;
  font: 400 var(--fs-ui)/1.45 var(--f-mono); color: var(--ink);
  background: var(--tx-paper), var(--g-paper); box-shadow: var(--shadow-slip);
}

/* ---------- 7. PAPER (long stories) ---------- */
.paper {
  position: relative; max-width: 46rem;
  padding: var(--s-4) clamp(18px, 4.5vw, 3.25rem);
  color: var(--ink); background: var(--tx-paper), var(--g-paper); box-shadow: var(--shadow-lift);
  overflow-wrap: break-word;
}
.paper::before, .paper::after,   /* two tape tabs holding the sheet down: every paper, contact sheet, video sheet and */
.sheet::before, .sheet::after,   /* gig month card on the bench carries the same pair, the same way. Each reaches 14px  */
.videos::before, .videos::after, /* into the sheet, so what is under it starts 30px+ down (16px+ clear)               */
.month::before, .month::after {
  content: ""; position: absolute; z-index: 3; top: -11px; width: 72px; height: 22px;
  background: var(--tape-rgba); clip-path: var(--tape-clip); pointer-events: none;
}
:is(.paper, .sheet, .videos, .month)::before { left: 22px; transform: rotate(-5deg); }
:is(.paper, .sheet, .videos, .month)::after  { right: 22px; transform: rotate(5deg); }
.paper > :first-child { margin-top: 0; }
.paper > :last-child { margin-bottom: 0; }
.paper p, .paper ul, .paper ol, .paper blockquote, .paper .table-wrap { max-width: var(--measure); }
.paper p { margin: 0 0 1em; }
.paper strong { font-weight: 600; }
.paper a { color: var(--ink); text-decoration-thickness: 1px; text-underline-offset: 3px; }
.paper a:hover { text-decoration-thickness: 2px; }
/* tappable phone / email (contact, publicity): a 44px-tall tap area without moving the line */
.paper a[href^="tel:"], .paper a[href^="mailto:"] { display: inline-block; padding: 11px 0; margin: -11px 0; }
/* ONE heading hierarchy on paper, by level and pattern (never by words):
     .sect divider (28-34px type, dashed rule above, stroke)  >  h2 section heading (22-26px type, stroke)
     >  title / label tier: h3, .h3, .h4, .lead labels, typed story titles (strong.lbl), the opening lines
        (19-21px type, never smaller than body, no stroke)  >  body (17-19px serif).
   Typewriter = a title or label. Bold serif = emphasis inside running text only. A whole sentence the source
   marks as a heading (.run) is reading text. The pencil stroke follows the heading's text line by line (.hl, one
   inline box per heading), so it never underlines a fragment and never sits under a label or an address. */
.paper h2, .paper h3, .paper h4, .paper .lead {
  margin: var(--s-4) 0 var(--s-2);
  font: 400 var(--fs-h3)/1.4 var(--f-type); color: var(--ink);
  overflow-wrap: break-word;
}
.paper h2 { font-size: var(--fs-h2); }
.paper h2 > .hl, .paper .sect > .hl {
  padding-bottom: calc(.16em + 4px);
  background: var(--tx-pencil) 0 100% / 100% 5px no-repeat;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
/* a linked post title (every blog post, and the same post embedded in news): ONE look everywhere: no stroke, a thin
   straight ink underline at rest (a link, like every link on paper, never the pencil divider stroke), set tight (a
   wrapped title reads as one title), a heavier underline on hover / focus. The link is a 44px tap target without
   moving the line. */
.paper h2:has(> .hl > a) > .hl { background: none; padding-bottom: 0; }
.paper h2:has(> .hl > a), .paper .post > h2 { line-height: 1.3; }
.paper h2 > .hl > a, .paper .post > h2 > a {
  display: inline-block; padding: 12px 0; margin: -12px 0;
  text-decoration: underline; text-decoration-thickness: 1px; text-decoration-color: var(--ink); text-underline-offset: 5px;
}
.paper h2 > .hl > a:is(:hover, :focus-visible), .paper .post > h2 > a:is(:hover, :focus-visible) {
  text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 5px;
}
/* a web address inside a typed heading or label keeps the typewriter face, a step smaller: a line never drops into
   the body serif halfway along. A body-styled heading (.run, .h5, .h6) is running text: its address keeps the text's size */
.paper :is(h2, h3, h4, .lead, .open, strong.lbl):not(.run, .h5, .h6) .seg { font-size: .85em; }
/* "Label: https://…" (render.LABEL_ADDR_RE): the address on its own line under the label, set exactly like the same
   kind of entry whose address is typed as its own paragraph (calendar) */
.paper .addr { display: block; margin-top: var(--s-2); font: 400 var(--fs-body)/1.6 var(--f-body); }
.paper :is(h2, h3, h4) > .hl > .addr > .seg { font-size: inherit; }
/* phones: a heading that wraps gets ONE calm stroke, under its last line only (as long as that line), not a stack of
   ruled lines. An absolutely positioned child of an inline box is placed against the box's first fragment's start
   and its last fragment's end, so left:0 / right:0 / bottom:0 is exactly the last line. */
/* (at every width: a stacked stroke under each line reads as ruled lines / a strike-through) */
.paper h2 { line-height: 1.45; margin-bottom: var(--s-2); text-wrap: balance; }   /* clear air between the stroke and what follows */
.paper h2 > .hl { background: none; padding-bottom: 0; }   /* story titles carry no stroke (see below) */
.paper .sect > .hl { position: relative; background: none; padding-bottom: calc(.16em + 4px); }
.paper .sect > .hl::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 5px;
  background: var(--tx-pencil) 0 0 / 100% 100% no-repeat;
}
.paper h2:has(> .hl > a) > .hl::after { content: none; }
/* STORY TITLES: one tier whatever tag the source used (an h2 band name, a bold-only label, a typed title on its
   own line above its story): Special Elite at section size, no stroke (only a .sect divider carries the pencil),
   so sibling stories never read at two ranks. Below a divider, above the body. */
.paper h2:not(.h3, .h4, .run, .sect), .paper .lead:not(.run), .paper p.ttl > strong.lbl:first-child {
  font-size: var(--fs-h2);
}
.paper :is(h2, h3, h4, .lead, .open) strong { font-weight: 400; }   /* the typewriter face has no bold */
/* clamped levels keep their source look: an h2 that was a source h3/h4 is a label */
.paper h2:is(.h3, .h4) { font-size: var(--fs-h3); line-height: 1.4; margin-bottom: var(--s-2); }
.paper h2:is(.h3, .h4) > .hl { background: none; padding-bottom: 0; }
/* a divider that opens straight into sub-headings or labels ("Past Credits:"): biggest, with a ruled break above */
.paper .sect {
  margin-top: var(--s-5); padding-top: var(--s-4);
  font-size: var(--fs-sect); line-height: 1.6; border-top: 2px dashed var(--g-rule-p);
}
.paper > .sect:first-child { border-top: 0; padding-top: 0; }
/* typed story title or run-in label on its own line inside a paragraph */
.paper strong.lbl { display: inline-block; font: 400 var(--fs-h3)/1.4 var(--f-type); text-wrap: balance; }
.paper p.ttl > strong.lbl:first-child { margin-bottom: .45em; }   /* the title sits off its body */
.paper br + strong.lbl { margin-top: var(--s-3); margin-bottom: .25em; }   /* a run-in label after a line break ("News flash :"): clear air above, a little below */
/* a label that closes its paragraph after a line break ("…<br><strong>Other Recording Credits:</strong>") heads what
   follows it: the same space and dashed story rule as its sibling labels (structural: last element after a <br>) */
.paper p > br + strong.lbl:last-child { display: block; margin-top: var(--s-4); }
.paper p > br + strong.lbl:last-child::before {
  content: ""; display: block; width: 100cqw; margin-bottom: var(--s-4); border-top: 1px dashed var(--g-rule-p);
}
/* the paper's opening line(s) (credits' two headline facts, a short page's first line): typed at section size,
   closed off from the page below by a dashed rule */
.paper .open { margin: 0 0 var(--s-2); font: 400 var(--fs-h2)/1.45 var(--f-type); color: var(--ink); max-width: var(--measure); }
.paper .open:not(:has(+ .open)) { padding-bottom: var(--s-3); margin-bottom: var(--s-4); border-bottom: 2px dashed var(--g-rule-p); }
@media (max-width: 599.98px) { .paper .open { line-height: 1.35; } }   /* phones: the same size as the story titles (never larger, never smaller), set a little tighter */
/* whole sentences marked as headings, and source h5/h6: ordinary reading text */
.paper .run, .paper h5, .paper h6, .paper .h5, .paper .h6 {
  margin: 0 0 1em; font: 400 var(--fs-body)/1.6 var(--f-body); color: var(--ink);
}
.paper :is(.run, .open, .h5, .h6) > .hl { background: none; padding: 0; }
.paper .run strong { font-weight: 400; }
.paper :is(h5, h6, .h5, .h6) strong { font-weight: 600; }
/* consecutive titles / labels with no text between them stay together. A body-styled sentence (.run, .h5, .h6)
   is reading text, never a heading: it never holds the next title or label against itself. */
.paper :is(h2, h3, h4, .lead):not(.run, .h5, .h6) + :is(h3, h4, .lead):not(.run, .h5, .h6, .sect) { margin-top: var(--s-2); }
.paper :is(h2, .lead) + h2 { margin-top: var(--s-2); }
/* each story / section starts with clear space and a dashed rule: an h2, a label, a typed story title, or the
   line that introduces a title (.intro: "… presenting;" right above the workshop title) */
.paper { container-type: inline-size; }   /* lets a story rule span the paper's full text width (100cqw) */
.paper > :is(h2, .lead, .ttl, .intro):not(:first-child, .sect, .open, .h3, .h4, .run) { margin-top: var(--s-4); }
.paper > :is(h2, .lead, .ttl, .intro):not(:first-child, .sect, .open, .h3, .h4, .run)::before {
  content: ""; display: block; width: 100cqw; margin-bottom: var(--s-4); border-top: 1px dashed var(--g-rule-p);
}
/* ... except straight under another title-tier heading or label (the story's own head), and a title right under
   the line that introduces it */
.paper > :is(h2, h3, h4, .lead, .intro):not(.run, .h5, .h6) + :is(.lead, .ttl, .intro),
.paper > :is(h2, .sect, .intro):not(.run) + h2:not(.sect) { margin-top: var(--s-2); }
.paper > :is(h2, h3, h4, .lead, .intro):not(.run, .h5, .h6) + :is(.lead, .ttl, .intro)::before,
.paper > :is(h2, .sect, .intro):not(.run) + h2:not(.sect)::before { content: none; }
.paper > .intro { margin-bottom: var(--s-2); }
.paper > .open + :is(h2, .lead, .ttl) { margin-top: 0; }
.paper > .open + :is(h2, .lead, .ttl)::before { content: none; }
.paper > .sect:not(:first-child) { margin-top: var(--s-5); padding-top: var(--s-4); border-top: 2px dashed var(--g-rule-p); }
.paper > :first-child { margin-top: 0; }   /* repeated after the heading rules so they cannot add air at the paper's edges */
.paper > :last-child { margin-bottom: 0; }
/* the "Posted on … by …" line of a post, the same on the blog and where a post is embedded (news) */
.paper .meta { font: 400 var(--fs-ui)/1.45 var(--f-mono); color: var(--ink-soft); text-wrap: balance; }   /* never an orphaned last word */
.paper .meta a { color: inherit; }
/* a plain-text web address: unbreakable pieces, so it wraps only after "/" or before "." */
.seg { display: inline-block; overflow-wrap: anywhere; }
a .seg { text-decoration: inherit; }
/* keyboard focus on a link whose text is cut into pieces (a web address): ONE calm mark for the whole link, a grey
   tint behind its text and a heavy ink underline, line by line. No ring per piece, nothing crosses the lines around. */
:is(.paper, .month) a:has(.seg):focus-visible {   /* tint 3.0:1 against the paper, ink text 4.9:1 on it, ink underline 14.9:1 */
  outline: none; background: var(--g-focus);
  text-decoration-line: underline; text-decoration-thickness: 3px; text-decoration-color: var(--ink);
}
.paper ul, .paper ol { margin: 0 0 1.1em; padding: 0; }
.paper ul { list-style: none; }
.paper ol { padding-left: 1.6rem; }
.paper ul > li { position: relative; padding-left: 1.5rem; margin: 0 0 .6em; }
.paper ul > li::before { /* pencil tick */
  content: ""; position: absolute; left: 0; top: .8em; width: .85rem; height: 2px;
  background: var(--g-pencil); transform: rotate(-8deg); border-radius: 2px;
}
.paper ol > li { margin: 0 0 .6em; padding-left: .25rem; }
/* runs of names: a short list or run of short paragraphs set in columns */
.paper .names { columns: 2 12rem; column-gap: 2rem; margin: 0 0 1.1em; max-width: var(--measure); }
.paper .names > li, .paper .names > p { break-inside: avoid; }
.paper .names > p { margin: 0 0 .45em; padding-left: .9em; text-indent: -.9em; }   /* hanging indent: a wrapped name's second line reads as its continuation, never as another name */
@media (max-width: 480px) {   /* phones: still two columns, tighter */
  .paper .names { columns: 2; column-gap: 1rem; }
  .paper .names > li { padding-left: 1.1rem; }
  .paper .names > li::before { width: .65rem; }
  .paper .names > li:has(.seg) { column-span: all; }   /* a name with a web address takes the list's full width: the address never splits mid-word */
}
@media (max-width: 380px) {   /* 360: two columns would split names ("Joseph / Modeliste") and web addresses */
  .paper .names { columns: 1; }
}
.paper hr { border: 0; height: 6px; margin: var(--s-4) 0; max-width: 12rem; background: var(--tx-pencil) center / 100% 100% no-repeat; }

/* ---------- 8. PRINT (single photo, white border, tape, caption in the bottom margin) ---------- */
.print {
  position: relative; display: block; width: fit-content; max-width: 100%;
  margin: var(--s-5) 0 var(--s-4);
  padding: 15px 10px 12px; background: var(--g-print); box-shadow: var(--shadow-lift);
}
.print::before { /* one strip of masking tape across the top border: its lowest corner (tilt included) stays 5px+ clear
                    of the photo, which starts 15px down */
  content: ""; position: absolute; top: -15px; left: 50%; z-index: 2;
  width: 84px; height: 22px; margin-left: -42px;
  background: var(--tape-rgba); clip-path: var(--tape-clip); transform: rotate(-3deg);
}
.print a, .print > img { display: block; line-height: 0; }
.print img, .frame img, .lightbox img { display: block; filter: grayscale(1) contrast(1.05); }
.print img { width: calc(var(--w, 364) * 1px); max-width: 100%; height: auto; }
.print figcaption {   /* typed in the bottom margin of the print; never wider than the photo */
  width: 0; min-width: 100%;
  padding: 10px 2px 0;
  font: 400 var(--fs-ui)/1.45 var(--f-mono); color: var(--ink);
}
.print figcaption em { font-style: italic; }
.paper .print { box-shadow: 0 1px 0 rgba(0,0,0,.25), 0 6px 14px -6px rgba(0,0,0,.45); }

/* ---------- 9. CONTACT SHEET + SHOTS ---------- */
/* TWO REGIMES, ONE BREAKPOINT: 1336px for every gallery sheet and the photos landing (docs/design-system.md 13.0;
   render.py computes GALLERY_BP and stops the build if this file disagrees: the smallest 8px step at which every
   photo on every sheet keeps >= 92% of the size it has in single file just below it, sweep rule S9, with a 17px
   desktop scrollbar allowed for; live is the worst sheet). Nothing else changes the layout of a sheet.
   NARROW (< 1336px): single file. Every frame centred at its natural size (photo + film edge), capped by the sheet;
     its caption directly under it at the frame's width. Never two frames on one row. Under 600px the sheet runs
     edge to edge; from 600px it is only as wide as its widest frame and heads the page's centred column (section 6).
   WIDE (>= 1336px): a grid of a column count fixed per sheet at build time (sheet--c2 / sheet--c3), each column as
     wide as its widest frame (max-content), rows top-aligned. The frames of a row close onto fixed gutters: in two
     columns the left frame sits against the right edge of its column and the right frame against the left edge of
     its column, so every pair is exactly 24px apart; three columns: start / centre / end. Captions follow their
     frame. The sheet hugs its grid. Lost-photo lines span the grid. */
.sheet {
  --edge: 8px;   /* film edge around the picture */
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  gap: 30px; padding: 32px 20px 28px;   /* 32px on top: the tape tabs (section 7) sit 16px+ clear of the first frame */
  background: var(--tx-paper), var(--g-sheet); box-shadow: var(--shadow-lift);
  color: var(--ink);
}
.shot {   /* natural size, never past it; shrinks only to fit the sheet */
  margin: 0; flex: 0 0 auto;
  width: calc(var(--w) * 1px + 2 * var(--edge)); max-width: 100%;
}
.frame {   /* one cut frame of film: one slim sprocket edge along the top */
  display: block; line-height: 0; width: 100%;
  padding: 18px var(--edge) var(--edge);
  background: var(--tx-holes) 0 4px / 20px 10px space no-repeat, var(--g-film);   /* whole holes only, never a cut one */
}
.frame img { width: 100%; height: auto; }
/* keyboard focus on a frame: a light ring with a dark halo (never read as a thicker black frame) */
.sheet .frame:focus-visible { outline: 3px solid var(--g-print); outline-offset: 2px; box-shadow: 0 0 0 7px var(--ink); }
.shot figcaption {
  margin-top: 10px; padding: 0 2px;
  font: 400 var(--fs-ui)/1.45 var(--f-mono); color: var(--ink);
  hyphens: manual; overflow-wrap: break-word;
}
.nw { white-space: nowrap; }   /* a hyphenated name, a phone number, a mark typed after a space, "/ Word" in a title */
/* caption-only entries (photo lost): a full-width run of typed lines at the end of the sheet. Inline-size
   containment: their long lines never make the sheet (or a grid column) wider than its frames */
.sheet .shot--missing { flex: none; align-self: stretch; width: auto; max-width: none; contain: inline-size; }
.shot--missing figcaption { margin: 0; padding: .1rem 0 .1rem .8rem; border-left: 3px solid var(--g-pencil); }
.shot:not(.shot--missing) + .shot--missing { padding-top: 18px; border-top: 1px dashed var(--g-rule-p); }
.shot--missing + .shot--missing { margin-top: -18px; }
@media (max-width: 599.98px) {   /* phones: the sheet runs edge to edge so photos get nearly the full width; every
                                    line of text sits at the shared 16px inset (8px sheet padding + 8px) */
  .sheet { --edge: 4px; margin-inline: calc(-1 * var(--gutter)); padding: 32px var(--pad-phone) 24px; gap: 36px; }   /* 36px between frames, as in the wide grid */
  .frame { padding-top: 16px; background-position: 0 3px, 0 0; }
  .shot figcaption { padding: 0 calc(var(--inset-text) - var(--pad-phone)); }
  .shot--missing figcaption { margin-left: calc(var(--inset-text) - var(--pad-phone)); }
}
@media (min-width: 600px) {   /* both regimes on a wide screen: the sheet hugs its frames / its grid; sheet and tab
                                  start on the column's (= the title's) left edge */
  .sheet-wrap { display: grid; grid-template-columns: minmax(0, max-content); justify-content: start; }
  .sheet-wrap > .old { justify-self: start; }
}
@media (min-width: 600px) and (max-width: 1335.98px) {   /* narrow regime: the sheet sides grow from 8px at 600px (as
     on phones, so a photo there keeps 92%+ of the edge-to-edge one at 599px) to 24px from 1000px (render.narrow_pad) */
  .sheet-wrap > .sheet { padding-inline: clamp(8px, 4vw - 16px, 24px); }
}
@media (min-width: 1336px) {   /* wide regime */
  .sheet:is(.sheet--c2, .sheet--c3) {
    display: grid; grid-template-columns: repeat(2, minmax(0, max-content)); justify-content: center; align-items: start;
    gap: 36px 24px; padding: 32px 24px 24px;   /* 32px on top for the tape tabs; render.WIDE_PAD / WIDE_GAP */
  }
  .sheet.sheet--c3 { grid-template-columns: repeat(3, minmax(0, max-content)); }
  .sheet.sheet--c2 > .shot:nth-child(odd of .shot:not(.shot--missing)) { justify-self: end; }
  .sheet.sheet--c2 > .shot:nth-child(even of .shot:not(.shot--missing)) { justify-self: start; }
  .sheet.sheet--c3 > .shot:nth-child(3n + 1 of .shot:not(.shot--missing)) { justify-self: start; }
  .sheet.sheet--c3 > .shot:nth-child(3n + 2 of .shot:not(.shot--missing)) { justify-self: center; }
  .sheet.sheet--c3 > .shot:nth-child(3n of .shot:not(.shot--missing)) { justify-self: end; }
  .sheet:is(.sheet--c2, .sheet--c3) > .shot--missing { grid-column: 1 / -1; justify-self: stretch; }
  .shot--missing + .shot--missing { margin-top: -24px; }
}

/* ---------- 10. PRINTS ROWS (photos landing: big prints with captions) ----------
   The same two regimes and the same breakpoint as the sheets. Narrow: single file, every print on the title's left
   edge. Wide: fixed rows of prints at their natural size (render.py emits them as .prints-row: prints--c3 = three,
   then two), the block as wide as its widest row and starting on the title's left edge; a shorter row sits centred
   under the one above. Tops level; each print's paper ends under its own caption (never stretched). */
.prints {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--s-5); padding-top: var(--s-3);
}
.prints-row { display: contents; }
.prints .print { margin: 0; }
@media (min-width: 1336px) {
  .prints { align-items: center; }
  .prints-row { display: flex; align-items: flex-start; gap: 32px; }   /* 32px: render.PRINT_GAP */
}

/* ---------- 11. VIDEO FACADE + VIDEOS GRID ---------- */
.video { margin: var(--s-4) 0; max-width: 480px; }
.video-box { position: relative; aspect-ratio: 24 / 13; background: var(--g-film); }   /* 480 x 260: a hair wider than 16:9 so cover trims 50px of 360 at top and bottom, the deepest baked bar on any thumbnail is 49px */
.video-btn {
  position: absolute; inset: 0; display: block; width: 100%; height: 100%;
  padding: 0; border: 0; cursor: pointer; background: #2b2b2b;   /* plain grey box until a thumbnail exists */
  overflow: hidden;
}
/* cover trims the baked letterbox bars (never upscaled: the box is never wider than 480px), so no dark sliver shows */
.video-btn img { display: block; width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) contrast(1.05); }
.video-btn.is-blank img { visibility: hidden; }
.video-btn::after { /* wordless play mark: a small dark chip with a thin light edge (it reads on dark thumbnails too)
                       and a white triangle, centred */
  content: ""; position: absolute; left: 50%; top: 50%; width: 56px; height: 40px; margin: -20px 0 0 -28px;
  border-radius: 8px; background: rgba(10,10,10,.82); box-shadow: 0 0 0 1px rgba(255,255,255,.35);
}
.video-btn::before {
  content: ""; position: absolute; left: 50%; top: 50%; z-index: 1; margin: -9px 0 0 -6px;
  border-style: solid; border-width: 9px 0 9px 15px; border-color: transparent transparent transparent var(--g-print);
}
.video-btn:hover::after, .video-btn:focus-visible::after { background: rgba(0,0,0,.95); }
.video-box iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video figcaption { margin-top: 10px; font: 400 var(--fs-ui)/1.45 var(--f-mono); overflow-wrap: anywhere; }
.paper .video figcaption { color: var(--ink); }
/* videos page: the grid lies on the same grey contact-sheet paper as the galleries (no film edge), held down by the
   same tape tabs. TWO REGIMES, ONE BREAKPOINT (1008px, render.VIDEO_BP: the smallest 8px step at which two boxes side
   by side keep >= 92% of the 480px they have in one column, sweep rule S9, a 17px scrollbar allowed for).
   NARROW: one column of boxes at most 480px (the thumbnail's real size); edge to edge under 600px, a centred sheet
   hugging the column from 600px. WIDE: exactly two columns of at most 480px at every width, the sheet hugging its
   grid, centred. Never three, never back to one. */
.videos {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-4) 24px;
  padding: 32px clamp(20px, 2.5vw, 24px) 24px; color: var(--ink);
  background: var(--tx-paper), var(--g-sheet); box-shadow: var(--shadow-lift);
}
.videos .video { margin: 0; width: 100%; }
.videos .video figcaption { color: var(--ink); }
.videos :focus-visible { outline-color: var(--ink); }
@media (max-width: 599.98px) {
  .videos { margin-inline: calc(-1 * var(--gutter)); padding: 32px var(--pad-phone) 24px; }
  .videos .video figcaption { padding: 0 calc(var(--inset-text) - var(--pad-phone)); }
}
@media (min-width: 600px) {   /* the sheet heads the page's centred column (section 6), on the title's left edge */
  .videos { width: fit-content; max-width: 100%; margin-inline: 0; grid-template-columns: minmax(0, 480px); }
}
@media (min-width: 1008px) {
  .videos { grid-template-columns: repeat(2, minmax(0, 480px)); padding-inline: 24px; }   /* render.VIDEO_PAD / VIDEO_GAP */
}

/* ---------- 12. OLD-ONLY INDEX CARD (older material from the previous site; wordless) ---------- */
.old {
  position: relative; margin: var(--s-5) 0 var(--s-4); max-width: var(--measure);
  padding: 1.25rem clamp(16px, 3vw, 1.75rem);
  color: var(--ink);
  background: var(--tx-paper), var(--tx-paper) 120px 80px, var(--g-old);                /* double grain = older stock */
  clip-path: polygon(0 0, calc(100% - 26px) 0, 100% 26px, 100% 100%, 0 100%);           /* dog-eared corner */
}
.old > :first-child { margin-top: 0; }
/* on gallery pages an old-only heading is a compact typed label, flush with the sheet's left edge */
:is(.wide, .sheet-wrap) > .old { max-width: max-content; margin: var(--s-4) 0 0; padding: .7rem 2.75rem .7rem 1.1rem; }   /* its own small card, 32px under the sheet, left edge on the sheet's left edge (never 16px: that reads as nearly touching) */
:is(.wide, .sheet-wrap) > .old :is(h2, h3) { margin: 0; padding: 0; background: none; font: 400 var(--fs-ui-l)/1.35 var(--f-mono); color: var(--ink); }
:is(.wide, .sheet-wrap) > .old :is(h2, h3) strong { font-weight: 400; }
.old::after { /* the folded-over corner */
  content: ""; position: absolute; top: 0; right: 0; width: 26px; height: 26px;
  background: linear-gradient(225deg, transparent 50%, #b0b0b0 50%);
}
.old > :last-child { margin-bottom: 0; }
.old + .old { margin-top: var(--s-4); }
.paper .old p { max-width: none; }
.paper .old { max-width: none; }   /* on a paper the card spans the text column: its edges are the text's left edge and the dashed rules' right end */

/* ---------- 13. BLOG POST ---------- */
.post { scroll-margin-top: var(--s-2); }   /* on top of html's scroll-padding (header + 16px) */
.post + .post { margin-top: var(--s-5); padding-top: var(--s-4); border-top: 1px dashed var(--g-rule-p); }
.post > h2 { margin-top: 0; }   /* its link: the linked-title look in section 7 */

/* ---------- 14. GIG ARCHIVE ----------
   TWO REGIMES, ONE BREAKPOINT (1024px; site.js uses the same media query for the months' default state).
   NARROW (< 1024px): every month card full width, one column of rows; every month but the first starts closed.
   WIDE (>= 1024px): every month starts open; a month of 5+ rows runs its rows in two balanced columns; a month of
     1-4 rows is one column and its card only as wide as a column.
   NARROW cards share one width on a page, min(100%, 34rem), left-aligned under the title: the paper is only as wide
   as a column of rows needs, never half blank (the loose-lines ledger hugs its lines).
   A row lays itself out by its OWN width (container query, not a viewport band): day | weekday | entry when the row
   is 25rem or wider, otherwise the weekday sits above the entry. */
.yearnav ul {   /* four equal tabs on one line at every width (320px up) */
  list-style: none; margin: 0 0 calc(var(--s-4) + 12px); padding: 0;   /* +12px: the tabs sit about 30px clear of the first card's tape */
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; max-width: 24rem;
}
@media (min-width: 600px) { .yearnav ul { gap: 16px; max-width: 26rem; } }
/* the gig masthead stays compact, so the ledger starts on the first screen: the year tabs sit 32px under the title's
   stroke (they carry no tape); from 1024px they sit beside the title, their bottom on the title's last line */
.wide > .pagehead:has(+ .yearnav) { margin-bottom: var(--s-4); }
@media (min-width: 1024px) {
  /* the column is capped at 68rem (two entry columns at a comfortable width; no half-blank cards on wide screens) and
     centred like every other page; title, year tabs and cards keep one shared left edge */
  .wide:has(> .months) { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--s-6); max-width: calc(68rem + 2 * var(--gutter)); }
  .wide:has(> .months) > * { grid-column: 1 / -1; }
  .wide:has(> .months) > .pagehead { grid-column: 1; margin-bottom: 0; }
  .wide:has(> .months) > .yearnav { grid-column: 2; align-self: end; margin-bottom: .6rem; }
  .wide:has(> .months) > .yearnav ul { margin-bottom: 0; }
  .wide:has(> .months) > .months:first-of-type { margin-top: 3.75rem; }   /* the title's stroke about 30px clear of the first card's tape (never nearly touching) */
}
.yearnav a {
  display: flex; align-items: center; justify-content: center; min-height: 44px; min-width: 0; padding: 0 .5rem;
  font: 400 var(--fs-ui-l)/1 var(--f-mono); color: var(--chalk); text-decoration: none;
  border: 1px solid #484848;
}
.yearnav a:hover { border-color: var(--chalk-soft); }
.yearnav a[aria-current="page"] { color: var(--ink); background: var(--tx-paper), var(--g-tape); border-color: var(--g-tape); font-weight: 700; }
.months + h2.scrawl { margin: var(--s-6) 0 var(--s-5); font-size: clamp(3rem, 2rem + 4vw, 5rem); }   /* second year heading: a smaller sibling of the page title (same tilt and stroke, section 6) */
.months { display: grid; gap: var(--s-4); grid-template-columns: minmax(0, 1fr); align-items: start; }
.month {
  --card-pad: clamp(1.1rem, .7rem + 1.2vw, 1.5rem);
  position: relative;   /* holds the tape tabs (section 7) */
  color: var(--ink); background: var(--tx-paper), var(--g-paper); box-shadow: var(--shadow-lift);
}
.month > summary {
  display: flex; align-items: center; gap: .75rem;
  margin-top: 22px; padding: 8px var(--card-pad) 12px;   /* 30px above the name: 16px+ below the tape tabs. The 22px
     is margin, so the stuck summary under the header stays one slim 51px band */
  cursor: pointer; list-style: none;
  font: 400 var(--fs-h2)/1.2 var(--f-type);
}
.month > summary::-webkit-details-marker { display: none; }
.month > summary::before { /* wordless caret */
  content: ""; flex: none; border-style: solid; border-width: 6px 0 6px 9px;
  border-color: transparent transparent transparent var(--ink);
}
.month[open] > summary::before { transform: rotate(90deg); }
.month[open] > summary { border-bottom: 2px solid var(--g-rule-p); }
/* a closed month is a plain stub: no tape (nothing to hold down), its name centred in it */
.month:not([open])::before, .month:not([open])::after { content: none; }
.month:not([open]) > summary { margin-top: 0; padding-block: 18px; }
.gigs { list-style: none; margin: 0; padding: .25rem var(--card-pad) 1rem; }
/* a row is a grid of fixed cells: day | entry column (3.5rem | rest); when the row is wide enough the entry column
   splits into weekday (6.75rem) | entry. The day (and, beside the entry, the weekday) stays in view under the stuck
   month name while a long entry scrolls past, and leaves with the end of its own row (sticky inside the row). */
.gig {
  position: relative; container-type: inline-size;
  display: grid; grid-template-columns: 3.5rem 6.75rem minmax(0, 1fr); align-items: start;
  min-height: calc(1.5rem + 1.3rem); padding: .65rem 0;
  font: 400 var(--fs-ui)/1.45 var(--f-mono);
  --stick: calc(var(--hdr-h) + 22px + 1.2 * var(--fs-h2) + .65rem);   /* header + the stuck month name (section 14 summary) + the row's own top padding */
}
.gig + .gig { border-top: 1px dashed var(--g-rule-p); }   /* separators BETWEEN rows: no rule ever ends a column */
.gig-d { grid-column: 1; grid-row: 1 / span 2; position: sticky; top: var(--stick); width: 2.75rem; font-weight: 700; font-size: 1.25rem; line-height: 1.2; }
.gig-w { grid-column: 2 / -1; grid-row: 1; display: block; color: var(--ink-soft); }
.gig-t { grid-column: 2 / -1; grid-row: 2; margin: 0; overflow-wrap: anywhere; }
.gig-t a { color: var(--ink); text-underline-offset: 3px; }
.gig-t strong { font-weight: 700; }
@container (min-width: 25rem) {   /* day | weekday | entry ("wednesday" fits 6rem) */
  .gig-w { grid-column: 2; width: 6rem; position: sticky; top: var(--stick); }
  .gig-t { grid-column: 3; grid-row: 1; }
}
/* a long open month keeps its name in view under the header, at every width */
.month[open] > summary { position: sticky; top: var(--hdr-h); z-index: 2; background: var(--tx-paper), var(--g-paper); }
html:has(.month) { scroll-padding-top: calc(var(--hdr-h) + 52px + 16px); }
/* NARROW: months open from the top until 20+ gigs show (site.js); the closed months after them are a compact typed
   ledger, not a stack of separate stubs: slim bars joined edge to edge into one card (a dashed rule between two),
   two bars to a row from 480px (site.js marks each closed month's place in its run: m-r2 = right-hand bar, m-j2 =
   joined to the row above, m-s2 = the last bar of an odd run, which takes the full row). An open month always takes
   the full card width and starts its own row. The ledger's shadow is drawn under the whole run (z-index -1), so no
   bar casts a shadow onto its neighbour. */
@media (max-width: 1023.98px) {
  .month { width: 100%; max-width: 34rem; }
  .months { max-width: 34rem; column-gap: 0; }
  .month[open] { grid-column: 1 / -1; }
  .month:not([open]) { box-shadow: none; }
  .months > .month:not([open])::before {
    content: ""; inset: 0; width: auto; height: auto; z-index: -1;
    background: none; clip-path: none; transform: none; box-shadow: var(--shadow-lift);
  }
  .month:not([open]) > summary { padding-block: 12px; }
  .months + .paper { width: 100%; max-width: 34rem; }   /* the loose-lines ledger: exactly as wide as the month cards */
}
@media (max-width: 479.98px) {
  .month:not([open]) + .month:not([open]) { margin-top: calc(-1 * var(--s-4)); border-top: 1px dashed var(--g-rule-p); }
}
@media (min-width: 480px) and (max-width: 1023.98px) {
  .months { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .month:not([open]).m-j2 { margin-top: calc(-1 * var(--s-4)); border-top: 1px dashed var(--g-rule-p); }
  .month:not([open]).m-r2 { border-left: 1px dashed var(--g-rule-p); }
  .month:not([open]).m-s2 { grid-column: 1 / -1; }
}
/* tablets (narrow regime on a wide screen): title, year tabs and month cards share ONE centred 34rem column, the
   way the gallery sheets are centred at the same widths (no empty bench hanging on the right) */
@media (min-width: 600px) and (max-width: 1023.98px) {
  .wide:has(> .months) { max-width: calc(34rem + 2 * var(--gutter)); }
}
@media (min-width: 1024px) {
  /* the list's top 2px are clipped, so the rule above the row that starts the right-hand column never shows */
  .gigs { column-gap: var(--s-5); padding-top: 0; clip-path: inset(2px 0 0 0); }
  .gigs:has(> .gig:nth-child(5)) { columns: 2; }
  .gig { break-inside: avoid; }
  /* 1-4 rows: one column, and the card is only as wide as that column (no half-blank paper) */
  .month:not(:has(.gig:nth-child(5))) { width: calc(50% + var(--card-pad) - var(--s-5) / 2); }
}
/* loose lines after a year's months (2003): typed on a plain ledger card like the months (same tape tabs) */
.months + .paper {
  margin-top: var(--s-5); max-width: none; padding: 32px var(--card-pad, 1.5rem) 1.1rem;
  font: 400 var(--fs-ui)/1.45 var(--f-mono);
  --card-pad: clamp(1.1rem, .7rem + 1.2vw, 1.5rem);
}
@media (min-width: 1024px) {   /* the ledger is as wide as a short month card, its entries stacked in one column */
  .months + .paper { width: calc(50% + var(--card-pad) - var(--s-5) / 2); }
}
/* each loose entry is set like a gig row: its opening line (the date) bold in the day's place, the lines under it
   hanging in the entry column (3.5rem, or 10.25rem when the card is as wide as a day | weekday | entry row), a dashed
   rule between entries. Styling only: the words and line breaks are the source's. */
.months + .paper p { margin: 0; padding: .65rem 0 .65rem 3.5rem; text-indent: -3.5rem; max-width: none; }
.months + .paper p:first-child { padding-top: 0; }
.months + .paper p:last-child { padding-bottom: 0; }
.months + .paper p + p { border-top: 1px dashed var(--g-rule-p); }
/* (removed in round 8) no bold on the date lines: the source has none, and bold may only appear where he put it */
@container (min-width: 25rem) { .months + .paper p { padding-left: 10.25rem; text-indent: -10.25rem; } }

/* ---------- 15. TABLE FALLBACK, BLOCKQUOTE ---------- */
.table-wrap { overflow-x: auto; margin: 0 0 1.25em; }
.paper table { border-collapse: collapse; width: 100%; font: 400 var(--fs-ui)/1.45 var(--f-mono); }
.paper th, .paper td { text-align: left; vertical-align: top; padding: .5rem .75rem .5rem 0; border-bottom: 1px dashed var(--g-rule-p); }
.paper th { font-weight: 700; border-bottom: 2px solid var(--g-rule-p); }
.paper blockquote {
  margin: var(--s-3) 0; padding: .1rem 0 .1rem 1.25rem;
  border-left: 3px solid var(--g-pencil); font-style: italic;
}

/* ---------- 16. HOME HERO ---------- */
.page--home { padding-top: var(--s-4); }
.hero { display: grid; grid-template-columns: minmax(0, max-content); grid-template-areas: "name" "portrait" "links"; gap: var(--s-3); justify-content: center; }
.hero-name { grid-area: name; }
.hero-name .scrawl { font-size: var(--fs-name); line-height: .9; display: inline-block; transform: rotate(-2deg); transform-origin: 0 0; }
.hero-name .tape { margin-top: var(--s-3); font: 400 clamp(1.125rem, 1rem + .5vw, 1.4rem)/1.3 var(--f-type); transform: rotate(1.6deg); }
.hero .print--portrait { grid-area: portrait; margin: 34px 0 0; justify-self: start; }   /* phones: clear air between the two tapes */
.hero-links { grid-area: links; align-self: end; }
.hero-links ul { /* his six front-page words, typed on an index card */
  list-style: none; margin: 0; padding: .4rem 1.25rem .6rem;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 1.25rem;
  background: var(--tx-paper), var(--g-paper); box-shadow: var(--shadow-lift);
  transform: rotate(-.8deg);
}
.hero-links li { border-bottom: 1px dashed var(--g-rule-p); }
.hero-links li:nth-last-child(-n+2) { border-bottom: 0; }
.hero-links a {
  display: flex; align-items: center; gap: .6rem; min-height: 48px;
  font: 400 1.25rem/1 var(--f-mono); color: var(--ink); text-decoration: none;
}
.hero-links a::before { /* typed caret, like his old "> news" rollovers */
  content: ""; flex: none; border-style: solid; border-width: 5px 0 5px 8px;
  border-color: transparent transparent transparent var(--g-pencil);
}
.hero-links a:hover, .hero-links a:focus-visible { text-decoration: underline; text-underline-offset: 5px; }
.hero-links a:hover::before { border-left-color: var(--ink); }
/* the home strip stays smaller than the portrait (the portrait is the dominant image), each frame with its own
   gallery caption. ONE BREAKPOINT for the whole home page (768px: hero side by side, strip 2 x 2).
   Narrow: single file, centred on the hero's centre line (edge to edge under 600px, hugging its frames from 600px);
   every photo at most 316px (so the 2 x 2 at 768px keeps 92%+ of it, sweep S9) and every frame 16px+
   narrower than the portrait photo above it (phones: the portrait is the page width less 2 gutters and its border).
   Wide: 2 x 2, top-aligned, shared left edges; the hero above and the strip share one centred column, so the
   portrait's left edge and the name's right edge line up with the strip's edges. */
@media (max-width: 767.98px) {   /* the sheet hugs its frames (no blank paper at the sides), centred under the hero */
  .home-sheet { width: fit-content; max-width: 100%; margin-inline: auto; }
  .home-sheet .shot { width: min(calc(316px + 2 * var(--edge)), calc(var(--w) * 1px + 2 * var(--edge))); }
}
@media (max-width: 599.98px) {   /* phones: the portrait photo is the screen less 2 gutters and its 20px border */
  .home-sheet .shot { width: min(calc(100vw - 2 * var(--gutter) - 36px), calc(316px + 2 * var(--edge)), calc(var(--w) * 1px + 2 * var(--edge))); }
}
.home-sheet { margin-top: var(--s-6); }
@media (min-width: 768px) {
  .page--home { display: grid; grid-template-columns: minmax(0, max-content); justify-content: center; }
  .page--home > .hero { justify-content: space-between; }
  .home-sheet {
    display: grid; grid-template-columns: repeat(2, minmax(0, 340px)); justify-content: center; align-items: start;
    gap: 24px; padding: 32px 20px 24px;
  }
  .home-sheet .shot { width: 100%; max-width: min(340px, calc(var(--w) * 1px + 2 * var(--edge))); justify-self: start; }
}
@media (min-width: 560px) {
  .hero-links ul { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .hero-links li:nth-last-child(-n+3) { border-bottom: 0; }
}
@media (min-width: 768px) {   /* the home page's one switch width: hero side by side, strip 2 x 2 */
  .hero {
    grid-template-columns: auto max-content;
    grid-template-areas: "portrait name" "portrait links";
    grid-template-rows: auto 1fr;
    column-gap: clamp(2rem, 5vw, 5rem); row-gap: var(--s-3);
    align-items: start;
  }
  .hero .print--portrait { margin-top: 14px; }
  .hero-name { padding-top: var(--s-2); }
  .hero-name .scrawl { white-space: nowrap; }
  .hero-links { margin-bottom: 4px; }   /* the card's bottom edge lines up with the print's (its tilt dips ~4px) */
  .hero-links ul { grid-template-columns: minmax(0, 1fr); max-width: 15rem; padding: .45rem 1.5rem .55rem; }
  .hero-links li:nth-last-child(-n+3) { border-bottom: 1px dashed var(--g-rule-p); }
  .hero-links li:last-child { border-bottom: 0; }
  .hero-links a { min-height: 44px; }
}

/* ---------- 17. STORY LAYOUT (bio, credits, news, blog, contact …) ----------
   One centred column: the title on the bench, the paper under it. Equal bench either side. */
.story { max-width: calc(46rem + 2 * var(--gutter)); }
.story > .paper { width: 100%; }
/* a short paper (render.FIT_CHARS, by text length) is only as wide as its lines need, at least 28rem, at most the
   usual 46rem; its left edge stays on the title's. No size containment, so it can size to its content. */
/* every short paper is ONE width (34rem, the footer slip's width), so the right edge never jumps between the short
   pages; its left edge stays on the title's */
.story > .paper.paper--fit { width: min(100%, 34rem); }
/* wide pages (galleries, videos, gigs): head on top, content uses the full page width */

/* ---------- 18. FOOTER SPONSOR SLIP ---------- */
.foot { padding: var(--s-3) var(--gutter) var(--s-6); display: flex; justify-content: center; }
/* under a short paper the slip sits on the paper's axis: the story column, its left edge on the paper's (both 34rem) */
body:has(.story > .paper--fit) .foot { max-width: calc(46rem + 2 * var(--gutter)); margin-inline: auto; justify-content: flex-start; }
.sponsor {
  max-width: 34rem; margin: 0; padding: .9rem 1.25rem;
  font: 400 var(--fs-ui)/2.2 var(--f-mono); color: var(--ink); text-align: center;
  position: relative; isolation: isolate;
}
.sponsor::before {   /* the slip itself is tilted; the words and logos stay square so the logos render at exactly their pixels */
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--tx-paper), var(--g-paper); box-shadow: var(--shadow-slip);
  transform: rotate(-.6deg);
}
.logo { display: inline-block; vertical-align: middle; line-height: 0; padding: 5px 7px; margin: 0 .15em; background: var(--g-film); }
.logo img { display: block; width: auto; height: auto; max-width: none; filter: grayscale(1); }   /* natural pixels, never stretched */
.logo--remo img { filter: grayscale(1) brightness(.72) contrast(2.6); }
.logo--regal img { filter: url(#regal) grayscale(1); }

/* ---------- 19. LIGHTBOX (dialog built by site.js) ---------- */
.lightbox {
  padding: 10px 10px 12px; border: 0; margin: auto;   /* room for the close button's focus ring */
  max-width: calc(100vw - 16px); max-height: calc(100dvh - 16px); overflow: hidden;
  color: var(--ink); background: var(--g-print);
}
.lightbox[open] { display: flex; flex-direction: column; }
.lightbox::backdrop { background: rgba(8, 8, 8, .9); }
/* the photo opens at its natural size, capped only by the screen width (never by its height: on a short screen a
   tall photo would open smaller than the page shows it). Only the photo and its caption scroll (.lb-scroll); the
   controls bar is pinned to the dialog's bottom edge on its own opaque strip, so previous / next / close (and the
   focused one) are always on screen */
.lb-scroll { flex: 0 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; }
.lightbox figure { display: table; margin: 0 auto; }
.lightbox img { width: auto; height: auto; max-width: calc(100vw - 36px); }
.lightbox figcaption { display: table-caption; caption-side: bottom; padding: 8px 2px 0; font: 400 var(--fs-ui)/1.45 var(--f-mono); }
.lb-bar { flex: none; display: flex; align-items: center; gap: 8px; margin-top: 4px; background: var(--g-print); }
.lb-bar button { position: relative; width: 44px; height: 44px; padding: 0; border: 0; background: none; color: var(--ink); cursor: pointer; }
.lb-bar .lb-close { margin-left: auto; }
.lb-bar .lb-close::before {
  content: ""; position: absolute; inset: 14px;
  background:
    linear-gradient(45deg, transparent 43%, currentColor 43% 57%, transparent 57%),
    linear-gradient(-45deg, transparent 43%, currentColor 43% 57%, transparent 57%);
}
.lb-prev::before, .lb-next::before {
  content: ""; position: absolute; top: 50%; left: 50%; margin: -8px 0 0 -6px;
  border-style: solid; border-width: 8px 0 8px 12px; border-color: transparent transparent transparent currentColor;
}
.lb-prev::before { transform: rotate(180deg); }
.lb-bar [hidden] { display: none; }
@media (max-width: 599.98px) {   /* phones: the photo uses the full screen width less 8px, so it is never smaller than
                                    on the page (which runs it edge to edge); never past its natural size */
  .lightbox { padding: 4px 4px 8px; max-width: 100vw; }
  .lightbox img { max-width: calc(100vw - 8px); }
  .lightbox figcaption { padding: 8px 10px 0; }   /* text 14px from the screen edge */
  .lb-bar { padding-inline: 4px; }
}

/* ---------- 20. MOTION (subtle; off under reduced motion) ---------- */
@media (prefers-reduced-motion: no-preference) {
  .sub.is-open, .navpanel.is-open { animation: drop .14s ease-out; }
  @keyframes drop { from { transform: translateY(-4px); } to { transform: none; } }   /* transform only: an opacity fade could stick half-transparent */
  @media (hover: hover) {
    .frame:hover img, .print a:hover img { filter: grayscale(1) contrast(1.1); }
  }
}
