/* ==========================================================================
   Thalia & Hal — 14 August 2027
   One stylesheet, one system. Order: tokens, base, components, pages, responsive.
   Visual language borrows from British Rail signage: poster capitals, hairline
   keylines, square corners, and the crimson totem from the save-the-date card.
   ========================================================================== */

/* --- Tokens --------------------------------------------------------------- */

:root {
  /* Surfaces: the invitation's beige survives as a panel tone, not the ground. */
  --paper:        #f8f6f0;
  --card:         #efece0;

  --navy:         #0a2b7d;
  /* The blue ink sampled off the save-the-date card, paired with the crimson. */
  --blue-ink:     #00177a;
  --crimson:      #a5203a;
  /* One green, and it is only ever used to say a reply landed. Pitched lighter
     than the navy and the crimson on purpose — this is the one mark on the site
     that is meant to feel like good news rather than signage. */
  --green:        #1f8a5b;
  --ink:          #16203a;
  --muted:        #6c6a63;

  --line:         rgba(22, 32, 58, .15);
  --line-strong:  rgba(22, 32, 58, .34);
  --line-light:   rgba(255, 255, 255, .38);

  --serif: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  --sans:  'Helvetica Neue', Helvetica, Arial, sans-serif;

  /* One type scale. Each step is a real drop from the last. */
  --t-display: clamp(48px, 8.5vw, 96px);
  --t-h1:      clamp(38px, 5.5vw, 62px);
  --t-h2:      clamp(26px, 3.2vw, 38px);
  --t-h3:      22px;
  --t-lead:    19px;
  --t-body:    17px;
  --t-small:   14px;

  /* One spacing rhythm. */
  --section:   clamp(64px, 9vw, 104px);
  --gutter:    clamp(20px, 5vw, 56px);
  --container: 1120px;
  --measure:   660px;
  --header-h:  84px;
}

/* --- Base ----------------------------------------------------------------- */

* { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: var(--header-h); }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 var(--t-body)/1.65 var(--sans);
  -webkit-font-smoothing: antialiased;
  /* The footer is anchored to the bottom of the window. On a page with too
     little to fill the screen the main column takes up the slack, so the dark
     bar never strands itself halfway down with bare ground beneath it.
     dvh over vh so the mobile browser's own chrome doesn't push it under. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}
body > main { flex: 1 0 auto; }

/* The bar is fixed, so it no longer holds its own space open. The hero fills
   the viewport beneath it on purpose; every other page has to make room.
   Every page but the home page also stands on the beige the invitation stands
   on, so the sheet has the same ground under it wherever you are. */
body:not(.home) { padding-top: var(--header-h); background: var(--card); }

img { display: block; width: 100%; }
a { color: inherit; }

h1, h2, h3 { font-family: var(--serif); font-weight: 400; margin: 0; }
h1 { font-size: var(--t-h1); line-height: 1.02; letter-spacing: -.03em; }
h2 { font-size: var(--t-h2); line-height: 1.1;  letter-spacing: -.02em; }
h3 { font-size: var(--t-h3); line-height: 1.25; }

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

.container { max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter); }
.measure   { max-width: var(--measure); }

/* --- Typographic components ----------------------------------------------- */

/* Poster capitals — the recurring signage voice. */
.eyebrow {
  margin: 0 0 16px;
  font: 700 11px/1.4 var(--sans);
  text-transform: uppercase;
  letter-spacing: .22em;
  color: var(--muted);
}
.eyebrow-light { color: rgba(255, 255, 255, .82); }

.lead  { font: 400 var(--t-lead)/1.7 var(--serif); }
.prose { font: 400 var(--t-body)/1.75 var(--serif); }
.note  { font-size: var(--t-small); color: var(--muted); }

/* The crimson totem from the save-the-date card. Used once per page, as a marker. */
.totem {
  display: inline-block;
  padding: 8px 26px;
  background: var(--crimson);
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px var(--crimson), inset 0 0 0 3px rgba(255, 255, 255, .9);
  color: #fff;
  font: 700 11px/1.4 var(--sans);
  text-transform: uppercase;
  letter-spacing: .2em;
}

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

/* --- Buttons and links ---------------------------------------------------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 0 30px;
  border: 1px solid transparent;
  background: none;
  font: 700 11px/1 var(--sans);
  text-transform: uppercase;
  letter-spacing: .18em;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .18s, color .18s, border-color .18s;
}
.button-navy    { background: var(--navy); border-color: var(--navy); color: #fff; }
.button-navy:hover    { background: #08215f; border-color: #08215f; }
.button-crimson { background: var(--crimson); border-color: var(--crimson); color: #fff; }
.button-crimson:hover { background: #85182e; border-color: #85182e; }
.button-outline { border-color: currentColor; color: var(--ink); }
.button-outline:hover { background: var(--ink); color: var(--paper); }
.button-light   { border-color: rgba(255,255,255,.75); color: #fff; background: rgba(10,43,125,.22); backdrop-filter: blur(3px); }
.button-light:hover   { background: #fff; color: var(--navy); border-color: #fff; }

/* A shorter cut, for an action offered rather than asked for. */
.button-small { min-height: 42px; padding: 0 22px; font-size: 10px; letter-spacing: .16em; }

.text-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--navy);
  font: 700 11px/1 var(--sans);
  text-transform: uppercase;
  letter-spacing: .16em;
  text-decoration: none;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--line-strong);
  transition: color .18s, border-color .18s;
}
.text-link:hover { color: var(--crimson); border-color: var(--crimson); }
.text-link i { font-style: normal; transition: transform .18s; }
.text-link:hover i { transform: translateX(3px); }

/* --- Header --------------------------------------------------------------- */

.site-header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 20;
  height: var(--header-h);
  padding: 0 var(--gutter);
  /* Equal outer tracks keep the tabs centred on the page, not merely centred
     in whatever space the wordmark and the clock leave behind. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
  background: var(--paper);
  color: var(--ink);
  transition: background-color .32s, color .32s, box-shadow .32s;
}

/* Lifted off the page once it has content running under it. */
.site-header.is-stuck { box-shadow: 0 12px 26px -22px rgba(22, 32, 58, .55); }

.header-aside {
  grid-column: 3;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 20px;
}
/* On the homepage the bar floats over the photograph — until the photograph
   is behind you, at which point it becomes the bar every other page carries. */
.home .site-header:not(.is-stuck) {
  background: transparent;
  color: #fff;
}
/* The hero states the date in full and runs its own large clock; whichever of
   the two the bar is carrying waits its turn behind them. */
.home .site-header:not(.is-stuck) .header-countdown,
.home .site-header:not(.is-stuck) .header-date {
  opacity: 0;
  visibility: hidden;
}
.home .header-countdown,
.home .header-date { transition: opacity .32s; }

