/* App shell. The stage is the ENTIRE app; the header floats over the top of it the way a
   video player's controls float over the picture, rather than sitting in a row beside it.
   That is what makes the auto-hide read as fullscreen — there is no strip to vacate, because
   the waterfall was always under there.
   It is also the only version that is safe. A header that collapsed its row would grow `main`,
   which changes `waterfall.clientHeight`, and `--fall` is measured off that: every hide and
   every reveal would re-scale the fall under a run that is already in flight. Overlaying costs
   the top ~78px of the waterfall while the bar is up, and that band is the fade-in zone where
   tiles are still arriving — the cheapest 78px on the screen to cover. */
.app {
  position: relative;
  height: 100svh;
  min-width: 680px;
  display: grid;
  grid-template-rows: 1fr;
  background:
    radial-gradient(circle at 50% -25%, rgba(255, 79, 154, .30), transparent 48%),
    linear-gradient(180deg, #3b1d2d 0%, #21131b 100%);
}
/* Full-bleed: the stage goes edge to edge. The 16/18px gutter framed the waterfall like a
   card, and the whole point of the floating header is that the picture IS the window. */
main { min-height: 0; display: grid; grid-template-rows: minmax(200px, 1fr) minmax(142px, 30vh); }

.key:focus-visible, .play:focus-visible, .note:focus-visible, .load:focus-within, .icon-btn:focus-visible,
.menu-row:focus-visible {
  outline: 3px solid #ffd2e5; outline-offset: 2px;
}

@media (max-height: 520px) {
  main { grid-template-rows: minmax(120px,1fr) minmax(150px,38vh); }
  header { padding: 0 16px; }
}

/* Scoped to the decorative bursts: the fall itself carries the timing, so blanket-disabling
   animation would make every note strike at once. */
@media (prefers-reduced-motion: reduce) {
  .plume { display: none; }   /* the lit key and the tile have already said it */
  .confetti-paw, .key[data-next]::after, .streak.bump {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
  }
  /* The anticipation cue still has to be visible, just not animated. */
  .key[data-next]::after { opacity: .9; }
  header { transition: none; }
  .scale-control, .scale-track i, .scale-track { transition: none; }
}
