Improve on this game in any way possible: <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,viewport-fit=cover" />
<title>Cyber‑Neon Snake 3D - Vibrant Edition</title>
<style>
  /* Reset & Base */
  * {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }
  html, body {
    height: 100%;
    background: linear-gradient(135deg, #02030A, #050F31, #010618);
    background-size: 400% 400%;
    animation: bgHueShift 35s linear infinite;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    user-select: none;
    overflow: hidden;
    color: #0ff5f5;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
  @keyframes bgHueShift {
    0% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
  }

  #game {
    position: relative;
    width: 90vmin;
    height: 90vmin;
    background: #0a101a;
    border: 6px solid #0ff8ff;
    border-radius: 28px;
    box-shadow:
      inset 0 0 20px #06f0ff,
      0 0 30px #09e9ffaa,
      0 0 40px #08d4ffaa;
    overflow: hidden;
    touch-action: none;
  }
  canvas {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 28px;
    background: radial-gradient(circle at center, #0d1423 0%, #03070f 85%);
    image-rendering: pixelated;
  }

  #scoreboard {
    margin-top: 16px;
    font-weight: 700;
    font-size: clamp(1.4rem, 2vw, 2rem);
    display: flex;
    gap: 2rem;
    justify-content: center;
    user-select: none;
    color: #07f4ff;
    text-shadow:
      0 0 8px #00ffffcc,
      0 0 20px #00ffff99;
    letter-spacing: 0.06em;
  }
  #scoreboard.flash {
    color: #aefdff;
    text-shadow:
      0 0 40px #00ffffff,
      0 0 60px #2bfbffff,
      0 0 100px #6fffffff;
    transition: all 0.25s ease;
  }

  #restartBtn {
    margin-top: 20px;
    padding: 14px 0;
    width: 90vmin;
    max-width: 400px;
    border-radius: 20px;
    background:
      linear-gradient(145deg, #00e5ff, #00a1ff);
    box-shadow:
      0 0 30px #0ff8ff,
      inset 0 0 12px #0ff8ffaa;
    border: none;
    color: #001918;
    font-weight: 800;
    font-size: 1.5rem;
    cursor: pointer;
    user-select: none;
    display: none;
    transition: transform 0.2s ease, box-shadow 0.3s ease;
  }
  #restartBtn:hover, #restartBtn:focus-visible {
    background: linear-gradient(45deg, #00c6ff, #0089e8);
    box-shadow:
      0 0 40px #17ffff,
      inset 0 0 18px #17ffffcc;
    transform: scale(1.05);
    outline: none;
  }
  #restartBtn:active {
    background: linear-gradient(135deg, #0089e8, #005dbb);
    box-shadow: inset 0 0 35px #0088cc;
    transform: scale(0.95);
  }

  #overlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 15, 30, 0.87);
    border-radius: 28px;
    display: none;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    z-index: 10;
    color: #05faff;
    font-weight: 900;
    font-size: clamp(2.4rem, 5vw, 3.6rem);
    text-shadow:
      0 0 20px #07ffff,
      0 0 40px #00ffffcc,
      0 0 90px #08f5ffff;
    text-align: center;
    user-select: none;
    animation: fadeIn 0.5s ease forwards;
  }
  #overlay p {
    margin-bottom: 1rem;
  }

  @keyframes fadeIn {
    from { opacity: 0 }
    to { opacity: 1 }
  }

  #countdown {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-weight: 900;
    font-size: clamp(6rem, 20vw, 12rem);
    color: #02dbff;
    text-shadow:
      0 0 15px #00ffffcc,
      0 0 35px #00ffff88,
      0 0 60px #05faffaa;
    user-select: none;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.3s ease;
    z-index: 20;
  }

  #instructions {
    margin-top: 14px;
    max-width: 380px;
    font-size: 1rem;
    color: #0ae5ffcc;
    user-select: none;
    text-align: center;
    line-height: 1.5;
    text-shadow:
      0 0 8px #0ae5ffcc,
      0 0 20px #04c3ffff;
    font-variant-numeric: tabular-nums;
  }

  kbd {
    background: rgba(0, 255, 255, 0.15);
    border-radius: 4px;
    padding: 3px 6px;
    font-weight: 600;
    font-size: 0.95rem;
    color: #0ff;
    box-shadow: 0 0 12px #0ff;
    user-select: none;
  }

  #fpsCounter {
    position: fixed;
    top: 12px;
    left: 12px;
    font-family: monospace;
    color: #0ff;
    font-weight: 900;
    font-size: 1.1rem;
    text-shadow: 0 0 14px #0ff, 0 0 30px #0af;
    user-select: none;
    pointer-events: none;
    z-index: 50;
    display: none;
  }
