  @font-face {
    font-family: 'Nunito Game';
    font-weight: 800;
    font-style: normal;
    font-display: swap;
    src: url('assets/fonts/nunito-800-cyrillic.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
  }
  @font-face {
    font-family: 'Nunito Game';
    font-weight: 800;
    font-style: normal;
    font-display: swap;
    src: url('assets/fonts/nunito-800-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+2000-206F, U+20AC, U+2122;
  }

  :root {
    --bg-fallback: #d7ebe0;
    --bowl: #d98a3d;
    --bowl-dark: #b56b28;
    --ink: #2e3b2f;
    --card: #fdf8ee;
    --accent: #e8622c;
    --accent-soft: #f4a259;
    --life-lost: #d9534f;
  }

  * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

  html, body {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    /* dvh reflects the actually-visible area on mobile (address bar
       included), unlike vh which many mobile browsers size against the
       LARGEST possible viewport -- without this, bottom-anchored elements
       (the bowl) end up positioned below the real fold, unreachable since
       overflow is hidden and there's nothing to scroll to reveal them.
       Declared after the vh fallback on purpose: browsers that don't know
       dvh ignore that line and keep the vh value above. */
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
    background: var(--bg-fallback) url('assets/bg-room.jpg') center center / cover no-repeat;
    font-family: 'Trebuchet MS', 'Segoe UI', sans-serif;
    touch-action: none;
    user-select: none;
  }

  #game-wrap {
    position: relative;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    /* Illustrated pet-room scene (dog bed, toy box, wall art) -- replaces
       the earlier flat mint wash + CSS gradient patch now that the image
       itself carries real depth (wall, floor, perspective), so the
       artificial light/floor gradients aren't needed on top of it anymore.
       Busy detail (bed, toy box, shelves) sits at the left/right edges by
       design, with a clean open column down the middle where items
       actually fall -- crops gracefully on narrow phones the same way the
       old pattern did, just with an actual room instead of a plain wash. */
    background: var(--bg-fallback) url('assets/bg-room.jpg') center center / cover no-repeat;
    overflow: hidden;
  }

  canvas {
    display: block;
    width: 100%;
    height: 100%;
  }

  #hud {
    position: absolute;
    top: 0; left: 0; right: 0;
    display: flex;
    justify-content: space-between;
    /* flex-start, not center: score-stack (score + medal chip stacked)
       and lives-box have different heights, and #pause-btn/#sound-toggle
       below lives-box are positioned with a fixed top offset independent
       of this row -- center-aligning let any height change on one side
       (like the medal chip getting taller) silently push lives-box down
       and into the pause/sound buttons beneath it. Flush-top keeps both
       sides pinned to the same starting line regardless. */
    align-items: flex-start;
    padding: 14px 16px;
    pointer-events: none;
    z-index: 5;
  }

  #score-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 5px;
  }

  /* Full illustrated capsule per tier again (own rounded ends baked in on
     both sides this time, real alpha transparency, no black-background
     extraction needed) -- swapped via JS (renderMedalGoal/MEDAL_BG_IMG).
     Source art is 480x160 (3:1) -- aspect-ratio below derives width from
     height automatically so it's IMPOSSIBLE for this to end up squashed
     again the way the first attempt did (that one had width/height picked
     by hand without checking them against the image's own ratio). */
  #medal-goal {
    display: flex;
    align-items: center;
    height: 40px;
    aspect-ratio: 480 / 160;
    padding: 0 8px 0 42px;
    background-image: url('assets/ui/medal-bronze.png');
    background-repeat: no-repeat;
    background-size: 100% 100%;
    /* no box-shadow -- the pill art already has its own baked-in shadow/
       dimension (same reasoning #score-box uses below), a CSS shadow on
       top of that just doubled up into a visible hard-edged stripe under
       the chip. */
    box-shadow: none;
    font-weight: 800;
    font-size: 12px;
    color: var(--ink);
    white-space: nowrap;
  }

  #score-box {
    width: 118px;
    height: 42px;
    box-sizing: border-box;
    padding: 0 12px 0 47px;
    background: url('assets/ui/score-panel.png') center / 100% 100% no-repeat;
    border-radius: 0;
    font-weight: 800;
    font-size: 20px;
    color: var(--ink);
    box-shadow: none;
    display: flex;
    align-items: center;
    /* was center -- centered the number in the leftover space after the
       icon padding, which reads as drifted away from the bone icon.
       flex-start keeps it right after the icon instead. */
    justify-content: flex-start;
  }

  #score-box img { display: none; }

  #score-box span {
    color: #ef4b55;
    line-height: 1;
  }

  #lives-box {
    display: flex;
    gap: 4px;
    background: var(--card);
    border-radius: 14px;
    padding: 8px 12px;
    box-shadow: 0 3px 0 rgba(0,0,0,0.08);
  }

  .life-paw {
    width: 26px;
    height: 26px;
    object-fit: contain;
    display: block;
    transform: scale(1);
    transition: transform 0.18s ease, filter 0.18s ease;
  }
  .life-paw.hit {
    transform: scale(1.28);
    filter: drop-shadow(0 0 5px rgba(255, 82, 35, 0.8));
    animation: life-hit-pop 0.36s ease-out;
  }
  @keyframes life-hit-pop {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.38); }
    100% { transform: scale(1.08); }
  }

  #streak-box {
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 7;
    background: var(--card);
    border-radius: 14px;
    padding: 6px 14px;
    font-weight: 700;
    font-size: 15px;
    color: var(--accent);
    box-shadow: 0 3px 0 rgba(0,0,0,0.08);
  }
  #streak-box.hidden {
    display: none;
  }
  #streak-box.pop {
    animation: streak-pop 0.35s ease;
  }
  @keyframes streak-pop {
    0% { transform: translateX(-50%) scale(1); }
    40% { transform: translateX(-50%) scale(1.35); }
    100% { transform: translateX(-50%) scale(1); }
  }
  #fever-bar-wrap {
    position: absolute;
    top: 40px;
    left: 50%;
    transform: translateX(-50%);
    width: 110px;
    height: 14px;
    background: rgba(0,0,0,0.22);
    border: 1px solid rgba(255,255,255,0.55);
    border-radius: 4px;
    overflow: hidden;
    z-index: 7;
  }
  #fever-bar-wrap.hidden {
    display: none;
  }
  #fever-bar-fill {
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg, #8bdb62, #4caf50);
  }
  #fever-bar-wrap.active {
    animation: fever-bar-pulse 0.5s ease-in-out infinite;
  }
  @keyframes fever-bar-pulse {
    0%, 100% { box-shadow: 0 0 0px rgba(76,175,80,0.7); }
    50% { box-shadow: 0 0 10px rgba(76,175,80,0.9); }
  }
  #fever-announce {
    position: absolute;
    top: 38%;
    left: 50%;
    font-size: 42px;
    font-weight: 800;
    color: #fff;
    text-shadow: 0 4px 0 rgba(0,0,0,0.25), 0 0 24px rgba(255,180,60,0.9);
    z-index: 12;
    pointer-events: none;
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.4);
  }
  #fever-announce.show {
    animation: fever-announce-pop 1.6s ease forwards;
  }
  @keyframes fever-announce-pop {
    0% { opacity: 0; transform: translate(-50%, -50%) scale(0.4); }
    15% { opacity: 1; transform: translate(-50%, -50%) scale(1.15); }
    25% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    80% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
  }

  #event-announce {
    position: absolute;
    top: 55%;
    left: 50%;
    font-size: 26px;
    font-weight: 800;
    color: #fff;
    text-shadow: 0 3px 0 rgba(0,0,0,0.25), 0 0 16px rgba(80,140,255,0.85);
    z-index: 11;
    pointer-events: none;
    opacity: 0;
    text-align: center;
    transform: translate(-50%, -50%) scale(0.4);
  }
  #event-announce.show {
    animation: fever-announce-pop 1.6s ease forwards;
  }

  /* image-based HUD controls: same visual language as the life paws. */
  .ui-icon-btn {
    border: 0;
    background: transparent;
    box-shadow: none;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    -webkit-tap-highlight-color: transparent;
  }
  .ui-icon-btn img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
    pointer-events: none;
    user-select: none;
  }
  .ui-icon-btn:active {
    transform: translateY(2px) scale(0.94);
  }

  #sound-toggle {
    position: absolute;
    top: 64px;
    right: 30px;
    z-index: 6;
    width: 38px;
    height: 38px;
  }
  #pause-btn {
    position: absolute;
    top: 64px;
    right: 74px;
    z-index: 6;
    width: 38px;
    height: 38px;
  }
  #pause-btn.hidden { display: none; }

  .sound-btn .sound-icon-off { display: none; }
  .sound-btn.off .sound-icon-on { display: none; }
  .sound-btn.off .sound-icon-off { display: block; }

  #pause-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    background: rgba(46, 59, 47, 0.55);
    backdrop-filter: blur(2px);
    z-index: 9;
    padding: 24px;
  }
  #pause-overlay.hidden {
    display: none;
  }

  #overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    z-index: 10;
    padding: 24px;
    overflow: hidden;
    background: #2f86d6;
  }
  /* menu screen (hero banner + "how to play" toggle + Play) can be taller
     than a short phone's viewport, or the accordion can get expanded on
     one -- scroll instead of clipping anything. justify-content stays
     flex-start on the *container*; #menu-screen itself gets margin:auto
     below instead, which is the standard flexbox trick for "centered when
     it fits, flush to the top instead of clipped off-screen once it
     doesn't" -- plain justify-content:center would centre it even while
     overflowing, pushing the top of the content above the scrollable area
     where it can't be scrolled back into view. */
  #overlay:not(.game-over) {
    overflow-y: auto;
    justify-content: flex-start;
  }
  #overlay:not(.game-over) #menu-screen {
    margin: auto 0;
  }

  #overlay.hidden { display: none; }

  .panel {
    position: relative;
    overflow: visible;
    background: var(--card);
    border-radius: 22px;
    padding: 28px 26px;
    max-width: 320px;
    width: 100%;
    box-shadow: 0 8px 0 rgba(0,0,0,0.15);
  }

  .panel h1 {
    margin: 0 0 6px;
    font-size: 26px;
    color: var(--ink);
    letter-spacing: -0.5px;
  }

  .panel p {
    margin: 0 0 14px;
    color: #2a1d10;
    font-size: 14px;
    line-height: 1.5;
    text-shadow: 0 1px 0 rgba(255,255,255,0.45);
  }

  .big-score {
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    font-size: 42px;
    font-weight: 800;
    color: var(--accent);
    margin: 4px 0 18px;
  }

  /* --- start screen, v4: playtesters (3 adults) didn't know what to catch
     vs. avoid. Earlier attempts missed: the original text explanation
     lived in .panel, display:none on the menu screen, so it was never
     actually visible; a glance-strip overlaid on the hero art didn't read
     well; a permanently-open "how to play" card read as a second logo /
     too busy for a returning player; a first-run-only two-screen sequence
     worked but added a step. This version is the simplest of the four: one
     screen, always -- hero art, a small "HOW TO PLAY" toggle button, Play.
     Tapping the toggle expands/collapses the catch/avoid rows in place
     (#howto-panel's grid-template-rows 0fr/1fr trick -- animates smoothly
     without needing JS to measure content height). Collapsed by default,
     so a returning player sees a short, calm screen; still one tap away
     from the rules for anyone (first-time or not) who wants them. */
  #menu-screen {
    display: none;
    width: 100%;
    max-width: 460px;
    margin: 0 auto;
    flex-direction: column;
    align-items: stretch;
    gap: 14px;
  }
  #overlay:not(.game-over) #menu-screen { display: flex; }

  #start-bg {
    width: 100%;
    aspect-ratio: 1536 / 784;
    background-image: url('assets/ui/start-bg.jpg');
    background-size: cover;
    background-position: center;
    border-radius: 18px;
    box-shadow: 0 6px 0 rgba(0,0,0,0.15);
  }

  /* real sound toggle, top-right corner -- not tied to the art at all.
     position:fixed (not absolute) specifically because #overlay scrolls on
     the menu screen (see below) -- absolute would scroll away with the
     content instead of staying put. */
  .start-sound-btn {
    position: fixed;
    top: 14px;
    right: 14px;
    width: 52px;
    height: 52px;
    z-index: 2;
  }
  .start-sound-btn:active { transform: translateY(2px) scale(0.94); }

  #overlay.game-over .start-sound-btn { display: none; }

  /* --- "HOW TO PLAY" accordion: real icons (the same PNGs the falling
     items themselves use), grouped into a green "catch" row and red
     "avoid" row. Chocolate/grapes are deliberately included in the avoid
     row (not just generic junk like the sock) since they look like food,
     which is the actual trap. Single row each (flex-nowrap, icons share
     the width evenly) instead of wrapping -- 7-8 small icons read fine in
     one line even on a phone. No per-icon card background either -- the
     sprites are contrasty enough on their own, an extra white square
     around each one was just adding bulk. */
  #howto-card {
    background: var(--card);
    border-radius: 18px;
    box-shadow: 0 6px 0 rgba(0,0,0,0.15);
    overflow: hidden;
  }
  #howto-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    background: none;
    border: none;
    /* the generic button{} rule further down sets a brown box-shadow +
       14px border-radius meant for the old default button look -- every
       other real button on the page overrides both via its own class
       (.btn-cta, .btn-secondary, .ui-icon-btn), but this one didn't have
       one, so the brown shadow was quietly bleeding through underneath
       the toggle. */
    box-shadow: none;
    border-radius: 0;
    padding: 13px 14px;
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    font-size: clamp(16px, 4.4vw, 19px);
    font-weight: 800;
    letter-spacing: 0.3px;
    color: var(--ink);
    cursor: pointer;
  }
  #howto-toggle:active { background: rgba(0,0,0,0.04); }
  .howto-toggle-arrow {
    font-size: 14px;
    transition: transform 0.25s ease;
  }
  #howto-toggle[aria-expanded="true"] .howto-toggle-arrow { transform: rotate(180deg); }

  /* grid-template-rows 0fr -> 1fr is what actually animates the open/close
     -- no JS height measurement needed, and it degrades gracefully (just
     snaps instead of sliding) on the rare browser that doesn't support
     animating grid-template-rows. */
  #howto-panel {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.28s ease;
  }
  #howto-panel.expanded { grid-template-rows: 1fr; }
  .howto-panel-inner {
    overflow: hidden;
    min-height: 0;
    padding: 0 12px;
  }
  #howto-panel.expanded .howto-panel-inner { padding: 0 12px 12px; }

  .howto-row {
    border-radius: 12px;
    padding: 7px 8px 8px;
  }
  .howto-row + .howto-row { margin-top: 7px; }
  .howto-row-head {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-bottom: 5px;
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    font-size: 16px;
    font-weight: 800;
    letter-spacing: 0.3px;
  }
  .howto-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    color: #fff;
    font-size: 13px;
    flex-shrink: 0;
  }
  .howto-icons {
    display: flex;
    flex-wrap: nowrap;
    gap: 5px;
  }
  .howto-icons img {
    flex: 1 1 0;
    min-width: 0;
    max-width: 48px;
    aspect-ratio: 1 / 1;
    object-fit: contain;
  }
  /* echoes drawItems()'s actual in-game glow (rgba(220,40,40,0.85),
     shadowBlur 12 around bad items) via drop-shadow (alpha-aware, unlike
     box-shadow, so it hugs the sprite's own silhouette) -- the point isn't
     "memorize these 8 icons", it's "red glow = bad", the same rule that
     applies to every bad item actually falling in the game. */
  .howto-bad .howto-icons img {
    filter:
      drop-shadow(0 0 3px rgba(220, 40, 40, 0.9))
      drop-shadow(0 0 6px rgba(220, 40, 40, 0.6));
  }
  .howto-good { background: rgba(111, 207, 90, 0.18); }
  .howto-good .howto-row-head { color: #2e7d32; }
  .howto-good .howto-badge { background: #4caf50; }
  .howto-bad { background: rgba(230, 57, 70, 0.14); }
  .howto-bad .howto-row-head { color: #c62828; }
  .howto-bad .howto-badge { background: #e63946; }

  #menu-screen .start-play-btn.btn-cta {
    padding: 12px 8px;
    font-size: 20px;
    border-radius: 16px;
  }

  @media (max-width: 340px) {
    .howto-icons img { max-width: 38px; }
  }

  /* --- premium "result" screen: swaps the menu's baked start-bg.jpg for
     a CSS blue-radial + spinning rays + dot pattern, and brings back the
     card panel (hidden by default -- the menu doesn't use it any more,
     it's a single full-bleed background image instead). --- */
  #overlay.game-over {
    background-image: none;
    background: radial-gradient(circle at 50% 38%, #4fa8e8, #1d6fc4 70%);
    overflow: hidden;
  }
  #overlay.game-over::before {
    content: '';
    position: absolute;
    inset: -20%;
    background: repeating-conic-gradient(
      from 0deg,
      rgba(255,255,255,0.20) 0deg 6deg,
      transparent 6deg 18deg
    );
    animation: result-rays-spin 60s linear infinite;
    z-index: 0;
    pointer-events: none;
  }
  #overlay.game-over::after {
    content: '';
    position: absolute;
    inset: 0;
    background-image:
      radial-gradient(circle, rgba(255,255,255,0.9) 2px, transparent 2.5px),
      radial-gradient(circle, rgba(255,255,255,0.55) 1.5px, transparent 2px);
    background-size: 140px 140px, 90px 90px;
    background-position: 10px 20px, 60px 90px;
    z-index: 0;
    pointer-events: none;
  }
  /* .panel (dog/trophy/ribbon frame art) is result-screen-only -- the menu
     screen builds its own layout from #menu-screen/#howto-card above,
     doesn't touch this element at all. */
  #overlay .panel { display: none; }
  #overlay.game-over .panel {
    display: block;
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 600px;
    /* Lock height to the card artwork's own ratio instead of letting
       content dictate it -- keeps the card border rendered at its true,
       undistorted size no matter how much score/stats content ends up
       inside. */
    aspect-ratio: 1231 / 1002;
    box-sizing: border-box;
    /* no fallback fill color here on purpose: the card art is fully
       opaque everywhere except a ~1-2px antialiased edge right at its own
       silhouette. A cream fallback there blended through that edge into a
       pale halo that didn't match the card's hand-drawn corner curve --
       transparent lets that sliver blend into the page's own blue instead,
       which is a much closer match. */
    background-color: transparent;
    background-image: url('assets/ui/result-frame.png');
    background-repeat: no-repeat;
    background-size: 100% 100%;
    box-shadow: none;
    border: none;
    border-radius: 0;
    padding: 0;
    overflow: visible;
  }
  /* dog / trophy / title ribbon are separate layered art (not baked into
     the card background) so they can be repositioned independently and so
     the caption can be real, translatable text instead of pixels. All
     sizes are % of the panel so they stay in lockstep with the
     fixed-ratio card above at any panel width. */
  .result-dog {
    display: block;
    position: absolute;
    top: -25%;
    left: 50%;
    width: 30%;
    transform: translateX(-50%);
    z-index: 3;
    filter: drop-shadow(0 6px 6px rgba(0,0,0,0.25));
  }
  /* the trophy only means something on the result screen ("achievement") --
     stays hidden on the plain menu. */
  .result-trophy-corner { display: none; }
  #overlay.game-over .result-trophy-corner {
    display: block;
    position: absolute;
    top: -8%;
    right: -3%;
    width: 23%;
    z-index: 1;
    transition: filter 220ms ease, transform 220ms ease;
  }

  /* Result trophy reflects the medal earned in this run.  The original
     artwork is the gold version, so the other finishes are produced with
     CSS filters and require no duplicate trophy assets. */
  #overlay.game-over .result-trophy-corner.trophy-none {
    filter: grayscale(1) saturate(0.25) brightness(0.82) drop-shadow(0 6px 6px rgba(0,0,0,0.3));
  }
  #overlay.game-over .result-trophy-corner.trophy-bronze {
    filter: sepia(0.72) saturate(1.35) hue-rotate(-18deg) brightness(0.78) contrast(1.08) drop-shadow(0 6px 6px rgba(0,0,0,0.3));
  }
  #overlay.game-over .result-trophy-corner.trophy-silver {
    filter: grayscale(1) saturate(0) brightness(1.18) contrast(0.92) drop-shadow(0 6px 6px rgba(0,0,0,0.3));
  }
  #overlay.game-over .result-trophy-corner.trophy-gold {
    filter: saturate(1.06) brightness(1.02) drop-shadow(0 6px 6px rgba(0,0,0,0.3));
  }
  #overlay.game-over .result-trophy-corner.trophy-diamond {
    filter: hue-rotate(145deg) saturate(0.92) brightness(1.18) contrast(0.94) drop-shadow(0 0 8px rgba(170,235,255,0.75)) drop-shadow(0 6px 6px rgba(0,0,0,0.3));
  }
  .result-title {
    display: block;
    position: absolute;
    top: 6%;
    left: 50%;
    /* wider by default (menu: no trophy competing for room on the right,
       and "ЛУНИНЫ КОСТОЧКИ" is a longer string than "РЕЗУЛЬТАТ") --
       narrower on the result screen so it doesn't run under the trophy. */
    width: 70%;
    transform: translateX(-50%);
    z-index: 2;
  }
  #overlay.game-over .result-title { width: 52%; }
  .result-title img {
    display: block;
    width: 100%;
    height: auto;
  }
  .result-title span {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    /* scales with the panel (which itself tracks the viewport up to its
       600px cap) instead of a fixed size -- a fixed px here read great on
       a wide desktop panel but overflowed the ribbon art on a narrow
       phone, where the ribbon itself renders much smaller. */
    font-size: clamp(14px, 4.2vw, 26px);
    font-weight: 800;
    letter-spacing: 0.5px;
    color: #fff;
    text-shadow: 0 2px 0 rgba(0,0,0,0.2);
    -webkit-text-stroke: 1px #15406e;
    paint-order: stroke fill;
  }
  /* "РЕЗУЛЬТАТ" is a shorter string in a narrower ribbon -- can afford to
     run bigger than the menu's title text. */
  #overlay.game-over .result-title span {
    font-size: clamp(20px, 5.6vw, 36px);
  }
  /* result screen's ribbon is narrower (52%) so it's also shorter --
     content can start higher than the menu's default. */
  #overlay.game-over .result-window {
    top: 19.5%;
  }
  /* Positions the actual content (intro text/score/stats/buttons) inside
     the card, below the title ribbon -- as a percentage rect of the
     fixed-ratio panel, so it always lines up regardless of panel width.
     Scrolls internally if a content set ends up taller than the space. */
  .result-window {
    position: absolute;
    /* top is bigger here than on the result screen -- the menu's wider
       ribbon (70% vs 52%) is also taller, so it needs more clearance or
       the intro text starts underneath it. */
    inset: 24% 3.2% 2.96% 3.6%;
    display: flex;
    flex-direction: column;
    /* flex-start, not center: at in-between panel widths the reserved box
       is taller than the actual content, and centering pushed that
       leftover space to the TOP as a phantom gap right under the ribbon.
       Anchoring to the top means any leftover space lands harmlessly at
       the bottom instead. */
    justify-content: flex-start;
    gap: 4px;
    overflow-y: auto;
    z-index: 1;
  }
  @keyframes result-rays-spin {
    to { transform: rotate(360deg); }
  }

  /* menu-screen's own sprinkle layer, same idea as .result-bg-decor below
     -- sits behind #menu-screen (z-index:0, #menu-screen itself is a
     normal in-flow flex item so it paints on top without needing its own
     z-index) and fills whatever empty space the centering above leaves
     around the card. */
  .start-bg-decor { display: none; }
  #overlay:not(.game-over) .start-bg-decor {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
  }
  .start-bg-decor span {
    position: absolute;
    opacity: 0.5;
    filter: drop-shadow(0 1px 1px rgba(0,0,0,0.2));
  }

  .result-bg-decor { display: none; }
  #overlay.game-over .result-bg-decor {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
  }
  .result-bg-decor span {
    position: absolute;
    opacity: 0.4;
    filter: drop-shadow(0 1px 1px rgba(0,0,0,0.15));
  }

  .result-score-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    margin: 0 0 6px;
  }

  /* "Frosted glass" card look shared by the score box and the stat tiles
     below -- translucent instead of a flat fill, so the card's own paw
     pattern shows through, softened -- no sliced PNG border needed. */
  .result-score-box {
    width: 38%;
    min-width: 108px;
    max-width: 200px;
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 6px 8px 10px;
    background: rgba(255, 255, 255, 0.42);
    backdrop-filter: blur(7px) saturate(160%);
    -webkit-backdrop-filter: blur(7px) saturate(160%);
    border: 1.5px solid rgba(255, 255, 255, 0.7);
    border-radius: 18px;
    box-shadow: 0 4px 10px rgba(60, 100, 150, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.6);
  }

  .result-label {
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    font-size: 14px;
    font-weight: 800;
    letter-spacing: 1px;
    color: #2166ab;
  }

  .result-score-box .big-score {
    margin: 2px 0 0;
    color: #2a1d10;
    font-size: 34px;
  }

  /* the trophy is now baked into the frame artwork itself (top-right,
     overlapping the border) -- this badge just clips onto it when the run
     is a new record, positioned relative to the whole panel (not the old
     JS-built trophy element) so it stays put regardless of score-row
     content/scrolling. */
  .result-record-badge {
    display: none;
    position: absolute;
    top: 19%;
    right: -1%;
    background: linear-gradient(180deg, #ffe487, #ffb300);
    color: #7a4a00;
    font-size: 11px;
    font-weight: 800;
    line-height: 1.25;
    text-align: center;
    border-radius: 12px;
    padding: 6px 8px;
    transform: rotate(8deg);
    box-shadow: 0 3px 0 #c98700;
    z-index: 4;
  }
  #overlay.game-over .result-record-badge.show {
    display: block;
  }

  .result-medal-progress {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    margin: -1px 0 7px;
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    font-size: 13px;
    font-weight: 800;
    color: #2a1d10;
  }

  .result-medal-progress .medal-earned {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 8px;
    border-radius: 10px;
    background: rgba(255,255,255,0.46);
    border: 1px solid rgba(255,255,255,0.7);
  }

  .result-medal-progress .medal-next {
    color: #2166ab;
  }

  .result-stats {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-bottom: 6px;
  }

  .result-stat {
    flex: none;
    width: 31%;
    min-width: 88px;
    max-width: 148px;
    padding: 10px 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    background: rgba(255, 255, 255, 0.42);
    backdrop-filter: blur(7px) saturate(160%);
    -webkit-backdrop-filter: blur(7px) saturate(160%);
    border: 1.5px solid rgba(255, 255, 255, 0.7);
    border-radius: 14px;
    box-shadow: 0 4px 10px rgba(60, 100, 150, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.6);
  }

  .result-stat-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
  }

  .result-stat img { width: 26px; height: auto; display: block; }
  .result-stat-icon { font-size: 23px; line-height: 1; }

  .result-stat-label {
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    font-size: 12.5px;
    font-weight: 800;
    color: #2a1d10;
    letter-spacing: 0.2px;
    text-align: center;
    line-height: 1.15;
  }

  .result-stat-value {
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    font-size: 20px;
    font-weight: 800;
    white-space: nowrap;
  }
  .result-stat-value.c-blue { color: #2f86d6; }
  .result-stat-value.c-pink { color: #e0468a; }
  .result-stat-value.c-brown { color: #a6621c; }

  .result-actions {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }

  .result-actions-row { display: none; gap: 8px; }
  #overlay.game-over .result-actions-row { display: flex; }

  /* CTA + secondary buttons are plain CSS gradients now -- no more sliced
     button PNGs to keep in sync when the copy or icon changes. */
  .btn-cta {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    background: linear-gradient(180deg, #8bdb62, #4caf50);
    border: none;
    border-radius: 16px;
    padding: 7px 8px;
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    font-size: 16px;
    font-weight: 800;
    letter-spacing: 0.3px;
    color: #fff;
    text-shadow: 0 2px 0 rgba(0,0,0,0.15);
    box-shadow: 0 4px 0 #2e7d32;
  }
  .btn-cta:active { box-shadow: 0 1px 0 #2e7d32; }

  .btn-secondary {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    background: linear-gradient(180deg, #5cb2f2, #2f86d6);
    border: none;
    border-radius: 14px;
    padding: 6px 6px;
    font-family: 'Nunito Game', 'Trebuchet MS', sans-serif;
    font-size: 12.5px;
    font-weight: 800;
    white-space: nowrap;
    color: #fff;
    text-shadow: 0 1px 0 rgba(0,0,0,0.15);
    box-shadow: 0 3px 0 #1d5fa8;
  }
  .btn-secondary:active { box-shadow: 0 1px 0 #1d5fa8; }

  button {
    background: var(--accent);
    color: white;
    border: none;
    border-radius: 14px;
    padding: 14px 28px;
    font-size: 17px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 4px 0 #b8481c;
    transition: transform 0.1s;
    font-family: inherit;
  }
  button:active {
    transform: translateY(3px);
    box-shadow: 0 1px 0 #b8481c;
  }

  #hint {
    position: absolute;
    bottom: 18px;
    left: 0; right: 0;
    text-align: center;
    color: var(--ink);
    opacity: 0.85;
    font-size: 16px;
    font-weight: 700;
    text-shadow: 0 1px 0 rgba(255,255,255,0.6);
    z-index: 4;
    pointer-events: none;
  }

  /* Narrow phones: the game-over panel is capped by the viewport (not the
     600px max-width), so the frame's hole shrinks with it while the score
     box / stat tiles keep their min-width floor -- reclaim the vertical
     rhythm around them so the button rows never get pushed out. Font
     sizes are untouched on purpose, only spacing. */
  @media (max-width: 480px) {
    #overlay.game-over .result-window { gap: 2px; }
    .result-score-row { margin: 0 0 3px; }
    .result-stats { margin-bottom: 3px; }
    .result-score-box { padding: 5px 8px 6px; }
    .result-stat { padding: 4px 4px; }
    .result-actions { gap: 4px; }
    .btn-cta { padding: 5px 8px; }
    .btn-secondary { padding: 4px 6px; }
  }

/* --- studio splash / intro: plays once on page load, in front of
   everything (even the HUD), then fades away to reveal the start
   screen underneath. Built entirely with this one static logo image +
   CSS keyframes -- no AI-generated video, so the logo's own text stays
   pixel-perfect and it costs nothing extra to load. */
#splash {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 45%, #1a0505 0%, #000 70%);
  transition: opacity 0.5s ease;
}
#splash.hide {
  opacity: 0;
  pointer-events: none;
}

/* logo + pixels animate together, from the start -- the logo just eases in
   once (scale 0.9 -> 1, opacity 0 -> 1, single direction, no back-and-forth),
   while the pixels fly in around it at the same time. One monotonic fade,
   no oscillation anywhere = impossible to flicker. */
.splash-logo-wrap {
  position: relative;
  z-index: 1;
  width: min(78vw, 460px);
}
.splash-logo {
  display: block;
  width: 100%;
  height: auto;
  opacity: 0;
  animation: splashLogoIn 0.65s ease-out both;
}
@keyframes splashLogoIn {
  0%   { opacity: 0; transform: scale(0.9); }
  100% { opacity: 1; transform: scale(1); }
}

/* flying pixel squares, converging on ~24%/52% of the image -- where the
   logo's own baked-in pixel-scatter detail sits. Nested inside
   .splash-logo-wrap so the percentages are relative to the logo's own box,
   not the full viewport -- keeps them precisely aligned at any screen size. */
.splash-pixels {
  position: absolute;
  z-index: 2;
  left: 24%;
  top: 52%;
  width: 0;
  height: 0;
  pointer-events: none;
}
.splash-pixels span {
  position: absolute;
  left: 0;
  top: 0;
  width: 14px;
  height: 14px;
  background: var(--c);
  opacity: 0;
  animation: splashPixelFly 0.65s cubic-bezier(.2, .8, .2, 1) both;
  animation-delay: var(--d);
}
@keyframes splashPixelFly {
  0%   { opacity: 0; transform: translate(var(--x), var(--y)) rotate(var(--r)) scale(0.5); }
  15%  { opacity: 1; }
  78%  { opacity: 1; transform: translate(calc(var(--x) * 0.06), calc(var(--y) * 0.06)) rotate(0deg) scale(1); }
  100% { opacity: 0; transform: translate(0, 0) rotate(0deg) scale(0.7); }
}

@media (prefers-reduced-motion: reduce) {
  .splash-logo, .splash-pixels span { animation: none; opacity: 1; transform: none; }
}