.wordmark {
  grid-column: 1;
  justify-self: start;
  font: 700 22px/1 var(--serif);
  letter-spacing: .16em;
  text-decoration: none;
  white-space: nowrap;
}
.wordmark span { font-weight: 400; font-style: italic; }

/* The bar carries the clock on a wide screen and the plain date on a handset —
   see the handset block at the foot of this sheet for why. Off by default. */
.header-date { display: none; }

.site-nav { grid-column: 2; justify-self: center; display: flex; align-items: center; gap: clamp(18px, 2.4vw, 34px); }
.site-nav a {
  font: 700 11px/1 var(--sans);
  text-transform: uppercase;
  letter-spacing: .18em;
  text-decoration: none;
  padding: 10px 0;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.site-nav a:hover,
.site-nav a[aria-current="page"] { border-bottom-color: currentColor; }


.menu-button {
  display: none;
  border: 0;
  background: none;
  color: inherit;
  font: 700 11px/1 var(--sans);
  text-transform: uppercase;
  letter-spacing: .18em;
  padding: 12px 0;
  cursor: pointer;
}

/* --- Footer --------------------------------------------------------------- */

.site-footer {
  padding: 30px var(--gutter);
  background: var(--ink);
  color: rgba(255, 255, 255, .85);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  font: 700 10px/1.6 var(--sans);
  text-transform: uppercase;
  letter-spacing: .2em;
}

/* --- Hero (home) ---------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  overflow: hidden;
  color: #fff;
  /* Shows only until the photograph paints, which on a slow handset connection
     is a real moment — so it is the footer's ink rather than a one-off navy. */
  background: var(--ink);
}
.hero > picture { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero-image,
.hero-shade { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero-image { object-fit: cover; object-position: center 55%; }
/* The shade darkens the top and bottom so the white copy holds, and it is mixed
   in neutral black on purpose. It used to be a navy — rgb(8,17,40), a one-off
   twice as blue-biased as --ink and belonging to no other part of the palette —
   which tinted the whole photograph. This is not a stock hero: it is the
   couple's own picture, so the shade lights it and does not grade it. Black also
   darkens harder than a navy at equal alpha, having no channel to lift the
   shadows back up, so these strengths run lower than the navy's did. */
.hero-shade {
  background:
    linear-gradient(180deg, rgba(0,0,0,.45) 0%, rgba(0,0,0,.09) 32%, rgba(0,0,0,.60) 100%);
}

.hero-copy {
  position: relative;
  text-align: center;
  padding: calc(var(--header-h) + 40px) var(--gutter) 108px;
  max-width: 860px;
  text-shadow: 0 2px 22px rgba(0, 0, 0, .4);
}
.hero-copy h1 {
  font-size: var(--t-display);
  line-height: .95;
  letter-spacing: -.045em;
  margin: 0 0 22px;
}
.hero-copy h1 i { font-size: .68em; }
.hero-meta {
  margin: 0 0 18px;
  font: 700 12px/1.8 var(--sans);
  text-transform: uppercase;
  letter-spacing: .2em;
}

/* The line is a date and a place joined by a dot, and it holds as one line for
   as long as the measure allows. Below about 550px it stops fitting and breaks
   wherever it runs out — after "Three Pools," on a 430px handset, which leaves
   the town stranded on a line of its own and reads as though the venue were
   two. Broken at the join instead, the date takes the first line and the place
   the second, which is how the two would be set on a card. The dot is only
   there to separate them inline; once the break is doing that work it goes. */
@media (max-width: 600px) {
  .hero-meta-dot { display: none; }
  .hero-meta-place { display: block; }
}

/* Countdown, set like a departure board. */
.countdown {
  --cd-top: 10px;
  --cd-gap: 34px;
  --cd-figure: clamp(26px, 4vw, 40px);
  display: flex;
  justify-content: center;
  gap: var(--cd-gap);
  width: min(360px, 100%);
  margin: 0 auto 36px;
  border-top: 1px solid var(--line-light);
}
/* Each unit is only as wide as its figure — the label underneath is taken out
   of the flow so a long word like MINUTES cannot widen the box — which puts the
   dots on the true midpoint between figures whatever the digit count. The rule
   above stays its full width; the shorter row of figures centres beneath it.
   Bottom padding stands in for the lifted label: 8 gap + 9 label + 16 clear. */
.countdown div { position: relative; padding: var(--cd-top) 0 33px; }
.countdown div + div::before {
  content: "";
  position: absolute;
  /* Not the middle of the line box — Georgia's old-style figures sit low in it,
     x-height with half of them descending, so a box-centred dot reads as high.
     Measured from the baseline instead: with line-height 1 that falls at .849em
     (ascent .917 less .068 of negative half-leading), and the figures' optical
     centre is about .21em above it. */
  top: calc(var(--cd-top) + var(--cd-figure) * .64);
  left: calc(var(--cd-gap) / -2);
  transform: translate(-50%, -50%);
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 2px 22px rgba(0, 0, 0, .4);
}
.footer-countdown div + div::before,
.header-countdown div + div::before { content: none; }
.countdown strong {
  display: block;
  font: 400 var(--cd-figure)/1 var(--serif);
  font-variant-numeric: tabular-nums;
}
/* One cell per digit. Georgia has no tabular figures to call on, so without a
   fixed cell the figures change width as they count and the whole clock — dots,
   labels and all — shuffles sideways every second. */
.countdown strong b {
  display: inline-block;
  width: 1ch;
  font-weight: inherit;
  text-align: center;
}
.countdown span {
  position: absolute;
  top: calc(var(--cd-top) + var(--cd-figure) + 8px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font: 700 9px/1 var(--sans);
  text-transform: uppercase;
  letter-spacing: .2em;
  opacity: .82;
}

/* The footer keeps its own, quieter clock in place of the old totem. */
.footer-countdown {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 18px;
  margin: 0;
  border: 0;
  width: auto;
  color: #fff;
}
.footer-countdown div {
  padding: 0;
  border: 0;
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.footer-countdown strong {
  display: inline;
  font: 400 20px/1 var(--serif);
  font-variant-numeric: tabular-nums;
}
.footer-countdown span {
  display: inline;
  position: static;
  transform: none;
  margin: 0;
  font: 700 9px/1 var(--sans);
  letter-spacing: .2em;
  opacity: .62;
}

/* Inner pages carry the clock in the top right corner; the home page has the
   big one over the photograph instead. */
.header-countdown {
  display: flex;
  align-items: baseline;
  gap: 12px;
  width: auto;
  margin: 0;
  border: 0;
}
.header-countdown div { padding: 0; border: 0; text-align: center; }
.header-countdown strong {
  display: block;
  font: 700 22px/1 var(--serif);
  letter-spacing: .16em;
  font-variant-numeric: tabular-nums;
}
.header-countdown span {
  display: block;
  position: static;
  transform: none;
  margin-top: 5px;
  font: 700 8px/1 var(--serif);
  letter-spacing: .16em;
  opacity: .6;
}

.hero-scroll {
  position: absolute;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: #fff;
  font: 700 10px/1 var(--sans);
  text-transform: uppercase;
  letter-spacing: .2em;
  text-decoration: none;
}

/* --- The poster ----------------------------------------------------------- */
/* Scrolling off the hero should feel like being handed a printed sheet: one
   piece of paper, centred, that answers everything. The section ground steps
   down to the save-the-date beige so the sheet can be the lighter thing on it.
   The two tones sit close on purpose — the shadow and keyline do the lifting.
   That sheet is now the site's only container: every page is one of these,
   printed on the same paper, trimmed to the same edge. */

.day {
  padding: var(--section) 0 calc(var(--section) + 10px);
  background: var(--card);
}

.poster {
  position: relative;
  max-width: 780px;
  margin: 0 auto;
  padding: clamp(40px, 4.8vw, 62px) clamp(26px, 4.8vw, 62px) clamp(30px, 3.7vw, 46px);
  background: var(--paper);
  border: 1px solid var(--line-strong);
  box-shadow: 0 2px 4px rgba(22, 32, 58, .06), 0 20px 46px -26px rgba(22, 32, 58, .40);
}

/* The printer's inner keyline, held off the trimmed edge. */
.poster::before {
  content: "";
  position: absolute;
  inset: clamp(11px, 1.6vw, 18px);
  border: 1px solid var(--line);
  pointer-events: none;
}

.poster > * { position: relative; }

/* The invitation is the one poster set narrower, and the one centred to the
   last line — it has fewer words to carry than the pages that follow it. */
.sheet {
  max-width: 700px;
  padding: clamp(44px, 5.1vw, 66px) clamp(28px, 5.5vw, 76px) clamp(26px, 3.7vw, 42px);
  text-align: center;
}

/* The Three Pools sign stands where the location totem used to, and every sign
   on the site is struck from this one rule — see .page-sign, which shares it.

   The signs are cut to the word, so their widths differ: RSVP is a stub next to
   INFORMATION. Sizing them all to one width would make the short ones tall and
   the long ones small. Matching height is what reads as one set of signs, so
   each page sets --sign-w to the width that lands its own artwork on the 92px
   the Three Pools sign stands at. */
.sheet-sign,
.page-sign {
  display: block;
  width: 100%;
  max-width: calc(var(--sign-w, 268px) * var(--sign-scale, 1));
  height: auto;
  margin: 0 auto;
}
.sheet-sign { margin-bottom: 26px; }

.schedule-page    { --sign-w: 251px; }  /* 800 × 295 */
.information-page { --sign-w: 260px; }  /* 800 × 285 */
.rsvp-page        { --sign-w: 201px; }  /* 800 × 368 */
.marmots-page     { --sign-w: 266px; }  /* 800 × 278 */
/* No measure cap — the title is short enough to hold one line on the sheet.
   The lead sits close under it: they are one address, not two. */
.sheet h2 { margin: 0 auto 14px; font-size: clamp(28px, 3.6vw, 42px); text-wrap: balance; }

/* Off the scale on purpose: the sheet is set a step larger than the rest of
   the page, and the lead follows it up. At 20px the line still holds on one
   line inside the measure, and balances the break on narrow screens. */
.sheet-lead {
  margin: 0 auto;
  max-width: 56ch;
  text-wrap: balance;
  font: 400 20px/1.6 var(--serif);
  color: var(--muted);
}

/* The prose is set to a narrower measure than the lead above it — a printed
   sheet reads taller than it is wide, and the shorter line gives the paper the
   height it wants without adding a word. */
.sheet-prose {
  margin: clamp(32px, 4.1vw, 42px) auto 0;
  max-width: 45ch;
  font: 400 18px/1.78 var(--serif);
}

/* The detail band — the part guests scan for, and the whole of what the sheet
   claims to know. Anything more exact lives on the schedule and information
   pages, which the foot links to. */
.sheet-facts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  margin: clamp(42px, 5.2vw, 56px) 0 0;
  border-top: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
}
.sheet-facts div {
  padding: clamp(23px, 3vw, 30px) 12px;
  border-left: 1px solid var(--line);
}
/* The date takes the head row alone, so it never breaks and the three times
   beneath it read as one run of the day. Both it and Arrive open a row, so
   neither carries a rule to its left. */
.sheet-facts div:nth-child(-n + 2) { border-left: 0; }

.sheet-facts .fact-date {
  grid-column: 1 / -1;
  padding-bottom: clamp(26px, 3.3vw, 35px);
  border-bottom: 1px solid var(--line);
}

.sheet-facts dt {
  margin-bottom: 11px;
  font: 700 11px/1.6 var(--sans);
  text-transform: uppercase;
  letter-spacing: .18em;
  color: var(--crimson);
}
.sheet-facts dd {
  margin: 0;
  font: 400 20px/1.35 var(--serif);
  color: var(--blue-ink);
  text-wrap: balance;
}
/* Set below the sheet's h2 at every width, so the hierarchy stays
   heading, then date, then details. */
.sheet-facts .fact-date dd {
  font-size: clamp(26px, 3.7vw, 34px);
  line-height: 1.15;
}

.sheet-facts dd small {
  display: block;
  margin-top: 7px;
  font: 400 13px/1.4 var(--sans);
  color: var(--muted);
}

/* Two quiet onward links, printed at the foot of the sheet rather than
   floated away as separate cards. */
.sheet-foot {
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.sheet-foot a {
  padding: clamp(21px, 2.8vw, 28px) 8px;
  border-left: 1px solid var(--line);
  font: 700 12px/1.4 var(--sans);
  text-transform: uppercase;
  letter-spacing: .18em;
  text-decoration: none;
  color: var(--ink);
  transition: color .18s;
}
.sheet-foot a:first-child { border-left: 0; }
.sheet-foot a:hover { color: var(--crimson); }
.sheet-foot i { font-style: normal; display: inline-block; transition: transform .18s; }
.sheet-foot a:hover i { transform: translateX(3px); }

/* --- Home: RSVP band ------------------------------------------------------ */

/* Full bleed, and kept to the home page: it is the colour field that answers
   the photograph the page opens on. The interior pages close on .reply-close
   instead — see below. */

.rsvp-band {
  background: var(--navy);
  color: #fff;
  padding: clamp(40px, 5vw, 56px) var(--gutter);
  text-align: center;
}
.rsvp-band h2 { margin: 0 auto 16px; color: #fff; }
.rsvp-band p  { margin: 0 auto 30px; max-width: 46ch; color: rgba(255,255,255,.82); font-family: var(--serif); }

/* --- Interior pages: the closing ask -------------------------------------- */

/* The same words as the band, said quietly. Rules at the poster's width and
   centred type on the beige — no slab, so nothing competes with the sheet above
   it and nothing collides with the navy footer below.

   Ruled top and bottom, so the ask is a panel on the page rather than a tail
   the page trails off into. The bottom rule is the top one repeated at the same
   width and weight, and it is given the same measure of air inside it — the
   button stands off it as far as the eyebrow stands under its opposite — so the
   two read as one bracket closed rather than two lines that happen to be
   there. */
.reply-close {
  max-width: 780px;
  margin: clamp(52px, 6vw, 76px) auto 0;
  padding-top: clamp(40px, 4.6vw, 54px);
  padding-bottom: clamp(44px, 4.6vw, 54px);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  text-align: center;
}
/* Set in the body's own ink, not an accent. Crimson on this site is signage —
   a filled pill with the type reversed out, or small caps and times — and it is
   never a large serif line; borrowing it to speak is what made this read wrong.
   Navy would work, but it puts a coloured line directly above a filled navy
   button and the two compete. Ink lets the words sit back and the button be the
   one thing that steps forward. */
.reply-close h2 {
  font-size: clamp(24px, 2.8vw, 32px);
  line-height: 1.15;
  color: var(--ink);
  margin: 0 0 14px;
}
.reply-close p:not(.eyebrow) {
  margin: 0 auto 28px;
  max-width: 46ch;
  color: var(--muted);
  font: 400 var(--t-body)/1.7 var(--serif);
}

/* --- Interior page shell -------------------------------------------------- */
/* The ground is on the body; the page only sets how far the poster stands off
   the header and the footer. */

.page { padding: clamp(52px, 7vw, 84px) 0 var(--section); }


.page-head {
  text-align: center;
  max-width: 720px;
  margin: 0 auto clamp(40px, 5vw, 60px);
}
.page-head h1 { color: var(--navy); margin-bottom: 18px; }

/* A page title set as a sign, the same totem the invitation uses, struck at the
   same size — the h1 keeps the words for the reader who can't see it, the image
   carries them. */
.sign-title { margin-bottom: 26px; }
.page-head > p:last-child { margin: 0 auto; color: var(--muted); max-width: 54ch; }

/* --- Schedule: departure board -------------------------------------------- */

/* The date moved off the page head to sit directly over the board it dates.
   Centred, and holding the eyebrow's own 16px gap to the head row. */
.board-date { text-align: center; }

/* The schedule's standfirst carries the page, so it is set in the body ink
   rather than the muted grey the other pages use for theirs. */
.schedule-page .page-head > p:last-child { color: var(--ink); }

/* The board is pulled up under the head and the same measure is given back
   below the last time, so the poster keeps its height while the schedule
   stops sitting on the bottom edge. */
.schedule-page .page-head { margin-bottom: clamp(24px, 3vw, 36px); }
.schedule-page .poster { padding-bottom: clamp(46px, 5vw, 70px); }

/* The last row closes the board on its own — no rule under the final time. */

.board-head {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: 28px;
  padding: 14px clamp(14px, 2vw, 20px);
  background: var(--navy);
  color: #fff;
  font: 700 10px/1.6 var(--sans);
  text-transform: uppercase;
  letter-spacing: .2em;
}

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: 28px;
  padding: 20px clamp(14px, 2vw, 20px);
  align-items: baseline;
  border-top: 1px solid var(--line);
}
.timeline li:first-child { border-top: 0; }
.timeline li:nth-child(odd) { background: rgba(255, 255, 255, .45); }
.timeline time {
  color: var(--crimson);
  font: 400 20px/1.3 var(--serif);
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
}
.timeline h2 { font-size: var(--t-h3); letter-spacing: 0; margin: 0 0 3px; }
/* Wrapped greedily, and deliberately so. These glosses run to two lines on a
   handset, and every wrap strategy that improves the last line does it by
   taking words off the first: pretty pulls one down to save the orphan and
   leaves line one stopping short of the margin, balance splits the pair evenly
   and narrows the whole block. Against a title set at 20.5px a short first
   line is the one that shows. A single word left on the last line is the
   cheaper fault, so the lines fill. */
.timeline p  { margin: 0; color: var(--muted); font-size: var(--t-small); line-height: 1.5; }

.board-foot {
  padding: 14px clamp(14px, 2vw, 20px);
  border-top: 1px solid var(--line-strong);
  text-align: center;
}

/* A slip tucked under the poster rather than printed on it, so it takes the
   poster's own width, set on the beige ground. Filled navy, the same slab the
   RSVP band uses, so every call to action on the site is the one object.

   It carries add-to-calendar, shown once a reply has landed. */
.save-card {
  position: relative;
  max-width: 780px;
  margin: clamp(28px, 3.4vw, 40px) auto 0;
  padding: clamp(18px, 2.1vw, 23px) clamp(26px, 3.4vw, 40px);
  background: var(--navy);
  border: 1px solid var(--navy);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
  box-shadow: 0 2px 4px rgba(22, 32, 58, .06), 0 20px 46px -26px rgba(22, 32, 58, .40);
}

/* The kicker is dropped back so the line below it is the one thing being said.
   The base .eyebrow is muted grey, which all but disappears on navy, so the
   colour is set here rather than left to each call site to remember
   .eyebrow-light. */
.save-card .eyebrow { font-size: 10px; margin: 0 0 7px; color: rgba(255, 255, 255, .58); }
.save-card h2 { font-size: 25px; line-height: 1.15; letter-spacing: -.01em; margin: 0; }
.save-card p  { margin: 0; color: rgba(255, 255, 255, .82); font-size: var(--t-small); }


/* --- Information page ----------------------------------------------------- */

/* Two printed columns — no competing headings, one weight per cell. The rules
   run the full width of the sheet; the words start at its margin. */
.info-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  border-top: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
}
.info-grid > div { padding: clamp(26px, 3.4vw, 36px) 0; }
.info-grid > div:nth-child(odd)  { padding-right: clamp(20px, 3vw, 32px); border-right: 1px solid var(--line); }
.info-grid > div:nth-child(even) { padding-left: clamp(20px, 3vw, 32px); }
.info-grid > div:nth-child(n+3)  { border-top: 1px solid var(--line); }
.info-grid h2 { font-size: var(--t-h3); letter-spacing: 0; margin: 0 0 10px; }
.info-grid p  { color: var(--muted); font-size: var(--t-small); }
.info-grid .text-link { margin-top: 14px; }
.info-grid address { font-style: normal; font-family: var(--serif); font-size: var(--t-body); color: var(--ink); line-height: 1.6; }
.info-index {
  display: block;
  margin-bottom: 12px;
  font: 400 15px/1 var(--serif);
  font-style: italic;
  color: var(--crimson);
}

/* --- Marmots -------------------------------------------------------------- */

/* The one page whose content is photographs, and the one page with no poster
   under it. The sheet is a printed page you are handed: it works when you can
   see its bottom edge, which is why it holds the schedule and the information
   whole. Eighteen photographs run it five screens deep, and a page of paper
   that never ends stops reading as paper. It also framed every marmot four
   times over — trimmed edge, inner keyline, padding, then the tile's own
   border — and left each print at 280px on a screen with room for far more.

   So this page keeps what the family resemblance actually lives in: the sign,
   the ground, the header, the closing rule. The sheet comes off, and the grid
   runs out to a width the pictures can use. */
.marmots-page { --container: 1360px; }
.marmots-page .page-head { margin-bottom: clamp(36px, 4.2vw, 52px); }

/* Narrow on purpose. The pictures are the page, so the gutter's whole job is
   to keep one from running into the next — enough air to separate them, not
   enough to read as white space of its own, which is what holds eighteen of
   them together as a single board rather than eighteen separate prints. */
.roster {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(10px, 1vw, 14px);
}

/* Every photograph is a button: the frame is the hit area, so you press the
   picture you want and it opens whole in the viewer.

   A fixed frame, so eighteen photographs of wildly different shapes still lay
   out as one grid. The ratio is set here rather than by width and height on the
   tags, because the source files are whatever the photographer happened to
   shoot — and setting it in the stylesheet holds the layout still while they
   load just as well as the attributes would. */
.marmot {
  display: block;
  width: 100%;
  padding: 0;
  background: var(--card);
  /* Held transparent rather than dropped: the box model stays identical, so
     nothing shifts when the hover puts the navy into it. */
  border: 1px solid transparent;
  border-radius: 0;
  overflow: hidden;
  cursor: zoom-in;
  transition: border-color .18s;
}
.marmot img {
  display: block;
  width: 100%;
  /* The thumbnails carry width and height attributes so the frame is reserved
     before they land. Those map to the height property as a presentational
     hint, and a definite height leaves aspect-ratio with nothing to resolve —
     the grid went tall the moment the attributes went on. Stated auto here,
     the hint is overridden, the ratio governs, and the attributes go back to
     doing only the job they were added for. */
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform .35s ease-out;
}

/* The picture comes forward under the cursor and the keyline takes the navy —
   the same pair of moves the rest of the site uses to say a thing is live. On
   this page the keyline is only ever there for that: at rest the photograph
   has no frame around it at all. */
.marmot:hover,
.marmot:focus-visible { border-color: var(--navy); }
.marmot:focus-visible  { outline: 2px solid var(--crimson); outline-offset: 2px; }

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .marmot:hover img { transform: scale(1.04); }
}


/* --- Marmots: the viewer -------------------------------------------------- */

/* One photograph, held over the page on the ink ground. Square corners and
   hairline keylines, like everything else — the controls are signage, not
   furniture, so they sit at the edges and stay out of the picture. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 60;
}

.lightbox-ground {
  position: absolute;
  inset: 0;
  background: rgba(22, 32, 58, .94);
  cursor: zoom-out;
}

.lightbox-dialog {
  position: relative;
  height: 100%;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: clamp(10px, 1.6vw, 18px);
  padding: clamp(14px, 2.2vw, 26px);
}

/* The single row is stated as minmax(0, 1fr) rather than left to size itself.
   An automatic row takes the height of the photograph inside it, so a tall
   frame made its own room and ran off the bottom of the screen, and the plate's
   max-height had nothing smaller than itself to measure against. Named at 1fr,
   the row is exactly the height the stage was given, and the plate fits to it. */
.lightbox-stage {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-rows: minmax(0, 1fr);
  align-items: center;
  justify-items: center;
  gap: clamp(8px, 1.6vw, 22px);
  min-height: 0;
}

/* The photograph is fitted, never cropped: the grid does the cropping, the
   viewer is where the whole frame is finally seen.

   Width and height are both left to the picture, capped by the space it has
   been given. The keyline then draws round the picture itself — square, wide or
   tall, it sits dead centre with the border tight to its edges. */
.lightbox-plate {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  margin: 0;
  object-fit: contain;
  border: 1px solid var(--line-light);
  background: rgba(255, 255, 255, .04);
}

.lightbox-close,
.lightbox-step {
  border: 1px solid rgba(255, 255, 255, .5);
  background: none;
  color: #fff;
  font: 700 11px/1 var(--sans);
  text-transform: uppercase;
  letter-spacing: .18em;
  cursor: pointer;
  transition: background-color .18s, color .18s, border-color .18s;
}
.lightbox-close:hover,
.lightbox-step:hover { background: #fff; color: var(--ink); border-color: #fff; }
.lightbox-close:focus-visible,
.lightbox-step:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

.lightbox-close {
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 42px;
  padding: 0 20px;
}
.lightbox-close span { font-size: 17px; letter-spacing: 0; }

.lightbox-step {
  width: 52px;
  height: 52px;
  font-size: 20px;
  letter-spacing: 0;
}

/* The count is the only label on the ground: which one of how many. */
.lightbox-count {
  margin: 0;
  text-align: center;
  color: rgba(255, 255, 255, .85);
  font: 700 11px/1 var(--sans);
  letter-spacing: .2em;
}
.lightbox-count i { font-style: normal; color: rgba(255, 255, 255, .45); margin: 0 4px; }

/* The page beneath is held still while the viewer is open, so a scroll meant
   for the photograph doesn't move the grid behind it. */
html.is-viewing, html.is-viewing body { overflow: hidden; }

@media (prefers-reduced-motion: no-preference) {
  .lightbox:not([hidden]) .lightbox-ground { animation: ground-in .2s ease-out both; }
  .lightbox:not([hidden]) .lightbox-plate  { animation: plate-in .24s ease-out both; }
}
@keyframes ground-in { from { opacity: 0; } }
@keyframes plate-in  { from { opacity: 0; transform: scale(.985); } }

/* On a narrow screen the arrows would eat the picture, so they come down to
   the bar and sit either side of the count, where a thumb can reach them. */
@media (max-width: 620px) {
  .lightbox-stage { grid-template-columns: minmax(0, 1fr); }
  .lightbox-step  { position: absolute; bottom: clamp(14px, 2.2vw, 26px); }
  .lightbox-prev  { left: clamp(14px, 2.2vw, 26px); }
  .lightbox-next  { right: clamp(14px, 2.2vw, 26px); }
  .lightbox-count { min-height: 52px; display: flex; align-items: center; justify-content: center; }
}

/* --- RSVP ----------------------------------------------------------------- */

/* The form is printed straight onto the sheet — the fields are the only boxes
   on it, which is what makes them read as the thing to fill in. No rule under
   the standfirst: the sign asks the question and the first field answers it,
   and nothing else on the site puts a line there. */
.rsvp-page .page-head { margin-bottom: clamp(24px, 3vw, 36px); }
.rsvp-form { padding: 0; }

.form-section { border: 0; padding: 0; margin: 0 0 24px; }
.form-section + .form-section { border-top: 1px solid var(--line); padding-top: 24px; }

.form-section > label,
.form-section > legend,
.field-pair label,
.songs-heading label {
  display: block;
  margin: 0 0 9px;
  padding: 0;
  font: 400 var(--t-body)/1.3 var(--serif);
}
.form-section > legend { float: left; width: 100%; }
.form-section > legend + * { clear: both; }

/* Two questions that belong on one line — forename and surname. */
.field-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }

.form-section > label span,
.songs-heading label span { font-size: 12px; color: var(--muted); }

input[type="text"],
textarea {
  width: 100%;
  padding: 10px 13px;
  border: 1px solid var(--line-strong);
  border-radius: 0;
  /* The beige ground, not a hard white — the keyline is what says "field". */
  background: var(--card);
  color: var(--ink);
  font: 400 16px/1.5 var(--sans);
  transition: border-color .18s, box-shadow .18s;
}
input[type="text"]:hover,
textarea:hover { border-color: var(--ink); }
input[type="text"]:focus,
textarea:focus {
  outline: 0;
  border-color: var(--navy);
  box-shadow: inset 0 0 0 1px var(--navy);
}
textarea { resize: vertical; }

/* Attending / not attending, as a pair of signs. */
.choice-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
/* The label is a grid so the box inside it fills the row. The outer grid
   stretches both labels to the taller of the two, but the span is what carries
   the border and the fill, and left to itself it sizes to its own text — so
   "Joyfully attending", which turns over to two lines at any handset width,
   stood six points taller than the box beside it while the height the label
   had been given went unused. Stretched, the pair holds whatever either
   caption does, and the turn reads as a set line rather than a fault. */
.choice-grid label { position: relative; display: grid; }
.choice-grid input { position: absolute; opacity: 0; inset: 0; }
.choice-grid span {
  display: grid;
  place-items: center;
  min-height: 52px;
  padding: 12px;
  background: var(--card);
  border: 1px solid var(--line-strong);
  text-align: center;
  font: 700 12px/1.4 var(--sans);
  text-transform: uppercase;
  letter-spacing: .1em;
  cursor: pointer;
  transition: background-color .18s, color .18s, border-color .18s;
}
.choice-grid label:hover span { border-color: var(--ink); }
.choice-grid input[value="yes"]:checked + span { background: var(--navy); border-color: var(--navy); color: #fff; }
.choice-grid input[value="no"]:checked  + span { background: var(--crimson); border-color: var(--crimson); color: #fff; }
.choice-grid input:focus-visible + span { outline: 2px solid var(--crimson); outline-offset: 2px; }

.songs-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-top: 22px;
}
.songs-heading label { margin: 0; }
.add-button {
  flex: none;
  border: 1px solid var(--line-strong);
  background: none;
  color: var(--navy);
  padding: 8px 14px;
  font: 700 10px/1 var(--sans);
  text-transform: uppercase;
  letter-spacing: .16em;
  cursor: pointer;
  transition: background-color .18s, color .18s;
}
.add-button:hover { background: var(--navy); border-color: var(--navy); color: #fff; }

/* Rows are set further apart than the two fields inside them. At an equal
   twelve the list read as one field of boxes and you had to work out which
   artist belonged to which song; at eighteen against twelve the pair closes up
   and the gap does the work a rule would otherwise have to. */
.song-list { display: grid; gap: 18px; margin-top: 12px; }
.song-row  { position: relative; display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.song-row label {
  display: block;
  margin-bottom: 5px;
  font: 700 10px/1 var(--sans);
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--muted);
}
/* Sized and zeroed rather than left to the browser. A button carries a default
   padding of its own, and with no border to show it that padding is invisible
   — it just holds the mark a few points in from wherever the button is put,
   and a few points down. Given an explicit box with the cross centred in it,
   the offsets below place the mark itself and not a cell around it. Thirty
   square is also a target you can hit with a thumb; the glyph alone was
   twenty. Set on the field's own centre line out in the margin. */
.remove-song {
  position: absolute;
  right: -33px;
  top: 23px;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--crimson);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.honeypot { position: absolute; left: -10000px; }

.form-status { display: none; margin: 0 0 18px; padding: 12px 14px; font-size: var(--t-small); }
.form-status.is-notice  { display: block; background: #fdf3d8; border-left: 3px solid var(--crimson); }
.form-status.is-success { display: block; background: #e6f2e7; border-left: 3px solid #2f6b36; }
.form-status.is-error   { display: block; background: #f8e2e5; border-left: 3px solid var(--crimson); }

.submit-button { width: 100%; margin-top: 8px; }
.submit-button:disabled { opacity: .6; cursor: wait; }


/* --- RSVP: the answer ------------------------------------------------------ */
/* When a reply lands the sheet answers in its own frame: the sign stays where
   it was, the form is taken away, and a signal badge stands where the first
   field had been. Green for landed, crimson for didn't — the site's own red,
   so nothing new had to be invented to say "that failed". The diary slip is
   held under the sheet until then; the date is worth saving once someone has
   said they are coming, and asking for it beside the empty form was asking for
   two things at once. */

.reply-result { text-align: center; padding: clamp(6px, 1.2vw, 14px) 0 clamp(4px, 1vw, 10px); }

/* The one round thing on a site of trimmed squares, and it earns the exception:
   a tick in a circle is read before it is looked at. */
.result-badge {
  width: clamp(78px, 9vw, 98px);
  aspect-ratio: 1;
  margin: 0 auto clamp(24px, 3vw, 32px);
  background: var(--green);
  border-radius: 50%;
}
.reply-result.is-error .result-badge { background: var(--crimson); }

.result-badge svg { display: block; width: 100%; height: 100%; }
.result-badge .mark {
  fill: none;
  stroke: #fff;
  stroke-width: 8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.result-badge .mark-cross,
.reply-result.is-error .mark-tick { display: none; }
.reply-result.is-error .mark-cross { display: block; }

/* With the standfirst gone the sign has no line to hold off, so it closes up
   on the badge rather than leaving a hole in the middle of the sheet. */
.poster.is-answered .sign-title { margin-bottom: 0; }

/* Set in the body ink rather than the navy the other heads take, and set that
   way whole: the thanks and the fact are one sentence said in one voice, and
   colouring the greeting apart from it made two announcements out of one. */
.reply-result h2 { margin: 0 0 14px; color: var(--ink); }
/* The heading takes focus so the answer is the next thing read; it is a
   destination, not a control, so it shows no ring. */
.reply-result h2:focus { outline: 0; }
.reply-result > p {
  margin: 0 auto clamp(26px, 3.2vw, 34px);
  max-width: 44ch;
  color: var(--muted);
  font: 400 var(--t-lead)/1.65 var(--serif);
}

/* The badge lands, then the mark is drawn on it. */
@media (prefers-reduced-motion: no-preference) {
  .reply-result:not([hidden]) .result-badge { animation: badge-in .26s ease-out both; }
  .reply-result:not([hidden]) .mark {
    stroke-dasharray: 130;
    stroke-dashoffset: 130;
    animation: mark-draw .44s .14s ease-out forwards;
  }
}
@keyframes badge-in { from { opacity: 0; transform: scale(.86); } }
@keyframes mark-draw { to { stroke-dashoffset: 0; } }

[hidden] { display: none !important; }

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

/* --- Responsive ----------------------------------------------------------- */

@media (max-width: 860px) {
  .roster { grid-template-columns: repeat(2, 1fr); }

  .info-grid { grid-template-columns: 1fr; }
  .info-grid > div:nth-child(odd)  { padding-right: 0; border-right: 0; }
  .info-grid > div:nth-child(even) { padding-left: 0; }
  .info-grid > div + div           { border-top: 1px solid var(--line); }
}

/* Two abreast, and square. One column would give each marmot the whole width,
   which sounds generous and isn't: at 4/3 only one photograph is ever in the
   eye at once, and eighteen of those is a feed to be scrolled past rather than
   a board to be read. Held at two the set stays legible as a set.

   The crop squares here because the frames are now half the width. A 4/3 tile
   at this size lands as a stripe; the same tile square lands as a picture, and
   the viewer is right there for anyone who wants one whole. */
@media (max-width: 520px) {
  .roster { grid-template-columns: repeat(2, 1fr); }
  .marmot img { aspect-ratio: 1 / 1; }

  /* The sign was sitting in a clearing. Two thirds of a handset screen went to
     the header, the gap, the sign and the gap again before a single marmot —
     and the gaps were not even with each other either: sixty above the
     artwork, forty-six below.

     The unevenness is in the artwork, not the layout. The PNG is cut with
     transparent air of its own, and it is not symmetrical — 76px at the top of
     739 and 92px at the foot — so equal CSS gaps land unequal on the eye. The
     numbers here are the difference: 26 and 24, which put the same 34px of
     clear ground on both sides of the sign once the file's own padding is
     counted in. Thinner than the gap it replaces, so the sign reads as the
     board's heading rather than as a plate of its own.

     The h1's own bottom margin is dropped rather than reduced. It collapses
     against the head's, so left at 26 it would simply overrule the 24 and the
     careful half of the pair would do nothing. */
  .marmots-page .page        { padding-top: 26px; }
  .marmots-page .sign-title  { margin-bottom: 0; }
  .marmots-page .page-head   { margin-bottom: 24px; }
}

/* Between the tabs and the clock the header runs out of room before the menu
   button arrives, so the clock sheds its finest units first. */
@media (max-width: 1100px) {
  .site-header { gap: 16px; }
  .header-countdown div:nth-child(n + 4) { display: none; }
}

@media (max-width: 980px) {
  .header-countdown div:nth-child(n + 3) { display: none; }
}

@media (max-width: 760px) {
  :root { --header-h: 68px; }

  .menu-button { display: block; }
  .site-nav {
    display: none;
    position: absolute;
    top: var(--header-h);
    left: 0;
    width: 100%;
    padding: 20px var(--gutter) 26px;
    background: var(--paper);
    color: var(--ink);
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    border-bottom: 1px solid var(--line);
    box-shadow: 0 18px 30px rgba(0, 0, 0, .12);
  }
  .site-nav.is-open { display: flex; }
  .site-nav a { padding: 14px 0; border-bottom: 1px solid var(--line); }
  .site-nav a[aria-current="page"] { color: var(--navy); }

  /* The reply is the one thing we are actually asking of anybody, and set as
     the fourth line of five it reads as the fourth of five. The bar has no room
     to carry it, so the open panel does: given as a button it holds the weight
     without costing a fixed row on every screen. Every page still closes with
     the same ask at its foot; this is that ask reachable from the top. */
  .site-nav .nav-rsvp {
    /* Last in the panel whatever its place in the tab order, so the list reads
       as four destinations closed off by a rule, and then the one thing to do. */
    order: 1;
    margin-top: 16px;
    padding: 18px 0;
    border-bottom: 0;
    background: var(--crimson);
    color: #fff;
    text-align: center;
  }
  .site-nav .nav-rsvp[aria-current="page"] { color: #fff; }

  /* The aside gives up its own box so its children fall straight into the
     header grid: the date takes the middle track and the button the right. The
     middle track is the centre of the page, so the date is centred on the bar
     rather than in whatever the wordmark happens to leave over. */
  .header-aside { display: contents; }

  /* The clock steps down here and the date takes the middle instead. Four units
     counting in a bar that is fixed to the top of every screen is motion in the
     corner of the eye of anyone reading the schedule, and it asks to be watched
     rather than glanced at. The date asks for nothing: it sits still, and it
     tells you the one thing the bar is there to keep saying. */
  .header-countdown { display: none; }
  .header-date {
    display: block;
    grid-column: 2;
    justify-self: center;
    margin: 0;
    /* The footer's date carries no rule of its own — it is simply the footer's
       own type — so this is that declaration, taken up two points. Past the
       button's eleven on purpose: the date is what the bar is there to say and
       Menu is only the way to the rest of it, so the centre is allowed to speak
       a little louder than the control at the edge. Same family, weight and
       tracking as the footer, so it is still the one date said twice. */
    font: 700 12px/1 var(--sans);
    letter-spacing: .2em;
    /* Tracking is added after the last figure as well as between them, and a
       centred line would sit that half-space to the left of true. Taken back. */
    margin-right: -.2em;
    white-space: nowrap;
  }
  .menu-button { grid-column: 3; justify-self: end; }

  .hero-copy { padding-bottom: 90px; }
  .countdown:not(.footer-countdown):not(.header-countdown) { --cd-top: 8px; --cd-gap: 38px; }
  .countdown:not(.footer-countdown):not(.header-countdown) div { padding: var(--cd-top) 0 30px; }
  /* The bar already says 14 · 08 · 27 and it says it on every screen of the
     page, so the footer saying it again a thumb's width below is the same date
     twice in one view. It goes, and the clock takes the line to itself. */
  .footer-date { display: none; }

  /* Set inline, the footer's clock is eight things in a row — four figures and
     four words, alternating — and at this width it wrapped mid-pair. Stacked,
     each unit is one column with its own label under it, the four columns read
     across as a clock rather than a sentence, and nothing has to wrap. */
  .footer-countdown { align-items: flex-start; gap: 24px; }
  .footer-countdown div { flex-direction: column; align-items: center; gap: 5px; }
  /* Tracking is added after the last letter as well as between them, so a
     centred label sits that half-space to the left of its figure. Taken back. */
  .footer-countdown span { margin-right: -.2em; }

  /* The time column is cut to the digits and no wider. At 76px a five-figure
     time left thirty points of dead air inside its own cell, and with the gap
     on top of it the board opened a trench down the middle — read as the
     layout's breathing room, and paid for by the column that had none to
     spare. Fifty-six clears 00:00 at tabular figures with room to sit, and
     hands the rest back to the entry. What keeps the times reading as a column
     at that width is not the air beside them but the entry they sit against:
     held at 20.5px the titles hold their own left edge, and the digits stay a
     column rather than a prefix. */
  .board-head,
  .timeline li { grid-template-columns: 56px 1fr; gap: 14px; padding-inline: 16px; }
  .timeline time { font-size: 17px; }

  /* Twenty-two over fourteen is a print ratio, and it holds on a sheet the
     width of the page; on a handset it puts the longest titles against both
     edges of their cell. Twenty and a half is the size that keeps the entry
     the thing your eye lands on first without reaching the margins. */
  .timeline h2 { font-size: 20.5px; }

  /* The pair stays a pair on a handset. Stacked, a song and its artist are two
     boxes among six and nothing says which belongs to which; held in a row,
     the row is the grouping. What it costs is width — about seventeen
     characters visible at rest — and that is a fair price for a field typed
     once and never read back, since the value scrolls rather than clips and
     comes back whole on focus. The columns stay even: a bias towards the song
     buys it barely more than a character, and the pair is cleaner square. */
  .song-row { column-gap: 10px; }
  .field-pair { grid-template-columns: 1fr; gap: 18px; }
  /* No margin to sit in at this width, so the cross comes up into the label
     line and out to the edge: level with ARTIST, which is the row it belongs
     to, and six points short of the field's right edge rather than sixteen.
     The box overhangs the sheet by nine and rises into the eighteen-point gap
     above the row — both are clear ground, and the first row never carries a
     cross, so there is always a gap above it. */
  .remove-song { right: -9px; top: -11px; }

  .site-footer { flex-direction: column; gap: 10px; text-align: center; }
}

/* --- Phones --------------------------------------------------------------- */
/* On a handset the sheet is the screen. Every millimetre the paper gives back
   to its own margins is a millimetre the lines lose, so here the trim comes in
   to a hairline, the printer's keyline follows it, and the padding drops to
   what a real card would leave. The type steps down with it — not to make the
   sheet smaller, but to buy the prose the extra characters per line that stop
   it breaking into short, ragged rows. */
@media (max-width: 480px) {
  /* Ground showing at the edge, not a gutter — the sheet takes the rest. */
  .day .container,
  .page .container { padding-inline: 12px; }

  /* And the same hairline of ground above and below it. The poster stood 52px
     under the bar and 12px in from the edges, and an inset that deep at the top
     and that thin at the sides reads as a sheet dropped down the screen rather
     than one set on it — the gap above became the first thing on the page.
     Squared to the 12px the sides already keep, the ground is an even border on
     all four sides and the page starts where the page starts. The width is the
     one the sides were already set to, not the header's: at this size the sheet
     is the screen, and every millimetre held back from the trim is a millimetre
     the lines lose. */
  .page { padding: 12px 0; }

  /* Once the ask is ruled at its foot, the ground under it is the same ground
     that runs above the poster: 12px of it, between a hairline and the bar at
     the top and between a hairline and the footer at the bottom. So the page
     opens and closes on the same trim, and .page's own padding is the whole of
     it — no margin of its own. The 44px the rule stands off the button is what
     keeps the hairline from reading as part of the navy; the gap below it is
     the page's border, not the section's air. */
  .reply-close { margin-bottom: 0; }

  .poster { padding: 34px 18px 26px; }
  .poster::before { inset: 7px; }
  .sheet   { padding: 32px 18px 24px; }

  /* Every sign steps down by the same factor, so the set keeps the matched
     height it was cut to. */
  :root { --sign-scale: .86; }
  .sheet-sign { margin-bottom: 20px; }

  /* The lead holds its own width — it is two short centred lines by design, and
     it is the narrower shape that tells you it is a standfirst and not the body.

     The body is set to fill the sheet instead, and the lever for that is the
     line count, not the measure: balanced text divides the paragraph evenly
     across however many lines it needs, so a paragraph that breaks to five
     lines sits at four fifths of the width whatever you set max-width to. The
     opening paragraph turns over to five lines at 17px and four at anything
     under about 4.25vw, and four lines fill the sheet almost exactly. Sizing
     the body in vw rather than px is what holds that break at four across every
     handset — 15.6px on a 375, 17.9px on a 430 — so the block reaches the same
     edge as the date band on all of them. The floor takes over below 373px,
     where four lines would mean type too small to read, and it falls back to
     five lines gracefully. */
  .sheet-lead { font-size: 19px; }
  .sheet-prose { font-size: clamp(15.5px, 4.15vw, 18px); line-height: 1.72; }
  .sheet-prose p { text-wrap: balance; }

  /* The band keeps its three columns here rather than unrolling into a list.
     Stacked, the four rows read as four separate announcements and push the
     onward links a screen and a half below the date; held in a row they stay
     what they are on paper — one line of times under one date, the whole of
     the day visible in a single glance. What has to give to hold three columns
     at this width is the type, not the structure: the labels lose a point and
     some of their tracking, the times drop to 18px, and the glosses beneath
     them tighten. Everything still clears comfortably at 375px. */
  .sheet-facts div { padding: 18px 5px; }
  .sheet-facts .fact-date { padding-bottom: 20px; }
  .sheet-facts dt { margin-bottom: 8px; font-size: 10px; letter-spacing: .11em; }
  .sheet-facts dd { font-size: 18px; }
  .sheet-facts dd small { margin-top: 5px; font-size: 11.5px; line-height: 1.35; }

  /* Held as a pair for the same reason, and tracked in to fit. */
  .sheet-foot a { padding: 17px 4px; font-size: 11px; letter-spacing: .1em; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; }
}
