/* ================================================================
   BONBON BLAST
   ================================================================
   The game behind the stack web, and everything drawn over it: the score, the
   rack, the shouting, and the five cards that go over the board.

   Its own file because it is its own thing. It had grown to about seven hundred
   lines inside console-charts.css, which is the stylesheet for the instruments
   on the dashboard, and a leaderboard has no business living next to a heat
   grid. Nothing else in the console uses any selector in here.

   What is NOT here: the stack web itself. The card is a survey instrument that
   happens to have a game running under it, and .cb-web-* is the instrument.
   The handful of rules where the web reacts to a round starting are here,
   though, because they only exist because of the game. */

/* ---- One colour, one job ----

   The field had colours doing two things at once, which is the one thing a
   board you have to read at speed cannot afford. A frozen sweet was drawn in
   the same green as a guard pickup, so a slowed board looked like a board
   covered in shields. Worse, --cbc-poor and --cbc-accent resolve to the same
   pink, so "an enemy just took a hit" and "that is a boost you can collect"
   were literally the same colour.

   Six jobs, six colours, and nothing shares:

     ink     the board you fly through, and you: sweets, your hull, your
             thrust, your shots, debris, and the flash when something is hit
     boost   pickups with a clock on them
     ammo    pickups you keep rounds of, and everything they fire
     guard   pickups that keep you alive, and the shield around your hull
     foe     the company: their hulls, their shots, the bar over a boss
     slow    anything stopped or slowed: a frozen board, a stunned ship, the
             pulse that did it

   Declared here rather than read straight off the theme, so the meaning has a
   name. The game asks for "foe"; it does not ask for the tech-stack tint and
   hope nobody repaints that card. */
.cb-web-canvas {
  --cbc-play-boost: var(--cbc-accent);
  --cbc-play-ammo: var(--cbc-warn);
  --cbc-play-guard: var(--cbc-good);
  /* Teal, not the blue it started as. Blue and the purple below it are
     neighbours, and "is this thing stunned or not" is the single distinction on
     this board that has to be readable in a glance. */
  --cbc-play-foe: var(--cbc-cat-design);
  --cbc-play-slow: var(--cbc-cat-localVisibility);
}

/* ---- The game's own furniture ----
   DOM rather than canvas text. A score is read, not drawn: it wants the
   console's own type, it can be selected, and a screen reader can say how many
   lives are left without anybody writing a description of a canvas. */
/* ---- The furniture scales as one piece ----
   Every readout on this layer is absolutely positioned in a corner at a size
   chosen for a card about four hundred pixels across. Filling the screen makes
   the board six times the area and left all of it exactly where it was and
   exactly as big: a rack of weapons in the corner of a whole monitor, set in
   twelve pixel type.

   Naming the sizes again for the big case was the first attempt and it was a
   dozen rules that only covered the type, so the rack, the boss bar and every
   gap between things stayed put. This does the whole layer at once: it is laid
   out at the size it was designed for and then scaled back up to fill the
   card, so the corners, the spacing and the type all grow together and stay in
   proportion to each other and to the board behind them.

   How much, and when the legend is dropped for want of room, are uiScale and
   tooTight in candy-screen.ts. */
.cb-play {
  position: absolute;
  inset: 0;
  /* Laid out at one over the scale, then scaled by it: the two cancel and the
     layer covers the card exactly, whatever the scale is. */
  inline-size: calc(100% / var(--cb-ui, 1));
  block-size: calc(100% / var(--cb-ui, 1));
  transform-origin: 0 0;
  scale: var(--cb-ui, 1);
  pointer-events: none;
  font-family: var(--cb-font-mono);
}
/* Only the button takes the pointer. Everything else here is a readout, and a
   readout that swallows a drag would break the card underneath it. */
/* NO BACKDROP BLUR OVER THE BOARD. modal-still.ts makes this argument at
   length about a dialog: a backdrop filter draws everything behind it into a
   texture and filters it again on every frame that anything back there moves.
   It is cheap over a still page and it is the most expensive thing a
   stylesheet can ask for over a moving one.

   What is behind every one of these is the game, repainting sixty times a
   second, and the texture being filtered is the whole surface, so the cost
   climbed with the screen: on the card nobody ever noticed, and filling a
   large monitor it was several full-screen blurs a frame on top of a canvas
   that was already too big (candy-screen.ts, canvasRatio).

   The ground is carried by the colour alone now, taken a few points more
   opaque to make up for the blur that was doing the separating. */
.cb-play-toggle {
  position: absolute;
  inset-block-end: var(--cb-space-md);
  inset-inline-end: var(--cb-space-md);
  pointer-events: auto;
  padding: var(--cb-space-xs) var(--cb-space-lg);
  border: 1px solid var(--cbc-line-strong);
  border-radius: var(--cb-radius-full);
  background: color-mix(in srgb, var(--cbc-raised) 94%, transparent);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink-muted);
  cursor: pointer;
  transition:
    color var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-play-toggle:hover {
  color: var(--cbc-ink);
  border-color: var(--cbc-accent);
}
.cb-play-score,
.cb-play-lives {
  position: absolute;
  inset-block-start: var(--cb-space-md);
  font-size: var(--cb-type-md-size);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink);
  opacity: 0;
  transition: opacity var(--cb-motion-base) var(--cb-motion-ease);
}
.cb-play-score {
  inset-inline-start: var(--cb-space-lg);
}
.cb-play-lives {
  inset-inline-end: var(--cb-space-lg);
  color: var(--cbc-accent);
}
/* Whatever the round last announced, under the score. A read-out rather than a
   control, so it never takes the pointer. What the ship is carrying used to sit
   here as a line of text and is the rack in the opposite corner now. */
