/* ROOFTOP QUEST — the juice.
 *
 * Three things: the score readout that lives in the HUD grid, the floating +N
 * pops, and the centre banner. src/ui/juice.js owns all three.
 *
 * ⚠ LOAD ORDER. This sheet must come AFTER assets/theme.css. theme.css ends with
 * `* { animation-duration: 1ms !important }` under prefers-reduced-motion, and
 * the only thing that beats an !important is a later !important — or, as here,
 * killing the animation outright with `animation: none`. Load this first and
 * every pop lasts one millisecond, which is to say nobody ever sees one.
 *
 * ⚠ WHERE THE SCORE SITS. assets/hud.css is ONE grid and all seven of its named
 * areas are taken. `grid-row: -1` starts the panel on the LAST line of the
 * explicit grid, which drops it into an implicit row underneath every named
 * area — the one place a new box can go without landing on a box that
 * src/selftest.js is watching. -1 counts from the end, so it still lands under
 * everything in hud.css's narrow layout, which has a fifth row. Do not "fix"
 * this to a row number: the number is different in the two layouts.
 *
 * ⚠ Anything here that JS toggles with `hidden` needs its own [hidden] rule.
 * A class that sets `display` outranks the UA's `[hidden] { display: none }`,
 * which is the same trap hud.css documents for .hud itself.
 *
 * Motion: every animation is transform and opacity only, so a pop costs the
 * compositor and nothing else. Under prefers-reduced-motion the animations are
 * switched off entirely and juice.js fades the same elements by hand — the
 * numbers stay, the movement goes.
 */

/* ========================================================== the readout */

.jc-score {
  grid-column: 1 / -1;
  grid-row: -1;
  justify-self: center;
  align-self: end;
  /* A grid item's automatic minimum is its content, so without this the pill
   * can push the grid wider than the viewport on a 320px phone. */
  min-width: 0;
  gap: var(--s-3);
  --icon-size: 15px;
}

.jc-score[hidden] { display: none; }

/* No grid to join — juice.js was handed something that is not the HUD. Pin it
 * where the grid would have put it rather than dropping it in the page flow. */
.jc-score--loose {
  position: fixed;
  left: 50%;
  bottom: var(--gutter);
  transform: translateX(-50%);
  z-index: var(--z-hud);
  pointer-events: none;
}

.jc-score .ico { color: var(--c-butter); }

.jc-score__n {
  font-size: var(--fs-lg);
  color: var(--c-butter);
  line-height: 1.1;
  /* Tabular digits stop the number shuffling sideways; the floor stops the
   * whole pill resizing — and re-centring — every time it gains a digit. */
  min-width: 6ch;
  text-align: right;
}

.jc-score__combo {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.jc-score__x {
  font-size: var(--fs-md);
  color: var(--c-green);
  min-width: 3ch;
}

.jc-score__meter {
  width: 34px;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--c-line-strong);
  overflow: hidden;
}

/* scaleX, not width, for the same reason hud.css gives for the progress rail:
 * this is written whenever the combo moves and a width write lays out the HUD. */
.jc-score__fill {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--c-cyan), var(--c-green));
  transform: scaleX(0);
  transform-origin: left center;
  will-change: transform;
}

/* The ladder climbed. */
.jc-score__combo.is-up { animation: jc-tick 420ms var(--ease-pop); }

/* The ladder went. Red, and it shakes — losing the multiplier is the only bad
 * news this readout ever has to deliver, so it should not look like good news. */
.jc-score__combo.is-break { animation: jc-break 460ms var(--ease-out); }

@keyframes jc-tick {
  0% { transform: scale(1); }
  35% { transform: scale(1.32); }
  100% { transform: scale(1); }
}

@keyframes jc-break {
  0% { transform: translateX(0); color: var(--c-pink-hot); }
  20% { transform: translateX(-5px); }
  45% { transform: translateX(4px); }
  70% { transform: translateX(-2px); }
  100% { transform: translateX(0); color: var(--c-pink-hot); }
}

.jc-score__combo.is-break .jc-score__x { color: var(--c-pink-hot); }

/* ============================================================== the pops */

.jc-pops {
  position: fixed;
  inset: 0;
  z-index: var(--z-toast);
  pointer-events: none;
  /* A pop projected to a point just off screen must not lengthen the page. */
  overflow: hidden;
}

.jc-pops[hidden] { display: none; }

/* Two nodes on purpose. The OUTER one is moved by juice.js every frame so the
 * number stays glued to the place in the world it was earned; the INNER one
 * carries the keyframes. One node cannot do both — an animation on `transform`
 * owns that property outright and would throw the world tracking away. */
.jc-pop {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate3d(-9999px, -9999px, 0);
  visibility: hidden;
}

.jc-pop__b {
  display: grid;
  justify-items: center;
  gap: 1px;
  white-space: nowrap;
  opacity: 0;
  /* ⚠ The keyframes below repeat this translate. An animation on `transform`
   * replaces the whole property, so a centring transform declared only here
   * disappears the moment the pop runs and every number jumps down-right by
   * half its own size. */
  transform: translate(-50%, -100%);
  /* 1150ms, and juice.js's POP_LIFE is 1.15 seconds. Change one, change both,
   * or the pool recycles a node that is still fading. */
  animation: jc-pop 1150ms var(--ease-pop) both;
}

.jc-pop__n {
  font-family: var(--display);
  font-weight: var(--fw-black);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tr-title);
  line-height: 1;
  font-size: var(--jc-fs, var(--fs-lg));
  color: var(--jc-col, var(--c-cream));
  /* Four offsets rather than a stroke, exactly as the countdown numeral does
   * it: these float over a hot pink sky with no card behind them. */
  text-shadow:
    0 3px 0 var(--c-ink-2),
    2px 0 0 var(--c-ink-2),
    -2px 0 0 var(--c-ink-2),
    0 -2px 0 var(--c-ink-2);
}

