/* Reflex Rush. Loaded after styles.css and owning nothing outside `.rush*`.
 *
 * This sheet borrows the design system rather than restating it: --brand,
 * --good, --bad, --streak, --crown, --radius and the 4px "shelf" all come from
 * styles.css, so a palette change there reaches this screen for free and the
 * arcade mode never drifts into looking like a different app. Both themes work
 * for the same reason -- every colour here is a token or a color-mix over one,
 * and the two hard-coded values are the ink on gold and on green, which are
 * fixed by contrast rather than by mode.
 *
 * THE HUD MUST NOT MOVE. This is the one hard layout rule in the file, and it
 * is why the numbers live in a three-column grid of equal fractions with
 * tabular figures rather than in a flex row that sizes to content. A score
 * going 90 -> 100 is one character wider; in a content-sized row that pushes
 * the two cells beside it, and every one of those reflows can walk the board
 * down the page by a pixel or two while the player is mid-drag. Equal columns
 * plus tabular-nums means the digits change and nothing else does. The clock
 * gets an explicit min-width in ch for the same reason: "1:30" and "0:09" are
 * different widths in a proportional face.
 *
 * MOTION. Everything that moves here is a one-shot on an element already on
 * screen, and the two loops (the urgent clock, the hot combo cell) exist only
 * in states that last seconds. styles.css kills all of it under
 * prefers-reduced-motion with a blanket !important rule; the block at the
 * bottom of this file repeats that so rush.css is still correct if it is ever
 * loaded without its parent.
 */

/* ---------- frame ----------
   Same column shape as .lesson: a fixed header, a body that owns the rest, and
   nothing that scrolls. A rush that scrolls is a rush where the board can be
   half off screen at the moment the clock runs out. */

.rush {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.rush-top {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: max(.6rem, env(safe-area-inset-top)) var(--pad) .7rem;
}

.rush-title {
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -.02em;
  flex: 1 1 auto;
  text-align: center;
}
/* Balances the close button so the title is optically centred without a grid. */
.rush-spacer { width: var(--tap); flex: none; }

/* ---------- clock ----------
   position:relative only so the floating "+2s" has something to hang off. The
   bonus is absolutely positioned precisely because it must never take part in
   this layout: a number appearing in the header flow would widen it, and a
   wider header re-centres the board mid-run. */

.rush-time {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .3rem;
}

.rush-clock {
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  /* Four characters plus the colon. Fixed so the minute rolling over from 1:00
     to 0:59 does not resize the header. */
  min-width: 4ch;
  text-align: center;
}

.clock-track {
  display: block;
  width: min(11rem, 46vw);
  height: .42rem;
  border-radius: 999px;
  background: var(--rule);
  overflow: hidden;
}
.clock-track > i {
  display: block;
  height: 100%;
  width: 100%;
  border-radius: 999px;
  background: var(--good);
  /* Linear, and only just longer than the 100ms tick. Anything springy here
     reads as the clock hesitating, which is the one thing a countdown must
     never look like. */
  transition: width .12s linear, background .3s ease;
}

/* Under ten seconds. Colour plus a slow pulse on the digits only -- the bar
   keeps draining smoothly, because a bar that also flashes stops being
   readable as a quantity at the exact moment it is being read as one. */
.rush-top.urgent .rush-clock { color: var(--bad); animation: rushUrgent .9s ease-in-out infinite; }
.rush-top.urgent .clock-track > i { background: var(--bad); }

.time-bump {
  position: absolute;
  left: 50%;
  top: -.15rem;
  transform: translateX(-50%);
  pointer-events: none;
  font-weight: 700;
  font-size: .95rem;
  color: var(--good);
  animation: rushBump .9s cubic-bezier(.21,.9,.3,1) forwards;
}

/* ---------- strikes ----------
   Three dots, not a number. "2 strikes left" has to be read; three dots with
   one greyed is seen, and this is a mode where the player has no attention to
   spare for reading. */

.rush-strikes {
  display: inline-flex;
  gap: .3rem;
  flex: none;
  width: var(--tap);
  justify-content: flex-end;
}
.strike {
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  background: var(--bad);
  box-shadow: 0 2px 0 var(--bad-dk);
  transition: transform .2s cubic-bezier(.34,1.6,.64,1), background .2s ease;
}
.strike.gone {
  background: var(--rule);
  box-shadow: none;
  transform: scale(.62);
}

/* ---------- body ---------- */

.rush-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 var(--pad);
}
/* Two elastic spacers, not one, and unequal on purpose. A single spacer under
   the board pins the whole run to the top of a tall phone with a hand's worth
   of dead space beneath it; a single spacer above pushes the board off the
   bottom on a short one. Splitting the slack 1:1.6 leaves the board a little
   above centre, which is where a thumb reaches without the hand moving, and
   both spacers collapse to their min-height on a short screen so nothing is
   ever pushed off. */