.cb-play-said {
  position: absolute;
  inset-block-start: var(--cb-space-6xl);
  inset-inline-start: var(--cb-space-2xl);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-accent);
  pointer-events: none;
  animation: cbc-fade var(--cb-motion-fast) var(--cb-motion-ease) backwards;
}

/* The pointer goes away while a round is on.

   The ship is flown with the keyboard, so a cursor sitting on the field is a
   thing that does nothing except open the card's own hover panels over the
   game. It comes back on the end screen, where there are buttons to press. */
.cb-web[data-playing] {
  cursor: none;
}
/* The instrument is out of reach for the whole of a game, end screen included.
   The cursor comes back on the end screen so its buttons can be pressed, and
   the field underneath was still catching it: nodes could be dragged and their
   panels opened through the screen the cursor had come back for. */
.cb-web[data-game] .cb-web-canvas {
  pointer-events: none;
}
.cb-web[data-playing] .cb-play-toggle {
  cursor: pointer;
}

/* The banner: the countdown, the "ouch" between lives, and the end screen. It
   sits over the field and takes the pointer only where it has a button on it,
   so a stray click during a round goes to the game rather than to a panel. */
.cb-play-banner {
  position: absolute;
  inset: 0;
  display: grid;
  /* Capped at the card rather than at its own content. A centred grid track is
     max-content by default, so `max-inline-size: 100%` on a child resolved
     against the child's own width and capped nothing: a long platform name ran
     off the side of the card with its last letter cut in half. */
  grid-template-columns: minmax(0, max-content);
  place-content: center;
  justify-items: center;
  gap: var(--cb-space-sm);
  padding: var(--cb-space-3xl);
  text-align: center;
  background: color-mix(in srgb, var(--cbc-base) 92%, transparent);
  animation: cbc-fade var(--cb-motion-fast) var(--cb-motion-ease) backwards;
}
.cb-play-count {
  font-size: var(--cb-type-3xl-size);
  font-weight: var(--cb-font-weight-semibold);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--cbc-accent);
}
.cb-play-head {
  font-size: var(--cb-type-lg-size);
  font-weight: var(--cb-font-weight-semibold);
  color: var(--cbc-ink);
}
.cb-play-line {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink-faint);
}

.cb-play-keys {
  position: absolute;
  inset-block-end: var(--cb-space-md);
  inset-inline-start: var(--cb-space-lg);
  /* Clear of the rack in the opposite corner, so a wide board does not run the
     two into each other. */
  max-inline-size: calc(100% - var(--cb-space-11xl) - var(--cb-space-8xl));
  /* OPACITY ALONE IS NOT HIDDEN, and that is not a testing detail. A row faded
     to zero is invisible to somebody looking at it and fully present to
     somebody listening to it: still in the accessibility tree, still read out.
     The line above says this stands down rather than "saying the same four
     things twice on one screen", and on the title card the four things are
     already spelled out in the middle of the screen. Faded only, it said them
     twice to exactly the person least able to skip the repeat.
     
     Both properties, transitioned together, which is the idiom this needs:
     going to visible, visibility flips at once and the opacity carries the
     fade; going to hidden, the opacity fades and visibility flips at the end,
     so nothing snaps and nothing is announced while it is on its way out. */
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--cb-motion-base) var(--cb-motion-ease),
    visibility var(--cb-motion-base) var(--cb-motion-ease);
}
/* The readouts appear when there is a round to read. */
.cb-play:not([data-phase="idle"]) .cb-play-score,
.cb-play:not([data-phase="idle"]) .cb-play-lives {
  opacity: 1;
}
/* The corner reminder is scaffolding: the most useful thing on the card for the
   first few seconds of a round, and clutter in the corner of a board for every
   second after that. The card decides when, and says so here. */
.cb-play[data-hint] .cb-play-keys {
  opacity: 1;
  visibility: visible;
}
/* Nothing on the end screen is being flown, so the score has nothing to say and
   is on the screen in front of it in much bigger type. After the rule above,
   not before it: the two selectors weigh the same, so the only thing deciding
   this is which one is written last. */
.cb-play[data-phase="over"] .cb-play-score,
.cb-play[data-phase="over"] .cb-play-lives {
  opacity: 0;
}

/* A card too small to hold two halves stacks them, and drops the key legend:
   a game on a quarter-width card is a backdrop, not a thing being played. */
@container card (max-width: 34rem) {
  .cb-over {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--cb-space-lg);
  }
  .cb-play-keys {
    display: none;
  }
}

/* ================================================================
   BONBON BLAST: the front of the machine
   ================================================================
   The title screen an arcade cabinet spent most of its life on. The demo runs
   behind it, which is the autopilot that has always flown this field, so
   nothing here is animated for show. */
/* The corner reminder is on the title card itself, in bigger type. Two copies
   of the same four controls on one screen is one too many. */
/* The legend is in the corner on the title as well, because it is in the
   corner during a round: reading it before you start and then hunting for it
   again ten seconds later is the version of this that was wrong. */
.cb-play[data-phase="title"] .cb-play-banner {
  /* Less of a veil than the other banners. The point of an attract screen is
     that you can see the game through it. */
  background: color-mix(in srgb, var(--cbc-base) 62%, transparent);
  gap: var(--cb-space-md);
}
/* Scanlines, on this screen and nowhere else. A console that renders every
   card through a CRT filter is a novelty; one that does it for the eight
   seconds a game is announcing itself is a joke told once. */
.cb-play[data-phase="title"] .cb-play-banner::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    color-mix(in srgb, var(--cb-color-black) 22%, transparent) 0 1px,
    transparent 1px 3px
  );
  /* A slow roll, the way a real tube never quite held still. */
  animation: cbc-scan 8s linear infinite;
}
@keyframes cbc-scan {
  from {
    background-position-y: 0;
  }
  to {
    background-position-y: var(--cb-space-4xl);
  }
}

