{"asset_map": {"balloon_red": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/balloon_red_e4647e25-1462-450e-ac19-3f553f0a54b9.webp", "type": "image", "prompt": "Generate a full-body, front-view cartoon red balloon asset. The balloon is perfectly round with a vibrant gradient from #FF4757 (bottom) to #FF6B81 (top). High-gloss finish with a subtle white highlight on the upper-left. Include a small tied knot and a short string at the bottom. Clean, polished, game-ready game asset with soft 3D shading on a transparent background.", "description": "A glossy red cartoon balloon with a subtle shine and string.", "subtype": "object", "aspect_ratio": [716, 1306], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "balloon_blue": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/balloon_blue_b79fa625-ac3f-4add-b22f-3ce8386cc15b.webp", "type": "image", "prompt": "Generate a full-body, front-view cartoon blue balloon asset. Match the exact style, glossy finish, and 3D shading of balloon_red. Vibrant blue gradient from #3742FA (bottom) to #5F6FFF (top). Subtle white highlight on the upper-left, small knot and string at the bottom. Clean, game-ready asset on a transparent background.", "description": "A glossy blue cartoon balloon matching the red balloon's style.", "subtype": "object", "aspect_ratio": [684, 1248], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "balloon_yellow": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/balloon_yellow_7d87119a-3e85-4341-b1c9-a28c398fffa8.webp", "type": "image", "prompt": "Generate a full-body, front-view cartoon yellow balloon asset. Match the exact style, glossy finish, and 3D shading of balloon_red. Vibrant yellow gradient from #FFA502 (bottom) to #FFBE33 (top). Subtle white highlight on the upper-left, small knot and string at the bottom. Clean, game-ready asset on a transparent background.", "description": "A glossy yellow cartoon balloon matching the red balloon's style.", "subtype": "object", "aspect_ratio": [684, 1248], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "balloon_green": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/balloon_green_560413f0-7965-49ce-991f-8bff17def2b9.webp", "type": "image", "prompt": "Generate a full-body, front-view cartoon green balloon asset. Match the exact style, glossy finish, and 3D shading of balloon_red. Vibrant green gradient from #2ED573 (bottom) to #7BED9F (top). Subtle white highlight on the upper-left, small knot and string at the bottom. Clean, game-ready asset on a transparent background.", "description": "A glossy green cartoon balloon matching the red balloon's style.", "subtype": "object", "aspect_ratio": [684, 1248], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "dart_red": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_red_c7185259-d525-45e4-ae74-77f113b01bfb.webp", "type": "image", "prompt": "Generate a cartoon-style game asset of a dart pointing vertically downward. Sleek metallic silver/gray body with a vibrant red tip (#FF4757). Streamlined design with small tail fins at the top. Subtle metallic gradient for dimension. Clean, polished, non-threatening casual game aesthetic on a transparent background.", "description": "A metallic dart with a red tip, pointing downwards.", "subtype": "object", "aspect_ratio": [407, 1249], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_blue": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_blue_b6492a3b-c1a4-4b15-9a21-94f2ae352022.webp", "type": "image", "prompt": "Generate a cartoon-style dart asset pointing vertically downward, matching the style of dart_red. Metallic silver/gray body with a vibrant blue tip (#3742FA). Streamlined design with small tail fins. Clean, polished game asset on a transparent background.", "description": "A metallic dart with a blue tip, pointing downwards.", "subtype": "object", "aspect_ratio": [406, 1248], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_yellow": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_yellow_ebbc332e-8b0a-4488-b41d-5b3ea9b0ccd0.webp", "type": "image", "prompt": "Generate a cartoon-style dart asset pointing vertically downward, matching the style of dart_red. Metallic silver/gray body with a vibrant yellow tip (#FFA502). Streamlined design with small tail fins. Clean, polished game asset on a transparent background.", "description": "A metallic dart with a yellow tip, pointing downwards.", "subtype": "object", "aspect_ratio": [406, 1248], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_green": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_green_acfe1388-9b09-4312-a0e4-38193d96fcfc.webp", "type": "image", "prompt": "Generate a cartoon-style dart asset pointing vertically downward, matching the style of dart_red. Metallic silver/gray body with a vibrant green tip (#2ED573). Streamlined design with small tail fins. Clean, polished game asset on a transparent background.", "description": "A metallic dart with a green tip, pointing downwards.", "subtype": "object", "aspect_ratio": [406, 1248], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_left_red": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_left_red_972f2736-8122-4323-9720-30d374b944ea.webp", "type": "image", "prompt": "Generate a cartoon-style dart asset pointing horizontally to the LEFT. Match the style and metallic body of dart_red. Vibrant red tip (#FF4757) on the left side, tail fins on the right side. Clean, polished casual game asset on a transparent background.", "description": "A metallic dart with a red tip, pointing to the left.", "subtype": "object", "aspect_ratio": [821, 317], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_left_blue": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_left_blue_8521265f-9707-4f04-83cd-c9897c81eeee.webp", "type": "image", "prompt": "Generate a cartoon-style dart asset pointing horizontally to the LEFT. Match the style of dart_left_red. Metallic silver/gray body with a vibrant blue tip (#3742FA) on the left side. Tail fins on the right side. Clean, polished game asset on a transparent background.", "description": "A metallic dart with a blue tip, pointing to the left.", "subtype": "object", "aspect_ratio": [1247, 480], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_left_yellow": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_left_yellow_a7e7e650-cef0-4ae9-8546-510b3296347f.webp", "type": "image", "prompt": "Generate a cartoon-style dart asset pointing horizontally to the LEFT. Match the style of dart_left_red. Metallic silver/gray body with a vibrant yellow tip (#FFA502) on the left side. Tail fins on the right side. Clean, polished game asset on a transparent background.", "description": "A metallic dart with a yellow tip, pointing to the left.", "subtype": "object", "aspect_ratio": [1247, 480], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_left_green": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_left_green_bdea3288-0afe-4503-8bf2-8e9e299b7a6c.webp", "type": "image", "prompt": "Generate a cartoon-style dart asset pointing horizontally to the LEFT. Match the style of dart_left_red. Metallic silver/gray body with a vibrant green tip (#2ED573) on the left side. Tail fins on the right side. Clean, polished game asset on a transparent background.", "description": "A metallic dart with a green tip, pointing to the left.", "subtype": "object", "aspect_ratio": [1247, 480], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "chain_pop_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/chain_pop_sound_e5d970e2-0565-4979-8790-0298b9e5d9d1.mp3", "type": "audio", "prompt": "A rapid sequence of 4 balloon pop sounds occurring in quick succession. Each pop is slightly higher in pitch than the last, creating a rewarding musical ascending feel. Bright, bubbly, and festive, conveying a successful game combo.", "description": "Satisfying sequence of multiple pops with ascending pitch for chain reactions.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.3406575963718821, "loop": false, "animation": null}, "victory_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/victory_sound_ffbeabeb-b231-4690-a155-99dbf97560d0.mp3", "type": "audio", "prompt": "A short, celebratory 3-second victory jingle in a bright major key. Features a rising melody played on a xylophone and glockenspiel with soft synth backing. Ends on a solid, happy resolved chord. Upbeat, playful, and rewarding for a puzzle game win.", "description": "Cheerful 3-star victory fanfare for level completion.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 3, "loop": false, "animation": null}, "background_music": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/background_music_2ebb6bd5-cbfd-4e56-bdd8-d98ae829e852.mp3", "type": "audio", "prompt": "A cheerful and light background music track for a casual mobile puzzle game. Features soft acoustic guitar plucking, a simple piano melody, and subtle, warm synth pads. Moderate tempo around 110 BPM, major key, very friendly and non-intrusive. Seamlessly loopable, avoiding heavy drums or intense bass.", "description": "Loopable, relaxing background track for the main gameplay.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30, "loop": true, "animation": null}, "dart_shoot_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/dart_shoot_sound_220fddda-ab22-4b03-882e-b4fc1efefa91.mp3", "type": "audio", "prompt": "A very short, sharp, and urgent 'whoosh' sound effect of a dart being thrown. The sound should have a quick, snappy attack and a punchy, concise body capturing rapid motion. Clean and dry with no long tails, no reverb, and no metallic clinking. High-frequency air displacement sound suitable for frequent repetitive use in a puzzle or arcade game.", "description": "Short, sharp whoosh sound of a dart being thrown quickly through the air.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.35, "loop": false, "animation": null}, "balloon_purple": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/balloon_purple_ac4946b1-7743-47f5-9bdf-9b8322a047d8.webp", "type": "image", "prompt": "Generate a glossy purple cartoon balloon with a subtle shine and a string at the bottom. Maintain the exact artistic style, shading, and proportions of the balloon in image 1. Front view, clean edges, high resolution, transparent background.", "description": "A glossy purple cartoon balloon with a subtle shine and string, matching the existing balloon set.", "subtype": "object", "aspect_ratio": [684, 1248], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "dart_purple": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_purple_000f6b58-087b-4a1e-a96b-497eb59d346b.webp", "type": "image", "prompt": "Generate a professional quality game asset of a metallic dart with a purple tip, pointing downwards. The style should be clean and polished, matching the metallic texture and shading of image 1. Side view, transparent background, front-facing downward orientation.", "description": "A metallic dart with a purple tip, pointing downwards, matching the style of existing darts.", "subtype": "object", "aspect_ratio": [406, 1246], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_left_purple": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_left_purple_242d6a86-e08c-4032-a8c2-5a0f602572eb.webp", "type": "image", "prompt": "Generate a metallic dart with a purple tip, pointing horizontally to the left. The style must perfectly match image 1, featuring a sleek metallic body and a vibrant purple colored tip. Side view, high resolution, clean game-ready asset, transparent background.", "description": "A metallic dart with a purple tip, pointing to the left, styled to match the existing red dart.", "subtype": "object", "aspect_ratio": [1247, 480], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "error_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/error_sound_e88442fa-ed93-41fd-80cd-1e1d5e635048.mp3", "type": "audio", "prompt": "A short, low-frequency dull thud mixed with a soft electronic buzzer. The sound should be non-intrusive and dampened, indicating a mistake or blocked action. Clean, minimal, no reverb tail, suitable for a casual puzzle game UI. 0.5 seconds of content with a quick decay.", "description": "Short, dull buzzer or thud sound indicating an invalid move or blocked action.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.88, "loop": false, "animation": null}, "balloon_pink": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/balloon_pink_329d67aa-3455-4be8-9257-a11478e7377d.webp", "type": "image", "prompt": "Generate a glossy hot pink (magenta #FF1493) cartoon balloon with a subtle shine and a thin string attached to the bottom. Match the visual style, lighting, and simple cartoon aesthetic of balloon_red. High resolution, game-ready asset, transparent background, front view.", "description": "A glossy hot pink cartoon balloon with a subtle shine and string, matching the existing balloon set.", "subtype": "object", "aspect_ratio": [684, 1245], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_pink": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_pink_9014c025-8f9a-4f43-8159-f36dfee5146f.webp", "type": "image", "prompt": "Generate a metallic dart with a vibrant hot pink (#FF1493) tip, pointing downwards. The dart should have a polished metallic body and a sleek, clean cartoon aesthetic. Match the exact lighting, shading, and material style of dart_red and dart_blue. Transparent background, high resolution, professional game asset.", "description": "A metallic dart with a hot pink tip, pointing downwards, styled to match the existing dart set.", "subtype": "object", "aspect_ratio": [406, 1248], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_left_pink": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_left_pink_cecb6bb5-543a-44ba-bacb-cdb85cb30c5d.webp", "type": "image", "prompt": "Generate a game-ready metallic dart with a vibrant hot pink (#FF1493) tip, pointing horizontally to the left. Match the clean, polished cartoon style and perspective of image 1 and image 2. The lighting and metallic texture should be consistent with the existing dart set. Transparent background, high resolution.", "description": "A metallic dart with a hot pink tip pointing to the left, matching the existing dart set.", "subtype": "object", "aspect_ratio": [832, 333], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "balloon_tiffany": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/balloon_tiffany_e0440738-8667-427d-90a2-be4043192016.webp", "type": "image", "prompt": "Generate a glossy Tiffany blue (hex #0ABAB5) cartoon balloon, matching the exact visual style, shine, and round shape of image 1. The balloon should have a subtle highlight reflection, a short string tied at the bottom, and be presented as a clean, high-quality game asset on a transparent background.", "description": "A glossy Tiffany blue cartoon balloon matching the existing balloon set style.", "subtype": "object", "aspect_ratio": [684, 1248], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "dart_tiffany": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_tiffany_ccd6f489-b638-4c96-8e0a-d54521b64363.webp", "type": "image", "prompt": "Generate a game-ready metallic dart pointing downwards, featuring a Tiffany blue (hex #0ABAB5) tip and flight. The style should be clean, glossy, and professional, perfectly matching the metallic texture and shading of the existing dart set in image 1. Transparent background, high resolution.", "description": "A metallic dart with a Tiffany blue tip, pointing downwards, styled to match the existing dart set.", "subtype": "object", "aspect_ratio": [407, 1246], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_left_tiffany": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_left_tiffany_f785c4cc-49c4-48b0-9dab-75279b3f1e0b.webp", "type": "image", "prompt": "Generate a metallic dart with a Tiffany blue (hex #0ABAB5) tip, pointing horizontally to the left. The style should be clean, polished, and match the existing game asset 'dart_left_red'. It should feature a sharp metal body, colored flight/tip, and be presented as a standalone object on a transparent background, side view.", "description": "A metallic dart with a Tiffany blue tip pointing to the left, matching the existing dart set.", "subtype": "object", "aspect_ratio": [822, 324], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "balloon_orange": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/balloon_orange_b7861306-245b-4ac0-85ec-1d65334a9f15.webp", "type": "image", "prompt": "Generate a single glossy orange cartoon balloon with a subtle shine and a thin string. The style must perfectly match the aesthetic of image 1, featuring clean lines, vibrant colors, and a simple 2D cartoon look. Transparent background, high resolution, game-ready asset.", "description": "A glossy orange cartoon balloon with a subtle shine and string, matching the existing balloon set style.", "subtype": "object", "aspect_ratio": [684, 1248], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "dart_orange": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_orange_049d136a-28ae-415e-ab76-4ebc206c4a78.webp", "type": "image", "prompt": "Generate a professional quality game asset of a metallic dart with an orange tip, pointing downwards. The style should be clean, polished, and match the metallic texture and shading of dart_red. Front view, standing illustration on a transparent background.", "description": "A metallic dart with an orange tip, pointing downwards, matching the game's existing projectile style.", "subtype": "effect", "aspect_ratio": [405, 1248], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dart_left_orange": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dart_left_orange_a9dcaab8-72fa-48eb-9630-748af57f9381.webp", "type": "image", "prompt": "Generate a metallic dart with an orange tip, pointing horizontally to the left. The style should be clean, polished, and match the existing dart set in image 1. High resolution, professional game asset with a metallic finish and a transparent background.", "description": "A metallic dart with an orange tip pointing to the left, matching the style of the existing dart set.", "subtype": "object", "aspect_ratio": [1248, 480], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Balloon Dart Pop</title>\n    <link href=\"https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n    <style>\n        :root {\n            --transition-fast: 150ms;\n            --transition-normal: 300ms;\n            --transition-slow: 500ms;\n            --ease-out: cubic-bezier(0.0, 0, 0.2, 1);\n            --ease-bounce: cubic-bezier(0.68, -0.6, 0.32, 1.6);\n            --space-xs: 8px;\n            --space-sm: 16px;\n            --space-md: 24px;\n            --space-lg: 32px;\n            --space-xl: 48px;\n            --color-red: #FF4757;\n            --color-blue: #3742FA;\n            --color-yellow: #FFA502;\n            --color-green: #2ED573;\n            --color-purple: #A020F0;\n            --color-pink: #FF1493;\n            --color-tiffany: #0ABAB5;\n            --color-orange: #FF6B00;\n            --bg-gradient-top: #E3F2FD;\n            --bg-gradient-bottom: #FFFFFF;\n            --shadow: 0 4px 12px rgba(0,0,0,0.15);\n            --radius: 16px;\n        }\n\n        body, html {\n            margin: 0;\n            padding: 0;\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            touch-action: manipulation;\n            font-family: 'Inter', sans-serif;\n        }\n\n        #game-world {\n            position: relative;\n            width: 100%;\n            height: 100%;\n            background: linear-gradient(180deg, var(--bg-gradient-top) 0%, var(--bg-gradient-bottom) 100%);\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n        }\n\n        .game-title {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 44px;\n            font-weight: 700;\n            color: #2C3E50;\n            margin-top: 20px;\n            text-shadow: 0 2px 4px rgba(0,0,0,0.1);\n        }\n\n        .timer-container {\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            margin-top: 60px;\n            width: 600px;\n            gap: 12px;\n            z-index: 10;\n        }\n\n        .progress-container {\n            width: 100%;\n            height: 48px;\n            background: rgba(0, 0, 0, 0.15);\n            border-radius: 24px;\n            padding: 4px;\n            box-shadow: inset 0 2px 6px rgba(0,0,0,0.2);\n            display: flex;\n            align-items: center;\n            position: relative;\n            overflow: hidden;\n        }\n\n        .progress-bar {\n            height: 100%;\n            width: 100%;\n            background: var(--color-green);\n            border-radius: 20px;\n            transition: width 1s linear, background 0.3s ease;\n        }\n\n        .timer-display {\n            position: absolute;\n            left: 50%;\n            top: 50%;\n            transform: translate(-50%, -50%);\n            font-family: 'Fredoka', sans-serif;\n            font-size: 28px;\n            font-weight: 700;\n            color: #FFFFFF;\n            text-shadow: 0 2px 4px rgba(0,0,0,0.5);\n            pointer-events: none;\n            z-index: 2;\n            white-space: nowrap;\n        }\n\n        .game-area {\n            position: relative;\n            margin-top: 24px;\n        }\n\n        .grid-container {\n            display: grid;\n            grid-template-columns: repeat(10, 60px);\n            grid-template-rows: repeat(10, 60px);\n            gap: 0px;\n            background: rgba(255,255,255,0.6);\n            border-radius: var(--radius);\n            padding: 8px;\n            box-shadow: var(--shadow);\n        }\n\n        .balloon-cell {\n            width: 60px;\n            height: 60px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            position: relative;\n            cursor: pointer;\n            border-radius: 8px;\n            transition: transform var(--transition-fast) var(--ease-out);\n        }\n\n        .balloon-cell .balloon-img {\n            width: 60px;\n            height: 60px;\n            object-fit: contain;\n            clip-path: inset(0 0 22% 0); /* Clip the bottom string completely */\n            transform: translateY(6px); /* Center the remaining balloon body */\n            will-change: auto;\n        }\n\n        .balloon-cell.popping .balloon-img {\n            transform: scale(1.3) translateY(6px);\n            opacity: 0;\n            transition: transform 300ms var(--ease-out), opacity 200ms ease;\n        }\n\n        .balloon-cell.empty .balloon-img {\n            display: none;\n        }\n\n        .balloon-cell .stuck-dart {\n            position: absolute;\n            width: 34px;\n            height: 34px;\n            object-fit: contain;\n            z-index: 5;\n            pointer-events: none;\n            animation: stickEffect 150ms cubic-bezier(0.175, 0.885, 0.32, 1.275);\n        }\n\n        @keyframes stickEffect {\n            0% { transform: scale(1.6); }\n            100% { transform: scale(1); }\n        }\n\n        .top-darts {\n            display: flex;\n            gap: 0px;\n            margin-bottom: 10px;\n            padding-left: 8px;\n        }\n\n        .right-darts {\n            display: flex;\n            flex-direction: column;\n            gap: 0px;\n            margin-left: 10px;\n            padding-top: 8px;\n        }\n\n        .dart-slot {\n            width: 60px;\n            height: 60px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            cursor: pointer;\n            border-radius: 12px;\n            background: rgba(255,255,255,0.5);\n            transition: transform var(--transition-fast) var(--ease-out), background var(--transition-fast);\n        }\n\n        .dart-slot:hover:not(.used):not(.disabled) {\n            background: rgba(255,255,255,0.9);\n            transform: scale(1.05);\n        }\n\n        .dart-slot:active:not(.used):not(.disabled) {\n            transform: scale(0.95);\n        }\n\n        .dart-slot.shake-v {\n            animation: shakeV 0.4s ease-in-out;\n        }\n\n        .dart-slot.shake-h {\n            animation: shakeH 0.4s ease-in-out;\n        }\n\n        @keyframes shakeV {\n            0%, 100% { transform: translateY(0); }\n            20% { transform: translateY(-8px); }\n            40% { transform: translateY(8px); }\n            60% { transform: translateY(-4px); }\n            80% { transform: translateY(4px); }\n        }\n\n        @keyframes shakeH {\n            0%, 100% { transform: translateX(0); }\n            20% { transform: translateX(8px); }\n            40% { transform: translateX(-8px); }\n            60% { transform: translateX(4px); }\n            80% { transform: translateX(-4px); }\n        }\n\n        .dart-slot.used {\n            cursor: default;\n            background: transparent;\n        }\n\n        .dart-slot.used .dart-visual {\n            display: none;\n        }\n\n        .dart-slot.disabled {\n            cursor: default;\n        }\n\n        .dart-slot .dart-visual {\n            width: 44px;\n            height: 44px;\n        }\n\n        .hud {\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 32px;\n            margin-top: 40px;\n            width: 100%;\n            padding: 0 16px;\n            box-sizing: border-box;\n        }\n\n        .move-counter {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 32px;\n            font-weight: 600;\n            color: #2C3E50;\n            background: rgba(255,255,255,0.8);\n            padding: 12px 24px;\n            border-radius: var(--radius);\n            box-shadow: var(--shadow);\n        }\n\n        .btn {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 24px;\n            font-weight: 600;\n            color: white;\n            background: #5352ED;\n            border: none;\n            border-radius: var(--radius);\n            padding: 12px 32px;\n            cursor: pointer;\n            box-shadow: var(--shadow);\n            transition: transform var(--transition-fast) var(--ease-out), background var(--transition-fast);\n            min-width: 96px;\n            min-height: 48px;\n        }\n\n        .btn:hover {\n            background: #3D3CC4;\n            transform: scale(1.05);\n        }\n\n        .btn:active {\n            transform: scale(0.95);\n        }\n\n        .overlay {\n            position: absolute;\n            top: 0; left: 0; right: 0; bottom: 0;\n            background: rgba(0,0,0,0.6);\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            z-index: 100;\n            opacity: 0;\n            pointer-events: none;\n            transition: opacity var(--transition-normal);\n        }\n\n        .overlay.visible {\n            opacity: 1;\n            pointer-events: auto;\n        }\n\n        .overlay-card {\n            background: white;\n            border-radius: 24px;\n            padding: 48px;\n            text-align: center;\n            box-shadow: 0 8px 32px rgba(0,0,0,0.3);\n            max-width: 500px;\n        }\n\n        .overlay-title {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 48px;\n            font-weight: 700;\n            color: #2C3E50;\n            margin-bottom: 16px;\n        }\n\n        .overlay-subtitle {\n            font-family: 'Inter', sans-serif;\n            font-size: 24px;\n            color: #666;\n            margin-bottom: 24px;\n        }\n\n        .stars {\n            font-size: 48px;\n            margin-bottom: 24px;\n        }\n\n        .edit-palette {\n            display: flex;\n            gap: 12px;\n            margin-top: 24px;\n            justify-content: center;\n            flex-wrap: wrap;\n        }\n\n        .color-btn {\n            width: 64px;\n            height: 64px;\n            border-radius: 50%;\n            border: 4px solid white;\n            box-shadow: var(--shadow);\n            cursor: pointer;\n            transition: transform var(--transition-fast) var(--ease-bounce);\n        }\n\n        .color-btn:hover {\n            transform: scale(1.15);\n        }\n\n        .color-btn.selected {\n            border-color: #2C3E50;\n            transform: scale(1.2);\n        }\n\n        .color-btn.empty-btn {\n            background: repeating-conic-gradient(#ccc 0% 25%, white 0% 50%) 50% / 16px 16px;\n        }\n\n        .edit-instructions {\n            font-family: 'Inter', sans-serif;\n            font-size: 20px;\n            color: #666;\n            margin-top: 16px;\n            text-align: center;\n        }\n\n        #particle-canvas {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            z-index: 50;\n        }\n\n        @keyframes pulse {\n            0% { transform: scale(1); }\n            50% { transform: scale(1.05); }\n            100% { transform: scale(1); }\n        }\n\n        @media (prefers-reduced-motion: reduce) {\n            * {\n                transition-duration: 0ms !important;\n                animation-duration: 0ms !important;\n            }\n        }\n    </style>\n</head>\n<body>\n    <div id=\"game-world\"></div>\n    <script>\n    /*\n     * BALLOON DART POP\n     * A color-matching puzzle game where players shoot darts from the top and right\n     * edges to pop matching-color balloons. Chain reactions occur when adjacent\n     * same-color balloons are popped together.\n     */\n    async function run(mode) {\n        const gameWorld = document.getElementById('game-world');\n        gameWorld.innerHTML = '';\n\n        if (mode === 'play') {\n            gameWorld.innerHTML = `\n                <div id=\"loading-screen\" style=\"position:fixed; top:0; left:0; width:100%; height:100%; background:linear-gradient(180deg, #E3F2FD 0%, #FFFFFF 100%); display:flex; flex-direction:column; align-items:center; justify-content:center; z-index:2000;\">\n                    <div style=\"font-family: 'Fredoka', sans-serif; font-size: 44px; font-weight: 700; color: #2C3E50; margin-bottom: 60px; text-align:center;\">Pop all the balloons</div>\n                    <div id=\"loading-status\" style=\"font-family: 'Inter', sans-serif; font-size: 24px; color: #666; display: flex; flex-direction: column; align-items: center; gap: 20px;\">\n                        <span>Loading Assets...</span>\n                    </div>\n                </div>\n            `;\n        }\n\n\n        function startGameDirect() {\n            currentLevelIndex = 0;\n            sessionScore = 0;\n            levelData = getLevelData();\n            grid = JSON.parse(JSON.stringify(levelData.grid));\n            topDarts = [...levelData.topDarts];\n            rightDarts = [...levelData.rightDarts];\n            topDartsUsed = new Array(10).fill(false);\n            rightDartsUsed = new Array(10).fill(false);\n            blockedDarts = [];\n            moves = 0;\n            timeLeft = 300;\n            animating = false;\n            gameOver = false;\n            particles = [];\n            gameStarted = false;\n            \n            buildUI();\n            renderAll();\n            setupPlayEvents();\n            requestAnimationFrame(particleLoop);\n        }\n\n        const config = window.gameConfig;\n\n        // Asset preloading\n        const assetCache = {};\n        const audioBuffers = {};\n        let audioCtx = null;\n        let audioReady = false;\n        const pendingAudioUrls = {};\n\n        function getAudioContext() {\n            if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();\n            return audioCtx;\n        }\n\n        let audioLoadingPromise = null;\n\n        function preloadImages(ids) {\n            return Promise.all(ids.map(id => {\n                return new Promise((resolve) => {\n                    const info = lib.getAsset(id);\n                    if (info && info.type === 'image') {\n                        const img = new Image();\n                        img.crossOrigin = 'anonymous';\n                        img.onload = () => { assetCache[id] = img; resolve(); };\n                        img.onerror = () => resolve();\n                        img.src = info.url;\n                    } else resolve();\n                });\n            }));\n        }\n\n        // Store audio URLs for lazy loading\n        function prepareAudioUrls(ids) {\n            for (const id of ids) {\n                const info = lib.getAsset(id);\n                if (info && info.type === 'audio') {\n                    pendingAudioUrls[id] = info.url;\n                }\n            }\n        }\n\n        // Actually load and decode audio buffers\n        async function loadAudioBuffers() {\n            const ctx = getAudioContext();\n            // Proactively resume context\n            if (ctx.state === 'suspended') {\n                try { await ctx.resume(); } catch (e) {}\n            }\n\n            if (audioReady) return true;\n            if (audioLoadingPromise) return audioLoadingPromise;\n\n            audioLoadingPromise = (async () => {\n                const loadPromises = Object.entries(pendingAudioUrls).map(async ([id, url]) => {\n                    if (audioBuffers[id]) return;\n                    try {\n                        const resp = await fetch(url);\n                        if (!resp.ok) throw new Error('Fetch failed');\n                        const buf = await resp.arrayBuffer();\n                        audioBuffers[id] = await ctx.decodeAudioData(buf);\n                    } catch(e) {\n                        lib.log(`Failed to load audio: ${id} - ${e.message}`);\n                    }\n                });\n\n                await Promise.all(loadPromises);\n                audioReady = true;\n                return true;\n            })();\n\n            return audioLoadingPromise;\n        }\n\n        let gameStarted = false;\n        async function triggerGameStart() {\n            if (mode !== 'play') return;\n            \n            const ctx = getAudioContext();\n            if (ctx && ctx.state === 'suspended') {\n                ctx.resume().catch(e => {});\n            }\n\n            if (!gameStarted) {\n                gameStarted = true;\n                startTimer();\n                // Load audio buffers in the background without blocking the interaction\n                loadAudioBuffers().then(() => {\n                    if (!bgMusic && gameStarted) {\n                        bgMusic = playSound('background_music', true);\n                    }\n                });\n            }\n        }\n\n        function playSound(id, loop = false, pitch = 1.0) {\n            try {\n                const ctx = getAudioContext();\n                if (ctx.state === 'suspended') {\n                    ctx.resume().catch(e => {});\n                }\n                \n                const buffer = audioBuffers[id];\n                if (!buffer) {\n                    // Try to load in background if not ready, but don't block\n                    loadAudioBuffers();\n                    return null;\n                }\n                \n                const source = ctx.createBufferSource();\n                source.buffer = buffer;\n                source.loop = loop;\n                source.playbackRate.value = pitch;\n                const gain = ctx.createGain();\n                gain.gain.value = loop ? musicVolume : sfxVolume;\n                source.connect(gain).connect(ctx.destination);\n                source.start(0);\n                return { source, gain };\n            } catch(e) { \n                lib.log('Play sound error: ' + id);\n                return null; \n            }\n        }\n        // Preload all assets\n        const imageIds = [\n            'balloon_red', 'balloon_blue', 'balloon_yellow', 'balloon_green', 'balloon_purple', 'balloon_pink', 'balloon_tiffany', 'balloon_orange',\n            'dart_red', 'dart_blue', 'dart_yellow', 'dart_green', 'dart_purple', 'dart_pink', 'dart_tiffany', 'dart_orange',\n            'dart_left_red', 'dart_left_blue', 'dart_left_yellow', 'dart_left_green', 'dart_left_purple', 'dart_left_pink', 'dart_left_tiffany', 'dart_left_orange'\n        ];\n        const audioIds = ['chain_pop_sound', 'dart_shoot_sound', 'victory_sound', 'background_music', 'error_sound'];\n\n        await preloadImages(imageIds);\n        prepareAudioUrls(audioIds);\n\n        // Color mappings\n        const colorMap = {\n            red: { balloon: 'balloon_red', dartDown: 'dart_red', dartLeft: 'dart_left_red', hex: '#FF4757' },\n            blue: { balloon: 'balloon_blue', dartDown: 'dart_blue', dartLeft: 'dart_left_blue', hex: '#3742FA' },\n            yellow: { balloon: 'balloon_yellow', dartDown: 'dart_yellow', dartLeft: 'dart_left_yellow', hex: '#FFA502' },\n            green: { balloon: 'balloon_green', dartDown: 'dart_green', dartLeft: 'dart_left_green', hex: '#2ED573' },\n            purple: { balloon: 'balloon_purple', dartDown: 'dart_purple', dartLeft: 'dart_left_purple', hex: '#A020F0' },\n            pink: { balloon: 'balloon_pink', dartDown: 'dart_pink', dartLeft: 'dart_left_pink', hex: '#FF1493' },\n            tiffany: { balloon: 'balloon_tiffany', dartDown: 'dart_tiffany', dartLeft: 'dart_left_tiffany', hex: '#0ABAB5' },\n            orange: { balloon: 'balloon_orange', dartDown: 'dart_orange', dartLeft: 'dart_left_orange', hex: '#FF6B00' }\n        };\n\n        function createDartElement(color, direction, className = '') {\n            const el = document.createElement('div');\n            if (className) el.className = className;\n            const isDown = direction === 'down';\n            const assetId = isDown ? colorMap[color].dartDown : colorMap[color].dartLeft;\n            const url = assetCache[assetId] ? assetCache[assetId].src : ''; // Fallback to empty string if not in cache\n            \n            el.style.backgroundColor = colorMap[color].hex;\n            el.style.webkitMaskImage = `url(\"${url}\")`;\n            el.style.maskImage = `url(\"${url}\")`;\n            el.style.webkitMaskSize = 'contain';\n            el.style.maskSize = 'contain';\n            el.style.webkitMaskRepeat = 'no-repeat';\n            el.style.maskRepeat = 'no-repeat';\n            el.style.webkitMaskPosition = 'center';\n            el.style.maskPosition = 'center';\n            \n            return el;\n        }\n\n        // Runtime state\n        let currentLevelIndex = mode === 'edit' ? parseInt(config.currentLevelIndex || 0) : 0;\n        let sessionScore = 0; // Total score accumulated from previous completed levels\n\n        let handleDartClick = null;\n        let handleCellClick = null;\n\n        // Helper to bind interaction events reliably across mobile browsers\n        function bindInteraction(el, handler) {\n            const wrappedHandler = async (e) => {\n                // Prevent default browser behavior (scroll, zoom, etc)\n                if (e.cancelable) e.preventDefault();\n                // Proactively activate audio context and start game features on interaction\n                await triggerGameStart();\n                handler(e);\n            };\n            el.addEventListener('pointerdown', wrappedHandler);\n            el.addEventListener('touchstart', wrappedHandler, { passive: false });\n        }\n\n        // Level Sanitization: Ensure levels exist and don't share references\n        if (mode === 'edit') {\n            if (!config.levels || !Array.isArray(config.levels)) {\n                // Initialize levels from root if missing\n                config.levels = [\n                    {\n                        grid: JSON.parse(JSON.stringify(config.grid || [])),\n                        topDarts: JSON.parse(JSON.stringify(config.topDarts || [])),\n                        rightDarts: JSON.parse(JSON.stringify(config.rightDarts || [])),\n                        optimalMoves: config.optimalMoves || 7\n                    }\n                ];\n            } else {\n                // Ensure all levels have the required properties and break any shared references\n                config.levels = config.levels.map(lvl => ({\n                    grid: JSON.parse(JSON.stringify(lvl.grid || config.grid || [])),\n                    topDarts: JSON.parse(JSON.stringify(lvl.topDarts || config.topDarts || [])),\n                    rightDarts: JSON.parse(JSON.stringify(lvl.rightDarts || config.rightDarts || [])),\n                    optimalMoves: lvl.optimalMoves || config.optimalMoves || 7\n                }));\n            }\n        }\n\n        // Show game parameters\n        function updateGameParameters() {\n            const levelOptions = config.levels ? config.levels.map((_, i) => ({ label: `Level ${i + 1}`, value: i })) : [{ label: 'Level 1', value: 0 }];\n            \n            lib.showGameParameters({\n                name: 'Pop all the balloons',\n                params: {\n                    'Current Level (Edit)': { \n                        key: 'currentLevelIndex', \n                        type: 'dropdown', \n                        options: levelOptions,\n                        onChange: (val) => {\n                            const numericVal = parseInt(val);\n                            config.currentLevelIndex = numericVal;\n                            currentLevelIndex = numericVal;\n                            restartGame();\n                            // Refresh parameters to update the 'Optimal Moves' key path\n                            updateGameParameters();\n                        }\n                    },\n                    'Optimal Moves': { \n                        key: config.levels ? `levels.${currentLevelIndex}.optimalMoves` : 'optimalMoves', \n                        type: 'slider', \n                        min: 1, \n                        max: 20, \n                        step: 1 \n                    }\n                }\n            });\n        }\n        updateGameParameters();\n        \n        function getLevelData() {\n            if (config.levels && config.levels[currentLevelIndex]) {\n                return config.levels[currentLevelIndex];\n            }\n            return config;\n        }\n\n        let levelData = getLevelData();\n        let grid = JSON.parse(JSON.stringify(levelData.grid));\n        let topDarts = [...levelData.topDarts];\n        let rightDarts = [...levelData.rightDarts];\n        let topDartsUsed = new Array(10).fill(false);\n        let rightDartsUsed = new Array(10).fill(false);\n        let blockedDarts = [];\n        let moves = 0;\n        let timeLeft = 300; // 5 minutes in seconds\n        let timerInterval = null;\n        let animating = false;\n        let gameOver = false;\n        let bgMusic = null;\n        let sfxVolume = 0.8;\n        let musicVolume = 0.3;\n        let menuOpen = false;\n\n        // Particles\n        let particles = [];\n\n        // Build DOM\n        function buildUI() {\n            gameWorld.innerHTML = '';\n\n            // Timer Container\n            const timerContainer = document.createElement('div');\n            timerContainer.className = 'timer-container';\n            timerContainer.id = 'timer-container';\n            if (mode !== 'play') timerContainer.style.display = 'none';\n            gameWorld.appendChild(timerContainer);\n\n            // Progress Bar (Timer embedded inside)\n            const progressContainer = document.createElement('div');\n            progressContainer.className = 'progress-container';\n            \n            const progressBar = document.createElement('div');\n            progressBar.className = 'progress-bar';\n            progressBar.id = 'progress-bar';\n            progressContainer.appendChild(progressBar);\n\n            const timer = document.createElement('div');\n            timer.className = 'timer-display';\n            timer.id = 'timer-display';\n            timer.textContent = 'Remaining time: 05:00';\n            progressContainer.appendChild(timer);\n\n            timerContainer.appendChild(progressContainer);\n\n            // Title\n            const title = document.createElement('div');\n            title.className = 'game-title';\n            title.textContent = 'Pop all the balloons';\n            gameWorld.appendChild(title);\n\n            // Level Indicator\n            const levelIndicator = document.createElement('div');\n            levelIndicator.className = 'overlay-subtitle';\n            levelIndicator.style.marginTop = '10px';\n            levelIndicator.style.marginBottom = '0';\n            levelIndicator.textContent = `Level ${currentLevelIndex + 1}`;\n            gameWorld.appendChild(levelIndicator);\n\n            // Game area container\n            const gameArea = document.createElement('div');\n            gameArea.className = 'game-area';\n            gameArea.style.display = 'flex';\n            gameArea.style.flexDirection = 'column';\n            gameArea.style.alignItems = 'flex-start';\n            gameWorld.appendChild(gameArea);\n\n            // Top darts row\n            const topDartsRow = document.createElement('div');\n            topDartsRow.className = 'top-darts';\n            topDartsRow.id = 'top-darts';\n            gameArea.appendChild(topDartsRow);\n\n            // Grid + right darts row\n            const midRow = document.createElement('div');\n            midRow.style.display = 'flex';\n            midRow.style.alignItems = 'flex-start';\n            gameArea.appendChild(midRow);\n\n            const gridContainer = document.createElement('div');\n            gridContainer.className = 'grid-container';\n            gridContainer.id = 'grid-container';\n            midRow.appendChild(gridContainer);\n\n            const rightDartsCol = document.createElement('div');\n            rightDartsCol.className = 'right-darts';\n            rightDartsCol.id = 'right-darts';\n            midRow.appendChild(rightDartsCol);\n\n            // HUD\n            const hud = document.createElement('div');\n            hud.className = 'hud';\n            hud.id = 'hud';\n            gameWorld.appendChild(hud);\n\n            // Particle canvas\n            const pCanvas = document.createElement('canvas');\n            pCanvas.id = 'particle-canvas';\n            pCanvas.width = 720;\n            pCanvas.height = 1280;\n            gameWorld.appendChild(pCanvas);\n\n            // Overlay\n            const overlay = document.createElement('div');\n            overlay.className = 'overlay';\n            overlay.id = 'overlay';\n            overlay.innerHTML = '<div class=\"overlay-card\" id=\"overlay-card\"></div>';\n            gameWorld.appendChild(overlay);\n\n            if (mode === 'edit') {\n                const editInstr = document.createElement('div');\n                editInstr.className = 'edit-instructions';\n                editInstr.textContent = 'Tap balloons/darts to change colors. Select a color below first.';\n                gameWorld.appendChild(editInstr);\n\n                const palette = document.createElement('div');\n                palette.className = 'edit-palette';\n                palette.id = 'edit-palette';\n                gameWorld.appendChild(palette);\n            }\n        }\n\n        buildUI();\n        // Shoot dart from top (column col)\n        async function shootTop(col, startRow = 0, reFireDart = null) {\n            if (animating || gameOver) return;\n            animating = true;\n            try {\n                const reFired = !!reFireDart;\n                const dartColor = reFired ? reFireDart.color : topDarts[col];\n                if (!dartColor) return;\n\n                let flying;\n                let initialDartPosition = null;\n                const area = document.querySelector('.game-area');\n                let areaRect = area.getBoundingClientRect();\n\n                if (reFired && reFireDart.element) {\n                    // Remove from blocked array when re-firing\n                    const idx = blockedDarts.indexOf(reFireDart);\n                    if (idx > -1) blockedDarts.splice(idx, 1);\n\n                    flying = reFireDart.element;\n                    flying.style.transition = 'all 150ms linear';\n                    initialDartPosition = { r: reFireDart.r, c: reFireDart.c };\n                    playSound('dart_shoot_sound');\n                } else {\n                    if (topDartsUsed[col]) return; \n\n                    // Check if blocked immediately by a mismatching balloon or any dart\n                    let firstObstacle = null;\n                    for (let r = 0; r < 10; r++) {\n                        if (grid[r][col] !== null) {\n                            firstObstacle = { type: 'balloon', color: grid[r][col] };\n                            break;\n                        }\n                        if (blockedDarts.find(d => d.c === col && d.r === r)) {\n                            firstObstacle = { type: 'dart' };\n                            break;\n                        }\n                    }\n\n                    if (firstObstacle && (firstObstacle.type === 'dart' || firstObstacle.color !== dartColor)) {\n                        // Shake and error\n                        const slot = document.getElementById('top-darts').children[col];\n                        if (slot) {\n                            slot.classList.add('shake-v');\n                            playSound('error_sound');\n                            await sleep(400);\n                            slot.classList.remove('shake-v');\n                        }\n                        return;\n                    }\n\n                    topDartsUsed[col] = true;\n                    moves++;\n                    playSound('dart_shoot_sound');\n\n                    const topDartsRow = document.getElementById('top-darts');\n                    const slot = topDartsRow.children[col];\n                    const slotRect = slot.getBoundingClientRect();\n\n                    flying = createDartElement(dartColor, 'down');\n                    flying.className = 'flying-dart-element';\n                    flying.style.position = 'absolute';\n                    flying.style.width = '60px';\n                    flying.style.height = '60px';\n                    flying.style.zIndex = '100';\n                    flying.style.left = `${slotRect.left - areaRect.left + (slotRect.width - 60) / 2}px`;\n                    flying.style.top = `${slotRect.top - areaRect.top + (slotRect.height - 60) / 2}px`;\n                    flying.style.transition = 'all 150ms linear';\n                    area.appendChild(flying);\n                }\n\n                renderAll();\n\n                // Fly through the entire column until blocked\n                let lastPoppedPosition = initialDartPosition;\n                let stoppedAtBlock = false;\n                let currentDartRow = reFired ? startRow : 0;\n\n                for (let r = currentDartRow; r < 10; r++) {\n                    const gridContainer = document.getElementById('grid-container');\n                    const cell = gridContainer.children[r * 10 + col];\n                    const cellRect = cell.getBoundingClientRect();\n                    \n                    flying.style.left = `${cellRect.left - areaRect.left + (cellRect.width - 60) / 2}px`;\n                    flying.style.top = `${cellRect.top - areaRect.top + (cellRect.height - 60) / 2}px`;\n                    \n                    await sleep(150);\n                    \n                    // Check if blocked by another dart\n                    if (blockedDarts.find(d => d.c === col && d.r === r)) {\n                        stoppedAtBlock = true;\n                        blockedDarts.push({ element: flying, color: dartColor, direction: 'down', r: r, c: col });\n                        break;\n                    }\n\n                    // If there's a balloon in the way\n                    if (grid[r][col] !== null) {\n                        // If it's a match, pop it and continue\n                        if (grid[r][col] === dartColor) {\n                            await popBalloonSingle(r, col);\n                            lastPoppedPosition = { r, c: col };\n                            await sleep(150); // Temporarily stop at the popped balloon\n                        } else {\n                            // Different color balloon blocks the dart\n                            stoppedAtBlock = true;\n                            blockedDarts.push({ element: flying, color: dartColor, direction: 'down', r: r, c: col });\n                            break;\n                        }\n                    }\n                }\n\n                if (stoppedAtBlock) {\n                    const targetR = lastPoppedPosition ? lastPoppedPosition.r : currentDartRow;\n                    const targetC = col;\n                    const gridContainer = document.getElementById('grid-container');\n                    const cell = gridContainer.children[targetR * 10 + targetC];\n                    const cellRect = cell.getBoundingClientRect();\n                    flying.style.left = `${cellRect.left - areaRect.left + (cellRect.width - 60) / 2}px`;\n                    flying.style.top = `${cellRect.top - areaRect.top + (cellRect.height - 60) / 2}px`;\n\n                    // Direct listener for the blocked dart itself\n                    bindInteraction(flying, (e) => {\n                        if (handleCellClick) handleCellClick(e, targetR, targetC);\n                    });\n\n                    await sleep(150);\n                    // Update r, c in the record we just pushed\n                    const record = blockedDarts[blockedDarts.length - 1];\n                    record.r = targetR;\n                    record.c = targetC;\n                } else {\n                    flying.remove();\n                }\n            } finally {\n                animating = false;\n                renderAll();\n                checkGameState();\n            }\n        }\n\n        // Shoot dart from right (row)\n        async function shootRight(row, startCol = 9, reFireDart = null) {\n            if (animating || gameOver) return;\n            animating = true;\n            try {\n                const reFired = !!reFireDart;\n                const dartColor = reFired ? reFireDart.color : rightDarts[row];\n                if (!dartColor) return;\n\n                let flying;\n                let initialDartPosition = null;\n                const area = document.querySelector('.game-area');\n                let areaRect = area.getBoundingClientRect();\n\n                if (reFired && reFireDart.element) {\n                    // Remove from blocked array when re-firing\n                    const idx = blockedDarts.indexOf(reFireDart);\n                    if (idx > -1) blockedDarts.splice(idx, 1);\n\n                    flying = reFireDart.element;\n                    flying.style.transition = 'all 150ms linear';\n                    initialDartPosition = { r: reFireDart.r, c: reFireDart.c };\n                    playSound('dart_shoot_sound');\n                } else {\n                    if (rightDartsUsed[row]) return; \n\n                    // Check if blocked immediately by a mismatching balloon or any dart\n                    let firstObstacle = null;\n                    for (let c = 9; c >= 0; c--) {\n                        if (grid[row][c] !== null) {\n                            firstObstacle = { type: 'balloon', color: grid[row][c] };\n                            break;\n                        }\n                        if (blockedDarts.find(d => d.r === row && d.c === c)) {\n                            firstObstacle = { type: 'dart' };\n                            break;\n                        }\n                    }\n\n                    if (firstObstacle && (firstObstacle.type === 'dart' || firstObstacle.color !== dartColor)) {\n                        // Shake and error\n                        const slot = document.getElementById('right-darts').children[row];\n                        if (slot) {\n                            slot.classList.add('shake-h');\n                            playSound('error_sound');\n                            await sleep(400);\n                            slot.classList.remove('shake-h');\n                        }\n                        return;\n                    }\n\n                    rightDartsUsed[row] = true;\n                    moves++;\n                    playSound('dart_shoot_sound');\n\n                    const rightDartsCol = document.getElementById('right-darts');\n                    const slot = rightDartsCol.children[row];\n                    const slotRect = slot.getBoundingClientRect();\n\n                    flying = createDartElement(dartColor, 'left');\n                    flying.className = 'flying-dart-element';\n                    flying.style.position = 'absolute';\n                    flying.style.width = '60px';\n                    flying.style.height = '60px';\n                    flying.style.zIndex = '100';\n                    flying.style.left = `${slotRect.left - areaRect.left + (slotRect.width - 60) / 2}px`;\n                    flying.style.top = `${slotRect.top - areaRect.top + (slotRect.height - 60) / 2}px`;\n                    flying.style.transition = 'all 150ms linear';\n                    area.appendChild(flying);\n                }\n\n                renderAll();\n\n                // Fly through the entire row until blocked\n                let lastPoppedPosition = initialDartPosition;\n                let stoppedAtBlock = false;\n                let currentDartCol = reFired ? startCol : 9;\n\n                for (let c = currentDartCol; c >= 0; c--) {\n                    const gridContainer = document.getElementById('grid-container');\n                    const cell = gridContainer.children[row * 10 + c];\n                    const cellRect = cell.getBoundingClientRect();\n                    \n                    flying.style.left = `${cellRect.left - areaRect.left + (cellRect.width - 60) / 2}px`;\n                    flying.style.top = `${cellRect.top - areaRect.top + (cellRect.height - 60) / 2}px`;\n                    \n                    await sleep(150);\n                    \n                    // Check if blocked by another dart\n                    if (blockedDarts.find(d => d.r === row && d.c === c)) {\n                        stoppedAtBlock = true;\n                        blockedDarts.push({ element: flying, color: dartColor, direction: 'left', r: row, c: c });\n                        break;\n                    }\n\n                    // If there's a balloon in the way\n                    if (grid[row][c] !== null) {\n                        // If it's a match, pop it and continue\n                        if (grid[row][c] === dartColor) {\n                            await popBalloonSingle(row, c);\n                            lastPoppedPosition = { r: row, c };\n                            await sleep(150); // Temporarily stop at the popped balloon\n                        } else {\n                            // Different color balloon blocks the dart\n                            stoppedAtBlock = true;\n                            blockedDarts.push({ element: flying, color: dartColor, direction: 'left', r: row, c: c });\n                            break;\n                        }\n                    }\n                }\n\n                if (stoppedAtBlock) {\n                    const targetR = row;\n                    const targetC = lastPoppedPosition ? lastPoppedPosition.c : currentDartCol;\n                    const gridContainer = document.getElementById('grid-container');\n                    const cell = gridContainer.children[targetR * 10 + targetC];\n                    const cellRect = cell.getBoundingClientRect();\n                    flying.style.left = `${cellRect.left - areaRect.left + (cellRect.width - 60) / 2}px`;\n                    flying.style.top = `${cellRect.top - areaRect.top + (cellRect.height - 60) / 2}px`;\n\n                    // Direct listener for the blocked dart itself\n                    bindInteraction(flying, (e) => {\n                        if (handleCellClick) handleCellClick(e, targetR, targetC);\n                    });\n\n                    await sleep(150);\n                    // Update r, c in the record we just pushed\n                    const record = blockedDarts[blockedDarts.length - 1];\n                    record.r = targetR;\n                    record.c = targetC;\n                } else {\n                    flying.remove();\n                }\n            } finally {\n                animating = false;\n                renderAll();\n                checkGameState();\n            }\n        }\n        async function popBalloonSingle(r, c) {\n            const color = grid[r][c];\n            if (!color) return;\n\n            const gridContainer = document.getElementById('grid-container');\n            const idx = r * 10 + c;\n            const cell = gridContainer.children[idx];\n            \n            if (cell) {\n                cell.classList.add('popping');\n                const rect = cell.getBoundingClientRect();\n                const gameRect = gameWorld.getBoundingClientRect();\n                const cx = rect.left - gameRect.left + rect.width / 2;\n                const cy = rect.top - gameRect.top + rect.height / 2;\n                spawnParticles(cx, cy, colorMap[color]?.hex || '#fff');\n            }\n\n            playSound('chain_pop_sound', false, 0.9 + Math.random() * 0.3);\n            \n            grid[r][c] = null;\n            // stuckDarts[r][c] = null; // No longer needed\n        }\n        function renderTopDarts() {\n            const container = document.getElementById('top-darts');\n            if (!container) return;\n            container.innerHTML = '';\n            for (let i = 0; i < 10; i++) {\n                const slot = document.createElement('div');\n                slot.className = 'dart-slot' + (topDartsUsed[i] ? ' used' : '') + (mode === 'edit' ? '' : '');\n                const color = topDarts[i];\n                if (color && colorMap[color] && !topDartsUsed[i]) {\n                    const dartEl = createDartElement(color, 'down', 'dart-visual');\n                    slot.appendChild(dartEl);\n                }\n                slot.dataset.index = i;\n                slot.dataset.type = 'top';\n                bindInteraction(slot, (e) => {\n                    if (handleDartClick) handleDartClick(e, 'top', i);\n                });\n                container.appendChild(slot);\n            }\n        }\n\n        function renderRightDarts() {\n            const container = document.getElementById('right-darts');\n            if (!container) return;\n            container.innerHTML = '';\n            for (let i = 0; i < 10; i++) {\n                const slot = document.createElement('div');\n                slot.className = 'dart-slot' + (rightDartsUsed[i] ? ' used' : '') + (mode === 'edit' ? '' : '');\n                const color = rightDarts[i];\n                if (color && colorMap[color] && !rightDartsUsed[i]) {\n                    const dartEl = createDartElement(color, 'left', 'dart-visual');\n                    slot.appendChild(dartEl);\n                }\n                slot.dataset.index = i;\n                slot.dataset.type = 'right';\n                bindInteraction(slot, (e) => {\n                    if (handleDartClick) handleDartClick(e, 'right', i);\n                });\n                container.appendChild(slot);\n            }\n        }\n\n        function renderGrid() {\n            const container = document.getElementById('grid-container');\n            if (!container) return;\n            container.innerHTML = '';\n            for (let row = 0; row < 10; row++) {\n                for (let col = 0; col < 10; col++) {\n                    const cell = document.createElement('div');\n                    cell.className = 'balloon-cell';\n                    cell.dataset.row = row;\n                    cell.dataset.col = col;\n                    \n                    const color = grid[row][col];\n                    if (color && colorMap[color]) {\n                        const img = document.createElement('img');\n                        img.className = 'balloon-img';\n                        const assetId = colorMap[color].balloon;\n                        img.src = assetCache[assetId] ? assetCache[assetId].src : lib.getAsset(assetId).url;\n                        cell.appendChild(img);\n                    }\n                    \n                    bindInteraction(cell, (e) => {\n                        if (handleCellClick) handleCellClick(e, row, col);\n                    });\n                    \n                    container.appendChild(cell);\n                }\n            }\n        }\n\n        function renderHUD() {\n            const hud = document.getElementById('hud');\n            if (!hud) return;\n            hud.innerHTML = '';\n            const counter = document.createElement('div');\n            counter.className = 'move-counter';\n            counter.textContent = `Moves: ${moves}`;\n            hud.appendChild(counter);\n\n            if (mode === 'play') {\n                const menuBtn = document.createElement('button');\n                menuBtn.className = 'btn';\n                menuBtn.textContent = '\u2699\ufe0f Menu';\n                bindInteraction(menuBtn, () => { openMenu(); });\n                hud.appendChild(menuBtn);\n            }\n\n            const restartBtn = document.createElement('button');\n            restartBtn.className = 'btn';\n            restartBtn.textContent = '\ud83d\udd04 Restart';\n            bindInteraction(restartBtn, (e) => {\n                restartGame(false);\n            });\n            hud.appendChild(restartBtn);\n        }\n\n        // ---- Settings / Level Select Menu ----\n        function closeMenu() {\n            menuOpen = false;\n            const el = document.getElementById('menu-overlay');\n            if (el) el.remove();\n            if (mode === 'play' && !gameOver && gameStarted) startTimer();\n        }\n\n        function openMenu() {\n            if (menuOpen) return;\n            menuOpen = true;\n            stopTimer();\n\n            const ov = document.createElement('div');\n            ov.id = 'menu-overlay';\n            ov.className = 'overlay visible';\n            ov.style.zIndex = '200';\n            ov.innerHTML = `\n                <div class=\"overlay-card\" style=\"padding:36px; min-width:420px;\">\n                    <div class=\"overlay-title\" style=\"font-size:40px; margin-bottom:24px;\">\u2699\ufe0f Menu</div>\n                    <div id=\"menu-main\">\n                        <div style=\"text-align:left; margin-bottom:24px;\">\n                            <div style=\"font-family:'Fredoka',sans-serif; font-size:24px; color:#2C3E50; margin-bottom:8px;\">\ud83d\udd0a Sound: <span id=\"sfx-val\">80</span>%</div>\n                            <input id=\"sfx-slider\" type=\"range\" min=\"0\" max=\"100\" step=\"5\" value=\"80\" style=\"width:100%; height:40px;\">\n                        </div>\n                        <div style=\"text-align:left; margin-bottom:28px;\">\n                            <div style=\"font-family:'Fredoka',sans-serif; font-size:24px; color:#2C3E50; margin-bottom:8px;\">\ud83c\udfb5 Music: <span id=\"music-val\">30</span>%</div>\n                            <input id=\"music-slider\" type=\"range\" min=\"0\" max=\"100\" step=\"5\" value=\"30\" style=\"width:100%; height:40px;\">\n                        </div>\n                        <button class=\"btn\" id=\"menu-levels-btn\" style=\"width:100%; margin-bottom:16px;\">\ud83d\uddfa\ufe0f Select Level</button>\n                        <button class=\"btn\" id=\"menu-close-btn\" style=\"width:100%; background:#7f8c8d;\">\u2716 Close</button>\n                    </div>\n                    <div id=\"menu-levels\" style=\"display:none;\">\n                        <div class=\"overlay-subtitle\" style=\"margin-bottom:16px;\">Choose a level</div>\n                        <div id=\"menu-level-grid\" style=\"display:flex; flex-wrap:wrap; gap:12px; justify-content:center; margin-bottom:24px;\"></div>\n                        <button class=\"btn\" id=\"menu-back-btn\" style=\"width:100%; background:#7f8c8d;\">\u2190 Back</button>\n                    </div>\n                </div>\n            `;\n            gameWorld.appendChild(ov);\n\n            const sfxSlider = ov.querySelector('#sfx-slider');\n            const musicSlider = ov.querySelector('#music-slider');\n            sfxSlider.value = Math.round(sfxVolume * 100);\n            musicSlider.value = Math.round(musicVolume * 100);\n            ov.querySelector('#sfx-val').textContent = sfxSlider.value;\n            ov.querySelector('#music-val').textContent = musicSlider.value;\n\n            sfxSlider.addEventListener('input', () => {\n                sfxVolume = parseInt(sfxSlider.value) / 100;\n                ov.querySelector('#sfx-val').textContent = sfxSlider.value;\n            });\n            musicSlider.addEventListener('input', () => {\n                musicVolume = parseInt(musicSlider.value) / 100;\n                ov.querySelector('#music-val').textContent = musicSlider.value;\n                if (bgMusic && bgMusic.gain) bgMusic.gain.gain.value = musicVolume;\n            });\n\n            bindInteraction(ov.querySelector('#menu-close-btn'), () => closeMenu());\n\n            const levelGrid = ov.querySelector('#menu-level-grid');\n            const levelCount = (config.levels && config.levels.length) ? config.levels.length : 1;\n            for (let i = 0; i < levelCount; i++) {\n                const b = document.createElement('button');\n                b.className = 'btn';\n                b.textContent = `${i + 1}`;\n                b.style.minWidth = '80px';\n                b.style.padding = '12px 0';\n                if (i === currentLevelIndex) b.style.background = '#2ED573';\n                bindInteraction(b, () => {\n                    closeMenu();\n                    currentLevelIndex = i;\n                    sessionScore = 0;\n                    restartGame();\n                    buildUI();\n                    renderAll();\n                });\n                levelGrid.appendChild(b);\n            }\n\n            bindInteraction(ov.querySelector('#menu-levels-btn'), () => {\n                ov.querySelector('#menu-main').style.display = 'none';\n                ov.querySelector('#menu-levels').style.display = 'block';\n            });\n            bindInteraction(ov.querySelector('#menu-back-btn'), () => {\n                ov.querySelector('#menu-levels').style.display = 'none';\n                ov.querySelector('#menu-main').style.display = 'block';\n            });\n        }\n\n        function renderAll() {\n            renderTopDarts();\n            renderRightDarts();\n            renderGrid();\n            renderHUD();\n            // Update level indicator text\n            const levelIndicator = gameWorld.querySelector('.overlay-subtitle');\n            if (levelIndicator) {\n                levelIndicator.textContent = `Level ${currentLevelIndex + 1}`;\n            }\n        }\n\n        async function animateFlyingDart(type, index, targetRow, targetCol, color, isMatch) {\n            const area = document.querySelector('.game-area');\n            if (!area) return;\n            \n            const sourceId = type === 'top' ? 'top-darts' : 'right-darts';\n            const sourceContainer = document.getElementById(sourceId);\n            if (!sourceContainer) return;\n            const slot = sourceContainer.children[index];\n            if (!slot) return;\n            \n            const gridContainer = document.getElementById('grid-container');\n            if (!gridContainer) return;\n            const cellIdx = targetRow * 10 + targetCol;\n            const cell = gridContainer.children[cellIdx];\n            if (!cell) return;\n            \n            const areaRect = area.getBoundingClientRect();\n            const slotRect = slot.getBoundingClientRect();\n            const cellRect = cell.getBoundingClientRect();\n            \n            const direction = type === 'top' ? 'down' : 'left';\n            const flying = createDartElement(color, direction);\n            flying.style.position = 'absolute';\n            flying.style.width = '60px';\n            flying.style.height = '60px';\n            flying.style.zIndex = '100';\n            \n            flying.style.left = `${slotRect.left - areaRect.left + (slotRect.width - 60) / 2}px`;\n            flying.style.top = `${slotRect.top - areaRect.top + (slotRect.height - 60) / 2}px`;\n            flying.style.transition = 'all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94)';\n            \n            area.appendChild(flying);\n            \n            flying.offsetHeight;\n            \n            flying.style.left = `${cellRect.left - areaRect.left + (cellRect.width - 60) / 2}px`;\n            flying.style.top = `${cellRect.top - areaRect.top + (cellRect.height - 60) / 2}px`;\n            \n            await sleep(200);\n            \n            if (!isMatch) {\n                flying.style.transition = 'all 200ms ease-in';\n                if (type === 'top') {\n                    flying.style.top = `${cellRect.top - areaRect.top - 20}px`;\n                } else {\n                    flying.style.left = `${cellRect.left - areaRect.left + 20}px`;\n                }\n                flying.style.opacity = '0';\n                await sleep(200);\n            }\n            \n            flying.remove();\n        }\n\n        function spawnParticles(x, y, color) {\n            for (let i = 0; i < 12; i++) {\n                const angle = (Math.PI * 2 * i) / 12 + Math.random() * 0.5;\n                const speed = 150 + Math.random() * 200;\n                particles.push({\n                    x, y,\n                    vx: Math.cos(angle) * speed,\n                    vy: Math.sin(angle) * speed,\n                    life: 1,\n                    color,\n                    size: 4 + Math.random() * 6\n                });\n            }\n        }\n\n        function checkGameState() {\n            // Check if all balloons popped\n            let balloonsLeft = 0;\n            for (let r = 0; r < 10; r++) {\n                for (let c = 0; c < 10; c++) {\n                    if (grid[r][c]) balloonsLeft++;\n                }\n            }\n\n            if (balloonsLeft === 0) {\n                gameOver = true;\n                showVictory();\n                return;\n            }\n\n            // Check if any darts remaining\n            let dartsLeft = false;\n            for (let i = 0; i < 10; i++) {\n                if (!topDartsUsed[i]) dartsLeft = true;\n                if (!rightDartsUsed[i]) dartsLeft = true;\n            }\n\n            if (!dartsLeft) {\n                gameOver = true;\n                showDefeat();\n            }\n        }\n\n        async function showVictory() {\n            stopTimer();\n            playSound('victory_sound');\n            const levelData = getLevelData();\n            const optimal = levelData.optimalMoves || 7;\n            let stars = 1;\n            if (moves <= optimal) stars = 3;\n            else if (moves <= optimal + 3) stars = 2;\n\n            // Calculate score for this level\n            const baseScore = 100;\n            const timeBonus = timeLeft * 10;\n            let unusedDartsCount = 0;\n            for (let i = 0; i < 10; i++) {\n                if (topDarts[i] && !topDartsUsed[i]) unusedDartsCount++;\n                if (rightDarts[i] && !rightDartsUsed[i]) unusedDartsCount++;\n            }\n            const dartBonus = unusedDartsCount * 50;\n            const levelScore = baseScore + timeBonus + dartBonus;\n            \n            // Cumulative score\n            const totalScore = sessionScore + levelScore;\n            sessionScore += levelScore;\n\n            const overlay = document.getElementById('overlay');\n            const card = document.getElementById('overlay-card');\n            \n            const isLastLevel = !config.levels || currentLevelIndex >= config.levels.length - 1;\n\n            // Initial victory display\n            card.innerHTML = `\n                <div class=\"overlay-title\">\ud83c\udf89 Victory!</div>\n                <div class=\"stars\">${'\u2b50'.repeat(stars)}${'\u2606'.repeat(3 - stars)}</div>\n                <div class=\"overlay-subtitle\">Level ${currentLevelIndex + 1} Cleared!</div>\n                <div class=\"overlay-subtitle\">Level Score: ${levelScore}</div>\n                <div class=\"overlay-subtitle\" style=\"color: #5352ED; font-weight: bold;\">Total Score: ${totalScore}</div>\n                <div style=\"margin: 20px 0; color: #666;\">Submitting to leaderboard...</div>\n            `;\n            overlay.classList.add('visible');\n\n            // Submit total score to leaderboard\n            try {\n                const response = await lib.addPlayerScoreToLeaderboard(totalScore, 10);\n                \n                let leaderboardHTML = '';\n                if (response.success) {\n                    leaderboardHTML = '<div style=\"margin-top: 24px; max-height: 250px; overflow-y: auto;\">';\n                    leaderboardHTML += '<h3 style=\"font-family: \\'Fredoka\\', sans-serif; color: #2C3E50; margin-bottom: 16px;\">\ud83c\udfc6 Top 10 Leaderboard</h3>';\n                    \n                    if (response.userRank !== null) {\n                        leaderboardHTML += `<p style=\"font-size: 18px; color: #5352ED; font-weight: 600; margin-bottom: 12px;\">Your Rank: #${response.userRank}</p>`;\n                    }\n                    \n                    leaderboardHTML += '<div style=\"text-align: left;\">';\n                    response.entries.forEach((entry, index) => {\n                        leaderboardHTML += `\n                            <div style=\"display: flex; align-items: center; padding: 8px; background: ${index % 2 === 0 ? '#f5f5f5' : 'white'}; border-radius: 8px; margin-bottom: 4px;\">\n                                <div style=\"font-weight: bold; width: 30px; color: #666;\">#${index + 1}</div>\n                                <img src=\"${entry.profilePicture}\" alt=\"${entry.username}\" style=\"width: 32px; height: 32px; border-radius: 50%; margin: 0 12px;\" onerror=\"this.style.display='none'\">\n                                <div style=\"flex: 1; font-weight: 500;\">${entry.username}</div>\n                                <div style=\"font-weight: bold; color: #5352ED;\">${entry.score}</div>\n                            </div>\n                        `;\n                    });\n                    leaderboardHTML += '</div></div>';\n                }\n\n                const actionBtn = isLastLevel \n                    ? `<button class=\"btn\" style=\"margin-top: 20px;\" onpointerdown=\"document.getElementById('overlay').classList.remove('visible'); restartGameGlobal(true);\">Play Again</button>`\n                    : `<button class=\"btn\" style=\"margin-top: 20px;\" onpointerdown=\"document.getElementById('overlay').classList.remove('visible'); window.nextLevel();\">Next Level</button>`;\n\n                card.innerHTML = `\n                    <div class=\"overlay-title\">\ud83c\udf89 Victory!</div>\n                    <div class=\"stars\">${'\u2b50'.repeat(stars)}${'\u2606'.repeat(3 - stars)}</div>\n                    <div class=\"overlay-subtitle\">Level ${currentLevelIndex + 1} Cleared!</div>\n                    <div class=\"overlay-subtitle\">Level Score: ${levelScore}</div>\n                    <div class=\"overlay-subtitle\" style=\"color: #5352ED; font-weight: bold;\">Total Score: ${totalScore}</div>\n                    ${leaderboardHTML}\n                    ${actionBtn}\n                `;\n            } catch (error) {\n                lib.log(`Failed to submit score: ${error.message}`);\n                const actionBtn = isLastLevel \n                    ? `<button class=\"btn\" style=\"margin-top: 20px;\" onpointerdown=\"document.getElementById('overlay').classList.remove('visible'); restartGameGlobal(true);\">Play Again</button>`\n                    : `<button class=\"btn\" style=\"margin-top: 20px;\" onpointerdown=\"document.getElementById('overlay').classList.remove('visible'); window.nextLevel();\">Next Level</button>`;\n                \n                card.innerHTML = `\n                    <div class=\"overlay-title\">\ud83c\udf89 Victory!</div>\n                    <div class=\"stars\">${'\u2b50'.repeat(stars)}${'\u2606'.repeat(3 - stars)}</div>\n                    <div class=\"overlay-subtitle\">Level ${currentLevelIndex + 1} Cleared!</div>\n                    <div class=\"overlay-subtitle\">Level Score: ${levelScore}</div>\n                    <div class=\"overlay-subtitle\" style=\"color: #5352ED; font-weight: bold;\">Total Score: ${totalScore}</div>\n                    ${actionBtn}\n                `;\n            }\n        }\n\n        window.nextLevel = async () => {\n            await triggerGameStart();\n            if (config.levels && currentLevelIndex < config.levels.length - 1) {\n                currentLevelIndex++;\n                restartGame();\n                buildUI();\n                renderAll();\n            }\n        };\n\n        function showDefeat(isTimeUp = false) {\n            stopTimer();\n            gameOver = true;\n            const overlay = document.getElementById('overlay');\n            const card = document.getElementById('overlay-card');\n            const title = isTimeUp ? \"\u23f0 Time's Up!\" : \"\ud83d\ude05 No Darts Left!\";\n            const subtitle = isTimeUp ? \"You ran out of time. Try to be faster!\" : \"Some balloons remain. Try again!\";\n            \n            card.innerHTML = `\n                <div class=\"overlay-title\">${title}</div>\n                <div class=\"overlay-subtitle\">${subtitle}</div>\n                <button class=\"btn\" onpointerdown=\"event.preventDefault(); document.getElementById('overlay').classList.remove('visible'); restartGameGlobal();\" ontouchstart=\"event.preventDefault(); document.getElementById('overlay').classList.remove('visible'); restartGameGlobal();\">Retry</button>\n            `;\n            overlay.classList.add('visible');\n        }\n\n        function restartGame(resetToFirst = false) {\n            triggerGameStart();\n            if (resetToFirst) {\n                currentLevelIndex = 0;\n                sessionScore = 0;\n            }\n            \n            stopTimer();\n            timeLeft = 300;\n            updateTimerDisplay();\n            \n            // Remove all blocked darts\n            blockedDarts.forEach(d => {\n                if (d.element) d.element.remove();\n            });\n            blockedDarts = [];\n            \n            // Clean up any orphaned flying dart elements\n            const area = document.querySelector('.game-area');\n            if (area) {\n                area.querySelectorAll('.flying-dart-element').forEach(el => el.remove());\n            }\n            \n            const levelData = getLevelData();\n            grid = JSON.parse(JSON.stringify(levelData.grid));\n            topDarts = [...levelData.topDarts];\n            rightDarts = [...levelData.rightDarts];\n            topDartsUsed = new Array(10).fill(false);\n            rightDartsUsed = new Array(10).fill(false);\n            moves = 0;\n            animating = false;\n            gameOver = false;\n            particles = [];\n            document.getElementById('overlay').classList.remove('visible');\n            renderAll();\n            \n            // Restart timer if in play mode\n            if (mode === 'play') {\n                startTimer();\n            }\n        }\n\n        void (window.restartGameGlobal = restartGame);\n\n        function updateTimerDisplay() {\n            const timerEl = document.getElementById('timer-display');\n            const progressEl = document.getElementById('progress-bar');\n            if (!timerEl || !progressEl) return;\n            \n            const mins = Math.floor(timeLeft / 60);\n            const secs = timeLeft % 60;\n            timerEl.textContent = `Remaining time: ${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;\n            \n            const totalTime = 300;\n            const percentage = (timeLeft / totalTime) * 100;\n            progressEl.style.width = `${percentage}%`;\n\n            if (timeLeft <= 60) {\n                // Critical: last minute\n                progressEl.style.background = 'var(--color-red)';\n                if (timeLeft <= 30) {\n                    timerEl.style.animation = 'pulse 1s infinite';\n                }\n            } else if (timeLeft <= 150) {\n                // Warning: less than 50%\n                progressEl.style.background = 'var(--color-yellow)';\n                timerEl.style.animation = 'none';\n            } else {\n                // Safe\n                progressEl.style.background = 'var(--color-green)';\n                timerEl.style.animation = 'none';\n            }\n        }\n\n        function startTimer() {\n            if (timerInterval || mode !== 'play') return;\n            timerInterval = setInterval(() => {\n                if (gameOver) {\n                    stopTimer();\n                    return;\n                }\n                timeLeft--;\n                updateTimerDisplay();\n                if (timeLeft <= 0) {\n                    stopTimer();\n                    showDefeat(true); // true means time's up\n                }\n            }, 1000);\n        }\n\n        function stopTimer() {\n            if (timerInterval) {\n                clearInterval(timerInterval);\n                timerInterval = null;\n            }\n        }\n\n        function sleep(ms) {\n            return new Promise(r => { setTimeout(r, ms); });\n        }\n\n        // Event handling for play mode\n        function setupPlayEvents() {\n            handleDartClick = (e, type, index) => {\n                if (animating || gameOver || menuOpen) return;\n                if (type === 'top') shootTop(index);\n                else if (type === 'right') shootRight(index);\n            };\n\n            handleCellClick = (e, row, col) => {\n                if (animating || gameOver || menuOpen) return;\n                \n                // Check for blocked darts at this location\n                if (blockedDarts.length > 0) {\n                    const clickedDart = blockedDarts.find(d => {\n                        const clickedCellIsDartCell = row === d.r && col === d.c;\n                        return e.target === d.element || clickedCellIsDartCell;\n                    });\n\n                    if (clickedDart) {\n                        e.stopPropagation();\n                        const { color, direction, r, c } = clickedDart;\n                        if (direction === 'down') {\n                            shootTop(c, r + 1, clickedDart);\n                        } else if (direction === 'left') {\n                            shootRight(r, c - 1, clickedDart);\n                        }\n                    }\n                }\n            };\n\n            // Keyboard\n            document.addEventListener('keydown', (e) => {\n                if (animating || gameOver || menuOpen || mode !== 'play') return;\n                const topKeys = ['1','2','3','4','5','6','7','8','9','0'];\n                const topAlt = ['q','w','e','r','t','y','u','i','o','p'];\n                const rightKeys = ['a','s','d','f','g','h','j','k','l',';'];\n\n                const k = e.key.toLowerCase();\n                if (topKeys.includes(k)) shootTop(topKeys.indexOf(k));\n                else if (topAlt.includes(k)) shootTop(topAlt.indexOf(k));\n                else if (rightKeys.includes(k)) shootRight(rightKeys.indexOf(k));\n            });\n        }\n\n        // Edit mode\n        let selectedColor = 'red';\n\n        function setupEditMode() {\n            const palette = document.getElementById('edit-palette');\n            const colors = ['red', 'blue', 'yellow', 'green', 'purple', 'pink', 'tiffany', 'orange', null];\n            const labels = ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Pink', 'Tiffany', 'Orange', 'Empty'];\n\n            colors.forEach((color, i) => {\n                const btn = document.createElement('div');\n                btn.className = 'color-btn' + (color === selectedColor ? ' selected' : '') + (!color ? ' empty-btn' : '');\n                if (color) btn.style.background = colorMap[color].hex;\n                btn.title = labels[i];\n                bindInteraction(btn, (e) => {\n                    selectedColor = color;\n                    palette.querySelectorAll('.color-btn').forEach(b => b.classList.remove('selected'));\n                    btn.classList.add('selected');\n                });\n                palette.appendChild(btn);\n            });\n\n            handleCellClick = (e, row, col) => {\n                // Update Config\n                if (config.levels && config.levels[currentLevelIndex]) {\n                    const targetLevel = config.levels[currentLevelIndex];\n                    targetLevel.grid[row][col] = selectedColor;\n                    \n                    // Break references and trigger proxy\n                    targetLevel.grid[row] = [...targetLevel.grid[row]];\n                    targetLevel.grid = [...targetLevel.grid];\n                    config.levels[currentLevelIndex] = targetLevel;\n                    config.levels = [...config.levels];\n                } else {\n                    config.grid[row][col] = selectedColor;\n                    config.grid[row] = [...config.grid[row]];\n                    config.grid = [...config.grid];\n                }\n                \n                // Update Local State\n                grid[row][col] = selectedColor;\n                renderGrid();\n            };\n\n            handleDartClick = (e, type, idx) => {\n                if (config.levels && config.levels[currentLevelIndex]) {\n                    const targetLevel = config.levels[currentLevelIndex];\n                    if (type === 'top') {\n                        targetLevel.topDarts[idx] = selectedColor;\n                        targetLevel.topDarts = [...targetLevel.topDarts];\n                        topDarts[idx] = selectedColor;\n                    } else {\n                        targetLevel.rightDarts[idx] = selectedColor;\n                        targetLevel.rightDarts = [...targetLevel.rightDarts];\n                        rightDarts[idx] = selectedColor;\n                    }\n                    config.levels[currentLevelIndex] = targetLevel;\n                    config.levels = [...config.levels];\n                } else {\n                    if (type === 'top') {\n                        config.topDarts[idx] = selectedColor;\n                        config.topDarts = [...config.topDarts];\n                        topDarts[idx] = selectedColor;\n                    } else {\n                        config.rightDarts[idx] = selectedColor;\n                        config.rightDarts = [...config.rightDarts];\n                        rightDarts[idx] = selectedColor;\n                    }\n                }\n                \n                if (type === 'top') renderTopDarts();\n                else renderRightDarts();\n            };\n        }\n\n        // Particle rendering\n        const pCanvas = document.getElementById('particle-canvas');\n        const pCtx = pCanvas.getContext('2d');\n        let lastTime = performance.now();\n\n        function particleLoop(timestamp) {\n            const dt = Math.min((timestamp - lastTime) / 1000, 0.11);\n            lastTime = timestamp;\n\n            pCtx.clearRect(0, 0, pCanvas.width, pCanvas.height);\n\n            for (let i = particles.length - 1; i >= 0; i--) {\n                const p = particles[i];\n                p.x += p.vx * dt;\n                p.y += p.vy * dt;\n                p.vy += 600 * dt; // gravity\n                p.life -= dt * 2;\n                if (p.life <= 0) {\n                    particles.splice(i, 1);\n                    continue;\n                }\n                pCtx.globalAlpha = p.life;\n                pCtx.fillStyle = p.color;\n                pCtx.fillRect(p.x - p.size/2, p.y - p.size/2, p.size, p.size);\n            }\n            pCtx.globalAlpha = 1;\n            requestAnimationFrame(particleLoop);\n        }\n\n        // Initialize\n        if (mode === 'play') {\n            const status = document.getElementById('loading-status');\n            if (status) {\n                status.innerHTML = `<button id=\"play-btn\" class=\"btn\" style=\"padding: 24px 80px; font-size: 36px; border-radius: 20px; box-shadow: 0 10px 20px rgba(0,0,0,0.2); animation: pulse 2s infinite;\">PLAY</button>`;\n                const playBtn = document.getElementById('play-btn');\n                bindInteraction(playBtn, () => {\n                    const screen = document.getElementById('loading-screen');\n                    if (screen) screen.remove();\n                    startGameDirect();\n                });\n            } else {\n                startGameDirect();\n            }\n        } else {\n            buildUI();\n            renderAll();\n            setupEditMode();\n            requestAnimationFrame(particleLoop);\n        }\n    }\n    </script>\n</body>\n</html>", "game_config": "{\"grid\":[[null,null,\"red\",\"red\",\"purple\",\"purple\",\"purple\",null,null,null],[null,null,\"red\",\"red\",\"purple\",\"purple\",\"purple\",null,null,null],[null,null,\"red\",\"red\",\"purple\",\"purple\",\"purple\",null,null,null],[\"blue\",\"blue\",\"red\",\"blue\",\"blue\",\"purple\",\"purple\",\"blue\",\"blue\",\"blue\"],[\"red\",\"red\",\"red\",\"yellow\",\"purple\",\"purple\",\"purple\",\"red\",\"red\",\"red\"],[\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"purple\",\"purple\",\"purple\",\"yellow\",\"yellow\",\"yellow\"],[\"red\",\"red\",\"red\",\"red\",\"purple\",\"purple\",\"purple\",\"red\",\"red\",\"red\"],[\"red\",\"red\",\"yellow\",\"green\",\"purple\",\"purple\",\"purple\",\"yellow\",\"yellow\",\"yellow\"],[null,null,\"red\",\"red\",\"purple\",\"purple\",\"purple\",null,null,null],[null,null,\"red\",\"red\",\"purple\",\"purple\",\"purple\",null,null,null]],\"topDarts\":[null,null,\"red\",\"red\",\"purple\",\"purple\",\"purple\",null,null,null],\"rightDarts\":[null,null,null,\"blue\",\"yellow\",\"blue\",\"red\",\"yellow\",null,null],\"optimalMoves\":20,\"levels\":[{\"grid\":[[\"yellow\",\"yellow\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"yellow\",\"yellow\"],[\"yellow\",\"yellow\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"yellow\",\"yellow\"],[\"purple\",\"purple\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"purple\",\"purple\"],[\"red\",\"red\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"red\",\"red\"],[\"red\",\"red\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"red\",\"red\"],[\"red\",\"red\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"red\",\"red\"],[\"red\",\"red\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"red\",\"red\"],[\"purple\",\"purple\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"purple\",\"purple\"],[\"yellow\",\"yellow\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"yellow\",\"yellow\"],[\"yellow\",\"yellow\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"yellow\",\"yellow\"]],\"topDarts\":[\"yellow\",\"yellow\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"yellow\",\"yellow\"],\"rightDarts\":[\"yellow\",\"yellow\",\"purple\",\"red\",\"red\",\"red\",\"red\",\"purple\",\"yellow\",\"yellow\"],\"optimalMoves\":20},{\"grid\":[[\"blue\",\"green\",\"blue\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\"],[\"blue\",\"green\",\"blue\",\"red\",\"red\",\"purple\",\"purple\",\"purple\",\"purple\",\"purple\"],[\"blue\",\"green\",\"blue\",\"red\",\"red\",\"red\",\"red\",\"green\",\"green\",\"green\"],[\"blue\",\"green\",\"blue\",\"red\",\"red\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\"],[\"blue\",\"green\",\"blue\",\"green\",\"green\",\"green\",\"red\",\"green\",\"green\",\"green\"],[\"blue\",\"purple\",\"purple\",\"purple\",\"purple\",\"purple\",\"red\",\"purple\",\"purple\",\"purple\"],[\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"red\",\"yellow\",\"yellow\",\"yellow\"],[\"blue\",\"green\",\"blue\",\"red\",\"red\",\"red\",\"red\",\"red\",\"red\",\"red\"],[\"blue\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"blue\",\"blue\",\"blue\",\"blue\"],[\"blue\",\"green\",\"blue\",\"purple\",\"purple\",\"purple\",\"purple\",\"purple\",\"purple\",\"purple\"]],\"topDarts\":[\"blue\",\"green\",\"blue\",\"red\",\"red\",\"red\",\"red\",\"yellow\",\"green\",\"purple\"],\"rightDarts\":[\"yellow\",\"purple\",\"green\",\"yellow\",\"green\",\"purple\",\"yellow\",\"red\",\"blue\",\"purple\"],\"optimalMoves\":20},{\"grid\":[[\"red\",\"blue\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"green\",\"green\",\"blue\",\"yellow\"],[\"red\",\"blue\",\"red\",\"red\",\"purple\",\"red\",\"red\",\"red\",\"blue\",\"red\"],[\"red\",\"blue\",\"yellow\",\"green\",\"purple\",\"green\",\"green\",\"green\",\"blue\",\"green\"],[\"purple\",\"blue\",\"purple\",\"purple\",\"purple\",\"purple\",\"green\",\"green\",\"blue\",\"red\"],[\"red\",\"blue\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"green\",\"yellow\",\"yellow\"],[\"red\",\"blue\",\"yellow\",\"green\",\"purple\",\"blue\",\"blue\",\"blue\",\"blue\",\"red\"],[\"red\",\"red\",\"red\",\"red\",\"purple\",\"red\",\"red\",\"green\",\"red\",\"red\"],[\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"yellow\",\"red\"],[\"red\",\"blue\",\"yellow\",\"green\",\"purple\",\"yellow\",\"green\",\"green\",\"yellow\",\"yellow\"],[\"green\",\"blue\",\"green\",\"green\",\"purple\",\"green\",\"green\",\"green\",\"blue\",\"red\"]],\"topDarts\":[\"red\",\"blue\",\"yellow\",\"green\",\"purple\",\"red\",\"green\",\"green\",\"blue\",\"red\"],\"rightDarts\":[\"yellow\",\"red\",\"green\",\"purple\",\"yellow\",\"blue\",\"red\",\"yellow\",\"yellow\",\"green\"],\"optimalMoves\":20},{\"grid\":[[\"red\",\"red\",\"blue\",\"blue\",\"yellow\",\"yellow\",\"green\",\"green\",\"purple\",\"purple\"],[\"red\",\"red\",\"blue\",\"blue\",\"yellow\",\"yellow\",\"green\",\"green\",\"purple\",\"purple\"],[\"red\",\"red\",\"blue\",\"blue\",\"yellow\",\"yellow\",\"green\",\"green\",\"purple\",\"purple\"],[\"red\",\"red\",\"blue\",\"blue\",\"yellow\",\"yellow\",\"green\",\"green\",\"purple\",\"purple\"],[\"red\",\"red\",\"blue\",\"blue\",\"yellow\",\"yellow\",\"green\",\"green\",\"purple\",\"purple\"],[\"red\",\"red\",\"red\",\"red\",\"red\",\"red\",\"red\",\"red\",\"red\",\"red\"],[\"blue\",\"blue\",\"blue\",\"blue\",\"blue\",\"blue\",\"blue\",\"blue\",\"blue\",\"blue\"],[\"red\",\"red\",\"blue\",\"blue\",\"yellow\",\"yellow\",\"green\",\"green\",\"purple\",\"purple\"],[\"red\",\"red\",\"blue\",\"blue\",\"yellow\",\"yellow\",\"green\",\"green\",\"purple\",\"purple\"],[\"red\",\"red\",\"blue\",\"blue\",\"yellow\",\"yellow\",\"green\",\"green\",\"purple\",\"purple\"]],\"topDarts\":[\"red\",\"red\",\"blue\",\"blue\",\"yellow\",\"yellow\",\"green\",\"green\",\"purple\",\"purple\"],\"rightDarts\":[null,null,null,null,null,\"red\",\"blue\",\"yellow\",\"green\",\"purple\"],\"optimalMoves\":15},{\"grid\":[[\"tiffany\",\"pink\",\"pink\",\"pink\",\"red\",\"red\",\"pink\",\"pink\",\"pink\",\"pink\"],[\"tiffany\",\"yellow\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"green\",\"yellow\",\"tiffany\"],[\"tiffany\",\"yellow\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"green\",\"yellow\",\"tiffany\"],[\"tiffany\",\"yellow\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"green\",\"yellow\",\"tiffany\"],[\"purple\",\"yellow\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"green\",\"yellow\",\"purple\"],[\"purple\",\"yellow\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"green\",\"yellow\",\"purple\"],[\"tiffany\",\"yellow\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"green\",\"yellow\",\"tiffany\"],[\"tiffany\",\"yellow\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"green\",\"yellow\",\"tiffany\"],[\"tiffany\",\"yellow\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"green\",\"yellow\",\"tiffany\"],[\"tiffany\",\"pink\",\"pink\",\"pink\",\"red\",\"red\",\"pink\",\"pink\",\"pink\",\"pink\"]],\"topDarts\":[\"tiffany\",\"yellow\",\"green\",\"blue\",\"red\",\"red\",\"blue\",\"green\",\"yellow\",\"tiffany\"],\"rightDarts\":[\"pink\",\"blue\",\"green\",\"yellow\",\"purple\",\"purple\",\"yellow\",\"green\",\"blue\",\"pink\"],\"optimalMoves\":14},{\"grid\":[[\"orange\",\"orange\",\"pink\",\"pink\",\"tiffany\",\"tiffany\",\"orange\",\"orange\",\"pink\",\"pink\"],[\"orange\",\"orange\",\"pink\",\"pink\",\"tiffany\",\"tiffany\",\"orange\",\"orange\",\"pink\",\"pink\"],[\"tiffany\",\"tiffany\",\"pink\",\"pink\",\"tiffany\",\"tiffany\",\"orange\",\"orange\",\"pink\",\"pink\"],[\"tiffany\",\"tiffany\",\"pink\",\"pink\",\"tiffany\",\"tiffany\",\"orange\",\"orange\",\"pink\",\"pink\"],[\"orange\",\"orange\",\"orange\",\"orange\",\"tiffany\",\"tiffany\",\"orange\",\"orange\",\"pink\",\"pink\"],[\"orange\",\"orange\",\"orange\",\"orange\",\"tiffany\",\"tiffany\",\"orange\",\"orange\",\"pink\",\"pink\"],[\"pink\",\"pink\",\"pink\",\"pink\",\"pink\",\"pink\",\"orange\",\"orange\",\"pink\",\"pink\"],[\"pink\",\"pink\",\"pink\",\"pink\",\"pink\",\"pink\",\"orange\",\"orange\",\"pink\",\"pink\"],[\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"pink\",\"pink\"],[\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"tiffany\",\"pink\",\"pink\"]],\"topDarts\":[\"orange\",\"orange\",\"pink\",\"pink\",\"tiffany\",\"tiffany\",\"orange\",\"orange\",\"pink\",\"pink\"],\"rightDarts\":[null,null,\"tiffany\",\"tiffany\",\"orange\",\"orange\",\"pink\",\"pink\",\"tiffany\",\"tiffany\"],\"optimalMoves\":18}],\"currentLevelIndex\":\"4\"}", "game_design_document": "## Core Game Loop and Mechanics\n\n**Balloon Dart Pop** is a color-matching puzzle game with a unique directional mechanic. Players must strategically use darts positioned around the edges of the game board to pop all balloons.\n\n### Moment-to-Moment Gameplay\n1. Player observes the grid of colorful balloons (5x5 grid)\n2. Player taps/clicks a dart from the top or right edges\n3. The dart shoots toward the balloons and pops all adjacent balloons of matching color\n4. Popped balloons disappear, remaining balloons stay in place\n5. Player continues selecting darts until all balloons are popped or no valid moves remain\n\n### Core Mechanics\n- **Color Matching**: Darts only pop balloons that match their color\n- **Directional Shooting**: \n  - Top darts shoot downward into the top row\n  - Right darts shoot leftward into the rightmost column\n- **Chain Reactions**: When a balloon pops, it affects all orthogonally adjacent balloons of the same color (they pop too), creating satisfying cascades\n- **Limited Darts**: Each dart can only be used once, adding strategic depth\n- **Move Counter**: Track number of moves to encourage efficiency\n\n### Progression and Engagement\n- Players must think ahead to maximize each dart's impact\n- Finding the optimal move order creates satisfying \"aha!\" moments\n- Chain reactions provide visual and audio feedback rewards\n- Star rating system (1-3 stars) based on moves used encourages replayability\n\n### Win/Lose Conditions\n- **Win**: All balloons are popped\n- **Lose**: Balloons remain but no darts are available\n- **Star Rating**:\n  - 3 stars: Complete in optimal moves or fewer\n  - 2 stars: Complete in optimal + 3 moves\n  - 1 star: Complete in any number of moves\n\n### What Makes It Compelling\n- Easy to understand, challenging to master\n- Satisfying pop animations and sound effects\n- Strategic depth through dart positioning and color matching\n- Quick gameplay sessions (1-2 minutes per level)\n- Visual feedback makes successful chains extremely satisfying\n\n## Starting Scope\n\n### First Iteration (Implement Now)\n- **Single Level**: 5x5 grid with 4 colors (red, blue, yellow, green)\n- **Basic Mechanics**: \n  - Dart selection and shooting\n  - Color matching and adjacent balloon popping\n  - Chain reaction system\n  - Win/lose detection\n- **UI Elements**:\n  - Move counter\n  - Restart button\n  - Victory screen with star rating\n  - Simple instructions overlay\n- **Visual Polish**:\n  - Smooth pop animations\n  - Particle effects for popped balloons\n  - Dart shooting animation\n- **Audio**:\n  - Pop sound effects\n  - Victory sound\n  - Background music\n\n### Future Iterations (Later Development)\n- Multiple levels with increasing difficulty\n- Level selection screen\n- Special balloon types (bombs, rainbow balloons)\n- Power-ups and special darts\n- More complex grid layouts (6x6, 7x7)\n- Obstacles (unbreakable balloons)\n- Time-based challenges\n\n## Game Perspective\n\n**Fixed-screen** - The entire game board is visible at once with no scrolling. This perspective is ideal for puzzle games as it allows players to see all available moves and plan their strategy.\n\nThe game uses a portrait orientation (720x1280) with:\n- Top section: Darts positioned above the grid\n- Center section: 5x5 balloon grid\n- Right section: Darts positioned to the right of the grid\n- Bottom section: UI elements (move counter, restart button)\n\n## Game World and Level Design\n\n### Visual Layout\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502     [D] [D] [D] [D] [D]         \u2502 \u2190 Top darts (shoot down)\n\u2502                                 \u2502\n\u2502     \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510         \u2502\n\u2502     \u2502 B  R  Y  G  B  \u2502 [D]     \u2502\n\u2502     \u2502 Y  G  B  R  Y  \u2502 [D]     \u2502 \u2190 Right darts (shoot left)\n\u2502     \u2502 R  B  G  Y  R  \u2502 [D]     \u2502\n\u2502     \u2502 G  Y  R  B  G  \u2502 [D]     \u2502\n\u2502     \u2502 B  R  Y  G  B  \u2502 [D]     \u2502\n\u2502     \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518         \u2502\n\u2502                                 \u2502\n\u2502     Moves: 0                    \u2502\n\u2502     [Restart]                   \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n### First Level Design\n- **Grid Size**: 5x5 (25 balloons total)\n- **Colors**: 4 colors (Red, Blue, Yellow, Green)\n- **Dart Distribution**: \n  - 5 darts on top (one per column)\n  - 5 darts on right (one per row)\n  - Colors distributed to ensure level is solvable\n- **Optimal Solution**: 6-8 moves\n- **Difficulty**: Easy - teaches the basic mechanics\n\n### Level Configuration\nThe level layout is stored in gameConfig with:\n- Balloon grid (5x5 array of color codes)\n- Top darts array (5 darts with colors)\n- Right darts array (5 darts with colors)\n- Optimal move count for star rating\n\nExample structure:\n```javascript\n{\n  grid: [\n    ['red', 'blue', 'yellow', 'green', 'blue'],\n    ['yellow', 'green', 'blue', 'red', 'yellow'],\n    ['red', 'blue', 'green', 'yellow', 'red'],\n    ['green', 'yellow', 'red', 'blue', 'green'],\n    ['blue', 'red', 'yellow', 'green', 'blue']\n  ],\n  topDarts: ['red', 'blue', 'yellow', 'green', 'blue'],\n  rightDarts: ['yellow', 'red', 'green', 'blue', 'yellow'],\n  optimalMoves: 7\n}\n```\n\n## Edit Mode Design\n\n### Editable Properties\n\n**Level Configuration**:\n- **Balloon Grid**: Users can change the color of each balloon in the 5x5 grid\n  - Color picker for each cell\n  - Options: Red, Blue, Yellow, Green, or Empty\n  - Visual grid editor with drag-to-paint functionality\n\n- **Dart Colors**: Users can modify the color of each dart\n  - Top darts (5 darts)\n  - Right darts (5 darts)\n  - Same color options as balloons\n\n- **Optimal Move Count**: Adjustable number for star rating calculation\n  - Range: 1-20 moves\n  - Affects 3-star threshold\n\n**Visual Customization**:\n- **Balloon Colors**: RGB values for each of the 4 color types\n  - Red balloon color\n  - Blue balloon color\n  - Yellow balloon color\n  - Green balloon color\n\n- **Background Color**: Single color picker for game background\n\n### Edit Mode UI\n- **Grid Editor**: \n  - Tap balloon to cycle through colors\n  - Color palette at bottom for quick selection\n  - Clear all button\n  - Randomize button (generates solvable layout)\n\n- **Dart Editor**:\n  - Separate sections for top and right darts\n  - Tap dart to cycle through colors\n  - Visual indicators showing dart positions\n\n- **Settings Panel**:\n  - Optimal moves slider\n  - Color customization section\n  - Test level button (switches to play mode)\n\n### Constraints\n- **Grid Size**: Fixed at 5x5 (cannot be changed to maintain game balance)\n- **Dart Count**: Always 5 top + 5 right (one per row/column)\n- **Color Limit**: Maximum 4 different colors to prevent overwhelming complexity\n- **Minimum Balloons**: At least 5 balloons must be present\n- **Solvability**: No automatic validation (users responsible for creating solvable levels)\n\n## Control Design\n\n### Mobile (Touch) Controls\n- **Dart Selection**: Tap on any dart to select and shoot it\n  - Visual feedback: dart highlights on touch\n  - Dart shoots immediately on tap\n- **Edit Mode**: \n  - Tap balloons to cycle colors\n  - Tap darts to change colors\n  - Tap UI buttons (restart, settings)\n- **Gestures**: No complex gestures needed - simple taps only\n\n### Desktop (Keyboard/Mouse) Controls\n- **Mouse**:\n  - Click on darts to shoot\n  - Click on UI buttons\n  - Edit mode: click to change colors\n- **Keyboard**:\n  - Number keys 1-5: Select top darts (columns 1-5)\n  - Q, W, E, R, T: Alternative for top darts\n  - A, S, D, F, G: Select right darts (rows 1-5)\n  - R key: Restart level\n  - ESC: Open/close menu\n\n### Control Implementation\n- **Touch Tracking**: Track touch IDs to prevent multi-touch interference\n- **Visual Feedback**: \n  - Hover state for darts (desktop)\n  - Pressed state for darts (mobile)\n  - Disabled state for used darts\n- **Input Prevention**: Disable input during animations\n- **Responsive Hit Areas**: Darts have minimum 96x96px touch targets\n\n### Control Flow\n1. Player selects dart (tap/click)\n2. Dart animates shooting toward grid\n3. Matching balloons pop with animation\n4. Chain reaction processes\n5. Input re-enabled after animations complete\n6. Repeat until win/lose condition\n\n## Art Style and Visual Theme\n\n### Overall Style\n**Colorful Cartoon Casual** - Bright, cheerful, and approachable with smooth gradients and playful shapes. The art style should feel welcoming to all ages while maintaining visual clarity for gameplay.\n\n### Color Palette\n- **Balloons**: Vibrant, saturated colors with subtle gradients\n  - Red: #FF4757 to #FF6B81\n  - Blue: #3742FA to #5F6FFF\n  - Yellow: #FFA502 to #FFBE33\n  - Green: #2ED573 to #7BED9F\n- **Background**: Soft gradient from light blue (#E3F2FD) to white\n- **UI Elements**: Clean white with subtle shadows\n- **Darts**: Metallic silver/gray with colored tips matching balloon colors\n\n### Visual Elements\n\n**Balloons**:\n- Round, slightly glossy appearance\n- Subtle shine/highlight on upper-left\n- Small string/knot at bottom\n- Size: Approximately 80x80px in grid\n- Transparent background for assets\n\n**Darts**:\n- Simple dart shape with pointed tip\n- Metallic body with colored tip\n- Size: Approximately 60x60px\n- Oriented correctly for direction (down or left)\n\n**Particles**:\n- Small confetti-like pieces when balloons pop\n- Match the balloon color\n- Rendered with code (Canvas)\n\n**UI Elements**:\n- Rounded rectangles for buttons\n- Clean sans-serif font (system font)\n- Drop shadows for depth\n- Rendered with CSS\n\n### Animation Style\n- **Smooth and Bouncy**: Ease-out animations for satisfying feedback\n- **Pop Animation**: Balloons scale up slightly then disappear with particle burst\n- **Dart Shooting**: Quick linear movement with slight trail effect\n- **Victory**: Confetti explosion and scale-up animation\n\n### Asset vs Code Rendering\n\n**Generate Assets For**:\n- Individual balloon images (4 colors)\n- Dart images (4 colored tips + neutral body)\n- Background pattern/texture (optional)\n\n**Render with Code**:\n- Grid layout (CSS Grid)\n- Particle effects (Canvas)\n- UI buttons and text (CSS/HTML)\n- Animations and transitions (CSS/JS)\n- Move counter and score display\n\n### Consistency Notes\n- All balloons should have the same art style and viewing angle (straight-on view)\n- Darts should match in style but be clearly distinguishable from balloons\n- Maintain consistent lighting direction across all assets\n- Keep visual complexity low for clear gameplay readability", "persistent_storage_config": {"leaderboard": {"sort_ascending": false}}, "game_metadata": {"game_id": "01KVC0DF6TXY7E48NFTEZGG5AX", "title": "Pop all the balloons", "description": "Match colored darts to pop all balloons in this strategic puzzle game where every move counts!", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_dceef935-7291-4848-8ffc-aa7b8a718686.webp", "created_at": 1781740884, "updated_at": 1786806388266.6182, "art_style": null}, "task_plan": null}