/* The landing page, where the board is the page.

   Layered on landing.css the way pricing.css is: that file is the poster palette, the `.btn`, and
   the whole of the trial dialog, and all of it is still wanted here. What this file does is undo the
   parts of it that assume a page you read and scroll, and then build the one screen that replaces
   them.

   The shape of that screen:

     .stage-hud     one frosted pill over the board: the mark and the wordmark
     .stage-board   a square chessboard, as tall as the window allows
     .stage-side    the only words on the screen, in one frosted card beside it
     .stage-scroll  the way down to the writing, for somebody who wants to read first

   `.beat` sits over all of it and is where every sentence of argument on this page lives: one card
   at a time, between positions, shown to somebody who has just solved something.

   Two rules decide the whole geometry, and they are worth stating before any of the numbers:

     1. **A square board can never be taller than the window**, so flush to the top and bottom is the
        largest it will ever be. Everything else has to happen beside it or on top of it, which is
        why the wordmark and the rail are pills floating over the board rather than a bar above it:
        a 44px bar costs 88px of board.

     2. **The column beside it has a capped width and is centred in whatever is left.** Without the
        cap it drifts to the far edge of a wide monitor and the composition falls apart into a board
        on one side and a lonely card on the other. */

/* --- Undoing the poster ------------------------------------------------------------------------ */
/*
   landing.css centres the body's single child in the viewport and pads it. This page has two
   children, the first is exactly one viewport tall and the second is a document, so all of that has
   to go. Scoped to the class rather than edited in place, because four other pages are built on
   those rules. */
body.stage-page {
  display: block;
  padding: 0;
  min-height: 100svh;
}

/* The faint chessboard landing.css paints across the middle of the window was the only thing on the
   poster that said "chess" before a word was read. There is a real board on this page now, and a
   ghost board behind a board reads as a rendering fault. */
body.stage-page::before { display: none; }

/* And the poster's warm gold hearth is replaced, for this page only, by a cooler one placed behind
   the *card* rather than behind the middle of the window. It is what the frosted panels have to
   have something to frost: glass over flat ink is just a grey box. */
body.stage-page::after {
  inset: -30%;
  background:
    radial-gradient(closest-side, rgba(120, 132, 210, 0.2) 0%, rgba(120, 132, 210, 0.05) 45%, transparent 72%);
}

/* --- The screen -------------------------------------------------------------------------------- */

/* `svh` and not `dvh`, everywhere on this page. One letter, and it decides whether the first
   screen holds still on a phone.
 *
 * `dvh` is the *dynamic* viewport height: on a phone it tracks the browser's chrome, so it grows by
 * ninety-odd pixels the moment a scroll collapses the URL bar and shrinks again when it comes back.
 * Every measurement here is derived from it, so that is not one thing moving, it is the whole
 * screen relaying out mid-gesture. Both of the gaps reported on this page were that: centred, the
 * card drifted away from the board as you scrolled; started, the same ninety pixels opened up
 * underneath it instead. Neither is a fix, because neither was the fault.
 *
 * `svh` is the height with the chrome *expanded*, which is a constant for a given device and
 * orientation. At rest, which is where a phone sits at scroll 0, it is exactly the visible height,
 * so the composition is unchanged. Scrolled, the stage stays the size it was and the section below
 * comes up to meet it, which is what scrolling is meant to look like.
 *
 * `lvh` would be the other way round and is the wrong one: sized for the collapsed bar, the bottom
 * of the first screen would be under the URL bar until you scrolled. */
.stage {
  /* The column, and the room around it. Every measurement below is derived from these three, so
     changing the column width cannot leave the board's own sum out of date. */
  --gap: clamp(20px, 3.4vw, 64px);
  --pad: clamp(12px, 2vw, 40px);
  --col: clamp(320px, 30vw, 470px);

  position: relative;
  z-index: 1;
  height: 100svh;
  display: grid;
  grid-template-columns: var(--board-size) var(--col);
  /* Three rows, and all three of them are the column's: the mark, the card, and the way down. The
     board spans the lot, so its size is decided by `--board-size` alone and nothing in the column
     can push it about. See `.stage-hud` for why the mark is in here rather than over the board. */
  grid-template-rows: auto 1fr auto;
  column-gap: var(--gap);
  row-gap: 14px;
  align-items: center;
  /* Centred rather than flush left. On a laptop the left margin lands at twenty-odd pixels, so it
     still reads as edge to edge; on a 21:9 monitor it is what stops the pair separating into two
     unrelated objects at opposite ends of the desk. */
  justify-content: center;
  padding: var(--pad);
}

/* The board is the smaller of what the height allows and what is left of the width once the column
   and the gaps have taken theirs. The `1120px` is a ceiling rather than a fitting: past that the
   board stops growing and the whole pair centres instead, because a board taller than about a
   thousand pixels is no longer easier to read, only further from the card. */