.cb-play-eyebrow {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  letter-spacing: var(--cb-tracking-xs);
  text-transform: uppercase;
  color: var(--cbc-ink-faint);
  animation: cbc-fade var(--cb-motion-slow) var(--cbc-spring) backwards;
}

/* The name, one letter at a time. A title that fades in whole is a graphic; a
   title that lands letter by letter is a machine warming up. */
.cb-play-title {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* Between the words. The letters inside a word have their own, tighter gap. */
  gap: var(--cb-space-lg);
  font-size: var(--cb-type-2xl-size);
  line-height: 1;
  font-weight: var(--cb-font-weight-bold);
  letter-spacing: var(--cb-tracking-xs);
  color: var(--cbc-accent);
}
.cb-play-word {
  display: flex;
  gap: var(--cb-space-xxs);
}
.cb-play-title .cb-play-word span {
  animation: cbc-letter var(--cb-motion-base) var(--cbc-spring) both;
  animation-delay: calc(var(--i) * 45ms);
}
@keyframes cbc-letter {
  from {
    opacity: 0;
    transform: translate3d(0, var(--cb-space-md), 0) scale(1.4);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* The best score, and the way into the whole table behind it.
   A button, so the layer has to let the pointer through to it: everything on
   .cb-play is a readout by default and a readout that swallows a drag would
   break the card underneath. */
.cb-play-best {
  pointer-events: auto;
  display: flex;
  align-items: baseline;
  gap: var(--cb-space-md);
  padding: var(--cb-space-xs) var(--cb-space-md);
  border: 1px solid transparent;
  border-radius: var(--cb-radius-full);
  background: transparent;
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink-faint);
  cursor: pointer;
  transition:
    color var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-play-best:hover,
.cb-play-best:focus-visible {
  border-color: var(--cbc-line-strong);
  color: var(--cbc-ink);
}
/* The word that says it opens something, quiet until you are near it. */
.cb-play-best-more {
  color: var(--cbc-ink-faint);
  opacity: 0.55;
}
.cb-play-best:hover .cb-play-best-more {
  color: var(--cbc-accent);
  opacity: 1;
}

/* ---- The board, on the title ----
   The end screen's table, on the screen where somebody is deciding whether to
   play. A cabinet's attract loop shows the high scores, and seeing who is
   ahead of you before you start is most of what a high score table is for. */
.cb-play-board {
  display: grid;
  gap: var(--cb-space-xxs);
  inline-size: min(var(--cb-width-sm), 100%);
  text-align: start;
}
.cb-play-back {
  pointer-events: auto;
}
.cb-play-best b {
  font-size: var(--cb-type-md-size);
  font-variant-numeric: tabular-nums;
  color: var(--cbc-ink);
}
.cb-play-howto {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-muted);
}
/* The line every cabinet blinked. It shows the key that actually starts it
   rather than spelling it, so it is a row rather than a sentence. */
.cb-play-insert {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--cb-space-sm);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  letter-spacing: var(--cb-tracking-xs);
  text-transform: uppercase;
  color: var(--cbc-accent);
  animation: cbc-blink var(--cbc-loop) steps(1, end) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .cb-play[data-phase="title"] .cb-play-banner::after,
  .cb-play-title .cb-play-word span,
  .cb-play-insert {
    animation: none;
  }
  .cb-play-title .cb-play-word span {
    opacity: 1;
  }
}

/* What the next kill is worth, on the score rather than beside the pickups.
   It climbs while nothing touches the ship and drops to one when something
   does, so it is the one number on this card that rewards not dying. */
.cb-play-score {
  display: flex;
  align-items: baseline;
  gap: var(--cb-space-sm);
}
.cb-play-multi {
  padding: 0 var(--cb-space-sm);
  border-radius: var(--cb-radius-xs);
  background: var(--cbc-accent-wash);
  color: var(--cbc-accent);
  font-size: var(--cb-type-xs-size);
  font-variant-numeric: tabular-nums;
  animation: cbc-pop var(--cb-motion-fast) var(--cbc-spring) backwards;
}

/* ================================================================
   BONBON BLAST: the rack, the shouting, and the screens
   ================================================================ */

/* ---- The rack ----
   What the ship is carrying, where the End button used to be. It was a line of
   text under the score reading "laser ×5 · rockets ×6 · rapid", which is a log
   entry: nobody reads a sentence mid-fight. Four numbered tiles for the
   equipment, pips for the rounds in each, and the armed one lit. Above them,
   the things with a clock on them, each inside a ring that empties. */
.cb-rack {
  position: absolute;
  inset-block-end: var(--cb-space-md);
  inset-inline-end: var(--cb-space-md);
  display: grid;
  gap: var(--cb-space-xs);
  justify-items: end;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--cb-motion-base) var(--cb-motion-ease);
}
.cb-play:not([data-phase="idle"]):not([data-phase="over"]) .cb-rack {
  opacity: 1;
}
.cb-rack-slots,
.cb-rack-timed {
  display: flex;
  gap: var(--cb-space-xs);
}
.cb-rack-glyph {
  inline-size: var(--cb-space-3xl);
  block-size: var(--cb-space-3xl);
  display: block;
}

.cb-rack-slot {
  position: relative;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--cb-space-xxs);
  inline-size: var(--cb-space-6xl);
  block-size: var(--cb-space-6xl);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-md);
  background: color-mix(in srgb, var(--cbc-raised) 90%, transparent);
  color: var(--cbc-ink-faint);
  transition:
    color var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cb-motion-ease),
    background var(--cb-motion-fast) var(--cb-motion-ease);
}
/* An empty slot is turned down rather than taken away, so the four keys are
   always in the same four places and pressing 3 always means the same thing. */
