{"project":{"id":"e7yOk6D","userId":"davidyarham@gmail.com","username":null,"userPicture":null,"name":"Fun Cube Runner","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<script type=\"importmap\">\n{\n  \"imports\": {\n    \"three\": \"https://unpkg.com/three@0.160.0/build/three.module.js\",\n    \"three/addons/\": \"https://unpkg.com/three@0.160.0/examples/jsm/\"\n  }\n}\n</script>\n</head>\n<body>\n<div class=\"game-shell\">\n\t<div class=\"hud\">\n\t\t<div class=\"hud__brand\">FUN CUBE RUNNER <span class=\"hud__brand-glow\">⚡</span></div>\n\t\t<div class=\"hud__stats\">\n\t\t\t<div class=\"hud__pill\">Score: <span id=\"score\">0</span></div>\n\t\t\t<div class=\"hud__pill\">Best: <span id=\"best\">0</span></div>\n\t\t\t<div class=\"hud__pill\">Speed: <span id=\"speed\">1.0x</span></div>\n\t\t\t<div class=\"hud__pill\">Combo: <span id=\"combo\">0</span></div>\n\t\t</div>\n\t\t<div class=\"meter\">\n\t\t\t<div class=\"meter__fill\" id=\"boostFill\"></div>\n\t\t</div>\n\t\t<div class=\"hud__hint\">Move: A / D / ← / → · Jump: Space / W / ↑ · Dash: Shift · Grab coins, chain combos, chase\n\t\t\tpower-ups, and survive the speed rush!</div>\n\t\t<div class=\"hud__hint\" id=\"statusText\">Ready to run.</div>\n\t\t<div class=\"hud__hint hud__hint--strong\" id=\"milestoneText\">Hit 50 points for a mini reward.</div>\n\t\t<div class=\"hud__hint\" id=\"pauseText\">Press P to pause</div>\n\t</div>\n\n\t<div id=\"game\"></div>\n\n\t<div class=\"fps-counter\" id=\"fpsCounter\">FPS 60</div>\n\n\t<div class=\"overlay\" id=\"overlay\">\n\t\t<div class=\"overlay__backdrop\"></div>\n\t\t<div class=\"overlay__card overlay__card--welcome\">\n\t\t\t<div class=\"overlay__spark overlay__spark--1\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"overlay__spark overlay__spark--2\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"overlay__spark overlay__spark--3\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"overlay__grid\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"overlay__eyebrow\">Neon arcade challenge</div>\n\t\t\t<h1>Fun Cube Runner</h1>\n\t\t\t<p class=\"overlay__lead\">Blast through neon lanes, scoop up coins, stack wild combos, and outrun the speed storm\n\t\t\t\tbefore the track eats you alive.</p>\n\t\t\t<div class=\"overlay__features\">\n\t\t\t\t<div class=\"overlay__feature\">\n\t\t\t\t\t<span>← →</span><strong>Snap between lanes</strong><small>Dodge danger fast</small>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"overlay__feature\">\n\t\t\t\t\t<span>Space</span><strong>Double jump</strong><small>Float over chaos</small>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"overlay__feature\">\n\t\t\t\t\t<span>Shift</span><strong>Dash burst</strong><small>Smash your speed meter</small>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"overlay__actions\">\n\t\t\t\t<button id=\"startBtn\" type=\"button\">Launch Run</button>\n\t\t\t\t<div class=\"overlay__subtext\">Keyboard, tap, and swipe ready • chase your best score</div>\n\t\t\t\t<div class=\"overlay__chips\" aria-hidden=\"true\">\n\t\t\t\t\t<span class=\"overlay__chip\">Coins</span>\n\t\t\t\t\t<span class=\"overlay__chip\">Combos</span>\n\t\t\t\t\t<span class=\"overlay__chip\">Boost</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\n\t<div class=\"overlay overlay--hidden\" id=\"gameOver\">\n\t\t<div class=\"overlay__backdrop overlay__backdrop--danger\"></div>\n\t\t<div class=\"overlay__card overlay__card--danger overlay__card--gameover\">\n\t\t\t<div class=\"overlay__spark overlay__spark--danger\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"overlay__eyebrow\">Run complete</div>\n\t\t\t<div class=\"overlay__burst\" aria-hidden=\"true\"></div>\n\t\t\t<h2>Oof. You crashed.</h2>\n\t\t\t<p class=\"overlay__lead\">Your final score was</p>\n\t\t\t<div class=\"overlay__score\" id=\"finalScoreWrap\"><strong id=\"finalScore\">0</strong></div>\n\t\t\t<div class=\"overlay__actions\">\n\t\t\t\t<button id=\"restartBtn\" type=\"button\">Play Again</button>\n\t\t\t\t<div class=\"overlay__subtext\">Try a cleaner lane switch or save boost for danger zones</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\n\t<div id=\"mobileControls\" class=\"mobile-controls\" aria-label=\"Mobile game controls\">\n\t\t<div class=\"mobile-controls__left\">\n\t\t\t<button id=\"btnLeft\" class=\"mobile-btn\" type=\"button\" aria-label=\"Move left\">◀</button>\n\t\t\t<button id=\"btnRight\" class=\"mobile-btn\" type=\"button\" aria-label=\"Move right\">▶</button>\n\t\t</div>\n\t\t<div class=\"mobile-controls__right\">\n\t\t\t<button id=\"btnJump\" class=\"mobile-btn mobile-btn--jump\" type=\"button\" aria-label=\"Jump\">Jump</button>\n\t\t\t<button id=\"btnDash\" class=\"mobile-btn mobile-btn--dash\" type=\"button\" aria-label=\"Dash\">Dash</button>\n\t\t</div>\n\t</div>\n</div>\n  <script type=\"module\" src=\"main.js\"></script>\n</body>\n</html>"},{"name":"main.js","content":"import * as THREE from 'three';\nimport {\n\tEffectComposer\n} from 'three/addons/postprocessing/EffectComposer.js';\nimport {\n\tRenderPass\n} from 'three/addons/postprocessing/RenderPass.js';\nimport {\n\tUnrealBloomPass\n} from 'three/addons/postprocessing/UnrealBloomPass.js';\n\nconst mount = document.getElementById('game');\nconst scoreEl = document.getElementById('score');\nconst bestEl = document.getElementById('best');\nconst speedEl = document.getElementById('speed');\nconst comboEl = document.getElementById('combo');\nconst boostFillEl = document.getElementById('boostFill');\nconst statusTextEl = document.getElementById('statusText');\nconst milestoneTextEl = document.getElementById('milestoneText');\nconst pauseTextEl = document.getElementById('pauseText');\nconst finalScoreEl = document.getElementById('finalScore');\nconst overlayEl = document.getElementById('overlay');\nconst gameOverEl = document.getElementById('gameOver');\nconst startBtn = document.getElementById('startBtn');\nconst restartBtn = document.getElementById('restartBtn');\nconst mobileControls = document.getElementById('mobileControls');\nconst btnLeft = document.getElementById('btnLeft');\nconst btnRight = document.getElementById('btnRight');\nconst btnJump = document.getElementById('btnJump');\nconst btnDash = document.getElementById('btnDash');\nconst fpsEl = document.getElementById('fpsCounter');\n\nconst safeText = (el, value) => {\n\tif (el) el.textContent = value;\n};\n\nconst isCoarsePointer = window.matchMedia('(hover: none), (pointer: coarse)').matches;\nconst isMobile = /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent) || isCoarsePointer;\nconst isLowPowerDevice = isCoarsePointer || (navigator.hardwareConcurrency || 4) <= 4;\nlet adaptivePixelRatio = Math.min(window.devicePixelRatio || 1, isLowPowerDevice ? 0.8 : 1);\n\nlet audioCtx;\nlet touchStartX = 0;\nlet touchStartY = 0;\nlet touchMoved = false;\nlet fpsFrames = 0;\nlet fpsTime = 0;\nlet fpsDisplay = 60;\nlet startedOnce = false;\n\nfunction ensureAudio() {\n\tif (!audioCtx) {\n\t\tconst Ctx = window.AudioContext || window.webkitAudioContext;\n\t\tif (!Ctx) return;\n\t\taudioCtx = new Ctx();\n\t}\n\tif (audioCtx.state === 'suspended') audioCtx.resume();\n}\n\nfunction playTone({\n\tfreq = 440,\n\tduration = 0.12,\n\ttype = 'sine',\n\tvolume = 0.05,\n\tslide = 0,\n\tdelay = 0\n}) {\n\tif (!audioCtx) return;\n\tconst start = audioCtx.currentTime + delay;\n\tconst osc = audioCtx.createOscillator();\n\tconst gain = audioCtx.createGain();\n\tosc.type = type;\n\tosc.frequency.setValueAtTime(freq, start);\n\tif (slide) osc.frequency.exponentialRampToValueAtTime(Math.max(40, freq + slide), start + duration);\n\tgain.gain.setValueAtTime(0.0001, start);\n\tgain.gain.exponentialRampToValueAtTime(volume, start + 0.01);\n\tgain.gain.exponentialRampToValueAtTime(0.0001, start + duration);\n\tosc.connect(gain);\n\tgain.connect(audioCtx.destination);\n\tosc.start(start);\n\tosc.stop(start + duration + 0.02);\n}\n\nfunction soundJump() {\n\tplayTone({\n\t\tfreq: 280,\n\t\tduration: 0.12,\n\t\ttype: 'square',\n\t\tvolume: 0.045,\n\t\tslide: 220\n\t});\n\tplayTone({\n\t\tfreq: 420,\n\t\tduration: 0.08,\n\t\ttype: 'triangle',\n\t\tvolume: 0.025,\n\t\tdelay: 0.02,\n\t\tslide: 80\n\t});\n}\n\nfunction soundCoin() {\n\tplayTone({\n\t\tfreq: 880,\n\t\tduration: 0.08,\n\t\ttype: 'triangle',\n\t\tvolume: 0.05,\n\t\tslide: 220\n\t});\n\tplayTone({\n\t\tfreq: 1320,\n\t\tduration: 0.12,\n\t\ttype: 'sine',\n\t\tvolume: 0.04,\n\t\tdelay: 0.05,\n\t\tslide: 120\n\t});\n}\n\nfunction soundCrash() {\n\tplayTone({\n\t\tfreq: 180,\n\t\tduration: 0.22,\n\t\ttype: 'sawtooth',\n\t\tvolume: 0.06,\n\t\tslide: -120\n\t});\n\tplayTone({\n\t\tfreq: 90,\n\t\tduration: 0.28,\n\t\ttype: 'square',\n\t\tvolume: 0.04,\n\t\tdelay: 0.03,\n\t\tslide: -40\n\t});\n}\n\nfunction soundDash() {\n\tplayTone({\n\t\tfreq: 210,\n\t\tduration: 0.09,\n\t\ttype: 'sawtooth',\n\t\tvolume: 0.05,\n\t\tslide: 500\n\t});\n\tplayTone({\n\t\tfreq: 420,\n\t\tduration: 0.08,\n\t\ttype: 'square',\n\t\tvolume: 0.03,\n\t\tdelay: 0.02,\n\t\tslide: 260\n\t});\n}\n\nfunction soundShield() {\n\tplayTone({\n\t\tfreq: 500,\n\t\tduration: 0.12,\n\t\ttype: 'sine',\n\t\tvolume: 0.04,\n\t\tslide: -40\n\t});\n\tplayTone({\n\t\tfreq: 760,\n\t\tduration: 0.15,\n\t\ttype: 'triangle',\n\t\tvolume: 0.03,\n\t\tdelay: 0.03,\n\t\tslide: -30\n\t});\n}\n\nfunction soundMega() {\n\tplayTone({\n\t\tfreq: 520,\n\t\tduration: 0.08,\n\t\ttype: 'square',\n\t\tvolume: 0.03,\n\t\tslide: 280\n\t});\n\tplayTone({\n\t\tfreq: 880,\n\t\tduration: 0.12,\n\t\ttype: 'triangle',\n\t\tvolume: 0.04,\n\t\tdelay: 0.05,\n\t\tslide: 260\n\t});\n}\n\nfunction soundStart() {\n\tplayTone({\n\t\tfreq: 520,\n\t\tduration: 0.08,\n\t\ttype: 'triangle',\n\t\tvolume: 0.035,\n\t\tslide: 80\n\t});\n\tplayTone({\n\t\tfreq: 780,\n\t\tduration: 0.1,\n\t\ttype: 'triangle',\n\t\tvolume: 0.035,\n\t\tdelay: 0.06,\n\t\tslide: 100\n\t});\n}\n\nfunction soundPower() {\n\tplayTone({\n\t\tfreq: 640,\n\t\tduration: 0.08,\n\t\ttype: 'square',\n\t\tvolume: 0.04,\n\t\tslide: 220\n\t});\n\tplayTone({\n\t\tfreq: 980,\n\t\tduration: 0.12,\n\t\ttype: 'triangle',\n\t\tvolume: 0.04,\n\t\tdelay: 0.04,\n\t\tslide: 180\n\t});\n}\n\nlet best = Number(localStorage.getItem('fun-cube-runner-best') || 0);\nsafeText(bestEl, best);\n\nconst scene = new THREE.Scene();\nscene.fog = new THREE.Fog(0x0b1020, 16, 76);\nscene.background = new THREE.Color(0x090e1a);\n\nconst camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 200);\ncamera.position.set(0, 6.2, 10.5);\ncamera.lookAt(0, 1, 0);\n\nconst renderer = new THREE.WebGLRenderer({\n\tantialias: !isLowPowerDevice,\n\talpha: true,\n\tpowerPreference: 'high-performance'\n});\nrenderer.setPixelRatio(adaptivePixelRatio);\nrenderer.setSize(window.innerWidth, window.innerHeight);\nrenderer.shadowMap.enabled = !isLowPowerDevice;\nrenderer.shadowMap.type = THREE.PCFSoftShadowMap;\nrenderer.outputColorSpace = THREE.SRGBColorSpace;\nrenderer.toneMapping = THREE.ACESFilmicToneMapping;\nrenderer.toneMappingExposure = 1.38;\nrenderer.physicallyCorrectLights = true;\nif (mount) mount.appendChild(renderer.domElement);\n\nconst usePostFX = !isLowPowerDevice;\nconst composer = usePostFX ? new EffectComposer(renderer) : null;\nconst renderPass = new RenderPass(scene, camera);\nif (composer) composer.addPass(renderPass);\nconst bloomPass = composer ? new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.38, 0.32, 0.4) : null;\nif (bloomPass) {\n\tbloomPass.threshold = 0.12;\n\tbloomPass.strength = 0.34;\n\tbloomPass.radius = 0.28;\n\tcomposer.addPass(bloomPass);\n}\n\nscene.add(new THREE.AmbientLight(0xbdd3ff, 2.05));\n\nconst dirLight = new THREE.DirectionalLight(0xfff2d6, 2.2);\ndirLight.position.set(6, 12, 8);\ndirLight.castShadow = !isLowPowerDevice;\ndirLight.shadow.mapSize.width = 1024;\ndirLight.shadow.mapSize.height = 1024;\ndirLight.shadow.camera.left = -20;\ndirLight.shadow.camera.right = 20;\ndirLight.shadow.camera.top = 20;\ndirLight.shadow.camera.bottom = -20;\nscene.add(dirLight);\n\nconst hemi = new THREE.HemisphereLight(0x6dd5ff, 0x1b2238, 1.45);\nscene.add(hemi);\n\nconst rimLight = new THREE.PointLight(0x7c5cff, 22, 34, 2);\nrimLight.position.set(0, 6, 8);\nscene.add(rimLight);\n\nconst glowLight = new THREE.PointLight(0x37e7ff, 14, 30, 2);\nconst accentLight = new THREE.PointLight(0xff4fd8, 10, 28, 2);\nconst energyBeams = [];\nglowLight.position.set(0, 3, -10);\nscene.add(glowLight);\naccentLight.position.set(0, 4, -24);\nscene.add(accentLight);\n\nfor (let i = 0; i < (isLowPowerDevice ? 1 : 4); i++) {\n\tconst beam = new THREE.Mesh(\n\t\tnew THREE.CylinderGeometry(0.18, 1.1, 22, 12, 1, true),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: i % 2 === 0 ? 0x37e7ff : 0xff4fd8,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.11,\n\t\t\tside: THREE.DoubleSide,\n\t\t\tdepthWrite: false\n\t\t})\n\t);\n\tbeam.position.set(i < 3 ? -8 - i * 4 : 8 + (i - 3) * 4, 8, -32 - i * 10);\n\tbeam.userData.phase = i * 0.9;\n\tenergyBeams.push(beam);\n\tscene.add(beam);\n}\n\nconst skyDome = new THREE.Mesh(\n\tnew THREE.SphereGeometry(90, 32, 32),\n\tnew THREE.MeshBasicMaterial({\n\t\tcolor: 0x142041,\n\t\tside: THREE.BackSide,\n\t\ttransparent: true,\n\t\topacity: 1\n\t})\n);\nscene.add(skyDome);\n\nconst horizonGlow = new THREE.Mesh(\n\tnew THREE.CylinderGeometry(18, 30, 20, 40, 1, true),\n\tnew THREE.MeshBasicMaterial({\n\t\tcolor: 0x16356e,\n\t\ttransparent: true,\n\t\topacity: 0.22,\n\t\tside: THREE.DoubleSide\n\t})\n);\nhorizonGlow.position.set(0, 7, -42);\nscene.add(horizonGlow);\n\nconst sunGlow = new THREE.Mesh(\n\tnew THREE.CircleGeometry(8, 40),\n\tnew THREE.MeshBasicMaterial({\n\t\tcolor: 0xff6aa8,\n\t\ttransparent: true,\n\t\topacity: 0.24\n\t})\n);\nsunGlow.position.set(0, 12, -70);\nscene.add(sunGlow);\n\nconst auroraBands = [];\nfor (let i = 0; i < 2; i++) {\n\tconst aurora = new THREE.Mesh(\n\t\tnew THREE.PlaneGeometry(70, 18),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: i === 1 ? 0xff4fd8 : 0x37e7ff,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.08,\n\t\t\tblending: THREE.AdditiveBlending,\n\t\t\tside: THREE.DoubleSide,\n\t\t\tdepthWrite: false\n\t\t})\n\t);\n\taurora.position.set(0, 14 + i * 3, -52 - i * 6);\n\taurora.rotation.x = -0.24;\n\taurora.userData.phase = i * 1.7;\n\tauroraBands.push(aurora);\n\tscene.add(aurora);\n}\n\nconst laneX = [-3.2, 0, 3.2];\nconst obstacles = [];\nconst coins = [];\nconst particles = [];\nconst stars = [];\nconst fogParticles = [];\nconst skyline = [];\nconst billboards = [];\nconst lanePulseLights = [];\nconst powerUps = [];\nconst trails = [];\nconst laneRings = [];\nconst railLights = [];\nlet shake = 0;\nconst clock = new THREE.Clock();\n\nlet lastObstacleLane = 1;\nlet safeLaneBias = 1;\n\nconst world = {\n\trunning: false,\n\tended: false,\n\tscore: 0,\n\tspeed: 14,\n\tspawnTimer: 0,\n\tcoinTimer: 0,\n\tdistance: 0,\n\tcombo: 0,\n\tcomboTimer: 0,\n\tshield: 0,\n\tshieldFlash: 0,\n\thitFlash: 0,\n\tmagnet: 0,\n\tboost: 0,\n\tdash: 0,\n\tpowerTimer: 0,\n\tjumpsLeft: 2,\n\tboostMeter: 0,\n\tlastGroundHit: 0,\n\tnextMilestone: 50,\n\tdangerPulse: 0,\n\tpaused: false\n};\n\nconst groundGroup = new THREE.Group();\nconst laneGlowBars = [];\nscene.add(groundGroup);\n\nfunction spawnPowerUp() {\n\tconst lane = Math.random() > 0.5 ? safeLaneBias : Math.floor(Math.random() * 3);\n\tconst types = ['shield', 'magnet', 'boost'];\n\tconst type = types[Math.floor(Math.random() * types.length)];\n\tconst colorMap = {\n\t\tshield: 0x65f0ff,\n\t\tmagnet: 0xff66cc,\n\t\tboost: 0x7cff6b\n\t};\n\tconst yMap = {\n\t\tshield: 2.1,\n\t\tmagnet: 2.9,\n\t\tboost: 3.7\n\t};\n\tconst geo = type === 'shield' ?\n\t\tnew THREE.OctahedronGeometry(0.7, 0) :\n\t\ttype === 'magnet' ?\n\t\tnew THREE.TorusKnotGeometry(0.42, 0.14, 24, 6) :\n\t\tnew THREE.ConeGeometry(0.6, 1.3, 5);\n\tconst mesh = new THREE.Mesh(\n\t\tgeo,\n\t\tnew THREE.MeshStandardMaterial({\n\t\t\tcolor: colorMap[type],\n\t\t\temissive: colorMap[type],\n\t\t\temissiveIntensity: 0.6,\n\t\t\tmetalness: 0.4,\n\t\t\troughness: 0.3\n\t\t})\n\t);\n\tmesh.castShadow = !isLowPowerDevice;\n\tconst edgeFrame = new THREE.LineSegments(\n\t\tnew THREE.EdgesGeometry(new THREE.BoxGeometry(1.9, 1.9, 1.9)),\n\t\tnew THREE.LineBasicMaterial({\n\t\t\tcolor: 0xffb4c5,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.55\n\t\t})\n\t);\n\tmesh.add(edgeFrame);\n\tconst aura = new THREE.Mesh(\n\t\tnew THREE.TorusGeometry(0.95, 0.06, 10, 24),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: colorMap[type],\n\t\t\ttransparent: true,\n\t\t\topacity: 0.52\n\t\t})\n\t);\n\taura.rotation.x = Math.PI / 2;\n\tmesh.add(aura);\n\tconst core = new THREE.Mesh(\n\t\tnew THREE.SphereGeometry(0.18, 10, 10),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: 0xffffff,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.9\n\t\t})\n\t);\n\tmesh.add(core);\n\tconst powerShadow = new THREE.Mesh(\n\t\tnew THREE.CircleGeometry(0.95, 24),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: colorMap[type],\n\t\t\ttransparent: true,\n\t\t\topacity: 0.2\n\t\t})\n\t);\n\tpowerShadow.rotation.x = -Math.PI / 2;\n\tpowerShadow.position.y = -yMap[type] + 0.04;\n\tmesh.add(powerShadow);\n\tmesh.position.set(laneX[lane], yMap[type], -58);\n\tmesh.userData = {\n\t\tlane,\n\t\ttype: 'power' + type[0].toUpperCase() + type.slice(1),\n\t\tkind: type\n\t};\n\tpowerUps.push(mesh);\n\tscene.add(mesh);\n}\n\nfunction updateHud() {\n\tsafeText(comboEl, world.combo);\n\tif (boostFillEl) boostFillEl.style.width = `${Math.min(100, world.boostMeter)}%`;\n\tsafeText(milestoneTextEl, world.score >= world.nextMilestone ? 'Reward ready! Keep moving!' : `Hit ${world.nextMilestone} points for a mini reward.`);\n}\n\nfunction spawnTrail() {\n\tconst trail = new THREE.Mesh(\n\t\tnew THREE.BoxGeometry(0.9, 0.9, 0.9),\n\t\tnew THREE.MeshStandardMaterial({\n\t\t\tcolor: 0x7c5cff,\n\t\t\temissive: 0x2d1d8b,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.4\n\t\t})\n\t);\n\ttrail.position.copy(player.position);\n\ttrail.userData.life = 0.24;\n\ttrails.push(trail);\n\tscene.add(trail);\n}\n\nfunction updateStatusText() {\n\tconst bits = [];\n\tif (world.combo > 1) bits.push(`Combo x${world.combo}`);\n\tif (world.shield > 0) bits.push(`Shield ${Math.ceil(world.shield)}s`);\n\tif (world.magnet > 0) bits.push('Magnet on');\n\tif (world.boost > 0) bits.push('Boost on');\n\tif (world.dash > 0) bits.push('Dashing');\n\tsafeText(statusTextEl, bits.length ? bits.join(' · ') : 'Pink/red blocks are bad. Gold and bright pickups are good.');\n\tupdateHud();\n}\n\nfunction activatePower(kind) {\n\tif (kind === 'shield') {\n\t\tworld.shield = 8;\n\t\tsoundShield();\n\t}\n\tif (kind === 'magnet') {\n\t\tworld.magnet = 8;\n\t\tsoundPower();\n\t}\n\tif (kind === 'boost') {\n\t\tworld.boost = 5;\n\t\tsoundPower();\n\t\tshake = Math.max(shake, 0.18);\n\t}\n\tupdateStatusText();\n}\n\nfunction dash() {\n\tif (!world.running || world.dash > 0) return;\n\tif (world.boostMeter < 35) return;\n\tworld.boostMeter -= 35;\n\tworld.dash = 0.34;\n\tshake = Math.max(shake, 0.16);\n\tsoundDash();\n\tupdateHud();\n\tupdateStatusText();\n}\n\nconst groundMat = new THREE.MeshStandardMaterial({\n\tcolor: 0x1e2854,\n\troughness: 0.34,\n\tmetalness: 0.34,\n\temissive: 0x11193d,\n\temissiveIntensity: 1.05\n});\n\nfor (let i = 0; i < 10; i++) {\n\tconst tile = new THREE.Mesh(new THREE.BoxGeometry(12, 1, 12), groundMat);\n\tconst tileGlow = new THREE.Mesh(\n\t\tnew THREE.PlaneGeometry(11.2, 11.2),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: i % 2 === 0 ? 0x2f76ff : 0x8a4dff,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.09,\n\t\t\tblending: THREE.AdditiveBlending,\n\t\t\tdepthWrite: false\n\t\t})\n\t);\n\ttileGlow.rotation.x = -Math.PI / 2;\n\ttileGlow.position.y = 0.02;\n\ttileGlow.userData.isTileGlow = true;\n\ttile.add(tileGlow);\n\ttile.position.set(0, -0.5, -i * 12);\n\ttile.receiveShadow = !isLowPowerDevice;\n\tgroundGroup.add(tile);\n\n\tconst wallMat = new THREE.MeshStandardMaterial({\n\t\tcolor: 0x202a52,\n\t\temissive: 0x0d1435,\n\t\temissiveIntensity: 0.6,\n\t\tmetalness: 0.2,\n\t\troughness: 0.45\n\t});\n\tconst wallL = new THREE.Mesh(new THREE.BoxGeometry(0.6, 2.2, 12), wallMat);\n\tconst wallR = wallL.clone();\n\twallL.position.set(-6.3, 0.6, -i * 12);\n\twallR.position.set(6.3, 0.6, -i * 12);\n\tgroundGroup.add(wallL, wallR);\n\n\tconst railGeo = new THREE.BoxGeometry(0.12, 0.12, 12);\n\tconst railMatL = new THREE.MeshBasicMaterial({\n\t\tcolor: 0x4ddcff,\n\t\ttransparent: true,\n\t\topacity: 0.75\n\t});\n\tconst railMatR = new THREE.MeshBasicMaterial({\n\t\tcolor: 0xa56bff,\n\t\ttransparent: true,\n\t\topacity: 0.75\n\t});\n\tconst railL = new THREE.Mesh(railGeo, railMatL);\n\tconst railR = new THREE.Mesh(railGeo, railMatR);\n\trailL.position.set(-5.9, 1.28, -i * 12);\n\trailR.position.set(5.9, 1.28, -i * 12);\n\trailL.userData.isRail = true;\n\trailR.userData.isRail = true;\n\trailLights.push(railL, railR);\n\tgroundGroup.add(railL, railR);\n\n\tfor (let p = 0; p < 3; p++) {\n\t\tconst pulse = new THREE.PointLight(p % 2 === 0 ? 0x52e0ff : 0xff4fd8, 1.8, 9, 2);\n\t\tpulse.position.set(p % 2 === 0 ? -5.7 : 5.7, 1.1, -i * 12 + p * 4 - 4);\n\t\tpulse.userData.phase = i * 0.6 + p;\n\t\tlanePulseLights.push(pulse);\n\t\tgroundGroup.add(pulse);\n\t}\n}\n\nfor (let i = 0; i < 10; i++) {\n\tconst bar = new THREE.Mesh(\n\t\tnew THREE.BoxGeometry(0.14, 0.03, 3.8),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: i % 2 === 0 ? 0x52e0ff : 0xff4fd8,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.32\n\t\t})\n\t);\n\tbar.position.set(i % 2 === 0 ? -1.6 : 1.6, 0.05, -i * 8);\n\tbar.userData.scroll = true;\n\tlaneGlowBars.push(bar);\n\tgroundGroup.add(bar);\n}\n\nconst laneStripeMat = new THREE.MeshStandardMaterial({\n\tcolor: 0x7ef0ff,\n\temissive: 0x2bd9ff,\n\temissiveIntensity: 1.55,\n\tmetalness: 0.35,\n\troughness: 0.18\n});\nfor (let i = 0; i < (isLowPowerDevice ? 5 : 12); i++) {\n\tlaneX.forEach((x) => {\n\t\tconst stripe = new THREE.Mesh(new THREE.BoxGeometry(0.16, 0.04, 1.3), laneStripeMat);\n\t\tstripe.position.set(x, 0.03, -i * 6);\n\t\tstripe.userData.isStripe = true;\n\t\tgroundGroup.add(stripe);\n\n\t\tif (i < 8) {\n\t\t\tconst ring = new THREE.Mesh(\n\t\t\t\tnew THREE.TorusGeometry(0.9, 0.05, 8, 24),\n\t\t\t\tnew THREE.MeshBasicMaterial({\n\t\t\t\t\tcolor: 0x4be7ff,\n\t\t\t\t\ttransparent: true,\n\t\t\t\t\topacity: 0.38\n\t\t\t\t})\n\t\t\t);\n\t\t\tring.rotation.x = Math.PI / 2;\n\t\t\tring.position.set(x, 0.06, -i * 15);\n\t\t\tring.userData.isRing = true;\n\t\t\tlaneRings.push(ring);\n\t\t\tgroundGroup.add(ring);\n\t\t}\n\t});\n}\n\nconst player = new THREE.Group();\nscene.add(player);\n\nconst body = new THREE.Mesh(\n\tnew THREE.BoxGeometry(1.3, 1.3, 1.3),\n\tnew THREE.MeshStandardMaterial({\n\t\tcolor: 0xff6f9d,\n\t\troughness: 0.22,\n\t\tmetalness: 0.24,\n\t\temissive: 0x3a0c1d,\n\t\temissiveIntensity: 0.45\n\t})\n);\nbody.castShadow = !isLowPowerDevice;\nplayer.add(body);\n\nconst face = new THREE.Mesh(\n\tnew THREE.BoxGeometry(0.86, 0.56, 0.08),\n\tnew THREE.MeshStandardMaterial({\n\t\tcolor: 0xffe7ef,\n\t\temissive: 0x5a1830,\n\t\temissiveIntensity: 0.65,\n\t\tmetalness: 0.08,\n\t\troughness: 0.35\n\t})\n);\nface.position.set(0, 0.08, 0.69);\nplayer.add(face);\n\nconst eyeGeo = new THREE.BoxGeometry(0.12, 0.12, 0.06);\nconst eyeMat = new THREE.MeshStandardMaterial({\n\tcolor: 0x111111\n});\nconst eyeL = new THREE.Mesh(eyeGeo, eyeMat);\nconst eyeR = new THREE.Mesh(eyeGeo, eyeMat);\neyeL.position.set(-0.2, 0.12, 0.75);\neyeR.position.set(0.2, 0.12, 0.75);\nplayer.add(eyeL, eyeR);\n\nconst playerCore = new THREE.Mesh(\n\tnew THREE.OctahedronGeometry(0.28, 0),\n\tnew THREE.MeshBasicMaterial({\n\t\tcolor: 0xffffff,\n\t\ttransparent: true,\n\t\topacity: 0.85\n\t})\n);\nplayerCore.position.set(0, 0.05, 0);\nplayer.add(playerCore);\n\nconst playerAura = new THREE.Mesh(\n\tnew THREE.TorusGeometry(0.95, 0.08, 12, 32),\n\tnew THREE.MeshBasicMaterial({\n\t\tcolor: 0xff7fb0,\n\t\ttransparent: true,\n\t\topacity: 0.4\n\t})\n);\nplayerAura.rotation.x = Math.PI / 2;\nplayerAura.position.y = -0.45;\nplayer.add(playerAura);\n\nconst playerShadowGlow = new THREE.Mesh(\n\tnew THREE.CircleGeometry(1.2, 28),\n\tnew THREE.MeshBasicMaterial({\n\t\tcolor: 0xff4d8d,\n\t\ttransparent: true,\n\t\topacity: 0.24\n\t})\n);\nplayerShadowGlow.rotation.x = -Math.PI / 2;\nplayerShadowGlow.position.y = -0.64;\nplayer.add(playerShadowGlow);\n\nconst crownGlow = new THREE.Mesh(\n\tnew THREE.TorusGeometry(0.42, 0.04, 10, 24),\n\tnew THREE.MeshBasicMaterial({\n\t\tcolor: 0xffffff,\n\t\ttransparent: true,\n\t\topacity: 0.5\n\t})\n);\ncrownGlow.position.y = 0.95;\ncrownGlow.rotation.x = Math.PI / 2;\nplayer.add(crownGlow);\n\nconst speedWingGeo = new THREE.BoxGeometry(0.08, 0.7, 1.2);\nconst speedWingMat = new THREE.MeshBasicMaterial({\n\tcolor: 0x72e8ff,\n\ttransparent: true,\n\topacity: 0.18,\n\tblending: THREE.AdditiveBlending,\n\tdepthWrite: false\n});\nconst wingL = new THREE.Mesh(speedWingGeo, speedWingMat);\nconst wingR = new THREE.Mesh(speedWingGeo, speedWingMat.clone());\nwingL.position.set(-0.78, 0.05, -0.15);\nwingR.position.set(0.78, 0.05, -0.15);\nwingL.rotation.z = 0.35;\nwingR.rotation.z = -0.35;\nplayer.add(wingL, wingR);\n\nplayer.position.set(0, 1, 4.5);\nplayer.userData = {\n\tlane: 1,\n\ttargetX: 0,\n\tvelocityY: 0,\n\tjumping: false\n};\n\nfunction makeStarfield() {\n\tconst geo = new THREE.SphereGeometry(0.08, 8, 8);\n\tconst mat = new THREE.MeshBasicMaterial({\n\t\tcolor: 0xdff6ff\n\t});\n\tfor (let i = 0; i < (isLowPowerDevice ? 18 : 48); i++) {\n\t\tconst star = new THREE.Mesh(geo, mat);\n\t\tresetStar(star, true);\n\t\tstars.push(star);\n\t\tscene.add(star);\n\t}\n}\n\nfunction resetStar(star, initial = false) {\n\tstar.position.set(\n\t\tTHREE.MathUtils.randFloatSpread(55),\n\t\tTHREE.MathUtils.randFloat(6, 26),\n\t\tinitial ? THREE.MathUtils.randFloat(-80, 10) : THREE.MathUtils.randFloat(-80, -40)\n\t);\n}\nmakeStarfield();\n\nfunction makeSkyline() {\n\tfor (let i = 0; i < (isLowPowerDevice ? 6 : 14); i++) {\n\t\tconst height = THREE.MathUtils.randFloat(6, 20);\n\t\tconst tower = new THREE.Mesh(\n\t\t\tnew THREE.BoxGeometry(1.8, height, 1.8),\n\t\t\tnew THREE.MeshStandardMaterial({\n\t\t\t\tcolor: 0x1a244a,\n\t\t\t\temissive: 0x0d1631,\n\t\t\t\temissiveIntensity: 0.9,\n\t\t\t\tmetalness: 0.35,\n\t\t\t\troughness: 0.55,\n\t\t\t\ttransparent: true,\n\t\t\t\topacity: 0.96\n\t\t\t})\n\t\t);\n\t\tconst side = i % 2 === 0 ? -1 : 1;\n\t\ttower.position.set(side * THREE.MathUtils.randFloat(11, 22), height * 0.5 - 1, THREE.MathUtils.randFloat(-95, -18));\n\t\ttower.userData.baseY = tower.position.y;\n\t\ttower.userData.speed = THREE.MathUtils.randFloat(0.4, 1.2);\n\t\tskyline.push(tower);\n\t\tscene.add(tower);\n\n\t\tfor (let w = 0; w < Math.floor(height * 0.9); w++) {\n\t\t\tif (Math.random() > 0.45) continue;\n\t\t\tconst windowStrip = new THREE.Mesh(\n\t\t\t\tnew THREE.PlaneGeometry(0.18, 0.32),\n\t\t\t\tnew THREE.MeshBasicMaterial({\n\t\t\t\t\tcolor: Math.random() > 0.5 ? 0x63d8ff : 0xb48cff,\n\t\t\t\t\ttransparent: true,\n\t\t\t\t\topacity: 0.56 + Math.random() * 0.22\n\t\t\t\t})\n\t\t\t);\n\t\t\twindowStrip.position.set(0, -height * 0.35 + w * 0.7, 0.92);\n\t\t\ttower.add(windowStrip);\n\t\t}\n\n\t\tconst sign = new THREE.Mesh(\n\t\t\tnew THREE.PlaneGeometry(1.2, 0.34),\n\t\t\tnew THREE.MeshBasicMaterial({\n\t\t\t\tcolor: side === -1 ? 0x63d8ff : 0xff73d4,\n\t\t\t\ttransparent: true,\n\t\t\t\topacity: 0.85,\n\t\t\t\tblending: THREE.AdditiveBlending,\n\t\t\t\tside: THREE.DoubleSide\n\t\t\t})\n\t\t);\n\t\tsign.position.set(0, height * 0.28, 0.93);\n\t\ttower.add(sign);\n\t\tbillboards.push(sign);\n\n\t\tconst beam = new THREE.Mesh(\n\t\t\tnew THREE.PlaneGeometry(0.24, height * 0.72),\n\t\t\tnew THREE.MeshBasicMaterial({\n\t\t\t\tcolor: side === -1 ? 0x53c7ff : 0xa56bff,\n\t\t\t\ttransparent: true,\n\t\t\t\topacity: 0.36,\n\t\t\t\tside: THREE.DoubleSide\n\t\t\t})\n\t\t);\n\t\tbeam.position.set(0, 0, 0.92);\n\t\ttower.add(beam);\n\t}\n}\nmakeSkyline();\n\nfunction makeFogParticles() {\n\tconst geo = new THREE.PlaneGeometry(2.8, 2.8);\n\tfor (let i = 0; i < 12; i++) {\n\t\tconst puff = new THREE.Mesh(\n\t\t\tgeo,\n\t\t\tnew THREE.MeshBasicMaterial({\n\t\t\t\tcolor: i % 2 === 0 ? 0x52c7ff : 0xb26cff,\n\t\t\t\ttransparent: true,\n\t\t\t\topacity: 0.12,\n\t\t\t\tside: THREE.DoubleSide,\n\t\t\t\tdepthWrite: false\n\t\t\t})\n\t\t);\n\t\tpuff.position.set(\n\t\t\tTHREE.MathUtils.randFloatSpread(26),\n\t\t\tTHREE.MathUtils.randFloat(1.5, 10),\n\t\t\tTHREE.MathUtils.randFloat(-80, -10)\n\t\t);\n\t\tpuff.rotation.z = Math.random() * Math.PI;\n\t\tpuff.userData.speed = THREE.MathUtils.randFloat(0.6, 1.4);\n\t\tfogParticles.push(puff);\n\t\tscene.add(puff);\n\t}\n}\nmakeFogParticles();\n\nfunction spawnObstacle() {\n\tlet lane = Math.floor(Math.random() * 3);\n\tif (Math.random() > 0.45) lane = safeLaneBias === lane ? (lane + 1 + Math.floor(Math.random() * 2)) % 3 : lane;\n\tif (lane === lastObstacleLane && Math.random() > 0.35) lane = (lane + 1 + Math.floor(Math.random() * 2)) % 3;\n\tlastObstacleLane = lane;\n\tconst h = THREE.MathUtils.randFloat(1.4, 3.6);\n\tconst mesh = new THREE.Mesh(\n\t\tnew THREE.BoxGeometry(1.8, h, 1.8),\n\t\tnew THREE.MeshStandardMaterial({\n\t\t\tcolor: 0xff4d6d,\n\t\t\troughness: 0.24,\n\t\t\tmetalness: 0.35,\n\t\t\temissive: 0x4a1020,\n\t\t\temissiveIntensity: 0.9\n\t\t})\n\t);\n\tmesh.castShadow = !isLowPowerDevice;\n\tconst warningRing = new THREE.Mesh(\n\t\tnew THREE.TorusGeometry(1.05, 0.08, 10, 26),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: 0xff9db2,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.5\n\t\t})\n\t);\n\twarningRing.rotation.x = Math.PI / 2;\n\twarningRing.position.y = -h * 0.5 + 0.08;\n\tmesh.add(warningRing);\n\tconst warningBeam = new THREE.Mesh(\n\t\tnew THREE.CylinderGeometry(0.18, 0.45, h + 1.8, 10, 1, true),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: 0xff6f91,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.12,\n\t\t\tside: THREE.DoubleSide\n\t\t})\n\t);\n\twarningBeam.position.y = 0.65;\n\tmesh.add(warningBeam);\n\tconst cap = new THREE.Mesh(\n\t\tnew THREE.BoxGeometry(1.15, 0.2, 1.15),\n\t\tnew THREE.MeshStandardMaterial({\n\t\t\tcolor: 0xffffff,\n\t\t\temissive: 0xffffff,\n\t\t\temissiveIntensity: 0.9\n\t\t})\n\t);\n\tcap.position.y = h * 0.5 + 0.18;\n\tmesh.add(cap);\n\tconst dangerShadow = new THREE.Mesh(\n\t\tnew THREE.CircleGeometry(1.1, 24),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: 0xff4d6d,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.18\n\t\t})\n\t);\n\tdangerShadow.rotation.x = -Math.PI / 2;\n\tdangerShadow.position.y = -h * 0.5 + 0.03;\n\tmesh.add(dangerShadow);\n\tmesh.position.set(laneX[lane], h / 2, -55);\n\tmesh.userData = {\n\t\tlane,\n\t\ttype: 'obstacle',\n\t\th\n\t};\n\tobstacles.push(mesh);\n\tscene.add(mesh);\n}\n\nfunction spawnCoin() {\n\tconst lane = Math.random() > 0.55 ? safeLaneBias : Math.floor(Math.random() * 3);\n\tconst coin = new THREE.Mesh(\n\t\tnew THREE.TorusGeometry(0.52, 0.18, 10, 18),\n\t\tnew THREE.MeshStandardMaterial({\n\t\t\tcolor: 0xffe27a,\n\t\t\temissive: 0xc88b00,\n\t\t\temissiveIntensity: 1,\n\t\t\tmetalness: 0.92,\n\t\t\troughness: 0.14\n\t\t})\n\t);\n\tcoin.rotation.y = Math.PI / 2;\n\tconst coinInner = new THREE.Mesh(\n\t\tnew THREE.CylinderGeometry(0.22, 0.22, 0.08, 12),\n\t\tnew THREE.MeshStandardMaterial({\n\t\t\tcolor: 0xfff7cf,\n\t\t\temissive: 0xffd84d,\n\t\t\temissiveIntensity: 0.6,\n\t\t\tmetalness: 0.9,\n\t\t\troughness: 0.12\n\t\t})\n\t);\n\tcoinInner.rotation.z = Math.PI / 2;\n\tcoin.add(coinInner);\n\tcoin.rotation.z = Math.PI * 0.08;\n\tconst pickupRing = new THREE.Mesh(\n\t\tnew THREE.TorusGeometry(0.85, 0.05, 8, 22),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: 0xffd84d,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.38\n\t\t})\n\t);\n\tpickupRing.rotation.x = Math.PI / 2;\n\tcoin.add(pickupRing);\n\tconst coinSpark = new THREE.Mesh(\n\t\tnew THREE.SphereGeometry(0.12, 8, 8),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: 0xffffff,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.8\n\t\t})\n\t);\n\tcoinSpark.position.z = 0.15;\n\tcoin.add(coinSpark);\n\tconst coinGlow = new THREE.Mesh(\n\t\tnew THREE.CircleGeometry(0.8, 24),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: 0xffcf4d,\n\t\t\ttransparent: true,\n\t\t\topacity: 0.22\n\t\t})\n\t);\n\tcoinGlow.rotation.x = -Math.PI / 2;\n\tcoinGlow.position.y = -0.52;\n\tcoin.add(coinGlow);\n\tcoin.position.set(laneX[lane], THREE.MathUtils.randFloat(1.4, 3.8), -55);\n\tconst mega = Math.random() > 0.88;\n\tif (mega) {\n\t\tcoin.scale.setScalar(1.7);\n\t\tcoin.material.emissive.setHex(0xff4fd8);\n\t\tcoin.material.color.setHex(0xff9af0);\n\t\tcoin.material.emissiveIntensity = 1.3;\n\t}\n\tcoin.userData = {\n\t\tlane,\n\t\ttype: mega ? 'megaCoin' : 'coin',\n\t\tvalue: mega ? 20 : 6,\n\t\tcombo: mega ? 3 : 1\n\t};\n\tcoins.push(coin);\n\tscene.add(coin);\n}\n\nfunction burst(position, color, amount = 12) {\n\tfor (let i = 0; i < Math.min(amount, isLowPowerDevice ? 4 : 8); i++) {\n\t\tconst p = new THREE.Mesh(\n\t\t\tnew THREE.SphereGeometry(0.1, 8, 8),\n\t\t\tnew THREE.MeshStandardMaterial({\n\t\t\t\tcolor,\n\t\t\t\temissive: color,\n\t\t\t\temissiveIntensity: 1.2,\n\t\t\t\tmetalness: 0.35,\n\t\t\t\troughness: 0.2\n\t\t\t})\n\t\t);\n\t\tp.position.copy(position);\n\t\tp.userData = {\n\t\t\tvelocity: new THREE.Vector3(\n\t\t\t\tTHREE.MathUtils.randFloatSpread(5),\n\t\t\t\tTHREE.MathUtils.randFloat(1, 5),\n\t\t\t\tTHREE.MathUtils.randFloatSpread(5)\n\t\t\t),\n\t\t\tlife: 0.7 + Math.random() * 0.5\n\t\t};\n\t\tparticles.push(p);\n\t\tscene.add(p);\n\t}\n}\n\nfunction setScore(v) {\n\tworld.score = v;\n\twhile (world.score >= world.nextMilestone) {\n\t\tworld.boostMeter = Math.min(100, world.boostMeter + 20);\n\t\tworld.shield = Math.max(world.shield, 1.8);\n\t\tworld.nextMilestone += 50;\n\t\tshake = Math.max(shake, 0.12);\n\t\tsoundPower();\n\t\tsafeText(milestoneTextEl, 'Mini reward! Shield + boost energy!');\n\t}\n\tsafeText(scoreEl, Math.floor(world.score));\n\tif (world.score > best) {\n\t\tbest = Math.floor(world.score);\n\t\tlocalStorage.setItem('fun-cube-runner-best', String(best));\n\t\tsafeText(bestEl, best);\n\t}\n\tupdateHud();\n}\n\nfunction clearActiveObjects() {\n\t[...obstacles, ...coins, ...powerUps, ...particles, ...trails].forEach((item) => scene.remove(item));\n\tobstacles.length = 0;\n\tcoins.length = 0;\n\tpowerUps.length = 0;\n\tparticles.length = 0;\n\ttrails.length = 0;\n}\n\nfunction startGame() {\n\tensureAudio();\n\tsoundStart();\n\tstartedOnce = true;\n\tworld.running = true;\n\tworld.ended = false;\n\tworld.speed = 16;\n\tworld.spawnTimer = 0;\n\tworld.coinTimer = 0;\n\tworld.distance = 0;\n\tworld.combo = 0;\n\tworld.comboTimer = 0;\n\tworld.shield = 0;\n\tworld.shieldFlash = 0;\n\tworld.hitFlash = 0;\n\tworld.magnet = 0;\n\tworld.boost = 0;\n\tworld.dash = 0;\n\tworld.powerTimer = 0;\n\tworld.jumpsLeft = 2;\n\tworld.boostMeter = 0;\n\tworld.lastGroundHit = 0;\n\tsafeLaneBias = 1;\n\tlastObstacleLane = 1;\n\tsafeText(statusTextEl, 'Go go go!');\n\tworld.nextMilestone = 50;\n\tworld.dangerPulse = 0;\n\tworld.paused = false;\n\tsafeText(pauseTextEl, 'Press P to pause');\n\tsafeText(milestoneTextEl, 'Hit 50 points for a mini reward.');\n\tupdateHud();\n\tsetScore(0);\n\n\tplayer.position.set(0, 1, 4.5);\n\tplayer.rotation.set(0, 0, 0);\n\tplayer.scale.set(1, 1, 1);\n\tplayer.userData.lane = 1;\n\tplayer.userData.targetX = 0;\n\tplayer.userData.velocityY = 0;\n\tplayer.userData.jumping = false;\n\tclearActiveObjects();\n\n\tif (overlayEl) overlayEl.classList.add('overlay--hidden');\n\tif (gameOverEl) gameOverEl.classList.add('overlay--hidden');\n}\n\nfunction endGame() {\n\tsoundCrash();\n\tsafeText(statusTextEl, 'Crashed! Tap Play Again and try a different lane.');\n\tworld.running = false;\n\tworld.ended = true;\n\tsafeText(finalScoreEl, Math.floor(world.score));\n\tif (gameOverEl) gameOverEl.classList.remove('overlay--hidden');\n\tburst(player.position.clone(), 0xff5d8f, 20);\n}\n\nfunction moveLane(dir) {\n\tif (!world.running || world.paused) return;\n\tplayer.userData.lane = THREE.MathUtils.clamp(player.userData.lane + dir, 0, 2);\n\tplayer.userData.targetX = laneX[player.userData.lane];\n}\n\nfunction jump() {\n\tif (!world.running || world.paused) return;\n\tif (world.jumpsLeft <= 0) return;\n\tplayer.userData.jumping = true;\n\tplayer.userData.velocityY = world.jumpsLeft === 2 ? 9.8 : 8.7;\n\tworld.jumpsLeft -= 1;\n\tsoundJump();\n\tupdateStatusText();\n}\n\nwindow.addEventListener('keydown', (e) => {\n\tif (['ArrowLeft', 'a', 'A'].includes(e.key)) moveLane(-1);\n\tif (['ArrowRight', 'd', 'D'].includes(e.key)) moveLane(1);\n\tif (['ArrowUp', 'w', 'W', ' '].includes(e.key)) {\n\t\te.preventDefault();\n\t\tjump();\n\t}\n\tif (e.key === 'Shift') dash();\n\tif (['p', 'P'].includes(e.key) && world.running) {\n\t\tworld.paused = !world.paused;\n\t\tsafeText(pauseTextEl, world.paused ? 'Paused — press P to resume' : 'Press P to pause');\n\t}\n\tif (e.key === 'Enter' && !world.running) startGame();\n}, {\n\tpassive: false\n});\n\nif (startBtn) startBtn.addEventListener('click', startGame);\nif (restartBtn) restartBtn.addEventListener('click', startGame);\n\nfunction bindMobileButton(btn, handler) {\n\tif (!btn) return;\n\tbtn.addEventListener('pointerdown', (e) => {\n\t\te.preventDefault();\n\t\tensureAudio();\n\t\thandler();\n\t}, {\n\t\tpassive: false\n\t});\n}\n\nbindMobileButton(btnLeft, () => moveLane(-1));\nbindMobileButton(btnRight, () => moveLane(1));\nbindMobileButton(btnJump, () => jump());\nbindMobileButton(btnDash, () => dash());\n\nif (mount) {\n\tmount.addEventListener('pointerdown', (e) => {\n\t\tif (!isCoarsePointer) return;\n\t\ttouchStartX = e.clientX;\n\t\ttouchStartY = e.clientY;\n\t\ttouchMoved = false;\n\t}, {\n\t\tpassive: true\n\t});\n\n\tmount.addEventListener('pointermove', (e) => {\n\t\tif (!isCoarsePointer) return;\n\t\tconst dx = e.clientX - touchStartX;\n\t\tconst dy = e.clientY - touchStartY;\n\t\tif (Math.abs(dx) > 20 || Math.abs(dy) > 20) touchMoved = true;\n\t}, {\n\t\tpassive: true\n\t});\n\n\tmount.addEventListener('pointerup', (e) => {\n\t\tif (!isCoarsePointer) return;\n\t\tconst dx = e.clientX - touchStartX;\n\t\tconst dy = e.clientY - touchStartY;\n\t\tconst absX = Math.abs(dx);\n\t\tconst absY = Math.abs(dy);\n\n\t\tif (!world.running && !world.ended) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!world.running) return;\n\n\t\tif (absX < 14 && absY < 14 && !touchMoved) {\n\t\t\tjump();\n\t\t\treturn;\n\t\t}\n\n\t\tif (absX > absY) {\n\t\t\tif (dx > 24) moveLane(1);\n\t\t\telse if (dx < -24) moveLane(-1);\n\t\t} else {\n\t\t\tif (dy < -24) jump();\n\t\t\telse if (dy > 28) dash();\n\t\t}\n\t}, {\n\t\tpassive: true\n\t});\n}\n\nif (mobileControls) {\n\tmobileControls.style.display = isCoarsePointer ? 'flex' : '';\n}\n\nfunction updateGround(speed, dt) {\n\tgroundGroup.children.forEach((tile, index) => {\n\t\ttile.position.z += speed * dt;\n\t\tif (tile.userData.scroll) tile.material.opacity = 0.14 + Math.sin((tile.position.z + index) * 0.12) * 0.08;\n\t\tif (tile.userData.isRing) tile.material.opacity = 0.18 + Math.sin(tile.position.z * 0.08) * 0.1;\n\t\tif (tile.userData.isRail) tile.material.opacity = 0.55 + Math.sin(tile.position.z * 0.05) * 0.18;\n\t\tif (tile.position.z > 14) tile.position.z -= 120;\n\t\tif (tile.userData.scroll && tile.position.z > 12) tile.position.z -= 128;\n\t\tif (tile.userData.isStripe && tile.position.z > 10) tile.position.z -= 120;\n\t});\n}\n\nfunction updatePlayer(dt, elapsed) {\n\tconst dashFactor = world.dash > 0 ? 3.2 : 1;\n\tplayer.position.x = THREE.MathUtils.lerp(player.position.x, player.userData.targetX, 13 * dt * dashFactor);\n\n\tplayer.userData.velocityY -= 22 * dt;\n\tplayer.position.y += player.userData.velocityY * dt;\n\n\tif (player.position.y <= 1) {\n\t\tif (player.userData.jumping && world.lastGroundHit <= 0) {\n\t\t\tshake = Math.max(shake, 0.05);\n\t\t\tworld.lastGroundHit = 0.12;\n\t\t}\n\t\tplayer.position.y = 1;\n\t\tplayer.userData.velocityY = 0;\n\t\tplayer.userData.jumping = false;\n\t\tworld.jumpsLeft = 2;\n\t}\n\tif (world.dash > 0) {\n\t\tworld.dash -= dt;\n\t\tif (Math.random() > 0.82) spawnTrail();\n\t}\n\tbody.rotation.x = Math.sin(elapsed * 10) * 0.08;\n\tface.rotation.x = Math.sin(elapsed * 6) * 0.03;\n\teyeL.scale.y = 0.9 + Math.abs(Math.sin(elapsed * 3.4)) * 0.18;\n\teyeR.scale.y = eyeL.scale.y;\n\tbody.rotation.y = world.magnet > 0 ? elapsed * 8 : 0;\n\tconst shieldPulse = world.shield > 0 ? 0.28 + Math.abs(Math.sin(elapsed * 10)) * 0.2 : 0;\n\tconst impactPulse = world.shieldFlash * 0.45 + world.hitFlash * 0.35;\n\tplayerAura.material.opacity = 0.18 + (world.shield > 0 ? shieldPulse : 0.1) + Math.sin(elapsed * 8) * 0.04 + impactPulse;\n\tplayerAura.material.color.setHex(world.shield > 0 ? (world.shieldFlash > 0.1 ? 0xffffff : 0x65f0ff) : world.boost > 0 ? 0x7cff6b : 0xff7fb0);\n\tplayerAura.scale.setScalar(1 + Math.sin(elapsed * 6) * 0.05 + (world.dash > 0 ? 0.18 : 0) + world.shieldFlash * 0.2);\n\tplayerShadowGlow.material.opacity = 0.12 + (world.dash > 0 ? 0.14 : 0.04) + world.shieldFlash * 0.14;\n\tcrownGlow.material.opacity = 0.2 + Math.abs(Math.sin(elapsed * 7)) * 0.22 + world.shieldFlash * 0.25;\n\tplayerCore.material.opacity = 0.45 + Math.abs(Math.sin(elapsed * 9)) * 0.22 + (world.boost > 0 ? 0.18 : 0) + world.shieldFlash * 0.25;\n\tplayerCore.scale.setScalar(0.9 + Math.abs(Math.sin(elapsed * 8)) * 0.16 + (world.dash > 0 ? 0.16 : 0));\n\twingL.material.opacity = 0.1 + (world.dash > 0 ? 0.32 : 0.04) + (world.boost > 0 ? 0.12 : 0);\n\twingR.material.opacity = wingL.material.opacity;\n\twingL.scale.z = 1 + (world.dash > 0 ? 1.3 : 0.1);\n\twingR.scale.z = wingL.scale.z;\n\tcrownGlow.material.color.setHex(world.shield > 0 ? (world.shieldFlash > 0.1 ? 0xffffff : 0x8cf6ff) : 0xffffff);\n\tcrownGlow.rotation.z += dt * (0.9 + world.shieldFlash * 3.2);\n\tbody.material.emissiveIntensity = 0.45 + shieldPulse * 0.6 + world.hitFlash * 0.9;\n\tface.material.emissiveIntensity = 0.65 + world.hitFlash * 1.1;\n\tbody.material.color.setHex(world.hitFlash > 0.2 ? 0xffffff : 0xff6f9d);\n\tplayer.rotation.z = (player.userData.targetX - player.position.x) * -0.08;\n\tplayer.rotation.x = player.userData.jumping ? -player.userData.velocityY * 0.03 : 0;\n\tif (world.lastGroundHit > 0) world.lastGroundHit -= dt;\n\tconst squash = player.userData.jumping ? 1.04 : 1 - Math.max(0, world.lastGroundHit) * 1.6;\n\tplayer.scale.set(1 + (1 - squash) * 0.7, squash, 1 + (1 - squash) * 0.35);\n\tconst sx = shake > 0 ? THREE.MathUtils.randFloatSpread(shake) : 0;\n\tconst sy = shake > 0 ? THREE.MathUtils.randFloatSpread(shake * 0.6) : 0;\n\tcamera.position.x = THREE.MathUtils.lerp(camera.position.x, player.position.x * 0.35 + sx, 4 * dt);\n\tcamera.position.y = THREE.MathUtils.lerp(camera.position.y, 6.2 + player.position.y * 0.12 + sy, 4 * dt);\n\tcamera.fov = THREE.MathUtils.lerp(camera.fov, 65 + Math.min(10, Math.max(0, world.speed - 16) * 0.6) + (world.dash > 0 ? 5 : 0), 4 * dt);\n\tcamera.updateProjectionMatrix();\n\tcamera.lookAt(player.position.x, 1.5, -4);\n}\n\nfunction collide(a, b, padY = 0.9) {\n\treturn Math.abs(a.position.x - b.position.x) < 1.2 && Math.abs(a.position.z - b.position.z) < 1.15 && Math.abs(a.position.y - b.position.y) < padY;\n}\n\nfunction updateObjects(dt, elapsed) {\n\tshake = Math.max(0, shake - dt * 1.2);\n\tif (world.comboTimer > 0) world.comboTimer -= dt;\n\telse world.combo = 0;\n\tif (world.shield > 0) world.shield -= dt;\n\tworld.shieldFlash = Math.max(0, world.shieldFlash - dt * 3.4);\n\tworld.hitFlash = Math.max(0, world.hitFlash - dt * 4.4);\n\tif (world.magnet > 0) world.magnet -= dt;\n\tif (world.boost > 0) world.boost -= dt;\n\tworld.spawnTimer += dt;\n\tworld.coinTimer += dt;\n\tconst effectiveSpeed = world.speed + (world.boost > 0 ? 9 : 0) + (world.dash > 0 ? 7 : 0);\n\tworld.distance += effectiveSpeed * dt;\n\tworld.speed += dt * (world.score < 120 ? 0.42 : 0.34);\n\tsafeLaneBias = player.userData.lane;\n\tworld.dangerPulse = Math.max(0, world.dangerPulse - dt * 1.8);\n\tsafeText(speedEl, `${(effectiveSpeed / 14).toFixed(1)}x${world.shield > 0 ? ` 🛡${Math.ceil(world.shield)}` : ''}${world.magnet > 0 ? ' 🧲' : ''}${world.boost > 0 ? ' ⚡' : ''}`);\n\tif (statusTextEl) statusTextEl.style.color = world.dangerPulse > 0.1 ? '#ff9db2' : '#fff2a8';\n\tif (mount) mount.style.filter = world.shield > 0 ? `drop-shadow(0 0 ${10 + world.shieldFlash * 18}px rgba(120,240,255,0.35))` : 'none';\n\n\tif (world.spawnTimer > Math.max(0.26, 0.86 - world.speed * 0.02)) {\n\t\tworld.spawnTimer = 0;\n\t\tspawnObstacle();\n\t\tif (Math.random() > 0.42) spawnCoin();\n\t\tif (Math.random() > 0.88) spawnCoin();\n\t}\n\tif (world.coinTimer > 0.85) {\n\t\tworld.coinTimer = 0;\n\t\tif (Math.random() > 0.35) spawnCoin();\n\t}\n\n\tworld.powerTimer += dt;\n\tif (world.powerTimer > 5.2) {\n\t\tworld.powerTimer = 0;\n\t\tif (Math.random() > 0.3) spawnPowerUp();\n\t}\n\n\tfor (let i = obstacles.length - 1; i >= 0; i--) {\n\t\tconst o = obstacles[i];\n\t\to.position.z += effectiveSpeed * dt;\n\t\to.rotation.x = 0;\n\t\to.rotation.y = 0;\n\t\to.material.emissiveIntensity = 0.85 + Math.abs(Math.sin(elapsed * 5 + i)) * 0.55;\n\t\tif (o.children[0]) o.children[0].material.opacity = 0.32 + Math.sin(elapsed * 7 + i) * 0.18;\n\t\tif (o.children[1]) o.children[1].material.opacity = 0.08 + Math.sin(elapsed * 9 + i) * 0.05;\n\t\tif (o.children[3]) o.children[3].material.opacity = 0.12 + Math.sin(elapsed * 8 + i) * 0.08;\n\t\tif (Math.abs(player.position.z - o.position.z) < 6) world.dangerPulse = Math.max(world.dangerPulse, 1);\n\t\tif (collide(player, o, 1.15) && player.position.y < o.userData.h + 0.35) {\n\t\t\tif (world.shield > 0 || world.dash > 0) {\n\t\t\t\tburst(o.position.clone(), world.shield > 0 ? 0x65f0ff : 0xffffff, 14);\n\t\t\t\tburst(player.position.clone().add(new THREE.Vector3(0, 0.4, 0)), 0xffffff, 10);\n\t\t\t\tsoundShield();\n\t\t\t\tshake = Math.max(shake, 0.22);\n\t\t\t\tworld.shieldFlash = 1;\n\t\t\t\tworld.dangerPulse = 1;\n\t\t\t\tworld.hitFlash = 1;\n\t\t\t\tif (world.shield > 0) world.shield = Math.max(0, world.shield - 2.5);\n\t\t\t\tscene.remove(o);\n\t\t\t\tobstacles.splice(i, 1);\n\t\t\t\tsetScore(world.score + 4 + world.combo);\n\t\t\t\tworld.boostMeter = Math.min(100, world.boostMeter + 12);\n\t\t\t\tupdateHud();\n\t\t\t\tupdateStatusText();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tendGame();\n\t\t\treturn;\n\t\t}\n\n\t\tif (o.position.z > 12) {\n\t\t\tscene.remove(o);\n\t\t\tobstacles.splice(i, 1);\n\t\t\tsetScore(world.score + 1 + Math.floor(world.combo * 0.5));\n\t\t}\n\t}\n\n\tfor (let i = coins.length - 1; i >= 0; i--) {\n\t\tconst c = coins[i];\n\t\tc.position.z += effectiveSpeed * dt;\n\t\tif (world.magnet > 0) {\n\t\t\tc.position.x = THREE.MathUtils.lerp(c.position.x, player.position.x, 2.8 * dt);\n\t\t\tc.position.y = THREE.MathUtils.lerp(c.position.y, player.position.y + 0.6, 2.8 * dt);\n\t\t}\n\t\tc.rotation.x += dt * 7;\n\t\tc.rotation.y += dt * 5;\n\t\tc.position.y += Math.sin(elapsed * 8 + i) * dt * 1.5;\n\t\tc.material.emissiveIntensity = c.userData.type === 'megaCoin' ? 1.15 + Math.sin(elapsed * 10 + i) * 0.35 : 0.9 + Math.sin(elapsed * 8 + i) * 0.2;\n\t\tif (c.children[0]) c.children[0].material.opacity = 0.22 + Math.sin(elapsed * 9 + i) * 0.12;\n\t\tif (c.children[1]) c.children[1].material.opacity = 0.15 + Math.sin(elapsed * 7 + i) * 0.08;\n\n\t\tif (collide(player, c, 1.4)) {\n\t\t\tif (c.userData.type === 'megaCoin') soundMega();\n\t\t\telse soundCoin();\n\t\t\tburst(c.position.clone(), c.userData.type === 'megaCoin' ? 0xff4fd8 : 0xffd84d, c.userData.type === 'megaCoin' ? 18 : 10);\n\t\t\tscene.remove(c);\n\t\t\tcoins.splice(i, 1);\n\t\t\tworld.combo += c.userData.combo;\n\t\t\tworld.comboTimer = 3;\n\t\t\tworld.boostMeter = Math.min(100, world.boostMeter + (c.userData.type === 'megaCoin' ? 28 : 10));\n\t\t\tshake = Math.max(shake, 0.05 + world.combo * 0.01);\n\t\t\tsetScore(world.score + c.userData.value + world.combo * 2);\n\t\t\tupdateStatusText();\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (c.position.z > 12) {\n\t\t\tscene.remove(c);\n\t\t\tcoins.splice(i, 1);\n\t\t}\n\t}\n\n\tfor (let i = powerUps.length - 1; i >= 0; i--) {\n\t\tconst p = powerUps[i];\n\t\tp.position.z += effectiveSpeed * dt;\n\t\tp.rotation.x += dt * 2.2;\n\t\tp.rotation.y += dt * 3.6;\n\t\tp.position.y = 2.1 + Math.sin(elapsed * 5 + i) * 0.35;\n\t\tp.material.emissiveIntensity = 0.45 + Math.sin(elapsed * 8 + i) * 0.18;\n\t\tif (p.children[0]) p.children[0].material.opacity = 0.26 + Math.sin(elapsed * 9 + i) * 0.12;\n\t\tif (p.children[1]) p.children[1].material.opacity = 0.14 + Math.sin(elapsed * 7 + i) * 0.06;\n\n\t\tif (collide(player, p, 1.8)) {\n\t\t\tactivatePower(p.userData.kind);\n\t\t\tburst(p.position.clone(), p.material.color.getHex(), 16);\n\t\t\tscene.remove(p);\n\t\t\tpowerUps.splice(i, 1);\n\t\t\tsetScore(world.score + 12);\n\t\t\tworld.boostMeter = Math.min(100, world.boostMeter + 18);\n\t\t\tupdateStatusText();\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (p.position.z > 12) {\n\t\t\tscene.remove(p);\n\t\t\tpowerUps.splice(i, 1);\n\t\t}\n\t}\n}\n\nfunction updateTrails(dt) {\n\tfor (let i = trails.length - 1; i >= 0; i--) {\n\t\tconst t = trails[i];\n\t\tt.userData.life -= dt;\n\t\tt.material.opacity = Math.max(0, t.userData.life * 1.6);\n\t\tt.scale.multiplyScalar(0.96);\n\t\tif (t.userData.life <= 0) {\n\t\t\tscene.remove(t);\n\t\t\ttrails.splice(i, 1);\n\t\t}\n\t}\n}\n\nfunction updateParticles(dt) {\n\tfor (let i = particles.length - 1; i >= 0; i--) {\n\t\tconst p = particles[i];\n\t\tp.userData.life -= dt;\n\t\tp.position.addScaledVector(p.userData.velocity, dt);\n\t\tp.userData.velocity.y -= 8 * dt;\n\t\tp.scale.setScalar(Math.max(0.01, p.userData.life * 1.2));\n\t\tp.material.opacity = Math.max(0, p.userData.life * 1.4);\n\t\tif (p.userData.life <= 0) {\n\t\t\tscene.remove(p);\n\t\t\tparticles.splice(i, 1);\n\t\t}\n\t}\n}\n\nfunction updateFog(dt, elapsed) {\n\tfogParticles.forEach((puff, i) => {\n\t\tpuff.position.z += (world.running && !world.paused ? world.speed * 0.34 : 1.2) * dt * puff.userData.speed;\n\t\tpuff.position.x += Math.sin(elapsed * 0.4 + i) * dt * 0.5;\n\t\tpuff.material.opacity = 0.04 + Math.abs(Math.sin(elapsed * 0.7 + i)) * 0.06;\n\t\tpuff.lookAt(camera.position);\n\t\tif (puff.position.z > 10) {\n\t\t\tpuff.position.z = THREE.MathUtils.randFloat(-80, -30);\n\t\t\tpuff.position.x = THREE.MathUtils.randFloatSpread(26);\n\t\t\tpuff.position.y = THREE.MathUtils.randFloat(1.5, 10);\n\t\t}\n\t});\n}\n\nfunction updateAurora(elapsed) {\n\tauroraBands.forEach((band, i) => {\n\t\tband.position.x = Math.sin(elapsed * 0.22 + i) * (4 + i * 2);\n\t\tband.material.opacity = 0.05 + Math.abs(Math.sin(elapsed * 0.7 + band.userData.phase)) * 0.06 + (world.boost > 0 ? 0.03 : 0);\n\t\tband.rotation.z = Math.sin(elapsed * 0.18 + i) * 0.08;\n\t});\n}\n\nfunction updateEnergyBeams(dt, elapsed) {\n\tenergyBeams.forEach((beam, i) => {\n\t\tbeam.rotation.y += dt * (0.08 + i * 0.01);\n\t\tbeam.material.opacity = 0.05 + Math.abs(Math.sin(elapsed * 1.6 + beam.userData.phase)) * 0.06 + (world.boost > 0 ? 0.03 : 0);\n\t\tbeam.scale.x = 1 + Math.sin(elapsed * 1.4 + i) * 0.08;\n\t\tbeam.scale.z = 1 + Math.cos(elapsed * 1.2 + i) * 0.08;\n\t});\n}\n\nfunction updateSkyline(dt, elapsed) {\n\tbillboards.forEach((sign, i) => {\n\t\tsign.material.opacity = 0.5 + Math.abs(Math.sin(elapsed * 2.4 + i)) * 0.35;\n\t\tsign.scale.x = 0.92 + Math.abs(Math.sin(elapsed * 1.8 + i)) * 0.18;\n\t});\n\tskyline.forEach((tower, i) => {\n\t\ttower.position.z += (world.running && !world.paused ? world.speed * 0.42 : 1.5) * dt * tower.userData.speed;\n\t\ttower.position.y = tower.userData.baseY + Math.sin(elapsed * 0.5 + i) * 0.08;\n\t\tif (tower.position.z > 18) tower.position.z = THREE.MathUtils.randFloat(-95, -55);\n\t});\n}\n\nfunction updateStars(dt) {\n\tstars.forEach((star, i) => {\n\t\tstar.position.z += world.running ? world.speed * dt * 1.15 : dt * 2;\n\t\tstar.position.x += Math.sin((star.position.z + i) * 0.08) * dt * 0.15;\n\t\tstar.scale.setScalar(0.7 + Math.abs(Math.sin((star.position.z + i) * 0.15)) * 0.9);\n\t\tif (star.position.z > 12) resetStar(star);\n\t});\n}\n\nfunction animate() {\n\trequestAnimationFrame(animate);\n\tconst dt = Math.min(clock.getDelta(), 0.033);\n\tconst elapsed = clock.elapsedTime;\n\n\tupdateStars(world.paused ? 0 : dt);\n\tupdateSkyline(world.paused ? 0 : dt, elapsed);\n\tupdateFog(world.paused ? 0 : dt, elapsed);\n\tupdateEnergyBeams(world.paused ? 0 : dt, elapsed);\n\tupdateAurora(elapsed);\n\tskyDome.material.color.setHSL(0.62 + Math.sin(elapsed * 0.08) * 0.02, 0.45, 0.12 + Math.sin(elapsed * 0.12) * 0.015);\n\thorizonGlow.material.opacity = 0.16 + Math.sin(elapsed * 0.9) * 0.04;\n\tsunGlow.material.opacity = 0.14 + Math.sin(elapsed * 1.2) * 0.04;\n\tsunGlow.scale.setScalar(1 + Math.sin(elapsed * 0.8) * 0.03 + (world.boost > 0 ? 0.03 : 0));\n\tdirLight.intensity = 2.1 + Math.sin(elapsed * 0.7) * 0.18;\n\trimLight.position.x = Math.sin(elapsed * 1.3) * 4;\n\trimLight.position.y = 5 + Math.sin(elapsed * 0.8) * 1.2;\n\trimLight.intensity = 16 + Math.sin(elapsed * 2.2) * 3 + (world.dash > 0 ? 4 : 0);\n\tglowLight.position.z = -10 + Math.sin(elapsed * 1.6) * 6;\n\taccentLight.position.x = Math.sin(elapsed * 1.1) * 5;\n\taccentLight.position.z = -24 + Math.cos(elapsed * 1.4) * 8;\n\taccentLight.intensity = 6 + Math.abs(Math.sin(elapsed * 2.6)) * 2.4 + (world.shield > 0 ? 2.2 : 0);\n\tglowLight.intensity = 11 + Math.sin(elapsed * 3) * 3 + (world.boost > 0 ? 3.5 : 0);\n\trenderer.toneMappingExposure = 1.32 + (world.boost > 0 ? 0.1 : 0) + (world.dash > 0 ? 0.06 : 0) + world.hitFlash * 0.05;\n\tif (bloomPass) {\n\t\tbloomPass.strength = isLowPowerDevice ? 0 : 0.18 + (world.boost > 0 ? 0.08 : 0) + (world.dash > 0 ? 0.04 : 0) + world.shieldFlash * 0.06;\n\t\tbloomPass.radius = isLowPowerDevice ? 0 : 0.18 + (world.boost > 0 ? 0.02 : 0);\n\t}\n\themi.intensity = 1.35 + Math.sin(elapsed * 0.6) * 0.08;\n\tupdateGround(world.running && !world.paused ? world.speed : 8, dt);\n\n\tif (world.running && !world.paused) {\n\t\tupdatePlayer(dt, elapsed);\n\t\tupdateObjects(dt, elapsed);\n\t} else if (!world.ended && !world.running) {\n\t\tplayer.rotation.y += dt * 0.8;\n\t\tplayer.position.y = 1 + Math.sin(elapsed * 2.2) * 0.18;\n\t\tcamera.lookAt(0, 1.4, 0);\n\t}\n\n\tupdateTrails(dt);\n\tupdateParticles(dt);\n\tif (world.shieldFlash > 0 || world.hitFlash > 0) {\n\t\tscene.fog.color.lerpColors(\n\t\t\tnew THREE.Color(0x0b1020),\n\t\t\tnew THREE.Color(world.shield > 0 ? 0xa8f7ff : 0xffffff),\n\t\t\tMath.min(0.45, world.shieldFlash * 0.35 + world.hitFlash * 0.25)\n\t\t);\n\t\tif (sunGlow) sunGlow.material.opacity = 0.14 + world.shieldFlash * 0.12;\n\t} else {\n\t\tscene.fog.color.set(0x0b1020);\n\t\tif (sunGlow) sunGlow.material.opacity = 0.14 + Math.sin(elapsed * 1.2) * 0.04;\n\t}\n\tfor (let i = 0; i < lanePulseLights.length; i += 2) {\n\t\tconst light = lanePulseLights[i];\n\t\tlight.intensity = 0.8 + Math.abs(Math.sin(elapsed * 2.6 + light.userData.phase)) * 1.1 + (world.boost > 0 ? 0.4 : 0);\n\t}\n\tfor (let i = 1; i < lanePulseLights.length; i += 2) {\n\t\tlanePulseLights[i].intensity = 0.35;\n\t}\n\n\tfpsFrames++;\n\tfpsTime += dt;\n\tif (fpsTime >= 0.4) {\n\t\tfpsDisplay = Math.round(fpsFrames / fpsTime);\n\t\tif (fpsEl) {\n\t\t\tfpsEl.textContent = `FPS ${fpsDisplay}`;\n\t\t\tfpsEl.style.color = fpsDisplay >= 58 ? '#72e8ff' : fpsDisplay >= 45 ? '#ffe27a' : '#ff8fb8';\n\t\t}\n\t\tif (fpsDisplay < 54 && adaptivePixelRatio > 0.9) {\n\t\t\tadaptivePixelRatio = Math.max(0.9, adaptivePixelRatio - 0.05);\n\t\t\trenderer.setPixelRatio(adaptivePixelRatio);\n\t\t\tif (composer) composer.setSize(window.innerWidth, window.innerHeight);\n\t\t} else if (fpsDisplay > 62 && adaptivePixelRatio < Math.min(window.devicePixelRatio || 1, isCoarsePointer ? 1.1 : 1.35)) {\n\t\t\tadaptivePixelRatio = Math.min(Math.min(window.devicePixelRatio || 1, isCoarsePointer ? 1.1 : 1.35), adaptivePixelRatio + 0.05);\n\t\t\trenderer.setPixelRatio(adaptivePixelRatio);\n\t\t\tif (composer) composer.setSize(window.innerWidth, window.innerHeight);\n\t\t}\n\t\tfpsFrames = 0;\n\t\tfpsTime = 0;\n\t}\n\n\tif (composer) composer.render();\n\telse renderer.render(scene, camera);\n}\nanimate();\n\nfunction resizeGame() {\n\tconst width = window.innerWidth;\n\tconst height = window.innerHeight;\n\tcamera.aspect = width / height;\n\tcamera.fov = width < 700 ? 72 : 65;\n\tcamera.updateProjectionMatrix();\n\trenderer.setSize(width, height, false);\n\tadaptivePixelRatio = Math.min(window.devicePixelRatio || 1, width < 700 ? 1.05 : (isLowPowerDevice ? 0.8 : 1.35));\n\trenderer.setPixelRatio(adaptivePixelRatio);\n\tif (composer) composer.setSize(width, height);\n\tif (bloomPass) bloomPass.setSize(width, height);\n}\n\nwindow.addEventListener('resize', resizeGame, {\n\tpassive: true\n});\nwindow.addEventListener('orientationchange', () => setTimeout(resizeGame, 120), {\n\tpassive: true\n});\n\ndocument.addEventListener('visibilitychange', () => {\n\tif (document.hidden && world.running) {\n\t\tworld.paused = true;\n\t\tsafeText(pauseTextEl, 'Paused — return to continue');\n\t}\n});\n\nif (isMobile) {\n\tconst unlock = () => {\n\t\tensureAudio();\n\t\twindow.removeEventListener('pointerdown', unlock);\n\t\twindow.removeEventListener('touchstart', unlock);\n\t};\n\twindow.addEventListener('pointerdown', unlock, {\n\t\tonce: true,\n\t\tpassive: true\n\t});\n\twindow.addEventListener('touchstart', unlock, {\n\t\tonce: true,\n\t\tpassive: true\n\t});\n}\n\nresizeGame();"},{"name":"style.css","content":":root {\n\tcolor-scheme: light;\n\tfont-family: Inter, system-ui, sans-serif;\n\t--bg1: #0b1020;\n\t--bg2: #151d38;\n\t--panel: rgba(10, 14, 28, 0.54);\n\t--panel-border: rgba(255, 255, 255, 0.16);\n\t--text: #f4f7ff;\n\t--muted: #a7b2d8;\n\t--accent: #7c5cff;\n\t--accent-2: #37e7ff;\n\t--danger: #ff5d8f;\n\t--gold: #ffd84d;\n}\n\n* {\n\tbox-sizing: border-box;\n}\n\nhtml,\nbody {\n\tmargin: 0;\n\tmin-height: 100%;\n\tbackground:\n\t\tradial-gradient(circle at top, #25315f 0%, transparent 35%),\n\t\tlinear-gradient(180deg, var(--bg2), var(--bg1));\n\tcolor: var(--text);\n}\n\nbody {\n\toverflow: hidden;\n\tbackground-attachment: fixed;\n\ttouch-action: manipulation;\n\t-webkit-tap-highlight-color: transparent;\n}\n\n.game-shell {\n\tposition: relative;\n\twidth: 100vw;\n\theight: 100vh;\n\toverflow: hidden;\n}\n\n.game-shell::before {\n\tcontent: '';\n\tposition: absolute;\n\tinset: 0;\n\tpointer-events: none;\n\tz-index: 1;\n\tbackground:\n\t\tradial-gradient(circle at 20% 15%, rgba(55, 231, 255, 0.12), transparent 28%),\n\t\tradial-gradient(circle at 80% 20%, rgba(255, 79, 216, 0.12), transparent 30%),\n\t\tradial-gradient(circle at 50% 100%, rgba(124, 92, 255, 0.18), transparent 36%);\n\tmix-blend-mode: screen;\n}\n\n.game-shell::after {\n\tcontent: '';\n\tposition: absolute;\n\tinset: 0;\n\tpointer-events: none;\n\tz-index: 2;\n\tbackground:\n\t\tlinear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 18%, transparent 82%, rgba(0, 0, 0, 0.2)),\n\t\tradial-gradient(circle at 50% 20%, rgba(114, 232, 255, 0.05), transparent 28%),\n\t\tradial-gradient(circle at 50% 85%, rgba(255, 79, 216, 0.06), transparent 34%);\n\topacity: 0.9;\n}\n\n#game {\n\tposition: relative;\n\tz-index: 0;\n\twidth: 100%;\n\theight: 100%;\n\tfilter: saturate(1.12) contrast(1.08) brightness(1.02);\n\ttouch-action: none;\n}\n\n#game::after {\n\tcontent: '';\n\tposition: absolute;\n\tinset: 0;\n\tpointer-events: none;\n\tz-index: 1;\n\tbackground:\n\t\tlinear-gradient(180deg, rgba(255, 255, 255, 0.03), transparent 18%, transparent 82%, rgba(0, 0, 0, 0.24)),\n\t\trepeating-linear-gradient(180deg, rgba(255, 255, 255, 0.024) 0 2px, rgba(0, 0, 0, 0) 2px 6px);\n\tmix-blend-mode: screen;\n\topacity: 0.42;\n}\n\n#game::before {\n\tcontent: '';\n\tposition: absolute;\n\tinset: 0;\n\tpointer-events: none;\n\tz-index: 1;\n\tbackground: radial-gradient(circle at 50% 45%, transparent 0%, transparent 52%, rgba(0, 0, 0, 0.24) 100%);\n}\n\n#game canvas {\n\tdisplay: block;\n\twidth: 100% !important;\n\theight: 100% !important;\n\timage-rendering: auto;\n}\n\n.fps-counter {\n\tposition: fixed;\n\ttop: 16px;\n\tright: 16px;\n\tz-index: 60;\n\tpadding: 8px 12px;\n\tborder-radius: 999px;\n\tborder: 1px solid rgba(255, 255, 255, 0.14);\n\tbackground: linear-gradient(180deg, rgba(16, 22, 40, 0.84), rgba(8, 12, 24, 0.72));\n\tbackdrop-filter: blur(10px) saturate(1.1);\n\tbox-shadow: 0 8px 24px rgba(0, 0, 0, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.08);\n\tcolor: #72e8ff;\n\tfont: 800 12px/1 Inter, system-ui, sans-serif;\n\tletter-spacing: 0.08em;\n\ttext-transform: uppercase;\n\tpointer-events: none;\n\tuser-select: none;\n\tmin-width: 74px;\n\ttext-align: center;\n}\n\n.hud {\n\tposition: absolute;\n\tinset: 0 0 auto 0;\n\tz-index: 5;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tpadding: 18px;\n\tmax-width: 960px;\n\tpointer-events: none;\n}\n\n.hud__brand {\n\talign-self: flex-start;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 10px 14px;\n\tborder: 1px solid var(--panel-border);\n\tborder-radius: 999px;\n\tbackground: linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.04)), var(--panel);\n\tbackdrop-filter: blur(16px) saturate(1.25);\n\tbox-shadow: 0 12px 40px rgba(0, 0, 0, 0.24), inset 0 1px 0 rgba(255, 255, 255, 0.14);\n\tfont-weight: 900;\n\tletter-spacing: 0.16em;\n\tfont-size: 0.9rem;\n\ttext-shadow: 0 0 14px rgba(55, 231, 255, 0.18);\n}\n\n.hud__brand-glow {\n\tdisplay: inline-grid;\n\tplace-items: center;\n\twidth: 22px;\n\theight: 22px;\n\tborder-radius: 50%;\n\tbackground: radial-gradient(circle, rgba(255, 226, 122, 0.95), rgba(255, 143, 208, 0.85));\n\tcolor: #08111f;\n\tfont-size: 12px;\n\tbox-shadow: 0 0 16px rgba(255, 226, 122, 0.35);\n}\n\n.hud__stats {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 10px;\n}\n\n.hud__pill {\n\tpadding: 10px 14px;\n\tborder-radius: 999px;\n\tbackground: linear-gradient(180deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.03)), var(--panel);\n\tborder: 1px solid var(--panel-border);\n\tbackdrop-filter: blur(14px) saturate(1.2);\n\tfont-weight: 700;\n\tbox-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.08);\n}\n\n.hud__hint {\n\tcolor: var(--muted);\n\tfont-size: 0.95rem;\n\tline-height: 1.45;\n\ttext-shadow: 0 2px 18px rgba(0, 0, 0, 0.45);\n}\n\n.hud__hint--strong {\n\tcolor: #fff0a8;\n}\n\n.meter {\n\twidth: min(320px, 82vw);\n\theight: 12px;\n\tborder-radius: 999px;\n\toverflow: hidden;\n\tborder: 1px solid var(--panel-border);\n\tbackground: rgba(255, 255, 255, 0.08);\n\tbackdrop-filter: blur(12px);\n}\n\n.meter__fill {\n\twidth: 0;\n\theight: 100%;\n\tborder-radius: inherit;\n\tbackground: linear-gradient(90deg, var(--gold), #ff9ecb, var(--accent-2));\n\tbox-shadow: 0 0 18px rgba(255, 216, 77, 0.28);\n\ttransition: width 0.18s ease;\n}\n\n.overlay {\n\tposition: fixed;\n\tinset: 0;\n\tdisplay: grid;\n\tplace-items: center;\n\tpadding: 32px;\n\tz-index: 40;\n}\n\n.overlay--hidden {\n\tdisplay: none;\n}\n\n.overlay__backdrop {\n\tposition: absolute;\n\tinset: 0;\n\tbackground:\n\t\tradial-gradient(circle at 50% 10%, rgba(255, 102, 196, 0.18), transparent 24%),\n\t\tradial-gradient(circle at 20% 80%, rgba(71, 224, 255, 0.12), transparent 24%),\n\t\tradial-gradient(circle at 80% 18%, rgba(124, 92, 255, 0.16), transparent 22%),\n\t\tlinear-gradient(180deg, rgba(5, 8, 18, 0.62), rgba(5, 8, 18, 0.9));\n\tbackdrop-filter: blur(14px) saturate(1.08);\n}\n\n.overlay__backdrop--danger {\n\tbackground:\n\t\tradial-gradient(circle at 50% 24%, rgba(255, 93, 143, 0.24), transparent 30%),\n\t\tradial-gradient(circle at 82% 18%, rgba(255, 216, 77, 0.12), transparent 24%),\n\t\tlinear-gradient(180deg, rgba(14, 8, 14, 0.62), rgba(14, 8, 14, 0.9));\n}\n\n.overlay__card {\n\tposition: relative;\n\tz-index: 1;\n\twidth: min(660px, 100%);\n\tpadding: 42px 42px 34px;\n\tborder-radius: 28px;\n\tborder: 1px solid rgba(255, 255, 255, 0.12);\n\tbackground:\n\t\tlinear-gradient(180deg, rgba(20, 26, 46, 0.95), rgba(8, 12, 24, 0.98)),\n\t\tradial-gradient(circle at top, rgba(255, 255, 255, 0.04), transparent 40%);\n\tbox-shadow:\n\t\t0 30px 90px rgba(0, 0, 0, 0.5),\n\t\t0 0 60px rgba(90, 120, 255, 0.12),\n\t\t0 0 120px rgba(255, 102, 196, 0.08),\n\t\t0 0 0 1px rgba(255, 255, 255, 0.04) inset,\n\t\tinset 0 1px 0 rgba(255, 255, 255, 0.08);\n\ttext-align: center;\n\tcolor: #f7f8ff;\n\toverflow: hidden;\n}\n\n.overlay__card::before {\n\tcontent: \"\";\n\tposition: absolute;\n\tinset: 0;\n\tpointer-events: none;\n\tbackground:\n\t\tlinear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 24%),\n\t\tradial-gradient(circle at top right, rgba(92, 118, 255, 0.14), transparent 24%),\n\t\tradial-gradient(circle at bottom left, rgba(255, 102, 196, 0.12), transparent 28%);\n}\n\n.overlay__card::after {\n\tcontent: \"\";\n\tposition: absolute;\n\tinset: 0;\n\tborder-radius: inherit;\n\tpadding: 1px;\n\tbackground: linear-gradient(135deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.03));\n\t-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n\t-webkit-mask-composite: xor;\n\tmask-composite: exclude;\n\tpointer-events: none;\n}\n\n.overlay__grid {\n\tposition: absolute;\n\tleft: -10%;\n\tright: -10%;\n\tbottom: -12%;\n\theight: 42%;\n\tbackground:\n\t\tlinear-gradient(rgba(114, 232, 255, 0.15) 1px, transparent 1px),\n\t\tlinear-gradient(90deg, rgba(114, 232, 255, 0.15) 1px, transparent 1px);\n\tbackground-size: 26px 26px;\n\topacity: 0.22;\n\ttransform: perspective(500px) rotateX(72deg);\n\ttransform-origin: bottom;\n\tpointer-events: none;\n}\n\n.overlay__spark {\n\tposition: absolute;\n\tborder-radius: 999px;\n\tfilter: blur(4px);\n\tpointer-events: none;\n\tanimation: overlayFloat 5s ease-in-out infinite;\n}\n\n.overlay__spark--1 {\n\twidth: 120px;\n\theight: 120px;\n\tleft: -24px;\n\ttop: 22px;\n\tbackground: radial-gradient(circle, rgba(114, 232, 255, 0.35), transparent 70%);\n}\n\n.overlay__spark--2 {\n\twidth: 150px;\n\theight: 150px;\n\tright: -36px;\n\ttop: -12px;\n\tbackground: radial-gradient(circle, rgba(255, 102, 196, 0.3), transparent 70%);\n\tanimation-delay: -1.2s;\n}\n\n.overlay__spark--3 {\n\twidth: 110px;\n\theight: 110px;\n\tright: 18%;\n\tbottom: 14%;\n\tbackground: radial-gradient(circle, rgba(255, 226, 122, 0.22), transparent 72%);\n\tanimation-delay: -2.2s;\n}\n\n.overlay__spark--danger {\n\twidth: 180px;\n\theight: 180px;\n\tright: -50px;\n\ttop: -50px;\n\tbackground: radial-gradient(circle, rgba(255, 93, 143, 0.28), transparent 72%);\n}\n\n.overlay__card--danger {\n\tbackground: linear-gradient(180deg, rgba(41, 18, 28, 0.97), rgba(20, 10, 16, 0.99));\n}\n\n.overlay__eyebrow {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tmargin-bottom: 16px;\n\tpadding: 8px 13px;\n\tborder-radius: 999px;\n\tborder: 1px solid rgba(255, 255, 255, 0.1);\n\tbackground: linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));\n\tcolor: #ffe27a;\n\tfont-size: 11px;\n\tfont-weight: 900;\n\tletter-spacing: 0.18em;\n\ttext-transform: uppercase;\n\tbox-shadow: 0 0 24px rgba(255, 226, 122, 0.08);\n}\n\n.overlay__card h1,\n.overlay__card h2 {\n\tposition: relative;\n\tmargin: 0;\n\tfont-size: clamp(36px, 5.8vw, 56px);\n\tline-height: 0.98;\n\tletter-spacing: -0.05em;\n\tfont-weight: 900;\n\ttext-wrap: balance;\n\ttext-shadow: 0 8px 30px rgba(0, 0, 0, 0.28);\n}\n\n.overlay__lead {\n\tposition: relative;\n\tmax-width: 540px;\n\tmargin: 16px auto 0;\n\tcolor: rgba(225, 233, 250, 0.82);\n\tfont-size: clamp(15px, 2.1vw, 18px);\n\tline-height: 1.65;\n}\n\n.overlay__features {\n\tposition: relative;\n\tdisplay: grid;\n\tgrid-template-columns: repeat(3, minmax(0, 1fr));\n\tgap: 14px;\n\tmargin: 30px 0 24px;\n}\n\n.overlay__feature {\n\tpadding: 18px 14px;\n\tborder-radius: 18px;\n\tbackground: linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));\n\tborder: 1px solid rgba(255, 255, 255, 0.08);\n\tbox-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 10px 24px rgba(0, 0, 0, 0.16);\n\tdisplay: grid;\n\tgap: 8px;\n\ttransition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;\n}\n\n.overlay__feature span {\n\tcolor: #9fdcff;\n\tfont-size: 12px;\n\tfont-weight: 900;\n\tletter-spacing: 0.08em;\n\ttext-transform: uppercase;\n}\n\n.overlay__feature strong {\n\tfont-size: 14px;\n\tfont-weight: 800;\n\tcolor: #f5f7ff;\n}\n\n.overlay__feature small {\n\tfont-size: 12px;\n\tcolor: rgba(200, 212, 236, 0.7);\n}\n\n.overlay__actions {\n\tposition: relative;\n\tdisplay: grid;\n\tjustify-items: center;\n\tgap: 12px;\n\tmargin-top: 10px;\n}\n\n.overlay button {\n\tappearance: none;\n\t-webkit-appearance: none;\n\tborder: 1px solid rgba(255, 255, 255, 0.08);\n\tborder-radius: 16px;\n\tpadding: 16px 30px;\n\tmin-width: 230px;\n\tfont: inherit;\n\tfont-weight: 900;\n\tletter-spacing: 0.08em;\n\ttext-transform: uppercase;\n\tcolor: #0d1526;\n\tbackground: linear-gradient(135deg, #ffe27a 0%, #ff8fd0 52%, #72e8ff 100%);\n\tbox-shadow: 0 18px 36px rgba(255, 120, 180, 0.28), 0 0 30px rgba(114, 232, 255, 0.14);\n\tcursor: pointer;\n\ttransition: transform 0.18s ease, box-shadow 0.18s ease, filter 0.18s ease;\n\ttouch-action: manipulation;\n}\n\n.overlay__subtext {\n\tfont-size: 13px;\n\tcolor: rgba(196, 206, 230, 0.72);\n\tletter-spacing: 0.01em;\n}\n\n.overlay__chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tjustify-content: center;\n\tgap: 8px;\n\tmargin-top: 4px;\n}\n\n.overlay__chip {\n\tpadding: 7px 10px;\n\tborder-radius: 999px;\n\tfont-size: 11px;\n\tfont-weight: 800;\n\tletter-spacing: 0.08em;\n\ttext-transform: uppercase;\n\tcolor: #dfe8ff;\n\tbackground: rgba(255, 255, 255, 0.06);\n\tborder: 1px solid rgba(255, 255, 255, 0.08);\n\tbox-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);\n}\n\n.overlay__score {\n\tposition: relative;\n\tmargin-top: 16px;\n\tfont-size: clamp(56px, 10vw, 100px);\n\tline-height: 1;\n\tfont-weight: 900;\n\tletter-spacing: -0.06em;\n\tcolor: #fff2a8;\n\ttext-shadow: 0 0 24px rgba(255, 226, 122, 0.22), 0 8px 30px rgba(0, 0, 0, 0.18);\n}\n\n.overlay__burst {\n\tposition: absolute;\n\twidth: 180px;\n\theight: 180px;\n\tright: -40px;\n\ttop: -40px;\n\tborder-radius: 50%;\n\tbackground: radial-gradient(circle, rgba(255, 93, 143, 0.22), rgba(255, 93, 143, 0) 68%);\n\tfilter: blur(4px);\n\tpointer-events: none;\n}\n\n.mobile-controls {\n\tposition: fixed;\n\tleft: 0;\n\tright: 0;\n\tbottom: max(14px, env(safe-area-inset-bottom));\n\tdisplay: none;\n\tjustify-content: space-between;\n\tgap: 16px;\n\tpadding: 0 14px;\n\tz-index: 30;\n\tpointer-events: none;\n}\n\n.mobile-controls__left,\n.mobile-controls__right {\n\tdisplay: flex;\n\tgap: 12px;\n\tpointer-events: auto;\n}\n\n.mobile-btn {\n\t-webkit-appearance: none;\n\tappearance: none;\n\ttouch-action: manipulation;\n\tmin-width: 64px;\n\theight: 64px;\n\tborder: 1px solid rgba(255, 255, 255, 0.22);\n\tbackground: linear-gradient(180deg, rgba(24, 34, 62, 0.86), rgba(8, 14, 28, 0.62));\n\tcolor: #fff;\n\tfont: inherit;\n\tfont-weight: 800;\n\tborder-radius: 18px;\n\tbackdrop-filter: blur(14px) saturate(1.2);\n\tbox-shadow: 0 10px 30px rgba(0, 0, 0, 0.28), 0 0 22px rgba(82, 224, 255, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.08);\n}\n\n.mobile-btn--jump,\n.mobile-btn--dash {\n\tmin-width: 88px;\n}\n\n.mobile-btn--dash {\n\tcolor: #aefcff;\n}\n\n@media (hover: hover) {\n\t.overlay__feature:hover {\n\t\ttransform: translateY(-4px);\n\t\tborder-color: rgba(114, 232, 255, 0.28);\n\t\tbox-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07), 0 14px 28px rgba(0, 0, 0, 0.22), 0 0 24px rgba(114, 232, 255, 0.08);\n\t}\n\n\t.overlay button:hover {\n\t\ttransform: translateY(-3px) scale(1.01);\n\t\tbox-shadow: 0 22px 42px rgba(255, 120, 180, 0.34), 0 0 34px rgba(114, 232, 255, 0.18);\n\t\tfilter: brightness(1.03) saturate(1.06);\n\t}\n}\n\n.overlay button:active,\n.mobile-btn:active {\n\ttransform: scale(0.98);\n}\n\n@media (max-width: 700px) {\n\t.overlay {\n\t\tpadding: 16px;\n\t}\n\n\t.overlay__card {\n\t\twidth: min(100%, 560px);\n\t\tpadding: 28px 20px 22px;\n\t\tborder-radius: 22px;\n\t}\n\n\t.overlay__card h1,\n\t.overlay__card h2 {\n\t\tfont-size: clamp(30px, 10vw, 42px);\n\t}\n\n\t.overlay__features {\n\t\tgrid-template-columns: 1fr;\n\t\tgap: 10px;\n\t\tmargin: 22px 0 18px;\n\t}\n\n\t.overlay__grid {\n\t\theight: 34%;\n\t\tbackground-size: 22px 22px;\n\t}\n\n\t.overlay__burst {\n\t\twidth: 120px;\n\t\theight: 120px;\n\t\tright: -24px;\n\t\ttop: -24px;\n\t}\n\n\t.overlay button {\n\t\twidth: 100%;\n\t\tmin-width: 0;\n\t}\n}\n\n@media (max-width: 640px) {\n\t.hud {\n\t\tpadding: 14px;\n\t\tgap: 10px;\n\t}\n\n\t.hud__brand {\n\t\tfont-size: 0.78rem;\n\t\tletter-spacing: 0.12em;\n\t}\n\n\t.hud__stats {\n\t\tgap: 8px;\n\t}\n\n\t.hud__pill {\n\t\tpadding: 8px 12px;\n\t\tfont-size: 0.84rem;\n\t}\n\n\t.hud__hint {\n\t\tfont-size: 0.85rem;\n\t\tmax-width: min(92vw, 320px);\n\t}\n\n\t.meter {\n\t\twidth: min(260px, 76vw);\n\t\theight: 11px;\n\t}\n\n\t.fps-counter {\n\t\ttop: auto;\n\t\tbottom: calc(100px + env(safe-area-inset-bottom));\n\t\tright: 12px;\n\t\tfont-size: 11px;\n\t\tpadding: 7px 10px;\n\t}\n}\n\n@media (hover: none), (pointer: coarse) {\n\t.mobile-controls {\n\t\tdisplay: flex;\n\t}\n}\n\n@keyframes overlayFloat {\n\n\t0%,\n\t100% {\n\t\ttransform: translateY(0px) scale(1);\n\t}\n\n\t50% {\n\t\ttransform: translateY(-10px) scale(1.04);\n\t}\n}"}],"folders":[]},"variants":null,"createdAt":"2026-03-06T02:03:51.946Z","updatedAt":"2026-03-06T03:20:56.453Z","hasThumbnail":true}}