// Uses global React when run in browser via CDN (no bundler needed)

// Optional: configure head images
// Closed mouth (default) and open mouth variants
const HEAD_IMAGE_CLOSED_SRC = "alec_closed.png";
const HEAD_IMAGE_OPEN_SRC = "alec_open_2.png";
// Optional enemy art (leave blank to use fallback drawing)
const ENEMY_HEAD_IMAGE_CLOSED_SRC = "Adam_Closed.png";
const ENEMY_HEAD_IMAGE_OPEN_SRC = "Adam_Open.png";
// Single place to tweak how far the enemy head/tongue anchor sits from its body
const ENEMY_HEAD_OFFSET_SCALE = 0.24;
// Fine-tune where the tongue exits Adam's head (px along facing + perpendicular)
const ENEMY_TONGUE_FORWARD_OFFSET = 23;
const ENEMY_TONGUE_SIDE_OFFSET = 14;

const DEFAULT_DIFFICULTY = "easy";
const DIFFICULTY_PRESETS = {
  easy: {
    label: "Easy",
    enemy: { maxSpeed: 120, tongue: { reach: 200, extendTime: 0.34, retractTime: 0.46, cooldown: 3.6 } },
  },
  medium: {
    label: "Medium",
    enemy: { maxSpeed: 155, tongue: { reach: 210, extendTime: 0.28, retractTime: 0.38, cooldown: 3.1 } },
  },
  hard: {
    label: "Hard",
    enemy: { maxSpeed: 180, tongue: { reach: 220, extendTime: 0.24, retractTime: 0.34, cooldown: 2.7 } },
  },
};