.stage {
  --board-size: min(
    calc(100svh - var(--pad) * 2),
    calc(100vw - var(--col) - var(--gap) - var(--pad) * 2),
    1120px
  );
}

/* The bar the trial return lands in is fixed to the bottom of the window, so on this page it would
   sit on top of the card rather than under the end of a poster. The stage gives up exactly the
   height the bar measured itself at. See `mountTrialReturn` in trial.js for where the number comes
   from. */
body.stage-page.has-trial-return { padding-bottom: 0; }
body.stage-page.has-trial-return .stage {
  height: calc(100svh - var(--trial-return-h, 143px));
  --board-size: min(
    calc(100svh - var(--trial-return-h, 143px) - var(--pad) * 2),
    calc(100vw - var(--col) - var(--gap) - var(--pad) * 2),
    1120px
  );
}
body.stage-page.has-trial-return .below { padding-bottom: calc(var(--trial-return-h, 143px) + 48px); }

/* --- The board --------------------------------------------------------------------------------- */

.stage-board {
  grid-column: 1;
  /* Every row, so the board is centred against the whole window rather than against the middle row
     of the column beside it. */
  grid-row: 1 / -1;
  min-height: 0;
  display: grid;
  place-items: center;
}

.stage-board-wrap .board {
  border-radius: 10px;
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.7), 0 2px 0 rgba(255, 255, 255, 0.05) inset;
}

/* A hairline that brightens for the last position. The only thing on the page that marks the tactic
   as different from the three primers before it, now that there is no step label saying so. */
.stage-board-wrap::before {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: 11px;
  border: 1px solid rgba(216, 172, 83, 0.2);
  pointer-events: none;
  transition: border-color 400ms ease;
  z-index: 1;
}

.stage.is-tactic .stage-board-wrap::before { border-color: rgba(244, 212, 137, 0.6); }

/* --- Glass ------------------------------------------------------------------------------------- */

.pill {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 99px;
  background: rgba(18, 20, 32, 0.5);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  color: #f2f4fa;
  white-space: nowrap;
}

/* At the top of the column, above the card, and it used to be pinned to the window's top left
   corner over the board. That worked at 1920 and nowhere smaller, because the room beside the board
   is not a margin anybody chose: it is whatever is left after the board and the column have taken
   theirs, halved by `justify-content: center`. The pill needs 126px of it. It gets 72px on a 1440
   laptop and 41px on an iPad in landscape, so it sat on the board's top left square, and on the
   report that prompted this the square had the white king on it.

   Beside the board there is no room to move it to, and this is the one place on the screen with
   room going spare: the card is centred in a column as tall as the board, so the space above it is
   empty at every size. It still costs the board nothing, which is the whole reason the mark was
   floated in the first place, because the board spans all three rows and is sized by `--board-size`
   rather than by what the rows add up to.

   Stacked, it goes back above the board and in the flow; see the query at the foot of this file. */
.stage-hud {
  grid-column: 2;
  grid-row: 1;
  align-self: start;
  justify-self: start;
  z-index: 5;
  display: flex;
  align-items: center;
  pointer-events: none;
}

.stage-hud > * { pointer-events: auto; }

.stage-mark {
  margin: 0;
  gap: 11px;
  padding: 9px 18px 9px 14px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

/* `brightness(0)` flattens every pixel to black and `invert(1)` turns that white, which leaves the
   alpha channel alone. One filter, any source colour, and it means the gold mark and the white
   wordmark match without a second file to keep in step with the first. */
/* 22px rather than 18, and fully opaque.
   The mark is tall and narrow, so at 18px it rendered ten pixels wide, and ten pixels is not enough
   for the strokes to survive: it read as blurry when what it actually was is too small to resolve.
   The source asset went up with it, to 160px tall against a 22px render, which is over seven times
   and covers a 3x phone with room to spare. `opacity` went because a mark at this size needs every
   bit of edge contrast it can get. */
.stage-logo {
  display: block;
  height: 22px;
  width: auto;
  filter: brightness(0) invert(1);
}

/* --- The rail, now inside the card ------------------------------------------------------------- */

.stage-rail { display: flex; align-items: center; gap: 7px; }

.stage-seg {
  height: 5px;
  flex: 1;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.16);
  transition: background-color 320ms ease, box-shadow 320ms ease;
}

/* The tactic is wider than a primer, because it is not one. */
.stage-seg.is-tactic { flex: 1.7; }
.stage-seg.is-current { background: rgba(255, 255, 255, 0.72); }
/* Solved first time. Solved after a hint or a reveal is the same segment, half lit: the rail is the
   only thing here that reports on how it went, and "done" and "done eventually" are not the same
   answer. */
.stage-seg.is-done { background: var(--gold-bright); box-shadow: 0 0 10px rgba(244, 212, 137, 0.6); }
.stage-seg.is-scraped { background: rgba(216, 172, 83, 0.55); }