.cb-rack-slot[data-state="empty"] {
  opacity: 0.4;
}
.cb-rack-slot[data-state="held"] {
  color: var(--cbc-warn-text);
  border-color: color-mix(in srgb, var(--cbc-warn) 42%, transparent);
}
/* The one the trigger is on. Filled, because at this size an outline change is
   something you have to look for. */
.cb-rack-slot[data-state="armed"] {
  color: var(--cbc-warn-text);
  border-color: var(--cbc-warn);
  background: color-mix(in srgb, var(--cbc-warn) 20%, var(--cbc-raised));
}
.cb-rack-number {
  position: absolute;
  inset-block-start: var(--cb-space-xxs);
  inset-inline-start: var(--cb-space-xs);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  line-height: 1;
  color: inherit;
  opacity: 0.7;
}
.cb-rack-pips {
  display: flex;
  gap: 1px;
}
.cb-rack-pip {
  inline-size: var(--cb-space-xs);
  block-size: var(--cb-space-xxs);
  border-radius: var(--cb-radius-full);
  background: currentColor;
  opacity: 0.22;
}
.cb-rack-pip[data-on] {
  opacity: 1;
}

/* A timed thing: the glyph inside a ring that empties as it runs out. */
.cb-rack-chip {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-5xl);
  block-size: var(--cb-space-5xl);
  border-radius: var(--cb-radius-full);
  background: color-mix(in srgb, var(--cbc-raised) 90%, transparent);
  /* One arrival, and a calm one. It was a spring that overshot, and the row was
     being rebuilt a dozen times over each chip's life, so it ran that overshoot
     a dozen times and the corner appeared to be shaking. The rebuilding is gone
     (see candy-loadout.ts) and what is left is a chip that rises into place
     once and then only its ring moves. */
  animation: cbc-chip-in var(--cb-motion-base) var(--cbc-spring) both;
}
@keyframes cbc-chip-in {
  from {
    opacity: 0;
    transform: translate3d(0, var(--cb-space-md), 0) scale(0.9);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
/* And it starts blinking near the end, faster the closer it gets. The duration
   comes from the card, in steps: a duration that changed every frame would
   re-time the animation every frame and stutter. */
.cb-rack-chip[data-low] {
  animation:
    cbc-chip-in var(--cb-motion-base) var(--cbc-spring) both,
    cbc-chip-low var(--beat, 900ms) var(--cb-motion-ease) infinite;
}
@keyframes cbc-chip-low {
  50% {
    opacity: 0.32;
  }
}
.cb-rack-chip[data-family="boost"] {
  color: var(--cbc-accent);
}
.cb-rack-chip[data-family="guard"] {
  color: var(--cbc-good-text);
}
.cb-rack-chip[data-family="ammo"] {
  color: var(--cbc-warn-text);
}
/* The ship's own jump, which came off no pickup and so takes none of the three
   pickup colours: plain ink, so a recharging jump is not mistaken for a boost
   somebody collected. */
.cb-rack-chip[data-family="ship"] {
  color: var(--cbc-ink-muted);
}
/* The ring, drawn as a cone masked to an annulus, which is the only way to draw
   a clock in CSS without an element per degree. The card sets --left to the
   exact fraction left every frame, so it sweeps rather than steps: it used to
   be rounded to a twelfth on the way in. No transition, on purpose. It is
   already being set sixty times a second, and easing between two values that
   are one frame apart only makes it lag. */
.cb-rack-chip::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: conic-gradient(
    from 180deg,
    currentColor var(--left, 100%),
    color-mix(in srgb, currentColor 16%, transparent) 0
  );
  mask: radial-gradient(closest-side, transparent 78%, var(--cb-color-black) 80%);
  -webkit-mask: radial-gradient(closest-side, transparent 78%, var(--cb-color-black) 80%);
}

/* ---- The shouting ----
   Call of Duty did not invent scoring several kills quickly. It invented saying
   so, in the biggest letters it had, at the exact frame it happened. Three
   volumes, because a double kill and a boss going down arriving at the same
   size would make both of them mean nothing. */
.cb-play-shout {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 28%;
  display: grid;
  justify-items: center;
  gap: var(--cb-space-xxs);
  text-align: center;
  pointer-events: none;
  animation: cbc-punch 1.3s var(--cbc-spring) both;
}
.cb-play-shout-head {
  font-size: var(--cb-type-lg-size);
  font-weight: var(--cb-font-weight-bold);
  line-height: 1;
  letter-spacing: var(--cb-tracking-xs);
  text-transform: uppercase;
  color: var(--cbc-accent);
}
.cb-play-shout[data-force="2"] .cb-play-shout-head {
  font-size: var(--cb-type-2xl-size);
}
.cb-play-shout[data-force="3"] .cb-play-shout-head {
  font-size: var(--cb-type-3xl-size);
  color: var(--cbc-warn-text);
}
.cb-play-shout-note {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink-muted);
}
/* In hard, out soft, and it lifts as it goes so it never simply vanishes. */
@keyframes cbc-punch {
  0% {
    opacity: 0;
    transform: scale(1.8);
  }
  9% {
    opacity: 1;
    transform: scale(1);
  }
  70% {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
  100% {
    opacity: 0;
    transform: translate3d(0, calc(var(--cb-space-2xl) * -1), 0);
  }
}

/* ---- The boss bar ----
   Across the top, between the score and the lives. Only up while there is a
   boss, so the card is not carrying an empty gauge for the other four waves. */
.cb-play-boss {
  position: absolute;
  inset-block-start: var(--cb-space-lg);
  inset-inline-start: 50%;
  translate: -50% 0;
  inline-size: min(46%, var(--cb-width-xs));
  display: grid;
  gap: var(--cb-space-xxs);
  justify-items: center;
  pointer-events: none;
  animation: cbc-fade var(--cb-motion-base) var(--cbc-spring) backwards;
}
/* In the company's colour, not in the colour of your own ammunition, which is
   what amber means everywhere else on this board. */
.cb-play-boss-who {
  max-inline-size: 100%;
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-cat-design);
}
.cb-play-boss-track {
  display: block;
  inline-size: 100%;
  block-size: var(--cb-space-xs);
  border-radius: var(--cb-radius-full);
  background: color-mix(in srgb, var(--cbc-cat-design) 16%, transparent);
  overflow: hidden;
}
.cb-play-boss-left {
  display: block;
  block-size: 100%;
  inline-size: var(--left, 100%);
  border-radius: inherit;
  background: var(--cbc-cat-design);
  transition: inline-size var(--cb-motion-fast) var(--cb-motion-ease);
}