// --- Sound Manager (Web Audio API) ---
const SoundManager = {
  ctx: null,
  init() {
    if (!this.ctx) {
      const AudioContext = window.AudioContext || window.webkitAudioContext;
      if (AudioContext) this.ctx = new AudioContext();
    }
    if (this.ctx && this.ctx.state === 'suspended') {
      this.ctx.resume();
    }
  },
  playTone(freq, type, duration, vol = 0.1) {
    if (!this.ctx) return;
    const osc = this.ctx.createOscillator();
    const gain = this.ctx.createGain();
    osc.type = type;
    osc.frequency.setValueAtTime(freq, this.ctx.currentTime);
    gain.gain.setValueAtTime(vol, this.ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + duration);
    osc.connect(gain);
    gain.connect(this.ctx.destination);
    osc.start();
    osc.stop(this.ctx.currentTime + duration);
  },
  playChomp() {
    if (!this.ctx) return;
    // Quick low pitch sweep
    const osc = this.ctx.createOscillator();
    const gain = this.ctx.createGain();
    osc.frequency.setValueAtTime(150, this.ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(50, this.ctx.currentTime + 0.1);
    gain.gain.setValueAtTime(0.3, this.ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.1);
    osc.connect(gain);
    gain.connect(this.ctx.destination);
    osc.start();
    osc.stop(this.ctx.currentTime + 0.1);
  },
  playTongue() {
    if (!this.ctx) return;
    // Zap sound
    const osc = this.ctx.createOscillator();
    const gain = this.ctx.createGain();
    osc.type = 'sawtooth';
    osc.frequency.setValueAtTime(400, this.ctx.currentTime);
    osc.frequency.linearRampToValueAtTime(100, this.ctx.currentTime + 0.3);
    gain.gain.setValueAtTime(0.1, this.ctx.currentTime);
    gain.gain.linearRampToValueAtTime(0.01, this.ctx.currentTime + 0.3);
    osc.connect(gain);
    gain.connect(this.ctx.destination);
    osc.start();
    osc.stop(this.ctx.currentTime + 0.3);
  },
  playHit() {
    if (!this.ctx) return;
    // Crunch/Thud
    const osc = this.ctx.createOscillator();
    const gain = this.ctx.createGain();
    osc.type = 'square';
    osc.frequency.setValueAtTime(100, this.ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(20, this.ctx.currentTime + 0.15);
    gain.gain.setValueAtTime(0.2, this.ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.15);
    osc.connect(gain);
    gain.connect(this.ctx.destination);
    osc.start();
    osc.stop(this.ctx.currentTime + 0.15);
  },
  playGameOver() {
    if (!this.ctx) return;
    // Single low thud instead of melody
    const osc = this.ctx.createOscillator();
    const gain = this.ctx.createGain();
    osc.type = 'triangle';
    osc.frequency.setValueAtTime(100, this.ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(30, this.ctx.currentTime + 0.5);
    gain.gain.setValueAtTime(0.3, this.ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.5);
    osc.connect(gain);
    gain.connect(this.ctx.destination);
    osc.start();
    osc.stop(this.ctx.currentTime + 0.5);
  },
  playCollect() {
    if (!this.ctx) return;
    // High pitched chime
    const osc = this.ctx.createOscillator();
    const gain = this.ctx.createGain();
    osc.type = 'sine';
    osc.frequency.setValueAtTime(800, this.ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(1200, this.ctx.currentTime + 0.1);
    gain.gain.setValueAtTime(0.1, this.ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + 0.3);
    osc.connect(gain);
    gain.connect(this.ctx.destination);
    osc.start();
    osc.stop(this.ctx.currentTime + 0.3);
  }
};

function HippoSnackdown() {
  // A tiny top‑down chomp game inspired by marble‑munchers.
  // Controls: Space (or tap) to chomp. R to restart. P to pause.
  const canvasRef = React.useRef(null);
  const containerRef = React.useRef(null);
  const [size, setSize] = React.useState({ w: 960, h: 600 });
  const [phase, setPhase] = React.useState("menu"); // menu | countdown | playing | paused | swallowed | gameover
  const [countdown, setCountdown] = React.useState(3);
  const [score, setScore] = React.useState(0);
  const [timeLeft, setTimeLeft] = React.useState(45); // seconds
  const [gameOverReason, setGameOverReason] = React.useState("time"); // time | swallowed
  const [difficulty, setDifficulty] = React.useState(DEFAULT_DIFFICULTY);
  const difficultyPreset = DIFFICULTY_PRESETS[difficulty] ?? DIFFICULTY_PRESETS[DEFAULT_DIFFICULTY];
  const defaultPreset = DIFFICULTY_PRESETS[DEFAULT_DIFFICULTY];
  const rafRef = React.useRef(null);
  const headClosedImgRef = React.useRef(null);
  const headOpenImgRef = React.useRef(null);
  const enemyHeadClosedImgRef = React.useRef(null);
  const enemyHeadOpenImgRef = React.useRef(null);
  const inputRef = React.useRef({ up: false, down: false, left: false, right: false });

  // Persistent game state stored in a ref to avoid re‑renders during the loop
  const gameRef = React.useRef({
    lastT: 0,
    marbles: [],
    spawnTimer: 0,
    hippo: {
      x: 0,
      y: 0,
      vx: 0,
      vy: 0,
      dirAngle: -Math.PI / 2, // up
      chompAngle: -Math.PI / 2, // locked during shot
      baseR: 46,
      mouthR: 34,
      neckLen: 0, // current
      neckMax: 170, // how far the mouth extends
      chomp: {
        active: false,
        t: 0, // progress seconds
        outTime: 0.22,
        inTime: 0.28,
      },
    },
    enemy: {
      x: 0,
      y: 0,
      vx: 0,
      vy: 0,
      dirAngle: 0,
      baseR: 42,
      mouthR: 52,
      maxSpeed: defaultPreset.enemy.maxSpeed,
      accel: 4,
      drag: 0.5,
      tongue: {
        active: false,
        angle: 0,
        len: 0,
        t: 0,
        extendTime: defaultPreset.enemy.tongue.extendTime,
        retractTime: defaultPreset.enemy.tongue.retractTime,
        reach: defaultPreset.enemy.tongue.reach,
        cooldown: defaultPreset.enemy.tongue.cooldown,
        cooldownTimer: 0,
        hit: false,
      },
      swallow: {
        active: false,
        t: 0,
        duration: 1.1,
        startX: 0,
        startY: 0,
      },
    },
  });

  // --- Dev tests (console only) ---
  React.useEffect(() => {
    runTests();
  }, []);

  // Load head images (closed + open)
  React.useEffect(() => {
    if (HEAD_IMAGE_CLOSED_SRC) {
      const imgClosed = new Image();
      imgClosed.decoding = 'async';
      imgClosed.onload = () => { headClosedImgRef.current = imgClosed; };
      imgClosed.onerror = () => { console.warn("Closed head image failed to load:", HEAD_IMAGE_CLOSED_SRC); };
      imgClosed.src = HEAD_IMAGE_CLOSED_SRC;
    }
    if (HEAD_IMAGE_OPEN_SRC) {
      const imgOpen = new Image();
      imgOpen.decoding = 'async';
      imgOpen.onload = () => { headOpenImgRef.current = imgOpen; };
      imgOpen.onerror = () => { console.warn("Open head image failed to load:", HEAD_IMAGE_OPEN_SRC); };
      imgOpen.src = HEAD_IMAGE_OPEN_SRC;
    }
    if (ENEMY_HEAD_IMAGE_CLOSED_SRC) {
      const imgEnemyClosed = new Image();
      imgEnemyClosed.decoding = 'async';
      imgEnemyClosed.onload = () => { enemyHeadClosedImgRef.current = imgEnemyClosed; };
      imgEnemyClosed.onerror = () => { console.warn("Enemy closed head image failed to load:", ENEMY_HEAD_IMAGE_CLOSED_SRC); };
      imgEnemyClosed.src = ENEMY_HEAD_IMAGE_CLOSED_SRC;
    }
    if (ENEMY_HEAD_IMAGE_OPEN_SRC) {
      const imgEnemyOpen = new Image();
      imgEnemyOpen.decoding = 'async';
      imgEnemyOpen.onload = () => { enemyHeadOpenImgRef.current = imgEnemyOpen; };
      imgEnemyOpen.onerror = () => { console.warn("Enemy open head image failed to load:", ENEMY_HEAD_IMAGE_OPEN_SRC); };
      imgEnemyOpen.src = ENEMY_HEAD_IMAGE_OPEN_SRC;
    }
  }, []);

  // Resize canvas to fit container while keeping 16:10 aspect
  React.useEffect(() => {
    function handleResize() {
      const el = containerRef.current;
      if (!el) return;
      const maxW = el.clientWidth;
      const maxH = el.clientHeight;
      const targetRatio = 16 / 10;
      let w = maxW, h = Math.floor(maxW / targetRatio);
      if (h > maxH) { h = maxH; w = Math.floor(maxH * targetRatio); }
      setSize({ w: Math.max(640, w), h: Math.max(400, h) });
    }
    handleResize();
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  // Keep canvas pixel size in sync with layout size
  React.useEffect(() => {
    const c = canvasRef.current; if (!c) return; c.width = size.w; c.height = size.h;
  }, [size]);

  // Initialize / reset game state
  function resetGame() {
    const g = gameRef.current;
    g.marbles = [];
    g.spawnTimer = 0;
    setScore(0);
    setTimeLeft(45);
    setGameOverReason("time");
    const preset = DIFFICULTY_PRESETS[difficulty] ?? defaultPreset;
    const { w, h } = size;
    g.hippo.x = w / 2;
    g.hippo.y = h - 80;
    g.hippo.vx = 0;
    g.hippo.vy = 0;
    g.hippo.dirAngle = -Math.PI / 2;
    g.hippo.chompAngle = -Math.PI / 2;
    g.hippo.neckLen = 0;
    g.hippo.chomp.active = false;
    g.hippo.chomp.t = 0;
    const enemy = g.enemy;
    enemy.maxSpeed = preset.enemy.maxSpeed;
    enemy.vx = 0;
    enemy.vy = 0;
    enemy.dirAngle = Math.PI / 2;
    enemy.tongue.active = false;
    enemy.tongue.len = 0;
    enemy.tongue.t = 0;
    enemy.tongue.extendTime = preset.enemy.tongue.extendTime;
    enemy.tongue.retractTime = preset.enemy.tongue.retractTime;
    enemy.tongue.reach = preset.enemy.tongue.reach;
    enemy.tongue.cooldown = preset.enemy.tongue.cooldown;
    enemy.tongue.cooldownTimer = rand(0.4, enemy.tongue.cooldown);
    enemy.tongue.hit = false;
    enemy.swallow.active = false;
    enemy.swallow.t = 0;
    enemy.swallow.startX = enemy.x;
    enemy.swallow.startY = enemy.y;
    // spawn enemy near the top but not right on Alec
    let placed = false;
    let attempts = 0;
    while (!placed && attempts < 10) {
      enemy.x = rand(enemy.baseR + 20, w - enemy.baseR - 20);
      enemy.y = rand(enemy.baseR + 30, h * 0.45);
      const dx = enemy.x - g.hippo.x;
      const dy = enemy.y - g.hippo.y;
      placed = Math.hypot(dx, dy) > 220;
      attempts += 1;
    }
    if (!placed) {
      enemy.x = w * 0.35;
      enemy.y = h * 0.3;
    }
    enemy.swallow.startX = enemy.x;
    enemy.swallow.startY = enemy.y;
    // seed some marbles
    for (let i = 0; i < 10; i++) g.marbles.push(makeMarble(w, h));
  }

  // Make a marble with random position/velocity
  function makeMarble(w, h) {
    const r = rand(10, 18);
    // avoid spawning too close to hippo base area
    let x = rand(r + 8, w - r - 8);
    let y = rand(r + 8, h * 0.3); // spawn near top half mostly
    const vx = rand(-80, 80);
    const vy = rand(10, 60);
    return { x, y, vx, vy, r, hue: Math.floor(rand(0, 360)) };
  }

  function rand(a, b) { return Math.random() * (b - a) + a; }
  function clamp(v, min, max) { return Math.min(max, Math.max(min, v)); }
  function lerp(a, b, t) { return a + (b - a) * t; }
  function enemyHeadOffset(enemy) { return enemy.baseR + enemy.mouthR * ENEMY_HEAD_OFFSET_SCALE; }
  function enemyTongueOrigin(enemy, angle, headX, headY) {
    const baseOffset = enemyHeadOffset(enemy);
    const cx = headX ?? (enemy.x + Math.cos(angle) * baseOffset);
    const cy = headY ?? (enemy.y + Math.sin(angle) * baseOffset);
    const dx = Math.cos(angle);
    const dy = Math.sin(angle);
    const px = -Math.sin(angle);
    const py = Math.cos(angle);
    const originX = cx + dx * ENEMY_TONGUE_FORWARD_OFFSET + px * ENEMY_TONGUE_SIDE_OFFSET;
    const originY = cy + dy * ENEMY_TONGUE_FORWARD_OFFSET + py * ENEMY_TONGUE_SIDE_OFFSET;
    return { x: originX, y: originY };
  }

  // Input handling (movement + controls)
  React.useEffect(() => {
    function onKeyDown(e) {
      const k = e.key.toLowerCase();
      if (["arrowup", "arrowdown", "arrowleft", "arrowright", "w", "a", "s", "d"].includes(k)) e.preventDefault();
      // movement flags + facing
      if (k === "arrowup" || k === "w") { inputRef.current.up = true; gameRef.current.hippo.dirAngle = -Math.PI / 2; }
      if (k === "arrowdown" || k === "s") { inputRef.current.down = true; gameRef.current.hippo.dirAngle = Math.PI / 2; }
      if (k === "arrowleft" || k === "a") { inputRef.current.left = true; gameRef.current.hippo.dirAngle = Math.PI; }
      if (k === "arrowright" || k === "d") { inputRef.current.right = true; gameRef.current.hippo.dirAngle = 0; }

      if (e.code === "Space") {
        e.preventDefault();
        SoundManager.init(); // Ensure audio context is ready
        if (phase === "menu") startCountdown();
        else if (phase === "gameover") { resetGame(); startCountdown(); }
        else if (phase === "paused") setPhase("playing");
        else if (phase === "playing") triggerChomp();
      }
      if (k === "r") {
        if (phase !== "countdown") {
          resetGame();
          setPhase("menu");
        }
      }
      if (k === "p") {
        if (phase === "playing") setPhase("paused");
        else if (phase === "paused") setPhase("playing");
      }
    }
    function onKeyUp(e) {
      const k = e.key.toLowerCase();
      if (k === "arrowup" || k === "w") inputRef.current.up = false;
      if (k === "arrowdown" || k === "s") inputRef.current.down = false;
      if (k === "arrowleft" || k === "a") inputRef.current.left = false;
      if (k === "arrowright" || k === "d") inputRef.current.right = false;
    }
    function clearAll() { inputRef.current = { up: false, down: false, left: false, right: false }; }
    window.addEventListener("keydown", onKeyDown);
    window.addEventListener("keyup", onKeyUp);
    window.addEventListener("blur", clearAll);
    return () => {
      window.removeEventListener("keydown", onKeyDown);
      window.removeEventListener("keyup", onKeyUp);
      window.removeEventListener("blur", clearAll);
    };
  }, [phase]);

  // Touch / mouse -> chomp or start
  React.useEffect(() => {
    function onPointerDown() {
      SoundManager.init();
      if (phase === "menu") startCountdown();
      else if (phase === "gameover") { resetGame(); startCountdown(); }
      else if (phase === "paused") setPhase("playing");
      else if (phase === "playing") triggerChomp();
    }
    const el = canvasRef.current;
    if (el) {
      el.addEventListener("pointerdown", onPointerDown);
      return () => el.removeEventListener("pointerdown", onPointerDown);
    }
  }, [phase]);

  function startCountdown() {
    resetGame();
    setPhase("countdown");
    setCountdown(3);
    let c = 3;
    const id = setInterval(() => {
      c -= 1;
      if (c <= 0) {
        clearInterval(id);
        setPhase("playing");
      }
      setCountdown(Math.max(0, c));
    }, 1000);
  }

  function triggerChomp() {
    const g = gameRef.current;
    if (g.hippo.chomp.active) return; // ignore if already chomping
    g.hippo.chomp.active = true;
    g.hippo.chomp.t = 0;
    // lock the current direction for the entire shot
    g.hippo.chompAngle = g.hippo.dirAngle ?? (-Math.PI / 2);
  }

  // The main loop
  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    function loop(t) {
      const g = gameRef.current;
      if (!g.lastT) g.lastT = t;
      const dt = Math.min(0.033, (t - g.lastT) / 1000);
      g.lastT = t;

      drawBackground(ctx);

      if (phase === "countdown") {
        drawWorld(ctx, 0);
        drawUI(ctx);
        drawCenterText(ctx, `${countdown || 1}`, 120);
      } else if (phase === "menu") {
        drawWorld(ctx, 0);
        drawUI(ctx);
        drawCenterText(ctx, "Hungry Hungry Alec", 62);
        drawSubCenterText(ctx, "Press Space (or tap) to start", 26, 44);
      } else if (phase === "paused") {
        drawWorld(ctx, 0);
        drawUI(ctx);
        drawCenterText(ctx, "Paused", 76);
        drawSubCenterText(ctx, "Press P to resume", 22, 36);
      } else if (phase === "gameover") {
        drawWorld(ctx, 0);
        drawUI(ctx);
      } else if (phase === "playing") {
        step(dt);
        drawWorld(ctx, dt);
        drawUI(ctx);
      } else if (phase === "swallowed") {
        step(dt);
        drawWorld(ctx, dt);
        drawUI(ctx);
        drawCenterText(ctx, "Gulp...", 76);
        drawSubCenterText(ctx, "The hunter is swallowing Alec!", 24, 48);
      }

      if (phase === "gameover") {
        drawCenterText(ctx, gameOverReason === "swallowed" ? "Gulp!" : "Time!", 80);
        if (gameOverReason === "swallowed") {
          drawSubCenterText(ctx, "The hunter swallowed Alec whole!", 28, 42);
          drawSubCenterText(ctx, `Final Score: ${score}`, 24, 86);
        } else {
          drawSubCenterText(ctx, `Final Score: ${score}`, 28, 42);
        }
        drawSubCenterText(ctx, "Press R for menu • Space to play again", 20, -32);
      }

      rafRef.current = requestAnimationFrame(loop);
    }

    rafRef.current = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(rafRef.current);
  }, [phase, countdown, size, score, timeLeft, gameOverReason]);

  function drawBackground(ctx) {
    const { w, h } = size;
    ctx.clearRect(0, 0, w, h);

    // Rich radial gradient background
    const grad = ctx.createRadialGradient(w / 2, h / 2, h * 0.2, w / 2, h / 2, h * 0.9);
    grad.addColorStop(0, "#1e293b"); // slate-800 lighter center
    grad.addColorStop(1, "#020617"); // slate-950 dark corners
    ctx.fillStyle = grad;
    ctx.fillRect(0, 0, w, h);

    // Subtle grid pattern
    ctx.save();
    ctx.strokeStyle = "#33415522";
    ctx.lineWidth = 2;
    const gridSize = 60;
    ctx.beginPath();
    for (let x = 0; x <= w; x += gridSize) {
      ctx.moveTo(x, 0);
      ctx.lineTo(x, h);
    }
    for (let y = 0; y <= h; y += gridSize) {
      ctx.moveTo(0, y);
      ctx.lineTo(w, y);
    }
    ctx.stroke();
    ctx.restore();

    // Arena border with glow
    ctx.save();
    ctx.shadowColor = "#38bdf8"; // sky-400 glow
    ctx.shadowBlur = 15;
    ctx.strokeStyle = "#38bdf8";
    ctx.lineWidth = 4;
    ctx.strokeRect(10, 10, w - 20, h - 20);
    ctx.restore();
  }

  function drawWorld(ctx) {
    const g = gameRef.current;
    const { w, h } = size;

    // draw marbles
    // draw marbles with 3D effect
    for (const m of g.marbles) {
      const grad = ctx.createRadialGradient(m.x - m.r * 0.3, m.y - m.r * 0.3, m.r * 0.1, m.x, m.y, m.r);
      grad.addColorStop(0, `hsl(${m.hue} 90% 80%)`);
      grad.addColorStop(0.4, `hsl(${m.hue} 90% 60%)`);
      grad.addColorStop(1, `hsl(${m.hue} 90% 40%)`);

      ctx.save();
      ctx.shadowColor = `hsl(${m.hue} 90% 60%)`;
      ctx.shadowBlur = 10;
      ctx.fillStyle = grad;
      ctx.beginPath();
      ctx.arc(m.x, m.y, m.r, 0, Math.PI * 2);
      ctx.fill();
      ctx.restore();
    }

    // draw hippo base
    const hip = g.hippo;
    // body shadow
    ctx.save();
    ctx.shadowColor = "#00000088";
    ctx.shadowBlur = 20;
    ctx.shadowOffsetY = 10;

    // body
    ctx.beginPath();
    const bodyGrad = ctx.createRadialGradient(hip.x, hip.y, hip.baseR * 0.2, hip.x, hip.y, hip.baseR);
    bodyGrad.addColorStop(0, "#9ca3af"); // gray-400
    bodyGrad.addColorStop(1, "#4b5563"); // gray-600
    ctx.fillStyle = bodyGrad;
    ctx.arc(hip.x, hip.y, hip.baseR, 0, Math.PI * 2);
    ctx.fill();
    ctx.restore(); // clear shadow

    // facing arrow indicator (shows shoot direction)
    {
      const a = (hip.chomp.active ? hip.chompAngle : hip.dirAngle) ?? -Math.PI / 2;
      const cx = hip.x, cy = hip.y;
      const tipR = hip.baseR + 14;
      const baseR = hip.baseR + 2;
      const hw = 6; // half width
      const dx = Math.cos(a), dy = Math.sin(a);
      const px = -Math.sin(a), py = Math.cos(a); // perpendicular
      const tipX = cx + dx * tipR, tipY = cy + dy * tipR;
      const leftX = cx + dx * baseR + px * hw, leftY = cy + dy * baseR + py * hw;
      const rightX = cx + dx * baseR - px * hw, rightY = cy + dy * baseR - py * hw;
      ctx.beginPath();
      ctx.moveTo(tipX, tipY);
      ctx.lineTo(leftX, leftY);
      ctx.lineTo(rightX, rightY);
      ctx.closePath();
      ctx.fillStyle = "#eab308"; // amber-500
      ctx.fill();
    }

    // neck + mouth
    const ang = (hip.chomp.active ? hip.chompAngle : hip.dirAngle) ?? -Math.PI / 2; // lock during shot
    const mouthCx = hip.x + Math.cos(ang) * hip.neckLen;
    const mouthCy = hip.y + Math.sin(ang) * hip.neckLen;

    // neck
    ctx.strokeStyle = "#9ca3af"; // gray‑400
    ctx.lineWidth = hip.mouthR * 0.9;
    ctx.lineCap = "round";
    ctx.beginPath();
    ctx.moveTo(hip.x, hip.y);
    ctx.lineTo(mouthCx, mouthCy);
    ctx.stroke();
    ctx.closePath();

    // mouth / head: draw circular-clipped image if available, else fallback circle
    const r = hip.mouthR;
    const useOpen = hip.chomp.active && hip.neckLen > 1;
    const img = (useOpen ? headOpenImgRef.current : headClosedImgRef.current) || headClosedImgRef.current;
    if (img && (img.naturalWidth || img.width)) {
      // Circular clip; rotate to angle, but for LEFT we mirror instead of 180° rotate
      ctx.save();
      ctx.beginPath();
      ctx.arc(mouthCx, mouthCy, r, 0, Math.PI * 2);
      ctx.closePath();
      ctx.clip();
      // Centered square crop to fill circle
      const iw = img.naturalWidth || img.width;
      const ih = img.naturalHeight || img.height;
      const s = Math.min(iw, ih);
      const sx = Math.max(0, (iw - s) / 2);
      const sy = Math.max(0, (ih - s) / 2);
      // Translate to circle center
      ctx.translate(mouthCx, mouthCy);
      // If facing left, flip horizontally instead of rotating 180°
      const isLeft = Math.abs(Math.cos(ang) + 1) < 1e-6; // ang ≈ π
      if (isLeft) {
        ctx.scale(-1, 1); // mirror x so default right-facing image becomes left
      } else {
        ctx.rotate(ang);
      }
      ctx.drawImage(img, sx, sy, s, s, -r, -r, r * 2, r * 2);
      ctx.restore();
      // subtle ring to blend with neck (hide during chomp so only the image shows)
      if (!hip.chomp.active) {
        ctx.beginPath();
        ctx.strokeStyle = "#e5e7eb"; // gray-200
        ctx.lineWidth = 2;
        ctx.arc(mouthCx, mouthCy, r - 1, 0, Math.PI * 2);
        ctx.stroke();
        ctx.closePath();
      }
    } else {
      // Fallback solid head if image not loaded
      ctx.beginPath();
      ctx.fillStyle = "#d1d5db"; // gray‑300
      ctx.arc(mouthCx, mouthCy, r, 0, Math.PI * 2);
      ctx.fill();
      ctx.closePath();

      // nostrils eyes (fallback style)
      ctx.fillStyle = "#111827";
      ctx.beginPath(); ctx.arc(mouthCx - 8, mouthCy - 6, 2.6, 0, Math.PI * 2); ctx.fill();
      ctx.beginPath(); ctx.arc(mouthCx + 8, mouthCy - 6, 2.6, 0, Math.PI * 2); ctx.fill();
      ctx.beginPath(); ctx.arc(mouthCx - 10, mouthCy + 6, 2, 0, Math.PI * 2); ctx.fill();
      ctx.beginPath(); ctx.arc(mouthCx + 10, mouthCy + 6, 2, 0, Math.PI * 2); ctx.fill();
    }

    // draw enemy hunter (after hippo so Adam appears on top)
    const enemy = g.enemy;
    if (enemy) {
      // body shadow
      ctx.save();
      ctx.shadowColor = "#00000088";
      ctx.shadowBlur = 20;
      ctx.shadowOffsetY = 10;

      // body
      ctx.beginPath();
      const enemyGrad = ctx.createRadialGradient(enemy.x, enemy.y, enemy.baseR * 0.2, enemy.x, enemy.y, enemy.baseR);
      enemyGrad.addColorStop(0, "#a78bfa"); // violet-400
      enemyGrad.addColorStop(1, "#7c3aed"); // violet-600
      ctx.fillStyle = enemyGrad;
      ctx.arc(enemy.x, enemy.y, enemy.baseR, 0, Math.PI * 2);
      ctx.fill();
      ctx.restore(); // clear shadow

      // aura
      ctx.beginPath();
      ctx.fillStyle = "#c4b5fd22";
      ctx.arc(enemy.x - enemy.baseR * 0.2, enemy.y - enemy.baseR * 0.2, enemy.baseR * 0.7, 0, Math.PI * 2);
      ctx.fill();
      ctx.closePath();

      const tongue = enemy.tongue;
      const headAngle = (tongue.active ? tongue.angle : enemy.dirAngle) ?? 0;
      const headOffset = enemyHeadOffset(enemy);
      const headX = enemy.x + Math.cos(headAngle) * headOffset;
      const headY = enemy.y + Math.sin(headAngle) * headOffset;

      // head
      const enemyClosedImg = enemyHeadClosedImgRef.current;
      const enemyOpenImg = enemyHeadOpenImgRef.current;
      const useOpenHead = (tongue.active && tongue.len > 3) || phase === "swallowed";
      const enemyImg = (useOpenHead ? enemyOpenImg : enemyClosedImg) || enemyClosedImg || enemyOpenImg;
      if (enemyImg && (enemyImg.naturalWidth || enemyImg.width)) {
        ctx.save();
        ctx.beginPath();
        ctx.arc(headX, headY, enemy.mouthR, 0, Math.PI * 2);
        ctx.closePath();
        ctx.clip();
        const iw = enemyImg.naturalWidth || enemyImg.width;
        const ih = enemyImg.naturalHeight || enemyImg.height;
        const s = Math.min(iw, ih);
        const sx = Math.max(0, (iw - s) / 2);
        const sy = Math.max(0, (ih - s) / 2);
        ctx.translate(headX, headY);
        const isLeft = Math.abs(Math.cos(headAngle) + 1) < 1e-3;
        if (isLeft) ctx.scale(-1, 1);
        else ctx.rotate(headAngle);
        ctx.drawImage(enemyImg, sx, sy, s, s, -enemy.mouthR, -enemy.mouthR, enemy.mouthR * 2, enemy.mouthR * 2);
        ctx.restore();
      } else {
        ctx.beginPath();
        ctx.fillStyle = "#c084fc";
        ctx.arc(headX, headY, enemy.mouthR, 0, Math.PI * 2);
        ctx.fill();
        ctx.closePath();
        // eyes
        ctx.fillStyle = "#312e81";
        const eyeOffsetX = Math.cos(headAngle + Math.PI / 2) * 6;
        const eyeOffsetY = Math.sin(headAngle + Math.PI / 2) * 6;
        ctx.beginPath();
        ctx.arc(headX + eyeOffsetX, headY + eyeOffsetY, 3, 0, Math.PI * 2);
        ctx.fill();
        ctx.beginPath();
        ctx.arc(headX - eyeOffsetX, headY - eyeOffsetY, 3, 0, Math.PI * 2);
        ctx.fill();
      }

      // Tongue drawn last so it can overlap the head art
      if (tongue.len > 2) {
        const { x: originX, y: originY } = enemyTongueOrigin(enemy, tongue.angle, headX, headY);
        const tipX = originX + Math.cos(tongue.angle) * tongue.len;
        const tipY = originY + Math.sin(tongue.angle) * tongue.len;

        ctx.save();
        ctx.shadowColor = tongue.hit ? "#ef4444" : "#f43f5e";
        ctx.shadowBlur = 15;

        ctx.beginPath();
        ctx.strokeStyle = tongue.hit ? "#dc2626" : "#fb7185";
        ctx.lineWidth = 12;
        ctx.lineCap = "round";
        ctx.moveTo(originX, originY);
        ctx.lineTo(tipX, tipY);
        ctx.stroke();
        ctx.closePath();

        ctx.beginPath();
        ctx.fillStyle = tongue.hit ? "#f87171" : "#fecdd3";
        ctx.arc(tipX, tipY, 9, 0, Math.PI * 2);
        ctx.fill();
        ctx.closePath();
        ctx.restore();
      }
    }
  }

  function drawUI(ctx) {
    const { w } = size;
    // HUD Background
    ctx.save();
    ctx.fillStyle = "#0f172a88";
    ctx.beginPath();
    ctx.roundRect(16, 16, 400, 100, 12);
    ctx.fill();
    ctx.restore();

    // HUD Text
    ctx.font = "700 24px 'Fredoka', sans-serif";
    ctx.fillStyle = "#f1f5f9"; // slate-100
    ctx.shadowColor = "#000000";
    ctx.shadowBlur = 4;
    ctx.fillText(`Score: ${score}`, 32, 50);

    ctx.font = "500 16px 'Fredoka', sans-serif";
    ctx.fillStyle = "#cbd5e1"; // slate-300
    ctx.shadowBlur = 0;
    ctx.fillText("Arrows/WASD move • Space eat • P pause • R menu", 32, 78);
    ctx.fillText("Beware the purple hunter — its tongue will swallow you!", 32, 100);

    ctx.textAlign = "right";
    ctx.fillText(`Difficulty: ${difficultyPreset.label}`, w - 32, 50);
    ctx.textAlign = "left";
  }

  function drawCenterText(ctx, text, sizePx = 72) {
    const { w, h } = size;
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.font = `800 ${sizePx}px 'Fredoka', sans-serif`;
    ctx.fillStyle = "#f8fafc";
    ctx.shadowColor = "#38bdf8";
    ctx.shadowBlur = 20;
    ctx.fillText(text, w / 2, h * 0.42);
    ctx.shadowBlur = 0; // reset
    ctx.textAlign = "left";
    ctx.textBaseline = "alphabetic";
  }
  function drawSubCenterText(ctx, text, sizePx = 24, dy = 68) {
    const { w, h } = size;
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.font = `600 ${sizePx}px 'Fredoka', sans-serif`;
    ctx.fillStyle = "#94a3b8";
    ctx.fillText(text, w / 2, h * 0.42 + dy);
    ctx.textAlign = "left";
    ctx.textBaseline = "alphabetic";
  }

  // Step simulation when playing
  function step(dt) {
    const g = gameRef.current;
    const { w, h } = size;

    const enemy = g.enemy;
    const swallow = enemy?.swallow;
    const swallowing = !!(swallow && swallow.active);

    // Time & end condition
    setTimeLeft((t) => {
      const nt = Math.max(0, t - (swallowing ? 0 : dt));
      if (nt <= 0 && phase === "playing") {
        setGameOverReason("time");
        setPhase("gameover");
        SoundManager.playGameOver();
      }
      return nt;
    });

    // spawn marbles gradually up to a cap
    g.spawnTimer += dt;
    const cap = 30;
    if (g.spawnTimer > 0.6 && g.marbles.length < cap) {
      g.spawnTimer = 0;
      g.marbles.push(makeMarble(w, h));
    }

    // Update marbles
    for (const m of g.marbles) {
      m.x += m.vx * dt;
      m.y += m.vy * dt;
      // gentle random drift
      m.vx += rand(-10, 10) * dt;
      m.vy += rand(-10, 10) * dt;
      // friction
      m.vx *= (1 - 0.15 * dt);
      m.vy *= (1 - 0.15 * dt);
      // bounce walls
      if (m.x < m.r + 12) { m.x = m.r + 12; m.vx = Math.abs(m.vx); }
      if (m.x > w - m.r - 12) { m.x = w - m.r - 12; m.vx = -Math.abs(m.vx); }
      if (m.y < m.r + 12) { m.y = m.r + 12; m.vy = Math.abs(m.vy); }
      if (m.y > h - m.r - 12) { m.y = h - m.r - 12; m.vy = -Math.abs(m.vy); }
    }

    // Player movement (floaty thrust + wrap)
    const hip = g.hippo;
    if (!swallowing) {
      const accel = 220;
      const drag = 0.35;
      const maxSpeed = 460;
      const inp = inputRef.current;
      let ax = 0, ay = 0;
      if (inp.up) ay -= accel;
      if (inp.down) ay += accel;
      if (inp.left) ax -= accel;
      if (inp.right) ax += accel;
      hip.vx = (hip.vx || 0) + ax * dt;
      hip.vy = (hip.vy || 0) + ay * dt;
      hip.vx *= (1 - drag * dt);
      hip.vy *= (1 - drag * dt);
      const sp = Math.hypot(hip.vx, hip.vy);
      if (sp > maxSpeed) {
        const k = maxSpeed / (sp || 1);
        hip.vx *= k;
        hip.vy *= k;
      }
      hip.x += hip.vx * dt;
      hip.y += hip.vy * dt;
      const R = hip.mouthR;
      if (hip.x < -R) hip.x = w + R;
      if (hip.x > w + R) hip.x = -R;
      if (hip.y < -R) hip.y = h + R;
      if (hip.y > h + R) hip.y = -R;
    } else {
      hip.vx = 0;
      hip.vy = 0;
    }

    // Enemy movement + tongue attack
    if (enemy) {
      const dx = hip.x - enemy.x;
      const dy = hip.y - enemy.y;
      const targetAngle = Math.atan2(dy, dx);
      const dist = Math.hypot(dx, dy) || 1;
      const chaseDirX = dx / dist;
      const chaseDirY = dy / dist;
      const tongue = enemy.tongue;
      if (!swallowing) {
        const desiredVX = chaseDirX * enemy.maxSpeed;
        const desiredVY = chaseDirY * enemy.maxSpeed;
        enemy.vx += (desiredVX - enemy.vx) * Math.min(enemy.accel * dt, 1);
        enemy.vy += (desiredVY - enemy.vy) * Math.min(enemy.accel * dt, 1);
        enemy.vx *= (1 - enemy.drag * dt);
        enemy.vy *= (1 - enemy.drag * dt);
        const enemySpd = Math.hypot(enemy.vx, enemy.vy);
        if (enemySpd > enemy.maxSpeed) {
          const s = enemy.maxSpeed / (enemySpd || 1);
          enemy.vx *= s;
          enemy.vy *= s;
        }
        enemy.x += enemy.vx * dt;
        enemy.y += enemy.vy * dt;
        if (enemySpd > 1) enemy.dirAngle = Math.atan2(enemy.vy, enemy.vx);
        else enemy.dirAngle = targetAngle;
        enemy.x = clamp(enemy.x, enemy.baseR + 18, w - enemy.baseR - 18);
        enemy.y = clamp(enemy.y, enemy.baseR + 18, h - enemy.baseR - 18);

        tongue.cooldownTimer += dt;
        const shouldAttack = !tongue.active && tongue.cooldownTimer >= tongue.cooldown && dist < tongue.reach + 160;
        if (shouldAttack) {
          tongue.active = true;
          tongue.t = 0;
          tongue.len = 0;
          tongue.angle = targetAngle;
          tongue.hit = false;
          tongue.cooldownTimer = 0;
          SoundManager.playTongue();
        }
        if (tongue.active) {
          tongue.t += dt;
          const total = tongue.extendTime + tongue.retractTime;
          if (tongue.t <= tongue.extendTime) {
            const u = tongue.t / tongue.extendTime;
            tongue.len = easeOutQuad(u) * tongue.reach;
          } else if (tongue.t <= total) {
            const u = (tongue.t - tongue.extendTime) / tongue.retractTime;
            tongue.len = (1 - easeInQuad(u)) * tongue.reach;
          } else {
            tongue.active = false;
            tongue.t = 0;
            tongue.len = 0;
            tongue.hit = false;
            tongue.cooldownTimer = 0;
          }

          if (tongue.len > 4) {
            const { x: originX, y: originY } = enemyTongueOrigin(enemy, tongue.angle);
            const tipX = originX + Math.cos(tongue.angle) * tongue.len;
            const tipY = originY + Math.sin(tongue.angle) * tongue.len;
            const hitDist = Math.hypot(tipX - hip.x, tipY - hip.y);
            if (!tongue.hit && hitDist <= hip.baseR * 0.9) {
              tongue.hit = true;
              if (swallow) {
                swallow.active = true;
                swallow.t = 0;
                swallow.startX = hip.x;
                swallow.startY = hip.y;
              }
              setPhase("swallowed");
              SoundManager.playHit();
              setTimeout(() => SoundManager.playGameOver(), 1000);
            }
          }
        }
      } else {
        enemy.dirAngle = targetAngle;
        enemy.vx = 0;
        enemy.vy = 0;
        tongue.angle = targetAngle;
        tongue.len = Math.max(enemy.baseR * 0.6, Math.hypot(enemy.x - hip.x, enemy.y - hip.y));
      }
    }

    if (swallowing && swallow) {
      swallow.t += dt;
      const duration = swallow.duration || 1;
      const u = Math.min(1, swallow.t / duration);
      const eased = easeInQuad(u);
      hip.x = lerp(swallow.startX, enemy.x, eased);
      hip.y = lerp(swallow.startY, enemy.y, eased);
      hip.vx = 0;
      hip.vy = 0;
      hip.dirAngle = Math.atan2(enemy.y - hip.y, enemy.x - hip.x);
      const mouthAngle = Math.atan2(hip.y - enemy.y, hip.x - enemy.x);
      enemy.tongue.angle = mouthAngle;
      const headOffset = enemyHeadOffset(enemy);
      const headX = enemy.x + Math.cos(mouthAngle) * headOffset;
      const headY = enemy.y + Math.sin(mouthAngle) * headOffset;
      const { x: anchorX, y: anchorY } = enemyTongueOrigin(enemy, mouthAngle, headX, headY);
      const tether = Math.max(enemy.mouthR * 0.5, Math.hypot(anchorX - hip.x, anchorY - hip.y));
      enemy.tongue.len = tether;
      if (u >= 1 && phase !== "gameover") {
        swallow.active = false;
        enemy.tongue.len = 0;
        enemy.tongue.active = false;
        enemy.tongue.hit = false;
        setGameOverReason("swallowed");
        setPhase("gameover");
      }
    }

    // Hippo chomp animation
    if (hip.chomp.active) {
      hip.chomp.t += dt;
      const { outTime, inTime } = hip.chomp;
      const total = outTime + inTime;
      let t = hip.chomp.t;
      if (t <= outTime) {
        // ease out
        const u = t / outTime;
        hip.neckLen = easeOutQuad(u) * hip.neckMax;
      } else if (t <= total) {
        const u = (t - outTime) / inTime;
        hip.neckLen = (1 - easeInQuad(u)) * hip.neckMax;
      } else {
        hip.chomp.active = false;
        hip.chomp.t = 0;
        hip.neckLen = 0;
      }
    }

    // Collision: only while chomping and at the mouth tip
    if (hip.chomp.active && hip.neckLen > 0) {
      const ang = hip.chompAngle ?? (-Math.PI / 2);
      const mx = hip.x + Math.cos(ang) * hip.neckLen;
      const my = hip.y + Math.sin(ang) * hip.neckLen;
      const eatIdx = [];
      for (let i = 0; i < g.marbles.length; i++) {
        const m = g.marbles[i];
        const dx = m.x - mx, dy = m.y - my;
        const dist2 = dx * dx + dy * dy;
        const rad = m.r + hip.mouthR * 0.92;
        if (dist2 <= rad * rad) eatIdx.push(i);
      }
      if (eatIdx.length) {
        // Remove eaten marbles and add score
        for (let k = eatIdx.length - 1; k >= 0; k--) g.marbles.splice(eatIdx[k], 1);
        setScore((s) => s + eatIdx.length);
        SoundManager.playCollect();
        // tiny recoil
        hip.neckLen = Math.max(0, hip.neckLen - 18);
      }
    }
  }

  function easeOutQuad(u) { return 1 - (1 - u) * (1 - u); }
  function easeInQuad(u) { return u * u; }

  // Simple dev tests for core helpers and collision logic
  function runTests() {
    try {
      // easing
      console.assert(easeOutQuad(0) === 0, "easeOutQuad(0) should be 0");
      console.assert(Math.abs(easeOutQuad(1) - 1) < 1e-9, "easeOutQuad(1) should be 1");
      console.assert(easeInQuad(0) === 0, "easeInQuad(0) should be 0");
      console.assert(Math.abs(easeInQuad(1) - 1) < 1e-9, "easeInQuad(1) should be 1");
      console.assert(easeOutQuad(0.7) > easeOutQuad(0.6), "easeOutQuad monotonic increasing");
      console.assert(easeInQuad(0.7) > easeInQuad(0.6), "easeInQuad monotonic increasing");

      // collision check near/far (mouth at end of neck)
      const hip = { x: 100, y: 100, mouthR: 26, neckLen: 50 };
      const ang = -Math.PI / 2;
      const mx = hip.x + Math.cos(ang) * hip.neckLen;
      const my = hip.y + Math.sin(ang) * hip.neckLen;
      const mNear = { x: mx, y: my + 20, r: 12 };
      const rad = mNear.r + hip.mouthR * 0.92;
      const nearDist2 = (mNear.x - mx) ** 2 + (mNear.y - my) ** 2;
      console.assert(nearDist2 <= rad * rad, "near marble should collide");
      const mFar = { x: mx + rad + 10, y: my, r: 12 };
      const farDist2 = (mFar.x - mx) ** 2 + (mFar.y - my) ** 2;
      console.assert(farDist2 > rad * rad, "far marble should not collide");

      // spawn boundaries
      for (let i = 0; i < 25; i++) {
        const m = makeMarble(800, 600);
        console.assert(m.x >= m.r + 8 - 1e-9 && m.x <= 800 - m.r - 8 + 1e-9, "marble x in bounds");
        console.assert(m.y >= m.r + 8 - 1e-9 && m.y <= 600 * 0.3 + 1e-9, "marble y approx in top band");
      }
      console.log("%cHippoSnackdown tests passed", "color: lime; font-weight: bold");
    } catch (err) {
      console.error("HippoSnackdown tests failed:", err);
    }
  }

  // Layout & canvas
  return (
    <div ref={containerRef} className="w-full h-full min-h-[520px] bg-slate-900 text-white flex items-center justify-center">
      <div className="relative w-full max-w-[1100px] aspect-[16/10] rounded-2xl overflow-hidden shadow-2xl ring-1 ring-slate-800">
        <canvas
          ref={canvasRef}
          width={size.w}
          height={size.h}
          className="absolute inset-0 w-full h-full"
          style={{ display: 'block' }}
        />
        {/* Pinned Alec image (closed) with time below */}
        <div className="absolute top-3 right-3 flex flex-col items-end gap-2 pointer-events-none select-none">
          <img
            src={HEAD_IMAGE_CLOSED_SRC}
            alt="Alec"
            className="w-28 h-auto rounded-lg shadow-xl ring-1 ring-slate-700/60"
            draggable={false}
          />
          <div className="text-xs md:text-sm font-semibold text-slate-200 bg-slate-900/70 px-2 py-1 rounded-md ring-1 ring-slate-700/60">
            {`Time: ${Math.max(0, Math.ceil(timeLeft))}s`}
          </div>
        </div>
        {/* Top overlay UI (non‑canvas) */}
        <div className="pointer-events-none absolute inset-0 p-4 flex flex-col">
          <div className="mt-auto grid grid-cols-3 gap-3 pointer-events-auto">
            <button onClick={() => { if (phase === 'menu') startCountdown(); }} className="col-start-1 justify-self-start text-xs bg-slate-800/60 hover:bg-slate-700/60 px-3 py-1 rounded-xl border border-slate-700">{phase === 'menu' ? 'Start' : ' '}</button>
            <div className="justify-self-center text-xs opacity-80">{phase === 'playing' ? 'P to pause' : phase === 'paused' ? 'P to resume' : ''}</div>
            <button onClick={() => { resetGame(); setPhase('menu'); }} className="col-start-3 justify-self-end text-xs bg-slate-800/60 hover:bg-slate-700/60 px-3 py-1 rounded-xl border border-slate-700">Menu</button>
          </div>
          <div className="mt-3 flex flex-wrap justify-center gap-2 pointer-events-auto">
            {Object.entries(DIFFICULTY_PRESETS).map(([key, cfg]) => {
              const active = key === difficulty;
              return (
                <button
                  key={key}
                  onClick={() => { setDifficulty(key); resetGame(); setPhase("menu"); }}
                  className={`text-xs px-3 py-1 rounded-xl border ${active ? "bg-amber-500 text-slate-900 border-amber-300" : "bg-slate-800/60 border-slate-700 hover:bg-slate-700/60"}`}
                >
                  {cfg.label}
                </button>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
}

// Expose globally for index.html to mount
if (typeof window !== 'undefined') {
  window.HippoSnackdown = HippoSnackdown;
}