</style>
</head>
<body>
  <div id="game" role="region" aria-label="Snake game area">
    <canvas id="canvas" tabindex="0" aria-live="polite" aria-label="Game canvas"></canvas>
    <div id="overlay" role="alert" aria-live="assertive"><p id="gameOverText"></p></div>
    <div id="countdown" aria-hidden="true"></div>
  </div>

  <div id="scoreboard" aria-live="polite" aria-atomic="true" role="status" aria-label="Current and high score">
    <div>Score: <span id="currentScore">0</span></div>
    <div>High: <span id="highScore">0</span></div>
  </div>

  <button id="restartBtn" type="button" aria-label="Restart game">Restart Game</button>

  <div id="instructions" aria-hidden="false">
    Use arrow keys or WASD to move<br>
    Swipe on touch devices<br>
    Press <kbd>F</kbd> to toggle FPS display
  </div>

  <div id="fpsCounter" aria-hidden="true">FPS: 0</div>

  <audio id="eatSfx" src="eat.mp3" preload="auto" aria-hidden="true"></audio>
  <audio id="gameOverSfx" src="gameover.mp3" preload="auto" aria-hidden="true"></audio>
  <audio id="turnSfx" src="turn.mp3" preload="auto" aria-hidden="true"></audio>

<script>
(function(){
  // DOM & audio refs
  const game       = document.getElementById('game');
  const canvas     = document.getElementById('canvas');
  const ctx        = canvas.getContext('2d');
  const scoreEl    = document.getElementById('currentScore');
  const highEl     = document.getElementById('highScore');
  const sb         = document.getElementById('scoreboard');
  const eatSfx     = document.getElementById('eatSfx');
  const restartBtn = document.getElementById('restartBtn');
  const overlay    = document.getElementById('overlay');
  const gameOverText = document.getElementById('gameOverText');
  const countdownEl = document.getElementById('countdown');
  const gameOverSfx = document.getElementById('gameOverSfx');
  const turnSfx = document.getElementById('turnSfx');
  const fpsCounter = document.getElementById('fpsCounter');

  // Config & state
  const tileCount    = 24;
  let gridSize;
  const baseFps      = 9;
  const maxApples    = 3;
  let snake, velocity, nextVel, apples, score, gameOver;
  let comboCounter, lastAppleTime, hue;
  let effects;
  let speedMultiplier = 1;
  let currentInterval;
  let gameStarted = false;
  let animFrameId;
  let showFPS = false;
  let fps = 0;
  let highScore     = parseInt(localStorage.getItem('snakeHighScore'),10) || 0;
  highEl.textContent = highScore;

  // Timing variables for fixed FPS using delta accumulator
  let lastTime = 0;
  let delta = 0;
  let interval;

  // For smooth animation of snake movement between tiles
  const smoothSpeed = 0.14; // smaller is slower (between 0 and 1)
  let smoothPos = []; // will store intermediate positions of snake parts (x and y floats)

  function initGame(){
    snake = [{x:12,y:12},{x:11,y:12},{x:10,y:12}];
    velocity = {x:1,y:0};
    nextVel = {x:1,y:0};
    apples = [];
    score = 0;
    comboCounter = 0;
    lastAppleTime = 0;
    hue = 180; // start close to cyan
    effects = { score: [], particles: [], trail: [] };
    speedMultiplier = 1;
    gameOver = false;
    interval = 1000 / baseFps;
    scoreEl.textContent = score;
    highEl.textContent = highScore;
    restartBtn.style.display = 'none';
    overlay.style.display = 'none';
    countdownEl.style.opacity = 0;
    smoothPos = snake.map(p => ({x:p.x, y:p.y}));
    spawnApples();

    runCountdown(() => {
      gameStarted = true;
      lastTime = performance.now();
      delta = 0;
      loop(lastTime);
    });
  }

  function runCountdown(onComplete){
    countdownEl.style.opacity = '1';
    let count = 3;
    countdownEl.textContent = count;
    const countdownInterval = setInterval(() => {
      count--;
      if(count > 0){
        countdownEl.textContent = count;
      } else if(count === 0){
        countdownEl.textContent = 'GO!';
      } else {
        countdownEl.style.opacity = '0';
        clearInterval(countdownInterval);
        if(onComplete) onComplete();
      }
    }, 850);
  }

  function resizeCanvas(){
    const size = game.clientWidth;
    canvas.width  = size;
    canvas.height = size;
    gridSize      = size / tileCount;
  }
  resizeCanvas();
  window.addEventListener('resize', resizeCanvas);

  function isEmpty(x,y){
    return !snake.some(s=>s.x===x&&s.y===y)
        && !apples.some(a=>a.x===x&&a.y===y);
  }

  function spawnApples(){
    while(apples.length < maxApples){
      let p = {
        x: Math.floor(Math.random()*tileCount),
        y: Math.floor(Math.random()*tileCount)
      };
      if(isEmpty(p.x,p.y)){
        apples.push(p);
      }
    }
  }

  function addScoreFx(x,y,amt){
    effects.score.push({
      x: x*gridSize + gridSize/2,
      y: y*gridSize + gridSize/2,
      alpha: 1, text: '+'+amt, size: 26, glow: 20
    });
  }
  function addParticles(x,y){
    for(let i=0; i<16; i++){
      effects.particles.push({
        x: x*gridSize + gridSize/2,
        y: y*gridSize + gridSize/2,
        dx: (Math.random()-0.5)*10,
        dy: (Math.random()-0.5)*10,
        alpha: 1,
        size: Math.random()*3+1.8,
        colorHue: (hue + i*22) % 360,
        life: 22 + Math.random()*10
      });
    }
  }

  function addTrail(){
    const head = snake[0];
    effects.trail.push({
      x: head.x*gridSize + gridSize/2 + (Math.random()-0.5)*6,
      y: head.y*gridSize + gridSize/2 + (Math.random()-0.5)*6,
      dx: (Math.random()-0.5)*1.2,
      dy: (Math.random()-0.5)*1.2,
      size: Math.random()*1.3+0.9,
      alpha: 0.5 + Math.random()*0.4,
      colorHue: (hue + 60) % 360,
    });
    if(effects.trail.length > 40) effects.trail.shift();
  }

  function updateEffects(){
    for(let i=effects.score.length-1; i >= 0; i--){
      let e = effects.score[i];
      e.y -= 1.7;
      e.alpha -= 0.022;
      e.size += 0.3;
      e.glow = Math.min(30, e.glow + 1);
      if(e.alpha <= 0) effects.score.splice(i,1);
    }

    for(let i=effects.particles.length - 1; i >= 0; i--){
      let p = effects.particles[i];
      p.x += p.dx;
      p.y += p.dy;
      p.alpha -= 0.05;
      p.life--;
      if(p.alpha <= 0 || p.life <= 0) effects.particles.splice(i,1);
    }

    for(let i=effects.trail.length - 1; i >= 0; i--){
      let t = effects.trail[i];
      t.x += t.dx;
      t.y += t.dy;
      t.alpha -= 0.02;
      if(t.alpha <= 0) effects.trail.splice(i,1);
    }
  }
  function flashScore(){
    sb.classList.add('flash');
    setTimeout(() => sb.classList.remove('flash'), 220);
    try {
      eatSfx.currentTime = 0;
      eatSfx.play();
    } catch {}
  }

  function updateSpeed(){
    const increaseEvery = 40;
    speedMultiplier = Math.min(2.8, 1 + Math.floor(score / increaseEvery) * 0.25);
    interval = 1000 / (baseFps * speedMultiplier);
  }

  function update(){
    if(gameOver || !gameStarted) return;

    if((nextVel.x !== -velocity.x || nextVel.y !== -velocity.y)
     && (nextVel.x !== velocity.x || nextVel.y !== velocity.y)) {
      velocity = nextVel;
    }

    let head = {
      x: (snake[0].x + velocity.x + tileCount) % tileCount,
      y: (snake[0].y + velocity.y + tileCount) % tileCount
    };

    if(snake.some(p => p.x === head.x && p.y === head.y)) {
      onGameOver();
      return;
    }

    snake.unshift(head);
    smoothPos.unshift({x: head.x, y: head.y});

    let ate = false;
    for(let i = 0; i < apples.length; i++){
      if(apples[i].x === head.x && apples[i].y === head.y){
        let now = Date.now();
        let timeBonus = (now - lastAppleTime < 2800) ? 6 : 0;
        let comboBonus = comboCounter * 3;
        let gain = 11 + timeBonus + comboBonus;
        score += gain;
        comboCounter++;
        lastAppleTime = now;
        scoreEl.textContent = score;
        if(score > highScore){
          highScore = score;
          localStorage.setItem('snakeHighScore', highScore);
          highEl.textContent = highScore;
        }
        addScoreFx(head.x, head.y, gain);
        addParticles(head.x, head.y);
        flashScore();
        apples.splice(i,1);
        spawnApples();
        ate = true;
        updateSpeed();
        break;
      }
    }

    if(!ate){
      comboCounter = 0;
      snake.pop();
      smoothPos.pop();
    }

    addTrail();
  }

  function draw(){
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // subtle glowing grid for neon effect
    ctx.lineWidth = 1;
    let colorBase = `rgba(0,255,255,0.07)`;
    for(let i = 0; i <= tileCount; i++){
      const pos = i * gridSize;
      ctx.strokeStyle = colorBase;
      ctx.beginPath();
      ctx.moveTo(pos, 0);
      ctx.lineTo(pos, canvas.height);
      ctx.moveTo(0, pos);
      ctx.lineTo(canvas.width, pos);
      ctx.stroke();
    }

    // Draw apples with dynamic 3D neon effect
    let now = performance.now();
    let pulse = 0.15 * Math.sin(now / 380) + 1;
    apples.forEach(a => {
      const cx = a.x * gridSize + gridSize/2;
      const cy = a.y * gridSize + gridSize/2;
      ctx.save();
      ctx.translate(cx, cy);
      ctx.rotate((now/900 + a.x + a.y) % (2*Math.PI));
      ctx.scale(pulse, pulse);

      // Apple body: vibrant neon gradient layered circle for 3D glow
      let grad = ctx.createRadialGradient(0, -gridSize*0.15, gridSize*0.15, 0, 0, gridSize*0.5);
      grad.addColorStop(0, "#ff3561");
      grad.addColorStop(0.4, "#ff5473");
      grad.addColorStop(1, "#a81440");
      ctx.fillStyle = grad;
      ctx.shadowColor = "#ff5080";
      ctx.shadowBlur = 18;
      ctx.beginPath();
      ctx.ellipse(0, 0, gridSize*0.44, gridSize*0.58, Math.PI/7, 0, 2*Math.PI);
      ctx.fill();

      // Apple shiny top highlight
      let highlightGrad = ctx.createRadialGradient(-gridSize*0.14, -gridSize*0.35, 6, -gridSize*0.1, -gridSize*0.3, gridSize*0.18);
      highlightGrad.addColorStop(0, "rgba(255,255,255,0.85)");
      highlightGrad.addColorStop(1, "rgba(255,255,255,0)");
      ctx.fillStyle = highlightGrad;
      ctx.beginPath();
      ctx.ellipse(-gridSize*0.14, -gridSize*0.35, gridSize*0.11, gridSize*0.18, Math.PI/3.8, 0, 2*Math.PI);
      ctx.fill();

      // Neon leafy stem with gradient
      let leafGrad = ctx.createLinearGradient(0, -gridSize*0.58, 0, -gridSize*0.18);
      leafGrad.addColorStop(0, "#44ee44");
      leafGrad.addColorStop(1, "#116811");
      ctx.fillStyle = leafGrad;
      ctx.beginPath();
      ctx.ellipse(0, -gridSize*0.32, gridSize*0.18, gridSize*0.35, Math.PI/4, 0, 2*Math.PI);
      ctx.fill();

      // Leaf highlights
      ctx.fillStyle = "rgba(130, 255, 130, 0.48)";
      ctx.beginPath();
      ctx.ellipse(-gridSize*0.05, -gridSize*0.46, gridSize*0.08, gridSize*0.16, Math.PI/6, 0, 2*Math.PI);
      ctx.fill();

      ctx.restore();
    });

    // Draw snake trail particles (glowing orbs)
    effects.trail.forEach(t => {
      ctx.fillStyle = `hsla(${t.colorHue}, 100%, 75%, ${t.alpha.toFixed(2)})`;
      ctx.shadowColor = `hsla(${t.colorHue}, 90%, 85%, ${t.alpha.toFixed(2)})`;
      ctx.shadowBlur = 6;
      ctx.beginPath();
      ctx.arc(t.x, t.y, t.size * 1.3, 0, 2*Math.PI);
      ctx.fill();
      ctx.shadowBlur = 0;
    });

    // Draw snake segments with 3D neon shader look, smooth motion & live color cycling
    for(let i=0; i < snake.length; i++){
      let target = snake[i];
      let pos = smoothPos[i];
      pos.x += (target.x - pos.x) * smoothSpeed;
      pos.y += (target.y - pos.y) * smoothSpeed;
      let px = pos.x * gridSize;
      let py = pos.y * gridSize;

      ctx.save();

      // Skew for subtle 3D sense
      ctx.transform(1, 0.17, 0, 1, 0, 0); 

      // Create segment base gradient with cool neon hues
      const segmentHue = (hue + i*14) % 360;
      const lighter = `hsl(${(segmentHue + 80) % 360}, 95%, 70%)`;
      const darker = `hsl(${segmentHue}, 85%, 35%)`;
      let grad = ctx.createLinearGradient(px, py, px + gridSize * 0.85, py + gridSize * .75);
      grad.addColorStop(0, lighter);
      grad.addColorStop(1, darker);

      ctx.fillStyle = grad;

      ctx.shadowColor = (i === 0 ? `hsl(${segmentHue}, 100%, 70%)` : `hsl(${segmentHue}, 80%, 60%)`);
      ctx.shadowBlur = (i === 0 ? 30 : 14);
      ctx.shadowOffsetX = 2;
      ctx.shadowOffsetY = 2;

      roundedRect(px + 3, py + 3, gridSize - 6, gridSize - 6, 16);

      // Snake Head Eyes & Highlights: brightest, with glowing pupils and 3D reflections
      if(i === 0){
        ctx.shadowColor = "transparent";
        ctx.fillStyle = "#0ff7ff"; // eye glow color

        const eyeSize = gridSize * 0.14;
        const eyeY = py + gridSize*0.38;
        const eyeOffsetX = velocity.x * gridSize * 0.18;
        const eyeOffsetY = velocity.y * gridSize * 0.18;

        // Glowing sclera for eyes
        ctx.beginPath();
        ctx.ellipse(px + gridSize*0.34 + eyeOffsetX, eyeY + eyeOffsetY, eyeSize*1.07, eyeSize*1.5, 0, 0, 2*Math.PI);
        ctx.ellipse(px + gridSize*0.66 + eyeOffsetX, eyeY + eyeOffsetY, eyeSize*1.07, eyeSize*1.5, 0, 0, 2*Math.PI);
        ctx.fill();

        // Pupils with depth gradient
        let pupilGrad = ctx.createRadialGradient(
          px + gridSize*0.34 + eyeOffsetX*1.1, eyeY + eyeOffsetY, eyeSize*0.64,
          px + gridSize*0.34 + eyeOffsetX*1.1, eyeY + eyeOffsetY, eyeSize*0.18
        );
        pupilGrad.addColorStop(0, '#003b44');
        pupilGrad.addColorStop(1, '#001515');
        ctx.fillStyle = pupilGrad;
        ctx.beginPath();
        ctx.ellipse(px + gridSize*0.34 + eyeOffsetX*1.1, eyeY + eyeOffsetY, eyeSize*0.7, eyeSize*1.3, 0, 0, 2*Math.PI);
        ctx.ellipse(px + gridSize*0.66 + eyeOffsetX*1.1, eyeY + eyeOffsetY, eyeSize*0.7, eyeSize*1.3, 0, 0, 2*Math.PI);
        ctx.fill();

        // Head highlight - strong neon shine
        let highlight = ctx.createRadialGradient(
          px + gridSize*0.48, py + gridSize*0.28, gridSize*0.44,
          px + gridSize*0.48, py + gridSize*0.28, gridSize*0.2
        );
        highlight.addColorStop(0, 'rgba(255,255,255,0.5)');
        highlight.addColorStop(1, 'rgba(255,255,255,0)');
        ctx.fillStyle = highlight;
        ctx.beginPath();
        ctx.ellipse(px + gridSize*0.5, py + gridSize*0.25, gridSize*0.45, gridSize*0.27, Math.PI / 7, 0, 2*Math.PI);
        ctx.fill();
      }

      ctx.restore();
    }

    // Exploded neon particles
    effects.particles.forEach(p => {
      ctx.fillStyle = `hsla(${p.colorHue}, 100%, 65%, ${p.alpha.toFixed(2)})`;
      ctx.shadowColor = `hsla(${p.colorHue}, 95%, 75%, ${p.alpha.toFixed(2)})`;
      ctx.shadowBlur = 12;
      ctx.beginPath();
      ctx.arc(p.x, p.y, p.size, 0, 2*Math.PI);
      ctx.fill();
      ctx.shadowBlur = 0;
    });

    // Score popup with neon text glow
    effects.score.forEach(e=>{
      ctx.fillStyle = `rgba(0,255,255,${e.alpha.toFixed(2)})`;
      ctx.font = `${e.size}px "Segoe UI", Arial, sans-serif`;
      ctx.textAlign = 'center';
      ctx.shadowColor = `rgba(0,255,255,${(e.alpha * 0.7).toFixed(2)})`;
      ctx.shadowBlur = e.glow;
      ctx.fillText(e.text, e.x, e.y);
    });
  }

  // Rounded rectangle drawing helper
  function roundedRect(x,y,w,h,r){
    ctx.beginPath();
    ctx.moveTo(x+r,y);
    ctx.lineTo(x+w-r,y);
    ctx.quadraticCurveTo(x+w,y,x+w,y+r);
    ctx.lineTo(x+w,y+h-r);
    ctx.quadraticCurveTo(x+w,y+h,x+w-r,y+h);
    ctx.lineTo(x+r,y+h);
    ctx.quadraticCurveTo(x,y+h,x,y+h-r);
    ctx.lineTo(x,y+r);
    ctx.quadraticCurveTo(x,y,x+r,y);
    ctx.closePath();
    ctx.fill();
  }

  // Input handling (keyboard + touch)
  let touchStart = null;
  const minSwipe = 30;

  canvas.addEventListener('touchstart', e => {
    if(e.touches.length === 1) {
      touchStart = { x:e.touches[0].clientX, y:e.touches[0].clientY };
    }
  }, {passive:true});

  canvas.addEventListener('touchmove', e => {
    if(!touchStart) return;
    const dx = e.touches[0].clientX - touchStart.x;
    const dy = e.touches[0].clientY - touchStart.y;
    if(Math.abs(dx) > minSwipe || Math.abs(dy) > minSwipe){
      const dir = Math.abs(dx) > Math.abs(dy)
                ? (dx > 0 ? 'right' : 'left')
                : (dy > 0 ? 'down' : 'up');
      changeDir(dir);
      touchStart = null;
    }
  }, {passive:true});

  canvas.addEventListener('touchend', () => { touchStart = null; });

  window.addEventListener('keydown', e => {
    if(!gameStarted || gameOver) return;
    const keyMap = {
      ArrowUp: 'up', ArrowDown: 'down',
      ArrowLeft: 'left', ArrowRight: 'right',
      w: 'up', s: 'down', a: 'left', d: 'right'
    };
    let k = e.key;
    if(k.length === 1) k = k.toLowerCase();
    if(k === 'f'){
      showFPS = !showFPS;
      fpsCounter.style.display = showFPS ? 'block' : 'none';
      return;
    }
    let dir = keyMap[k];
    if(dir){
      e.preventDefault();
      changeDir(dir);
    }
  });

  function changeDir(direction){
    const map = {
      up: {x:0, y:-1},
      down: {x:0, y:1},
      left: {x:-1, y:0},
      right: {x:1, y:0}
    };
    const nv = map[direction];
    if(nv && (nv.x !== -velocity.x || nv.y !== -velocity.y)){
      if(nv.x !== velocity.x || nv.y !== velocity.y){
        nextVel = nv;
        try {
          turnSfx.currentTime = 0;
          turnSfx.play();
        } catch {}
      }
    }
  }

  function onGameOver(){
    gameOver = true;
    gameStarted = false;
    try {
      gameOverSfx.currentTime = 0;
      gameOverSfx.play();
    }catch{}

    setTimeout(() => {
      gameOverText.textContent = `Game Over! Your Score: ${score}`;
      overlay.style.display = 'flex';
      restartBtn.style.display = 'block';
      restartBtn.focus();
    }, 340);
  }
  restartBtn.addEventListener('click', () => {
    initGame();
    restartBtn.style.display = 'none';
  });

  function loop(ts=0){
    animFrameId = requestAnimationFrame(loop);
    if(!lastTime) lastTime = ts;
    delta += ts - lastTime;
    lastTime = ts;

    while(delta >= interval){
      update();
      updateEffects();
      delta -= interval;
    }

    // Shift hue slowly for cycling neon effect
    hue = (hue + 0.9) % 360;

    draw();

    if(showFPS){
      fps = Math.round(1000 / (interval));
      fpsCounter.textContent = `FPS: ${fps}`;
    }
  }

  initGame();
})();
</script>
</body>
</html>