/* ---- The death card ----
   A hundred lines, so a long session rarely reads the same one twice. It sits
   under the count, which is now a sentence rather than "2 left". */
.cb-play-quip {
  max-inline-size: var(--cb-width-sm);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-sm-size);
  line-height: var(--cb-type-sm-line-height);
  color: var(--cbc-ink-muted);
}
/* The boss wave card.
   The mark sits beside the name rather than over it. Stacked, the card was an
   eyebrow, a crest, a name, a form, a line and a weakness in one column, and it
   ran off the bottom of a card at the size the dashboard actually gives this
   one. Side by side it is four rows instead of six and it reads like a fight
   card, which is what it is. */
.cb-play-boss-title {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  max-inline-size: 100%;
  text-align: start;
}
.cb-play-boss-id {
  display: grid;
  gap: var(--cb-space-xxs);
  min-inline-size: 0;
}
/* The platform's own mark. The same logo the boss is wearing out on the field
   and the same one the stack web puts on its hubs, in the field's ink rather
   than the brand's colour. */
.cb-play-boss-crest {
  display: grid;
  place-items: center;
  flex: none;
  inline-size: var(--cb-space-6xl);
  block-size: var(--cb-space-6xl);
  border-radius: var(--cb-radius-full);
  border: 1px solid color-mix(in srgb, var(--cbc-cat-design) 45%, transparent);
  background: color-mix(in srgb, var(--cbc-cat-design) 12%, transparent);
  color: var(--cbc-cat-design);
  animation: cbc-pop var(--cb-motion-base) var(--cbc-spring) backwards;
}
.cb-play-boss-mark {
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  fill: currentColor;
}
/* The boss wave card: the platform's name, landing letter by letter. */
.cb-play-boss-name {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cb-space-xxs);
  font-size: var(--cb-type-xl-size);
  line-height: 1;
  font-weight: var(--cb-font-weight-bold);
  letter-spacing: var(--cb-tracking-xs);
  text-transform: uppercase;
  color: var(--cbc-cat-design);
}
/* Set smaller when the platform has a long name. The letters are one element
   each so they can land in order, which means a wrap would break the word in
   the middle rather than at a space: "SQUARESPAC" over "E". The name is mono,
   so how many characters it has is how wide it is. */
.cb-play-boss-name[data-fit="mid"] {
  font-size: var(--cb-type-lg-size);
  letter-spacing: var(--cb-tracking-xxs);
}
.cb-play-boss-name[data-fit="tight"] {
  font-size: var(--cb-type-md-size);
  letter-spacing: var(--cb-tracking-xxs);
}
.cb-play-boss-name span {
  animation: cbc-letter var(--cb-motion-base) var(--cbc-spring) both;
  animation-delay: calc(var(--i) * 45ms);
}
/* Which of the five it is, under whose logo it is wearing. Set quieter than
   the name, because the name is the thing on the card and this is what kind of
   thing it is. */
.cb-play-boss-form {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  letter-spacing: var(--cb-tracking-xxs);
  text-transform: uppercase;
  color: var(--cbc-cat-design);
  animation: cbc-rise var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: var(--cb-motion-fast);
}
/* How to kill it, in the colour of your own ammunition.
   Deliberately not the company's teal: everything else on this card is a fact
   about the thing arriving, and this one line is an instruction about what is
   on your rack. The one place on the board where amber is text rather than a
   pickup, and it is amber for exactly that reason. */
.cb-play-boss-tell {
  max-inline-size: min(var(--cb-width-sm), 100%);
  text-wrap: balance;
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
  color: var(--cbc-warn-text);
  animation: cbc-rise var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: var(--cb-motion-base);
}
/* The line between them arrives in the same order, so the card reads top to
   bottom rather than all at once. */
.cb-play-banner .cb-play-boss-title ~ .cb-play-line {
  max-inline-size: min(var(--cb-width-sm), 100%);
  text-wrap: balance;
  animation: cbc-rise var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: calc(var(--cb-motion-fast) * 2);
}

/* ---- The pause screen ----
   Tilde, because nothing else in a browser or in this console wants that key.
   It stops the field's clock, which is what drives every wobble and glow in the
   game, so the board holds the exact frame it was on. */
:is(.cb-play-banner):has(.cb-play-boss-title) {
  gap: var(--cb-space-xs);
  padding: var(--cb-space-lg);
}
.cb-play[data-phase="paused"] .cb-play-banner {
  /* The one screen somebody sits on for more than a second, so it is the one
     worth giving room to. It was four readings, a line and two keys packed
     into about eighty pixels. Room, though, is what is left over after it
     fits: this card is resizable and the pause screen is the tallest thing
     the banner ever holds, so the gap is the first thing to give. */
  gap: var(--cb-space-lg);
  background: color-mix(in srgb, var(--cbc-base) 88%, transparent);
}
.cb-pause-word {
  display: flex;
  gap: var(--cb-space-sm);
  font-size: var(--cb-type-3xl-size);
  line-height: 1;
  font-weight: var(--cb-font-weight-bold);
  letter-spacing: var(--cb-tracking-lg);
  /* The word sat off the centre line every other thing on this card is centred
     on, by half the tracking. Letter-spacing puts its space AFTER a character,
     including the last one, and the word is one span per letter inside a
     centred max-content track, so the box was a full step wider on the right
     than the letters in it. Pulling that step back off the end makes the box
     the letters again. Not by dropping the tracking, which is the look. */
  margin-inline-end: calc(var(--cb-tracking-lg) * -1);
  color: var(--cbc-ink);
}
.cb-pause-aside {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-sm-size);
  color: var(--cbc-ink-muted);
  max-inline-size: var(--cb-width-sm);
}
.cb-pause-word span {
  animation: cbc-letter var(--cb-motion-base) var(--cbc-spring) both;
  animation-delay: calc(var(--i) * 40ms);
}
.cb-over-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--cb-space-xs) var(--cb-space-3xl);
  margin: 0;
  font-family: var(--cb-font-mono);
}
/* The pause readings, spread rather than crowded, each one its own column with
   the figure under the label. */
