/* ──────────────────────────────────────────────────────────────────
   WS QUIZ — PRESENTATION (mirrors the cookie-consent module split:
   quiz-config.js = data, quiz.js = logic, this file = CSS)

   REDESIGN — monochrome pixel mini-game, screen genuinely built into
   the cabinet (not a flat color pasted over it). No discount hints
   anywhere in the cabinet; the lead form (the site's own header
   modal, see script.js's window.WSCtaModal) carries the 15% message.

   GLASS CUTOUT — assets/images/arcade-cabinet.png's glass area is now
   ACTUAL alpha transparency (edited with Pillow — rounded-rect cutout
   at image px (426,818)-(1748,1750), radius 88px, 2.5px feathered
   edge so it blends into the bezel's own groove instead of a hard
   line; re-verified corner-by-corner against the real artwork, see
   PROJECT-NOTES.md for the method). That means the SCREEN LAYER
   (`.stages__screen` idle / `#quizLayer` zoomed) now sits BEHIND the
   cabinet image in z-order/DOM order and just needs to be a plain
   rect slightly larger than the glass opening — the bezel's own
   opaque pixels do the actual rounded-corner masking now, so neither
   layer needs its own border-radius or shadow trick to "look like
   glass" any more.

   GEOMETRY SOURCE OF TRUTH — same pixel-overlay measurement method as
   before, re-measured against the edited artwork (2202×2606 image):
     glass (cutout)     : (426,818)-(1748,1750)  → aspect 1322/932 = 1.41845
     screen (this layer): glass ×1.025 both axes, same center — its
       (square, unrounded) corners always land safely behind opaque
       bezel plastic. As % of .stages__cabwrap's own box:
       left 18.5956% / top 30.9420% / width 61.5372% / height 36.6578%
     cabwrap transform-origin (glass center) : 49.3642% 49.2709%
   quiz.js's GLASS/GLASS_ASPECT/SCREEN_PAD_FACTOR constants mirror
   these — keep both in sync if the artwork is ever re-measured.
   ────────────────────────────────────────────────────────────────── */

