JSFiddle - React, Tailwind, and code Playground

HTML

<main id="leafCatcherApp" class="game-shell">
  <header class="game-header">
    <span class="eyebrow">GREEN WEEK CHALLENGE</span>
    <h1>Catch the Green Leaves</h1>
    <p>Move the basket, catch the good items, and build your best score!</p>
  </header>

  <section class="game-card" aria-label="Catch the Green Leaves game">
    <div class="hud">
      <div class="hud-box">
        <span class="hud-label">TIME</span>
        <strong><span id="timeValue">30</span>s</strong>
      </div>

      <div class="hud-box score-box">
        <span class="hud-label">SCORE</span>
        <strong id="scoreValue">0</strong>
      </div>

      <div id="effectBadge" class="effect-badge" aria-live="polite">
        Normal speed
      </div>
    </div>

    <div class="time-track" aria-hidden="true">
      <div id="timeFill" class="time-fill"></div>
    </div>

    <div id="playfield" class="playfield">
      <div class="cloud cloud-one" aria-hidden="true"></div>
      <div class="cloud cloud-two" aria-hidden="true"></div>
      <div class="hill hill-back" aria-hidden="true"></div>
      <div class="hill hill-front" aria-hidden="true"></div>

      <section id="startOverlay" class="overlay">
        <div class="overlay-card">
          <div class="big-icon" aria-hidden="true">🍃</div>
          <h2>Ready to Catch?</h2>
          <p>Use the buttons or your keyboard to move the basket.</p>
          <button id="startButton" class="primary-button" type="button">
            Start Game
          </button>
        </div>
      </section>

      <section id="resultOverlay" class="overlay hidden" aria-live="polite">
        <div class="overlay-card result-card">
          <span class="eyebrow">CHALLENGE COMPLETE</span>
          <h2 id="resultRank">Leaf Collector</h2>

          <p class="final-score">
            <strong id="finalScore">0</strong>
            <span> points</span>
          </p>

          <p...

CSS

:root {
  --forest: #164b35;
  --deep-green: #0f3727;
  --leaf: #39a96b;
  --lime: #94d65c;
  --cream: #fffdf2;
  --sun: #ffd45a;
  --orange: #ff9d42;
  --ink: #173429;
  --muted: #60776d;
  --card: rgba(255, 255, 255, 0.94);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-width: 300px;
  color: var(--ink);
  background:
    radial-gradient(
      circle at 15% 10%,
      rgba(255, 255, 255, 0.8),
      transparent 26%
    ),
    linear-gradient(145deg, #e8f7d9 0%, #c9efcf 48%, #b7e6cc 100%);
  font-family:
    Inter,
    ui-rounded,
    "Trebuchet MS",
    system-ui,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    sans-serif;
}

button {
  font: inherit;
}

.game-shell {
  width: min(760px, 100%);
  margin: 0 auto;
  padding: 22px 14px 28px;
}

.game-header {
  margin-bottom: 14px;
  text-align: center;
}

.eyebrow {
  display: inline-block;
  margin-bottom: 7px;
  color: #2f7f4f;
  font-size: 0.75rem;
  font-weight: 900;
  letter-spacing: 0.15em;
}

.game-header h1 {
  margin: 0;
  color: var(--forest);
  font-size: clamp(1.75rem, 5vw, 2.65rem);
  line-height: 1.05;
}

.game-header p {
  max-width: 540px;
  margin: 9px auto 0;
  color: var(--muted);
  font-size: 0.98rem;
}

.game-card {
  overflow: hidden;
  border: 1px solid rgba(22, 75, 53, 0.13);
  border-radius: 24px;
  background: var(--card);
  box-shadow: 0 18px 50px rgba(31, 93, 62, 0.17);
}

.hud {
  display: grid;
  grid-template-columns: 1fr 1fr minmax(125px, 1.4fr);
  gap: 8px;
  align-items: stretch;
  padding: 12px 12px 9px;
  background: #fffef8;
}

.hud-box,
.effect-badge {
  display: flex;
  min-height: 52px;
  align-items: center;
  justify-content: center;
  border: 1px solid #dcebdc;
  border-radius: 14px;
  background: #f6fbf2;
}

.hud-box {
  flex-direction: column;
}

.hud-label {
  color: #73907e;
  font-size: 0.65rem;
  font-weight: 900;
 ...

JavaScript

(() => {
  const GAME_SECONDS = 30;
  const BASKET_WIDTH = 98;
  const BASKET_HEIGHT = 62;

  const NORMAL_SPEED = 330;
  const SLOW_SPEED = 185;
  const BOOST_SPEED = 500;
  const EFFECT_SECONDS = 5;

  const ITEM_TYPES = [
    {
      key: "leaf",
      emoji: "🍃",
      label: "Green Leaf",
      points: 1,
      weight: 47
    },
    {
      key: "apple",
      emoji: "🍏",
      label: "Green Apple",
      points: 3,
      weight: 16
    },
    {
      key: "dry",
      emoji: "🍂",
      label: "Dry Leaf",
      points: -2,
      weight: 17
    },
    {
      key: "twig",
      emoji: "🐛",
      label: "Twig",
      effect: "slow",
      weight: 10
    },
    {
      key: "caterpillar",
      emoji: "☘️",
      label: "Caterpillar",
      effect: "boost",
      weight: 10
    }
  ];

  const app = document.querySelector("#leafCatcherApp");
  if (!app) return;

  const playfield = app.querySelector("#playfield");
  const basket = app.querySelector("#basket");
  const startOverlay = app.querySelector("#startOverlay");
  const resultOverlay = app.querySelector("#resultOverlay");

  const startButton = app.querySelector("#startButton");
  const playAgainButton = app.querySelector("#playAgainButton");
  const copyButton = app.querySelector("#copyButton");
  const copyStatus = app.querySelector("#copyStatus");

  const leftButton = app.querySelector("#leftButton");
  const rightButton = app.querySelector("#rightButton");

  const timeValue = app.querySelector("#timeValue");
  const scoreValue = app.querySelector("#scoreValue");
  const timeFill = app.querySelector("#timeFill");
  const effectBadge = app.querySelector("#effectBadge");

  const finalScore = app.querySelector("#finalScore");
  const resultRank = app.querySelector("#resultRank");
  const resultMessage = app.querySelector("#resultMessage");
  const scorePopups = app.querySelector("#scorePopups");

  let score = 0;
  let timeLeft...