.cb-pause-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, auto));
  grid-template-rows: auto auto;
  grid-auto-flow: column;
  justify-content: center;
  gap: var(--cb-space-xs) var(--cb-space-6xl);
  padding-block: var(--cb-space-md);
  margin: 0;
  border-block: 1px solid var(--cbc-line);
  font-family: var(--cb-font-mono);
}
.cb-pause-strip dt,
.cb-over-strip dt {
  font-size: var(--cb-type-xs-size);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink-faint);
}
.cb-pause-strip dd,
.cb-over-strip dd {
  margin: 0;
  font-size: var(--cb-type-md-size);
  font-variant-numeric: tabular-nums;
  color: var(--cbc-ink);
}
.cb-pause-strip dt {
  text-align: center;
}
.cb-pause-strip dd {
  text-align: center;
  font-size: var(--cb-type-lg-size);
}
.cb-pause-keys {
  display: flex;
  align-items: center;
  gap: var(--cb-space-3xl);
}

/* ---- Key caps ----
   The controls are shown, not spelled. A word is not what is on the key, and a
   row of caps is read in about a quarter of the time a sentence is. One rule
   for every screen that names a control, so they cannot drift apart. */
.cb-play kbd {
  display: inline-grid;
  place-items: center;
  min-inline-size: var(--cb-space-2xl);
  block-size: var(--cb-space-2xl);
  padding: 0 var(--cb-space-xs);
  border: 1px solid var(--cbc-line-strong);
  /* A cap sits on its own bottom edge, the way a key sits in a board. */
  border-block-end-width: 2px;
  border-radius: var(--cb-radius-xs);
  background: var(--cbc-sunken);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  line-height: 1;
  color: var(--cbc-ink);
}
/* The space bar is a bar, and "esc" needs room for three letters. */
.cb-play kbd[data-wide] {
  min-inline-size: var(--cb-space-5xl);
}
/* The two caps whose symbols no monospace stack draws properly. Same size as a
   letter on the caps beside them, so a row of keys reads as one row. */
.cb-caps-glyph {
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
}
.cb-caps-glyph svg {
  inline-size: 100%;
  block-size: 100%;
}
/* THE ROW OF KEYCAPS, AND IT IS NOT THE PASSKEY LIST.
   It was `.cb-keys`, and so is the list of passkeys on the profile screen
   (console-panels.css). Two components, two meanings of the word, one class
   name, and which one won was decided by which stylesheet loaded last. The
   guard catches exactly this (scripts/check-classes.ts) and it had been
   failing the build; caps is what these are, and the passkey list keeps the
   name that reads as a list of keys. */
.cb-caps {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cb-space-xs) var(--cb-space-lg);
}
.cb-caps-bit {
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-xs);
}
.cb-caps-does,
.cb-caps-to {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}

/* ---- The end screen ----
   The old one asked a signed-in person to type three letters into a box, then
   flashed one leaderboard and replaced it with a different one. The name comes
   off the account now, the board fills in place, and the number has a sentence
   under it: "your best yet, by 40,000" is what somebody actually wanted. */
/* ---- How the end screen arrives ----
   One orchestrated entrance rather than a panel that is simply there. The veil
   comes up, the panel settles into it, and then the contents deal themselves
   out: the eyebrow, the score punching in and counting up to itself, the
   verdict, the four readings one column at a time, the buttons, and the board a
   row at a time. Everything is over inside a second, because nothing should
   animate for longer than it takes to read. */