@font-face {
  font-family: 'Press Start 2P';
  src: url('fonts/PressStart2P-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

.stages {
  position: relative;
  padding: 40px var(--pad-x) 60px;
  box-sizing: border-box;
}

.stages__viewport {
  position: relative;
  /* dvh-preferred, same reasoning as .stages__backdrop's own height
     fallback below — on mobile Safari, 100vh is the LARGER
     address-bar-hidden viewport height, not the actually-visible one;
     a width capped off that inflated number can size the cabinet
     bigger than the real visible space calls for, leaving an unlit gap
     under it once the real (shorter, address-bar-showing) viewport
     settles. 100dvh tracks the real visible height continuously. The
     100vh line stays first as a fallback for browsers without dvh
     support — declared-twice, not a shorthand, so the second wins
     wherever it's understood and the first is simply ignored there. */
  width: min(100%, calc((100vh - 160px) * 0.84536), 760px);
  width: min(100%, calc((100dvh - 160px) * 0.84536), 760px);
  aspect-ratio: 2202 / 2606;
  margin: 0 auto;
  container-type: inline-size;
}

/* pointer-events:none on the WRAPPER itself (not just the image — see
   below) is load-bearing, not cosmetic: lacking a visible background
   does NOT make a positioned element transparent to hit-testing, only
   pointer-events:none does. Without this, the portaled cabwrap (scaled
   up to roughly fill the viewport during zoom, z-index 160, set
   inline by quiz.js) swallowed every click meant for #quizLayer's
   menu/buttons underneath it (z-index 155) — found live-testing this
   pass (elementFromPoint at a menu item's own center returned
   .stages__cabwrap, not the button). .stages__screen and
   .stages__startbtn re-enable their own pointer-events below, since a
   descendant can opt back in even when an ancestor sets none. */
.stages__cabwrap {
  position: absolute;
  inset: 0;
  cursor: pointer;
  pointer-events: none;
  transform-origin: 49.3642% 49.2709%; /* the glass's own center, re-measured against the edited artwork */
  transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

/* The cabinet IMAGE — note pointer-events:none: it visually sits ON
   TOP of .stages__screen (plain DOM order, no z-index needed — see
   file header), but its own bounding box must not swallow clicks
   meant for the screen underneath (a transparent PNG pixel still
   counts as "inside the element" for hit-testing purposes, it's not
   per-pixel-alpha-aware) — letting clicks fall through to whatever's
   behind it is the one deliberate exception to "cursor:pointer on the
   whole wrapper" below. */
.stages__cabwrap picture {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.stages__cab-img {
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ─── Screen layer (idle) — BEHIND the cabinet image (DOM order: this
   div comes first, <picture> after), plain monochrome rect, no
   border-radius/shadow — the glass cutout's own rounded alpha edge is
   what gives it its shape. Hosts the idle mini pixel-game scene
   (quiz.js renders into #stagesScreenInner). */
.stages__screen {
  position: absolute;
  left: 18.5956%;
  top: 30.942%;
  width: 61.5372%;
  height: 36.6578%;
  background: #f7f7f7;
  overflow: hidden;
  /* Matches the glass cutout's own measured 88px radius (see file
     header), expressed as a % of THIS layer's own box (glass × 1.025,
     same SCREEN_PAD_FACTOR as #quizLayer below) rather than the glass
     box directly, since that's the box border-radius actually rounds.
     Without this the screen layer's own square corners could poke out
     past the bezel's opaque rounding at the BOTTOM specifically — the
     bezel ring is thinner there than at the top/sides on this artwork,
     so the same 1.025x overscan that stays safely hidden under the top
     bezel was visible at the bottom. Rounding all 4 corners is still
     correct even though only the bottom two were ever visibly wrong —
     the top two are already fully covered by bezel either way. */
  border-radius: 6.5% / 9.2%;
  cursor: pointer;
  pointer-events: auto; /* opts back in despite .stages__cabwrap's own pointer-events:none above */
}

/* Once zoomed, #quizLayer (z-index 155, see below) takes over as the
   visible screen — it sits physically at the same spot, scaled up,
   just BELOW the portaled cabwrap (z-index 160, set inline by
   quiz.js). .stages__screen is still a child of that portaled
   cabwrap, so without this its own opaque #f7f7f7 fill (and the idle
   dino/bug/text still sitting inside it) would stay ON TOP of
   #quizLayer at every size the zoom transform scales it to — found
   live-testing this pass (quizLayer's real DOM content was there and
   correctly styled, just fully hidden under this). body.is-quiz-zoomed
   is toggled by quiz.js's openZoom()/runZoomCloseCleanup(). */
body.is-quiz-zoomed .stages__screen {
  visibility: hidden;
}

/* The round START button only makes sense pre-zoom (it's what opens
   game mode) — hidden once zoomed (explicit ask, "may be hidden during
   game mode"), freeing up the glass-sizing math from needing to keep
   room for the joystick/button area of the cabinet visible too. */
body.is-quiz-zoomed .stages__startbtn {
  visibility: hidden;
}

/* Scanlines as a static 1x3px tileable PNG — see .quiz-layer::before
   for the full rationale (same exact pattern, shared file). */
.stages__screen::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url('images/quiz-scanlines.png?v=item41-fixpass5');
  background-repeat: repeat;
}

/* No longer flex-centered content — the attract-mode runner
   (.quiz__runner--idle, built by quiz.js's renderIdleScreen()) fills
   this as an absolute background layer, with the idle label floating
   on top of it (own absolute position below), so "the same runner
   loops in the background behind 'НАТИСНИ СТАРТ'" reads literally. */
.stages__screen-inner {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.stages__screen-idle-label {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 2;
  transform: translate(-50%, -50%);
  margin: 0;
  padding: 0.5em 0.9em;
  background: rgba(247, 247, 247, 0.8);
  font-family: 'Press Start 2P', monospace;
  color: #535353;
  font-size: clamp(9px, 2.6cqw, 20px);
  letter-spacing: 0.02em;
  white-space: nowrap;
  animation: stagesScreenBlink 1s steps(1, jump-none) infinite;
}

/* "done" (quiz already submitted this session) — a static info
   display, not a "press me" prompt, so no blink. */
.stages__screen-idle-label.is-done {
  animation: none;
  opacity: 1;
}

@keyframes stagesScreenBlink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* Round START button, drawn in CSS (no image asset) — unchanged, a UI
   control rather than part of the screen, so it keeps its own color. */
.stages__startbtn {
  position: absolute;
  left: 34.832%;
  top: 74.8273%;
  width: 8.1744%;
  aspect-ratio: 1;
  min-width: 56px;
  min-height: 56px;
  border: none;
  border-radius: 50%;
  padding: 0;
  pointer-events: auto; /* opts back in despite .stages__cabwrap's own pointer-events:none above */
  background: radial-gradient(circle at 34% 28%, #ff7a7a, #ff2828 48%, #b50000 100%);
  box-shadow:
    0 5px 0 #7a0000,
    0 7px 10px rgba(0, 0, 0, 0.35),
    inset 0 2px 3px rgba(255, 255, 255, 0.55);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.stages__startbtn:active {
  transform: translateY(3px);
  box-shadow:
    0 2px 0 #7a0000,
    0 3px 6px rgba(0, 0, 0, 0.3),
    inset 0 1px 2px rgba(255, 255, 255, 0.45);
}

/* Solid (not translucent) and an explicit `height` rather than relying
   on min-height alone — found this pass on a real iPhone: the page
   showed through at the bottom edge during game mode (reviews-section
   text/video button visible). `inset:0` alone should already size this
   to the full viewport, but an explicit height removes any ambiguity
   from mobile Safari's own dynamic-toolbar viewport-unit quirks rather
   than trusting inset's implicit bottom:0 to resolve the same way.
   #0b0d0d solid (not an rgba) guarantees full opacity regardless of
   any compositing edge case — no partial-transparency math left to get
   wrong. */
.stages__backdrop {
  position: fixed;
  inset: 0;
  height: 100vh;
  height: 100dvh;
  z-index: 150;
  background: #0b0d0d;
  opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
  touch-action: none;
}

.stages__backdrop.is-visible {
  opacity: 1;
  pointer-events: auto;
}

/* z-index 155 — BELOW the portaled cabwrap (160, set inline by
   quiz.js) so the cabinet image's own alpha cutout masks this layer
   during zoom exactly like .stages__screen does at idle; ABOVE the
   backdrop (150). Sized/positioned by quiz.js to the padded "screen"
   rect (glass ×1.025, see file header) — no border-radius needed,
   same reasoning as .stages__screen. */
.quiz-layer {
  position: fixed;
  z-index: 155;
  background: #f7f7f7;
  overflow: hidden;
  /* Same measured-radius clip as .stages__screen above (idle) — see
     that rule's own comment. Identical % values since this layer uses
     the same SCREEN_PAD_FACTOR (glass × 1.025) sizing, just at a
     different absolute size (quiz.js's applyZoomRects()). */
  border-radius: 6.5% / 9.2%;
  opacity: 0;
  transition: opacity 0.3s ease;
  /* "game layer" touch handling (explicit ask) — kills page scroll,
     pinch-zoom, text selection and the iOS long-press callout menu
     everywhere under game mode, not just on the canvas itself (tap-
     anywhere-to-jump now listens at the document level, see quiz.js,
     so the whole layer needs to behave like a single control, not a
     normal scrollable/selectable page area). */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

.quiz-layer.is-visible {
  opacity: 1;
}

/* Scanlines as a static 1x3px tileable PNG (explicit ask — was a live
   repeating-linear-gradient background; a bitmap tile composites more
   cheaply than a procedurally-evaluated gradient on a mobile GPU, part
   of this pass's "freezes on the phone" fix) — generated to the exact
   same rgba(83,83,83,0.025)-on-transparent, 1px-dark/2px-clear, 3px-tall
   pattern the gradient drew, not just a close approximation. */
.quiz-layer::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url('images/quiz-scanlines.png?v=item41-fixpass5');
  background-repeat: repeat;
}

/* Two top-level panels share this box, never both visible at once —
   .quiz__screens (mission/level select, unchanged full-glass layout)
   and .quiz__gamebox (countdown/playing/gameover/win — the canvas
   always takes the full glass, contain-fitting its own 600x150 world
   internally; countdown/gameover/win are DOM overlays pinned to its
   upper half, see .quiz__overlay below). `.is-game-mode` (quiz.js's
   showScreen()) switches which one is `display`-visible — a hard
   switch, not opacity, so there's never a frame where both are
   simultaneously laid out. No padding here any more (moved onto
   .quiz__screen itself) — the gamebox's own 6% inset is an
   engine.js/canvas-math concept, not a DOM one. */
.quiz-layer__inner {
  position: absolute;
  inset: 0;
  color: #535353;
  text-align: center;
}

/* No CSS transitions/animations on the cabinet or this layer while a
   run is actually live (explicit ask) — body.is-quiz-playing is
   toggled alongside #quizInner's own is-game-mode, see quiz.js's
   showScreen(); covers the whole countdown/playing/gameover/win
   stretch, not just 'playing' specifically, since any of those could
   in principle be reached by a resize/orientation event re-triggering
   a transform recompute mid-run. Stronger than just not CHANGING these
   properties — this guarantees the engine never has to pay for a
   transition even if something unexpected re-triggers one. */
body.is-quiz-playing .stages__cabwrap,
body.is-quiz-playing .quiz-layer {
  transition: none !important;
}

/* Lead modal opened FROM the quiz (window.WSCtaModal.open({quiz:...}),
   see quiz.js's openQuizModal()/onQuizModalClosed()) — body.is-
   quiz-modal-open is toggled for exactly that window. The modal's own
   z-index (2000, style.css) was already numerically above the cabinet
   (160)/quiz-layer (155)/backdrop (150), so paint order was never
   actually wrong — the real bug: .modal's frosted-glass look is
   `background: rgba(184,184,184,.4); backdrop-filter: blur(50px);`,
   which SAMPLES whatever sits behind it. Behind the header-opened
   modal that's ordinary light page content; behind the quiz-opened one
   it's #stagesBackdrop (near-black) + the cabinet art, which a
   blur that fails to apply (GPU/compositor-dependent — backdrop-filter
   has no reliable fallback) leaves as a flat dark tint straight through
   the translucent card, reading as "grey and washed out" even though
   stacking is correct. Fixing the blur's reliability isn't under our
   control; removing what it would sample is — fade the three quiz
   layers out while the modal is open so the card always composites
   over nothing but the modal's own backdrop, identical to the header
   path, on every browser/GPU regardless of backdrop-filter support.
   opacity, not display/hidden, so the layers resume exactly where
   they left off if the modal is dismissed without submitting
   (onQuizModalClosed's own showScreen() still runs under here, just
   invisible until the class comes off). No transition on this one:
   the modal is opened/closed from 'win'/'gameover', where body still
   carries is-quiz-playing too (game mode isn't exited until a real
   exit, separately) — that rule's own `transition: none !important`
   on these same two selectors would just fight a transition added
   here, so this snaps instantly rather than racing it; the modal's
   OWN backdrop still fades in over its usual 0.25s (style.css), which
   is the fade the user actually sees. */
body.is-quiz-modal-open #stagesBackdrop,
body.is-quiz-modal-open .stages__cabwrap,
body.is-quiz-modal-open .quiz-layer {
  opacity: 0;
}

/* Question/options block — takes the remaining space ABOVE the
   runner strip and centers itself within it (not the whole glass any
   more, since the runner strip below is a fixed ~20% height, not part
   of this flexible center). */
.quiz__content {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  overflow-y: auto;
}

.quiz__eyebrow {
  margin: 0;
  font-family: 'Press Start 2P', monospace;
  font-size: 11px;
  letter-spacing: 0.02em;
  color: #8a8a8a;
  flex-shrink: 0;
}

/* Two spans (arrow + text, see quiz.js's buildBackButton()) in a flex
   row sharing one baseline — a single text string mixing the "◀" glyph
   with Cyrillic text let the font's own glyph metrics for that symbol
   sit noticeably off relative to the text at this font-size (Press
   Start 2P), found from a real screenshot. */
.quiz__back {
  align-self: flex-start;
  margin: 0;
  padding: 4px;
  min-height: 26px;
  display: flex;
  flex-direction: row;
  align-items: baseline;
  gap: 0.4em;
  border: none;
  background: none;
  color: #8a8a8a;
  font-family: 'Press Start 2P', monospace;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}

.quiz__back-arrow,
.quiz__back-text {
  display: inline-block;
  line-height: 1;
}

.quiz__back:hover,
.quiz__back:focus-visible {
  color: #535353;
}

.quiz__question {
  margin: 0;
  font-family: 'Press Start 2P', monospace;
  font-size: 12px;
  line-height: 1.35;
  max-width: 100%;
  flex-shrink: 0;
  color: #535353;
}

/* Answers — 2-column grid by default (fits 4 items as 2×2, 6 items as
   2×3, and 3 items as 2+1 acceptably); quiz.js adds .quiz__menu--list
   on desktop when a 1-column list measures as fitting, or
   .quiz__menu--grid3 for the 6-option budget step when a 3-column
   layout measures as fitting and the list didn't. Game-menu feel: a
   "▶" marker (hidden until hover/focus/selected) rather than a heavy
   form-field outline; selected/pressed = inverted (graphite fill,
   light text) — monochrome only, no accent color anywhere in here. */
.quiz__menu {
  width: 100%;
  max-width: 460px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: 1fr;
  gap: 4px;
  margin: 0;
}

.quiz__menu.quiz__menu--list {
  display: flex;
  flex-direction: column;
}

.quiz__menu.quiz__menu--grid3 {
  grid-template-columns: repeat(3, 1fr);
}

.quiz__menu-item {
  position: relative;
  /* Shrunk from an earlier 56px: the zoom-sizing fix (whole cabinet
     visible, see computeTarget()) made the glass itself noticeably
     smaller than before (prioritizing "cabinet not clipped" over a
     bigger glass — see quiz.js's own comment on that trade-off), and
     the old fixed sizes no longer fit the 6-option level grid without
     overflowing past the glass into the canvas strip / off the top.
     34px (+ smaller padding/font below) was measured to fit live at a
     typical desktop viewport — see the ?quiz-debug=1 FITS IN GLASS
     readout, re-added this pass specifically to verify this. */
  min-height: 30px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3px 10px;
  background: #fff;
  /* 1.5px border + up to 2px outline-equivalent box-shadow below keep
     every cell's LAYOUT BOX identical in every state — the previous
     .is-active used transform:scale(0.97), which visibly shrank the
     selected cell relative to its siblings (found from a real
     screenshot: "the selected item is a different size"). The
     box-shadow is drawn OUTSIDE the border box (not inset), so it adds
     a visible ring without affecting size/layout at all. */
  border: 1.5px solid rgba(83, 83, 83, 0.35);
  color: #535353;
  font-family: var(--font-body, sans-serif);
  font-weight: 600;
  font-size: 11px;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}

.quiz__menu-item::before {
  content: '▶';
  position: absolute;
  left: 8px;
  top: 50%;
  transform: translateY(-50%) scale(0.8);
  color: #535353;
  font-size: 0.75em;
  opacity: 0;
  transition: opacity 0.12s ease, transform 0.12s ease;
  pointer-events: none;
}

.quiz__menu-item:hover::before,
.quiz__menu-item:focus-visible::before,
.quiz__menu-item.is-active::before {
  opacity: 1;
  transform: translateY(-50%) scale(1);
}

.quiz__menu-item:hover,
.quiz__menu-item:focus-visible {
  background: #ececec;
  border-color: #535353;
}

.quiz__menu-item.is-active,
.quiz__menu-item:active {
  background: #535353;
  border-color: #535353;
  color: #fff;
  box-shadow: 0 0 0 2px rgba(83, 83, 83, 0.35);
}

.quiz__menu-item.is-active::before,
.quiz__menu-item:active::before {
  color: #fff;
}

/* ─── Game canvas — ONE <canvas> per engine instance (see
   assets/game/engine.js), replacing the previous SVG-<rect>-per-pixel
   runner entirely (see PROJECT-NOTES.md for the measured stutter that
   caused: ~1,600 DOM nodes per mounted instance, rebuilt from scratch
   on every screen change). The canvas element itself needs almost no
   styling — sizing/devicePixelRatio/imageSmoothingEnabled are all set
   by the engine in JS (measure() — a real layout read, but only on
   init + debounced resize, never per frame). */
.quiz__gamecanvas-host {
  position: absolute;
  inset: 0;
  overflow: hidden;
  touch-action: none; /* kills scroll/pinch-zoom entirely (explicit ask) — tap-anywhere-to-jump is handled at the document level, see quiz.js */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Idle "attract mode" — same canvas/engine, filling the small idle
   screen as a background layer behind "НАТИСНИ СТАРТ". */
.quiz__gamecanvas-host--idle {
  position: absolute;
  inset: 0;
}

.quiz__gamecanvas {
  display: block;
  width: 100%;
  height: 100%;
  /* touch-action isn't inherited — setting it only on the host div left
     the canvas ELEMENT itself (the actual tap target returned by
     elementFromPoint/hit-testing) with the browser default, which on
     some mobile browsers is enough to let a tap start a scroll/gesture
     attempt instead of cleanly firing pointerdown for the jump input.
     Repeating it here closes that gap. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
}

/* ─── Select screens (mission/level) — lightweight DOM built ONCE
   (quiz.js's buildGameScreens()), swapped via opacity only (never
   innerHTML-rebuilt) via the .is-active class below. Always the full
   glass, unchanged from before this pass. ─── */
.quiz__screens {
  position: absolute;
  inset: 0;
  display: flex;
  transition: opacity 0.2s ease;
}

/* Padding lives HERE, not on .quiz__screens (found this pass — the
   real "menus overflow the glass on mobile" root cause): a
   position:absolute child with inset:0 is sized against its
   containing block's PADDING box, which INCLUDES the ancestor's own
   padding area — so a padding declared on .quiz__screens had ZERO
   effect on this child's actual rendered bounds, full stop, on any
   viewport size (fixed px paddings just happened to be small enough
   relative to a roomy desktop glass to go unnoticed; on a much
   narrower mobile glass the option grid and "◀ Назад" genuinely
   touched/crossed the bezel edge). Percentage-based (not fixed px) so
   it scales with the glass at every size, and generous enough (7%,
   over the explicit >=6% ask) to also clear the glass cutout's own
   ROUNDED corners, not just its flat edges. */
.quiz__screen {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  padding: 7%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.quiz__screen.is-active {
  opacity: 1;
  pointer-events: auto;
}

/* ─── Game box (countdown/playing/gameover/win) — the canvas always
   takes the full box (engine.js contain-fits the 600x150 world into
   it, 6% inner padding, never cropped — see engine.js header). Hidden
   entirely while a select screen is active; shown via
   .quiz-layer__inner.is-game-mode (quiz.js's showScreen()). ─── */
.quiz__gamebox {
  position: absolute;
  inset: 0;
  display: none;
}
.quiz-layer__inner.is-game-mode .quiz__screens { display: none; }
.quiz-layer__inner.is-game-mode .quiz__gamebox { display: block; }

/* A countdown/gameover/win overlay — pinned to the UPPER HALF of the
   game box only, so the ground + dino stay visible below it at all
   times (explicit ask). Hard show/hide via the `hidden` IDL property
   (quiz.js's showScreen()) — display:none, not an opacity fade — so a
   screen can never be left half-visible behind the next one (a real
   "GAME OVER ghost stays visible on the next run" bug from a real
   screenshot). The [hidden]/:not([hidden]) pair below is deliberately
   higher-specificity than the bare class so this can't accidentally
   get overridden by ordering. */
.quiz__overlay {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 50%;
  box-sizing: border-box;
  padding: 8px 7%;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  pointer-events: none; /* individual controls (buttons) opt back in below */
}
.quiz__overlay[hidden] { display: none !important; }
.quiz__overlay:not([hidden]) { display: flex; }
.quiz__overlay button { pointer-events: auto; }

.quiz__question--arcade {
  margin-bottom: 4px;
}

.quiz__countdown-label {
  margin: 0;
  font-family: 'Press Start 2P', monospace;
  font-size: 13px;
  color: #8a8a8a;
}
.quiz__countdown-number {
  margin: 0;
  font-family: 'Press Start 2P', monospace;
  font-size: 48px;
  color: #535353;
}

/* Tap hint — shown over the running game for its first ~2s. Not a
   .quiz__overlay (it has no buttons and isn't mutually exclusive with
   the canvas being fully visible), just its own small opacity toggle,
   positioned in the game box's upper area. */
.quiz__tap-hint {
  position: absolute;
  left: 50%;
  top: 14px;
  transform: translateX(-50%);
  z-index: 3;
  margin: 0;
  padding: 0.5em 0.9em;
  background: rgba(247, 247, 247, 0.85);
  font-family: 'Press Start 2P', monospace;
  font-size: 10px;
  color: #535353;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
.quiz__tap-hint.is-active {
  opacity: 1;
}

/* ─── GAME OVER / YOU WIN — fills the GLASS (corrected from an earlier
   pass of this redesign, which wrongly went full-viewport). A plain
   child of .quiz__gamebox (`position:absolute;inset:0`), so it
   inherits the exact glass rect AND the rounded-corner clip for free
   from the same ancestor chain the canvas itself uses (.quiz-layer's
   own border-radius + overflow:hidden, see above) — no separate
   positioning/clip math needed here. Own opaque #f7f7f7 background +
   its own scanlines ::before (identical pattern/asset to
   .quiz-layer::before) so it fully REPLACES the canvas visually —
   nothing of the frozen scene shows through. z-index above the
   canvas/tap-hint/countdown (tap-hint explicitly sets z-index:3, so
   this needs its own explicit value to reliably paint over it
   regardless of DOM order) — still only relative to its OWN stacking
   context inside the glass, nothing to do with the cabinet bezel/dark
   backdrop/"Пропустити", which live entirely outside .quiz-layer and
   stay exactly as during normal play. */
.quiz-endscreen {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: #f7f7f7;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 7%;
  box-sizing: border-box;
  text-align: center;
}
.quiz-endscreen[hidden] { display: none !important; }
.quiz-endscreen::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url('images/quiz-scanlines.png?v=item41-fixpass5');
  background-repeat: repeat;
}

/* Fireworks canvas (WIN only) — absolutely positioned FIRST child; the
   content wrapper right after it is also a positioned element
   (position:relative), so it paints above the canvas purely from DOM
   order within the positioned-elements paint order — no z-index
   needed. Pre-sized to the glass (its own parent's box) once at build
   time, not per-win — see quiz.js's setupFireworks(). */
.quiz-endscreen__fireworks {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
/* width:100% here is the actual fix for the clipped-button bug: without
   an explicit width, this is a flex item of .quiz-endscreen whose
   align-items:center means NO stretch — its width becomes shrink-to-fit
   (content-based), which is circular for a CHILD button whose own
   width is a PERCENTAGE (min(320px,80%)): a percentage against an
   indeterminate/content-based containing block resolves unpredictably
   (observed: collapses to a few px, clipping "Отримати знижку" against
   the glass's own overflow:hidden). width:100% makes this wrapper's
   width a deterministic value (.quiz-endscreen's own content-box width,
   after its 7% padding) BEFORE any child percentage is resolved, so
   .quiz__overlay-btn's 80% now means something real. */
.quiz-endscreen__content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: 90%;
}

/* GAME OVER's one-shot entrance shake (of the glass content only, not
   the cabinet) — restarted via quiz.js's restartShake() (remove class,
   force reflow, re-add) so a second crash in a row re-triggers it.
   200ms, explicit ask. Killed outright under reduced-motion, see that
   media block further down — not just left to no-op via `both`/short
   duration, since even a brief shake is exactly the kind of motion
   that preference asks to skip entirely. */
@keyframes quizEndscreenShake {
  10%, 90% { transform: translateX(-1px); }
  20%, 80% { transform: translateX(2px); }
  30%, 50%, 70% { transform: translateX(-4px); }
  40%, 60% { transform: translateX(4px); }
}
.quiz-endscreen--shake { animation: quizEndscreenShake 0.2s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; }

.quiz__win-heading {
  margin: 0;
  font-family: 'Press Start 2P', monospace;
  font-size: 16px;
  color: #535353;
}

.quiz__win-score {
  margin: 0;
  font-family: 'Press Start 2P', monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: #8a8a8a;
}

.quiz__win-sub {
  margin: 0;
  font-family: var(--font-body, sans-serif);
  font-size: 12px;
  color: #8a8a8a;
}

/* GAME OVER's two buttons and WIN's one button all share this class —
   a FIXED width (not content-driven padding), "same component as menu
   options", so "▶ Грати ще" and "Залишити заявку" are pixel-identical
   widths regardless of their own text length. min(320px, 80%) — NOT a
   direct descendant of .quiz-endscreen (it's inside
   .quiz-endscreen__content), so this 80% only resolves correctly
   because that wrapper now has width:100% (see its own comment) —
   without that, 80% was resolving against a content-based/indeterminate
   containing block and collapsing the button to a few px, clipping
   "Отримати знижку" against the glass's own overflow:hidden (real bug,
   fixed this pass). Padding is HORIZONTAL ONLY (0 24px) — vertical
   centering is done by flex (align-items/justify-content below), not
   padding, so min-height:56px is honored exactly regardless of font
   metrics, and nothing here or on an ancestor up to
   .quiz-endscreen__content clips overflow, so nowrap text is always
   fully visible. ≥56px tall (explicit ask, up from a previous pass's
   34px — these are real tap targets on an end screen, not a compact
   in-glass overlay any more). Stacked via the screen's own flex `gap`
   (12px) — no per-button margin-top needed. */
.quiz__overlay-btn {
  width: min(320px, 80%);
  min-height: 56px;
  padding: 0 24px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: visible;
  border: 1.5px solid transparent;
  font-family: var(--font-body, sans-serif);
  font-weight: 700;
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.12s ease, transform 0.08s ease, border-color 0.12s ease;
}
.quiz__overlay-btn:active { transform: scale(0.97); }
.quiz__overlay-btn:disabled {
  opacity: 0.5;
  cursor: default;
  transform: none;
}

.quiz__overlay-btn--primary {
  background: #535353;
  border-color: #535353;
  color: #fff;
}
.quiz__overlay-btn--primary:hover { background: #3c3c3c; border-color: #3c3c3c; }

.quiz__overlay-btn--ghost {
  background: none;
  border-color: rgba(83, 83, 83, 0.4);
  color: #535353;
}
.quiz__overlay-btn--ghost:hover { background: rgba(83, 83, 83, 0.06); }

/* ===================== Skip button — fixed, game-mode only ========= */
.stages__skip {
  position: fixed;
  left: 50%;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 180;
  min-height: 44px;
  padding: 10px 22px;
  border: 1.5px solid rgba(255, 255, 255, 0.55);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.25);
  color: #fff;
  font-family: var(--font-body, sans-serif);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.3s ease, background 0.15s ease;
}

.stages__skip.is-visible {
  opacity: 1;
}

.stages__skip:hover,
.stages__skip:focus-visible {
  background: rgba(0, 0, 0, 0.4);
}

/* ===================== Admin off-switch fallback =================== */
.stages__disabled {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 60px var(--pad-x);
  text-align: center;
}
.stages__disabled-heading {
  margin: 0;
  font-family: var(--font-display, sans-serif);
  font-size: 28px;
  color: #1a1a1a;
}
.stages__disabled-btn {
  min-height: 52px;
  padding: 12px 32px;
  border: none;
  border-radius: 10px;
  background: var(--color-accent, #ff2828);
  color: #fff;
  font-family: var(--font-body, sans-serif);
  font-weight: 700;
  font-size: 15px;
  cursor: pointer;
  transition: background 0.12s ease;
}
.stages__disabled-btn:hover { background: #cc0000; }

/* ===================== DESKTOP — roomier text ===================== */
@media (min-width: 560px) {
  /* Glass height is viewport-height-driven (computeTarget(), now ≈60%
     of viewport height on desktop — a real jump up from a previous
     pass's ~30%), not width-driven, so these bumps are re-verified
     against the live FITS IN GLASS debug readout for the 6-option
     level grid specifically (the tightest-fitting screen) rather than
     carried over from any earlier sizing assumption. */
  .quiz__eyebrow { font-size: 11px; }
  .quiz__question { font-size: 15px; }
  .quiz__menu-item { font-size: 13px; min-height: 36px; }
  .quiz__back { font-size: 11px; }
  .quiz__win-heading { font-size: 18px; }
  .quiz__win-sub { font-size: 13px; }
}

@media (min-width: 900px) {
  .quiz__eyebrow { font-size: 12px; }
  .quiz__question { font-size: 16px; }
  .quiz__menu-item { font-size: 14px; min-height: 40px; }
  .quiz__back { font-size: 12px; }
  .quiz__win-heading { font-size: 20px; }
}

/* Short landscape (phones rotated) — matches the previous build's own
   fallback: 4(or more)-across uses the available width instead of
   stacking short rows. */
@media (orientation: landscape) and (max-height: 420px) {
  .quiz__menu:not(.quiz__menu--list) {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* The canvas engine checks prefers-reduced-motion itself and never
   starts its rAF loop at all in that case (one static frame only) —
   quiz.js also skips the real game entirely when reduced motion is on
   (level select goes straight to YOU WIN). This block only needs to
   cover the handful of plain CSS transitions/animations still left
   outside the engine's own control. */
@media (prefers-reduced-motion: reduce) {
  .stages__cabwrap,
  .stages__backdrop,
  .quiz-layer,
  .stages__startbtn,
  .stages__skip,
  .quiz__menu-item,
  .quiz__overlay-btn,
  .quiz__screen,
  .quiz__tap-hint {
    transition: none !important;
  }
  .stages__screen-idle-label {
    animation: none !important;
  }
  .quiz-endscreen--shake {
    animation: none !important;
  }
}

