{"project":{"id":"uzI1Vfp","userId":"davidyarham@gmail.com","username":null,"userPicture":null,"name":"Pong","visible":true,"contributors":"","githubRepo":null,"forkedFrom":null,"isTemplate":false,"tags":"","files":{"folder":"","files":[{"name":"index.html","content":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Untitled</title>\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n<body>\n<div class=\"pong\">\n\t<div class=\"pong__header\">\n\t\t<span class=\"pong__score pong__score--player\">0</span>\n\t\t<span class=\"pong__title\">PONG</span>\n\t\t<span class=\"pong__score pong__score--cpu\">0</span>\n\t</div>\n\t<canvas id=\"pongCanvas\" class=\"pong__canvas\"></canvas>\n\t<div class=\"pong__overlay\" id=\"overlay\">\n\t\t<h1 class=\"pong__start-title\">NEON PONG</h1>\n\t\t<p class=\"pong__start-sub\">Move your paddle with the mouse or touch</p>\n\t\t<button class=\"pong__btn\" id=\"startBtn\">START GAME</button>\n\t</div>\n\t<div class=\"pong__controls\">\n\t\t<span>▲▼ Mouse / Touch to move</span>\n\t\t<span>First to 7 wins</span>\n\t</div>\n</div>\n  <script type=\"module\" src=\"main.js\"></script>\n</body>\n</html>"},{"name":"main.js","content":"(() => {\n\tconst canvas = document.getElementById('pongCanvas');\n\tconst ctx = canvas.getContext('2d');\n\tconst overlay = document.getElementById('overlay');\n\tconst startBtn = document.getElementById('startBtn');\n\tconst playerScoreEl = document.querySelector('.pong__score--player');\n\tconst cpuScoreEl = document.querySelector('.pong__score--cpu');\n\n\tconst W = 800;\n\tconst H = 600;\n\tcanvas.width = W;\n\tcanvas.height = H;\n\n\tconst WINNING_SCORE = 7;\n\tconst PADDLE_W = 14;\n\tconst PADDLE_H = 100;\n\tconst BALL_R = 8;\n\tconst PADDLE_SPEED = 7;\n\tconst BALL_BASE_SPEED = 6;\n\tconst CPU_SPEED = 4.5;\n\n\tlet gameRunning = false;\n\tlet animId;\n\n\t// === AUDIO ENGINE (Web Audio API) ===\n\tlet audioCtx;\n\n\tfunction ensureAudio() {\n\t\tif (!audioCtx) audioCtx = new(window.AudioContext || window.webkitAudioContext)();\n\t\tif (audioCtx.state === 'suspended') audioCtx.resume();\n\t}\n\n\tfunction playTone(freq, duration, type = 'square', vol = 0.15, slide = 0) {\n\t\tif (!audioCtx) return;\n\t\tconst osc = audioCtx.createOscillator();\n\t\tconst gain = audioCtx.createGain();\n\t\tosc.type = type;\n\t\tosc.frequency.setValueAtTime(freq, audioCtx.currentTime);\n\t\tif (slide) osc.frequency.exponentialRampToValueAtTime(Math.max(slide, 20), audioCtx.currentTime + duration);\n\t\tgain.gain.setValueAtTime(vol, audioCtx.currentTime);\n\t\tgain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + duration);\n\t\tosc.connect(gain);\n\t\tgain.connect(audioCtx.destination);\n\t\tosc.start(audioCtx.currentTime);\n\t\tosc.stop(audioCtx.currentTime + duration);\n\t}\n\n\tfunction sndPaddleHit(intensity) {\n\t\t// Higher pitch based on hit intensity\n\t\tplayTone(440 + intensity * 200, 0.08, 'square', 0.12);\n\t\tplayTone(880 + intensity * 200, 0.06, 'sine', 0.06);\n\t}\n\n\tfunction sndWallBounce() {\n\t\tplayTone(220, 0.05, 'triangle', 0.08);\n\t}\n\n\tfunction sndScore(isPlayer) {\n\t\tif (isPlayer) {\n\t\t\t// Ascending happy tone\n\t\t\tplayTone(523, 0.12, 'square', 0.1);\n\t\t\tsetTimeout(() => playTone(659, 0.12, 'square', 0.1), 80);\n\t\t\tsetTimeout(() => playTone(784, 0.2, 'square', 0.12), 160);\n\t\t} else {\n\t\t\t// Descending sad tone\n\t\t\tplayTone(400, 0.15, 'sawtooth', 0.08, 100);\n\t\t\tsetTimeout(() => playTone(250, 0.25, 'sawtooth', 0.06, 80), 120);\n\t\t}\n\t}\n\n\tfunction sndWin() {\n\t\tconst notes = [523, 659, 784, 1047];\n\t\tnotes.forEach((n, i) => {\n\t\t\tsetTimeout(() => playTone(n, 0.2, 'square', 0.1), i * 120);\n\t\t\tsetTimeout(() => playTone(n * 1.5, 0.15, 'sine', 0.05), i * 120 + 30);\n\t\t});\n\t}\n\n\tfunction sndLose() {\n\t\tconst notes = [400, 350, 300, 200];\n\t\tnotes.forEach((n, i) => {\n\t\t\tsetTimeout(() => playTone(n, 0.25, 'sawtooth', 0.08, n * 0.5), i * 150);\n\t\t});\n\t}\n\n\tconst state = {\n\t\tplayer: {\n\t\t\tx: 30,\n\t\t\ty: H / 2 - PADDLE_H / 2,\n\t\t\tw: PADDLE_W,\n\t\t\th: PADDLE_H,\n\t\t\tscore: 0\n\t\t},\n\t\tcpu: {\n\t\t\tx: W - 30 - PADDLE_W,\n\t\t\ty: H / 2 - PADDLE_H / 2,\n\t\t\tw: PADDLE_W,\n\t\t\th: PADDLE_H,\n\t\t\tscore: 0\n\t\t},\n\t\tball: {\n\t\t\tx: W / 2,\n\t\t\ty: H / 2,\n\t\t\tvx: BALL_BASE_SPEED,\n\t\t\tvy: 3,\n\t\t\tspeed: BALL_BASE_SPEED,\n\t\t\tr: BALL_R\n\t\t},\n\t\tparticles: [],\n\t\ttrail: [],\n\t\tmouseY: H / 2\n\t};\n\n\t// Neon glow helper\n\tfunction setNeonStroke(ctx, color, width, blur) {\n\t\tctx.strokeStyle = color;\n\t\tctx.lineWidth = width;\n\t\tctx.shadowColor = color;\n\t\tctx.shadowBlur = blur;\n\t}\n\n\tfunction setNeonFill(ctx, color, blur) {\n\t\tctx.fillStyle = color;\n\t\tctx.shadowColor = color;\n\t\tctx.shadowBlur = blur;\n\t}\n\n\tfunction clearGlow(ctx) {\n\t\tctx.shadowBlur = 0;\n\t\tctx.shadowColor = 'transparent';\n\t}\n\n\t// Particles\n\tfunction spawnParticles(x, y, color, count) {\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst angle = Math.random() * Math.PI * 2;\n\t\t\tconst speed = 1 + Math.random() * 4;\n\t\t\tstate.particles.push({\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tvx: Math.cos(angle) * speed,\n\t\t\t\tvy: Math.sin(angle) * speed,\n\t\t\t\tlife: 1,\n\t\t\t\tdecay: 0.02 + Math.random() * 0.03,\n\t\t\t\tcolor,\n\t\t\t\tsize: 2 + Math.random() * 3\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction updateParticles() {\n\t\tfor (let i = state.particles.length - 1; i >= 0; i--) {\n\t\t\tconst p = state.particles[i];\n\t\t\tp.x += p.vx;\n\t\t\tp.y += p.vy;\n\t\t\tp.life -= p.decay;\n\t\t\tp.vx *= 0.98;\n\t\t\tp.vy *= 0.98;\n\t\t\tif (p.life <= 0) state.particles.splice(i, 1);\n\t\t}\n\t}\n\n\tfunction drawParticles() {\n\t\tfor (const p of state.particles) {\n\t\t\tctx.globalAlpha = p.life;\n\t\t\tsetNeonFill(ctx, p.color, 15 * p.life);\n\t\t\tctx.beginPath();\n\t\t\tctx.arc(p.x, p.y, p.size * p.life, 0, Math.PI * 2);\n\t\t\tctx.fill();\n\t\t}\n\t\tctx.globalAlpha = 1;\n\t\tclearGlow(ctx);\n\t}\n\n\t// Trail\n\tfunction updateTrail() {\n\t\tstate.trail.push({\n\t\t\tx: state.ball.x,\n\t\t\ty: state.ball.y,\n\t\t\tlife: 1\n\t\t});\n\t\tif (state.trail.length > 20) state.trail.shift();\n\t\tfor (const t of state.trail) {\n\t\t\tt.life -= 0.05;\n\t\t}\n\t\tstate.trail = state.trail.filter(t => t.life > 0);\n\t}\n\n\tfunction drawTrail() {\n\t\tfor (const t of state.trail) {\n\t\t\tctx.globalAlpha = t.life * 0.4;\n\t\t\tsetNeonFill(ctx, '#ffe600', 10 * t.life);\n\t\t\tctx.beginPath();\n\t\t\tctx.arc(t.x, t.y, BALL_R * t.life, 0, Math.PI * 2);\n\t\t\tctx.fill();\n\t\t}\n\t\tctx.globalAlpha = 1;\n\t\tclearGlow(ctx);\n\t}\n\n\t// Draw dashed center line\n\tfunction drawCenterLine() {\n\t\tctx.setLineDash([10, 14]);\n\t\tsetNeonStroke(ctx, '#ffffff15', 2, 0);\n\t\tctx.beginPath();\n\t\tctx.moveTo(W / 2, 0);\n\t\tctx.lineTo(W / 2, H);\n\t\tctx.stroke();\n\t\tctx.setLineDash([]);\n\t\tclearGlow(ctx);\n\t}\n\n\t// Draw paddle with neon glow\n\tfunction drawPaddle(paddle, color) {\n\t\tconst {\n\t\t\tx,\n\t\t\ty,\n\t\t\tw,\n\t\t\th\n\t\t} = paddle;\n\t\t// Outer glow\n\t\tsetNeonFill(ctx, color, 30);\n\t\tctx.beginPath();\n\t\tctx.roundRect(x, y, w, h, 4);\n\t\tctx.fill();\n\t\t// Inner bright\n\t\tsetNeonFill(ctx, '#fff', 10);\n\t\tctx.beginPath();\n\t\tctx.roundRect(x + 3, y + 3, w - 6, h - 6, 2);\n\t\tctx.fill();\n\t\tclearGlow(ctx);\n\t}\n\n\t// Draw ball\n\tfunction drawBall() {\n\t\tconst {\n\t\t\tx,\n\t\t\ty,\n\t\t\tr\n\t\t} = state.ball;\n\t\t// Outer glow\n\t\tsetNeonFill(ctx, '#ffe600', 40);\n\t\tctx.beginPath();\n\t\tctx.arc(x, y, r + 2, 0, Math.PI * 2);\n\t\tctx.fill();\n\t\t// Core\n\t\tsetNeonFill(ctx, '#fff', 15);\n\t\tctx.beginPath();\n\t\tctx.arc(x, y, r - 2, 0, Math.PI * 2);\n\t\tctx.fill();\n\t\tclearGlow(ctx);\n\t}\n\n\t// Draw scanlines\n\tfunction drawScanlines() {\n\t\tctx.globalAlpha = 0.03;\n\t\tctx.fillStyle = '#000';\n\t\tfor (let i = 0; i < H; i += 3) {\n\t\t\tctx.fillRect(0, i, W, 1);\n\t\t}\n\t\tctx.globalAlpha = 1;\n\t}\n\n\t// Reset ball\n\tfunction resetBall(direction) {\n\t\tstate.ball.x = W / 2;\n\t\tstate.ball.y = H / 2;\n\t\tstate.ball.speed = BALL_BASE_SPEED;\n\t\tconst angle = (Math.random() * 0.8 - 0.4);\n\t\tstate.ball.vx = Math.cos(angle) * state.ball.speed * direction;\n\t\tstate.ball.vy = Math.sin(angle) * state.ball.speed;\n\t\tstate.trail = [];\n\t}\n\n\t// Player input\n\tfunction handleMouseMove(e) {\n\t\tconst rect = canvas.getBoundingClientRect();\n\t\tconst scaleY = H / rect.height;\n\t\tstate.mouseY = (e.clientY - rect.top) * scaleY;\n\t}\n\n\tfunction handleTouchMove(e) {\n\t\te.preventDefault();\n\t\tconst rect = canvas.getBoundingClientRect();\n\t\tconst scaleY = H / rect.height;\n\t\tstate.mouseY = (e.touches[0].clientY - rect.top) * scaleY;\n\t}\n\n\tcanvas.addEventListener('mousemove', handleMouseMove);\n\tcanvas.addEventListener('touchmove', handleTouchMove, {\n\t\tpassive: false\n\t});\n\n\t// Update player paddle\n\tfunction updatePlayer() {\n\t\tconst target = state.mouseY - state.player.h / 2;\n\t\tconst diff = target - state.player.y;\n\t\tstate.player.y += diff * 0.15;\n\t\tstate.player.y = Math.max(0, Math.min(H - PADDLE_H, state.player.y));\n\t}\n\n\t// CPU AI\n\tfunction updateCPU() {\n\t\tconst ball = state.ball;\n\t\tlet targetY = ball.y - state.cpu.h / 2;\n\n\t\t// Only react when ball is coming towards CPU\n\t\tif (ball.vx > 0) {\n\t\t\t// Predict where ball will be\n\t\t\tconst timeToReach = (state.cpu.x - ball.x) / ball.vx;\n\t\t\tlet predictedY = ball.y + ball.vy * timeToReach;\n\t\t\t// Bounce prediction\n\t\t\twhile (predictedY < 0 || predictedY > H) {\n\t\t\t\tif (predictedY < 0) predictedY = -predictedY;\n\t\t\t\tif (predictedY > H) predictedY = 2 * H - predictedY;\n\t\t\t}\n\t\t\ttargetY = predictedY - state.cpu.h / 2;\n\t\t}\n\n\t\tconst diff = targetY - state.cpu.y;\n\t\tconst move = Math.sign(diff) * Math.min(CPU_SPEED, Math.abs(diff));\n\t\tstate.cpu.y += move;\n\t\tstate.cpu.y = Math.max(0, Math.min(H - PADDLE_H, state.cpu.y));\n\t}\n\n\t// Ball collision\n\tfunction paddleCollision(paddle, color) {\n\t\tconst bx = state.ball.x,\n\t\t\tby = state.ball.y,\n\t\t\tbr = state.ball.r;\n\t\tif (\n\t\t\tbx - br < paddle.x + paddle.w &&\n\t\t\tbx + br > paddle.x &&\n\t\t\tby - br < paddle.y + paddle.h &&\n\t\t\tby + br > paddle.y\n\t\t) {\n\t\t\t// Reflect\n\t\t\tstate.ball.vx *= -1;\n\t\t\t// Angle based on hit position\n\t\t\tconst hitPos = (by - (paddle.y + paddle.h / 2)) / (paddle.h / 2);\n\t\t\tstate.ball.vy = hitPos * 6;\n\t\t\t// Speed up\n\t\t\tstate.ball.speed = Math.min(state.ball.speed + 0.3, 12);\n\t\t\tconst magnitude = Math.sqrt(state.ball.vx ** 2 + state.ball.vy ** 2);\n\t\t\tstate.ball.vx = (state.ball.vx / magnitude) * state.ball.speed;\n\t\t\tstate.ball.vy = (state.ball.vy / magnitude) * state.ball.speed;\n\t\t\t// Push ball out of paddle\n\t\t\tif (paddle === state.player) {\n\t\t\t\tstate.ball.x = paddle.x + paddle.w + br;\n\t\t\t} else {\n\t\t\t\tstate.ball.x = paddle.x - br;\n\t\t\t}\n\t\t\tspawnParticles(state.ball.x, state.ball.y, color, 15);\n\t\t\tsndPaddleHit(Math.abs(hitPos));\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tfunction updateBall() {\n\t\tconst ball = state.ball;\n\t\tball.x += ball.vx;\n\t\tball.y += ball.vy;\n\n\t\t// Top/bottom walls\n\t\tif (ball.y - ball.r <= 0) {\n\t\t\tball.y = ball.r;\n\t\t\tball.vy *= -1;\n\t\t\tspawnParticles(ball.x, ball.y, '#ffe600', 8);\n\t\t\tsndWallBounce();\n\t\t}\n\t\tif (ball.y + ball.r >= H) {\n\t\t\tball.y = H - ball.r;\n\t\t\tball.vy *= -1;\n\t\t\tspawnParticles(ball.x, ball.y, '#ffe600', 8);\n\t\t\tsndWallBounce();\n\t\t}\n\n\t\t// Paddle collisions\n\t\tpaddleCollision(state.player, '#00f0ff');\n\t\tpaddleCollision(state.cpu, '#ff00aa');\n\n\t\t// Scoring\n\t\tif (ball.x < -30) {\n\t\t\tstate.cpu.score++;\n\t\t\tcpuScoreEl.textContent = state.cpu.score;\n\t\t\tspawnParticles(0, ball.y, '#ff00aa', 30);\n\t\t\tsndScore(false);\n\t\t\tresetBall(1);\n\t\t\tcheckWin();\n\t\t}\n\t\tif (ball.x > W + 30) {\n\t\t\tstate.player.score++;\n\t\t\tplayerScoreEl.textContent = state.player.score;\n\t\t\tspawnParticles(W, ball.y, '#00f0ff', 30);\n\t\t\tsndScore(true);\n\t\t\tresetBall(-1);\n\t\t\tcheckWin();\n\t\t}\n\t}\n\n\tfunction checkWin() {\n\t\tif (state.player.score >= WINNING_SCORE) {\n\t\t\tendGame('YOU WIN!');\n\t\t} else if (state.cpu.score >= WINNING_SCORE) {\n\t\t\tendGame('CPU WINS');\n\t\t}\n\t}\n\n\tfunction endGame(msg) {\n\t\tgameRunning = false;\n\t\tcancelAnimationFrame(animId);\n\t\toverlay.classList.remove('pong__overlay--hidden');\n\t\tdocument.querySelector('.pong__start-title').textContent = msg;\n\t\tdocument.querySelector('.pong__start-sub').textContent = 'Click below to play again';\n\t\tstartBtn.textContent = 'PLAY AGAIN';\n\t\tif (msg === 'YOU WIN!') sndWin();\n\t\telse sndLose();\n\t}\n\n\t// Render\n\tfunction draw() {\n\t\t// BG\n\t\tctx.fillStyle = '#0a0a20';\n\t\tctx.fillRect(0, 0, W, H);\n\n\t\t// Subtle radial gradient in center\n\t\tconst grad = ctx.createRadialGradient(W / 2, H / 2, 0, W / 2, H / 2, 400);\n\t\tgrad.addColorStop(0, 'rgba(100, 0, 150, 0.06)');\n\t\tgrad.addColorStop(1, 'transparent');\n\t\tctx.fillStyle = grad;\n\t\tctx.fillRect(0, 0, W, H);\n\n\t\tdrawCenterLine();\n\t\tdrawTrail();\n\t\tdrawParticles();\n\t\tdrawPaddle(state.player, '#00f0ff');\n\t\tdrawPaddle(state.cpu, '#ff00aa');\n\t\tdrawBall();\n\t\tdrawScanlines();\n\t}\n\n\tfunction gameLoop() {\n\t\tif (!gameRunning) return;\n\t\tupdatePlayer();\n\t\tupdateCPU();\n\t\tupdateBall();\n\t\tupdateTrail();\n\t\tupdateParticles();\n\t\tdraw();\n\t\tanimId = requestAnimationFrame(gameLoop);\n\t}\n\n\tfunction startGame() {\n\t\tstate.player.score = 0;\n\t\tstate.cpu.score = 0;\n\t\tstate.player.y = H / 2 - PADDLE_H / 2;\n\t\tstate.cpu.y = H / 2 - PADDLE_H / 2;\n\t\tstate.particles = [];\n\t\tstate.trail = [];\n\t\tplayerScoreEl.textContent = '0';\n\t\tcpuScoreEl.textContent = '0';\n\t\tresetBall(Math.random() > 0.5 ? 1 : -1);\n\t\tensureAudio();\n\t\toverlay.classList.add('pong__overlay--hidden');\n\t\tgameRunning = true;\n\t\tgameLoop();\n\t}\n\n\tstartBtn.addEventListener('click', startGame);\n\n\t// Initial static draw\n\tdraw();\n})();"},{"name":"style.css","content":"@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Rajdhani:wght@300;500;700&display=swap');\n\n:root {\n\tcolor-scheme: dark;\n\t--neon-cyan: #00f0ff;\n\t--neon-magenta: #ff00aa;\n\t--neon-yellow: #ffe600;\n\t--bg-deep: #050510;\n\t--bg-surface: #0a0a20;\n}\n\n* {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tpadding: 0;\n}\n\nbody {\n\tbackground: var(--bg-deep);\n\tmin-height: 100vh;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tfont-family: 'Rajdhani', sans-serif;\n\toverflow: hidden;\n}\n\n.pong {\n\tposition: relative;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 0;\n\twidth: 100%;\n\tmax-width: 900px;\n\tpadding: 1rem;\n}\n\n.pong__header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\twidth: 100%;\n\tmax-width: 800px;\n\tpadding: 0.5rem 2rem 0.75rem;\n\tfont-family: 'Orbitron', sans-serif;\n}\n\n.pong__title {\n\tfont-size: 1rem;\n\tletter-spacing: 0.5em;\n\tcolor: #ffffff22;\n\tfont-weight: 400;\n\ttext-transform: uppercase;\n}\n\n.pong__score {\n\tfont-size: 3rem;\n\tfont-weight: 900;\n\tmin-width: 60px;\n\ttext-align: center;\n\tline-height: 1;\n}\n\n.pong__score--player {\n\tcolor: var(--neon-cyan);\n\ttext-shadow: 0 0 10px var(--neon-cyan), 0 0 30px var(--neon-cyan), 0 0 60px var(--neon-cyan);\n}\n\n.pong__score--cpu {\n\tcolor: var(--neon-magenta);\n\ttext-shadow: 0 0 10px var(--neon-magenta), 0 0 30px var(--neon-magenta), 0 0 60px var(--neon-magenta);\n}\n\n.pong__canvas {\n\tdisplay: block;\n\twidth: 100%;\n\tmax-width: 800px;\n\taspect-ratio: 4 / 3;\n\tborder: 1px solid #ffffff10;\n\tborder-radius: 8px;\n\tbackground: var(--bg-surface);\n\tcursor: none;\n}\n\n.pong__overlay {\n\tposition: absolute;\n\tinset: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tbackground: rgba(5, 5, 16, 0.85);\n\tbackdrop-filter: blur(10px);\n\tz-index: 10;\n\tborder-radius: 8px;\n\ttransition: opacity 0.4s, visibility 0.4s;\n}\n\n.pong__overlay--hidden {\n\topacity: 0;\n\tvisibility: hidden;\n\tpointer-events: none;\n}\n\n.pong__start-title {\n\tfont-family: 'Orbitron', sans-serif;\n\tfont-size: clamp(2.5rem, 7vw, 5rem);\n\tfont-weight: 900;\n\tletter-spacing: 0.08em;\n\tcolor: #fff;\n\ttext-shadow: 0 0 20px var(--neon-cyan), 0 0 60px var(--neon-cyan), 0 0 120px var(--neon-magenta);\n\tmargin-bottom: 0.5rem;\n}\n\n.pong__start-sub {\n\tfont-size: 1.1rem;\n\tcolor: #ffffff66;\n\tmargin-bottom: 2rem;\n\tletter-spacing: 0.05em;\n}\n\n.pong__btn {\n\tfont-family: 'Orbitron', sans-serif;\n\tfont-size: 1.1rem;\n\tfont-weight: 700;\n\tletter-spacing: 0.15em;\n\tpadding: 1rem 3rem;\n\tborder: 2px solid var(--neon-cyan);\n\tbackground: transparent;\n\tcolor: var(--neon-cyan);\n\tborder-radius: 4px;\n\tcursor: pointer;\n\ttransition: all 0.25s;\n\ttext-transform: uppercase;\n}\n\n.pong__btn:hover {\n\tbackground: var(--neon-cyan);\n\tcolor: var(--bg-deep);\n\tbox-shadow: 0 0 20px var(--neon-cyan), 0 0 60px var(--neon-cyan);\n}\n\n.pong__controls {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\twidth: 100%;\n\tmax-width: 800px;\n\tpadding: 0.75rem 1rem 0;\n\tfont-size: 0.8rem;\n\tcolor: #ffffff30;\n\tletter-spacing: 0.05em;\n}\n\n@media (max-width: 600px) {\n\t.pong__score {\n\t\tfont-size: 2rem;\n\t}\n\n\t.pong__controls {\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tgap: 0.25rem;\n\t}\n}"}],"folders":[]},"variants":null,"createdAt":"2026-02-05T20:37:46.257Z","updatedAt":"2026-02-05T20:38:00.037Z","hasThumbnail":true}}