/* --- The column -------------------------------------------------------------------------------- */

.stage-side {
  grid-column: 2;
  grid-row: 2;
  min-width: 0;
  display: grid;
  align-content: center;
}

.stage-card {
  display: grid;
  gap: 20px;
  padding: 34px 34px 30px;
  border: 1px solid rgba(255, 255, 255, 0.13);
  border-radius: 22px;
  background: rgba(18, 20, 32, 0.5);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}

/* Which position you are on, as the one piece of Split this layout keeps.
   Outlined rather than filled, and sized to the card rather than to the screen. It sat behind the
   card at almost exactly the card's own size in the first draft, so all you saw was an outline
   peeking out from four edges, which read as a bug rather than as a numeral. */
.stage-num {
  margin: 0;
  font-family: inherit;  /* the page serif, from landing.css */
  font-size: 54px;
  font-weight: 700;
  line-height: 0.8;
  letter-spacing: -0.03em;
  color: transparent;
  -webkit-text-stroke: 1px rgba(244, 212, 137, 0.5);
  font-variant-numeric: tabular-nums;
}

.stage-prompt {
  margin: 0;
  min-height: 2.4em;
  font-size: clamp(19px, 1.6vw, 24px);
  line-height: 1.28;
  color: #f4f1ea;
}

/* Nothing has been touched for a few seconds, so the ask starts breathing. The cheapest way to say
   "this board is waiting for you" without another sentence of text. */
.stage-prompt.is-nudging { animation: prompt-pulse 1.9s ease-in-out infinite; }

@keyframes prompt-pulse {
  0%, 100% { color: #f4f1ea; }
  50% { color: var(--gold-bright); }
}

/* Two full lines held open, always.
 *
 * `min-height` here is in `em`, so it is a multiple of the *font* size and not of the line box:
 * 2.6em at a line-height of 1.45 is 1.79 lines, not 2.6 of them, and the second line of "Nf3 is not
 * it. The board is back where it was, have another look." therefore grew the card. Two lines is
 * `2 * 1.45em`, and that is what this is. */
.stage-verdict {
  margin: 0;
  min-height: 2.9em;
  font-size: 13.5px;
  line-height: 1.45;
  color: rgba(240, 240, 255, 0.5);
}

.stage-verdict.is-miss { color: #f0998a; }
.stage-verdict.is-ok { color: #a8d894; }
.stage-verdict.is-done { color: var(--gold-bright); }
.stage-verdict.is-aside { color: rgba(240, 240, 255, 0.45); }

.stage-helps { display: flex; gap: 8px; flex-wrap: wrap; }

.stage-help {
  padding: 10px 15px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.05);
  color: #dfe2ee;
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease;
}

.stage-help:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
.stage-help:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.stage-help:disabled { opacity: 0.4; cursor: default; }
/* Not `hidden`: see `refreshHelps` in landing.js. The row has to keep its height while a reply is
   playing, or the card shortens and everything in the column moves. */
.stage-help.is-away { visibility: hidden; }
.stage-help-quiet { border-color: rgba(255, 255, 255, 0.08); color: rgba(240, 240, 255, 0.45); }

/* --- The way down ------------------------------------------------------------------------------- */

/* Under the card, in the column, and it used to be pinned to the bottom centre of the *window*.
   That is the same mistake the mark made at the other end of the screen and it failed on the same
   screens: the board is as tall as the window allows, so the foot of the window is the foot of the
   board, and "WHAT THIS IS" was printed across the bottom rank at 1194x697 and at 1440x800. In the
   column it is under the thing it is a footnote to, and it cannot cross the board at any size.

   The stacked query still drops it rather than moving it: there the column is under the board and
   the cue would be a fourth row on a screen that is already spending its last pixels on the board.
*/
.stage-scroll {
  grid-column: 2;
  grid-row: 3;
  justify-self: center;
  align-self: end;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 14px;
  border-radius: 99px;
  color: rgba(240, 240, 255, 0.42);
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
}

.stage-scroll:hover { color: var(--gold-bright); }
.stage-scroll svg { animation: scroll-bob 2.4s ease-in-out infinite; }

@keyframes scroll-bob {
  0%, 100% { transform: translateY(-2px); opacity: 0.6; }
  50% { transform: translateY(2px); opacity: 1; }
}

/* --- Marks on the board -------------------------------------------------------------------------- */
/*
   board.js rebuilds the squares on every `setPosition`, so nothing here is written onto them:
   everything below lives in a layer of its own over the top, positioned in eighths of the board the
   same way board.js positions its own glyph layer.

   All of it is decoration for something the card already says in words, which is why the
   reduced-motion block at the foot of this file switches it off rather than replacing it. */
.fx-layer {
  position: absolute;
  inset: 0;
  z-index: 45;
  pointer-events: none;
}

.fx { position: absolute; width: 12.5%; height: 12.5%; }

.fx::before,
.fx::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
}

.fx-right::before {
  background: radial-gradient(circle, rgba(244, 212, 137, 0.7), rgba(244, 212, 137, 0) 70%);
  animation: fx-fade 720ms ease-out forwards;
}

.fx-right::after {
  border: 2px solid var(--gold-bright);
  animation: fx-ring 800ms cubic-bezier(0.2, 0.75, 0.3, 1) forwards;
}

/* Red, and it does not travel: a ring going out is a reward, and this is not one. */
.fx-wrong::before {
  background: radial-gradient(circle, rgba(214, 74, 58, 0.78), rgba(214, 74, 58, 0) 72%);
  animation: fx-fade 700ms ease-out forwards;
}

.fx-wrong::after { border: none; }

@keyframes fx-ring {
  from { transform: scale(0.4); opacity: 0.95; }
  to { transform: scale(1.7); opacity: 0; }
}

@keyframes fx-fade {
  from { opacity: 1; }
  to { opacity: 0; }
}

/* Light crossing the whole board, once, when a position is solved. Its own clipping box rather than
   `overflow: hidden` on the effects layer, because the rings above are meant to spill past the edge
   of the board and would be sliced in half by it. */
.fx-sweep {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 10px;
}

.fx-sweep i {
  position: absolute;
  top: -20%;
  bottom: -20%;
  width: 55%;
  background: linear-gradient(100deg,
    rgba(244, 212, 137, 0) 0%,
    rgba(244, 212, 137, 0.28) 42%,
    rgba(255, 245, 220, 0.5) 50%,
    rgba(244, 212, 137, 0.28) 58%,
    rgba(244, 212, 137, 0) 100%);
  animation: fx-sweep 880ms cubic-bezier(0.3, 0, 0.2, 1) forwards;
}

@keyframes fx-sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(280%); }
}