@keyframes cbc-over-in {
  from {
    opacity: 0;
    transform: translate3d(0, var(--cb-space-lg), 0);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
/* The score is the one number anybody came for, so it lands rather than
   appears: in oversized, past its mark, and back. */
@keyframes cbc-over-hit {
  0% {
    opacity: 0;
    transform: scale(1.45);
  }
  55% {
    opacity: 1;
    transform: scale(0.97);
  }
  100% {
    opacity: 1;
    transform: none;
  }
}
/* The board deals from the left, which is the direction it is read in. */
@keyframes cbc-over-deal {
  from {
    opacity: 0;
    transform: translate3d(calc(var(--cb-space-3xl) * -1), 0, 0);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes cbc-over-panel {
  from {
    opacity: 0;
    transform: scale(0.96);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.cb-over {
  animation: cbc-over-panel var(--cb-motion-slow) var(--cbc-spring) both;
  display: grid;
  /* The left half a little wider, so the two buttons sit on one line: a
     primary action stacked over a secondary one reads as a form. */
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--cb-space-4xl);
  inline-size: min(100%, var(--cb-width-xl));
  text-align: start;
}
.cb-over-mine {
  display: grid;
  justify-items: start;
  gap: var(--cb-space-xs);
}
/* Each in turn, and the figure gets its own arrival. */
.cb-over-head,
.cb-over-verdict,
.cb-over-buttons {
  animation: cbc-over-in var(--cb-motion-base) var(--cbc-spring) both;
}
.cb-over-head {
  animation-delay: 60ms;
}
.cb-over-verdict {
  animation-delay: 240ms;
}
.cb-over-buttons {
  animation-delay: 400ms;
}
.cb-over-figure {
  animation: cbc-over-hit var(--cb-motion-slow) var(--cbc-spring) 120ms both;
  transform-origin: left center;
}
.cb-over-strip dt,
.cb-over-strip dd {
  animation: cbc-over-in var(--cb-motion-base) var(--cbc-spring) both;
  animation-delay: calc(300ms + var(--i, 0) * 55ms);
}
.cb-over-board-head {
  animation: cbc-over-in var(--cb-motion-base) var(--cbc-spring) 220ms both;
}
.cb-over-run {
  animation: cbc-over-deal var(--cb-motion-base) var(--cbc-spring) both;
  animation-delay: calc(280ms + var(--i, 0) * 38ms);
}
.cb-over-head {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xs);
  color: var(--cbc-ink-faint);
}
.cb-over-figure {
  font-size: var(--cb-type-3xl-size);
  font-weight: var(--cb-font-weight-bold);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--cbc-ink);
}
.cb-over-verdict {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--cb-space-sm);
  font-size: var(--cb-type-sm-size);
  color: var(--cbc-ink-muted);
}
.cb-over-verdict b {
  color: var(--cbc-accent);
  font-weight: var(--cb-font-weight-semibold);
}
.cb-over-strip {
  justify-content: start;
  gap: var(--cb-space-xxs) var(--cb-space-2xl);
  margin-block-start: var(--cb-space-sm);
  /* Two rows of term and figure rather than a list: four readings about one
     round read as a set, and a set is what they are. */
  display: grid;
  grid-template-columns: repeat(4, auto);
  grid-auto-flow: column;
  grid-template-rows: auto auto;
}
.cb-over-strip dd {
  font-size: var(--cb-type-sm-size);
}
.cb-over-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cb-space-md);
  margin-block-start: var(--cb-space-lg);
}
/* The layer around these does not take the pointer, so a stray click during a
   round goes to the game rather than to a panel. Every button on it has to opt
   back in. */
.cb-over-again,
.cb-over-leave {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-md);
  padding: var(--cb-space-sm) var(--cb-space-2xl);
  border-radius: var(--cb-radius-full);
  font-size: var(--cb-type-sm-size);
  cursor: pointer;
  transition:
    filter var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-over-again {
  border: 1px solid var(--cbc-accent);
  background: var(--cbc-accent);
  color: var(--cbc-on-accent);
}
.cb-over-again:hover {
  filter: brightness(1.08);
}
.cb-over-again kbd {
  border-color: color-mix(in srgb, var(--cbc-on-accent) 40%, transparent);
  background: color-mix(in srgb, var(--cbc-on-accent) 16%, transparent);
  color: var(--cbc-on-accent);
}
.cb-over-leave {
  border: 1px solid var(--cbc-line-strong);
  background: transparent;
  color: var(--cbc-ink-muted);
}
.cb-over-leave:hover {
  border-color: var(--cbc-ink-faint);
  color: var(--cbc-ink);
}

/* ---- The two switches: sound, and filling the screen ----
   The same pair on the title, the pause and the end screen, which are the
   three places nobody is flying and the three places the pointer is allowed
   back. They are set below the buttons beside them on purpose: neither is an
   action somebody came here to take, they are states somebody may want to
   change, and a mute button as loud as "Play again" would be the loudest thing
   on a screen about how the round went.

   The icon says the state a second time, so each of them reads without being
   read. */
/* The start prompt and the two switches, one line. */
.cb-play-footrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--cb-space-xl);
}
.cb-play-switch {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-xs);
  padding: var(--cb-space-xs) var(--cb-space-md);
  border: 1px solid transparent;
  border-radius: var(--cb-radius-full);
  background: transparent;
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  letter-spacing: var(--cb-tracking-xxs);
  /* Two words, kept together. On a card somebody has dragged narrow the row
     wraps, which is fine, but "Full" over "screen" is not. */
  white-space: nowrap;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  transition:
    color var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-play-switch .cb-icon {
  display: inline-flex;
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
}
.cb-play-switch .cb-icon svg {
  inline-size: 100%;
  block-size: 100%;
  stroke: currentColor;
}
/* Approaching it shows the affordance; it was chrome until then. */
.cb-play-switch:hover,
.cb-play-switch:focus-visible {
  border-color: var(--cbc-line-strong);
  color: var(--cbc-ink);
}
/* Sound being off is stated rather than implied. A control whose only
   difference between its two states is a glyph nobody looked at is a control
   that gets pressed twice. Fullscreen does not get the same treatment: being
   windowed is not a warning, it is the ordinary state. */
.cb-play-sound[aria-pressed="false"] {
  color: var(--cbc-ink-muted);
}
.cb-play-sound[aria-pressed="false"] .cb-icon {
  color: var(--cbc-warn-text);
}
/* And the one the browser will not honour is turned off, not taken away. */
.cb-play-switch[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}
/* On the end screen they stand in a row with two real buttons, so they need
   the same height as them without pretending to be one. */
.cb-over-buttons .cb-play-switch {
  padding-block: var(--cb-space-sm);
}

/* ---- Filling the screen ----
   What goes fullscreen is the card's field rather than the page, so the
   browser paints its own black behind whatever it is given: the card has to
   bring its own ground with it, and give back the corners it had as a card. */
.cb-web:fullscreen {
  background: var(--cbc-surface);
  border-radius: 0;
}
.cb-web:fullscreen .cb-web-field {
  block-size: 100%;
}
/* Nothing here names a size for the fullscreen case. The whole furniture layer
   scales as one piece; see the note on .cb-play.

   The legend is the exception, and it is dropped rather than scaled. On a card
   dragged down to a quarter of the board it wrapped into a column filling the
   left half and there was no game left to look at. It is scaffolding rather
   than a readout: the pause screen names the same keys, and so does every
   tooltip on the card. */
