{"asset_map": {"character_mannequin": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/character_mannequin_6f0d8829-2bca-4f20-aa06-7d73bf86cb6e.webp", "type": "image", "prompt": "Generate a full-body, front-facing 3D stylized character mannequin in a neutral T-pose. The character should have a smooth, rounded body with skin-toned surfaces, inspired by Pixar or Disney 3D character models. Include subtle specular highlights, soft ambient occlusion shadows in the neck and joints, and a slight cool rim light on the left edge. The character should have friendly facial features and a neutral expression. Proportions: head is approximately 1/5 of total height, with a clearly defined neck, shoulders, chest, waist, hips, and limbs. 3D rendered, high resolution, soft lighting, transparent background.", "description": "A 3D-rendered stylized mannequin character in a T-pose, designed as a base for a dress-up game.", "subtype": "character", "aspect_ratio": [558, 934], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "hat_pirate": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hat_pirate_48cd3fad-8e86-4510-91c0-9a0b07e37ec2.webp", "type": "image", "prompt": "Generate a classic black pirate tricorn hat with gold trim and a small white skull and crossbones emblem. Cartoon style, thick black outlines, vibrant colors. Front-facing view suitable for a character to wear. Transparent background.", "description": "A cartoon pirate tricorn hat with a skull emblem", "subtype": "object", "aspect_ratio": [1240, 721], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "hat_chef": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hat_chef_7aa48913-06e4-4e4b-8471-fd76ed3bf8f0.webp", "type": "image", "prompt": "Generate a tall white chef's toque with pleated top. Clean cartoon style, thick black outlines, bright white with subtle grey shading. Front-facing view. Transparent background.", "description": "A tall white pleated chef's hat", "subtype": "object", "aspect_ratio": [954, 1191], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "hat_beanie": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hat_beanie_b59750d3-9203-4595-9a2e-e8a01de25985.webp", "type": "image", "prompt": "Generate a cozy red knit beanie with a fluffy white pom-pom on top. Cartoon style, thick black outlines, visible knit texture. Front-facing view. Transparent background.", "description": "A red knit winter beanie with a pom-pom", "subtype": "object", "aspect_ratio": [907, 1178], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "hat_crown": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hat_crown_81e2a5f6-4991-4ded-b3cc-3cc5702f0faf.webp", "type": "image", "prompt": "Generate a shiny golden royal crown with red velvet interior and colorful inset jewels like rubies and emeralds. Cartoon style, thick black outlines. Front-facing view. Transparent background.", "description": "A golden royal crown with jewels", "subtype": "object", "aspect_ratio": [1202, 946], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "hat_space_helmet": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/hat_space_helmet_f0d22e89-acf8-433e-8cca-78e620d03962.webp", "type": "image", "prompt": "Generate a futuristic space helmet with a clear blue-tinted visor and white/silver body. Includes tech details like small vents. Cartoon style, thick black outlines. Front-facing view. Transparent background.", "description": "A futuristic white space helmet", "subtype": "object", "aspect_ratio": [852, 922], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "top_tshirt_casual": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/top_tshirt_casual_0ba1d559-37a2-4acc-93ff-8382f69ff858.webp", "type": "image", "prompt": "Generate a bright blue short-sleeved t-shirt. Cartoon style, thick black outlines, clean look with minor fabric wrinkles. Shown flat as a game asset. Transparent background.", "description": "A simple bright blue casual t-shirt", "subtype": "object", "aspect_ratio": [888, 885], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "top_leather_jacket": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/top_leather_jacket_8ad8c065-b0d1-42fe-b6e0-28c9dae1509f.webp", "type": "image", "prompt": "Generate a black leather jacket with silver zippers, studs, and collar details. Cartoon style, thick black outlines, edgy aesthetic. Shown flat as a game asset. Transparent background.", "description": "A black leather rock-and-roll jacket", "subtype": "object", "aspect_ratio": [912, 855], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "top_armor_chest": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/top_armor_chest_25d8716a-54dd-4695-8aea-1f885f785e1b.webp", "type": "image", "prompt": "Generate a medieval knight's chest plate armor in shiny silver with gold accents. Cartoon style, thick black outlines, showing rivets and metallic shine. Shown flat. Transparent background.", "description": "Silver medieval knight chest armor", "subtype": "object", "aspect_ratio": [949, 1007], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "top_chef_coat": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/top_chef_coat_18159d5c-1a8c-4118-880e-8b83b5eb16e1.webp", "type": "image", "prompt": "Generate a white double-breasted chef's coat with black buttons and a collar. Clean cartoon style, thick black outlines, crisp look. Shown flat with long sleeves. Transparent background.", "description": "A professional white chef's coat", "subtype": "object", "aspect_ratio": [934, 913], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "top_hawaiian_shirt": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/top_hawaiian_shirt_66a45123-091a-4bec-8ed8-d3d475f45db2.webp", "type": "image", "prompt": "Generate a vibrant Hawaiian shirt with tropical flower patterns in pink and yellow on light blue. Cartoon style, thick black outlines, short sleeves. Shown flat. Transparent background.", "description": "A colorful tropical Hawaiian shirt", "subtype": "object", "aspect_ratio": [1003, 923], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "bottom_jeans": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/bottom_jeans_e69afc0b-5800-4d5b-a228-7f65c479acd3.webp", "type": "image", "prompt": "Generate classic blue denim jeans with pockets and belt loops. Cartoon style, thick black outlines, medium blue with subtle denim texture. Shown flat. Transparent background.", "description": "Classic blue denim jeans", "subtype": "object", "aspect_ratio": [904, 1345], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "bottom_shorts_beach": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/bottom_shorts_beach_60403b86-a038-4d1f-8343-cf56ccd25200.webp", "type": "image", "prompt": "Generate bright orange swim shorts with a white wave pattern, drawstring, and pockets. Cartoon style, thick black outlines, summery look. Shown flat. Transparent background.", "description": "Bright orange wave-pattern swim shorts", "subtype": "object", "aspect_ratio": [1013, 892], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "bottom_skirt_fancy": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/bottom_skirt_fancy_a30c24e7-4c5c-4316-a150-76f3f18d5847.webp", "type": "image", "prompt": "Generate an elegant flowing purple skirt with subtle sparkles and shimmer. Cartoon style, thick black outlines, formal appearance. Shown flat. Transparent background.", "description": "An elegant purple sparkly skirt", "subtype": "object", "aspect_ratio": [884, 845], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "bottom_pants_space": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/bottom_pants_space_bb4c51a4-5873-45f2-8cfd-559365c189c2.webp", "type": "image", "prompt": "Generate futuristic space suit pants in white and silver with tech panels and colored stripes. Cartoon style, thick black outlines, sci-fi look. Shown flat. Transparent background.", "description": "Futuristic white space suit pants", "subtype": "object", "aspect_ratio": [754, 1383], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "bottom_cargo_pants": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/bottom_cargo_pants_6dc30432-007c-45f0-8fd7-9998f65b2a8b.webp", "type": "image", "prompt": "Generate khaki cargo pants with multiple side pockets. Cartoon style, thick black outlines, rugged explorer look. Shown flat. Transparent background.", "description": "Rugged khaki cargo pants for safari", "subtype": "object", "aspect_ratio": [815, 1337], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "shoes_sneakers": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/shoes_sneakers_c6f2fb32-567a-4040-932b-7051cd86c10a.webp", "type": "image", "prompt": "Generate a pair of red high-top sneakers with white laces and soles. Cartoon style, thick black outlines, shown side-by-side. Transparent background.", "description": "A pair of red high-top sneakers", "subtype": "object", "aspect_ratio": [1119, 790], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "shoes_boots_pirate": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/shoes_boots_pirate_cafe4ff8-6fee-4a3b-8f0d-bc4f09ce407b.webp", "type": "image", "prompt": "Generate a pair of brown leather pirate boots with folded tops and buckles. Cartoon style, thick black outlines, worn texture, side-by-side. Transparent background.", "description": "Brown leather pirate boots with buckles", "subtype": "object", "aspect_ratio": [1270, 889], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "shoes_space_boots": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/shoes_space_boots_a3196376-ae35-43db-be96-4473deacebc1.webp", "type": "image", "prompt": "Generate a pair of high-tech space boots in white and silver with thick soles. Cartoon style, thick black outlines, side-by-side. Transparent background.", "description": "Futuristic white and silver space boots", "subtype": "object", "aspect_ratio": [1413, 1018], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "shoes_heels_fancy": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/shoes_heels_fancy_eb6dd0fe-3b29-43e3-ac84-69337465baad.webp", "type": "image", "prompt": "Generate a pair of elegant sparkly silver high heels with thin straps. Cartoon style, thick black outlines, side-by-side. Transparent background.", "description": "Sparkly silver high heel shoes", "subtype": "object", "aspect_ratio": [1176, 887], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "shoes_flipflops": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/shoes_flipflops_eeabda1d-ac1c-4be0-bece-de7f4ba3e410.webp", "type": "image", "prompt": "Generate a pair of bright yellow flip-flops with simple straps. Cartoon style, thick black outlines, beachy look, side-by-side. Transparent background.", "description": "Yellow casual beach flip-flops", "subtype": "object", "aspect_ratio": [1148, 952], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "accessory_sunglasses": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/accessory_sunglasses_9e50cccb-572f-48d4-abeb-2f89910e5256.webp", "type": "image", "prompt": "Generate gold-framed aviator sunglasses with dark tinted lenses and a subtle shine. Cartoon style, thick black outlines. Transparent background.", "description": "Stylish gold aviator sunglasses", "subtype": "object", "aspect_ratio": [1235, 563], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "accessory_cape_hero": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/accessory_cape_hero_d331d2d5-3dd5-41ab-8a07-de14201e5d9b.webp", "type": "image", "prompt": "Generate a dramatic flowing red superhero cape with a gold neck clasp. Cartoon style, thick black outlines, showing slight movement. Transparent background.", "description": "A flowing red superhero cape", "subtype": "object", "aspect_ratio": [1163, 757], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "accessory_scarf_winter": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/accessory_scarf_winter_34ea1da8-3161-4fc4-9adb-bd5cb470bccd.webp", "type": "image", "prompt": "Generate a cozy green and white striped knit scarf with fringed ends. Cartoon style, thick black outlines, visible knit texture. Transparent background.", "description": "A green and white striped winter scarf", "subtype": "object", "aspect_ratio": [857, 1222], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "accessory_eyepatch": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/accessory_eyepatch_e42c47d2-25dc-4042-bcbe-39b65d599235.webp", "type": "image", "prompt": "Generate a classic black pirate eye patch with a thin string. Cartoon style, thick black outlines, simple iconic design. Transparent background.", "description": "A simple black pirate eye patch", "subtype": "object", "aspect_ratio": [886, 763], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "accessory_apron": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/accessory_apron_e4455dfd-8dfb-40d0-bf11-f961435861f4.webp", "type": "image", "prompt": "Generate a clean white chef's apron with a front pocket and straps. Cartoon style, thick black outlines, professional look. Transparent background.", "description": "A professional white chef's apron", "subtype": "object", "aspect_ratio": [943, 1268], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "background_beach": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/background_beach_6b6ad939-ad06-48b9-a731-a85013581b12.webp", "type": "image", "prompt": "Generate a 9:16 tall image of a simplified, soft pastel beach. Light blue sky, gentle waves, and yellow sand. Cartoon style, blurred background to avoid distraction, cheerful atmosphere.", "description": "Soft pastel beach background for the game screen", "subtype": "background", "aspect_ratio": [1024, 1536], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "background_space": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/background_space_69326916-e476-4b65-a785-adcc1c96567b.webp", "type": "image", "prompt": "Generate a 9:16 tall image of a soft pastel space scene. Dark blue and purple gradient with twinkling white stars and a distant planet. Cartoon style, simplified and blurred.", "description": "Soft pastel space background with stars", "subtype": "background", "aspect_ratio": [1024, 1536], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "background_castle": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/background_castle_8416b955-6888-4942-a4ca-b437437c63d5.webp", "type": "image", "prompt": "Generate a 9:16 tall image of a simplified medieval castle interior. Gray stone walls, soft banners, and gold accents. Cartoon style, soft pastel colors, blurred.", "description": "Soft pastel medieval castle interior background", "subtype": "background", "aspect_ratio": [1024, 1536], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "background_stage": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/background_stage_74d3690a-22bb-41ec-844f-1c338ceb11d5.webp", "type": "image", "prompt": "Generate a 9:16 tall image of a simplified concert stage. Soft purple and pink colors with gold lights and speaker silhouettes. Cartoon style, blurred background.", "description": "Soft pastel concert stage background", "subtype": "background", "aspect_ratio": [1024, 1536], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "background_ballroom": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/background_ballroom_54c7b523-6986-4d25-b53f-fd26ac8c261f.webp", "type": "image", "prompt": "Generate a 9:16 tall image of an elegant ballroom. Gold and cream colors, chandelier silhouettes, and soft curtains. Cartoon style, simplified and blurred.", "description": "Soft pastel elegant ballroom background", "subtype": "background", "aspect_ratio": [1024, 1536], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "sfx_item_pickup": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_item_pickup_8b6e6c12-2ae8-4dd2-81b8-95330ffe8fc8.mp3", "type": "audio", "prompt": "A clean, high-pitched cartoonish bubble pop sound effect. Short, snappy, and playful with a bright texture. No echo, suitable for rapid UI interaction in a mobile fashion game.", "description": "Short cheerful bubble pop sound when a player picks up a clothing item.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.82, "loop": false, "animation": null}, "sfx_item_place_correct": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_item_place_correct_817eb142-6469-423a-b5e9-b658a51dbd7d.mp3", "type": "audio", "prompt": "A rewarding musical chime sound effect, bright xylophone or glockenspiel note, rising pitch, crystalline and positive. Sparkling feel, designed for game feedback on a successful action.", "description": "Positive chime sound for a successful theme-matching item placement.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.39, "loop": false, "animation": null}, "sfx_item_place_wrong": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_item_place_wrong_f5591b80-fb6f-4fee-b710-15ae9bc171c9.mp3", "type": "audio", "prompt": "A soft, muted rubbery 'boop' or 'bonk' sound effect. Low-frequency, non-aggressive, neutral feedback. Like a soft ball bouncing on a padded surface. Short decay.", "description": "Neutral soft thud for placing an item that doesn't match the theme.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.94, "loop": false, "animation": null}, "sfx_round_complete": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_round_complete_eff7e3aa-59e9-4d3b-ac75-829abdd3e987.mp3", "type": "audio", "prompt": "A triumphant and upbeat mini-fanfare, ascending melodic motif, 4-5 notes, bright brass and synth bells. 120 BPM, high energy, celebratory feel for a level completion.", "description": "Short celebratory fanfare for finishing a round.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.52, "loop": false, "animation": null}, "sfx_timer_warning": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sfx_timer_warning_7ec741c7-54a5-4c8e-b3da-104f0e09413b.mp3", "type": "audio", "prompt": "A rhythmic, high-frequency percussive tick, like a mechanical clock or a woodblock metronome. Clean and isolated. Precise and steady, designed to be played in sequence to create urgency.", "description": "Urgent but gentle ticking for the final seconds of the round.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.98, "loop": false, "animation": null}, "music_gameplay": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/music_gameplay_163b996c-84d1-4ace-a5f1-1030724bc705.mp3", "type": "audio", "prompt": "Upbeat fashion runway pop music, 128 BPM, 4/4 time signature. Driving electronic bassline, rhythmic synth stabs, light house percussion with handclaps, and a cheerful lead melody. Energetic, playful, and chic. Loops seamlessly without abrupt transitions. High-quality production suitable for a fast-paced puzzle game.", "description": "High-energy fashion runway style background music.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30, "loop": true, "animation": null}, "char_robot": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/char_robot_85cad363-9c6b-4a2b-9b6c-fca827868352.webp", "type": "image", "prompt": "Generate a full-body, front-facing 3D-rendered metallic robot mannequin in a neutral T-pose, maintaining the same proportions as the 'character_mannequin'. Use a stylized 3D art style similar to Pixar robots. The character should have shiny chrome and silver metallic panels with strong specular reflections and environment mapping highlights. Features include blue glowing LED eyes, rounded panel edges with soft ambient occlusion in the seams, and a cool blue rim light on the silhouettes. Transparent background, high resolution, professional quality 3D asset, clean edges.", "description": "A 3D-rendered friendly metallic robot mannequin in a T-pose for the dress-up game.", "subtype": "character", "aspect_ratio": [493, 1248], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "char_alien": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/char_alien_e8d09acd-0d96-44fc-a444-72e7ca9e158b.webp", "type": "image", "prompt": "Generate a 3D-rendered cute green alien mannequin. Front-facing, full body picture, standing in a T-pose. Style is stylized 3D like a modern animation film with smooth lime-green skin, subtle subsurface scattering glow, and large shiny dark oval eyes with specular highlights. Soft ambient occlusion and warm rim lighting on the edges. Friendly Pixar-style proportions, matching the body structure and scale of the 'character_mannequin'. Professional quality, high resolution, transparent background, portrait orientation.", "description": "A 3D-rendered cute green alien mannequin in a T-pose for the dress-up game base character.", "subtype": "character", "aspect_ratio": [711, 1241], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "char_wood": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/char_wood_f639e815-f1c5-4dfc-9bef-c1591280ba8f.webp", "type": "image", "prompt": "Generate a high-quality 3D-rendered artist's jointed wooden mannequin. The figure should be front-facing, full body, in a T-pose, with proportions matching a standard human mannequin. Style: stylized 3D art, smooth warm oak-colored wood with visible grain texture, professional specular sheen, and realistic ambient occlusion in the ball-and-socket joints (shoulders, elbows, hips, knees). The joints should be clearly visible rounded spheres. Use warm rim lighting on the edges to define the silhouette. Set against a transparent background, portrait orientation, clean and polished game-ready asset.", "description": "A 3D-rendered jointed wooden artist's mannequin in a T-pose for character dress-up.", "subtype": "character", "aspect_ratio": [514, 1247], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "astra_mascot": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/astra_mascot_5c33b4e6-a2eb-45cd-924e-c31528ce771f.webp", "type": "image", "prompt": "Generate a portrait shot from the chest up of a friendly and stylish female mascot character named Astra. She has vibrant purple hair and is wearing a futuristic neon-accented fashion designer outfit. She has a confident, sparkling expression and bright, energetic vibes. High-quality cartoon/anime style, clean line art, professional digital illustration, transparent background.", "description": "Friendly and stylish female mascot character Astra with purple hair and futuristic outfit.", "subtype": "character", "aspect_ratio": [973, 1497], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Costume Chaos</title>\n    <link href=\"https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&family=Inter:wght@400;600;700&display=swap\" rel=\"stylesheet\">\n    <style>\n        /* CSS Reset */\n        *, *::before, *::after {\n            box-sizing: border-box;\n            margin: 0;\n            padding: 0;\n        }\n\n        /* Custom Properties */\n        :root {\n            --transition-fast: 150ms;\n            --transition-normal: 300ms;\n            --transition-slow: 500ms;\n            --ease-out: cubic-bezier(0.0, 0, 0.2, 1);\n            --ease-bounce: cubic-bezier(0.68, -0.6, 0.32, 1.6);\n            --space-xs: 8px;\n            --space-sm: 16px;\n            --space-md: 24px;\n            --space-lg: 32px;\n            --space-xl: 48px;\n            --primary: #ff00ff; /* Neon Pink */\n            --secondary: #8a2be2; /* Blue Violet */\n            --accent: #00ffff; /* Cyan */\n            --success: #4ade80;\n            --danger: #ff6b6b;\n            --neon-glow: 0 0 20px rgba(255, 0, 255, 0.6), 0 0 40px rgba(0, 255, 255, 0.4);\n        }\n\n        body, html {\n            margin: 0;\n            padding: 0;\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            touch-action: none;\n            -ms-touch-action: none;\n            font-family: 'Inter', sans-serif;\n        }\n\n        #game-world {\n            position: relative;\n            width: 100%;\n            height: 100%;\n            background: #000;\n            transition: background var(--transition-slow);\n        }\n\n        /* Game Canvas */\n        #game-canvas {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n        }\n\n        /* UI Overlay */\n        .ui-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n        }\n\n        .ui-overlay > * {\n            pointer-events: auto;\n        }\n\n        /* Header Section */\n        .game-header {\n            position: absolute;\n            top: 0;\n            left: 0;\n            right: 0;\n            height: 140px;\n            background: linear-gradient(180deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0) 100%);\n            padding: var(--space-sm);\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            gap: var(--space-xs);\n            z-index: 10;\n        }\n\n        .theme-display {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 40px;\n            font-weight: 700;\n            color: white;\n            text-shadow: \n                3px 3px 0 rgba(0,0,0,0.3),\n                -1px -1px 0 rgba(0,0,0,0.3),\n                1px -1px 0 rgba(0,0,0,0.3),\n                -1px 1px 0 rgba(0,0,0,0.3);\n            text-align: center;\n            animation: slideDown var(--transition-slow) var(--ease-bounce);\n        }\n\n        .timer-display {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 32px;\n            font-weight: 600;\n            color: white;\n            text-shadow: 2px 2px 0 rgba(0,0,0,0.3);\n            display: flex;\n            align-items: center;\n            gap: var(--space-xs);\n        }\n\n        .timer-display.warning {\n            color: var(--danger);\n            animation: pulse 0.5s ease-in-out infinite;\n        }\n\n        /* Footer Section */\n        .game-footer {\n            position: absolute;\n            bottom: 0;\n            left: 0;\n            right: 0;\n            height: 120px;\n            background: linear-gradient(0deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0) 100%);\n            padding: var(--space-sm);\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            gap: var(--space-xs);\n            z-index: 10;\n        }\n\n        .score-display {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 36px;\n            font-weight: 700;\n            color: white;\n            text-shadow: 2px 2px 0 rgba(0,0,0,0.3);\n        }\n\n        .feedback-display {\n            font-size: 24px;\n            font-weight: 600;\n            color: var(--accent);\n            text-shadow: 2px 2px 0 rgba(0,0,0,0.3);\n            min-height: 32px;\n        }\n\n        .stars-display {\n            display: flex;\n            gap: 5px;\n        }\n\n        .streak-active {\n            animation: flame-pulse 0.6s infinite ease-in-out;\n            color: #ff4500 !important;\n            text-shadow: 0 0 10px #ff0000, 0 0 20px #ff8c00 !important;\n        }\n\n        #rank-progress-fill {\n            background: linear-gradient(90deg, var(--primary), var(--accent));\n            box-shadow: 0 0 10px var(--primary);\n        }\n\n        .screen {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0, 0, 0, 0.9);\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            gap: var(--space-md);\n            padding: var(--space-lg);\n            opacity: 0;\n            pointer-events: none;\n            transition: opacity var(--transition-normal);\n            z-index: 100;\n            overflow-y: auto;\n        }\n\n        .screen.active {\n            opacity: 1;\n            pointer-events: auto;\n            animation: fadeIn var(--transition-normal);\n        }\n\n        @keyframes gradientShift {\n            0% { background-position: 0% 50%; }\n            50% { background-position: 100% 50%; }\n            100% { background-position: 0% 50%; }\n        }\n\n        .screen-title {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 80px;\n            font-weight: 700;\n            text-align: center;\n            animation: bounceIn 0.8s var(--ease-bounce);\n            background: linear-gradient(to bottom, #ffffff 20%, #ff00ff 50%, #00ffff 80%);\n            -webkit-background-clip: text;\n            -webkit-text-fill-color: transparent;\n            filter: drop-shadow(0 0 15px rgba(255, 0, 255, 0.7));\n            letter-spacing: -2px;\n            margin-bottom: -20px;\n        }\n\n        .screen-subtitle {\n            font-size: 28px;\n            font-weight: 600;\n            color: #fff;\n            text-shadow: 0 0 10px rgba(0, 255, 255, 0.8);\n            text-align: center;\n            opacity: 0.9;\n        }\n\n        .screen-text {\n            font-size: 24px;\n            color: white;\n            text-align: center;\n            max-width: 600px;\n        }\n\n        /* Tabs / Menu */\n        .menu-container {\n            width: 100%;\n            max-width: 650px;\n            display: flex;\n            flex-direction: column;\n            gap: var(--space-md);\n            z-index: 5;\n        }\n\n        .tab-buttons {\n            display: flex;\n            justify-content: center;\n            gap: var(--space-xs);\n            margin-bottom: var(--space-md);\n            flex-wrap: wrap;\n        }\n        .tab-button {\n            background: rgba(0, 0, 0, 0.4);\n            color: #fff;\n            border: 2px solid rgba(255, 255, 255, 0.5);\n            padding: 12px 24px;\n            border-radius: 30px;\n            font-size: 20px;\n            font-weight: 700;\n            cursor: pointer;\n            transition: all var(--transition-fast);\n            backdrop-filter: blur(5px);\n            text-transform: uppercase;\n            letter-spacing: 1px;\n            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);\n        }\n\n        .tab-button.active {\n            background: #fff;\n            color: var(--primary);\n            border-color: #fff;\n            box-shadow: 0 0 20px rgba(255, 255, 255, 0.6);\n            transform: scale(1.1);\n        }\n\n        /* Lists (Shop, Levels) */\n        .item-grid {\n            display: grid;\n            grid-template-columns: repeat(2, 1fr);\n            gap: var(--space-md);\n            width: 100%;\n            padding: var(--space-sm);\n        }\n\n        .card {\n            background: rgba(0, 0, 0, 0.4);\n            border-radius: 24px;\n            padding: var(--space-md);\n            display: flex; flex-direction: column;\n            align-items: center;\n            gap: var(--space-sm);\n            border: 3px solid rgba(255, 255, 255, 0.3);\n            transition: all var(--transition-fast);\n            cursor: pointer;\n            position: relative;\n            backdrop-filter: blur(10px);\n        }\n\n        .card:hover {\n            transform: translateY(-8px);\n            border-color: var(--accent);\n            background: rgba(0, 0, 0, 0.5);\n        }\n\n        .card.locked {\n            opacity: 0.7;\n            cursor: not-allowed;\n            filter: grayscale(0.5);\n        }\n\n        .card.selected {\n            border-color: #fff;\n            background: rgba(255, 0, 255, 0.3);\n            box-shadow: 0 0 30px rgba(255, 0, 255, 0.5);\n        }\n\n        .lookbook-item {\n            position: relative;\n            background: rgba(255, 255, 255, 0.1);\n            border-radius: 20px;\n            padding: 10px;\n            border: 2px solid rgba(255, 255, 255, 0.2);\n            height: 280px;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            overflow: hidden;\n        }\n\n        .lookbook-preview {\n            width: 100%;\n            height: 200px;\n            position: relative;\n            background: rgba(0, 0, 0, 0.2);\n            border-radius: 15px;\n            margin-bottom: 10px;\n        }\n\n        .lookbook-preview canvas {\n            width: 100%;\n            height: 100%;\n            object-fit: contain;\n        }\n\n        .lookbook-delete {\n            position: absolute;\n            top: 5px;\n            right: 5px;\n            background: var(--danger);\n            color: white;\n            border: none;\n            border-radius: 50%;\n            width: 30px;\n            height: 30px;\n            cursor: pointer;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 16px;\n            z-index: 2;\n        }\n\n        .card-img {\n            width: 120px;\n            height: 120px;\n            object-fit: contain;\n            filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.3));\n        }\n\n        .card-title {\n            color: #fff;\n            font-size: 26px;\n            font-weight: 700;\n            text-align: center;\n            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);\n        }\n\n        .card-info {\n            color: var(--accent);\n            font-size: 20px;\n            font-weight: 600;\n            text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);\n        }\n\n        /* Particles */\n        .particles-container {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            z-index: 1;\n        }\n\n        .bg-particle {\n            position: absolute;\n            background: #fff;\n            border-radius: 50%;\n            opacity: 0.2;\n            animation: floatUp 10s infinite linear;\n        }\n\n        @keyframes floatUp {\n            0% { transform: translateY(1300px) rotate(0deg); opacity: 0; }\n            10% { opacity: 0.2; }\n            90% { opacity: 0.2; }\n            100% { transform: translateY(-100px) rotate(360deg); opacity: 0; }\n        }\n\n        /* Buttons */\n        .button {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 28px;\n            font-weight: 600;\n            padding: var(--space-md) var(--space-xl);\n            background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);\n            color: white;\n            border: 4px solid white;\n            border-radius: 48px;\n            cursor: pointer;\n            transition: transform var(--transition-fast), box-shadow var(--transition-fast);\n            box-shadow: 0 8px 0 rgba(0,0,0,0.3);\n            text-shadow: 2px 2px 0 rgba(0,0,0,0.3);\n            min-width: 200px;\n            text-align: center;\n        }\n\n        #play-now-button {\n            width: 75%;\n            height: 120px;\n            font-size: 56px;\n            border-radius: 60px;\n            background: linear-gradient(90deg, #ff00ff, #8a2be2);\n            border: 5px solid #fff;\n            box-shadow: 0 0 30px rgba(255, 0, 255, 0.6), inset 0 0 15px rgba(255, 255, 255, 0.5);\n            text-transform: uppercase;\n            letter-spacing: 4px;\n            animation: buttonPulse 2s infinite ease-in-out;\n            margin: 20px 0;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n        }\n\n        @keyframes buttonPulse {\n            0%, 100% { transform: scale(1); box-shadow: 0 0 30px rgba(255, 0, 255, 0.6); }\n            50% { transform: scale(1.05); box-shadow: 0 0 50px rgba(255, 0, 255, 0.8); }\n        }\n\n        .button:hover {\n            transform: translateY(-4px);\n            box-shadow: 0 12px 0 rgba(0,0,0,0.3);\n        }\n\n        .button:active {\n            transform: translateY(2px);\n            box-shadow: 0 4px 0 rgba(0,0,0,0.3);\n        }\n\n        .button.secondary {\n            background: rgba(255, 255, 255, 0.1);\n        }\n\n        .streak-active {\n            animation: flame-pulse 0.6s infinite ease-in-out;\n            color: #ff4500 !important;\n            text-shadow: 0 0 10px #ff0000, 0 0 20px #ff8c00 !important;\n        }\n\n        @keyframes flame-pulse {\n            0%, 100% { transform: scale(1); filter: drop-shadow(0 0 5px #ff4500); }\n            50% { transform: scale(1.1) translateY(-5px); filter: drop-shadow(0 0 15px #ff8c00); }\n        }\n\n        #streak-flame {\n            display: none;\n            font-size: 48px;\n            margin-left: 10px;\n            vertical-align: middle;\n        }\n\n        /* Animations */\n        @keyframes slideDown {\n            from { transform: translateY(-100px); opacity: 0; }\n            to { transform: translateY(0); opacity: 1; }\n        }\n        @keyframes pulse {\n            0%, 100% { transform: scale(1); }\n            50% { transform: scale(1.1); }\n        }\n        @keyframes sparkle {\n            0%, 100% { opacity: 0; transform: scale(0.5) rotate(0deg); }\n            50% { opacity: 1; transform: scale(1) rotate(180deg); }\n        }\n        @keyframes fadeIn {\n            from { opacity: 0; }\n            to { opacity: 1; }\n        }\n        @keyframes bounceIn {\n            0% { transform: scale(0.3); opacity: 0; }\n            50% { transform: scale(1.05); opacity: 1; }\n            70% { transform: scale(0.9); }\n            100% { transform: scale(1); }\n        }\n        @keyframes float {\n            0%, 100% { transform: translateY(0); }\n            50% { transform: translateY(-10px); }\n        }\n\n        .pause-button, .reroll-button {\n            position: absolute;\n            top: 20px;\n            width: 60px;\n            height: 60px;\n            background: rgba(255, 255, 255, 0.2);\n            border: 2px solid white;\n            border-radius: 50%;\n            color: white;\n            font-size: 30px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            cursor: pointer;\n            z-index: 20;\n            transition: all var(--transition-fast);\n            pointer-events: auto;\n        }\n\n        .reroll-button {\n            right: 100px;\n        }\n\n        .pause-button:hover, .reroll-button:hover {\n            background: rgba(255, 255, 255, 0.4);\n            transform: scale(1.1);\n        }\n\n        .speech-bubble {\n            background: white;\n            color: var(--secondary);\n            padding: var(--space-sm) var(--space-md);\n            border-radius: 24px;\n            font-weight: 700;\n            font-size: 24px;\n            position: relative;\n            max-width: 80%;\n            text-align: center;\n            box-shadow: 0 10px 25px rgba(0,0,0,0.2);\n        }\n\n        .speech-bubble::after {\n            content: '';\n            position: absolute;\n            bottom: -18px;\n            left: 50%;\n            transform: translateX(-50%);\n            border-width: 15px 15px 0 15px;\n            border-style: solid;\n            border-color: white transparent transparent transparent;\n        }\n\n        .mascot-container {\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            gap: var(--space-sm);\n            margin: var(--space-md) 0;\n            animation: float 3s ease-in-out infinite;\n        }\n\n        .mascot-img {\n            width: 220px;\n            height: 220px;\n            object-fit: contain;\n            filter: drop-shadow(0 0 20px rgba(255, 0, 255, 0.5));\n        }\n    </style>\n</head>\n<body>\n    <div id=\"game-world\">\n        <canvas id=\"game-canvas\"></canvas>\n        \n        <div class=\"ui-overlay\">\n            <!-- Game HUD -->\n            <div id=\"gameplay-mascot\" style=\"display: none; position: absolute; bottom: 130px; right: 20px; z-index: 5; pointer-events: none; flex-direction: column; align-items: center; transition: opacity 0.3s;\">\n                <div id=\"gameplay-speech\" class=\"speech-bubble\" style=\"opacity: 0; transition: opacity 0.2s; font-size: 18px; padding: 10px 20px; margin-bottom: 10px; min-width: 100px;\">Nice pick!</div>\n                <img id=\"gameplay-astra-img\" src=\"\" style=\"width: 120px; height: 120px; filter: drop-shadow(0 0 10px rgba(255,0,255,0.4));\">\n            </div>\n\n            <div class=\"game-header\" id=\"game-ui-header\" style=\"display: none;\">\n                <div class=\"pause-button\" id=\"btn-pause\">\u23f8\ufe0f</div>\n                <div class=\"reroll-button\" id=\"btn-reroll\" title=\"Refresh Styles\">\ud83d\udd04</div>\n                <div class=\"hint-button\" id=\"btn-hint\" title=\"Get a Hint\" style=\"position: absolute; left: 20px; top: 20px; width: 60px; height: 60px; background: rgba(255, 255, 255, 0.2); border: 2px solid white; border-radius: 50%; color: white; font-size: 30px; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 20; transition: all var(--transition-fast); pointer-events: auto;\">\ud83d\udca1</div>\n                <div class=\"theme-display\" id=\"theme-display\"></div>\n                <div id=\"round-type-display\" style=\"font-family: 'Fredoka'; font-size: 24px; font-weight: 600; color: var(--accent); text-transform: uppercase; letter-spacing: 2px; margin-top: -10px; text-shadow: 2px 2px 0 rgba(0,0,0,0.3); opacity: 0.9;\">NORMAL ROUND</div>\n                <div class=\"timer-display\" id=\"timer-display\">\u23f1\ufe0f <span id=\"timer-value\">0:00</span></div>\n                <div id=\"combo-display\" style=\"position: absolute; right: 20px; top: 100px; font-family: 'Fredoka'; font-size: 32px; font-weight: 700; color: #ffd93d; text-shadow: 2px 2px 0 rgba(0,0,0,0.3); opacity: 0; transition: opacity 0.3s; pointer-events: none;\">COMBO X1</div>\n            </div>\n\n            <div class=\"game-footer\" id=\"game-ui-footer\" style=\"display: none;\">\n                <div style=\"display: flex; align-items: center; justify-content: center;\">\n                    <div class=\"score-display\" id=\"score-display\">Score: 0</div>\n                    <div id=\"streak-flame\">\ud83d\udd25</div>\n                </div>\n                <div class=\"feedback-display\" id=\"feedback-display\"></div>\n                <div class=\"stars-display\" id=\"stars-display\"></div>\n            </div>\n\n            <!-- Home Screen (Main Menu) -->\n            <div class=\"screen active\" id=\"home-screen\">\n                <div class=\"particles-container\" id=\"particles-container\"></div>\n                <div class=\"screen-title\">Costume Chaos</div>\n                <div class=\"screen-subtitle\">The Ultimate Fashion Challenge</div>\n                \n                <div class=\"menu-container\">\n                    <div class=\"tab-buttons\">\n                        <button class=\"tab-button active\" id=\"btn-play\" onclick=\"switchTab('play')\">Play</button>\n                        <button class=\"tab-button\" id=\"btn-shop\" onclick=\"switchTab('shop')\">Shop</button>\n                        <button class=\"tab-button\" id=\"btn-levels\" onclick=\"switchTab('levels')\">Levels</button>\n                        <button class=\"tab-button\" id=\"btn-lookbook\" onclick=\"switchTab('lookbook')\">Lookbook</button>\n                        <button class=\"tab-button\" id=\"btn-leaderboard\" onclick=\"switchTab('leaderboard')\">Leaderboard</button>\n                        <button class=\"tab-button\" id=\"btn-settings\" onclick=\"switchTab('settings')\">Settings</button>\n                    </div>\n\n                    <div id=\"tab-play\" class=\"tab-content\" style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%;\">\n                        <div class=\"mascot-container\">\n                            <div class=\"speech-bubble\">Astra: Ready for a challenge?</div>\n                            <img src=\"https://storage.googleapis.com/astrocade-assets/astra_mascot.png\" class=\"mascot-img\" id=\"home-astra-img\">\n                        </div>\n                        <div id=\"rank-container\" style=\"width: 100%; max-width: 450px; background: rgba(0,0,0,0.5); padding: 20px; border-radius: 30px; border: 2px solid rgba(255,255,255,0.1); margin-bottom: 25px; display: flex; flex-direction: column; align-items: center; gap: 10px; backdrop-filter: blur(10px);\">\n                            <div style=\"font-size: 16px; color: rgba(255,255,255,0.7); text-transform: uppercase; letter-spacing: 2px; font-weight: 700;\">Global Fashion Rank</div>\n                            <div id=\"rank-name\" style=\"font-family: 'Fredoka'; font-size: 42px; font-weight: 700; color: #ffd93d; text-shadow: 0 0 15px rgba(255, 217, 61, 0.4); margin-top: -5px;\">STYLE ROOKIE</div>\n                            <div id=\"rank-progress-bg\" style=\"width: 100%; height: 16px; background: rgba(255,255,255,0.1); border-radius: 8px; overflow: hidden; margin: 10px 0; border: 1px solid rgba(255,255,255,0.1);\">\n                                <div id=\"rank-progress-fill\" style=\"width: 0%; height: 100%; transition: width 1.5s cubic-bezier(0.34, 1.56, 0.64, 1);\"></div>\n                            </div>\n                            <div style=\"display: flex; justify-content: space-between; width: 100%; font-size: 16px; font-weight: 600;\">\n                                <span id=\"rank-accuracy-text\" style=\"color: var(--accent);\">Accuracy: 0%</span>\n                                <span id=\"rank-next-threshold\" style=\"color: white; opacity: 0.6;\">Next: Style Pro (50%)</span>\n                            </div>\n                        </div>\n\n                        <button class=\"button\" id=\"play-now-button\">Play Now</button>\n                    </div>\n\n                    <div id=\"tab-shop\" class=\"tab-content\" style=\"display: none;\">\n                        <div class=\"screen-subtitle shop-star-display\" style=\"color: #ffd93d; margin-bottom: 10px; text-align: center;\">Stars: 0</div>\n                        <div class=\"item-grid\" id=\"shop-grid\"></div>\n                    </div>\n\n                    <div id=\"tab-levels\" class=\"tab-content\" style=\"display: none;\">\n                        <div class=\"screen-subtitle level-star-display\" style=\"color: #ffd93d; margin-bottom: 10px; text-align: center;\">Stars: 0</div>\n                        <div class=\"item-grid\" id=\"levels-grid\"></div>\n                    </div>\n\n                    <div id=\"tab-lookbook\" class=\"tab-content\" style=\"display: none;\">\n                        <div class=\"screen-subtitle\" style=\"color: #fff; margin-bottom: 20px; text-align: center;\">Your Saved Styles</div>\n                        <div id=\"lookbook-grid\" class=\"item-grid\"></div>\n                        <div id=\"lookbook-empty\" style=\"text-align: center; color: rgba(255,255,255,0.5); font-style: italic; margin-top: 40px; display: none;\">No outfits saved yet! Finish a round to save your look.</div>\n                    </div>\n\n                    <div id=\"tab-leaderboard\" class=\"tab-content\" style=\"display: none;\">\n                        <div class=\"leaderboard-container\" id=\"leaderboard-container\">\n                            <div class=\"player-rank-display\" id=\"player-rank-display\">Loading...</div>\n                            <div id=\"leaderboard-list\"></div>\n                        </div>\n                    </div>\n\n                    <div id=\"tab-settings\" class=\"tab-content\" style=\"display: none; color: white;\">\n                        <div style=\"display: flex; flex-direction: column; gap: var(--space-sm); width: 100%;\">\n                            <div style=\"display: flex; justify-content: space-between; align-items: center;\">\n                                <label>Music</label>\n                                <input type=\"checkbox\" id=\"music-toggle\" checked>\n                            </div>\n                            <div style=\"display: flex; justify-content: space-between; align-items: center;\">\n                                <label>SFX</label>\n                                <input type=\"checkbox\" id=\"sfx-toggle\" checked>\n                            </div>\n                            <button class=\"button secondary\" id=\"reset-button\" style=\"margin-top: 20px;\">Reset Progress</button>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <div class=\"screen\" id=\"round-screen\" style=\"background: rgba(0,0,0,0.6); justify-content: space-between; padding: 80px 20px;\">\n                <div class=\"round-display\" id=\"round-text\" style=\"font-size: 56px; color: white; font-weight: 700; text-shadow: 0 4px 15px rgba(0,0,0,0.8); background: rgba(0,0,0,0.3); padding: 10px 40px; border-radius: 30px;\">Round Complete!</div>\n                \n                <div class=\"summary-bottom-ui\" style=\"display: flex; flex-direction: column; align-items: center; gap: 20px; width: 100%; background: rgba(0,0,0,0.4); padding: 30px; border-radius: 40px; backdrop-filter: blur(5px); border: 2px solid rgba(255,255,255,0.1);\">\n                    <div class=\"mascot-container\" style=\"margin: 0; animation: float 3s ease-in-out infinite;\">\n                        <div id=\"astra-speech\" class=\"speech-bubble\">Astra: Heroic look!</div>\n                        <img id=\"astra-mascot\" src=\"\" class=\"mascot-img\" style=\"width: 160px; height: 160px;\">\n                    </div>\n                    <div class=\"screen-subtitle\" id=\"round-score\" style=\"font-size: 36px; color: var(--accent); text-shadow: 0 2px 5px rgba(0,0,0,0.5);\"></div>\n                    <div style=\"display: flex; gap: 10px; width: 100%; justify-content: center;\">\n                        <button class=\"button\" id=\"next-round-button\" style=\"flex: 1;\">Next Round</button>\n                        <button class=\"button secondary\" id=\"save-lookbook-button\" style=\"min-width: 120px; padding: 0 20px; font-size: 18px;\">\ud83d\udcbe Save Look</button>\n                    </div>\n                </div>\n            </div>\n\n            <!-- End Screen -->\n            <div class=\"screen\" id=\"end-screen\">\n                <div class=\"screen-title\" id=\"end-title\">Game Complete!</div>\n                <div class=\"screen-subtitle\" id=\"end-subtitle\"></div>\n                <div class=\"screen-text\" id=\"end-score\"></div>\n                <div class=\"screen-text\" id=\"end-rank\" style=\"color: var(--accent); margin-top: 10px;\"></div>\n                \n                <div id=\"style-breakdown\" style=\"background: rgba(255,255,255,0.1); padding: 20px; border-radius: 20px; width: 100%; margin: 15px 0; border: 1px solid rgba(255,255,255,0.2);\">\n                    <div style=\"display: flex; justify-content: space-around; margin-bottom: 10px;\">\n                        <div style=\"text-align: center;\"><div style=\"font-size: 14px; opacity: 0.8;\">CORRECT</div><div id=\"stat-correct\" style=\"font-size: 32px; font-weight: 700; color: var(--success);\">0</div></div>\n                        <div style=\"text-align: center;\"><div style=\"font-size: 14px; opacity: 0.8;\">COMBO</div><div id=\"stat-combo\" style=\"font-size: 32px; font-weight: 700; color: #ffd93d;\">0</div></div>\n                        <div style=\"text-align: center;\"><div style=\"font-size: 14px; opacity: 0.8;\">ACCURACY</div><div id=\"stat-accuracy\" style=\"font-size: 32px; font-weight: 700; color: var(--accent);\">0%</div></div>\n                    </div>\n                    <div id=\"end-insight\" style=\"font-style: italic; font-size: 20px; text-align: center; color: #fff; margin-top: 10px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 10px;\">Astra is reflecting on your style...</div>\n                </div>\n\n                <div class=\"stars-display\" id=\"end-stars\"></div>\n                <button class=\"button\" id=\"share-look-button\" style=\"margin-top: 10px; background: var(--accent); color: var(--secondary);\">Share Your Look \ud83d\udcf8</button>\n                <button class=\"button\" id=\"back-home-button\">Back to Menu</button>\n            </div>\n\n            <div class=\"screen\" id=\"share-screen\" style=\"background: rgba(0,0,0,0.95);\">\n                <div class=\"share-card\" id=\"share-card\" style=\"width: 600px; height: 1000px; background: white; border-radius: 40px; display: flex; flex-direction: column; align-items: center; padding: 40px; position: relative; border: 10px solid var(--primary); box-shadow: 0 0 50px rgba(255,0,255,0.5);\">\n                    <div class=\"screen-title\" style=\"font-size: 50px; -webkit-text-fill-color: var(--secondary); margin-bottom: 20px; filter: none;\">Fashion Icon!</div>\n                    <div id=\"share-canvas-container\" style=\"width: 450px; height: 600px; background: #f8f8f8; border-radius: 30px; position: relative; overflow: hidden; border: 4px solid #eee;\">\n                        <canvas id=\"share-canvas\" width=\"450\" height=\"600\"></canvas>\n                    </div>\n                    <div class=\"share-info\" style=\"margin-top: 30px; text-align: center; width: 100%;\">\n                        <div id=\"share-score\" style=\"font-size: 48px; font-weight: 700; color: var(--primary); font-family: 'Fredoka';\">Score: 5000</div>\n                        <div id=\"share-stars\" style=\"font-size: 60px; margin: 10px 0;\">\u2b50\u2b50\u2b50</div>\n                        <div style=\"font-size: 24px; color: #666; font-style: italic;\">Made in Costume Chaos</div>\n                    </div>\n                    <img id=\"share-astra\" src=\"\" style=\"position: absolute; bottom: 20px; right: 20px; width: 120px; height: 120px; filter: drop-shadow(0 0 10px rgba(0,0,0,0.2));\">\n                </div>\n                <div style=\"margin-top: 30px; color: white; font-size: 20px; opacity: 0.8;\">Screenshot to share your style!</div>\n                <button class=\"button secondary\" id=\"close-share-button\" style=\"margin-top: 20px;\">Back</button>\n            </div>\n\n            <!-- Edit Mode Indicator -->\n            <div class=\"screen\" id=\"pause-screen\">\n                <div class=\"screen-title\">Game Paused</div>\n                <button class=\"button\" id=\"resume-button\">Resume</button>\n                <button class=\"button secondary\" id=\"quit-button\">Quit to Menu</button>\n            </div>\n\n            <div class=\"screen\" id=\"ad-screen\">\n                <div class=\"screen-title\">Astra's Fashion Fresh!</div>\n                <div class=\"screen-subtitle\">Getting New Styles...</div>\n                <div class=\"screen-text\" id=\"ad-text\" style=\"font-style: italic;\">\"True style never sleeps!\" - Astra</div>\n                <div id=\"ad-countdown\" class=\"screen-subtitle\" style=\"font-size: 64px;\">3</div>\n                <button class=\"button\" id=\"collect-items-button\" style=\"display: none;\">Collect New Styles \u2728</button>\n            </div>\n\n            <div class=\"edit-indicator\" id=\"edit-indicator\" style=\"display: none;\">EDIT MODE</div>\n        </div>\n    </div>\n\n    <script>\n        /**\n         * COSTUME CHAOS - A fast-paced fashion game\n         * \n         * Players race against the clock to create themed outfits from random clothing items.\n         * Drag and drop items onto a character mannequin, matching items to the theme for points.\n         * \n         * Game Flow:\n         * 1. Theme revealed with timer\n         * 2. Drag clothing items to character\n         * 3. Score based on theme match\n         * 4. Progress through 5 rounds\n         * 5. Final score and rating\n         */\n\n        async function run(mode) {\n            const gameWorld = document.getElementById('game-world');\n            const canvas = document.getElementById('game-canvas');\n            const ctx = canvas.getContext('2d');\n\n            // Game state variables\n            let mannequinX, mannequinY;\n            let gameState = {\n                currentRound: 0,\n                totalScore: 0,\n                roundScore: 0,\n                roundPointsAccumulated: 0,\n                timer: 30,\n                currentTheme: null,\n                activeLevelThemes: [],\n                playedThemes: [],\n                totalCorrectPicks: 0,\n                totalIncorrectPicks: 0,\n                availableItems: [],\n                equippedItems: { head: null, face: null, top: null, bottom: null, feet: null },\n                draggedItem: null,\n                isPaused: false,\n                mousePos: { x: 0, y: 0 },\n                clickStartPos: { x: 0, y: 0 },\n                particles: [],\n                combo: 0,\n                maxCombo: 0,\n                hintTimer: 0,\n                rotationAngle: 0,\n                isRotating: false,\n                lastMouseX: 0,\n                currentRoundType: 'NORMAL',\n                astraReactionTimeout: null,\n                rotationVelocity: 0\n            };\n            // Initialize display\n            function resizeCanvas() {\n                canvas.width = gameWorld.clientWidth || 720;\n                canvas.height = gameWorld.clientHeight || 1280;\n                if (typeof updatePositions === 'function') updatePositions();\n            }\n            resizeCanvas();\n            window.addEventListener('resize', resizeCanvas);\n            // Re-check size after a moment to handle late iframe rendering\n            setTimeout(resizeCanvas, 500);\n\n            // Constants\n            const MANNEQUIN_WIDTH = 250;\n            const MANNEQUIN_HEIGHT = 500;\n            const ITEM_SIZE = 96;\n            const DRAG_THRESHOLD = 5;\n\n            // Slot configuration for fitting items to mannequin\n            // Mannequin is 250x500, centered at mannequinY\n            // Head top ~-250, shoulder ~-150, waist ~0, knee ~+130, feet ~+220\n            // Slot offsets calibrated for 250\u00d7500 mannequin centered at mannequinY.\n            // Head center \u2248 -215, face/eyes \u2248 -185, chest \u2248 -65, hips \u2248 +100, feet \u2248 +220\n            const SLOT_CONFIG = {\n                // Mannequin: 250\u00d7500, centered at mannequinY\n                // Crown of head: -250 | Head center: -195 | Eyes/chin: -165\n                // Shoulders: -115  | Chest center: -50  | Waist: +10\n                // Hips: +65        | Knee: +140          | Ankle: +208  | Sole: +250\n                head:   { offsetY: -198, maxW: 148, maxH: 128, swayAmp: 1.0, swaySpeed: 0.9 },\n                face:   { offsetY: -168, maxW:  94, maxH:  60, swayAmp: 1.0, swaySpeed: 0.9 },\n                top:    { offsetY:  -36, maxW: 238, maxH: 188, swayAmp: 1.4, swaySpeed: 0.7 },\n                bottom: { offsetY:  108, maxW: 192, maxH: 200, swayAmp: 0.8, swaySpeed: 0.65 },\n                feet:   { offsetY:  220, maxW: 158, maxH:  80, swayAmp: 0.4, swaySpeed: 0.6 }\n            };\n            const DRAW_ORDER = ['bottom', 'feet', 'top', 'face', 'head'];\n\n            const THEME_COLORS = {\n                \"Beach Party\": \"yellow\", \"Space Explorer\": \"white\", \"Medieval Knight\": \"silver\",\n                \"80s Rockstar\": \"black\", \"Formal Gala\": \"purple\", \"Pirate Captain\": \"brown\",\n                \"Winter Wonderland\": \"blue\", \"Safari Adventure\": \"khaki\", \"Superhero\": \"red\",\n                \"Chef's Kitchen\": \"white\"\n            };\n            const ITEM_COLORS = {\n                \"item_hat_pirate\": \"black\", \"item_hat_chef\": \"white\", \"item_hat_beanie\": \"red\",\n                \"item_hat_crown\": \"gold\", \"item_hat_space\": \"white\", \"item_top_tshirt\": \"blue\",\n                \"item_top_leather\": \"black\", \"item_top_armor\": \"silver\", \"item_top_chef\": \"white\",\n                \"item_top_hawaiian\": \"colorful\", \"item_bottom_jeans\": \"blue\", \"item_bottom_shorts\": \"orange\",\n                \"item_bottom_skirt\": \"purple\", \"item_bottom_space\": \"white\", \"item_bottom_cargo\": \"khaki\",\n                \"item_shoes_sneakers\": \"red\", \"item_shoes_pirate\": \"brown\", \"item_shoes_space\": \"white\",\n                \"item_shoes_heels\": \"silver\", \"item_shoes_flipflops\": \"yellow\", \"item_accessory_sunglasses\": \"gold\",\n                \"item_accessory_cape\": \"red\", \"item_accessory_scarf\": \"green\", \"item_accessory_eyepatch\": \"black\",\n                \"item_accessory_apron\": \"white\"\n            };\n\n            // Asset cache\n            const assetCache = {};\n            const audioBuffers = {};\n            let audioContext = null;\n\n            // Initialize audio context on first user interaction\n            function initAudio() {\n                if (!audioContext) {\n                    audioContext = new (window.AudioContext || window.webkitAudioContext)();\n                }\n            }\n\n            // Preload assets\n            async function preloadAssets() {\n                const assetIds = [\n                    'character_mannequin', 'char_robot', 'char_alien', 'char_wood',\n                    'hat_pirate', 'hat_chef', 'hat_beanie', 'hat_crown', 'hat_space_helmet',\n                    'top_tshirt_casual', 'top_leather_jacket', 'top_armor_chest', 'top_chef_coat', 'top_hawaiian_shirt',\n                    'bottom_jeans', 'bottom_shorts_beach', 'bottom_skirt_fancy', 'bottom_pants_space', 'bottom_cargo_pants',\n                    'shoes_sneakers', 'shoes_boots_pirate', 'shoes_space_boots', 'shoes_heels_fancy', 'shoes_flipflops',\n                    'accessory_sunglasses', 'accessory_cape_hero', 'accessory_scarf_winter', 'accessory_eyepatch', 'accessory_apron',\n                    'background_beach', 'background_space', 'background_castle', 'background_stage', 'background_ballroom',\n                    'sfx_item_pickup', 'sfx_item_place_correct', 'sfx_item_place_wrong', 'sfx_round_complete', 'sfx_timer_warning', 'music_gameplay',\n                    'astra_mascot'\n                ];\n\n                const loadPromises = assetIds.map(id => {\n                    return new Promise((resolve) => {\n                        const assetInfo = lib.getAsset(id);\n                        if (!assetInfo) {\n                            resolve();\n                            return;\n                        }\n\n                        if (assetInfo.type === 'image') {\n                            const img = new Image();\n                            img.onload = () => {\n                                assetCache[id] = { img, info: assetInfo };\n                                resolve();\n                            };\n                            img.onerror = () => {\n                                lib.log(`Failed to load image: ${id}`);\n                                resolve();\n                            };\n                            img.src = assetInfo.url;\n                        } else if (assetInfo.type === 'audio') {\n                            fetch(assetInfo.url)\n                                .then(response => response.arrayBuffer())\n                                .then(arrayBuffer => {\n                                    audioBuffers[id] = arrayBuffer;\n                                    resolve();\n                                })\n                                .catch((err) => {\n                                    lib.log(`Failed to fetch audio: ${id} - ${err.message}`);\n                                    resolve();\n                                });\n                        } else {\n                            resolve();\n                        }\n                    });\n                });\n\n                await Promise.all(loadPromises);\n            }\n\n            // Play sound effect\n            function playSound(soundId, loop = false) {\n                if (!audioContext || !audioBuffers[soundId]) return;\n\n                audioContext.decodeAudioData(audioBuffers[soundId].slice(0), (buffer) => {\n                    const source = audioContext.createBufferSource();\n                    source.buffer = buffer;\n                    source.loop = loop;\n                    source.connect(audioContext.destination);\n                    source.start(0);\n                    \n                    if (soundId === 'music_gameplay') {\n                        window.gameplayMusic = source;\n                    }\n                });\n            }\n\n            // Stop gameplay music\n            function stopMusic() {\n                if (window.gameplayMusic) {\n                    window.gameplayMusic.stop();\n                    window.gameplayMusic = null;\n                }\n            }\n\n            // UI Elements\n            const homeScreen = document.getElementById('home-screen');\n            const roundScreen = document.getElementById('round-screen');\n            const endScreen = document.getElementById('end-screen');\n            const gameUIHeader = document.getElementById('game-ui-header');\n            const gameUIFooter = document.getElementById('game-ui-footer');\n            const themeDisplay = document.getElementById('theme-display');\n            const timerDisplay = document.getElementById('timer-display');\n            const timerValue = document.getElementById('timer-value');\n            const scoreDisplay = document.getElementById('score-display');\n            const feedbackDisplay = document.getElementById('feedback-display');\n            const starsDisplay = document.getElementById('stars-display');\n            const homeStarsTotal = document.getElementById('home-stars-total');\n            const nextRoundButton = document.getElementById('next-round-button');\n\n            const pauseScreen = document.getElementById('pause-screen');\n            const adScreen = document.getElementById('ad-screen');\n            const adCountdown = document.getElementById('ad-countdown');\n            const collectItemsButton = document.getElementById('collect-items-button');\n            const btnPause = document.getElementById('btn-pause');\n            const btnReroll = document.getElementById('btn-reroll');\n            const resumeButton = document.getElementById('resume-button');\n            const quitButton = document.getElementById('quit-button');\n            \n            const playNowButton = document.getElementById('play-now-button');\n            const backHomeButton = document.getElementById('back-home-button');\n            const resetButton = document.getElementById('reset-button');\n            const musicToggle = document.getElementById('music-toggle');\n            const sfxToggle = document.getElementById('sfx-toggle');\n            \n            const shopGrid = document.getElementById('shop-grid');\n            const levelsGrid = document.getElementById('levels-grid');\n            const leaderboardList = document.getElementById('leaderboard-list');\n            const playerRankDisplay = document.getElementById('player-rank-display');\n            const editIndicator = document.getElementById('edit-indicator');\n            const endRankDisplay = document.getElementById('end-rank');\n\n            // Player State (will be handled by persistence tool later)\n            let playerState = {\n                totalStars: 0,\n                unlockedLevels: [1],\n                unlockedCharacters: ['mannequin'],\n                selectedCharacterId: 'mannequin',\n                musicEnabled: true,\n                sfxEnabled: true,\n                lookbook: [],\n                cumulativeCorrectPicks: 0,\n                cumulativeTotalPicks: 0\n            };\n\n            const RANKS = [\n                { name: 'Style Rookie', threshold: 0, color: '#4ade80' },\n                { name: 'Style Pro', threshold: 50, color: '#8a2be2' },\n                { name: 'Style Icon', threshold: 80, color: '#ffd93d' }\n            ];\n\n            function updateRankUI() {\n                const total = playerState.cumulativeTotalPicks || 0;\n                const correct = playerState.cumulativeCorrectPicks || 0;\n                const accuracy = total > 0 ? Math.round((correct / total) * 100) : 0;\n                \n                let currentRank = RANKS[0];\n                let nextRank = RANKS[1];\n                let rankIndex = 0;\n\n                for (let i = RANKS.length - 1; i >= 0; i--) {\n                    if (accuracy >= RANKS[i].threshold) {\n                        currentRank = RANKS[i];\n                        rankIndex = i;\n                        nextRank = RANKS[i + 1] || null;\n                        break;\n                    }\n                }\n\n                const rankNameEl = document.getElementById('rank-name');\n                const progressFillEl = document.getElementById('rank-progress-fill');\n                const accuracyTextEl = document.getElementById('rank-accuracy-text');\n                const nextThresholdEl = document.getElementById('rank-next-threshold');\n\n                if (rankNameEl) {\n                    rankNameEl.textContent = currentRank.name.toUpperCase();\n                    rankNameEl.style.color = currentRank.color;\n                    rankNameEl.style.textShadow = `0 0 15px ${currentRank.color}66`;\n                }\n\n                if (accuracyTextEl) accuracyTextEl.textContent = `Overall Accuracy: ${accuracy}%`;\n\n                let progressPercent = 0;\n                if (!nextRank) {\n                    progressPercent = 100;\n                    if (nextThresholdEl) nextThresholdEl.textContent = \"MAX RANK REACHED\";\n                } else {\n                    const range = nextRank.threshold - currentRank.threshold;\n                    const currentProgress = accuracy - currentRank.threshold;\n                    progressPercent = Math.min(100, Math.max(0, (currentProgress / range) * 100));\n                    if (nextThresholdEl) nextThresholdEl.textContent = `Next: ${nextRank.name} (${nextRank.threshold}%)`;\n                }\n\n                if (progressFillEl) {\n                    progressFillEl.style.width = `${progressPercent}%`;\n                }\n            }\n\n            // Switch between menu tabs\n            window.switchTab = (tabId) => {\n                if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                document.querySelectorAll('.tab-content').forEach(el => el.style.display = 'none');\n                document.querySelectorAll('.tab-button').forEach(el => el.classList.remove('active'));\n                \n                document.getElementById(`tab-${tabId}`).style.display = tabId === 'play' ? 'flex' : 'block';\n                document.getElementById(`btn-${tabId}`).classList.add('active');\n                \n                if (tabId === 'shop') renderShop();\n                if (tabId === 'levels') renderLevels();\n                if (tabId === 'leaderboard') renderLeaderboard();\n                if (tabId === 'lookbook') renderLookbook();\n                updateStarDisplays();\n            };\n\n            function updateStarDisplays() {\n                const text = `Stars: ${playerState.totalStars}`;\n                if (homeStarsTotal) homeStarsTotal.textContent = `Total Stars: ${playerState.totalStars}`;\n                document.querySelectorAll('.shop-star-display').forEach(el => el.textContent = text);\n                document.querySelectorAll('.level-star-display').forEach(el => el.textContent = text);\n            }\n\n            function renderShop() {\n                shopGrid.innerHTML = '';\n                window.gameConfig.characters.forEach(char => {\n                    const isOwned = playerState.unlockedCharacters.includes(char.id);\n                    const canAfford = playerState.totalStars >= char.unlockLevel;\n\n                    const card = document.createElement('div');\n                    card.className = `card ${!isOwned ? 'locked' : ''} ${playerState.selectedCharacterId === char.id ? 'selected' : ''}`;\n                    \n                    const asset = lib.getAsset(char.assetId);\n                    card.innerHTML = `\n                        <img src=\"${asset?.url}\" class=\"card-img\">\n                        <div class=\"card-title\">${char.name}</div>\n                        <div class=\"card-info\">${isOwned ? (playerState.selectedCharacterId === char.id ? 'Selected' : 'Owned') : `Unlock: ${char.unlockLevel} \u2b50`}</div>\n                        ${!isOwned ? '<div class=\"lock-icon\">\ud83d\udd12</div>' : ''}\n                    `;\n                    \n                    card.onclick = async () => {\n                        if (isOwned) {\n                            playerState.selectedCharacterId = char.id;\n                            renderShop();\n                            await saveProgress();\n                        } else if (canAfford) {\n                            playerState.totalStars -= char.unlockLevel;\n                            playerState.unlockedCharacters.push(char.id);\n                            playerState.selectedCharacterId = char.id;\n                            renderShop();\n                            updateStarDisplays();\n                            await saveProgress();\n                            if (playerState.sfxEnabled) playSound('sfx_item_place_correct');\n                        } else {\n                            if (playerState.sfxEnabled) playSound('sfx_item_place_wrong');\n                        }\n                    };\n                    shopGrid.appendChild(card);\n                });\n            }\n\n            function renderLevels() {\n                levelsGrid.innerHTML = '';\n                window.gameConfig.levels.forEach(level => {\n                    const isUnlocked = playerState.unlockedLevels.includes(level.id);\n                    const canAfford = playerState.totalStars >= level.requiredStars;\n                    const card = document.createElement('div');\n                    card.className = `card ${!isUnlocked ? 'locked' : ''}`;\n                    card.innerHTML = `\n                        <div class=\"card-title\">${level.name}</div>\n                        <div class=\"card-info\">${isUnlocked ? 'Play Now' : `Unlock: ${level.requiredStars} \u2b50`}</div>\n                        ${!isUnlocked ? '<div class=\"lock-icon\">\ud83d\udd12</div>' : ''}\n                    `;\n                    \n                    card.onclick = async () => {\n                        if (isUnlocked) {\n                            homeScreen.classList.remove('active');\n                            startLevel(level);\n                        } else if (canAfford) {\n                            playerState.totalStars -= level.requiredStars;\n                            playerState.unlockedLevels.push(level.id);\n                            renderLevels();\n                            updateStarDisplays();\n                            await saveProgress();\n                            if (playerState.sfxEnabled) playSound('sfx_item_place_correct');\n                        } else {\n                            if (playerState.sfxEnabled) playSound('sfx_item_place_wrong');\n                        }\n                    };\n                    levelsGrid.appendChild(card);\n                });\n            }\n\n            function renderLookbook() {\n                const lookbookGrid = document.getElementById('lookbook-grid');\n                const lookbookEmpty = document.getElementById('lookbook-empty');\n                lookbookGrid.innerHTML = '';\n                \n                if (playerState.lookbook.length === 0) {\n                    lookbookEmpty.style.display = 'block';\n                    return;\n                }\n                lookbookEmpty.style.display = 'none';\n\n                playerState.lookbook.forEach((entry, index) => {\n                    const item = document.createElement('div');\n                    item.className = 'lookbook-item';\n                    \n                    const preview = document.createElement('div');\n                    preview.className = 'lookbook-preview';\n                    \n                    const pCanvas = document.createElement('canvas');\n                    pCanvas.width = 300;\n                    pCanvas.height = 400;\n                    preview.appendChild(pCanvas);\n                    \n                    const pCtx = pCanvas.getContext('2d');\n                    renderOutfitOnCanvas(pCtx, pCanvas.width, pCanvas.height, entry);\n                    \n                    const deleteBtn = document.createElement('button');\n                    deleteBtn.className = 'lookbook-delete';\n                    deleteBtn.innerHTML = '\u2715';\n                    deleteBtn.onclick = async (e) => {\n                        e.stopPropagation();\n                        playerState.lookbook.splice(index, 1);\n                        renderLookbook();\n                        await saveProgress();\n                    };\n\n                    const title = document.createElement('div');\n                    title.className = 'card-title';\n                    title.style.fontSize = '18px';\n                    title.textContent = entry.themeName || \"My Style\";\n\n                    item.appendChild(deleteBtn);\n                    item.appendChild(preview);\n                    item.appendChild(title);\n                    lookbookGrid.appendChild(item);\n                });\n            }\n\n            function renderOutfitOnCanvas(pCtx, width, height, entry) {\n                const cX = width / 2;\n                const cY = height / 2 + 20;\n                const scale = 0.7;\n\n                const selectedChar = window.gameConfig.characters.find(c => c.id === entry.characterId) || window.gameConfig.characters[0];\n                const mannequinAsset = assetCache[selectedChar.assetId];\n\n                if (mannequinAsset) {\n                    const w = MANNEQUIN_WIDTH * scale;\n                    const h = MANNEQUIN_HEIGHT * scale;\n                    pCtx.drawImage(mannequinAsset.img, cX - w/2, cY - h/2, w, h);\n                }\n\n                DRAW_ORDER.forEach(slot => {\n                    const itemData = entry.equippedItems[slot];\n                    if (itemData) {\n                        const originalItem = window.gameConfig.clothingItems.find(i => i.id === itemData.id);\n                        if (originalItem && assetCache[originalItem.assetId]) {\n                            const asset = assetCache[originalItem.assetId];\n                            const config = SLOT_CONFIG[slot];\n                            const w = config.maxW * scale;\n                            const h = config.maxH * scale;\n                            const offsetY = config.offsetY * scale;\n                            const img = asset.img;\n                            \n                            const imgAspect = img.width / img.height;\n                            let drawW = w;\n                            let drawH = h;\n                            if (imgAspect > w / h) drawH = w / imgAspect;\n                            else drawW = h * imgAspect;\n\n                            pCtx.drawImage(img, cX - drawW/2, cY + offsetY - drawH/2, drawW, drawH);\n                        }\n                    }\n                });\n            }\n\n            async function saveToLookbook() {\n                const entry = {\n                    themeName: gameState.currentTheme.name,\n                    characterId: playerState.selectedCharacterId,\n                    equippedItems: {}\n                };\n\n                for (let slot in gameState.equippedItems) {\n                    if (gameState.equippedItems[slot]) {\n                        entry.equippedItems[slot] = {\n                            id: gameState.equippedItems[slot].id\n                        };\n                    } else {\n                        entry.equippedItems[slot] = null;\n                    }\n                }\n\n                playerState.lookbook.unshift(entry); // Add to start\n                if (playerState.lookbook.length > 20) playerState.lookbook.pop(); // Max 20 items\n                \n                await saveProgress();\n                if (playerState.sfxEnabled) playSound('sfx_item_place_correct');\n                \n                const saveBtn = document.getElementById('save-lookbook-button');\n                saveBtn.textContent = '\u2705 Saved!';\n                saveBtn.disabled = true;\n                setTimeout(() => {\n                    saveBtn.textContent = '\ud83d\udcbe Save Look';\n                    saveBtn.disabled = false;\n                }, 2000);\n            }\n\n            // Render leaderboard\n            async function renderLeaderboard() {\n                leaderboardList.innerHTML = '<div style=\"text-align: center; color: white; padding: 20px;\">Loading leaderboard...</div>';\n                playerRankDisplay.textContent = 'Loading your rank...';\n                \n                try {\n                    const response = await lib.getTopNEntriesFromLeaderboard(10);\n                    \n                    // Display player rank\n                    if (response.userRank !== null) {\n                        playerRankDisplay.textContent = `Your Rank: #${response.userRank}`;\n                    } else {\n                        playerRankDisplay.textContent = 'Complete a game to get ranked!';\n                    }\n                    \n                    // Display leaderboard entries\n                    if (response.entries && response.entries.length > 0) {\n                        leaderboardList.innerHTML = '';\n                        response.entries.forEach((entry, index) => {\n                            const rank = index + 1;\n                            const isCurrentPlayer = entry.userId === lib.getUserId?.();\n                            const rankEmoji = rank === 1 ? '\ud83e\udd47' : rank === 2 ? '\ud83e\udd48' : rank === 3 ? '\ud83e\udd49' : `#${rank}`;\n                            \n                            const entryDiv = document.createElement('div');\n                            entryDiv.className = `leaderboard-entry ${isCurrentPlayer ? 'current-player' : ''}`;\n                            entryDiv.innerHTML = `\n                                <div class=\"leaderboard-rank ${rank <= 3 ? 'top3' : ''}\">${rankEmoji}</div>\n                                <img src=\"${entry.profilePicture}\" class=\"leaderboard-avatar\" alt=\"${entry.username}\">\n                                <div class=\"leaderboard-info\">\n                                    <div class=\"leaderboard-username\">${entry.username}</div>\n                                </div>\n                                <div class=\"leaderboard-score\">${entry.score.toLocaleString()}</div>\n                            `;\n                            leaderboardList.appendChild(entryDiv);\n                        });\n                    } else {\n                        leaderboardList.innerHTML = '<div style=\"text-align: center; color: white; padding: 20px;\">No scores yet! Be the first to play!</div>';\n                    }\n                } catch (error) {\n                    lib.log(`Failed to fetch leaderboard: ${error.message}`);\n                    leaderboardList.innerHTML = '<div style=\"text-align: center; color: white; padding: 20px;\">Unable to load leaderboard</div>';\n                    playerRankDisplay.textContent = 'Leaderboard unavailable';\n                }\n            }\n\n            // Persistence - Save player progress\n            async function saveProgress() {\n                try {\n                    await lib.saveUserGameState(playerState);\n                    lib.log('Progress saved successfully');\n                } catch (error) {\n                    lib.log(`Failed to save progress: ${error.message}`);\n                }\n            }\n\n            // Persistence - Load player progress\n            async function loadProgress() {\n                try {\n                    const response = await lib.getUserGameState();\n                    if (response.state) {\n                        // Restore saved data\n                        playerState.totalStars = response.state.totalStars || 0;\n                        playerState.unlockedLevels = response.state.unlockedLevels || [1];\n                        playerState.unlockedCharacters = response.state.unlockedCharacters || ['mannequin'];\n                        playerState.selectedCharacterId = response.state.selectedCharacterId || 'mannequin';\n                        playerState.musicEnabled = response.state.musicEnabled !== undefined ? response.state.musicEnabled : true;\n                        playerState.sfxEnabled = response.state.sfxEnabled !== undefined ? response.state.sfxEnabled : true;\n                        playerState.lookbook = response.state.lookbook || [];\n                        playerState.cumulativeCorrectPicks = response.state.cumulativeCorrectPicks || 0;\n                        playerState.cumulativeTotalPicks = response.state.cumulativeTotalPicks || 0;\n                        \n                        lib.log('Progress loaded successfully');\n                    } else {\n                        // No saved progress - use defaults\n                        lib.log('No saved progress found, starting fresh');\n                    }\n                } catch (error) {\n                    lib.log(`Failed to load progress: ${error.message}`);\n                }\n                \n                // Update UI elements\n                updateStarDisplays();\n                updateRankUI();\n                musicToggle.checked = playerState.musicEnabled;\n                sfxToggle.checked = playerState.sfxEnabled;\n                renderShop();\n                renderLevels();\n            }\n\n            // Reset all progress\n            window.resetProgress = async () => {\n                try {\n                    await lib.deleteUserGameState();\n                    lib.log('Progress reset successfully');\n                } catch (error) {\n                    lib.log(`Failed to delete progress: ${error.message}`);\n                }\n                \n                // Reset to defaults\n                playerState = {\n                    totalStars: 0,\n                    unlockedLevels: [1],\n                    unlockedCharacters: ['mannequin'],\n                    selectedCharacterId: 'mannequin',\n                    musicEnabled: true,\n                    sfxEnabled: true,\n                    lookbook: [],\n                    cumulativeCorrectPicks: 0,\n                    cumulativeTotalPicks: 0\n                };\n                \n                await loadProgress();\n                window.switchTab('play');\n            };\n\n            // Show game parameters\n            lib.showGameParameters({\n                name: \"Game Settings\",\n                params: {\n                    \"Starting Timer (seconds)\": { key: \"settings.startingTimer\", type: \"slider\", min: 15, max: 60, step: 5 },\n                    \"Timer Reduction Per Round\": { key: \"settings.timerReduction\", type: \"slider\", min: 0, max: 5, step: 1 },\n                    \"Number of Rounds\": { key: \"settings.numberOfRounds\", type: \"slider\", min: 3, max: 10, step: 1 },\n                    \"Items Per Round\": { key: \"settings.itemsPerRound\", type: \"slider\", min: 6, max: 15, step: 1 },\n                    \"Great Score Threshold\": { key: \"settings.greatThreshold\", type: \"number\" },\n                    \"Amazing Score Threshold\": { key: \"settings.amazingThreshold\", type: \"number\" },\n                    \"Perfect Score Threshold\": { key: \"settings.perfectThreshold\", type: \"number\" }\n                }\n            });\n\n            await preloadAssets();\n            loadProgress();\n            createParticlesForMenu();\n\n            function createParticlesForMenu() {\n                const container = document.getElementById('particles-container');\n                if (!container) return;\n                \n                const colors = ['#ff00ff', '#00ffff', '#8a2be2', '#ffd93d', '#ffffff'];\n                for (let i = 0; i < 20; i++) {\n                    const p = document.createElement('div');\n                    p.className = 'bg-particle';\n                    const size = Math.random() * 15 + 5;\n                    p.style.width = `${size}px`;\n                    p.style.height = `${size}px`;\n                    p.style.left = `${Math.random() * 100}%`;\n                    p.style.top = `${Math.random() * 100}%`;\n                    p.style.background = colors[Math.floor(Math.random() * colors.length)];\n                    p.style.animationDuration = `${Math.random() * 5 + 5}s`;\n                    p.style.animationDelay = `${Math.random() * 5}s`;\n                    p.style.filter = 'blur(2px)';\n                    container.appendChild(p);\n                }\n            }\n\n            function updatePositions() {\n                mannequinX = canvas.width / 2;\n                // Center the mannequin vertically in the playfield\n                // Items sit at y=180 (top) and y=canvas.height-180 (bottom)\n                // Mannequin needs to fit between those rows with good visual spacing\n                mannequinY = canvas.height / 2 + 10;\n                \n                // Update item positions if not dragging\n                if (gameState.availableItems.length > 0 && !gameState.draggedItem) {\n                    const positions = getItemPositions(gameState.availableItems.length);\n                    gameState.availableItems.forEach((item, i) => {\n                        if (positions[i]) {\n                            item.originalX = positions[i].x;\n                            item.originalY = positions[i].y;\n                            if (item.isAvailable) {\n                                item.x = item.originalX;\n                                item.y = item.originalY;\n                            }\n                        }\n                    });\n                }\n            }\n\n            function getItemPositions(numItems) {\n                const positions = [];\n                const itemsPerRow = Math.ceil(numItems / 2);\n                const topRowY = 180;\n                const bottomRowY = canvas.height - 180;\n                const spacing = Math.min((canvas.width - 32) / itemsPerRow, 120);\n                const startX = (canvas.width - (itemsPerRow - 1) * spacing) / 2;\n                for (let i = 0; i < numItems; i++) {\n                    const row = i < itemsPerRow ? 0 : 1;\n                    const col = i % itemsPerRow;\n                    positions.push({ x: startX + col * spacing, y: row === 0 ? topRowY : bottomRowY });\n                }\n                return positions;\n            }\n\n            function startLevel(level) {\n                initAudio();\n                gameState.currentRound = 0;\n                gameState.totalScore = 0;\n                gameState.totalCorrectPicks = 0;\n                gameState.totalIncorrectPicks = 0;\n                gameState.playedThemes = [];\n                gameState.combo = 0;\n                gameState.maxCombo = 0;\n                gameState.hintTimer = 0;\n                const comboUI = document.getElementById('combo-display');\n                if (comboUI) comboUI.style.opacity = '0';\n                gameState.activeLevelThemes = level.themeIndices.map(idx => window.gameConfig.themes[idx]);\n                \n                gameUIHeader.style.display = 'flex';\n                btnReroll.style.display = 'flex';\n                gameUIFooter.style.display = 'flex';\n\n                const gameplayMascot = document.getElementById('gameplay-mascot');\n                const gameplayAstraImg = document.getElementById('gameplay-astra-img');\n                const astraAsset = lib.getAsset('astra_mascot');\n                if (astraAsset) gameplayAstraImg.src = astraAsset.url;\n                gameplayMascot.style.display = 'flex';\n                gameplayMascot.style.opacity = '1';\n                \n                startNewRound();\n                if (playerState.musicEnabled) playSound('music_gameplay', true);\n            }\n\n            function quickPlay() {\n                initAudio();\n                gameState.currentRound = 0;\n                gameState.totalScore = 0;\n                gameState.totalCorrectPicks = 0;\n                gameState.totalIncorrectPicks = 0;\n                gameState.playedThemes = [];\n                gameState.combo = 0;\n                gameState.maxCombo = 0;\n                gameState.hintTimer = 0;\n                const comboUI = document.getElementById('combo-display');\n                if (comboUI) comboUI.style.opacity = '0';\n                // Pick 5 random themes\n                const shuffled = [...window.gameConfig.themes].sort(() => 0.5 - Math.random());\n                gameState.activeLevelThemes = shuffled.slice(0, window.gameConfig.settings.numberOfRounds);\n                \n                homeScreen.classList.remove('active');\n                gameUIHeader.style.display = 'flex';\n                btnReroll.style.display = 'flex';\n                gameUIFooter.style.display = 'flex';\n\n                const gameplayMascot = document.getElementById('gameplay-mascot');\n                const gameplayAstraImg = document.getElementById('gameplay-astra-img');\n                const astraAsset = lib.getAsset('astra_mascot');\n                if (astraAsset) gameplayAstraImg.src = astraAsset.url;\n                gameplayMascot.style.display = 'flex';\n                gameplayMascot.style.opacity = '1';\n                \n                startNewRound();\n                if (playerState.musicEnabled) playSound('music_gameplay', true);\n            }\n\n            function startNewRound() {\n                gameState.currentRound++;\n                gameState.roundScore = 0;\n                gameState.roundPointsAccumulated = 0;\n                gameState.combo = 0;\n                \n                // Hide combo UI\n                const comboUI = document.getElementById('combo-display');\n                if (comboUI) comboUI.style.opacity = '0';\n                const flameUI = document.getElementById('streak-flame');\n                if (flameUI) flameUI.style.display = 'none';\n                \n                const speech = document.getElementById('gameplay-speech');\n                if (speech) speech.style.opacity = '0';\n\n                // Select theme from active set\n                if (gameState.activeLevelThemes.length > 0) {\n                    gameState.currentTheme = gameState.activeLevelThemes[(gameState.currentRound - 1) % gameState.activeLevelThemes.length];\n                } else {\n                    gameState.currentTheme = window.gameConfig.themes[0];\n                }\n                gameState.playedThemes.push(gameState.currentTheme.name);\n\n                // Randomize round type\n                const roundTypes = ['NORMAL', 'SPEED', 'COLOR_MATCH'];\n                gameState.currentRoundType = roundTypes[Math.floor(Math.random() * roundTypes.length)];\n                \n                const roundTypeDisplay = document.getElementById('round-type-display');\n                if (roundTypeDisplay) {\n                    roundTypeDisplay.textContent = gameState.currentRoundType.replace('_', ' ') + ' ROUND';\n                    roundTypeDisplay.style.color = gameState.currentRoundType === 'SPEED' ? 'var(--danger)' : \n                                                  gameState.currentRoundType === 'COLOR_MATCH' ? '#ffd93d' : 'var(--accent)';\n                }\n\n                const timerReduction = (gameState.currentRound - 1) * window.gameConfig.settings.timerReduction;\n                let baseTimer = Math.max(15, window.gameConfig.settings.startingTimer - timerReduction);\n                \n                if (gameState.currentRoundType === 'SPEED') {\n                    baseTimer = Math.floor(baseTimer * 0.5); // 50% time for speed round\n                    if (roundTypeDisplay) roundTypeDisplay.textContent += ' (FAST!)';\n                }\n                gameState.timer = baseTimer;\n                \n                const allItems = window.gameConfig.clothingItems;\n                const numItems = window.gameConfig.settings.itemsPerRound;\n                \n                // Identify \"correct\" items based on round type\n                let correctPool, incorrectPool;\n                \n                if (gameState.currentRoundType === 'COLOR_MATCH') {\n                    const targetColor = THEME_COLORS[gameState.currentTheme.name] || \"white\";\n                    correctPool = allItems.filter(item => ITEM_COLORS[item.id] === targetColor);\n                    incorrectPool = allItems.filter(item => ITEM_COLORS[item.id] !== targetColor);\n                    themeDisplay.textContent = `Match the color: ${targetColor.toUpperCase()}!`;\n                } else {\n                    const themeTagsSet = new Set(gameState.currentTheme.tags);\n                    correctPool = allItems.filter(item => {\n                        const itemTagsSet = new Set(item.tags);\n                        return [...itemTagsSet].some(tag => themeTagsSet.has(tag));\n                    });\n                    incorrectPool = allItems.filter(item => {\n                        const itemTagsSet = new Set(item.tags);\n                        return ![...itemTagsSet].some(tag => themeTagsSet.has(tag));\n                    });\n                    themeDisplay.textContent = gameState.currentTheme.name;\n                }\n\n                // Guarantee 2-3 correct items if possible\n                const targetCorrectCount = Math.min(3, Math.max(2, correctPool.length));\n                const selectedCorrect = [...correctPool].sort(() => Math.random() - 0.5).slice(0, targetCorrectCount);\n                const selectedIncorrect = [...incorrectPool].sort(() => Math.random() - 0.5).slice(0, numItems - selectedCorrect.length);\n                \n                const selectedItemsPool = [...selectedCorrect, ...selectedIncorrect].sort(() => Math.random() - 0.5);\n                \n                const positions = getItemPositions(selectedItemsPool.length);\n                gameState.availableItems = selectedItemsPool.map((item, i) => {\n                    const isCorrect = selectedCorrect.some(c => c.id === item.id);\n                    return {\n                        ...item,\n                        x: positions[i].x,\n                        y: positions[i].y,\n                        originalX: positions[i].x,\n                        originalY: positions[i].y,\n                        isAvailable: true,\n                        isCorrect: isCorrect,\n                        hasGivenPoints: false,\n                        feedbackIcon: null,\n                        feedbackTimer: 0,\n                        flashTimer: 0,\n                        flashColor: null\n                    };\n                });\n                \n                gameState.equippedItems = { head: null, face: null, top: null, bottom: null, feet: null };\n                \n                themeDisplay.style.animation = 'none';\n                setTimeout(() => {\n                    themeDisplay.style.animation = 'slideDown var(--transition-slow) var(--ease-bounce)';\n                }, 10);\n                \n                if (gameState.currentTheme.backgroundId && assetCache[gameState.currentTheme.backgroundId]) {\n                    gameWorld.style.background = `url('${assetCache[gameState.currentTheme.backgroundId].img.src}') center/cover`;\n                }\n\n                feedbackDisplay.textContent = '';\n                starsDisplay.innerHTML = '';\n                gameState.hintTimer = 0;\n            }\n\n            function calculateScore() {\n                let score = 0;\n                Object.values(gameState.equippedItems).forEach(item => {\n                    if (item) {\n                        if (gameState.currentRoundType === 'COLOR_MATCH') {\n                            const targetColor = THEME_COLORS[gameState.currentTheme.name] || \"white\";\n                            if (ITEM_COLORS[item.id] === targetColor) score += 100;\n                            else score += 10;\n                        } else {\n                            const themeTagsSet = new Set(gameState.currentTheme.tags);\n                            const itemTagsSet = new Set(item.tags);\n                            const intersection = [...itemTagsSet].filter(tag => themeTagsSet.has(tag));\n                            if (intersection.length === itemTagsSet.size && itemTagsSet.size > 0) score += 100;\n                            else if (intersection.length > 0) score += 50;\n                        }\n                    }\n                });\n                if (Object.values(gameState.equippedItems).every(item => item !== null)) score += 200;\n                return { score };\n            }\n\n            function endRound() {\n                let fullSetBonus = 0;\n                if (Object.values(gameState.equippedItems).every(item => item !== null)) {\n                    fullSetBonus = 200;\n                }\n                \n                const timeBonus = Math.floor(gameState.timer * 10);\n                const roundTotal = gameState.roundPointsAccumulated + timeBonus + fullSetBonus;\n                \n                gameState.roundScore = roundTotal;\n                gameState.totalScore += (timeBonus + fullSetBonus);\n                \n                stopMusic();\n                if (playerState.sfxEnabled) playSound('sfx_round_complete');\n                \n                const roundText = document.getElementById('round-text');\n                const roundScore = document.getElementById('round-score');\n                const astraSpeech = document.getElementById('astra-speech');\n                const astraImg = document.getElementById('astra-mascot');\n                \n                // Astra reactions\n                const reactions = {\n                    great: [\"Astra: Heroic look!\", \"Astra: Absolutely stunning!\", \"Astra: Runway ready!\", \"Astra: Total fashion icon!\"],\n                    good: [\"Astra: Not bad at all!\", \"Astra: I see the vision!\", \"Astra: Fashionable effort!\", \"Astra: Very stylish!\"],\n                    bad: [\"Astra: Hmm, questionable choices...\", \"Astra: Is that... avant-garde?\", \"Astra: Maybe try a different hat?\", \"Astra: Style is... subjective!\"]\n                };\n\n                let pool = reactions.good;\n                if (roundTotal >= 250) pool = reactions.great;\n                else if (roundTotal < 120) pool = reactions.bad;\n                \n                astraSpeech.textContent = pool[Math.floor(Math.random() * pool.length)];\n                \n                const astraAsset = lib.getAsset('astra_mascot');\n                if (astraAsset) astraImg.src = astraAsset.url;\n\n                roundText.textContent = `Round ${gameState.currentRound} Complete!`;\n                roundScore.textContent = `Round Score: ${roundTotal}`;\n                \n                const saveLookBtn = document.getElementById('save-lookbook-button');\n                saveLookBtn.textContent = '\ud83d\udcbe Save Look';\n                saveLookBtn.disabled = false;\n                saveLookBtn.onclick = () => saveToLookbook();\n\n                roundScreen.classList.add('active');\n                document.getElementById('gameplay-mascot').style.display = 'none';\n                \n                nextRoundButton.onclick = () => {\n                    if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                    roundScreen.classList.remove('active');\n                    const maxRounds = Math.min(window.gameConfig.settings.numberOfRounds, gameState.activeLevelThemes.length || 99);\n                    if (gameState.currentRound >= maxRounds) {\n                        endGame();\n                    } else {\n                        startNewRound();\n                        if (playerState.musicEnabled) playSound('music_gameplay', true);\n                    }\n                };\n            }\n\n            async function endGame() {\n                const endTitle = document.getElementById('end-title');\n                const endSubtitle = document.getElementById('end-subtitle');\n                const endScore = document.getElementById('end-score');\n                const endStars = document.getElementById('end-stars');\n                const endInsight = document.getElementById('end-insight');\n                const statCorrect = document.getElementById('stat-correct');\n                const statCombo = document.getElementById('stat-combo');\n                const statAccuracy = document.getElementById('stat-accuracy');\n                \n                const settings = window.gameConfig.settings;\n                let rating = 'Good!';\n                let stars = 1;\n                \n                if (gameState.totalScore >= settings.perfectThreshold) { rating = 'Perfect! \ud83c\udf89'; stars = 3; }\n                else if (gameState.totalScore >= settings.amazingThreshold) { rating = 'Amazing! \ud83c\udf1f'; stars = 2; }\n                else if (gameState.totalScore >= settings.greatThreshold) { rating = 'Great! \u2728'; stars = 1; }\n                else { rating = 'Good!'; stars = 1; }\n                \n                // Set stats\n                const totalPicks = gameState.totalCorrectPicks + gameState.totalIncorrectPicks;\n                const accuracy = totalPicks > 0 ? Math.round((gameState.totalCorrectPicks / totalPicks) * 100) : 0;\n                \n                // Update cumulative stats\n                playerState.cumulativeCorrectPicks += gameState.totalCorrectPicks;\n                playerState.cumulativeTotalPicks += totalPicks;\n                \n                statCorrect.textContent = gameState.totalCorrectPicks;\n                statCombo.textContent = gameState.maxCombo;\n                statAccuracy.textContent = `${accuracy}%`;\n                endInsight.textContent = \"Astra is reflecting on your style...\";\n\n                // Add confetti effect\n                for(let i=0; i<50; i++) {\n                    createParticles(canvas.width * Math.random(), -20, `hsl(${Math.random() * 360}, 100%, 50%)`, 1);\n                }\n                \n                playerState.totalStars += stars;\n                updateStarDisplays();\n                updateRankUI();\n                await saveProgress();\n\n                // Submit score to leaderboard\n                let userRank = null;\n                try {\n                    const leaderboardResponse = await lib.addPlayerScoreToLeaderboard(gameState.totalScore, 10);\n                    if (leaderboardResponse.success && leaderboardResponse.userRank !== null) {\n                        userRank = leaderboardResponse.userRank;\n                        lib.log(`Score submitted! Your rank: #${userRank}`);\n                    }\n                } catch (error) {\n                    lib.log(`Leaderboard submission failed: ${error.message}`);\n                }\n\n                endTitle.textContent = 'Level Complete!';\n                endSubtitle.textContent = rating;\n                endScore.textContent = `Final Score: ${gameState.totalScore}`;\n                endStars.innerHTML = '\u2b50'.repeat(stars);\n                \n                // Display rank if available\n                if (userRank !== null) {\n                    endRankDisplay.textContent = `Global Rank: #${userRank} \ud83c\udfc6`;\n                    endRankDisplay.style.display = 'block';\n                } else {\n                    endRankDisplay.style.display = 'none';\n                }\n                \n                endScreen.classList.add('active');\n\n                // Generate insight line via LLM\n                try {\n                    const themesList = gameState.playedThemes.join(', ');\n                    const systemPrompt = \"You are Astra, a stylish and playful fashion critic. Provide a short, fun, and encouraging insight line (max 15 words) about the player's fashion performance. Use the provided stats to make it feel personalized.\";\n                    const userPrompt = `I played a game called Costume Chaos.\nThemes I styled for: ${themesList}.\nMy stats:\n- Correct matches: ${gameState.totalCorrectPicks}\n- Incorrect matches: ${gameState.totalIncorrectPicks}\n- Accuracy: ${accuracy}%\n- Total Score: ${gameState.totalScore}\n\nGive me a playful style-personality insight!`;\n\n                    const result = await lib.llm({\n                        messages: [{ role: 'user', content: userPrompt }],\n                        systemPrompt: systemPrompt,\n                        config: { temperature: 0.8, maxOutputTokens: 60 }\n                    });\n                    \n                    if (result && result.text) {\n                        endInsight.textContent = result.text.trim().replace(/^\"|\"$/g, '');\n                    }\n                } catch (err) {\n                    lib.log(\"LLM insight failed: \" + err.message);\n                    // Fallback\n                    const accuracyPool = accuracy > 80 ? \n                        [\"You've got a killer eye for detail!\", \"A total fashion prodigy!\", \"Runway ready, darling!\"] :\n                        accuracy > 50 ? [\"You've got a unique vision!\", \"Fashion is about risks, and you take them!\", \"I see the style emerging!\"] :\n                        [\"Avant-garde is definitely your thing!\", \"Bold choices! The fashion world needs more of that.\", \"You're rewriting the rulebook!\"];\n                    endInsight.textContent = accuracyPool[Math.floor(Math.random() * accuracyPool.length)];\n                }\n            }\n\n            function drawImage(img, x, y, maxWidth, maxHeight, mode = 'contain') {\n                if (!img) return;\n                const imgAspect = img.width / img.height;\n                const boxAspect = maxWidth / maxHeight;\n                let drawWidth, drawHeight;\n                if (mode === 'contain') {\n                    // Scale to fit within box while preserving aspect ratio\n                    if (imgAspect > boxAspect) {\n                        drawWidth  = maxWidth;\n                        drawHeight = maxWidth / imgAspect;\n                    } else {\n                        drawHeight = maxHeight;\n                        drawWidth  = maxHeight * imgAspect;\n                    }\n                } else {\n                    drawWidth  = maxWidth;\n                    drawHeight = maxHeight;\n                }\n                // Always draw centered on (x, y) \u2014 no vertical drift\n                const drawX = x - drawWidth  / 2;\n                const drawY = y - drawHeight / 2;\n                ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);\n                return { x: drawX, y: drawY, width: drawWidth, height: drawHeight };\n            }\n\n            function drawMannequin(timestamp) {\n                // 3D breathing / idle bob\n                const breathScale = Math.sin(timestamp / 700) * 0.012 + 1;\n                const idleBob     = Math.sin(timestamp / 900) * 3; // subtle vertical bob\n\n                const selectedChar = window.gameConfig.characters.find(c => c.id === playerState.selectedCharacterId) || window.gameConfig.characters[0];\n                const mannequinAsset = assetCache[selectedChar.assetId];\n\n                const angle = gameState.rotationAngle || 0;\n                const cosA  = Math.cos(angle);\n                const sinA  = Math.sin(angle);\n                const absC  = Math.abs(cosA);\n\n                let normAngle = angle % (Math.PI * 2);\n                if (normAngle < 0) normAngle += Math.PI * 2;\n                const isBack = normAngle > Math.PI / 2 && normAngle < 3 * Math.PI / 2;\n\n                // \u2500\u2500 3D Ground shadow (ellipse, shrinks at side angles) \u2500\u2500\n                ctx.save();\n                ctx.translate(mannequinX, mannequinY + MANNEQUIN_HEIGHT / 2 + idleBob);\n                ctx.scale(absC * 0.85 + 0.15, 0.28);\n                const shadowGrad = ctx.createRadialGradient(0, 0, 10, 0, 0, 180);\n                shadowGrad.addColorStop(0, 'rgba(0,0,0,0.35)');\n                shadowGrad.addColorStop(1, 'rgba(0,0,0,0)');\n                ctx.fillStyle = shadowGrad;\n                ctx.beginPath();\n                ctx.arc(0, 0, 180, 0, Math.PI * 2);\n                ctx.fill();\n                ctx.restore();\n\n                // \u2500\u2500 Main character \u2500\u2500\n                ctx.save();\n                ctx.translate(mannequinX, mannequinY + idleBob);\n\n                // Perspective Y compression: character looks slightly shorter when turned sideways\n                const perspY = 1 - Math.abs(sinA) * 0.04;\n                ctx.scale(cosA * breathScale, breathScale * perspY);\n\n                if (mannequinAsset) {\n                    drawImage(mannequinAsset.img, 0, 0, MANNEQUIN_WIDTH, MANNEQUIN_HEIGHT);\n                }\n\n                // \u2500\u2500 3D Lighting overlay \u2500\u2500\n                ctx.save();\n                ctx.beginPath();\n                ctx.rect(-MANNEQUIN_WIDTH, -MANNEQUIN_HEIGHT, MANNEQUIN_WIDTH * 2, MANNEQUIN_HEIGHT * 2);\n\n                // Shadow: darkens the \"away\" side as character turns\n                const shadowIntensity = (1 - cosA) * 0.5 * (isBack ? 1.3 : 1);\n                if (shadowIntensity > 0.02) {\n                    ctx.globalCompositeOperation = 'source-atop';\n                    ctx.fillStyle = `rgba(10,10,40,${Math.min(0.65, shadowIntensity)})`;\n                    ctx.fill();\n                }\n\n                // Specular highlight: brightens the leading edge when rotating\n                if (Math.abs(sinA) > 0.15) {\n                    const highlightSide = sinA > 0 ? 1 : -1;\n                    const hlGrad = ctx.createLinearGradient(\n                        highlightSide * MANNEQUIN_WIDTH * 0.5, 0,\n                        -highlightSide * MANNEQUIN_WIDTH * 0.5, 0\n                    );\n                    hlGrad.addColorStop(0, `rgba(255,255,255,${Math.abs(sinA) * 0.22})`);\n                    hlGrad.addColorStop(0.35, 'rgba(255,255,255,0)');\n                    hlGrad.addColorStop(1, 'rgba(0,0,0,0)');\n                    ctx.globalCompositeOperation = 'source-atop';\n                    ctx.fillStyle = hlGrad;\n                    ctx.fill();\n                }\n\n                // Rim light: cool blue glow on the turning edge\n                if (Math.abs(sinA) > 0.2) {\n                    const rimSide = sinA > 0 ? -1 : 1;\n                    const rimGrad = ctx.createLinearGradient(\n                        rimSide * MANNEQUIN_WIDTH * 0.48, 0,\n                        rimSide * MANNEQUIN_WIDTH * 0.1, 0\n                    );\n                    rimGrad.addColorStop(0, `rgba(100,180,255,${Math.abs(sinA) * 0.35})`);\n                    rimGrad.addColorStop(1, 'rgba(100,180,255,0)');\n                    ctx.globalCompositeOperation = 'source-atop';\n                    ctx.fillStyle = rimGrad;\n                    ctx.fill();\n                }\n                ctx.restore();\n\n                // \u2500\u2500 Clothing slots \u2500\u2500\n                DRAW_ORDER.forEach(slot => {\n                    if (isBack && slot === 'face') return;\n\n                    const item = gameState.equippedItems[slot];\n                    if (item && assetCache[item.assetId]) {\n                        const asset = assetCache[item.assetId];\n                        const config = SLOT_CONFIG[slot];\n                        const img = asset.img;\n\n                        const isUpperSlot = slot === 'head' || slot === 'face';\n                        const swayAmp   = config.swayAmp   || 1.5;\n                        const swaySpeed = config.swaySpeed || 0.8;\n                        const swayOffset = isUpperSlot\n                            ? Math.sin(timestamp / (600 / swaySpeed)) * swayAmp * 0.4\n                            : Math.sin(timestamp / (600 / swaySpeed) + 0.5) * swayAmp;\n\n                        ctx.save();\n                        ctx.translate(0, config.offsetY + swayOffset);\n\n                        // Flash ring\n                        if (item.flashTimer > 0) {\n                            ctx.save();\n                            ctx.beginPath();\n                            ctx.arc(0, 0, Math.min(config.maxW, config.maxH) / 1.8, 0, Math.PI * 2);\n                            ctx.fillStyle = item.flashColor;\n                            ctx.globalAlpha = (item.flashTimer / 0.5) * 0.35;\n                            ctx.fill();\n                            ctx.restore();\n                        }\n\n                        // \u2500\u2500 Pixel-perfect 3D shading via offscreen canvas \u2500\u2500\n                        // Shading is applied with source-atop so it only touches\n                        // the actual clothing pixels, never the transparent areas.\n                        const cw = config.maxW;\n                        const ch = config.maxH;\n\n                        // Grow offscreen canvas only when needed\n                        if (clothingOffCanvas.width < cw)  clothingOffCanvas.width  = cw;\n                        if (clothingOffCanvas.height < ch) clothingOffCanvas.height = ch;\n\n                        clothingOffCtx.clearRect(0, 0, cw, ch);\n\n                        // 1. Draw clothing image, aspect-ratio preserved, centred in box\n                        const imgAspect = img.width / img.height;\n                        const boxAspect = cw / ch;\n                        let drawW = cw, drawH = ch;\n                        if (imgAspect > boxAspect) drawH = cw / imgAspect;\n                        else drawW = ch * imgAspect;\n                        clothingOffCtx.drawImage(img, (cw - drawW) / 2, (ch - drawH) / 2, drawW, drawH);\n\n                        // 2. Ambient shadow \u2014 darker on the away-side\n                        if (shadowIntensity > 0.02) {\n                            clothingOffCtx.globalCompositeOperation = 'source-atop';\n                            clothingOffCtx.fillStyle = `rgba(10,10,40,${Math.min(0.55, shadowIntensity)})`;\n                            clothingOffCtx.fillRect(0, 0, cw, ch);\n                        }\n\n                        // 3. Specular highlight \u2014 bright leading edge\n                        if (Math.abs(sinA) > 0.15) {\n                            const hSide = sinA > 0 ? 1 : -1;\n                            const hlGrad = clothingOffCtx.createLinearGradient(\n                                hSide > 0 ? cw * 0.85 : cw * 0.15, 0,\n                                hSide > 0 ? 0 : cw, 0\n                            );\n                            hlGrad.addColorStop(0, `rgba(255,255,255,${Math.abs(sinA) * 0.30})`);\n                            hlGrad.addColorStop(0.45, 'rgba(255,255,255,0)');\n                            clothingOffCtx.globalCompositeOperation = 'source-atop';\n                            clothingOffCtx.fillStyle = hlGrad;\n                            clothingOffCtx.fillRect(0, 0, cw, ch);\n                        }\n\n                        // 4. Cool rim light on trailing edge\n                        if (Math.abs(sinA) > 0.2) {\n                            const rSide = sinA > 0 ? -1 : 1;\n                            const rimGrad = clothingOffCtx.createLinearGradient(\n                                rSide > 0 ? 0 : cw, 0,\n                                rSide > 0 ? cw * 0.28 : cw * 0.72, 0\n                            );\n                            rimGrad.addColorStop(0, `rgba(100,180,255,${Math.abs(sinA) * 0.45})`);\n                            rimGrad.addColorStop(1, 'rgba(100,180,255,0)');\n                            clothingOffCtx.globalCompositeOperation = 'source-atop';\n                            clothingOffCtx.fillStyle = rimGrad;\n                            clothingOffCtx.fillRect(0, 0, cw, ch);\n                        }\n\n                        // 5. Subtle inner-edge depth (makes it look \"worn\" not floating)\n                        clothingOffCtx.globalCompositeOperation = 'source-atop';\n                        const edgeGrad = clothingOffCtx.createRadialGradient(\n                            cw / 2, ch / 2, Math.min(cw, ch) * 0.28,\n                            cw / 2, ch / 2, Math.min(cw, ch) * 0.52\n                        );\n                        edgeGrad.addColorStop(0, 'rgba(0,0,0,0)');\n                        edgeGrad.addColorStop(1, 'rgba(0,0,0,0.18)');\n                        clothingOffCtx.fillStyle = edgeGrad;\n                        clothingOffCtx.fillRect(0, 0, cw, ch);\n\n                        clothingOffCtx.globalCompositeOperation = 'source-over';\n\n                        // Stamp composited clothing onto main canvas, centred at origin\n                        ctx.drawImage(clothingOffCanvas, 0, 0, cw, ch, -cw / 2, -ch / 2, cw, ch);\n\n                        // Feedback icon (drawn after, unaffected by shading)\n                        if (item.feedbackTimer > 0 && item.feedbackIcon) {\n                            ctx.save();\n                            ctx.font = 'bold 60px Arial';\n                            ctx.textAlign = 'center';\n                            ctx.textBaseline = 'middle';\n                            ctx.globalAlpha = Math.min(1, item.feedbackTimer / 0.3);\n                            ctx.shadowColor = 'rgba(0,0,0,0.5)';\n                            ctx.shadowBlur = 10;\n                            ctx.scale(cosA < 0 ? -1 : 1, 1);\n                            ctx.fillText(item.feedbackIcon, 0, -config.maxH / 2 - 10);\n                            ctx.restore();\n                        }\n\n                        ctx.restore();\n                    }\n                });\n\n                ctx.restore();\n            }\n\n                function drawClothingItem(item, timestamp, alpha = 1) {\n                if (!assetCache[item.assetId]) return;\n                ctx.save();\n                ctx.globalAlpha = alpha;\n                const asset = assetCache[item.assetId];\n                \n                const hoverOffset = item.isAvailable && !gameState.draggedItem ? Math.sin(timestamp / 300 + (item.x + item.y)) * 5 : 0;\n                const scale = item === gameState.draggedItem ? 1.2 : 1;\n                \n                ctx.translate(item.x, item.y + hoverOffset);\n                ctx.scale(scale, scale);\n                \n                // Draw flash effect\n                if (item.flashTimer > 0) {\n                    ctx.save();\n                    ctx.beginPath();\n                    ctx.arc(0, 0, ITEM_SIZE / 1.5, 0, Math.PI * 2);\n                    ctx.fillStyle = item.flashColor;\n                    ctx.globalAlpha = (item.flashTimer / 0.5) * 0.5;\n                    ctx.fill();\n                    ctx.restore();\n                }\n\n                // Hint sparkles\n                if (gameState.hintTimer > 0 && item.isCorrect && item.isAvailable) {\n                    const sparkleCount = Math.floor(Math.sin(timestamp / 100) * 2 + 3);\n                    for (let i = 0; i < sparkleCount; i++) {\n                        const sx = (Math.random() - 0.5) * ITEM_SIZE;\n                        const sy = (Math.random() - 0.5) * ITEM_SIZE;\n                        ctx.fillStyle = '#ffd93d';\n                        ctx.beginPath();\n                        ctx.arc(sx, sy, 2, 0, Math.PI * 2);\n                        ctx.fill();\n                    }\n                    // Glow effect\n                    ctx.shadowColor = '#ffd93d';\n                    ctx.shadowBlur = 15;\n                }\n\n                drawImage(asset.img, 0, 0, ITEM_SIZE, ITEM_SIZE);\n                \n                if (gameState.draggedItem === item) {\n                    ctx.shadowColor = '#ffd93d';\n                    ctx.shadowBlur = 20;\n                    drawImage(asset.img, 0, 0, ITEM_SIZE, ITEM_SIZE);\n                }\n\n                // Draw feedback icon\n                if (item.feedbackTimer > 0 && item.feedbackIcon) {\n                    ctx.save();\n                    ctx.font = 'bold 40px Arial';\n                    ctx.textAlign = 'center';\n                    ctx.textBaseline = 'middle';\n                    ctx.globalAlpha = Math.min(1, item.feedbackTimer / 0.3);\n                    ctx.fillText(item.feedbackIcon, 0, -ITEM_SIZE / 2);\n                    ctx.restore();\n                }\n\n                ctx.restore();\n            }\n\n            function drawParticles() {\n                gameState.particles.forEach((particle, index) => {\n                    ctx.save();\n                    ctx.globalAlpha = particle.alpha;\n                    ctx.fillStyle = particle.color;\n                    ctx.translate(particle.x, particle.y);\n                    ctx.rotate(particle.rotation);\n                    if (particle.type === 'star') {\n                        ctx.font = `${particle.size}px Arial`;\n                        ctx.fillText('\u2b50', 0, 0);\n                    } else {\n                        ctx.fillRect(-particle.size / 2, -particle.size / 2, particle.size, particle.size);\n                    }\n                    ctx.restore();\n                    particle.x += particle.vx;\n                    particle.y += particle.vy;\n                    particle.vy += 0.5;\n                    particle.rotation += particle.rotationSpeed;\n                    particle.alpha -= 0.02;\n                    if (particle.alpha <= 0) gameState.particles.splice(index, 1);\n                });\n            }\n\n            function createParticles(x, y, color = '#ffd93d', count = 10) {\n                for (let i = 0; i < count; i++) {\n                    gameState.particles.push({\n                        x, y, vx: (Math.random() - 0.5) * 8, vy: (Math.random() - 0.5) * 8 - 4,\n                        size: Math.random() * 8 + 4, alpha: 1, color,\n                        rotation: Math.random() * Math.PI * 2, rotationSpeed: (Math.random() - 0.5) * 0.2,\n                        type: Math.random() > 0.5 ? 'star' : 'square'\n                    });\n                }\n            }\n\n            function showAstraReaction(isCorrect) {\n                const speech = document.getElementById('gameplay-speech');\n                const positiveLines = [\"Nice pick!\", \"Style!\", \"Love it!\", \"Yes!\", \"Perfect!\", \"On point!\", \"Work it!\", \"Glow up!\"];\n                const negativeLines = [\"Hmm...\", \"Not quite!\", \"Oops!\", \"Try again?\", \"Style check!\", \"Wait...\", \"Not that one!\"];\n                \n                const lines = isCorrect ? positiveLines : negativeLines;\n                speech.textContent = lines[Math.floor(Math.random() * lines.length)];\n                speech.style.opacity = '1';\n                \n                if (gameState.astraReactionTimeout) clearTimeout(gameState.astraReactionTimeout);\n                gameState.astraReactionTimeout = setTimeout(() => {\n                    speech.style.opacity = '0';\n                }, 1500);\n            }\n\n            function isPointInItem(x, y, item) {\n                const halfSize = ITEM_SIZE / 2;\n                return x >= item.x - halfSize && x <= item.x + halfSize && y >= item.y - halfSize && y <= item.y + halfSize;\n            }\n\n            function isNearMannequin(x, y) {\n                const dx = Math.abs(x - mannequinX);\n                const dy = Math.abs(y - mannequinY);\n                return dx < MANNEQUIN_WIDTH / 1.2 && dy < MANNEQUIN_HEIGHT / 1.2;\n            }\n\n            function getInputPosition(e) {\n                const rect = canvas.getBoundingClientRect();\n                const x = (e.clientX || e.touches?.[0]?.clientX || 0) - rect.left;\n                const y = (e.clientY || e.touches?.[0]?.clientY || 0) - rect.top;\n                return { x, y };\n            }\n\n            if (mode === 'play') {\n                canvas.addEventListener('mousedown', handleInputStart);\n                canvas.addEventListener('touchstart', handleInputStart, { passive: false });\n                canvas.addEventListener('mousemove', handleInputMove);\n                canvas.addEventListener('touchmove', handleInputMove, { passive: false });\n                canvas.addEventListener('mouseup', handleInputEnd);\n                canvas.addEventListener('touchend', handleInputEnd);\n\n                function equipItem(item, isPlacement = true) {\n                const oldItem = gameState.equippedItems[item.slot];\n                if (oldItem) {\n                    oldItem.isAvailable = true;\n                    oldItem.x = oldItem.originalX;\n                    oldItem.y = oldItem.originalY;\n                }\n                \n                gameState.equippedItems[item.slot] = item;\n                item.isAvailable = false;\n                \n                // Real scoring logic: Correct items get more points\n                if (item.isCorrect) {\n                    showAstraReaction(true);\n                    gameState.combo++;\n                    gameState.maxCombo = Math.max(gameState.maxCombo, gameState.combo);\n                    \n                    let multiplier = 1.0;\n                    if (gameState.combo >= 3) {\n                        multiplier = 1.5 + (gameState.combo - 3) * 0.1;\n                    }\n\n                    if (!item.hasGivenPoints) {\n                        const basePoints = 100;\n                        const pointsWithMultiplier = Math.round(basePoints * multiplier);\n                        gameState.totalScore += pointsWithMultiplier;\n                        gameState.roundPointsAccumulated += pointsWithMultiplier;\n                        gameState.totalCorrectPicks++;\n                        item.hasGivenPoints = true;\n                    }\n\n                    if (playerState.sfxEnabled) playSound('sfx_item_place_correct');\n                    createParticles(mannequinX, mannequinY, gameState.combo >= 3 ? '#ff4500' : '#4ade80', 20); \n                    \n                    // Show combo/streak UI\n                    const comboUI = document.getElementById('combo-display');\n                    const flameUI = document.getElementById('streak-flame');\n                    \n                    if (gameState.combo >= 3) {\n                        feedbackDisplay.textContent = `STREAK! +${Math.round(100 * multiplier)} (x${multiplier.toFixed(1)} Bonus!) \ud83d\udd25`;\n                        if (comboUI) {\n                            comboUI.textContent = `STREAK X${multiplier.toFixed(1)}`;\n                            comboUI.style.opacity = '1';\n                            comboUI.classList.add('streak-active');\n                        }\n                        if (flameUI) {\n                            flameUI.style.display = 'inline-block';\n                            flameUI.classList.add('streak-active');\n                        }\n                    } else {\n                        feedbackDisplay.textContent = `Correct Match! +100 \u2728`;\n                        if (comboUI) {\n                            comboUI.textContent = `COMBO ${gameState.combo}`;\n                            comboUI.style.opacity = '1';\n                            comboUI.classList.remove('streak-active');\n                        }\n                        if (flameUI) {\n                            flameUI.style.display = 'none';\n                        }\n                    }\n\n                    // Visual feedback\n                    item.feedbackIcon = gameState.combo >= 3 ? '\ud83d\udd25' : '\u2705';\n                    item.feedbackTimer = 1.0;\n                    item.flashTimer = 0.5;\n                    item.flashColor = gameState.combo >= 3 ? 'rgba(255, 69, 0, 0.5)' : 'rgba(74, 222, 128, 0.5)';\n                } else {\n                    showAstraReaction(false);\n                    gameState.combo = 0;\n                    const comboUI = document.getElementById('combo-display');\n                    if (comboUI) {\n                        comboUI.style.opacity = '0';\n                        comboUI.classList.remove('streak-active');\n                    }\n                    const flameUI = document.getElementById('streak-flame');\n                    if (flameUI) {\n                        flameUI.style.display = 'none';\n                        flameUI.classList.remove('streak-active');\n                    }\n\n                    if (!item.hasGivenPoints) {\n                        gameState.totalScore += 10;\n                        gameState.roundPointsAccumulated += 10;\n                        gameState.totalIncorrectPicks++;\n                        item.hasGivenPoints = true;\n                    }\n                    if (playerState.sfxEnabled) playSound('sfx_item_place_wrong');\n                    feedbackDisplay.textContent = 'Streak Broken! +10';\n                    \n                    // Visual feedback\n                    item.feedbackIcon = '\u274c';\n                    item.feedbackTimer = 1.0;\n                    item.flashTimer = 0.5;\n                    item.flashColor = 'rgba(255, 107, 107, 0.5)';\n                }\n\n                // Update stars based on progress\n                const settings = window.gameConfig.settings;\n                const stars = gameState.totalScore >= settings.perfectThreshold ? 3 : \n                              gameState.totalScore >= settings.amazingThreshold ? 2 : \n                              gameState.totalScore >= settings.greatThreshold ? 1 : 0;\n                starsDisplay.innerHTML = '\u2b50'.repeat(stars);\n            }\n\n            function handleInputStart(e) {\n                if (gameState.isPaused) return;\n                e.preventDefault();\n                initAudio();\n                const pos = getInputPosition(e);\n                gameState.mousePos = pos;\n                gameState.clickStartPos = { ...pos }; // Track for tap detection\n                \n                // 1. Check available items for dragging\n                for (let item of gameState.availableItems) {\n                    if (item.isAvailable && isPointInItem(pos.x, pos.y, item)) {\n                        gameState.draggedItem = item;\n                        if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                        return;\n                    }\n                }\n\n                // 2. Check equipped items for removing (un-equipping)\n                const angle = gameState.rotationAngle || 0;\n                const scaleX = Math.cos(angle);\n                \n                for (let slot in gameState.equippedItems) {\n                    const item = gameState.equippedItems[slot];\n                    if (item) {\n                        const config = SLOT_CONFIG[slot];\n                        const itemX = mannequinX;\n                        const itemY = mannequinY + config.offsetY;\n                        \n                        // Hitbox scales with character rotation\n                        const halfW = (config.maxW / 2) * Math.abs(scaleX);\n                        const halfH = config.maxH / 2;\n                        \n                        if (pos.x >= itemX - halfW && pos.x <= itemX + halfW &&\n                            pos.y >= itemY - halfH && pos.y <= itemY + halfH) {\n                            \n                            gameState.equippedItems[slot] = null;\n                            item.isAvailable = true;\n                            item.x = item.originalX;\n                            item.y = item.originalY;\n                            if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                            feedbackDisplay.textContent = '';\n                            return;\n                        }\n                    }\n                }\n\n                // 3. Start rotation if clicking the character area\n                if (isNearMannequin(pos.x, pos.y)) {\n                    gameState.isRotating = true;\n                    gameState.lastMouseX = pos.x;\n                }\n            }\n\n            function handleInputMove(e) {\n                if (gameState.isPaused) return;\n                e.preventDefault();\n                const pos = getInputPosition(e);\n                gameState.mousePos = pos;\n                if (gameState.draggedItem) {\n                    gameState.draggedItem.x = pos.x;\n                    gameState.draggedItem.y = pos.y;\n                } else if (gameState.isRotating) {\n                    const dx = pos.x - gameState.lastMouseX;\n                    gameState.rotationVelocity = dx * 0.02;\n                    gameState.rotationAngle = (gameState.rotationAngle || 0) + dx * 0.02;\n                    gameState.lastMouseX = pos.x;\n                }\n            }\n\n            function handleInputEnd(e) {\n                if (gameState.isPaused) return;\n                e.preventDefault();\n                const pos = gameState.mousePos;\n                \n                gameState.isRotating = false;\n                \n                if (gameState.draggedItem) {\n                    const item = gameState.draggedItem;\n                    \n                    // Check if it was a tap or a drag\n                    const dist = Math.sqrt(Math.pow(pos.x - gameState.clickStartPos.x, 2) + Math.pow(pos.y - gameState.clickStartPos.y, 2));\n                    const isTap = dist < DRAG_THRESHOLD;\n\n                    if (isNearMannequin(pos.x, pos.y) || isTap) {\n                        equipItem(item);\n                    } else {\n                        item.x = item.originalX;\n                        item.y = item.originalY;\n                    }\n                    gameState.draggedItem = null;\n                }\n            }\n\n                const btnHint = document.getElementById('btn-hint');\n                btnHint.onclick = () => {\n                    if (gameState.isPaused || homeScreen.classList.contains('active')) return;\n                    if (gameState.hintTimer > 0) return;\n                    \n                    gameState.hintTimer = 3.0; // Hint lasts 3 seconds\n                    if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                    feedbackDisplay.textContent = 'Look for the sparkles! \u2728';\n                    \n                    // Disable button briefly\n                    btnHint.style.opacity = '0.5';\n                    setTimeout(() => {\n                        btnHint.style.opacity = '1';\n                    }, 5000);\n                };\n\n                btnReroll.onclick = () => {\n                    if (gameState.isPaused || homeScreen.classList.contains('active')) return;\n                    startAdRefresh();\n                };\n\n                collectItemsButton.onclick = () => {\n                    adScreen.classList.remove('active');\n                    collectItemsButton.style.display = 'none';\n                    refreshAvailableItems();\n                    gameState.isPaused = false;\n                };\n\n                function startAdRefresh() {\n                    gameState.isPaused = true;\n                    adScreen.classList.add('active');\n                    collectItemsButton.style.display = 'none';\n                    adCountdown.style.display = 'block';\n                    \n                    if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                    \n                    let count = 3;\n                    adCountdown.textContent = count;\n                    \n                    const interval = setInterval(() => {\n                        count--;\n                        if (count <= 0) {\n                            clearInterval(interval);\n                            adCountdown.style.display = 'none';\n                            collectItemsButton.style.display = 'block';\n                        } else {\n                            adCountdown.textContent = count;\n                        }\n                    }, 1000);\n                }\n\n                function refreshAvailableItems() {\n                    const allItems = window.gameConfig.clothingItems;\n                    const numItems = window.gameConfig.settings.itemsPerRound;\n                    \n                    // Filter out items already equipped\n                    const equippedIds = Object.values(gameState.equippedItems)\n                        .filter(item => item !== null)\n                        .map(item => item.id);\n                        \n                    const filteredItems = allItems.filter(item => !equippedIds.includes(item.id));\n                    \n                    // Theme logic for refresh too\n                    let correctPool, incorrectPool;\n                    if (gameState.currentRoundType === 'COLOR_MATCH') {\n                        const targetColor = THEME_COLORS[gameState.currentTheme.name] || \"white\";\n                        correctPool = filteredItems.filter(item => ITEM_COLORS[item.id] === targetColor);\n                        incorrectPool = filteredItems.filter(item => ITEM_COLORS[item.id] !== targetColor);\n                    } else {\n                        const themeTagsSet = new Set(gameState.currentTheme.tags);\n                        correctPool = filteredItems.filter(item => {\n                            const itemTagsSet = new Set(item.tags);\n                            return [...itemTagsSet].some(tag => themeTagsSet.has(tag));\n                        });\n                        incorrectPool = filteredItems.filter(item => {\n                            const itemTagsSet = new Set(item.tags);\n                            return ![...itemTagsSet].some(tag => themeTagsSet.has(tag));\n                        });\n                    }\n\n                    const targetCorrectCount = Math.min(3, Math.max(2, correctPool.length));\n                    const selectedCorrect = [...correctPool].sort(() => Math.random() - 0.5).slice(0, targetCorrectCount);\n                    const selectedIncorrect = [...incorrectPool].sort(() => Math.random() - 0.5).slice(0, numItems - selectedCorrect.length);\n                    \n                    const selectedItemsPool = [...selectedCorrect, ...selectedIncorrect].sort(() => Math.random() - 0.5);\n                    \n                    const positions = getItemPositions(selectedItemsPool.length);\n                    gameState.availableItems = selectedItemsPool.map((item, i) => {\n                        const isCorrect = selectedCorrect.some(c => c.id === item.id);\n                        return {\n                            ...item,\n                            x: positions[i].x,\n                            y: positions[i].y,\n                            originalX: positions[i].x,\n                            originalY: positions[i].y,\n                            isAvailable: true,\n                            isCorrect: isCorrect,\n                            hasGivenPoints: false,\n                            feedbackIcon: null,\n                            feedbackTimer: 0,\n                            flashTimer: 0,\n                            flashColor: null\n                        };\n                    });\n                    \n                    if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                    createParticles(canvas.width / 2, canvas.height / 2, '#ffd93d', 20);\n                }\n\n                playNowButton.onclick = () => {\n                    if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                    quickPlay();\n                };\n\n                btnPause.onclick = () => {\n                    togglePause();\n                };\n\n                resumeButton.onclick = () => {\n                    togglePause();\n                };\n\n                quitButton.onclick = async () => {\n                    togglePause(false); // Ensure unpaused\n                    endScreen.classList.remove('active');\n                    pauseScreen.classList.remove('active');\n                    homeScreen.classList.add('active');\n                    gameUIHeader.style.display = 'none';\n                    gameUIFooter.style.display = 'none';\n                    document.getElementById('gameplay-mascot').style.display = 'none';\n                    stopMusic();\n                    await loadProgress();\n                };\n\n                function togglePause(forcedState) {\n                    if (gameState.currentRound === 0) return; // Not in game\n                    \n                    gameState.isPaused = forcedState !== undefined ? forcedState : !gameState.isPaused;\n                    \n                    if (gameState.isPaused) {\n                        pauseScreen.classList.add('active');\n                        if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                        // Optional: Pause music if you want\n                        // if (window.gameplayMusic) audioContext.suspend();\n                    } else {\n                        pauseScreen.classList.remove('active');\n                        if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                        // if (window.gameplayMusic) audioContext.resume();\n                    }\n                }\n\n                backHomeButton.onclick = async () => {\n                    if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                    endScreen.classList.remove('active');\n                    homeScreen.classList.add('active');\n                    gameUIHeader.style.display = 'none';\n                    gameUIFooter.style.display = 'none';\n                    document.getElementById('gameplay-mascot').style.display = 'none';\n                    stopMusic();\n                    await loadProgress();\n                };\n\n                const shareLookButton = document.getElementById('share-look-button');\n                const shareScreen = document.getElementById('share-screen');\n                const closeShareButton = document.getElementById('close-share-button');\n                const shareCanvas = document.getElementById('share-canvas');\n                const shareCtx = shareCanvas.getContext('2d');\n                const shareAstra = document.getElementById('share-astra');\n                const shareScoreText = document.getElementById('share-score');\n                const shareStarsText = document.getElementById('share-stars');\n\n                shareLookButton.onclick = () => {\n                    if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                    \n                    // Setup Astra on share card\n                    const astraAsset = lib.getAsset('astra_mascot');\n                    if (astraAsset) shareAstra.src = astraAsset.url;\n\n                    // Update Score and Stars\n                    shareScoreText.textContent = `Total Score: ${gameState.totalScore}`;\n                    const settings = window.gameConfig.settings;\n                    let stars = 1;\n                    if (gameState.totalScore >= settings.perfectThreshold) stars = 3;\n                    else if (gameState.totalScore >= settings.amazingThreshold) stars = 3;\n                    else if (gameState.totalScore >= settings.greatThreshold) stars = 2;\n                    shareStarsText.textContent = '\u2b50'.repeat(stars);\n\n                    // Draw Character on Share Canvas\n                    renderShareCharacter();\n                    \n                    shareScreen.classList.add('active');\n                };\n\n                closeShareButton.onclick = () => {\n                    if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                    shareScreen.classList.remove('active');\n                };\n\n                function renderShareCharacter() {\n                    shareCtx.clearRect(0, 0, shareCanvas.width, shareCanvas.height);\n                    \n                    // Background\n                    const gradient = shareCtx.createLinearGradient(0, 0, 0, shareCanvas.height);\n                    gradient.addColorStop(0, '#f0f0f0');\n                    gradient.addColorStop(1, '#e0e0e0');\n                    shareCtx.fillStyle = gradient;\n                    shareCtx.fillRect(0, 0, shareCanvas.width, shareCanvas.height);\n\n                    const centerX = shareCanvas.width / 2;\n                    const centerY = shareCanvas.height / 2 + 50;\n                    const scale = 1.2;\n\n                    const selectedChar = window.gameConfig.characters.find(c => c.id === playerState.selectedCharacterId) || window.gameConfig.characters[0];\n                    const mannequinAsset = assetCache[selectedChar.assetId];\n\n                    if (mannequinAsset) {\n                        const img = mannequinAsset.img;\n                        const w = MANNEQUIN_WIDTH * scale;\n                        const h = MANNEQUIN_HEIGHT * scale;\n                        shareCtx.drawImage(img, centerX - w/2, centerY - h/2, w, h);\n                    }\n\n                    DRAW_ORDER.forEach(slot => {\n                        const item = gameState.equippedItems[slot];\n                        if (item && assetCache[item.assetId]) {\n                            const asset = assetCache[item.assetId];\n                            const config = SLOT_CONFIG[slot];\n                            const w = config.maxW * scale;\n                            const h = config.maxH * scale;\n                            const offsetY = config.offsetY * scale;\n                            const img = asset.img;\n                            \n                            // Use a helper to maintain aspect ratio on share canvas\n                            const imgAspect = img.width / img.height;\n                            let drawW = w;\n                            let drawH = h;\n                            const boxAspect = w / h;\n                            if (imgAspect > boxAspect) drawH = w / imgAspect;\n                            else drawW = h * imgAspect;\n\n                            shareCtx.drawImage(img, centerX - drawW/2, centerY + offsetY - drawH/2, drawW, drawH);\n                        }\n                    });\n                }\n                resetButton.onclick = async () => {\n                    if (playerState.sfxEnabled) playSound('sfx_item_pickup');\n                    await window.resetProgress();\n                };\n                musicToggle.onchange = async (e) => {\n                    playerState.musicEnabled = e.target.checked;\n                    if (!playerState.musicEnabled) stopMusic();\n                    else if (!homeScreen.classList.contains('active')) playSound('music_gameplay', true);\n                    await saveProgress();\n                };\n                sfxToggle.onchange = async (e) => {\n                    playerState.sfxEnabled = e.target.checked;\n                    await saveProgress();\n                };\n            }\n\n            // Edit mode handlers\n            if (mode === 'edit') {\n                homeScreen.classList.remove('active');\n                editIndicator.style.display = 'block';\n                \n                const previewTheme = window.gameConfig.themes[0];\n                themeDisplay.textContent = previewTheme.name + ' (Preview)';\n                gameUIHeader.style.display = 'flex';\n                btnPause.style.display = 'none';\n                btnReroll.style.display = 'none';\n                timerDisplay.style.display = 'none';\n                \n                if (previewTheme.backgroundId && assetCache[previewTheme.backgroundId]) {\n                    gameWorld.style.background = `url('${assetCache[previewTheme.backgroundId].img.src}') center/cover`;\n                }\n                \n                const sampleItems = window.gameConfig.clothingItems.slice(0, 8);\n                const positions = getItemPositions(sampleItems.length);\n                gameState.availableItems = sampleItems.map((item, i) => ({\n                    ...item, x: positions[i].x, y: positions[i].y, isAvailable: true\n                }));\n            }\n\n            // Offscreen canvas for per-clothing 3D shading (pixel-perfect, reused each frame)\n            const clothingOffCanvas = document.createElement('canvas');\n            clothingOffCanvas.width = 300;\n            clothingOffCanvas.height = 300;\n            const clothingOffCtx = clothingOffCanvas.getContext('2d');\n\n            // Game loop\n            let lastTime = 0;\n            let timerAccumulator = 0;\n            let lastWarningTime = 0;\n\n            function gameLoop(timestamp) {\n                const deltaTime = Math.min((timestamp - lastTime) / 1000, 0.11);\n                lastTime = timestamp;\n                ctx.clearRect(0, 0, canvas.width, canvas.height);\n                // Apply rotation inertia when not actively dragging\n                if (!gameState.isRotating && Math.abs(gameState.rotationVelocity) > 0.0005) {\n                    gameState.rotationAngle = (gameState.rotationAngle || 0) + gameState.rotationVelocity;\n                    gameState.rotationVelocity *= 0.92; // friction\n                }\n                drawMannequin(timestamp);\n                gameState.availableItems.forEach(item => {\n                    if (item.isAvailable) drawClothingItem(item, timestamp, item === gameState.draggedItem ? 0.7 : 1);\n                    \n                    // Update feedback timers\n                    if (item.feedbackTimer > 0) item.feedbackTimer -= deltaTime;\n                    if (item.flashTimer > 0) item.flashTimer -= deltaTime;\n                });\n                drawParticles();\n\n                if (mode === 'play' && gameState.currentTheme && !gameState.isPaused && !homeScreen.classList.contains('active') && !roundScreen.classList.contains('active') && !endScreen.classList.contains('active')) {\n                    timerAccumulator += deltaTime;\n                    \n                    // Update hint timer\n                    if (gameState.hintTimer > 0) {\n                        gameState.hintTimer -= deltaTime;\n                    }\n\n                    if (timerAccumulator >= 1) {\n                        timerAccumulator = 0;\n                        gameState.timer = Math.max(0, gameState.timer - 1);\n                        const minutes = Math.floor(gameState.timer / 60);\n                        const seconds = gameState.timer % 60;\n                        timerValue.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;\n                        if (gameState.timer <= 5) {\n                            timerDisplay.classList.add('warning');\n                            if (timestamp - lastWarningTime > 1000) {\n                                if (playerState.sfxEnabled) playSound('sfx_timer_warning');\n                                lastWarningTime = timestamp;\n                            }\n                        } else {\n                            timerDisplay.classList.remove('warning');\n                        }\n                        if (gameState.timer === 0) endRound();\n                    }\n                    scoreDisplay.textContent = `Score: ${gameState.totalScore}`;\n                }\n                requestAnimationFrame(gameLoop);\n            }\n            requestAnimationFrame(gameLoop);\n        }\n    </script>\n</body>\n</html>", "game_config": "{\n  \"themes\": [\n    {\n      \"name\": \"Beach Party\",\n      \"tags\": [\n        \"casual\",\n        \"summer\",\n        \"outdoor\"\n      ],\n      \"backgroundId\": \"background_beach\"\n    },\n    {\n      \"name\": \"Space Explorer\",\n      \"tags\": [\n        \"futuristic\",\n        \"adventure\",\n        \"tech\"\n      ],\n      \"backgroundId\": \"background_space\"\n    },\n    {\n      \"name\": \"Medieval Knight\",\n      \"tags\": [\n        \"historical\",\n        \"fantasy\",\n        \"armor\"\n      ],\n      \"backgroundId\": \"background_castle\"\n    },\n    {\n      \"name\": \"80s Rockstar\",\n      \"tags\": [\n        \"retro\",\n        \"music\",\n        \"edgy\"\n      ],\n      \"backgroundId\": \"background_stage\"\n    },\n    {\n      \"name\": \"Formal Gala\",\n      \"tags\": [\n        \"formal\",\n        \"elegant\",\n        \"fancy\"\n      ],\n      \"backgroundId\": \"background_ballroom\"\n    },\n    {\n      \"name\": \"Pirate Captain\",\n      \"tags\": [\n        \"adventure\",\n        \"historical\",\n        \"nautical\"\n      ],\n      \"backgroundId\": \"background_beach\"\n    },\n    {\n      \"name\": \"Winter Wonderland\",\n      \"tags\": [\n        \"winter\",\n        \"cozy\",\n        \"outdoor\"\n      ],\n      \"backgroundId\": \"background_castle\"\n    },\n    {\n      \"name\": \"Safari Adventure\",\n      \"tags\": [\n        \"outdoor\",\n        \"adventure\",\n        \"practical\"\n      ],\n      \"backgroundId\": \"background_beach\"\n    },\n    {\n      \"name\": \"Superhero\",\n      \"tags\": [\n        \"fantasy\",\n        \"heroic\",\n        \"action\"\n      ],\n      \"backgroundId\": \"background_space\"\n    },\n    {\n      \"name\": \"Chef's Kitchen\",\n      \"tags\": [\n        \"professional\",\n        \"culinary\",\n        \"practical\"\n      ],\n      \"backgroundId\": \"background_ballroom\"\n    }\n  ],\n  \"clothingItems\": [\n    {\n      \"id\": \"item_hat_pirate\",\n      \"assetId\": \"hat_pirate\",\n      \"name\": \"Pirate Hat\",\n      \"slot\": \"head\",\n      \"tags\": [\n        \"adventure\",\n        \"historical\",\n        \"nautical\"\n      ]\n    },\n    {\n      \"id\": \"item_hat_chef\",\n      \"assetId\": \"hat_chef\",\n      \"name\": \"Chef Hat\",\n      \"slot\": \"head\",\n      \"tags\": [\n        \"professional\",\n        \"culinary\",\n        \"practical\"\n      ]\n    },\n    {\n      \"id\": \"item_hat_beanie\",\n      \"assetId\": \"hat_beanie\",\n      \"name\": \"Winter Beanie\",\n      \"slot\": \"head\",\n      \"tags\": [\n        \"winter\",\n        \"cozy\",\n        \"casual\"\n      ]\n    },\n    {\n      \"id\": \"item_hat_crown\",\n      \"assetId\": \"hat_crown\",\n      \"name\": \"Royal Crown\",\n      \"slot\": \"head\",\n      \"tags\": [\n        \"formal\",\n        \"fantasy\",\n        \"elegant\"\n      ]\n    },\n    {\n      \"id\": \"item_hat_space\",\n      \"assetId\": \"hat_space_helmet\",\n      \"name\": \"Space Helmet\",\n      \"slot\": \"head\",\n      \"tags\": [\n        \"futuristic\",\n        \"adventure\",\n        \"tech\"\n      ]\n    },\n    {\n      \"id\": \"item_top_tshirt\",\n      \"assetId\": \"top_tshirt_casual\",\n      \"name\": \"Casual T-Shirt\",\n      \"slot\": \"top\",\n      \"tags\": [\n        \"casual\",\n        \"everyday\",\n        \"comfortable\"\n      ]\n    },\n    {\n      \"id\": \"item_top_leather\",\n      \"assetId\": \"top_leather_jacket\",\n      \"name\": \"Leather Jacket\",\n      \"slot\": \"top\",\n      \"tags\": [\n        \"retro\",\n        \"music\",\n        \"edgy\"\n      ]\n    },\n    {\n      \"id\": \"item_top_armor\",\n      \"assetId\": \"top_armor_chest\",\n      \"name\": \"Knight Armor\",\n      \"slot\": \"top\",\n      \"tags\": [\n        \"historical\",\n        \"fantasy\",\n        \"armor\"\n      ]\n    },\n    {\n      \"id\": \"item_top_chef\",\n      \"assetId\": \"top_chef_coat\",\n      \"name\": \"Chef Coat\",\n      \"slot\": \"top\",\n      \"tags\": [\n        \"professional\",\n        \"culinary\",\n        \"practical\"\n      ]\n    },\n    {\n      \"id\": \"item_top_hawaiian\",\n      \"assetId\": \"top_hawaiian_shirt\",\n      \"name\": \"Hawaiian Shirt\",\n      \"slot\": \"top\",\n      \"tags\": [\n        \"casual\",\n        \"summer\",\n        \"outdoor\"\n      ]\n    },\n    {\n      \"id\": \"item_bottom_jeans\",\n      \"assetId\": \"bottom_jeans\",\n      \"name\": \"Blue Jeans\",\n      \"slot\": \"bottom\",\n      \"tags\": [\n        \"casual\",\n        \"everyday\",\n        \"comfortable\"\n      ]\n    },\n    {\n      \"id\": \"item_bottom_shorts\",\n      \"assetId\": \"bottom_shorts_beach\",\n      \"name\": \"Beach Shorts\",\n      \"slot\": \"bottom\",\n      \"tags\": [\n        \"casual\",\n        \"summer\",\n        \"outdoor\"\n      ]\n    },\n    {\n      \"id\": \"item_bottom_skirt\",\n      \"assetId\": \"bottom_skirt_fancy\",\n      \"name\": \"Fancy Skirt\",\n      \"slot\": \"bottom\",\n      \"tags\": [\n        \"formal\",\n        \"elegant\",\n        \"fancy\"\n      ]\n    },\n    {\n      \"id\": \"item_bottom_space\",\n      \"assetId\": \"bottom_pants_space\",\n      \"name\": \"Space Pants\",\n      \"slot\": \"bottom\",\n      \"tags\": [\n        \"futuristic\",\n        \"adventure\",\n        \"tech\"\n      ]\n    },\n    {\n      \"id\": \"item_bottom_cargo\",\n      \"assetId\": \"bottom_cargo_pants\",\n      \"name\": \"Cargo Pants\",\n      \"slot\": \"bottom\",\n      \"tags\": [\n        \"outdoor\",\n        \"adventure\",\n        \"practical\"\n      ]\n    },\n    {\n      \"id\": \"item_shoes_sneakers\",\n      \"assetId\": \"shoes_sneakers\",\n      \"name\": \"Red Sneakers\",\n      \"slot\": \"feet\",\n      \"tags\": [\n        \"casual\",\n        \"sporty\",\n        \"comfortable\"\n      ]\n    },\n    {\n      \"id\": \"item_shoes_pirate\",\n      \"assetId\": \"shoes_boots_pirate\",\n      \"name\": \"Pirate Boots\",\n      \"slot\": \"feet\",\n      \"tags\": [\n        \"adventure\",\n        \"historical\",\n        \"nautical\"\n      ]\n    },\n    {\n      \"id\": \"item_shoes_space\",\n      \"assetId\": \"shoes_space_boots\",\n      \"name\": \"Space Boots\",\n      \"slot\": \"feet\",\n      \"tags\": [\n        \"futuristic\",\n        \"adventure\",\n        \"tech\"\n      ]\n    },\n    {\n      \"id\": \"item_shoes_heels\",\n      \"assetId\": \"shoes_heels_fancy\",\n      \"name\": \"Fancy Heels\",\n      \"slot\": \"feet\",\n      \"tags\": [\n        \"formal\",\n        \"elegant\",\n        \"fancy\"\n      ]\n    },\n    {\n      \"id\": \"item_shoes_flipflops\",\n      \"assetId\": \"shoes_flipflops\",\n      \"name\": \"Flip Flops\",\n      \"slot\": \"feet\",\n      \"tags\": [\n        \"casual\",\n        \"summer\",\n        \"outdoor\"\n      ]\n    },\n    {\n      \"id\": \"item_accessory_sunglasses\",\n      \"assetId\": \"accessory_sunglasses\",\n      \"name\": \"Sunglasses\",\n      \"slot\": \"face\",\n      \"tags\": [\n        \"casual\",\n        \"summer\",\n        \"stylish\"\n      ]\n    },\n    {\n      \"id\": \"item_accessory_cape\",\n      \"assetId\": \"accessory_cape_hero\",\n      \"name\": \"Hero Cape\",\n      \"slot\": \"top\",\n      \"tags\": [\n        \"fantasy\",\n        \"heroic\",\n        \"action\"\n      ]\n    },\n    {\n      \"id\": \"item_accessory_scarf\",\n      \"assetId\": \"accessory_scarf_winter\",\n      \"name\": \"Winter Scarf\",\n      \"slot\": \"top\",\n      \"tags\": [\n        \"winter\",\n        \"cozy\",\n        \"outdoor\"\n      ]\n    },\n    {\n      \"id\": \"item_accessory_eyepatch\",\n      \"assetId\": \"accessory_eyepatch\",\n      \"name\": \"Eye Patch\",\n      \"slot\": \"face\",\n      \"tags\": [\n        \"adventure\",\n        \"historical\",\n        \"nautical\"\n      ]\n    },\n    {\n      \"id\": \"item_accessory_apron\",\n      \"assetId\": \"accessory_apron\",\n      \"name\": \"Chef Apron\",\n      \"slot\": \"top\",\n      \"tags\": [\n        \"professional\",\n        \"culinary\",\n        \"practical\"\n      ]\n    }\n  ],\n  \"settings\": {\n    \"startingTimer\": 30,\n    \"timerReduction\": 1,\n    \"numberOfRounds\": 5,\n    \"itemsPerRound\": 8,\n    \"greatThreshold\": 1000,\n    \"amazingThreshold\": 1500,\n    \"perfectThreshold\": 2000\n  },\n  \"characters\": [\n    {\n      \"assetId\": \"character_mannequin\",\n      \"id\": \"mannequin\",\n      \"unlockLevel\": 0,\n      \"name\": \"Classic\"\n    },\n    {\n      \"assetId\": \"char_robot\",\n      \"id\": \"robot\",\n      \"name\": \"Robo-Style\",\n      \"unlockLevel\": 2\n    },\n    {\n      \"unlockLevel\": 4,\n      \"name\": \"Zorgon\",\n      \"id\": \"alien\",\n      \"assetId\": \"char_alien\"\n    },\n    {\n      \"name\": \"Artiste\",\n      \"unlockLevel\": 6,\n      \"assetId\": \"char_wood\",\n      \"id\": \"wood\"\n    }\n  ],\n  \"levels\": [\n    {\n      \"requiredStars\": 0,\n      \"themeIndices\": [\n        0,\n        9\n      ],\n      \"id\": 1,\n      \"name\": \"Beach & Kitchen\"\n    },\n    {\n      \"id\": 2,\n      \"requiredStars\": 2,\n      \"themeIndices\": [\n        1,\n        2\n      ],\n      \"name\": \"Space & Castle\"\n    },\n    {\n      \"name\": \"Music & Gala\",\n      \"id\": 3,\n      \"requiredStars\": 5,\n      \"themeIndices\": [\n        3,\n        4\n      ]\n    },\n    {\n      \"name\": \"Pirate & Winter\",\n      \"requiredStars\": 8,\n      \"themeIndices\": [\n        5,\n        6\n      ],\n      \"id\": 4\n    },\n    {\n      \"name\": \"Safari & Hero\",\n      \"requiredStars\": 11,\n      \"themeIndices\": [\n        7,\n        8\n      ],\n      \"id\": 5\n    }\n  ]\n}", "game_design_document": "## Core Game Loop and Mechanics\n\n**Costume Chaos** is a fast-paced fashion game where players race against the clock to create themed outfits from random clothing items.\n\n### Gameplay Flow:\n1. **Theme Reveal**: A random theme appears (e.g., \"Beach Party\", \"Space Explorer\", \"Medieval Knight\", \"80s Rockstar\")\n2. **Clothing Selection**: Players see 8-12 random clothing items scattered around the screen\n3. **Dress Up**: Players drag and drop items onto a character mannequin in the center\n4. **Time Pressure**: 30 seconds to complete the outfit\n5. **Scoring**: Points awarded based on how well items match the theme\n6. **Next Round**: New theme and new random items appear\n\n### Scoring System:\n- Each clothing item has hidden \"theme tags\" (e.g., \"formal\", \"casual\", \"sporty\", \"fantasy\")\n- Themes have matching tags that score points\n- Perfect matches: 100 points per item\n- Partial matches: 50 points per item\n- Wrong items: 0 points\n- Bonus: +200 points for completing outfit with all slots filled\n- Time bonus: +10 points per second remaining\n\n### Clothing Slots:\n- Head (hats, helmets, crowns)\n- Top (shirts, jackets, armor)\n- Bottom (pants, skirts, shorts)\n- Feet (shoes, boots, sandals)\n- Accessory (glasses, jewelry, props)\n\n### Win/Lose Conditions:\n- **Win**: Score above a threshold (500+ points = Great!, 700+ = Amazing!, 900+ = Perfect!)\n- **Continue**: Play endless rounds, trying to beat your high score\n- **Challenge**: Each round gets slightly faster (timer reduces by 1 second, minimum 15 seconds)\n\n## Starting Scope\n\n**First Implementation Should Include:**\n- Single player mode only\n- 10 diverse themes with clear visual identity\n- 30+ clothing items with varied styles\n- Simple drag-and-drop mechanics\n- Score display and feedback\n- 5 rounds per game session\n- Basic particle effects for successful placements\n- Victory screen with final score\n\n**Future Additions:**\n- Multiplayer comparison mode\n- More themes and clothing items\n- Character customization (skin tone, body type)\n- Photo mode to save outfits\n- Daily challenges\n- Unlockable rare items\n\n## Game Perspective\n\n**Fixed-screen** with a centered character mannequin and clothing items arranged around the edges. The layout is optimized for portrait mobile screens (720\u00d71280).\n\n## Game World and Level Design\n\n### Screen Layout:\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502   THEME: \"Beach Party\"  \u2502 \u2190 Top 100px: Theme display\n\u2502      \u23f1\ufe0f 0:23            \u2502 \u2190 Timer\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502  \ud83d\udc55  \ud83e\ude73  \ud83d\udc52  \ud83d\udc53        \u2502 \u2190 Top shelf: 4 items\n\u2502                         \u2502\n\u2502      [CHARACTER]        \u2502 \u2190 Center: Dress-up mannequin\n\u2502         \ud83d\udc64              \u2502   (400px tall)\n\u2502                         \u2502\n\u2502  \ud83d\udc5f  \ud83e\udde5  \ud83d\udc57  \ud83c\udfa9        \u2502 \u2190 Bottom shelf: 4 items\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502   Score: 450  \u2b50\u2b50\u2b50    \u2502 \u2190 Bottom 100px: Score/feedback\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n### Visual Design:\n- Clean, colorful interface with rounded corners\n- Character mannequin is a simple, neutral figure (paper doll style)\n- Clothing items are oversized and cartoonish for easy recognition\n- Bright background colors that change per theme\n- Sparkle effects when items are correctly placed\n\n## Edit Mode Design\n\nPlayers can customize:\n\n### Theme List:\n- Add/remove/edit theme names\n- Assign theme tags (formal, casual, sporty, fantasy, historical, modern, etc.)\n- Set theme background colors\n\n### Clothing Items:\n- Add new clothing item names\n- Assign tags to items (determines scoring)\n- Set which slot each item fills\n\n### Game Settings:\n- Starting timer duration (15-60 seconds)\n- Timer reduction per round (0-5 seconds)\n- Number of rounds per game (3-10)\n- Number of items shown per round (6-15)\n- Score thresholds for ratings\n\n### Constraints:\n- Minimum 5 themes required\n- Minimum 20 clothing items required\n- At least 3 items per slot type\n- Timer must be at least 10 seconds\n\n## Control Design\n\n### Mobile (Touch):\n- **Drag & Drop**: Touch and hold a clothing item, drag to character, release to place\n- **Remove Item**: Tap an item on the character to remove it back to the shelf\n- **Auto-snap**: Items snap to the correct slot when dragged near the character\n\n### Desktop (Mouse & Keyboard):\n- **Drag & Drop**: Click and hold item, drag to character, release to place\n- **Remove Item**: Click an item on the character to remove it\n- **Keyboard Shortcuts**: \n  - Number keys 1-8 to quick-select items\n  - Space to place selected item on character\n  - R to remove last placed item\n\n### Interaction Details:\n- Items glow when picked up\n- Valid drop zones highlight when dragging\n- Satisfying \"snap\" animation and sound when placed correctly\n- Shake animation for incorrect placements\n- Items return to original position if dropped in invalid area\n\n## Art Style and Visual Theme\n\n**Cartoon/Playful Style:**\n- Bright, saturated colors\n- Thick black outlines on all elements\n- Simplified, iconic clothing designs\n- Friendly, neutral character mannequin (simple humanoid shape)\n- Smooth animations and bouncy transitions\n- Confetti and star particle effects for celebrations\n\n**Color Palette:**\n- Primary: Vibrant rainbow colors for clothing\n- Background: Soft pastels that change per theme\n- UI: White panels with colorful accents\n- Text: Bold, rounded sans-serif font\n\n**Visual Hierarchy:**\n- Character mannequin is largest and centered\n- Clothing items are medium-sized and clearly visible\n- UI elements are clean and unobtrusive\n- Theme text is large and readable\n\n## Additional Game-Specific Elements\n\n### Theme Examples (10 themes):\n1. **Beach Party** - swimwear, sunglasses, flip-flops, sun hats\n2. **Space Explorer** - helmets, spacesuits, boots, visors\n3. **Medieval Knight** - armor, helmets, capes, swords\n4. **80s Rockstar** - leather jackets, ripped jeans, sunglasses, bandanas\n5. **Formal Gala** - tuxedos, gowns, bow ties, fancy shoes\n6. **Pirate Captain** - tricorn hats, eye patches, coats, boots\n7. **Winter Wonderland** - scarves, coats, mittens, beanies\n8. **Safari Adventure** - khaki, vests, hats, boots\n9. **Superhero** - capes, masks, boots, gloves\n10. **Chef's Kitchen** - aprons, chef hats, oven mitts, clogs\n\n### Feedback System:\n- **Visual**: Stars appear above character (1-3 stars based on score)\n- **Audio**: Cheerful sounds for correct items, gentle buzz for wrong items\n- **Text**: Encouraging messages (\"Fabulous!\", \"Keep going!\", \"Perfect match!\")\n- **Particles**: Confetti burst when completing outfit with high score\n\n### Progression:\n- Each round increases difficulty slightly (faster timer)\n- Score accumulates across rounds\n- Final score determines overall rating\n- High score persists between sessions", "persistent_storage_config": {"leaderboard": {"sort_ascending": false}}, "game_metadata": {"game_id": "01KVBHHAB73SQVBDD7ZAER3N3S", "title": "Costume Chaos", "description": "Race against time to dress your character in the wackiest outfit that matches the theme!", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_9588a7e2-c646-4833-82f6-a80af1116341.webp", "created_at": 1781725325, "updated_at": 1784555743821.6213, "art_style": null}, "task_plan": null}