.rush-lead { flex: 1 1 auto; min-height: .15rem; }
.rush-foot { flex: 1.6 1 auto; min-height: .5rem; }

/* ---------- the HUD ----------
   Three equal columns. See the header: this is the no-shift rule. */

.rush-hud {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: .45rem;
  width: 100%;
  max-width: min(30rem, 92vw);
  margin: .1rem 0 .55rem;
}
.hud-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .05rem;
  padding: .45rem .2rem .4rem;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: 0 3px 0 var(--rule);
  overflow: hidden;
}
.hud-cell b {
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.hud-cell span {
  font-size: .62rem;
  font-weight: 700;
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: .06em;
  white-space: nowrap;
}

/* A live multiplier is the only thing on this row that changes colour, so the
   eye finds it without being told where to look. */
.hud-cell.hot {
  background: var(--streak);
  box-shadow: 0 3px 0 var(--streak-dk);
}
.hud-cell.hot b, .hud-cell.hot span { color: #5a3200; }
.hud-cell.hot b { animation: rushBeat 1.1s ease-in-out infinite; }

/* Passing your own record, announced in place. Gold rather than green: green is
   "that answer was right" everywhere else in the app and this is a different
   kind of good. */
.hud-cell.pb {
  background: var(--crown);
  box-shadow: 0 3px 0 var(--crown-dk);
}
.hud-cell.pb b, .hud-cell.pb span { color: #4a3400; }

/* ---------- prompt ----------
   Fixed height, because it carries three different lengths of string ("Mate in
   one", "+30", "Qxh7#") and a pill that changes height between puzzles moves
   the board. */

.rush-prompt {
  margin: 0 0 .5rem;
  min-height: 2.1rem;
  display: flex;
  align-items: center;
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -.01em;
  padding: .3rem .95rem;
  border-radius: 999px;
  background: var(--card);
  box-shadow: 0 2px 0 var(--rule);
}
.rush-prompt.good {
  color: #fff;
  background: var(--good);
  box-shadow: 0 3px 0 var(--good-dk);
  animation: pop .35s cubic-bezier(.34,1.7,.64,1);
}
/* A miss shows the move that was there, so it is set in the mono face the rest
   of the app uses for notation rather than in the UI face. */
.rush-prompt.bad {
  color: #fff;
  background: var(--bad);
  box-shadow: 0 3px 0 var(--bad-dk);
  font-family: var(--mono);
  font-size: .98rem;
  animation: shake .4s cubic-bezier(.36,.07,.19,.97);
}

/* ---------- intro ---------- */

.rush-intro-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0 var(--pad) calc(1rem + env(safe-area-inset-bottom));
}
.rush-intro-body h1 { margin: .5rem 0 .2rem; }
.rush-intro-body .sub { max-width: 22rem; }

/* --rush-accent defaults to --brand and is overridden per GROUP at the bottom
   of this file. Declared here so the badge is a complete, correct object on its
   own: a mode whose group has no tint, or a mount that never set data-group,
   still gets the original brand badge rather than an unresolved variable and a
   transparent circle. */
.rush-badge {
  --rush-accent: var(--brand);
  display: grid;
  place-items: center;
  width: 4.6rem;
  height: 4.6rem;
  margin-top: .6rem;
  border-radius: 50%;
  font-size: 2.4rem;
  line-height: 1;
  color: #fff;
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--rush-accent) 80%, #fff) 0%, var(--rush-accent) 55%,
    color-mix(in srgb, var(--rush-accent) 85%, #000) 100%);
  box-shadow: 0 var(--shelf) 0 color-mix(in srgb, var(--rush-accent) 80%, #000);
  animation: rushBadgeIn .45s cubic-bezier(.34,1.55,.64,1) both;
}

.rush-rules {
  list-style: none;
  margin: 1rem 0 .2rem;
  padding: 0;
  width: 100%;
  max-width: 24rem;
  display: grid;
  gap: .45rem;
  text-align: left;
}
.rush-rules li {
  background: var(--card);
  border-radius: var(--radius);
  padding: .65rem .9rem;
  font-size: .92rem;
  line-height: 1.35;
  color: var(--dim);
  box-shadow: 0 3px 0 var(--rule);
}
.rush-rules b { color: var(--ink); }

.rush-pbs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: .45rem;
  width: 100%;
  max-width: 24rem;
  margin: 1rem 0 .3rem;
}
.pb-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: .6rem .2rem;
  border-radius: var(--radius);
  border: 2px solid var(--rule);
}
.pb-cell b { font-size: 1.3rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.pb-cell span {
  font-size: .62rem; font-weight: 700; color: var(--dim);
  text-transform: uppercase; letter-spacing: .06em; white-space: nowrap;
}

.rush-recent { margin: .3rem 0 .2rem; font-variant-numeric: tabular-nums; }
.rush-err {
  color: var(--bad); font-weight: 600; font-size: .92rem;
  margin: .6rem 0 .2rem;
}
.rush-intro-body .btn { width: 100%; max-width: 24rem; margin-top: auto; }

/* ---------- summary ----------
   Two auto margins, the same trick .results uses: the score block sits
   optically centred while the buttons stay pinned where a thumb is. */

.rush-over {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .7rem;
  text-align: center;
  padding: var(--pad) var(--pad) calc(var(--pad) + env(safe-area-inset-bottom));
}
.rush-over h1 {
  margin: auto 0 0;
  font-size: 1.9rem;
  letter-spacing: -.03em;
  animation: bigPop .5s cubic-bezier(.34,1.65,.64,1) both;
}
.rush-over h1.pb { color: var(--crown); }

.over-score {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.over-score b {
  font-size: 3.6rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -.045em;
  font-variant-numeric: tabular-nums;
  animation: bigPop .55s cubic-bezier(.34,1.65,.64,1) .06s both;
}
.over-score span {
  font-size: .68rem; font-weight: 700; color: var(--dim);
  text-transform: uppercase; letter-spacing: .08em;
}

.over-cells { display: flex; gap: .5rem; width: 100%; max-width: 24rem; }
.over-cell {
  flex: 1;
  background: var(--card);
  border: 2px solid var(--rule);
  border-radius: var(--radius);
  padding: .7rem .3rem;
  display: flex; flex-direction: column; align-items: center; gap: .1rem;
  animation: cellIn .4s cubic-bezier(.21,.9,.3,1.1) both;
}
.over-cell:nth-child(2) { animation-delay: .06s; }
.over-cell:nth-child(3) { animation-delay: .12s; }
.over-cell b { font-size: 1.45rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.over-cell span {
  font-size: .62rem; font-weight: 700; color: var(--dim);
  text-transform: uppercase; letter-spacing: .06em; white-space: nowrap;
}

/* The line that decides whether they press the button again. */
.over-chase { margin: 0; font-weight: 700; font-size: 1rem; color: var(--dim); }

/* Last five runs, newest on the left, scaled against the personal best. Tiny on
   purpose: it is there to show a direction, not to be read as a chart. */
.over-runs {
  display: flex; align-items: flex-end; gap: .3rem;
  height: 2.6rem; width: 100%; max-width: 24rem;
  justify-content: center;
}
.run-bar {
  width: .8rem;
  height: calc(var(--h) * 1%);
  border-radius: .25rem .25rem 0 0;
  background: var(--rule);
  animation: barGrow .4s cubic-bezier(.21,.9,.3,1.1) both;
  transform-origin: 50% 100%;
}
/* The newest run is the one being talked about, so it is the only coloured one. */
.run-bar:first-child { background: var(--brand); }

.over-cta { width: 100%; max-width: 24rem; margin-top: auto; display: grid; gap: .5rem; }

/* ---------- sixteen modes ----------
   Everything below exists because the roster stopped being two buttons. It is
   deliberately small: the modes differ in their RULES, and rules are read, not
   coloured, so the only things that got a visual treatment are the two facts a
   player needs before pressing Start and cannot get from the blurb.

   THE BADGE IS COLOURED BY GROUP, NOT BY MODE. Sixteen colours is a swatch;
   five is a signal. It is there to answer "did I open the one I meant" in
   peripheral vision after a tap on a list of sixteen near-identical cards, and
   it does that only while the mapping is small enough to learn. Every value is
   a mix over --brand so a palette change in styles.css still reaches here, and
   the badge stays recognisably the same object in every mode rather than five
   unrelated buttons. */

.rush[data-group="motif"]   .rush-badge { --rush-accent: color-mix(in srgb, var(--brand) 55%, var(--crown)); }
.rush[data-group="ladder"]  .rush-badge { --rush-accent: color-mix(in srgb, var(--brand) 55%, var(--streak)); }
.rush[data-group="format"]  .rush-badge { --rush-accent: color-mix(in srgb, var(--brand) 62%, var(--bad)); }
.rush[data-group="opening"] .rush-badge { --rush-accent: color-mix(in srgb, var(--brand) 55%, var(--good)); }

/* The mono icon the opening modes carry instead of a piece. "e5" identifies
   Open Games to a repertoire player faster than any glyph can, and it is set in
   the notation face for the same reason a missed move is: this app writes chess
   in --mono everywhere or the two faces start meaning nothing. Sized down
   because two glyphs at the badge's 2.4rem overflow a 4.6rem circle. */
.rush-mono {
  font-family: var(--mono);
  font-style: normal;
  font-weight: 700;
  font-size: .62em;
  letter-spacing: -.03em;
}

/* The rating range, on the intro only. It is the single fact that separates
   Warm Up from Expert, and on the opening modes it is the warning: those pools
   are mined by opening tag and start around 900, so a beginner who picks
   Sicilian off the name deserves to see that before the clock starts rather
   than after three strikes. Styled as a quiet chip and not as a heading -- it
   is a qualifier on the blurb above it, not a second title. */
.rush-elo {
  margin: .5rem 0 0;
  padding: .18rem .6rem;
  border-radius: 999px;
  border: 2px solid var(--rule);
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 700;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---------- the shot clock ----------
   Survival's clock measures the PUZZLE, not the run, so it must not speak the
   run clock's language. Green draining away means "this is nearly over" on
   every other mode, and it would be a lie here: the bar hits zero, costs one
   strike of three, and immediately refills. Brand blue keeps it a gauge rather
   than a countdown. The urgent red is kept, because the last few seconds of a
   shot clock DO mean something is about to be taken from you -- it just repeats
   the rule above it, which the general urgent rule would otherwise win. */
.rush-time.shot .clock-track > i { background: var(--brand); }
.rush-top.urgent .rush-time.shot .clock-track > i { background: var(--bad); }

/* ---------- keyframes ----------
   Namespaced. styles.css owns `pop`, `shake`, `bigPop`, `cellIn` and `barGrow`,
   which are reused above by name; anything new here is prefixed so this file
   can never shadow one of them and silently change the tree. */

@keyframes rushUrgent { 50% { transform: scale(1.09); } }
@keyframes rushBeat   { 50% { transform: scale(1.12); } }
@keyframes rushBump {
  0%   { opacity: 0; transform: translate(-50%, .4rem) scale(.8); }
  25%  { opacity: 1; transform: translate(-50%, -.5rem) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -1.7rem) scale(1); }
}
@keyframes rushBadgeIn { from { opacity: 0; transform: translateY(10px) scale(.7); } }

/* Repeated from styles.css so this sheet is correct on its own. The blanket
   kill is safe for the same reason it is safe there: every entrance uses
   fill:both from an opacity:0 start, so switching animations off leaves
   elements in their final state rather than invisible. */
@media (prefers-reduced-motion: reduce) {
  .rush, .rush *, .rush-over, .rush-over * {
    animation: none !important;
    transition: none !important;
  }
}


/* ---------- practice cards ----------
   Laid out from the Practice screen, not from inside a run, but the numbers
   are the mode's so they live with the mode's styles. The score column is a
   fixed width with tabular figures so a three digit best does not shove the
   name of the mode sideways relative to the card above it. */
.rush-card {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center; gap: .85rem;
  text-align: left; width: 100%;
  margin-bottom: .6rem;
}
.rush-card-icon { font-size: 1.6rem; line-height: 1; }
.rush-card-body { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.rush-card-body b { font-size: 1rem; }
.rush-card-body .dim {
  font-size: .78rem; font-weight: 600;
  color: rgb(255 255 255 / .75);
  line-height: 1.35;
  /* Wraps to two lines rather than truncating. The blurb is the only thing
     telling you what a mode actually is, and "Nothing but finishing moves.
     Find the ..." cuts off exactly where it starts being useful. Clamped at
     two so a long one cannot push the card taller than its neighbours. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.rush-card-best {
  display: flex; flex-direction: column; align-items: center;
  min-width: 3.2rem;
  font-variant-numeric: tabular-nums;
}
.rush-card-best b { font-size: 1.35rem; line-height: 1; }
.rush-card-best span {
  font-size: .6rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; opacity: .75;
}


/* The "not played yet" badge. A score and the word New occupy the same slot,
   so it is sized to match the score's line box and keep the column aligned. */
.rush-new {
  font-size: .68rem; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
  opacity: .8;
}