/* A wrong move also moves the board, briefly. Short and small: this is the page flinching, not the
   page telling you off. */
.stage-board-wrap.is-shake { animation: board-shake 320ms ease-in-out; }

@keyframes board-shake {
  10%, 90% { transform: translateX(-3px); }
  30%, 70% { transform: translateX(5px); }
  50% { transform: translateX(-5px); }
}

/* --- The cards between positions ------------------------------------------------------------------ */
/*
   Every sentence of argument on the first screen is on one of these, and each is shown to somebody
   who has just solved a chess position rather than to somebody who has just arrived. That is the
   whole design of the page in one rule: the reading is the reward, not the toll. */
.beat {
  position: absolute;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 28px 22px;
  background: rgba(8, 9, 14, 0.72);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  opacity: 0;
  transition: opacity 300ms ease;
}

/* `display: grid` above beats the browser's own `[hidden] { display: none }`, because a rule in a
   stylesheet outranks the user-agent one whatever the attribute says. Without this the card is still
   laid out at `inset: 0` with `z-index: 60` while `hidden` is set: invisible, and a full-screen
   shield over the board that swallows every click at the pieces.
   This shipped once, and the run was unplayable with a mouse. It survived the tests because those
   dispatched events straight at the square element, which skips hit-testing entirely and so could
   never see the thing in the way. */
.beat[hidden] { display: none; }

/* And the same failure one step later: between `hidden` being cleared and `is-open` landing, and
   again on the way out, the card is displayed at `opacity: 0`. It must not be clickable in either
   window. */
.beat:not(.is-open) { pointer-events: none; }

.beat.is-open { opacity: 1; pointer-events: auto; }

.beat-card {
  max-width: 52ch;
  padding: 40px 44px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 26px;
  background: rgba(20, 22, 34, 0.72);
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
  text-align: center;
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 380ms ease 90ms, transform 380ms cubic-bezier(0.2, 0.8, 0.3, 1) 90ms;
}

.beat.is-open .beat-card { opacity: 1; transform: none; }

/* On a touch screen the card fades in without rising into place, and the reason is the button on it.
 *
 * The rise takes 470ms counting its delay, and for all of it the button is up to sixteen pixels
 * below where it comes to rest. A cursor arrives after the animation because a cursor is already on
 * the screen; a finger arrives *during* it, because somebody who has just solved a mate reaches for
 * the card as it appears. Aim at the label, land above the button, and nothing happens, which is
 * how a button that works comes to need two presses. Nothing is lost by fading in place: the card
 * appearing at all is the thing that reads, and the sixteen pixels were never the message. */
@media (hover: none) {
  .beat-card { transform: none; }
}

.beat-eyebrow {
  margin: 0 0 14px;
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  font-size: 10.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold);
}

.beat-title {
  margin: 0;
  font-size: clamp(24px, 3.4vw, 40px);
  font-weight: 400;
  line-height: 1.15;
  color: #f4f1ea;
}

