{"asset_map": {"hero_idle": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hero_idle_aac89083-c256-48a7-84fa-ffb17936317e.webp", "type": "image", "prompt": "Generate a 16-bit pixel art character, side view profile, standing idle. The hero wears bright blue overalls, a red cap, and has a friendly, cheerful expression. Style: clean 16-bit SNES-era pixel art, 32x32 pixel scale, chunky pixels, thick black outline (2-3 pixels), no anti-aliasing, vibrant saturated colors, transparent background.", "description": "The main hero character standing idle in a profile view.", "subtype": "character", "aspect_ratio": [433, 908], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "hero_run_1": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hero_run_1_7add783b-a49b-49fa-aec4-eb25169ffae4.webp", "type": "image", "prompt": "Modify the pixel art character from hero_idle into a running pose, side view. One leg forward, one leg back, arms pumping in a running motion. Maintain the same blue overalls, red cap, and chunky pixel art style with thick black outlines. Transparent background.", "description": "First frame of the hero's running animation.", "subtype": "character", "aspect_ratio": [717, 1098], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "hero_run_2": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hero_run_2_67f6edf4-43b7-441e-9a1d-0cb2c4c01b40.webp", "type": "image", "prompt": "Modify the pixel art character from hero_run_1 into the alternate running pose, side view. The opposite leg should be forward to create a seamless two-frame run cycle. Maintain the blue overalls, red cap, and 16-bit chunky pixel style. Transparent background.", "description": "Second frame of the hero's running animation with opposite leg positioning.", "subtype": "character", "aspect_ratio": [818, 1242], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "hero_jump": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hero_jump_2ef04604-fae1-4172-ab1d-8254e0deffba.webp", "type": "image", "prompt": "Modify the pixel art character from hero_idle into a jumping pose, side view. Arms raised upward, legs slightly bent, expressing upward momentum. Maintain the same blue overalls, red cap, and 16-bit style with thick black outlines. Transparent background.", "description": "The hero character in an upward jumping pose.", "subtype": "character", "aspect_ratio": [468, 1048], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "hero_fall": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hero_fall_0f93493c-add8-4d0f-ade3-7597199efd2d.webp", "type": "image", "prompt": "Modify the pixel art character from hero_idle into a falling pose, side view. Arms slightly out to the sides for balance, legs dangling downward. Maintain the same blue overalls, red cap, and 16-bit style. Transparent background.", "description": "The hero character in a falling pose.", "subtype": "character", "aspect_ratio": [473, 965], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "enemy_walker": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/enemy_walker_b11fa676-bbdc-4b7c-a89b-84a0cffe1e71.webp", "type": "image", "prompt": "Generate a 16-bit pixel art mushroom enemy, side view. Brown cap with white spots, stubby legs, and a grumpy expression. Style: chunky pixels, 32x32 pixel scale, thick black outline, no anti-aliasing, classic platformer aesthetic. Transparent background.", "description": "A mushroom-like walking enemy (Goomba-style).", "subtype": "character", "aspect_ratio": [842, 880], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "enemy_flyer": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/enemy_flyer_660efc74-6a2c-48e4-841d-c89a3a0b3873.webp", "type": "image", "prompt": "Generate a flying enemy creature, side view. Red round body with small white wings, cartoonish menacing expression. Style: 16-bit chunky pixel art, 40x40 pixel scale, thick black outline, no anti-aliasing, bright saturated colors. Transparent background.", "description": "A flying red enemy with a cartoonish menacing look.", "subtype": "character", "aspect_ratio": [1000, 916], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "coin": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/coin_bae2b958-6bdc-4c35-a206-346cdf45af34.webp", "type": "image", "prompt": "Generate a spinning gold coin, side view. Circular shape, golden yellow (#F4D03F) with a bright white shine highlight. Style: 16-bit pixel art, 24x24 pixel scale, chunky pixels with black outline, no anti-aliasing. Transparent background.", "description": "A collectible golden coin with a shine effect.", "subtype": "object", "aspect_ratio": [704, 807], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "brick_block": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/brick_block_ae479ada-2348-4802-aa72-427fceeb2bf9.webp", "type": "image", "prompt": "Generate a breakable brick block, side view. Orange-brown color (#D68910) with a visible 4x4 brick pattern. Style: 16-bit pixel art, 64x64 pixel scale, chunky pixels, thick black outline, no anti-aliasing. Transparent background.", "description": "A breakable orange-brown brick block.", "subtype": "ground", "aspect_ratio": [734, 798], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "question_block": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/question_block_a5c6f814-adcc-45a4-8e5a-9ab8dfa8989d.webp", "type": "image", "prompt": "Generate a question mark block, side view. Bright yellow/gold color with a large, bold white question mark symbol in the center. Style: 16-bit pixel art, 64x64 pixel scale, chunky pixels, thick black outline, no anti-aliasing. Transparent background.", "description": "A mysterious yellow block with a question mark.", "subtype": "ground", "aspect_ratio": [883, 888], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "solid_block": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/solid_block_dbd918ff-5aef-4f85-9fb7-082d84e70ead.webp", "type": "image", "prompt": "Generate a solid stone block, side view. Gray stone color with subtle texture and small cracks for detail. Style: 16-bit pixel art, 64x64 pixel scale, chunky pixels, thick black outline, no anti-aliasing. Transparent background.", "description": "An unbreakable gray stone block.", "subtype": "ground", "aspect_ratio": [946, 770], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "flag_pole": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/flag_pole_68953274-0cae-49a2-b38e-541e28f164ea.webp", "type": "image", "prompt": "Generate a tall flagpole with a waving checkered flag (black and white pattern). Brown wooden pole. Style: 16-bit pixel art, 64x128 tall scale, chunky pixels, thick black outline, no anti-aliasing. Transparent background.", "description": "The level goal flag pole with a checkered flag.", "subtype": "object", "aspect_ratio": [659, 1410], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "cloud": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cloud_80e0a798-9d28-439b-8a29-ec98b7aa4ee2.webp", "type": "image", "prompt": "Generate a puffy white cloud, side view. Rounded cloud shape with subtle shading for depth. Style: 16-bit pixel art, 128x64 wide scale, chunky pixels, 2px black outline, no anti-aliasing. Transparent background.", "description": "A decorative puffy white cloud for the background.", "subtype": "other", "aspect_ratio": [1169, 671], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "bush": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/bush_32f13efa-f80d-4b25-8913-0a15d43df1be.webp", "type": "image", "prompt": "Generate a green rounded bush, side view. Darker green shading for a 16-bit look. Style: 16-bit pixel art, 96x48 scale, chunky pixels, 2px black outline, no anti-aliasing. Transparent background.", "description": "A decorative green bush for ground level.", "subtype": "other", "aspect_ratio": [1063, 806], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "hill_background": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hill_background_fa2138f3-4a06-4760-97c4-050334c6f26b.webp", "type": "image", "prompt": "Generate a horizontally tiling silhouette of rolling green hills (#27AE60). Simple curved shapes, darker green than foreground grass. Style: 16-bit pixel art, 512x256 wide, chunky pixels, no outlines. The image must repeat seamlessly on the left and right edges for horizontal scrolling. Opaque background.", "description": "A horizontally tiling silhouette of green hills for parallax scrolling.", "subtype": "background", "aspect_ratio": [1536, 1024], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "bgm_main": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/bgm_main_13d633e0-b2bd-4a93-b398-b948204a669d.mp3", "type": "audio", "prompt": "Upbeat, cheerful 16-bit SNES-style background music for a platformer. Bright, bouncy lead synth melody with a driving 4/4 bassline, 140 BPM, major key. Reminiscent of classic 90s console games like Super Mario World. High-energy, positive mood, seamless loop, rich chiptune instrumentation with square and pulse waves.", "description": "Main background theme for the level, upbeat 16-bit adventure style.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30, "loop": true, "animation": null}, "sfx_jump": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_jump_a021f5f5-b461-45df-9cf6-23f39a23c209.mp3", "type": "audio", "prompt": "Short, bouncy 16-bit chiptune jump sound effect. Features a quick upward frequency sweep, clean pulse wave texture, snappy and responsive for a platformer hero. Classic retro game aesthetic.", "description": "Satisfying 16-bit jump sound with an upward pitch sweep.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2, "loop": false, "animation": null}, "sfx_coin": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_coin_e38f9fed-a000-499e-a0f5-369e72127951.mp3", "type": "audio", "prompt": "Bright, pleasant 16-bit coin collection sound effect. High-pitched 'pling' or 'ding', resonant and shimmering with a very brief delay tail. Pure retro chiptune rewarding feedback sound.", "description": "High-pitched, rewarding 16-bit 'pling' for collecting coins.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.18, "loop": false, "animation": null}, "sfx_stomp": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_stomp_15ff9c41-487e-4933-8772-97b4a5667e90.mp3", "type": "audio", "prompt": "Enemy stomp sound effect, 16-bit style. A short, percussive 'thump' or 'bop' with a slight squishy texture. Impactful and satisfying but soft enough for a cheerful pixel art game.", "description": "Percussive 'thump' sound for defeating enemies by jumping on them.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.35, "loop": false, "animation": null}, "sfx_block_break": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_block_break_61401e94-1a8e-4b2b-8154-11007bc210e8.mp3", "type": "audio", "prompt": "16-bit brick breaking sound effect. Crunchy, multi-layered noise burst suggesting shattering stone or ceramic into small pieces. Energetic and satisfying destruction sound for a platformer.", "description": "Crunchy breaking sound for brick blocks hit from below.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.92, "loop": false, "animation": null}, "sfx_block_hit": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_block_hit_46426b42-ea2d-4ba5-b3aa-0f10360cfd4e.mp3", "type": "audio", "prompt": "16-bit solid block hit sound effect. A dull 'bonk' or 'thud' with a short decay. Sounds like an unsuccessful attempt to break a hard surface. Low-frequency square wave hit.", "description": "Dull 'bonk' sound for hitting solid, unbreakable blocks.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.24, "loop": false, "animation": null}, "sfx_death": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_death_eaa78645-0008-424b-a613-828fcf2159df.mp3", "type": "audio", "prompt": "16-bit player death sound effect. A short, descending pitch-bent melody, classic 'game over' jingle vibe, slightly sad but whimsical and non-violent. Chiptune pulse wave instrumentation.", "description": "Descending pitch jingle for player defeat.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.73, "loop": false, "animation": null}, "sfx_victory": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_victory_03fc9425-99dc-469d-a311-8b96b10f1f69.mp3", "type": "audio", "prompt": "16-bit level complete victory fanfare. Triumphant, ascending major key melody, bright brassy synth patches, ends on a sustained high note. Celebratory, short, and rewarding reward sound.", "description": "Triumphant fanfare played when reaching the flag pole.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 5, "loop": false, "animation": null}, "shield": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/shield_83971bb7-c615-42b9-bb45-ebaa5b8b021d.webp", "type": "image", "prompt": "Generate a 16-bit pixel art shield power-up icon, front view. The shield is heater-shaped, colored light blue and cyan (#00FFFF) with a bright white metallic shine. Style: chunky pixels, 32x32 pixel scale, thick black outline, no anti-aliasing, vibrant saturated colors. Transparent background. Matches the SNES-style aesthetic of hero_idle and brick_block.", "description": "A 16-bit pixel art shield power-up icon with a cyan glow for a classic platformer.", "subtype": "object", "aspect_ratio": [725, 818], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "enemy_spider": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/enemy_spider_9ba1c011-f5d8-47cd-974f-ca4b75766a99.webp", "type": "image", "prompt": "Generate a 16-bit pixel art spider enemy, side view. Dark purple or black body with small red glowing eyes, 8 stubby legs. Style: chunky pixels, 32x32 pixel scale, thick black outline, no anti-aliasing, vibrant saturated colors, classic SNES-style platformer aesthetic. Transparent background, high resolution, detailed professional game asset.", "description": "A 16-bit pixel art spider enemy with red eyes for a side-scrolling platformer.", "subtype": "character", "aspect_ratio": [845, 560], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "enemy_snail": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/enemy_snail_55e92bb2-244a-43d5-b76c-a5e6b2f805ce.webp", "type": "image", "prompt": "Generate a 16-bit pixel art snail enemy, side view, right-facing. Features a vibrant green shell with a tan or beige body and small antennae. Style: chunky pixels, 32x32 pixel scale appearance, thick black outline, no anti-aliasing, vibrant saturated colors. Consistent with the style of enemy_walker and enemy_spider. Transparent background, professional game-ready asset.", "description": "A 16-bit pixel art snail enemy with a green shell and tan body, viewed from the side.", "subtype": "character", "aspect_ratio": [1181, 832], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "pixel_heart": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/pixel_heart_237d6698-2c1d-4c86-9488-aed052dfd1c4.webp", "type": "image", "prompt": "Generate a 16-bit pixel art red heart icon for a classic platformer game. The heart should be vibrant red with a dark pixel outline to ensure it pops against various backgrounds. High-quality game-ready UI asset, clean pixel edges, transparent background, front view.", "description": "A small 16-bit pixel art red heart icon for the player's health HUD.", "subtype": "ui", "aspect_ratio": [805, 713], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n<head>\n    <title>PIXEL DASH KINGDOM</title>\n    <link href=\"https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Silkscreen:wght@400;700&display=swap\" rel=\"stylesheet\">\n    <style>\n        :root {\n            --transition-fast: 150ms;\n            --transition-normal: 300ms;\n            --ease-out: cubic-bezier(0.0, 0, 0.2, 1);\n            --space-xs: 8px;\n            --space-sm: 16px;\n            --space-md: 24px;\n            --space-lg: 32px;\n            --space-xl: 48px;\n        }\n\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        #game-world {\n            position: relative;\n            width: 100%;\n            height: 100%;\n            background-color: #AED6F1;\n        }\n\n        #game-world.mobile-landscape {\n            width: 1280px !important;\n            height: 720px !important;\n            position: absolute !important;\n            left: -280px !important;\n            top: 280px !important;\n            transform: rotate(90deg);\n            transform-origin: center;\n        }\n\n        #game-canvas {\n            display: block;\n            width: 100%;\n            height: 100%;\n            image-rendering: pixelated;\n            image-rendering: crisp-edges;\n            filter: contrast(1.2) saturate(1.3);\n        }\n\n        .touch-controls {\n            position: absolute;\n            bottom: 0;\n            left: 0;\n            right: 0;\n            height: 200px;\n            pointer-events: none;\n            z-index: 10;\n        }\n\n        .touch-btn {\n            position: absolute;\n            pointer-events: auto;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            border-radius: 20px;\n            background: linear-gradient(145deg, rgba(255, 225, 50, 0.8), rgba(255, 160, 0, 0.7));\n            border: 3px solid rgba(255, 255, 255, 0.6);\n            color: white;\n            font-family: 'Press Start 2P', monospace;\n            font-size: 24px;\n            text-shadow: 0 2px 4px rgba(0,0,0,0.5);\n            user-select: none;\n            -webkit-user-select: none;\n            transition: all var(--transition-fast) var(--ease-out);\n            backdrop-filter: blur(4px);\n            box-shadow: 0 6px 12px rgba(0,0,0,0.3), inset 0 2px 0 rgba(255,255,255,0.4);\n        }\n\n        .touch-btn:active, .touch-btn.active {\n            background: rgba(255, 69, 0, 0.6); /* Darker Orange-Red */\n            transform: scale(0.95);\n        }\n\n        .btn-left {\n            left: 20px;\n            bottom: 40px;\n            width: 100px;\n            height: 100px;\n        }\n\n        .btn-right {\n            left: 140px;\n            bottom: 40px;\n            width: 100px;\n            height: 100px;\n        }\n\n        .btn-jump {\n            right: 20px;\n            bottom: 40px;\n            width: 140px;\n            height: 140px;\n            border-radius: 50%;\n        }\n\n        .hud {\n            position: absolute;\n            top: 20px;\n            left: 20px;\n            right: 20px;\n            display: flex;\n            justify-content: space-between;\n            pointer-events: none;\n            z-index: 10;\n        }\n\n        .hud-item {\n            color: #FFFFFF;\n            font-size: 18px;\n            background: linear-gradient(180deg, rgba(40,40,60,0.85) 0%, rgba(20,20,40,0.9) 100%);\n            padding: 8px 16px;\n            border-radius: 12px;\n            display: flex;\n            align-items: center;\n            gap: 10px;\n            box-shadow: 0 4px 10px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.15);\n            min-width: 80px;\n            border: 2px solid rgba(255,255,255,0.2);\n        }\n\n        .hud-icon {\n            width: 24px;\n            height: 24px;\n            image-rendering: pixelated;\n        }\n\n        .hud-item span {\n            font-family: 'Press Start 2P', monospace;\n            text-shadow: 2px 2px 0px rgba(0,0,0,0.5);\n        }\n\n        .game-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            right: 0;\n            bottom: 0;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            background: rgba(0,0,0,0.7);\n            z-index: 20;\n            pointer-events: auto;\n        }\n\n        .game-overlay h1 {\n            font-size: 42px;\n            margin-bottom: 40px;\n            text-align: center;\n            line-height: 1.2;\n            background: linear-gradient(to right, #FF5733, #F4D03F, #27AE60, #3498DB, #9B59B6);\n            -webkit-background-clip: text;\n            -webkit-text-fill-color: transparent;\n            filter: drop-shadow(4px 4px 0px #8B4513);\n            letter-spacing: 2px;\n            animation: titleFloat 3s ease-in-out infinite, rainbowText 5s linear infinite;\n            background-size: 200% auto;\n        }\n\n        @keyframes rainbowText {\n            to { background-position: 200% center; }\n        }\n\n        .game-overlay {\n            background: linear-gradient(135deg, rgba(0,0,0,0.85) 0%, rgba(20,20,40,0.9) 100%);\n        }\n\n        @keyframes titleFloat {\n            0%, 100% { transform: translateY(0) rotate(-1deg); }\n            50% { transform: translateY(-10px) rotate(1deg); }\n        }\n\n        .menu-btn {\n            pointer-events: auto;\n            width: 320px;\n            padding: 24px;\n            font-family: 'Press Start 2P', monospace;\n            font-size: 18px;\n            color: white;\n            border-radius: 12px;\n            cursor: pointer;\n            margin: 12px 0;\n            transition: all 0.2s;\n            text-shadow: 2px 2px 0px #000, 3px 3px 0px rgba(255,255,255,0.3);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 15px;\n            box-shadow: 0 8px 0 rgba(0,0,0,0.4), inset 0 4px 0 rgba(255,255,255,0.4);\n            position: relative;\n            top: 0;\n            overflow: hidden;\n            border: 4px solid #FFFFFF;\n        }\n\n        .menu-btn:hover {\n            transform: scale(1.05);\n            filter: brightness(1.2);\n            box-shadow: 0 10px 20px rgba(0,0,0,0.3), 0 6px 0 rgba(0,0,0,0.4), inset 0 4px 0 rgba(255,255,255,0.5);\n        }\n\n        #menu-btn-play {\n            background: linear-gradient(180deg, #FF5733 0%, #C70039 100%);\n            text-shadow: 2px 2px 0px #8B0000;\n        }\n\n        #menu-btn-how {\n            background: linear-gradient(180deg, #3498DB 0%, #2980B9 100%);\n            text-shadow: 2px 2px 0px #1B4F72;\n        }\n\n        #menu-btn-leaderboard {\n            background: linear-gradient(180deg, #F1C40F 0%, #F39C12 100%);\n            text-shadow: 2px 2px 0px #8B4513;\n        }\n\n        .menu-btn:active {\n            top: 4px;\n            box-shadow: 0 4px 0 rgba(0,0,0,0.4);\n            transform: scale(0.98);\n        }\n\n        .menu-decoration {\n            position: absolute;\n            pointer-events: none;\n            image-rendering: pixelated;\n            animation: bounce 2s infinite ease-in-out;\n        }\n\n        .how-to-content {\n            background: rgba(255, 255, 255, 0.1);\n            padding: 20px;\n            border-radius: 12px;\n            margin-bottom: 20px;\n            max-width: 80%;\n        }\n\n        .how-to-content p {\n            font-family: 'Silkscreen', cursive;\n            font-size: 14px;\n            line-height: 1.6;\n            margin-bottom: 12px;\n            text-align: left !important;\n            color: #FFF;\n            text-shadow: 1px 1px 0px #000;\n        }\n\n        .leaderboard-content {\n            background: rgba(255, 255, 255, 0.1);\n            padding: 20px;\n            border-radius: 12px;\n            margin-bottom: 20px;\n            max-width: 80%;\n            width: 300px;\n            max-height: 400px;\n            overflow-y: auto;\n            border: 2px solid rgba(255,255,255,0.3);\n        }\n\n        #leaderboard-list {\n            list-style: none;\n            padding: 0;\n            margin: 0;\n        }\n\n        #leaderboard-list li {\n            display: flex;\n            align-items: center;\n            padding: 8px 0;\n            border-bottom: 1px solid rgba(255,255,255,0.1);\n            color: white;\n            font-size: 12px;\n            text-shadow: 1px 1px 0px rgba(0,0,0,0.5);\n        }\n\n        #leaderboard-list li:last-child {\n            border-bottom: none;\n        }\n\n        #leaderboard-list li .rank {\n            font-weight: bold;\n            width: 30px;\n            text-align: right;\n            margin-right: 10px;\n            color: #F4D03F;\n        }\n\n        #leaderboard-list li .profile-pic {\n            width: 24px;\n            height: 24px;\n            border-radius: 50%;\n            margin-right: 10px;\n            background-color: #555;\n            border: 1px solid #AAA;\n        }\n\n        #leaderboard-list li .username {\n            flex-grow: 1;\n            text-align: left;\n        }\n\n        #leaderboard-list li .score {\n            font-weight: bold;\n            color: #27AE60;\n        }\n\n        #overlay-menu {\n            background: linear-gradient(-45deg, rgba(46, 134, 193, 0.8), rgba(39, 174, 96, 0.8), rgba(243, 156, 18, 0.8), rgba(231, 76, 60, 0.8));\n            background-size: 400% 400%;\n            animation: gradientBG 15s ease infinite;\n        }\n\n        @keyframes gradientBG {\n            0% { background-position: 0% 50%; }\n            50% { background-position: 100% 50%; }\n            100% { background-position: 0% 50%; }\n        }\n\n        #overlay-loading h1 {\n            font-size: 40px;\n            color: #2E86C1;\n            text-shadow: 4px 4px 0px #FFFFFF, 8px 8px 0px rgba(0,0,0,0.1);\n            animation: bounce 2s infinite ease-in-out;\n        }\n\n        @keyframes bounce {\n            0%, 100% { transform: translateY(0); }\n            50% { transform: translateY(-20px); }\n        }\n\n        .loading-text {\n            color: white;\n            font-size: 14px;\n            margin-top: 40px;\n            letter-spacing: 4px;\n            animation: blink 1s infinite;\n        }\n\n        @keyframes blink {\n            0%, 100% { opacity: 1; }\n            50% { opacity: 0.3; }\n        }\n\n        .game-overlay p {\n            color: white;\n            font-size: 16px;\n            margin-bottom: 16px;\n            text-align: center;\n        }\n\n        .overlay-btn {\n            pointer-events: auto;\n            padding: 16px 32px;\n            font-family: 'Press Start 2P', monospace;\n            font-size: 18px;\n            color: white;\n            background: linear-gradient(180deg, #2ECC71 0%, #27AE60 100%);\n            border: 4px solid #FFFFFF;\n            border-radius: 12px;\n            cursor: pointer;\n            margin-top: 16px;\n            box-shadow: 0 6px 0 rgba(0,0,0,0.3), inset 0 3px 0 rgba(255,255,255,0.4);\n            text-shadow: 2px 2px 0px #1E8449;\n            transition: all 0.2s;\n        }\n\n        .overlay-btn:hover {\n            transform: translateY(-2px) scale(1.05);\n            filter: brightness(1.1);\n        }\n\n        .overlay-btn:active {\n            transform: translateY(2px) scale(0.98);\n            box-shadow: 0 2px 0 rgba(0,0,0,0.3);\n        }\n\n        .edit-toolbar {\n            position: absolute;\n            bottom: 16px;\n            left: 50%;\n            transform: translateX(-50%);\n            display: flex;\n            gap: 8px;\n            background: rgba(0,0,0,0.8);\n            padding: 12px;\n            border-radius: 12px;\n            z-index: 15;\n        }\n\n        .edit-btn {\n            width: 64px;\n            height: 64px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            background: rgba(255,255,255,0.2);\n            border: 2px solid rgba(255,255,255,0.5);\n            border-radius: 8px;\n            color: white;\n            font-size: 10px;\n            font-family: 'Press Start 2P', monospace;\n            cursor: pointer;\n            text-align: center;\n        }\n\n        .edit-btn:hover {\n            background: rgba(255,255,255,0.4);\n        }\n\n        .edit-btn.selected {\n            background: rgba(39, 174, 96, 0.6);\n            border-color: #27AE60;\n        }\n\n        .hidden { display: none !important; }\n    </style>\n</head>\n<body>\n    <div id=\"game-world\">\n        <canvas id=\"game-canvas\"></canvas>\n        <div class=\"hud\" id=\"hud\">\n            <div class=\"hud-item\" id=\"hud-coins-panel\">\n                <img id=\"hud-coin-img\" src=\"\" class=\"hud-icon\" alt=\"Coins\">\n                <span id=\"hud-coins-text\">0</span>\n            </div>\n            <div class=\"hud-item\" id=\"hud-lives-panel\">\n                <img id=\"hud-heart-img\" src=\"\" class=\"hud-icon\" alt=\"Lives\">\n                <span id=\"hud-lives-text\">3</span>\n            </div>\n            <div class=\"hud-item\" id=\"hud-score-panel\">\n                <span class=\"hud-icon\" style=\"display: flex; align-items: center; justify-content: center;\">\u2b50</span>\n                <span id=\"hud-score-text\">0</span>\n            </div>\n        </div>\n        <div class=\"touch-controls\" id=\"touch-controls\">\n            <div class=\"touch-btn btn-left\" id=\"btn-left\">\u25c0</div>\n            <div class=\"touch-btn btn-right\" id=\"btn-right\">\u25b6</div>\n            <div class=\"touch-btn btn-jump\" id=\"btn-jump\">\u25b2</div>\n        </div>\n        <div id=\"tutorial-banner\" class=\"hidden\" style=\"position: absolute; top: 110px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.85); border: 3px solid #F4D03F; border-radius: 8px; padding: 12px 16px; color: white; font-family: 'Press Start 2P', monospace; font-size: 10px; text-align: center; max-width: 80%; pointer-events: auto; z-index: 12; box-shadow: 0 4px 10px rgba(0,0,0,0.5); line-height: 1.4; cursor: pointer;\">\n            <div id=\"tutorial-text\">TUTORIAL STEP</div>\n            <div style=\"font-size: 8px; color: #FF5733; margin-top: 6px; font-weight: bold;\">[TAP TO SKIP TUTORIAL]</div>\n        </div>\n        <div class=\"game-overlay\" id=\"overlay-loading\">\n            <h1 style=\"color: #FF5733; text-shadow: 4px 4px 0px #C70039, 8px 8px 0px #F4D03F; font-size: 48px;\">PIXEL DASH<br><span style=\"color: #F4D03F;\">KINGDOM</span></h1>\n            <div class=\"loading-text\" style=\"color: #F4D03F; text-shadow: 2px 2px 0px #000;\">PREPARING ADVENTURE...</div>\n        </div>\n        <div class=\"game-overlay hidden\" id=\"overlay-menu\">\n            <h1 style=\"background: linear-gradient(to bottom, #FFD700, #FFA500, #FF4500); -webkit-background-clip: text; -webkit-text-fill-color: transparent; filter: drop-shadow(4px 4px 0px #8B4513);\">PIXEL DASH<br>KINGDOM</h1>\n            <button class=\"menu-btn\" id=\"menu-btn-play\"><span>\ud83c\udfae</span> PLAY</button>\n            <button class=\"menu-btn\" id=\"menu-btn-how\"><span>\ud83d\udcd6</span> HOW TO PLAY</button>\n            <button class=\"menu-btn\" id=\"menu-btn-leaderboard\"><span>\ud83c\udfc6</span> LEADERBOARD</button>\n        </div>\n        <div class=\"game-overlay hidden\" id=\"overlay-how-to-play\">\n            <h1>HOW TO PLAY</h1>\n            <div class=\"how-to-content\">\n                <p>\u2022 <b>MOVE:</b> Use <b>Left/Right arrows</b> or <b>A/D keys</b> (or on-screen buttons) to run through the kingdom.</p>\n                <p>\u2022 <b>JUMP SYSTEM:</b> Press <b>Up arrow</b>, <b>W</b>, or the <b>Jump button</b>. You can <b>multi-jump up to 5 times</b> in mid-air to reach high platforms!</p>\n                <p>\u2022 <b>STOMP:</b> Jump on enemies from above to defeat them. Some enemies are tougher than others!</p>\n                <p>\u2022 <b>BLOCKS:</b> Hit <b>Bricks</b> and <b>Question Blocks</b> from below to find coins, hidden paths, or power-ups.</p>\n                <p>\u2022 <b>SHIELD:</b> Look for the <b>Cyan Shield</b> power-up! It makes you invincible for 5 seconds, allowing you to smash through enemies.</p>\n                <p>\u2022 <b>GOAL:</b> Reach the <b>Checkered Flag</b> at the end of the level to win!</p>\n            </div>\n            <button class=\"overlay-btn\" id=\"btn-back\">BACK</button>\n        </div>\n        <div class=\"game-overlay hidden\" id=\"overlay-leaderboard\">\n            <h1>LEADERBOARD</h1>\n            <div class=\"leaderboard-content\">\n                <ul id=\"leaderboard-list\"></ul>\n            </div>\n            <button class=\"overlay-btn\" id=\"leaderboard-btn-back\">BACK</button>\n        </div>\n        <div class=\"game-overlay hidden\" id=\"overlay-death\">\n            <h1>GAME OVER</h1>\n            <p id=\"death-score\">Score: 0</p>\n            <button class=\"overlay-btn\" id=\"btn-retry\">RETRY</button>\n        </div>\n        <div class=\"game-overlay hidden\" id=\"overlay-win\">\n            <h1>LEVEL CLEAR!</h1>\n            <p id=\"win-score\">Score: 0</p>\n            <button class=\"overlay-btn\" id=\"btn-next\">PLAY AGAIN</button>\n        </div>\n        <div class=\"edit-toolbar hidden\" id=\"edit-toolbar\">\n            <div class=\"edit-btn\" data-tool=\"select\">SEL</div>\n            <div class=\"edit-btn\" data-tool=\"brick\">BRK</div>\n            <div class=\"edit-btn\" data-tool=\"question\">Q?</div>\n            <div class=\"edit-btn\" data-tool=\"solid\">SOL</div>\n            <div class=\"edit-btn\" data-tool=\"enemy_walker\">WKR</div>\n            <div class=\"edit-btn\" data-tool=\"enemy_flyer\">FLY</div>\n            <div class=\"edit-btn\" data-tool=\"enemy_spider\">SPD</div>\n            <div class=\"edit-btn\" data-tool=\"enemy_snail\">SNL</div>\n            <div class=\"edit-btn\" data-tool=\"coin\">CIN</div>\n            <div class=\"edit-btn\" data-tool=\"delete\">DEL</div>\n        </div>\n    </div>\n\n    <script>\n    /*\n     * PIXEL DASH KINGDOM\n     * A classic 16-bit side-scrolling platformer.\n     * Run, jump, break blocks, stomp enemies, collect coins, reach the flag!\n     */\n    async function run(mode) {\n        const canvas = document.getElementById('game-canvas');\n        const ctx = canvas.getContext('2d');\n        const gameWorld = document.getElementById('game-world');\n\n        const isLandscape = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || (window.innerWidth < 800 && 'ontouchstart' in window);\n        if (isLandscape) {\n            gameWorld.classList.add('mobile-landscape');\n        }\n\n        const VIRTUAL_WIDTH = isLandscape ? 640 : 360;\n        const VIRTUAL_HEIGHT = isLandscape ? 360 : 640;\n\n        function resizeCanvas() {\n            canvas.width = VIRTUAL_WIDTH;\n            canvas.height = VIRTUAL_HEIGHT;\n        }\n        resizeCanvas();\n        window.addEventListener('resize', resizeCanvas);\n\n        const config = window.gameConfig;\n        let currentLevelIndex = 0;\n        const TILE = 64;\n        const GRAVITY = 1800;\n        const JUMP_VELOCITY = -750;\n        const MAX_RUN_SPEED = 300;\n        const RUN_ACCEL = 1200;\n        const RUN_DECEL = 800;\n        const COYOTE_TIME = 0.1;\n\n        class Animation {\n            constructor(initialState = 'idle') {\n                this.state = initialState;\n                this.frameIndex = 0;\n                this.timer = 0;\n                this.animations = {\n                    'idle': { frames: ['hero_idle'], frameRate: 0 },\n                    'run': { frames: ['hero_run_1', 'hero_run_2'], frameRate: 10 },\n                    'jump': { frames: ['hero_jump'], frameRate: 0 },\n                    'fall': { frames: ['hero_fall'], frameRate: 0 }\n                };\n            }\n            setState(newState) {\n                if (this.state !== newState) {\n                    this.state = newState;\n                    this.frameIndex = 0;\n                    this.timer = 0;\n                }\n            }\n            update(dt) {\n                const currentAnim = this.animations[this.state];\n                if (!currentAnim || currentAnim.frameRate === 0) return;\n                this.timer += dt;\n                if (this.timer >= 1 / currentAnim.frameRate) {\n                    this.frameIndex = (this.frameIndex + 1) % currentAnim.frames.length;\n                    this.timer -= 1 / currentAnim.frameRate;\n                }\n            }\n            getCurrentFrame() {\n                const currentAnim = this.animations[this.state];\n                return currentAnim ? currentAnim.frames[this.frameIndex] : 'hero_idle';\n            }\n        }\n\n        // Audio context\n        let audioCtx = null;\n        const audioBuffers = {};\n        const JUMP_BUFFER = 0.1;\n\n        function getAudioCtx() {\n            if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();\n            return audioCtx;\n        }\n\n        async function loadAudio(id) {\n            try {\n                const asset = lib.getAsset(id);\n                if (!asset || asset.type !== 'audio') return;\n                const response = await fetch(asset.url);\n                const arrayBuffer = await response.arrayBuffer();\n                const ctx = getAudioCtx();\n                audioBuffers[id] = await ctx.decodeAudioData(arrayBuffer);\n            } catch(e) { lib.log('Audio load failed: ' + id); }\n        }\n\n        let particles = [];\n        let shields = [];\n        let poppedCoins = [];\n        let hearts = [];\n        let floatingTexts = [];\n        \n        // Tutorial state\n        let tutorialActive = false;\n        let tutorialStep = 0;\n        let tutorialTimer = 0;\n        let runTimer = 0;\n\n        function playSound(id, loop = false, volume = 0.5) {\n            try {\n                const ctx = getAudioCtx();\n                if (ctx.state === 'suspended') ctx.resume();\n                const buffer = audioBuffers[id];\n                if (!buffer) return null;\n                const source = ctx.createBufferSource();\n                const gain = ctx.createGain();\n                source.buffer = buffer;\n                source.loop = loop;\n                gain.gain.value = volume;\n                source.connect(gain);\n                gain.connect(ctx.destination);\n                source.start(0);\n                return { source, gain };\n            } catch(e) { return null; }\n        }\n\n        function spawnParticles(x, y, color, count) {\n            for (let i = 0; i < count; i++) {\n                particles.push({\n                    x, y, vx: (Math.random()-0.5)*300, vy: -Math.random()*400,\n                    life: 0.5 + Math.random()*0.3, maxLife: 0.8, color, size: 4+Math.random()*6\n                });\n            }\n        }\n\n        function spawnFloatingText(x, y, text, color) {\n            floatingTexts.push({ x, y, text, color, life: 1.0 });\n        }\n\n        // Image loading\n        const images = {};\n        const imageIds = ['hero_idle','hero_run_1','hero_run_2','hero_jump','hero_fall',\n                         'enemy_walker','enemy_flyer','enemy_spider','enemy_snail','coin','brick_block','question_block',\n                         'solid_block', 'shield', 'pixel_heart'];\n        const lazyLoadAssets = ['flag_pole', 'cloud', 'bush', 'hill_background'];\n        const loadedLazyAssets = {};\n\n        async function loadImage(id) {\n            return new Promise((resolve) => {\n                const asset = lib.getAsset(id);\n                if (!asset) { resolve(); return; }\n                const img = new Image();\n                img.crossOrigin = 'anonymous';\n                img.onload = () => { images[id] = img; resolve(); };\n                img.onerror = () => { resolve(); };\n                img.src = asset.url;\n            });\n        }\n\n        async function loadImages() {\n            const promises = imageIds.map(id => loadImage(id));\n            await Promise.all(promises);\n        }\n        async function lazyLoadImage(id) {\n            if (!loadedLazyAssets[id]) {\n                await loadImage(id);\n                loadedLazyAssets[id] = true;\n            }\n        }\n\n        // Load audio\n        const audioIds = ['bgm_main','sfx_jump','sfx_coin','sfx_stomp','sfx_block_break','sfx_block_hit','sfx_death','sfx_victory'];\n        async function loadAllAudio() {\n            for (const id of audioIds) await loadAudio(id);\n        }\n\n        try {\n            await loadImages();\n            await loadAllAudio();\n        } catch (err) {\n            lib.log(\"Asset loading failed: \" + err);\n        }\n\n        lib.showGameParameters({\n            name: 'PIXEL DASH KINGDOM',\n            params: {\n                'Background Color': { key: 'backgroundColor', type: 'color' },\n                'Difficulty': { key: 'difficulty', type: 'dropdown', options: [\n                    { label: 'Easy', value: 'easy' },\n                    { label: 'Normal', value: 'normal' },\n                    { label: 'Hard', value: 'hard' }\n                ]},\n                'Max Jumps': { key: 'maxJumps', type: 'slider', min: 1, max: 5, step: 1 }\n            }\n        });\n\n        let gameState = mode === 'edit' ? 'editing' : 'loading';\n        let score = 0;\n        let coins = 0;\n        let cameraX = 0;\n        let cameraY = 0;\n        let bgMusic = null;\n\n        let platforms = [];\n        let blocks = [];\n        let enemies = [];\n        let coinObjects = [];\n        let flagX = config.flagX || 3400;\n        let levelWidth = config.levelWidth || 3600;\n\n        let initialBlocks = [];\n        let initialEnemies = [];\n        let initialCoinObjects = [];\n\n        function initLevel() {\n            const level = (config.levels && config.levels[currentLevelIndex]) ? config.levels[currentLevelIndex] : config;\n            platforms = JSON.parse(JSON.stringify(level.platforms || []));\n            initialBlocks = JSON.parse(JSON.stringify(level.blocks || [])).map(b => ({...b, broken: false, used: false, bounceTimer: 0}));\n            initialEnemies = (level.enemies || []).map(e => ({\n                ...e, \n                vx: e.speed || 60, \n                startX: e.startX !== undefined ? e.startX : e.x, \n                startY: e.startY !== undefined ? e.startY : e.y,\n                alive: true, \n                animTimer: 0\n            }));\n            initialCoinObjects = (level.coins || []).map(c => ({...c, collected: false, animY: 0, animTimer: Math.random() * Math.PI * 2}));\n            flagX = level.flagX || 3400;\n            levelWidth = level.levelWidth || 3600;\n            \n            // Reset player start state for the new level\n            if (typeof player !== 'undefined') {\n                player.lastSafeX = 100;\n                player.lastSafeY = 600;\n            }\n            \n            resetLevelElements();\n        }\n\n        function resetLevelElements() {\n            blocks = initialBlocks.filter(b => !b.broken);\n            enemies = JSON.parse(JSON.stringify(initialEnemies));\n            coinObjects = JSON.parse(JSON.stringify(initialCoinObjects));\n            shields = [];\n            hearts = [];\n            poppedCoins = [];\n            particles = [];\n            floatingTexts = [];\n        }\n\n        let player = { x: 100, y: 600, vx: 0, vy: 0, w: 48, h: 56, onGround: false,\n                       facing: 1, animTimer: 0, coyoteTimer: 0, jumpBufferTimer: 0,\n                       jumpHeld: false, dead: false, jumpCount: 0, maxJumps: config.maxJumps || 5,\n                       lives: 3, lastSafeX: 100, lastSafeY: 600, \n                       invulnerableTimer: 0, shieldTimer: 0,\n                       animation: new Animation() };\n\n        function resetPlayerPosition() {\n            player.x = player.lastSafeX; player.y = player.lastSafeY; player.vx = 0; player.vy = 0;\n            player.onGround = false; player.dead = false; player.facing = 1;\n            player.coyoteTimer = 0; player.jumpBufferTimer = 0; player.jumpCount = 0;\n            cameraX = Math.max(0, Math.min(levelWidth - (isLandscape ? 1280 : 720), player.x - (isLandscape ? 1280 : 720) * 0.35));\n            cameraY = isLandscape ? player.y - 360 : 0;\n        }\n\n        const keys = {};\n        let touchLeft = false, touchRight = false, touchJump = false;\n\n        document.addEventListener('keydown', e => {\n            keys[e.key.toLowerCase()] = true;\n            if (['arrowleft','arrowright','arrowup','arrowdown',' '].includes(e.key.toLowerCase())) e.preventDefault();\n            if (e.key.toLowerCase() === ' ' || e.key.toLowerCase() === 'arrowup' || e.key.toLowerCase() === 'w') {\n                player.jumpBufferTimer = JUMP_BUFFER;\n            }\n        });\n        document.addEventListener('keyup', e => { keys[e.key.toLowerCase()] = false; });\n\n        const btnLeft = document.getElementById('btn-left');\n        const btnRight = document.getElementById('btn-right');\n        const btnJump = document.getElementById('btn-jump');\n        const touchControls = document.getElementById('touch-controls');\n\n        function setupTouch(el, onStart, onEnd) {\n            el.addEventListener('touchstart', e => { e.preventDefault(); onStart(); el.classList.add('active'); });\n            el.addEventListener('touchend', e => { e.preventDefault(); onEnd(); el.classList.remove('active'); });\n            el.addEventListener('touchcancel', e => { onEnd(); el.classList.remove('active'); });\n        }\n        setupTouch(btnLeft, () => touchLeft = true, () => touchLeft = false);\n        setupTouch(btnRight, () => touchRight = true, () => touchRight = false);\n        setupTouch(btnJump, () => { touchJump = true; player.jumpBufferTimer = JUMP_BUFFER; player.jumpHeld = true; }, () => { touchJump = false; player.jumpHeld = false; });\n\n        const hud = document.getElementById('hud');\n        const overlayLoading = document.getElementById('overlay-loading');\n        const overlayMenu = document.getElementById('overlay-menu');\n        const overlayHowToPlay = document.getElementById('overlay-how-to-play');\n        const overlayDeath = document.getElementById('overlay-death');\n        const overlayWin = document.getElementById('overlay-win');\n        const overlayLeaderboard = document.getElementById('overlay-leaderboard');\n        const editToolbar = document.getElementById('edit-toolbar');\n\n        function hideAllOverlays() {\n            overlayLoading.classList.add('hidden');\n            overlayMenu.classList.add('hidden');\n            overlayHowToPlay.classList.add('hidden');\n            overlayDeath.classList.add('hidden');\n            overlayWin.classList.add('hidden');\n            overlayLeaderboard.classList.add('hidden');\n        }\n\n        function showMenu() {\n            gameState = 'menu';\n            hideAllOverlays();\n            overlayMenu.classList.remove('hidden');\n            hud.classList.add('hidden');\n            touchControls.classList.add('hidden');\n            cameraX = 0;\n            currentLevelIndex = 0;\n            initLevel();\n        }\n\n        function showHowToPlay() {\n            gameState = 'howToPlay';\n            hideAllOverlays();\n            overlayHowToPlay.classList.remove('hidden');\n        }\n\n        async function showLeaderboard() {\n            gameState = 'leaderboard';\n            hideAllOverlays();\n            overlayLeaderboard.classList.remove('hidden');\n            const leaderboardList = document.getElementById('leaderboard-list');\n            leaderboardList.innerHTML = '<p style=\"text-align: center; color: #AAA;\">Loading...</p>';\n            try {\n                const response = await lib.getTopNEntriesFromLeaderboard(10);\n                leaderboardList.innerHTML = '';\n                if (response.entries && response.entries.length > 0) {\n                    response.entries.forEach((entry, index) => {\n                        const listItem = document.createElement('li');\n                        listItem.innerHTML = `\n                            <span class=\"rank\">${index + 1}.</span>\n                            <img src=\"${entry.profilePicture || ''}\" alt=\"Profile\" class=\"profile-pic\" onerror=\"this.style.display='none'\">\n                            <span class=\"username\">${entry.username || 'Anonymous'}</span>\n                            <span class=\"score\">${entry.score}</span>\n                        `;\n                        leaderboardList.appendChild(listItem);\n                    });\n                } else {\n                    leaderboardList.innerHTML = '<p style=\"text-align: center; color: #AAA;\">No scores yet. Be the first!</p>';\n                }\n            } catch (error) {\n                leaderboardList.innerHTML = '<p style=\"text-align: center; color: #E74C3C;\">Error loading leaderboard.</p>';\n            }\n        }\n\n        document.getElementById('menu-btn-play').addEventListener('click', startGame);\n        document.getElementById('menu-btn-how').addEventListener('click', showHowToPlay);\n        document.getElementById('menu-btn-leaderboard').addEventListener('click', showLeaderboard);\n        document.getElementById('btn-back').addEventListener('click', showMenu);\n        document.getElementById('leaderboard-btn-back').addEventListener('click', showMenu);\n        document.getElementById('btn-retry').addEventListener('click', retryLevel);\n        document.getElementById('btn-next').addEventListener('click', () => {\n            if (config.levels && currentLevelIndex < config.levels.length - 1) {\n                currentLevelIndex++;\n                startNextLevel();\n            } else {\n                startGame();\n            }\n        });\n\n        function startGame() {\n            hideAllOverlays();\n            getAudioCtx();\n            gameState = 'playing';\n            hud.classList.remove('hidden');\n            touchControls.classList.remove('hidden');\n            score = 0; coins = 0;\n            player.lives = 3;\n            currentLevelIndex = 0;\n            initLevel(); \n            resetLevelElements(); \n            resetPlayerPosition();\n            tutorialActive = true;\n            tutorialStep = 1;\n            runTimer = 0;\n            updateHUD();\n            if (!bgMusic) bgMusic = playSound('bgm_main', true, 0.3);\n        }\n\n        function retryLevel() {\n            startGame();\n        }\n\n        function startNextLevel() {\n            hideAllOverlays();\n            gameState = 'playing';\n            player.lives = 3; // Reset lives on new level per user request\n            initLevel();\n            resetLevelElements();\n            resetPlayerPosition();\n            tutorialActive = false; // Only show on Level 1\n            updateHUD();\n            if (!bgMusic) bgMusic = playSound('bgm_main', true, 0.3);\n        }\n\n        function updateHUD() {\n            document.getElementById('hud-coins-text').textContent = coins;\n            document.getElementById('hud-score-text').textContent = score;\n            document.getElementById('hud-lives-text').textContent = player.lives;\n            const coinImg = document.getElementById('hud-coin-img');\n            if (coinImg) {\n                const coinAsset = lib.getAsset('coin');\n                if (coinAsset && coinImg.src !== coinAsset.url) coinImg.src = coinAsset.url;\n            }\n            const heartImg = document.getElementById('hud-heart-img');\n            if (heartImg) {\n                const heartAsset = lib.getAsset('pixel_heart');\n                if (heartAsset && heartImg.src !== heartAsset.url) heartImg.src = heartAsset.url;\n            }\n            updateTutorialUI();\n        }\n\n        // Handle skip click on tutorial banner\n        const tutorialBanner = document.getElementById('tutorial-banner');\n        if (tutorialBanner) {\n            tutorialBanner.addEventListener('click', () => {\n                tutorialActive = false;\n                updateTutorialUI();\n            });\n        }\n\n        function updateTutorialUI() {\n            const banner = document.getElementById('tutorial-banner');\n            const txt = document.getElementById('tutorial-text');\n            if (!banner || !txt) return;\n\n            if (gameState === 'playing' && tutorialActive) {\n                banner.classList.remove('hidden');\n                if (tutorialStep === 1) {\n                    txt.innerHTML = \"STEP 1: RUN!<br>Use <span style='color: #F4D03F;'>\u25c0 \u25b6 / A D KEYS</span><br>or Left/Right Buttons to move.\";\n                } else if (tutorialStep === 2) {\n                    txt.innerHTML = \"STEP 2: MULTI-JUMP!<br>Press <span style='color: #F4D03F;'>\u25b2 / SPACE / W</span> to leap.<br>You can jump up to 5 times in air!\";\n                } else if (tutorialStep === 3) {\n                    txt.innerHTML = \"STEP 3: BREAK BLOCKS!<br>Jump into <span style='color: #F4D03F;'>BRICKS</span> or <span style='color: #F4D03F;'>? BLOCKS</span> from below for items!\";\n                } else if (tutorialStep === 4) {\n                    txt.innerHTML = \"STEP 4: STOMP ENEMIES!<br>Jump <span style='color: #F4D03F;'>ON TOP</span> of enemies to stomp them!\";\n                } else if (tutorialStep === 5) {\n                    txt.innerHTML = \"STEP 5: REACH THE FLAG!<br>Run to the <span style='color: #F4D03F;'>checkered flag</span> at the end of the level!\";\n                }\n            } else {\n                banner.classList.add('hidden');\n            }\n        }\n\n        async function playerDie() {\n            if (player.dead) return;\n            player.dead = true;\n            player.lives--;\n            updateHUD();\n            if (player.lives > 0) {\n                playSound('sfx_death', false, 0.6);\n                const respawnX = player.y > canvas.height ? player.lastSafeX : player.x;\n                const respawnY = player.y > canvas.height ? player.lastSafeY : player.y;\n                player.x = respawnX; player.y = respawnY; player.vx = 0; player.vy = 0;\n                cameraX = Math.max(0, Math.min(levelWidth - (isLandscape ? 1280 : 720), player.x - (isLandscape ? 1280 : 720) * 0.35));\n                cameraY = isLandscape ? player.y - 360 : 0;\n                player.invulnerableTimer = 3;\n                setTimeout(() => {\n                    player.dead = false;\n                    gameState = 'playing';\n                }, 1000);\n            } else {\n                gameState = 'dead';\n                if (bgMusic) { bgMusic.source.stop(); bgMusic = null; }\n                playSound('sfx_death', false, 0.6);\n                document.getElementById('death-score').textContent = 'Score: ' + score;\n                setTimeout(() => overlayDeath.classList.remove('hidden'), 1000);\n                try { await lib.addPlayerScoreToLeaderboard(score); } catch (e) {}\n            }\n        }\n\n        async function playerWin() {\n            gameState = 'won';\n            if (bgMusic) { bgMusic.source.stop(); bgMusic = null; }\n            playSound('sfx_victory', false, 0.7);\n            const isLastLevel = config.levels ? currentLevelIndex >= config.levels.length - 1 : true;\n            document.getElementById('btn-next').textContent = isLastLevel ? 'PLAY AGAIN' : 'NEXT LEVEL';\n            document.getElementById('win-score').textContent = 'Score: ' + score + ' | Coins: ' + coins;\n            setTimeout(() => overlayWin.classList.remove('hidden'), 500);\n            try { await lib.addPlayerScoreToLeaderboard(score); } catch (e) {}\n        }\n\n        function rectOverlap(a, b) {\n            return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;\n        }\n\n        function updatePlayer(dt) {\n            if (player.dead || gameState !== 'playing') return;\n            \n            // Tutorial state tracking\n            if (tutorialActive) {\n                if (tutorialStep === 1) {\n                    if (Math.abs(player.vx) > 50) {\n                        runTimer += dt;\n                        if (runTimer > 0.8) {\n                            tutorialStep = 2;\n                            updateTutorialUI();\n                        }\n                    }\n                } else if (tutorialStep === 2) {\n                    if (player.vy < -150) {\n                        tutorialStep = 3;\n                        updateTutorialUI();\n                    }\n                } else if (tutorialStep === 4) {\n                    // Fallback in case they pass the first enemy without stomping\n                    if (player.x > 600) {\n                        tutorialStep = 5;\n                        tutorialTimer = 5;\n                        updateTutorialUI();\n                    }\n                } else if (tutorialStep === 5) {\n                    tutorialTimer -= dt;\n                    if (tutorialTimer <= 0) {\n                        tutorialActive = false;\n                        updateTutorialUI();\n                    }\n                }\n            }\n\n            if (player.invulnerableTimer > 0) player.invulnerableTimer -= dt;\n            if (player.shieldTimer > 0) player.shieldTimer -= dt;\n            const moveLeft = keys['arrowleft'] || keys['a'] || touchLeft;\n            const moveRight = keys['arrowright'] || keys['d'] || touchRight;\n            const jumpHeld = keys[' '] || keys['arrowup'] || keys['w'] || touchJump;\n            if (moveLeft) { player.vx -= RUN_ACCEL * dt; player.facing = -1; }\n            else if (moveRight) { player.vx += RUN_ACCEL * dt; player.facing = 1; }\n            else {\n                if (player.vx > 0) player.vx = Math.max(0, player.vx - RUN_DECEL * dt);\n                else player.vx = Math.min(0, player.vx + RUN_DECEL * dt);\n            }\n            player.vx = Math.max(-MAX_RUN_SPEED, Math.min(MAX_RUN_SPEED, player.vx));\n            if (player.onGround) player.coyoteTimer = COYOTE_TIME; else player.coyoteTimer -= dt;\n            player.jumpBufferTimer -= dt;\n            if (player.jumpBufferTimer > 0 && player.jumpCount < player.maxJumps) {\n                if (player.onGround) player.jumpCount = 0;\n                player.jumpCount++;\n                player.vy = JUMP_VELOCITY * (1 - (player.jumpCount - 1) * 0.2);\n                player.onGround = false; player.coyoteTimer = 0; player.jumpBufferTimer = 0;\n                playSound('sfx_jump', false, 0.4);\n            }\n            if (!jumpHeld && player.vy < JUMP_VELOCITY * 0.4) player.vy *= 0.85;\n            player.vy += GRAVITY * dt;\n            if (player.vy > 800) player.vy = 800;\n            player.x += player.vx * dt;\n            player.onGround = false;\n            resolveCollisionsX();\n            player.y += player.vy * dt;\n            resolveCollisionsY();\n            resolveBlockCollisions(dt);\n            if (player.x < 0) player.x = 0;\n            if (player.x > levelWidth - player.w) player.x = levelWidth - player.w;\n            if (player.y > 1000) playerDie();\n            if (player.x + player.w > flagX && player.x < flagX + 40) playerWin();\n            if (!player.onGround && player.vy < -50) player.animation.setState('jump');\n            else if (!player.onGround && player.vy > 50) player.animation.setState('fall');\n            else if (Math.abs(player.vx) > 20) player.animation.setState('run');\n            else player.animation.setState('idle');\n            player.animation.update(dt);\n        }\n\n        function getColliders() {\n            let colliders = [];\n            for (const p of platforms) colliders.push(p);\n            for (const b of blocks) if (!b.broken) colliders.push({ x: b.x, y: b.y, w: TILE, h: TILE });\n            return colliders;\n        }\n\n        function resolveCollisionsX() {\n            const colliders = getColliders();\n            const pr = { x: player.x, y: player.y, w: player.w, h: player.h };\n            for (const c of colliders) {\n                if (rectOverlap(pr, c)) {\n                    if (player.vx > 0) player.x = c.x - player.w;\n                    else if (player.vx < 0) player.x = c.x + c.w;\n                    player.vx = 0; pr.x = player.x;\n                }\n            }\n        }\n\n        function resolveCollisionsY() {\n            const colliders = getColliders();\n            const pr = { x: player.x, y: player.y, w: player.w, h: player.h };\n            for (const c of colliders) {\n                if (rectOverlap(pr, c)) {\n                    if (player.vy > 0) {\n                        player.y = c.y - player.h; player.vy = 0; player.onGround = true;\n                        player.jumpCount = 0; player.lastSafeX = player.x; player.lastSafeY = player.y;\n                    } else if (player.vy < 0) {\n                        player.y = c.y + c.h; player.vy = 0;\n                    }\n                    pr.y = player.y;\n                }\n            }\n        }\n\n        function spawnBlockReward(b) {\n            const rand = Math.random();\n            let rewardType = 'none';\n            \n            // Heart chance decreases as levels progress\n            const heartChance = Math.max(0.02, 0.15 - (currentLevelIndex * 0.03));\n            \n            if (rand < 0.6) rewardType = 'coin';\n            else if (rand < 0.8) rewardType = 'shield';\n            else if (rand < 0.8 + heartChance) rewardType = 'heart';\n            \n            if (rewardType === 'coin') {\n                poppedCoins.push({ x: b.x + TILE/2, y: b.y, vy: -600, timer: 0 });\n                coins++; score += 100;\n                updateHUD();\n                playSound('sfx_coin', false, 0.5);\n                spawnFloatingText(b.x + TILE/2, b.y - 20, '+100', '#F4D03F');\n            } else if (rewardType === 'shield') {\n                shields.push({ x: b.x + TILE/2, y: b.y, vx: 120, vy: -400, life: 15, maxLife: 15, collected: false, isPopping: true, popTimer: 0.4 });\n                playSound('sfx_block_hit', false, 0.5);\n            } else if (rewardType === 'heart') {\n                hearts.push({ x: b.x + TILE/2, y: b.y, vx: 100, vy: -450, life: 20, maxLife: 20, collected: false, isPopping: true, popTimer: 0.4 });\n                playSound('sfx_block_hit', false, 0.5);\n                spawnFloatingText(b.x + TILE/2, b.y - 20, '1-UP?', '#FF69B4');\n            } else {\n                playSound('sfx_block_hit', false, 0.4);\n            }\n        }\n\n        function resolveBlockCollisions(dt) {\n            const headRect = { x: player.x + 8, y: player.y - 2, w: player.w - 16, h: 4 };\n            for (const b of blocks) {\n                if (b.broken) continue;\n                const br = { x: b.x, y: b.y, w: TILE, h: TILE };\n                if (player.vy < 0 && rectOverlap(headRect, br)) {\n                    if (tutorialActive && tutorialStep === 3) {\n                        tutorialStep = 4;\n                        updateTutorialUI();\n                    }\n                    if (b.type === 'brick') {\n                        b.broken = true;\n                        playSound('sfx_block_break', false, 0.5);\n                        spawnParticles(b.x + TILE/2, b.y + TILE/2, '#D68910', 6);\n                        if (b.hasCoin) spawnBlockReward(b);\n                        blocks = blocks.filter(block => block !== b);\n                    } else if (b.type === 'question' && !b.used) {\n                        b.used = true;\n                        spawnBlockReward(b);\n                        b.bounceTimer = 0.2;\n                    } else {\n                        playSound('sfx_block_hit', false, 0.4);\n                    }\n                }\n            }\n        }\n\n        function updateEnemies(dt) {\n            for (const e of enemies) {\n                if (!e.alive) continue;\n                e.animTimer += dt;\n                if (e.type === 'walker' || e.type === 'snail' || e.type === 'spider') {\n                    const speedMult = e.type === 'spider' ? 1.5 : (e.type === 'snail' ? 0.5 : 1);\n                    e.x += e.vx * dt * speedMult;\n                    if (e.x < e.startX - (e.range || 100) || e.x > e.startX + (e.range || 100)) e.vx = -e.vx;\n                } else if (e.type === 'flyer') {\n                    e.y = e.startY + Math.sin(e.animTimer * 2) * (e.range || 50);\n                }\n                const er = { x: e.x, y: e.y, w: 32, h: 32 };\n                const pr = { x: player.x, y: player.y, w: player.w, h: player.h };\n                if (gameState === 'playing' && rectOverlap(pr, er)) {\n                    if (player.invulnerableTimer <= 0 && player.shieldTimer <= 0) {\n                        if (player.vy > 0 && player.y < e.y) {\n                            e.alive = false;\n                            if (tutorialActive && tutorialStep === 4) {\n                                tutorialStep = 5;\n                                tutorialTimer = 5; // Keep step 5 visible for 5 seconds\n                                updateTutorialUI();\n                            }\n                            const jumpHeld = keys[' '] || keys['arrowup'] || keys['w'] || touchJump;\n                            player.vy = jumpHeld ? JUMP_VELOCITY * 0.9 : JUMP_VELOCITY * 0.6;\n                            score += 200;\n                            spawnFloatingText(e.x + 16, e.y - 10, '+200', '#E74C3C');\n                            spawnParticles(e.x + 16, e.y + 16, '#8B4513', 4);\n                            playSound('sfx_stomp', false, 0.5);\n                            updateHUD();\n                        } else {\n                            playerDie();\n                        }\n                    } else if (player.shieldTimer > 0) {\n                        e.alive = false; score += 200;\n                        spawnFloatingText(e.x + 16, e.y - 10, '+200', '#E74C3C');\n                        spawnParticles(e.x + 16, e.y + 16, '#8B4513', 4);\n                        playSound('sfx_stomp', false, 0.5);\n                        updateHUD();\n                    }\n                }\n            }\n        }\n\n        function updateCoins(dt) {\n            for (const c of coinObjects) {\n                if (c.collected) continue;\n                c.animTimer += dt * 3; c.animY = Math.sin(c.animTimer) * 4;\n                const cr = { x: c.x - 12, y: c.y + c.animY - 12, w: 24, h: 24 };\n                const pr = { x: player.x, y: player.y, w: player.w, h: player.h };\n                if (gameState === 'playing' && rectOverlap(pr, cr)) {\n                    c.collected = true; coins++; score += 50;\n                    playSound('sfx_coin', false, 0.5);\n                    spawnFloatingText(c.x, c.y - 20, '+50', '#F4D03F');\n                    updateHUD();\n                }\n            }\n        }\n\n        function updateShields(dt) {\n            for (let i = shields.length - 1; i >= 0; i--) {\n                const shield = shields[i];\n                if (shield.collected) continue;\n                if (shield.isPopping) {\n                    shield.popTimer -= dt; shield.y += shield.vy * dt; shield.vy += GRAVITY * dt;\n                    if (shield.popTimer <= 0) { shield.isPopping = false; shield.vy = 0; }\n                } else {\n                    shield.vy += GRAVITY * dt; shield.y += shield.vy * dt; shield.x += (shield.vx || 0) * dt;\n                    const sr = { x: shield.x - 16, y: shield.y - 16, w: 32, h: 32 };\n                    for (const p of platforms) {\n                        if (rectOverlap(sr, p)) {\n                            if (shield.vy > 0 && shield.y < p.y + 10) { shield.y = p.y - 16; shield.vy = 0; }\n                            else { shield.vx *= -1; shield.x += shield.vx * dt * 2; }\n                        }\n                    }\n                    for (const b of blocks) {\n                        if (b.broken) continue;\n                        if (rectOverlap(sr, { x: b.x, y: b.y, w: TILE, h: TILE })) { shield.vx *= -1; shield.x += shield.vx * dt * 2; }\n                    }\n                }\n                shield.life -= dt;\n                if (shield.life <= 0) { shields.splice(i, 1); continue; }\n                if (gameState === 'playing' && rectOverlap({x:player.x, y:player.y, w:player.w, h:player.h}, {x:shield.x-16, y:shield.y-16, w:32, h:32})) {\n                    shield.collected = true; player.shieldTimer = 5;\n                    spawnFloatingText(shield.x, shield.y - 20, 'SHIELD!', '#00FFFF');\n                    shields.splice(i, 1); updateHUD();\n                }\n            }\n        }\n\n        function updateHearts(dt) {\n            for (let i = hearts.length - 1; i >= 0; i--) {\n                const heart = hearts[i];\n                if (heart.collected) continue;\n                if (heart.isPopping) {\n                    heart.popTimer -= dt; heart.y += heart.vy * dt; heart.vy += GRAVITY * dt;\n                    if (heart.popTimer <= 0) { heart.isPopping = false; heart.vy = 0; }\n                } else {\n                    heart.vy += GRAVITY * dt; heart.y += heart.vy * dt; heart.x += (heart.vx || 0) * dt;\n                    const hr = { x: heart.x - 16, y: heart.y - 16, w: 32, h: 32 };\n                    for (const p of platforms) {\n                        if (rectOverlap(hr, p)) {\n                            if (heart.vy > 0 && heart.y < p.y + 10) { heart.y = p.y - 16; heart.vy = 0; }\n                            else { heart.vx *= -1; heart.x += heart.vx * dt * 2; }\n                        }\n                    }\n                }\n                heart.life -= dt;\n                if (heart.life <= 0) { hearts.splice(i, 1); continue; }\n                if (gameState === 'playing' && rectOverlap({x:player.x, y:player.y, w:player.w, h:player.h}, {x:heart.x-16, y:heart.y-16, w:32, h:32})) {\n                    heart.collected = true; player.lives++;\n                    spawnFloatingText(heart.x, heart.y - 20, '1-UP!', '#FF0000');\n                    hearts.splice(i, 1); updateHUD();\n                }\n            }\n        }\n\n        function updatePoppedCoins(dt) {\n            for (let i = poppedCoins.length - 1; i >= 0; i--) {\n                const pc = poppedCoins[i]; pc.y += pc.vy * dt; pc.vy += GRAVITY * 1.5 * dt;\n                pc.timer += dt; if (pc.timer > 0.5) poppedCoins.splice(i, 1);\n            }\n        }\n\n        function updateParticles(dt) {\n            for (let i = particles.length - 1; i >= 0; i--) {\n                const p = particles[i]; p.x += p.vx * dt; p.y += p.vy * dt; p.vy += 600 * dt;\n                p.life -= dt; if (p.life <= 0) particles.splice(i, 1);\n            }\n            for (let i = floatingTexts.length - 1; i >= 0; i--) {\n                const t = floatingTexts[i]; t.life -= dt; if (t.life <= 0) floatingTexts.splice(i, 1);\n            }\n        }\n\n        function updateBlocks(dt) {\n            for (const b of blocks) if (b.bounceTimer > 0) b.bounceTimer -= dt;\n        }\n\n        function updateCamera() {\n            const targetX = player.x - (isLandscape ? 1280 : 720) * 0.35 + player.facing * 50;\n            cameraX += (targetX - cameraX) * 0.1;\n            cameraX = Math.max(0, Math.min(levelWidth - (isLandscape ? 1280 : 720), cameraX));\n\n            if (isLandscape) {\n                const targetY = player.y - 360;\n                cameraY += (targetY - cameraY) * 0.1;\n                cameraY = Math.max(-100, Math.min(400, cameraY));\n            } else {\n                cameraY = 0;\n            }\n        }\n        function drawBackground() {\n            const level = (config.levels && config.levels[currentLevelIndex]) ? config.levels[currentLevelIndex] : config;\n            const bgColor = level.backgroundColor || '#AED6F1'; \n\n            // Sky gradient instead of flat color\n            const skyGrad = ctx.createLinearGradient(0, 0, 0, VIRTUAL_HEIGHT * 2);\n            if (bgColor === '#AED6F1') {\n                skyGrad.addColorStop(0, '#5DADE2');\n                skyGrad.addColorStop(0.4, '#AED6F1');\n                skyGrad.addColorStop(1, '#D4EFDF');\n            } else if (bgColor === '#FFD700') {\n                skyGrad.addColorStop(0, '#F39C12');\n                skyGrad.addColorStop(0.4, '#FFD700');\n                skyGrad.addColorStop(1, '#FDEBD0');\n            } else if (bgColor === '#8B008B') {\n                skyGrad.addColorStop(0, '#1B1464');\n                skyGrad.addColorStop(0.4, '#4A235A');\n                skyGrad.addColorStop(1, '#8B008B');\n            } else if (bgColor === '#006400') {\n                skyGrad.addColorStop(0, '#0B3D0B');\n                skyGrad.addColorStop(0.5, '#145A14');\n                skyGrad.addColorStop(1, '#006400');\n            } else {\n                skyGrad.addColorStop(0, bgColor);\n                skyGrad.addColorStop(1, bgColor);\n            }\n            ctx.fillStyle = skyGrad;\n            ctx.fillRect(0, 0, VIRTUAL_WIDTH * 2, VIRTUAL_HEIGHT * 2);\n\n            // Decorative pixel stars for dark levels\n            if (bgColor === '#8B008B' || bgColor === '#1B1464') {\n                ctx.fillStyle = 'rgba(255,255,255,0.7)';\n                const time = performance.now() / 1000;\n                for (let i = 0; i < 30; i++) {\n                    const sx = ((i * 137 + 50) % (VIRTUAL_WIDTH * 2));\n                    const sy = ((i * 89 + 20) % (VIRTUAL_HEIGHT));\n                    const twinkle = Math.sin(time * 2 + i) * 0.4 + 0.6;\n                    ctx.globalAlpha = twinkle;\n                    ctx.fillRect(sx, sy, 4, 4);\n                }\n                ctx.globalAlpha = 1;\n            }\n\n            // Hills parallax (middle distance)\n            if (images['hill_background']) {\n                const hillParallax = cameraX * 0.4;\n                const hillImg = images['hill_background'];\n                const hillW = 720;\n                const hillH = 300;\n                \n                ctx.save();\n                ctx.globalAlpha = 0.6;\n                ctx.filter = `hue-rotate(${currentLevelIndex * 70}deg) brightness(${0.8 + (currentLevelIndex % 3) * 0.1})`;\n                \n                for (let i = 0; i < 10; i++) {\n                    const hx = (i * hillW) - (hillParallax % (hillW * 10));\n                    const hy = (isLandscape ? 720 : 1280) - hillH - 120 - (cameraY * 0.4);\n                    ctx.drawImage(hillImg, hx, hy, hillW, hillH);\n                }\n                ctx.restore();\n            }\n\n            // Clouds parallax (far distance) with gentle bob\n            if (images['cloud']) {\n                const cloudParallax = cameraX * 0.2;\n                const cloudImg = images['cloud'];\n                const time = performance.now() / 1000;\n                for (let i = 0; i < 8; i++) {\n                    const cx = (i * 500) - (cloudParallax % (levelWidth + 1000));\n                    const bob = Math.sin(time * 0.5 + i * 1.3) * 8;\n                    ctx.globalAlpha = 0.85;\n                    ctx.drawImage(cloudImg, cx, 80 + (i % 3) * 110 + bob, 180, 90);\n                }\n                ctx.globalAlpha = 1;\n            }\n        }\n\n        function drawPlatforms() {\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            for (const p of platforms) {\n                const solidImg = images['solid_block'];\n                if (solidImg) {\n                    for (let x = 0; x < p.w; x += TILE) {\n                        for (let y = 0; y < p.h; y += TILE) {\n                            const drawW = Math.min(TILE, p.w - x);\n                            const drawH = Math.min(TILE, p.h - y);\n                            ctx.drawImage(solidImg, 0, 0, (drawW/TILE)*solidImg.width, (drawH/TILE)*solidImg.height, p.x + x, p.y + y, drawW, drawH);\n                        }\n                    }\n                    // Grass top with highlight\n                    const grassGrad = ctx.createLinearGradient(0, p.y, 0, p.y + 12);\n                    grassGrad.addColorStop(0, '#2ECC71');\n                    grassGrad.addColorStop(1, '#1E8449');\n                    ctx.fillStyle = grassGrad;\n                    ctx.fillRect(p.x, p.y, p.w, 12);\n                    // Grass highlight dots\n                    ctx.fillStyle = '#82E0AA';\n                    for (let gx = p.x + 8; gx < p.x + p.w; gx += 32) {\n                        ctx.fillRect(gx, p.y + 2, 6, 4);\n                    }\n                } else {\n                    ctx.fillStyle = '#27AE60';\n                    ctx.fillRect(p.x, p.y, p.w, 12);\n                    ctx.fillStyle = '#8B4513';\n                    ctx.fillRect(p.x, p.y + 12, p.w, p.h - 12);\n                }\n            }\n            ctx.restore();\n        }\n\n        function drawBlocks() {\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            for (const b of blocks) {\n                if (b.broken) continue;\n                let img;\n                let bounceY = 0;\n                if (b.bounceTimer > 0) bounceY = -Math.sin(b.bounceTimer / 0.2 * Math.PI) * 8;\n\n                if (b.type === 'brick') img = images['brick_block'];\n                else if (b.type === 'question' && !b.used) img = images['question_block'];\n                else if (b.type === 'question' && b.used) img = images['solid_block'];\n                else img = images['solid_block'];\n\n                if (img) {\n                    ctx.drawImage(img, b.x, b.y + bounceY, TILE, TILE);\n                } else {\n                    ctx.fillStyle = b.type === 'brick' ? '#D68910' : b.type === 'question' ? '#F4D03F' : '#7F8C8D';\n                    ctx.fillRect(b.x, b.y + bounceY, TILE, TILE);\n                    ctx.strokeStyle = '#000';\n                    ctx.strokeRect(b.x, b.y + bounceY, TILE, TILE);\n                }\n            }\n            ctx.restore();\n        }\n\n        function drawCoins() {\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            for (const c of coinObjects) {\n                if (c.collected) continue;\n                const img = images['coin'];\n                const yOff = c.animY;\n                if (img) {\n                    // Coin glow\n                    ctx.shadowColor = '#FFD700';\n                    ctx.shadowBlur = 8;\n                    ctx.drawImage(img, c.x - 14, c.y + yOff - 14, 28, 28);\n                    ctx.shadowBlur = 0;\n                } else {\n                    ctx.fillStyle = '#F4D03F';\n                    ctx.beginPath();\n                    ctx.arc(c.x, c.y + yOff, 10, 0, Math.PI * 2);\n                    ctx.fill();\n                }\n            }\n            ctx.restore();\n        }\n\n        function drawEnemies() {\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            for (const e of enemies) {\n                if (!e.alive) continue;\n                let img;\n                if (e.type === 'flyer') img = images['enemy_flyer'];\n                else if (e.type === 'spider') img = images['enemy_spider'];\n                else if (e.type === 'snail') img = images['enemy_snail'];\n                else img = images['enemy_walker'];\n                \n                const size = e.type === 'flyer' ? 40 : 32;\n                // Drop shadow\n                ctx.fillStyle = 'rgba(0,0,0,0.2)';\n                ctx.beginPath();\n                ctx.ellipse(e.x + size/2, e.y + size + 2, size * 0.4, 6, 0, 0, Math.PI * 2);\n                ctx.fill();\n                \n                // Bounce animation for walkers\n                const bounce = (e.type === 'walker' || e.type === 'snail' || e.type === 'spider') ? Math.abs(Math.sin(e.animTimer * 6)) * 4 : 0;\n                \n                if (img) {\n                    ctx.save();\n                    if (e.vx < 0) {\n                        ctx.translate(e.x + size, e.y - bounce);\n                        ctx.scale(-1, 1);\n                        ctx.drawImage(img, 0, 0, size, size);\n                    } else {\n                        ctx.drawImage(img, e.x, e.y - bounce, size, size);\n                    }\n                    ctx.restore();\n                } else {\n                    ctx.fillStyle = e.type === 'flyer' ? '#E74C3C' : '#8B4513';\n                    ctx.fillRect(e.x, e.y - bounce, size, size);\n                }\n            }\n            ctx.restore();\n        }\n\n        function drawShields() {\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            for (const shield of shields) {\n                if (shield.collected) continue;\n                const img = images['shield'];\n                if (img) {\n                    ctx.drawImage(img, shield.x - 16, shield.y - 16, 32, 32);\n                } else {\n                    ctx.fillStyle = '#00FFFF';\n                    ctx.fillRect(shield.x - 16, shield.y - 16, 32, 32);\n                }\n            }\n            ctx.restore();\n        }\n\n        function drawHearts() {\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            for (const heart of hearts) {\n                if (heart.collected) continue;\n                const img = images['pixel_heart'];\n                if (img) {\n                    ctx.drawImage(img, heart.x - 16, heart.y - 16, 32, 32);\n                } else {\n                    ctx.fillStyle = '#FF0000';\n                    ctx.fillRect(heart.x - 16, heart.y - 16, 32, 32);\n                }\n            }\n            ctx.restore();\n        }\n\n        function drawPoppedCoins() {\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            for (const pc of poppedCoins) {\n                const img = images['coin'];\n                if (img) {\n                    ctx.drawImage(img, pc.x - 12, pc.y - 12, 24, 24);\n                } else {\n                    ctx.fillStyle = '#F4D03F';\n                    ctx.beginPath();\n                    ctx.arc(pc.x, pc.y, 10, 0, Math.PI * 2);\n                    ctx.fill();\n                }\n            }\n            ctx.restore();\n        }\n\n        function drawPlayer() {\n            if (gameState === 'dead') return;\n            \n            const isRespawning = player.dead;\n            const isInvulnerable = player.invulnerableTimer > 0;\n            \n            if ((isRespawning || isInvulnerable) && Math.floor(Date.now() / 100) % 2 === 0) {\n                ctx.globalAlpha = 0.5;\n            }\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            let img = images[player.animation.getCurrentFrame()];\n\n            if (img) {\n                ctx.save();\n                // Shield glow effect\n                if (player.shieldTimer > 0 && !player.dead) {\n                    const pulse = Math.sin(Date.now() / 80) * 0.3 + 0.5;\n                    ctx.shadowColor = 'cyan';\n                    ctx.shadowBlur = 20 + pulse * 10;\n                    ctx.beginPath();\n                    ctx.arc(player.x + player.w/2, player.y + player.h/2, 44, 0, Math.PI * 2);\n                    ctx.strokeStyle = `rgba(0, 255, 255, ${pulse})`;\n                    ctx.lineWidth = 4;\n                    ctx.stroke();\n                    ctx.shadowBlur = 0;\n                }\n\n                // Running dust particles\n                if (player.onGround && Math.abs(player.vx) > 150 && Math.random() < 0.3) {\n                    particles.push({\n                        x: player.x + player.w/2 - player.facing * 10,\n                        y: player.y + player.h - 4,\n                        vx: -player.facing * (50 + Math.random() * 30),\n                        vy: -Math.random() * 40,\n                        life: 0.3, maxLife: 0.3,\n                        color: 'rgba(200,180,150,0.6)', size: 6 + Math.random() * 4\n                    });\n                }\n\n                // Squash and stretch\n                let scaleX = 1, scaleY = 1;\n                if (player.vy < -200) { scaleX = 0.85; scaleY = 1.15; }\n                else if (player.vy > 200) { scaleX = 1.1; scaleY = 0.9; }\n                else if (Math.abs(player.vx) > 200) { scaleX = 1.05; scaleY = 0.95; }\n\n                const cx = player.x + player.w/2;\n                const cy = player.y + player.h/2;\n                ctx.translate(cx, cy);\n                ctx.scale(player.facing * scaleX, scaleY);\n                ctx.drawImage(img, -player.w/2, -player.h/2, player.w, player.h);\n                ctx.restore();\n            } else {\n                ctx.fillStyle = '#2E86C1';\n                ctx.fillRect(player.x, player.y, player.w, player.h);\n                ctx.fillStyle = '#E74C3C';\n                ctx.fillRect(player.x + 8, player.y, player.w - 16, 16);\n            }\n            ctx.restore();\n            ctx.globalAlpha = 1;\n        }\n\n        function drawFlag() {\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            const img = images['flag_pole'];\n            if (img) {\n                const asset = lib.getAsset('flag_pole');\n                const ar = asset.aspect_ratio || [64, 128];\n                const h = 160;\n                const w = h * ar[0] / ar[1];\n                ctx.drawImage(img, flagX - w/4, 800 - h + 10, w, h);\n            } else {\n                ctx.fillStyle = '#8B4513';\n                ctx.fillRect(flagX + 15, 800 - 200, 8, 200);\n                ctx.fillStyle = '#000';\n                ctx.fillRect(flagX, 800 - 200, 40, 30);\n            }\n            ctx.restore();\n        }\n\n        function drawBushes() {\n            if (!images['bush']) return;\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            const bushPositions = [200, 800, 1500, 2300, 3000];\n            for (const bx of bushPositions) {\n                // Find platform at this x\n                for (const p of platforms) {\n                    if (bx > p.x && bx < p.x + p.w - 96) {\n                        ctx.drawImage(images['bush'], bx, p.y - 40, 96, 48);\n                        break;\n                    }\n                }\n            }\n            ctx.restore();\n        }\n\n        function drawParticles() {\n            ctx.save();\n            ctx.translate(-cameraX, -cameraY);\n            for (const p of particles) {\n                ctx.globalAlpha = p.life / p.maxLife;\n                ctx.fillStyle = p.color;\n                // Rotate particles for more dynamic look\n                ctx.save();\n                ctx.translate(p.x, p.y);\n                ctx.rotate(p.life * 5);\n                ctx.fillRect(-p.size/2, -p.size/2, p.size, p.size);\n                ctx.restore();\n            }\n            ctx.globalAlpha = 1;\n            for (const t of floatingTexts) {\n                const progress = 1 - t.life;\n                ctx.globalAlpha = t.life;\n                ctx.fillStyle = t.color;\n                ctx.font = 'bold 18px \"Press Start 2P\"';\n                ctx.textAlign = 'center';\n                ctx.shadowColor = 'rgba(0,0,0,0.5)';\n                ctx.shadowBlur = 4;\n                ctx.shadowOffsetY = 2;\n                ctx.fillText(t.text, t.x, t.y - progress * 40);\n                ctx.shadowBlur = 0;\n            }\n            ctx.globalAlpha = 1;\n            ctx.restore();\n        }\n\n        // Edit mode\n        let editTool = 'select';\n        let selectedObj = null;\n        let dragging = false;\n        let dragOffX = 0, dragOffY = 0;\n\n        if (mode === 'edit') {\n            hud.classList.add('hidden');\n            touchControls.classList.add('hidden');\n            editToolbar.classList.remove('hidden');\n            gameState = 'editing';\n            initLevel();\n\n            const editBtns = editToolbar.querySelectorAll('.edit-btn');\n            editBtns.forEach(btn => {\n                btn.addEventListener('click', () => {\n                    editBtns.forEach(b => b.classList.remove('selected'));\n                    btn.classList.add('selected');\n                    editTool = btn.dataset.tool;\n                    selectedObj = null;\n                });\n            });\n            editBtns[0].classList.add('selected');\n\n            // Edit interactions\n            let editCamDrag = false;\n            let lastTouchX = 0;\n\n            canvas.addEventListener('pointerdown', e => {\n                const mx = e.offsetX * (canvas.width / canvas.clientWidth) + cameraX;\n                const my = e.offsetY * (canvas.height / canvas.clientHeight) + cameraY;\n\n                if (editTool === 'select') {\n                    selectedObj = null;\n                    // Check blocks\n                    for (const b of blocks) {\n                        if (mx > b.x && mx < b.x + TILE && my > b.y && my < b.y + TILE) {\n                            selectedObj = { type: 'block', obj: b };\n                            dragOffX = mx - b.x; dragOffY = my - b.y;\n                            dragging = true;\n                            return;\n                        }\n                    }\n                    // Check enemies\n                    for (const e2 of enemies) {\n                        if (mx > e2.x && mx < e2.x + 32 && my > e2.y && my < e2.y + 32) {\n                            selectedObj = { type: 'enemy', obj: e2 };\n                            dragOffX = mx - e2.x; dragOffY = my - e2.y;\n                            dragging = true;\n                            return;\n                        }\n                    }\n                    // Pan camera\n                    editCamDrag = true;\n                    lastTouchX = e.clientX;\n                } else if (editTool === 'delete') {\n                    const level = (config.levels && config.levels[currentLevelIndex]) ? config.levels[currentLevelIndex] : config;\n                    for (let i = blocks.length - 1; i >= 0; i--) {\n                        const b = blocks[i];\n                        if (mx > b.x && mx < b.x + TILE && my > b.y && my < b.y + TILE) {\n                            blocks.splice(i, 1);\n                            level.blocks = blocks.map(bl => ({x:bl.x, y:bl.y, type:bl.type, hasCoin:bl.hasCoin}));\n                            return;\n                        }\n                    }\n                    for (let i = enemies.length - 1; i >= 0; i--) {\n                        const en = enemies[i];\n                        if (mx > en.x && mx < en.x + 32 && my > en.y && my < en.y + 32) {\n                            enemies.splice(i, 1);\n                            level.enemies = enemies.map(en2 => ({x:en2.x, y:en2.y, startY:en2.startY, type:en2.type, speed:Math.abs(en2.vx), range:en2.range}));\n                            return;\n                        }\n                    }\n                    for (let i = coinObjects.length - 1; i >= 0; i--) {\n                        const c = coinObjects[i];\n                        if (mx > c.x - 12 && mx < c.x + 12 && my > c.y - 12 && my < c.y + 12) {\n                            coinObjects.splice(i, 1);\n                            level.coins = coinObjects.map(co => ({x:co.x, y:co.y}));\n                            return;\n                        }\n                    }\n                } else if (['brick','question','solid'].includes(editTool)) {\n                    const snapX = Math.floor(mx / TILE) * TILE;\n                    const snapY = Math.floor(my / TILE) * TILE;\n                    const level = (config.levels && config.levels[currentLevelIndex]) ? config.levels[currentLevelIndex] : config;\n                    blocks.push({ x: snapX, y: snapY, type: editTool, hasCoin: editTool === 'brick' ? Math.random() > 0.5 : false, broken: false, used: false });\n                    level.blocks = blocks.map(bl => ({x:bl.x, y:bl.y, type:bl.type, hasCoin:bl.hasCoin}));\n                } else if (['enemy_walker', 'enemy_flyer', 'enemy_spider', 'enemy_snail'].includes(editTool)) {\n                    const level = (config.levels && config.levels[currentLevelIndex]) ? config.levels[currentLevelIndex] : config;\n                    const type = editTool.replace('enemy_', '');\n                    enemies.push({ x: mx - 16, y: my - 16, startX: mx - 16, startY: my - 16, type: type, vx: 60, speed: 60, range: 100, alive: true, animTimer: 0 });\n                    level.enemies = enemies.map(en => ({x:en.x, y:en.y, startY:en.startY, type:en.type, speed:Math.abs(en.vx), range:en.range}));\n                } else if (editTool === 'coin') {\n                    const level = (config.levels && config.levels[currentLevelIndex]) ? config.levels[currentLevelIndex] : config;\n                    coinObjects.push({ x: mx, y: my, collected: false, animY: 0, animTimer: 0 });\n                    level.coins = coinObjects.map(co => ({x:co.x, y:co.y}));\n                }\n            });\n\n            canvas.addEventListener('pointermove', e => {\n                if (dragging && selectedObj) {\n                    const mx = e.offsetX * (canvas.width / canvas.clientWidth) + cameraX;\n                    const my = e.offsetY * (canvas.height / canvas.clientHeight) + cameraY;\n                    const snapX = Math.round((mx - dragOffX) / TILE) * TILE;\n                    const snapY = Math.round((my - dragOffY) / TILE) * TILE;\n                    selectedObj.obj.x = snapX;\n                    selectedObj.obj.y = snapY;\n                    if (selectedObj.obj.startX !== undefined) selectedObj.obj.startX = snapX;\n                    if (selectedObj.obj.startY !== undefined) selectedObj.obj.startY = snapY;\n                } else if (editCamDrag) {\n                    const dx = e.clientX - lastTouchX;\n                    cameraX -= dx * 4; // Multiplied because of half resolution\n                    cameraX = Math.max(0, Math.min(levelWidth - 720, cameraX));\n                    lastTouchX = e.clientX;\n                }\n            });\n\n            canvas.addEventListener('pointerup', () => {\n                if (dragging && selectedObj) {\n                    // Save to config\n                    const level = (config.levels && config.levels[currentLevelIndex]) ? config.levels[currentLevelIndex] : config;\n                    if (selectedObj.type === 'block') level.blocks = blocks.map(bl => ({x:bl.x, y:bl.y, type:bl.type, hasCoin:bl.hasCoin}));\n                    else if (selectedObj.type === 'enemy') level.enemies = enemies.map(en => ({x:en.x, y:en.y, startY:en.startY||en.y, type:en.type, speed:Math.abs(en.vx), range:en.range||100}));\n                }\n                dragging = false;\n                editCamDrag = false;\n            });\n        } else {\n            // Play mode\n            hideAllOverlays();\n            overlayLoading.classList.remove('hidden');\n            hud.classList.add('hidden');\n            touchControls.classList.add('hidden');\n            \n            setTimeout(() => {\n                showMenu();\n            }, 1000);\n            \n            initLevel();\n        }\n\n        // Main game loop\n        let lastTime = performance.now();\n\n        function gameLoop(timestamp) {\n            let dt = (timestamp - lastTime) / 1000;\n            lastTime = timestamp;\n            dt = Math.min(dt, 0.11);\n\n            ctx.clearRect(0, 0, canvas.width, canvas.height);\n            \n            ctx.save();\n            // Scale context so we can keep using 720x1280 coordinates on a 360x640 canvas\n            ctx.scale(0.5, 0.5);\n            ctx.imageSmoothingEnabled = false;\n\n            if (mode === 'play') {\n                if (gameState === 'loading') {\n                    // Aesthetic loading screen: animated background and hero\n                    const time = performance.now() / 1000;\n                    const wave = Math.sin(time * 2) * 20 + 20;\n                    const waveColor1 = `hsl(200, 70%, ${50 + Math.sin(time * 0.5) * 10}%)`;\n                    const waveColor2 = `hsl(220, 80%, ${60 + Math.cos(time * 0.5) * 10}%)`;\n\n                    ctx.fillStyle = waveColor1;\n                    ctx.fillRect(0, 0, VIRTUAL_WIDTH * 2, VIRTUAL_HEIGHT * 2);\n\n                    ctx.fillStyle = waveColor2;\n                    ctx.beginPath();\n                    ctx.moveTo(0, VIRTUAL_HEIGHT * 2);\n                    for (let i = 0; i <= VIRTUAL_WIDTH * 2; i += 20) {\n                        ctx.lineTo(i, VIRTUAL_HEIGHT * 2 - 100 + Math.sin((i + time * 100) * 0.05) * wave);\n                    }\n                    ctx.lineTo(VIRTUAL_WIDTH * 2, VIRTUAL_HEIGHT * 2);\n                    ctx.closePath();\n                    ctx.fill();\n\n                    // Draw some floating \"pixel stars\"\n                    ctx.fillStyle = \"rgba(255, 255, 255, 0.8)\";\n                    for(let i=0; i<15; i++) {\n                        const sx = ((i * 137) + time * 50) % (VIRTUAL_WIDTH * 2);\n                        const sy = ((i * 223) + time * 30) % (VIRTUAL_HEIGHT * 2);\n                        const size = 4 + (i % 3) * 4;\n                        ctx.fillRect(sx, sy, size, size);\n                    }\n\n                    // Draw hero running in place\n                    player.animation.setState('run');\n                    player.animation.update(dt);\n                    const frame = player.animation.getCurrentFrame();\n                    const img = images[frame];\n                    if (img) {\n                        ctx.drawImage(img, VIRTUAL_WIDTH - player.w/2, 800, player.w, player.h);\n                    }\n\n                } else if (gameState === 'menu' || gameState === 'howToPlay' || gameState === 'leaderboard') {\n                    // Update idling hero or anything else if needed\n                    player.animation.setState('idle');\n                    player.animation.update(dt);\n                } else if (gameState === 'playing') {\n                    updatePlayer(dt);\n                    updateEnemies(dt);\n                    updateCoins(dt);\n                    updateShields(dt);\n                    updateHearts(dt);\n                    updatePoppedCoins(dt);\n                    updateParticles(dt);\n                    updateBlocks(dt);\n                    updateCamera();\n                }\n\n                // Lazy load assets based on camera position\n                for (const assetId of lazyLoadAssets) {\n                    // Simple heuristic: load if player is within 800px of asset's typical appearance\n                    // This can be made more sophisticated with actual object positions\n                    if (assetId === 'flag_pole' && player.x > flagX - 1000) {\n                        lazyLoadImage(assetId);\n                    } else if (assetId === 'cloud' && player.x > cameraX - 800) {\n                        lazyLoadImage(assetId);\n                    } else if (assetId === 'bush' && player.x > cameraX - 800) {\n                        lazyLoadImage(assetId);\n                    } else if (assetId === 'hill_background' && player.x > cameraX - 800) {\n                        lazyLoadImage(assetId);\n                    }\n                }\n\n                drawBackground();\n                \n                if (gameState === 'loading') {\n                    player.animation.setState('run');\n                    player.animation.update(dt);\n                    const frame = player.animation.getCurrentFrame();\n                    const img = images[frame];\n                    if (img) {\n                        ctx.drawImage(img, VIRTUAL_WIDTH - player.w/2, 800, player.w, player.h);\n                    }\n                } else if (gameState === 'menu' || gameState === 'howToPlay') {\n                    // Draw background elements for menu\n                    drawBushes();\n                    drawPlatforms();\n                } else {\n                    drawBushes();\n                    drawPlatforms();\n                    drawBlocks();\n                    drawCoins();\n                    drawEnemies();\n                    drawShields();\n                    drawHearts();\n                    drawPoppedCoins();\n                    drawFlag();\n                    drawPlayer();\n                    drawParticles();\n                }\n            } else {\n                // Edit mode render\n                drawBackground();\n                drawBushes();\n                drawPlatforms();\n                drawBlocks();\n                drawCoins();\n                drawEnemies();\n                drawShields();\n                drawFlag();\n\n                // Grid overlay\n                ctx.save();\n                ctx.translate(-cameraX, -cameraY);\n                ctx.strokeStyle = 'rgba(255,255,255,0.1)';\n                ctx.lineWidth = 1;\n                const startGridX = Math.floor(cameraX / TILE) * TILE;\n                for (let x = startGridX; x < cameraX + 720; x += TILE) {\n                    ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, 1280); ctx.stroke();\n                }\n                for (let y = 0; y < 1280; y += TILE) {\n                    ctx.beginPath(); ctx.moveTo(cameraX, y); ctx.lineTo(cameraX + 720, y); ctx.stroke();\n                }\n\n                // Selection highlight\n                if (selectedObj) {\n                    const o = selectedObj.obj;\n                    const size = selectedObj.type === 'enemy' ? 32 : TILE;\n                    ctx.strokeStyle = '#F4D03F';\n                    ctx.lineWidth = 3;\n                    ctx.setLineDash([5, 5]);\n                    ctx.strokeRect(o.x - 2, o.y - 2, size + 4, size + 4);\n                    ctx.setLineDash([]);\n                }\n                ctx.restore();\n            }\n\n            ctx.restore();\n            requestAnimationFrame(gameLoop);\n        }\n        requestAnimationFrame(gameLoop);\n    }\n    </script>\n</body>\n</html>", "game_config": "{\n  \"skyColor\": \"blue\",\n  \"difficulty\": \"normal\",\n  \"levelWidth\": 3600,\n  \"flagX\": 3400,\n  \"platforms\": [\n    {\n      \"x\": 0,\n      \"y\": 800,\n      \"w\": 600,\n      \"h\": 80\n    },\n    {\n      \"x\": 700,\n      \"y\": 800,\n      \"w\": 400,\n      \"h\": 80\n    },\n    {\n      \"x\": 500,\n      \"y\": 620,\n      \"w\": 192,\n      \"h\": 32\n    },\n    {\n      \"x\": 1200,\n      \"y\": 800,\n      \"w\": 500,\n      \"h\": 80\n    },\n    {\n      \"x\": 1350,\n      \"y\": 600,\n      \"w\": 192,\n      \"h\": 32\n    },\n    {\n      \"x\": 1600,\n      \"y\": 480,\n      \"w\": 128,\n      \"h\": 32\n    },\n    {\n      \"x\": 1800,\n      \"y\": 800,\n      \"w\": 400,\n      \"h\": 80\n    },\n    {\n      \"x\": 1900,\n      \"y\": 600,\n      \"w\": 192,\n      \"h\": 32\n    },\n    {\n      \"x\": 2300,\n      \"y\": 800,\n      \"w\": 300,\n      \"h\": 80\n    },\n    {\n      \"x\": 2350,\n      \"y\": 600,\n      \"w\": 128,\n      \"h\": 32\n    },\n    {\n      \"x\": 2500,\n      \"y\": 450,\n      \"w\": 128,\n      \"h\": 32\n    },\n    {\n      \"x\": 2700,\n      \"y\": 800,\n      \"w\": 400,\n      \"h\": 80\n    },\n    {\n      \"x\": 2750,\n      \"y\": 600,\n      \"w\": 192,\n      \"h\": 32\n    },\n    {\n      \"x\": 3200,\n      \"y\": 800,\n      \"w\": 400,\n      \"h\": 80\n    }\n  ],\n  \"blocks\": [\n    {\n      \"x\": 256,\n      \"y\": 608,\n      \"type\": \"question\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 320,\n      \"y\": 608,\n      \"type\": \"brick\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 384,\n      \"y\": 608,\n      \"type\": \"brick\",\n      \"hasCoin\": false\n    },\n    {\n      \"x\": 448,\n      \"y\": 608,\n      \"type\": \"question\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 768,\n      \"y\": 608,\n      \"type\": \"brick\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 832,\n      \"y\": 608,\n      \"type\": \"question\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 896,\n      \"y\": 608,\n      \"type\": \"brick\",\n      \"hasCoin\": false\n    },\n    {\n      \"x\": 960,\n      \"y\": 608,\n      \"type\": \"brick\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 1280,\n      \"y\": 608,\n      \"type\": \"solid\",\n      \"hasCoin\": false\n    },\n    {\n      \"x\": 1344,\n      \"y\": 608,\n      \"type\": \"question\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 1408,\n      \"y\": 608,\n      \"type\": \"solid\",\n      \"hasCoin\": false\n    },\n    {\n      \"x\": 1856,\n      \"y\": 608,\n      \"type\": \"brick\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 1920,\n      \"y\": 608,\n      \"type\": \"question\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 1984,\n      \"y\": 608,\n      \"type\": \"brick\",\n      \"hasCoin\": false\n    },\n    {\n      \"x\": 2048,\n      \"y\": 608,\n      \"type\": \"brick\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 2368,\n      \"y\": 416,\n      \"type\": \"question\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 2752,\n      \"y\": 480,\n      \"type\": \"brick\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 2816,\n      \"y\": 480,\n      \"type\": \"question\",\n      \"hasCoin\": true\n    },\n    {\n      \"x\": 2880,\n      \"y\": 480,\n      \"type\": \"brick\",\n      \"hasCoin\": true\n    }\n  ],\n  \"enemies\": [\n    {\n      \"x\": 400,\n      \"y\": 768,\n      \"startY\": 768,\n      \"type\": \"walker\",\n      \"speed\": 60,\n      \"range\": 120\n    },\n    {\n      \"x\": 800,\n      \"y\": 768,\n      \"startY\": 768,\n      \"type\": \"walker\",\n      \"speed\": 70,\n      \"range\": 100\n    },\n    {\n      \"x\": 1300,\n      \"y\": 768,\n      \"startY\": 768,\n      \"type\": \"walker\",\n      \"speed\": 60,\n      \"range\": 150\n    },\n    {\n      \"x\": 1500,\n      \"y\": 400,\n      \"startY\": 400,\n      \"type\": \"flyer\",\n      \"speed\": 50,\n      \"range\": 120\n    },\n    {\n      \"x\": 1900,\n      \"y\": 768,\n      \"startY\": 768,\n      \"type\": \"walker\",\n      \"speed\": 80,\n      \"range\": 100\n    },\n    {\n      \"x\": 2100,\n      \"y\": 768,\n      \"startY\": 768,\n      \"type\": \"walker\",\n      \"speed\": 70,\n      \"range\": 80\n    },\n    {\n      \"x\": 2400,\n      \"y\": 500,\n      \"startY\": 500,\n      \"type\": \"flyer\",\n      \"speed\": 60,\n      \"range\": 100\n    },\n    {\n      \"x\": 2800,\n      \"y\": 768,\n      \"startY\": 768,\n      \"type\": \"walker\",\n      \"speed\": 90,\n      \"range\": 120\n    },\n    {\n      \"x\": 3000,\n      \"y\": 768,\n      \"startY\": 768,\n      \"type\": \"walker\",\n      \"speed\": 80,\n      \"range\": 100\n    }\n  ],\n  \"coins\": [\n    {\n      \"x\": 150,\n      \"y\": 720\n    },\n    {\n      \"x\": 200,\n      \"y\": 720\n    },\n    {\n      \"x\": 250,\n      \"y\": 720\n    },\n    {\n      \"x\": 550,\n      \"y\": 560\n    },\n    {\n      \"x\": 610,\n      \"y\": 560\n    },\n    {\n      \"x\": 670,\n      \"y\": 560\n    },\n    {\n      \"x\": 1050,\n      \"y\": 720\n    },\n    {\n      \"x\": 1100,\n      \"y\": 720\n    },\n    {\n      \"x\": 1620,\n      \"y\": 420\n    },\n    {\n      \"x\": 1680,\n      \"y\": 420\n    },\n    {\n      \"x\": 2000,\n      \"y\": 540\n    },\n    {\n      \"x\": 2060,\n      \"y\": 540\n    },\n    {\n      \"x\": 2520,\n      \"y\": 390\n    },\n    {\n      \"x\": 2580,\n      \"y\": 390\n    },\n    {\n      \"x\": 3300,\n      \"y\": 720\n    },\n    {\n      \"x\": 3350,\n      \"y\": 720\n    }\n  ],\n  \"maxJumps\": 5,\n  \"levels\": [\n    {\n      \"levelWidth\": 3600,\n      \"platforms\": [\n        {\n          \"w\": 600,\n          \"y\": 800,\n          \"x\": 0,\n          \"h\": 80\n        },\n        {\n          \"h\": 80,\n          \"w\": 400,\n          \"x\": 700,\n          \"y\": 800\n        },\n        {\n          \"y\": 620,\n          \"x\": 500,\n          \"w\": 192,\n          \"h\": 32\n        },\n        {\n          \"h\": 80,\n          \"w\": 500,\n          \"x\": 1200,\n          \"y\": 800\n        },\n        {\n          \"h\": 32,\n          \"w\": 192,\n          \"x\": 1350,\n          \"y\": 600\n        },\n        {\n          \"h\": 32,\n          \"x\": 1600,\n          \"y\": 480,\n          \"w\": 128\n        },\n        {\n          \"h\": 80,\n          \"y\": 800,\n          \"x\": 1800,\n          \"w\": 400\n        },\n        {\n          \"w\": 192,\n          \"y\": 600,\n          \"x\": 1900,\n          \"h\": 32\n        },\n        {\n          \"w\": 300,\n          \"x\": 2300,\n          \"y\": 800,\n          \"h\": 80\n        },\n        {\n          \"w\": 128,\n          \"x\": 2350,\n          \"y\": 600,\n          \"h\": 32\n        },\n        {\n          \"h\": 32,\n          \"w\": 128,\n          \"x\": 2500,\n          \"y\": 450\n        },\n        {\n          \"w\": 400,\n          \"x\": 2700,\n          \"y\": 800,\n          \"h\": 80\n        },\n        {\n          \"h\": 32,\n          \"w\": 192,\n          \"y\": 600,\n          \"x\": 2750\n        },\n        {\n          \"h\": 80,\n          \"w\": 400,\n          \"y\": 800,\n          \"x\": 3200\n        }\n      ],\n      \"blocks\": [\n        {\n          \"hasCoin\": true,\n          \"y\": 608,\n          \"x\": 256,\n          \"type\": \"question\"\n        },\n        {\n          \"x\": 320,\n          \"y\": 608,\n          \"hasCoin\": true,\n          \"type\": \"brick\"\n        },\n        {\n          \"type\": \"brick\",\n          \"hasCoin\": false,\n          \"x\": 384,\n          \"y\": 608\n        },\n        {\n          \"type\": \"question\",\n          \"hasCoin\": true,\n          \"x\": 448,\n          \"y\": 608\n        },\n        {\n          \"type\": \"brick\",\n          \"hasCoin\": true,\n          \"x\": 768,\n          \"y\": 608\n        },\n        {\n          \"type\": \"question\",\n          \"y\": 608,\n          \"x\": 832,\n          \"hasCoin\": true\n        },\n        {\n          \"type\": \"brick\",\n          \"x\": 896,\n          \"y\": 608,\n          \"hasCoin\": false\n        },\n        {\n          \"type\": \"brick\",\n          \"hasCoin\": true,\n          \"y\": 608,\n          \"x\": 960\n        }\n      ],\n      \"enemies\": [\n        {\n          \"x\": 300,\n          \"type\": \"walker\",\n          \"startY\": 768,\n          \"range\": 120,\n          \"speed\": 60,\n          \"y\": 768\n        },\n        {\n          \"x\": 600,\n          \"type\": \"walker\",\n          \"startY\": 768,\n          \"speed\": 70,\n          \"range\": 100,\n          \"y\": 768\n        },\n        {\n          \"y\": 768,\n          \"range\": 150,\n          \"speed\": 80,\n          \"x\": 800,\n          \"startY\": 768,\n          \"type\": \"walker\"\n        },\n        {\n          \"range\": 120,\n          \"y\": 400,\n          \"speed\": 50,\n          \"type\": \"flyer\",\n          \"startY\": 400,\n          \"x\": 1500\n        },\n        {\n          \"x\": 2000,\n          \"startY\": 768,\n          \"type\": \"walker\",\n          \"range\": 150,\n          \"speed\": 110,\n          \"y\": 768\n        },\n        {\n          \"type\": \"walker\",\n          \"startY\": 580,\n          \"x\": 300,\n          \"range\": 50,\n          \"speed\": 80,\n          \"y\": 580\n        },\n        {\n          \"type\": \"flyer\",\n          \"startY\": 580,\n          \"x\": 800,\n          \"range\": 80,\n          \"y\": 580,\n          \"speed\": 70\n        },\n        {\n          \"x\": 1000,\n          \"startY\": 768,\n          \"type\": \"walker\",\n          \"range\": 100,\n          \"y\": 768,\n          \"speed\": 90\n        },\n        {\n          \"range\": 120,\n          \"speed\": 100,\n          \"y\": 600,\n          \"x\": 1200,\n          \"type\": \"flyer\",\n          \"startY\": 600\n        },\n        {\n          \"x\": 2900,\n          \"startY\": 768,\n          \"type\": \"walker\",\n          \"range\": 120,\n          \"speed\": 90,\n          \"y\": 768\n        },\n        {\n          \"x\": 3100,\n          \"startY\": 768,\n          \"type\": \"walker\",\n          \"range\": 100,\n          \"speed\": 100,\n          \"y\": 768\n        },\n        {\n          \"x\": 3300,\n          \"startY\": 768,\n          \"type\": \"flyer\",\n          \"range\": 80,\n          \"speed\": 80,\n          \"y\": 768\n        }\n      ],\n      \"flagX\": 3400,\n      \"coins\": [\n        {\n          \"y\": 720,\n          \"x\": 150\n        },\n        {\n          \"y\": 720,\n          \"x\": 200\n        },\n        {\n          \"x\": 250,\n          \"y\": 720\n        }\n      ],\n      \"skyColor\": \"blue\",\n      \"backgroundColor\": \"#AED6F1\"\n    },\n    {\n      \"blocks\": [\n        {\n          \"type\": \"question\",\n          \"hasCoin\": true,\n          \"y\": 600,\n          \"x\": 640\n        },\n        {\n          \"y\": 600,\n          \"x\": 704,\n          \"hasCoin\": true,\n          \"type\": \"brick\"\n        },\n        {\n          \"type\": \"question\",\n          \"hasCoin\": true,\n          \"x\": 768,\n          \"y\": 600\n        }\n      ],\n      \"enemies\": [\n        {\n          \"range\": 150,\n          \"speed\": 100,\n          \"startY\": 768,\n          \"type\": \"walker\",\n          \"y\": 768,\n          \"x\": 700\n        },\n        {\n          \"y\": 768,\n          \"x\": 900,\n          \"range\": 100,\n          \"speed\": 110,\n          \"startY\": 768,\n          \"type\": \"walker\"\n        },\n        {\n          \"y\": 768,\n          \"x\": 1100,\n          \"speed\": 120,\n          \"startY\": 768,\n          \"type\": \"walker\",\n          \"range\": 120\n        },\n        {\n          \"y\": 500,\n          \"x\": 1200,\n          \"speed\": 80,\n          \"startY\": 500,\n          \"type\": \"flyer\",\n          \"range\": 150\n        },\n        {\n          \"y\": 500,\n          \"x\": 1400,\n          \"range\": 100,\n          \"speed\": 90,\n          \"startY\": 500,\n          \"type\": \"flyer\"\n        },\n        {\n          \"y\": 768,\n          \"x\": 1800,\n          \"speed\": 120,\n          \"startY\": 768,\n          \"type\": \"walker\",\n          \"range\": 200\n        },\n        {\n          \"speed\": 130,\n          \"startY\": 768,\n          \"type\": \"walker\",\n          \"range\": 150,\n          \"y\": 768,\n          \"x\": 2000\n        },\n        {\n          \"range\": 100,\n          \"speed\": 100,\n          \"startY\": 400,\n          \"type\": \"flyer\",\n          \"y\": 400,\n          \"x\": 2500\n        },\n        {\n          \"y\": 400,\n          \"x\": 2700,\n          \"speed\": 110,\n          \"startY\": 400,\n          \"type\": \"flyer\",\n          \"range\": 120\n        },\n        {\n          \"y\": 768,\n          \"x\": 3000,\n          \"speed\": 140,\n          \"startY\": 768,\n          \"type\": \"walker\",\n          \"range\": 150\n        },\n        {\n          \"y\": 768,\n          \"x\": 3200,\n          \"range\": 100,\n          \"speed\": 150,\n          \"startY\": 768,\n          \"type\": \"walker\"\n        },\n        {\n          \"range\": 80,\n          \"speed\": 100,\n          \"startY\": 580,\n          \"type\": \"walker\",\n          \"y\": 580,\n          \"x\": 650\n        },\n        {\n          \"range\": 100,\n          \"speed\": 90,\n          \"startY\": 580,\n          \"type\": \"flyer\",\n          \"y\": 580,\n          \"x\": 1300\n        },\n        {\n          \"y\": 580,\n          \"x\": 1900,\n          \"speed\": 110,\n          \"startY\": 580,\n          \"type\": \"walker\",\n          \"range\": 120\n        },\n        {\n          \"y\": 580,\n          \"x\": 2600,\n          \"range\": 150,\n          \"speed\": 100,\n          \"startY\": 580,\n          \"type\": \"flyer\"\n        },\n        {\n          \"y\": 768,\n          \"x\": 3100,\n          \"range\": 100,\n          \"speed\": 120,\n          \"startY\": 768,\n          \"type\": \"walker\"\n        },\n        {\n          \"y\": 768,\n          \"x\": 3300,\n          \"speed\": 100,\n          \"startY\": 768,\n          \"type\": \"flyer\",\n          \"range\": 80\n        }\n      ],\n      \"coins\": [\n        {\n          \"x\": 350,\n          \"y\": 550\n        },\n        {\n          \"y\": 500,\n          \"x\": 850\n        },\n        {\n          \"y\": 450,\n          \"x\": 1350\n        }\n      ],\n      \"flagX\": 3400,\n      \"skyColor\": \"sunset\",\n      \"levelWidth\": 3600,\n      \"platforms\": [\n        {\n          \"w\": 500,\n          \"x\": 0,\n          \"y\": 800,\n          \"h\": 80\n        },\n        {\n          \"w\": 500,\n          \"x\": 600,\n          \"y\": 800,\n          \"h\": 80\n        },\n        {\n          \"h\": 80,\n          \"w\": 400,\n          \"y\": 800,\n          \"x\": 1200\n        },\n        {\n          \"y\": 800,\n          \"x\": 1700,\n          \"w\": 600,\n          \"h\": 80\n        },\n        {\n          \"h\": 80,\n          \"w\": 400,\n          \"x\": 2400,\n          \"y\": 800\n        },\n        {\n          \"x\": 2900,\n          \"y\": 800,\n          \"w\": 700,\n          \"h\": 80\n        },\n        {\n          \"x\": 300,\n          \"y\": 600,\n          \"w\": 128,\n          \"h\": 32\n        },\n        {\n          \"x\": 800,\n          \"y\": 550,\n          \"w\": 128,\n          \"h\": 32\n        },\n        {\n          \"w\": 128,\n          \"x\": 1300,\n          \"y\": 500,\n          \"h\": 32\n        },\n        {\n          \"w\": 128,\n          \"x\": 1800,\n          \"y\": 550,\n          \"h\": 32\n        },\n        {\n          \"x\": 2300,\n          \"y\": 600,\n          \"w\": 128,\n          \"h\": 32\n        },\n        {\n          \"x\": 2800,\n          \"y\": 500,\n          \"w\": 128,\n          \"h\": 32\n        }\n      ],\n      \"backgroundColor\": \"#FFD700\"\n    },\n    {\n      \"blocks\": [\n        {\n          \"x\": 600,\n          \"hasCoin\": true,\n          \"y\": 550,\n          \"type\": \"brick\"\n        },\n        {\n          \"type\": \"question\",\n          \"y\": 500,\n          \"hasCoin\": true,\n          \"x\": 1000\n        },\n        {\n          \"type\": \"brick\",\n          \"y\": 450,\n          \"hasCoin\": true,\n          \"x\": 1400\n        },\n        {\n          \"x\": 2200,\n          \"hasCoin\": true,\n          \"y\": 550,\n          \"type\": \"question\"\n        },\n        {\n          \"x\": 700,\n          \"hasCoin\": false,\n          \"y\": 400,\n          \"type\": \"solid\"\n        },\n        {\n          \"y\": 350,\n          \"type\": \"brick\",\n          \"x\": 1100,\n          \"hasCoin\": true\n        },\n        {\n          \"hasCoin\": false,\n          \"x\": 1500,\n          \"type\": \"solid\",\n          \"y\": 300\n        },\n        {\n          \"x\": 1900,\n          \"hasCoin\": true,\n          \"y\": 450,\n          \"type\": \"question\"\n        },\n        {\n          \"type\": \"solid\",\n          \"y\": 400,\n          \"hasCoin\": false,\n          \"x\": 2300\n        },\n        {\n          \"type\": \"brick\",\n          \"y\": 350,\n          \"hasCoin\": true,\n          \"x\": 2700\n        },\n        {\n          \"type\": \"solid\",\n          \"y\": 300,\n          \"hasCoin\": false,\n          \"x\": 3100\n        }\n      ],\n      \"enemies\": [\n        {\n          \"y\": 718,\n          \"type\": \"walker\",\n          \"speed\": 160,\n          \"range\": 120,\n          \"startY\": 718,\n          \"x\": 600\n        },\n        {\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"speed\": 180,\n          \"range\": 150,\n          \"startY\": 768,\n          \"x\": 800\n        },\n        {\n          \"y\": 668,\n          \"type\": \"walker\",\n          \"speed\": 200,\n          \"range\": 120,\n          \"startY\": 668,\n          \"x\": 1000\n        },\n        {\n          \"y\": 618,\n          \"type\": \"walker\",\n          \"speed\": 220,\n          \"range\": 100,\n          \"startY\": 618,\n          \"x\": 1200\n        },\n        {\n          \"speed\": 150,\n          \"range\": 200,\n          \"startY\": 300,\n          \"x\": 1400,\n          \"y\": 300,\n          \"type\": \"flyer\"\n        },\n        {\n          \"y\": 700,\n          \"type\": \"walker\",\n          \"x\": 1600,\n          \"speed\": 240,\n          \"range\": 150,\n          \"startY\": 700\n        },\n        {\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"speed\": 260,\n          \"range\": 100,\n          \"startY\": 768,\n          \"x\": 1800\n        },\n        {\n          \"x\": 2000,\n          \"speed\": 180,\n          \"range\": 150,\n          \"startY\": 400,\n          \"y\": 400,\n          \"type\": \"flyer\"\n        },\n        {\n          \"speed\": 280,\n          \"range\": 120,\n          \"startY\": 750,\n          \"x\": 2200,\n          \"y\": 750,\n          \"type\": \"walker\"\n        },\n        {\n          \"x\": 2400,\n          \"speed\": 300,\n          \"range\": 100,\n          \"startY\": 700,\n          \"y\": 700,\n          \"type\": \"walker\"\n        },\n        {\n          \"y\": 650,\n          \"type\": \"walker\",\n          \"x\": 2600,\n          \"speed\": 320,\n          \"range\": 150,\n          \"startY\": 650\n        },\n        {\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"speed\": 340,\n          \"range\": 100,\n          \"startY\": 768,\n          \"x\": 2800\n        },\n        {\n          \"y\": 350,\n          \"type\": \"flyer\",\n          \"x\": 3000,\n          \"speed\": 200,\n          \"range\": 120,\n          \"startY\": 350\n        },\n        {\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"x\": 3200,\n          \"speed\": 360,\n          \"range\": 150,\n          \"startY\": 768\n        },\n        {\n          \"x\": 3400,\n          \"speed\": 380,\n          \"range\": 100,\n          \"startY\": 768,\n          \"y\": 768,\n          \"type\": \"walker\"\n        },\n        {\n          \"speed\": 400,\n          \"range\": 120,\n          \"startY\": 768,\n          \"x\": 3600,\n          \"y\": 768,\n          \"type\": \"walker\"\n        },\n        {\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"speed\": 300,\n          \"range\": 100,\n          \"startY\": 768,\n          \"x\": 3500\n        },\n        {\n          \"x\": 3650,\n          \"speed\": 250,\n          \"range\": 120,\n          \"startY\": 768,\n          \"y\": 768,\n          \"type\": \"flyer\"\n        }\n      ],\n      \"flagX\": 3800,\n      \"coins\": [\n        {\n          \"y\": 400,\n          \"x\": 1500\n        },\n        {\n          \"x\": 2300,\n          \"y\": 500\n        },\n        {\n          \"y\": 750,\n          \"x\": 3000\n        }\n      ],\n      \"skyColor\": \"night\",\n      \"levelWidth\": 4000,\n      \"platforms\": [\n        {\n          \"h\": 80,\n          \"x\": 0,\n          \"y\": 800,\n          \"w\": 400\n        },\n        {\n          \"h\": 80,\n          \"x\": 500,\n          \"y\": 750,\n          \"w\": 300\n        },\n        {\n          \"w\": 300,\n          \"y\": 700,\n          \"x\": 900,\n          \"h\": 80\n        },\n        {\n          \"x\": 1300,\n          \"h\": 80,\n          \"w\": 300,\n          \"y\": 650\n        },\n        {\n          \"y\": 700,\n          \"w\": 300,\n          \"h\": 80,\n          \"x\": 1700\n        },\n        {\n          \"h\": 80,\n          \"x\": 2100,\n          \"y\": 750,\n          \"w\": 300\n        },\n        {\n          \"x\": 2500,\n          \"h\": 80,\n          \"y\": 800,\n          \"w\": 1200\n        },\n        {\n          \"h\": 32,\n          \"x\": 600,\n          \"y\": 500,\n          \"w\": 150\n        },\n        {\n          \"y\": 450,\n          \"w\": 150,\n          \"h\": 32,\n          \"x\": 1000\n        },\n        {\n          \"w\": 150,\n          \"y\": 400,\n          \"h\": 32,\n          \"x\": 1400\n        },\n        {\n          \"h\": 32,\n          \"x\": 1800,\n          \"w\": 150,\n          \"y\": 500\n        },\n        {\n          \"h\": 32,\n          \"x\": 2200,\n          \"w\": 150,\n          \"y\": 450\n        },\n        {\n          \"h\": 32,\n          \"x\": 2600,\n          \"w\": 150,\n          \"y\": 400\n        },\n        {\n          \"w\": 150,\n          \"y\": 350,\n          \"h\": 32,\n          \"x\": 3000\n        }\n      ],\n      \"backgroundColor\": \"#8B008B\"\n    },\n    {\n      \"skyColor\": \"sunset\",\n      \"coins\": [\n        {\n          \"y\": 750,\n          \"x\": 2100\n        },\n        {\n          \"x\": 2400,\n          \"y\": 750\n        },\n        {\n          \"x\": 2700,\n          \"y\": 750\n        }\n      ],\n      \"flagX\": 4300,\n      \"enemies\": [\n        {\n          \"x\": 500,\n          \"y\": 200,\n          \"type\": \"flyer\",\n          \"speed\": 180,\n          \"startY\": 200,\n          \"range\": 150\n        },\n        {\n          \"speed\": 200,\n          \"startY\": 150,\n          \"range\": 100,\n          \"x\": 1100,\n          \"y\": 150,\n          \"type\": \"flyer\"\n        },\n        {\n          \"range\": 150,\n          \"speed\": 220,\n          \"startY\": 768,\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"x\": 2000\n        },\n        {\n          \"x\": 2300,\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"speed\": 240,\n          \"startY\": 768,\n          \"range\": 150\n        },\n        {\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"x\": 2600,\n          \"range\": 150,\n          \"speed\": 260,\n          \"startY\": 768\n        },\n        {\n          \"x\": 2900,\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"speed\": 280,\n          \"startY\": 768,\n          \"range\": 150\n        },\n        {\n          \"speed\": 180,\n          \"startY\": 768,\n          \"range\": 100,\n          \"x\": 400,\n          \"y\": 768,\n          \"type\": \"walker\"\n        },\n        {\n          \"range\": 120,\n          \"speed\": 200,\n          \"startY\": 768,\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"x\": 800\n        },\n        {\n          \"x\": 1200,\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"speed\": 220,\n          \"startY\": 768,\n          \"range\": 150\n        },\n        {\n          \"x\": 1500,\n          \"y\": 300,\n          \"type\": \"flyer\",\n          \"speed\": 190,\n          \"startY\": 300,\n          \"range\": 100\n        },\n        {\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"x\": 3200,\n          \"range\": 120,\n          \"speed\": 210,\n          \"startY\": 768\n        },\n        {\n          \"range\": 100,\n          \"speed\": 230,\n          \"startY\": 768,\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"x\": 3500\n        },\n        {\n          \"range\": 150,\n          \"speed\": 250,\n          \"startY\": 768,\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"x\": 3800\n        },\n        {\n          \"x\": 1800,\n          \"y\": 250,\n          \"type\": \"flyer\",\n          \"speed\": 200,\n          \"startY\": 250,\n          \"range\": 120\n        },\n        {\n          \"y\": 200,\n          \"type\": \"flyer\",\n          \"x\": 2100,\n          \"range\": 100,\n          \"speed\": 220,\n          \"startY\": 200\n        },\n        {\n          \"y\": 768,\n          \"type\": \"walker\",\n          \"x\": 4000,\n          \"range\": 100,\n          \"speed\": 280,\n          \"startY\": 768\n        },\n        {\n          \"x\": 4150,\n          \"y\": 768,\n          \"type\": \"flyer\",\n          \"speed\": 250,\n          \"startY\": 768,\n          \"range\": 120\n        }\n      ],\n      \"blocks\": [\n        {\n          \"x\": 500,\n          \"hasCoin\": true,\n          \"type\": \"question\",\n          \"y\": 400\n        },\n        {\n          \"y\": 300,\n          \"x\": 800,\n          \"hasCoin\": true,\n          \"type\": \"brick\"\n        },\n        {\n          \"y\": 200,\n          \"x\": 1100,\n          \"hasCoin\": true,\n          \"type\": \"question\"\n        },\n        {\n          \"x\": 600,\n          \"hasCoin\": false,\n          \"type\": \"solid\",\n          \"y\": 250\n        },\n        {\n          \"x\": 900,\n          \"hasCoin\": true,\n          \"type\": \"brick\",\n          \"y\": 150\n        },\n        {\n          \"x\": 1200,\n          \"hasCoin\": false,\n          \"type\": \"solid\",\n          \"y\": 100\n        },\n        {\n          \"y\": 250,\n          \"x\": 1500,\n          \"hasCoin\": true,\n          \"type\": \"question\"\n        },\n        {\n          \"y\": 200,\n          \"x\": 1800,\n          \"hasCoin\": false,\n          \"type\": \"solid\"\n        },\n        {\n          \"x\": 2100,\n          \"hasCoin\": true,\n          \"type\": \"brick\",\n          \"y\": 150\n        },\n        {\n          \"x\": 2400,\n          \"hasCoin\": false,\n          \"type\": \"solid\",\n          \"y\": 100\n        }\n      ],\n      \"platforms\": [\n        {\n          \"h\": 80,\n          \"w\": 300,\n          \"x\": 0,\n          \"y\": 800\n        },\n        {\n          \"h\": 32,\n          \"w\": 200,\n          \"x\": 400,\n          \"y\": 700\n        },\n        {\n          \"w\": 200,\n          \"h\": 32,\n          \"y\": 600,\n          \"x\": 700\n        },\n        {\n          \"y\": 500,\n          \"x\": 1000,\n          \"w\": 200,\n          \"h\": 32\n        },\n        {\n          \"h\": 32,\n          \"w\": 200,\n          \"x\": 1300,\n          \"y\": 600\n        },\n        {\n          \"h\": 32,\n          \"w\": 200,\n          \"x\": 1600,\n          \"y\": 700\n        },\n        {\n          \"x\": 1900,\n          \"y\": 800,\n          \"h\": 80,\n          \"w\": 2500\n        },\n        {\n          \"x\": 500,\n          \"y\": 400,\n          \"h\": 32,\n          \"w\": 150\n        },\n        {\n          \"x\": 800,\n          \"y\": 300,\n          \"h\": 32,\n          \"w\": 150\n        },\n        {\n          \"w\": 150,\n          \"h\": 32,\n          \"y\": 200,\n          \"x\": 1100\n        },\n        {\n          \"w\": 150,\n          \"h\": 32,\n          \"y\": 100,\n          \"x\": 1400\n        },\n        {\n          \"h\": 32,\n          \"w\": 150,\n          \"x\": 1700,\n          \"y\": 200\n        },\n        {\n          \"h\": 32,\n          \"w\": 150,\n          \"x\": 2000,\n          \"y\": 300\n        },\n        {\n          \"h\": 32,\n          \"w\": 150,\n          \"x\": 2300,\n          \"y\": 400\n        },\n        {\n          \"w\": 150,\n          \"h\": 32,\n          \"y\": 500,\n          \"x\": 2600\n        },\n        {\n          \"x\": 2900,\n          \"y\": 600,\n          \"h\": 32,\n          \"w\": 150\n        },\n        {\n          \"y\": 700,\n          \"x\": 3200,\n          \"w\": 150,\n          \"h\": 32\n        }\n      ],\n      \"levelWidth\": 4500,\n      \"backgroundColor\": \"#006400\"\n    },\n    {\n      \"platforms\": [\n        {\n          \"x\": 0,\n          \"y\": 800,\n          \"h\": 80,\n          \"w\": 200\n        },\n        {\n          \"x\": 300,\n          \"y\": 700,\n          \"h\": 32,\n          \"w\": 150\n        },\n        {\n          \"w\": 150,\n          \"x\": 550,\n          \"y\": 600,\n          \"h\": 32\n        },\n        {\n          \"w\": 150,\n          \"x\": 800,\n          \"y\": 500,\n          \"h\": 32\n        },\n        {\n          \"w\": 150,\n          \"x\": 1050,\n          \"y\": 400,\n          \"h\": 32\n        },\n        {\n          \"x\": 1300,\n          \"y\": 300,\n          \"h\": 32,\n          \"w\": 150\n        },\n        {\n          \"w\": 150,\n          \"x\": 1550,\n          \"y\": 400,\n          \"h\": 32\n        },\n        {\n          \"w\": 150,\n          \"x\": 1800,\n          \"y\": 500,\n          \"h\": 32\n        },\n        {\n          \"w\": 150,\n          \"x\": 2050,\n          \"y\": 600,\n          \"h\": 32\n        },\n        {\n          \"y\": 700,\n          \"h\": 32,\n          \"x\": 2300,\n          \"w\": 150\n        },\n        {\n          \"y\": 800,\n          \"h\": 80,\n          \"x\": 2550,\n          \"w\": 2400\n        },\n        {\n          \"x\": 400,\n          \"y\": 500,\n          \"h\": 32,\n          \"w\": 100\n        },\n        {\n          \"x\": 900,\n          \"y\": 300,\n          \"h\": 32,\n          \"w\": 100\n        },\n        {\n          \"x\": 1400,\n          \"y\": 200,\n          \"h\": 32,\n          \"w\": 100\n        },\n        {\n          \"y\": 100,\n          \"h\": 32,\n          \"x\": 1900,\n          \"w\": 100\n        },\n        {\n          \"w\": 100,\n          \"x\": 2400,\n          \"y\": 50,\n          \"h\": 32\n        },\n        {\n          \"w\": 100,\n          \"y\": 100,\n          \"h\": 32,\n          \"x\": 2900\n        },\n        {\n          \"y\": 200,\n          \"h\": 32,\n          \"x\": 3400,\n          \"w\": 100\n        },\n        {\n          \"w\": 100,\n          \"x\": 3900,\n          \"y\": 300,\n          \"h\": 32\n        },\n        {\n          \"y\": 400,\n          \"h\": 32,\n          \"x\": 4400,\n          \"w\": 100\n        }\n      ],\n      \"levelWidth\": 5000,\n      \"flagX\": 4800,\n      \"coins\": [\n        {\n          \"y\": 750,\n          \"x\": 3500\n        },\n        {\n          \"x\": 4000,\n          \"y\": 750\n        },\n        {\n          \"x\": 4500,\n          \"y\": 750\n        }\n      ],\n      \"skyColor\": \"night\",\n      \"blocks\": [\n        {\n          \"x\": 400,\n          \"y\": 500,\n          \"hasCoin\": true,\n          \"type\": \"brick\"\n        },\n        {\n          \"y\": 300,\n          \"hasCoin\": true,\n          \"type\": \"question\",\n          \"x\": 900\n        },\n        {\n          \"x\": 1400,\n          \"y\": 200,\n          \"hasCoin\": true,\n          \"type\": \"brick\"\n        },\n        {\n          \"y\": 350,\n          \"hasCoin\": false,\n          \"type\": \"solid\",\n          \"x\": 500\n        },\n        {\n          \"y\": 150,\n          \"hasCoin\": true,\n          \"type\": \"question\",\n          \"x\": 1000\n        },\n        {\n          \"x\": 1500,\n          \"y\": 50,\n          \"hasCoin\": false,\n          \"type\": \"solid\"\n        },\n        {\n          \"y\": 100,\n          \"hasCoin\": true,\n          \"type\": \"brick\",\n          \"x\": 2000\n        },\n        {\n          \"x\": 2500,\n          \"y\": 150,\n          \"hasCoin\": false,\n          \"type\": \"solid\"\n        },\n        {\n          \"y\": 200,\n          \"hasCoin\": true,\n          \"type\": \"question\",\n          \"x\": 3000\n        },\n        {\n          \"x\": 3500,\n          \"y\": 250,\n          \"hasCoin\": false,\n          \"type\": \"solid\"\n        },\n        {\n          \"y\": 300,\n          \"hasCoin\": true,\n          \"type\": \"brick\",\n          \"x\": 4000\n        }\n      ],\n      \"enemies\": [\n        {\n          \"startY\": 400,\n          \"speed\": 200,\n          \"range\": 100,\n          \"type\": \"flyer\",\n          \"y\": 400,\n          \"x\": 400\n        },\n        {\n          \"y\": 250,\n          \"x\": 900,\n          \"type\": \"flyer\",\n          \"speed\": 220,\n          \"range\": 100,\n          \"startY\": 250\n        },\n        {\n          \"y\": 150,\n          \"x\": 1400,\n          \"type\": \"flyer\",\n          \"speed\": 240,\n          \"range\": 100,\n          \"startY\": 150\n        },\n        {\n          \"speed\": 280,\n          \"range\": 100,\n          \"startY\": 768,\n          \"y\": 768,\n          \"x\": 2700,\n          \"type\": \"walker\"\n        },\n        {\n          \"startY\": 768,\n          \"speed\": 300,\n          \"range\": 100,\n          \"type\": \"walker\",\n          \"y\": 768,\n          \"x\": 3000\n        },\n        {\n          \"speed\": 320,\n          \"range\": 100,\n          \"startY\": 768,\n          \"y\": 768,\n          \"x\": 3300,\n          \"type\": \"walker\"\n        },\n        {\n          \"y\": 768,\n          \"x\": 3600,\n          \"type\": \"walker\",\n          \"speed\": 340,\n          \"range\": 100,\n          \"startY\": 768\n        },\n        {\n          \"type\": \"walker\",\n          \"y\": 768,\n          \"x\": 3900,\n          \"startY\": 768,\n          \"speed\": 360,\n          \"range\": 100\n        },\n        {\n          \"type\": \"walker\",\n          \"y\": 768,\n          \"x\": 4200,\n          \"startY\": 768,\n          \"speed\": 380,\n          \"range\": 100\n        },\n        {\n          \"type\": \"walker\",\n          \"y\": 768,\n          \"x\": 2800,\n          \"startY\": 768,\n          \"speed\": 260,\n          \"range\": 120\n        },\n        {\n          \"y\": 768,\n          \"x\": 3100,\n          \"type\": \"walker\",\n          \"speed\": 280,\n          \"range\": 150,\n          \"startY\": 768\n        },\n        {\n          \"speed\": 300,\n          \"range\": 100,\n          \"startY\": 768,\n          \"y\": 768,\n          \"x\": 3400,\n          \"type\": \"walker\"\n        },\n        {\n          \"speed\": 240,\n          \"range\": 120,\n          \"startY\": 350,\n          \"y\": 350,\n          \"x\": 1000,\n          \"type\": \"flyer\"\n        },\n        {\n          \"speed\": 260,\n          \"range\": 100,\n          \"startY\": 200,\n          \"y\": 200,\n          \"x\": 1600,\n          \"type\": \"flyer\"\n        },\n        {\n          \"startY\": 100,\n          \"speed\": 280,\n          \"range\": 150,\n          \"type\": \"flyer\",\n          \"y\": 100,\n          \"x\": 2200\n        },\n        {\n          \"startY\": 50,\n          \"speed\": 300,\n          \"range\": 120,\n          \"type\": \"flyer\",\n          \"y\": 50,\n          \"x\": 2800\n        },\n        {\n          \"speed\": 350,\n          \"range\": 100,\n          \"startY\": 768,\n          \"y\": 768,\n          \"x\": 4500,\n          \"type\": \"walker\"\n        },\n        {\n          \"startY\": 768,\n          \"speed\": 300,\n          \"range\": 120,\n          \"type\": \"flyer\",\n          \"y\": 768,\n          \"x\": 4650\n        }\n      ]\n    }\n  ],\n  \"backgroundColor\": \"#AED6F1\"\n}", "game_design_document": "## Core Game Loop and Mechanics\n\n**PIXEL DASH KINGDOM** is a classic side-scrolling platformer that captures the essence of 16-bit era gaming. The player controls a small hero who must navigate through a colorful kingdom level, collecting coins, defeating enemies, and reaching the goal.\n\n### Moment-to-Moment Gameplay\n- **Movement**: The hero runs left and right with responsive controls, building momentum as they move\n- **Jumping**: Precise jump mechanics allow for platforming challenges - the hero can jump on platforms, over gaps, and onto enemies\n- **Block Breaking**: Jumping into brick blocks from below breaks them, revealing coins or power-ups\n- **Enemy Stomping**: Landing on enemies from above defeats them with a satisfying bounce\n- **Coin Collection**: Coins pop out of blocks and can be collected for score\n- **Power-ups**: Special blocks contain power-ups that enhance the hero's abilities\n\n### Progression and Engagement\n- Players progress by moving from left to right through the level\n- The challenge comes from timing jumps, avoiding enemies, and navigating platforms\n- Collecting all coins and finding hidden blocks provides optional objectives\n- The satisfying feedback of breaking blocks and stomping enemies drives engagement\n\n### Win/Lose Conditions\n- **Win**: Reach the checkered flag pole at the end of the level\n- **Lose**: Fall into a pit or get hit by an enemy (respawn at the start of the level)\n- **Score**: Based on coins collected and enemies defeated\n\n### What Makes It Compelling\n- Tight, responsive controls that feel good to master\n- Satisfying audio-visual feedback for every action\n- Classic gameplay that's immediately familiar yet always fun\n- Simple to learn, challenging to master perfectly\n\n## Starting Scope\n\nFor the first iteration, we'll implement a **single, complete level** that demonstrates all core mechanics in a polished, playable experience.\n\n### Must Have (First Iteration)\n- One complete level with varied platforming challenges\n- Hero character with run and jump mechanics\n- 2-3 enemy types with simple AI (walking patrol patterns)\n- Brick blocks that break when hit from below\n- Coin blocks that release coins when hit\n- Collectible coins with score tracking\n- Enemy stomping mechanic with bounce feedback\n- Checkered flag pole goal\n- Death and respawn system (fall in pits or hit by enemies)\n- Mobile touch controls (left/right buttons, jump button)\n- Desktop keyboard controls (arrow keys or WASD + Space)\n- Background music and sound effects for all actions\n- Parallax scrolling background (sky, distant hills)\n\n### Future Iterations (Not in First Version)\n- Power-ups (mushroom for size increase, fire flower for projectiles)\n- Multiple levels with increasing difficulty\n- Moving platforms\n- More enemy variety\n- Hidden areas and secrets\n- Time-based challenges\n- Lives system\n- High score persistence\n\n## Game Perspective\n\n**Side-scrolling 2D platformer** - Classic side view with the camera following the player horizontally. The camera should:\n- Keep the player roughly centered horizontally (with some lead room in the direction they're moving)\n- Show enough space ahead for players to see upcoming obstacles\n- Lock vertically to show the full level height (or follow player if level is taller than viewport)\n\n## Game World and Level Design\n\n### Level Layout\nThe level is a single horizontal stage approximately **3000-4000 pixels wide** and **1000 pixels tall**, designed to fit within the 720\u00d71280 mobile viewport with scrolling.\n\n### Level Structure (Left to Right)\n\n**Starting Area (0-500px)**\n- Flat ground for player to get oriented\n- A few easy coin blocks floating above\n- One slow-moving enemy (Goomba-style walker) to practice stomping\n- Tutorial-like safe space\n\n**First Challenge Section (500-1200px)**\n- Series of platforms at varying heights\n- Gaps that require jumping\n- Brick blocks arranged in patterns (some breakable, some solid)\n- 2-3 enemies patrolling platforms\n- Coin blocks scattered throughout\n\n**Mid-Level Section (1200-2200px)**\n- More complex platforming with higher platforms\n- Longer gaps requiring running jumps\n- Clusters of brick blocks to break through\n- Flying enemy type (moves in sine wave pattern)\n- A pit that requires careful jumping\n\n**Challenge Gauntlet (2200-3200px)**\n- Tighter platform spacing\n- Multiple enemies in sequence\n- Brick block \"ceiling\" sections to navigate under\n- Coin trail leading through optimal path\n\n**Goal Area (3200-3500px)**\n- Final platform leading to checkered flag pole\n- Clear visual indication of the goal\n- Victory fanfare when flag is touched\n\n### Environmental Elements\n\n**Ground/Platforms**\n- Solid green grass-topped platforms with brown dirt sides\n- Floating platforms suspended in air\n- All platforms use chunky 16-bit pixel art style\n\n**Blocks**\n- **Brick Blocks**: Brown/orange bricks that break when hit from below, may contain coins\n- **Question Blocks**: Yellow blocks with \"?\" symbol, contain coins or power-ups\n- **Solid Blocks**: Gray stone blocks that cannot be broken\n\n**Background**\n- Bright blue sky gradient (lighter at top, deeper blue at bottom)\n- White puffy pixel clouds (parallax layer, moves slower than foreground)\n- Green rolling hills in the distance (parallax layer, moves slower than clouds)\n- Occasional pixel art bushes and decorative elements on ground level\n\n**Pits**\n- Gaps in the ground that lead to instant death\n- Clearly visible as missing ground sections\n\n### Physics and Feel\n- **Gravity**: Strong enough to feel weighty but allow for precise jumps\n- **Jump Height**: Approximately 3-4 character heights\n- **Run Speed**: Fast enough to feel responsive, slow enough to control precisely\n- **Jump Arc**: Slightly floaty at the peak for that classic platformer feel\n- **Momentum**: Slight acceleration when starting to run, slight deceleration when stopping\n\n## Edit Mode Design\n\nFor the first iteration, keep edit mode **simple and focused on level layout**. Players can modify the level structure without breaking core gameplay.\n\n### Editable Properties\n\n**Level Layout**\n- **Platform Placement**: Add, remove, or move platforms\n  - Snap to grid for clean placement\n  - Minimum platform width: 2 blocks (128px)\n  - Platforms cannot overlap\n  \n- **Block Placement**: Add, remove, or move blocks\n  - Choose block type: Brick (breakable), Question (contains coin), Solid (unbreakable)\n  - Snap to grid alignment\n  - Blocks can be placed in mid-air or on platforms\n\n- **Enemy Placement**: Add, remove, or move enemy spawn points\n  - Choose enemy type from available types\n  - Set patrol range (left/right boundaries)\n  - Enemies must be placed on solid ground or platforms\n\n- **Coin Placement**: Add or remove floating coins in the level\n  - Snap to grid\n  - Can be placed anywhere in the air\n\n**Level Properties**\n- **Level Width**: Adjust total level length (minimum 2000px, maximum 5000px)\n- **Background Color**: Choose sky color from preset palette (blue, purple, orange sunset)\n- **Music Track**: Select from 2-3 background music options\n\n### Edit Mode Interface\n\n**Visual Indicators**\n- Grid overlay when in edit mode\n- Selected object highlights with outline\n- Drag handles for moving objects\n- Snap-to-grid visual feedback\n\n**Controls**\n- **Mobile**: Tap to select, drag to move, toolbar at bottom for add/delete/change type\n- **Desktop**: Click to select, drag to move, right-click for context menu, keyboard shortcuts\n\n**Constraints**\n- Cannot remove the starting ground platform\n- Cannot remove the goal flag pole\n- Must maintain at least one valid path from start to goal\n- Maximum 50 enemies per level\n- Maximum 100 blocks per level\n- Flag pole must be at the end of the level (rightmost position)\n\n### What's NOT Editable (First Iteration)\n- Hero character appearance or abilities\n- Physics constants (gravity, jump height, run speed)\n- Enemy AI behavior\n- Block contents (all question blocks give coins)\n- Power-ups (not implemented yet)\n\n## Control Design\n\n### Mobile Controls (Touch)\n\n**Layout**\n- **Left Side**: D-pad or left/right arrow buttons in bottom-left corner (96\u00d796px each)\n- **Right Side**: Jump button in bottom-right corner (128\u00d7128px, larger for importance)\n- **Top**: Pause button in top-right corner (64\u00d764px)\n\n**D-Pad Configuration**\n- Left arrow: Move left\n- Right arrow: Move right\n- Buttons positioned at ~100px from bottom, ~50px from sides\n- Visual feedback: buttons highlight when pressed\n- Support simultaneous press (run + jump)\n\n**Touch Handling**\n- Track touch IDs to prevent interference\n- Allow holding for continuous movement\n- Jump on button press (not release) for responsiveness\n\n### Desktop Controls (Keyboard + Mouse)\n\n**Keyboard**\n- **Arrow Keys OR WASD**: Movement\n  - Left/A: Move left\n  - Right/D: Move right\n  - Up/W or Space: Jump\n- **Escape or P**: Pause\n- **Enter**: Restart level (when dead)\n\n**Mouse**\n- Used only in edit mode for placing/moving objects\n- Not used during gameplay\n\n**Input Handling**\n- Prevent default browser behavior for arrow keys and space\n- Clear input state on window blur\n- Convert keycodes to lowercase before storing state\n- Support simultaneous key presses (run + jump)\n\n### Control Feel\n- **Responsive**: Immediate feedback to input\n- **Run Acceleration**: Slight ramp-up to max speed (0.1s)\n- **Jump Buffer**: 0.1s window to register jump input before landing\n- **Coyote Time**: 0.1s grace period to jump after leaving platform edge\n- **Variable Jump Height**: Hold jump longer = jump higher (up to max height)\n\n## Art Style and Visual Theme\n\n### Overall Style\n**16-bit pixel art** inspired by classic SNES-era platformers. Bright, colorful, and cheerful with clean pixel work and no anti-aliasing. Every asset should feel cohesive with chunky, readable pixels.\n\n### Color Palette\n- **Sky**: Bright blue (#5DADE2) gradient to lighter blue (#AED6F1) at top\n- **Grass/Ground**: Vibrant green (#27AE60) with brown dirt (#8B4513)\n- **Bricks**: Orange-brown (#D68910) with darker outline\n- **Coins**: Golden yellow (#F4D03F) with shine highlights\n- **Hero**: Blue overalls (#2E86C1), red cap (#E74C3C), skin tone (#F0B27A)\n- **Enemies**: Varied but saturated colors (brown, red, green)\n\n### Asset Specifications\n\n**Character Scale**\n- Hero: 32\u00d732 pixels (small, classic platformer size)\n- Enemies: 32\u00d732 pixels (Goomba-style) to 48\u00d748 pixels (larger enemies)\n- Blocks: 64\u00d764 pixels each (chunky, easy to see)\n- Coins: 24\u00d724 pixels\n\n**Viewing Angle**\n- Pure side view (profile) for all characters and objects\n- No perspective or isometric angles\n- Flat 2D presentation\n\n**Animation Considerations**\n- Since we're generating static images, we'll need multiple poses for the hero:\n  - Idle/standing\n  - Running (we'll alternate between 2-3 frames in code)\n  - Jumping\n  - Falling\n- Enemies will have 1-2 poses that we'll animate in code\n- Blocks and environmental elements are static\n\n### Visual Consistency\n- All assets should use the same pixel density (no mixing high-res and low-res)\n- Consistent black outlines (2-3 pixels thick) around all sprites\n- Transparent backgrounds for all character and object sprites\n- Lighting should be flat and bright (no complex shadows)\n- Maintain the cheerful, accessible aesthetic throughout\n\n### Rendering Approach\n- **Generated Assets**: Characters, enemies, coins, flag pole, decorative elements\n- **Code-Rendered**: Platforms (simple rectangles with CSS), UI elements, particle effects (coin sparkles, break effects)", "persistent_storage_config": {"leaderboard": {"sort_ascending": false}}, "game_metadata": {"game_id": "01KTZF3VWYP8XR5BJYTNPVSMRK", "title": "PIXEL DASH KINGDOM", "description": "**Description:** Run, jump, and stomp through a vibrant pixel kingdom! Break blocks for coins, defeat enemies, and reach the flag in this classic platformer.", "thumbnail_url": "https://dzmv1mxivz48k.cloudfront.net/images/Google_113345162873827784654/1781345697895_01KV07MG37WZ165RSMXGDWA8ZG.file_00000000605071fab26c460e7d38c35c.png", "created_at": 1781319997, "updated_at": 1785732629070.8643, "art_style": null}, "task_plan": null}