{"asset_map": {"cow_character": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cow_character_131c0cb3-0a22-48da-9ce1-f514b049c277.webp", "type": "image", "prompt": "Generate a full-body, front-facing illustration of a cute, friendly cartoon cow. The cow is smiling with large expressive eyes, black and white spotted pattern, pink nose, and small horns. Simple rounded shapes, bright cheerful colors, child-friendly educational game aesthetic. Standing upright in a friendly pose. High resolution, detailed, professional quality, transparent background.", "description": "Friendly cartoon cow for the learning phase animal cards", "subtype": "character", "aspect_ratio": [825, 1308], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "duck_character": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/duck_character_de8ed329-dd82-46c3-b33b-e03c84a5e554.webp", "type": "image", "prompt": "Generate a full-body, front-facing illustration of a cute, friendly cartoon duck. Yellow feathers with a bright orange beak and feet, smiling expression, large friendly eyes. Standing upright in a cheerful pose with wings slightly out. Simple rounded shapes, matching the art style of image 1. Child-friendly educational game aesthetic, bright colors. High resolution, professional quality, transparent background.", "description": "Friendly cartoon duck for the learning phase animal cards", "subtype": "character", "aspect_ratio": [804, 1230], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "lion_character": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/lion_character_23df9cfd-bc6a-410c-b4da-665463281e14.webp", "type": "image", "prompt": "Generate a full-body, front-facing illustration of a cute, friendly cartoon lion. Golden orange fur with a big fluffy mane, smiling expression, large friendly eyes. Majestic but approachable and not scary. Standing upright in a proud but friendly pose. Simple rounded shapes, matching the art style of image 1 and image 2. Child-friendly educational game aesthetic, bright colors. High resolution, professional quality, transparent background.", "description": "Friendly cartoon lion for the learning phase animal cards", "subtype": "character", "aspect_ratio": [781, 1223], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "monkey_character": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/monkey_character_1ad7bb0a-865a-4110-a59a-3e5078dd0a48.webp", "type": "image", "prompt": "Generate a full-body, front-facing illustration of a cute, friendly cartoon monkey. Brown fur with a lighter tan face and belly, long curled tail, big expressive eyes, cheerful smile. Playful pose with arms slightly raised. Simple rounded shapes, matching the art style of image 1. Child-friendly educational game aesthetic, bright colors. High resolution, professional quality, transparent background.", "description": "Friendly cartoon monkey for the learning phase animal cards", "subtype": "character", "aspect_ratio": [788, 1094], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "rabbit_character": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/rabbit_character_9ea44249-f6c2-4c56-a185-c7cdc0ba3aac.webp", "type": "image", "prompt": "Generate a full-body, front-facing illustration of a cute, friendly cartoon rabbit. White or light gray fur with long upright ears, pink inner ears, small pink nose, big friendly eyes, cheerful expression. Standing upright with paws visible. Simple rounded shapes, matching the art style of image 1. Child-friendly educational game aesthetic, bright colors. High resolution, professional quality, transparent background.", "description": "Friendly cartoon rabbit for the learning phase animal cards", "subtype": "character", "aspect_ratio": [532, 1224], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "master_explorer_badge": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/master_explorer_badge_940fbdd7-80bc-454b-b3ae-64916b06b589.webp", "type": "image", "prompt": "Generate a shiny gold circular achievement badge for 'Master Explorer'. High-quality metallic gold finish, featuring a large star in the center. Includes decorative ribbons, sparkles, and small animal paw print motifs around the edges. Prestigious and exciting for children, bright celebratory colors. High resolution, 3D rendered style but cartoonish. Transparent background.", "description": "Gold achievement badge for scores 6-8", "subtype": "ui", "aspect_ratio": [863, 815], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "junior_explorer_badge": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/junior_explorer_badge_6cd60671-b7ac-4819-8479-053039d3d7b8.webp", "type": "image", "prompt": "Generate a shiny silver circular achievement badge for 'Junior Explorer'. High-quality metallic silver finish, featuring a star in the center. Style matches image 1 exactly but with silver as the primary color. Includes decorative ribbons and sparkles. Encouraging and bright, child-friendly aesthetic. High resolution, transparent background.", "description": "Silver achievement badge for scores 4-5", "subtype": "ui", "aspect_ratio": [1024, 967], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "animal_friend_badge": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/animal_friend_badge_ead9e19e-07d1-492e-9e32-80e8d2a3e297.webp", "type": "image", "prompt": "Generate a shiny bronze or copper circular achievement badge for 'Animal Friend'. High-quality metallic bronze finish, featuring a heart in the center. Style matches image 1 exactly but with warm bronze coloring. Includes decorative ribbons and small paw prints. Encouraging child-friendly aesthetic. High resolution, transparent background.", "description": "Bronze achievement badge for scores 1-3", "subtype": "ui", "aspect_ratio": [1024, 969], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "background_pattern_learning": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/background_pattern_learning_ef08220f-7510-43c5-a625-d0a47c92eeb8.webp", "type": "image", "prompt": "Generate a seamless, tileable 9:16 portrait background pattern featuring a vibrant, child-friendly forest scene. Design includes lush green trees, soft foliage, and a cheerful blue sky peeking through a canopy. Use warm, inviting colors and a clean cartoon illustration style suitable for an educational game. The composition should be soft and non-threatening, featuring leaves, branches, and natural textures. Ensure the pattern repeats perfectly in all directions and is designed to sit behind white UI elements and animal cards without being distracting.", "description": "A vibrant, child-friendly cartoon forest pattern that tiles seamlessly for the learning phase background.", "subtype": "background", "aspect_ratio": [1024, 1536], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "background_pattern_challenge": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/background_pattern_challenge_f1be9f99-2099-4792-a01f-568f5835a4f2.webp", "type": "image", "prompt": "Generate a 9:16 tall, vibrant and adventurous cartoon forest scene. The composition should feature dense, exciting forest scenery with tall tree trunks, lush foliage, and winding vines. Use a bright color palette of deep greens, rich browns, and natural earthy tones. The style is child-friendly, illustrated, and energetic, suitable for an adventure game. The image must be a seamless tileable pattern that repeats perfectly in all directions. Ensure the details are dynamic but not overwhelming, allowing for white text boxes and UI buttons to be clearly visible when overlaid. Professional quality, high resolution, 720x1280 aspect ratio style.", "description": "A vibrant and adventurous cartoon forest background that tiles seamlessly for the quiz and challenge phases.", "subtype": "background", "aspect_ratio": [832, 1248], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "cow_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/cow_sound_0fcb9076-37c4-4010-a95e-2365cd02d58c.mp3", "type": "audio", "prompt": "A friendly, clear cow 'moo' sound effect for a children's educational game. Warm, medium pitch, single recognizable vocalization, non-threatening and high quality.", "description": "A friendly and clear cow moo for the learning phase.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.9, "loop": false, "animation": null}, "duck_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/duck_sound_c64761c2-360b-4ec6-8e87-1aee85004cd1.mp3", "type": "audio", "prompt": "A cheerful and playful duck 'quack' sound effect. Medium-high pitch, single clear quack, friendly tone suitable for a toddler's learning app.", "description": "A cheerful and playful duck quack for the learning phase.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.87, "loop": false, "animation": null}, "lion_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/lion_sound_f7d1b9b9-28dd-435a-a30a-3fab06808504.mp3", "type": "audio", "prompt": "A majestic but friendly lion roar sound effect. Medium-low pitch, powerful but soft edges, not scary or aggressive, appropriate for young children.", "description": "A majestic but friendly lion roar for the learning phase.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.32, "loop": false, "animation": null}, "monkey_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/monkey_sound_aa1a4469-5327-48a8-b1ad-9d56f1968168.mp3", "type": "audio", "prompt": "Playful monkey chattering sound effect, energetic 'ooh ooh ah ah' vocalizations, fun and engaging atmosphere for a children's game.", "description": "Playful monkey chattering for the learning phase.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.25, "loop": false, "animation": null}, "rabbit_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/rabbit_sound_03817c29-0332-4072-9bb1-b719470cce61.mp3", "type": "audio", "prompt": "Soft, gentle rabbit sound effect, cute high-pitched sniffing and tiny squeaks, quiet and delicate for a children's educational game.", "description": "Soft and cute rabbit sniffing and squeaking sound.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.82, "loop": false, "animation": null}, "correct_answer_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/correct_answer_sound_8abd7e88-1643-4fd2-ba8b-8011e1aaa205.mp3", "type": "audio", "prompt": "Cheerful positive success chime, bright uplifting bell sound with a short happy melody, rewarding and encouraging for kids, 90s adventure game item vibe.", "description": "Rewarding chime for correct answers in the challenge and quiz.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.43, "loop": false, "animation": null}, "wrong_answer_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/wrong_answer_sound_58617dc1-9e70-4484-aef8-d633128ff63e.mp3", "type": "audio", "prompt": "Gentle 'try again' sound effect, soft descending 'boop' tone, non-punitive, encouraging and light, suitable for a kind educational environment.", "description": "Soft, non-discouraging sound for incorrect answers.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.0, "loop": false, "animation": null}, "celebration_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/celebration_sound_192dbd29-b271-4c7e-83b9-f5d7728a79be.mp3", "type": "audio", "prompt": "Joyful celebration fanfare, upbeat triumphant melody with cheerful brass and bells, high energy achievement sound for earning a badge, child-friendly victory.", "description": "Triumphant fanfare for the final results screen.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 4.0, "loop": false, "animation": null}}, "game_code": "<!DOCTYPE html>\n\n<html lang=\"ms\">\n<head>\n<meta charset=\"utf-8\"/>\n<title>Peneroka Haiwan Bijak</title>\n<meta content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" name=\"viewport\"/>\n<link href=\"https://fonts.googleapis.com\" rel=\"preconnect\"/>\n<link crossorigin=\"\" href=\"https://fonts.gstatic.com\" rel=\"preconnect\"/>\n<link href=\"https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&amp;family=Poppins:wght@400;600&amp;display=swap\" rel=\"stylesheet\"/>\n<style>/* ==================================================\n * CSS Reset and Base Styles\n * ================================================== */\n* {\n    margin: 0;\n    padding: 0;\n    box-sizing: border-box;\n}\n\nbody, html {\n    width: 100%;\n    height: 100%;\n    overflow: hidden;\n    touch-action: none;\n    -ms-touch-action: none;\n    font-family: 'Poppins', sans-serif;\n}\n\n/* ==================================================\n * CSS Custom Properties\n * ================================================== */\n:root {\n    /* Colors from gameConfig */\n    --color-primary: #4A90E2;\n    --color-secondary: #FFD93D;\n    --color-success: #6BCF7F;\n    --color-error: #FF6B6B;\n    --color-background: #FFF9E6;\n    --color-text: #2C3E50;\n    \n    /* Animation Timings */\n    --transition-fast: 150ms;\n    --transition-normal: 300ms;\n    --transition-slow: 500ms;\n    \n    /* Easing Functions */\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    /* Spacing Scale (8px grid) */\n    --space-xs: 8px;\n    --space-sm: 16px;\n    --space-md: 24px;\n    --space-lg: 32px;\n    --space-xl: 48px;\n}\n\n/* ==================================================\n * Game World Container\n * ================================================== */\n#game-world {\n    position: relative;\n    width: 100%;\n    height: 100%;\n    background-color: var(--color-background);\n    overflow: hidden;\n    filter: none;\n    -webkit-filter: none;\n}\n\n/* ==================================================\n * Game Screens\n * ================================================== */\n.game-screen {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    padding: var(--space-sm);\n    padding-bottom: var(--space-md);\n    opacity: 1;\n    transition: opacity var(--transition-normal) var(--ease-out);\n}\n\n.game-screen.hidden {\n    opacity: 0;\n    pointer-events: none;\n}\n\n/* ==================================================\n * Screen Headers and Titles\n * ================================================== */\n.screen-header {\n    width: 100%;\n    text-align: center;\n    margin-bottom: var(--space-md);\n}\n\n.screen-title {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 48px;\n    font-weight: 700;\n    color: var(--color-primary);\n    margin-bottom: var(--space-sm);\n    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);\n}\n\n.instruction-text {\n    font-size: 26px;\n    color: var(--color-text);\n    font-weight: 600;\n}\n\n/* ==================================================\n * Part 1: Animal Cards\n * ================================================== */\n.animal-cards-grid {\n    display: grid;\n    grid-template-columns: repeat(2, 1fr);\n    gap: var(--space-md);\n    width: 100%;\n    max-width: 640px;\n    margin: var(--space-sm) auto;\n    flex-shrink: 0;\n}\n\n.animal-card {\n    background: white;\n    border-radius: 24px;\n    padding: var(--space-md);\n    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);\n    cursor: pointer;\n    transition: transform var(--transition-fast) var(--ease-out),\n                box-shadow var(--transition-fast) var(--ease-out);\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    min-height: 280px;\n    border: 4px solid;\n    touch-action: manipulation;\n}\n\n.animal-card:hover {\n    transform: translateY(-4px);\n    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);\n}\n\n.animal-card:active {\n    transform: scale(0.95);\n}\n\n.animal-card:focus-visible {\n    outline: 4px solid var(--color-primary);\n    outline-offset: 2px;\n}\n\n.animal-card.selected {\n    animation: pulse 0.5s var(--ease-bounce);\n}\n\n.animal-card:nth-child(5) {\n    grid-column: 1 / -1;\n    max-width: 320px;\n    margin: 0 auto;\n}\n\n.animal-image {\n    width: 160px;\n    height: 160px;\n    object-fit: contain;\n    margin-bottom: var(--space-sm);\n    pointer-events: none;\n}\n\n.animal-name {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 28px;\n    font-weight: 700;\n    color: var(--color-text);\n    text-align: center;\n}\n\n/* ==================================================\n * Animal Modal (Warkah Haiwan)\n * ================================================== */\n.modal-overlay {\n    position: fixed;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    background: rgba(0, 0, 0, 0.5);\n    z-index: 100;\n    transition: opacity var(--transition-normal) var(--ease-out);\n}\n\n.modal-overlay.hidden {\n    opacity: 0;\n    pointer-events: none;\n}\n\n.animal-modal {\n    position: fixed;\n    top: 50%;\n    left: 50%;\n    transform: translate(-50%, -50%) scale(0.8);\n    width: 90%;\n    max-width: 600px;\n    max-height: 80vh;\n    background: linear-gradient(135deg, #FFF8DC 0%, #FFFACD 50%, #FFF8DC 100%);\n    border: 8px solid #D4A574;\n    border-radius: 30px;\n    padding: var(--space-lg);\n    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4),\n                inset 0 1px 0 rgba(255, 255, 255, 0.8);\n    z-index: 101;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    transition: transform var(--transition-normal) var(--ease-bounce),\n                opacity var(--transition-normal) var(--ease-out);\n    overflow-y: auto;\n}\n\n.animal-modal.hidden {\n    opacity: 0;\n    pointer-events: none;\n    transform: translate(-50%, -50%) scale(0.5);\n}\n\n.animal-modal:not(.hidden) {\n    opacity: 1;\n    transform: translate(-50%, -50%) scale(1);\n}\n\n/* Modal Close Button */\n.modal-close-btn {\n    position: absolute;\n    top: var(--space-md);\n    right: var(--space-md);\n    width: 80px;\n    height: 80px;\n    background: linear-gradient(135deg, var(--color-error), #E53935);\n    color: white;\n    border: none;\n    border-radius: 50%;\n    font-size: 48px;\n    font-weight: 700;\n    cursor: pointer;\n    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);\n    transition: transform var(--transition-fast) var(--ease-out),\n                box-shadow var(--transition-fast) var(--ease-out);\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    touch-action: manipulation;\n    z-index: 102;\n}\n\n.modal-close-btn:hover {\n    transform: scale(1.1);\n    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);\n}\n\n.modal-close-btn:active {\n    transform: scale(0.95);\n}\n\n.modal-close-btn:focus-visible {\n    outline: 4px solid var(--color-primary);\n    outline-offset: 4px;\n}\n\n/* Modal Scroll Decorations */\n.modal-scroll-decoration {\n    width: 100%;\n    height: 40px;\n    background: repeating-linear-gradient(\n        90deg,\n        #D4A574 0px,\n        #D4A574 20px,\n        #C19A6B 20px,\n        #C19A6B 40px\n    );\n    border-radius: 50%;\n    margin: var(--space-sm) 0;\n    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);\n}\n\n.modal-scroll-decoration.top {\n    transform: scaleY(0.6);\n}\n\n.modal-scroll-decoration.bottom {\n    transform: scaleY(0.6);\n}\n\n/* Modal Content */\n.modal-content {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    width: 100%;\n    text-align: center;\n}\n\n.modal-animal-image-container {\n    width: 200px;\n    height: 200px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    margin-bottom: var(--space-md);\n    background: rgba(255, 255, 255, 0.6);\n    border-radius: 20px;\n    padding: var(--space-md);\n    box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n\n.modal-animal-image {\n    width: 100%;\n    height: 100%;\n    object-fit: contain;\n    pointer-events: none;\n}\n\n.modal-animal-name {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 44px;\n    font-weight: 700;\n    color: var(--color-primary);\n    margin-bottom: var(--space-md);\n    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);\n}\n\n.modal-animal-info {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 32px;\n    font-weight: 600;\n    color: var(--color-text);\n    line-height: 1.6;\n    margin: 0;\n    padding: 0 var(--space-md);\n}\n\n/* Modal Animation */\n@keyframes modalSlideIn {\n    0% {\n        transform: translate(-50%, -50%) scale(0.5) rotateX(-20deg);\n        opacity: 0;\n    }\n    100% {\n        transform: translate(-50%, -50%) scale(1) rotateX(0deg);\n        opacity: 1;\n    }\n}\n\n.animal-modal:not(.hidden) {\n    animation: modalSlideIn var(--transition-normal) var(--ease-bounce);\n}\n\n/* ==================================================\n * Buttons\n * ================================================== */\n.big-button {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 32px;\n    font-weight: 700;\n    padding: var(--space-md) var(--space-xl);\n    border: none;\n    border-radius: 20px;\n    cursor: pointer;\n    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);\n    transition: transform var(--transition-fast) var(--ease-out),\n                box-shadow var(--transition-fast) var(--ease-out);\n    min-width: 300px;\n    min-height: 96px;\n    margin-top: auto;\n    margin-bottom: var(--space-sm);\n    touch-action: manipulation;\n}\n\n.big-button:hover {\n    transform: translateY(-2px);\n    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.25);\n}\n\n.big-button:active {\n    transform: scale(0.95);\n}\n\n.big-button:focus-visible {\n    outline: 4px solid var(--color-primary);\n    outline-offset: 4px;\n}\n\n.big-button.hidden {\n    opacity: 0;\n    pointer-events: none;\n}\n\n.primary-button {\n    background: linear-gradient(135deg, var(--color-primary), #357ABD);\n    color: white;\n}\n\n/* ==================================================\n * Progress Bar and Score Display\n * ================================================== */\n.progress-bar {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    width: 100%;\n    max-width: 640px;\n    margin: 0 auto var(--space-md);\n    padding: var(--space-sm) var(--space-md);\n    background: white;\n    border-radius: 16px;\n    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n}\n\n.progress-bar span {\n    font-size: 28px;\n    font-weight: 600;\n    color: var(--color-text);\n}\n\n.score-display {\n    color: var(--color-primary);\n    font-weight: 700;\n}\n\n/* ==================================================\n * Question Container\n * ================================================== */\n.question-container {\n    width: 100%;\n    max-width: 640px;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    flex: 1;\n    justify-content: center;\n}\n\n.question-text {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 40px;\n    font-weight: 600;\n    color: var(--color-text);\n    text-align: center;\n    margin-bottom: var(--space-xl);\n    line-height: 1.4;\n    padding: 0 var(--space-md);\n    text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.8);\n}\n\n/* ==================================================\n * Answer Buttons (Part 2)\n * ================================================== */\n.answer-buttons-container {\n    width: 100%;\n    display: flex;\n    flex-direction: column;\n    gap: var(--space-md);\n    margin-bottom: var(--space-lg);\n}\n\n.answer-button {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 28px;\n    font-weight: 600;\n    padding: var(--space-lg);\n    background: linear-gradient(135deg, var(--color-secondary), #FFB93D);\n    color: var(--color-text);\n    border: none;\n    border-radius: 20px;\n    cursor: pointer;\n    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);\n    transition: transform var(--transition-fast) var(--ease-out),\n                box-shadow var(--transition-fast) var(--ease-out);\n    min-height: 120px;\n    width: 100%;\n    touch-action: manipulation;\n}\n\n.answer-button:hover {\n    transform: translateY(-2px);\n    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);\n}\n\n.answer-button:active {\n    transform: scale(0.95);\n}\n\n.answer-button:disabled {\n    opacity: 0.6;\n    cursor: not-allowed;\n}\n\n.answer-button:focus-visible {\n    outline: 4px solid var(--color-primary);\n    outline-offset: 4px;\n}\n\n/* ==================================================\n * True/False Buttons (Part 3)\n * ================================================== */\n.tf-buttons-container {\n    width: 100%;\n    display: flex;\n    gap: var(--space-md);\n    margin-bottom: var(--space-lg);\n}\n\n.tf-button {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 32px;\n    font-weight: 700;\n    padding: var(--space-lg);\n    border: none;\n    border-radius: 20px;\n    cursor: pointer;\n    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);\n    transition: transform var(--transition-fast) var(--ease-out),\n                box-shadow var(--transition-fast) var(--ease-out);\n    min-height: 120px;\n    flex: 1;\n    color: white;\n    touch-action: manipulation;\n}\n\n.true-button {\n    background: linear-gradient(135deg, var(--color-success), #4CAF50);\n}\n\n.false-button {\n    background: linear-gradient(135deg, var(--color-error), #E53935);\n}\n\n.tf-button:hover {\n    transform: translateY(-2px);\n    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);\n}\n\n.tf-button:active {\n    transform: scale(0.95);\n}\n\n.tf-button:disabled {\n    opacity: 0.6;\n    cursor: not-allowed;\n}\n\n.tf-button:focus-visible {\n    outline: 4px solid white;\n    outline-offset: 4px;\n}\n\n/* ==================================================\n * Feedback Messages\n * ================================================== */\n.feedback-message {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 36px;\n    font-weight: 700;\n    text-align: center;\n    padding: var(--space-md);\n    border-radius: 16px;\n    margin-top: var(--space-md);\n    transition: opacity var(--transition-normal) var(--ease-out),\n                transform var(--transition-normal) var(--ease-out);\n}\n\n.feedback-message.hidden {\n    opacity: 0;\n    transform: scale(0.8);\n}\n\n.feedback-message.correct {\n    background: var(--color-success);\n    color: white;\n    animation: feedbackPop 0.5s var(--ease-bounce);\n}\n\n.feedback-message.incorrect {\n    background: var(--color-error);\n    color: white;\n    animation: shake 0.5s var(--ease-out);\n}\n\n/* ==================================================\n * Results Screen\n * ================================================== */\n.results-container {\n    width: 100%;\n    max-width: 640px;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    flex: 1;\n    z-index: 10;\n    position: relative;\n}\n\n.badge-container {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    margin: var(--space-lg) 0;\n    animation: badgeAppear 1s var(--ease-bounce);\n}\n\n.badge-image {\n    width: 300px;\n    height: 300px;\n    object-fit: contain;\n    filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.3));\n    animation: rotate360 2s ease-in-out;\n    pointer-events: none;\n}\n\n.badge-title {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 44px;\n    font-weight: 700;\n    color: var(--color-primary);\n    margin-top: var(--space-md);\n    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);\n}\n\n.final-score-display {\n    text-align: center;\n    margin: var(--space-lg) 0;\n}\n\n.final-score {\n    font-family: 'Fredoka', sans-serif;\n    font-size: 36px;\n    font-weight: 700;\n    color: var(--color-text);\n    margin-bottom: var(--space-sm);\n}\n\n.badge-message {\n    font-size: 26px;\n    color: var(--color-text);\n    font-weight: 600;\n    line-height: 1.4;\n}\n\n/* ==================================================\n * Confetti Canvas\n * ================================================== */\n.confetti-canvas {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    pointer-events: none;\n    z-index: 5;\n}\n\n/* ==================================================\n * Animations\n * ================================================== */\n@keyframes pulse {\n    0%, 100% {\n        transform: scale(1);\n    }\n    50% {\n        transform: scale(1.05);\n    }\n}\n\n@keyframes feedbackPop {\n    0% {\n        transform: scale(0.5);\n        opacity: 0;\n    }\n    50% {\n        transform: scale(1.1);\n    }\n    100% {\n        transform: scale(1);\n        opacity: 1;\n    }\n}\n\n@keyframes shake {\n    0%, 100% {\n        transform: translateX(0);\n    }\n    25% {\n        transform: translateX(-10px);\n    }\n    75% {\n        transform: translateX(10px);\n    }\n}\n\n@keyframes badgeAppear {\n    0% {\n        transform: scale(0) rotate(-180deg);\n        opacity: 0;\n    }\n    60% {\n        transform: scale(1.2) rotate(20deg);\n    }\n    100% {\n        transform: scale(1) rotate(0deg);\n        opacity: 1;\n    }\n}\n\n@keyframes rotate360 {\n    0% {\n        transform: rotate(0deg);\n    }\n    100% {\n        transform: rotate(360deg);\n    }\n}\n\n/* ==================================================\n * Background Patterns\n * ================================================== */\n#part1-screen {\n    background-size: cover;\n    background-position: center;\n    background-repeat: no-repeat;\n    background-attachment: fixed;\n    background-color: var(--color-background);\n    image-rendering: crisp-edges;\n    image-rendering: -webkit-optimize-contrast;\n}\n\n#part2-screen,\n#part3-screen {\n    background-size: cover;\n    background-position: center;\n    background-repeat: no-repeat;\n    background-attachment: fixed;\n    background-color: var(--color-background);\n    image-rendering: crisp-edges;\n    image-rendering: -webkit-optimize-contrast;\n}\n\n#results-screen {\n    background-size: cover;\n    background-position: center;\n    background-repeat: no-repeat;\n    background-attachment: fixed;\n    background-color: var(--color-background);\n    image-rendering: crisp-edges;\n    image-rendering: -webkit-optimize-contrast;\n}\n\n/* ==================================================\n * Reduced Motion Support\n * ================================================== */\n@media (prefers-reduced-motion: reduce) {\n    *,\n    *::before,\n    *::after {\n        animation-duration: 0.01ms !important;\n        animation-iteration-count: 1 !important;\n        transition-duration: 0.01ms !important;\n    }\n    \n    .animal-card:hover {\n        transform: none;\n    }\n    \n    .big-button:hover,\n    .answer-button:hover,\n    .tf-button:hover {\n        transform: none;\n    }\n}\n\n/* ==================================================\n * Utility Classes\n * ================================================== */\n.hidden {\n    display: none !important;\n}\n</style>\n</head>\n<body>\n<div id=\"game-world\">\n<div class=\"game-screen\" id=\"part1-screen\">\n<div class=\"screen-header\">\n<h1 class=\"screen-title\">Kenali Haiwan-Haiwan!</h1>\n<p class=\"instruction-text\">Ketik seekor haiwan untuk mengetahui tentangnya!</p>\n</div>\n<div class=\"animal-cards-grid\" id=\"animal-cards-container\">\n</div>\n<!-- Animal Letter Modal (Warkah Haiwan) -->\n<div class=\"modal-overlay hidden\" id=\"animal-modal-overlay\"></div>\n<div class=\"animal-modal hidden\" id=\"animal-modal\">\n<button aria-label=\"Tutup\" class=\"modal-close-btn\" id=\"modal-close-btn\">\u2715</button>\n<div class=\"modal-scroll-decoration top\"></div>\n<div class=\"modal-content\">\n<div class=\"modal-animal-image-container\" id=\"modal-animal-image-container\">\n<img alt=\"Haiwan\" class=\"modal-animal-image\" id=\"modal-animal-image\" src=\"\"/>\n</div>\n<h2 class=\"modal-animal-name\" id=\"modal-animal-name\"></h2>\n<p class=\"modal-animal-info\" id=\"modal-animal-info\"></p>\n</div>\n<div class=\"modal-scroll-decoration bottom\"></div>\n</div>\n<button class=\"big-button primary-button hidden\" id=\"start-challenge-btn\">\n                Mulai Cabar Minda! \ud83d\ude80\n            </button>\n</div>\n<div class=\"game-screen hidden\" id=\"part2-screen\">\n<div class=\"screen-header\">\n<div class=\"progress-bar\">\n<span id=\"part2-progress\">Soalan 1 daripada 3</span>\n<span class=\"score-display\" id=\"part2-score\">Skor: 0/5</span>\n</div>\n</div>\n<div class=\"question-container\">\n<h2 class=\"question-text\" id=\"part2-question\">Soalan akan muncul di sini</h2>\n<div class=\"answer-buttons-container\">\n<button class=\"answer-button\" data-answer=\"0\">Jawapan 1</button>\n<button class=\"answer-button\" data-answer=\"1\">Jawapan 2</button>\n</div>\n<div class=\"feedback-message hidden\" id=\"part2-feedback\"></div>\n</div>\n</div>\n<div class=\"game-screen hidden\" id=\"part3-screen\">\n<div class=\"screen-header\">\n<h1 class=\"screen-title\">Ujian Akhir!</h1>\n<div class=\"progress-bar\">\n<span id=\"part3-progress\">Soalan 1 daripada 2</span>\n<span class=\"score-display\" id=\"part3-score\">Jumlah Skor: 0/5</span>\n</div>\n</div>\n<div class=\"question-container\">\n<h2 class=\"question-text\" id=\"part3-question\">Soalan Betul atau Salah</h2>\n<div class=\"tf-buttons-container\">\n<button class=\"tf-button true-button\" data-answer=\"true\">Betul</button>\n<button class=\"tf-button false-button\" data-answer=\"false\">Salah</button>\n</div>\n<div class=\"feedback-message hidden\" id=\"part3-feedback\"></div>\n</div>\n</div>\n<div class=\"game-screen hidden\" id=\"results-screen\">\n<div class=\"results-container\">\n<h1 class=\"screen-title\">Tahniah!</h1>\n<div class=\"badge-container\" id=\"badge-display\">\n<img alt=\"Lencana\" class=\"badge-image\" id=\"badge-image\" src=\"\"/>\n<h2 class=\"badge-title\" id=\"badge-title\">Peneroka Hebat!</h2>\n</div>\n<div class=\"final-score-display\">\n<p class=\"final-score\" id=\"final-score-text\">Anda mendapat skor 5 daripada 5!</p>\n<p class=\"badge-message\" id=\"badge-message\">TAHNIAH! ANDA PENEROKA HEBAT</p>\n</div>\n<button class=\"big-button primary-button\" id=\"play-again-btn\">\n                    Main Lagi! \ud83c\udfae\n                </button>\n</div>\n<canvas class=\"confetti-canvas\" id=\"confetti-canvas\"></canvas>\n</div>\n</div>\n<script>(() => {\n  // game.js\n  var currentMode = \"play\";\n  var audioContext = null;\n  var audioBuffers = {};\n  var imageCache = {};\n  var currentPart = 1;\n  var part2Score = 0;\n  var part3Score = 0;\n  var currentQuestionIndex = 0;\n  var hasInteractedWithAnimal = false;\n  var awaitingNextQuestion = false;\n  async function preloadAssets() {\n    lib.log(\"Memuatkan aset...\");\n    audioContext = new (window.AudioContext || window.webkitAudioContext)();\n    const config = window.gameConfig;\n    const assetPromises = [];\n    for (const animal of config.animals) {\n      const imgAsset = lib.getAsset(animal.imageAssetId);\n      if (imgAsset) {\n        const imgPromise = new Promise((resolve, reject) => {\n          const img = new Image();\n          img.onload = () => {\n            imageCache[animal.imageAssetId] = img;\n            resolve();\n          };\n          img.onerror = () => {\n            lib.log(\"Gagal memuatkan imej: \" + animal.imageAssetId);\n            resolve();\n          };\n          img.src = imgAsset.url;\n        });\n        assetPromises.push(imgPromise);\n      }\n      const soundAsset = lib.getAsset(animal.soundAssetId);\n      if (soundAsset) {\n        const soundPromise = fetch(soundAsset.url).then((response) => response.arrayBuffer()).then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer)).then((audioBuffer) => {\n          audioBuffers[animal.soundAssetId] = audioBuffer;\n        }).catch((error) => {\n          lib.log(\"Gagal memuatkan bunyi: \" + animal.soundAssetId + \" - \" + error.message);\n        });\n        assetPromises.push(soundPromise);\n      }\n    }\n    const correctSound = lib.getAsset(\"correct_answer_sound\");\n    if (correctSound) {\n      const promise = fetch(correctSound.url).then((response) => response.arrayBuffer()).then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer)).then((audioBuffer) => {\n        audioBuffers[\"correct_answer_sound\"] = audioBuffer;\n      }).catch((error) => {\n        lib.log(\"Gagal memuatkan bunyi betul: \" + error.message);\n      });\n      assetPromises.push(promise);\n    }\n    const wrongSound = lib.getAsset(\"wrong_answer_sound\");\n    if (wrongSound) {\n      const promise = fetch(wrongSound.url).then((response) => response.arrayBuffer()).then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer)).then((audioBuffer) => {\n        audioBuffers[\"wrong_answer_sound\"] = audioBuffer;\n      }).catch((error) => {\n        lib.log(\"Gagal memuatkan bunyi salah: \" + error.message);\n      });\n      assetPromises.push(promise);\n    }\n    const celebrationSound = lib.getAsset(\"celebration_sound\");\n    if (celebrationSound) {\n      const promise = fetch(celebrationSound.url).then((response) => response.arrayBuffer()).then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer)).then((audioBuffer) => {\n        audioBuffers[\"celebration_sound\"] = audioBuffer;\n      }).catch((error) => {\n        lib.log(\"Gagal memuatkan bunyi perayaan: \" + error.message);\n      });\n      assetPromises.push(promise);\n    }\n    for (const badgeKey in config.badges) {\n      const badge = config.badges[badgeKey];\n      const badgeAsset = lib.getAsset(badge.assetId);\n      if (badgeAsset) {\n        const imgPromise = new Promise((resolve, reject) => {\n          const img = new Image();\n          img.onload = () => {\n            imageCache[badge.assetId] = img;\n            resolve();\n          };\n          img.onerror = () => {\n            lib.log(\"Gagal memuatkan lencana: \" + badge.assetId);\n            resolve();\n          };\n          img.src = badgeAsset.url;\n        });\n        assetPromises.push(imgPromise);\n      }\n    }\n    const bgLearning = lib.getAsset(\"background_pattern_learning\");\n    if (bgLearning) {\n      const bgPromise = new Promise((resolve) => {\n        const img = new Image();\n        img.onload = () => {\n          imageCache[\"background_pattern_learning\"] = img;\n          document.getElementById(\"part1-screen\").style.backgroundImage = `url('${bgLearning.url}')`;\n          resolve();\n        };\n        img.onerror = () => {\n          lib.log(\"Gagal memuatkan latar belakang pembelajaran\");\n          resolve();\n        };\n        img.src = bgLearning.url;\n      });\n      assetPromises.push(bgPromise);\n    }\n    const bgChallenge = lib.getAsset(\"background_pattern_challenge\");\n    if (bgChallenge) {\n      const bgPromise = new Promise((resolve) => {\n        const img = new Image();\n        img.onload = () => {\n          imageCache[\"background_pattern_challenge\"] = img;\n          document.getElementById(\"part2-screen\").style.backgroundImage = `url('${bgChallenge.url}')`;\n          document.getElementById(\"part3-screen\").style.backgroundImage = `url('${bgChallenge.url}')`;\n          document.getElementById(\"results-screen\").style.backgroundImage = `url('${bgChallenge.url}')`;\n          resolve();\n        };\n        img.onerror = () => {\n          lib.log(\"Gagal memuatkan latar belakang cabaran\");\n          resolve();\n        };\n        img.src = bgChallenge.url;\n      });\n      assetPromises.push(bgPromise);\n    }\n    await Promise.all(assetPromises);\n    lib.log(\"Semua aset telah dimuatkan dengan berjaya\");\n  }\n  function playSound(assetId) {\n    if (!audioContext || !audioBuffers[assetId]) {\n      lib.log(\"Bunyi tidak tersedia: \" + assetId);\n      return;\n    }\n    if (audioContext.state === \"suspended\") {\n      audioContext.resume();\n    }\n    const source = audioContext.createBufferSource();\n    source.buffer = audioBuffers[assetId];\n    source.connect(audioContext.destination);\n    source.start(0);\n  }\n  function setupPart1() {\n    lib.log(\"Menyediakan Bahagian 1: Fasa Belajar\");\n    const container = document.getElementById(\"animal-cards-container\");\n    container.innerHTML = \"\";\n    const config = window.gameConfig;\n    config.animals.forEach((animal, index) => {\n      const card = document.createElement(\"div\");\n      card.className = \"animal-card\";\n      card.style.borderColor = animal.color;\n      card.dataset.animalId = animal.id;\n      card.tabIndex = 0;\n      card.role = \"button\";\n      card.setAttribute(\"aria-label\", `Ketahui tentang ${animal.name}`);\n      const img = document.createElement(\"img\");\n      img.className = \"animal-image\";\n      img.src = imageCache[animal.imageAssetId]?.src || \"\";\n      img.alt = animal.name;\n      const name = document.createElement(\"div\");\n      name.className = \"animal-name\";\n      name.textContent = animal.name;\n      card.appendChild(img);\n      card.appendChild(name);\n      container.appendChild(card);\n      card.addEventListener(\"click\", () => handleAnimalClick(animal));\n      card.addEventListener(\"keydown\", (e) => {\n        if (e.key === \"Enter\" || e.key === \" \") {\n          e.preventDefault();\n          handleAnimalClick(animal);\n        }\n      });\n    });\n    const startBtn = document.getElementById(\"start-challenge-btn\");\n    startBtn.classList.add(\"hidden\");\n    startBtn.addEventListener(\"click\", startPart2);\n    const closeBtn = document.getElementById(\"modal-close-btn\");\n    closeBtn.addEventListener(\"click\", closeAnimalModal);\n    const overlay = document.getElementById(\"animal-modal-overlay\");\n    overlay.addEventListener(\"click\", closeAnimalModal);\n    const modal = document.getElementById(\"animal-modal\");\n    modal.addEventListener(\"click\", (e) => {\n      e.stopPropagation();\n    });\n  }\n  function handleAnimalClick(animal) {\n    lib.log(\"Haiwan diklik: \" + animal.name);\n    playSound(animal.soundAssetId);\n    showAnimalModal(animal);\n    const allCards = document.querySelectorAll(\".animal-card\");\n    allCards.forEach((card) => card.classList.remove(\"selected\"));\n    const clickedCard = document.querySelector(`[data-animal-id=\"${animal.id}\"]`);\n    if (clickedCard) {\n      clickedCard.classList.add(\"selected\");\n    }\n    if (!hasInteractedWithAnimal) {\n      hasInteractedWithAnimal = true;\n      const startBtn = document.getElementById(\"start-challenge-btn\");\n      startBtn.classList.remove(\"hidden\");\n    }\n  }\n  function showAnimalModal(animal) {\n    lib.log(\"Menunjukkan modal haiwan: \" + animal.name);\n    document.getElementById(\"modal-animal-name\").textContent = animal.name;\n    document.getElementById(\"modal-animal-info\").textContent = animal.modalText;\n    const modalImage = document.getElementById(\"modal-animal-image\");\n    modalImage.src = imageCache[animal.imageAssetId]?.src || \"\";\n    modalImage.alt = animal.name;\n    const overlay = document.getElementById(\"animal-modal-overlay\");\n    const modal = document.getElementById(\"animal-modal\");\n    overlay.classList.remove(\"hidden\");\n    modal.classList.remove(\"hidden\");\n  }\n  function closeAnimalModal() {\n    lib.log(\"Menutup modal haiwan\");\n    const overlay = document.getElementById(\"animal-modal-overlay\");\n    const modal = document.getElementById(\"animal-modal\");\n    overlay.classList.add(\"hidden\");\n    modal.classList.add(\"hidden\");\n  }\n  function startPart2() {\n    lib.log(\"Memulai Bahagian 2: Cabar Minda\");\n    currentPart = 2;\n    part2Score = 0;\n    currentQuestionIndex = 0;\n    awaitingNextQuestion = false;\n    document.getElementById(\"part1-screen\").classList.add(\"hidden\");\n    document.getElementById(\"part2-screen\").classList.remove(\"hidden\");\n    showPart2Question();\n  }\n  function showPart2Question() {\n    const config = window.gameConfig;\n    const question = config.part2Questions[currentQuestionIndex];\n    if (!question) {\n      startPart3();\n      return;\n    }\n    lib.log(`Bahagian 2 Soalan ${currentQuestionIndex + 1}`);\n    document.getElementById(\"part2-progress\").textContent = `Soalan ${currentQuestionIndex + 1} daripada ${config.part2Questions.length}`;\n    const maxScore = config.part2Questions.length + config.part3Questions.length;\n    document.getElementById(\"part2-score\").textContent = `Skor: ${part2Score}/${maxScore}`;\n    document.getElementById(\"part2-question\").textContent = question.question;\n    const answers = [question.correctAnswer, question.wrongAnswer];\n    const shuffled = Math.random() < 0.5 ? answers : [answers[1], answers[0]];\n    const buttons = document.querySelectorAll(\".answer-button\");\n    buttons.forEach((btn, index) => {\n      btn.textContent = shuffled[index];\n      btn.disabled = false;\n      btn.dataset.isCorrect = shuffled[index] === question.correctAnswer ? \"true\" : \"false\";\n    });\n    document.getElementById(\"part2-feedback\").classList.add(\"hidden\");\n    awaitingNextQuestion = false;\n  }\n  function handlePart2Answer(button) {\n    if (awaitingNextQuestion)\n      return;\n    const isCorrect = button.dataset.isCorrect === \"true\";\n    const feedback = document.getElementById(\"part2-feedback\");\n    if (isCorrect) {\n      part2Score++;\n      feedback.textContent = \"PANDAINYA! \\u{1F31F}\";\n      feedback.className = \"feedback-message correct\";\n      playSound(\"correct_answer_sound\");\n      const maxScore = window.gameConfig.part2Questions.length + window.gameConfig.part3Questions.length;\n      document.getElementById(\"part2-score\").textContent = `Skor: ${part2Score}/${maxScore}`;\n      document.querySelectorAll(\".answer-button\").forEach((btn) => btn.disabled = true);\n      awaitingNextQuestion = true;\n      setTimeout(() => {\n        currentQuestionIndex++;\n        showPart2Question();\n      }, 1500);\n    } else {\n      feedback.textContent = \"CUBA LAGI! \\u2764\\uFE0F\";\n      feedback.className = \"feedback-message incorrect\";\n      playSound(\"wrong_answer_sound\");\n    }\n    feedback.classList.remove(\"hidden\");\n  }\n  function startPart3() {\n    lib.log(\"Memulai Bahagian 3: Ujian Akhir\");\n    currentPart = 3;\n    part3Score = 0;\n    currentQuestionIndex = 0;\n    awaitingNextQuestion = false;\n    document.getElementById(\"part2-screen\").classList.add(\"hidden\");\n    document.getElementById(\"part3-screen\").classList.remove(\"hidden\");\n    showPart3Question();\n  }\n  function showPart3Question() {\n    const config = window.gameConfig;\n    const question = config.part3Questions[currentQuestionIndex];\n    if (!question) {\n      showResults();\n      return;\n    }\n    lib.log(`Bahagian 3 Soalan ${currentQuestionIndex + 1}`);\n    document.getElementById(\"part3-progress\").textContent = `Soalan ${currentQuestionIndex + 1} daripada ${config.part3Questions.length}`;\n    const totalScore = part2Score + part3Score;\n    const maxScore = config.part2Questions.length + config.part3Questions.length;\n    document.getElementById(\"part3-score\").textContent = `Jumlah Skor: ${totalScore}/${maxScore}`;\n    document.getElementById(\"part3-question\").textContent = question.question;\n    document.querySelectorAll(\".tf-button\").forEach((btn) => {\n      btn.disabled = false;\n    });\n    document.getElementById(\"part3-feedback\").classList.add(\"hidden\");\n    awaitingNextQuestion = false;\n  }\n  function handlePart3Answer(button) {\n    if (awaitingNextQuestion)\n      return;\n    const config = window.gameConfig;\n    const question = config.part3Questions[currentQuestionIndex];\n    const userAnswer = button.dataset.answer === \"true\";\n    const isCorrect = userAnswer === question.correctAnswer;\n    const feedback = document.getElementById(\"part3-feedback\");\n    if (isCorrect) {\n      part3Score++;\n      feedback.textContent = \"PANDAINYA! \\u{1F31F}\";\n      feedback.className = \"feedback-message correct\";\n      playSound(\"correct_answer_sound\");\n      const totalScore = part2Score + part3Score;\n      const maxScore = config.part2Questions.length + config.part3Questions.length;\n      document.getElementById(\"part3-score\").textContent = `Jumlah Skor: ${totalScore}/${maxScore}`;\n      document.querySelectorAll(\".tf-button\").forEach((btn) => btn.disabled = true);\n      awaitingNextQuestion = true;\n      setTimeout(() => {\n        currentQuestionIndex++;\n        showPart3Question();\n      }, 1500);\n    } else {\n      feedback.textContent = \"CUBA LAGI! \\u2764\\uFE0F\";\n      feedback.className = \"feedback-message incorrect\";\n      playSound(\"wrong_answer_sound\");\n    }\n    feedback.classList.remove(\"hidden\");\n  }\n  function showResults() {\n    lib.log(\"Menunjukkan skrin keputusan\");\n    currentPart = 4;\n    const config = window.gameConfig;\n    const totalScore = part2Score + part3Score;\n    const maxScore = config.part2Questions.length + config.part3Questions.length;\n    document.getElementById(\"part3-screen\").classList.add(\"hidden\");\n    document.getElementById(\"results-screen\").classList.remove(\"hidden\");\n    let badge = config.badges.friend;\n    if (totalScore >= config.badges.master.minScore) {\n      badge = config.badges.master;\n    } else if (totalScore >= config.badges.junior.minScore) {\n      badge = config.badges.junior;\n    }\n    const badgeImg = document.getElementById(\"badge-image\");\n    badgeImg.src = imageCache[badge.assetId]?.src || \"\";\n    badgeImg.alt = badge.name;\n    document.getElementById(\"badge-title\").textContent = badge.name;\n    document.getElementById(\"final-score-text\").textContent = `Anda mendapat skor ${totalScore} daripada ${maxScore}!`;\n    document.getElementById(\"badge-message\").textContent = \"TAHNIAH! ANDA PENEROKA HEBAT\";\n    playSound(\"celebration_sound\");\n    startConfetti();\n  }\n  var confettiAnimationId = null;\n  function startConfetti() {\n    const canvas = document.getElementById(\"confetti-canvas\");\n    const ctx = canvas.getContext(\"2d\");\n    canvas.width = 720;\n    canvas.height = 1280;\n    const confetti = [];\n    const colors = [\"#FFD93D\", \"#4A90E2\", \"#6BCF7F\", \"#FF6B6B\", \"#9C27B0\", \"#FF9800\"];\n    for (let i = 0; i < 100; i++) {\n      confetti.push({\n        x: Math.random() * canvas.width,\n        y: Math.random() * canvas.height - canvas.height,\n        size: Math.random() * 8 + 4,\n        speedY: Math.random() * 3 + 2,\n        speedX: Math.random() * 2 - 1,\n        color: colors[Math.floor(Math.random() * colors.length)],\n        rotation: Math.random() * 360,\n        rotationSpeed: Math.random() * 10 - 5\n      });\n    }\n    function animate() {\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n      let allOffScreen = true;\n      confetti.forEach((piece) => {\n        piece.y += piece.speedY;\n        piece.x += piece.speedX;\n        piece.rotation += piece.rotationSpeed;\n        if (piece.y < canvas.height) {\n          allOffScreen = false;\n        }\n        ctx.save();\n        ctx.translate(piece.x, piece.y);\n        ctx.rotate(piece.rotation * Math.PI / 180);\n        ctx.fillStyle = piece.color;\n        ctx.fillRect(-piece.size / 2, -piece.size / 2, piece.size, piece.size);\n        ctx.restore();\n      });\n      if (!allOffScreen) {\n        confettiAnimationId = requestAnimationFrame(animate);\n      } else {\n        confettiAnimationId = null;\n      }\n    }\n    animate();\n  }\n  function stopConfetti() {\n    if (confettiAnimationId) {\n      cancelAnimationFrame(confettiAnimationId);\n      confettiAnimationId = null;\n    }\n    const canvas = document.getElementById(\"confetti-canvas\");\n    const ctx = canvas.getContext(\"2d\");\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\n  }\n  function playAgain() {\n    lib.log(\"Memulakan semula permainan\");\n    stopConfetti();\n    currentPart = 1;\n    part2Score = 0;\n    part3Score = 0;\n    currentQuestionIndex = 0;\n    hasInteractedWithAnimal = false;\n    awaitingNextQuestion = false;\n    const part1Screen = document.getElementById(\"part1-screen\");\n    const part2Screen = document.getElementById(\"part2-screen\");\n    const part3Screen = document.getElementById(\"part3-screen\");\n    const resultsScreen = document.getElementById(\"results-screen\");\n    if (part1Screen)\n      part1Screen.classList.remove(\"hidden\");\n    if (part2Screen)\n      part2Screen.classList.add(\"hidden\");\n    if (part3Screen)\n      part3Screen.classList.add(\"hidden\");\n    if (resultsScreen)\n      resultsScreen.classList.add(\"hidden\");\n    const startBtn = document.getElementById(\"start-challenge-btn\");\n    if (startBtn)\n      startBtn.classList.add(\"hidden\");\n  }\n  function setupKeyboardControls() {\n    document.addEventListener(\"keydown\", (e) => {\n      if (currentMode !== \"play\")\n        return;\n      if (currentPart === 2 && !awaitingNextQuestion) {\n        const buttons = document.querySelectorAll(\".answer-button\");\n        if (e.key === \"1\" && buttons[0]) {\n          buttons[0].click();\n        } else if (e.key === \"2\" && buttons[1]) {\n          buttons[1].click();\n        }\n      } else if (currentPart === 3 && !awaitingNextQuestion) {\n        if (e.key.toLowerCase() === \"t\") {\n          document.querySelector(\".true-button\")?.click();\n        } else if (e.key.toLowerCase() === \"f\") {\n          document.querySelector(\".false-button\")?.click();\n        }\n      } else if (currentPart === 4) {\n        if (e.key.toLowerCase() === \"r\" || e.key === \"Enter\") {\n          playAgain();\n        }\n      }\n    });\n  }\n  function setupEditMode() {\n    lib.log(\"Menyediakan Mod Edit\");\n    lib.showGameParameters({\n      name: \"Tetapan Permainan\",\n      params: {\n        \"Warna Utama\": {\n          key: \"colors.primary\",\n          type: \"color\",\n          onChange: (value) => {\n            window.gameConfig.colors.primary = value;\n            document.documentElement.style.setProperty(\"--color-primary\", value);\n          }\n        },\n        \"Warna Sekunder\": {\n          key: \"colors.secondary\",\n          type: \"color\",\n          onChange: (value) => {\n            window.gameConfig.colors.secondary = value;\n            document.documentElement.style.setProperty(\"--color-secondary\", value);\n          }\n        },\n        \"Warna Kejayaan\": {\n          key: \"colors.success\",\n          type: \"color\",\n          onChange: (value) => {\n            window.gameConfig.colors.success = value;\n            document.documentElement.style.setProperty(\"--color-success\", value);\n          }\n        },\n        \"Warna Ralat\": {\n          key: \"colors.error\",\n          type: \"color\",\n          onChange: (value) => {\n            window.gameConfig.colors.error = value;\n            document.documentElement.style.setProperty(\"--color-error\", value);\n          }\n        },\n        \"Warna Latar Belakang\": {\n          key: \"colors.background\",\n          type: \"color\",\n          onChange: (value) => {\n            window.gameConfig.colors.background = value;\n            document.documentElement.style.setProperty(\"--color-background\", value);\n          }\n        }\n      }\n    });\n    setupPart1();\n  }\n  async function run(mode) {\n    lib.log(\"run() dipanggil. Mod: \" + mode);\n    currentMode = mode;\n    try {\n      await preloadAssets();\n      const colors = window.gameConfig.colors;\n      document.documentElement.style.setProperty(\"--color-primary\", colors.primary);\n      document.documentElement.style.setProperty(\"--color-secondary\", colors.secondary);\n      document.documentElement.style.setProperty(\"--color-success\", colors.success);\n      document.documentElement.style.setProperty(\"--color-error\", colors.error);\n      document.documentElement.style.setProperty(\"--color-background\", colors.background);\n      document.documentElement.style.setProperty(\"--color-text\", colors.text);\n      if (mode === \"edit\") {\n        setupEditMode();\n      } else {\n        lib.showGameParameters({\n          name: \"Tetapan Permainan\",\n          params: {}\n        });\n        setupPart1();\n        setupKeyboardControls();\n        document.querySelectorAll(\".answer-button\").forEach((btn) => {\n          btn.addEventListener(\"click\", () => handlePart2Answer(btn));\n        });\n        document.querySelectorAll(\".tf-button\").forEach((btn) => {\n          btn.addEventListener(\"click\", () => handlePart3Answer(btn));\n        });\n        document.getElementById(\"play-again-btn\").addEventListener(\"click\", playAgain);\n      }\n      lib.log(\"Permainan telah dimulakan dengan berjaya\");\n    } catch (error) {\n      lib.log(\"Ralat memulakan permainan: \" + error.message);\n    }\n  }\n  if (typeof window !== \"undefined\") {\n    window.run = run;\n  }\n})();\n</script>\n</body>\n</html>\n", "game_config": "{\n  \"animals\": [\n    {\n      \"id\": \"cow\",\n      \"name\": \"Lembu\",\n      \"food\": \"Rumput\",\n      \"habitat\": \"Ladang\",\n      \"modalText\": \"Saya tinggal di Ladang. Saya suka makan Rumput!\",\n      \"imageAssetId\": \"cow_character\",\n      \"soundAssetId\": \"cow_sound\",\n      \"color\": \"#8B4513\"\n    },\n    {\n      \"id\": \"duck\",\n      \"name\": \"Itik\",\n      \"food\": \"Biji-bijian dan Tumbuhan Air\",\n      \"habitat\": \"Kolam\",\n      \"modalText\": \"Saya tinggal di Sungai. Saya suka makan Cacing & Biji-bijian!\",\n      \"imageAssetId\": \"duck_character\",\n      \"soundAssetId\": \"duck_sound\",\n      \"color\": \"#FFD700\"\n    },\n    {\n      \"id\": \"lion\",\n      \"name\": \"Singa\",\n      \"food\": \"Daging\",\n      \"habitat\": \"Hutan Rimba\",\n      \"modalText\": \"Saya tinggal di Hutan Rimba. Saya suka makan Daging!\",\n      \"imageAssetId\": \"lion_character\",\n      \"soundAssetId\": \"lion_sound\",\n      \"color\": \"#FF8C00\"\n    },\n    {\n      \"id\": \"monkey\",\n      \"name\": \"Monyet\",\n      \"food\": \"Pisang\",\n      \"habitat\": \"Hutan\",\n      \"modalText\": \"Saya tinggal di Atas Pokok. Saya suka makan Pisang!\",\n      \"imageAssetId\": \"monkey_character\",\n      \"soundAssetId\": \"monkey_sound\",\n      \"color\": \"#8B4513\"\n    },\n    {\n      \"id\": \"rabbit\",\n      \"name\": \"Arnab\",\n      \"food\": \"Lobak Merah\",\n      \"habitat\": \"Padang Rumput\",\n      \"modalText\": \"Saya tinggal di Lubang Tanah. Saya suka makan Lobak Merah!\",\n      \"imageAssetId\": \"rabbit_character\",\n      \"soundAssetId\": \"rabbit_sound\",\n      \"color\": \"#C0C0C0\"\n    }\n  ],\n  \"part2Questions\": [\n    {\n      \"question\": \"Mana habitat Monyet?\",\n      \"correctAnswer\": \"Hutan\",\n      \"wrongAnswer\": \"Laut\"\n    },\n    {\n      \"question\": \"Apa makanan Lembu?\",\n      \"correctAnswer\": \"Rumput\",\n      \"wrongAnswer\": \"Ikan\"\n    },\n    {\n      \"question\": \"Bunyi 'Kuek' ini siapa?\",\n      \"correctAnswer\": \"Itik\",\n      \"wrongAnswer\": \"Singa\"\n    }\n  ],\n  \"part3Questions\": [\n    {\n      \"question\": \"Adakah Singa tinggal dalam air?\",\n      \"correctAnswer\": false\n    },\n    {\n      \"question\": \"Adakah Arnab makan lobak?\",\n      \"correctAnswer\": true\n    }\n  ],\n  \"colors\": {\n    \"primary\": \"#4A90E2\",\n    \"secondary\": \"#FFD93D\",\n    \"success\": \"#6BCF7F\",\n    \"error\": \"#FF6B6B\",\n    \"background\": \"#FFF9E6\",\n    \"text\": \"#2C3E50\"\n  },\n  \"badges\": {\n    \"master\": {\n      \"name\": \"Peneroka Hebat\",\n      \"minScore\": 4,\n      \"assetId\": \"master_explorer_badge\",\n      \"message\": \"Luar biasa! Anda seorang ahli haiwan yang sejati!\"\n    },\n    \"junior\": {\n      \"name\": \"Peneroka Muda\",\n      \"minScore\": 3,\n      \"assetId\": \"junior_explorer_badge\",\n      \"message\": \"Bagus! Anda belajar dengan sangat baik!\"\n    },\n    \"friend\": {\n      \"name\": \"Sahabat Haiwan\",\n      \"minScore\": 0,\n      \"assetId\": \"animal_friend_badge\",\n      \"message\": \"Hebat! Terus meneroka dan belajar!\"\n    }\n  }\n}\n", "game_design_document": "## Core Game Loop and Mechanics\n\nThe game follows a structured three-part educational journey:\n\n**Part 1: Introduction (Learning Phase)**\n- Players see 5 animal cards displayed on screen: Cow, Duck, Lion, Monkey, and Rabbit\n- Each card shows the animal's image with its name\n- When a player taps/clicks an animal card, it plays the animal's sound and displays educational text\n- Text format: \"I am a [Animal]. I love [Food] and I live in [Habitat]!\"\n- Players can explore all animals in any order before proceeding\n- A large \"Start Challenge!\" button appears after exploring at least one animal\n\n**Part 2: Brain Challenge (The Game)**\n- Players answer 5 \"Who Am I?\" multiple-choice questions\n- Each question presents a riddle with two answer buttons\n- Questions are presented one at a time\n- Correct answer: Show \"Genius! \ud83c\udf1f\" feedback, play success sound, award 1 point, move to next question\n- Wrong answer: Show \"Try again, Explorer! \u2764\ufe0f\" feedback, allow retry (no point deduction)\n- Progress indicator shows question number (e.g., \"Question 3 of 5\")\n- After all 5 questions, automatically transition to Part 3\n\n**Part 3: The Final Quiz (Assessment Phase)**\n- Players answer 3 True/False questions about animals\n- Each question has two large buttons: [True] and [False]\n- Same feedback system as Part 2\n- After completing all questions, show final results screen\n- Display total score out of 8 possible points (5 from Part 2 + 3 from Part 3)\n- Award \"Master Explorer\" badge if score is 6 or higher\n- Award \"Junior Explorer\" badge if score is 4-5\n- Award \"Animal Friend\" badge if score is 1-3\n- Show celebratory animation and sound\n- \"Play Again\" button to restart from Part 1\n\n## Starting Scope\n\n**First Implementation Must Include:**\n- All three parts fully functional\n- 5 animals with complete data (name, sound, food, habitat)\n- All 5 \"Who Am I?\" questions with correct/incorrect answer pairs\n- All 3 True/False questions\n- Score tracking and badge system\n- Touch-friendly UI with large buttons\n- Sound effects for animal sounds, correct answers, and wrong answers\n- Smooth transitions between parts\n\n**Future Enhancements (Not in First Version):**\n- Additional animals to learn about\n- More question variations\n- Difficulty levels\n- Animated animal characters\n- Customizable avatar for the explorer\n- Progress saving across sessions\n\n## Game Perspective\n\n**fixed-screen** - The game uses a single-screen layout with no scrolling. Each part of the game occupies the full 720\u00d71280 viewport with content centered and optimized for portrait mobile viewing.\n\n## Game World and Level Design\n\n**Part 1 Layout (Learning Phase):**\n- Title at top: \"Meet the Animals!\" (80px from top)\n- 5 animal cards arranged in a grid (2-2-1 layout)\n- Each card: 280\u00d7280px with animal image, name label below\n- Cards have colorful borders and subtle shadow\n- Instruction text: \"Tap an animal to learn about it!\"\n- Large \"Start Challenge!\" button at bottom (appears after first interaction)\n\n**Part 2 Layout (Brain Challenge):**\n- Progress indicator at top: \"Question X of 5\"\n- Score display: \"Score: X/5\"\n- Question text in large, readable font (centered, 200px from top)\n- Two answer buttons stacked vertically (each 600\u00d7120px)\n- Feedback area below buttons for \"Genius!\" or \"Try again!\" messages\n- Cute animal illustration in background (semi-transparent)\n\n**Part 3 Layout (Final Quiz):**\n- Title: \"Final Quiz!\"\n- Progress: \"Question X of 3\"\n- Total score from Part 2 displayed\n- Question text (centered)\n- Two buttons: [True] and [False] (each 300\u00d7120px, side by side)\n- Feedback area\n- Background with quiz theme\n\n**Results Screen:**\n- Large badge image (400\u00d7400px centered)\n- Badge title: \"Master Explorer!\" / \"Junior Explorer!\" / \"Animal Friend!\"\n- Final score: \"You scored X out of 8!\"\n- Encouraging message based on performance\n- Large \"Play Again!\" button (600\u00d7100px)\n- Confetti or star animation\n\n## Edit Mode Design\n\nPlayers can customize the following in Edit Mode:\n\n**Animal Data (Part 1):**\n- Each animal's food preference (text field)\n- Each animal's habitat (text field)\n- Animal images (upload custom images)\n- Animal sounds (upload custom audio)\n\n**Questions (Part 2):**\n- Question text for each of the 5 \"Who Am I?\" riddles\n- Correct answer text\n- Incorrect answer text (distractor)\n\n**True/False Questions (Part 3):**\n- Question text for each of the 3 statements\n- Correct answer (True or False toggle)\n\n**Visual Customization:**\n- Background colors for each part\n- Button colors\n- Text colors\n- Badge images\n\n**Constraints:**\n- Must have exactly 5 animals in Part 1\n- Must have exactly 5 questions in Part 2\n- Must have exactly 3 questions in Part 3\n- Question text limited to 150 characters\n- Animal names limited to 20 characters\n\n## Control Design\n\n**Mobile (Touch):**\n- Tap animal cards to hear sounds and see information\n- Tap answer buttons to submit answers\n- Tap \"Start Challenge!\" to proceed to Part 2\n- Tap \"Play Again!\" to restart\n- All interactive elements are minimum 96\u00d796px\n\n**Desktop (Mouse + Keyboard):**\n- Click animal cards to interact\n- Click answer buttons\n- Hover effects on all interactive elements\n- Keyboard support:\n  - Number keys 1-2 to select answers in Part 2\n  - T/F keys for True/False in Part 3\n  - Enter to proceed to next screen\n  - R to restart from results screen\n\n**Feedback:**\n- Visual feedback: buttons scale slightly on press\n- Audio feedback: sounds play on correct/incorrect answers\n- Haptic-like animation: gentle shake on wrong answer\n- Color feedback: green flash for correct, red flash for incorrect\n\n## Art Style and Visual Theme\n\n**Overall Style:**\n- Bright, colorful, child-friendly cartoon aesthetic\n- Rounded corners on all UI elements\n- High contrast for readability\n- Playful, educational tone\n\n**Color Palette:**\n- Primary: Bright blue (#4A90E2)\n- Secondary: Sunny yellow (#FFD93D)\n- Success: Vibrant green (#6BCF7F)\n- Error: Soft red (#FF6B6B)\n- Background: Light cream (#FFF9E6)\n- Text: Dark navy (#2C3E50)\n\n**Typography:**\n- Large, rounded, sans-serif fonts\n- High contrast text\n- Minimum 24px for body text\n- 36-48px for questions\n- 20px for labels\n\n**Animal Cards:**\n- Colorful borders (each animal gets unique color)\n- Soft drop shadows\n- White background for animal images\n- Name labels in bold, colorful text\n\n**Buttons:**\n- Large, rounded rectangles\n- Gradient fills\n- Subtle shadows\n- Clear icons or text\n- Pressed state with scale animation\n\n**Badges:**\n- Circular or shield-shaped\n- Metallic/shiny appearance\n- Gold for Master Explorer\n- Silver for Junior Explorer\n- Bronze for Animal Friend\n- Star decorations\n\n**Backgrounds:**\n- Subtle patterns or textures\n- Nature-themed elements (leaves, paw prints, clouds)\n- Non-distracting, light colors\n- Different theme per part (learning = nature, challenge = adventure, quiz = classroom)", "persistent_storage_config": null, "game_metadata": {"game_id": "01KPZW3402V9QVW4FG08HBYRJ1", "title": "The Smart Animal Explorer", "description": "Learn about animals, solve \"Who Am I?\" riddles, and earn your Master Explorer badge in this colorful educational adventure!", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_5a725283-59d0-42ac-a258-48bdfb6ea62f.webp", "created_at": 1777038709.0, "updated_at": 1777041773982.093, "art_style": null}}