/* One. The board, and almost nothing else.
   A phone or tablet gets one screen that never scrolls, so a finger on the board always draws and never fights the page.
   After a painting, it holds at full size for a moment, then settles smaller so the next steps fit beneath it; nothing
   is ever pushed out of view. A computer hangs the board with its label above it and keeps the round and the week
   beside its foot. Colour only ever comes from paintings.
   Sizes from the two scales in game.css: space 4 8 12 16 24 32 48 64 96; type 12 14 16 18 20 24 30. */

h1, h2 { margin: 0; font-family: var(--serif); font-optical-sizing: auto; font-weight: 500; letter-spacing: -0.01em; }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 4px; border-radius: 4px; }
body { touch-action: manipulation; }
.mast, .placard, .after-actions, .week, .week-mini { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }

.wordmark {
  display: block;
  width: 112px;
  height: 24px;
  background: currentColor;
  -webkit-mask: url("/assets/img/wordmark.svg") left center / contain no-repeat;
  mask: url("/assets/img/wordmark.svg") left center / contain no-repeat;
}
.badge { display: inline-block; flex: none; line-height: 0; border-radius: 8px; }
.badge img { height: 40px; width: auto; }

/* MARK: The label. Two lines, one voice: the work's number, then its date in the caption's size and colour. */

.board-label { display: grid; gap: 2px; min-width: 0; }
.board-label h1 { font: 600 20px/1.1 var(--serif); letter-spacing: 0; font-variant-numeric: lining-nums; }
.board-date { font-size: 14px; line-height: 1.3; color: var(--quiet); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* MARK: The week in miniature: seven boards a fingertip wide, today marked beneath, painted days in their colours */

.week-mini {
  appearance: none;
  display: block;
  margin: -12px -8px -12px 0;   /* a 44 px target around a small mark */
  padding: 12px 8px;
  border: 0;
  background: none;
  cursor: pointer;
}
.week-mini ol { display: grid; grid-template-columns: repeat(7, 10px); gap: 4px; margin: 0; padding: 0; list-style: none; }
.week-mini li { display: grid; justify-items: center; gap: 4px; }
.week-mini canvas { display: block; width: 10px; aspect-ratio: 32 / 52; background: var(--linen); }
.week-mini .to-come canvas { background: none; box-shadow: inset 0 0 0 1px var(--hairline); }
.week-mini li::after { content: ""; width: 3px; height: 3px; border-radius: 50%; }
.week-mini li.today::after { background: var(--ink); }

/* MARK: The week, full size (the sheet on a phone, beside the board on a computer) */

.week { display: grid; gap: 12px; }
.week-title { font: 600 14px/1.2 var(--sans); color: var(--quiet); }
.week-days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; margin: 0; padding: 0; list-style: none; }
.week-day {
  appearance: none;
  display: grid;
  gap: 8px;
  justify-items: center;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: 500 12px/1 var(--sans);
  cursor: pointer;
}
.week-day:disabled { cursor: default; }
.week-day canvas { display: block; width: 100%; aspect-ratio: 32 / 52; background: var(--linen); }
.week-day.to-come canvas { background: none; box-shadow: inset 0 0 0 1px var(--hairline); }
.week-day .letter { color: var(--quiet); }
.week-day.today .letter { color: var(--ink); font-weight: 700; }
.week-day.today::after { content: ""; width: 4px; height: 4px; margin-top: -4px; border-radius: 50%; background: var(--ink); }
@media (hover: hover) { .week-day:not(:disabled):hover canvas { box-shadow: var(--hung); } }

/* MARK: The board and its placard */

.hero-play { grid-area: play; display: grid; gap: 12px; align-content: start; justify-items: center; }
.board { width: var(--board-w); view-transition-name: board; }
.placard { width: var(--board-w); min-height: 48px; view-transition-name: placard; }
.placard-line { display: none; }   /* the game teaches by showing: the hand, then the paint */
.daily .board.finished ~ .placard { align-items: flex-start; }

/* MARK: After a round. Words only when they tell you something (a race won or lost, a record): a first finish lets the
   painting and its time speak. Then send it, try again, and the app, once. */

.side { grid-area: side; display: grid; gap: 24px; align-content: start; }
.board-note { font-size: 14px; line-height: 1.4; color: var(--quiet); }
.after { display: grid; gap: 16px; }
.result { margin: 0; gap: 2px; }
.result-big { font-size: 24px; }
.result-small { font-size: 14px; }
.after-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 24px; }
.next-board { margin: 0; font-size: 14px; }
.record-line { margin: 0; font-size: 12px; }

.offer { display: grid; gap: 12px; }
.offer [data-for][hidden] { display: none; }
.offer-apple { display: flex; justify-content: center; }
.offer-line { font-size: 14px; line-height: 1.35; }
.scan { margin: 0; }
.qr { display: block; width: 72px; height: 72px; color: var(--ink); }
[data-device="computer"] .offer-apple .badge { display: none; }

