{"asset_map": {"grass_block_texture": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/grass_block_texture_5769db33-f810-44da-8a64-00845e583bf3.webp", "type": "image", "prompt": "Generate a 32x32 pixel art texture for the top face of a grass block, Minecraft style. Use bright green (#7CBD3B) with subtle variations in green shades and darker green pixels to suggest grass blades. Flat, tileable, seamless design with no baked lighting. Low resolution, blocky aesthetic.", "description": "A 32x32 pixel-art grass top texture for voxel blocks.", "subtype": "ground", "aspect_ratio": [1024, 1024], "compression_suggestion": "original", "duration_secs": null, "loop": null, "animation": null}, "dirt_block_texture": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dirt_block_texture_755144a5-a594-490b-9b94-a9eb13458ef0.webp", "type": "image", "prompt": "Generate a 32x32 pixel art texture for a dirt block, Minecraft style. Use medium brown (#8B6F47) with variations in lighter and darker brown pixels. Include small dark specks suggesting soil particles. Flat, tileable, seamless design, earthy organic pattern. No baked lighting.", "description": "A 32x32 pixel-art dirt texture for voxel blocks.", "subtype": "ground", "aspect_ratio": [1024, 1024], "compression_suggestion": "original", "duration_secs": null, "loop": null, "animation": null}, "stone_block_texture": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/stone_block_texture_b709e725-b194-4e28-b9e2-d3e05fab4389.webp", "type": "image", "prompt": "Generate a 32x32 pixel art texture for a stone block, Minecraft style. Use gray (#7F7F7F) with variations in gray shades and subtle cracks or lines suggesting mineral structure. Flat, tileable, seamless rocky pattern. No baked lighting.", "description": "A 32x32 pixel-art stone texture for voxel blocks.", "subtype": "ground", "aspect_ratio": [1024, 1024], "compression_suggestion": "original", "duration_secs": null, "loop": null, "animation": null}, "coin_sprite": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/coin_sprite_5cb150ac-4874-4ace-ab90-c85d68407e94.webp", "type": "image", "prompt": "Generate a 64x64 pixel art sprite of a circular golden coin, Minecraft style. Golden yellow color (#FFD700) with pixelated edges and simple highlights to suggest 3D form. Include a simple star symbol in the center. Front-facing, game-ready collectible icon on a transparent background.", "description": "A golden pixel-art coin sprite for collection.", "subtype": "object", "aspect_ratio": [787, 816], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "treasure_chest_texture": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/treasure_chest_texture_a55d1a09-7246-4d6c-9e50-0eafd1561601.webp", "type": "image", "prompt": "Generate a 128x128 pixel art texture map for a 3D treasure chest, Minecraft style. Layout should include sections for brown wood planks (#8B4513) with visible grain and golden trim/metal bands (#FFD700). Include a simple lock detail. Flat UV unwrap style, blocky aesthetic, no complex shadows.", "description": "A 128x128 pixel-art UV texture map for a treasure chest model.", "subtype": "object", "aspect_ratio": [1024, 1024], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "sky_background": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/sky_background_3dbecf3f-780b-445d-8f42-4e066e946137.webp", "type": "image", "prompt": "Generate a horizontally tiling sky background for a voxel game, bright blue (#87CEEB) with a subtle vertical gradient. Include simple, rectangular, blocky white clouds in a pixelated style. Seamless horizontal loop, clean and cheerful aesthetic, low resolution feel.", "description": "A horizontally tiling skybox texture with blocky clouds.", "subtype": "background", "aspect_ratio": [1024, 1024], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "jump_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/jump_sound_68f19fd9-ca91-4a10-87de-021f7ec41a5c.mp3", "type": "audio", "prompt": "Short, bouncy jump sound effect for a voxel game. A light and springy 'hup' with a subtle cartoonish 'boing' quality. Quick attack, low-to-medium pitch, suggests physical effort in a playful 8-bit style. Clean and punchy, 0.3 seconds long.", "description": "A short, springy jump sound effect for the player's movement.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.0, "loop": false, "animation": null}, "block_break_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/block_break_sound_0be74345-be32-425c-a990-05676ff10ad1.mp3", "type": "audio", "prompt": "Crunchy dirt block breaking sound effect. Earthy, textured crumbling of soil and debris. Satisfying and tactile impact with a medium pitch. Minecraft-inspired voxel mining sound, non-metallic, sharp decay.", "description": "A crunchy, tactile sound effect for mining dirt blocks.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.44, "loop": false, "animation": null}, "coin_collect_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/coin_collect_sound_331f445e-5a2a-41e3-a2c8-56ba91f924a6.mp3", "type": "audio", "prompt": "High-pitched digital 'ding' sound effect for collecting items. Bright, cheerful, and musical with a shimmering resonance. Rewarding and satisfying chime, clear 8-bit aesthetic, very brief with a clean tail.", "description": "A bright, high-pitched musical chime for collecting coins.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.53, "loop": false, "animation": null}, "victory_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/victory_sound_c2d0a451-90e6-410f-95a7-2e43118d2b07.mp3", "type": "audio", "prompt": "Short triumphant victory jingle, 2 seconds long. Celebratory ascending major scale notes using bright glockenspiel and warm synth pads. Suggests accomplishment and success, ending on a high, sustained chord. Positive, rewarding, and high-energy.", "description": "A triumphant musical fanfare for completing the level.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 3.0, "loop": false, "animation": null}, "background_music": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/background_music_d9fd3a35-495d-4423-8a7c-7b7bd0943917.mp3", "type": "audio", "prompt": "Looping background music for a voxel adventure game. Calm, exploratory mood with a medium tempo around 110 BPM. Features soft piano melodies, light plucked acoustic guitar, and airy synth pads. Upbeat but relaxing, non-intrusive, suggesting discovery. Seamless loop, no vocals, instrumental only.", "description": "Calm and exploratory background music for the floating islands level.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30.0, "loop": true, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n<head>\n    <title>BlockVerse Adventure</title>\n    <style>\n        /* CSS Reset */\n        body, html {\n            margin: 0;\n            padding: 0;\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            touch-action: none;\n            -ms-touch-action: none;\n            font-family: 'Press Start 2P', monospace;\n        }\n\n        /* CSS Custom Properties */\n        :root {\n            --transition-fast: 150ms;\n            --transition-normal: 300ms;\n            --transition-slow: 500ms;\n            --ease-out: cubic-bezier(0.0, 0, 0.2, 1);\n            --ease-bounce: cubic-bezier(0.68, -0.6, 0.32, 1.6);\n            --space-xs: 8px;\n            --space-sm: 16px;\n            --space-md: 24px;\n            --space-lg: 32px;\n            --space-xl: 48px;\n        }\n\n        /* Game Container */\n        #game-world {\n            position: relative;\n            width: 100%;\n            height: 100%;\n            background-color: #87CEEB;\n        }\n\n        #game-canvas {\n            width: 100%;\n            height: 100%;\n            display: block;\n        }\n\n        /* UI Overlay */\n        .ui-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n        }\n\n        .ui-overlay > * {\n            pointer-events: auto;\n        }\n\n        /* HUD Elements */\n        .hud {\n            position: absolute;\n            top: var(--space-sm);\n            left: var(--space-sm);\n            color: white;\n            font-size: 24px;\n            text-shadow: 3px 3px 6px rgba(0,0,0,0.9);\n            pointer-events: none;\n            z-index: 10;\n        }\n\n        /* Crosshair */\n        .crosshair {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            width: 24px;\n            height: 24px;\n            pointer-events: none;\n            z-index: 5;\n        }\n\n        .crosshair::before,\n        .crosshair::after {\n            content: '';\n            position: absolute;\n            background: white;\n            box-shadow: 0 0 3px rgba(0,0,0,0.8);\n        }\n\n        .crosshair::before {\n            top: 50%;\n            left: 0;\n            width: 100%;\n            height: 3px;\n            transform: translateY(-50%);\n        }\n\n        .crosshair::after {\n            left: 50%;\n            top: 0;\n            width: 3px;\n            height: 100%;\n            transform: translateX(-50%);\n        }\n\n        /* Mobile Controls */\n        .mobile-controls {\n            position: absolute;\n            bottom: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            display: none;\n            z-index: 20;\n        }\n\n        .mobile-controls.active {\n            display: block;\n        }\n\n        .joystick {\n            position: absolute;\n            width: 160px;\n            height: 160px;\n            pointer-events: auto;\n        }\n\n        .joystick-base {\n            position: absolute;\n            width: 100%;\n            height: 100%;\n            border-radius: 50%;\n            background: rgba(255, 255, 255, 0.3);\n            border: 4px solid rgba(255, 255, 255, 0.6);\n            box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n        }\n\n        .joystick-knob {\n            position: absolute;\n            width: 64px;\n            height: 64px;\n            border-radius: 50%;\n            background: rgba(255, 255, 255, 0.9);\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            border: 4px solid white;\n            box-shadow: 0 2px 8px rgba(0,0,0,0.4);\n            transition: transform var(--transition-fast) var(--ease-out);\n        }\n\n        .action-button {\n            position: absolute;\n            width: 96px;\n            height: 96px;\n            border-radius: 50%;\n            background: rgba(255, 255, 255, 0.9);\n            border: 4px solid white;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 40px;\n            color: #333;\n            font-weight: bold;\n            pointer-events: auto;\n            user-select: none;\n            box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n            transition: all var(--transition-fast) var(--ease-out);\n        }\n\n        .action-button:active {\n            background: rgba(200, 200, 200, 0.9);\n            transform: scale(0.95);\n        }\n\n        .jump-button {\n            bottom: var(--space-lg);\n            right: var(--space-lg);\n        }\n\n        .mine-button {\n            bottom: var(--space-lg);\n            right: 144px;\n        }\n\n        .interact-button {\n            bottom: var(--space-lg);\n            left: 50%;\n            transform: translateX(-50%);\n            display: none;\n            background: rgba(76, 175, 80, 0.9);\n        }\n\n        .interact-button.visible {\n            display: flex;\n        }\n\n        .interact-button:active {\n            background: rgba(56, 142, 60, 0.9);\n        }\n\n        /* Victory Screen */\n        .victory-screen {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0, 0, 0, 0.85);\n            display: none;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            pointer-events: auto;\n            z-index: 100;\n        }\n\n        .victory-screen.visible {\n            display: flex;\n            animation: fadeIn var(--transition-slow) var(--ease-out);\n        }\n\n        @keyframes fadeIn {\n            from { opacity: 0; }\n            to { opacity: 1; }\n        }\n\n        .victory-title {\n            font-size: 56px;\n            color: #FFD700;\n            margin-bottom: var(--space-lg);\n            text-shadow: 4px 4px 8px rgba(0,0,0,0.9);\n            animation: bounce 1s var(--ease-bounce);\n        }\n\n        @keyframes bounce {\n            0%, 100% { transform: translateY(0); }\n            50% { transform: translateY(-20px); }\n        }\n\n        .victory-stats {\n            font-size: 28px;\n            color: white;\n            margin-bottom: var(--space-lg);\n            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);\n        }\n\n        .restart-button {\n            padding: var(--space-md) var(--space-xl);\n            font-size: 24px;\n            background: #4CAF50;\n            color: white;\n            border: 4px solid white;\n            border-radius: 8px;\n            cursor: pointer;\n            font-family: 'Press Start 2P', monospace;\n            box-shadow: 0 4px 12px rgba(0,0,0,0.5);\n            transition: all var(--transition-fast) var(--ease-out);\n        }\n\n        .restart-button:hover {\n            background: #66BB6A;\n            transform: translateY(-2px);\n            box-shadow: 0 6px 16px rgba(0,0,0,0.6);\n        }\n\n        .restart-button:active {\n            background: #388E3C;\n            transform: translateY(0);\n            box-shadow: 0 2px 8px rgba(0,0,0,0.4);\n        }\n\n        /* Tutorial Text */\n        .tutorial {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            background: rgba(0, 0, 0, 0.8);\n            padding: var(--space-lg);\n            border-radius: 8px;\n            border: 3px solid white;\n            color: white;\n            font-size: 18px;\n            text-align: center;\n            max-width: 500px;\n            pointer-events: none;\n            display: none;\n            z-index: 50;\n            box-shadow: 0 8px 24px rgba(0,0,0,0.6);\n        }\n\n        .tutorial.visible {\n            display: block;\n            animation: fadeIn var(--transition-slow) var(--ease-out);\n        }\n\n        /* Media Query for Reduced Motion */\n        @media (prefers-reduced-motion: reduce) {\n            .victory-title {\n                animation: none;\n            }\n            .victory-screen {\n                animation: none;\n            }\n            .tutorial {\n                animation: none;\n            }\n        }\n    </style>\n    <link href=\"https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap\" rel=\"stylesheet\">\n</head>\n<body>\n    <div id=\"game-world\">\n        <canvas id=\"game-canvas\"></canvas>\n        <div class=\"ui-overlay\">\n            <div class=\"hud\">\n                <div>COINS: <span id=\"coin-count\">0</span>/<span id=\"total-coins\">0</span></div>\n                <div style=\"margin-top: 10px;\">TIME: <span id=\"timer\">00:00.00</span></div>\n            </div>\n            <div class=\"crosshair\"></div>\n            <div class=\"tutorial visible\" id=\"tutorial\">\n                <div id=\"tutorial-text\">WASD to move<br>Mouse to look<br>SPACE to jump<br>CLICK to mine</div>\n            </div>\n            <div class=\"mobile-controls\" id=\"mobile-controls\">\n                <div class=\"joystick\" id=\"move-joystick\" style=\"bottom: 32px; left: 32px;\">\n                    <div class=\"joystick-base\">\n                        <div class=\"joystick-knob\"></div>\n                    </div>\n                </div>\n                <div class=\"joystick\" id=\"look-joystick\" style=\"bottom: 220px; right: 32px;\">\n                    <div class=\"joystick-base\">\n                        <div class=\"joystick-knob\"></div>\n                    </div>\n                </div>\n                <div class=\"action-button jump-button\" id=\"jump-button\">\u2191</div>\n                <div class=\"action-button mine-button\" id=\"mine-button\">\u26cf</div>\n            </div>\n            <div class=\"action-button interact-button\" id=\"interact-button\">E</div>\n            <div class=\"victory-screen\" id=\"victory-screen\">\n                <div class=\"victory-title\">VICTORY!</div>\n                <div class=\"victory-stats\">\n                    <div>COINS: <span id=\"final-coins\">0</span>/<span id=\"final-total\">0</span></div>\n                    <div>TIME: <span id=\"final-time\">00:00.00</span></div>\n                </div>\n                <div id=\"leaderboard\" style=\"margin-bottom: 24px; color: white; text-align: center; width: 80%; background: rgba(255,255,255,0.1); padding: 15px; border-radius: 8px;\">\n                    <div style=\"color: #FFD700; margin-bottom: 10px; font-size: 20px;\">TOP SPEEDRUNNERS</div>\n                    <div id=\"leaderboard-list\">Loading...</div>\n                </div>\n                <button class=\"restart-button\" id=\"restart-button\">PLAY AGAIN</button>\n            </div>\n        </div>\n    </div>\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js\"></script>\n    <script>\n        /**\n         * BlockVerse Adventure\n         * \n         * A first-person 3D platformer set in a Minecraft-inspired voxel world.\n         * Players navigate through a blocky landscape, jumping across platforms,\n         * mining destructible blocks, and collecting coins to reach the treasure chest goal.\n         * \n         * Core Mechanics:\n         * - First-person movement with WASD and camera control via mouse/touch\n         * - Jumping physics with gravity\n         * - Block mining system (destructible dirt blocks)\n         * - Coin collection with satisfying feedback\n         * - Fall respawn system when entering void\n         * - Victory condition when reaching treasure chest\n         * \n         * Technical Implementation:\n         * - Three.js for 3D rendering\n         * - Voxel-based world construction\n         * - Dual joystick mobile controls\n         * - Box collision detection\n         * - Web Audio API for sound effects\n         */\n\n        async function run(mode) {\n            const gameWorld = document.getElementById('game-world');\n            const canvas = document.getElementById('game-canvas');\n\n            // Initialize Three.js renderer and scene\n            const scene = new THREE.Scene();\n            const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);\n            camera.rotation.order = 'YXZ';\n            const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });\n            \n            function resizeRenderer() {\n                const width = gameWorld.clientWidth;\n                const height = gameWorld.clientHeight;\n                renderer.setSize(width, height);\n                camera.aspect = width / height;\n                camera.updateProjectionMatrix();\n            }\n            resizeRenderer();\n            window.addEventListener('resize', resizeRenderer);\n\n            // Audio Context for sound effects\n            const audioContext = new (window.AudioContext || window.webkitAudioContext)();\n            const audioBuffers = {};\n\n            // Asset preloading\n            const assetCache = {};\n            async function preloadAssets() {\n                const assetIds = [\n                    'grass_block_texture', 'dirt_block_texture', 'stone_block_texture',\n                    'coin_sprite', 'treasure_chest_texture', 'sky_background',\n                    'jump_sound', 'block_break_sound', 'coin_collect_sound',\n                    'victory_sound', 'background_music'\n                ];\n\n                const loadPromises = assetIds.map(id => {\n                    return new Promise((resolve) => {\n                        const assetInfo = lib.getAsset(id);\n                        if (!assetInfo) {\n                            console.warn(`Asset ${id} not found`);\n                            resolve();\n                            return;\n                        }\n\n                        if (assetInfo.type === 'audio') {\n                            fetch(assetInfo.url)\n                                .then(response => response.arrayBuffer())\n                                .then(arrayBuffer => audioContext.decodeAudioData(arrayBuffer))\n                                .then(buffer => {\n                                    audioBuffers[id] = buffer;\n                                    resolve();\n                                })\n                                .catch(err => {\n                                    console.warn(`Failed to load audio ${id}:`, err);\n                                    resolve();\n                                });\n                        } else {\n                            const loader = new THREE.TextureLoader();\n                            loader.load(\n                                assetInfo.url,\n                                (texture) => {\n                                    texture.magFilter = THREE.NearestFilter;\n                                    texture.minFilter = THREE.NearestFilter;\n                                    assetCache[id] = texture;\n                                    resolve();\n                                },\n                                undefined,\n                                (err) => {\n                                    console.warn(`Failed to load texture ${id}:`, err);\n                                    resolve();\n                                }\n                            );\n                        }\n                    });\n                });\n\n                await Promise.all(loadPromises);\n            }\n\n            await preloadAssets();\n\n            // Play sound helper function\n            function playSound(soundId, loop = false) {\n                if (!audioBuffers[soundId]) return null;\n                \n                const source = audioContext.createBufferSource();\n                source.buffer = audioBuffers[soundId];\n                source.loop = loop;\n                \n                const gainNode = audioContext.createGain();\n                gainNode.gain.value = soundId === 'background_music' ? 0.3 : 0.5;\n                \n                source.connect(gainNode);\n                gainNode.connect(audioContext.destination);\n                source.start(0);\n                \n                return source;\n            }\n\n            // Start background music in play mode\n            let bgMusic = null;\n            if (mode === 'play') {\n                bgMusic = playSound('background_music', true);\n            }\n\n            // Scene lighting setup\n            scene.background = new THREE.Color(window.gameConfig.settings.skyColor);\n            \n            const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);\n            scene.add(ambientLight);\n            \n            const directionalLight = new THREE.DirectionalLight(0xffffff, 0.4);\n            directionalLight.position.set(5, 10, 5);\n            scene.add(directionalLight);\n\n            // Skybox for immersive environment\n            if (assetCache['sky_background']) {\n                const skyGeo = new THREE.SphereGeometry(500, 32, 32);\n                const skyMat = new THREE.MeshBasicMaterial({\n                    map: assetCache['sky_background'],\n                    side: THREE.BackSide\n                });\n                const skyMesh = new THREE.Mesh(skyGeo, skyMat);\n                scene.add(skyMesh);\n            }\n\n            // Block materials with pixelated textures\n            const materials = {\n                grass: new THREE.MeshLambertMaterial({\n                    map: assetCache['grass_block_texture']\n                }),\n                dirt: new THREE.MeshLambertMaterial({\n                    map: assetCache['dirt_block_texture']\n                }),\n                stone: new THREE.MeshLambertMaterial({\n                    map: assetCache['stone_block_texture']\n                })\n            };\n\n            // Game state (runtime variables, NOT in gameConfig)\n            const state = {\n                player: {\n                    position: new THREE.Vector3(...window.gameConfig.settings.respawnPoint),\n                    velocity: new THREE.Vector3(0, 0, 0),\n                    rotation: new THREE.Euler(0, 0, 0, 'YXZ'),\n                    onGround: false,\n                    height: 1.0,\n                    radius: 0.2,\n                    model: null,\n                    walkTime: 0\n                },\n                coinsCollected: 0,\n                totalCoins: window.gameConfig.coins.length,\n                startTime: 0,\n                elapsedTime: 0,\n                timerStarted: false,\n                gameOver: false,\n                won: false,\n                keys: {},\n                pointerLocked: false,\n                isClimbing: false,\n                currentCheckpoint: null,\n                checkpointsReached: [],\n                movingPlatforms: [],\n                slimeBouncers: []\n            };\n\n            // Level container for easy resetting\n            let levelGroup = new THREE.Group();\n            scene.add(levelGroup);\n            let blockMeshes = [];\n            let coinMeshes = [];\n            let checkpointMeshes = [];\n            let ladderMeshes = [];\n            let treasureChest = null;\n\n            // Reset game session without page reload\n            async function resetGame() {\n                state.gameOver = false;\n                state.won = false;\n                state.coinsCollected = 0;\n                state.elapsedTime = 0;\n                state.timerStarted = false;\n                state.currentCheckpoint = null;\n                state.checkpointsReached = [];\n                state.movingPlatforms = [];\n                state.slimeBouncers = [];\n                \n                // Clear and recreate level\n                scene.remove(levelGroup);\n                levelGroup = new THREE.Group();\n                scene.add(levelGroup);\n                blockMeshes = [];\n                coinMeshes = [];\n                checkpointMeshes = [];\n                ladderMeshes = [];\n                \n                buildLevel();\n                \n                // Reset Player\n                state.player.position.set(...window.gameConfig.settings.respawnPoint);\n                state.player.velocity.set(0, 0, 0);\n                state.player.rotation.set(0, 0, 0, 'YXZ');\n                camera.position.copy(state.player.position);\n                camera.rotation.copy(state.player.rotation);\n                \n                // UI\n                updateCoinUI();\n                document.getElementById('timer').textContent = \"00:00.00\";\n                document.getElementById('victory-screen').classList.remove('visible');\n                \n                // Audio\n                if (bgMusic) bgMusic.stop();\n                bgMusic = playSound('background_music', true);\n                \n                // Clear saved progress for fresh start\n                await lib.deleteUserGameState();\n                \n                lib.log(\"Game Reset Successful\");\n            }\n\n            // Move object creation into a function\n            function buildLevel() {\n                // Create platforms\n                window.gameConfig.platforms.forEach(platform => {\n                    const [px, py, pz] = platform.position;\n                    const [sx, sy, sz] = platform.size;\n                    for (let x = 0; x < sx; x++) {\n                        for (let y = 0; y < sy; y++) {\n                            for (let z = 0; z < sz; z++) {\n                                createBlock(px + x, py + y, pz + z, platform.blockType, !platform.indestructible);\n                            }\n                        }\n                    }\n                });\n\n                // Create individual blocks\n                window.gameConfig.blocks.forEach(block => {\n                    createBlock(...block.position, block.blockType, block.destructible);\n                });\n\n                // Create ladders\n                if (window.gameConfig.ladders) {\n                    window.gameConfig.ladders.forEach(ladder => {\n                        const [lx, ly, lz] = ladder.position;\n                        const height = ladder.height;\n                        const group = new THREE.Group();\n                        group.rotation.y = ladder.rotationY || 0;\n                        const railGeo = new THREE.BoxGeometry(0.1, height, 0.1);\n                        const woodMat = new THREE.MeshLambertMaterial({ color: 0x8B4513 });\n                        const leftRail = new THREE.Mesh(railGeo, woodMat);\n                        leftRail.position.set(-0.4, height/2 - 0.5, 0);\n                        group.add(leftRail);\n                        const rightRail = new THREE.Mesh(railGeo, woodMat);\n                        rightRail.position.set(0.4, height/2 - 0.5, 0);\n                        group.add(rightRail);\n                        const rungGeo = new THREE.BoxGeometry(0.8, 0.05, 0.05);\n                        for (let h = 0; h < height; h += 0.5) {\n                            const rung = new THREE.Mesh(rungGeo, woodMat);\n                            rung.position.set(0, h - 0.5, 0);\n                            group.add(rung);\n                        }\n                        group.position.set(lx, ly, lz);\n                        levelGroup.add(group);\n                        const climbGeo = new THREE.BoxGeometry(1.2, height, 0.6);\n                        const climbMesh = new THREE.Mesh(climbGeo, new THREE.MeshBasicMaterial({ visible: false }));\n                        climbMesh.rotation.y = ladder.rotationY || 0;\n                        climbMesh.position.set(lx, ly + height/2 - 0.5, lz);\n                        levelGroup.add(climbMesh);\n                        ladderMeshes.push({ mesh: climbMesh, height, bottom: ly });\n                    });\n                }\n\n                // Create checkpoints\n                if (window.gameConfig.checkpoints) {\n                    window.gameConfig.checkpoints.forEach(checkpoint => {\n                        const [cx, cy, cz] = checkpoint.position;\n                        const group = new THREE.Group();\n                        const base = new THREE.Mesh(new THREE.CylinderGeometry(0.6, 0.8, 0.2, 8), new THREE.MeshLambertMaterial({ color: 0x444444 }));\n                        base.position.y = 0.1;\n                        group.add(base);\n                        const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.1, 2, 8), new THREE.MeshLambertMaterial({ color: 0x888888 }));\n                        pole.position.y = 1.2;\n                        group.add(pole);\n                        const beacon = new THREE.Mesh(new THREE.OctahedronGeometry(0.4, 0), new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.8 }));\n                        beacon.position.y = 2.3;\n                        group.add(beacon);\n                        const glow = new THREE.Mesh(new THREE.SphereGeometry(0.6, 16, 16), new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.2 }));\n                        glow.position.y = 2.3;\n                        group.add(glow);\n                        group.position.set(cx, cy, cz);\n                        levelGroup.add(group);\n                        checkpointMeshes.push({ id: checkpoint.id, position: new THREE.Vector3(cx, cy + 1, cz), group, beacon, glow, reached: false });\n                    });\n                }\n\n                // Create moving platforms\n                if (window.gameConfig.movingPlatforms) {\n                    window.gameConfig.movingPlatforms.forEach(config => {\n                        const mesh = new THREE.Mesh(new THREE.BoxGeometry(...config.size), materials[config.blockType] || materials.stone);\n                        mesh.position.set(...config.startPos);\n                        levelGroup.add(mesh);\n                        blockMeshes.push(mesh);\n                        state.movingPlatforms.push({ mesh, config, velocity: new THREE.Vector3(0, 0, 0) });\n                    });\n                }\n\n                // Create slime bouncers\n                if (window.gameConfig.slimeBouncers) {\n                    const slimeMat = new THREE.MeshLambertMaterial({ color: 0x32CD32, transparent: true, opacity: 0.8 });\n                    window.gameConfig.slimeBouncers.forEach(config => {\n                        const mesh = new THREE.Mesh(new THREE.BoxGeometry(...config.size), slimeMat);\n                        mesh.position.set(...config.position);\n                        levelGroup.add(mesh);\n                        blockMeshes.push(mesh);\n                        state.slimeBouncers.push({ mesh, config });\n                    });\n                }\n\n                // Create coins\n                if (assetCache['coin_sprite']) {\n                    const coinGeo = new THREE.PlaneGeometry(0.5, 0.5);\n                    const coinMat = new THREE.MeshBasicMaterial({ map: assetCache['coin_sprite'], transparent: true, side: THREE.DoubleSide });\n                    window.gameConfig.coins.forEach(coin => {\n                        const mesh = new THREE.Mesh(coinGeo, coinMat);\n                        mesh.position.set(...coin.position);\n                        mesh.userData = { id: coin.id, collected: false };\n                        levelGroup.add(mesh);\n                        coinMeshes.push(mesh);\n                    });\n                }\n\n                // Create treasure chest\n                if (assetCache['treasure_chest_texture']) {\n                    treasureChest = new THREE.Mesh(new THREE.BoxGeometry(1, 0.8, 0.8), new THREE.MeshLambertMaterial({ map: assetCache['treasure_chest_texture'] }));\n                    treasureChest.position.set(...window.gameConfig.treasureChest.position);\n                    treasureChest.position.y += 0.4;\n                    levelGroup.add(treasureChest);\n                }\n            }\n\n            function createBlock(x, y, z, type, destructible = false) {\n                const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materials[type] || materials.dirt);\n                mesh.position.set(x, y, z);\n                mesh.userData = { type, destructible };\n                levelGroup.add(mesh);\n                blockMeshes.push(mesh);\n                return mesh;\n            }\n\n            // Load saved checkpoint progress\n            async function loadCheckpointProgress() {\n                try {\n                    const savedData = await lib.getUserGameState();\n                    if (savedData && savedData.state) {\n                        const checkpointId = savedData.state.lastCheckpoint;\n                        if (checkpointId && window.gameConfig.checkpoints) {\n                            const checkpoint = window.gameConfig.checkpoints.find(cp => cp.id === checkpointId);\n                            if (checkpoint) {\n                                // Spawn at saved checkpoint\n                                state.player.position.set(\n                                    checkpoint.position[0],\n                                    checkpoint.position[1] + 3,\n                                    checkpoint.position[2]\n                                );\n                                state.currentCheckpoint = checkpointId;\n                                state.checkpointsReached = savedData.state.checkpointsReached || [checkpointId];\n                                lib.log(`Loaded checkpoint: ${checkpointId}`);\n                            }\n                        }\n                    }\n                } catch (error) {\n                    lib.log(`Failed to load checkpoint: ${error.message}`);\n                }\n            }\n\n            // Save checkpoint progress\n            async function saveCheckpointProgress(checkpointId) {\n                try {\n                    const saveData = {\n                        lastCheckpoint: checkpointId,\n                        checkpointsReached: state.checkpointsReached\n                    };\n                    await lib.saveUserGameState(saveData);\n                    lib.log(`Checkpoint saved: ${checkpointId}`);\n                } catch (error) {\n                    lib.log(`Failed to save checkpoint: ${error.message}`);\n                }\n            }\n\n            // Create Player Model (Character Controller)\n            function createPlayerModel() {\n                const group = new THREE.Group();\n                \n                const skinColor = 0xffdbac;\n                const shirtColor = 0x00ced1;\n                const pantsColor = 0x0000ff;\n                \n                // Head\n                const headGeo = new THREE.BoxGeometry(0.2, 0.2, 0.2);\n                const headMat = new THREE.MeshLambertMaterial({ color: skinColor });\n                const head = new THREE.Mesh(headGeo, headMat);\n                head.position.y = 0.9;\n                group.add(head);\n                \n                // Body\n                const bodyGeo = new THREE.BoxGeometry(0.2, 0.32, 0.1);\n                const bodyMat = new THREE.MeshLambertMaterial({ color: shirtColor });\n                const body = new THREE.Mesh(bodyGeo, bodyMat);\n                body.position.y = 0.64;\n                group.add(body);\n                \n                // Leg Group (to rotate around hip)\n                function createLeg(x) {\n                    const legPivot = new THREE.Group();\n                    legPivot.position.set(x, 0.48, 0);\n                    const legGeo = new THREE.BoxGeometry(0.09, 0.32, 0.09);\n                    const legMat = new THREE.MeshLambertMaterial({ color: pantsColor });\n                    const legMesh = new THREE.Mesh(legGeo, legMat);\n                    legMesh.position.y = -0.16; \n                    legPivot.add(legMesh);\n                    return legPivot;\n                }\n                \n                const leftLeg = createLeg(-0.05);\n                const rightLeg = createLeg(0.05);\n                group.add(leftLeg, rightLeg);\n                \n                // Arm Group (to rotate around shoulder)\n                function createArm(x) {\n                    const armPivot = new THREE.Group();\n                    armPivot.position.set(x, 0.76, 0);\n                    const armGeo = new THREE.BoxGeometry(0.09, 0.32, 0.09);\n                    const armMat = new THREE.MeshLambertMaterial({ color: skinColor });\n                    const armMesh = new THREE.Mesh(armGeo, armMat);\n                    armMesh.position.y = -0.16;\n                    armPivot.add(armMesh);\n                    return armPivot;\n                }\n                \n                const leftArm = createArm(-0.14);\n                const rightArm = createArm(0.14);\n                group.add(leftArm, rightArm);\n                \n                group.userData = { head, body, leftLeg, rightLeg, leftArm, rightArm };\n                return group;\n            }\n\n            state.player.model = createPlayerModel();\n            scene.add(state.player.model);\n\n            // Initial level build\n            buildLevel();\n\n            function checkCollision(pos, floorPadding = 0) {\n                const playerBox = {\n                    min: {\n                        x: pos.x - state.player.radius,\n                        y: pos.y - state.player.height + floorPadding,\n                        z: pos.z - state.player.radius\n                    },\n                    max: {\n                        x: pos.x + state.player.radius,\n                        y: pos.y + 0.2,\n                        z: pos.z + state.player.radius\n                    }\n                };\n\n                for (let mesh of blockMeshes) {\n                    const blockPos = mesh.position;\n                    // Support different sizes for moving platforms (standard blocks are 1x1x1)\n                    const halfW = (mesh.geometry.parameters.width / 2) || 0.5;\n                    const halfH = (mesh.geometry.parameters.height / 2) || 0.5;\n                    const halfD = (mesh.geometry.parameters.depth / 2) || 0.5;\n\n                    const blockBox = {\n                        min: {\n                            x: blockPos.x - halfW,\n                            y: blockPos.y - halfH,\n                            z: blockPos.z - halfD\n                        },\n                        max: {\n                            x: blockPos.x + halfW,\n                            y: blockPos.y + halfH,\n                            z: blockPos.z + halfD\n                        }\n                    };\n\n                    if (boxIntersect(playerBox, blockBox)) {\n                        return mesh;\n                    }\n                }\n\n                return null;\n            }\n\n            // Create treasure chest (goal object)\n            // buildLevel() already handles this.\n\n            // Initialize camera to player position\n            camera.position.copy(state.player.position);\n            camera.rotation.copy(state.player.rotation);\n\n            // Load checkpoint progress in play mode\n            if (mode === 'play') {\n                await loadCheckpointProgress();\n                \n                // Update camera to loaded position\n                camera.position.copy(state.player.position);\n                \n                // Mark checkpoints as reached if loaded from save\n                if (state.checkpointsReached.length > 0) {\n                    checkpointMeshes.forEach(cp => {\n                        if (state.checkpointsReached.includes(cp.id)) {\n                            cp.reached = true;\n                            // Change color to green for reached checkpoints\n                            cp.beacon.material.color.setHex(0x00ff00);\n                            cp.glow.material.color.setHex(0x00ff00);\n                        }\n                    });\n                }\n            }\n            canvas.addEventListener('click', () => {\n                if (mode === 'play' && !state.gameOver) {\n                    canvas.requestPointerLock();\n                }\n            });\n\n            document.addEventListener('pointerlockchange', () => {\n                state.pointerLocked = document.pointerLockElement === canvas;\n            });\n\n            // Mouse look control\n            window.addEventListener('mousemove', (e) => {\n                if (state.pointerLocked && !state.gameOver) {\n                    state.player.rotation.y -= e.movementX * 0.002;\n                    state.player.rotation.x -= e.movementY * 0.002;\n                    // Clamp vertical rotation to prevent flipping\n                    state.player.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, state.player.rotation.x));\n                }\n            });\n\n            // Keyboard controls\n            window.addEventListener('keydown', (e) => {\n                state.keys[e.key.toLowerCase()] = true;\n                \n                // Handle jump via keyboard\n                if (e.code === 'Space' && state.player.onGround && !state.gameOver) {\n                    state.player.velocity.y = 8;\n                    playSound('jump_sound');\n                }\n\n                // Handle interaction via keyboard\n                if (e.key.toLowerCase() === 'e' && !state.gameOver) {\n                    checkTreasureInteraction();\n                }\n            });\n\n            window.addEventListener('keyup', (e) => {\n                state.keys[e.key.toLowerCase()] = false;\n            });\n\n            // Mining mechanic (left click)\n            window.addEventListener('mousedown', (e) => {\n                if (mode === 'play' && e.button === 0 && state.pointerLocked && !state.gameOver) {\n                    mineBlock();\n                }\n            });\n\n            function mineBlock() {\n                const raycaster = new THREE.Raycaster();\n                raycaster.setFromCamera(new THREE.Vector2(0, 0), camera);\n                \n                const intersects = raycaster.intersectObjects(blockMeshes);\n                \n                if (intersects.length > 0) {\n                    const hit = intersects[0];\n                    if (hit.distance <= 3 && hit.object.userData.destructible) {\n                        levelGroup.remove(hit.object);\n                        const index = blockMeshes.indexOf(hit.object);\n                        if (index > -1) {\n                            blockMeshes.splice(index, 1);\n                        }\n                        playSound('block_break_sound');\n                    }\n                }\n            }\n\n            // Detect mobile device\n            const isMobile = lib.shouldUseTouchControls();\n            \n            if (isMobile) {\n                document.getElementById('mobile-controls').classList.add('active');\n                setupMobileControls();\n            }\n\n            // Always setup interact button click for desktop/mobile\n            document.getElementById('interact-button').addEventListener('click', () => {\n                checkTreasureInteraction();\n            });\n\n            function setupMobileControls() {\n                setupJoystick('move-joystick');\n                setupJoystick('look-joystick');\n\n                document.getElementById('jump-button').addEventListener('touchstart', (e) => {\n                    e.preventDefault();\n                    if (state.player.onGround && !state.gameOver) {\n                        state.player.velocity.y = 8;\n                        playSound('jump_sound');\n                    }\n                });\n\n                document.getElementById('mine-button').addEventListener('touchstart', (e) => {\n                    e.preventDefault();\n                    if (!state.gameOver) {\n                        mineBlock();\n                    }\n                });\n\n                document.getElementById('interact-button').addEventListener('touchstart', (e) => {\n                    e.preventDefault();\n                    checkTreasureInteraction();\n                });\n            }\n\n            function setupJoystick(elementId) {\n                const joystick = document.getElementById(elementId);\n                const knob = joystick.querySelector('.joystick-knob');\n                const maxRadius = 80;\n                const deadZone = 15;\n                \n                let active = false;\n                let touchId = null;\n                let centerX = 0;\n                let centerY = 0;\n                let currentX = 0;\n                let currentY = 0;\n\n                function updateKnob() {\n                    const dx = currentX - centerX;\n                    const dy = currentY - centerY;\n                    const distance = Math.sqrt(dx * dx + dy * dy);\n                    \n                    let finalX = dx;\n                    let finalY = dy;\n                    \n                    if (distance > maxRadius) {\n                        const angle = Math.atan2(dy, dx);\n                        finalX = Math.cos(angle) * maxRadius;\n                        finalY = Math.sin(angle) * maxRadius;\n                    }\n                    \n                    knob.style.transform = `translate(${finalX}px, ${finalY}px)`;\n                    \n                    const normalizedDx = distance > deadZone ? finalX / maxRadius : 0;\n                    const normalizedDy = distance > deadZone ? finalY / maxRadius : 0;\n                    \n                    joystick.userData = { dx: normalizedDx, dy: normalizedDy };\n                }\n\n                joystick.addEventListener('touchstart', (e) => {\n                    if (!active) {\n                        active = true;\n                        touchId = e.touches[0].identifier;\n                        const rect = joystick.getBoundingClientRect();\n                        centerX = rect.left + rect.width / 2;\n                        centerY = rect.top + rect.height / 2;\n                        currentX = e.touches[0].clientX;\n                        currentY = e.touches[0].clientY;\n                        updateKnob();\n                    }\n                    e.preventDefault();\n                });\n\n                document.addEventListener('touchmove', (e) => {\n                    if (active) {\n                        for (let touch of e.touches) {\n                            if (touch.identifier === touchId) {\n                                currentX = touch.clientX;\n                                currentY = touch.clientY;\n                                updateKnob();\n                                break;\n                            }\n                        }\n                    }\n                });\n\n                document.addEventListener('touchend', (e) => {\n                    if (active) {\n                        let touchEnded = true;\n                        for (let touch of e.touches) {\n                            if (touch.identifier === touchId) {\n                                touchEnded = false;\n                                break;\n                            }\n                        }\n                        if (touchEnded) {\n                            active = false;\n                            touchId = null;\n                            knob.style.transform = 'translate(0, 0)';\n                            joystick.userData = { dx: 0, dy: 0 };\n                        }\n                    }\n                });\n\n                joystick.userData = { dx: 0, dy: 0 };\n            }\n\n            function boxIntersect(a, b) {\n                return (a.min.x <= b.max.x && a.max.x >= b.min.x) &&\n                       (a.min.y <= b.max.y && a.max.y >= b.min.y) &&\n                       (a.min.z <= b.max.z && a.max.z >= b.min.z);\n            }\n\n            function formatTime(seconds) {\n                const mins = Math.floor(seconds / 60);\n                const secs = Math.floor(seconds % 60);\n                const ms = Math.floor((seconds % 1) * 100);\n                return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}.${ms.toString().padStart(2, '0')}`;\n            }\n\n            // Coin collection system\n            function checkCoinCollection() {\n                coinMeshes.forEach(coin => {\n                    if (!coin.userData.collected) {\n                        const distance = state.player.position.distanceTo(coin.position);\n                        if (distance < 1) {\n                            coin.userData.collected = true;\n                            levelGroup.remove(coin);\n                            state.coinsCollected++;\n                            playSound('coin_collect_sound');\n                            updateCoinUI();\n                        }\n                    }\n                });\n            }\n\n            // Checkpoint system\n            function checkCheckpointCollection() {\n                checkpointMeshes.forEach(checkpoint => {\n                    if (!checkpoint.reached) {\n                        const distance = state.player.position.distanceTo(checkpoint.position);\n                        if (distance < 2) {\n                            checkpoint.reached = true;\n                            state.currentCheckpoint = checkpoint.id;\n                            \n                            // Add to reached checkpoints if not already there\n                            if (!state.checkpointsReached.includes(checkpoint.id)) {\n                                state.checkpointsReached.push(checkpoint.id);\n                            }\n                            \n                            // Visual feedback - change color to green\n                            checkpoint.beacon.material.color.setHex(0x00ff00);\n                            checkpoint.glow.material.color.setHex(0x00ff00);\n                            \n                            // Audio feedback\n                            playSound('coin_collect_sound'); // Reusing coin sound for now\n                            \n                            // Save progress\n                            saveCheckpointProgress(checkpoint.id);\n                            \n                            // Show brief message\n                            lib.log(`Checkpoint reached: ${checkpoint.id}`);\n                        }\n                    }\n                });\n            }\n\n            function updateCoinUI() {\n                document.getElementById('coin-count').textContent = state.coinsCollected;\n            }\n\n            // Treasure chest interaction (win condition)\n            async function checkTreasureInteraction() {\n                if (treasureChest && !state.won) {\n                    const distance = state.player.position.distanceTo(treasureChest.position);\n                    if (distance < 2) {\n                        state.won = true;\n                        state.gameOver = true;\n                        showVictoryScreen();\n                        playSound('victory_sound');\n                        if (bgMusic) {\n                            bgMusic.stop();\n                        }\n                        \n                        // Submit score to leaderboard and fetch top 3\n                        await submitScoreAndShowLeaderboard();\n                    }\n                }\n            }\n            \n            // Submit speedrun time and display leaderboard\n            async function submitScoreAndShowLeaderboard() {\n                const leaderboardList = document.getElementById('leaderboard-list');\n                leaderboardList.innerHTML = 'Loading...';\n                \n                try {\n                    // Submit the player's time and get top 3 entries\n                    const response = await lib.addPlayerScoreToLeaderboard(state.elapsedTime, 3);\n                    \n                    if (response.success && response.entries && response.entries.length > 0) {\n                        // Display top 3 speedrunners\n                        let html = '';\n                        response.entries.forEach((entry, index) => {\n                            const rank = index + 1;\n                            const formattedTime = formatTime(entry.score);\n                            html += `<div style=\"margin: 8px 0; font-size: 16px;\">${rank}. ${entry.username}: ${formattedTime}</div>`;\n                        });\n                        leaderboardList.innerHTML = html;\n                        \n                        // Show player's rank if available\n                        if (response.userRank) {\n                            const rankText = document.createElement('div');\n                            rankText.style.marginTop = '15px';\n                            rankText.style.color = '#FFD700';\n                            rankText.style.fontSize = '14px';\n                            rankText.textContent = `Your Rank: #${response.userRank}`;\n                            leaderboardList.appendChild(rankText);\n                        }\n                    } else {\n                        leaderboardList.innerHTML = '<div style=\"font-size: 14px;\">No scores yet. Be the first!</div>';\n                    }\n                } catch (error) {\n                    lib.log(`Failed to submit score to leaderboard: ${error.message}`);\n                    leaderboardList.innerHTML = '<div style=\"font-size: 14px;\">Leaderboard unavailable</div>';\n                }\n            }\n\n            function showVictoryScreen() {\n                document.getElementById('final-coins').textContent = state.coinsCollected;\n                document.getElementById('final-total').textContent = state.totalCoins;\n                document.getElementById('final-time').textContent = formatTime(state.elapsedTime);\n                document.getElementById('victory-screen').classList.add('visible');\n                if (state.pointerLocked) {\n                    document.exitPointerLock();\n                }\n            }\n\n            document.getElementById('restart-button').addEventListener('click', () => {\n                resetGame();\n            });\n\n            // Update interact button visibility\n            function updateInteractButton() {\n                if (treasureChest && !state.gameOver) {\n                    const distance = state.player.position.distanceTo(treasureChest.position);\n                    const interactBtn = document.getElementById('interact-button');\n                    if (distance < 3) {\n                        interactBtn.classList.add('visible');\n                    } else {\n                        interactBtn.classList.remove('visible');\n                    }\n                }\n            }\n\n            // Hide tutorial text after a few seconds\n            setTimeout(() => {\n                document.getElementById('tutorial').classList.remove('visible');\n            }, 5000);\n\n            // Initialize UI\n            document.getElementById('total-coins').textContent = state.totalCoins;\n\n            // Show game parameters panel\n            lib.showGameParameters({\n                name: 'Game Settings',\n                params: {\n                    'Sky Color': {\n                        key: 'settings.skyColor',\n                        type: 'color'\n                    },\n                    'Void Height': {\n                        key: 'settings.voidHeight',\n                        type: 'slider',\n                        min: -20,\n                        max: 0,\n                        step: 1\n                    }\n                }\n            });\n\n            // Main game loop\n            let lastTime = performance.now();\n            function gameLoop(timestamp) {\n                const deltaTime = Math.min((timestamp - lastTime) / 1000, 0.11);\n                lastTime = timestamp;\n\n                if (mode === 'play' && !state.gameOver) {\n                    // Start timer on first movement or look\n                    if (!state.timerStarted && (\n                        state.keys['w'] || state.keys['s'] || state.keys['a'] || state.keys['d'] || \n                        isMobile && (document.getElementById('move-joystick').userData.dx !== 0 || document.getElementById('move-joystick').userData.dy !== 0)\n                    )) {\n                        state.timerStarted = true;\n                        state.startTime = timestamp;\n                    }\n\n                    if (state.timerStarted) {\n                        state.elapsedTime = (timestamp - state.startTime) / 1000;\n                        document.getElementById('timer').textContent = formatTime(state.elapsedTime);\n                    }\n\n                    // Animate moving platforms\n                    state.movingPlatforms.forEach(platform => {\n                        const config = platform.config;\n                        const start = new THREE.Vector3(...config.startPos);\n                        const end = new THREE.Vector3(...config.endPos);\n                        \n                        const t = (Math.sin(timestamp * 0.001 * config.speed) + 1) / 2;\n                        const newPos = new THREE.Vector3().lerpVectors(start, end, t);\n                        \n                        platform.velocity.copy(newPos).sub(platform.mesh.position);\n                        platform.mesh.position.copy(newPos);\n                    });\n\n                    // Check ladder climbing\n                    state.isClimbing = false;\n                    const playerPos = state.player.position;\n                    const playerBox = {\n                        min: { x: playerPos.x - 0.5, y: playerPos.y - state.player.height, z: playerPos.z - 0.5 },\n                        max: { x: playerPos.x + 0.5, y: playerPos.y + 0.2, z: playerPos.z + 0.5 }\n                    };\n\n                    for (let ladder of ladderMeshes) {\n                        const ladderBox = new THREE.Box3().setFromObject(ladder.mesh);\n                        if (ladderBox.intersectsBox(new THREE.Box3(\n                            new THREE.Vector3(playerBox.min.x, playerBox.min.y, playerBox.min.z),\n                            new THREE.Vector3(playerBox.max.x, playerBox.max.y, playerBox.max.z)\n                        ))) {\n                            state.isClimbing = true;\n                            break;\n                        }\n                    }\n\n                    // Player movement\n                    const moveSpeed = 5;\n                    const climbSpeed = 4;\n                    const moveDir = new THREE.Vector3();\n\n                    // Keyboard input\n                    if (state.keys['w']) moveDir.z -= 1;\n                    if (state.keys['s']) moveDir.z += 1;\n                    if (state.keys['a']) moveDir.x -= 1;\n                    if (state.keys['d']) moveDir.x += 1;\n\n                    if (state.isClimbing) {\n                        state.player.velocity.y = 0;\n                        if (state.keys['w']) state.player.position.y += climbSpeed * deltaTime;\n                        if (state.keys['s']) state.player.position.y -= climbSpeed * deltaTime;\n                    }\n\n                    // Mobile touch controls\n                    if (isMobile) {\n                        const moveJoy = document.getElementById('move-joystick');\n                        const lookJoy = document.getElementById('look-joystick');\n\n                        if (moveJoy && moveJoy.userData) {\n                            const dx = moveJoy.userData.dx || 0;\n                            const dy = moveJoy.userData.dy || 0;\n                            \n                            if (state.isClimbing) {\n                                state.player.position.y -= dy * climbSpeed * deltaTime;\n                                moveDir.x += dx;\n                            } else {\n                                moveDir.x += dx;\n                                moveDir.z += dy;\n                            }\n                        }\n                        \n                        if (lookJoy && lookJoy.userData) {\n                            const dx = lookJoy.userData.dx || 0;\n                            const dy = lookJoy.userData.dy || 0;\n                            state.player.rotation.y -= dx * deltaTime * 3;\n                            state.player.rotation.x -= dy * deltaTime * 3;\n                            state.player.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, state.player.rotation.x));\n                        }\n                    }\n\n                    if (moveDir.length() > 0) {\n                        moveDir.normalize();\n                        const moveVector = new THREE.Vector3(moveDir.x, 0, moveDir.z);\n                        moveVector.applyAxisAngle(new THREE.Vector3(0, 1, 0), state.player.rotation.y);\n                        \n                        state.player.position.x += moveVector.x * moveSpeed * deltaTime;\n                        state.player.position.z += moveVector.z * moveSpeed * deltaTime;\n                        \n                        if (checkCollision(state.player.position, 0.1)) {\n                            state.player.position.x -= moveVector.x * moveSpeed * deltaTime;\n                            state.player.position.z -= moveVector.z * moveSpeed * deltaTime;\n                        }\n                    }\n\n                    // Apply gravity\n                    if (!state.isClimbing) {\n                        state.player.velocity.y -= 20 * deltaTime;\n                    }\n                    state.player.position.y += state.player.velocity.y * deltaTime;\n\n                    // Check vertical collision (ground)\n                    state.player.onGround = false;\n                    const collision = checkCollision(state.player.position, 0);\n                    if (collision) {\n                        const movingPlatform = state.movingPlatforms.find(p => p.mesh === collision);\n                        const slimeBouncer = state.slimeBouncers.find(s => s.mesh === collision);\n                        \n                        if (state.player.velocity.y < 0) {\n                            const halfH = collision.geometry.parameters.height / 2 || 0.5;\n                            const platformTop = collision.position.y + halfH;\n                            state.player.position.y = platformTop + state.player.height;\n                            \n                            if (slimeBouncer) {\n                                state.player.velocity.y = slimeBouncer.config.bounceStrength || 15;\n                                playSound('jump_sound');\n                            } else {\n                                state.player.velocity.y = 0;\n                                state.player.onGround = true;\n                            }\n                        }\n                        \n                        if (movingPlatform) {\n                            state.player.position.add(movingPlatform.velocity);\n                        }\n                    }\n\n                    // Respawn if fallen into void\n                    if (state.player.position.y < window.gameConfig.settings.voidHeight) {\n                        if (state.currentCheckpoint && window.gameConfig.checkpoints) {\n                            const checkpoint = window.gameConfig.checkpoints.find(cp => cp.id === state.currentCheckpoint);\n                            if (checkpoint) {\n                                state.player.position.set(checkpoint.position[0], checkpoint.position[1] + 3, checkpoint.position[2]);\n                            } else {\n                                state.player.position.copy(new THREE.Vector3(...window.gameConfig.settings.respawnPoint));\n                            }\n                        } else {\n                            state.player.position.copy(new THREE.Vector3(...window.gameConfig.settings.respawnPoint));\n                        }\n                        state.player.velocity.set(0, 0, 0);\n                    }\n\n                    // Update player model\n                    state.player.model.position.copy(state.player.position);\n                    state.player.model.position.y -= state.player.height;\n                    state.player.model.rotation.y = state.player.rotation.y;\n\n                    // Simple Animation Controller\n                    const speed = moveDir.length();\n                    if (speed > 0 && state.player.onGround) {\n                        state.player.walkTime += deltaTime * 10;\n                        const swing = Math.sin(state.player.walkTime) * 0.5;\n                        state.player.model.userData.leftLeg.rotation.x = swing;\n                        state.player.model.userData.rightLeg.rotation.x = -swing;\n                        state.player.model.userData.leftArm.rotation.x = -swing;\n                        state.player.model.userData.rightArm.rotation.x = swing;\n                    } else if (state.player.onGround) {\n                        state.player.model.userData.leftLeg.rotation.x = 0;\n                        state.player.model.userData.rightLeg.rotation.x = 0;\n                        state.player.model.userData.leftArm.rotation.x = 0;\n                        state.player.model.userData.rightArm.rotation.x = 0;\n                    } else {\n                        state.player.model.userData.leftLeg.rotation.x = -0.2;\n                        state.player.model.userData.rightLeg.rotation.x = 0.2;\n                        state.player.model.userData.leftArm.rotation.x = 0.4;\n                        state.player.model.userData.rightArm.rotation.x = -0.4;\n                    }\n\n                    // Update camera\n                    camera.rotation.copy(state.player.rotation);\n                    const headPos = new THREE.Vector3();\n                    state.player.model.userData.head.getWorldPosition(headPos);\n                    const offset = new THREE.Vector3(0, 0, 1); \n                    offset.applyEuler(camera.rotation);\n                    camera.position.copy(headPos).add(offset.multiplyScalar(1.2)).add(new THREE.Vector3(0, 0.2, 0));\n                    state.player.model.userData.head.visible = camera.position.distanceTo(headPos) >= 0.2;\n\n                    checkCoinCollection();\n                    checkCheckpointCollection();\n                    updateInteractButton();\n\n                    coinMeshes.forEach(coin => {\n                        if (!coin.userData.collected) {\n                            coin.rotation.y += deltaTime * 2;\n                            coin.lookAt(camera.position);\n                        }\n                    });\n                    \n                    // Animate checkpoints (rotate beacon and pulse glow)\n                    checkpointMeshes.forEach(checkpoint => {\n                        checkpoint.beacon.rotation.y += deltaTime * 2;\n                        const pulseScale = 1 + Math.sin(timestamp * 0.003) * 0.2;\n                        checkpoint.glow.scale.set(pulseScale, pulseScale, pulseScale);\n                    });\n                }\n\n                // Update scene background if changed in edit mode\n                scene.background.setStyle(window.gameConfig.settings.skyColor);\n\n                renderer.render(scene, camera);\n                requestAnimationFrame(gameLoop);\n            }\n\n            requestAnimationFrame(gameLoop);\n        }\n    </script>\n</body>\n</html>", "game_config": "{\n  \"platforms\": [\n    {\n      \"position\": [\n        0,\n        0,\n        0\n      ],\n      \"size\": [\n        5,\n        1,\n        5\n      ],\n      \"blockType\": \"grass\",\n      \"indestructible\": true,\n      \"id\": \"start\"\n    },\n    {\n      \"position\": [\n        7,\n        3,\n        0\n      ],\n      \"size\": [\n        3,\n        1,\n        3\n      ],\n      \"blockType\": \"dirt\",\n      \"id\": \"first_jump\",\n      \"indestructible\": true\n    },\n    {\n      \"position\": [\n        17,\n        0,\n        0\n      ],\n      \"size\": [\n        4,\n        1,\n        4\n      ],\n      \"blockType\": \"grass\",\n      \"id\": \"mining_platform\",\n      \"indestructible\": true\n    },\n    {\n      \"size\": [\n        2,\n        1,\n        2\n      ],\n      \"position\": [\n        25,\n        0,\n        0\n      ],\n      \"indestructible\": true,\n      \"id\": \"parkour1\",\n      \"blockType\": \"grass\"\n    },\n    {\n      \"size\": [\n        2,\n        1,\n        2\n      ],\n      \"position\": [\n        29,\n        0,\n        0\n      ],\n      \"id\": \"parkour2\",\n      \"indestructible\": true,\n      \"blockType\": \"grass\"\n    },\n    {\n      \"size\": [\n        2,\n        1,\n        2\n      ],\n      \"position\": [\n        33,\n        1,\n        0\n      ],\n      \"id\": \"parkour3\",\n      \"indestructible\": true,\n      \"blockType\": \"grass\"\n    },\n    {\n      \"id\": \"parkour4\",\n      \"indestructible\": true,\n      \"blockType\": \"grass\",\n      \"size\": [\n        2,\n        1,\n        2\n      ],\n      \"position\": [\n        37,\n        1,\n        0\n      ]\n    },\n    {\n      \"position\": [\n        43,\n        1,\n        -1\n      ],\n      \"size\": [\n        6,\n        1,\n        6\n      ],\n      \"blockType\": \"grass\",\n      \"id\": \"final_old\",\n      \"indestructible\": true\n    },\n    {\n      \"indestructible\": true,\n      \"id\": \"bridge1\",\n      \"blockType\": \"dirt\",\n      \"size\": [\n        5,\n        1,\n        1\n      ],\n      \"position\": [\n        52,\n        2,\n        1\n      ]\n    },\n    {\n      \"size\": [\n        4,\n        1,\n        4\n      ],\n      \"position\": [\n        60,\n        3,\n        -2\n      ],\n      \"id\": \"island1\",\n      \"indestructible\": true,\n      \"blockType\": \"grass\"\n    },\n    {\n      \"size\": [\n        3,\n        1,\n        3\n      ],\n      \"position\": [\n        68,\n        5,\n        0\n      ],\n      \"indestructible\": true,\n      \"id\": \"island2\",\n      \"blockType\": \"stone\"\n    },\n    {\n      \"size\": [\n        6,\n        1,\n        6\n      ],\n      \"position\": [\n        75,\n        4,\n        -2\n      ],\n      \"indestructible\": true,\n      \"id\": \"new_final\",\n      \"blockType\": \"grass\"\n    },\n    {\n      \"blockType\": \"grass\",\n      \"indestructible\": true,\n      \"id\": \"stage3_platform1\",\n      \"position\": [\n        85,\n        4,\n        0\n      ],\n      \"size\": [\n        3,\n        1,\n        3\n      ]\n    },\n    {\n      \"position\": [\n        92,\n        12,\n        0\n      ],\n      \"size\": [\n        3,\n        1,\n        3\n      ],\n      \"blockType\": \"stone\",\n      \"indestructible\": true,\n      \"id\": \"stage3_platform2\"\n    },\n    {\n      \"size\": [\n        3,\n        1,\n        3\n      ],\n      \"position\": [\n        97,\n        20,\n        0\n      ],\n      \"indestructible\": true,\n      \"id\": \"stage3_platform3\",\n      \"blockType\": \"stone\"\n    },\n    {\n      \"blockType\": \"grass\",\n      \"indestructible\": true,\n      \"id\": \"final_island_v2\",\n      \"position\": [\n        126,\n        20,\n        -2\n      ],\n      \"size\": [\n        6,\n        1,\n        6\n      ]\n    }\n  ],\n  \"blocks\": [\n    {\n      \"position\": [\n        12,\n        1,\n        1\n      ],\n      \"blockType\": \"dirt\",\n      \"destructible\": true\n    },\n    {\n      \"position\": [\n        13,\n        1,\n        1\n      ],\n      \"blockType\": \"dirt\",\n      \"destructible\": true\n    },\n    {\n      \"position\": [\n        12,\n        2,\n        1\n      ],\n      \"blockType\": \"dirt\",\n      \"destructible\": true\n    },\n    {\n      \"position\": [\n        13,\n        2,\n        1\n      ],\n      \"blockType\": \"dirt\",\n      \"destructible\": true\n    },\n    {\n      \"position\": [\n        12,\n        3,\n        1\n      ],\n      \"blockType\": \"dirt\",\n      \"destructible\": true\n    },\n    {\n      \"position\": [\n        13,\n        3,\n        1\n      ],\n      \"blockType\": \"dirt\",\n      \"destructible\": true\n    }\n  ],\n  \"coins\": [\n    {\n      \"id\": \"coin1\",\n      \"position\": [\n        8,\n        2,\n        1\n      ]\n    },\n    {\n      \"id\": \"coin2\",\n      \"position\": [\n        8,\n        2,\n        -1\n      ]\n    },\n    {\n      \"id\": \"coin3\",\n      \"position\": [\n        18,\n        2,\n        1\n      ]\n    },\n    {\n      \"id\": \"coin4\",\n      \"position\": [\n        25.5,\n        2,\n        0.5\n      ]\n    },\n    {\n      \"id\": \"coin5\",\n      \"position\": [\n        29.5,\n        2,\n        0.5\n      ]\n    },\n    {\n      \"id\": \"coin6\",\n      \"position\": [\n        33.5,\n        3,\n        0.5\n      ]\n    },\n    {\n      \"id\": \"coin7\",\n      \"position\": [\n        45,\n        3,\n        1\n      ]\n    },\n    {\n      \"id\": \"coin8\",\n      \"position\": [\n        45,\n        3,\n        -1\n      ]\n    },\n    {\n      \"id\": \"coin9\",\n      \"position\": [\n        54,\n        4,\n        1.5\n      ]\n    },\n    {\n      \"id\": \"coin10\",\n      \"position\": [\n        62,\n        5,\n        0\n      ]\n    },\n    {\n      \"id\": \"coin11\",\n      \"position\": [\n        70,\n        7,\n        1.5\n      ]\n    },\n    {\n      \"id\": \"coin12\",\n      \"position\": [\n        78,\n        6,\n        0\n      ]\n    },\n    {\n      \"id\": \"coin13\",\n      \"position\": [\n        86.5,\n        6,\n        1.5\n      ]\n    },\n    {\n      \"id\": \"coin14\",\n      \"position\": [\n        92,\n        14,\n        0\n      ]\n    },\n    {\n      \"id\": \"coin15\",\n      \"position\": [\n        97,\n        22,\n        0\n      ]\n    },\n    {\n      \"id\": \"coin16_old_now_19\",\n      \"position\": [\n        129,\n        22,\n        1\n      ]\n    },\n    {\n      \"id\": \"coin17\",\n      \"position\": [\n        104,\n        22,\n        4\n      ]\n    },\n    {\n      \"id\": \"coin18\",\n      \"position\": [\n        111,\n        22,\n        -4\n      ]\n    },\n    {\n      \"id\": \"coin19_extra_on_slime\",\n      \"position\": [\n        118,\n        22,\n        4\n      ]\n    }\n  ],\n  \"treasureChest\": {\n    \"position\": [\n      129,\n      20,\n      1\n    ]\n  },\n  \"settings\": {\n    \"skyColor\": \"#87CEEB\",\n    \"voidHeight\": -10,\n    \"respawnPoint\": [\n      2,\n      3,\n      2\n    ]\n  },\n  \"ladders\": [\n    {\n      \"id\": \"ladder_to_first_jump\",\n      \"rotationY\": 1.5708,\n      \"height\": 4,\n      \"position\": [\n        6.4,\n        0,\n        1\n      ]\n    }\n  ],\n  \"checkpoints\": [\n    {\n      \"id\": \"checkpoint1\",\n      \"position\": [\n        46,\n        2,\n        0\n      ]\n    },\n    {\n      \"position\": [\n        78,\n        5,\n        1\n      ],\n      \"id\": \"checkpoint2\"\n    }\n  ],\n  \"movingPlatforms\": [\n    {\n      \"startPos\": [\n        74,\n        5,\n        -5\n      ],\n      \"speed\": 2,\n      \"endPos\": [\n        74,\n        5,\n        5\n      ],\n      \"id\": \"moving1\",\n      \"blockType\": \"stone\",\n      \"size\": [\n        2,\n        1,\n        2\n      ]\n    },\n    {\n      \"speed\": 1.5,\n      \"endPos\": [\n        66,\n        4,\n        3\n      ],\n      \"startPos\": [\n        66,\n        4,\n        -3\n      ],\n      \"size\": [\n        2,\n        1,\n        2\n      ],\n      \"id\": \"moving2\",\n      \"blockType\": \"stone\"\n    }\n  ],\n  \"slimeBouncers\": [\n    {\n      \"id\": \"slime1\",\n      \"bounceStrength\": 22,\n      \"position\": [\n        92,\n        4,\n        0\n      ],\n      \"size\": [\n        2,\n        1,\n        2\n      ]\n    },\n    {\n      \"size\": [\n        2,\n        1,\n        2\n      ],\n      \"bounceStrength\": 22,\n      \"position\": [\n        97,\n        12,\n        0\n      ],\n      \"id\": \"slime2\"\n    },\n    {\n      \"bounceStrength\": 22,\n      \"position\": [\n        104,\n        19,\n        4\n      ],\n      \"id\": \"slime3\",\n      \"size\": [\n        2,\n        1,\n        2\n      ]\n    },\n    {\n      \"size\": [\n        2,\n        1,\n        2\n      ],\n      \"bounceStrength\": 22,\n      \"position\": [\n        111,\n        19,\n        -4\n      ],\n      \"id\": \"slime4\"\n    },\n    {\n      \"bounceStrength\": 22,\n      \"position\": [\n        118,\n        19,\n        4\n      ],\n      \"id\": \"slime5\",\n      \"size\": [\n        2,\n        1,\n        2\n      ]\n    }\n  ]\n}", "game_design_document": "## Core Game Loop and Mechanics\n\n**BlockVerse Adventure** is a first-person 3D platformer set in a Minecraft-inspired voxel world. Players navigate through a blocky landscape, jumping across platforms, mining destructible blocks, and avoiding hazards to reach the goal.\n\n**Moment-to-Moment Gameplay:**\n- Move through a 3D voxel environment in first-person perspective\n- Jump across gaps and climb block structures\n- Mine/destroy certain blocks to create paths or reveal secrets\n- Avoid falling into void areas\n- Collect optional coins scattered throughout the level\n- Reach the treasure chest at the end to complete the level\n\n**Progression & Engagement:**\n- Players must use spatial awareness and timing to navigate platforms\n- Mining blocks creates dynamic pathways, encouraging experimentation\n- Coins provide optional collectibles for completionists\n- The voxel aesthetic and first-person perspective create an immersive experience\n\n**Win/Lose Conditions:**\n- **Win:** Reach and interact with the treasure chest at the level's end\n- **Lose:** Fall off the map into the void (respawn at last checkpoint or level start)\n\n**Replayability:**\n- Try to collect all coins in a single run\n- Speedrun through the level\n- Experiment with different mining strategies\n\n## Starting Scope\n\nFor the initial implementation, we'll create a **single, complete level** that demonstrates all core mechanics in a polished, playable experience:\n\n**Must Have (First Iteration):**\n- First-person 3D movement with WASD + mouse look (pointer lock)\n- Jump mechanic with proper physics\n- One complete level with ~3-5 platform sections\n- Destructible blocks that can be mined (left-click to break)\n- Indestructible blocks (ground, walls)\n- Void areas (falling = respawn)\n- Treasure chest goal object\n- 5-10 collectible coins\n- Simple UI showing coin count\n- Victory screen when reaching treasure\n- Mobile controls: left joystick (move), right joystick (look), jump button, mine button\n\n**Future Iterations:**\n- Multiple levels with increasing difficulty\n- Different block types (ice blocks, bouncy blocks, etc.)\n- Enemies or moving obstacles\n- Power-ups (double jump, speed boost)\n- Timer/scoring system\n- Checkpoint system for longer levels\n- Inventory system for collected items\n\n## Game Perspective\n\n**3D First-Person** - The player experiences the voxel world through their character's eyes, with full 360-degree camera control via mouse look (desktop) or right joystick (mobile). This perspective:\n- Creates immersion in the blocky world\n- Makes depth perception and jumping more challenging and engaging\n- Allows players to look around and plan their route\n- Matches the Minecraft-inspired aesthetic\n\n## Game World and Level Design\n\n**Level 1: \"The Floating Islands\"**\n\nThe level consists of a series of floating voxel platforms suspended over a void. The environment uses a bright, colorful Minecraft-inspired aesthetic.\n\n**Layout (Start to Finish):**\n\n1. **Starting Platform (Safe Zone)**\n   - 5x5 block flat platform made of grass blocks\n   - Tutorial text appears: \"WASD to move, Mouse to look, Space to jump, Click to mine\"\n   - 2 destructible dirt blocks blocking a narrow path forward\n\n2. **First Jump Section**\n   - 3-block gap to next platform\n   - Landing platform is 3x3 grass blocks\n   - 2 coins floating above this platform (requires jumping to collect)\n\n3. **Mining Challenge**\n   - A wall of 6 dirt blocks (2 wide, 3 tall) blocking the path\n   - Player must mine through to continue\n   - Behind the wall: a 4x4 platform with 1 coin\n\n4. **Parkour Section**\n   - Series of 4 small platforms (2x2 each) with 2-block gaps between them\n   - Requires precise jumping\n   - 3 coins placed along this route (one per platform, last platform has none)\n\n5. **Final Platform & Goal**\n   - Large 6x6 grass platform\n   - Treasure chest in the center\n   - 2 coins near the chest\n   - Reaching the chest triggers victory\n\n**Environmental Details:**\n- All platforms float in a bright blue sky with simple cloud textures\n- Void below all platforms (falling = respawn at start)\n- Platforms are made of grass blocks (top texture) and dirt blocks (sides)\n- Destructible blocks are dirt-colored and slightly different shade\n- Indestructible blocks are stone-gray\n\n**Spatial Design:**\n- Total level length: ~50 blocks forward\n- Platform heights vary by 0-2 blocks to add vertical interest\n- All jumps are possible without special abilities (max 3-block gap, 1-block height difference)\n\n## Edit Mode Design\n\nEdit mode allows players to customize the level layout and difficulty while maintaining playability.\n\n**Editable Properties:**\n\n1. **Platform Configuration**\n   - Add/remove/move platforms (must keep start and end platforms)\n   - Adjust platform size (2x2 minimum, 10x10 maximum)\n   - Change platform height (Y position)\n   - Toggle platform block type (grass, dirt, stone, wood)\n\n2. **Block Placement**\n   - Add destructible blocks anywhere\n   - Add indestructible blocks anywhere\n   - Remove existing blocks (except start/end platforms)\n\n3. **Collectibles**\n   - Add/remove/move coins (0-50 maximum)\n   - Adjust coin positions (X, Y, Z coordinates)\n\n4. **Level Settings**\n   - Respawn point location (defaults to start platform)\n   - Sky color (hex color picker)\n   - Void height (how far below platforms before respawn)\n\n**Constraints:**\n- Start platform (5x5) and end platform with treasure chest cannot be deleted\n- Minimum 1 valid path must exist from start to end (no impossible jumps >4 blocks)\n- Maximum 100 total blocks in the level (performance)\n- Platforms cannot overlap\n- Treasure chest position is locked to end platform\n\n**Edit Mode UI:**\n- Top toolbar: \"Add Platform\", \"Add Block\", \"Add Coin\", \"Settings\"\n- Click objects to select and show property panel\n- Property panel shows: position (X/Y/Z), size, type, delete button\n- \"Test Level\" button to switch to play mode\n- \"Reset to Default\" button to restore original level\n\n## Control Design\n\n**Desktop Controls:**\n- **WASD**: Move forward/left/backward/right\n- **Mouse**: Look around (pointer lock enabled during gameplay)\n- **Space**: Jump\n- **Left Click**: Mine/destroy block (when looking at destructible block within range)\n- **ESC**: Release pointer lock / pause menu\n- **E**: Interact with treasure chest\n\n**Mobile Controls:**\n- **Left Joystick**: Movement (analog, 8-directional)\n  - Base appears where touched on left half of screen\n  - Max radius: 80px\n  - Dead zone: 15px\n  - Joystick base slides to keep knob under finger if moved beyond max radius\n- **Right Joystick**: Camera look (analog, 360-degree)\n  - Base appears where touched on right half of screen\n  - Max radius: 80px\n  - Dead zone: 15px\n  - Joystick base slides to keep knob under finger if moved beyond max radius\n- **Jump Button**: Fixed button in bottom-right (96x96px, above right joystick area)\n- **Mine Button**: Fixed button in bottom-right, next to jump button (96x96px)\n- **Interact Button**: Appears on-screen when near treasure chest (96x96px, center-bottom)\n\n**Control Behavior:**\n- Pointer lock engages automatically on desktop when clicking game canvas\n- Pointer lock releases when opening pause menu or completing level\n- Movement respects camera direction (forward = where you're looking)\n- Jump has fixed height and gravity\n- Mining has 3-block range from camera center\n- Crosshair appears in center of screen to show mining target\n\n**Input Handling:**\n- Prevent default browser behavior for WASD, Space, arrow keys\n- Clear all input states on window blur\n- Track touch IDs separately for each joystick\n- Convert keycodes to lowercase before storing state\n\n## Art Style and Visual Theme\n\n**Overall Style:** Minecraft-inspired voxel/block aesthetic with bright, cheerful colors and simple geometric forms.\n\n**Visual Characteristics:**\n- All objects are made of 1x1x1 unit cubes (voxels)\n- Textures are pixelated/low-resolution (16x16 or 32x32 pixel textures)\n- Bright, saturated color palette\n- Simple lighting (ambient + directional sun light)\n- No complex shaders or effects - keep it clean and readable\n\n**Color Palette:**\n- Grass blocks: Bright green top (#7CBD3B), brown sides (#8B6F47)\n- Dirt blocks: Medium brown (#8B6F47)\n- Stone blocks: Gray (#7F7F7F)\n- Sky: Bright blue (#87CEEB)\n- Coins: Golden yellow (#FFD700) with slight glow\n- Treasure chest: Brown wood (#8B4513) with gold trim (#FFD700)\n\n**Asset Rendering Strategy:**\n- **Code-rendered (WebGL/Three.js):** All blocks, platforms, basic geometry\n- **Generated assets:** Block textures (grass, dirt, stone), coin sprite, treasure chest model texture, skybox/background, UI elements\n\n**Technical Specifications:**\n- Block size: 1 unit = 1 meter in 3D space\n- Camera FOV: 75 degrees\n- Render distance: 100 units\n- Player height: 1.8 units (eye level)\n- Player collision: 0.8 units wide, 1.8 units tall\n\n**Consistency Requirements:**\n- All block textures must use same pixel density (32x32 pixels per block face)\n- All textures should have slight pixelation/aliasing for retro feel\n- Maintain consistent lighting angle across all assets\n- UI elements should use blocky, pixel-art style fonts", "persistent_storage_config": {"leaderboard": {"sort_ascending": true}}, "game_metadata": {"game_id": "01KT66DXNH4EG5B9SWA1E0G05X", "title": "MINE CRAFT RUN", "description": "**Description:** Explore a voxel world in first-person, jumping across platforms and mining blocks to reach the hidden treasure chest!", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_6dbdd416-cecb-4e42-b8ea-e6d295bd36de.webp", "created_at": 1780472055.0, "updated_at": 1780481146184.4937, "art_style": null}}