{"asset_map": {"kucing_illustration": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/kucing_illustration_eca97ecf-aeed-4c76-a8f1-1ccc6030505e.webp", "type": "image", "prompt": "Generate a full-body, front-facing cute cartoon orange tabby cat sitting down. Style: adorable preschool game illustration, rounded shapes, soft edges, big expressive eyes with sparkle highlights, cheerful friendly expression. Colors: orange tabby with cream belly, pink nose. Transparent background, soft drop shadow, high resolution, clean lines, professional character design.", "description": "Cute orange tabby cat character for preschool learning game", "subtype": "character", "aspect_ratio": [792, 888], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "ayam_illustration": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/ayam_illustration_b30cecac-72aa-4021-ad6c-f1dfa3b660d0.webp", "type": "image", "prompt": "Generate a full-body, front-facing cute cartoon chicken standing. Style: same adorable preschool game illustration style as kucing_illustration, rounded shapes, soft edges, big expressive eyes with sparkle highlights. Colors: white feathers, red comb and wattle, orange beak. Small wings at sides. Transparent background, soft drop shadow, high resolution, clean lines, professional character design.", "description": "Cute white chicken character matching the cat illustration style", "subtype": "character", "aspect_ratio": [756, 917], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "lembu_illustration": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/lembu_illustration_a3017773-3fff-45af-83ef-bfc88b83dc15.webp", "type": "image", "prompt": "Generate a full-body, front-facing cute cartoon dairy cow standing. Style: same adorable preschool game illustration style as kucing_illustration, rounded shapes, soft edges, big expressive eyes with sparkle highlights. Colors: white body with black spots, pink nose, small horns. Transparent background, soft drop shadow, high resolution, clean lines, professional character design.", "description": "Cute dairy cow character matching the animal illustration style", "subtype": "character", "aspect_ratio": [827, 939], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "kambing_illustration": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/kambing_illustration_cb546d69-4755-4668-8761-abc3f2987f29.webp", "type": "image", "prompt": "Generate a full-body, front-facing cute cartoon goat standing. Style: same adorable preschool game illustration style as kucing_illustration, rounded shapes, soft edges, big expressive eyes with sparkle highlights. Colors: light brown body with white patches, small curved horns and a tiny fluffy beard. Transparent background, soft drop shadow, high resolution, clean lines, professional character design.", "description": "Cute brown goat character matching the animal illustration style", "subtype": "character", "aspect_ratio": [896, 916], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "burung_illustration": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/burung_illustration_7551a9f3-9b40-42d9-a430-4ebdad880e0b.webp", "type": "image", "prompt": "Generate a full-body, front-facing cute cartoon bird perched. Style: same adorable preschool game illustration style as kucing_illustration, rounded shapes, soft edges, big expressive eyes with sparkle highlights. Colors: bright blue body, yellow belly, orange beak. Transparent background, soft drop shadow, high resolution, clean lines, professional character design.", "description": "Cute blue bird character matching the animal illustration style", "subtype": "character", "aspect_ratio": [614, 884], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "kucing_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/kucing_sound_64e693f3-f305-45ff-8a50-47648cedd672.mp3", "type": "audio", "prompt": "High-quality recording of a real domestic cat meow. Medium pitch, friendly and clear vocalization, natural sounding, no background noise, suitable for preschool education. 2 seconds duration.", "description": "A clear and friendly real cat meow for the identification task.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.72, "loop": false, "animation": null}, "ayam_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/ayam_sound_79328725-869a-4fa7-94c6-839a28cf7183.mp3", "type": "audio", "prompt": "Authentic chicken clucking sound effect. Friendly 'buck-buck' vocalization, clear and distinct, no background noise, professional studio quality for children's app. 2 seconds duration.", "description": "A recognizable and cheerful chicken clucking sound for the identification task.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.0, "loop": false, "animation": null}, "lembu_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/lembu_sound_ef868cf4-b6ac-4b5e-a378-ee7c58b3a7c9.mp3", "type": "audio", "prompt": "Realistic cow moo sound effect. Deep, characteristic, and gentle bovine vocalization. Clear recording, no wind or farm background noise, suitable for kids. 3 seconds duration.", "description": "A deep and gentle cow mooing sound for the identification task.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.48, "loop": false, "animation": null}, "kambing_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/kambing_sound_cef6454e-b405-4b1a-93db-590da8e9dd4c.mp3", "type": "audio", "prompt": "Real goat bleating 'baa' sound. Playful and characteristic vocalization, clear and distinct, isolated recording with no background noise, friendly tone. 2 seconds duration.", "description": "A playful goat bleating sound for the Level 2 identification task.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.79, "loop": false, "animation": null}, "burung_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/burung_sound_983fd916-bac2-4a02-8914-878d2c42e92f.mp3", "type": "audio", "prompt": "Natural melodic bird chirping and tweeting. Pleasant, high-quality recording, clear and distinct chirps, no background wind or traffic, cheerful mood. 2 seconds duration.", "description": "A melodic and cheerful bird chirping sound for the Level 2 identification task.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.0, "loop": false, "animation": null}, "voice_bagus": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/voice_bagus_f7c5a393-c55d-4f3c-8460-39ff4743ae47.mp3", "type": "audio", "prompt": "Sound effect of a warm, enthusiastic female voice clearly pronouncing the Malay word 'Bagus!'. High energy, encouraging, friendly adult tone, clean studio recording, no reverb. Short vocal cue.", "description": "Enthusiastic Malay voice saying 'Bagus!' for correct answers.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.91, "loop": false, "animation": null}, "voice_tahniah": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/voice_tahniah_20355ce0-04e1-45ae-9b56-7d642b52e851.mp3", "type": "audio", "prompt": "Sound effect of a warm, enthusiastic female voice clearly pronouncing the Malay word 'Tahniah!'. Celebratory and encouraging tone, friendly adult voice, clean studio recording. Short vocal cue.", "description": "Celebratory Malay voice saying 'Tahniah!' for correct answers.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.47, "loop": false, "animation": null}, "voice_cuba_lagi": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/voice_cuba_lagi_5d3a5ff7-4d6e-4f67-938e-47ab8cecef7c.mp3", "type": "audio", "prompt": "Sound effect of a gentle, supportive female voice saying the Malay phrase 'Cuba lagi ya!'. Kind and encouraging tone, not punitive, clear pronunciation, no background noise. Short vocal cue.", "description": "Gentle and supportive Malay voice saying 'Cuba lagi ya!' for incorrect attempts.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.04, "loop": false, "animation": null}, "applause_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/applause_sound_3c0bcf61-05db-4bc6-b7fb-845086626df7.mp3", "type": "audio", "prompt": "Light, cheerful applause from a small group of people. Encouraging and celebratory, no cheering or shouting, clean recording, suitable for a toddler game. 3 seconds duration.", "description": "Small group clapping sound to celebrate round completion.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 3.0, "loop": false, "animation": null}, "background_music": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/background_music_69db1aab-7236-4b5d-9dc7-1ddbed89cd6f.mp3", "type": "audio", "prompt": "Cheerful and upbeat preschool game background music. Playful melody using xylophone, ukulele, and light bells. Simple rhythmic percussion, 130 BPM, C Major key, happy and educational vibe, seamless loop. 45 seconds duration.", "description": "Upbeat and playful theme music for the game's background.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30.0, "loop": true, "animation": null}, "button_click_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/button_click_sound_02152235-f2a4-4045-be92-0017f0347511.mp3", "type": "audio", "prompt": "Friendly and satisfying soft plastic pop or bubble click. Short, responsive, non-metallic, high quality, suitable for kids' UI interaction. 2 seconds total including tail.", "description": "A soft, pleasant UI click for button interactions.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.51, "loop": false, "animation": null}, "star_collect_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/star_collect_sound_8b66e7e7-a9b2-4172-bc67-02c05d4a4f50.mp3", "type": "audio", "prompt": "Bright, sparkly magical chime with an ascending pitch. High-quality bell-like resolution, rewarding and celebratory tone for earning a star in a game. 2 seconds duration.", "description": "Magical chime sound for earning a star.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.31, "loop": false, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html lang=\"ms\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Teka Bunyi Haiwan</title>\n    <link href=\"https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&family=Poppins:wght@400;600&display=swap\" rel=\"stylesheet\">\n    <style>\n        /* CSS Reset */\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n\n        /* Root Variables */\n        :root {\n            /* Colors */\n            --sky-blue: #87CEEB;\n            --grass-green: #90EE90;\n            --sunny-yellow: #FFD700;\n            --warm-orange: #FFA500;\n            --text-brown: #4A4A4A;\n            --white: #FFFFFF;\n            \n            /* Pastel Card Colors */\n            --pastel-pink: #FFB6C1;\n            --pastel-lavender: #E6E6FA;\n            --pastel-mint: #B0E0E6;\n            --pastel-peach: #FFDAB9;\n            \n            /* Spacing */\n            --space-xs: 8px;\n            --space-sm: 16px;\n            --space-md: 24px;\n            --space-lg: 32px;\n            --space-xl: 48px;\n            \n            /* Transitions */\n            --transition-fast: 150ms;\n            --transition-normal: 300ms;\n            --transition-slow: 500ms;\n            \n            /* Easing */\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        }\n\n        /* Base Styles */\n        body, html {\n            margin: 0;\n            padding: 0;\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            touch-action: none;\n            -ms-touch-action: none;\n            font-family: 'Poppins', 'Inter', sans-serif;\n        }\n\n        #game-world {\n            position: relative;\n            width: 100%;\n            height: 100%;\n            background: linear-gradient(to bottom, var(--sky-blue) 0%, var(--sky-blue) 75%, var(--grass-green) 75%, var(--grass-green) 100%);\n            overflow: hidden;\n        }\n\n        /* Decorative Elements */\n        .cloud {\n            position: absolute;\n            background: var(--white);\n            border-radius: 100px;\n            opacity: 0.8;\n        }\n\n        .cloud::before,\n        .cloud::after {\n            content: '';\n            position: absolute;\n            background: var(--white);\n            border-radius: 100px;\n        }\n\n        .cloud-1 {\n            width: 100px;\n            height: 40px;\n            top: 50px;\n            left: 50px;\n        }\n\n        .cloud-1::before {\n            width: 50px;\n            height: 50px;\n            top: -25px;\n            left: 10px;\n        }\n\n        .cloud-1::after {\n            width: 60px;\n            height: 60px;\n            top: -30px;\n            right: 10px;\n        }\n\n        .cloud-2 {\n            width: 120px;\n            height: 45px;\n            top: 120px;\n            right: 80px;\n        }\n\n        .cloud-2::before {\n            width: 55px;\n            height: 55px;\n            top: -28px;\n            left: 15px;\n        }\n\n        .cloud-2::after {\n            width: 65px;\n            height: 65px;\n            top: -32px;\n            right: 15px;\n        }\n\n        .flower {\n            position: absolute;\n            bottom: 20px;\n            width: 30px;\n            height: 30px;\n            background: var(--sunny-yellow);\n            border-radius: 50%;\n        }\n\n        .flower::before {\n            content: '';\n            position: absolute;\n            width: 8px;\n            height: 40px;\n            background: var(--grass-green);\n            bottom: -40px;\n            left: 50%;\n            transform: translateX(-50%);\n        }\n\n        .flower-1 { left: 50px; }\n        .flower-2 { left: 150px; }\n        .flower-3 { right: 50px; }\n        .flower-4 { right: 150px; }\n\n        /* Screen Container */\n        .screen {\n            position: absolute;\n            width: 100%;\n            height: 100%;\n            display: none;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            z-index: 10;\n        }\n\n        .screen.active {\n            display: flex;\n        }\n\n        /* Start Screen */\n        #start-screen {\n            text-align: center;\n        }\n\n        .game-title {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 56px;\n            font-weight: 700;\n            color: var(--warm-orange);\n            text-shadow: 4px 4px 0px rgba(0,0,0,0.1);\n            margin-bottom: var(--space-xl);\n            line-height: 1.2;\n        }\n\n        .start-button {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 40px;\n            font-weight: 600;\n            color: var(--white);\n            background: var(--sunny-yellow);\n            border: none;\n            border-radius: 24px;\n            padding: var(--space-md) var(--space-xl);\n            box-shadow: 0 8px 0 var(--warm-orange), 0 12px 20px rgba(0,0,0,0.2);\n            cursor: pointer;\n            transition: transform var(--transition-fast) var(--ease-out);\n            min-width: 280px;\n            min-height: 96px;\n        }\n\n        .start-button:focus-visible {\n            outline: 4px solid var(--warm-orange);\n            outline-offset: 4px;\n        }\n\n        .start-button:active {\n            transform: translateY(4px);\n            box-shadow: 0 4px 0 var(--warm-orange), 0 8px 15px rgba(0,0,0,0.2);\n        }\n\n        /* Level Select Screen */\n        #level-select-screen {\n            text-align: center;\n        }\n\n        .level-title {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 48px;\n            font-weight: 700;\n            color: var(--text-brown);\n            margin-bottom: var(--space-xl);\n        }\n\n        .level-buttons {\n            display: flex;\n            flex-direction: column;\n            gap: var(--space-lg);\n        }\n\n        .level-button {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 36px;\n            font-weight: 600;\n            color: var(--white);\n            background: var(--sunny-yellow);\n            border: none;\n            border-radius: 20px;\n            padding: var(--space-md) var(--space-lg);\n            box-shadow: 0 6px 0 var(--warm-orange), 0 10px 20px rgba(0,0,0,0.15);\n            cursor: pointer;\n            transition: transform var(--transition-fast) var(--ease-out);\n            min-width: 320px;\n            min-height: 96px;\n        }\n\n        .level-button:focus-visible {\n            outline: 4px solid var(--warm-orange);\n            outline-offset: 4px;\n        }\n\n        .level-button:active {\n            transform: translateY(3px);\n            box-shadow: 0 3px 0 var(--warm-orange), 0 7px 15px rgba(0,0,0,0.15);\n        }\n\n        /* Gameplay Screen */\n        #gameplay-screen {\n            justify-content: flex-start;\n            padding-top: var(--space-sm);\n        }\n\n        /* Top HUD */\n        .top-hud {\n            display: flex;\n            justify-content: space-between;\n            align-items: center;\n            width: 100%;\n            padding: 0 var(--space-md);\n            margin-bottom: var(--space-md);\n        }\n\n        .star-counter,\n        .round-counter {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 28px;\n            font-weight: 600;\n            color: var(--text-brown);\n            background: var(--white);\n            padding: 10px var(--space-sm);\n            border-radius: 16px;\n            box-shadow: 0 4px 8px rgba(0,0,0,0.1);\n            display: flex;\n            align-items: center;\n            gap: var(--space-xs);\n        }\n\n        .star-icon {\n            font-size: 32px;\n        }\n\n        /* Speaker Button */\n        .speaker-button {\n            width: 120px;\n            height: 120px;\n            background: var(--sunny-yellow);\n            border: none;\n            border-radius: 50%;\n            box-shadow: 0 6px 0 var(--warm-orange), 0 10px 20px rgba(0,0,0,0.2);\n            cursor: pointer;\n            font-size: 60px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            margin: var(--space-sm) auto var(--space-md) auto;\n            transition: transform var(--transition-fast) var(--ease-out);\n        }\n\n        .speaker-button:focus-visible {\n            outline: 4px solid var(--warm-orange);\n            outline-offset: 4px;\n        }\n\n        .speaker-button:active {\n            transform: translateY(3px);\n            box-shadow: 0 3px 0 var(--warm-orange), 0 7px 15px rgba(0,0,0,0.2);\n        }\n\n        /* Animal Grid */\n        .animal-grid {\n            display: grid;\n            gap: var(--space-md);\n            padding: 0 var(--space-md);\n            margin: var(--space-lg) 0;\n        }\n\n        .animal-grid.three-animals {\n            grid-template-columns: repeat(3, 1fr);\n            max-width: 630px;\n            margin-left: auto;\n            margin-right: auto;\n        }\n\n        .animal-grid.four-animals {\n            grid-template-columns: repeat(2, 1fr);\n            max-width: 480px;\n            margin-left: auto;\n            margin-right: auto;\n        }\n\n        .animal-card {\n            background: var(--white);\n            border-radius: 20px;\n            padding: var(--space-sm);\n            box-shadow: 0 6px 12px rgba(0,0,0,0.15);\n            cursor: pointer;\n            transition: transform var(--transition-normal) var(--ease-out);\n            min-height: 200px;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            position: relative;\n        }\n\n        .animal-card:focus-visible {\n            outline: 4px solid var(--warm-orange);\n            outline-offset: 2px;\n        }\n\n        .animal-card:active {\n            transform: scale(0.95);\n        }\n\n        .animal-card.disabled {\n            pointer-events: none;\n            opacity: 0.6;\n        }\n\n        .animal-image {\n            width: 100%;\n            height: auto;\n            max-width: 140px;\n            object-fit: contain;\n        }\n\n        .animal-name {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 22px;\n            font-weight: 600;\n            color: var(--text-brown);\n            margin-top: var(--space-xs);\n            text-transform: capitalize;\n        }\n\n        /* Feedback Text */\n        .feedback-text {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 32px;\n            font-weight: 600;\n            color: var(--text-brown);\n            text-align: center;\n            background: rgba(255, 255, 255, 0.9);\n            padding: var(--space-sm) var(--space-md);\n            border-radius: 16px;\n            margin: var(--space-sm) var(--space-md);\n            min-height: 60px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            box-shadow: 0 4px 8px rgba(0,0,0,0.1);\n        }\n\n        /* Home Button */\n        .home-button {\n            position: absolute;\n            top: var(--space-sm);\n            left: var(--space-sm);\n            width: 64px;\n            height: 64px;\n            background: rgba(255, 255, 255, 0.95);\n            border: none;\n            border-radius: 50%;\n            box-shadow: 0 4px 8px rgba(0,0,0,0.15);\n            cursor: pointer;\n            font-size: 32px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            transition: transform var(--transition-fast) var(--ease-out);\n            z-index: 100;\n        }\n\n        .home-button:focus-visible {\n            outline: 4px solid var(--warm-orange);\n            outline-offset: 2px;\n        }\n\n        .home-button:active {\n            transform: scale(0.9);\n        }\n\n        /* Completion Screen */\n        #completion-screen {\n            text-align: center;\n        }\n\n        .completion-title {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 52px;\n            font-weight: 700;\n            color: var(--warm-orange);\n            margin-bottom: var(--space-lg);\n        }\n\n        .completion-stars {\n            font-size: 80px;\n            margin-bottom: var(--space-lg);\n        }\n\n        .completion-message {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 32px;\n            font-weight: 600;\n            color: var(--text-brown);\n            margin-bottom: var(--space-xl);\n            padding: 0 var(--space-md);\n        }\n\n        .completion-buttons {\n            display: flex;\n            flex-direction: column;\n            gap: var(--space-md);\n            align-items: center;\n        }\n\n        .replay-button,\n        .menu-button {\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 32px;\n            font-weight: 600;\n            color: var(--white);\n            background: var(--sunny-yellow);\n            border: none;\n            border-radius: 20px;\n            padding: var(--space-md) var(--space-lg);\n            box-shadow: 0 6px 0 var(--warm-orange), 0 10px 20px rgba(0,0,0,0.15);\n            cursor: pointer;\n            transition: transform var(--transition-fast) var(--ease-out);\n            min-width: 280px;\n            min-height: 96px;\n        }\n\n        .replay-button:focus-visible,\n        .menu-button:focus-visible {\n            outline: 4px solid var(--warm-orange);\n            outline-offset: 4px;\n        }\n\n        .replay-button:active,\n        .menu-button:active {\n            transform: translateY(3px);\n            box-shadow: 0 3px 0 var(--warm-orange), 0 7px 15px rgba(0,0,0,0.15);\n        }\n\n        /* Animations */\n        @keyframes bounce {\n            0%, 100% { transform: scale(1); }\n            50% { transform: scale(1.2); }\n        }\n\n        @keyframes shake {\n            0%, 100% { transform: translateX(0); }\n            25% { transform: translateX(-10px); }\n            75% { transform: translateX(10px); }\n        }\n\n        .bounce {\n            animation: bounce 0.5s var(--ease-bounce);\n        }\n\n        .shake {\n            animation: shake 0.4s var(--ease-out);\n        }\n\n        /* Confetti */\n        .confetti-container {\n            position: fixed;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            z-index: 1000;\n        }\n\n        .confetti {\n            position: absolute;\n            width: 10px;\n            height: 10px;\n            border-radius: 50%;\n        }\n\n        /* Edit Mode Indicator */\n        .edit-mode-indicator {\n            position: absolute;\n            top: var(--space-sm);\n            right: var(--space-sm);\n            background: var(--warm-orange);\n            color: var(--white);\n            padding: var(--space-xs) var(--space-sm);\n            border-radius: 8px;\n            font-family: 'Fredoka', 'Chewy', sans-serif;\n            font-size: 18px;\n            font-weight: 600;\n            z-index: 1000;\n        }\n\n        /* Reduced Motion Support */\n        @media (prefers-reduced-motion: reduce) {\n            * {\n                animation-duration: 0.01ms !important;\n                animation-iteration-count: 1 !important;\n                transition-duration: 0.01ms !important;\n            }\n        }\n    </style>\n</head>\n<body>\n    <div id=\"game-world\">\n        <!-- Decorative Elements -->\n        <div class=\"cloud cloud-1\"></div>\n        <div class=\"cloud cloud-2\"></div>\n        <div class=\"flower flower-1\"></div>\n        <div class=\"flower flower-2\"></div>\n        <div class=\"flower flower-3\"></div>\n        <div class=\"flower flower-4\"></div>\n\n        <!-- Start Screen -->\n        <div id=\"start-screen\" class=\"screen active\">\n            <h1 class=\"game-title\">Teka Bunyi<br>Haiwan \ud83d\udc3e</h1>\n            <button class=\"start-button\" id=\"start-btn\">Mula Main!</button>\n        </div>\n\n        <!-- Level Select Screen -->\n        <div id=\"level-select-screen\" class=\"screen\">\n            <h2 class=\"level-title\">Pilih Tahap</h2>\n            <div class=\"level-buttons\">\n                <button class=\"level-button\" data-level=\"1\">\n                    Level 1 - Mudah \ud83c\udf1f\n                </button>\n                <button class=\"level-button\" data-level=\"2\">\n                    Level 2 - Mencabar \ud83c\udf1f\ud83c\udf1f\n                </button>\n            </div>\n        </div>\n\n        <!-- Gameplay Screen -->\n        <div id=\"gameplay-screen\" class=\"screen\">\n            <button class=\"home-button\" id=\"home-btn\">\ud83c\udfe0</button>\n            \n            <div class=\"top-hud\">\n                <div class=\"star-counter\">\n                    <span class=\"star-icon\">\u2b50</span>\n                    <span id=\"star-count\">0</span>\n                </div>\n                <div class=\"round-counter\">\n                    <span id=\"round-display\">1/5</span>\n                </div>\n            </div>\n\n            <div class=\"feedback-text\" id=\"feedback-text\">Dengar dan pilih!</div>\n\n            <button class=\"speaker-button\" id=\"speaker-btn\">\ud83d\udd0a</button>\n\n            <div class=\"animal-grid\" id=\"animal-grid\"></div>\n        </div>\n\n        <!-- Completion Screen -->\n        <div id=\"completion-screen\" class=\"screen\">\n            <h2 class=\"completion-title\">Tahniah! \ud83c\udf89</h2>\n            <div class=\"completion-stars\" id=\"completion-stars\"></div>\n            <p class=\"completion-message\" id=\"completion-message\"></p>\n            <div class=\"completion-buttons\">\n                <button class=\"replay-button\" id=\"replay-btn\">Main Lagi</button>\n                <button class=\"menu-button\" id=\"menu-btn\">Menu Utama</button>\n            </div>\n        </div>\n\n        <!-- Confetti Container -->\n        <div class=\"confetti-container\" id=\"confetti-container\"></div>\n    </div>\n\n    <script>\n        /**\n         * TEKA BUNYI HAIWAN - Animal Sound Guessing Game\n         * \n         * An educational game for preschoolers where they listen to animal sounds\n         * and select the correct animal illustration. Features two difficulty levels,\n         * encouraging feedback, and reward animations.\n         * \n         * Game Flow:\n         * 1. Start screen \u2192 Level select \u2192 Gameplay (5 rounds) \u2192 Completion\n         * 2. Each round: Play sound \u2192 Player selects animal \u2192 Feedback \u2192 Next round\n         * 3. Correct answers earn stars and celebration animations\n         * 4. Incorrect answers provide gentle encouragement and replay the sound\n         */\n\n        async function run(mode) {\n            const gameWorld = document.getElementById('game-world');\n\n            // Audio Context Setup\n            const AudioContext = window.AudioContext || window.webkitAudioContext;\n            const audioContext = new AudioContext();\n            const audioBuffers = {};\n            let backgroundMusicSource = null;\n\n            // Asset Cache\n            const imageCache = {};\n\n            // Game State\n            let currentMode = mode;\n            let currentLevel = null;\n            let currentRound = 0;\n            let currentAnimal = null;\n            let starsEarned = 0;\n            let isProcessingAnswer = false;\n            let previousAnimal = null;\n\n            // Animal Names in Malay\n            const animalNames = {\n                kucing: 'Kucing',\n                ayam: 'Ayam',\n                lembu: 'Lembu',\n                kambing: 'Kambing',\n                burung: 'Burung'\n            };\n\n            // Pastel colors for animal cards\n            const cardColors = ['#FFB6C1', '#E6E6FA', '#B0E0E6', '#FFDAB9', '#FFE4B5'];\n\n            // Preload Assets\n            async function preloadAssets() {\n                const assetIds = [\n                    'kucing_illustration', 'ayam_illustration', 'lembu_illustration',\n                    'kambing_illustration', 'burung_illustration',\n                    'kucing_sound', 'ayam_sound', 'lembu_sound', 'kambing_sound', 'burung_sound',\n                    'voice_bagus', 'voice_tahniah', 'voice_cuba_lagi',\n                    'applause_sound', 'button_click_sound', 'star_collect_sound',\n                    'background_music'\n                ];\n\n                const loadPromises = assetIds.map(async (id) => {\n                    const assetInfo = lib.getAsset(id);\n                    if (!assetInfo) {\n                        lib.log(`Asset not found: ${id}`);\n                        return;\n                    }\n\n                    if (assetInfo.type === 'image') {\n                        return new Promise((resolve, reject) => {\n                            const img = new Image();\n                            img.onload = () => {\n                                imageCache[id] = img;\n                                resolve();\n                            };\n                            img.onerror = () => {\n                                lib.log(`Failed to load image: ${id}`);\n                                reject();\n                            };\n                            img.src = assetInfo.url;\n                        });\n                    } else if (assetInfo.type === 'audio') {\n                        try {\n                            const response = await fetch(assetInfo.url);\n                            const arrayBuffer = await response.arrayBuffer();\n                            const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n                            audioBuffers[id] = audioBuffer;\n                        } catch (error) {\n                            lib.log(`Failed to load audio: ${id} - ${error}`);\n                        }\n                    }\n                });\n\n                await Promise.all(loadPromises);\n            }\n\n            // Play Sound Effect\n            function playSFX(soundId, volume = 1.0) {\n                if (!audioBuffers[soundId]) return;\n\n                const source = audioContext.createBufferSource();\n                const gainNode = audioContext.createGain();\n                \n                source.buffer = audioBuffers[soundId];\n                gainNode.gain.value = volume;\n                \n                source.connect(gainNode);\n                gainNode.connect(audioContext.destination);\n                \n                source.start(0);\n                return source;\n            }\n\n            // Play Background Music\n            function playBackgroundMusic() {\n                if (backgroundMusicSource) {\n                    backgroundMusicSource.stop();\n                }\n\n                if (!audioBuffers['background_music']) return;\n\n                backgroundMusicSource = audioContext.createBufferSource();\n                const gainNode = audioContext.createGain();\n                \n                backgroundMusicSource.buffer = audioBuffers['background_music'];\n                backgroundMusicSource.loop = true;\n                gainNode.gain.value = window.gameConfig.backgroundMusicVolume;\n                \n                backgroundMusicSource.connect(gainNode);\n                gainNode.connect(audioContext.destination);\n                \n                backgroundMusicSource.start(0);\n            }\n\n            // Stop Background Music\n            function stopBackgroundMusic() {\n                if (backgroundMusicSource) {\n                    backgroundMusicSource.stop();\n                    backgroundMusicSource = null;\n                }\n            }\n\n            // Screen Management\n            function showScreen(screenId) {\n                document.querySelectorAll('.screen').forEach(screen => {\n                    screen.classList.remove('active');\n                });\n                document.getElementById(screenId).classList.add('active');\n            }\n\n            // Create Confetti\n            function createConfetti() {\n                const container = document.getElementById('confetti-container');\n                container.innerHTML = '';\n\n                const colors = ['#FFD700', '#FFA500', '#FF69B4', '#87CEEB', '#90EE90'];\n                const confettiCount = 50;\n\n                for (let i = 0; i < confettiCount; i++) {\n                    const confetti = document.createElement('div');\n                    confetti.className = 'confetti';\n                    confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];\n                    confetti.style.left = Math.random() * 100 + '%';\n                    confetti.style.top = '-10px';\n                    \n                    const size = Math.random() * 10 + 5;\n                    confetti.style.width = size + 'px';\n                    confetti.style.height = size + 'px';\n                    \n                    container.appendChild(confetti);\n\n                    // Animate confetti\n                    const duration = Math.random() * 2000 + 2000;\n                    const endX = (Math.random() - 0.5) * 200;\n                    const rotation = Math.random() * 720;\n\n                    confetti.animate([\n                        { transform: 'translateY(0) translateX(0) rotate(0deg)', opacity: 1 },\n                        { transform: `translateY(${window.innerHeight}px) translateX(${endX}px) rotate(${rotation}deg)`, opacity: 0 }\n                    ], {\n                        duration: duration,\n                        easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'\n                    });\n\n                    setTimeout(() => confetti.remove(), duration);\n                }\n            }\n\n            // Select Random Animal\n            function selectRandomAnimal(animals) {\n                let availableAnimals = animals.filter(a => a !== previousAnimal);\n                if (availableAnimals.length === 0) {\n                    availableAnimals = animals;\n                }\n                const selected = availableAnimals[Math.floor(Math.random() * availableAnimals.length)];\n                previousAnimal = selected;\n                return selected;\n            }\n\n            // Start New Round\n            function startNewRound() {\n                currentRound++;\n                \n                if (currentRound > currentLevel.rounds) {\n                    showCompletionScreen();\n                    return;\n                }\n\n                // Update round display\n                document.getElementById('round-display').textContent = `${currentRound}/${currentLevel.rounds}`;\n                document.getElementById('feedback-text').textContent = 'Dengar dan pilih!';\n\n                // Select random animal\n                currentAnimal = selectRandomAnimal(currentLevel.animals);\n\n                // Render animal grid\n                renderAnimalGrid();\n\n                // Play animal sound\n                setTimeout(() => {\n                    playAnimalSound();\n                }, 500);\n\n                isProcessingAnswer = false;\n            }\n\n            // Play Animal Sound\n            function playAnimalSound() {\n                const soundId = currentAnimal + '_sound';\n                playSFX(soundId, window.gameConfig.sfxVolume);\n            }\n\n            // Render Animal Grid\n            function renderAnimalGrid() {\n                const grid = document.getElementById('animal-grid');\n                grid.innerHTML = '';\n                \n                // Set grid class based on animal count\n                grid.className = 'animal-grid';\n                if (currentLevel.animalCount === 3) {\n                    grid.classList.add('three-animals');\n                } else {\n                    grid.classList.add('four-animals');\n                }\n\n                // Shuffle animals for display\n                const displayAnimals = [...currentLevel.animals].sort(() => Math.random() - 0.5);\n\n                displayAnimals.forEach((animal, index) => {\n                    const card = document.createElement('div');\n                    card.className = 'animal-card';\n                    card.style.backgroundColor = cardColors[index % cardColors.length];\n                    card.dataset.animal = animal;\n                    card.tabIndex = 0;\n                    card.role = 'button';\n                    card.ariaLabel = `Pilih ${animalNames[animal]}`;\n\n                    const img = document.createElement('img');\n                    img.className = 'animal-image';\n                    img.src = imageCache[animal + '_illustration']?.src || '';\n                    img.alt = animalNames[animal];\n\n                    const name = document.createElement('div');\n                    name.className = 'animal-name';\n                    name.textContent = animalNames[animal];\n\n                    card.appendChild(img);\n                    card.appendChild(name);\n\n                    card.addEventListener('click', () => handleAnimalSelection(animal, card));\n                    card.addEventListener('keydown', (e) => {\n                        if (e.key === 'Enter' || e.key === ' ') {\n                            e.preventDefault();\n                            handleAnimalSelection(animal, card);\n                        }\n                    });\n\n                    grid.appendChild(card);\n                });\n            }\n\n            // Handle Animal Selection\n            function handleAnimalSelection(selectedAnimal, cardElement) {\n                if (isProcessingAnswer) return;\n                isProcessingAnswer = true;\n\n                // Play button click sound\n                playSFX('button_click_sound', window.gameConfig.sfxVolume);\n\n                // Disable all cards\n                document.querySelectorAll('.animal-card').forEach(card => {\n                    card.classList.add('disabled');\n                });\n\n                if (selectedAnimal === currentAnimal) {\n                    // Correct answer\n                    handleCorrectAnswer(cardElement);\n                } else {\n                    // Incorrect answer\n                    handleIncorrectAnswer(cardElement);\n                }\n            }\n\n            // Handle Correct Answer\n            function handleCorrectAnswer(cardElement) {\n                // Add bounce animation\n                cardElement.classList.add('bounce');\n\n                // Update stars\n                starsEarned++;\n                document.getElementById('star-count').textContent = starsEarned;\n\n                // Play celebration sounds\n                playSFX('star_collect_sound', window.gameConfig.sfxVolume);\n                playSFX('applause_sound', window.gameConfig.sfxVolume);\n\n                // Random encouraging message\n                const messages = window.gameConfig.correctMessages;\n                const message = messages[Math.floor(Math.random() * messages.length)];\n                document.getElementById('feedback-text').textContent = message;\n\n                // Play voice feedback\n                const voiceMap = {\n                    'Bagus!': 'voice_bagus',\n                    'Tahniah!': 'voice_tahniah',\n                    'Hebat!': 'voice_tahniah'\n                };\n                const voiceId = voiceMap[message] || 'voice_bagus';\n                setTimeout(() => {\n                    playSFX(voiceId, window.gameConfig.voiceVolume);\n                }, 300);\n\n                // Create confetti\n                createConfetti();\n\n                // Proceed to next round\n                setTimeout(() => {\n                    cardElement.classList.remove('bounce');\n                    startNewRound();\n                }, window.gameConfig.celebrationDuration);\n            }\n\n            // Handle Incorrect Answer\n            function handleIncorrectAnswer(cardElement) {\n                // Add shake animation\n                cardElement.classList.add('shake');\n\n                // Show encouraging message\n                const messages = window.gameConfig.incorrectMessages;\n                const message = messages[Math.floor(Math.random() * messages.length)];\n                document.getElementById('feedback-text').textContent = message;\n\n                // Play voice feedback\n                setTimeout(() => {\n                    playSFX('voice_cuba_lagi', window.gameConfig.voiceVolume);\n                }, 300);\n\n                // Replay sound and re-enable cards\n                setTimeout(() => {\n                    cardElement.classList.remove('shake');\n                    playAnimalSound();\n                    \n                    // Re-enable cards\n                    document.querySelectorAll('.animal-card').forEach(card => {\n                        card.classList.remove('disabled');\n                    });\n                    \n                    document.getElementById('feedback-text').textContent = 'Cuba lagi!';\n                    isProcessingAnswer = false;\n                }, window.gameConfig.feedbackDisplayTime);\n            }\n\n            // Show Completion Screen\n            function showCompletionScreen() {\n                showScreen('completion-screen');\n\n                // Display stars\n                const starsDisplay = document.getElementById('completion-stars');\n                starsDisplay.textContent = '\u2b50'.repeat(starsEarned);\n\n                // Display message\n                const messageElement = document.getElementById('completion-message');\n                if (starsEarned === currentLevel.rounds) {\n                    messageElement.textContent = `Sempurna! Kamu dapat ${starsEarned} bintang! \ud83c\udf1f`;\n                } else {\n                    messageElement.textContent = `Bagus! Kamu dapat ${starsEarned} daripada ${currentLevel.rounds} bintang!`;\n                }\n\n                // Play celebration\n                createConfetti();\n                playSFX('applause_sound', window.gameConfig.sfxVolume);\n            }\n\n            // Start Level\n            function startLevel(levelId) {\n                currentLevel = window.gameConfig.levels.find(l => l.id === levelId);\n                currentRound = 0;\n                starsEarned = 0;\n                previousAnimal = null;\n\n                document.getElementById('star-count').textContent = '0';\n                \n                showScreen('gameplay-screen');\n                startNewRound();\n            }\n\n            // Event Listeners Setup\n            function setupEventListeners() {\n                // Start button\n                document.getElementById('start-btn').addEventListener('click', () => {\n                    playSFX('button_click_sound', window.gameConfig.sfxVolume);\n                    showScreen('level-select-screen');\n                });\n\n                // Level buttons\n                document.querySelectorAll('.level-button').forEach(button => {\n                    button.addEventListener('click', () => {\n                        playSFX('button_click_sound', window.gameConfig.sfxVolume);\n                        const levelId = parseInt(button.dataset.level);\n                        startLevel(levelId);\n                    });\n                });\n\n                // Speaker button\n                document.getElementById('speaker-btn').addEventListener('click', () => {\n                    if (!isProcessingAnswer) {\n                        playSFX('button_click_sound', window.gameConfig.sfxVolume);\n                        playAnimalSound();\n                    }\n                });\n\n                // Home button\n                document.getElementById('home-btn').addEventListener('click', () => {\n                    playSFX('button_click_sound', window.gameConfig.sfxVolume);\n                    showScreen('level-select-screen');\n                    currentRound = 0;\n                    starsEarned = 0;\n                });\n\n                // Replay button\n                document.getElementById('replay-btn').addEventListener('click', () => {\n                    playSFX('button_click_sound', window.gameConfig.sfxVolume);\n                    startLevel(currentLevel.id);\n                });\n\n                // Menu button\n                document.getElementById('menu-btn').addEventListener('click', () => {\n                    playSFX('button_click_sound', window.gameConfig.sfxVolume);\n                    showScreen('level-select-screen');\n                });\n\n                // Keyboard support\n                document.addEventListener('keydown', (e) => {\n                    if (e.code === 'Space' && !isProcessingAnswer) {\n                        e.preventDefault();\n                        playAnimalSound();\n                    }\n                });\n            }\n\n            // Show Game Parameters\n            lib.showGameParameters({\n                name: 'Tetapan Permainan',\n                params: {\n                    'Bilangan Pusingan (Level 1)': {\n                        key: 'levels.0.rounds',\n                        type: 'slider',\n                        min: 3,\n                        max: 10,\n                        step: 1\n                    },\n                    'Bilangan Pusingan (Level 2)': {\n                        key: 'levels.1.rounds',\n                        type: 'slider',\n                        min: 3,\n                        max: 10,\n                        step: 1\n                    },\n                    'Masa Perayaan (ms)': {\n                        key: 'celebrationDuration',\n                        type: 'slider',\n                        min: 500,\n                        max: 5000,\n                        step: 100\n                    },\n                    'Masa Maklum Balas (ms)': {\n                        key: 'feedbackDisplayTime',\n                        type: 'slider',\n                        min: 500,\n                        max: 5000,\n                        step: 100\n                    },\n                    'Kelantangan Muzik': {\n                        key: 'backgroundMusicVolume',\n                        type: 'slider',\n                        min: 0,\n                        max: 1,\n                        step: 0.1\n                    },\n                    'Kelantangan SFX': {\n                        key: 'sfxVolume',\n                        type: 'slider',\n                        min: 0,\n                        max: 1,\n                        step: 0.1\n                    },\n                    'Kelantangan Suara': {\n                        key: 'voiceVolume',\n                        type: 'slider',\n                        min: 0,\n                        max: 1,\n                        step: 0.1\n                    }\n                }\n            });\n\n            // Initialize Game\n            try {\n                await preloadAssets();\n                setupEventListeners();\n\n                if (mode === 'edit') {\n                    // Edit mode: Show edit indicator\n                    const indicator = document.createElement('div');\n                    indicator.className = 'edit-mode-indicator';\n                    indicator.textContent = 'MOD EDIT';\n                    gameWorld.appendChild(indicator);\n                    \n                    // Show start screen but disable gameplay\n                    showScreen('start-screen');\n                } else {\n                    // Play mode: Start background music and show start screen\n                    playBackgroundMusic();\n                    showScreen('start-screen');\n                }\n            } catch (error) {\n                lib.log('Error initializing game: ' + error);\n            }\n        }\n    </script>\n</body>\n</html>", "game_config": "{\"levels\": [{\"id\": 1, \"name\": \"Mudah\", \"animalCount\": 3, \"animals\": [\"kucing\", \"ayam\", \"lembu\"], \"rounds\": 5}, {\"id\": 2, \"name\": \"Mencabar\", \"animalCount\": 5, \"animals\": [\"kucing\", \"ayam\", \"lembu\", \"kambing\", \"burung\"], \"rounds\": 5}], \"correctMessages\": [\"Bagus!\", \"Tahniah!\", \"Hebat!\"], \"incorrectMessages\": [\"Cuba lagi ya!\", \"Hampir betul!\", \"Jom cuba sekali lagi!\"], \"celebrationDuration\": 2000, \"feedbackDisplayTime\": 1500, \"starsPerCorrect\": 1, \"backgroundMusicVolume\": 0.3, \"sfxVolume\": 0.7, \"voiceVolume\": 0.8}", "game_design_document": "## Core Game Loop and Mechanics\n\n**Main Gameplay Flow:**\n1. Player enters the game and sees a cheerful start screen with a large \"Mula Main!\" (Start Playing) button\n2. Player selects Level 1 (Mudah) or Level 2 (Mencabar) from a colorful level selection screen\n3. Game presents a round:\n   - An animal sound plays automatically\n   - 3-4 cute animal illustrations appear on screen (depending on level)\n   - Player taps the animal they think made the sound\n4. Immediate feedback:\n   - **Correct answer**: Confetti animation, star reward appears, applause sound, encouraging voice says \"Bagus!\" or \"Tahniah!\", animal does a happy bounce animation\n   - **Wrong answer**: Gentle shake animation, encouraging voice says \"Cuba lagi ya!\", sound plays again automatically\n5. After correct answer, brief celebration (1-2 seconds), then automatically proceeds to next round\n6. After completing 5 rounds, player sees completion screen with total stars earned and option to replay or try other level\n\n**Progression System:**\n- **Level 1 (Mudah)**: 3 animal choices per round, 5 rounds total, animals: kucing (cat), ayam (chicken), lembu (cow)\n- **Level 2 (Mencabar)**: 4 animal choices per round, 5 rounds total, adds kambing (goat) and burung (bird) to the mix\n- Stars earned: 1 star per correct answer (max 5 stars per level)\n- Badge system: Complete Level 1 = \"Pendengar Bijak\" badge, Complete Level 2 = \"Pakar Haiwan\" badge\n\n**What Makes It Engaging:**\n- Instant audio-visual feedback keeps young players engaged\n- Cute, bouncy animations make animals feel alive\n- Positive reinforcement encourages learning without pressure\n- Simple tap controls perfect for small hands\n- Repetition helps learning while variety prevents boredom\n- Achievement system motivates completion\n\n## Starting Scope\n\n**First Iteration Must Include:**\n- Complete Level 1 (Mudah) with 3 animals: kucing, ayam, lembu\n- 5 rounds per playthrough\n- Animal sound playback system with replay button\n- Tap-to-select animal mechanics\n- Correct/incorrect feedback with animations (confetti, bounce, shake)\n- Star collection system\n- Encouraging voice feedback (\"Bagus!\", \"Tahniah!\", \"Cuba lagi ya!\")\n- Cheerful background music (looping)\n- Level completion screen with star count\n- Replay functionality\n\n**Future Iterations Can Add:**\n- Level 2 (Mencabar) with 4 animals including kambing and burung\n- Badge system and badge display\n- More animal varieties\n- Progress saving\n- Parent dashboard showing learning progress\n\n## Game Perspective\n\n**Fixed-screen** - Single screen view with no scrolling. All interactive elements (animal choices, replay button, UI) are visible at once. This is ideal for preschoolers who need simple, clear layouts without navigation complexity.\n\n## Game World and Level Design\n\n**Visual Layout (720\u00d71280 portrait):**\n\n**Top Section (0-300px):**\n- Star counter display (top-left corner)\n- Round counter \"Soalan 1/5\" (top-right corner)\n- Large speaker icon button (center, ~200px from top) - taps to replay sound\n\n**Middle Section (300-900px):**\n- Animal choice grid:\n  - Level 1: 3 animals in a single row or triangular arrangement\n  - Level 2: 4 animals in 2\u00d72 grid\n- Each animal card: ~200\u00d7200px with padding\n- Cards have subtle shadow and rounded corners\n- Animals are centered in cards with names below in clear, large font\n\n**Bottom Section (900-1280px):**\n- Encouragement text area (shows \"Dengar dan pilih!\" or feedback messages)\n- Decorative elements (clouds, grass, flowers) for visual appeal\n- Home button (bottom-left corner) to return to level select\n\n**Color Scheme:**\n- Bright, cheerful pastels: sky blue background, grass green footer, sunny yellow accents\n- High contrast for readability\n- Each animal card has a distinct pastel background color\n\n**Level Progression:**\n- Level 1: 5 rounds, randomly selected from 3 animals (kucing, ayam, lembu)\n- Level 2: 5 rounds, randomly selected from 5 animals (adds kambing, burung)\n- No animal repeats consecutively to maintain variety\n\n## Edit Mode Design\n\n**Editable Properties:**\n\n```javascript\nwindow.gameConfig = {\n  // Level Configuration\n  levels: [\n    {\n      id: 1,\n      name: \"Mudah\",\n      animalCount: 3,\n      animals: [\"kucing\", \"ayam\", \"lembu\"],\n      rounds: 5\n    },\n    {\n      id: 2,\n      name: \"Mencabar\", \n      animalCount: 4,\n      animals: [\"kucing\", \"ayam\", \"lembu\", \"kambing\", \"burung\"],\n      rounds: 5\n    }\n  ],\n  \n  // Feedback Messages\n  correctMessages: [\"Bagus!\", \"Tahniah!\", \"Hebat!\"],\n  incorrectMessages: [\"Cuba lagi ya!\", \"Hampir betul!\", \"Jom cuba sekali lagi!\"],\n  \n  // Timing (in milliseconds)\n  celebrationDuration: 2000,\n  feedbackDisplayTime: 1500,\n  \n  // Rewards\n  starsPerCorrect: 1,\n  \n  // Audio Settings\n  backgroundMusicVolume: 0.3,\n  sfxVolume: 0.7,\n  voiceVolume: 0.8\n}\n```\n\n**Constraints:**\n- Animal count per level: 3-6 (prevents overcrowding)\n- Rounds per level: 3-10 (maintains engagement)\n- Volume levels: 0.0-1.0\n- Timing values: 500-5000ms (prevents too fast/slow feedback)\n- Must have at least 3 animals defined for Level 1\n\n**What Users Can Customize:**\n- Number of rounds per level\n- Which animals appear in each level\n- Feedback message text (can add more variety or translate)\n- Timing of celebrations and transitions\n- Audio volume levels\n- Stars awarded per correct answer\n\n## Control Design\n\n**Mobile (Touch) - Primary Interface:**\n- **Tap animal card**: Select that animal as answer\n- **Tap speaker button**: Replay the current animal sound\n- **Tap \"Mula Main!\" button**: Start game from main menu\n- **Tap level button**: Select difficulty level\n- **Tap \"Main Lagi\" button**: Replay current level\n- **Tap home button**: Return to level select\n\n**Desktop (Mouse + Keyboard):**\n- **Click animal card**: Select that animal as answer\n- **Click speaker button**: Replay the current animal sound\n- **Click buttons**: Same as tap for all UI buttons\n- **Spacebar**: Replay current animal sound (alternative to clicking speaker)\n- **Number keys 1-4**: Quick select animals (1=top-left, 2=top-right, etc.)\n\n**Touch Optimization:**\n- All interactive elements minimum 96\u00d796px\n- Animal cards are large (~200\u00d7200px) for easy tapping\n- Buttons have clear visual feedback (scale up slightly on press)\n- No complex gestures - only simple taps\n- Speaker button prominently placed and large (~120\u00d7120px)\n\n**Input Handling:**\n- Prevent multiple rapid taps during feedback animations\n- Disable animal selection during celebration/feedback period\n- Clear visual indication of selected animal (brief highlight before feedback)\n- Haptic feedback on tap (if device supports)\n\n## Art Style and Visual Theme\n\n**Overall Style:**\n- **Cute, friendly cartoon illustration style** - rounded shapes, big eyes, cheerful expressions\n- **Bright, saturated colors** - appeals to preschoolers, creates joyful atmosphere\n- **Soft shadows and highlights** - adds depth without complexity\n- **Consistent character proportions** - all animals similar size for fairness\n\n**Color Palette:**\n- Background: Soft sky blue (#87CEEB) with white fluffy clouds\n- Ground/footer: Grass green (#90EE90) with small flowers\n- UI elements: Sunny yellow (#FFD700), warm orange (#FFA500)\n- Animal cards: Pastel backgrounds - pink, lavender, mint, peach\n- Text: Dark brown (#4A4A4A) for readability\n\n**Asset Specifications:**\n- **Animal illustrations**: 400\u00d7400px, transparent background, facing forward, full body visible\n- **UI elements**: Scalable vector-style rendering (can use CSS/Canvas)\n- **Decorative elements**: Simple shapes (clouds, flowers, grass) - CSS/Canvas rendering\n- **Confetti**: Colorful particle system - code-rendered\n- **Star icons**: Simple 5-pointed stars - CSS/Canvas rendering\n\n**Visual Consistency:**\n- All animals drawn in same illustration style\n- Consistent line weight and shading approach\n- Similar level of detail across all assets\n- Unified color temperature (warm, inviting)\n\n**Animation Style:**\n- Bouncy, elastic animations (ease-out timing)\n- Gentle, non-startling movements\n- Celebratory effects are joyful but not overwhelming\n- Smooth transitions between states", "persistent_storage_config": null, "game_metadata": {"game_id": "01KPYQ77YKCV73AHCEH5FB9M3Z", "title": "Teka Bunyi Haiwan", "description": "", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_d50dae10-34ed-423e-964a-b598cd05e8f6.webp", "created_at": 1777000008.0, "updated_at": 1777000009317.0952, "art_style": null}}