/* The round arrives in a short cascade once the painting has settled. */
.settled .after > :not([hidden]) { animation: rise 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
.settled .after > :nth-child(2) { animation-delay: 60ms; }
.settled .after > :nth-child(3) { animation-delay: 120ms; }
.settled .after > :nth-child(4) { animation-delay: 180ms; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }
/* Until then, the painting has the screen to itself. */
.hero:not(.settled) .after { display: none; }

/* MARK: Arriving. The wall first, then the work, then its label: a room's lights coming up. */

.board { animation: appear 0.9s cubic-bezier(0.2, 0.7, 0.2, 1) 0.05s both; }
.board-label, .week-mini, .week-full { animation: appear 0.6s ease-out 0.35s both; }
.placard { animation: appear 0.6s ease-out 0.55s both; }
@keyframes appear { from { opacity: 0; } }
::view-transition-group(board), ::view-transition-group(placard) { animation-duration: 0.7s; animation-timing-function: cubic-bezier(0.3, 0.9, 0.3, 1); }
::view-transition-old(week-today), ::view-transition-new(week-today) { animation-duration: 0.6s; }

/* MARK: Sheets */

.week-sheet[open] { gap: 24px; justify-items: stretch; }
/* Wider than the other sheets on a computer, for seven days in a row; on a phone it spans the screen like the rest. */
@media (min-width: 600px) { .week-sheet { width: 400px; } }
.sheet-links { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 14px; }
.sheet-links a { color: var(--ink); text-decoration: none; }
.legal { font-size: 12px; line-height: 1.5; color: var(--quiet); }
.label-title { font: 600 20px/1.2 var(--serif); }
.label-name { font: italic 400 18px/1.3 var(--serif); color: var(--quiet); }
.day-sheet .action-primary { text-decoration: none; }
.day-app { display: grid; gap: 12px; width: 100%; }

.small-print { grid-area: small; display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 0 16px; font-size: 12px; line-height: 1; color: var(--quiet); }
.small-print nav { display: contents; }
.small-print a, .colophon { color: var(--quiet); text-decoration: none; padding: 8px 0; }
.colophon { appearance: none; border: 0; background: none; font: inherit; cursor: pointer; }
@media (hover: hover) { .small-print a:hover, .colophon:hover { color: var(--ink); } }
.colophon-sheet[open] { gap: 16px; }
.colophon-sheet .wordmark { width: 128px; height: 24px; margin-bottom: 8px; }

/* MARK: Phones and tablets: one screen, never scrolled. A touch screen with room for the computer layout (an iPad on its
   side: wider than 999 and 700 tall or more) takes the computer layout instead; the split is by space, not by pointer.
   The two queries are exact complements (range syntax, so a fractional width can't fall between them):
     phone     (width <= 999px), (pointer: coarse) and (height < 700px)
     computer  (width > 999px) and (pointer: fine), (width > 999px) and (pointer: none), (width > 999px) and (height >= 700px) */

@media (width <= 999px), (pointer: coarse) and (height < 700px) {
  html, body { height: 100%; overflow: hidden; overscroll-behavior: none; }
  .hero {
    --gutter: 16px;
    --mast: 56px;
    --insets: calc(env(safe-area-inset-top) + env(safe-area-inset-bottom));
    /* Playing: everything the label and the placard leave. The frame keeps that width when the board settles smaller,
       so the label and everything under it stay put, aligned to the board's own edges. */
    --frame-w: min(100vw - 2 * var(--gutter), (100dvh - var(--insets) - var(--mast) - 8px - 12px - 48px - 8px - var(--foot, 72px)) * 32 / 52);
    --board-w: var(--frame-w);
    display: grid;
    grid-template-rows: var(--mast) auto auto minmax(0, 1fr);
    grid-template-areas: "mast" "play" "side" "small";
    row-gap: 8px;
    height: 100dvh;
    padding: env(safe-area-inset-top) max(var(--gutter), env(safe-area-inset-right)) env(safe-area-inset-bottom) max(var(--gutter), env(safe-area-inset-left));
    align-content: start;
    justify-items: center;
  }
  .mast, .side { width: var(--frame-w); }
  /* Settled, meeting a friend's run, or with a note under it (a link to a board this page doesn't play): the board makes
     room for what comes next (title, actions, the app, the note). The note's case at the specificity of .hero alone, so
     the landscape layout's own sizing (later in this file) still decides there. */
  .hero.settled, .hero.inviting, .hero:where(:has(#board-note:not([hidden])), :has(.side > .story)) {
    --board-w: max(120px, min(var(--frame-w), (100dvh - var(--insets) - var(--mast) - 8px - 12px - var(--placard, 72px) - 8px - var(--next, 160px) - 8px - var(--foot, 72px)) * 32 / 52));
  }
  /* The top bar. A (default): the name of the place on the left, the work's label on the right: its number on the
     wordmark's line, its date beneath as the caption. Tap the label for the week. */
  .mast { grid-area: mast; display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; column-gap: 16px; padding-top: 10px; }
  .mast .wordmark { display: block; width: 92px; height: 22px; }
  .mast .board-label { justify-items: end; text-align: end; cursor: pointer; }
  .mast .week-mini { display: none; }
  /* B: two rows. The place and your week on the first; the work's label, stacked, over the board's left edge. */
  [data-bar="b"] .hero { --mast: 96px; }
  [data-bar="b"] .mast { grid-template-columns: auto auto; grid-template-areas: "mark week" "label label"; justify-content: space-between; row-gap: 12px; padding-top: 4px; }
  [data-bar="b"] .mast .wordmark { grid-area: mark; align-self: center; }
  [data-bar="b"] .mast .week-mini { grid-area: week; display: block; margin: 0; padding: 4px 0; align-self: center; }
  [data-bar="b"] .mast .board-label { grid-area: label; justify-items: start; text-align: start; }
  /* C: the work's label leads, stacked on the left; the wordmark signs the top right, on the number's line. */
  [data-bar="c"] .mast { grid-template-columns: minmax(0, 1fr) auto; }
  [data-bar="c"] .mast .wordmark { order: 2; -webkit-mask-position: right center; mask-position: right center; }
  [data-bar="c"] .mast .board-label { justify-items: start; text-align: start; }
  .side { padding-top: 16px; }
  /* The foot keeps clear of the screen's bottom edge (and of Safari's toolbar floating over it): more room under the last
     line than between its lines. */
  .small-print { align-self: end; padding-bottom: 24px; }
  /* Settled, the painting is narrower than the screen, so its label centres under it, as a card does under a print:
     the name, then the colours, then the time. */
  .hero.settled .placard, .daily.settled .board.finished ~ .placard { width: var(--frame-w); flex-direction: column; align-items: center; gap: 4px; min-height: 0; text-align: center; }
  .hero.settled .placard-title { font-size: 18px; }
  .hero.settled .placard-title i { margin-top: 2px; font-size: 16px; }
  .hero.settled .board.finished ~ .placard .race, .hero.settled .placard .race { margin: 0; }
  .hero.settled .clock { font-size: 14px; font-weight: 500; color: var(--quiet); }
  .hero.settled .after-actions { justify-content: center; }
  .hero.settled .result { text-align: center; }
  .week-full { display: none; }
}
@media (600px <= width <= 999px), (width >= 600px) and (pointer: coarse) and (height < 700px) {
  .hero { --gutter: 32px; --mast: 64px; }
  .board-label h1 { font-size: 24px; }
  .week-mini ol { grid-template-columns: repeat(7, 14px); gap: 6px; }
  .week-mini canvas { width: 14px; }
}

/* MARK: A computer: the label above the board, its number and date at either end; the round and the week beside it */

@media (width > 999px) and (pointer: fine), (width > 999px) and (pointer: none), (width > 999px) and (height >= 700px) {
  .hero {
    --board-w: clamp(200px, (100svh - var(--offer) - 48px - 32px - 36px - 12px - 12px - 48px - 144px) * 32 / 52, 560px);   /* 144: the foot */
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--board-w) minmax(280px, 1fr);
    /* The work centred in the space above the foot: equal room above and below it, the foot at the wall's edge. */
    grid-template-rows: minmax(0, 1fr) auto auto minmax(0, 1fr) auto;
    grid-template-areas: ". . ." ". mast ." ". play side" ". . ." "foot foot foot";
    column-gap: 64px;
    row-gap: 12px;
    min-height: calc(100svh - var(--offer));
    padding: 48px 48px 32px;
    position: relative;
  }
  /* The foot of the page, in the page (Refactoring UI: tertiary content, grouped, more space around than within):
     the links and the copyright on one line at the wall's edges, Apple's credit beneath in the faintest grey. */
  .small-print {
    grid-area: foot; display: grid; grid-template-columns: 1fr auto; align-items: center; row-gap: 8px;
    margin-top: 32px; padding-top: 24px; border-top: 1px solid var(--hairline); font-size: 14px;
  }
  .small-print nav { display: flex; gap: 24px; }
  .offer-apple { justify-content: flex-start; }
  .mast { grid-area: mast; display: flex; align-items: baseline; justify-content: space-between; }
  .mast .wordmark { position: absolute; top: 32px; left: max(48px, (100vw - 1344px) / 2); }   /* on the same edge as the foot */
  .board-label { display: contents; }
  .board-label h1 { font-size: 24px; }
  .board-date { font-size: 16px; }
  .week-mini { display: none; }
  .side { align-self: end; max-width: 320px; padding-bottom: 60px; gap: 32px; }
}

.notes { margin-top: 24px; padding: 16px; border: 1px dashed var(--hairline); border-radius: 4px; font-size: 14px; color: var(--ink); }
.notes summary { cursor: pointer; font-weight: 600; }
.notes-body { display: grid; gap: 12px; max-width: 48em; margin-top: 12px; }

@media (display-mode: standalone) { .hero { padding-top: max(env(safe-area-inset-top), 12px); } }

@media (prefers-reduced-motion: reduce) {
  .board, .board-label, .week-mini, .week-full, .placard, .settled .after > * { animation: none !important; }
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

.colophon .short { display: none; }
@media (max-width: 599px) { .colophon .long { display: none; } .colophon .short { display: inline; } }

/* MARK: The marks, as the app draws them (Composition/UI/Marks.swift, Placard.swift). The clock on the left, in Title 3
   semibold with fixed-width digits, faint until the first line. On the right one glass capsule, 44 tall, holding the
   lines you can still lose (strokes 2.5 by 14, 4 apart; a lost one snapped in two at 20%) and the ring (28 across,
   a 2.5 stroke over a 12% track, the percentage in 10 semibold). Each appears only once it means something. */

:root { --faint: #888580; --glass-edge: rgba(255, 255, 255, 0.5); }
@media (prefers-color-scheme: dark) { :root { --faint: #66645F; --glass-edge: rgba(255, 255, 255, 0.1); } }

.placard { align-items: center; }
.placard-words { order: 2; }
.placard-marks { display: contents; }
.race { order: 1; margin-right: auto; display: flex; align-items: baseline; gap: 8px; }
.clock { font: 600 20px/1 var(--sans); font-variant-numeric: tabular-nums; letter-spacing: 0; }
.clock.idle { color: var(--quiet); }   /* the app uses its fainter grey; on the web that fails contrast (3.05:1) */
/* Finished: the painting's name leads and its time stands at the right, as the app sets it. */
.board.finished ~ .placard .race { order: 3; margin-right: 0; margin-left: auto; }

.progress {
  order: 3;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 0 14px;
  border-radius: 22px;
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  box-shadow: inset 0 0 0 0.5px var(--hairline), inset 0 1px 0 var(--glass-edge);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  backdrop-filter: blur(20px) saturate(1.6);
}
.progress:not(:has(> :not([hidden]))) { display: none; }
.progress > :not([hidden]) { animation: pop 0.35s cubic-bezier(0.3, 1.4, 0.5, 1) both; }
@keyframes pop { from { opacity: 0; transform: scale(0.4); } }

.spare { display: flex; align-items: center; gap: 4px; height: 14px; }
.spare i { display: block; width: 2.5px; height: 14px; border-radius: 1.25px; background: var(--ink); }
.spare i.lost { display: grid; align-content: space-between; background: none; opacity: 0.2; }
.spare i.lost::before, .spare i.lost::after { content: ""; display: block; height: 5.5px; border-radius: 1.25px; background: var(--ink); }

.ring { width: 28px; height: 28px; }
.ring circle { stroke-width: 3.2; }   /* 2.5 at 28 across, in the 36 the circle is drawn in */
.ring-track { stroke: var(--ink); opacity: 0.12; }
.ring-fill { transition: stroke-dasharray 0.5s cubic-bezier(0.4, 0, 0.2, 1); }
.ring-number { font: 600 12.9px var(--sans); font-variant-numeric: tabular-nums; }   /* 10 at 28 across */

/* Settled after a finish, the marks have said their piece: the time moves under the name (see the phone rules). */
.hero.settled .progress { display: none; }

/* MARK: When the day's tries are spent: the next board's number, and a countdown to it. */
.next-board { display: none !important; }

/* The next board is also the way to hear about it: tap it to put every board in your calendar. */
.cal { width: 18px; height: 18px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.calendar-link { text-decoration: none; justify-self: start; }

/* MARK: The day's times, as the app's past-board page lists them: a card a step forward from the wall (the app's
   editionCard), rows 12 apart with hairlines between, a 28 mark, the name, the time in semibold with fixed digits. */
.times { margin: 0; padding: 0 16px; list-style: none; border-radius: 24px; background: rgba(255, 255, 255, 0.62); }
@media (prefers-color-scheme: dark) { .times { background: rgba(255, 255, 255, 0.07); } }
.times[hidden] { display: none; }
.times li { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; gap: 12px; padding: 12px 0; font-size: 15px; line-height: 1.25; }
.times li + li { border-top: 0.5px solid var(--hairline); }
.times .who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.times .you .who { font-weight: 600; }
.times .t { font-weight: 600; font-variant-numeric: tabular-nums; }
/* Each row's coin (coin.js): its own painting struck as a toy token, the record's in gold. The canvas is a fifth
   larger than the coin so the shadow can fall below it; the negative margin keeps the coin's 28 in the row. */
.times .coin { display: block; width: 33.6px; height: 33.6px; margin: -2.8px; }
.times:not([hidden]) { animation: rise 0.4s cubic-bezier(0.23, 1, 0.32, 1) both; }

/* MARK: The next board, once today's tries are spent. Its number and the time left on the left; on the right, the
   one thing that lets you hear the moment it's out: the App Store on iPhone and iPad (the app's reminder), the
   calendar everywhere else. A quiet line under it says why the badge is there; nothing else needs saying. */
.next { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: 16px; row-gap: 6px; }
.next[hidden], .next [data-for][hidden] { display: none; }
.next-when { display: grid; gap: 2px; line-height: 1.2; }
.next-when b { font: 600 18px/1.2 var(--serif); }
.next-when span { font-size: 14px; color: var(--quiet); font-variant-numeric: tabular-nums; }
.action-quiet {
  display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 14px; border-radius: 20px;
  color: var(--ink); font-size: 15px; font-weight: 600; text-decoration: none; white-space: nowrap;
  box-shadow: inset 0 0 0 1px var(--hairline);
  transition: transform 160ms cubic-bezier(0.23, 1, 0.32, 1);
}
.action-quiet:active, .action-primary:active, .badge:active { transform: scale(0.97); opacity: 1; }
.action-primary, .badge { transition: transform 160ms cubic-bezier(0.23, 1, 0.32, 1); }
.next:not([hidden]) { animation: rise 0.3s cubic-bezier(0.23, 1, 0.32, 1) both; }
/* With the next board on offer, the badge already stands beside it: the round's own badge steps aside. */
.after:has(.next:not([hidden]) [data-for="apple"]:not([hidden])) .offer { display: none; }
@media (width <= 999px), (pointer: coarse) and (height < 700px) { .hero.settled .after-actions:has(#again[hidden]) { justify-content: center; } }

/* MARK: The top bar while you play. Before the first line: the wordmark, and the board's label on the right. Once a
   try begins, the wordmark steps out, the label slides to the left, and today's tries take the right-hand side:
   what you're playing, and how many goes you have left. */
.tries-mark { display: inline-flex; align-items: center; gap: 4px; }
.tries-mark[hidden] { display: none; }
.tries-mark i { display: block; width: 8px; height: 13px; background: #8A847A; box-shadow: none; transition: background-color 300ms ease, box-shadow 300ms ease; }
.tries-mark i.spent { background: transparent; box-shadow: inset 0 0 0 1px var(--faint); }
@media (prefers-color-scheme: dark) { .tries-mark i { background: #857E72; box-shadow: none; } .tries-mark i.spent { background: transparent; box-shadow: inset 0 0 0 1px var(--faint); } }
@media (width <= 999px), (pointer: coarse) and (height < 700px) {
  .mast .tries-mark { grid-column: 2; grid-row: 1; justify-self: end; align-self: center; }   /* centred on the stacked number and date */
  .mast { position: relative; }
  .mast.playing { grid-template-columns: minmax(0, 1fr) auto; }
  .mast.playing .wordmark { position: absolute; opacity: 0; pointer-events: none; }
  .mast.playing .board-label { grid-column: 1; grid-row: 1; justify-items: start; text-align: start; }
}
@media (width > 999px) and (pointer: fine), (width > 999px) and (pointer: none), (width > 999px) and (height >= 700px) {
  .mast .tries-mark { margin-left: 16px; align-self: center; }
  .mast.playing .board-date { margin-left: auto; }
}

/* Today's tries live in one place at a time: in the top bar once it shows them, otherwise on the retry button. */
.hero:has(.mast.playing) #again .tries { display: none; }

/* Your time lives in one place: on the day's times once they're shown, otherwise beside the painting's name. */
.hero:has(#times:not([hidden])) .board.finished ~ .placard .race { display: none; }


/* MARK: The round, laid out once (Refactoring UI): one column, every block the column's width, two shapes from the app
   (the card, the capsule), one solid action. Blocks sit 16 apart; inside a card, rows 12. */
:root { --card: rgba(255, 255, 255, 0.62); --radius: 24px; }
@media (prefers-color-scheme: dark) { :root { --card: rgba(255, 255, 255, 0.07); } }
.after { gap: 16px; }
.times { background: var(--card); border-radius: var(--radius); }

/* The actions: Share alone takes the whole width; with Try again beside it, the two share the row equally. */
.after-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 12px; }
.after-actions > [hidden] { display: none; }
.after-actions .action-primary, .after-actions .action {
  height: 48px; padding: 0 12px; border-radius: var(--radius); justify-content: center; font-size: 16px; font-weight: 600; white-space: nowrap;
}
.after-actions .action { background: var(--card); color: var(--ink); }
.hero.settled .after-actions:has(#again[hidden]) { justify-content: stretch; }

/* Tomorrow: a card like the day's times, the board's number and the time left on the left, the one way to hear the
   moment it's out on the right: the App Store badge on iPhone and iPad, a quiet calendar action anywhere else. */
.next, .daily .next { margin: 0; background: var(--card); border-radius: var(--radius); padding: 12px 16px; column-gap: 12px; }
.next-when b { font: 600 16px/1.25 var(--serif); }
.next-when span { font-size: 14px; }
.next .action-quiet { appearance: none; border: 0; background: none; cursor: pointer; font-family: inherit; height: 44px; padding: 0 4px; box-shadow: none; border-radius: 12px; font-size: 15px; color: var(--ink); }
.next .badge img { height: 36px; }

/* The App Store, when it stands alone (tries left): centred under the actions, the same 16 below them. */
.offer { justify-items: center; }
.offer-apple { justify-content: center; }

/* The code inside the next board's card sits where the badge sits on a phone: right-aligned, the card's own height. */
.next .scan .qr { width: 64px; height: 64px; }
/* With the next board on offer, its card carries the app: the round's own code steps aside. */
.after:has(.next:not([hidden])) .offer [data-for="computer"] { display: none; }
.after:has(.next:not([hidden])) .offer:not(:has([data-for]:not([hidden]):not([data-for="computer"]))) { display: none; }
/* Remind me, as a secondary capsule: the bell in the label's colour, the same size as Share's icon. */
.after-actions #remind { text-decoration: none; }
.after-actions .cal { width: 20px; height: 20px; }
/* Share leads; the other half is quieter. Two halves exactly, whichever pair is showing; one alone takes the row. */
.after-actions { grid-template-columns: 1fr; }
.after-actions:has(> :not([hidden]) ~ :not([hidden])) { grid-template-columns: 1fr 1fr; }

/* At 0 the ring is only its track: a round cap on a zero-length stroke would leave a dot at the top. */
.ring-fill[stroke-dasharray^="0 "] { opacity: 0; }

/* MARK: Edge cases (QA, 16 Oct) */

@media (width <= 999px), (pointer: coarse) and (height < 700px) {
  /* Settled, the painting never shrinks below 120: on a short phone the page scrolls instead (the board is done, so a
     scroll can't be mistaken for a line). The sides use the screen's full width, not the playing board's. */
  .hero.settled { --board-w: max(120px, min(var(--frame-w), (100dvh - var(--insets) - var(--mast) - 8px - 12px - var(--placard, 72px) - 8px - var(--next, 160px) - 8px - var(--foot, 72px)) * 32 / 52)); height: auto; min-height: 100dvh; }
  html:has(.hero.settled), body:has(.hero.settled) { height: auto; overflow: auto; }
  /* The round keeps the header's edges, unless that's narrower than the content needs (288 on the smallest phones). */
  .hero.settled .side { width: max(var(--frame-w), min(100vw - 2 * var(--gutter), 288px)); }
  .small-print { max-width: max(var(--frame-w), 288px); justify-self: center; }
}

/* Phones on their side: the board on the left at the screen's height, the round beside it, scrolling on its own. */
@media (orientation: landscape) and (height <= 560px) and (width <= 999px), (orientation: landscape) and (height <= 560px) and (pointer: coarse) {
  .hero, .hero.settled, .hero.inviting {
    /* 64: the label, number over date, with room under it before the board (it was 48, and the date sat on the board). */
    --frame-w: calc((100dvh - var(--insets) - 64px - 8px - 12px - var(--placard, 48px) - 8px) * 32 / 52);
    --board-w: var(--frame-w);
    grid-template-columns: var(--board-w) minmax(0, 1fr);
    grid-template-rows: 64px minmax(0, 1fr);
    grid-template-areas: "mast mast" "play side";
    column-gap: 32px;
    height: 100dvh;
    justify-items: stretch;
  }
  html:has(.hero.settled), body:has(.hero.settled) { height: 100%; overflow: hidden; }
  .mast, .hero.settled .side, .side { width: auto; }
  .side { overflow-y: auto; padding-top: 0; align-self: stretch; }
  .small-print { display: none; }
}

/* High contrast (forced colours): the marks drawn with backgrounds keep their own colours; cards and capsules get an edge. */
@media (forced-colors: active) {
  .wordmark, .tries-mark i, .spare i, .ring { forced-color-adjust: none; }
  .wordmark, .spare i { background: CanvasText; }
  .tries-mark i { background: Canvas; box-shadow: inset 0 0 0 1px CanvasText; }
  .tries-mark i:not(.spent) { background: CanvasText; }
  .times, .next, .progress, .after-actions .action-primary, .after-actions .action, .offer.on-card { border: 1px solid CanvasText; }
  .week-day.to-come canvas { box-shadow: none; outline: 1px solid GrayText; outline-offset: -1px; }
}

/* A sheet opens without a focus ring on its close button (it gets focus first); keyboard focus still shows, inside. */
.sheet-close:focus:not(:focus-visible) { outline: none; }
.sheet-close:focus-visible { outline-offset: -4px; }

/* On a computer with tries left, the app's code stands in a card like the next board's: the icon, then the code. */
.offer.on-card { grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; background: var(--card); border-radius: var(--radius); padding: 12px 16px; justify-items: stretch; }
.offer.on-card .offer-apple { display: contents; }
.offer.on-card .scan { grid-column: 3; }
.offer .offer-icon { width: 40px; height: 40px; border-radius: 9px; }
.offer:not(.on-card) .offer-icon { display: none; }

/* Very wide screens: the foot keeps to the page's measure, not the screen's edges. */
@media (width > 999px) and (pointer: fine), (width > 999px) and (pointer: none), (width > 999px) and (height >= 700px) { .small-print { width: 100%; max-width: 1344px; justify-self: center; } }

/* The record book's line is explainer copy: the check mark beside a time says it. */
#record-line { display: none !important; }

/* The invitation in the same two shapes as the round: Race this run, solid, the whole width; Play and Open the
   painting as two quiet capsules sharing the row beneath. */
.invite-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.invite-actions > [hidden] { display: none; }
.invite-actions .action-primary, .invite-actions .action {
  height: 48px; padding: 0 12px; border-radius: var(--radius); justify-content: center; font-size: 16px; font-weight: 600; white-space: nowrap;
}
.invite-actions .action-primary { grid-column: 1 / -1; }
.invite-actions .action { background: var(--card); color: var(--ink); }
.invite-actions .action:only-of-type, .invite-actions .action-primary[hidden] ~ .action:last-child { grid-column: 1 / -1; }

/* A computer whose next board is on offer: its card carries the code, so the round's own card steps aside entirely. */
.after:has(.next:not([hidden])) .offer.on-card { display: none; }
/* Short computer windows: the label keeps to the board's edges however small the board gets; when number and date
   no longer fit on one line, the date goes under the number, as on a phone, rather than hanging past the board. */
@media (width > 999px) and (pointer: fine), (width > 999px) and (pointer: none), (width > 999px) and (height >= 700px) {
  .mast { justify-self: center; width: var(--board-w); flex-wrap: wrap; column-gap: 16px; }
  .board-label h1, .board-date { white-space: nowrap; }
}

/* The painting sheet's Race this run: the same solid capsule as everywhere else. */
.painting-sheet .action-primary { height: 48px; border-radius: var(--radius); font-size: 16px; }

/* MARK: The race (race.js): the clock counts down their time, the number large and the words beside it in the
   placard's quieter voice, as the app sets "13.94s to beat Ana"; their ring beside yours in the rival's blue. */
.racing #clock, .racing #rival { display: none !important; }
.race-clock { display: inline-flex; align-items: baseline; gap: 6px; white-space: nowrap; }
.race-clock[hidden] { display: none; }
.race-clock b { font: 600 20px/1 var(--sans); font-variant-numeric: tabular-nums; }
.race-clock span { font-size: 14px; color: var(--quiet); }
.race-clock.behind b { color: var(--quiet); }

/* A run with no name (shared from the web, or by someone not signed in): "Shared run" stands where the name would,
   in the name's own type, rather than a tag beside an empty space. */
.sender-name:has(.name:empty) .sender-label { padding: 0; border-radius: 0; box-shadow: none; font-size: 16px; font-weight: 600; color: var(--ink); }
/* The race's words never push the capsule past the board's edge: they drop under the number when there isn't room. */
.race { min-width: 0; }
.race-clock { flex-wrap: wrap; row-gap: 2px; min-width: 0; white-space: normal; }
.race-clock span { white-space: nowrap; }

/* MARK: A friend's run (the invitation), refactored: the web has no avatars (a Game Center photo never leaves the
   app), so no faces anywhere. One card says whose time it is and the time itself, checked; under it, the one thing to
   do (Race this run) and the one alternative (Open the painting). Play appears only when the run can't be raced. */
.face { display: none !important; }
#invite { display: grid; gap: 12px; }
#invite .sender {
  grid-template-columns: minmax(0, 1fr) auto; gap: 12px; padding: 14px 16px;
  background: var(--card); border-radius: var(--radius);
}
#invite .sender-name { gap: 0; }
#invite .sender-name .name { font-size: 16px; }
/* Named, the name is enough: the tag only stands in when there's no name. */
#invite .sender-name:has(.name:not(:empty)) .sender-label { display: none; }
#invite .sender-time { font-size: 20px; }
.invite-actions { grid-template-columns: 1fr; }
.invite-actions:has(#race:not([hidden])) #play-plain { display: none; }
.invite-actions .action, .invite-actions .action:only-of-type { grid-column: 1 / -1; }
/* The painting sheet's runner: the same, without a card. */
.painting-sheet .sender-name:has(.name:not(:empty)) .sender-label { display: none; }

/* The invitation's own card goes: the clock's place already says whose time it is and that it's checked. */
#invite .sender { display: none; }
.race-clock .mark { align-self: center; width: 14px; height: 14px; color: var(--ink); }
.race-clock.unchecked b { color: var(--quiet); font-weight: 500; }
/* The check sits with the words it qualifies: the words' size (not the number's), on their baseline, centred on the
   lowercase letters rather than floating at the line's middle. */
.race-clock .mark { align-self: center; width: 15px; height: 15px; margin-left: -2px; }   /* 15 beside a 20 time, 4 after it, as the app sets it */

/* Verified runs carry a gold check (the painting's yellow, deepened on the light wall so it holds 3:1); a run that
   didn't replay carries the grey cross. The same two marks wherever a time appears. */
:root { --verified: #A67C00; }
@media (prefers-color-scheme: dark) { :root { --verified: #F4C736; } }
.mark-checked, .race-clock .mark-checked, .sender-time .mark-checked, .rival .mark { color: var(--verified) !important; }
.mark-broken { color: var(--quiet) !important; }
@media (forced-colors: active) { .mark-checked, .mark-broken { forced-color-adjust: none; background: CanvasText; } }

/* The painting sheet, as a museum label: the title, its colours, then the line a label gives its maker, here the
   runner and their time ("Ana · 7.84s ✓", or "Rival · 7.84s ✓"), quiet, in one line. Then the one action. */
.painting-sheet .sender {
  display: flex; align-items: center; gap: 8px; width: 100%; margin-top: -4px;
  font-size: 16px; color: var(--quiet);
}
.painting-sheet .sender-name { gap: 0; }
.painting-sheet .sender-name .name { font-size: 16px; font-weight: 500; color: var(--quiet); }
.painting-sheet .sender-name:has(.name:empty) .sender-label { padding: 0; box-shadow: none; border-radius: 0; font-size: 16px; font-weight: 500; color: var(--quiet); }
.painting-sheet .sender-time { font: 600 16px/1 var(--sans); color: var(--ink); gap: 4px; }
.painting-sheet .sender-time::before { content: "·" / ""; margin-right: 4px; color: var(--quiet); font-weight: 400; }
.painting-sheet .sender-time .mark { width: 13px; height: 13px; transform: none; }
.painting-sheet .label-name { color: var(--quiet); }

/* MARK: The race's finish (race.js): two paintings, one moment. They take the painting's place, side by side at
   the column's width; the one finished first lifts off the wall when the replay ends. Labels as a museum's. */
.duel { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; width: var(--frame-w, var(--board-w)); }
.duel[hidden] { display: none; }
.duel figure { margin: 0; display: grid; gap: 10px; align-content: start; }
.duel canvas { display: block; width: 100%; aspect-ratio: 32 / 52; background: var(--linen); transition: box-shadow 0.5s ease, transform 0.5s cubic-bezier(0.23, 1, 0.32, 1); }
.duel.done .first canvas { box-shadow: var(--hung); transform: translateY(-4px); }
.duel figcaption { display: grid; gap: 2px; font-size: 14px; line-height: 1.3; }
.duel .who { color: var(--quiet); }
.duel .duel-you .who { color: var(--ink); font-weight: 600; }
.duel .t { display: inline-flex; align-items: center; gap: 4px; font-weight: 600; font-variant-numeric: tabular-nums; }
.duel .t .mark { width: 13px; height: 13px; transform: none; }
/* Raced, the pair is the result: the single board and its placard step aside, as do the day's times. */
:is(.raced, .paired) .hero-play > #play, :is(.raced, .paired) .hero-play > .placard { display: none !important; }
:is(.raced, .paired) #times { display: none !important; }
/* The race's result in words ("You beat Ana.", "A tie.") isn't shown, the pair shows it; it stays for screen readers. */
:is(.raced, .paired) #result { position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* Raced on a phone, the pair and the actions sit in the middle of the space the footer leaves, not stacked at the top. */
@media (width <= 999px), (pointer: coarse) and (height < 700px) {
  :is(.raced, .paired) .hero.settled { grid-template-rows: var(--mast) minmax(0, 1fr) auto auto minmax(0, 1fr) auto; grid-template-areas: "mast" "." "play" "side" "." "small"; }
  :is(.raced, .paired) .hero.settled .hero-play { align-self: end; }
  :is(.raced, .paired) .hero.settled .side { align-self: start; }
}

/* The finish's choreography (race.js): your board moves into the pair under its own name; theirs comes in beside it. */
.raced .duel-you canvas { view-transition-name: board; }
.raced .duel-them { view-transition-name: rival; }
::view-transition-new(rival) { animation: rival-in 0.5s cubic-bezier(0.23, 1, 0.32, 1) 0.2s both; }
@keyframes rival-in { from { opacity: 0; transform: translateX(12px); } }
.duel figure { position: relative; }
.duel .ghost { view-transition-name: none !important; position: absolute; top: 0; left: 0; width: 100%; aspect-ratio: 32 / 52; pointer-events: none; }

/* Raced on a phone on its side: the pair takes the left at the screen's height (two boards wide), the round beside it. */
@media (orientation: landscape) and (height <= 560px) and (width <= 999px), (orientation: landscape) and (height <= 560px) and (pointer: coarse) {
  :is(.raced, .paired) .hero.settled {
    --board-w: calc((100dvh - var(--insets) - 64px - 12px - 12px - 48px) * 32 / 52);
    --frame-w: calc(2 * var(--board-w) + 12px);
    grid-template-columns: var(--frame-w) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    row-gap: 12px;
    grid-template-areas: "mast mast" "play side";
  }
  :is(.raced, .paired) .hero.settled .hero-play, :is(.raced, .paired) .hero.settled .side { align-self: center; }
}

/* A narrow placard (the smallest phones, or the board made room for an invitation): the race's words go under its
   time rather than under the capsule. */
.placard { container: placard / inline-size; }
@container placard (max-width: 300px) {
  /* The race takes what the capsule leaves (the capsule never shrinks), its words ending in "…" when a name is long. */
  .race { flex: 1 1 0; min-width: 0; }
  .progress { flex-shrink: 0; }
  .race-clock { display: inline-grid; grid-template-columns: auto minmax(0, 1fr); justify-content: start; align-items: center; column-gap: 6px; row-gap: 2px; max-width: 100%; }
  .race-clock .race-words { grid-column: 1 / -1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
/* The smallest placards (a 320 screen with the invitation up): the number a size down, so time and capsule share the
   board's width. */
@container placard (max-width: 180px) {
  .race-clock b { font-size: 16px; }
}

/* The top bar's two parts move as themselves through a settle, rather than cross-fading into a doubled label. */
.mast .board-label { view-transition-name: board-label; }
.mast .wordmark { view-transition-name: wordmark; }

/* A long name ends in an ellipsis inside the placard rather than running under the capsule or off the screen. */
.race-clock { max-width: 100%; }
.race-clock .race-words { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }

/* The game's code didn't arrive (index.html): the placard holds one capsule, Reload, at the board's width. */
#reload { flex: 1; height: 48px; padding: 0 12px; border-radius: var(--radius); justify-content: center; font-size: 16px; font-weight: 600; }

/* Raced on a computer: the pair is two boards wide, each at the size the board was, the round beside it as before.
   On a narrower window the pair gives way first, never the round's column. */
@media (width > 999px) and (pointer: fine), (width > 999px) and (pointer: none), (width > 999px) and (height >= 700px) {
  :is(.raced, .paired) .hero {
    --pair-w: min(2 * var(--board-w) + 12px, 100vw - 96px - 128px - 280px);
    grid-template-columns: minmax(0, 1fr) var(--pair-w) minmax(280px, 1fr);
  }
  .raced .duel, html.paired .diptych, :is(.raced, .paired) .mast { width: var(--pair-w); }
}
#play:focus:not(:focus-visible) { outline: none; }

/* Coming back with the day's tries spent (daily.js showPair): the same pair as a race's finish, shown already finished.
   Name over time, the first one done lifted off the wall, no chip, no faces. */
.paired .diptych { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; width: var(--frame-w, var(--board-w)); }
.paired .diptych figure { margin: 0; display: grid; gap: 10px; align-content: start; }
.paired .diptych .still { box-shadow: none; }
.paired .diptych .first .still { box-shadow: var(--hung); transform: translateY(-4px); }
.paired .diptych .hung-label { display: grid; gap: 2px; font-size: 14px; line-height: 1.3; }
.paired .diptych .hung-who { order: -1; color: var(--quiet); font-size: 14px; font-weight: 400; }
.paired .diptych figure:first-child .hung-who { color: var(--ink); font-weight: 600; }
.paired .diptych .hung-time { display: inline-flex; align-items: center; gap: 4px; font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; }
.paired .diptych .hung-time .mark { width: 13px; height: 13px; transform: none; }
.paired .diptych .face, .paired .diptych .sender-label { display: none; }

/* A sheet's badge with Apple's credit under it, in the faintest voice that still reads (the foot's own credit style). */
.day-app .credit { margin: 0; font-size: 12px; line-height: 1.4; color: var(--quiet); text-wrap: pretty; }

/* MARK: Sheets dim the room behind them, as a sheet does on iPhone: the page steps back so the sheet has your attention.
   The dimming fades with the sheet (in 0.3 s, out faster); on a computer the sheet rises a touch into place, on a phone
   it slides up from the bottom edge, both on the drawer curve. */
.painting-sheet {
  transition: opacity 0.3s cubic-bezier(0.32, 0.72, 0, 1), transform 0.3s cubic-bezier(0.32, 0.72, 0, 1),
    overlay 0.3s allow-discrete, display 0.3s allow-discrete;
}
.painting-sheet:not([open]) { opacity: 0; transform: translateY(8px) scale(0.98); transition-duration: 0.2s; }
@starting-style { .painting-sheet[open] { opacity: 0; transform: translateY(8px) scale(0.98); } }
.painting-sheet::backdrop {
  background: rgba(0, 0, 0, 0.32);
  transition: background-color 0.3s ease, overlay 0.3s allow-discrete, display 0.3s allow-discrete;
}
.painting-sheet:not([open])::backdrop { background: rgba(0, 0, 0, 0); transition-duration: 0.2s; }
@starting-style { .painting-sheet[open]::backdrop { background: rgba(0, 0, 0, 0); } }
/* On the dark wall a light veil wouldn't read: a deeper one, so the page clearly recedes. */
@media (prefers-color-scheme: dark) { .painting-sheet::backdrop { background: rgba(0, 0, 0, 0.56); } }
@media (max-width: 599px) {
  .painting-sheet:not([open]) { opacity: 1; transform: translateY(100%); }
  @starting-style { .painting-sheet[open] { opacity: 1; transform: translateY(100%); } }
}
@media (prefers-reduced-motion: reduce) {
  .painting-sheet, .painting-sheet:not([open]) { transform: none; }
  @starting-style { .painting-sheet[open] { transform: none; } }
}

/* A control that ticks on iOS (haptics.js) holds its invisible tap target inside itself. Set here, not from script:
   a control made before it's on the page has no computed position yet, and its target would cover the whole sheet.
   :where() keeps this at no weight, so a control placed absolutely (a sheet's close) stays where it is. */
:where([data-feel]) { position: relative; }
/* Your finished painting opens This Week (one.js). */
.hero.settled #play.finished, .hero.settled .duel-you canvas, .hero.settled #still-you { cursor: pointer; }

/* A narrow label on a computer or an iPad: the board is sized by the screen's height (--board-w, above), and below
   about 885 tall it's under 340 wide, too narrow for number, date and tries on one line. Then the number and the day's
   tries stay on the top line, at the board's two edges, and the date goes under the number. */
@media (width > 999px) and (height < 885px) and (pointer: fine), (width > 999px) and (height < 885px) and (pointer: none), (width > 999px) and (700px <= height < 885px) {
  /* (Not under a pair: the label is two boards wide there, room enough for one line.) */
  /* The label takes two lines here, so the board gives up the second line's height (36 for one line, 60 for two). */
  html:not(.raced, .paired) .hero { --board-w: clamp(200px, (100svh - var(--offer) - 48px - 32px - 60px - 12px - 12px - 48px - 144px) * 32 / 52, 560px); }
  html:not(.raced, .paired) .mast { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; row-gap: 2px; }
  html:not(.raced, .paired) .mast #board-title { grid-column: 1; grid-row: 1; }
  html:not(.raced, .paired) .mast #tries-mark { grid-column: 2; grid-row: 1; align-self: center; }
  html:not(.raced, .paired) .mast .board-date { grid-column: 1 / -1; grid-row: 2; justify-self: start; text-align: start; margin-left: 0; }
}

/* A day's sheet on a short screen (a phone on its side, or the smallest phones): the painting gives way so its title and
   Play it stay in view without scrolling inside the sheet. */
.day-sheet .still { width: min(224px, 56vw, (100svh - 300px) * 32 / 52); min-width: 96px; }

/* The narrowest phones (under 360 wide) while a friend's run is offered: the board is small there, too small for the
   race's time and words beside the capsule. The capsule, still at nothing before the first line, waits until the race
   begins; the countdown has the board's width, and only a long name gives way ("…"). */
@media (width < 360px) {
  .hero.inviting .progress { display: none; }
}

/* MARK: The board's story (worker.js boardStory, wall: true): only what someone looking a board up wants, its solution
   and the boards either side. The solution replays as a small painting hung between its neighbours; until the day has
   passed there's no solution, only the way back. On a computer it's the next stretch of wall below the board, in the
   board's column; on a phone it's one row in the week's sheet, under the week. */
.story[hidden] { display: none; }
.story-heading { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.story-title { display: grid; gap: 4px; margin: 0; font: 600 18px/1.2 var(--serif); color: var(--ink); }
.story-title span { font: 400 14px/1.3 var(--sans); color: var(--quiet); }
.story-art { display: grid; justify-items: center; gap: 4px; margin: 0; }
.story-art canvas { display: block; width: 160px; aspect-ratio: 32 / 52; background: var(--linen); box-shadow: var(--hung); }
.story-art figcaption { display: grid; justify-items: center; gap: 4px; max-width: 256px; text-align: center; }
.story-art figcaption p { margin: 8px 0 0; font-size: 14px; line-height: 1.5; color: var(--quiet); text-wrap: balance; }
.story-art .action { font-size: 14px; font-weight: 600; color: var(--ink); }
/* Under the caption: the App Store badge for a board that plays only in the app (the section's one primary action),
   then Watch again as a quiet text action beside it. */
.story-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px 24px; margin-top: 8px; }
.story-actions .badge[hidden] { display: none; }
/* Today's hint, behind a tap so it doesn't spoil the board. */
.story-hint { display: grid; justify-items: center; text-align: center; }
.story-hint summary {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
  font-size: 14px; font-weight: 600; color: var(--ink); cursor: pointer; list-style: none;
}
.story-hint summary::-webkit-details-marker { display: none; }
.story-hint summary::after {
  content: ""; width: 6px; height: 6px; margin-top: -3px; border: solid currentColor; border-width: 0 2px 2px 0;
  transform: rotate(45deg); transition: transform 0.2s cubic-bezier(0.23, 1, 0.32, 1);
}
.story-hint[open] summary::after { margin-top: 3px; transform: rotate(-135deg); }
.story-hint p { margin: 0; max-width: 256px; font-size: 14px; line-height: 1.5; color: var(--quiet); text-wrap: balance; }
@media (prefers-reduced-motion: reduce) { .story-hint summary::after { transition: none; } }
.story-pager { display: flex; justify-content: space-between; gap: 16px; font-size: 14px; font-weight: 600; }
.story-pager a { padding: 12px 0; color: var(--ink); text-decoration: none; white-space: nowrap; }
@media (hover: hover) { .story-pager a:hover, .story-art .action:hover { color: var(--quiet); } }

/* A computer: the first screen is the board alone, centred; below it, in the board's column, the solution hangs between
   the boards either side. The small print closes the page. */
@media (width > 999px) and (pointer: fine), (width > 999px) and (pointer: none), (width > 999px) and (height >= 700px) {
  .hero:has(> .story:not([hidden])) {
    /* Equal room above and below the board's block (padding, label, board, placard and the gaps between them). */
    --rest: max(16px, (100svh - var(--offer) - 80px - 36px - 60px - 36px - var(--board-w) * 52 / 32) / 2);
    grid-template-rows: var(--rest) auto auto var(--rest) auto auto;
    grid-template-areas: ". . ." ". mast ." ". play side" ". . ." "story story story" "foot foot foot";
  }
  /* On the hero's own columns: the work and its caption in the board's column, the boards either side in the columns
     beside it, each close to the work and level with its middle. */
  .hero > .story {
    grid-area: story; display: grid; grid-template-columns: subgrid; column-gap: 16px; row-gap: 4px;
    align-items: center; padding: 32px 0 64px;
  }
  .hero > .story .story-title { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .hero > .story :is(.story-art, .story-pager) { display: contents; }
  .hero > .story .story-art canvas, .hero > .story .story-hint { grid-column: 2; grid-row: 1; justify-self: center; }
  .hero > .story .story-art figcaption { grid-column: 2; grid-row: 2; justify-self: center; max-width: 256px; }
  .hero > .story [rel="prev"] { grid-column: 1; grid-row: 1; justify-self: end; }
  .hero > .story [rel="next"] { grid-column: 3; grid-row: 1; justify-self: start; }
}

/* A phone: the page is one screen, so the story waits in the week's sheet, after a hairline. */
@media (width <= 999px), (pointer: coarse) and (height < 700px) {
  .hero > .story { display: none; }
  .week-sheet .story { padding-top: 24px; border-top: 1px solid var(--hairline); }
}

/* The story as a row, the way a wall label sits beside a small work: the work on the left, and beside it the board's
   number and date, the caption, then the badge and Watch again. In the week's sheet on a phone, and on any screen in
   the note's place under the board when a link names an older board (one.js): there it answers what the link was
   for, on the first screen. */
:is(.week-sheet, .side) > .story {
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; column-gap: 16px; row-gap: 8px;
}
:is(.week-sheet, .side) > .story .story-art { display: contents; }
:is(.week-sheet, .side) > .story .story-art canvas { grid-column: 1; grid-row: 1 / span 2; width: 64px; box-shadow: none; }
:is(.week-sheet, .side) > .story .story-title { grid-column: 2; grid-row: 1; }
:is(.week-sheet, .side) > .story .story-art figcaption { grid-column: 2; grid-row: 2; justify-items: start; max-width: none; text-align: left; }
:is(.week-sheet, .side) > .story .story-art figcaption p { margin-top: 0; }
:is(.week-sheet, .side) > .story .story-art .action { height: 32px; }
:is(.week-sheet, .side) > .story .story-actions { justify-content: flex-start; gap: 8px 16px; }
:is(.week-sheet, .side) > .story .story-actions .badge img { height: 36px; }
:is(.week-sheet, .side) > .story:not(:has(canvas)) :is(.story-title, .story-hint) { grid-column: 1 / -1; }
:is(.week-sheet, .side) > .story .story-hint { justify-items: start; text-align: left; }
.week-sheet > .story .story-pager { grid-column: 1 / -1; margin-bottom: -12px; }
/* On the wall the row is the work and its label only; the boards either side stay in the page for search. It spans the
   frame, the top bar's edges (the wordmark's left, the number's right), never the board's: the board shrinks to make
   room for it, so sizing it by the board would feed back. */
.side > .story .story-pager { display: none; }
/* A short phone held upright: the board comes first, so the row on the wall is the work, its number and the badge; its date, caption
   and Watch again are in the week's sheet (and in the page for search). */
@media (width <= 999px) and (height < 700px) and (orientation: portrait) {
  .side > .story { align-items: center; }
  .side > .story .story-art canvas { grid-row: 1 / span 2; width: 48px; }
  .side > .story :is(.story-title span, figcaption p, .story-actions .action) { display: none; }
  .side > .story .story-actions { margin-top: 0; }
}
/* The board makes room for the row, so it can be narrower than the frame: its placard spans the frame instead (as it
   does once a painting settles), so the clock and the capsule never meet. */
@media (width <= 999px), (pointer: coarse) and (height < 700px) {
  .hero:has(.side > .story) .placard { width: var(--frame-w); }
}

/* The colophon's language list (localize.mjs): the page in every edition, a quiet disclosure under the small print. */
.colophon-sheet .web-language { justify-self: stretch; }
.colophon-sheet .web-language a { text-align: match-parent; }   /* every name on the page's own edge, whatever its script */
.colophon-sheet .web-language summary {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
  font-size: 14px; color: var(--quiet); cursor: pointer; list-style: none;
}
.colophon-sheet .web-language summary::-webkit-details-marker { display: none; }
.colophon-sheet .web-language summary bdi { color: var(--ink); font-weight: 600; }
.colophon-sheet .web-language summary::after {
  content: ""; width: 6px; height: 6px; margin-top: -3px; border: solid currentColor; border-width: 0 2px 2px 0;
  transform: rotate(45deg); transition: transform 0.2s cubic-bezier(0.23, 1, 0.32, 1);
}
.colophon-sheet .web-language[open] summary::after { margin-top: 3px; transform: rotate(-135deg); }
.colophon-sheet .web-language nav { display: grid; grid-template-columns: repeat(auto-fill, minmax(144px, 1fr)); gap: 0 16px; margin-top: 4px; }
.colophon-sheet .web-language a { padding: 8px 0; font-size: 14px; color: var(--ink); text-decoration: none; }
.colophon-sheet .web-language a[aria-current] { font-weight: 600; }
@media (hover: hover) { .colophon-sheet .web-language a:hover { color: var(--quiet); } }
@media (prefers-reduced-motion: reduce) { .colophon-sheet .web-language summary::after { transition: none; } }

/* MARK: The language offer (language-offer.js): one line above everything, offering the page in the visitor's own
   language, decided before the page draws. --offer is the height it takes from the top of the screen (its line and,
   on a phone, the status bar it sits under), and every first-screen fit takes it out, so nothing moves or overflows. */
:root { --offer: 0px; }
html[data-offer] { --offer: calc(44px + env(safe-area-inset-top)); }
language-offer { display: none; }
html[data-offer] language-offer {
  display: flex; align-items: center; position: relative; height: 44px; margin-top: env(safe-area-inset-top);
  padding-inline: max(16px, env(safe-area-inset-left)) 56px;
  border-bottom: 1px solid var(--hairline); font: 400 14px/1.2 var(--sans);
}
.offer-go { display: flex; align-items: center; gap: 8px; min-width: 0; min-height: 44px; color: var(--ink); text-decoration: none; }
.offer-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (hover: hover) { .offer-go:hover .offer-label { text-decoration: underline; text-underline-offset: 4px; } }
.offer-arrow { flex: none; }
.offer-close {
  position: absolute; top: 0; inset-inline-end: max(2px, calc(env(safe-area-inset-right) - 14px));
  display: grid; place-items: center; width: 44px; height: 44px; padding: 0;
  border: 0; background: none; color: var(--quiet); cursor: pointer;
}
.offer-close svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; }
/* A phone: the screen's height less the offer is the board's (the offer already sits under the status bar). */
@media (width <= 999px), (pointer: coarse) and (height < 700px) {
  html[data-offer] .hero { --insets: calc(var(--offer) + env(safe-area-inset-bottom)); height: calc(100dvh - var(--offer)); padding-top: 0; }
  html[data-offer] .hero.settled { height: auto; min-height: calc(100dvh - var(--offer)); }   /* settled, a short phone scrolls (above) */
}
/* A computer: the words start at the wordmark's edge and the cross ends at the foot's. */
@media (width > 999px) and (pointer: fine), (width > 999px) and (pointer: none), (width > 999px) and (height >= 700px) {
  html[data-offer] language-offer { padding-inline: max(48px, (100vw - 1344px) / 2) 56px; }
  html[data-offer] .offer-close { inset-inline-end: calc(max(48px, (100vw - 1344px) / 2) - 14px); }
}

/* MARK: The toast (one.js): news the board can't show, in the page's glass, near the top of the screen like the
   system's own banners. One line or two, never a sentence more. Enters from just above (ease-out, 240 ms), leaves faster. */
.toast {
  position: fixed; z-index: 20; top: calc(max(var(--offer), env(safe-area-inset-top)) + 12px); left: 50%;
  width: max-content; max-width: min(100vw - 64px, 340px); margin: 0; padding: 10px 16px;
  border-radius: 20px; text-align: center; text-wrap: balance;
  font: 500 14px/1.35 var(--sans); color: var(--ink);
  background: color-mix(in srgb, var(--sheet) 82%, transparent);
  -webkit-backdrop-filter: blur(20px) saturate(1.6); backdrop-filter: blur(20px) saturate(1.6);
  box-shadow: inset 0 0 0 0.5px var(--hairline), 0 8px 24px -10px rgba(0, 0, 0, 0.4);
  opacity: 0; translate: -50% -8px; pointer-events: none; cursor: default;
  transition: opacity 160ms ease-out, translate 160ms ease-out;
}
.toast[hidden] { display: none; }
.toast.on { opacity: 1; translate: -50% 0; pointer-events: auto; transition-duration: 240ms; transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1); }
@media (prefers-reduced-motion: reduce) { .toast { translate: -50% 0; } }
/* A phone: just under the top bar (its 56), over the board's top edge, so the wordmark and the label stay readable. */
@media (width <= 999px), (pointer: coarse) and (height < 700px) {
  .toast { top: calc(max(var(--offer), env(safe-area-inset-top)) + 56px + 24px); }
}