.jc-pop__t {
  font-size: var(--fs-3xs);
  font-weight: var(--fw-black);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--c-text);
  text-shadow: 0 1px 2px rgba(18, 14, 32, 0.9), 0 0 6px rgba(18, 14, 32, 0.7);
}

/* Bigger awards are bigger and brighter. The tiers are picked in juice.js off
 * the points, so a rebalance in src/game/score.js moves them on its own. */
.jc-pop--sm { --jc-fs: var(--fs-lg); --jc-col: var(--c-cream); }
.jc-pop--md { --jc-fs: var(--fs-xl); --jc-col: var(--c-cyan); }
.jc-pop--lg { --jc-fs: var(--fs-2xl); --jc-col: var(--c-butter); }
.jc-pop--xl { --jc-fs: clamp(2.25rem, 6vw, 3rem); --jc-col: var(--c-green); }

.jc-pop--lg .jc-pop__t { color: var(--c-butter); }
.jc-pop--xl .jc-pop__t { color: var(--c-green); }

/* Arrives too big, settles, drifts up and goes. jc-pop-b is a byte-for-byte
 * copy: juice.js flips between the two names to restart the animation on a
 * recycled node, which is the one way to restart one that does NOT need a
 * forced reflow first. */
@keyframes jc-pop {
  0% { opacity: 0; transform: translate(-50%, -96%) scale(0.45); }
  18% { opacity: 1; transform: translate(-50%, -116%) scale(1.22); }
  34% { opacity: 1; transform: translate(-50%, -124%) scale(0.97); }
  62% { opacity: 1; transform: translate(-50%, -160%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -215%) scale(0.92); }
}

@keyframes jc-pop-b {
  0% { opacity: 0; transform: translate(-50%, -96%) scale(0.45); }
  18% { opacity: 1; transform: translate(-50%, -116%) scale(1.22); }
  34% { opacity: 1; transform: translate(-50%, -124%) scale(0.97); }
  62% { opacity: 1; transform: translate(-50%, -160%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -215%) scale(0.92); }
}

/* ============================================================ the banner */

.jc-banner {
  position: fixed;
  inset: 0;
  z-index: var(--z-toast);
  display: grid;
  place-items: center;
  pointer-events: none;
  /* ⚠ Dead centre is the toast's, and 26vh above centre is where the countdown
   * numeral sits (hud.css lifts it with a 26vh bottom padding of its own). The
   * padding goes on the CONTAINER, never on the banner, because the banner's
   * own transform belongs to its animation. */
  padding-bottom: 48vh;
}

.jc-banner[hidden] { display: none; }

.jc-banner__b {
  font-family: var(--display);
  font-size: clamp(1.6rem, 5.6vw, 3.4rem);
  font-weight: var(--fw-black);
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-title);
  text-transform: uppercase;
  text-align: center;
  color: var(--c-cream);
  text-shadow:
    0 5px 0 var(--c-ink-2),
    3px 0 0 var(--c-ink-2),
    -3px 0 0 var(--c-ink-2),
    0 -3px 0 var(--c-ink-2);
  opacity: 0;
}

/* 1700ms, and juice.js's BANNER_LIFE is 1.7 seconds. Keep the two in step. */
.jc-banner__b.is-in { animation: jc-flourish 1700ms var(--ease-pop) both; }

.jc-banner--qualify { color: var(--c-green); }
.jc-banner--best { color: var(--c-butter); }
.jc-banner--combo { color: var(--c-cyan); }
.jc-banner--finish { color: var(--c-pink); }

/* The results card's own pair. src/main.js raises 'good' for QUALIFIED and
 * 'bad' for NICE TRY; without these two the banner that ends a race is the one
 * banner in the game with no colour of its own. */
.jc-banner--good { color: var(--c-green); }
.jc-banner--bad { color: var(--c-pink-hot); }

@keyframes jc-flourish {
  0% { opacity: 0; transform: scale(0.6) rotate(-3deg); }
  14% { opacity: 1; transform: scale(1.14) rotate(1.5deg); }
  26% { transform: scale(0.98) rotate(0deg); }
  72% { opacity: 1; transform: scale(1) rotate(0deg); }
  100% { opacity: 0; transform: scale(1.06) rotate(0deg); }
}

/* ============================================================ responsive */

@media (max-width: 460px) {
  .jc-score { --icon-size: 13px; }
  .jc-score__n { font-size: var(--fs-md); min-width: 5ch; }
  .jc-score__x { font-size: var(--fs-sm); }
  .jc-score__meter { width: 26px; }
  .jc-pop--sm { --jc-fs: var(--fs-md); }
  .jc-pop--md { --jc-fs: var(--fs-lg); }
  .jc-pop--lg { --jc-fs: var(--fs-xl); }
  .jc-pop--xl { --jc-fs: var(--fs-2xl); }
}

/* A landscape phone has no room to hold a banner at 26vh AND the top cards. */
@media (max-height: 460px) {
  .jc-banner { padding-bottom: 44vh; }
  .jc-banner__b { font-size: clamp(1.3rem, 5vh, 2rem); }
}

/* ======================================================= reduced motion
 *
 * The class is set by juice.js, not by the media query, because the same JS has
 * to know: with the animations off it drives the fades itself, one opacity
 * write per live pop per frame. Numbers stay, movement goes. */

.jc-still .jc-pop__b,
.jc-still.jc-banner .jc-banner__b,
.jc-still .jc-score__combo {
  animation: none !important;
}

.jc-still.jc-banner .jc-banner__b { opacity: 1; }