.cb-web[data-tight] .cb-play-keys {
  display: none;
}

/* ---- And the cards go compact at that size too ----
   A card dragged down to two rows leaves about two hundred pixels of board,
   and the title screen wants nearly three hundred. It was cutting the top off
   its own name.

   What goes is what a card this size cannot afford: the "Candybox presents"
   line, the best score, the pause screen's aside, and most of the padding
   around all of it. What stays is the name, the state, the way in and the way
   out, which is the part somebody at this size is actually looking for. */
.cb-web[data-tight] .cb-play-banner {
  padding: var(--cb-space-md);
  gap: var(--cb-space-xs);
}
.cb-web[data-tight] .cb-play-eyebrow,
.cb-web[data-tight] .cb-play-best,
.cb-web[data-tight] .cb-pause-aside {
  display: none;
}
.cb-web[data-tight] .cb-play-title {
  font-size: var(--cb-type-lg-size);
}
.cb-web[data-tight] .cb-play-title {
  gap: var(--cb-space-sm);
}
.cb-web[data-tight] .cb-pause-word {
  font-size: var(--cb-type-xl-size);
}
.cb-web[data-tight] .cb-pause-strip {
  gap: var(--cb-space-md) var(--cb-space-lg);
}

.cb-over-board {
  display: grid;
  gap: var(--cb-space-xxs);
  min-inline-size: 0;
}
.cb-over-board-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--cb-space-md);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink-faint);
  margin-block-end: var(--cb-space-xs);
}
/* Said, rather than left to be guessed: a board of one person's rounds under a
   heading claiming to be everybody's would be the one thing here that lies. */
.cb-over-board-note {
  text-transform: none;
  color: var(--cbc-ink-faint);
}
.cb-over-runs {
  display: grid;
  gap: 1px;
  min-inline-size: 0;
}
.cb-over-run {
  display: grid;
  grid-template-columns: var(--cb-space-2xl) minmax(0, 1fr) auto var(--cb-space-4xl);
  /* Every place the same height, taken or not. An empty one is a rank and a
     rule and a filled one is a line of text, so eight empties came out six
     pixels shorter than eight names and the panel still moved a little when the
     board landed. Centred rather than on the baseline, which is the same thing
     to look at when every cell is one line of the same type. */
  min-block-size: var(--cb-space-3xl);
  align-items: center;
  gap: var(--cb-space-md);
  padding: var(--cb-space-xxs) var(--cb-space-sm);
  border-radius: var(--cb-radius-xs);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  font-variant-numeric: tabular-nums;
  color: var(--cbc-ink-faint);
}
/* The round just played, picked out rather than left to be worked out from the
   number. */
.cb-over-run[data-now] {
  background: var(--cbc-accent-wash);
  color: var(--cbc-accent);
}
/* The place number, which every other column in this row had a rule for and
   this one did not: it therefore took the row's default and came out DARKER
   than the player's name, which is backwards on a leaderboard. Tabular, so a
   column running past nine does not shuffle sideways. */
.cb-over-rank {
  color: var(--cbc-ink-faint);
  font-variant-numeric: tabular-nums;
}
.cb-over-who {
  color: var(--cbc-ink-muted);
}
.cb-over-run-score {
  color: var(--cbc-ink);
}
.cb-over-run[data-now] .cb-over-who,
.cb-over-run[data-now] .cb-over-run-score {
  color: var(--cbc-accent);
}
.cb-over-run-wave {
  text-align: end;
}
/* A place nobody has taken. Drawn rather than left out: it is what an arcade
   table looked like, and it is the only way the panel is the same height
   before the board lands and after it. */
.cb-over-run[data-empty] {
  color: var(--cbc-ink-faint);
  opacity: 0.4;
}
.cb-over-rule {
  grid-column: 2 / -1;
  align-self: center;
  block-size: 1px;
  background: repeating-linear-gradient(
    to right,
    var(--cbc-line-strong) 0 var(--cb-space-xs),
    transparent var(--cb-space-xs) var(--cb-space-md)
  );
}
.cb-over-gap {
  padding-inline-start: var(--cb-space-lg);
  font-family: var(--cb-font-mono);
  color: var(--cbc-ink-faint);
}

@media (prefers-reduced-motion: reduce) {
  /* Nothing on this card moves. The chips still show what is left of a timer,
     because that is a reading rather than an animation: the ring is set from a
     style property, not from a keyframe. What goes is the blink and every
     entrance. */
  .cb-over,
  .cb-over-head,
  .cb-over-figure,
  .cb-over-verdict,
  .cb-over-buttons,
  .cb-over-strip dt,
  .cb-over-strip dd,
  .cb-over-board-head,
  .cb-over-run,
  .cb-play-shout,
  .cb-rack-chip,
  .cb-rack-chip[data-low],
  .cb-play-boss-crest,
  .cb-play-boss-name span,
  .cb-play-boss-form,
  .cb-play-boss-tell,
  .cb-play-banner .cb-play-boss-title ~ .cb-play-line,
  .cb-pause-word span {
    animation: none;
  }
  .cb-over,
  .cb-over-head,
  .cb-over-figure,
  .cb-over-verdict,
  .cb-over-buttons,
  .cb-over-strip dt,
  .cb-over-strip dd,
  .cb-over-board-head,
  .cb-over-run,
  .cb-rack-chip,
  .cb-play-boss-name span,
  .cb-play-boss-form,
  .cb-play-boss-tell,
  .cb-play-banner .cb-play-boss-title ~ .cb-play-line,
  .cb-pause-word span {
    opacity: 1;
  }
}
