{
  "schema_version": 1,
  "publisher": "alphaBell",
  "model": "RecursiveSI/flint",
  "quantization": "Q8_0",
  "source_dataset": "alphabell/Flint-Arcade-Trajectories",
  "source_revision": "5187421145ca1e521c366f9892460ee2881cb194",
  "source_jsonl_sha256": "abeee71e6f0d53c325bdb255298aa03fd9cb049e4ebd4961f00ec66a978b507a",
  "source_url": "https://huggingface.co/datasets/alphabell/Flint-Arcade-Trajectories/resolve/5187421145ca1e521c366f9892460ee2881cb194/trajectories.jsonl",
  "license": "Apache-2.0",
  "cases": [
    {
      "id": "moon-garden",
      "title": "Moon Garden",
      "label": "One missing angle",
      "line": 240,
      "lesson": "Canvas arc needs a start angle and an end angle. The greenhouse circle supplied only one.",
      "note": "The recorded repair also updates frame timing and keyboard start/restart handling.",
      "prompt": "Moon Garden: a cozy arcade game about a tiny astronaut collecting floating seeds on a round little moon. Arrow keys or WASD move, with large visible touch direction buttons. Carry three seeds back to the glowing greenhouse to win. Avoid two slow drifting moon moths; three collisions lose. Draw a beautiful warm ivory, emerald and golden night garden with Canvas. Show a clear seed counter, hearts, instructions and Start/Restart buttons. Make movement immediately obvious and controls forgiving.",
      "before": "<!doctype html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">\n<meta name=\"description\" content=\"A cozy arcade game about a tiny astronaut collecting floating seeds on a round little moon.\">\n<title>Moon Garden</title>\n<style>\n  html, body { margin:0; padding:0; height:100%; background:#f6f1e7; overflow:hidden; font-family:ui-rounded, system-ui, sans-serif; }\n  #game { position:relative; width:min(94vw, 520px); height:min(70vh, 560px); margin:0 auto; }\n  canvas { display:block; width:100%; height:100%; background:radial-gradient(circle at 50% 35%, #fffdf6, #f6efdd); border-radius:18px; }\n  #overlay { position:absolute; inset:0; display:none; align-items:center; justify-content:center; background:rgba(246,241,231,.86); border-radius:18px; text-align:center; padding:16px; box-sizing:border-box; z-index:5; }\n  #overlay.visible { display:flex; }\n  #panel { max-width:92%; }\n  #overlay h2 { margin:0 0 6px; color:#2f5d3a; font-size:28px; }\n  #overlay p { margin:0 0 12px; color:#6b5b3e; font-size:14px; line-height:1.45; }\n  #overlay .big { font-size:44px; margin:6px 0; }\n  button { font:inherit; border:0; border-radius:999px; padding:12px 22px; background:#e9c46a; color:#5a3b12; font-weight:700; box-shadow:0 4px 0 #c99b3f; }\n  button:active { transform:translateY(2px); box-shadow:0 2px 0 #c99b3f; }\n  #restartBtn { position:absolute; top:8px; right:8px; z-index:10; padding:8px 14px; font-size:13px; background:#e9c46a; }\n  #touch-controls { display:none; grid-template-columns:repeat(3,64px); grid-template-rows:repeat(2,64px); gap:8px; justify-content:center; margin-top:10px; }\n  #touch-controls.active { display:grid; }\n  .touch-btn { width:64px; height:64px; border-radius:50%; border:0; background:rgba(47,93,58,.85); color:#fff; font-size:22px; touch-action:none; user-select:none; }\n  .touch-btn:active { background:rgba(47,93,58,1); }\n  @media (max-width: 480px) { #game { width:min(94vw, 520px); height:min(62vh, 500px); } }\n</style>\n</head>\n<body>\n<div id=\"game\">\n  <canvas id=\"gameCanvas\" width=\"480\" height=\"480\"></canvas>\n  <div id=\"overlay\">\n    <div id=\"panel\">\n      <h2 id=\"overlayTitle\">Moon Garden</h2>\n      <p id=\"overlayText\">Collect 3 floating seeds and carry them back to the glowing greenhouse. Avoid 2 moon moths. Three collisions lose.</p>\n      <div id=\"overlayBig\"></div>\n      <button id=\"startBtn\">Start</button>\n    </div>\n  </div>\n  <button id=\"restartBtn\">Restart</button>\n</div>\n<div id=\"touch-controls\">\n  <div></div>\n  <button class=\"touch-btn\" id=\"upBtn\">▲</button>\n  <div></div>\n  <button class=\"touch-btn\" id=\"leftBtn\">◀</button>\n  <button class=\"touch-btn\" id=\"rightBtn\">▶</button>\n  <button class=\"touch-btn\" id=\"downBtn\">▼</button>\n</div>\n<script>\nconst canvas = document.getElementById('gameCanvas');\nconst ctx = canvas.getContext('2d');\nconst overlay = document.getElementById('overlay');\nconst overlayTitle = document.getElementById('overlayTitle');\nconst overlayText = document.getElementById('overlayText');\nconst overlayBig = document.getElementById('overlayBig');\nconst startBtn = document.getElementById('startBtn');\nconst restartBtn = document.getElementById('restartBtn');\nconst touchControls = document.getElementById('touch-controls');\nconst upBtn = document.getElementById('upBtn');\nconst downBtn = document.getElementById('downBtn');\nconst leftBtn = document.getElementById('leftBtn');\nconst rightBtn = document.getElementById('rightBtn');\n\nconst W = 480, H = 480;\nconst state = {\n  phase: 'ready',\n  score: 0,\n  hearts: 3,\n  inventory: 0,\n  seeds: [],\n  moths: [],\n  player: { x: 240, y: 140 },\n  keys: {},\n  touch: {},\n  lastTime: 0,\n  seedTimer: 0,\n  time: 0\n};\n\nconst PLAYER_R = 14;\nconst GREENHOUSE = { x: 240, y: 365 };\nconst MOON = { x: 240, y: 240, r: 180 };\n\nfunction reset() {\n  state.phase = 'ready';\n  state.score = 0;\n  state.hearts = 3;\n  state.inventory = 0;\n  state.seeds = [\n    { x: 240, y: 75, taken: false },\n    { x: 90, y: 210, taken: false },\n    { x: 390, y: 210, taken: false }\n  ];\n  state.moths = [\n    { x: 140, y: 170, vx: 18, vy: 8, phase: 0 },\n    { x: 340, y: 300, vx: -14, vy: 12, phase: 1 }\n  ];\n  state.player = { x: 240, y: 140 };\n  state.keys = {};\n  state.touch = {};\n  state.lastTime = 0;\n  state.seedTimer = 0;\n  state.time = 0;\n}\n\nfunction setOverlayForPhase() {\n  if (state.phase === 'ready') {\n    overlay.classList.add('visible');\n    overlayTitle.textContent = 'Moon Garden';\n    overlayText.textContent = 'Collect 3 floating seeds and carry them back to the glowing greenhouse. Avoid 2 moon moths. Three collisions lose.';\n    overlayBig.textContent = '';\n  } else if (state.phase === 'won') {\n    overlay.classList.add('visible');\n    overlayTitle.textContent = 'Moon Garden';\n    overlayText.textContent = 'The greenhouse glows. The little astronaut made it home.';\n    overlayBig.textContent = 'Won';\n  } else if (state.phase === 'lost') {\n    overlay.classList.add('visible');\n    overlayTitle.textContent = 'Moon Garden';\n    overlayText.textContent = 'The moths found you. The garden will miss one more heartbeat.';\n    overlayBig.textContent = 'Lost';\n  } else {\n    overlay.classList.remove('visible');\n  }\n}\n\nfunction startGame() {\n  reset();\n  state.phase = 'playing';\n  setOverlayForPhase();\n}\n\nfunction restartGame() {\n  reset();\n  setOverlayForPhase();\n}\n\nfunction lose() {\n  state.phase = 'lost';\n  setOverlayForPhase();\n}\n\nfunction win() {\n  state.phase = 'won';\n  setOverlayForPhase();\n}\n\nfunction update(dt) {\n  if (state.phase !== 'playing') return;\n  let ix = (state.keys['arrowleft'] || state.keys['a'] ? -1 : 0) + (state.keys['arrowright'] || state.keys['d'] ? 1 : 0);\n  let iy = (state.keys['arrowup'] || state.keys['w'] ? -1 : 0) + (state.keys['arrowdown'] || state.keys['s'] ? 1 : 0);\n  if (state.touch.up) iy -= 1;\n  if (state.touch.down) iy += 1;\n  if (state.touch.left) ix -= 1;\n  if (state.touch.right) ix += 1;\n  let len = Math.sqrt(ix * ix + iy * iy);\n  if (len > 1) { ix /= len; iy /= len; len = 1; }\n  const speed = 150;\n  state.player.x += ix * speed * dt;\n  state.player.y += iy * speed * dt;\n  state.player.x = Math.max(20, Math.min(W - 20, state.player.x));\n  state.player.y = Math.max(20, Math.min(H - 20, state.player.y));\n\n  state.seedTimer -= dt;\n  if (state.seedTimer <= 0) {\n    state.seedTimer = 3;\n    const a = Math.random() * Math.PI * 2;\n    const r = 90 + Math.random() * 60;\n    state.seeds.push({ x: 240 + Math.cos(a) * r, y: 240 + Math.sin(a) * r, taken: false });\n  }\n\n  for (const s of state.seeds) {\n    if (s.taken) continue;\n    const d = Math.hypot(state.player.x - s.x, state.player.y - s.y);\n    if (d < 42) {\n      s.taken = true;\n      state.inventory++;\n      state.score += 100;\n    }\n  }\n\n  const p = state.player;\n  for (const m of state.moths) {\n    m.phase += dt * 0.8;\n    m.x += m.vx * dt + Math.sin(m.phase) * 8 * dt;\n    m.y += m.vy * dt + Math.cos(m.phase * 0.7) * 6 * dt;\n    m.x = Math.max(30, Math.min(W - 30, m.x));\n    m.y = Math.max(30, Math.min(H - 30, m.y));\n    if (Math.hypot(p.x - m.x, p.y - m.y) < 26) {\n      state.hearts--;\n      if (state.hearts <= 0) { lose(); return; }\n    }\n  }\n\n  if (state.inventory >= 3 && Math.hypot(p.x - GREENHOUSE.x, p.y - GREENHOUSE.y) < 34) {\n    win();\n  }\n}\n\nfunction drawHeart(x, y, s, color) {\n  ctx.save();\n  ctx.translate(x, y);\n  ctx.scale(s, s);\n  ctx.beginPath();\n  ctx.moveTo(0, 4);\n  ctx.bezierCurveTo(-8, -2, -4, -8, 0, -3);\n  ctx.bezierCurveTo(4, -8, 8, -2, 0, 4);\n  ctx.fillStyle = color;\n  ctx.fill();\n  ctx.restore();\n}\n\nfunction draw() {\n  ctx.clearRect(0, 0, W, H);\n  const g = ctx.createRadialGradient(240, 210, 40, 240, 240, 250);\n  g.addColorStop(0, '#fffdf6');\n  g.addColorStop(1, '#f3ead6');\n  ctx.fillStyle = g;\n  ctx.fillRect(0, 0, W, H);\n\n  for (let i = 0; i < 24; i++) {\n    const x = (i * 97) % W;\n    const y = (i * 173) % H;\n    const r = (i * 7) % 2 + 1;\n    ctx.fillStyle = i % 3 ? '#d9c9a3' : '#e9c46a';\n    ctx.beginPath();\n    ctx.arc(x, y, r, 0, Math.PI * 2);\n    ctx.fill();\n  }\n\n  ctx.beginPath();\n  ctx.arc(MOON.x, MOON.y, MOON.r, 0, Math.PI * 2);\n  ctx.fillStyle = '#f8f3e6';\n  ctx.fill();\n  ctx.lineWidth = 3;\n  ctx.strokeStyle = '#e0cfa6';\n  ctx.stroke();\n\n  ctx.beginPath();\n  ctx.arc(GREENHOUSE.x, GREENHOUSE.y, 34, Math.PI * 2);\n  ctx.fillStyle = '#1f4d33';\n  ctx.fill();\n  ctx.beginPath();\n  ctx.ellipse(GREENHOUSE.x, GREENHOUSE.y - 10, 26, 14, 0, Math.PI, 0);\n  ctx.fillStyle = '#e9c46a';\n  ctx.fill();\n  ctx.beginPath();\n  ctx.arc(GREENHOUSE.x, GREENHOUSE.y - 18, 20, Math.PI, 0);\n  ctx.fillStyle = '#bfe3ff';\n  ctx.fill();\n  ctx.strokeStyle = '#8ecae6';\n  ctx.lineWidth = 3;\n  ctx.stroke();\n\n  for (const s of state.seeds) {\n    const bob = Math.sin(state.time * 2 + s.x) * 5;\n    ctx.save();\n    ctx.translate(s.x, s.y + bob);\n    ctx.rotate(Math.sin(state.time + s.y) * 0.2);\n    ctx.beginPath();\n    ctx.moveTo(0, -9);\n    ctx.quadraticCurveTo(7, -2, 5, 5);\n    ctx.quadraticCurveTo(0, 9, -5, 5);\n    ctx.quadraticCurveTo(-7, -2, 0, -9);\n    ctx.fillStyle = '#e9c46a';\n    ctx.fill();\n    ctx.strokeStyle = '#c99b3f';\n    ctx.lineWidth = 2;\n    ctx.stroke();\n    ctx.restore();\n  }\n\n  ctx.save();\n  ctx.translate(state.player.x, state.player.y);\n  const ang = Math.atan2(\n    state.keys['arrowdown'] || state.keys['s'] ? 1 : state.keys['arrowup'] || state.keys['w'] ? -1 : 0,\n    state.keys['arrowright'] || state.keys['d'] ? 1 : state.keys['arrowleft'] || state.keys['a'] ? -1 : 0\n  );\n  ctx.rotate(ang);\n  ctx.beginPath();\n  ctx.arc(0, 0, PLAYER_R, 0, Math.PI * 2);\n  ctx.fillStyle = '#f4f1e6';\n  ctx.fill();\n  ctx.lineWidth = 3;\n  ctx.strokeStyle = '#2f5d3a';\n  ctx.stroke();\n  ctx.beginPath();\n  ctx.arc(0, -3, 7, Math.PI * 0.1, Math.PI * 0.9);\n  ctx.fillStyle = '#bfe3ff';\n  ctx.fill();\n  ctx.strokeStyle = '#2f5d3a';\n  ctx.lineWidth = 2;\n  ctx.stroke();\n  ctx.fillStyle = '#2f5d3a';\n  ctx.fillRect(-5, 7, 10, 6);\n  ctx.restore();\n\n  for (const m of state.moths) {\n    ctx.save();\n    ctx.translate(m.x, m.y);\n    ctx.rotate(Math.atan2(m.vy, m.vx));\n    ctx.beginPath();\n    ctx.ellipse(0, -8, 12, 7, -0.2, 0, Math.PI * 2);\n    ctx.ellipse(0, 8, 12, 7, 0.2, 0, Math.PI * 2);\n    ctx.fillStyle = '#2b2b3a';\n    ctx.fill();\n    ctx.beginPath();\n    ctx.arc(0, 0, 6, 0, Math.PI * 2);\n    ctx.fillStyle = '#23232f';\n    ctx.fill();\n    ctx.restore();\n  }\n\n  ctx.fillStyle = '#2f5d3a';\n  for (let i = 0; i < 3; i++) {\n    drawHeart(40 + i * 34, 42, 1, i < state.hearts ? '#e4572e' : '#d8d2c2');\n  }\n  ctx.fillStyle = '#8a6d2f';\n  ctx.font = 'bold 20px ui-rounded, system-ui, sans-serif';\n  ctx.textAlign = 'center';\n  ctx.fillText('Seeds: ' + state.inventory + '/3', W / 2, 68);\n\n  ctx.fillStyle = '#5a3b12';\n  ctx.font = 'bold 16px ui-rounded, system-ui, sans-serif';\n  ctx.fillText('Score: ' + state.score, W / 2, H - 18);\n}\n\nlet rafId = 0;\nfunction loop(t) {\n  const dt = Math.min(0.033, Math.max(0.0001, (t - (state.lastTime || t)) / 1000));\n  state.time += dt;\n  update(dt);\n  draw();\n  rafId = requestAnimationFrame(loop);\n}\n\nfunction keyHandler(e) {\n  const k = e.key.toLowerCase();\n  if (['arrowup','arrowdown','arrowleft','arrowright',' '].includes(k) || ['w','a','s','d','arrowup','arrowdown','arrowleft','arrowright'].includes(k)) {\n    state.keys[k] = true;\n  }\n  if (k === 'r') restartGame();\n}\n\nfunction touchHandler(type, key) {\n  return function(e) {\n    e.preventDefault();\n    if (type === 'start') {\n      state.touch[key] = true;\n      state.keys[key] = true;\n    } else {\n      state.touch[key] = false;\n      if (key === 'arrowup') state.keys['arrowup'] = false;\n      if (key === 'arrowdown') state.keys['arrowdown'] = false;\n      if (key === 'arrowleft') state.keys['arrowleft'] = false;\n      if (key === 'arrowright') state.keys['arrowright'] = false;\n      if (key === 'w') state.keys['w'] = false;\n      if (key === 'a') state.keys['a'] = false;\n      if (key === 's') state.keys['s'] = false;\n      if (key === 'd') state.keys['d'] = false;\n    }\n  };\n}\n\nstartBtn.addEventListener('pointerdown', function(e) { e.preventDefault(); startGame(); });\nrestartBtn.addEventListener('pointerdown', function(e) { e.preventDefault(); restartGame(); });\nupBtn.addEventListener('pointerdown', touchHandler('start', 'up'));\nupBtn.addEventListener('pointerup', touchHandler('end', 'up'));\nupBtn.addEventListener('pointercancel', touchHandler('end', 'up'));\ndownBtn.addEventListener('pointerdown', touchHandler('start', 'down'));\ndownBtn.addEventListener('pointerup', touchHandler('end', 'down'));\nleftBtn.addEventListener('pointerdown', touchHandler('start', 'left'));\nleftBtn.addEventListener('pointerup', touchHandler('end', 'left'));\nrightBtn.addEventListener('pointerdown', touchHandler('start', 'right'));\nrightBtn.addEventListener('pointerup', touchHandler('end', 'right'));\n\ncanvas.addEventListener('pointerdown', function(e) { e.preventDefault(); if (state.phase === 'ready') startGame(); });\nwindow.addEventListener('keydown', keyHandler);\nwindow.addEventListener('keyup', function(e) { const k = e.key.toLowerCase(); if (state.keys[k] !== undefined) state.keys[k] = false; });\n\nfunction refreshTouch() {\n  const active = state.phase === 'playing';\n  touchControls.classList.toggle('active', active);\n  upBtn.classList.toggle('active', !!state.touch.up);\n  downBtn.classList.toggle('active', !!state.touch.down);\n  leftBtn.classList.toggle('active', !!state.touch.left);\n  rightBtn.classList.toggle('active', !!state.touch.right);\n}\nsetInterval(refreshTouch, 100);\n\nreset();\nsetOverlayForPhase();\nrequestAnimationFrame(loop);\n\nwindow.arcade = {\n  restart: function() { restartGame(); },\n  snapshot: function() {\n    return {\n      phase: state.phase,\n      score: state.score,\n      player: { x: state.player.x, y: state.player.y }\n    };\n  }\n};\n</script>\n</body>\n</html>",
      "after": "<!doctype html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">\n<meta name=\"description\" content=\"A cozy arcade game about a tiny astronaut collecting floating seeds on a round little moon.\">\n<title>Moon Garden</title>\n<style>\n  html, body { margin:0; padding:0; height:100%; background:#f6f1e7; overflow:hidden; font-family:ui-rounded, system-ui, sans-serif; }\n  #game { position:relative; width:min(94vw, 520px); height:min(70vh, 560px); margin:0 auto; }\n  canvas { display:block; width:100%; height:100%; background:radial-gradient(circle at 50% 35%, #fffdf6, #f6efdd); border-radius:18px; }\n  #overlay { position:absolute; inset:0; display:none; align-items:center; justify-content:center; background:rgba(246,241,231,.86); border-radius:18px; text-align:center; padding:16px; box-sizing:border-box; z-index:5; }\n  #overlay.visible { display:flex; }\n  #panel { max-width:92%; }\n  #overlay h2 { margin:0 0 6px; color:#2f5d3a; font-size:28px; }\n  #overlay p { margin:0 0 12px; color:#6b5b3e; font-size:14px; line-height:1.45; }\n  #overlay .big { font-size:44px; margin:6px 0; }\n  button { font:inherit; border:0; border-radius:999px; padding:12px 22px; background:#e9c46a; color:#5a3b12; font-weight:700; box-shadow:0 4px 0 #c99b3f; }\n  button:active { transform:translateY(2px); box-shadow:0 2px 0 #c99b3f; }\n  #restartBtn { position:absolute; top:8px; right:8px; z-index:10; padding:8px 14px; font-size:13px; background:#e9c46a; }\n  #touch-controls { display:none; grid-template-columns:repeat(3,64px); grid-template-rows:repeat(2,64px); gap:8px; justify-content:center; margin-top:10px; }\n  #touch-controls.active { display:grid; }\n  .touch-btn { width:64px; height:64px; border-radius:50%; border:0; background:rgba(47,93,58,.85); color:#fff; font-size:22px; touch-action:none; user-select:none; }\n  .touch-btn:active { background:rgba(47,93,58,1); }\n  @media (max-width: 480px) { #game { width:min(94vw, 520px); height:min(62vh, 500px); } }\n</style>\n</head>\n<body>\n<div id=\"game\">\n  <canvas id=\"gameCanvas\" width=\"480\" height=\"480\"></canvas>\n  <div id=\"overlay\">\n    <div id=\"panel\">\n      <h2 id=\"overlayTitle\">Moon Garden</h2>\n      <p id=\"overlayText\">Collect 3 floating seeds and carry them back to the glowing greenhouse. Avoid 2 moon moths. Three collisions lose.</p>\n      <div id=\"overlayBig\"></div>\n      <button id=\"startBtn\">Start</button>\n    </div>\n  </div>\n  <button id=\"restartBtn\">Restart</button>\n</div>\n<div id=\"touch-controls\">\n  <div></div>\n  <button class=\"touch-btn\" id=\"upBtn\">▲</button>\n  <div></div>\n  <button class=\"touch-btn\" id=\"leftBtn\">◀</button>\n  <button class=\"touch-btn\" id=\"rightBtn\">▶</button>\n  <button class=\"touch-btn\" id=\"downBtn\">▼</button>\n</div>\n<script>\nconst canvas = document.getElementById('gameCanvas');\nconst ctx = canvas.getContext('2d');\nconst overlay = document.getElementById('overlay');\nconst overlayTitle = document.getElementById('overlayTitle');\nconst overlayText = document.getElementById('overlayText');\nconst overlayBig = document.getElementById('overlayBig');\nconst startBtn = document.getElementById('startBtn');\nconst restartBtn = document.getElementById('restartBtn');\nconst touchControls = document.getElementById('touch-controls');\nconst upBtn = document.getElementById('upBtn');\nconst downBtn = document.getElementById('downBtn');\nconst leftBtn = document.getElementById('leftBtn');\nconst rightBtn = document.getElementById('rightBtn');\n\nconst W = 480, H = 480;\nconst state = {\n  phase: 'ready',\n  score: 0,\n  hearts: 3,\n  inventory: 0,\n  seeds: [],\n  moths: [],\n  player: { x: 240, y: 140 },\n  keys: {},\n  touch: {},\n  lastTime: 0,\n  seedTimer: 0,\n  time: 0\n};\n\nconst PLAYER_R = 14;\nconst GREENHOUSE = { x: 240, y: 365 };\nconst MOON = { x: 240, y: 240, r: 180 };\n\nfunction reset() {\n  state.phase = 'ready';\n  state.score = 0;\n  state.hearts = 3;\n  state.inventory = 0;\n  state.seeds = [\n    { x: 240, y: 75, taken: false },\n    { x: 90, y: 210, taken: false },\n    { x: 390, y: 210, taken: false }\n  ];\n  state.moths = [\n    { x: 140, y: 170, vx: 18, vy: 8, phase: 0 },\n    { x: 340, y: 300, vx: -14, vy: 12, phase: 1 }\n  ];\n  state.player = { x: 240, y: 140 };\n  state.keys = {};\n  state.touch = {};\n  state.lastTime = 0;\n  state.seedTimer = 0;\n  state.time = 0;\n}\n\nfunction setOverlayForPhase() {\n  if (state.phase === 'ready') {\n    overlay.classList.add('visible');\n    overlayTitle.textContent = 'Moon Garden';\n    overlayText.textContent = 'Collect 3 floating seeds and carry them back to the glowing greenhouse. Avoid 2 moon moths. Three collisions lose.';\n    overlayBig.textContent = '';\n  } else if (state.phase === 'won') {\n    overlay.classList.add('visible');\n    overlayTitle.textContent = 'Moon Garden';\n    overlayText.textContent = 'The greenhouse glows. The little astronaut made it home.';\n    overlayBig.textContent = 'Won';\n  } else if (state.phase === 'lost') {\n    overlay.classList.add('visible');\n    overlayTitle.textContent = 'Moon Garden';\n    overlayText.textContent = 'The moths found you. The garden will miss one more heartbeat.';\n    overlayBig.textContent = 'Lost';\n  } else {\n    overlay.classList.remove('visible');\n  }\n}\n\nfunction startGame() {\n  reset();\n  state.phase = 'playing';\n  setOverlayForPhase();\n}\n\nfunction restartGame() {\n  reset();\n  setOverlayForPhase();\n}\n\nfunction lose() {\n  state.phase = 'lost';\n  setOverlayForPhase();\n}\n\nfunction win() {\n  state.phase = 'won';\n  setOverlayForPhase();\n}\n\nfunction update(dt) {\n  if (state.phase !== 'playing') return;\n  let ix = (state.keys['arrowleft'] || state.keys['a'] ? -1 : 0) + (state.keys['arrowright'] || state.keys['d'] ? 1 : 0);\n  let iy = (state.keys['arrowup'] || state.keys['w'] ? -1 : 0) + (state.keys['arrowdown'] || state.keys['s'] ? 1 : 0);\n  if (state.touch.up) iy -= 1;\n  if (state.touch.down) iy += 1;\n  if (state.touch.left) ix -= 1;\n  if (state.touch.right) ix += 1;\n  let len = Math.sqrt(ix * ix + iy * iy);\n  if (len > 1) { ix /= len; iy /= len; len = 1; }\n  const speed = 150;\n  state.player.x += ix * speed * dt;\n  state.player.y += iy * speed * dt;\n  state.player.x = Math.max(20, Math.min(W - 20, state.player.x));\n  state.player.y = Math.max(20, Math.min(H - 20, state.player.y));\n\n  state.seedTimer -= dt;\n  if (state.seedTimer <= 0) {\n    state.seedTimer = 3;\n    const a = Math.random() * Math.PI * 2;\n    const r = 90 + Math.random() * 60;\n    state.seeds.push({ x: 240 + Math.cos(a) * r, y: 240 + Math.sin(a) * r, taken: false });\n  }\n\n  for (const s of state.seeds) {\n    if (s.taken) continue;\n    const d = Math.hypot(state.player.x - s.x, state.player.y - s.y);\n    if (d < 42) {\n      s.taken = true;\n      state.inventory++;\n      state.score += 100;\n    }\n  }\n\n  const p = state.player;\n  for (const m of state.moths) {\n    m.phase += dt * 0.8;\n    m.x += m.vx * dt + Math.sin(m.phase) * 8 * dt;\n    m.y += m.vy * dt + Math.cos(m.phase * 0.7) * 6 * dt;\n    m.x = Math.max(30, Math.min(W - 30, m.x));\n    m.y = Math.max(30, Math.min(H - 30, m.y));\n    if (Math.hypot(p.x - m.x, p.y - m.y) < 26) {\n      state.hearts--;\n      if (state.hearts <= 0) { lose(); return; }\n    }\n  }\n\n  if (state.inventory >= 3 && Math.hypot(p.x - GREENHOUSE.x, p.y - GREENHOUSE.y) < 34) {\n    win();\n  }\n}\n\nfunction drawHeart(x, y, s, color) {\n  ctx.save();\n  ctx.translate(x, y);\n  ctx.scale(s, s);\n  ctx.beginPath();\n  ctx.moveTo(0, 4);\n  ctx.bezierCurveTo(-8, -2, -4, -8, 0, -3);\n  ctx.bezierCurveTo(4, -8, 8, -2, 0, 4);\n  ctx.fillStyle = color;\n  ctx.fill();\n  ctx.restore();\n}\n\nfunction draw() {\n  ctx.clearRect(0, 0, W, H);\n  const g = ctx.createRadialGradient(240, 210, 40, 240, 240, 250);\n  g.addColorStop(0, '#fffdf6');\n  g.addColorStop(1, '#f3ead6');\n  ctx.fillStyle = g;\n  ctx.fillRect(0, 0, W, H);\n\n  for (let i = 0; i < 24; i++) {\n    const x = (i * 97) % W;\n    const y = (i * 173) % H;\n    const r = (i * 7) % 2 + 1;\n    ctx.fillStyle = i % 3 ? '#d9c9a3' : '#e9c46a';\n    ctx.beginPath();\n    ctx.arc(x, y, r, 0, Math.PI * 2);\n    ctx.fill();\n  }\n\n  ctx.beginPath();\n  ctx.arc(MOON.x, MOON.y, MOON.r, 0, Math.PI * 2);\n  ctx.fillStyle = '#f8f3e6';\n  ctx.fill();\n  ctx.lineWidth = 3;\n  ctx.strokeStyle = '#e0cfa6';\n  ctx.stroke();\n\n  ctx.beginPath();\n  ctx.arc(GREENHOUSE.x, GREENHOUSE.y, 34, 0, Math.PI * 2);\n  ctx.fillStyle = '#1f4d33';\n  ctx.fill();\n  ctx.beginPath();\n  ctx.ellipse(GREENHOUSE.x, GREENHOUSE.y - 10, 26, 14, 0, Math.PI, 0);\n  ctx.fillStyle = '#e9c46a';\n  ctx.fill();\n  ctx.beginPath();\n  ctx.arc(GREENHOUSE.x, GREENHOUSE.y - 18, 20, Math.PI, 0);\n  ctx.fillStyle = '#bfe3ff';\n  ctx.fill();\n  ctx.strokeStyle = '#8ecae6';\n  ctx.lineWidth = 3;\n  ctx.stroke();\n\n  for (const s of state.seeds) {\n    const bob = Math.sin(state.time * 2 + s.x) * 5;\n    ctx.save();\n    ctx.translate(s.x, s.y + bob);\n    ctx.rotate(Math.sin(state.time + s.y) * 0.2);\n    ctx.beginPath();\n    ctx.moveTo(0, -9);\n    ctx.quadraticCurveTo(7, -2, 5, 5);\n    ctx.quadraticCurveTo(0, 9, -5, 5);\n    ctx.quadraticCurveTo(-7, -2, 0, -9);\n    ctx.fillStyle = '#e9c46a';\n    ctx.fill();\n    ctx.strokeStyle = '#c99b3f';\n    ctx.lineWidth = 2;\n    ctx.stroke();\n    ctx.restore();\n  }\n\n  ctx.save();\n  ctx.translate(state.player.x, state.player.y);\n  const ang = Math.atan2(\n    state.keys['arrowdown'] || state.keys['s'] ? 1 : state.keys['arrowup'] || state.keys['w'] ? -1 : 0,\n    state.keys['arrowright'] || state.keys['d'] ? 1 : state.keys['arrowleft'] || state.keys['a'] ? -1 : 0\n  );\n  ctx.rotate(ang);\n  ctx.beginPath();\n  ctx.arc(0, 0, PLAYER_R, 0, Math.PI * 2);\n  ctx.fillStyle = '#f4f1e6';\n  ctx.fill();\n  ctx.lineWidth = 3;\n  ctx.strokeStyle = '#2f5d3a';\n  ctx.stroke();\n  ctx.beginPath();\n  ctx.arc(0, -3, 7, Math.PI * 0.1, Math.PI * 0.9);\n  ctx.fillStyle = '#bfe3ff';\n  ctx.fill();\n  ctx.strokeStyle = '#2f5d3a';\n  ctx.lineWidth = 2;\n  ctx.stroke();\n  ctx.fillStyle = '#2f5d3a';\n  ctx.fillRect(-5, 7, 10, 6);\n  ctx.restore();\n\n  for (const m of state.moths) {\n    ctx.save();\n    ctx.translate(m.x, m.y);\n    ctx.rotate(Math.atan2(m.vy, m.vx));\n    ctx.beginPath();\n    ctx.ellipse(0, -8, 12, 7, -0.2, 0, Math.PI * 2);\n    ctx.ellipse(0, 8, 12, 7, 0.2, 0, Math.PI * 2);\n    ctx.fillStyle = '#2b2b3a';\n    ctx.fill();\n    ctx.beginPath();\n    ctx.arc(0, 0, 6, 0, Math.PI * 2);\n    ctx.fillStyle = '#23232f';\n    ctx.fill();\n    ctx.restore();\n  }\n\n  ctx.fillStyle = '#2f5d3a';\n  for (let i = 0; i < 3; i++) {\n    drawHeart(40 + i * 34, 42, 1, i < state.hearts ? '#e4572e' : '#d8d2c2');\n  }\n  ctx.fillStyle = '#8a6d2f';\n  ctx.font = 'bold 20px ui-rounded, system-ui, sans-serif';\n  ctx.textAlign = 'center';\n  ctx.fillText('Seeds: ' + state.inventory + '/3', W / 2, 68);\n\n  ctx.fillStyle = '#5a3b12';\n  ctx.font = 'bold 16px ui-rounded, system-ui, sans-serif';\n  ctx.fillText('Score: ' + state.score, W / 2, H - 18);\n}\n\nlet rafId = 0;\nfunction loop(t) {\n  const dt = Math.min(0.033, Math.max(0.0001, (t - (state.lastTime || t)) / 1000));\n  state.lastTime = t;\n  state.time += dt;\n  update(dt);\n  draw();\n  rafId = requestAnimationFrame(loop);\n}\n\nfunction keyHandler(e) {\n  const k = e.key.toLowerCase();\n  if (k === ' ') { startGame(); return; }\n  if (k === 'r') { restartGame(); return; }\n  if (['arrowup','arrowdown','arrowleft','arrowright',' '].includes(k) || ['w','a','s','d','arrowup','arrowdown','arrowleft','arrowright'].includes(k)) {\n    state.keys[k] = true;\n  }\n}\n\nfunction touchHandler(type, key) {\n  return function(e) {\n    e.preventDefault();\n    if (type === 'start') {\n      state.touch[key] = true;\n      state.keys[key] = true;\n    } else {\n      state.touch[key] = false;\n      if (key === 'arrowup') state.keys['arrowup'] = false;\n      if (key === 'arrowdown') state.keys['arrowdown'] = false;\n      if (key === 'arrowleft') state.keys['arrowleft'] = false;\n      if (key === 'arrowright') state.keys['arrowright'] = false;\n      if (key === 'w') state.keys['w'] = false;\n      if (key === 'a') state.keys['a'] = false;\n      if (key === 's') state.keys['s'] = false;\n      if (key === 'd') state.keys['d'] = false;\n    }\n  };\n}\n\nstartBtn.addEventListener('pointerdown', function(e) { e.preventDefault(); startGame(); });\nrestartBtn.addEventListener('pointerdown', function(e) { e.preventDefault(); restartGame(); });\nupBtn.addEventListener('pointerdown', touchHandler('start', 'up'));\nupBtn.addEventListener('pointerup', touchHandler('end', 'up'));\nupBtn.addEventListener('pointercancel', touchHandler('end', 'up'));\ndownBtn.addEventListener('pointerdown', touchHandler('start', 'down'));\ndownBtn.addEventListener('pointerup', touchHandler('end', 'down'));\nleftBtn.addEventListener('pointerdown', touchHandler('start', 'left'));\nleftBtn.addEventListener('pointerup', touchHandler('end', 'left'));\nrightBtn.addEventListener('pointerdown', touchHandler('start', 'right'));\nrightBtn.addEventListener('pointerup', touchHandler('end', 'right'));\n\ncanvas.addEventListener('pointerdown', function(e) { e.preventDefault(); if (state.phase === 'ready') startGame(); });\nwindow.addEventListener('keydown', keyHandler);\nwindow.addEventListener('keyup', function(e) { const k = e.key.toLowerCase(); if (state.keys[k] !== undefined) state.keys[k] = false; });\n\nfunction refreshTouch() {\n  const active = state.phase === 'playing';\n  touchControls.classList.toggle('active', active);\n  upBtn.classList.toggle('active', !!state.touch.up);\n  downBtn.classList.toggle('active', !!state.touch.down);\n  leftBtn.classList.toggle('active', !!state.touch.left);\n  rightBtn.classList.toggle('active', !!state.touch.right);\n}\nsetInterval(refreshTouch, 100);\n\nreset();\nsetOverlayForPhase();\nrequestAnimationFrame(loop);\n\nwindow.arcade = {\n  restart: function() { restartGame(); },\n  snapshot: function() {\n    return {\n      phase: state.phase,\n      score: state.score,\n      player: { x: state.player.x, y: state.player.y }\n    };\n  }\n};\n</script>\n</body>\n</html>",
      "before_record": "arcade-5d611ce7feef444c855c-1",
      "after_record": "arcade-5d611ce7feef444c855c-2",
      "before_sha256": "118d5fa2c12580a1aa89d71da08f0137164349ccb414f57cefa8bb67ccf09529",
      "after_sha256": "3a70df371c3bfc0ec504e021ffe8af77406453ae8d4522b2be8a94da6479d6a5",
      "errors": [
        "Game startup JavaScript errors: Failed to execute 'arc' on 'CanvasRenderingContext2D': 5 arguments required, but only 4 present."
      ],
      "historical_checks": {
        "accepted": true,
        "checks": [
          {
            "blocked_requests": 0,
            "canvas_after_input": null,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": null,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"phase\": \"playing\", \"player\": {\"x\": 279.91499999999996, \"y\": 107.60000000000001}, \"score\": 0}, \"initial\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"reset\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"restarted\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}}",
            "start_button": null,
            "viewport": "desktop"
          },
          {
            "blocked_requests": 0,
            "canvas_after_input": null,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": null,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"phase\": \"playing\", \"player\": {\"x\": 244.95, \"y\": 140}, \"score\": 100}, \"initial\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"reset\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"restarted\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}}",
            "start_button": null,
            "viewport": "mobile"
          }
        ],
        "errors": [],
        "scope": "Static policy, canvas rendering, snapshot contract, restart and brief desktop/mobile input smoke tests. Full gameplay and game balance are not measured.",
        "status": "smoke_check_passed"
      },
      "release_checks": {
        "accepted": true,
        "checks": [
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"phase\": \"playing\", \"player\": {\"x\": 277.51499999999993, \"y\": 99.98000000000003}, \"score\": 0}, \"after_start\": {\"phase\": \"playing\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"initial\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"reset\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"restarted\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}}",
            "start_button": true,
            "viewport": "desktop"
          },
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"phase\": \"playing\", \"player\": {\"x\": 237.52500000000003, \"y\": 142.50500000000005}, \"score\": 100}, \"after_start\": {\"phase\": \"playing\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"initial\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"reset\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}, \"restarted\": {\"phase\": \"ready\", \"player\": {\"x\": 240, \"y\": 140}, \"score\": 0}}",
            "start_button": true,
            "viewport": "mobile"
          }
        ],
        "errors": [],
        "scope": "Static policy, visible Start/Restart controls, canvas rendering, finite snapshot contract, restart and brief desktop/mobile input smoke tests. Full gameplay and game balance are not measured.",
        "status": "smoke_check_passed"
      },
      "diff": "--- failed.html\n\n+++ recorded-repair.html\n\n@@ -237,7 +237,7 @@\n\n   ctx.stroke();\n \n   ctx.beginPath();\n-  ctx.arc(GREENHOUSE.x, GREENHOUSE.y, 34, Math.PI * 2);\n+  ctx.arc(GREENHOUSE.x, GREENHOUSE.y, 34, 0, Math.PI * 2);\n   ctx.fillStyle = '#1f4d33';\n   ctx.fill();\n   ctx.beginPath();\n@@ -328,6 +328,7 @@\n\n let rafId = 0;\n function loop(t) {\n   const dt = Math.min(0.033, Math.max(0.0001, (t - (state.lastTime || t)) / 1000));\n+  state.lastTime = t;\n   state.time += dt;\n   update(dt);\n   draw();\n@@ -336,10 +337,11 @@\n\n \n function keyHandler(e) {\n   const k = e.key.toLowerCase();\n+  if (k === ' ') { startGame(); return; }\n+  if (k === 'r') { restartGame(); return; }\n   if (['arrowup','arrowdown','arrowleft','arrowright',' '].includes(k) || ['w','a','s','d','arrowup','arrowdown','arrowleft','arrowright'].includes(k)) {\n     state.keys[k] = true;\n   }\n-  if (k === 'r') restartGame();\n }\n \n function touchHandler(type, key) {",
      "deep_review": {
        "errors": [],
        "keyboard_final": {
          "phase": "won",
          "player": {
            "x": 272.49000000000075,
            "y": 359.99000000000007
          },
          "score": 400
        },
        "mobile_touch_hold": {
          "phase": "playing",
          "player": {
            "x": 295.005,
            "y": 140
          },
          "score": 0
        },
        "scope": "Real keyboard and touch input only; snapshot reads, no state writes"
      },
      "game_url": "https://alphabell.com/arcade/g/moon-garden"
    },
    {
      "id": "bee-bop",
      "title": "Bee Bop",
      "label": "One extra parenthesis",
      "line": 125,
      "lesson": "The fillText call closes before its x and y coordinates. Move the closing parenthesis after both coordinates.",
      "note": "An earlier attempt in this trajectory also lacked the required game interface. This case starts from the next complete candidate.",
      "prompt": "Bee Bop: a cheerful arcade game controlling a round bee in a little garden. Move with arrows/WASD or touch direction buttons, collect six pollen dots and carry them to the hive. Avoid two slowly patrolling beetles; three hits lose. Flowers pulse gently and every pollen pickup gives a clear satisfying visual change. Use an original sunny yellow and coral Canvas illustration style, visible controls, score, hearts, Start and Restart. Make the first pickup close enough to reach quickly.",
      "before": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"description\" content=\"A cheerful bee gathers pollen and delivers it to the hive while dodging beetles.\">\n<title>Bee Bop</title>\n<style>\n*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:#0e2a1e;color:#f2f9f0;font:15px system-ui,sans-serif}\nbody{display:flex;justify-content:center;align-items:flex-start;padding:12px}\nmain{width:min(960px,calc(100vw - 24px),calc(160dvh - 400px));min-width:280px;display:grid;gap:10px}\nheader{display:flex;justify-content:space-between;align-items:center;gap:12px}h1{font-size:20px;margin:0;color:#ffe27a}\n#hud{font-size:14px;text-align:right}canvas{display:block;width:100%;height:auto;aspect-ratio:8/5;border:1px solid #2e5c43;border-radius:18px;background:#123524;touch-action:none}\n.toolbar{display:flex;justify-content:center;gap:10px}button{border:0;border-radius:12px;background:#ffe27a;color:#1d3a24;padding:10px 18px;font:700 15px system-ui;cursor:pointer;touch-action:none;user-select:none}\nbutton:focus-visible{outline:3px solid #fff;outline-offset:3px}button:disabled{opacity:.6;cursor:default}\np{margin:0;text-align:center;font-size:13px;line-height:1.4;color:#b8d8c4}\n.pad{display:grid;grid-template-columns:repeat(3,52px);grid-template-rows:repeat(2,48px);gap:6px;justify-content:center}\n.pad button{padding:0;background:#2e6b45;color:#fff;font-size:22px}.pad button:active,.pad button.held{background:#3f8a58}\n[data-dir=\"up\"]{grid-column:2;grid-row:1}[data-dir=\"left\"]{grid-column:1;grid-row:2}[data-dir=\"down\"]{grid-column:2;grid-row:2}[data-dir=\"right\"]{grid-column:3;grid-row:2}\n@media(max-width:480px){h1{font-size:17px}#hud{font-size:12px}main{gap:8px}}\n</style>\n</head>\n<body>\n<main>\n<header><h1>Bee Bop</h1><div id=\"hud\" aria-live=\"polite\"></div></header>\n<canvas id=\"board\" width=\"960\" height=\"600\" aria-label=\"Bee Bop game board\"></canvas>\n<div class=\"toolbar\"><button id=\"start\">Start</button><button id=\"restart\">Restart</button></div>\n<p id=\"instructions\">Guide the round bee through the sunny garden. Collect all six pollen dots and carry them to the hive. Avoid the two slow beetles, three hits lose. Arrows or WASD move; touch pad below.</p>\n<div class=\"pad\" aria-label=\"Touch movement controls\">\n<button data-dir=\"up\" aria-label=\"Move up\">▲</button><button data-dir=\"left\" aria-label=\"Move left\">◀</button>\n<button data-dir=\"down\" aria-label=\"Move down\">▼</button><button data-dir=\"right\" aria-label=\"Move right\">▶</button>\n</div>\n</main>\n<script>\n'use strict';\nconst canvas=document.querySelector('#board'), ctx=canvas.getContext('2d');\nconst hud=document.querySelector('#hud'), startButton=document.querySelector('#start');\nconst W=960,H=600,SPEED=250,HOME={x:180,y:470},POLLEN=6;\nconst seedPositions=[{x:180,y:140},{x:420,y:90},{x:660,y:150},{x:830,y:100},{x:300,y:330},{x:720,y:360}];\nconst enemyBases=[{x:470,y:290},{x:500,y:150}];\nconst keyboard={up:false,down:false,left:false,right:false};\nconst pointerDirections=new Map();\nlet state,lastTime=null;\nfunction clearInputs(){\n  for(const key of Object.keys(keyboard))keyboard[key]=false;\n  pointerDirections.clear();\n  document.querySelectorAll('.pad button').forEach(button=>button.classList.remove('held'));\n}\nfunction reset(){\n  clearInputs();\n  state={phase:'ready',score:0,lives:3,bag:0,time:0,invulnerableUntil:0,\n    player:{x:100,y:300,r:13,vx:0,vy:0},\n    seeds:seedPositions.map(point=>({...point,taken:false})),\n    enemies:enemyBases.map((point,index)=>({...point,r:21,phase:index*2}))};\n  lastTime=null;\n  updateHUD();\n}\nfunction start(){if(state.phase!=='playing'){reset();state.phase='playing';updateHUD();}}\nfunction updateHUD(){\n  hud.textContent=`Pollen ${state.bag}/${POLLEN} · Hearts ${state.lives} · Score ${state.score}`;\n  startButton.textContent=state.phase==='won'||state.phase==='lost'?'Play again':'Start';\n  startButton.disabled=state.phase==='playing';\n}\nfunction update(dt){\n  if(state.phase!=='playing')return;\n  state.time+=dt;\n  const held=new Set(pointerDirections.values());\n  let dx=Number(keyboard.right||held.has('right'))-Number(keyboard.left||held.has('left'));\n  let dy=Number(keyboard.down||held.has('down'))-Number(keyboard.up||held.has('up'));\n  const length=Math.hypot(dx,dy)||1;\n  const player=state.player;\n  player.vx=dx/length*SPEED;player.vy=dy/length*SPEED;\n  player.x=Math.max(20,Math.min(W-20,player.x+player.vx*dt));\n  player.y=Math.max(20,Math.min(H-20,player.y+player.vy*dt));\n  state.seeds.forEach(seed=>{\n    if(!seed.taken&&Math.hypot(player.x-seed.x,player.y-seed.y)<34){seed.taken=true;state.bag++;state.score+=100;}\n  });\n  state.enemies.forEach((enemy,index)=>{\n    const base=enemyBases[index];\n    enemy.x=base.x+Math.sin(state.time*.8+enemy.phase)*62;\n    enemy.y=base.y+Math.cos(state.time*.6+enemy.phase)*34;\n    if(state.time>=state.invulnerableUntil&&Math.hypot(player.x-enemy.x,player.y-enemy.y)<player.r+enemy.r){\n      state.lives--;state.invulnerableUntil=state.time+1;\n      if(state.lives<=0)state.phase='lost';\n    }\n  });\n  if(state.phase==='playing'&&state.bag===POLLEN&&Math.hypot(player.x-HOME.x,player.y-HOME.y)<38){state.phase='won';state.score+=250;}\n  if(state.phase!=='playing')clearInputs();\n  updateHUD();\n}\nfunction circle(x,y,r,color){ctx.fillStyle=color;ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.fill();}\nfunction draw(){\n  const background=ctx.createLinearGradient(0,0,W,H);background.addColorStop(0,'#f6c445');background.addColorStop(1,'#f7975e');\n  ctx.fillStyle=background;ctx.fillRect(0,0,W,H);\n  ctx.fillStyle='#fdf6d8';ctx.beginPath();ctx.arc(W-60,80,42,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#f2b8d8';ctx.beginPath();ctx.arc(90,120,34,0,Math.PI*2);ctx.fill();\n  for(let index=0;index<40;index++)circle((index*197+60)%W,(index*131+40)%H,1+index%2,'rgba(255,255,255,0.5)');\n  ctx.strokeStyle='rgba(210,120,50,0.35)';ctx.lineWidth=2;\n  for(let x=40;x<W;x+=80){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke();}\n  for(let y=40;y<H;y+=80){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke();}\n  circle(HOME.x,HOME.y,47,'#c96f4a');circle(HOME.x,HOME.y,36,'#7aa75a');\n  ctx.fillStyle='#d8edb5';ctx.fillRect(HOME.x-18,HOME.y-12,36,30);\n  ctx.fillStyle='#ffd881';ctx.beginPath();ctx.moveTo(HOME.x-27,HOME.y-12);ctx.lineTo(HOME.x,HOME.y-36);ctx.lineTo(HOME.x+27,HOME.y-12);ctx.fill();\n  ctx.fillStyle='#3e6b32';ctx.fillRect(HOME.x-5,HOME.y+3,10,15);\n  state.seeds.forEach(seed=>{\n    if(seed.taken)return;\n    circle(seed.x,seed.y,26,'#8a5a2b');circle(seed.x,seed.y,16,'#ffb84d');\n    circle(seed.x-4,seed.y-5,8,'#fff0a2');\n  });\n  state.enemies.forEach(enemy=>{\n    circle(enemy.x+3,enemy.y+6,enemy.r,'#4a2c20');circle(enemy.x,enemy.y,enemy.r,'#d95d5d');\n    circle(enemy.x-7,enemy.y-3,4,'#fce7df');circle(enemy.x+7,enemy.y-3,4,'#fce7df');\n    ctx.fillStyle='#8a3a3a';ctx.fillRect(enemy.x-6,enemy.y+7,12,3);\n  });\n  const player=state.player;\n  if(state.time<state.invulnerableUntil)circle(player.x,player.y,27,'rgba(255,255,255,0.55)');\n  circle(player.x+2,player.y+6,17,'#4a3512');circle(player.x,player.y,16,'#fff3d6');\n  circle(player.x,player.y-3,10,'#c9e4ff');circle(player.x-3,player.y-5,3,'#d9ffff');\n  if(state.bag>0)circle(player.x+17,player.y+8,7,'#c9a227');\n  if(state.phase!=='playing'){\n    ctx.fillStyle='#123524cc';ctx.fillRect(180,210,600,180);\n    ctx.textAlign='center';ctx.fillStyle='#ffe27a';ctx.font='700 38px system-ui';\n    ctx.fillText(state.phase==='won'?'Delivery complete!':state.phase==='lost'?'The beetles got you':'Bee Bop',W/2,270);\n    ctx.fillStyle='#e8f5ec';ctx.font='21px system-ui';\n    ctx.fillText(state.phase==='ready'?'Six pollen dots. One sweet delivery.':'Press Start to play again, or R to restart.'),W/2,315);\n    ctx.font='17px system-ui';ctx.fillText('Use the arrows, WASD or the touch pad below.',W/2,350);ctx.textAlign='left';\n  }\n}\nfunction loop(timestamp){\n  const dt=lastTime===null?0:Math.min((timestamp-lastTime)/1000,.04);\n  lastTime=timestamp;update(dt);draw();requestAnimationFrame(loop);\n}\nconst keyDirections={ArrowUp:'up',ArrowDown:'down',ArrowLeft:'left',ArrowRight:'right',w:'up',s:'down',a:'left',d:'right'};\nwindow.addEventListener('keydown',event=>{\n  const key=event.key.length===1?event.key.toLowerCase():event.key;\n  if(keyDirections[key]){event.preventDefault();keyboard[keyDirections[key]]=true;}\n  if(event.code==='Space'){event.preventDefault();if(state.phase!=='playing'&&!event.repeat)start();}\n  if(key==='r'&&!event.repeat)reset();\n});\nwindow.addEventListener('keyup',event=>{\n  const key=event.key.length===1?event.key.toLowerCase():event.key;\n  if(keyDirections[key]){event.preventDefault();keyboard[keyDirections[key]]=false;}\n});\ndocument.querySelectorAll('[data-dir]').forEach(button=>{\n  button.addEventListener('pointerdown',event=>{event.preventDefault();pointerDirections.set(event.pointerId,button.dataset.dir);button.classList.add('held');button.setPointerCapture(event.pointerId);});\n  const release=event=>{pointerDirections.delete(event.pointerId);button.classList.remove('held');};\n  button.addEventListener('pointerup',release);button.addEventListener('pointercancel',release);button.addEventListener('lostpointercapture',release);\n});\nwindow.addEventListener('blur',clearInputs);\ndocument.addEventListener('visibilitychange',()=>{if(document.hidden)clearInputs();});\nstartButton.addEventListener('click',start);\ndocument.querySelector('#restart').addEventListener('click',reset);\ncanvas.addEventListener('pointerdown',()=>{if(state.phase==='ready')start();});\nwindow.arcade={restart:()=>reset(),snapshot:()=>({phase:state.phase,score:state.score,player:{x:state.player.x,y:state.player.y}})};\nreset();requestAnimationFrame(loop);\n</script>\n</body>\n</html>",
      "after": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"description\" content=\"A cheerful bee gathers pollen and delivers it to the hive while dodging beetles.\">\n<title>Bee Bop</title>\n<style>\n*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:#0e2a1e;color:#f2f9f0;font:15px system-ui,sans-serif}\nbody{display:flex;justify-content:center;align-items:flex-start;padding:12px}\nmain{width:min(960px,calc(100vw - 24px),calc(160dvh - 400px));min-width:280px;display:grid;gap:10px}\nheader{display:flex;justify-content:space-between;align-items:center;gap:12px}h1{font-size:20px;margin:0;color:#ffe27a}\n#hud{font-size:14px;text-align:right}canvas{display:block;width:100%;height:auto;aspect-ratio:8/5;border:1px solid #2e5c43;border-radius:18px;background:#123524;touch-action:none}\n.toolbar{display:flex;justify-content:center;gap:10px}button{border:0;border-radius:12px;background:#ffe27a;color:#1d3a24;padding:10px 18px;font:700 15px system-ui;cursor:pointer;touch-action:none;user-select:none}\nbutton:focus-visible{outline:3px solid #fff;outline-offset:3px}button:disabled{opacity:.6;cursor:default}\np{margin:0;text-align:center;font-size:13px;line-height:1.4;color:#b8d8c4}\n.pad{display:grid;grid-template-columns:repeat(3,52px);grid-template-rows:repeat(2,48px);gap:6px;justify-content:center}\n.pad button{padding:0;background:#2e6b45;color:#fff;font-size:22px}.pad button:active,.pad button.held{background:#3f8a58}\n[data-dir=\"up\"]{grid-column:2;grid-row:1}[data-dir=\"left\"]{grid-column:1;grid-row:2}[data-dir=\"down\"]{grid-column:2;grid-row:2}[data-dir=\"right\"]{grid-column:3;grid-row:2}\n@media(max-width:480px){h1{font-size:17px}#hud{font-size:12px}main{gap:8px}}\n</style>\n</head>\n<body>\n<main>\n<header><h1>Bee Bop</h1><div id=\"hud\" aria-live=\"polite\"></div></header>\n<canvas id=\"board\" width=\"960\" height=\"600\" aria-label=\"Bee Bop game board\"></canvas>\n<div class=\"toolbar\"><button id=\"start\">Start</button><button id=\"restart\">Restart</button></div>\n<p id=\"instructions\">Guide the round bee through the sunny garden. Collect all six pollen dots and carry them to the hive. Avoid the two slow beetles, three hits lose. Arrows or WASD move; touch pad below.</p>\n<div class=\"pad\" aria-label=\"Touch movement controls\">\n<button data-dir=\"up\" aria-label=\"Move up\">▲</button><button data-dir=\"left\" aria-label=\"Move left\">◀</button>\n<button data-dir=\"down\" aria-label=\"Move down\">▼</button><button data-dir=\"right\" aria-label=\"Move right\">▶</button>\n</div>\n</main>\n<script>\n'use strict';\nconst canvas=document.querySelector('#board'), ctx=canvas.getContext('2d');\nconst hud=document.querySelector('#hud'), startButton=document.querySelector('#start');\nconst W=960,H=600,SPEED=250,HOME={x:180,y:470},POLLEN=6;\nconst seedPositions=[{x:180,y:140},{x:420,y:90},{x:660,y:150},{x:830,y:100},{x:300,y:330},{x:720,y:360}];\nconst enemyBases=[{x:470,y:290},{x:500,y:150}];\nconst keyboard={up:false,down:false,left:false,right:false};\nconst pointerDirections=new Map();\nlet state,lastTime=null;\nfunction clearInputs(){\n  for(const key of Object.keys(keyboard))keyboard[key]=false;\n  pointerDirections.clear();\n  document.querySelectorAll('.pad button').forEach(button=>button.classList.remove('held'));\n}\nfunction reset(){\n  clearInputs();\n  state={phase:'ready',score:0,lives:3,bag:0,time:0,invulnerableUntil:0,\n    player:{x:100,y:300,r:13,vx:0,vy:0},\n    seeds:seedPositions.map(point=>({...point,taken:false})),\n    enemies:enemyBases.map((point,index)=>({...point,r:21,phase:index*2}))};\n  lastTime=null;\n  updateHUD();\n}\nfunction start(){if(state.phase!=='playing'){reset();state.phase='playing';updateHUD();}}\nfunction updateHUD(){\n  hud.textContent=`Pollen ${state.bag}/${POLLEN} · Hearts ${state.lives} · Score ${state.score}`;\n  startButton.textContent=state.phase==='won'||state.phase==='lost'?'Play again':'Start';\n  startButton.disabled=state.phase==='playing';\n}\nfunction update(dt){\n  if(state.phase!=='playing')return;\n  state.time+=dt;\n  const held=new Set(pointerDirections.values());\n  let dx=Number(keyboard.right||held.has('right'))-Number(keyboard.left||held.has('left'));\n  let dy=Number(keyboard.down||held.has('down'))-Number(keyboard.up||held.has('up'));\n  const length=Math.hypot(dx,dy)||1;\n  const player=state.player;\n  player.vx=dx/length*SPEED;player.vy=dy/length*SPEED;\n  player.x=Math.max(20,Math.min(W-20,player.x+player.vx*dt));\n  player.y=Math.max(20,Math.min(H-20,player.y+player.vy*dt));\n  state.seeds.forEach(seed=>{\n    if(!seed.taken&&Math.hypot(player.x-seed.x,player.y-seed.y)<34){seed.taken=true;state.bag++;state.score+=100;}\n  });\n  state.enemies.forEach((enemy,index)=>{\n    const base=enemyBases[index];\n    enemy.x=base.x+Math.sin(state.time*.8+enemy.phase)*62;\n    enemy.y=base.y+Math.cos(state.time*.6+enemy.phase)*34;\n    if(state.time>=state.invulnerableUntil&&Math.hypot(player.x-enemy.x,player.y-enemy.y)<player.r+enemy.r){\n      state.lives--;state.invulnerableUntil=state.time+1;\n      if(state.lives<=0)state.phase='lost';\n    }\n  });\n  if(state.phase==='playing'&&state.bag===POLLEN&&Math.hypot(player.x-HOME.x,player.y-HOME.y)<38){state.phase='won';state.score+=250;}\n  if(state.phase!=='playing')clearInputs();\n  updateHUD();\n}\nfunction circle(x,y,r,color){ctx.fillStyle=color;ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.fill();}\nfunction draw(){\n  const background=ctx.createLinearGradient(0,0,W,H);background.addColorStop(0,'#f6c445');background.addColorStop(1,'#f7975e');\n  ctx.fillStyle=background;ctx.fillRect(0,0,W,H);\n  ctx.fillStyle='#fdf6d8';ctx.beginPath();ctx.arc(W-60,80,42,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#f2b8d8';ctx.beginPath();ctx.arc(90,120,34,0,Math.PI*2);ctx.fill();\n  for(let index=0;index<40;index++)circle((index*197+60)%W,(index*131+40)%H,1+index%2,'rgba(255,255,255,0.5)');\n  ctx.strokeStyle='rgba(210,120,50,0.35)';ctx.lineWidth=2;\n  for(let x=40;x<W;x+=80){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke();}\n  for(let y=40;y<H;y+=80){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke();}\n  circle(HOME.x,HOME.y,47,'#c96f4a');circle(HOME.x,HOME.y,36,'#7aa75a');\n  ctx.fillStyle='#d8edb5';ctx.fillRect(HOME.x-18,HOME.y-12,36,30);\n  ctx.fillStyle='#ffd881';ctx.beginPath();ctx.moveTo(HOME.x-27,HOME.y-12);ctx.lineTo(HOME.x,HOME.y-36);ctx.lineTo(HOME.x+27,HOME.y-12);ctx.fill();\n  ctx.fillStyle='#3e6b32';ctx.fillRect(HOME.x-5,HOME.y+3,10,15);\n  state.seeds.forEach(seed=>{\n    if(seed.taken)return;\n    circle(seed.x,seed.y,26,'#8a5a2b');circle(seed.x,seed.y,16,'#ffb84d');\n    circle(seed.x-4,seed.y-5,8,'#fff0a2');\n  });\n  state.enemies.forEach(enemy=>{\n    circle(enemy.x+3,enemy.y+6,enemy.r,'#4a2c20');circle(enemy.x,enemy.y,enemy.r,'#d95d5d');\n    circle(enemy.x-7,enemy.y-3,4,'#fce7df');circle(enemy.x+7,enemy.y-3,4,'#fce7df');\n    ctx.fillStyle='#8a3a3a';ctx.fillRect(enemy.x-6,enemy.y+7,12,3);\n  });\n  const player=state.player;\n  if(state.time<state.invulnerableUntil)circle(player.x,player.y,27,'rgba(255,255,255,0.55)');\n  circle(player.x+2,player.y+6,17,'#4a3512');circle(player.x,player.y,16,'#fff3d6');\n  circle(player.x,player.y-3,10,'#c9e4ff');circle(player.x-3,player.y-5,3,'#d9ffff');\n  if(state.bag>0)circle(player.x+17,player.y+8,7,'#c9a227');\n  if(state.phase!=='playing'){\n    ctx.fillStyle='#123524cc';ctx.fillRect(180,210,600,180);\n    ctx.textAlign='center';ctx.fillStyle='#ffe27a';ctx.font='700 38px system-ui';\n    ctx.fillText(state.phase==='won'?'Delivery complete!':state.phase==='lost'?'The beetles got you':'Bee Bop',W/2,270);\n    ctx.fillStyle='#e8f5ec';ctx.font='21px system-ui';\n    ctx.fillText(state.phase==='ready'?'Six pollen dots. One sweet delivery.':'Press Start to play again, or R to restart.',W/2,315);\n    ctx.font='17px system-ui';ctx.fillText('Use the arrows, WASD or the touch pad below.',W/2,350);ctx.textAlign='left';\n  }\n}\nfunction loop(timestamp){\n  const dt=lastTime===null?0:Math.min((timestamp-lastTime)/1000,.04);\n  lastTime=timestamp;update(dt);draw();requestAnimationFrame(loop);\n}\nconst keyDirections={ArrowUp:'up',ArrowDown:'down',ArrowLeft:'left',ArrowRight:'right',w:'up',s:'down',a:'left',d:'right'};\nwindow.addEventListener('keydown',event=>{\n  const key=event.key.length===1?event.key.toLowerCase():event.key;\n  if(keyDirections[key]){event.preventDefault();keyboard[keyDirections[key]]=true;}\n  if(event.code==='Space'){event.preventDefault();if(state.phase!=='playing'&&!event.repeat)start();}\n  if(key==='r'&&!event.repeat)reset();\n});\nwindow.addEventListener('keyup',event=>{\n  const key=event.key.length===1?event.key.toLowerCase():event.key;\n  if(keyDirections[key]){event.preventDefault();keyboard[keyDirections[key]]=false;}\n});\ndocument.querySelectorAll('[data-dir]').forEach(button=>{\n  button.addEventListener('pointerdown',event=>{event.preventDefault();pointerDirections.set(event.pointerId,button.dataset.dir);button.classList.add('held');button.setPointerCapture(event.pointerId);});\n  const release=event=>{pointerDirections.delete(event.pointerId);button.classList.remove('held');};\n  button.addEventListener('pointerup',release);button.addEventListener('pointercancel',release);button.addEventListener('lostpointercapture',release);\n});\nwindow.addEventListener('blur',clearInputs);\ndocument.addEventListener('visibilitychange',()=>{if(document.hidden)clearInputs();});\nstartButton.addEventListener('click',start);\ndocument.querySelector('#restart').addEventListener('click',reset);\ncanvas.addEventListener('pointerdown',()=>{if(state.phase==='ready')start();});\nwindow.arcade={restart:()=>reset(),snapshot:()=>({phase:state.phase,score:state.score,player:{x:state.player.x,y:state.player.y}})};\nreset();requestAnimationFrame(loop);\n</script>\n</body>\n</html>",
      "before_record": "arcade-5c8d05cc6e73db5ab16a-2",
      "after_record": "arcade-5c8d05cc6e73db5ab16a-3",
      "before_sha256": "0e1db87f005d3068e4b0d0cbd1d8dfb54eed1165358d441f76ef7826ca74650e",
      "after_sha256": "118125865f00f9c056f1dc6a8f69b4f6e578fd53ca5c179db75268b1bfb59be6",
      "errors": [
        "Game startup JavaScript errors: Unexpected token ')'"
      ],
      "historical_checks": {
        "accepted": true,
        "checks": [
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"phase\": \"playing\", \"player\": {\"x\": 170.825, \"y\": 233.35000000000002}, \"score\": 0}, \"initial\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"reset\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"restarted\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}}",
            "start_button": true,
            "viewport": "desktop"
          },
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"phase\": \"playing\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"initial\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"reset\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"restarted\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}}",
            "start_button": true,
            "viewport": "mobile"
          }
        ],
        "errors": [],
        "scope": "Static policy, visible Start/Restart controls, canvas rendering, finite snapshot contract, restart and brief desktop/mobile input smoke tests. Full gameplay and game balance are not measured.",
        "status": "smoke_check_passed"
      },
      "release_checks": {
        "accepted": true,
        "checks": [
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"phase\": \"playing\", \"player\": {\"x\": 179.175, \"y\": 233.325}, \"score\": 0}, \"after_start\": {\"phase\": \"playing\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"initial\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"reset\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"restarted\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}}",
            "start_button": true,
            "viewport": "desktop"
          },
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"phase\": \"playing\", \"player\": {\"x\": 99.97500000000002, \"y\": 299.9749999999999}, \"score\": 0}, \"after_start\": {\"phase\": \"playing\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"initial\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"reset\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"restarted\": {\"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}}",
            "start_button": true,
            "viewport": "mobile"
          }
        ],
        "errors": [],
        "scope": "Static policy, visible Start/Restart controls, canvas rendering, finite snapshot contract, restart and brief desktop/mobile input smoke tests. Full gameplay and game balance are not measured.",
        "status": "smoke_check_passed"
      },
      "diff": "--- failed.html\n\n+++ recorded-repair.html\n\n@@ -122,7 +122,7 @@\n\n     ctx.textAlign='center';ctx.fillStyle='#ffe27a';ctx.font='700 38px system-ui';\n     ctx.fillText(state.phase==='won'?'Delivery complete!':state.phase==='lost'?'The beetles got you':'Bee Bop',W/2,270);\n     ctx.fillStyle='#e8f5ec';ctx.font='21px system-ui';\n-    ctx.fillText(state.phase==='ready'?'Six pollen dots. One sweet delivery.':'Press Start to play again, or R to restart.'),W/2,315);\n+    ctx.fillText(state.phase==='ready'?'Six pollen dots. One sweet delivery.':'Press Start to play again, or R to restart.',W/2,315);\n     ctx.font='17px system-ui';ctx.fillText('Use the arrows, WASD or the touch pad below.',W/2,350);ctx.textAlign='left';\n   }\n }",
      "deep_review": {
        "errors": [],
        "mobile_after": {
          "phase": "playing",
          "player": {
            "x": 182.49999999999997,
            "y": 300
          },
          "score": 0
        },
        "restart_after_win": {
          "phase": "ready",
          "player": {
            "x": 100,
            "y": 300
          },
          "score": 0
        },
        "scope": "Framework validation through actual Start, keyboard movement, Restart and native touch events; snapshots read, no game-state writes",
        "win": {
          "phase": "won",
          "player": {
            "x": 179.17499999999995,
            "y": 505.7999999999997
          },
          "score": 850
        },
        "win_hud": "Pollen 6/6 · Hearts 3 · Score 850"
      },
      "game_url": "https://alphabell.com/arcade/g/bee-bop-starter"
    },
    {
      "id": "cloud-courier",
      "title": "Cloud Courier",
      "label": "Canvas is not chainable",
      "line": 114,
      "lesson": "Canvas arc and ellipse return undefined. Call ctx.fill() separately, rather than chaining .fill().",
      "note": "The recorded repair also restores vertical keyboard movement and corrects a letter rotation expression.",
      "prompt": "Keep this Cloud Courier game intact and fix exactly its keyboard vertical movement. In update(dt), dy currently uses only held touch directions. Mirror dx: dy must include both keyboard.down and held.has('down') for positive motion, and keyboard.up and held.has('up') for negative motion. Preserve the working horizontal movement, phone controls, cooldown, letter collection, mailbox delivery, all three lives, finite snapshot API and one loop. Do not rewrite unrelated mechanics. Return complete HTML.",
      "before": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"description\" content=\"A paper airplane courier steers through a calm sky, collecting five letters and delivering them to a floating mailbox.\">\n<title>Cloud Courier</title>\n<style>\n*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:#f2ead9;color:#4a3b2a;font:15px system-ui,sans-serif}\nbody{display:flex;justify-content:center;align-items:flex-start;padding:12px;background:linear-gradient(180deg,#dbe9f5 0%,#f2ead9 100%)}\nmain{width:min(960px,calc(100vw - 24px),calc(160dvh - 400px));min-width:280px;display:grid;gap:10px}\nheader{display:flex;justify-content:space-between;align-items:center;gap:12px}h1{font-size:20px;margin:0;color:#e0703a}\n#hud{font-size:14px;text-align:right;color:#7c4a2b}\ncanvas{display:block;width:100%;height:auto;aspect-ratio:8/5;border:2px solid #c9b391;border-radius:18px;background:linear-gradient(180deg,#a8d4e8 0%,#d9ecf5 60%,#cfe8c8 61%,#c8d9a8 100%);touch-action:none}\n.toolbar{display:flex;justify-content:center;gap:10px}button{border:0;border-radius:12px;background:#ff8c42;color:#fff8ef;padding:10px 18px;font:700 15px system-ui;cursor:pointer;touch-action:none;user-select:none;box-shadow:0 3px 0 #c75f22}\nbutton:focus-visible{outline:3px solid #ffb27a;outline-offset:3px}button:disabled{opacity:.6;cursor:default}\np{margin:0;text-align:center;font-size:13px;line-height:1.4;color:#8a7360}\n.pad{display:grid;grid-template-columns:repeat(3,52px);grid-template-rows:repeat(2,48px);gap:6px;justify-content:center}\n.pad button{padding:0;background:#5a6d7d;color:#fff;font-size:22px}.pad button:active,.pad button.held{background:#8aa5b5}\n[data-dir=\"up\"]{grid-column:2;grid-row:1}[data-dir=\"left\"]{grid-column:1;grid-row:2}[data-dir=\"down\"]{grid-column:2;grid-row:2}[data-dir=\"right\"]{grid-column:3;grid-row:2}\n@media(max-width:480px){h1{font-size:17px}#hud{font-size:12px}main{gap:8px}}\n</style>\n</head>\n<body>\n<main>\n<header><h1>Cloud Courier</h1><div id=\"hud\" aria-live=\"polite\"></div></header>\n<canvas id=\"board\" width=\"960\" height=\"600\" aria-label=\"Cloud Courier game board\"></canvas>\n<div class=\"toolbar\"><button id=\"start\">Start</button><button id=\"restart\">Restart</button></div>\n<p id=\"instructions\">Collect all five letters, then reach the floating mailbox. Avoid two storm clouds. Arrows or WASD move. Space starts. R restarts.</p>\n<div class=\"pad\" aria-label=\"Touch movement controls\">\n<button data-dir=\"up\" aria-label=\"Move up\">&#9650;</button><button data-dir=\"left\" aria-label=\"Move left\">&#9664;</button>\n<button data-dir=\"down\" aria-label=\"Move down\">&#9660;</button><button data-dir=\"right\" aria-label=\"Move right\">&#9654;</button>\n</div>\n</main>\n<script>\n'use strict';\nconst canvas=document.querySelector('#board'),ctx=canvas.getContext('2d');\nconst hud=document.querySelector('#hud'),startButton=document.querySelector('#start');\nconst W=960,H=600,SPEED=250,HOME={x:170,y:470};\nconst LETTERS=5,HEARTS=3;\nconst letterDefs=[{x:180,y:140},{x:620,y:110},{x:820,y:350},{x:420,y:420},{x:250,y:390}];\nconst stormBases=[{x:480,y:300,r:70},{x:490,y:150,r:80}];\nconst keyboard={up:false,down:false,left:false,right:false};\nconst pointerDirections=new Map();\nlet state,lastTime=null;\nfunction clearInputs(){\n  for(const key of Object.keys(keyboard))keyboard[key]=false;\n  pointerDirections.clear();\n  document.querySelectorAll('.pad button').forEach(button=>button.classList.remove('held'));\n}\nfunction reset(){\n  clearInputs();\n  state={phase:'ready',score:0,lives:HEARTS,bag:0,time:0,invulnerableUntil:0,\n    player:{x:100,y:300,r:13,vx:0,vy:0},\n    letters:letterDefs.map(point=>({...point,taken:false})),\n    storms:stormBases.map((point,index)=>({...point,r:point.r,phase:index*.9}))};\n  lastTime=null;\n  updateHUD();\n}\nfunction start(){if(state.phase!=='playing'){reset();state.phase='playing';updateHUD();}}\nfunction updateHUD(){\n  hud.textContent=`Letters ${Math.min(state.bag,LETTERS)}/${LETTERS} &#9829; ${state.lives} &#9825; Score ${state.score}`;\n  startButton.textContent=state.phase==='won'||state.phase==='lost'?'Play again':'Start';\n  startButton.disabled=state.phase==='playing';\n}\nfunction update(dt){\n  if(state.phase!=='playing')return;\n  state.time+=dt;\n  const held=new Set(pointerDirections.values());\n  let dx=Number(keyboard.right||held.has('right'))-Number(keyboard.left||held.has('left'));\n  let dy=Number(held.has('down'))-Number(held.has('up'));\n  const length=Math.hypot(dx,dy)||1;\n  const player=state.player;\n  player.vx=dx/length*SPEED;player.vy=dy/length*SPEED;\n  player.x=Math.max(20,Math.min(W-20,player.x+player.vx*dt));\n  player.y=Math.max(20,Math.min(H-20,player.y+player.vy*dt));\n  state.letters.forEach(letter=>{\n    if(!letter.taken&&Math.hypot(player.x-letter.x,player.y-letter.y)<30){letter.taken=true;state.bag++;state.score+=50;}\n  });\n  state.storms.forEach((storm,index)=>{\n    storm.x=Math.max(60,Math.min(W-60,stormBases[index].x+Math.sin(state.time*.8+storm.phase)*160));\n    storm.y=Math.max(60,Math.min(H-60,stormBases[index].y+Math.cos(state.time*.6+storm.phase)*80));\n    if(state.time>=state.invulnerableUntil&&Math.hypot(player.x-storm.x,player.y-storm.y)<player.r+storm.r){\n      state.lives--;state.invulnerableUntil=state.time+1;\n      if(state.lives<=0)state.phase='lost';\n    }\n  });\n  if(state.phase==='playing'&&state.bag===LETTERS&&Math.hypot(player.x-HOME.x,player.y-HOME.y)<40){state.phase='won';state.score+=200;}\n  if(state.phase!=='playing')clearInputs();\n  updateHUD();\n}\nfunction circle(x,y,r,color){ctx.fillStyle=color;ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.fill();}\nfunction draw(){\n  const sky=ctx.createLinearGradient(0,0,W,H);sky.addColorStop(0,'#9fd0e8');sky.addColorStop(1,'#d6ecf5');\n  ctx.fillStyle=sky;ctx.fillRect(0,0,W,H);\n  ctx.fillStyle='#f7f3e8';for(let i=0;i<8;i++){const x=(i*180+40)%W,y=(i*140+20)%H;ctx.beginPath();ctx.arc(x,y,34,0,Math.PI*2);ctx.fill();}\n  ctx.fillStyle='#cfe4ec';for(let i=0;i<5;i++){const x=(i*200+60)%W,y=(i*160+60)%H;ctx.beginPath();ctx.arc(x,y,46,0,Math.PI*2);ctx.fill();}\n  ctx.fillStyle='#fff8e8';ctx.fillRect(0,0,W,14);ctx.fillRect(0,H-14,W,14);\n  ctx.strokeStyle='#e3d3b8';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(0,14);ctx.lineTo(W,14);ctx.moveTo(0,H-14);ctx.lineTo(W,H-14);ctx.stroke();\n  ctx.fillStyle='#f9e2b8';ctx.beginPath();ctx.moveTo(0,14);ctx.quadraticCurveTo(W*0.3,20,W*0.55,14);ctx.quadraticCurveTo(W*0.8,8,W,14);ctx.fill();\n  ctx.fillStyle='#f9e2b8';ctx.beginPath();ctx.moveTo(0,H-14);ctx.quadraticCurveTo(W*0.3,H-20,W*0.55,H-14);ctx.quadraticCurveTo(W*0.8,H-20,W,14);ctx.fill();\n  ctx.strokeStyle='#b98a5a';ctx.lineWidth=4;ctx.beginPath();ctx.moveTo(0,20);ctx.quadraticCurveTo(W*0.25,14,W*0.5,18);ctx.quadraticCurveTo(W*0.75,22,W,16);ctx.stroke();\n  ctx.fillStyle='#c9e4ea';ctx.beginPath();ctx.ellipse(W*0.8,80,90,24,0,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#b7d8e5';ctx.beginPath();ctx.ellipse(W*0.2,70,70,20,-0.2,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#fff3d6';ctx.beginPath();ctx.moveTo(HOME.x-40,HOME.y);ctx.quadraticCurveTo(HOME.x,HOME.y-58,HOME.x+40,HOME.y);ctx.lineTo(HOME.x+30,HOME.y);ctx.quadraticCurveTo(HOME.x,HOME.y-42,HOME.x-30,HOME.y);ctx.fill();\n  ctx.fillStyle='#fff';ctx.beginPath();ctx.arc(HOME.x,HOME.y-16,7,0,Math.PI*2);ctx.fill();ctx.fillStyle='#ff8c42';ctx.beginPath();ctx.arc(HOME.x,HOME.y-16,3.5,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#e0703a';ctx.font='700 22px system-ui';ctx.textAlign='center';ctx.fillText('FLOATING MAILBOX',HOME.x,HOME.y-74);\n  ctx.textAlign='left';\n  state.storms.forEach(storm=>{\n    ctx.fillStyle='#5d6b7a';ctx.beginPath();ctx.ellipse(storm.x,storm.y+10,storm.r*0.95,22,0,0,Math.PI*2);ctx.fill();\n    ctx.fillStyle='#8fa3b5';ctx.beginPath();ctx.ellipse(storm.x,storm.y,storm.r,30,0,0,Math.PI*2);ctx.fill();\n    ctx.fillStyle='#4c5a68';ctx.beginPath();ctx.ellipse(storm.x-storm.r*0.3,storm.y-8,storm.r*0.5,14,0,0,Math.PI*2);ctx.fill();\n    ctx.fillStyle='#dfe9ee';ctx.beginPath();ctx.arc(storm.x-storm.r*0.35,storm.y-16,storm.r*0.22,0,Math.PI*2);ctx.fill();\n    ctx.fillStyle='#cfdce4';ctx.beginPath();ctx.arc(storm.x+storm.r*0.3,storm.y-20,storm.r*0.18,0,Math.PI*2).fill();\n    ctx.fillStyle='#cfdce4';ctx.beginPath();ctx.arc(storm.x+storm.r*0.3,storm.y-20,storm.r*0.18,0,Math.PI*2);ctx.fill();\n  });\n  state.letters.forEach(letter=>{\n    if(letter.taken)return;\n    ctx.save();ctx.translate(letter.x,letter.y);ctx.rotate(Math.atan2(letter.y-state.player.y,letter.x-letter.x)*0.5);\n    ctx.fillStyle='#b4552d';ctx.fillRect(-24,-7,48,14);ctx.fillStyle='#fff3d6';ctx.fillRect(-17,-7,34,14);\n    ctx.fillStyle='#b4552d';ctx.fillRect(-24,-7,48,3);ctx.fillRect(-24,4,48,3);ctx.fillRect(-24,0,48,3);ctx.fillRect(-24,7,48,3);\n    ctx.restore();\n  });\n  const player=state.player;\n  if(state.time<state.invulnerableUntil)ctx.globalAlpha=0.55;\n  ctx.save();ctx.translate(player.x,player.y);ctx.rotate(Math.atan2(player.vy,player.vx));\n  ctx.fillStyle='#ff8c42';ctx.beginPath();ctx.ellipse(0,0,20,13,0,0,Math.PI*2).fill();\n  ctx.fillStyle='#fff1d6';ctx.beginPath();ctx.ellipse(0,0,13,6.5,0,0,Math.PI*2).fill();\n  ctx.fillStyle='#e0703a';ctx.beginPath();ctx.moveTo(20,0);ctx.lineTo(33,-8);ctx.lineTo(20,8);ctx.closePath();ctx.fill();\n  ctx.restore();\n  ctx.globalAlpha=1;\n  if(state.bag>0)ctx.fillStyle='#ff8c42';ctx.beginPath();ctx.arc(player.x+20,player.y+7,6,0,Math.PI*2);ctx.fill();\n  if(state.phase!=='playing'){\n    ctx.fillStyle='rgba(74,59,42,0.66)';ctx.fillRect(180,210,600,180);\n    ctx.textAlign='center';ctx.fillStyle='#ffb27a';ctx.font='700 38px system-ui';\n    ctx.fillText(state.phase==='won'?'Delivery complete!':state.phase==='lost'?'Storm caught you':'Cloud Courier',W/2,270);\n    ctx.fillStyle='#fff3dd';ctx.font='21px system-ui';\n    ctx.fillText(state.phase==='ready'?'Collect five letters, then reach the floating mailbox.':'Press Start to fly again, or R to restart.',W/2,315);\n    ctx.font='17px system-ui';ctx.fillStyle='#ffd9ae';ctx.fillText('Use the arrows, WASD or the touch pad below.',W/2,350);ctx.textAlign='left';\n  }\n}\nfunction loop(timestamp){\n  const dt=lastTime===null?0:Math.min((timestamp-lastTime)/1000,.04);\n  lastTime=timestamp;update(dt);draw();requestAnimationFrame(loop);\n}\nconst keyDirections={ArrowUp:'up',ArrowDown:'down',ArrowLeft:'left',ArrowRight:'right',w:'up',s:'down',a:'left',d:'right'};\nwindow.addEventListener('keydown',event=>{\n  const key=event.key.length===1?event.key.toLowerCase():event.key;\n  if(keyDirections[key]){event.preventDefault();keyboard[keyDirections[key]]=true;}\n  if(event.code==='Space'){event.preventDefault();if(state.phase!=='playing'&&!event.repeat)start();}\n  if(key==='r'&&!event.repeat)reset();\n});\nwindow.addEventListener('keyup',event=>{\n  const key=event.key.length===1?event.key.toLowerCase():event.key;\n  if(keyDirections[key]){event.preventDefault();keyboard[keyDirections[key]]=false;}\n});\ndocument.querySelectorAll('[data-dir]').forEach(button=>{\n  button.addEventListener('pointerdown',event=>{event.preventDefault();pointerDirections.set(event.pointerId,button.dataset.dir);button.classList.add('held');button.setPointerCapture(event.pointerId);});\n  const release=event=>{pointerDirections.delete(event.pointerId);button.classList.remove('held');};\n  button.addEventListener('pointerup',release);button.addEventListener('pointercancel',release);button.addEventListener('lostpointercapture',release);\n});\nwindow.addEventListener('blur',clearInputs);\ndocument.addEventListener('visibilitychange',()=>{if(document.hidden)clearInputs();});\nstartButton.addEventListener('click',start);\ndocument.querySelector('#restart').addEventListener('click',reset);\ncanvas.addEventListener('pointerdown',()=>{if(state.phase==='ready')start();});\nwindow.arcade={restart:reset,snapshot:()=>({phase:state.phase,score:state.score,player:{x:state.player.x,y:state.player.y},lives:state.lives,collected:state.bag})};\nreset();requestAnimationFrame(loop);\n</script>\n</body>\n</html>",
      "after": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"description\" content=\"A paper airplane courier steers through a calm sky, collecting five letters and delivering them to a floating mailbox.\">\n<title>Cloud Courier</title>\n<style>\n*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:#f2ead9;color:#4a3b2a;font:15px system-ui,sans-serif}\nbody{display:flex;justify-content:center;align-items:flex-start;padding:12px;background:linear-gradient(180deg,#dbe9f5 0%,#f2ead9 100%)}\nmain{width:min(960px,calc(100vw - 24px),calc(160dvh - 400px));min-width:280px;display:grid;gap:10px}\nheader{display:flex;justify-content:space-between;align-items:center;gap:12px}h1{font-size:20px;margin:0;color:#e0703a}\n#hud{font-size:14px;text-align:right;color:#7c4a2b}\ncanvas{display:block;width:100%;height:auto;aspect-ratio:8/5;border:2px solid #c9b391;border-radius:18px;background:linear-gradient(180deg,#a8d4e8 0%,#d9ecf5 60%,#cfe8c8 61%,#c8d9a8 100%);touch-action:none}\n.toolbar{display:flex;justify-content:center;gap:10px}button{border:0;border-radius:12px;background:#ff8c42;color:#fff8ef;padding:10px 18px;font:700 15px system-ui;cursor:pointer;touch-action:none;user-select:none;box-shadow:0 3px 0 #c75f22}\nbutton:focus-visible{outline:3px solid #ffb27a;outline-offset:3px}button:disabled{opacity:.6;cursor:default}\np{margin:0;text-align:center;font-size:13px;line-height:1.4;color:#8a7360}\n.pad{display:grid;grid-template-columns:repeat(3,52px);grid-template-rows:repeat(2,48px);gap:6px;justify-content:center}\n.pad button{padding:0;background:#5a6d7d;color:#fff;font-size:22px}.pad button:active,.pad button.held{background:#8aa5b5}\n[data-dir=\"up\"]{grid-column:2;grid-row:1}[data-dir=\"left\"]{grid-column:1;grid-row:2}[data-dir=\"down\"]{grid-column:2;grid-row:2}[data-dir=\"right\"]{grid-column:3;grid-row:2}\n@media(max-width:480px){h1{font-size:17px}#hud{font-size:12px}main{gap:8px}}\n</style>\n</head>\n<body>\n<main>\n<header><h1>Cloud Courier</h1><div id=\"hud\" aria-live=\"polite\"></div></header>\n<canvas id=\"board\" width=\"960\" height=\"600\" aria-label=\"Cloud Courier game board\"></canvas>\n<div class=\"toolbar\"><button id=\"start\">Start</button><button id=\"restart\">Restart</button></div>\n<p id=\"instructions\">Collect all five letters, then reach the floating mailbox. Avoid two storm clouds. Arrows or WASD move. Space starts. R restarts.</p>\n<div class=\"pad\" aria-label=\"Touch movement controls\">\n<button data-dir=\"up\" aria-label=\"Move up\">&#9650;</button><button data-dir=\"left\" aria-label=\"Move left\">&#9664;</button>\n<button data-dir=\"down\" aria-label=\"Move down\">&#9660;</button><button data-dir=\"right\" aria-label=\"Move right\">&#9654;</button>\n</div>\n</main>\n<script>\n'use strict';\nconst canvas=document.querySelector('#board'),ctx=canvas.getContext('2d');\nconst hud=document.querySelector('#hud'),startButton=document.querySelector('#start');\nconst W=960,H=600,SPEED=250,HOME={x:170,y:470};\nconst LETTERS=5,HEARTS=3;\nconst letterDefs=[{x:180,y:140},{x:620,y:110},{x:820,y:350},{x:420,y:420},{x:250,y:390}];\nconst stormBases=[{x:480,y:300,r:70},{x:490,y:150,r:80}];\nconst keyboard={up:false,down:false,left:false,right:false};\nconst pointerDirections=new Map();\nlet state,lastTime=null;\nfunction clearInputs(){\n  for(const key of Object.keys(keyboard))keyboard[key]=false;\n  pointerDirections.clear();\n  document.querySelectorAll('.pad button').forEach(button=>button.classList.remove('held'));\n}\nfunction reset(){\n  clearInputs();\n  state={phase:'ready',score:0,lives:HEARTS,bag:0,time:0,invulnerableUntil:0,\n    player:{x:100,y:300,r:13,vx:0,vy:0},\n    letters:letterDefs.map(point=>({...point,taken:false})),\n    storms:stormBases.map((point,index)=>({...point,r:point.r,phase:index*.9}))};\n  lastTime=null;\n  updateHUD();\n}\nfunction start(){if(state.phase!=='playing'){reset();state.phase='playing';updateHUD();}}\nfunction updateHUD(){\n  hud.textContent=`Letters ${Math.min(state.bag,LETTERS)}/${LETTERS} &#9829; ${state.lives} &#9825; Score ${state.score}`;\n  startButton.textContent=state.phase==='won'||state.phase==='lost'?'Play again':'Start';\n  startButton.disabled=state.phase==='playing';\n}\nfunction update(dt){\n  if(state.phase!=='playing')return;\n  state.time+=dt;\n  const held=new Set(pointerDirections.values());\n  let dx=Number(keyboard.right||held.has('right'))-Number(keyboard.left||held.has('left'));\n  let dy=Number(keyboard.down||held.has('down'))-Number(keyboard.up||held.has('up'));\n  const length=Math.hypot(dx,dy)||1;\n  const player=state.player;\n  player.vx=dx/length*SPEED;player.vy=dy/length*SPEED;\n  player.x=Math.max(20,Math.min(W-20,player.x+player.vx*dt));\n  player.y=Math.max(20,Math.min(H-20,player.y+player.vy*dt));\n  state.letters.forEach(letter=>{\n    if(!letter.taken&&Math.hypot(player.x-letter.x,player.y-letter.y)<30){letter.taken=true;state.bag++;state.score+=50;}\n  });\n  state.storms.forEach((storm,index)=>{\n    storm.x=Math.max(60,Math.min(W-60,stormBases[index].x+Math.sin(state.time*.8+storm.phase)*160));\n    storm.y=Math.max(60,Math.min(H-60,stormBases[index].y+Math.cos(state.time*.6+storm.phase)*80));\n    if(state.time>=state.invulnerableUntil&&Math.hypot(player.x-storm.x,player.y-storm.y)<player.r+storm.r){\n      state.lives--;state.invulnerableUntil=state.time+1;\n      if(state.lives<=0)state.phase='lost';\n    }\n  });\n  if(state.phase==='playing'&&state.bag===LETTERS&&Math.hypot(player.x-HOME.x,player.y-HOME.y)<40){state.phase='won';state.score+=200;}\n  if(state.phase!=='playing')clearInputs();\n  updateHUD();\n}\nfunction circle(x,y,r,color){ctx.fillStyle=color;ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.fill();}\nfunction draw(){\n  const sky=ctx.createLinearGradient(0,0,W,H);sky.addColorStop(0,'#9fd0e8');sky.addColorStop(1,'#d6ecf5');\n  ctx.fillStyle=sky;ctx.fillRect(0,0,W,H);\n  ctx.fillStyle='#f7f3e8';for(let i=0;i<8;i++){const x=(i*180+40)%W,y=(i*140+20)%H;ctx.beginPath();ctx.arc(x,y,34,0,Math.PI*2);ctx.fill();}\n  ctx.fillStyle='#cfe4ec';for(let i=0;i<5;i++){const x=(i*200+60)%W,y=(i*160+60)%H;ctx.beginPath();ctx.arc(x,y,46,0,Math.PI*2);ctx.fill();}\n  ctx.fillStyle='#fff8e8';ctx.fillRect(0,0,W,14);ctx.fillRect(0,H-14,W,14);\n  ctx.strokeStyle='#e3d3b8';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(0,14);ctx.lineTo(W,14);ctx.moveTo(0,H-14);ctx.lineTo(W,H-14);ctx.stroke();\n  ctx.fillStyle='#f9e2b8';ctx.beginPath();ctx.moveTo(0,14);ctx.quadraticCurveTo(W*0.3,20,W*0.55,14);ctx.quadraticCurveTo(W*0.8,8,W,14);ctx.fill();\n  ctx.fillStyle='#f9e2b8';ctx.beginPath();ctx.moveTo(0,H-14);ctx.quadraticCurveTo(W*0.3,H-20,W*0.55,H-14);ctx.quadraticCurveTo(W*0.8,H-20,W,14);ctx.fill();\n  ctx.strokeStyle='#b98a5a';ctx.lineWidth=4;ctx.beginPath();ctx.moveTo(0,20);ctx.quadraticCurveTo(W*0.25,14,W*0.5,18);ctx.quadraticCurveTo(W*0.75,22,W,16);ctx.stroke();\n  ctx.fillStyle='#c9e4ea';ctx.beginPath();ctx.ellipse(W*0.8,80,90,24,0,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#b7d8e5';ctx.beginPath();ctx.ellipse(W*0.2,70,70,20,-0.2,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#fff3d6';ctx.beginPath();ctx.moveTo(HOME.x-40,HOME.y);ctx.quadraticCurveTo(HOME.x,HOME.y-58,HOME.x+40,HOME.y);ctx.lineTo(HOME.x+30,HOME.y);ctx.quadraticCurveTo(HOME.x,HOME.y-42,HOME.x-30,HOME.y);ctx.fill();\n  ctx.fillStyle='#fff';ctx.beginPath();ctx.arc(HOME.x,HOME.y-16,7,0,Math.PI*2);ctx.fill();ctx.fillStyle='#ff8c42';ctx.beginPath();ctx.arc(HOME.x,HOME.y-16,3.5,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#e0703a';ctx.font='700 22px system-ui';ctx.textAlign='center';ctx.fillText('FLOATING MAILBOX',HOME.x,HOME.y-74);\n  ctx.textAlign='left';\n  state.storms.forEach(storm=>{\n    ctx.fillStyle='#5d6b7a';ctx.beginPath();ctx.ellipse(storm.x,storm.y+10,storm.r*0.95,22,0,0,Math.PI*2);ctx.fill();\n    ctx.fillStyle='#8fa3b5';ctx.beginPath();ctx.ellipse(storm.x,storm.y,storm.r,30,0,0,Math.PI*2);ctx.fill();\n    ctx.fillStyle='#4c5a68';ctx.beginPath();ctx.ellipse(storm.x-storm.r*0.3,storm.y-8,storm.r*0.5,14,0,0,Math.PI*2);ctx.fill();\n    ctx.fillStyle='#dfe9ee';ctx.beginPath();ctx.arc(storm.x-storm.r*0.35,storm.y-16,storm.r*0.22,0,Math.PI*2);ctx.fill();\n    ctx.fillStyle='#cfdce4';ctx.beginPath();ctx.arc(storm.x+storm.r*0.3,storm.y-20,storm.r*0.18,0,Math.PI*2);ctx.fill();\n  });\n  state.letters.forEach(letter=>{\n    if(letter.taken)return;\n    ctx.save();ctx.translate(letter.x,letter.y);ctx.rotate(Math.atan2(letter.y-state.player.y,letter.x-state.player.x)*0.5);\n    ctx.fillStyle='#b4552d';ctx.fillRect(-24,-7,48,14);ctx.fillStyle='#fff3d6';ctx.fillRect(-17,-7,34,14);\n    ctx.fillStyle='#b4552d';ctx.fillRect(-24,-7,48,3);ctx.fillRect(-24,4,48,3);ctx.fillRect(-24,0,48,3);ctx.fillRect(-24,7,48,3);\n    ctx.restore();\n  });\n  const player=state.player;\n  if(state.time<state.invulnerableUntil)ctx.globalAlpha=0.55;\n  ctx.save();ctx.translate(player.x,player.y);ctx.rotate(Math.atan2(player.vy,player.vx));\n  ctx.fillStyle='#ff8c42';ctx.beginPath();ctx.ellipse(0,0,20,13,0,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#fff1d6';ctx.beginPath();ctx.ellipse(0,0,13,6.5,0,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#e0703a';ctx.beginPath();ctx.moveTo(20,0);ctx.lineTo(33,-8);ctx.lineTo(20,8);ctx.closePath();ctx.fill();\n  ctx.restore();\n  ctx.globalAlpha=1;\n  if(state.bag>0)ctx.fillStyle='#ff8c42';ctx.beginPath();ctx.arc(player.x+20,player.y+7,6,0,Math.PI*2);ctx.fill();\n  if(state.phase!=='playing'){\n    ctx.fillStyle='rgba(74,59,42,0.66)';ctx.fillRect(180,210,600,180);\n    ctx.textAlign='center';ctx.fillStyle='#ffb27a';ctx.font='700 38px system-ui';\n    ctx.fillText(state.phase==='won'?'Delivery complete!':state.phase==='lost'?'Storm caught you':'Cloud Courier',W/2,270);\n    ctx.fillStyle='#fff3dd';ctx.font='21px system-ui';\n    ctx.fillText(state.phase==='ready'?'Collect five letters, then reach the floating mailbox.':'Press Start to fly again, or R to restart.',W/2,315);\n    ctx.font='17px system-ui';ctx.fillStyle='#ffd9ae';ctx.fillText('Use the arrows, WASD or the touch pad below.',W/2,350);ctx.textAlign='left';\n  }\n}\nfunction loop(timestamp){\n  const dt=lastTime===null?0:Math.min((timestamp-lastTime)/1000,.04);\n  lastTime=timestamp;update(dt);draw();requestAnimationFrame(loop);\n}\nconst keyDirections={ArrowUp:'up',ArrowDown:'down',ArrowLeft:'left',ArrowRight:'right',w:'up',s:'down',a:'left',d:'right'};\nwindow.addEventListener('keydown',event=>{\n  const key=event.key.length===1?event.key.toLowerCase():event.key;\n  if(keyDirections[key]){event.preventDefault();keyboard[keyDirections[key]]=true;}\n  if(event.code==='Space'){event.preventDefault();if(state.phase!=='playing'&&!event.repeat)start();}\n  if(key==='r'&&!event.repeat)reset();\n});\nwindow.addEventListener('keyup',event=>{\n  const key=event.key.length===1?event.key.toLowerCase():event.key;\n  if(keyDirections[key]){event.preventDefault();keyboard[keyDirections[key]]=false;}\n});\ndocument.querySelectorAll('[data-dir]').forEach(button=>{\n  button.addEventListener('pointerdown',event=>{event.preventDefault();pointerDirections.set(event.pointerId,button.dataset.dir);button.classList.add('held');button.setPointerCapture(event.pointerId);});\n  const release=event=>{pointerDirections.delete(event.pointerId);button.classList.remove('held');};\n  button.addEventListener('pointerup',release);button.addEventListener('pointercancel',release);button.addEventListener('lostpointercapture',release);\n});\nwindow.addEventListener('blur',clearInputs);\ndocument.addEventListener('visibilitychange',()=>{if(document.hidden)clearInputs();});\nstartButton.addEventListener('click',start);\ndocument.querySelector('#restart').addEventListener('click',reset);\ncanvas.addEventListener('pointerdown',()=>{if(state.phase==='ready')start();});\nwindow.arcade={restart:reset,snapshot:()=>({phase:state.phase,score:state.score,player:{x:state.player.x,y:state.player.y},lives:state.lives,collected:state.bag})};\nreset();requestAnimationFrame(loop);\n</script>\n</body>\n</html>",
      "before_record": "arcade-b3e5fa2351450c18ffb1-1",
      "after_record": "arcade-b3e5fa2351450c18ffb1-2",
      "before_sha256": "a6ab1fa88cdddf1d876558d374da75a94710c9ea55063d435cc83d081d440da8",
      "after_sha256": "2bc8e1870d88e1d11f50362fa98c9c199324d2db870649cf5db53143799bc3b1",
      "errors": [
        "Game startup JavaScript errors: Cannot read properties of undefined (reading 'fill')\nat game document\nat draw (game document)\nat loop (game document)"
      ],
      "historical_checks": {
        "accepted": true,
        "checks": [
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"collected\": 0, \"lives\": 3, \"phase\": \"playing\", \"player\": {\"x\": 170.8, \"y\": 229.125}, \"score\": 0}, \"after_start\": {\"collected\": 0, \"lives\": 3, \"phase\": \"playing\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"initial\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"reset\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"restarted\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}}",
            "start_button": true,
            "viewport": "desktop"
          },
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"collected\": 0, \"lives\": 3, \"phase\": \"playing\", \"player\": {\"x\": 90.82500000000005, \"y\": 300.0000000000001}, \"score\": 0}, \"after_start\": {\"collected\": 0, \"lives\": 3, \"phase\": \"playing\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"initial\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"reset\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"restarted\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}}",
            "start_button": true,
            "viewport": "mobile"
          }
        ],
        "errors": [],
        "scope": "Static policy, visible Start/Restart controls, canvas rendering, finite snapshot contract, restart and brief desktop/mobile input smoke tests. Full gameplay and game balance are not measured.",
        "status": "smoke_check_passed"
      },
      "release_checks": {
        "accepted": true,
        "checks": [
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"collected\": 0, \"lives\": 3, \"phase\": \"playing\", \"player\": {\"x\": 162.52499999999998, \"y\": 219.17500000000007}, \"score\": 0}, \"after_start\": {\"collected\": 0, \"lives\": 3, \"phase\": \"playing\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"initial\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"reset\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"restarted\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}}",
            "start_button": true,
            "viewport": "desktop"
          },
          {
            "blocked_requests": 0,
            "canvas_after_input": true,
            "canvas_nonblank": true,
            "input_progress": true,
            "restart": true,
            "restart_button": true,
            "runtime_errors": [],
            "snapshots_json": "{\"after_input\": {\"collected\": 0, \"lives\": 3, \"phase\": \"playing\", \"player\": {\"x\": 99.97500000000002, \"y\": 304.19999999999993}, \"score\": 0}, \"after_start\": {\"collected\": 0, \"lives\": 3, \"phase\": \"playing\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"initial\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"reset\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}, \"restarted\": {\"collected\": 0, \"lives\": 3, \"phase\": \"ready\", \"player\": {\"x\": 100, \"y\": 300}, \"score\": 0}}",
            "start_button": true,
            "viewport": "mobile"
          }
        ],
        "errors": [],
        "scope": "Static policy, visible Start/Restart controls, canvas rendering, finite snapshot contract, restart and brief desktop/mobile input smoke tests. Full gameplay and game balance are not measured.",
        "status": "smoke_check_passed"
      },
      "diff": "--- failed.html\n\n+++ recorded-repair.html\n\n@@ -68,7 +68,7 @@\n\n   state.time+=dt;\n   const held=new Set(pointerDirections.values());\n   let dx=Number(keyboard.right||held.has('right'))-Number(keyboard.left||held.has('left'));\n-  let dy=Number(held.has('down'))-Number(held.has('up'));\n+  let dy=Number(keyboard.down||held.has('down'))-Number(keyboard.up||held.has('up'));\n   const length=Math.hypot(dx,dy)||1;\n   const player=state.player;\n   player.vx=dx/length*SPEED;player.vy=dy/length*SPEED;\n@@ -111,12 +111,11 @@\n\n     ctx.fillStyle='#8fa3b5';ctx.beginPath();ctx.ellipse(storm.x,storm.y,storm.r,30,0,0,Math.PI*2);ctx.fill();\n     ctx.fillStyle='#4c5a68';ctx.beginPath();ctx.ellipse(storm.x-storm.r*0.3,storm.y-8,storm.r*0.5,14,0,0,Math.PI*2);ctx.fill();\n     ctx.fillStyle='#dfe9ee';ctx.beginPath();ctx.arc(storm.x-storm.r*0.35,storm.y-16,storm.r*0.22,0,Math.PI*2);ctx.fill();\n-    ctx.fillStyle='#cfdce4';ctx.beginPath();ctx.arc(storm.x+storm.r*0.3,storm.y-20,storm.r*0.18,0,Math.PI*2).fill();\n     ctx.fillStyle='#cfdce4';ctx.beginPath();ctx.arc(storm.x+storm.r*0.3,storm.y-20,storm.r*0.18,0,Math.PI*2);ctx.fill();\n   });\n   state.letters.forEach(letter=>{\n     if(letter.taken)return;\n-    ctx.save();ctx.translate(letter.x,letter.y);ctx.rotate(Math.atan2(letter.y-state.player.y,letter.x-letter.x)*0.5);\n+    ctx.save();ctx.translate(letter.x,letter.y);ctx.rotate(Math.atan2(letter.y-state.player.y,letter.x-state.player.x)*0.5);\n     ctx.fillStyle='#b4552d';ctx.fillRect(-24,-7,48,14);ctx.fillStyle='#fff3d6';ctx.fillRect(-17,-7,34,14);\n     ctx.fillStyle='#b4552d';ctx.fillRect(-24,-7,48,3);ctx.fillRect(-24,4,48,3);ctx.fillRect(-24,0,48,3);ctx.fillRect(-24,7,48,3);\n     ctx.restore();\n@@ -124,8 +123,8 @@\n\n   const player=state.player;\n   if(state.time<state.invulnerableUntil)ctx.globalAlpha=0.55;\n   ctx.save();ctx.translate(player.x,player.y);ctx.rotate(Math.atan2(player.vy,player.vx));\n-  ctx.fillStyle='#ff8c42';ctx.beginPath();ctx.ellipse(0,0,20,13,0,0,Math.PI*2).fill();\n-  ctx.fillStyle='#fff1d6';ctx.beginPath();ctx.ellipse(0,0,13,6.5,0,0,Math.PI*2).fill();\n+  ctx.fillStyle='#ff8c42';ctx.beginPath();ctx.ellipse(0,0,20,13,0,0,Math.PI*2);ctx.fill();\n+  ctx.fillStyle='#fff1d6';ctx.beginPath();ctx.ellipse(0,0,13,6.5,0,0,Math.PI*2);ctx.fill();\n   ctx.fillStyle='#e0703a';ctx.beginPath();ctx.moveTo(20,0);ctx.lineTo(33,-8);ctx.lineTo(20,8);ctx.closePath();ctx.fill();\n   ctx.restore();\n   ctx.globalAlpha=1;",
      "deep_review": {},
      "game_url": "https://alphabell.com/arcade/g/cloud-courier-fixed"
    }
  ]
}
