
  :root {
    --ground-h: 14%;     /* brown ground strip height (% of viewport) */
    --hills-h:  46%;     /* hills layer height */
    --grass-h:  18%;     /* foreground grass height */
    /* Hero is a FIXED pixel size that matches the fixed 56px world tiles, so
       the character keeps the same Mario-ish proportion on every screen. (It
       used to scale with the window, which made it balloon huge on desktop
       while the platforms stayed small.) On big screens you simply see more
       of the world left and right instead of a giant hero. */
    --hero-px: 96px;
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  html, body {
    width: 100%; height: 100dvh;
    overflow: hidden;
    /* Sky is a CSS gradient — perfectly seamless, never wraps. */
    background: linear-gradient(180deg,
      #b6e5ff 0%,
      #d8eef5 35%,
      #ffd5ec 70%,
      #ffe9b0 95%,
      #f9d18f 100%);
    font-family: "Comic Sans MS", "Chalkboard SE", system-ui, sans-serif;
    color: #2b1140;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    touch-action: none;
  }

  #stage {
    position: relative;
    width: 100%; height: 100%;
    overflow: hidden;
  }

  /* ===== Sky decorations (sun + clouds) =====
     Pure CSS shapes, drifting at sky-parallax speed. */
  .sky-deco {
    position: absolute;
    will-change: transform;
    pointer-events: none;
    z-index: 1;
    top: 0;
    left: 0;
    width: 250%;          /* wider than viewport so we can scroll */
    height: 50%;
  }
  .sun {
    position: absolute;
    top: 6%;
    right: 8%;
    z-index: 1;
    pointer-events: none;
    width: clamp(60px, 10vw, 110px);
    height: clamp(60px, 10vw, 110px);
    border-radius: 50%;
    background:
      radial-gradient(circle at 35% 35%, #fff7c4 0%, #ffd56b 55%, #ffba3a 80%, #ff9a1a 100%);
    box-shadow: 0 0 30px #ffe18a, 0 0 60px #ffd56b66;
  }
  .cloud {
    position: absolute;
    background: #fff;
    border-radius: 100px;
    opacity: 0.92;
    box-shadow:
      18px -8px 0 -2px #fff,
      -22px -4px 0 -4px #fff,
       38px -2px 0 -8px #fff;
  }
  .cloud.c1 { top: 18%; left: 6%;  width: 80px;  height: 28px; }
  .cloud.c2 { top: 28%; left: 18%; width: 110px; height: 36px; }
  .cloud.c3 { top: 12%; left: 50%; width: 90px;  height: 30px; }
  .cloud.c4 { top: 22%; left: 70%; width: 70px;  height: 24px; }
  .cloud.c5 { top: 32%; left: 90%; width: 100px; height: 32px; }
  .cloud.c6 { top: 14%; left: 110%; width: 86px; height: 28px; }
  .cloud.c7 { top: 26%; left: 140%; width: 96px; height: 32px; }

  /* ===== Parallax bands (hills + grass) =====
     Each band has 2 image copies; the SECOND is mirrored horizontally so
     a non-tileable image still tiles seamlessly:
       copy1 → mirrored copy → copy1 → mirrored copy ...
     The seam where two copies meet is always between matching edges
     (right of copy1 meets right of mirrored copy, left of mirrored
     meets left of next copy1) so the color/content match perfectly.
     We translateX the container modulo (2 × imageWidth) for infinite
     scroll. */
  .parallax {
    position: absolute;
    pointer-events: none;
    will-change: transform;
    display: flex;
    flex-wrap: nowrap;
    image-rendering: -webkit-optimize-contrast;
    filter: var(--band-filter, none);   /* retinted per biome */
  }
  .parallax img {
    display: block;
    height: 100%;
    width: auto;
    flex-shrink: 0;          /* IMPORTANT — without this, flex squashes the
                                images to fit the container, which makes the
                                wrap period tiny and the hills "disappear"
                                after walking ~one viewport. */
  }
  .parallax img:nth-child(even) {
    transform: scaleX(-1);   /* mirror trick — every other copy is mirrored
                                so the seam between any two adjacent copies
                                is always between matching edges. */
  }

  #hills {
    z-index: 2;
    bottom: var(--ground-h);
    left: 0; right: 0;
    height: var(--hills-h);
  }
  /* Ground is a green grass field — bright at the surface where the
     hero runs, slightly darker below. Tiny coloured dots painted in
     via repeating radial-gradients give a sprinkling of wildflowers. */
  #ground {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: var(--ground-h);
    background:
      /* tiny scattered flowers, a few sizes & colours */
      radial-gradient(circle at 14% 38%, #ff6bb5 2.5px, transparent 3.5px),
      radial-gradient(circle at 36% 65%, #ffd166 2.5px, transparent 3.5px),
      radial-gradient(circle at 58% 30%, #fff   2.5px, transparent 3.5px),
      radial-gradient(circle at 78% 60%, #b36bff 2.5px, transparent 3.5px),
      /* base field — recoloured per biome via CSS vars (grass→sand→snow…) */
      linear-gradient(180deg, var(--gb1,#6dbf52) 0%, var(--gb2,#5fa844) 55%, var(--gb3,#4d8c39) 100%);
    background-size:
      90px 60%,
      70px 60%,
      100px 60%,
      80px 60%,
      auto;
    background-repeat:
      repeat-x, repeat-x, repeat-x, repeat-x, no-repeat;
    background-position:
      0 30%, 50px 60%, 25px 35%, 75px 55%, 0 0;
    z-index: 3;
  }
  /* The brighter grass strip at the very top of the ground — the
     surface the hero actually runs on. */
  #ground::before {
    content: "";
    position: absolute;
    left: 0; right: 0;
    top: 0;
    height: 22%;
    background: linear-gradient(180deg, var(--gt1,#8ac256) 0%, var(--gt2,#6dbf52) 100%);
    box-shadow: inset 0 6px 0 var(--ge,#a3d864);
    pointer-events: none;
  }
  /* The painted-grass parallax band sits at the TOP of the ground (at
     the same level as the hero's feet) so grass tufts are around the
     hero, not below the mud. */
  #grass {
    z-index: 4;            /* in front of ground, BEHIND hero */
    bottom: var(--ground-h);
    left: 0; right: 0;
    height: var(--grass-h);
  }
  /* Hero z=5 — in front of grass so the grass tucks UNDER the feet
     for foreground depth without hiding the character. */
  #hero {
    position: absolute;
    bottom: var(--ground-h);
    /* Kenney character sprites are small (~32-64 px) and chibi-style.
       Locked-box + object-fit lets us upscale them without the frame
       size jumping between poses, and image-rendering: pixelated keeps
       the upscaled edges crisp instead of blurry. */
    width:  var(--hero-px);
    height: var(--hero-px);
    object-fit: contain;
    object-position: bottom center;
    image-rendering: pixelated;
    image-rendering: -moz-crisp-edges;
    z-index: 5;
    transform-origin: bottom center;
  }

  /* ===== Foreground decorations =====
     Tiny flowers/clovers/bushes that scroll FASTER than the camera so
     they pass in front of the hero — gives the scene foreground depth.
     Position is set in JS each frame from a stored worldX. */
  .fg-deco {
    position: absolute;
    z-index: 6;          /* in front of hero (z=5) */
    pointer-events: none;
    line-height: 1;
    will-change: transform;
    filter: drop-shadow(0 3px 3px rgba(0,0,0,0.25));
  }

  /* ===== Level objects (platforms, coins, mushrooms, goal flag) =====
     The #level container stays at z=4 — in front of the parallax bands
     but behind the hero (z=5), so the hero renders ON TOP of platforms. */
  #level {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 4;
  }
  /* Cloud platforms — composed from 3 Kenney tiles (left edge, repeating
     middle, right edge). Width set inline per platform. */
  .platform {
    position: absolute;
    height: 56px;
    background-image:
      url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/terrain_grass_cloud_left.png'),
      url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/terrain_grass_cloud_right.png'),
      url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/terrain_grass_cloud_middle.png');
    background-position: left top, right top, center top;
    background-repeat: no-repeat, no-repeat, repeat-x;
    background-size: 56px 56px, 56px 56px, 56px 56px;
    image-rendering: pixelated;
    transform: translateX(-50%);   /* anchor x at platform center */
    filter: drop-shadow(0 4px 4px rgba(0,0,0,0.18));
  }
  /* Moving platforms (lifts / sliders) — soft blue glow so kids spot them. */
  .platform.mover {
    filter: drop-shadow(0 0 7px #6cc7ff) drop-shadow(0 4px 4px rgba(0,0,0,0.18));
  }
  /* Bounce pads (trampolines) — bob + bright glow + up-arrows say "boing!" */
  .platform.bounce {
    filter: drop-shadow(0 0 9px #ffe04d) drop-shadow(0 4px 4px rgba(0,0,0,0.2));
    animation: padBob 0.9s ease-in-out infinite;
  }
  .platform.bounce::after {       /* a real bouncy spring sitting on the pad */
    content: "";
    position: absolute;
    left: 50%; bottom: 100%;
    width: 44px; height: 44px;
    margin: 0 0 -10px -22px;
    background: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/spring.png') center bottom / contain no-repeat;
    image-rendering: pixelated;
    filter: drop-shadow(0 2px 2px rgba(0,0,0,0.25));
    animation: padArrows 0.9s ease-in-out infinite;
  }
  .platform.bounce.sprung::after {   /* compressed frame the moment you launch */
    background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/spring_out.png');
  }
  @keyframes padBob   { 0%,100%{ transform: translateX(-50%) translateY(0); } 50%{ transform: translateX(-50%) translateY(3px); } }
  @keyframes padArrows{ 0%,100%{ opacity:.5; transform: translateY(0); } 50%{ opacity:1; transform: translateY(-3px); } }
  /* Crumbling platforms — cracked, faintly reddish so they read as "old". */
  .platform.crumble {
    filter: sepia(.4) saturate(1.3) hue-rotate(-18deg) drop-shadow(0 4px 4px rgba(0,0,0,0.2));
  }
  .platform.crumble::after {
    content: "";
    position: absolute; inset: 6px 8px;
    background:
      linear-gradient(120deg, transparent 46%, rgba(60,30,10,.55) 47%, rgba(60,30,10,.55) 50%, transparent 51%),
      linear-gradient(60deg,  transparent 60%, rgba(60,30,10,.45) 61%, rgba(60,30,10,.45) 63%, transparent 64%);
    pointer-events: none;
  }
  .platform.crumble.crumbling { animation: crumbleShake 0.12s linear infinite; }
  @keyframes crumbleShake { 0%,100%{ transform: translateX(-50%) translateX(-2px); } 50%{ transform: translateX(-50%) translateX(2px); } }
  .platform.crumble.gone { opacity: 0; transition: opacity 0.18s ease-out; pointer-events: none; }
  /* Deadly pit — an obvious dark hole cut into the ground. Designed to pop
     on ANY biome: a bright glowing rim shows on dark grounds, the dark
     interior shows on light (snow/sand) grounds, and a red danger glow +
     gentle pulse draw the eye. */
  .pit {
    position: absolute;
    bottom: 0;
    height: var(--ground-h);
    transform: translateX(-50%);
    background:
      radial-gradient(135% 80% at 50% 100%, rgba(255,64,92,.55) 0%, rgba(255,64,92,0) 58%),
      linear-gradient(180deg, #2a1442 0%, #0a0414 100%);
    border-radius: 0 0 8px 8px;
    outline: 3px solid rgba(255,255,255,.92);
    outline-offset: -3px;
    box-shadow: inset 0 0 0 6px rgba(0,0,0,.32);
    z-index: 4;
    pointer-events: none;
    animation: pitPulse 1.1s ease-in-out infinite;
  }
  @keyframes pitPulse {
    0%,100% { filter: drop-shadow(0 0 4px rgba(120,180,255,.55)); }
    50%     { filter: drop-shadow(0 0 11px rgba(120,180,255,.95)); }
  }
  .pit::before {   /* bright glowing danger lip across the top */
    content: "";
    position: absolute; left: -6px; right: -6px; top: -7px; height: 15px;
    border-radius: 8px;
    background: linear-gradient(180deg, #ffffff 0%, rgba(255,255,255,0) 100%);
    box-shadow: 0 0 12px 2px rgba(120,180,255,.9);
  }
  /* ===== New toys (shared world-object base) ===== */
  .wobj {
    position: absolute;
    transform: translateX(-50%);
    background-position: bottom center;
    background-repeat: no-repeat;
    background-size: contain;
    image-rendering: pixelated;
    pointer-events: none;
    z-index: 4;
  }
  /* Checkpoint flag — green until you reach it, then it pops up red. */
  .checkpoint {
    width: 52px; height: 80px;
    filter: drop-shadow(0 3px 4px rgba(0,0,0,0.25));
    background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/flag_green_a.png');
  }
  .checkpoint.on {
    background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/flag_red_a.png');
    animation: cpPop 0.4s ease-out;
  }
  @keyframes cpPop { 0%{ transform: translateX(-50%) translateY(6px) scale(.8);} 60%{ transform: translateX(-50%) translateY(-4px) scale(1.12);} 100%{ transform: translateX(-50%) translateY(0) scale(1);} }
  /* Bonk blocks — hit from below for coins / a fire-gem. */
  .bonk-block { width: 48px; height: 48px; filter: drop-shadow(0 3px 3px rgba(0,0,0,0.25)); }
  .bonk-block.question { background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/block_exclamation.png'); animation: blockGlow 1.1s ease-in-out infinite; }
  .bonk-block.coin     { background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/block_coin.png'); }
  .bonk-block.empty    { background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/block_empty.png'); animation: none; filter: grayscale(.3) drop-shadow(0 3px 3px rgba(0,0,0,0.25)); }
  .bonk-block.hit { animation: blockBonk 0.22s ease-out; }
  /* Reward that pops out the TOP of a bonked block, rises and fades so it's
     obvious what you got (coins / a life / fire power). */
  .reward-pop {
    position: absolute; left: 50%; bottom: 100%; transform: translateX(-50%);
    white-space: nowrap; pointer-events: none; z-index: 40;
    font-family: 'Baloo 2', system-ui, sans-serif; font-weight: 900;
    font-size: 22px; color: #fff;
    text-shadow: 0 2px 4px rgba(0,0,0,.6), 0 0 4px rgba(0,0,0,.5);
    animation: rewardPop 0.95s ease-out forwards;
  }
  @keyframes rewardPop {
    0%   { opacity: 0; transform: translateX(-50%) translateY(8px)  scale(.5); }
    18%  { opacity: 1; transform: translateX(-50%) translateY(-8px) scale(1.15); }
    100% { opacity: 0; transform: translateX(-50%) translateY(-58px) scale(1); }
  }
  @keyframes blockGlow { 0%,100%{ filter: drop-shadow(0 0 3px #ffd23e) drop-shadow(0 3px 3px rgba(0,0,0,0.25)); } 50%{ filter: drop-shadow(0 0 9px #ffd23e) drop-shadow(0 3px 3px rgba(0,0,0,0.25)); } }
  @keyframes blockBonk { 0%{ transform: translateX(-50%) translateY(0);} 40%{ transform: translateX(-50%) translateY(-9px);} 100%{ transform: translateX(-50%) translateY(0);} }
  /* Key pickup + locked door at the goal. */
  .keyitem {
    width: 38px; height: 38px;
    background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/key_blue.png');
    filter: drop-shadow(0 0 8px #6cc7ff) drop-shadow(0 2px 3px rgba(0,0,0,0.3));
    animation: firePowerSpin 1.4s ease-in-out infinite;
  }
  .keyitem.collected { animation: coinPop 320ms ease-out forwards; }
  .door {
    width: 72px; height: 112px;
    background-image:
      url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/door_closed_top.png'),
      url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/door_closed.png');
    background-position: center top, center bottom;
    background-size: 56px 56px, 56px 56px;
    background-repeat: no-repeat, no-repeat;
    filter: drop-shadow(0 3px 5px rgba(0,0,0,0.3));
  }
  .door.open {
    background-image:
      url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/door_open_top.png'),
      url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/door_open.png');
  }
  /* Ladders — climb with ↑ / ↓ (or W / S). */
  .ladder {
    width: 44px;
    background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/ladder_middle.png');
    background-repeat: repeat-y;
    background-position: center top;
    background-size: 44px 44px;
    z-index: 4;
    opacity: 0.96;
  }
  .ladder::before {
    content: "";
    position: absolute; left: 0; right: 0; top: -40px; height: 44px;
    background: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/ladder_top.png') center / contain no-repeat;
    image-rendering: pixelated;
  }
  /* Candy-zone ladders wear the candy-cane skin. */
  .ladder.cane { background-image: url('../assets/kenney/platformer-art-candy/Tiles/canePink.png'); }
  .ladder.cane::before { background-image: url('../assets/kenney/platformer-art-candy/Tiles/canePinkTop.png'); }
  /* Direction sign — friendly "this way!" hint. */
  .sign { width: 60px; height: 60px; filter: drop-shadow(0 3px 3px rgba(0,0,0,0.22)); }
  .coin {
    position: absolute;
    width: 32px; height: 32px;
    transform: translate(-50%, 50%);   /* anchor at coin's world center */
    background: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/coin_gold.png')
                center / contain no-repeat;
    image-rendering: pixelated;
    filter: drop-shadow(0 2px 3px rgba(0,0,0,0.3));
    animation: coinSpin 1.6s ease-in-out infinite;
  }
  @keyframes coinSpin {
    0%, 100% { transform: translate(-50%, 50%) scale(1)   rotate(0deg); }
    50%      { transform: translate(-50%, 40%) scale(1.1) rotate(20deg); }
  }
  .coin.collected { animation: coinPop 320ms ease-out forwards; }
  @keyframes coinPop {
    0%   { opacity: 1; transform: translate(-50%, 50%)  scale(1); }
    100% { opacity: 0; transform: translate(-50%, -40%) scale(1.6); }
  }
  .mushroom {
    position: absolute;
    width: 56px; height: 56px;
    transform: translateX(-50%);
    background-position: bottom center;
    background-size: contain;
    background-repeat: no-repeat;
    image-rendering: pixelated;
    filter: drop-shadow(0 3px 3px rgba(0,0,0,0.25));
  }
  .mushroom.brown { background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/mushroom_brown.png'); }
  .mushroom.red   { background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/mushroom_red.png'); }
  .goal {
    position: absolute;
    width: 64px; height: 96px;
    transform: translateX(-50%);
    background: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/flag_blue_a.png')
                bottom center / contain no-repeat;
    image-rendering: pixelated;
    filter: drop-shadow(0 4px 6px rgba(0,0,0,0.3));
    transform-origin: bottom center;
    animation: flagSway 2.2s ease-in-out infinite;
  }
  @keyframes flagSway {
    0%, 100% { transform: translateX(-50%) rotate(-3deg); }
    50%      { transform: translateX(-50%) rotate(3deg); }
  }
  /* Jetpack HUD badge — top-center while jetpack is active */
  #jetpack-hud {
    position: absolute;
    top: 14px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 30;
    background: linear-gradient(135deg, #ff6bb5, #ffd166);
    border: 3px solid #fff;
    border-radius: 999px;
    padding: 5px 18px;
    font-weight: bold;
    color: #fff;
    font-size: 16px;
    text-shadow: 0 2px 3px rgba(0,0,0,0.35);
    box-shadow: 0 4px 10px rgba(0,0,0,0.25);
    display: none;
    pointer-events: none;
    white-space: nowrap;
  }
  #jetpack-hud.show { display: flex; align-items: center; gap: 8px; animation: jetpackPulse 0.6s ease-in-out infinite; }
  #jetpack-hud .fuel-bar { width: 72px; height: 12px; background: rgba(255,255,255,0.45);
    border-radius: 999px; overflow: hidden; box-shadow: inset 0 1px 2px rgba(0,0,0,0.3); }
  #jetpack-hud .fuel-fill { height: 100%; width: 100%;
    background: linear-gradient(90deg,#7CFC00,#bdf58a); transition: width 0.08s linear; }
  #jetpack-hud.low .fuel-fill { background: linear-gradient(90deg,#ff5d5d,#ffb15d); }
  #jetpack-hud .fuel-label { font-weight: 900; font-size: 18px; line-height: 1; }
  @keyframes jetpackPulse {
    0%, 100% { transform: translateX(-50%) scale(1); }
    50%      { transform: translateX(-50%) scale(1.07); }
  }
  /* Hero gets a flame trail while jetpacking */
  #hero.jetpack-on {
    filter: drop-shadow(0 0 8px #ffd166)
            drop-shadow(0 0 16px #ff6bb5)
            drop-shadow(0 6px 6px rgba(0,0,0,0.25));
  }
  /* ===== Multiplayer ghosts =====
     Other players in the same level. Anchored at feet (bottom-center
     of the box) like the local hero, but with a name bubble overhead
     and a slight transparency so the local hero stays visually clear. */
  .ghost {
    position: absolute;
    width: var(--hero-px);
    height: var(--hero-px);
    z-index: 4;        /* behind the local hero (z=5) and platforms (z=4) */
    transform-origin: bottom center;
    pointer-events: none;
    opacity: 0.92;
    transition: opacity 200ms;
  }
  .ghost img {
    width: 100%; height: 100%;
    object-fit: contain;
    object-position: bottom center;
    image-rendering: pixelated;
    image-rendering: -moz-crisp-edges;
    pointer-events: none;
  }
  .ghost .name {
    position: absolute;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-bottom: 4px;
    background: #fff;
    border: 2px solid #b08bd8;
    border-radius: 999px;
    padding: 1px 8px;
    font-size: 12px;
    font-weight: 700;
    color: #2b1140;
    white-space: nowrap;
    text-shadow: none;
  }

  /* T7: Off-screen partner arrow — pinned to the viewport edge, points toward partner */
  .coop-arrow {
    position: fixed;
    top: 50%;
    transform: translateY(-50%);
    z-index: 90;
    background: #ffffffcc;
    border: 2px solid #b08bd8;
    border-radius: 12px;
    padding: 6px 10px;
    font-size: 18px;
    font-weight: 900;
    color: #2b1140;
    pointer-events: none;
    white-space: nowrap;
    display: none;
    text-shadow: none;
    box-shadow: 0 2px 8px #0003;
  }
  .coop-arrow.left  { left: 6px; }
  .coop-arrow.right { right: 6px; }

  /* Death tumble — a clear "you got hit" spin + fade, played before the hero is
     swept back to the start / last flag. Uses the independent rotate/translate/
     opacity properties so it COMPOSES with the hero's inline facing transform
     (scaleX/translateY/rotate) instead of overwriting it. */
  #hero.dying { animation: heroDie 0.95s ease-in forwards; z-index: 60; }
  @keyframes heroDie {
    0%   { rotate: 0deg;   translate: 0 0;     opacity: 1;   }
    22%  { rotate: -18deg; translate: 0 -34px; opacity: 1;   }  /* a little death hop */
    100% { rotate: 150deg; translate: 0 46px;  opacity: .12; }  /* tumble down + fade */
  }

  /* Brief flicker after taking a hit (invincibility frames) */
  #hero.invuln { animation: heroFlicker 0.18s linear infinite; }
  @keyframes heroFlicker {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.35; }
  }

  /* Fireball projectiles — flung forward from the hero. */
  .fireball {
    position: absolute;
    width: 36px; height: 36px;
    transform: translate(-50%, 50%);
    background: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/fireball.png')
                center / contain no-repeat;
    image-rendering: pixelated;
    filter: drop-shadow(0 0 8px #ff6b3b) drop-shadow(0 2px 3px rgba(0,0,0,0.3));
    animation: fireballSpin 0.25s linear infinite;
    pointer-events: none;
  }
  .fireball.face-left {
    transform: translate(-50%, 50%) scaleX(-1);
    animation: fireballSpinL 0.25s linear infinite;
  }
  @keyframes fireballSpin {
    from { transform: translate(-50%, 50%) rotate(0deg); }
    to   { transform: translate(-50%, 50%) rotate(360deg); }
  }
  @keyframes fireballSpinL {
    from { transform: translate(-50%, 50%) scaleX(-1) rotate(0deg); }
    to   { transform: translate(-50%, 50%) scaleX(-1) rotate(360deg); }
  }
  .fireball.dead { animation: fireballPop 200ms ease-out forwards; }
  @keyframes fireballPop {
    0%   { opacity: 1; transform: translate(-50%, 50%) scale(1); }
    100% { opacity: 0; transform: translate(-50%, 50%) scale(2.4); }
  }

  /* Fire-power pickup — a red gem floating in mid-air. Grab to enable
     fireball shooting; lose the power on the next hit you take. */
  .firepower {
    position: absolute;
    width: 38px; height: 38px;
    transform: translate(-50%, 50%);
    background: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/gem_red.png')
                center / contain no-repeat;
    image-rendering: pixelated;
    filter: drop-shadow(0 0 12px #ff6b3b) drop-shadow(0 2px 3px rgba(0,0,0,0.3));
    animation: firePowerSpin 1.4s ease-in-out infinite;
    pointer-events: none;
  }
  @keyframes firePowerSpin {
    0%, 100% { transform: translate(-50%, 50%)  scale(1)   rotate(-12deg); }
    50%      { transform: translate(-50%, 30%)  scale(1.18) rotate(12deg); }
  }
  .firepower.collected { animation: firePowerPop 360ms ease-out forwards; }
  @keyframes firePowerPop {
    0%   { opacity: 1; transform: translate(-50%, 50%)  scale(1); }
    100% { opacity: 0; transform: translate(-50%, -90%) scale(2.4); }
  }

  /* ⭐ Star Power pickup — grab it to grow DOUBLE SIZE and become
     invincible for 30 seconds (enemies splat on touch; pits still count). */
  .starpower {
    position: absolute;
    width: 46px; height: 46px;
    transform: translate(-50%, 50%);
    background: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/star.png')
                center / contain no-repeat;
    image-rendering: pixelated;
    filter: drop-shadow(0 0 14px #ffd93b) drop-shadow(0 2px 3px rgba(0,0,0,0.3));
    animation: starPowerSpin 1.1s ease-in-out infinite;
    pointer-events: none;
  }
  @keyframes starPowerSpin {
    0%, 100% { transform: translate(-50%, 50%)  scale(1)    rotate(-14deg); }
    50%      { transform: translate(-50%, 26%)  scale(1.22) rotate(14deg); }
  }
  .starpower.collected { animation: firePowerPop 360ms ease-out forwards; }
  /* Rainbow shimmer while the star is active (filter only — the hero's
     transform is JS-owned, the ghost's transform is the scale below). */
  #hero.star-power, .ghost.star-power img {
    animation: starRainbow 0.9s linear infinite;
  }
  @keyframes starRainbow {
    0%   { filter: hue-rotate(0deg)   drop-shadow(0 0 14px #ffd93b); }
    50%  { filter: hue-rotate(180deg) drop-shadow(0 0 20px #ff9df5); }
    100% { filter: hue-rotate(360deg) drop-shadow(0 0 14px #ffd93b); }
  }
  /* Peers render a star-powered friend at double size too (keep the camera
     translate in the same transform so the rules don't fight). */
  .ghost.star-power { transform: translateY(var(--cam-y, 0px)) scale(2); }
  /* Star countdown pill — same spot family as the jetpack fuel HUD. */
  #star-hud {
    position: absolute;
    top: 146px; left: 12px;   /* under the hearts (50) + coin (96) pills */
    z-index: 30;
    display: none;
    background: linear-gradient(135deg, #ffd93b, #ff9df5);
    border: 3px solid #fff;
    border-radius: 999px;
    padding: 6px 14px 6px 10px;
    font-weight: 900;
    font-size: 18px;
    color: #7a3a00;
    box-shadow: 0 3px 0 rgba(0,0,0,0.18);
  }
  #star-hud.show { display: flex; align-items: center; gap: 8px; animation: jetpackPulse 0.6s ease-in-out infinite; }
  #star-hud img { width: 24px; height: 24px; image-rendering: pixelated; }

  /* ===== Level specials (phase 2) ===== */
  /* Vertical camera: on tall levels the world slides DOWN while the hero is
     up high (GPU transform; var stays 0px on normal levels = unchanged). */
  #level, #ground, #grass, #hills { transform: translateY(var(--cam-y, 0px)); will-change: transform; }
  .ghost { transform: translateY(var(--cam-y, 0px)); }

  /* Spotlight dark — a circle of light follows the hero; the rest is night. */
  #spotlight {
    position: absolute; inset: 0; z-index: 18; pointer-events: none;
    background: radial-gradient(circle 230px at var(--spot-x, 30%) var(--spot-y, 70%),
                rgba(0,0,0,0) 0 52%, rgba(14,10,46,0.55) 74%, rgba(8,6,34,0.82) 100%);
  }

  /* Conveyor-belt platforms — sliding stripe overlay (.rev = flows left). */
  .platform.belt::before {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: 40%;
    border-radius: 8px;
    background: repeating-linear-gradient(90deg, rgba(255,255,255,0.45) 0 12px, rgba(60,30,10,0.18) 12px 24px);
    animation: beltSlide 0.5s linear infinite;
  }
  .platform.belt.rev::before { animation-direction: reverse; }
  @keyframes beltSlide { from { background-position-x: 0; } to { background-position-x: 24px; } }

  /* Super-bounce pads — stronger glow than a normal trampoline. */
  .platform.bounce2 { animation: bounce2Glow 0.9s ease-in-out infinite; }
  @keyframes bounce2Glow {
    0%, 100% { filter: drop-shadow(0 0 8px #ff7bd5); }
    50%      { filter: drop-shadow(0 0 18px #ffd166) saturate(1.25); }
  }

  /* Geysers — a column that erupts on a shared rhythm and flings you up. */
  .geyser {
    position: absolute; width: 52px; height: 240px;
    transform: translateX(-50%) scaleY(0); transform-origin: bottom center;
    border-radius: 26px 26px 0 0;
    background: linear-gradient(180deg, rgba(255,255,255,0.85), rgba(110,190,255,0.75) 30%, rgba(70,150,240,0.6));
    transition: transform 220ms ease-out;
    pointer-events: none;
  }
  .geyser.erupting { transform: translateX(-50%) scaleY(1); }
  .geyser::after {
    content: ""; position: absolute; top: -10px; left: 8px; right: 8px; height: 20px;
    border-radius: 50%; background: rgba(255,255,255,0.9); filter: blur(3px);
  }
  .geyser-base {
    position: absolute; width: 56px; height: 12px; transform: translateX(-50%);
    border-radius: 8px 8px 0 0; background: rgba(110,190,255,0.65);
    box-shadow: 0 0 10px rgba(110,190,255,0.8); pointer-events: none;
  }

  /* Speed pads — glowing strips; run across one for a 1.5s zoom. */
  .speedpad {
    position: absolute; width: 200px; height: 14px; transform: translateX(-50%);
    border-radius: 8px;
    background: repeating-linear-gradient(90deg, #ffd166 0 18px, #ff8ad1 18px 36px);
    animation: padSlide 0.45s linear infinite; opacity: 0.9; pointer-events: none;
    box-shadow: 0 0 12px rgba(255,209,102,0.8);
  }
  @keyframes padSlide { from { background-position-x: 0; } to { background-position-x: 36px; } }
  #hero.boosting { filter: drop-shadow(-10px 0 8px rgba(255,209,102,0.85)); }

  /* Wind gusts — faint streaks slide across while a gust blows (.rev = leftward). */
  #windfx {
    position: absolute; inset: 0; z-index: 17; pointer-events: none; opacity: 0;
    transition: opacity 400ms;
    background: repeating-linear-gradient(100deg, transparent 0 90px, rgba(255,255,255,0.28) 90px 96px, transparent 96px 200px);
    animation: windSlide 0.8s linear infinite;
  }
  #windfx.on { opacity: 0.55; }
  #windfx.rev { animation-direction: reverse; }
  @keyframes windSlide { from { background-position-x: 0; } to { background-position-x: 200px; } }

  /* ===== Hidden heart treasure =====
     Sits to the LEFT of the start position so the player has to walk
     backwards to find it. Picking it up unlocks the heart-decoration
     for the home-page avatar (saved in localStorage). */
  .heart-treasure {
    position: absolute;
    width: 64px; height: 64px;
    transform: translate(-50%, 50%);
    background: url('../assets/platformer/heart-treasure.webp')
                center / contain no-repeat;
    filter: drop-shadow(0 0 18px #ff8fc4) drop-shadow(0 3px 4px rgba(0,0,0,0.3));
    animation: heartFloat 1.8s ease-in-out infinite;
    pointer-events: none;
  }
  @keyframes heartFloat {
    0%, 100% { transform: translate(-50%, 50%)  scale(1)    rotate(-6deg); }
    50%      { transform: translate(-50%, 30%)  scale(1.10) rotate(6deg);  }
  }
  .heart-treasure.collected { animation: heartPop 540ms ease-out forwards; }
  @keyframes heartPop {
    0%   { opacity: 1; transform: translate(-50%, 50%)   scale(1); }
    100% { opacity: 0; transform: translate(-50%, -120%) scale(3); }
  }

  /* Treasure-found celebration overlay */
  #heart-unlock {
    position: absolute; inset: 0;
    z-index: 60;
    display: none;
    align-items: center; justify-content: center;
    background: radial-gradient(circle at 50% 50%, rgba(255, 192, 230, 0.85), rgba(255, 105, 180, 0.7));
    backdrop-filter: blur(2px);
  }
  #heart-unlock.show { display: flex; }
  #heart-unlock .card {
    background: #fff;
    border: 6px solid #ff6bb5;
    border-radius: 28px;
    padding: 28px 32px;
    text-align: center;
    box-shadow: 0 12px 40px rgba(0,0,0,0.3);
    max-width: 90vw;
    animation: winPop 380ms cubic-bezier(.5,1.6,.4,1) both;
  }
  #heart-unlock h1 {
    color: #ff6bb5;
    font-size: clamp(24px, 5vw, 40px);
    margin: 0 0 8px;
  }
  #heart-unlock p { font-size: clamp(16px, 3vw, 22px); margin: 6px 0; color: #6a1b6a; }
  #heart-unlock img.preview {
    width: 96px; height: 96px;
    margin: 6px auto;
    display: block;
    filter: drop-shadow(0 4px 8px rgba(255, 105, 180, 0.6));
  }
  #heart-unlock button {
    margin-top: 12px;
    font-family: inherit;
    font-size: clamp(18px, 4vw, 24px);
    font-weight: bold;
    color: #fff;
    padding: 10px 28px;
    border-radius: 999px;
    border: 4px solid #b558a8;
    background: linear-gradient(135deg, #ff6bb5, #ffd166);
    box-shadow: 0 5px 0 #b558a8;
    cursor: pointer;
    letter-spacing: 1px;
  }
  #heart-unlock button:active { translate: 0 3px; box-shadow: 0 2px 0 #b558a8; }

  /* Full-bleed painted title screen that doubles as the asset-loading
     curtain. The gate script keeps Start at "Loading…" until every
     <link rel="preload" as="image"> in the head has decoded — so the
     game beneath it is fully primed by the time the kid taps Start. */
  #title-screen {
    position: fixed; inset: 0;
    z-index: 9000;
    display: grid; place-items: center;
    transition: opacity 0.5s ease;
    background: #0b2a44;
    overflow: hidden;
  }
  #title-screen.hide {
    opacity: 0;
    pointer-events: none;
  }
  #title-screen #title-art {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    object-position: center;
    z-index: 0;
    pointer-events: none;
  }
  #title-screen #title-text {
    position: absolute; z-index: 1;
    left: 50%; top: 4vh;
    transform: translateX(-50%);
    text-align: center;
    pointer-events: none;
    user-select: none;
    line-height: 0.9;
    font-family: "Fredoka", "Comic Sans MS", system-ui, sans-serif;
    font-weight: 800;
    width: max-content;
  }
  #title-screen #title-text .row1,
  #title-screen #title-text .row2 {
    display: block;
    color: #fff7d6;
    letter-spacing: 2px;
    text-shadow:
      -3px 0 0 #1a3f5c, 3px 0 0 #1a3f5c,
      0 -3px 0 #1a3f5c, 0 3px 0 #1a3f5c,
      -3px -3px 0 #1a3f5c, 3px 3px 0 #1a3f5c,
      -3px 3px 0 #1a3f5c, 3px -3px 0 #1a3f5c,
      0 6px 0 rgba(0,0,0,0.35),
      0 12px 22px rgba(0,0,0,0.55);
    animation: title-bob 2.4s ease-in-out infinite alternate;
  }
  #title-screen #title-text .row1 {
    font-size: clamp(24px, 6.5vw, 76px);
    color: #ffe18f;
    margin-bottom: 4px;
  }
  /* Real alien sprite in the title (was the grey 👽 emoji) */
  #title-screen #title-text .title-alien {
    height: 1.05em;
    vertical-align: -0.15em;
    filter: drop-shadow(0 3px 2px rgba(26,63,92,.55));
  }
  #title-screen #title-text .row2 {
    font-size: clamp(40px, 11vw, 132px);
    color: #ffdf63;
    letter-spacing: 4px;
    animation-delay: 0.2s;
  }
  @keyframes title-bob {
    from { transform: translateY(0)    rotate(-1deg); }
    to   { transform: translateY(-6px) rotate(1deg); }
  }
  #title-screen .title-actions {
    position: absolute; z-index: 1;
    left: 50%; bottom: 10vh;
    transform: translateX(-50%);
    display: flex; flex-direction: column;
    align-items: center; gap: 14px;
  }
  #title-screen .wood-btn {
    appearance: none;
    border: none;
    cursor: pointer;
    font-family: "Georgia", "Times New Roman", serif;
    font-weight: 900;
    font-size: clamp(20px, 3.4vw, 34px);
    letter-spacing: 0.06em;
    color: #4a2a14;
    text-shadow: 0 1px 0 rgba(255, 240, 210, 0.7);
    padding: 0.5em 1.6em;
    border-radius: 18px;
    background: linear-gradient(180deg, #fbe6b1 0%, #f1c878 45%, #d99a3f 100%);
    box-shadow:
      0 0 0 4px #4a2a14,
      0 0 0 7px #b6803a,
      inset 0 4px 0 rgba(255, 250, 220, 0.65),
      inset 0 -6px 8px rgba(120, 60, 20, 0.45),
      0 8px 14px rgba(0, 0, 0, 0.45);
    transition: transform 0.12s ease;
  }
  #title-screen .wood-btn:hover  { transform: translateY(-2px); }
  #title-screen .wood-btn:active { transform: translateY(2px); }
  #title-screen .wood-btn:disabled {
    cursor: wait;
    filter: saturate(0.5) brightness(0.85);
  }
  /* Sparkle hearts that rain across the celebration screen */
  #heart-unlock .sparkle {
    position: absolute;
    font-size: clamp(20px, 4vw, 32px);
    pointer-events: none;
    animation: heartRain 1.8s linear forwards;
  }
  @keyframes heartRain {
    0%   { transform: translateY(-120%) rotate(-20deg); opacity: 0; }
    20%  { opacity: 1; }
    100% { transform: translateY(120vh)  rotate(40deg);  opacity: 0; }
  }

  /* ===== Enemies =====
     Walking enemies pace back and forth between min/max world X.
     Touching from the side hurts; stomping (falling onto from above)
     squashes them and bounces the hero. */
  .enemy {
    position: absolute;
    width: 56px; height: 56px;
    transform: translateX(-50%);
    background-position: bottom center;
    background-size: contain;
    background-repeat: no-repeat;
    object-fit: contain;
    object-position: bottom center;
    image-rendering: pixelated;
    filter: drop-shadow(0 3px 3px rgba(0,0,0,0.25));
    pointer-events: none;
  }
  .enemy.flip-x { transform: translateX(-50%) scaleX(-1); }
  /* Themed background scenery — Kenney prop PNGs (bush/cactus/rock/snow…)
     placed along the ground per biome. Scrolls with the level, sits behind
     the hero. Size is set inline per prop. */
  .scenery {
    position: absolute;
    transform: translateX(-50%);
    background-position: bottom center;
    background-repeat: no-repeat;
    background-size: contain;
    image-rendering: pixelated;
    pointer-events: none;
    z-index: 4;
    filter: drop-shadow(0 4px 4px rgba(0,0,0,0.16));
  }
  .enemy.dead { animation: enemyPop 360ms ease-out forwards; }
  @keyframes enemyPop {
    0%   { opacity: 1; transform: translateX(-50%) scale(1)   translateY(0); }
    100% { opacity: 0; transform: translateX(-50%) scale(1.2) translateY(40px); }
  }

  /* Dust poof where an enemy got squashed (spawned by poofAt, self-removing) */
  .stomp-poof {
    position: absolute;
    width: 52px; height: 52px;
    transform: translate(-50%, 50%);
    border-radius: 50%;
    background: radial-gradient(circle,
      rgba(255,255,255,.95) 0%, rgba(255,241,200,.8) 40%, rgba(255,255,255,0) 70%);
    pointer-events: none;
    z-index: 9;
    animation: stompPoof 420ms ease-out forwards;
  }
  @keyframes stompPoof {
    0%   { opacity: .95; transform: translate(-50%, 50%) scale(.4); }
    100% { opacity: 0;   transform: translate(-50%, 50%) scale(1.7); }
  }

  /* Quick camera bump when the hero takes a hit (shakeStage) */
  #stage.shake { animation: stageShake 200ms ease-out; }
  @keyframes stageShake {
    0%, 100% { transform: translate(0, 0); }
    25%      { transform: translate(-5px, 2px); }
    50%      { transform: translate(4px, -2px); }
    75%      { transform: translate(-2px, 1px); }
  }

  /* ===== Lives HUD (3 hearts top-left, under the coin counter) ===== */
  #lives {
    position: absolute;
    top: 50px;
    left: 12px;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 4px;
    background: rgba(255,255,255,0.85);
    padding: 6px 12px;
    border-radius: 999px;
    border: 3px solid #ff6bb5;
    box-shadow: 0 3px 0 #b558a8;
  }
  #lives .heart {
    width: 24px; height: 24px;
    background: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/hud_heart.png')
                center / contain no-repeat;
    image-rendering: pixelated;
    transition: opacity 200ms;
  }
  #lives .heart.lost { opacity: 0.18; filter: grayscale(1); }
  /* Bonus hearts unlocked by the secret heart treasure — hidden until
     the player has the unlock; flagged in JS by adding .bonus-on
     to #lives. */
  #lives .heart.bonus { display: none; }
  #lives.bonus-on .heart.bonus { display: inline-block; }

  /* Game Over screen — same layout as win-screen but red border + retry */
  #game-over {
    position: absolute;
    inset: 0;
    z-index: 60;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(0,0,0,0.6);
    pointer-events: auto;
  }
  #game-over.show { display: flex; }
  #game-over .card {
    background: linear-gradient(135deg, #fff 0%, #fff0f0 100%);
    border: 6px solid #ff6b6b;
    border-radius: 28px;
    padding: 28px;
    text-align: center;
    max-width: 90vw;
    box-shadow: 0 12px 30px rgba(0,0,0,0.4);
    animation: winPop 320ms cubic-bezier(.5,1.6,.4,1) both;
  }
  #game-over h1   { color: #c2185b; font-size: clamp(28px, 6vw, 48px); margin: 0; }
  #game-over .score { font-size: clamp(20px, 4.5vw, 30px); margin: 12px 0; color: #6a1b6a; font-weight: bold; }
  #game-over button#over-try-again {
    background: linear-gradient(135deg, #ff6b6b, #ffd166);
    border: 5px solid #fff;
    border-radius: 999px;
    padding: 16px 40px;
    font: inherit;
    font-weight: 800;
    font-size: clamp(22px, 5vw, 30px);
    color: #fff;
    cursor: pointer;
    box-shadow: 0 6px 0 #b03b3b;
    text-shadow: 0 2px 2px rgba(0,0,0,0.25);
    letter-spacing: 1px;
  }
  #game-over button#over-try-again:active { translate: 0 4px; box-shadow: 0 2px 0 #b03b3b; }
  #game-over button#over-map {
    background: linear-gradient(135deg, #ffa64d, #ff7d33);
    border: 5px solid #fff; border-radius: 999px;
    padding: 12px 28px; font: inherit; font-weight: 800;
    font-size: clamp(18px, 4vw, 24px); color: #fff; cursor: pointer;
    box-shadow: 0 6px 0 #d85f1e; text-shadow: 0 2px 2px rgba(0,0,0,0.25);
    letter-spacing: 1px; margin-top: 8px;
  }
  #game-over button#over-map:active { translate: 0 4px; box-shadow: 0 2px 0 #d85f1e; }

  /* Win screen overlay — shows when hero touches the goal flag. */
  #win-screen {
    position: absolute;
    inset: 0;
    z-index: 60;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(0,0,0,0.55);
    pointer-events: auto;
  }
  #win-screen.show { display: flex; }
  #win-screen .card {
    background: linear-gradient(135deg, #fff 0%, #fff5f9 100%);
    border: 6px solid #ffd166;
    border-radius: 28px;
    padding: 28px;
    text-align: center;
    max-width: 90vw;
    box-shadow: 0 12px 30px rgba(0,0,0,0.4);
    animation: winPop 320ms cubic-bezier(.5,1.6,.4,1) both;
  }
  @keyframes winPop {
    from { transform: scale(0.5); opacity: 0; }
    to   { transform: scale(1);   opacity: 1; }
  }
  #win-screen h1   { color: #ff6bb5; font-size: clamp(28px, 6vw, 48px); margin: 0; }
  #win-screen .score { font-size: clamp(22px, 5vw, 34px); margin: 12px 0; color: #6a1b6a; font-weight: bold; }
  #win-screen button#win-play-again {
    background: linear-gradient(135deg, #ff6bb5, #ffd166);
    border: 5px solid #fff;
    border-radius: 999px;
    padding: 16px 40px;
    font: inherit;
    font-weight: 800;
    font-size: clamp(22px, 5vw, 30px);
    color: #fff;
    cursor: pointer;
    box-shadow: 0 6px 0 #b558a8;
    text-shadow: 0 2px 2px rgba(0,0,0,0.25);
    letter-spacing: 1px;
  }
  #win-screen button#win-play-again:active { translate: 0 4px; box-shadow: 0 2px 0 #b558a8; }

  /* Both end-screen panels share this layout: big play-again pill on
     top, blue Home pill underneath. The Home button is a real <a>
     so the browser handles the nav itself — localStorage already
     auto-saves the avatar + heart-unlock so nothing extra to do. */
  #win-screen .end-buttons,
  #game-over .end-buttons {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    margin-top: 16px;
  }
  #win-screen .home-link,
  #game-over .home-link {
    display: inline-block;
    background: linear-gradient(135deg, #66a6ff, #4d8fd8);
    border: 4px solid #fff;
    border-radius: 999px;
    padding: 12px 32px;
    font: inherit;
    font-weight: 800;
    font-size: clamp(18px, 4vw, 24px);
    color: #fff;
    text-decoration: none;
    cursor: pointer;
    box-shadow: 0 5px 0 #2d6db8;
    text-shadow: 0 2px 2px rgba(0,0,0,0.25);
    letter-spacing: 1px;
  }
  #win-screen .home-link:active,
  #game-over .home-link:active { translate: 0 4px; box-shadow: 0 2px 0 #2d6db8; }

  /* World transition card — shown briefly between worlds. Reuses the
     win-screen overlay look with a happy green frame. */
  #next-world {
    position: absolute; inset: 0; z-index: 60;
    display: none; align-items: center; justify-content: center;
    padding: 16px; background: rgba(0,0,0,0.45); pointer-events: auto;
    cursor: pointer;
  }
  #next-world.show { display: flex; }
  #next-world .card {
    background: linear-gradient(135deg, #fff 0%, #f3fff0 100%);
    border: 6px solid #7ed07a; border-radius: 28px; padding: 28px 36px;
    text-align: center; max-width: 90vw;
    box-shadow: 0 12px 30px rgba(0,0,0,0.4);
    animation: winPop 320ms cubic-bezier(.5,1.6,.4,1) both;
  }
  #next-world h1   { color: #3a9b46; font-size: clamp(26px, 6vw, 44px); margin: 0; }
  #next-world .sub { font-size: clamp(18px, 4vw, 26px); margin: 12px 0 4px; color: #4a7a2a; font-weight: bold; }
  #next-world .hint{ font-size: clamp(14px, 3vw, 18px); color: #6a8a5a; margin-top: 10px; }

  /* World X/6 indicator — top-center pill. */
  #world-hud {
    position: absolute; top: calc(env(safe-area-inset-top,0px) + 90px); left: 50%; transform: translateX(-50%);
    z-index: 30; background: rgba(255,255,255,0.92);
    padding: 5px 16px; border-radius: 999px; border: 3px solid #7ed07a;
    box-shadow: 0 3px 0 #4a8a46; font-weight: 800; color: #2f6b34;
    font-size: 16px; pointer-events: none;
  }

  /* ===== HUD =====
     Coin counter sits top-LEFT under the hearts row — the old top-RIGHT
     spot was covered by the site-wide Who's-Online/Chat pill. */
  #hud {
    position: absolute;
    top: 96px; left: 12px;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(255,255,255,0.92);
    padding: 6px 14px;
    border-radius: 999px;
    border: 3px solid #ffd166;
    box-shadow: 0 3px 0 #b07020;
    font-size: 18px;
    font-weight: bold;
    color: #6b3a00;
  }
  #hud .coin-icon { width: 22px; height: 22px; image-rendering: pixelated; }
  /* topbar.css pushes generic #hud pills to top:64 right-ish with a
     body:has(#kg-topbar) rule — out-rank it: ours lives under the hearts. */
  body:has(#kg-topbar) #stage #hud { top: 96px; left: 12px; right: auto; }

  /* ===== Picker ===== */
  #picker {
    position: absolute; inset: 0;
    z-index: 50;
    background: rgba(0,0,0,0.55);
    display: flex; align-items: center; justify-content: center;
    padding: 16px;
  }
  #picker.hide { display: none; }
  #picker .card {
    background: #fff;
    border: 6px solid #ff6bb5;
    border-radius: 28px;
    padding: 22px;
    text-align: center;
    max-width: 480px; width: 100%;
    box-shadow: 0 10px 0 #b558a8;
  }
  #picker h1 { color: #ff6bb5; font-size: clamp(22px,5vw,30px); }
  #picker .row { display: flex; justify-content: center; gap: 18px; margin: 14px 0; flex-wrap: wrap; }
  #picker button.pick {
    background: #fff8e1;
    border: 4px solid #ffd166;
    border-radius: 22px;
    padding: 8px;
    cursor: pointer;
    font-family: inherit;
    width: clamp(96px, 24vw, 140px);
    box-shadow: 0 4px 0 #d4a12a;
  }
  #picker button.pick:active { translate: 0 3px; box-shadow: 0 2px 0 #d4a12a; }
  #picker button.pick img { width: 100%; aspect-ratio: 1; object-fit: contain; }
  #picker button.pick .name { font-weight: bold; color: #b34d8a; margin-top: 6px; }
  #picker .tip { font-size: 13px; color: #5a4470; margin-top: 10px; }

  /* ===== Touch controls ===== */
  #touch {
    position: absolute;
    bottom: 16px; left: 0; right: 0;
    display: none;
    justify-content: space-between;
    pointer-events: none;
    z-index: 20;
    padding: 0 12px;
  }
  @media (pointer: coarse) {
    #touch { display: flex; }
  }
  .touch-side, .touch-jump-wrap { display: flex; gap: 12px; pointer-events: none; }
  .tbtn {
    width: 64px; height: 64px;
    border-radius: 50%;
    /* clip-path matches the visual circle so corners of the bounding
       box don't receive taps — hitbox = button shape, no phantom taps
       in the empty corners. The press shadow uses filter: drop-shadow
       (NOT box-shadow) so it renders outside the clip and follows the
       circular silhouette. */
    clip-path: circle(50%);
    border: 4px solid #ff6bb5;
    background: rgba(255,255,255,0.9);
    filter: drop-shadow(0 4px 0 #b558a8);
    color: #ff6bb5;
    font-size: 28px;
    font-weight: bold;
    pointer-events: auto;
    user-select: none;
    display: flex; align-items: center; justify-content: center;
    cursor: pointer;
  }
  .tbtn:active { translate: 0 2px; filter: drop-shadow(0 2px 0 #b558a8); }
  .tbtn.jump {
    width: 76px; height: 76px;
    background: linear-gradient(135deg, #ff6bb5, #ffd166);
    color: #fff;
    border-color: #fff;
    filter: drop-shadow(0 4px 0 #c94d79);
  }
  .tbtn.jump:active { filter: drop-shadow(0 2px 0 #c94d79); }
  .tbtn.fire {
    background: linear-gradient(135deg, #ff8a3b, #ffd166);
    color: #fff;
    border-color: #fff;
    filter: drop-shadow(0 4px 0 #b04500);
    font-size: 26px;
    /* Disarmed by default — needs the red-gem pickup to light up. */
    opacity: 0.35;
    transition: opacity 200ms;
  }
  .tbtn.fire.armed { opacity: 1; }
  .tbtn.fire:active { filter: drop-shadow(0 2px 0 #b04500); }
  /* Left/Right buttons wear Kenney's clearer direction arrows. */
  #btn-left, #btn-right {
    color: transparent;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 58%;
  }
  #btn-left  { background-image: url('../assets/kenney/mobile-controls/direction_left.png'); }
  #btn-right { background-image: url('../assets/kenney/mobile-controls/direction_right.png'); }
  /* Climb buttons — only appear while standing at a ladder (the loop
     toggles .near-ladder on #touch), so the HUD stays clean elsewhere. */
  .touch-climb { display: none; flex-direction: column; gap: 10px; pointer-events: none; }
  #touch.near-ladder .touch-climb { display: flex; }
  .tbtn.climb {
    width: 58px; height: 58px;
    background: linear-gradient(135deg, #7ce3a8, #ffd166);
    color: #fff;
    border-color: #fff;
    filter: drop-shadow(0 4px 0 #2f9c63);
  }
  .tbtn.climb:active { filter: drop-shadow(0 2px 0 #2f9c63); }
  /* Jump / climb / fire buttons wear real art too (matching ◀▶): the Kenney
     arrow rotated for up/down, and the game's own fireball sprite for fire.
     The glyph text stays for screen readers but paints transparent. */
  .tbtn.jump, .tbtn.climb, .tbtn.fire { color: transparent; position: relative; }
  .tbtn.jump::after, .tbtn.climb::after, .tbtn.fire::after {
    content: "";
    position: absolute; inset: 0;
    background: center / 54% no-repeat;
    pointer-events: none;
  }
  .tbtn.jump::after, #btn-up::after {
    background-image: url('../assets/kenney/mobile-controls/direction_left.png');
    transform: rotate(90deg);           /* left arrow rotated → points UP */
  }
  #btn-down::after {
    background-image: url('../assets/kenney/mobile-controls/direction_left.png');
    transform: rotate(-90deg);          /* → points DOWN */
  }
  .tbtn.fire::after {
    background-image: url('../assets/kenney/newplatformerpack1.1/Sprites/Tiles/Default/fireball.png');
    background-size: 62%;
  }

  /* The shared topbar (assets/topbar.js) mounts its own Home + Music/SFX
     row, so the game's hand-rolled copies stay in the DOM only as proxy
     targets (topbar clicks #music-btn to drive the game's music state) —
     never shown. */
  #home-btn, #music-btn { display: none !important; }

  #home-btn {
    position: absolute;
    top: 12px; right: 12px;
    z-index: 30;
    background: #fff;
    border: 3px solid #ff6bb5;
    border-radius: 999px;
    padding: 6px 14px;
    color: #ff6bb5;
    font-family: inherit;
    font-weight: bold;
    text-decoration: none;
    box-shadow: 0 3px 0 #b558a8;
    font-size: 14px;
  }
  #home-btn:active { translate: 0 2px; box-shadow: 0 1px 0 #b558a8; }
  #music-btn {
    position: absolute;
    top: 12px; right: 96px;     /* sits left of Home */
    z-index: 30;
    background: #fff;
    border: 3px solid #66d2ff;
    border-radius: 999px;
    padding: 6px 12px;
    color: #2a86b3;
    font-family: inherit;
    font-weight: bold;
    box-shadow: 0 3px 0 #2a86b3;
    font-size: 16px;
    cursor: pointer;
  }
  #music-btn:active { translate: 0 2px; box-shadow: 0 1px 0 #2a86b3; }
  #music-btn.muted { opacity: 0.55; }

  #tip {
    position: absolute;
    bottom: 16px; left: 50%;
    transform: translateX(-50%);
    z-index: 9;
    background: rgba(255,255,255,0.9);
    padding: 8px 16px;
    border-radius: 999px;
    border: 3px solid #ffd166;
    font-size: 14px;
    font-weight: bold;
    color: #5a4470;
    box-shadow: 0 3px 0 #d4a12a;
    transition: opacity 1s;
    white-space: nowrap;
  }
  #tip.fade { opacity: 0; pointer-events: none; }
  /* Touch devices use the on-screen ◀▶/jump buttons, so the keyboard hint is
     pointless clutter there — hide it. (Desktop keeps it, bottom-centre.) */
  @media (hover: none) and (pointer: coarse) { #tip { display: none; } }

  /* ===== World map (Super Mario World-style level select) ===== */
  #world-map{
    position:fixed; inset:0; z-index:8000; display:none;
    flex-direction:column; align-items:center; justify-content:center;
    gap:clamp(12px,2.4vh,24px);
    background:linear-gradient(180deg,#bfe6ff,#e9d9f7);
    overflow:auto; padding:env(safe-area-inset-top) 0 0;
  }
  #world-map.show{ display:flex; }
  #world-map .map-head{
    position:absolute; top:calc(env(safe-area-inset-top,0px) + 54px); left:50%; transform:translateX(-50%);
    z-index:3; text-align:center; pointer-events:none;
    font-family:'Baloo 2',system-ui,sans-serif;
    color:#fff; text-shadow:0 3px 0 rgba(120,70,160,.55), 0 0 12px rgba(255,255,255,.5);
    font-weight:800; font-size:clamp(18px,4.4vw,34px); letter-spacing:.5px;
  }
  #world-map .map-head small{ display:block; font-size:.5em; font-weight:700; opacity:.95; }
  /* Letterboxed frame: fixed 3:2 so node % always lines up with the art. */
  .map-frame{
    position:relative; width:min(96vw, 120vh); aspect-ratio:3/2;
    max-width:1200px; margin:0; flex-shrink:0;
    border-radius:22px; overflow:hidden;
    box-shadow:0 16px 48px rgba(80,40,120,.35), 0 0 0 5px #fff, 0 0 0 9px rgba(255,255,255,.5);
  }
  .map-frame > img.map-bg{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; }
  .map-frame > svg.map-paths{ position:absolute; inset:0; width:100%; height:100%; pointer-events:none; }
  .map-paths path{ fill:none; stroke-linecap:round; stroke-dasharray:1.4 3.2;
    stroke:rgba(255,255,255,.45); stroke-width:1.1; }
  .map-paths path.lit{ stroke:#fff; stroke-dasharray:none; stroke-width:1.5;
    filter:drop-shadow(0 0 2px rgba(255,210,120,.9)); }
  /* Nodes */
  .map-node{
    position:absolute; transform:translate(-50%,-50%);
    width:clamp(34px,6.2vw,52px); height:clamp(34px,6.2vw,52px);
    border-radius:50%; border:none; padding:0; cursor:pointer;
    display:flex; align-items:center; justify-content:center;
    font-size:clamp(16px,3.2vw,26px); line-height:1;
    background:radial-gradient(circle at 35% 30%,#fff,#ffe7a3 60%,#ffc24d);
    box-shadow:0 4px 0 #e89c1e, 0 6px 10px rgba(0,0,0,.25);
    -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  }
  .map-node .lbl{
    position:absolute; bottom:108%; left:50%; transform:translateX(-50%);
    white-space:nowrap; font-family:'Baloo 2',system-ui,sans-serif; font-weight:800;
    font-size:clamp(9px,1.9vw,13px); color:#5a3a86;
    background:rgba(255,255,255,.86); padding:1px 7px; border-radius:999px;
    box-shadow:0 2px 5px rgba(0,0,0,.18); pointer-events:none;
  }
  .map-node.locked{ cursor:default;
    background:radial-gradient(circle at 35% 30%,#dcdce6,#a9a9bd 70%,#8a8aa0);
    box-shadow:0 4px 0 #6f6f86, 0 6px 10px rgba(0,0,0,.22); }
  .map-node.locked .lbl{ display:none; }
  .map-node.unlocked{ animation:nodePulse 1.4s ease-in-out infinite; }
  .map-node.done{ background:radial-gradient(circle at 35% 30%,#fff,#bdf0c4 60%,#5fd07e);
    box-shadow:0 4px 0 #2faa55, 0 6px 10px rgba(0,0,0,.25); }
  .map-node .badge{ position:absolute; top:-8px; right:-6px; font-size:.62em;
    filter:drop-shadow(0 1px 1px rgba(0,0,0,.3)); }
  @keyframes nodePulse{ 0%,100%{ transform:translate(-50%,-50%) scale(1);}
    50%{ transform:translate(-50%,-50%) scale(1.16);} }
  /* Hero token riding the map */
  #map-token{
    position:absolute; transform:translate(-50%,-92%); z-index:4;
    width:clamp(30px,5.4vw,46px); height:auto; pointer-events:none;
    transition:left .34s ease-in-out, top .34s ease-in-out;
    filter:drop-shadow(0 4px 4px rgba(0,0,0,.3)); animation:tokenBob 1s ease-in-out infinite;
  }
  @keyframes tokenBob{ 0%,100%{ margin-top:0;} 50%{ margin-top:-5px;} }
  /* Big Start button + the picked level's name, stacked below the map. */
  .map-start-name{
    font-family:'Baloo 2',system-ui,sans-serif; font-weight:800; flex-shrink:0;
    font-size:clamp(18px,4.4vw,28px); color:#7a3a9e; text-align:center; line-height:1;
    text-shadow:0 2px 0 rgba(255,255,255,.7);
  }
  .map-start{
    font-family:'Baloo 2',system-ui,sans-serif; font-weight:900; flex-shrink:0;
    font-size:clamp(22px,5.2vw,34px); color:#5a3a10; letter-spacing:.5px;
    background:linear-gradient(180deg,#ffd24d,#ffb01e);
    border:none; border-radius:999px; cursor:pointer;
    padding:clamp(12px,1.8vh,18px) clamp(40px,9vw,72px);
    box-shadow:0 6px 0 #d98e12, 0 10px 20px rgba(120,80,20,.3), 0 0 0 4px rgba(255,255,255,.6);
    -webkit-tap-highlight-color:transparent; touch-action:manipulation;
    animation:startPulse 1.5s ease-in-out infinite;
  }
  .map-start:active{ transform:translateY(4px);
    box-shadow:0 2px 0 #d98e12, 0 4px 10px rgba(120,80,20,.3), 0 0 0 4px rgba(255,255,255,.6); }
  @keyframes startPulse{ 0%,100%{ transform:scale(1);} 50%{ transform:scale(1.05);} }
  /* Shop marker on the map (a shopkeeper destination). */
  .shop-node{ position:absolute; top:calc(env(safe-area-inset-top,0px) + 132px); right:12px; z-index:5; border:none; cursor:pointer;
    background:rgba(255,255,255,.92); border-radius:18px; padding:6px 10px 4px;
    display:flex; flex-direction:column; align-items:center; gap:1px;
    box-shadow:0 4px 0 #c99a3a, 0 6px 12px rgba(0,0,0,.25);
    -webkit-tap-highlight-color:transparent; touch-action:manipulation;
    animation:shopBob 1.6s ease-in-out infinite; }
  .shop-node img{ width:clamp(40px,9vw,56px); height:clamp(40px,9vw,56px); object-fit:contain; }
  .shop-node .lbl{ font-family:'Baloo 2',system-ui,sans-serif; font-weight:800; font-size:12px; color:#7a3a9e; }
  .shop-node:active{ animation:none; transform:translateY(3px); box-shadow:0 1px 0 #c99a3a; }
  @keyframes shopBob{ 0%,100%{ transform:translateY(0);} 50%{ transform:translateY(-3px);} }
  /* Coin shop overlay (mirrors #picker). */
  #shop{ position:fixed; inset:0; z-index:9000; background:rgba(0,0,0,.55);
    display:flex; align-items:center; justify-content:center; padding:16px; }
  #shop.hide{ display:none; }
  #shop .card{ background:#fff; border:6px solid #ffd166; border-radius:28px; padding:18px;
    text-align:center; max-width:540px; width:100%; box-shadow:0 10px 0 #c99a3a;
    max-height:94vh; overflow:auto; }
  #shop h1{ color:#b06b00; font-size:clamp(22px,5vw,30px); margin:0 0 2px; }
  #shop .wallet{ font-weight:900; color:#6b3a00; font-size:18px; margin-bottom:6px; }
  #shop .shopkeeper{ width:120px; max-width:38%; margin:0 auto 4px; display:block; border-radius:16px; }
  #shop .row{ display:flex; justify-content:center; gap:12px; margin:10px 0; flex-wrap:wrap; }
  #shop .pick{ background:#fff8e1; border:4px solid #ffd166; border-radius:22px; padding:8px;
    width:clamp(118px,40vw,152px); box-shadow:0 4px 0 #d4a12a; font-family:inherit;
    display:flex; flex-direction:column; align-items:center; gap:2px; }
  #shop .pick .icon{ font-size:38px; line-height:1; height:56px; display:flex; align-items:center; justify-content:center; }
  #shop .pick .icon img{ width:56px; height:56px; object-fit:contain; }
  #shop .pick .name{ font-weight:800; color:#b06b00; font-size:14px; }
  #shop .pick .blurb{ font-size:11px; color:#9a6ab5; line-height:1.15; min-height:26px; }
  #shop .pick .tier{ font-size:13px; color:#7a5a96; letter-spacing:2px; }
  #shop .pick .buy{ margin-top:6px; border:none; border-radius:999px; padding:7px 14px;
    font-family:inherit; font-weight:800; font-size:14px; cursor:pointer;
    background:linear-gradient(180deg,#ffd24d,#ffb01e); color:#5a3a10; box-shadow:0 3px 0 #d98e12;
    -webkit-tap-highlight-color:transparent; touch-action:manipulation; }
  #shop .pick .buy:active{ transform:translateY(2px); box-shadow:0 1px 0 #d98e12; }
  #shop .pick .buy:disabled{ filter:grayscale(.6); opacity:.55; cursor:default; box-shadow:0 3px 0 #b0b0b0; }
  /* Level-name toast — shows for 3s when a level starts. */
  #level-toast{
    position:fixed; top:40%; left:50%;
    transform:translateX(-50%) translateY(-14px); z-index:7000;
    pointer-events:none; opacity:0; white-space:nowrap;
    font-family:'Baloo 2',system-ui,sans-serif; font-weight:800;
    font-size:clamp(18px,4.6vw,30px); color:#fff;
    background:linear-gradient(180deg, rgba(124,72,176,.94), rgba(92,52,150,.94));
    padding:10px 24px; border-radius:999px;
    box-shadow:0 8px 24px rgba(70,30,120,.4), 0 0 0 4px rgba(255,255,255,.5);
    text-shadow:0 2px 0 rgba(0,0,0,.22);
    transition:opacity .35s ease, transform .35s ease;
  }
  #level-toast.show{ opacity:1; transform:translateX(-50%) translateY(0); }
  /* Little 🗺 Map button shown during a level (toggled by JS). */
  #map-btn{
    display:none;
    position:absolute; left:50%; transform:translateX(-50%); top:calc(env(safe-area-inset-top,0px) + 50px); z-index:60;
    font:700 14px 'Baloo 2',system-ui,sans-serif; color:#fff; text-decoration:none;
    background:linear-gradient(180deg,#ffa64d,#ff7d33); border:none; border-radius:999px;
    padding:6px 14px; box-shadow:0 3px 0 #d85f1e, 0 4px 8px rgba(0,0,0,.25);
    cursor:pointer; -webkit-tap-highlight-color:transparent;
  }