.beat-line {
  margin: 18px auto 0;
  max-width: 46ch;
  font-size: clamp(14px, 1.5vw, 16px);
  line-height: 1.66;
  color: rgba(240, 240, 255, 0.6);
}

/* A column rather than a stack of margins, so the button, the note and the links are spaced by one
   number instead of three that have to be kept in agreement. */
.beat-actions {
  margin-top: 32px;
  display: grid;
  justify-items: center;
  gap: 18px;
}

.beat-actions .btn {
  padding: 16px 38px;
  font-size: 13.5px;
  border-radius: 99px;
  border: none;
  box-shadow: none;
}

.beat-note {
  margin: 0;
  font-size: 13px;
  color: rgba(240, 240, 255, 0.55);
}

/* Two links, side by side, with a rule above them: they are a different kind of thing from the
   button and the note, and the closing card read as a pile because nothing said so. */
.beat-aside {
  margin: 4px 0 0;
  padding-top: 20px;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* The secondary action, as an outlined pill: present enough to be found, quiet enough not to argue
   with the filled one above it. Same height and radius as the primary, so the two read as a pair
   rather than as a button and a leftover. */
.beat-ghost {
  padding: 12px 22px;
  white-space: nowrap;
  border: 1px solid rgba(244, 212, 137, 0.55);
  border-radius: 99px;
  color: var(--gold-bright);
  text-decoration: none;
  transition: background-color 160ms ease, border-color 160ms ease;
}

.beat-ghost:hover {
  background: rgba(244, 212, 137, 0.14);
  border-color: var(--gold-bright);
}

.beat-quiet {
  padding: 12px 6px;
  color: rgba(240, 240, 255, 0.5);
  text-decoration: none;
  text-underline-offset: 3px;
}

.beat-quiet:hover { color: var(--gold-bright); text-decoration: underline; }
.beat-aside a:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

/* The one link on the stinger, under its button: where the game itself can be watched. */
.beat-after {
  margin: 0;
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.beat-after a { color: rgba(240, 240, 255, 0.45); text-underline-offset: 4px; }
.beat-after a:hover { color: var(--gold-bright); }

/* The closing card carries more than the others and needs the title to stop shouting over it. */
.beat-close .beat-title { font-size: clamp(22px, 2.9vw, 33px); }

/* The one moment on this page that is pure theatre: a world champion's opponent resigning a won
   position. Darker than the others, and the title is the size of the claim. */
.beat-stinger { background: rgba(4, 5, 8, 0.9); }
.beat-stinger .beat-title { font-size: clamp(27px, 4.2vw, 46px); }
.beat-stinger .beat-eyebrow { color: rgba(240, 240, 255, 0.45); }

/* --- The resignation ------------------------------------------------------------------------------ */
/*
   The board is the scene, so this layer holds only what is drawn over it: a draining time bar at the
   top of the window, a caption under the board the way a subtitle sits under a shot, and at the end
   a card. The board itself is not covered at any point, which is the whole reason this is a layer
   over the stage rather than another `.beat`. */
.reel {
  position: absolute;
  inset: 0;
  z-index: 55;
  cursor: pointer;
}

.reel[hidden] { display: none; }

/* The card column steps back while the scene runs. Not hidden: it holding its place is what stops
   the board jumping sideways when the scene ends. */
.stage.is-reel .stage-card { opacity: 0.18; transition: opacity 500ms ease; }
.stage.is-reel .stage-hud { opacity: 0.35; transition: opacity 500ms ease; }

.reel-time {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: rgba(255, 255, 255, 0.08);
}

.reel-time i {
  display: block;
  height: 100%;
  width: 100%;
  transform-origin: left;
  background: linear-gradient(90deg, #d64a3a, #f0998a);
  animation: reel-drain 9.8s linear forwards;
}

@keyframes reel-drain {
  from { transform: scaleX(1); }
  to { transform: scaleX(0.06); }
}

/* An explicit way out, so skipping is something somebody chooses rather than something they trip
   over. It appears with the click-anywhere handler, not before. */
.reel-skip {
  position: absolute;
  top: 16px;
  right: calc(var(--pad, 20px) + 44px);
  padding: 7px 15px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 99px;
  background: rgba(0, 0, 0, 0.45);
  color: rgba(255, 255, 255, 0.75);
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
}

.reel-skip[hidden] { display: none; }
.reel-skip:hover { color: #fff; border-color: rgba(255, 255, 255, 0.5); }
.reel.is-end .reel-skip { display: none; }

/* Under the card, in the column's own flow.
   Two goes at this. First it was centred at the foot of the window, which put it across the bottom
   rank of the board. Then it was absolutely positioned over the column, which put it on top of the
   panel. Flow layout is the version that cannot collide with anything: the column stacks the card
   and the caption, and whatever height either of them takes, the other moves. */
.reel-lower {
  display: none;
  gap: 8px;
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  justify-items: start;
  text-align: left;
}

.stage.is-reel .reel-lower { display: grid; }

.reel-eyebrow {
  margin: 0;
  min-height: 1.2em;
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  font-size: 10.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold);
}

.reel-cap {
  margin: 0;
  max-width: 100%;
  font-size: clamp(15px, 1.55vw, 20px);
  line-height: 1.4;
  color: #fff;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.95), 0 1px 3px rgba(0, 0, 0, 0.9);
}

/* The end of the scene, over a board that has gone dark. */
.reel-card { display: none; }

.reel.is-end { background: rgba(6, 6, 10, 0.86); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.reel.is-end .reel-time { display: none; }

.reel.is-end .reel-card {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 16px;
  padding: 28px;
  text-align: center;
}

.reel-title {
  margin: 0;
  font-size: clamp(28px, 4.6vw, 52px);
  font-weight: 400;
  line-height: 1.1;
  color: #f4f1ea;
}

.reel-line { margin: 0; font-size: clamp(16px, 2vw, 22px); color: var(--gold-bright); }
.reel-actions { margin-top: 14px; }
.reel-actions .btn { padding: 16px 38px; font-size: 13.5px; border-radius: 99px; border: none; box-shadow: none; }

/* --- Confetti ------------------------------------------------------------------------------------- */
/*
   Above the closing card, because paper falling behind a panel is paper nobody sees, and
   `pointer-events: none` so it cannot come between anybody and the button underneath it. */
.party {
  position: absolute;
  inset: 0;
  z-index: 70;
  pointer-events: none;
}

/* --- Below the fold ------------------------------------------------------------------------------- */

.below {
  position: relative;
  z-index: 1;
  width: min(920px, 100%);
  margin: 0 auto;
  padding: 84px 24px 96px;
  text-align: center;
  border-top: 1px solid rgba(216, 172, 83, 0.14);
}

/* The direct answers. Set as a real question-and-answer list rather than as prose, because that is
   what it is, and because a heading followed immediately by a complete sentence is the shape an
   overview can lift. Left-aligned inside a centred section on purpose: three centred paragraphs of
   four lines each is a wall. */
.explainer {
  margin: 56px auto 0;
  max-width: 68ch;
  display: grid;
  gap: 26px;
  text-align: left;
}

.explainer-q {
  margin: 0 0 8px;
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}

.explainer-a {
  margin: 0;
  font-size: 15px;
  line-height: 1.68;
  color: var(--parchment-dim);
}

.explainer-a b { color: var(--parchment); font-weight: 600; }

/* `.foot` is shared with the pricing and archive pages, so the row it now has to hold is arranged
   here rather than there. */
body.stage-page .foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px 26px;
}

.foot-social { display: inline-flex; align-items: center; gap: 16px; }

/* Every mark takes its colour from the anchor, so one `currentColor` lights whichever is hovered.
   Instagram is an outline and the other three are filled paths, which is how each brand draws its
   own, so the fill and stroke are set per-svg in the markup rather than forced to match here. */
.soc {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  color: #968871;
  transition: color 150ms ease, background-color 150ms ease;
}

.soc svg { width: 17px; height: 17px; display: block; }

.soc:hover { color: var(--gold-bright); background: rgba(216, 172, 83, 0.12); }
.soc:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.below-title {
  margin: 20px 0 0;
  font-size: clamp(24px, 3.4vw, 36px);
  font-weight: 400;
  font-style: italic;
  line-height: 1.2;
  color: var(--parchment);
}

/* --- Stacked ---------------------------------------------------------------------------------------- */
/*
   Below this width, or in portrait, the column goes under the board. The board is then limited by
   the width of the window rather than by its height, which is the physics of a portrait phone and
   not a layout decision: a square cannot be wider than the screen. */
@media (max-width: 1080px), (orientation: portrait) {
  .stage {
    grid-template-columns: 1fr;
    /* The board is pinned, and the third row takes everything left over.
     *
     * This is the fix for "moving a piece shifts the whole board". With three `auto` rows and
     * `align-content: center` the three of them were weighed together and the stack re-centred
     * whenever any one of them changed height, and the card changes height constantly: a verdict
     * arrives, a second line of it wraps, the help buttons go away while the reply plays. All of
     * that is below the board and none of it has any business moving the board, but centring the
     * column meant every one of those pushed the board up or down by half the difference.
     *
     * With `1fr` on the card's row there is no free space left for `align-content` to hand out, so
     * the wordmark and the board sit where they sit and the card grows and shrinks in the space
     * under them.
     *
     * The board's row is `var(--board-size)` and not `auto`, which is the second half of the same
     * fix and was learned the hard way. An `auto` row is compressible: when the row under it wanted
     * more than the window had left, the algorithm took the difference out of the board's row, and
     * a 369px board ended up in a 288px row. Being a fixed-size item it did not shrink with it, it
     * *centred* in it and hung 40px out of each end, over the wordmark above and over the card
     * below. That is what the resignation scene looked like on a phone: its caption is 92px the
     * layout had not been asked to find, so it came out of the board.
     * A row stated in the board's own size cannot be borrowed from. Anything that will not fit
     * under it overflows the bottom of the screen instead, which is a thing you can see. */
    grid-template-rows: auto var(--board-size) 1fr;
    justify-content: stretch;
    align-content: start;
    /* Twelve rather than sixteen, twice over, and the eight pixels go straight into the board:
       every pixel of height not spent between the three rows is a pixel the board can be wide,
       right up until `100vw` takes over as the limit. */
    gap: 12px;
    padding: 12px;
    /* Everything on the screen that is not the board, in one number: the wordmark's row, the two
       gaps, the padding top and bottom, and the card.
       It is the whole argument about how big the board gets, because on a phone this is what wins:
       384 here left a 288px board under a 369px card, and the report was that the pieces were too
       small to hit. The card below is trimmed to 208 (see `.stage-card` and the rules under it) and
       the number follows it down, which is what lets `100vw - 24px` be the binding term instead and
       the board come out exactly as wide as the card under it. Measured at 393x672. */
    --board-size: min(calc(100vw - 24px), calc(100svh - 289px));
  }

  body.stage-page.has-trial-return .stage {
    --board-size: min(calc(100vw - 24px), calc(100svh - var(--trial-return-h, 143px) - 289px));
  }

  /* Above the board, not beside it, and every explicit placement from the two-column layout is
     handed back here: stacked, these are auto-placed into the three rows above in source order,
     which is the mark, the board, then the column.

     The mark is centred over the board rather than sitting at the start of a column, because there
     is no column left to start: at this width the board is as wide as the window. */
  .stage-hud {
    grid-area: auto;
    justify-self: auto;
    /* `start` as well, or the pill sticks to the top of whatever row it lands in rather than
       centring in it. Measured at 844x390, where that row is not the pill's own height: it moved
       from y=85 to y=12. */
    align-self: auto;
    justify-content: center;
    padding: 0;
  }

  .stage-board { grid-area: auto; }

  /* And a smaller pill inside it, because that row is 42px of the height the board is competing
     for. The mark itself stays 22px: it is tall and narrow and does not survive being shrunk. */
  .stage-mark { padding: 6px 16px 6px 12px; }

  /* Stacked, the column is under the board and the caption is under the card, which is already
     where it should be. It only needs centring to match everything else at this width. */
  .reel-lower { justify-items: center; text-align: center; margin-top: 14px; padding-top: 14px; }

  /* Pinned under the board rather than centred in the row, so the gap above the card is the 12px
     of grid gap and nothing else. Its row is the `1fr` one, so it is the row that absorbs whatever
     height is going spare, and a card centred in it moves by half of any change to that. See the
     note on `svh` at the top of this file for what used to change it. */
  .stage-side { grid-area: auto; justify-items: center; align-self: start; }
  .stage-card {
    width: min(100%, 520px);
    padding: 15px 18px;
    gap: 11px;
    justify-self: center;
  }

  /* The numeral goes, and it is the single biggest thing paying for the board.
     Forty-one pixels of card, counting the gap under it, for a piece of information the rail
     directly above it already carries, and carries better: the rail says which position you are on
     *and* how the earlier ones went. It is also the one element here that is purely decorative,
     an outlined 34px glyph, so on a screen where the board is fighting the card for every pixel it
     is the obvious thing to lose. The rail keeps the accessible answer, it is a `progressbar` with
     `aria-valuenow` set by `paintRail`. */
  .stage-num { display: none; }

  /* And the scroll cue goes, which is the last thing the full-width board is paid for with.
     It is absolutely positioned at the foot of the stage, so it takes part in no layout and cannot
     be pushed out of the way: once the board is as wide as the card, the card's help buttons end
     where the cue begins and "WHAT THIS IS" is printed across them. Measured at 393x672, three
     pixels of overlap between two things you are meant to tap, and again at 768x1024, where it is
     the tablet's own squeeze rather than a short screen, which is why this is not a height query.
     Putting it back in the flow instead would cost it 38px of height, and 38px of height is 24px
     of board on any screen where the height is what limits the board, which is every screen this
     rule applies to. The landscape phone below drops it for the same reason. What is lost is a
     hint: the section is still directly under the board, and a phone is scrolled by reflex. */
  .stage-scroll { grid-area: auto; display: none; }

  /* The scene plays under the board's own caption, and the card is not there at all.
     Beside the board the card can afford to stay and fade to 18%, which is what it does, so that
     the board does not shift when the scene ends. Under the board it cannot: it is 208px of ghost
     panel between the board and the subtitle belonging to it, and the subtitle is then 92px the
     screen does not have. With the card out of the way the caption sits directly under the shot,
     which is where a subtitle goes, and the board still does not move, because its row is stated
     in its own size and the row beneath it is `1fr` either way. */
  .stage.is-reel .stage-card { display: none; }
  .stage.is-reel .reel-lower { margin-top: 0; padding-top: 0; border-top: none; }

  /* Three lines of the ask and two of the verdict, held open rather than measured.
     Both are read by somebody looking at the board rather than at the card, so the card changing
     shape underneath them is pure distraction, and reserving the room costs nothing the reflow was
     not already costing. Those are the counts the longest of each actually takes, measured from
     320px up: the ask runs to three lines and the verdict to two.
     `1.28` and `1.45` are the two line-heights above; see `.stage-verdict` for why these are
     multiples of those rather than round numbers. */
  .stage-prompt { min-height: 3.84em; font-size: 17px; }
  .stage-verdict { min-height: 2.9em; }
  .beat-card { padding: 30px 26px; }
  .below { padding-top: 56px; }
}

/* A landscape phone has no vertical budget at all: the card goes back beside the board, everything
   shrinks, and the scroll cue and the long paragraph on each beat card are dropped. */
@media (max-height: 560px) and (orientation: landscape) {
  .stage {
    /* Its own three numbers, so everything below is derived rather than guessed at. `--col` used to
       be `minmax(220px, 300px)`, which is a track and not a length: `grid-template-columns` took it
       happily and every `calc()` that reads it did not, so the trial-return rule further up, which
       is more specific than this block and computes `--board-size` from `var(--col)`, produced an
       invalid length and left the grid with no columns at all. A phone held sideways with a trial
       in progress is a real combination. */
    --pad: 12px;
    --gap: 16px;
    --col: clamp(220px, 34vw, 300px);
    --board-size: min(
      calc(100svh - var(--pad) * 2),
      calc(100vw - var(--col) - var(--gap) - var(--pad) * 2)
    );

    grid-template-columns: var(--board-size) var(--col);
    /* The mark, then the card. The same two rows the wide layout uses, minus the cue's, which is
       dropped at this size. */
    grid-template-rows: auto 1fr;
    /* Back to centred, both ways. A landscape phone matches the stacked query above as well as this
       one, and what that sets is for a column of three rows stacked under a full-width board. */
    align-content: center;
    justify-content: center;
    column-gap: var(--gap);
    row-gap: 10px;
    padding: var(--pad);
  }

  /* And the placements taken back, because this is a two-column layout again and the query above
     handed them to auto-placement. That is what "completely cooked" was: with two columns and one
     row, auto-placement put the mark in the board's track, the *board* in the 300px column beside
     it, where a 366px square overflowed and centred itself, and the card in an implicit row under
     both. Measured at 844x390 the board's top edge was 72px above the window, so the first rank and
     a half were off the top of the screen, and the card was bottom left. */
  .stage-hud { grid-column: 2; grid-row: 1; align-self: start; justify-self: start; }
  .stage-board { grid-column: 1; grid-row: 1 / -1; }
  .stage-side { grid-column: 2; grid-row: 2; align-self: center; justify-items: start; }

  /* The trial-return bar, sized for this layout rather than for the stacked one.
     Without this the stacked query's version won on specificity, and it subtracts 289px for a card
     that is *under* the board there and beside it here: on a 390px-tall screen that is
     `390 - 143 - 289`, a negative length, which `grid-template-columns` floors at zero. The board
     column came out 0px wide and the board was not on the screen at all. Measured, not reasoned
     about: `grid-template-columns` read `0px 286.953px` at 844x390 with the bar up. */
  body.stage-page.has-trial-return .stage {
    --board-size: min(
      calc(100svh - var(--trial-return-h, 143px) - var(--pad) * 2),
      calc(100vw - var(--col) - var(--gap) - var(--pad) * 2)
    );
  }

  .stage-card { padding: 18px 20px; gap: 12px; }
  .stage-num { font-size: 28px; }
  .stage-prompt { min-height: 0; font-size: 15px; }
  .stage-verdict { min-height: 0; font-size: 12px; }
  .stage-scroll { display: none; }
  .beat-line { display: none; }
}

/* --- Reduced motion ---------------------------------------------------------------------------------- */
/*
   Every animation on this page is a second telling of something already said in words: the rail says
   which position you are on, the card says whether the move was right, and the beat says what the run
   was for. So they are switched off rather than substituted. landing.js makes the same call in
   JavaScript for the marks it draws, and never appends them at all. */
@media (prefers-reduced-motion: reduce) {
  .stage-prompt.is-nudging,
  .stage-scroll svg,
  .fx-sweep i,
  .stage-board-wrap.is-shake { animation: none; }
  .beat,
  .beat-card,
  .stage-seg,
  .stage-board-wrap::before { transition: none; }
  .stage-help { transition: none; }
}
