{"asset_map": {"kitchen_background": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/kitchen_background_5b3f654c-0041-4e58-bbb7-0fc4cb8c2e4a.webp", "type": "image", "prompt": "Generate a 16:9 wide background image of a polished, dimensional 3D CGI home kitchen scene. Based on the composition of image 1, render wooden countertops, cream cabinets, and cookware with realistic depth and soft ambient occlusion. Use refined materials with a palette of warm wood, muted rose, and champagne-gold. The scene is bathed in gentle golden morning light from a window. Calm, inviting, and uncluttered professional game environment style, high resolution, soft focus on distant details, no text or people.", "description": "A polished 3D CGI version of the cozy kitchen background with golden morning light and elegant materials.", "subtype": "background", "aspect_ratio": [1264, 848], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "digital_scale": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/digital_scale_7a916ef8-a524-4f42-a452-b95346bf65b6.webp", "type": "image", "prompt": "Generate a premium high-quality 3D CGI game prop of a digital kitchen scale. Maintain the exact silhouette and design of the scale in image 1, featuring a cream-colored platform and a digital display showing '0g'. Use a warm ivory and champagne-gold color palette with realistic dimensional edges. The material should show subtle brushed metal and polished ceramic textures with soft contact shadows. Clean, readable digital font. Isolated asset on a transparent background, professional studio lighting, high resolution, 4k detail.", "description": "A premium 3D CGI digital kitchen scale in warm ivory and champagne gold, matching the existing scale's form.", "subtype": "object", "aspect_ratio": [982, 824], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "mixing_bowl_large": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/mixing_bowl_large_84c9da6a-616a-4e8b-b504-d8cf91ba6e3d.webp", "type": "image", "prompt": "Generate a high-end 3D CGI render of a large stainless-steel mixing bowl, viewed from a slight top-down angle. The bowl should feature realistic metal thickness, a smooth curved rim, and sophisticated studio lighting with soft highlights and clean reflections. Professional patisserie quality with subtle ambient occlusion and a gentle grounding shadow. Style: Grand Luxe, crisp and dimensional. Maintain the exact silhouette and perspective of the existing bowl in image 1. Isolated object on a transparent background, no ingredients or extra props.", "description": "A premium 3D CGI stainless steel mixing bowl with professional studio lighting and realistic metallic reflections.", "subtype": "object", "aspect_ratio": [1113, 840], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "electric_mixer": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/electric_mixer_027f4194-757a-4cdd-b228-dff0361e342b.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI game prop of a handheld electric mixer. The mixer should have a cream-colored molded body with refined surfaces, visible bevels, and subtle champagne-gold accents. It features realistic polished metal beaters with soft specular highlights. Maintain the exact silhouette and view of image 1. The object should have a soft contact shadow, isolated on a transparent background, with no hands, no text, and no extra props. Professional 3D render, high resolution, clean studio lighting.", "description": "A polished 3D CGI handheld electric mixer with cream body and metal beaters.", "subtype": "object", "aspect_ratio": [828, 1264], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "cake_pan_empty": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cake_pan_empty_10c443e7-55d8-4be9-938b-775152c5b7cc.webp", "type": "image", "prompt": "Generate a premium 3D CGI object of an empty round metal cake baking tin. Use a slight top-down perspective to show the interior and believable wall thickness with a rolled rim. The material should be brushed metal with subtle champagne reflections and an elegant patisserie finish. Ensure professional lighting, clear perspective depth, and a soft grounding shadow. Maintain an isolated composition on a transparent background. Match the perspective and form of image 1 but upgrade to a realistic 3D render style.", "description": "A premium 3D CGI empty round metal cake tin with brushed metal reflections and perspective depth.", "subtype": "object", "aspect_ratio": [1024, 872], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "oven_closed": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/oven_closed_fcd5a5cd-a21c-4411-9fa0-ece40fca79c7.webp", "type": "image", "prompt": "Transform the oven in image 1 into a premium 3D CGI game prop. Maintain the original cream-colored porcelain-enamel design, closed glass door, and control dials. Enhance with distinct dimensional form, crisp bevels, realistic glass reflections, and refined champagne-gold metal accents. Soft ambient shadows, high-quality 3D render style, isolated on a transparent background, professional game asset quality, front-perspective view.", "description": "A premium 3D CGI version of the cream-colored kitchen oven with refined metal details.", "subtype": "object", "aspect_ratio": [788, 799], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "oven_open": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/oven_open_784d40ef-68ac-435f-937e-ee0a5867ce9b.webp", "type": "image", "prompt": "Transform the existing oven in image 1 into a premium 3D CGI game prop. An open modern cream-colored oven with a clean enamel finish, reflective metal racks, and a warm golden interior glow. High-quality 3D render, polished surfaces, realistic lighting and depth, soft highlights on the glass door. Isolated on a transparent background, front-angled view, no food or characters, matching a Grand Luxe patisserie aesthetic.", "description": "Premium 3D CGI open oven with a warm interior glow and metallic racks.", "subtype": "object", "aspect_ratio": [1021, 941], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "cooling_rack": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cooling_rack_482f3078-e3ea-44c6-9edd-866d73c879f5.webp", "type": "image", "prompt": "Generate a premium 3D CGI render of a rectangular wire cooling rack, isolated on a transparent background. Each wire is visibly rounded and dimensional with accurate perspective. Use a refined stainless steel or champagne metallic finish with subtle reflections and soft self-shadowing. High-quality bakery prop aesthetic, clean and polished, no extra objects or baked goods.", "description": "A premium 3D CGI stainless steel wire cooling rack with realistic metallic reflections and depth.", "subtype": "object", "aspect_ratio": [1264, 780], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "long_knife": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/long_knife_699e7580-cd03-433a-aef7-8d698518e7d6.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI prop of a long serrated bread knife. The blade is made of polished stainless steel with subtle realistic reflections and clearly visible serrations. The handle is made of warm, tactile wood with a visible grain and slight bevels. Match the shape and side-view angle of image 1. Professional studio lighting with soft shading, isolated on a transparent background, no hands, no food, no text, clean and polished game-ready asset.", "description": "A 3D CGI rendered version of the long serrated bread knife with realistic textures.", "subtype": "object", "aspect_ratio": [806, 1263], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "offset_spatula": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/offset_spatula_7c56543f-0584-4bca-91ca-004c3d9d601a.webp", "type": "image", "prompt": "Generate a professional 3D CGI offset spatula, isolated object on a transparent background. The tool features a thin, flexible brushed metal blade with a distinct offset bend and a refined warm-toned wooden handle. High-end patisserie aesthetic with elegant Grand Luxe finish, subtle metallic reflections, and soft dimensional shadows. Clean studio lighting, sharp focus, 4k resolution, no hands or extra props.", "description": "A polished 3D CGI offset spatula with a metal blade and wooden handle for spreading frosting.", "subtype": "object", "aspect_ratio": [1021, 1011], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "flour_bag": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/flour_bag_40425c8e-53f1-4e35-8591-170adf5bea10.webp", "type": "image", "prompt": "Transform the flour bag from image 1 into a premium 3D CGI baking ingredient prop. Create a high-quality 3D render of an open paper bag with realistic folded tops, visible paper thickness, soft creases, and tactile matte shading. Keep the sharp and legible English 'Flour' label from the original. Use warm ivory paper tones and gentle, soft shadows. Maintain a clean front-view, isolated composition on a transparent background. High resolution, professional studio lighting, 3D animated movie style, no hands, no extra objects.", "description": "Premium 3D CGI version of the flour bag with realistic textures and soft lighting.", "subtype": "object", "aspect_ratio": [901, 1022], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "sugar_bag": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/sugar_bag_de345924-8339-4c1e-83d1-2e8c1230f5d5.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI render of an open paper bag of sugar. The bag should have the English word 'Sugar' printed clearly on the front in a clean font. Use realistic paper textures with subtle folds, depth, and a slight visible sugar texture at the top opening. Match the premium 'Grand Luxe' style with soft studio lighting, realistic shadows, and a cozy warm aesthetic. Isolated on a transparent background, slight top-down perspective, no hands or extra objects.", "description": "A premium 3D CGI rendered open paper bag of sugar with realistic textures and 'Sugar' label.", "subtype": "object", "aspect_ratio": [902, 1021], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "eggs_carton": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/eggs_carton_cf6122d6-8d48-43ee-aab5-af8b5ac88e30.webp", "type": "image", "prompt": "An elegant 3D CGI render of an open cardboard egg carton containing six white eggs, arranged in two rows of three. High-quality 3D model with realistic paper pulp texture on the carton and smooth matte shells on the eggs. Soft contact shadows, warm studio lighting, 3/4 top-down view. Professional 3D asset, clean isolated composition on a transparent background, matching the style of the electric_mixer and kitchen_background_french_realistic.", "description": "A 3D CGI rendered open cardboard egg carton with six white eggs.", "subtype": "object", "aspect_ratio": [842, 843], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "milk_bottle": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/milk_bottle_2d206584-0149-4a8a-af76-29ba85f39e93.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI game prop of the milk bottle from image 1. Maintain the exact shape, front view, and 'Milk' English label. Transform it with professional textures: translucent glass or high-quality plastic material, visible dimensional white milk inside, crisp specular reflections, and soft contact shadows. Use a refined ivory and subtle champagne-gold accent color palette. Professional studio lighting, clean edges, isolated object on a transparent background, high resolution, 3D render style.", "description": "A polished 3D CGI milk bottle with a readable 'Milk' label and realistic material properties.", "subtype": "object", "aspect_ratio": [511, 1248], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "butter_block": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/butter_block_1b6d3fed-5120-4890-bddf-8f63103ac22a.webp", "type": "image", "prompt": "A premium 3D CGI rendered block of pale yellow butter, partially unwrapped. The wrapper is realistic with visible thickness and folds, featuring the English text 'Butter' in a clean font. Realistic tactile shading, soft contact shadow on a neutral surface. High-resolution, professional studio lighting, polished game asset quality, maintaining the same orientation and shape as the original butter_block.", "description": "Premium 3D CGI block of butter with an English label, matching the realistic kitchen style.", "subtype": "object", "aspect_ratio": [1177, 841], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "baking_powder_container": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/baking_powder_container_579c104a-ea1e-4dc2-b0eb-a1490bbcc534.webp", "type": "image", "prompt": "Generate a premium 3D CGI prop of a small baking powder container. Based on the style and label of image 1, create a highly detailed version with a dimensional lid, subtle metallic and plastic material reflections, and a crisp readable label. The aesthetic should be warm Grand Luxe patisserie style with gentle highlights and a soft contact shadow. Isolated composition, front view, transparent background, professional quality, high resolution.", "description": "Premium 3D CGI baking powder container with a polished patisserie aesthetic.", "subtype": "object", "aspect_ratio": [783, 1109], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "raw_batter": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/raw_batter_49a42ff0-1a6d-4bc7-8727-6d8cb5557bc8.webp", "type": "image", "prompt": "Generate a dimensional and appetizing 3D CGI surface of smooth, liquid pale yellow cake batter. The texture should have gentle glossy highlights, soft rounded folds, and subtle depth to look creamy and realistic. Maintain a pale vanilla color. Close-up view, high resolution, professional 3D render, no bowl, no utensils, no text, no extra ingredients, clean composition.", "description": "Dimensional and glossy 3D CGI vanilla cake batter surface.", "subtype": "other", "aspect_ratio": [1264, 843], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "cake_baking_stage1": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cake_baking_stage1_92b811f7-8c67-4406-90e1-f7f01ccc17a6.webp", "type": "image", "prompt": "Generate a detailed 3D CGI game asset of a round cake in a metal baking pan at the early baking stage. The cake should be pale and just beginning to rise, with a subtle sponge texture visible. Include realistic dimensional reflections on the metal tin, warm oven light casting soft shadows, and an appetizing, polished finish. Maintain a slight top-down angle consistent with the existing game perspective. No props, no text, and do not show it as fully baked.", "description": "A 3D CGI rendered round cake in a pan at the early pale rising stage of baking.", "subtype": "object", "aspect_ratio": [1024, 873], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "cake_baking_stage2": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cake_baking_stage2_728b3693-fcf3-4976-bc1f-301596be3422.webp", "type": "image", "prompt": "Create a high-quality 3D CGI rendered image of a round cake in a metal baking pan at the mid-baking stage. The cake should be golden and risen with a realistic sponge texture and soft warm highlights. The pan should have dimensional metallic walls and realistic shadows. Match the composition and perspective of image 1, but transform the style into a polished, professional 3D game asset. No text or extra props.", "description": "A 3D CGI rendered round cake in a pan at the mid-bake stage, showing a golden risen surface.", "subtype": "object", "aspect_ratio": [1024, 1024], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "cake_baking_stage3": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cake_baking_stage3_1fa70485-17c0-44d6-a1dc-ba656e29cd82.webp", "type": "image", "prompt": "Generate a premium 3D CGI game asset of a fully baked golden-brown sponge cake in a round metal pan. The cake should have a realistic sponge crumb texture and a perfectly browned top, matching the framing of image 1. Use warm patisserie lighting with soft contact shadows. The style should be high-quality and appetizing, maintaining a slight top-down angle consistent with a modern kitchen game. No decorations, no text, and no other objects in the frame, transparent background.", "description": "A premium 3D CGI fully baked golden-brown sponge cake in a round baking pan.", "subtype": "object", "aspect_ratio": [1024, 872], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "cake_layer_bottom": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cake_layer_bottom_23366dc4-444f-4ed9-8692-a105dbad8c21.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI bakery asset of a single bottom layer of a sponge cake. The cake should have a flat cut top surface revealing an airy, porous golden crumb texture. The sides should show natural baked edges. Use soft studio lighting with subtle shading to create dimension. Match the shape and perspective of image 1, but transformed into a polished 3D render. Isolated on a transparent background, no frosting, no text, no extra props.", "description": "A detailed 3D CGI bottom sponge cake layer showing a porous golden crumb and flat cut surface.", "subtype": "object", "aspect_ratio": [1024, 820], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "cake_layer_top": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cake_layer_top_1b543192-4b8b-458d-952f-1b4abe503fed.webp", "type": "image", "prompt": "Generate a premium 3D CGI rendered top layer of a sponge cake, featuring a smooth golden-brown rounded top and visible delicate airy crumb texture on the cut bottom. The asset should have dimensional sides, soft warm highlights, and high-quality realistic textures. Side view, isolated on a transparent background, no frosting, no decorations, no text. Match the perspective and shape of image 1 but transform it into a highly detailed realistic style.", "description": "A realistic, premium 3D CGI top layer of a sponge cake with a golden rounded top and detailed crumb texture.", "subtype": "object", "aspect_ratio": [1023, 829], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "whipped_cream_bowl": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/whipped_cream_bowl_7531e505-c754-40af-9c36-940f10447a89.webp", "type": "image", "prompt": "Transform the existing whipped_cream_bowl into a premium 3D CGI version. High-quality 3D render of a cream-colored ceramic bowl with a glossy dimensional glaze and defined rim, filled with fluffy white whipped cream showing realistic airy swirls, soft peaks, and delicate highlights. Use warm patisserie studio lighting to create soft shadows and depth. Isolated composition from a slight top-down angle, high resolution, professional digital art, transparent background.", "description": "A premium 3D CGI cream-colored bowl filled with fluffy white whipped cream.", "subtype": "object", "aspect_ratio": [999, 957], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "strawberry_whole": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/strawberry_whole_766b9c22-c39c-41d0-8879-c7141cfee06f.webp", "type": "image", "prompt": "Generate a polished, appetizing 3D CGI ripe red strawberry with green leaves. Transform the existing design into a high-fidelity food model while preserving its silhouette and vibrant red color. Add realistic seed dimples, glossy fresh skin reflecting soft light, and dimensional green leaves with visible veins. Include a soft grounding shadow beneath it. High resolution, professional food photography style, isolated on a transparent background, no extra objects or text.", "description": "A polished 3D CGI ripe strawberry topping with realistic textures.", "subtype": "object", "aspect_ratio": [843, 1168], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "strawberry_half": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/strawberry_half_67a2ac32-7525-40ee-a230-b4884a8aac66.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI render of a strawberry cut in half, showcasing a juicy dimensional pink interior with realistic seeds. The outer skin should be a vibrant glossy red with fresh green leaf details at the top. Use soft studio lighting with delicate highlights to emphasize the texture. Isolated on a transparent background, top-down perspective, matching the style of the existing kitchen tools.", "description": "A realistic 3D CGI sliced strawberry showing the juicy interior for cake decoration.", "subtype": "object", "aspect_ratio": [1216, 843], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "blueberry": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/blueberry_16c33d75-9b53-402b-8d4b-53e070f1c095.webp", "type": "image", "prompt": "Generate a single, realistic deep-blue blueberry as a premium 3D CGI cake topping. The berry should be round with a natural white bloom, featuring dimensional surface texture, soft realistic highlights, and a gentle shadow. High-resolution, professional quality, isolated on a transparent background, no leaves or props.", "description": "A crisp, premium 3D CGI blueberry topping with realistic texture and soft highlights.", "subtype": "object", "aspect_ratio": [737, 729], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "chocolate_shaving": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/chocolate_shaving_e633d046-647f-4fc7-9315-e406a6509028.webp", "type": "image", "prompt": "Generate a single, high-quality 3D CGI delicate dark chocolate curl. The shaving should have a realistic thickness, a smooth chocolate sheen with subtle highlights, and a soft professional shadow. Match the dark chocolate color and curl shape of image 1. Isolated on a transparent background, professional patisserie quality, 8k resolution, highly detailed textures.", "description": "A finely detailed 3D CGI dark chocolate curl for patisserie decoration.", "subtype": "object", "aspect_ratio": [1024, 918], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "sugar_pearl_pink": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/sugar_pearl_pink_76567c77-826e-43ca-b827-6020b30e5aaa.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI render of a single, small pink edible sugar pearl. The pearl should be perfectly round with a smooth, pearlescent finish and a soft specular highlight. Use a pale pink color palette. The asset should be shown from a slight top-down perspective with a soft contact shadow underneath to give it dimension. Clean, polished, game-ready object on a transparent background.", "description": "A 3D CGI pink edible sugar pearl with a pearlescent shine for cake decoration.", "subtype": "object", "aspect_ratio": [908, 922], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "sugar_pearl_white": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/sugar_pearl_white_54392db5-9c0f-4ef1-be11-0a7cd07877ac.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI render of a single, small white edible sugar pearl. The pearl should have a perfect round shape, a smooth pearlescent surface with a subtle iridescent highlight, dimensional shading, and a soft contact shadow to give it depth. The style should be clean and polished, matching a premium 3D dessert simulation game. Isolated object on a transparent background, no text, no extra props.", "description": "A high-quality 3D CGI white edible sugar pearl with an iridescent shine for cake decoration.", "subtype": "object", "aspect_ratio": [738, 744], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "birthday_candle_unlit": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/birthday_candle_unlit_c5b6d67a-8300-4aeb-bc2f-9b3c86d54ea6.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI model of an unlit birthday candle. The candle features a pink and white spiral-striped pattern with dimensional spiral grooves and a realistic wax texture. Include a small, unlit cotton wick at the top. The lighting should feature soft studio highlights and subtle realistic shadows to give it depth. Side view, isolated object, transparent background, professional 3D render, polished finish, matching the refined style of existing kitchen props.", "description": "Refined 3D CGI pink-and-white spiral-striped unlit birthday candle.", "subtype": "object", "aspect_ratio": [275, 1264], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "birthday_candle_lit": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/birthday_candle_lit_9a7daece-6352-44d9-84fd-0dc072549f6b.webp", "type": "image", "prompt": "A premium 3D CGI game prop of a lit birthday candle, pink and white spiral-striped design. Features detailed dimensional wax grooves, a realistic soft warm flame glow, and subtle highlights. High-quality render with realistic textures, soft shadows, and a clean professional finish. Front view, standing illustration, isolated on a transparent background.", "description": "A premium 3D CGI lit pink-and-white spiral birthday candle with a realistic flame and warm glow.", "subtype": "object", "aspect_ratio": [281, 1220], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "completed_cake_base": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/completed_cake_base_c18edb0c-54c5-4aca-a18b-eb3cc89af679.webp", "type": "image", "prompt": "A high-quality 3D CGI render of a single-layer round sponge cake fully covered in smooth ivory-white cream frosting. The cake features a realistic spatula texture on the sides, soft professional lighting with gentle highlights, and a subtle grounding shadow. The view is a slight top-down perspective, isolated on a transparent background. Match the exact silhouette and proportions of image 1 but transform it into a photorealistic, elegant style suitable for a high-end dessert simulator.", "description": "An elegant, photorealistic 3D CGI single-layer round cake covered in smooth white ivory frosting.", "subtype": "object", "aspect_ratio": [856, 849], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "progress_bar_empty": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/progress_bar_empty_77290d05-67ba-483e-a756-4be195bb7f14.webp", "type": "image", "prompt": "Generate a polished 3D CGI UI graphic of an empty progress bar frame. The asset should feature a refined ivory enamel body with a subtle beveled depth and a very restrained, elegant champagne-gold metallic trim. The interior is a crisp, clean white, currently empty. Match the exact shape and proportions of image 1. Professional studio lighting with soft shadows, high-quality material rendering, game-ready UI element, transparent background.", "description": "A polished 3D CGI empty progress bar frame with ivory enamel and champagne-gold trim.", "subtype": "ui", "aspect_ratio": [1264, 323], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "progress_bar_fill": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/progress_bar_fill_7b121484-4ee5-415b-8bec-c6239b0c4281.webp", "type": "image", "prompt": "Generate a dimensional premium 3D CGI UI element of a progress bar fill. The shape is a horizontal capsule with rounded ends. Use a vibrant pink gradient, adding a subtle glossy bevel and soft dimensional highlights consistent with a luxury game interface. High-quality render, clean edges, professional studio lighting, isolated on a transparent background.", "description": "A premium 3D CGI pink progress bar fill with glossy highlights and bevels.", "subtype": "ui", "aspect_ratio": [1264, 321], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "checkmark_icon": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/checkmark_icon_20953769-0cc6-4efd-9d32-fa1abb4dc16d.webp", "type": "image", "prompt": "Generate a clean dimensional 3D CGI UI icon of a cheerful green checkmark with rounded ends. Maintain the exact shape and silhouette of image 1. Add a subtle raised enamel finish, crisp edges, soft studio highlights, and a delicate shadow for depth. High-quality 3D render, isolated on a transparent background, professional game UI asset.", "description": "A dimensional 3D CGI green checkmark icon for UI feedback.", "subtype": "ui", "aspect_ratio": [1021, 940], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "star_particle": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/star_particle_0fd06bea-dda6-4226-b16e-3a332910259e.webp", "type": "image", "prompt": "Generate a polished 3D CGI game effect of a small four-pointed yellow star. The star should have a soft, gentle glow and a delicate luminous highlight to provide dimensional depth while remaining lightweight. Maintain a clean four-point silhouette and vibrant yellow color. Isolated object on a transparent background, high resolution, professional game-ready VFX quality.", "description": "A polished 3D CGI four-pointed yellow star particle with a soft luminous glow.", "subtype": "effect", "aspect_ratio": [1024, 1019], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "heart_particle": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/heart_particle_4b9e592d-e055-4281-9d29-2dd43ffb3d13.webp", "type": "image", "prompt": "Generate a small, clean, isolated 3D CGI pink heart with rounded dimensional depth and a soft luminous highlight. The heart should have a gentle outer glow, polished material finish, and a professional game-ready look. Match the pink tone of image 1. High resolution, transparent background, centered composition, no text or extra symbols.", "description": "A polished 3D CGI pink heart particle effect with a soft glow and rounded depth.", "subtype": "effect", "aspect_ratio": [844, 757], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "button_primary": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/button_primary_6cbda616-a7fc-4590-8137-bb0cc8bd07ee.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI rendered UI button, front view. The button should have a soft rounded rectangular shape in a warm welcoming pink color. Add dimensional beveled thickness, subtle glossy surface reflections, and a refined champagne-gold metallic edge detail. Ensure it has a soft drop shadow and a clean, polished appearance consistent with a 'Grand Luxe' edition game. Transparent background, no text, no icons, professional studio lighting. Match the pink color and form of image 1.", "description": "A premium 3D CGI pink primary UI button with champagne-gold edges and a glossy finish.", "subtype": "ui", "aspect_ratio": [1104, 529], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "button_secondary": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/button_secondary_e8543b81-24af-444c-bb18-0a7a8a832041.webp", "type": "image", "prompt": "Generate a premium dimensional 3D CGI UI button asset. The button should have a rounded rectangular form in a soft cream color with an elegant ivory finish. Add subtle beveled depth and a restrained champagne-gold trim around the edges. Professional studio lighting with a soft shadow underneath. No text, no icons, clean and polished game-ready UI element with a transparent background.", "description": "A premium 3D CGI cream-colored secondary UI button with an elegant ivory finish and champagne-gold trim.", "subtype": "ui", "aspect_ratio": [1237, 585], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "menu_panel": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/menu_panel_05dce490-762a-4e4b-aa24-071324cef5b8.webp", "type": "image", "prompt": "Generate a blank, high-quality 3D CGI UI menu panel. The panel is a large cream-colored surface with a luxurious ivory raised edge and a subtle champagne-gold inset border. Design with soft realistic depth, gentle shadows, and professional studio lighting. Keep the center clean and blank for game content. No text, icons, or extra objects. Match the 3D aesthetic of image 1.", "description": "A luxurious 3D CGI cream-colored UI menu panel with gold accents and realistic depth.", "subtype": "ui", "aspect_ratio": [1264, 802], "compression_suggestion": "low", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "click_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/click_sound_568fbf5a-426f-4505-8126-036ea95873ad.mp3", "type": "audio", "prompt": "Short, organic wooden click sound effect, gentle and muffled, soft ceramic tap, minimalist UI feedback, warm acoustic quality, 0.1 seconds.", "description": "A brief, warm wooden click sound for button interactions.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.46, "loop": false, "opus_url": null, "animation": null}, "select_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/select_sound_36b7a9bd-062b-4adb-a653-d7d5d69da68d.mp3", "type": "audio", "prompt": "Soft chime ding, light bell-like quality, high-pitched but gentle, cozy game UI sound, rewarding and clean, subtle resonance tail.", "description": "A gentle bell-like ding for item selection.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.98, "loop": false, "opus_url": null, "animation": null}, "step_complete_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/step_complete_sound_0650548a-5c5f-4f96-9138-368d7b0c5acd.mp3", "type": "audio", "prompt": "Ascending musical chime phrase, sparkling glockenspiel and soft bells, light 'ta-da' moment, celebratory and warm, 1 second duration, cozy atmosphere.", "description": "A cheerful ascending chime phrase for completing a baking step.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.8, "loop": false, "opus_url": null, "animation": null}, "pour_liquid_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/pour_liquid_sound_df01925a-82df-4b6d-8e0d-63a939acfa19.mp3", "type": "audio", "prompt": "Thick liquid pouring sound, viscous batter or cream flowing into a bowl, smooth continuous glug, low splashing, realistic culinary sound, satisfying and rich texture.", "description": "Smooth, thick batter pouring sound effect.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.04, "loop": false, "opus_url": null, "animation": null}, "mixer_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/mixer_sound_f9afde65-b98f-453e-b6e6-c0bf874e89d6.mp3", "type": "audio", "prompt": "Electric hand mixer motor hum, whirring beaters, steady mechanical sound, moderate pitch, realistic kitchen appliance, loopable, clean and consistent.", "description": "A steady, loopable electric hand mixer hum.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 4.0, "loop": true, "opus_url": null, "animation": null}, "oven_door_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/oven_door_sound_b4734047-99f8-4136-a896-c3d009994252.mp3", "type": "audio", "prompt": "Kitchen oven door opening with a slight metallic creak followed by a soft heavy thud closing, realistic domestic sound, gentle and sturdy.", "description": "Realistic sound of an oven door opening and closing.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.17, "loop": false, "opus_url": null, "animation": null}, "knife_cutting_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/knife_cutting_sound_ad48c7d3-e21f-4bb5-b3f1-b4a46912a2a0.mp3", "type": "audio", "prompt": "Soft knife cutting through spongy cake, gentle sawing motion, airy and fluffy texture sound, satisfying culinary slice, non-harsh, realistic.", "description": "Soft sawing sound of a knife cutting through a fluffy cake.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.46, "loop": false, "opus_url": null, "animation": null}, "spreading_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/spreading_sound_8e6a9110-ab5f-44db-9160-2fd30386955a.mp3", "type": "audio", "prompt": "Frosting spreading sound effect, smooth spatula moving over soft cake, creamy texture, rhythmic and gentle, like spreading butter, satisfying kitchen sound.", "description": "Soft sound of spreading cream or frosting with a spatula.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.72, "loop": false, "opus_url": null, "animation": null}, "place_decoration_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/place_decoration_sound_b292723b-a93c-4111-b060-f39869610a4e.mp3", "type": "audio", "prompt": "Playful soft boop sound, light bubble pop, gentle placement sound, organic and cute, brief 0.2s impact, cozy game aesthetic.", "description": "A playful, soft 'boop' for placing fruit or toppings.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.99, "loop": false, "opus_url": null, "animation": null}, "background_music": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/background_music_53e84b2d-2bc5-4a58-844a-f9bd3d6805e5.mp3", "type": "audio", "prompt": "Relaxing background music, gentle piano melody, soft legato strings, cozy home kitchen vibe, 70 BPM, C major, peaceful and nostalgic, loopable, high quality instrumental.", "description": "Calm, nostalgic piano and strings for the kitchen atmosphere.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30.0, "loop": true, "opus_url": null, "animation": null}, "birthday_song": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/birthday_song_02161e8a-ee40-4ffe-98e9-0df861f9a7cc.mp3", "type": "audio", "prompt": "Instrumental Happy Birthday song, cheerful and light arrangement, piano and glockenspiel lead, acoustic guitar backing, upbeat but cozy, celebratory finale.", "description": "Light, instrumental birthday theme for the final celebration.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30.0, "loop": false, "opus_url": null, "animation": null}, "ambient_kitchen_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/ambient_kitchen_sound_8cdf7954-7932-42ab-8f63-4f998f8de6f2.mp3", "type": "audio", "prompt": "Subtle kitchen ambience, distant wall clock ticking, faint hum of a refrigerator, soft air movement, peaceful and homey atmosphere, very low volume, loopable.", "description": "Subtle background room tone with a distant clock tick.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30.0, "loop": true, "opus_url": null, "animation": null}, "kitchen_background_japanese": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/kitchen_background_japanese_95171dc9-c737-46e8-9a47-6c72e9115101.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI full-screen background of a minimalist Japanese dessert kitchen. Maintain the layout of image 1, featuring wooden countertops and a view of a peaceful Zen garden. The style should be dimensional with refined tactile materials like polished light wood and cream surfaces. Use soft global illumination and gentle morning light. The color palette should be 'Grand Luxe' with cream, natural wood, muted rose, and subtle champagne-gold accents. No text, no characters, 16:9 wide aspect ratio, professional architectural visualization style.", "description": "A polished 3D CGI version of the Japanese dessert kitchen with a Zen garden view, featuring soft morning light and premium materials.", "subtype": "background", "aspect_ratio": [1264, 848], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "kitchen_background_realistic": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/kitchen_background_realistic_8d012550-da67-431e-9661-a768ef59812b.webp", "type": "image", "prompt": "Transform the composition of image 1 into a high-end 3D-rendered CGI game environment. A sun-drenched modern home kitchen with realistic dimensional cabinetry, wooden countertops, and soft global illumination. The color palette should be elegant: cream, warm wood, muted rose, and subtle champagne-gold accents. Ensure clear depth of field and premium material textures like polished stone and smooth wood. High resolution, professional game cinematic quality, full-screen background, no text, no people, no UI elements. 16:9 aspect ratio.", "description": "A refined 3D-rendered CGI modern kitchen background with warm lighting and premium materials.", "subtype": "background", "aspect_ratio": [1376, 768], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "kitchen_background_japanese_realistic": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/kitchen_background_japanese_realistic_3c3f26ae-fbcb-4b22-a045-244ad43d88a4.webp", "type": "image", "prompt": "Generate a wide 16:9 full-screen game background of a crisp, elegant 3D CGI minimalist Japanese dessert kitchen. Preserve the composition of image 1, featuring a Zen garden view through a large window. Use highly dimensional architectural details with realistic textures for light oak wood, smooth stone, and shoji paper. Implement gentle global illumination and serene soft daylight. Use a premium Grand Luxe color palette of cream, natural wood, and restrained champagne-gold accents. Professional 3D render, high-end architectural visualization, no people, no text.", "description": "An elegant 3D CGI minimalist Japanese kitchen background with a Zen garden view.", "subtype": "background", "aspect_ratio": [1264, 848], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "kitchen_background_french_realistic": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/kitchen_background_french_realistic_066d1524-0a54-44b8-99f4-db8cbff293eb.webp", "type": "image", "prompt": "Transform the existing kitchen background into a polished 3D CGI scene. A professional French patisserie kitchen with high depth and dimension. Features luxurious ivory and cream cabinets, elegant marble worktops with visible grain, and polished copper fixtures with tasteful champagne gold accents. The color palette includes muted wine-rose tones. Lighting is warm, soft, and cinematic, creating a premium Grand Luxe atmosphere. No text, no people, wide aspect ratio for a full-screen game background, highly detailed 3D render style.", "description": "A premium 3D CGI French patisserie kitchen background with marble and copper accents.", "subtype": "background", "aspect_ratio": [1264, 848], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "egg_single": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/egg_single_b2947211-97f0-4c59-bc80-1fbf110f8e5f.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI render of a single white egg. The egg should have a perfect oval shape with a clean, smooth white shell showing subtle realistic texture. Use professional studio lighting to create dimensional soft highlights and a gentle grounding shadow. The asset should be an isolated object, front-facing, with a polished patisserie aesthetic consistent with a premium 3D kitchen game. Transparent background, high resolution, detailed material properties.", "description": "A polished realistic 3D CGI white egg prop for the baking process.", "subtype": "object", "aspect_ratio": [843, 1120], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "egg_yolk_liquid": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/egg_yolk_liquid_e23b86b7-56cf-44ea-bb77-819205ace9aa.webp", "type": "image", "prompt": "Generate a professional 3D CGI rendered asset of a fresh egg yolk with dripping egg white. The yolk should be a rich, dimensional golden-yellow sphere with soft highlights. The egg white should be glossy and translucent, forming natural gravity-shaped drips and a flowing silhouette. Studio lighting, high-quality material shaders, realistic textures, isolated on a transparent background, no shell, no hands, no text.", "description": "A realistic 3D CGI fresh egg yolk with dripping egg white for cooking animations.", "subtype": "effect", "aspect_ratio": [816, 763], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "egg_shell_broken_top": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/egg_shell_broken_top_223dc99a-22f0-4b17-b875-893ec17df639.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI render of the top half of a broken white eggshell, matching the style of existing kitchen assets. Focus on a crisp, porcelain-like texture with subtle highlights and soft shadows. The shell should have a natural jagged rim and visible thin thickness. Isolated object, front view, transparent background, no hands, no egg contents, professional studio lighting.", "description": "A premium 3D CGI top half of a broken white eggshell used for baking animations.", "subtype": "object", "aspect_ratio": [843, 961], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "egg_shell_broken_bottom": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/egg_shell_broken_bottom_400efbc6-7b42-4f61-b736-d2e80c985e16.webp", "type": "image", "prompt": "Generate a detailed 3D CGI render of the bottom half of a broken white eggshell. Match the style and material of the top shell in image 1. The shell should have a natural jagged edge, visible curved thickness, and a smooth matte texture. Use professional studio lighting with gentle highlights and soft shadows to create depth. Isolated object on a transparent background, no hands, no text, high resolution, premium patisserie game asset style.", "description": "A detailed 3D CGI bottom half of a broken white eggshell with a jagged edge.", "subtype": "object", "aspect_ratio": [738, 740], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "flour_stream_texture": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/flour_stream_texture_797ad0d1-06ab-4ec6-9eb2-82232c32dae4.webp", "type": "image", "prompt": "Generate a 9:16 tall image of a continuous, vertical stream of white flour pouring down through the center of the frame. Render with 3D CGI particle depth, featuring fine-grain sparkle and soft, powdery edges. Use realistic studio lighting to create dimension within the white powder. The silhouette should be clean and isolated on a transparent background. No containers, no hands, no text, just the flowing flour stream, matching the premium 3D CGI aesthetic of image 1.", "description": "A 3D rendered continuous stream of white flour with particle depth for pouring animations", "subtype": "effect", "aspect_ratio": [552, 1264], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "cream_dollop": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/cream_dollop_96959238-d2d7-4985-b63d-79b3d84f8c60.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI photorealistic dollop of white whipped cream. The cream should be in a perfect piped swirl shape with clearly defined dimensional ridges and a soft, airy texture. Include delicate glossy highlights where light hits the peaks and subtle realistic shadows in the folds. Isolated on a transparent background, clean edges, professional food photography lighting, premium dessert topping aesthetic, no extra garnishes.", "description": "A photorealistic 3D CGI swirl of white whipped cream for cake decoration.", "subtype": "object", "aspect_ratio": [1022, 987], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "happy_birthday_topper": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/happy_birthday_topper_62beb3e5-32b3-4a20-b733-469c355804c1.webp", "type": "image", "prompt": "Generate a dimensional 3D CGI cake topper that says 'Happy Birthday'. The design should feature crisp, readable lettering with realistic thickness and a polished, premium plastic or acrylic finish. Match the colorful palette of image 1. Include delicate highlights and soft ambient occlusion shadows to give it depth. The asset should be a standalone decoration with a transparent background, high resolution, professional studio lighting, and maintain the exact spelling and silhouette of the original topper.", "description": "A dimensional 3D CGI 'Happy Birthday' cake topper with a polished premium finish.", "subtype": "object", "aspect_ratio": [1024, 891], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "kuromi_topper": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/kuromi_topper_3cbdc8ff-f67b-4006-800d-980b5862d3ab.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI collectible-style cake topper of Kuromi. Preserve the signature black hood with pink skull, pointed ears, and mischievous expression. The asset should have dimensional sculpted forms, smooth matte plastic material shading, and soft premium highlights. Render as a standalone character decoration with a clear silhouette. Professional studio lighting, 4k resolution, transparent background, front-facing view.", "description": "A polished 3D CGI collectible-style Kuromi character cake topper with premium material shading.", "subtype": "object", "aspect_ratio": [754, 1263], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "little_twin_stars_topper": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/little_twin_stars_topper_262cec4b-2997-4926-b970-8b9da582bda5.webp", "type": "image", "prompt": "Transform the characters in image 1 into a high-quality 3D CGI cake topper. Generate a dimensional, softly sculpted plastic or fondant-like figurine of Kiki and Lala (Little Twin Stars). Maintain their exact identities, pastel hair colors (pink and blue), and original composition. Use premium studio lighting with soft highlights and gentle shadows to create depth. The style should be clean, polished, and charming, matching a premium 3D game aesthetic. Transparent background, front-facing illustration.", "description": "A 3D CGI rendered character cake topper featuring Kiki and Lala in a soft pastel style.", "subtype": "object", "aspect_ratio": [842, 1030], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "mint_leaf": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/mint_leaf_db55f487-900a-4adf-8738-7704cb834c52.webp", "type": "image", "prompt": "Generate a high-quality 3D CGI pair of fresh green mint leaves used for cake decoration. Maintain the exact leaf count and shape of image 1, but transform into a realistic dimensional garnish. Add intricate visible veins, natural gentle curvature to the leaves, and soft fresh highlights. Include delicate self-shadowing to enhance 3D depth. Professional food photography quality, isolated on a transparent background, side-view angle.", "description": "A pair of fresh green mint leaves as a realistic 3D CGI cake garnish.", "subtype": "object", "aspect_ratio": [1264, 796], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "orange_slice": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/orange_slice_cd8abae8-16eb-4538-a88d-867fb45a5339.webp", "type": "image", "prompt": "Generate a detailed 3D CGI rendered candied orange half-slice as a cake garnish. The orange slice should have translucent flesh with visible juicy segments, a thick dimensional orange peel, and a delicate glistening sugar glaze. Use professional studio lighting with soft shadows. Ensure it is a single isolated object with a clean, high-quality look that matches the existing 3D premium kitchen assets. High resolution, 8k render, photorealistic but stylized for a game.", "description": "A detailed 3D CGI candied orange half-slice for cake decoration.", "subtype": "object", "aspect_ratio": [1024, 921], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "bear_cookie": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/bear_cookie_d9968d11-ea81-4232-8191-767c8b697ace.webp", "type": "image", "prompt": "Transform the bear cookie in image 1 into a polished 3D CGI bakery decoration. Generate a full-body, front-facing cute bear-shaped butter cookie with a golden-baked crumb texture. Ensure the cookie has a slightly rounded thickness with fine baked details and softly dimensional facial features. Apply gentle warm highlights and soft professional studio lighting to create depth. Maintain the exact silhouette and identity of the original bear while upgrading to a high-quality 3D rendered style. Isolated on a transparent background.", "description": "A polished 3D CGI bear-shaped butter cookie for cake decoration.", "subtype": "object", "aspect_ratio": [842, 1171], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "chocolate_cream": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/chocolate_cream_85d029df-254e-4095-b159-33dc92fc5745.webp", "type": "image", "prompt": "A high-quality 3D CGI render of a single, fluffy swirl of rich chocolate whipped cream frosting. The object should have distinct piped ridges and an appetizing dimensional texture with a gentle chocolate sheen and professional studio highlights. Rendered as a refined patisserie decoration, isolated on a transparent background with soft natural shadows at the base. Maintain the rich dark cocoa color and elegant spiral shape, similar to a high-end pastry topping. Professional lighting, 8k resolution, clean edges.", "description": "Refined 3D CGI swirl of chocolate whipped cream for cake decoration.", "subtype": "object", "aspect_ratio": [1023, 977], "compression_suggestion": "high", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "halloween_cake_base": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/halloween_cake_base_a2b65c8a-e4ee-4ca1-8f12-9f9f9f193d40.webp", "type": "image", "prompt": "Generate a single isolated, premium photorealistic 3D CGI round frosted cake body. Match the lighting, camera angle, and polished realistic rendering of image 1. The cake features smooth deep aubergine/plum-purple frosting on the sides, a neat glossy orange pumpkin-spice frosting top, and elegant orange icing drips running down the sides. Include a subtle antique-gold piped trim at the base. High-resolution, professional studio lighting, three-quarter elevated view. No decorations, no pumpkins, no ghosts, no plates, no background. Transparent background, centered, full silhouette.", "description": "A single-tier Halloween-themed cake base with plum-purple frosting and orange drips, matching the 3D CGI patisserie style.", "subtype": "object", "aspect_ratio": [1024, 1013], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "halloween_kitchen_background": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/halloween_kitchen_background_59948364-d298-4c60-a0b4-c46e0fabdfd1.webp", "type": "image", "prompt": "Transform the existing kitchen background into a cozy Halloween-themed luxury French patisserie. Maintain the 16:9 wide composition, polished 3D CGI style, and warm evening lighting. Add elegant Halloween decor: small carved pumpkins with warm candlelight, purple and orange bunting, subtle hanging bats, and delicate cobwebs in the upper corners. Place autumn leaves and mini pumpkins on the counters while keeping the central workspace clear. The window should show a soft purple-orange twilight sky. The atmosphere must be welcoming and refined, not scary or cluttered. Match the high-end materials and lighting of the original kitchen.", "description": "A luxury Halloween-themed French patisserie kitchen background with warm evening lighting and festive decor.", "subtype": "background", "aspect_ratio": [1264, 848], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}, "christmas_background_music": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/music/christmas_background_music_720babfe-16fa-4a40-8a41-050e2b66fa92.mp3", "type": "audio", "prompt": "Create a gentle, cozy instrumental Christmas background track for a premium patisserie game. Use a warm festive arrangement featuring a soft melodic piano, sparkling celesta, delicate sleigh bells, and light legato strings. The mood should be elegant, relaxed, and whimsical, evoking a sense of seasonal wonder without being energetic or grand. Avoid any recognizable copyrighted melodies. Tempo around 80-90 BPM, mellow dynamics designed to sit comfortably under gameplay SFX. Instrumental only, seamless loop with a natural flow and no abrupt transitions.", "description": "A cozy, elegant instrumental Christmas theme with piano and celesta for festive cake decorating.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30.0, "loop": true, "opus_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/music/christmas_background_music_720babfe-16fa-4a40-8a41-050e2b66fa92.ogg", "animation": null}, "christmas_patisserie_background": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/christmas_patisserie_background_46422578-e100-4463-8d0e-aede78f45e4f.webp", "type": "image", "prompt": "Generate a 9:16 tall portrait background image for a premium Christmas cake shop. Polished high-end 3D CGI style with elegant realistic materials. The scene features a luxurious French patisserie kitchen with cream cabinetry, marble countertops, and copper/gold accents. Festive decorations include rich evergreen garlands with glowing warm fairy lights, red and gold ornaments framing the upper wall, and a decorated Christmas tree near a snowy window. The central and lower-middle areas are kept clean, uncluttered, and open for game UI. Warm amber lighting creates a magical Christmas Eve atmosphere. No people, no text, no logos. Match the premium 3D aesthetic of existing assets like kitchen_background_french_realistic.", "description": "A luxury Christmas-themed French patisserie kitchen background with festive decor and warm lighting.", "subtype": "background", "aspect_ratio": [848, 1264], "compression_suggestion": "original", "duration_secs": null, "loop": null, "opus_url": null, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Christmas Cake Diary \u2014 Winter Patisserie Edition</title>\n    <style>\n        /* CSS Reset */\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\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: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\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            --color-bg-primary: #F5E6D3;\n            --color-bg-secondary: #FFFAF0;\n            --color-accent-pink: #FFB6C1;\n            --color-accent-red: #FF6B6B;\n            --color-accent-blue: #4A90E2;\n            --color-accent-green: #90EE90;\n            --color-text-dark: #333333;\n            --color-text-light: #666666;\n        }\n\n        #game-world {\n            position: relative;\n            width: 100%;\n            height: 100%;\n            background-color: var(--color-bg-primary);\n            overflow: hidden;\n        }\n\n        #game-canvas {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            z-index: 1;\n        }\n\n        .ui-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            z-index: 100;\n        }\n\n        .ui-overlay > * {\n            pointer-events: auto;\n        }\n\n        .top-bar {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: auto;\n            padding: var(--space-sm);\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            gap: var(--space-sm);\n            pointer-events: none;\n            z-index: 100;\n        }\n\n        .step-info {\n            position: absolute;\n            top: 20px;\n            right: 20px;\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            background: rgba(255, 255, 255, 0.8);\n            padding: 8px 20px;\n            border-radius: 25px;\n            backdrop-filter: blur(4px);\n            pointer-events: auto;\n        }\n\n        .step-title {\n            display: none !important;\n        }\n\n        .step-number {\n            font-size: 24px;\n            font-weight: bold;\n            color: var(--color-text-dark);\n        }\n\n        .progress-container {\n            display: none !important;\n        }\n\n        .bottom-bar {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            padding: 0 var(--space-md);\n            display: flex;\n            flex-direction: column;\n            gap: var(--space-sm);\n            align-items: center;\n            pointer-events: none;\n            z-index: 150;\n        }\n\n        .hint-text {\n            background: rgba(255, 255, 255, 0.98);\n            padding: 12px 28px;\n            border-radius: 0 0 20px 20px;\n            font-size: 22px;\n            font-weight: 600;\n            color: var(--color-text-dark);\n            text-align: center;\n            box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n            border: 3px solid var(--color-accent-pink);\n            border-top: none;\n            max-width: 85%;\n            pointer-events: auto;\n        }\n\n        .action-button {\n            position: absolute;\n            bottom: 20px;\n            left: 50%;\n            transform: translateX(-50%);\n            width: 240px;\n            height: 80px;\n            padding: 0;\n            font-size: 32px;\n            font-weight: 700;\n            color: white;\n            background: linear-gradient(to bottom, var(--color-accent-pink), #FF8C9F);\n            border: 4px solid white;\n            border-radius: 40px;\n            box-shadow: 0 8px 20px rgba(0,0,0,0.2);\n            cursor: pointer;\n            transition: all var(--transition-fast) var(--ease-out);\n            z-index: 160;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            pointer-events: auto;\n        }\n\n        .action-button:hover {\n            transform: translateX(-50%) translateY(-2px);\n            box-shadow: 0 10px 25px rgba(0,0,0,0.3);\n        }\n\n        .action-button:active {\n            transform: translateX(-50%) translateY(0);\n            box-shadow: 0 4px 10px rgba(0,0,0,0.2);\n        }\n\n        .action-button:disabled {\n            opacity: 0.5;\n            cursor: not-allowed;\n        }\n\n        .ingredient-list {\n            display: flex;\n            flex-direction: row;\n            overflow-x: auto;\n            gap: var(--space-sm);\n            padding: var(--space-sm);\n            width: 100%;\n            max-width: 100%;\n            background: rgba(255, 255, 255, 0.6);\n            backdrop-filter: blur(8px);\n            scrollbar-width: none;\n            border-bottom: 2px solid rgba(0,0,0,0.05);\n        }\n\n        .topping-item {\n            flex: 0 0 100px;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            gap: 4px;\n            padding: 8px;\n            background: rgba(255,255,255,0.8);\n            border-radius: 12px;\n            border: 3px solid transparent;\n            cursor: pointer;\n            transition: all 0.2s;\n        }\n\n        .topping-item.selected {\n            border-color: var(--color-accent-pink);\n            background: white;\n            transform: scale(1.05);\n            box-shadow: 0 4px 8px rgba(0,0,0,0.1);\n        }\n\n        .topping-icon {\n            width: 60px;\n            height: 60px;\n            object-fit: contain;\n        }\n\n        .topping-name {\n            font-size: 14px;\n            font-weight: 600;\n            color: var(--color-text-dark);\n        }\n\n        .ingredient-item {\n            flex: 0 0 160px; /* Fixed width for horizontal scrolling */\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            gap: 4px;\n            padding: var(--space-xs);\n            background: rgba(255,255,255,0.9);\n            border-radius: 16px;\n            box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n            border: 3px solid transparent;\n            transition: border-color var(--transition-fast);\n        }\n\n        .ingredient-item.correct {\n            border-color: var(--color-accent-green);\n        }\n\n        .ingredient-icon {\n            width: 100px;\n            height: 100px;\n            object-fit: contain;\n            image-rendering: -webkit-optimize-contrast;\n        }\n\n        .ingredient-name {\n            font-size: 20px;\n            font-weight: 600;\n            color: var(--color-text-dark);\n        }\n\n        .ingredient-weight {\n            font-size: 18px;\n            color: var(--color-text-light);\n            margin: 2px 0;\n        }\n\n        .ingredient-weight.correct {\n            color: var(--color-accent-green);\n        }\n\n        .add-button {\n            min-width: 80px;\n            min-height: 80px;\n            font-size: 32px;\n            font-weight: bold;\n            background: var(--color-accent-pink);\n            color: white;\n            border: none;\n            border-radius: 50%;\n            box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n            cursor: pointer;\n            transition: transform var(--transition-fast) var(--ease-out);\n        }\n\n        .add-button:active {\n            transform: scale(0.95);\n        }\n\n        .modal {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            background: white;\n            border-radius: 32px;\n            padding: var(--space-xl);\n            box-shadow: 0 8px 32px rgba(0,0,0,0.3);\n            max-width: 600px;\n            max-height: 80%;\n            overflow-y: auto;\n            z-index: 200;\n            text-align: center;\n        }\n\n        .modal-backdrop {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0,0,0,0.5);\n            z-index: 199;\n        }\n\n        .modal-title {\n            font-size: 40px;\n            font-weight: 600;\n            color: var(--color-text-dark);\n            margin-bottom: var(--space-md);\n        }\n\n        .modal-content {\n            font-size: 24px;\n            color: var(--color-text-dark);\n            line-height: 1.6;\n        }\n\n        .score-display {\n            font-size: 48px;\n            font-weight: bold;\n            color: var(--color-accent-pink);\n            margin: var(--space-md) 0;\n        }\n\n        .rating-display {\n            font-size: 32px;\n            font-weight: 600;\n            color: var(--color-accent-green);\n            margin: var(--space-sm) 0;\n        }\n\n        .modal .action-button {\n            position: relative !important;\n            bottom: auto !important;\n            left: auto !important;\n            transform: none !important;\n            margin: 32px auto 0 !important;\n        }\n\n        .hidden {\n            display: none !important;\n        }\n\n        @media (prefers-reduced-motion: reduce) {\n            * {\n                animation-duration: 0.01ms !important;\n                animation-iteration-count: 1 !important;\n                transition-duration: 0.01ms !important;\n            }\n        }\n        /* Grand Luxe Limited Edition visual treatment */\n        :root {\n            --color-bg-primary: #f3e9dc;\n            --color-bg-secondary: #fffaf2;\n            --color-accent-pink: #b86d79;\n            --color-accent-red: #8b4351;\n            --color-accent-blue: #627c89;\n            --color-accent-green: #849b78;\n            --color-text-dark: #49333a;\n            --color-text-light: #806c68;\n            --luxe-gold: #c9a96a;\n            --luxe-gold-light: #f5e4b7;\n            --luxe-wine: #713b49;\n        }\n        .top-bar {\n            top: 14px !important;\n            left: 0;\n            padding: 0 36px !important;\n            gap: 9px !important;\n        }\n        .brand-name {\n            color: #fff9eb;\n            font: 600 25px/1.05 Georgia, 'Times New Roman', serif;\n            letter-spacing: 4px;\n            text-shadow: 0 2px 8px rgba(46, 26, 26, .75);\n            text-align: center;\n            pointer-events: none;\n        }\n        .brand-name span {\n            display: block;\n            margin-top: 5px;\n            color: #f4dfad;\n            font: 600 12px/1.2 'Segoe UI', sans-serif;\n            letter-spacing: 4px;\n        }\n        .step-info {\n            position: relative !important;\n            top: auto !important;\n            right: auto !important;\n            width: min(620px, 94%);\n            min-height: 55px;\n            justify-content: space-between !important;\n            padding: 10px 23px !important;\n            color: #49333a;\n            background: linear-gradient(135deg, rgba(255,252,245,.96), rgba(246,231,204,.94)) !important;\n            border: 1px solid rgba(201,169,106,.95);\n            border-radius: 17px !important;\n            box-shadow: 0 5px 18px rgba(54,35,28,.2), inset 0 0 0 3px rgba(255,255,255,.55);\n            backdrop-filter: blur(8px);\n        }\n        .step-title {\n            display: inline !important;\n            color: #543843;\n            font: 600 19px/1.2 Georgia, 'Times New Roman', serif;\n            letter-spacing: .3px;\n        }\n        .step-number {\n            color: #855b37 !important;\n            font: 700 18px/1 'Segoe UI', sans-serif !important;\n            letter-spacing: 1px;\n        }\n        .progress-container {\n            display: block !important;\n            width: min(580px, 86%);\n            height: 13px;\n            padding: 2px;\n            overflow: hidden;\n            background: rgba(255,250,240,.78);\n            border: 1px solid rgba(201,169,106,.9);\n            border-radius: 12px;\n            box-shadow: 0 3px 10px rgba(44,28,25,.18);\n        }\n        .progress-bar {\n            height: 100%;\n            min-width: 10px;\n            background: linear-gradient(90deg, #a94d61, #d58b87 55%, #e8c985);\n            border-radius: 10px;\n            box-shadow: 0 0 9px rgba(223,177,119,.65);\n            transition: width .35s ease;\n        }\n        .bottom-bar {\n            top: 180px !important;\n            bottom: auto !important;\n            left: 0;\n            width: 100%;\n            padding: 0 36px !important;\n        }\n        .hint-text {\n            max-width: 88% !important;\n            padding: 11px 25px !important;\n            color: #563c42 !important;\n            background: linear-gradient(135deg, rgba(255,252,244,.96), rgba(246,233,214,.94)) !important;\n            border: 1px solid var(--luxe-gold) !important;\n            border-radius: 15px !important;\n            box-shadow: 0 5px 16px rgba(52,33,28,.18), inset 0 0 0 2px rgba(255,255,255,.6) !important;\n            font: 600 17px/1.35 'Segoe UI', sans-serif !important;\n        }\n        .action-button {\n            bottom: 22px !important;\n            width: 290px !important;\n            height: 82px !important;\n            color: #fffaf0 !important;\n            background: linear-gradient(145deg, #8f4c58, #673341 70%, #542d39) !important;\n            border: 3px solid #e8cf94 !important;\n            border-radius: 25px !important;\n            box-shadow: 0 8px 21px rgba(48,27,31,.34), inset 0 1px 0 rgba(255,255,255,.35), inset 0 0 0 2px rgba(255,255,255,.08) !important;\n            font: 700 24px/1 Georgia, 'Times New Roman', serif !important;\n            letter-spacing: 1.2px;\n        }\n        .action-button:hover {\n            transform: translateX(-50%) translateY(-2px) !important;\n            box-shadow: 0 11px 26px rgba(48,27,31,.4), inset 0 1px 0 rgba(255,255,255,.4) !important;\n        }\n        .action-button:active {\n            transform: translateX(-50%) translateY(1px) !important;\n        }\n        .ingredient-list {\n            background: rgba(255,250,240,.88) !important;\n            border-top: 1px solid rgba(201,169,106,.72);\n            border-bottom: 1px solid rgba(201,169,106,.72) !important;\n            box-shadow: 0 6px 18px rgba(53,34,27,.16);\n        }\n        .ingredient-list[style*=\"top: 140px\"] {\n            top: 238px !important;\n        }\n        .topping-item, .ingredient-item {\n            background: linear-gradient(145deg, rgba(255,255,255,.98), rgba(247,237,220,.96)) !important;\n            border: 1px solid rgba(201,169,106,.58) !important;\n            border-radius: 16px !important;\n            box-shadow: 0 4px 10px rgba(75,51,43,.12) !important;\n        }\n        .topping-item.selected {\n            border: 2px solid #b9874d !important;\n            box-shadow: 0 0 0 3px rgba(239,216,164,.55), 0 6px 14px rgba(75,51,43,.18) !important;\n        }\n        .topping-name, .ingredient-name {\n            color: #573b42 !important;\n            font-family: Georgia, 'Times New Roman', serif !important;\n        }\n        .modal {\n            color: #503740;\n            background: linear-gradient(145deg, #fffdf7, #f5e8d3) !important;\n            border: 2px solid var(--luxe-gold);\n            box-shadow: 0 14px 40px rgba(32,19,20,.42), inset 0 0 0 5px rgba(255,255,255,.55) !important;\n        }\n        .modal-title {\n            color: #713b49 !important;\n            font-family: Georgia, 'Times New Roman', serif;\n        }\n        .rating-display, .score-display {\n            color: #a36e41 !important;\n        }\n        .modal-backdrop {\n            background: rgba(36,22,27,.62) !important;\n            backdrop-filter: blur(4px);\n        }\n        @media (max-width: 600px) {\n            .brand-name { font-size: 21px; letter-spacing: 3px; }\n            .brand-name span { font-size: 10px; letter-spacing: 2.5px; }\n            .top-bar { padding: 0 20px !important; }\n            .step-title { font-size: 16px; }\n            .hint-text { font-size: 15px !important; }\n            .action-button { width: 250px !important; height: 76px !important; }\n        }\n        /* Dimensional Grand Luxe interface: raised enamel surfaces and layered depth */\n        .step-info, .hint-text, .ingredient-list, .topping-item, .ingredient-item, .modal {\n            box-shadow: 0 10px 24px rgba(45, 29, 27, .22),\n                        inset 0 2px 0 rgba(255, 255, 255, .78),\n                        inset 0 -2px 0 rgba(132, 91, 56, .08) !important;\n        }\n        .action-button {\n            position: absolute;\n            background-image: linear-gradient(155deg, #a95e69 0%, #7a3f4c 48%, #542d39 100%) !important;\n            box-shadow: 0 12px 0 #462631, 0 16px 26px rgba(48, 27, 31, .34),\n                        inset 0 3px 0 rgba(255, 255, 255, .32),\n                        inset 0 -4px 0 rgba(40, 20, 24, .2) !important;\n        }\n        .action-button:active {\n            box-shadow: 0 5px 0 #462631, 0 8px 16px rgba(48, 27, 31, .3),\n                        inset 0 2px 0 rgba(255, 255, 255, .25) !important;\n        }\n        .ingredient-item, .topping-item {\n            transform: translateZ(0);\n            transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;\n        }\n        .ingredient-item:hover, .topping-item:hover {\n            transform: translateY(-4px);\n            box-shadow: 0 10px 18px rgba(75, 51, 43, .2),\n                        inset 0 2px 0 rgba(255, 255, 255, .85) !important;\n        }\n        .ingredient-icon, .topping-icon {\n            filter: drop-shadow(0 5px 5px rgba(65, 42, 32, .2));\n        }\n        .modal {\n            border-radius: 30px !important;\n        }\n        .photo-mode-panel {\n            position: absolute;\n            inset: 0;\n            z-index: 300;\n            pointer-events: none;\n            color: #fff9eb;\n            text-align: center;\n        }\n        .photo-mode-panel.hidden { display: none !important; }\n        .photo-mode-heading {\n            position: absolute;\n            top: 52px;\n            left: 8%;\n            width: 84%;\n            padding: 18px 20px;\n            color: #fff9eb;\n            background: linear-gradient(135deg, rgba(64,34,39,.78), rgba(113,59,73,.72));\n            border: 2px solid #d8ba7d;\n            border-radius: 20px;\n            box-shadow: 0 8px 24px rgba(36,22,27,.28), inset 0 1px 0 rgba(255,255,255,.3);\n            font: 600 25px/1.3 Georgia, 'Times New Roman', serif;\n            letter-spacing: 1px;\n            text-shadow: 0 2px 6px rgba(0,0,0,.4);\n        }\n        .photo-mode-heading span {\n            display: block;\n            margin-top: 6px;\n            color: #f4dfad;\n            font: 500 16px/1.5 'Segoe UI', 'Noto Sans TC', sans-serif;\n            letter-spacing: .3px;\n        }\n        .photo-mode-footer {\n            position: absolute;\n            left: 7%;\n            bottom: 32px;\n            width: 86%;\n            padding: 16px 20px;\n            pointer-events: auto;\n            color: #543843;\n            background: linear-gradient(135deg, rgba(255,252,244,.97), rgba(246,233,214,.96));\n            border: 2px solid var(--luxe-gold);\n            border-radius: 18px;\n            box-shadow: 0 8px 22px rgba(36,22,27,.3), inset 0 1px 0 rgba(255,255,255,.8);\n            font: 600 16px/1.45 'Segoe UI', 'Noto Sans TC', sans-serif;\n        }\n        .photo-mode-close {\n            display: block;\n            min-width: 220px;\n            min-height: 72px;\n            margin: 12px auto 0;\n            padding: 12px 24px;\n            color: #fffaf0;\n            background: linear-gradient(145deg, #8f4c58, #673341 70%, #542d39);\n            border: 2px solid #d8ba7d;\n            border-radius: 18px;\n            box-shadow: 0 5px 0 #462631, 0 8px 16px rgba(48,27,31,.25);\n            font: 700 18px Georgia, 'Times New Roman', serif;\n            cursor: pointer;\n        }\n        .photo-mode-close:active { transform: translateY(2px); box-shadow: 0 3px 0 #462631; }\n        .photo-studio-button {\n            margin-top: 12px !important;\n            color: #573b42 !important;\n            background: linear-gradient(145deg, #fffdf7, #ead9b8) !important;\n            border-color: #c9a96a !important;\n            box-shadow: 0 5px 0 #a88a55, 0 8px 16px rgba(48,27,31,.2), inset 0 2px 0 rgba(255,255,255,.8) !important;\n            font-size: 20px !important;\n        }\n        #game-world.photo-mode .ui-overlay > :not(#photo-mode-panel) {\n            display: none !important;\n        }\n        @media (max-width: 600px) {\n            .photo-mode-heading { top: 38px; font-size: 20px; }\n            .photo-mode-heading span { font-size: 14px; }\n            .photo-mode-footer { bottom: 22px; font-size: 14px; }\n        }\n        /* Readability pass: larger, sharper UI typography for the fixed 720x1280 game frame. */\n        body, html, button, .ui-overlay {\n            font-family: 'Noto Sans TC', 'Segoe UI', Tahoma, sans-serif;\n            -webkit-font-smoothing: antialiased;\n            text-rendering: optimizeLegibility;\n        }\n        .brand-name { font-size: 32px; line-height: 1.2; }\n        .brand-name span { font-size: 18px; line-height: 1.35; }\n        .step-title { font-size: 26px !important; line-height: 1.3; }\n        .step-number { font-size: 24px !important; }\n        .hint-text { font-size: 24px !important; line-height: 1.45 !important; padding: 16px 28px !important; }\n        .action-button { font-size: 28px !important; }\n        .topping-name { font-size: 20px !important; line-height: 1.3; }\n        .ingredient-name { font-size: 24px !important; line-height: 1.3; }\n        .ingredient-weight { font-size: 22px; }\n        .modal-title { font-size: 46px !important; }\n        .modal-content { font-size: 28px; line-height: 1.55; }\n        .score-display { font-size: 54px; }\n        .rating-display { font-size: 38px; }\n        .photo-mode-heading { font-size: 30px; line-height: 1.4; }\n        .photo-mode-heading span { font-size: 20px; }\n        .photo-mode-footer { font-size: 20px; line-height: 1.6; }\n        .photo-mode-close { font-size: 23px; }\n        .topping-item { flex-basis: 120px; gap: 8px; padding: 12px; }\n        .topping-icon { width: 72px; height: 72px; }\n        @media (max-width: 600px) {\n            .brand-name { font-size: 27px; }\n            .brand-name span { font-size: 16px; }\n            .step-title { font-size: 22px !important; }\n            .step-number { font-size: 22px !important; }\n            .hint-text { font-size: 21px !important; }\n            .photo-mode-heading { font-size: 24px; }\n            .photo-mode-heading span { font-size: 17px; }\n            .photo-mode-footer { font-size: 18px; }\n        }\n        /* Cozy Halloween patisserie palette: plum, pumpkin and antique gold. */\n        :root {\n            --color-bg-primary: #211b2b;\n            --color-bg-secondary: #fff4e3;\n            --color-accent-pink: #e98b3a;\n            --color-accent-red: #713b79;\n            --color-accent-blue: #6b587e;\n            --color-accent-green: #91a36e;\n            --color-text-dark: #38283e;\n            --color-text-light: #746477;\n            --luxe-gold: #d5ad62;\n            --luxe-gold-light: #ffe3a2;\n            --luxe-wine: #392442;\n        }\n        .brand-name { color: #fff7e8; text-shadow: 0 2px 10px rgba(20,12,28,.88); }\n        .brand-name span { color: #ffd184; }\n        .step-info, .hint-text { background: linear-gradient(135deg, rgba(255,249,235,.97), rgba(244,226,198,.96)) !important; border-color: #d5ad62 !important; }\n        .step-title, .hint-text { color: #38283e !important; }\n        .action-button { color: #fff9eb !important; background: linear-gradient(145deg, #e89a42, #bb602e 58%, #713b79) !important; border-color: #f1d18a !important; box-shadow: 0 12px 0 #46263f, 0 16px 26px rgba(24,15,31,.38), inset 0 3px 0 rgba(255,255,255,.35), inset 0 -4px 0 rgba(40,20,24,.2) !important; }\n        .action-button:active { box-shadow: 0 5px 0 #46263f, 0 8px 16px rgba(24,15,31,.32) !important; }\n        .topping-item.selected { border-color: #e98b3a !important; box-shadow: 0 0 0 3px rgba(233,139,58,.38), 0 6px 14px rgba(45,28,48,.2) !important; }\n        .topping-icon-emoji { display: block; width: 72px; height: 72px; font-size: 54px; line-height: 72px; text-align: center; }\n        .photo-mode-heading { background: linear-gradient(135deg, rgba(37,25,48,.9), rgba(89,48,83,.86)); border-color: #d5ad62; }\n        .modal-title { color: #593663 !important; }\n        /* Christmas patisserie palette: evergreen, cranberry, ivory and warm gold. */\n        :root {\n            --color-bg-primary: #20352f;\n            --color-bg-secondary: #fff9ef;\n            --color-accent-pink: #b9434b;\n            --color-accent-red: #7f2635;\n            --color-accent-blue: #456b59;\n            --color-accent-green: #47764a;\n            --color-text-dark: #293a32;\n            --color-text-light: #6d746b;\n            --luxe-gold: #d2b16d;\n            --luxe-gold-light: #f7e7b8;\n            --luxe-wine: #71323d;\n        }\n        .brand-name { color: #fff9e9 !important; text-shadow: 0 2px 10px rgba(10,25,19,.88); }\n        .brand-name span { color: #f4d88f !important; }\n        .step-info, .hint-text { background: linear-gradient(135deg, rgba(255,251,241,.98), rgba(239,231,208,.97)) !important; border-color: #d2b16d !important; }\n        .step-title, .hint-text { color: #293a32 !important; }\n        .action-button { color: #fff9ef !important; background: linear-gradient(145deg, #47764a, #286043 58%, #71323d) !important; border-color: #ecd28d !important; box-shadow: 0 12px 0 #382c31, 0 16px 26px rgba(15,32,24,.38), inset 0 3px 0 rgba(255,255,255,.35), inset 0 -4px 0 rgba(40,20,24,.2) !important; }\n        .action-button:active { box-shadow: 0 5px 0 #382c31, 0 8px 16px rgba(15,32,24,.32) !important; }\n        .topping-item.selected { border-color: #b9434b !important; box-shadow: 0 0 0 3px rgba(185,67,75,.3), 0 6px 14px rgba(45,28,48,.2) !important; }\n        .photo-mode-heading { background: linear-gradient(135deg, rgba(31,59,46,.92), rgba(113,50,61,.88)) !important; border-color: #d2b16d !important; }\n        .modal-title { color: #71323d !important; }\n        /* Keep the baking canvas clear: hide step banners, hint cards, progress prompts and tutorial arrows. */\n        .step-info, .progress-container, .bottom-bar { display: none !important; }\n        /* Clearly discoverable, touch-friendly pages for the full decoration collection. */\n        .topping-pagination {\n            position: absolute;\n            top: 410px;\n            left: 50%;\n            transform: translateX(-50%);\n            z-index: 106;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 18px;\n            width: min(620px, 94%);\n            pointer-events: auto;\n        }\n        .topping-page-button {\n            min-width: 112px;\n            min-height: 96px;\n            padding: 10px 14px;\n            color: #fff9ef;\n            background: linear-gradient(145deg, #47764a, #286043 58%, #71323d);\n            border: 2px solid #ecd28d;\n            border-radius: 18px;\n            box-shadow: 0 5px 0 #382c31, 0 8px 14px rgba(15,32,24,.28);\n            font: 700 20px/1.2 'Segoe UI', sans-serif;\n            cursor: pointer;\n            touch-action: manipulation;\n        }\n        .topping-page-button:disabled { opacity: .45; }\n        .topping-page-count {\n            min-width: 150px;\n            color: #293a32;\n            background: rgba(255,251,241,.96);\n            border: 1px solid #d2b16d;\n            border-radius: 14px;\n            padding: 14px 10px;\n            text-align: center;\n            font: 700 18px/1.25 'Segoe UI', sans-serif;\n        }\n        @media (max-width: 600px) {\n            .topping-pagination { top: 410px; gap: 10px; }\n            .topping-page-button { min-width: 100px; font-size: 17px; }\n            .topping-page-count { min-width: 120px; font-size: 16px; }\n        }\n    </style>\n    <link href=\"https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n</head>\n<body>\n    <div id=\"game-world\">\n        <canvas id=\"game-canvas\"></canvas>\n        <div class=\"ui-overlay\">\n            <div class=\"top-bar\">\n                <div class=\"brand-name\">CHRISTMAS CAKE DIARY<span>WINTER MAGIC \u00b7 PATISSERIE EDITION</span></div>\n                <div class=\"step-info\">\n                    <span class=\"step-title\" id=\"step-title\">Prepare Ingredients</span>\n                    <span class=\"step-number\" id=\"step-number\">1/10</span>\n                </div>\n                <div class=\"progress-container\">\n                    <div class=\"progress-bar\" id=\"progress-bar\" style=\"width: 10%\"></div>\n                </div>\n            </div>\n            <div class=\"bottom-bar\">\n                <div class=\"hint-text\" id=\"hint-text\">Select and weigh ingredients</div>\n            </div>\n            <button class=\"action-button\" id=\"action-button\">Next</button>\n            <div id=\"step-content\"></div>\n            <div id=\"photo-mode-panel\" class=\"photo-mode-panel hidden\" aria-label=\"Cake photo mode\">\n                <div class=\"photo-mode-heading\">CHRISTMAS CAKE DIARY<span>WINTER MAGIC \u00b7 PATISSERIE EDITION</span></div>\n                <div class=\"photo-mode-footer\">\n                    \u64fa\u597d\u756b\u9762\u5f8c\uff0c\u4f7f\u7528\u88dd\u7f6e\u7684\u622a\u5716\u529f\u80fd\u5132\u5b58\u86cb\u7cd5\u76f8\u7247\uff0c\u518d\u5f9e\u76f8\u7c3f\u4e0b\u8f09\u6216\u5206\u4eab\u7d66\u670b\u53cb\u3002<br>\n                    Frame your cake, then use your device's screenshot feature to save it and share it from your photo gallery.\n                    <button id=\"photo-mode-close\" class=\"photo-mode-close\" type=\"button\">\u8fd4\u56de\u5b8c\u6210\u756b\u9762 \u00b7 Back</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        // Game Overview:\n        // Christmas Cake Diary is a step-by-step baking simulation where players\n        // experience the complete process of making and decorating a festive Christmas cake. It features 10 sequential\n        // steps: preparing ingredients, mixing batter, pouring into pan, baking, cooling, slicing,\n        // whipping cream, assembling layers, decorating, and celebrating. It offers a relaxing,\n        // no-pressure experience with warm visuals and gentle gameplay, allowing players to learn\n        // real baking processes while enjoying a healing atmosphere.\n\n        async function run(mode) {\n            const gameWorld = document.getElementById('game-world');\n            let canvas = document.getElementById('game-canvas');\n            let ctx = canvas.getContext('2d');\n\n            function resizeCanvas() {\n                canvas.width = gameWorld.clientWidth;\n                canvas.height = gameWorld.clientHeight;\n            }\n            resizeCanvas();\n            window.addEventListener('resize', resizeCanvas);\n\n            // Initialize gameConfig\n            if (!window.gameConfig) {\n                window.gameConfig = {\n                    cakeFlavor: 'vanilla',\n                    cakeSize: 6,\n                    decorationTheme: 'classic',\n                    showHints: true,\n                    soundVolume: 50,\n                    musicVolume: 30,\n                    language: 'en-US',\n                    kitchenStyle: 'christmas',\n                    timeOfDay: 'morning',\n                    ingredients: {\n                        flour: { target: 200, tolerance: 5, current: 0 },\n                        sugar: { target: 150, tolerance: 5, current: 0 },\n                        eggs: { target: 3, tolerance: 0, current: 0 },\n                        milk: { target: 100, tolerance: 5, current: 0 },\n                        butter: { target: 100, tolerance: 5, current: 0 },\n                        bakingPowder: { target: 10, tolerance: 2, current: 0 }\n                    }\n                };\n            }\n\n            // Asset cache\n            const assets = {};\n            const audioContext = new (window.AudioContext || window.webkitAudioContext)();\n            const audioBuffers = {};\n\n            async function preloadAssets() {\n                const assetIds = [\n                    'kitchen_background', 'kitchen_background_japanese', 'kitchen_background_realistic', 'kitchen_background_japanese_realistic', 'kitchen_background_french_realistic', 'halloween_kitchen_background', 'halloween_cake_base', 'christmas_patisserie_background', 'digital_scale', 'mixing_bowl_large', 'electric_mixer',\n                    'cake_pan_empty', 'oven_closed', 'oven_open', 'cooling_rack', 'long_knife',\n                    'offset_spatula', 'flour_bag', 'sugar_bag', 'eggs_carton', 'milk_bottle',\n                    'butter_block', 'baking_powder_container', 'raw_batter', 'cake_baking_stage1',\n                    'cake_baking_stage2', 'cake_baking_stage3', 'cake_layer_bottom', 'cake_layer_top',\n                    'whipped_cream_bowl', 'strawberry_whole', 'strawberry_half', 'blueberry',\n                    'chocolate_shaving', 'sugar_pearl_pink', 'sugar_pearl_white', 'birthday_candle_unlit',\n                    'birthday_candle_lit', 'completed_cake_base', 'checkmark_icon', 'star_particle', 'chocolate_cream',\n                    'heart_particle', 'egg_single', 'egg_yolk_liquid', 'egg_shell_broken_top', 'egg_shell_broken_bottom', 'flour_stream_texture', 'cream_dollop', 'happy_birthday_topper', 'kuromi_topper', 'little_twin_stars_topper', 'mint_leaf', 'orange_slice', 'bear_cookie'\n                ];\n\n                const audioIds = [\n                    'click_sound', 'select_sound', 'step_complete_sound', 'pour_liquid_sound',\n                    'mixer_sound', 'oven_door_sound', 'knife_cutting_sound', 'spreading_sound',\n                    'place_decoration_sound', 'background_music', 'christmas_background_music', 'birthday_song', 'ambient_kitchen_sound'\n                ];\n\n                const imagePromises = assetIds.map(id => {\n                    return new Promise((resolve) => {\n                        const assetInfo = lib.getAsset(id);\n                        if (assetInfo && assetInfo.type === 'image') {\n                            const img = new Image();\n                            img.onload = () => {\n                                assets[id] = { img, info: assetInfo };\n                                resolve();\n                            };\n                            img.onerror = () => resolve();\n                            img.src = assetInfo.url;\n                        } else {\n                            resolve();\n                        }\n                    });\n                });\n\n                const audioPromises = audioIds.map(id => {\n                    return new Promise(async (resolve) => {\n                        const assetInfo = lib.getAsset(id);\n                        if (assetInfo && assetInfo.type === 'audio') {\n                            try {\n                                const response = await fetch(assetInfo.url);\n                                const arrayBuffer = await response.arrayBuffer();\n                                const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n                                audioBuffers[id] = { buffer: audioBuffer, info: assetInfo };\n                            } catch (error) {\n                                lib.log(`Failed to load audio: ${id}`);\n                            }\n                        }\n                        resolve();\n                    });\n                });\n                await Promise.all([...imagePromises, ...audioPromises]);\n            }\n\n            function playSound(id, volume = 1, loop = false) {\n                if (audioBuffers[id]) {\n                    const source = audioContext.createBufferSource();\n                    const gainNode = audioContext.createGain();\n                    \n                    source.buffer = audioBuffers[id].buffer;\n                    source.connect(gainNode);\n                    gainNode.connect(audioContext.destination);\n                    \n                    const adjustedVolume = (window.gameConfig.soundVolume / 100) * volume;\n                    gainNode.gain.value = adjustedVolume;\n                    \n                    source.loop = loop;\n                    source.start(0);\n                    \n                    return { source, gainNode };\n                }\n                return null;\n            }\n\n            let currentMusicSource = null;\n            function playMusic(id, volume = 1) {\n                if (currentMusicSource) {\n                    try { currentMusicSource.stop(); } catch(e) {}\n                }\n                if (audioBuffers[id]) {\n                    const source = audioContext.createBufferSource();\n                    const gainNode = audioContext.createGain();\n                    \n                    source.buffer = audioBuffers[id].buffer;\n                    source.connect(gainNode);\n                    gainNode.connect(audioContext.destination);\n                    \n                    const adjustedVolume = (window.gameConfig.musicVolume / 100) * volume;\n                    gainNode.gain.value = adjustedVolume;\n                    \n                    source.loop = true;\n                    source.start(0);\n                    \n                    currentMusicSource = source;\n                    return { source, gainNode };\n                }\n                return null;\n            }\n\n            await preloadAssets();\n\n            // Runtime state (not in gameConfig)\n            let currentStep = 0;\n            let stepScores = new Array(10).fill(0);\n            let currentStepData = {};\n            let cakeData = { decorations: [] };\n            let backgroundMusic = null;\n            let photoMode = false;\n\n            const stepTitle = document.getElementById('step-title');\n            const stepNumber = document.getElementById('step-number');\n            const progressBar = document.getElementById('progress-bar');\n            const hintText = document.getElementById('hint-text');\n            const actionButton = document.getElementById('action-button');\n            const stepContent = document.getElementById('step-content');\n            const photoModePanel = document.getElementById('photo-mode-panel');\n            const photoModeClose = document.getElementById('photo-mode-close');\n\n            const steps = [\n                { name: 'Prepare Ingredients', hint: 'Select and weigh ingredients into the bowl' },\n                { name: 'Mix Batter', hint: 'Use the mixer to stir in the stainless steel bowl' },\n                { name: 'Pour into Pan', hint: 'Tilt the bowl to pour batter into the cake tin' },\n                { name: 'Baking', hint: 'Set temp to 180\u00b0C and click \"Start Baking\"' },\n                { name: 'Cooling', hint: 'Wait for the cake to cool down...' },\n                { name: 'Ready to Decorate', hint: 'Click \"Take Out\" to place it on the turntable' },\n                { name: 'Whip Cream', hint: 'Move in circles to whip the cream' },\n                { name: 'Coat Cream', hint: 'Slide horizontally to spread cream evenly' },\n                { name: 'Decorate', hint: 'Add holly, berries, citrus and winter sweets to your Christmas cake!' },\n                { name: 'Christmas Reveal', hint: 'Admire your Christmas cake and celebrate your festive bake!' }\n            ];\n\n            lib.showGameParameters({\n                params: {\n                    'Cake Flavor': {\n                        key: 'cakeFlavor',\n                        type: 'dropdown',\n                        options: [\n                            { label: 'Vanilla', value: 'vanilla' },\n                            { label: 'Chocolate', value: 'chocolate' },\n                            { label: 'Pumpkin Spice', value: 'pumpkin' },\n                            { label: 'Strawberry', value: 'strawberry' },\n                            { label: 'Matcha', value: 'matcha' }\n                        ]\n                    },\n                    'Cake Size': {\n                        key: 'cakeSize',\n                        type: 'dropdown',\n                        options: [\n                            { label: '6 inch', value: 6 },\n                            { label: '8 inch', value: 8 },\n                            { label: '10 inch', value: 10 }\n                        ]\n                    },\n                    'Kitchen Style': {\n                        key: 'kitchenStyle',\n                        type: 'dropdown',\n                        options: [\n                            { label: 'Christmas Winter Patisserie', value: 'christmas' },\n                            { label: 'Realistic French', value: 'french_real' },\n                            { label: 'Realistic Modern', value: 'modern_real' },\n                            { label: 'Realistic Japanese', value: 'japanese_real' },\n                            { label: 'Illustrated Modern', value: 'modern' },\n                            { label: 'Illustrated Japanese', value: 'japanese' }\n                        ]\n                    },\n                    'Show Hints': {\n                        key: 'showHints',\n                        type: 'checkbox'\n                    },\n                    'Sound Volume': {\n                        key: 'soundVolume',\n                        type: 'slider',\n                        min: 0,\n                        max: 100,\n                        step: 10\n                    },\n                    'Music Volume': {\n                        key: 'musicVolume',\n                        type: 'slider',\n                        min: 0,\n                        max: 100,\n                        step: 10\n                    }\n                }\n            });\n\n            function updateUI() {\n                stepTitle.textContent = steps[currentStep].name;\n                stepNumber.textContent = `${currentStep + 1}/10`;\n                progressBar.style.width = `${((currentStep + 1) / 10) * 100}%`;\n                if (window.gameConfig.showHints) {\n                    hintText.textContent = steps[currentStep].hint;\n                    hintText.classList.remove('hidden');\n                } else {\n                    hintText.classList.add('hidden');\n                }\n            }\n\n            function drawImage(id, x, y, w, h) {\n                if (assets[id]) {\n                    const { img, info } = assets[id];\n                    let width = w;\n                    let height = h;\n                    if (info.aspect_ratio && width && !height) {\n                        const [aspectW, aspectH] = info.aspect_ratio;\n                        height = width * (aspectH / aspectW);\n                    } else if (info.aspect_ratio && height && !width) {\n                        const [aspectW, aspectH] = info.aspect_ratio;\n                        width = height * (aspectW / aspectH);\n                    }\n                    ctx.drawImage(img, Math.floor(x), Math.floor(y), Math.floor(width), Math.floor(height));\n                }\n            }\n\n            function drawBackground() {\n                let bgId = assets['christmas_patisserie_background'] ? 'christmas_patisserie_background' : (assets['kitchen_background_french_realistic'] ? 'kitchen_background_french_realistic' : 'kitchen_background');\n                if (window.gameConfig.kitchenStyle === 'japanese' && assets['kitchen_background_japanese']) {\n                    bgId = 'kitchen_background_japanese';\n                } else if (window.gameConfig.kitchenStyle === 'japanese_real' && assets['kitchen_background_japanese_realistic']) {\n                    bgId = 'kitchen_background_japanese_realistic';\n                } else if (window.gameConfig.kitchenStyle === 'modern_real' && assets['kitchen_background_realistic']) {\n                    bgId = 'kitchen_background_realistic';\n                } else if (window.gameConfig.kitchenStyle === 'french_real' && assets['kitchen_background_french_realistic']) {\n                    bgId = 'kitchen_background_french_realistic';\n                } else if (window.gameConfig.kitchenStyle === 'christmas' && assets['christmas_patisserie_background']) {\n                    bgId = 'christmas_patisserie_background';\n                } else if (window.gameConfig.kitchenStyle === 'halloween' && assets['halloween_kitchen_background']) {\n                    bgId = 'halloween_kitchen_background';\n                }\n                \n                if (assets[bgId]) {\n                    drawImage(bgId, 0, 0, canvas.width, canvas.height);\n                } else {\n                    ctx.fillStyle = '#F5E6D3';\n                    ctx.fillRect(0, 0, canvas.width, canvas.height);\n                }\n                drawChristmasAtmosphere();\n            }\n\n            function drawLuxuryFrame() {\n                const inset = 13;\n                const w = canvas.width;\n                const h = canvas.height;\n                ctx.save();\n                ctx.lineWidth = 2;\n                ctx.strokeStyle = 'rgba(210, 177, 109, 0.9)';\n                ctx.strokeRect(inset, inset, w - inset * 2, h - inset * 2);\n                ctx.lineWidth = 1;\n                ctx.strokeStyle = 'rgba(255, 248, 224, 0.82)';\n                ctx.strokeRect(inset + 6, inset + 6, w - (inset + 6) * 2, h - (inset + 6) * 2);\n                ctx.lineWidth = 3;\n                ctx.strokeStyle = 'rgba(225, 197, 137, 0.98)';\n                const corners = [[inset + 2, inset + 2, 1, 1], [w - inset - 2, inset + 2, -1, 1], [inset + 2, h - inset - 2, 1, -1], [w - inset - 2, h - inset - 2, -1, -1]];\n                corners.forEach(([x, y, sx, sy]) => {\n                    ctx.beginPath();\n                    ctx.moveTo(x, y + sy * 34);\n                    ctx.lineTo(x, y);\n                    ctx.lineTo(x + sx * 34, y);\n                    ctx.stroke();\n                    ctx.fillStyle = 'rgba(245, 224, 178, 0.98)';\n                    ctx.beginPath();\n                    ctx.moveTo(x + sx * 8, y);\n                    ctx.lineTo(x, y + sy * 8);\n                    ctx.lineTo(x - sx * 8, y);\n                    ctx.lineTo(x, y - sy * 8);\n                    ctx.closePath();\n                    ctx.fill();\n                });\n                ctx.restore();\n            }\n\n            function drawChristmasCake(x, y, w, h) {\n                drawImage('completed_cake_base', x, y, w, h);\n                // A delicate holly garland and tiny cranberry berries make the plain\n                // ivory cake read as a Christmas centerpiece before toppings are placed.\n                ctx.save();\n                ctx.lineCap = 'round';\n                ctx.strokeStyle = '#47764a';\n                ctx.lineWidth = Math.max(4, w * 0.014);\n                ctx.beginPath();\n                ctx.moveTo(x + w * 0.19, y + h * 0.38);\n                ctx.bezierCurveTo(x + w * 0.34, y + h * 0.48, x + w * 0.66, y + h * 0.48, x + w * 0.81, y + h * 0.38);\n                ctx.stroke();\n                for (let i = 0; i < 5; i++) {\n                    const bx = x + w * (0.24 + i * 0.13);\n                    const by = y + h * (0.405 + Math.sin(i * 1.2) * 0.025);\n                    ctx.fillStyle = '#4e8050';\n                    ctx.beginPath();\n                    ctx.ellipse(bx - 5, by - 5, w * 0.025, h * 0.045, -0.55, 0, Math.PI * 2);\n                    ctx.ellipse(bx + 5, by - 5, w * 0.025, h * 0.045, 0.55, 0, Math.PI * 2);\n                    ctx.fill();\n                    ctx.fillStyle = '#b8323d';\n                    ctx.beginPath();\n                    ctx.arc(bx, by + 3, Math.max(4, w * 0.014), 0, Math.PI * 2);\n                    ctx.arc(bx + 8, by - 1, Math.max(3, w * 0.011), 0, Math.PI * 2);\n                    ctx.fill();\n                }\n                ctx.restore();\n            }\n\n            function drawHalloweenFrosting(x, y, w, h) {\n                // Preserve the cake image underneath while giving its frosting a clear Halloween palette.\n                ctx.save();\n                ctx.globalAlpha = 0.76;\n                ctx.fillStyle = '#482858';\n                const left = x + w * 0.1, top = y + h * 0.27, bodyW = w * 0.8, bodyH = h * 0.55;\n                const r = Math.min(22, bodyW * 0.08);\n                ctx.beginPath();\n                ctx.moveTo(left + r, top);\n                ctx.lineTo(left + bodyW - r, top);\n                ctx.quadraticCurveTo(left + bodyW, top, left + bodyW, top + r);\n                ctx.lineTo(left + bodyW, top + bodyH - r);\n                ctx.quadraticCurveTo(left + bodyW, top + bodyH, left + bodyW - r, top + bodyH);\n                ctx.lineTo(left + r, top + bodyH);\n                ctx.quadraticCurveTo(left, top + bodyH, left, top + bodyH - r);\n                ctx.lineTo(left, top + r);\n                ctx.quadraticCurveTo(left, top, left + r, top);\n                ctx.closePath();\n                ctx.fill();\n                ctx.globalAlpha = 0.94;\n                ctx.fillStyle = '#75478a';\n                ctx.strokeStyle = '#ee9a3b';\n                ctx.lineWidth = Math.max(3, w * 0.012);\n                ctx.beginPath();\n                ctx.ellipse(x + w / 2, y + h * 0.28, w * 0.41, h * 0.16, 0, 0, Math.PI * 2);\n                ctx.fill();\n                ctx.stroke();\n                ctx.fillStyle = '#f2a344';\n                for (let i = 0; i < 9; i++) {\n                    const a = Math.PI + (i / 8) * Math.PI;\n                    const dx = x + w / 2 + Math.cos(a) * w * 0.36;\n                    const dy = y + h * 0.28 + Math.sin(a) * h * 0.13;\n                    ctx.beginPath();\n                    ctx.arc(dx, dy + h * 0.035, Math.max(3, w * 0.012), 0, Math.PI * 2);\n                    ctx.fill();\n                }\n                ctx.restore();\n            }\n\n            function drawHalloweenDecoration(type, x, y, size) {\n                ctx.save();\n                ctx.translate(x, y);\n                ctx.scale(size / 100, size / 100);\n                ctx.lineJoin = 'round';\n                ctx.lineCap = 'round';\n                if (type === 'halloween_pumpkin') {\n                    ctx.fillStyle = '#f28b2f'; ctx.strokeStyle = '#a94d22'; ctx.lineWidth = 3;\n                    [-19, 0, 19].forEach((offset, i) => { ctx.beginPath(); ctx.ellipse(offset, 5, i === 1 ? 19 : 16, 27, 0, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); });\n                    ctx.fillStyle = '#477348'; ctx.fillRect(-5, -29, 11, 16);\n                    ctx.fillStyle = '#492947';\n                    ctx.beginPath(); ctx.moveTo(-18, -2); ctx.lineTo(-9, 7); ctx.lineTo(-20, 10); ctx.closePath(); ctx.fill();\n                    ctx.beginPath(); ctx.moveTo(18, -2); ctx.lineTo(9, 7); ctx.lineTo(20, 10); ctx.closePath(); ctx.fill();\n                    ctx.beginPath(); ctx.moveTo(-12, 17); ctx.quadraticCurveTo(0, 29, 12, 17); ctx.lineTo(8, 12); ctx.lineTo(3, 20); ctx.lineTo(0, 12); ctx.lineTo(-5, 20); ctx.closePath(); ctx.fill();\n                } else if (type === 'halloween_ghost') {\n                    ctx.fillStyle = '#fff7e8'; ctx.strokeStyle = '#b9a7c6'; ctx.lineWidth = 3;\n                    ctx.beginPath(); ctx.moveTo(-28, 30); ctx.lineTo(-28, -4); ctx.quadraticCurveTo(-26, -32, 0, -34); ctx.quadraticCurveTo(27, -31, 28, -3); ctx.lineTo(28, 30); ctx.lineTo(14, 21); ctx.lineTo(2, 31); ctx.lineTo(-10, 21); ctx.closePath(); ctx.fill(); ctx.stroke();\n                    ctx.fillStyle = '#38233f'; ctx.beginPath(); ctx.ellipse(-10, -5, 4, 7, 0, 0, Math.PI * 2); ctx.ellipse(10, -5, 4, 7, 0, 0, Math.PI * 2); ctx.fill();\n                    ctx.beginPath(); ctx.arc(0, 9, 5, 0, Math.PI * 2); ctx.fill();\n                } else if (type === 'halloween_bat') {\n                    ctx.fillStyle = '#39213e'; ctx.strokeStyle = '#23162a'; ctx.lineWidth = 3;\n                    ctx.beginPath(); ctx.moveTo(-8, -8); ctx.lineTo(-18, -27); ctx.lineTo(-19, -12); ctx.quadraticCurveTo(-35, -27, -44, -18); ctx.lineTo(-38, 1); ctx.lineTo(-48, 14); ctx.quadraticCurveTo(-28, 12, -17, 28); ctx.lineTo(-8, 18); ctx.lineTo(0, 28); ctx.lineTo(17, 12); ctx.quadraticCurveTo(31, 12, 48, 14); ctx.lineTo(38, 1); ctx.lineTo(44, -18); ctx.quadraticCurveTo(28, -27, 18, -12); ctx.lineTo(18, -27); ctx.lineTo(8, -8); ctx.closePath(); ctx.fill(); ctx.stroke();\n                    ctx.fillStyle = '#ffd15f'; ctx.beginPath(); ctx.arc(-5, 0, 2.5, 0, Math.PI * 2); ctx.arc(5, 0, 2.5, 0, Math.PI * 2); ctx.fill();\n                } else if (type === 'halloween_candy_corn') {\n                    ctx.fillStyle = '#fff7e8'; ctx.strokeStyle = '#c97932'; ctx.lineWidth = 4;\n                    ctx.beginPath(); ctx.moveTo(0, -37); ctx.quadraticCurveTo(5, -29, 37, 32); ctx.quadraticCurveTo(40, 41, 29, 41); ctx.lineTo(-29, 41); ctx.quadraticCurveTo(-40, 41, -37, 32); ctx.quadraticCurveTo(-5, -29, 0, -37); ctx.closePath(); ctx.fill(); ctx.stroke();\n                    ctx.save(); ctx.beginPath(); ctx.moveTo(0, -37); ctx.quadraticCurveTo(5, -29, 37, 32); ctx.quadraticCurveTo(40, 41, 29, 41); ctx.lineTo(-29, 41); ctx.quadraticCurveTo(-40, 41, -37, 32); ctx.quadraticCurveTo(-5, -29, 0, -37); ctx.clip();\n                    ctx.fillStyle = '#f28b2f'; ctx.fillRect(-50, 3, 100, 22); ctx.fillStyle = '#ffd45a'; ctx.fillRect(-50, 25, 100, 25); ctx.restore();\n                } else if (type === 'halloween_spiderweb') {\n                    ctx.strokeStyle = '#fff0d3'; ctx.lineWidth = 3;\n                    for (let i = 0; i < 5; i++) { const a = (i / 4) * Math.PI / 2; ctx.beginPath(); ctx.moveTo(-35, -35); ctx.lineTo(-35 + Math.cos(a) * 75, -35 + Math.sin(a) * 75); ctx.stroke(); }\n                    for (let r = 16; r <= 70; r += 18) { ctx.beginPath(); ctx.arc(-35, -35, r, 0, Math.PI / 2); ctx.stroke(); }\n                } else if (type === 'halloween_boo') {\n                    ctx.font = 'bold 31px Georgia, serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';\n                    ctx.lineWidth = 5; ctx.strokeStyle = '#49264f'; ctx.strokeText('BOO!', 0, 0); ctx.fillStyle = '#ffb54c'; ctx.fillText('BOO!', 0, 0);\n                }\n                ctx.restore();\n            }\n\n            function drawChristmasAtmosphere() {\n                // Snow, garlands and warm fairy lights frame the festive kitchen without\n                // covering the central baking play area.\n                const w = canvas.width, h = canvas.height;\n                ctx.save();\n                ctx.lineCap = 'round';\n                ctx.strokeStyle = 'rgba(31, 86, 56, .92)';\n                ctx.lineWidth = 11;\n                ctx.beginPath();\n                ctx.moveTo(8, 185);\n                ctx.bezierCurveTo(55, h * .32, 3, h * .65, 28, h - 80);\n                ctx.moveTo(w - 8, 185);\n                ctx.bezierCurveTo(w - 55, h * .32, w - 3, h * .65, w - 28, h - 80);\n                ctx.moveTo(12, 195);\n                ctx.bezierCurveTo(w * .25, 145, w * .72, 145, w - 12, 195);\n                ctx.stroke();\n                for (let side = 0; side < 2; side++) {\n                    const x = side ? w - 24 : 24;\n                    for (let i = 0; i < 22; i++) {\n                        const y = 215 + i * ((h - 330) / 21);\n                        const sway = Math.sin(i * 1.15) * 22 * (side ? -1 : 1);\n                        ctx.strokeStyle = '#28583b';\n                        ctx.lineWidth = 3;\n                        ctx.beginPath();\n                        ctx.moveTo(x, y);\n                        ctx.lineTo(x + sway, y - 12);\n                        ctx.moveTo(x, y);\n                        ctx.lineTo(x - sway, y + 11);\n                        ctx.stroke();\n                        if (i % 2 === 0) {\n                            const colors = ['#e34a4f', '#ffe08a', '#63b979'];\n                            ctx.shadowColor = colors[(i / 2) % colors.length];\n                            ctx.shadowBlur = 11;\n                            ctx.fillStyle = colors[(i / 2) % colors.length];\n                            ctx.beginPath();\n                            ctx.arc(x + sway * .4, y + 4, 5, 0, Math.PI * 2);\n                            ctx.fill();\n                            ctx.shadowBlur = 0;\n                        }\n                    }\n                }\n                // Garland-mounted baubles add a richer Christmas-shop frame.\n                for (let i = 0; i < 9; i++) {\n                    const x = 65 + i * ((w - 130) / 8);\n                    const y = 188 + Math.sin(i * 0.8) * 18;\n                    ctx.strokeStyle = 'rgba(230, 210, 160, .9)';\n                    ctx.lineWidth = 2;\n                    ctx.beginPath(); ctx.moveTo(x, y - 18); ctx.lineTo(x, y - 5); ctx.stroke();\n                    ctx.fillStyle = i % 2 ? '#bd3945' : '#e2bd65';\n                    ctx.shadowColor = i % 2 ? '#ff7373' : '#ffe39a';\n                    ctx.shadowBlur = 14;\n                    ctx.beginPath(); ctx.arc(x, y + 5, 9, 0, Math.PI * 2); ctx.fill();\n                    ctx.shadowBlur = 0;\n                }\n                // Gentle falling snow is kept mostly at the edges and is deterministic.\n                ctx.fillStyle = 'rgba(255,255,255,.72)';\n                for (let i = 0; i < 46; i++) {\n                    const px = (i * 137 + 31) % w;\n                    const py = (i * 251 + 73) % h;\n                    const edge = px < 70 || px > w - 70 || py < 210;\n                    if (!edge) continue;\n                    const r = 1.5 + (i % 3);\n                    ctx.beginPath(); ctx.arc(px, py, r, 0, Math.PI * 2); ctx.fill();\n                }\n                ctx.restore();\n            }\n\n            function drawChristmasDecoration(type, x, y, size) {\n                ctx.save();\n                ctx.translate(x, y);\n                ctx.scale(size / 100, size / 100);\n                ctx.lineJoin = 'round';\n                ctx.lineCap = 'round';\n                if (type === 'christmas_tree') {\n                    ctx.fillStyle = '#23633f'; ctx.strokeStyle = '#d2b16d'; ctx.lineWidth = 2.5;\n                    [[-30, 12, 30], [-24, -8, 25], [-17, -27, 19]].forEach(([top, base, half]) => {\n                        ctx.beginPath(); ctx.moveTo(0, top - 22); ctx.lineTo(half, base); ctx.lineTo(-half, base); ctx.closePath(); ctx.fill(); ctx.stroke();\n                    });\n                    ctx.fillStyle = '#8b542d'; ctx.fillRect(-6, 12, 12, 20);\n                    [[-13,-10,'#c93e48'],[12,-4,'#f0c866'],[-4,-26,'#d94b50'],[18,10,'#f0c866']].forEach(([bx,by,c]) => { ctx.fillStyle=c; ctx.beginPath(); ctx.arc(bx,by,4,0,Math.PI*2); ctx.fill(); });\n                    ctx.fillStyle = '#f4d36f'; ctx.beginPath(); for(let i=0;i<10;i++){const a=-Math.PI/2+i*Math.PI/5,r=i%2?6:12;ctx.lineTo(Math.cos(a)*r, -48+Math.sin(a)*r);} ctx.closePath(); ctx.fill();\n                } else if (type === 'candy_cane') {\n                    ctx.lineWidth = 18; ctx.strokeStyle = '#fff8e8'; ctx.beginPath(); ctx.moveTo(-16, 38); ctx.lineTo(-16, -16); ctx.bezierCurveTo(-16, -48, 35, -48, 30, -12); ctx.stroke();\n                    ctx.save(); ctx.beginPath(); ctx.moveTo(-30, 45); ctx.lineTo(-30, -15); ctx.bezierCurveTo(-30, -62, 50, -62, 43, -10); ctx.lineTo(22, -10); ctx.bezierCurveTo(25, -35, 4, -35, 4, -14); ctx.lineTo(4, 45); ctx.closePath(); ctx.clip();\n                    ctx.strokeStyle = '#c83e48'; ctx.lineWidth = 9; for(let i=-60;i<75;i+=24){ctx.beginPath();ctx.moveTo(-45,i);ctx.lineTo(45,i+35);ctx.stroke();} ctx.restore();\n                } else if (type === 'snowflake') {\n                    ctx.strokeStyle = '#e4f5ff'; ctx.lineWidth = 7;\n                    for(let i=0;i<6;i++){ctx.save();ctx.rotate(i*Math.PI/3);ctx.beginPath();ctx.moveTo(0,-39);ctx.lineTo(0,39);ctx.moveTo(0,-23);ctx.lineTo(-12,-34);ctx.moveTo(0,-23);ctx.lineTo(12,-34);ctx.moveTo(0,23);ctx.lineTo(-12,34);ctx.moveTo(0,23);ctx.lineTo(12,34);ctx.stroke();ctx.restore();}\n                    ctx.fillStyle='#b5dff1';ctx.beginPath();ctx.arc(0,0,5,0,Math.PI*2);ctx.fill();\n                } else if (type === 'christmas_bauble') {\n                    ctx.strokeStyle='#d7b96d';ctx.lineWidth=5;ctx.beginPath();ctx.moveTo(0,-43);ctx.lineTo(0,-32);ctx.stroke();\n                    const grad=ctx.createRadialGradient(-12,-15,3,0,0,35);grad.addColorStop(0,'#ffb2a9');grad.addColorStop(.55,'#c84450');grad.addColorStop(1,'#7c2437');\n                    ctx.fillStyle=grad;ctx.beginPath();ctx.arc(0,4,32,0,Math.PI*2);ctx.fill();ctx.strokeStyle='#f0d188';ctx.lineWidth=3;ctx.stroke();\n                    ctx.fillStyle='rgba(255,255,255,.8)';ctx.beginPath();ctx.ellipse(-11,-8,6,11,-.5,0,Math.PI*2);ctx.fill();\n                } else if (type === 'golden_star') {\n                    ctx.fillStyle='#f3cd70';ctx.strokeStyle='#fff1bd';ctx.lineWidth=4;ctx.beginPath();for(let i=0;i<10;i++){const a=-Math.PI/2+i*Math.PI/5,r=i%2?22:43;ctx.lineTo(Math.cos(a)*r,Math.sin(a)*r);}ctx.closePath();ctx.fill();ctx.stroke();\n                } else if (type === 'christmas_wreath') {\n                    ctx.strokeStyle='#286344';ctx.lineWidth=19;ctx.beginPath();ctx.arc(0,0,28,0,Math.PI*2);ctx.stroke();\n                    for(let i=0;i<12;i++){const a=i*Math.PI/6;ctx.fillStyle=i%2?'#397c4c':'#4d9256';ctx.beginPath();ctx.ellipse(Math.cos(a)*31,Math.sin(a)*31,8,4,a,0,Math.PI*2);ctx.fill();}\n                    ctx.fillStyle='#c83e48';[-8,0,8].forEach((dx,i)=>{ctx.beginPath();ctx.arc(dx,i===1?-1:5,6,0,Math.PI*2);ctx.fill();});\n                }\n                ctx.restore();\n            }\n\n            function drawCakeDecoration(type, x, y, size) {\n                if (['christmas_tree','candy_cane','snowflake','christmas_bauble','golden_star','christmas_wreath'].includes(type)) {\n                    drawChristmasDecoration(type, x + size / 2, y + size / 2, size);\n                } else {\n                    drawImage(type, x, y, size, size);\n                }\n            }\n\n            function drawTutorialArrow(x, y, angle = 0) {\n                // Tutorial arrows are disabled so they never cover the baking scene.\n            }\n            function draw3DPan(x, y, width, depth, height, fillProgress, isBakingStage = 0) {\n                const bottomScale = 0.88; \n                const bottomWidth = width * bottomScale;\n                const bottomDepth = depth * bottomScale;\n                const centerX = x;\n                const centerY = y;\n\n                // 1. Bottom outer shadow\n                ctx.save();\n                ctx.fillStyle = 'rgba(0,0,0,0.15)';\n                ctx.beginPath();\n                ctx.ellipse(centerX, centerY + height + 3, bottomWidth / 2 + 5, bottomDepth / 2 + 5, 0, 0, Math.PI * 2);\n                ctx.fill();\n                ctx.restore();\n\n                // 2. Inner back wall (stainless steel texture)\n                ctx.save();\n                const innerGradient = ctx.createLinearGradient(centerX - width / 2, centerY, centerX + width / 2, centerY);\n                innerGradient.addColorStop(0, '#555');\n                innerGradient.addColorStop(0.5, '#888');\n                innerGradient.addColorStop(1, '#555');\n                ctx.fillStyle = innerGradient;\n                ctx.beginPath();\n                ctx.ellipse(centerX, centerY, width / 2, depth / 2, 0, Math.PI, 0, false);\n                ctx.lineTo(centerX + bottomWidth / 2, centerY + height);\n                ctx.ellipse(centerX, centerY + height, bottomWidth / 2, bottomDepth / 2, 0, 0, Math.PI, true);\n                ctx.lineTo(centerX - width / 2, centerY);\n                ctx.fill();\n                ctx.restore();\n\n                // 3. Pan bottom (solves crack issues)\n                ctx.save();\n                ctx.fillStyle = '#777';\n                ctx.beginPath();\n                ctx.ellipse(centerX, centerY + height, bottomWidth / 2, bottomDepth / 2, 0, 0, Math.PI * 2);\n                ctx.fill();\n                ctx.restore();\n\n                // 4. Batter fill (Procedural filling)\n                if (fillProgress > 0) {\n                    const fillHeight = fillProgress * height * 0.95;\n                    const fillY = centerY + height - fillHeight;\n                    const currentScale = bottomScale + (1 - bottomScale) * fillProgress;\n                    const fillW = width * currentScale;\n                    const fillD = depth * currentScale;\n\n                    ctx.save();\n                    let batterColor = '#F5DEB3'; \n                    if (isBakingStage === 1) batterColor = '#EACD9E';\n                    else if (isBakingStage === 2) batterColor = '#D2B48C';\n                    else if (isBakingStage === 3) batterColor = '#B8860B';\n\n                    ctx.fillStyle = batterColor;\n                    ctx.beginPath();\n                    ctx.ellipse(centerX, fillY, fillW / 2 - 2, fillD / 2 - 2, 0, 0, Math.PI * 2);\n                    ctx.fill();\n\n                    if (isBakingStage < 2) {\n                        ctx.globalAlpha = 0.3;\n                        ctx.strokeStyle = '#EACD9E';\n                        ctx.lineWidth = 2;\n                        for (let i = 1; i <= 2; i++) {\n                            ctx.beginPath();\n                            const rippleScale = 0.3 * i + Math.sin(Date.now() / 200 + i) * 0.05;\n                            ctx.ellipse(centerX, fillY, (fillW / 2) * rippleScale, (fillD / 2) * rippleScale, 0, 0, Math.PI * 2);\n                            ctx.stroke();\n                        }\n                    }\n                    ctx.restore();\n                }\n\n                // 5. Outer front wall (polished stainless steel)\n                ctx.save();\n                const outerGradient = ctx.createLinearGradient(centerX - width / 2, centerY, centerX + width / 2, centerY);\n                outerGradient.addColorStop(0, '#666'); \n                outerGradient.addColorStop(0.2, '#DDD'); \n                outerGradient.addColorStop(0.4, '#888'); \n                outerGradient.addColorStop(0.6, '#FFF'); \n                outerGradient.addColorStop(0.8, '#AAA'); \n                outerGradient.addColorStop(1, '#666'); \n                ctx.fillStyle = outerGradient;\n                ctx.beginPath();\n                ctx.ellipse(centerX, centerY, width / 2, depth / 2, 0, 0, Math.PI, false);\n                ctx.lineTo(centerX - bottomWidth / 2, centerY + height);\n                ctx.ellipse(centerX, centerY + height, bottomWidth / 2, bottomDepth / 2, 0, Math.PI, 0, true);\n                ctx.lineTo(centerX + width / 2, centerY);\n                ctx.fill();\n                // Removed stroke for smoother edges\n                \n                // Top edge reflection\n                ctx.beginPath();\n                ctx.ellipse(centerX, centerY, width / 2, depth / 2, 0, 0, Math.PI * 2);\n                ctx.strokeStyle = 'rgba(255,255,255,0.4)';\n                ctx.lineWidth = 1.5;\n                ctx.stroke();\n                ctx.restore();\n            }\n\n            function drawTurntable(x, y, rotation = 0, scale = 1.0) {\n                const width = 440 * scale;\n                const depth = 110 * scale;\n                const height = 25 * scale;\n                const baseWidth = 220 * scale;\n                const baseHeight = 50 * scale;\n                \n                // 1. Bottom outer shadow\n                ctx.save();\n                ctx.shadowBlur = 20 * scale;\n                ctx.shadowColor = 'rgba(0,0,0,0.2)';\n                ctx.fillStyle = 'rgba(0,0,0,0.1)';\n                ctx.beginPath();\n                ctx.ellipse(x, y + baseHeight + 20 * scale, width / 2 + 15 * scale, depth / 2 + 15 * scale, 0, 0, Math.PI * 2);\n                ctx.fill();\n                ctx.restore();\n\n                // 2. Base (Sturdy professional base)\n                ctx.save();\n                const baseGrad = ctx.createLinearGradient(x - baseWidth/2, y, x + baseWidth/2, y);\n                baseGrad.addColorStop(0, '#9e7543');\n                baseGrad.addColorStop(0.3, '#e9d5a5');\n                baseGrad.addColorStop(0.5, '#fff5d9');\n                baseGrad.addColorStop(0.7, '#dfc58e');\n                baseGrad.addColorStop(1, '#9e7543');\n                \n                // Base bottom plate\n                ctx.fillStyle = baseGrad;\n                ctx.beginPath();\n                ctx.ellipse(x, y + baseHeight + 10 * scale, baseWidth / 2, 30 * scale, 0, 0, Math.PI * 2);\n                ctx.fill();\n                ctx.strokeStyle = '#888';\n                ctx.lineWidth = 1 * scale;\n                ctx.stroke();\n\n                // Column support\n                ctx.beginPath();\n                ctx.moveTo(x - 40 * scale, y + height);\n                ctx.lineTo(x + 40 * scale, y + height);\n                ctx.lineTo(x + 50 * scale, y + baseHeight + 10 * scale);\n                ctx.lineTo(x - 50 * scale, y + baseHeight + 10 * scale);\n                ctx.fill();\n                ctx.stroke();\n                ctx.restore();\n\n                // 3. Turntable top\n                ctx.save();\n                // Plate side thickness\n                ctx.fillStyle = '#b18b50';\n                ctx.beginPath();\n                ctx.ellipse(x, y + height/2, width / 2, depth / 2, 0, 0, Math.PI, false);\n                ctx.lineTo(x - width / 2, y);\n                ctx.ellipse(x, y, width / 2, depth / 2, 0, Math.PI, 0, true);\n                ctx.fill();\n\n                // Plate top surface\n                const plateGrad = ctx.createRadialGradient(x, y, 0, x, y, width/2);\n                plateGrad.addColorStop(0, '#fffdf5');\n                plateGrad.addColorStop(0.8, '#f2e4c5');\n                plateGrad.addColorStop(1, '#d6bd85');\n                ctx.fillStyle = plateGrad;\n                ctx.beginPath();\n                ctx.ellipse(x, y, width / 2, depth / 2, 0, 0, Math.PI * 2);\n                ctx.fill();\n                \n                // Rotation scale lines/decorations\n                ctx.save();\n                ctx.translate(x, y);\n                ctx.rotate(rotation);\n                ctx.strokeStyle = 'rgba(165,125,61,0.22)';\n                ctx.lineWidth = 2 * scale;\n                for(let i = 0; i < 12; i++) {\n                    ctx.beginPath();\n                    const angle = (i / 12) * Math.PI * 2;\n                    ctx.moveTo(Math.cos(angle) * (width/2 - 15 * scale), Math.sin(angle) * (depth/2 - 4 * scale));\n                    ctx.lineTo(Math.cos(angle) * (width/2 - 4 * scale), Math.sin(angle) * (depth/2 - 2 * scale));\n                    ctx.stroke();\n                }\n                \n                // Ring decorations\n                for(let i = 1; i <= 2; i++) {\n                    ctx.beginPath();\n                    ctx.ellipse(0, 0, (width / 2) * (i / 3), (depth / 2) * (i / 3), 0, 0, Math.PI * 2);\n                    ctx.stroke();\n                }\n                ctx.restore();\n                \n                // Plate highlight\n                ctx.globalAlpha = 0.3;\n                ctx.fillStyle = '#FFF';\n                ctx.beginPath();\n                ctx.ellipse(x - 15 * scale, y - 4 * scale, width / 3, depth / 4, 0.4, 0, Math.PI * 2);\n                ctx.fill();\n                ctx.restore();\n            }\n\n            // Step 1: Prepare Ingredients\n            function initStep1() {\n                currentStepData = {\n                    added: {},\n                    animating: null,\n                    particles: [],\n                    positions: {\n                        flour: { x: 80, y: 250, w: 140, h: 180, asset: 'flour_bag', color: '#FFFFFF', name: 'Flour' },\n                        sugar: { x: 500, y: 250, w: 140, h: 180, asset: 'sugar_bag', color: '#FFFFFF', name: 'Sugar' },\n                        eggs: { x: 80, y: 780, w: 160, h: 120, asset: 'eggs_carton', color: '#FFD700', name: 'Eggs' },\n                        milk: { x: 500, y: 780, w: 120, h: 200, asset: 'milk_bottle', color: '#F0F8FF', name: 'Milk' },\n                        butter: { x: 50, y: 530, w: 140, h: 100, asset: 'butter_block', color: '#FFFACD', name: 'Butter' },\n                        bakingPowder: { x: 530, y: 530, w: 120, h: 120, asset: 'baking_powder_container', color: '#FFFFFF', name: 'Baking Powder' }\n                    }\n                };\n                stepContent.innerHTML = '';\n                \n                const handleClick = (e) => {\n                    if (currentStepData.animating) return;\n                    const pos = getInputPosition(e);\n                    Object.keys(currentStepData.positions).forEach(key => {\n                        if (currentStepData.added[key]) return;\n                        const p = currentStepData.positions[key];\n                        if (pos.x >= p.x && pos.x <= p.x + p.w && pos.y >= p.y && pos.y <= p.y + p.h) {\n                            currentStepData.animating = { key, startTime: Date.now() };\n                            playSound('select_sound');\n                        }\n                    });\n                };\n\n                canvas.addEventListener('click', handleClick);\n                canvas.addEventListener('touchstart', (e) => {\n                    e.preventDefault();\n                    handleClick(e);\n                }, { passive: false });\n                \n                actionButton.textContent = 'Next';\n                actionButton.disabled = true;\n                actionButton.onclick = () => {\n                    if (checkStep1Complete()) {\n                        playSound('step_complete_sound');\n                        nextStep();\n                    }\n                };\n            }\n\n            function checkStep1Complete() {\n                if (!currentStepData.added) return false;\n                const required = ['flour', 'sugar', 'eggs', 'milk', 'butter', 'bakingPowder'];\n                const allAdded = required.every(key => currentStepData.added[key]);\n                if (allAdded) stepScores[0] = 10;\n                return allAdded;\n            }\n            function renderStep1() {\n                drawBackground();\n                \n                const bowlWidth = 350;\n                const bowlX = canvas.width / 2 - bowlWidth / 2;\n                const bowlY = canvas.height / 2 - 120;\n                const bowlCenterX = canvas.width / 2;\n                const bowlCenterY = bowlY + 150;\n\n                if (currentStepData.particles) {\n                    for (let i = currentStepData.particles.length - 1; i >= 0; i--) {\n                        const p = currentStepData.particles[i];\n                        p.y += p.vy;\n                        p.x += p.vx;\n                        p.vy += 0.5;\n                        p.life -= 0.02;\n                        ctx.save();\n                        ctx.globalAlpha = p.life;\n                        ctx.fillStyle = p.color;\n                        ctx.beginPath();\n                        ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);\n                        ctx.fill();\n                        ctx.restore();\n                        if (p.life <= 0 || p.y > bowlCenterY + 50) {\n                            currentStepData.particles.splice(i, 1);\n                        }\n                    }\n                }\n\n                drawImage('mixing_bowl_large', bowlX, bowlY, bowlWidth, null);\n\n                if (currentStepData.positions) {\n                    Object.keys(currentStepData.positions).forEach(key => {\n                        const isAnimating = currentStepData.animating && currentStepData.animating.key === key;\n                        if (!currentStepData.added[key] && !isAnimating) {\n                            const p = currentStepData.positions[key];\n                            drawImage(p.asset, p.x, p.y, p.w, null);\n                        }\n                    });\n                }\n\n                if (currentStepData.animating) {\n                    const anim = currentStepData.animating;\n                    const elapsed = Date.now() - anim.startTime;\n                    const isEgg = anim.key === 'eggs';\n                    const isButter = anim.key === 'butter';\n                    const isPowder = ['flour', 'sugar', 'bakingPowder'].includes(anim.key);\n                    const duration = isEgg ? 3600 : 1800; \n                    const progress = Math.min(elapsed / duration, 1);\n                    const p = currentStepData.positions[anim.key];\n                    const startX = p.x + p.w / 2;\n                    const startY = p.y + p.h / 2;\n                    const targetX = bowlCenterX;\n                    const targetY = bowlY - 40;\n\n                    if (isEgg) {\n                        const eggProgress = (progress * 3) % 1;\n                        if (progress < 1) {\n                            if (eggProgress < 0.3) {\n                                const p1 = eggProgress / 0.3;\n                                const curX = startX + (targetX - startX) * p1;\n                                const curY = startY + (targetY - startY) * p1;\n                                drawImage('egg_single', curX - 35, curY - 45, 70, 90);\n                            } else if (eggProgress < 0.5) {\n                                const p2 = (eggProgress - 0.3) / 0.2;\n                                const shake = Math.sin(Date.now() / 30) * 4;\n                                drawImage('egg_single', targetX - 35 + shake, targetY - 45, 70, 90);\n                                ctx.strokeStyle = '#444';\n                                ctx.lineWidth = 3;\n                                ctx.beginPath();\n                                ctx.moveTo(targetX - 25, targetY);\n                                ctx.lineTo(targetX + 25, targetY);\n                                ctx.stroke();\n                            } else if (eggProgress < 0.8) {\n                                const p3 = (eggProgress - 0.5) / 0.3;\n                                const separation = p3 * 60;\n                                drawImage('egg_shell_broken_top', targetX - 35 - separation / 2, targetY - 50 - separation / 2, 70, 50);\n                                drawImage('egg_shell_broken_bottom', targetX - 35 + separation / 2, targetY + 10 + separation / 2, 70, 50);\n                                const yolkY = targetY + p3 * 180;\n                                drawImage('egg_yolk_liquid', targetX - 35, yolkY - 40, 70, 110);\n                                if (Math.floor(elapsed / 300) !== Math.floor((elapsed - 16) / 300)) playSound('pour_liquid_sound', 0.15);\n                            } else {\n                                const p4 = (eggProgress - 0.8) / 0.2;\n                                ctx.save();\n                                ctx.globalAlpha = 1 - p4;\n                                drawImage('egg_shell_broken_top', targetX - 35 - 30, targetY - 80, 70, 50);\n                                drawImage('egg_shell_broken_bottom', targetX - 35 + 30, targetY + 40, 70, 50);\n                                ctx.restore();\n                            }\n                        }\n                    } else if (isButter) {\n                        if (progress < 0.3) {\n                            const p1 = progress / 0.3;\n                            const curX = startX + (targetX - startX) * p1;\n                            const curY = startY + (targetY - startY) * p1;\n                            drawImage('butter_block', curX - p.w/2, curY - p.h/2, p.w, null);\n                        } else if (progress < 0.6) {\n                            const p2 = (progress - 0.3) / 0.3;\n                            const dropY = targetY + p2 * 180;\n                            drawImage('butter_block', targetX - p.w/2, dropY - p.h/2, p.w, null);\n                        } else if (progress < 0.7) {\n                            drawImage('butter_block', targetX - p.w/2, targetY + 180 - p.h/2, p.w, null);\n                            if (Math.floor(elapsed / 16) === Math.floor((anim.startTime + duration * 0.6) / 16)) playSound('place_decoration_sound', 0.5);\n                            for(let i=0; i<3; i++) {\n                                currentStepData.particles.push({\n                                    x: targetX + (Math.random() - 0.5) * 80,\n                                    y: targetY + 180,\n                                    vx: (Math.random() - 0.5) * 12,\n                                    vy: -Math.random() * 10,\n                                    size: 5 + Math.random() * 8,\n                                    color: p.color,\n                                    life: 1.0\n                                });\n                            }\n                        } else {\n                            const p4 = (progress - 0.7) / 0.3;\n                            ctx.save();\n                            ctx.globalAlpha = 1 - p4;\n                            drawImage('butter_block', targetX - p.w/2, targetY + 180 - p.h/2, p.w, null);\n                            ctx.restore();\n                        }\n                    } else {\n                        if (progress < 0.2) {\n                            const p1 = progress / 0.2;\n                            const curX = p.x + (targetX - p.w / 2 - p.x) * p1;\n                            const curY = p.y + (targetY - p.y) * p1;\n                            drawImage(p.asset, curX, curY, p.w, null);\n                        } else if (progress < 0.8) {\n                            const p2 = (progress - 0.2) / 0.6;\n                            const tilt = Math.min(p2 * 4, 1) * (Math.PI * 0.75);\n                            const shake = Math.sin(Date.now() / 40) * 4;\n                            ctx.save();\n                            ctx.translate(targetX + shake, targetY);\n                            ctx.rotate(tilt);\n                            drawImage(p.asset, -p.w / 2, -p.h / 2 || -80, p.w, null);\n                            ctx.restore();\n                            \n                            if (isPowder) {\n                                ctx.save();\n                                const jitter = Math.sin(Date.now() / 20) * 4;\n                                if (assets['flour_stream_texture']) {\n                                    ctx.globalAlpha = 0.3;\n                                    const pulse = 10 + Math.sin(Date.now() / 50) * 10;\n                                    drawImage('flour_stream_texture', targetX - 45 - pulse/2, targetY + 20, 90 + pulse, 160);\n                                    ctx.globalAlpha = 0.85;\n                                    drawImage('flour_stream_texture', targetX - 25 + jitter, targetY + 10, 50, 175);\n                                } else {\n                                    ctx.fillStyle = p.color;\n                                    ctx.globalAlpha = 0.6;\n                                    ctx.beginPath();\n                                    ctx.moveTo(targetX - 20, targetY + 10);\n                                    ctx.lineTo(targetX + 20, targetY + 10);\n                                    ctx.lineTo(targetX - 35, targetY + 180);\n                                    ctx.fill();\n                                }\n                                ctx.restore();\n                            } else {\n                                ctx.fillStyle = p.color;\n                                ctx.globalAlpha = 0.95; \n                                ctx.beginPath();\n                                ctx.moveTo(targetX - 15, targetY + 10);\n                                ctx.lineTo(targetX + 15, targetY + 10);\n                                ctx.lineTo(targetX + 8, targetY + 180);\n                                ctx.lineTo(targetX - 8, targetY + 180);\n                                ctx.fill();\n                            }\n                            \n                            const particleCount = isPowder ? 12 : 3; \n                            for (let i = 0; i < particleCount; i++) {\n                                currentStepData.particles.push({\n                                    x: targetX + (Math.random() - 0.5) * (isPowder ? 60 : 25),\n                                    y: targetY + 10 + (isPowder ? Math.random() * 20 : Math.random() * 160),\n                                    vx: (Math.random() - 0.5) * (isPowder ? 6 : 3),\n                                    vy: 7 + Math.random() * 8,\n                                    size: (isPowder ? 1 + Math.random() * 6 : 3 + Math.random() * 4),\n                                    color: p.color,\n                                    life: isPowder ? 0.7 + Math.random() * 0.3 : 0.9\n                                });\n                            }\n                            if (Math.floor(elapsed / 180) !== Math.floor((elapsed - 16) / 180)) playSound('pour_liquid_sound', isPowder ? 0.1 : 0.25);\n                        } else {\n                            const p3 = (progress - 0.8) / 0.2;\n                            ctx.save();\n                            ctx.globalAlpha = 1 - p3;\n                            ctx.translate(targetX, targetY);\n                            ctx.rotate(Math.PI * 0.75);\n                            drawImage(p.asset, -p.w / 2, -p.h / 2 || -80, p.w, null);\n                            ctx.restore();\n                        }\n                    }\n                    if (progress >= 1) {\n                        currentStepData.added[anim.key] = true;\n                        currentStepData.animating = null;\n                        const ing = window.gameConfig.ingredients[anim.key];\n                        if (ing) ing.current = ing.target;\n                    }\n                }\n\n                if (!checkStep1Complete()) {\n                    if (!currentStepData.animating) {\n                        const nextKey = Object.keys(currentStepData.positions).find(k => !currentStepData.added[k]);\n                        if (nextKey) {\n                            const p = currentStepData.positions[nextKey];\n                            // Adjusting to point exactly to the middle of the bag/item\n                            drawTutorialArrow(p.x + p.w / 2, p.y + p.h / 4, 0);\n                        }\n                    }\n                } else {\n                    // Point to the Next Button specifically\n                    drawTutorialArrow(canvas.width / 2, canvas.height - 150, 0);\n                }\n            }\n\n            function updateStep1() {\n                if (checkStep1Complete() && !currentStepData.animating) {\n                    actionButton.disabled = false;\n                    actionButton.style.opacity = \"1\";\n                } else {\n                    actionButton.disabled = true;\n                }\n            }\n\n            // Step 2: Mix Batter\n            function initStep2() {\n                currentStepData = {\n                    mixingProgress: 0,\n                    lastX: canvas.width / 2,\n                    lastY: canvas.height / 2,\n                    mixerX: canvas.width / 2,\n                    mixerY: canvas.height / 2 - 100,\n                    mixing: false,\n                    mixerSound: null\n                };\n                stepContent.innerHTML = '';\n                const startMixing = (e) => {\n                    e.preventDefault();\n                    currentStepData.mixing = true;\n                    const pos = getInputPosition(e);\n                    currentStepData.lastX = pos.x;\n                    currentStepData.lastY = pos.y;\n                    if (!currentStepData.mixerSound) {\n                        currentStepData.mixerSound = playSound('mixer_sound', 0.5, true);\n                    }\n                };\n                const doMixing = (e) => {\n                    const pos = getInputPosition(e);\n                    currentStepData.mixerX = pos.x;\n                    currentStepData.mixerY = pos.y;\n                    if (!currentStepData.mixing) return;\n                    e.preventDefault();\n                    const dx = pos.x - currentStepData.lastX;\n                    const dy = pos.y - currentStepData.lastY;\n                    const distance = Math.sqrt(dx * dx + dy * dy);\n                    if (distance > 5) {\n                        currentStepData.mixingProgress = Math.min(100, currentStepData.mixingProgress + distance / 25);\n                        currentStepData.lastX = pos.x;\n                        currentStepData.lastY = pos.y;\n                    }\n                };\n                const stopMixing = () => { \n                    currentStepData.mixing = false;\n                    if (currentStepData.mixerSound) {\n                        currentStepData.mixerSound.source.stop();\n                        currentStepData.mixerSound = null;\n                    }\n                };\n                canvas.addEventListener('mousedown', startMixing);\n                canvas.addEventListener('touchstart', startMixing);\n                canvas.addEventListener('mousemove', doMixing);\n                canvas.addEventListener('touchmove', doMixing);\n                canvas.addEventListener('mouseup', stopMixing);\n                canvas.addEventListener('touchend', stopMixing);\n                actionButton.textContent = 'Next';\n                actionButton.disabled = true;\n                actionButton.onclick = () => {\n                    if (currentStepData.mixingProgress >= 100) {\n                        playSound('step_complete_sound');\n                        stepScores[1] = 10;\n                        nextStep();\n                    }\n                };\n            }\n\n            function updateStep2() {\n                if (currentStepData.mixingProgress >= 100) {\n                    actionButton.disabled = false;\n                }\n            }\n\n            function renderStep2() {\n                drawBackground();\n                const bowlX = canvas.width / 2 - 175;\n                const bowlY = canvas.height / 2 - 100;\n                const centerX = canvas.width / 2;\n                const centerY = bowlY + 180;\n                const radiusX = 130;\n                const radiusY = 50;\n                const batterFillOpacity = 0.7 + (currentStepData.mixingProgress / 100) * 0.3;\n                const rotationSpeed = currentStepData.mixing ? 0.8 : 0.15;\n                const time = Date.now() * rotationSpeed;\n\n                ctx.save();\n                ctx.beginPath();\n                ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);\n                ctx.clip();\n                ctx.save();\n                ctx.translate(centerX, centerY);\n                ctx.rotate(time / 50);\n                ctx.globalAlpha = batterFillOpacity;\n                if (assets['raw_batter']) {\n                    const shakeX = currentStepData.mixing ? Math.sin(Date.now() / 50) * 3 : 0;\n                    drawImage('raw_batter', -radiusX - 20 + shakeX, -radiusY - 20, radiusX * 2 + 40, radiusY * 2 + 40);\n                } else {\n                    ctx.fillStyle = '#F5DEB3';\n                    ctx.fill();\n                }\n                ctx.restore();\n\n                if (currentStepData.mixingProgress > 0) {\n                    ctx.save();\n                    ctx.translate(centerX, centerY);\n                    ctx.rotate(time / 80);\n                    ctx.strokeStyle = '#EACD9E';\n                    ctx.lineWidth = 4;\n                    ctx.globalAlpha = 0.5 * (currentStepData.mixingProgress / 100);\n                    for (let i = 0; i < 4; i++) {\n                        ctx.beginPath();\n                        ctx.rotate(Math.PI / 2);\n                        ctx.arc(0, 0, radiusX * 0.6, 0, Math.PI / 2.5);\n                        ctx.stroke();\n                    }\n                    ctx.fillStyle = '#FFF5E1';\n                    for (let i = 0; i < 12; i++) {\n                        const angle = (i / 12) * Math.PI * 2;\n                        const r = radiusX * (0.2 + Math.sin(time/400 + i) * 0.4);\n                        const px = Math.cos(angle + time/120) * r;\n                        const py = Math.sin(angle + time/120) * (r * (radiusY/radiusX));\n                        ctx.beginPath();\n                        ctx.arc(px, py, 1.5 + Math.random() * 3.5, 0, Math.PI * 2);\n                        ctx.fill();\n                    }\n                    ctx.restore();\n                }\n                ctx.restore();\n\n                drawImage('mixing_bowl_large', bowlX, bowlY, 350, null);\n                const mixerWidth = 220;\n                const shake = currentStepData.mixing ? Math.sin(Date.now() / 20) * 8 : 0;\n                drawImage('electric_mixer', currentStepData.mixerX - mixerWidth / 2 + shake, currentStepData.mixerY - mixerWidth / 2 + shake, mixerWidth, null);\n                \n                // Keep the mixing scene unobstructed; completion is indicated by enabling Next.\n                if (currentStepData.mixingProgress < 100) {\n                    // Mix in a circular motion near the bowl center\n                    const time = Date.now() / 300;\n                    const mixX = canvas.width / 2 + Math.cos(time) * 100;\n                    const mixY = bowlY + 180 + Math.sin(time) * 40;\n                    drawTutorialArrow(mixX, mixY, 0);\n                } else {\n                    // Point to the Next Button\n                    drawTutorialArrow(canvas.width / 2, canvas.height - 150, 0);\n                }\n            }\n\n            // Step 3: Pour into Pan\n            function initStep3() {\n                currentStepData = { pouring: false, pourProgress: 0, particles: [], lastSoundTime: 0 };\n                stepContent.innerHTML = '';\n                actionButton.textContent = 'Start Pouring';\n                actionButton.disabled = false;\n                actionButton.onclick = () => {\n                    if (currentStepData.pouring) return;\n                    currentStepData.pouring = true;\n                    actionButton.disabled = true;\n                    const pourInterval = setInterval(() => {\n                        currentStepData.pourProgress += 0.8;\n                        if (currentStepData.pourProgress >= 100) {\n                            clearInterval(pourInterval);\n                            stepScores[2] = 10;\n                            actionButton.textContent = 'Next';\n                            actionButton.disabled = false;\n                            actionButton.onclick = () => {\n                                playSound('step_complete_sound');\n                                nextStep();\n                            };\n                        }\n                    }, 40);\n                };\n            }\n\n            function renderStep3() {\n                drawBackground();\n                const panX = canvas.width / 2;\n                const panY = canvas.height / 2 + 150;\n                const panW = 460;\n                const panD = 140;\n                const panH = 55;\n                const progress = currentStepData.pourProgress / 100;\n                draw3DPan(panX, panY, panW, panD, panH, progress);\n\n                if (currentStepData.particles) {\n                    for (let i = currentStepData.particles.length - 1; i >= 0; i--) {\n                        const p = currentStepData.particles[i];\n                        p.y += p.vy;\n                        p.x += p.vx;\n                        p.vy += 0.4;\n                        p.life -= 0.02;\n                        ctx.save();\n                        ctx.globalAlpha = p.life;\n                        ctx.fillStyle = p.color;\n                        ctx.beginPath();\n                        ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);\n                        ctx.fill();\n                        ctx.restore();\n                        if (p.life <= 0 || p.y > panY + panH) currentStepData.particles.splice(i, 1);\n                    }\n                }\n\n                const bowlWidth = 350;\n                const aspect = assets['mixing_bowl_large'] ? assets['mixing_bowl_large'].info.aspect_ratio : [1, 1];\n                const bowlH = bowlWidth * (aspect[1] / aspect[0]);\n                const fillHeight = progress * panH * 0.95;\n                const batterSurfaceY = panY + panH - fillHeight;\n                const lipX = canvas.width / 2;\n                const lipY = panY - 60; \n                const tilt = progress * -1.3;\n\n                if (currentStepData.pouring && currentStepData.pourProgress < 100) {\n                    ctx.save();\n                    const streamW = 40 + progress * 10;\n                    const dist = batterSurfaceY - lipY;\n                    if (dist > 0) {\n                        ctx.save();\n                        ctx.translate(lipX, lipY);\n                        if (assets['flour_stream_texture']) {\n                            const jitter = Math.sin(Date.now() / 25) * 3;\n                            ctx.globalAlpha = 0.5;\n                            drawImage('flour_stream_texture', -streamW/2 + jitter, 0, streamW, dist);\n                            ctx.globalAlpha = 0.95;\n                            drawImage('flour_stream_texture', -streamW/4 + jitter, 0, streamW/2, dist);\n                        } else {\n                            ctx.fillStyle = '#F5DEB3';\n                            ctx.beginPath();\n                            ctx.moveTo(-streamW / 2, 0);\n                            ctx.lineTo(streamW / 2, 0);\n                            ctx.lineTo(streamW / 3, dist);\n                            ctx.lineTo(-streamW / 3, dist);\n                            ctx.fill();\n                        }\n                        ctx.restore();\n                    }\n                    ctx.restore();\n                    for (let i = 0; i < 3; i++) {\n                        currentStepData.particles.push({\n                            x: lipX + (Math.random() - 0.5) * 50,\n                            y: batterSurfaceY,\n                            vx: (Math.random() - 0.5) * 6,\n                            vy: -Math.random() * 5,\n                            size: 2 + Math.random() * 4,\n                            color: '#FFF5E1',\n                            life: 0.8\n                        });\n                    }\n                    if (Date.now() - currentStepData.lastSoundTime > 300) {\n                        playSound('pour_liquid_sound', 0.2);\n                        currentStepData.lastSoundTime = Date.now();\n                    }\n                }\n\n                ctx.save();\n                ctx.translate(lipX, lipY);\n                ctx.rotate(tilt);\n                drawImage('mixing_bowl_large', 0, -25, bowlWidth, bowlH);\n                ctx.restore();\n\n                // The pouring action is shown by the batter stream and filling pan, without an overlay bar.\n                \n                // \u6307\u5411\u4e0b\u4e00\u6b65\u6309\u9215\u6216\u50be\u5012\u4f4d\u7f6e\n                if (currentStepData.pourProgress < 100) {\n                    drawTutorialArrow(canvas.width / 2, canvas.height - 130, 0);\n                } else {\n                    drawTutorialArrow(canvas.width / 2, canvas.height - 130, 0);\n                }\n            }\n\n            // Step 4: Baking\n            function initStep4() {\n                currentStepData = { baking: false, bakingProgress: 0, stage: 0 };\n                stepContent.innerHTML = '';\n                actionButton.textContent = 'Start Baking';\n                actionButton.disabled = false;\n                actionButton.onclick = () => {\n                    if (!currentStepData.baking) {\n                        currentStepData.baking = true;\n                        actionButton.disabled = true;\n                        playSound('oven_door_sound');\n                        const bakingInterval = setInterval(() => {\n                            currentStepData.bakingProgress += 100 / 5;\n                            if (currentStepData.bakingProgress >= 33 && currentStepData.stage === 0) currentStepData.stage = 1;\n                            else if (currentStepData.bakingProgress >= 66 && currentStepData.stage === 1) currentStepData.stage = 2;\n                            else if (currentStepData.bakingProgress >= 100) {\n                                clearInterval(bakingInterval);\n                                currentStepData.stage = 3;\n                                stepScores[3] = 10;\n                                playSound('oven_door_sound');\n                                actionButton.textContent = 'Next';\n                                actionButton.disabled = false;\n                                actionButton.onclick = () => {\n                                    playSound('step_complete_sound');\n                                    nextStep();\n                                };\n                            }\n                        }, 1000);\n                    }\n                };\n            }\n\n            function renderStep4() {\n                drawBackground();\n                const ovenX = canvas.width / 2 - 250;\n                const ovenY = canvas.height / 2 - 220;\n                const panX = canvas.width / 2;\n                const panY = canvas.height / 2 + 150;\n                const panW = 460;\n                const panD = 140;\n                const panH = 55;\n\n                if (currentStepData.baking) {\n                    drawImage('oven_closed', ovenX, ovenY, 500, null);\n                    const windowX = ovenX + 120;\n                    const windowY = ovenY + 200;\n                    if (currentStepData.stage === 0) { ctx.fillStyle = 'rgba(245, 222, 179, 0.6)'; ctx.fillRect(windowX, windowY, 260, 180); }\n                    else if (currentStepData.stage === 1) { ctx.fillStyle = 'rgba(210, 180, 140, 0.6)'; ctx.fillRect(windowX, windowY, 260, 180); }\n                    else if (currentStepData.stage >= 2) { ctx.fillStyle = 'rgba(184, 134, 11, 0.6)'; ctx.fillRect(windowX, windowY, 260, 180); }\n                    // Keep the oven unobstructed; the bake state is visible through the oven window.\n                    \n                    if (currentStepData.bakingProgress >= 100) {\n                        drawTutorialArrow(canvas.width / 2, canvas.height - 130, 0);\n                    }\n                } else {\n                    drawImage('oven_open', ovenX, ovenY, 500, null);\n                    draw3DPan(panX, panY, panW, panD, panH, 1.0, currentStepData.stage);\n                    // Point to the \"Start Baking\" button\n                    drawTutorialArrow(canvas.width / 2, canvas.height - 130, 0);\n                }\n            }\n\n            // Step 5: Cooling\n            function initStep5() {\n                currentStepData = { coolingProgress: 0, steamParticles: [] };\n                stepContent.innerHTML = '';\n                actionButton.classList.add('hidden');\n                const coolingInterval = setInterval(() => {\n                    currentStepData.coolingProgress += 100 / 5;\n                    if (currentStepData.coolingProgress >= 100) {\n                        clearInterval(coolingInterval);\n                        stepScores[4] = 10;\n                        actionButton.classList.remove('hidden');\n                        actionButton.textContent = 'Next';\n                        actionButton.disabled = false;\n                        actionButton.onclick = () => {\n                            playSound('step_complete_sound');\n                            nextStep();\n                        };\n                    }\n                }, 1000);\n            }\n            function renderStep5() {\n                drawBackground();\n                \n                const rackX = canvas.width / 2 - 200;\n                const rackY = canvas.height / 2 + 100;\n                drawImage('cooling_rack', rackX, rackY, 400, null);\n                \n                const cakeX = canvas.width / 2 - 175;\n                const cakeY = rackY - 30;\n                drawImage('cake_layer_bottom', cakeX, cakeY - 60, 350, null);\n                \n                if (currentStepData.coolingProgress < 95) {\n                    const alpha = Math.max(0, (95 - currentStepData.coolingProgress) / 95);\n                    if (Math.random() < 0.3 * alpha) {\n                        currentStepData.steamParticles.push({\n                            x: canvas.width / 2 + (Math.random() - 0.5) * 200,\n                            y: cakeY - 60,\n                            vx: (Math.random() - 0.5) * 0.5,\n                            vy: -1 - Math.random() * 1.5,\n                            size: 15 + Math.random() * 25,\n                            life: 1.0,\n                            drift: Math.random() * Math.PI * 2\n                        });\n                    }\n\n                    ctx.save();\n                    for (let i = currentStepData.steamParticles.length - 1; i >= 0; i--) {\n                        const p = currentStepData.steamParticles[i];\n                        p.x += p.vx + Math.sin(p.drift + Date.now() / 500) * 0.5;\n                        p.y += p.vy;\n                        p.life -= 0.008;\n                        p.size += 0.2;\n                        \n                        if (p.life <= 0) {\n                            currentStepData.steamParticles.splice(i, 1);\n                            continue;\n                        }\n\n                        const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size);\n                        grad.addColorStop(0, `rgba(255, 255, 255, ${0.3 * p.life * alpha})`);\n                        grad.addColorStop(0.5, `rgba(245, 245, 255, ${0.1 * p.life * alpha})`);\n                        grad.addColorStop(1, 'rgba(255, 255, 255, 0)');\n                        \n                        ctx.fillStyle = grad;\n                        ctx.beginPath();\n                        ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);\n                        ctx.fill();\n                    }\n                    ctx.restore();\n                }\n                \n                // Cooling progress stays unobtrusive; the cake and steam communicate the scene.\n            }\n\n            // Step 6: Placing Cake on Turntable\n            function initStep6() {\n                currentStepData = { placingProgress: 0, placing: false };\n                stepContent.innerHTML = '';\n                \n                actionButton.textContent = 'Take Out Cake';\n                actionButton.disabled = false;\n                actionButton.onclick = () => {\n                    currentStepData.placing = true;\n                    actionButton.disabled = true;\n                    playSound('select_sound');\n                    \n                    const placeInterval = setInterval(() => {\n                        currentStepData.placingProgress += 4;\n                        if (currentStepData.placingProgress >= 100) {\n                            clearInterval(placeInterval);\n                            stepScores[5] = 10;\n                            actionButton.textContent = 'Next';\n                            actionButton.disabled = false;\n                            actionButton.onclick = () => {\n                                playSound('step_complete_sound');\n                                nextStep();\n                            };\n                        }\n                    }, 40);\n                };\n            }\n\n            function renderStep6() {\n                drawBackground();\n                const centerX = canvas.width / 2;\n                const turntableY = canvas.height / 2 + 150;\n                const rackY = canvas.height / 2 + 100;\n                \n                if (!currentStepData.placing) {\n                    drawImage('cooling_rack', centerX - 200, rackY, 400, null);\n                    drawImage('cake_layer_bottom', centerX - 175, rackY - 90, 350, null);\n                    // Point to the \"Take Out Cake\" button\n                    drawTutorialArrow(centerX, canvas.height - 130, 0);\n                } else {\n                    const progress = currentStepData.placingProgress / 100;\n                    drawTurntable(centerX, turntableY);\n                    \n                    ctx.save();\n                    ctx.globalAlpha = Math.max(0, 1 - progress * 1.5);\n                    drawImage('cooling_rack', centerX - 200, rackY, 400, null);\n                    ctx.restore();\n                    \n                    const startY = rackY - 20;\n                    const targetY = turntableY;\n                    const cakeBottomY = startY + (targetY - startY) * progress - (Math.sin(progress * Math.PI) * 120);\n                    \n                    ctx.save();\n                    drawImage('cake_layer_bottom', centerX - 175, cakeBottomY - 200, 350, 200);\n                    ctx.restore();\n                    \n                    if (progress >= 1) {\n                        drawTutorialArrow(centerX, canvas.height - 130, 0);\n                    }\n                }\n            }\n\n            // Step 7: Whip Cream\n            function initStep7() {\n                currentStepData = { \n                    whippingProgress: 0, \n                    lastX: canvas.width / 2,\n                    lastY: canvas.height / 2,\n                    mixerX: canvas.width / 2,\n                    mixerY: canvas.height / 2 - 100,\n                    whipping: false,\n                    mixerSound: null,\n                    creamPoured: 0\n                };\n                stepContent.innerHTML = '';\n                \n                const pourInterval = setInterval(() => {\n                    currentStepData.creamPoured += 0.05;\n                    if (currentStepData.creamPoured >= 1) {\n                        currentStepData.creamPoured = 1;\n                        clearInterval(pourInterval);\n                    }\n                }, 50);\n\n                const startWhipping = (e) => {\n                    if (currentStepData.creamPoured < 1) return;\n                    e.preventDefault();\n                    currentStepData.whipping = true;\n                    const pos = getInputPosition(e);\n                    currentStepData.lastX = pos.x;\n                    currentStepData.lastY = pos.y;\n                    if (!currentStepData.mixerSound) {\n                        currentStepData.mixerSound = playSound('mixer_sound', 0.6, true);\n                    }\n                };\n\n                const doWhipping = (e) => {\n                    const pos = getInputPosition(e);\n                    currentStepData.mixerX = pos.x;\n                    currentStepData.mixerY = pos.y;\n                    if (!currentStepData.whipping) return;\n                    e.preventDefault();\n                    const dx = pos.x - currentStepData.lastX;\n                    const dy = pos.y - currentStepData.lastY;\n                    const distance = Math.sqrt(dx * dx + dy * dy);\n                    if (distance > 5) {\n                        currentStepData.whippingProgress = Math.min(100, currentStepData.whippingProgress + distance / 25);\n                        currentStepData.lastX = pos.x;\n                        currentStepData.lastY = pos.y;\n                    }\n                };\n\n                const stopWhipping = () => { \n                    currentStepData.whipping = false;\n                    if (currentStepData.mixerSound) {\n                        currentStepData.mixerSound.source.stop();\n                        currentStepData.mixerSound = null;\n                    }\n                };\n\n                canvas.addEventListener('mousedown', startWhipping);\n                canvas.addEventListener('touchstart', startWhipping);\n                canvas.addEventListener('mousemove', doWhipping);\n                canvas.addEventListener('touchmove', doWhipping);\n                canvas.addEventListener('mouseup', stopWhipping);\n                canvas.addEventListener('touchend', stopWhipping);\n\n                actionButton.textContent = 'Next';\n                actionButton.disabled = true;\n                actionButton.onclick = () => {\n                    if (currentStepData.whippingProgress >= 100) {\n                        playSound('step_complete_sound');\n                        stepScores[6] = 10;\n                        nextStep();\n                    }\n                };\n            }\n\n            function updateStep7() {\n                if (currentStepData.whippingProgress >= 100) {\n                    actionButton.disabled = false;\n                }\n            }\n\n            function renderStep7() {\n                drawBackground();\n                const bowlWidth = 350;\n                const bowlX = canvas.width / 2 - bowlWidth / 2;\n                const bowlY = canvas.height / 2 - 100;\n                const centerX = canvas.width / 2;\n                const centerY = bowlY + 180;\n                const radiusX = 130;\n                const radiusY = 50;\n\n                drawImage('mixing_bowl_large', bowlX, bowlY, bowlWidth, null);\n\n                if (currentStepData.creamPoured < 1) {\n                    ctx.save();\n                    ctx.fillStyle = '#FFFFFF';\n                    ctx.globalAlpha = 0.8;\n                    const streamW = 40;\n                    ctx.fillRect(centerX - streamW/2, bowlY - 100, streamW, 250);\n                    ctx.restore();\n                    if (Math.floor(Date.now() / 150) % 2 === 0) playSound('pour_liquid_sound', 0.1);\n                }\n\n                if (currentStepData.creamPoured > 0) {\n                    ctx.save();\n                    ctx.beginPath();\n                    ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);\n                    ctx.clip();\n                    \n                    const pourProgress = currentStepData.creamPoured;\n                    const whipProgress = currentStepData.whippingProgress / 100;\n                    const baseHeight = pourProgress * 20;\n                    const extraHeight = whipProgress * 40;\n                    const creamY = centerY + 10 - baseHeight - extraHeight;\n                    \n                    ctx.fillStyle = '#FFFFFF';\n                    ctx.beginPath();\n                    ctx.ellipse(centerX, creamY, radiusX, radiusY + (whipProgress * 25), 0, 0, Math.PI * 2);\n                    ctx.fill();\n                    \n                    if (whipProgress > 0.05) {\n                        ctx.strokeStyle = '#F0F0F0';\n                        ctx.lineWidth = 2;\n                        ctx.globalAlpha = whipProgress;\n                        for(let i = 0; i < 6; i++) {\n                            ctx.beginPath();\n                            const swirl = (Date.now() / 400 + i * Math.PI / 3);\n                            const rx = radiusX * (0.3 + 0.5 * whipProgress);\n                            const ry = radiusY * (0.3 + 0.5 * whipProgress);\n                            ctx.ellipse(centerX, creamY, rx, ry, swirl, 0, Math.PI * 0.4);\n                            ctx.stroke();\n                        }\n                    }\n                    ctx.restore();\n                }\n\n                if (currentStepData.creamPoured >= 1) {\n                    const mixerWidth = 220;\n                    const shake = currentStepData.whipping ? Math.sin(Date.now() / 20) * 8 : 0;\n                    drawImage('electric_mixer', currentStepData.mixerX - mixerWidth / 2 + shake, currentStepData.mixerY - mixerWidth / 2 + shake, mixerWidth, null);\n                }\n\n                // Whipping progress is communicated by the cream's changing texture and Next button.\n\n                if (currentStepData.creamPoured < 1) {\n                    // Waiting for pour\n                } else if (currentStepData.whippingProgress < 100) {\n                    const time = Date.now() / 300;\n                    const mixX = canvas.width / 2 + Math.cos(time) * 100;\n                    const mixY = bowlY + 180 + Math.sin(time) * 40;\n                    drawTutorialArrow(mixX, mixY, 0);\n                } else {\n                    drawTutorialArrow(canvas.width / 2, canvas.height - 130, 0);\n                }\n            }\n\n            // Step 8: Coat with Cream\n            function initStep8() {\n                currentStepData = { \n                    spreadingProgress: 0,\n                    rotation: 0,\n                    isRotating: false,\n                    lastAngle: 0,\n                    spreadingSound: null\n                };\n                stepContent.innerHTML = '';\n                \n                const handleStart = (e) => {\n                    const pos = getInputPosition(e);\n                    currentStepData.isRotating = true;\n                    currentStepData.lastX = pos.x;\n                    if (!currentStepData.spreadingSound) {\n                        currentStepData.spreadingSound = playSound('spreading_sound', 0.4, true);\n                    }\n                };\n\n                const handleMove = (e) => {\n                    if (!currentStepData.isRotating) return;\n                    const pos = getInputPosition(e);\n                    const deltaX = pos.x - currentStepData.lastX;\n                    const deltaAngle = deltaX * 0.01;\n                    \n                    currentStepData.rotation += deltaAngle;\n                    currentStepData.lastX = pos.x;\n                    \n                    if (currentStepData.spreadingProgress < 100) {\n                        currentStepData.spreadingProgress = Math.min(100, currentStepData.spreadingProgress + Math.abs(deltaAngle) * 5);\n                    }\n                };\n\n                const handleEnd = () => {\n                    currentStepData.isRotating = false;\n                    if (currentStepData.spreadingSound) {\n                        currentStepData.spreadingSound.source.stop();\n                        currentStepData.spreadingSound = null;\n                    }\n                };\n\n                canvas.addEventListener('mousedown', handleStart);\n                canvas.addEventListener('mousemove', handleMove);\n                canvas.addEventListener('mouseup', handleEnd);\n                canvas.addEventListener('touchstart', handleStart);\n                canvas.addEventListener('touchmove', handleMove);\n                canvas.addEventListener('touchend', handleEnd);\n\n                actionButton.textContent = 'Next';\n                actionButton.disabled = true;\n                actionButton.onclick = () => {\n                    if (currentStepData.spreadingProgress >= 100) {\n                        playSound('step_complete_sound');\n                        stepScores[7] = 10;\n                        nextStep();\n                    }\n                };\n            }\n\n            function renderStep8() {\n                drawBackground();\n                const centerX = canvas.width / 2;\n                const turntableY = canvas.height / 2 + 150;\n                \n                drawTurntable(centerX, turntableY, currentStepData.rotation);\n                \n                const progress = currentStepData.spreadingProgress / 100;\n                const cakeY = turntableY;\n                \n                if (progress < 1) {\n                    drawImage('cake_layer_bottom', centerX - 175, cakeY - 200, 350, 200);\n                    ctx.save();\n                    ctx.globalAlpha = progress;\n                    drawChristmasCake(centerX - 175, cakeY - 200, 350, 200);\n                    ctx.restore();\n                    \n                    const spatulaX = centerX + 110 + Math.sin(Date.now() / 200) * 10;\n                    const spatulaY = cakeY - 170; \n                    ctx.save();\n                    ctx.translate(spatulaX, spatulaY);\n                    ctx.rotate(-0.2 + (currentStepData.isRotating ? Math.sin(Date.now() / 50) * 0.05 : 0));\n                    drawImage('offset_spatula', -75, -40, 150, null);\n                    ctx.restore();\n\n                    if (!currentStepData.isRotating) {\n                        drawTutorialArrow(centerX + 150, turntableY - 100, Math.PI / 2);\n                    }\n                } else {\n                    drawChristmasCake(centerX - 175, cakeY - 200, 350, 200);\n                    drawTutorialArrow(canvas.width / 2, canvas.height - 130, 0);\n                    actionButton.disabled = false;\n                }\n                // The frosting spread is visible directly on the cake, so no separate bar is needed.\n            }\n            function initStep9() {\n                currentStepData = {\n                    selectedTopping: 'christmas_tree',\n                    toppingPage: 0,\n                    rotation: 0,\n                    isRotating: false,\n                    isDraggingDeco: false,\n                    draggedDecoIndex: -1,\n                    selectedDecoIndex: -1,\n                    lastX: 0,\n                    dragStartTime: 0,\n                    dragStartPos: { x: 0, y: 0 },\n                    zoom: 1.2,\n                    toppings: [\n                        { id: 'christmas_tree', name: 'Christmas Tree', icon: '\ud83c\udf84' },\n                        { id: 'candy_cane', name: 'Candy Cane', icon: '\ud83c\udf6d' },\n                        { id: 'snowflake', name: 'Snowflake', icon: '\u2744\ufe0f' },\n                        { id: 'christmas_bauble', name: 'Festive Bauble', icon: '\ud83d\udd34' },\n                        { id: 'golden_star', name: 'Golden Star', icon: '\u2b50' },\n                        { id: 'christmas_wreath', name: 'Mini Wreath', icon: '\ud83d\udc9a' },\n                        { id: 'mint_leaf', name: 'Holly Leaves', asset: 'mint_leaf' },\n                        { id: 'strawberry_whole', name: 'Cranberries', asset: 'strawberry_whole' },\n                        { id: 'orange_slice', name: 'Citrus', asset: 'orange_slice' },\n                        { id: 'blueberry', name: 'Winter Berries', asset: 'blueberry' },\n                        { id: 'sugar_pearl_white', name: 'Snow Pearls', asset: 'sugar_pearl_white' },\n                        { id: 'chocolate_shaving', name: 'Chocolate Curls', asset: 'chocolate_shaving' },\n                        { id: 'chocolate_cream', name: 'Chocolate Cream', asset: 'chocolate_cream' },\n                        { id: 'cream_dollop', name: 'Cream Swirl', asset: 'cream_dollop' },\n                        { id: 'bear_cookie', name: 'Gingerbread Bear', asset: 'bear_cookie' },\n                        { id: 'birthday_candle_unlit', name: 'Holiday Candle', asset: 'birthday_candle_unlit' }\n                    ]\n                };\n                \n                // Convert old decos if any (legacy compatibility)\n                if (cakeData.decorations && cakeData.decorations.length > 0 && cakeData.decorations[0].relX !== undefined) {\n                    cakeData.decorations = cakeData.decorations.map(d => {\n                        const angle = Math.atan2(d.relX, 175); // rough estimate\n                        return { angle: angle, y: d.relY, type: d.type };\n                    });\n                }\n                \n                stepContent.innerHTML = '';\n                const toppingList = document.createElement('div');\n                toppingList.className = 'ingredient-list';\n                toppingList.style.position = 'absolute';\n                toppingList.style.top = '238px';\n                toppingList.style.left = '0';\n                toppingList.style.zIndex = '105';\n                toppingList.style.justifyContent = 'center';\n                toppingList.style.overflowX = 'hidden';\n\n                const itemsPerPage = 4;\n                const pageCount = Math.ceil(currentStepData.toppings.length / itemsPerPage);\n                const pagination = document.createElement('div');\n                pagination.className = 'topping-pagination';\n                const previousPageButton = document.createElement('button');\n                previousPageButton.className = 'topping-page-button';\n                previousPageButton.type = 'button';\n                previousPageButton.textContent = '\u2039 Previous';\n                const pageLabel = document.createElement('div');\n                pageLabel.className = 'topping-page-count';\n                pageLabel.setAttribute('aria-live', 'polite');\n                const nextPageButton = document.createElement('button');\n                nextPageButton.className = 'topping-page-button';\n                nextPageButton.type = 'button';\n                nextPageButton.textContent = 'Next \u203a';\n\n                function renderToppingPage() {\n                    toppingList.innerHTML = '';\n                    const start = currentStepData.toppingPage * itemsPerPage;\n                    currentStepData.toppings.slice(start, start + itemsPerPage).forEach(topping => {\n                        const item = document.createElement('div');\n                        item.className = `topping-item ${currentStepData.selectedTopping === topping.id ? 'selected' : ''}`;\n                        item.id = `topping-${topping.id}`;\n\n                        const asset = topping.asset ? lib.getAsset(topping.asset) : null;\n                        item.innerHTML = topping.icon\n                            ? `<span class=\"topping-icon-emoji\" aria-hidden=\"true\">${topping.icon}</span><span class=\"topping-name\">${topping.name}</span>`\n                            : `<img src=\"${asset ? asset.url : ''}\" class=\"topping-icon\" alt=\"\"><span class=\"topping-name\">${topping.name}</span>`;\n\n                        item.onclick = (e) => {\n                            e.stopPropagation();\n                            currentStepData.selectedTopping = topping.id;\n                            toppingList.querySelectorAll('.topping-item').forEach(el => el.classList.remove('selected'));\n                            item.classList.add('selected');\n                            playSound('select_sound');\n                        };\n                        toppingList.appendChild(item);\n                    });\n                    pageLabel.textContent = `Decorations ${currentStepData.toppingPage + 1} of ${pageCount}`;\n                    previousPageButton.disabled = currentStepData.toppingPage === 0;\n                    nextPageButton.disabled = currentStepData.toppingPage >= pageCount - 1;\n                }\n\n                previousPageButton.onclick = () => {\n                    if (currentStepData.toppingPage > 0) {\n                        currentStepData.toppingPage--;\n                        renderToppingPage();\n                        playSound('select_sound');\n                    }\n                };\n                nextPageButton.onclick = () => {\n                    if (currentStepData.toppingPage < pageCount - 1) {\n                        currentStepData.toppingPage++;\n                        renderToppingPage();\n                        playSound('select_sound');\n                    }\n                };\n                pagination.append(previousPageButton, pageLabel, nextPageButton);\n                stepContent.append(toppingList, pagination);\n                renderToppingPage();\n\n                const trashBtn = document.createElement('button');\n                trashBtn.id = 'trash-button';\n                trashBtn.innerHTML = '\ud83d\uddd1\ufe0f';\n                trashBtn.style.position = 'absolute';\n                trashBtn.style.bottom = '180px';\n                trashBtn.style.right = '40px';\n                trashBtn.style.width = '100px';\n                trashBtn.style.height = '100px';\n                trashBtn.style.fontSize = '48px';\n                trashBtn.style.borderRadius = '50%';\n                trashBtn.style.background = '#FF6B6B';\n                trashBtn.style.color = 'white';\n                trashBtn.style.border = '4px solid white';\n                trashBtn.style.boxShadow = '0 6px 12px rgba(0,0,0,0.3)';\n                trashBtn.style.zIndex = '110';\n                trashBtn.style.display = 'flex';\n                trashBtn.style.alignItems = 'center';\n                trashBtn.style.justifyContent = 'center';\n                trashBtn.style.transition = 'transform 0.2s, opacity 0.2s';\n                trashBtn.style.opacity = '0.5';\n                trashBtn.style.pointerEvents = 'none';\n\n                trashBtn.onclick = (e) => {\n                    e.stopPropagation();\n                    if (currentStepData.selectedDecoIndex !== -1) {\n                        cakeData.decorations.splice(currentStepData.selectedDecoIndex, 1);\n                        currentStepData.selectedDecoIndex = -1;\n                        trashBtn.style.opacity = '0.5';\n                        trashBtn.style.pointerEvents = 'none';\n                        trashBtn.style.transform = 'scale(1)';\n                        playSound('select_sound', 0.5);\n                    }\n                };\n                stepContent.appendChild(trashBtn);\n                \n                actionButton.textContent = 'Finish Decorating';\n                actionButton.disabled = false;\n                actionButton.onclick = () => {\n                    stepScores[8] = 10;\n                    playSound('step_complete_sound');\n                    nextStep();\n                };\n                \n                const handleStart = (e) => {\n                    const pos = getInputPosition(e);\n                    const centerX = canvas.width / 2;\n                    const zoom = currentStepData.zoom;\n                    const turntableY = canvas.height / 2 + 150;\n                    const radius = 175 * zoom;\n                    const radiusY = radius * 0.25; \n                    const cakeTopY = turntableY - 200 * zoom;\n                    \n                    currentStepData.dragStartTime = Date.now();\n                    currentStepData.dragStartPos = { x: pos.x, y: pos.y };\n                    currentStepData.lastX = pos.x;\n                    currentStepData.hasMoved = false;\n\n                    // Detect clicking existing toppings (3D)\n                    let foundIndex = -1;\n                    if (cakeData.decorations) {\n                        for (let i = cakeData.decorations.length - 1; i >= 0; i--) {\n                            const deco = cakeData.decorations[i];\n                            const effAngle = deco.angle + currentStepData.rotation;\n                            const z = Math.cos(effAngle);\n                            if (z < 0) continue; \n                            \n                            const rx = centerX + radius * Math.sin(effAngle);\n                            const ry = cakeTopY + deco.y * zoom + (radiusY * z);\n                            const distToDeco = Math.sqrt((pos.x - rx) ** 2 + (pos.y - ry) ** 2);\n                            if (distToDeco < 50 * zoom) {\n                                foundIndex = i;\n                                break;\n                            }\n                        }\n                    }\n\n                    if (foundIndex !== -1) {\n                        currentStepData.selectedDecoIndex = foundIndex;\n                        currentStepData.isDraggingDeco = true; // \u65b0\u589e\u62d6\u62fd\u72c0\u614b\n                        const trashBtn = document.getElementById('trash-button');\n                        if (trashBtn) {\n                            trashBtn.style.opacity = '1';\n                            trashBtn.style.pointerEvents = 'auto';\n                            trashBtn.style.transform = 'scale(1.1)';\n                        }\n                    } else {\n                        currentStepData.selectedDecoIndex = -1;\n                        currentStepData.isDraggingDeco = false;\n                        const trashBtn = document.getElementById('trash-button');\n                        if (trashBtn) {\n                            trashBtn.style.opacity = '0.5';\n                            trashBtn.style.pointerEvents = 'none';\n                            trashBtn.style.transform = 'scale(1)';\n                        }\n                    }\n                    \n                    currentStepData.isRotating = true;\n                };\n\n                const handleMove = (e) => {\n                    const pos = getInputPosition(e);\n                    const centerX = canvas.width / 2;\n                    const zoom = currentStepData.zoom;\n                    const turntableY = canvas.height / 2 + 150;\n                    const radius = 175 * zoom;\n                    const radiusY = radius * 0.25;\n                    const cakeTopY = turntableY - 200 * zoom;\n\n                    const distMoved = Math.sqrt(Math.pow(pos.x - currentStepData.dragStartPos.x, 2) + Math.pow(pos.y - currentStepData.dragStartPos.y, 2));\n                    if (distMoved > 10) currentStepData.hasMoved = true;\n\n                    if (currentStepData.isDraggingDeco && currentStepData.selectedDecoIndex !== -1) {\n                        // Update selected topping position\n                        const dx = pos.x - centerX;\n                        const dy = pos.y - cakeTopY;\n                        \n                        const clampedRatio = Math.max(-1, Math.min(1, dx / radius));\n                        const effAngleOnScreen = Math.asin(clampedRatio);\n                        const z = Math.cos(effAngleOnScreen);\n                        \n                        const deco = cakeData.decorations[currentStepData.selectedDecoIndex];\n                        deco.angle = effAngleOnScreen - currentStepData.rotation;\n                        deco.y = (dy - radiusY * z) / zoom;\n                        \n                        // Limit height range\n                        deco.y = Math.max(-20, Math.min(180, deco.y));\n                    }\n                    \n                    currentStepData.lastX = pos.x;\n                };\n\n                const handleEnd = (e) => {\n                    const pos = getInputPosition(e);\n                    const centerX = canvas.width / 2;\n                    const zoom = currentStepData.zoom;\n                    const turntableY = canvas.height / 2 + 150;\n                    const radius = 175 * zoom;\n                    const radiusY = radius * 0.25;\n                    const cakeTopY = turntableY - 200 * zoom;\n                    \n                    if (!currentStepData.hasMoved && !currentStepData.isDraggingDeco) {\n                        const dx = (pos.x - centerX);\n                        const dy = (pos.y - cakeTopY);\n                        const isOnSide = Math.abs(dx) <= radius && dy >= -20 && dy <= 200 * zoom;\n\n                        if (isOnSide && currentStepData.selectedDecoIndex === -1) {\n                            playSound('place_decoration_sound');\n                            const clampedRatio = Math.max(-1, Math.min(1, dx / radius));\n                            const effAngleOnScreen = Math.asin(clampedRatio);\n                            const actualAngle = effAngleOnScreen - currentStepData.rotation;\n                            const z = Math.cos(effAngleOnScreen);\n                            const relativeY = (dy - radiusY * z) / zoom;\n                            \n                            cakeData.decorations.push({\n                                angle: actualAngle,\n                                y: relativeY,\n                                type: currentStepData.selectedTopping\n                            });\n                        }\n                    }\n\n                    currentStepData.isDraggingDeco = false;\n                    currentStepData.isRotating = false;\n                };\n\n                canvas.addEventListener('mousedown', handleStart);\n                canvas.addEventListener('mousemove', handleMove);\n                canvas.addEventListener('mouseup', handleEnd);\n                canvas.addEventListener('touchstart', handleStart);\n                canvas.addEventListener('touchmove', handleMove);\n                canvas.addEventListener('touchend', handleEnd);\n            }\n\n            function renderStep9() {\n                drawBackground();\n                const centerX = canvas.width / 2;\n                const zoom = currentStepData.zoom;\n                const turntableY = canvas.height / 2 + 150;\n                const radius = 175 * zoom;\n                const radiusY = radius * 0.25;\n                const cakeTopY = turntableY - 200 * zoom;\n                \n                drawTurntable(centerX, turntableY, currentStepData.rotation, zoom);\n                drawChristmasCake(centerX - 175 * zoom, cakeTopY, 350 * zoom, 200 * zoom);\n\n                // Render Decorations with depth sorting and perspective\n                if (cakeData.decorations) {\n                    cakeData.decorations.forEach((deco, index) => {\n                        const effAngle = deco.angle + currentStepData.rotation;\n                        const z = Math.cos(effAngle);\n                        if (z > 0) { // Front side only\n                            const rx = centerX + radius * Math.sin(effAngle);\n                            const ry = cakeTopY + deco.y * zoom + (radiusY * z);\n                            \n                            const isSelected = index === currentStepData.selectedDecoIndex;\n                            if (isSelected) {\n                                ctx.save();\n                                ctx.shadowBlur = 15;\n                                ctx.shadowColor = '#FFB6C1';\n                                ctx.strokeStyle = 'white';\n                                ctx.lineWidth = 3;\n                                ctx.beginPath();\n                                ctx.arc(rx, ry, 30 * zoom, 0, Math.PI * 2);\n                                ctx.stroke();\n                                ctx.restore();\n                            }\n                            let size = 50 * zoom;\n                            if (deco.type === 'happy_birthday_topper' || deco.type === 'kuromi_topper' || deco.type === 'little_twin_stars_topper') size *= 1.6;\n                            drawCakeDecoration(deco.type, rx - size / 2, ry - size / 2, size);\n                        }\n                    });\n                }\n                if (cakeData.decorations.length < 1) {\n                    drawTutorialArrow(centerX, cakeTopY + 50 * zoom, 0);\n                }\n            }\n            // Step 10: Complete\n            function initStep10() {\n                currentStepData = { \n                    completed: true, \n                    rotation: 0, \n                    isRotating: false, \n                    lastX: 0,\n                    zoom: 1.2\n                };\n                stepContent.innerHTML = '';\n                \n                const handleStart = (e) => {\n                    const pos = getInputPosition(e);\n                    currentStepData.isRotating = true;\n                    currentStepData.lastX = pos.x;\n                };\n\n                const handleMove = (e) => {\n                    // The panel stays static after completion, cancel rotation logic\n                };\n\n                const handleEnd = () => {\n                    currentStepData.isRotating = false;\n                };\n\n                canvas.addEventListener('mousedown', handleStart);\n                canvas.addEventListener('mousemove', handleMove);\n                canvas.addEventListener('mouseup', handleEnd);\n                canvas.addEventListener('touchstart', handleStart);\n                canvas.addEventListener('touchmove', handleMove);\n                canvas.addEventListener('touchend', handleEnd);\n\n                actionButton.textContent = 'Admire Cake';\n                actionButton.disabled = false;\n                actionButton.onclick = () => {\n                    actionButton.classList.add('hidden');\n                    playSound('step_complete_sound', 1.2);\n                    stepScores[9] = 10;\n                    \n                    setTimeout(() => {\n                        showFinalScore();\n                    }, 3000);\n                };\n            }\n\n            function renderStep10() {\n                drawBackground();\n                const centerX = canvas.width / 2;\n                const zoom = currentStepData.zoom;\n                const turntableY = canvas.height / 2 + 150;\n                const radius = 175 * zoom;\n                const radiusY = radius * 0.25;\n                const cakeTopY = turntableY - 200 * zoom;\n                \n                drawTurntable(centerX, turntableY, currentStepData.rotation, zoom);\n                drawChristmasCake(centerX - 175 * zoom, cakeTopY, 350 * zoom, 200 * zoom);\n                \n                if (cakeData.decorations) {\n                    cakeData.decorations.forEach(deco => {\n                        const effAngle = deco.angle + currentStepData.rotation;\n                        const z = Math.cos(effAngle);\n                        if (z > 0) {\n                            const rx = centerX + radius * Math.sin(effAngle);\n                            const ry = cakeTopY + deco.y * zoom + (radiusY * z);\n                            let size = 50 * zoom;\n                            if (deco.type === 'happy_birthday_topper' || deco.type === 'kuromi_topper' || deco.type === 'little_twin_stars_topper') size *= 1.6;\n                            drawCakeDecoration(deco.type, rx - size / 2, ry - size / 2, size);\n                        }\n                    });\n                }\n\n                if (cakeData.decorations.length === 0) {\n                    drawTutorialArrow(centerX, canvas.height - 130, 0);\n                }\n            }\n\n            function enterPhotoMode() {\n                const modal = document.querySelector('.modal');\n                const backdrop = document.querySelector('.modal-backdrop');\n                if (modal) modal.remove();\n                if (backdrop) backdrop.remove();\n                photoMode = true;\n                gameWorld.classList.add('photo-mode');\n                photoModePanel.classList.remove('hidden');\n            }\n\n            function exitPhotoMode() {\n                photoMode = false;\n                gameWorld.classList.remove('photo-mode');\n                photoModePanel.classList.add('hidden');\n                showFinalScore();\n            }\n            photoModeClose.addEventListener('click', exitPhotoMode);\n\n            function showFinalScore() {\n                const totalScore = stepScores.reduce((a, b) => a + b, 0);\n                let rating = '';\n                \n                if (totalScore >= 60) rating = 'Master Baker';\n                else if (totalScore >= 45) rating = 'Expert Baker';\n                else if (totalScore >= 30) rating = 'Qualified Baker';\n                else rating = 'Apprentice Baker';\n                \n                const modal = document.createElement('div');\n                modal.className = 'modal';\n                modal.innerHTML = `\n                    <div class=\"modal-title\">\ud83c\udf84 Christmas Cake Complete!</div>\n                    <div class=\"modal-content\">\n                        <div class=\"score-display\">${totalScore.toFixed(0)} Points</div>\n                        <div class=\"rating-display\">${rating}</div>\n                        <p style=\"margin-top: 16px;\">Your festive cake is ready to bring a little Christmas magic to the dessert table!</p>\n                    </div>\n                    <button class=\"action-button photo-studio-button\" type=\"button\">Open Christmas Cake Photo Studio</button>\n                    <button class=\"action-button\" type=\"button\">Bake Again</button>\n                `;\n                \n                const backdrop = document.createElement('div');\n                backdrop.className = 'modal-backdrop';\n                \n                const photoStudioBtn = modal.querySelector('.photo-studio-button');\n                photoStudioBtn.onclick = enterPhotoMode;\n                const restartBtn = modal.querySelector('.action-button:not(.photo-studio-button)');\n                restartBtn.onclick = () => {\n                    restartGame();\n                };\n                \n                gameWorld.appendChild(backdrop);\n                gameWorld.appendChild(modal);\n            }\n\n            function restartGame() {\n                const modal = document.querySelector('.modal');\n                const backdrop = document.querySelector('.modal-backdrop');\n                if (modal) modal.remove();\n                if (backdrop) backdrop.remove();\n\n                currentStep = 0;\n                stepScores = new Array(10).fill(0);\n                cakeData = { decorations: [] };\n                \n                // Restart music\n                playMusic('christmas_background_music', 0.5) || playMusic('background_music', 0.5);\n                \n                cleanupEventListeners();\n                updateUI();\n                initCurrentStep();\n                \n                actionButton.classList.remove('hidden');\n                actionButton.disabled = false;\n            }\n\n            function cleanupEventListeners() {\n                // Remove all event listeners from canvas by cloning and replacing it\n                const oldCanvas = canvas;\n                const newCanvas = oldCanvas.cloneNode(true);\n                oldCanvas.parentNode.replaceChild(newCanvas, oldCanvas);\n                canvas = newCanvas;\n                ctx = canvas.getContext('2d');\n                \n                // Also clear the UI content panel\n                stepContent.innerHTML = '';\n            }\n\n            function nextStep() {\n                currentStep++;\n                if (currentStep < 10) {\n                    cleanupEventListeners();\n                    updateUI();\n                    initCurrentStep();\n                }\n            }\n\n            function initCurrentStep() {\n                // Ensure audio context is resumed on any interaction start\n                if (audioContext.state === 'suspended') {\n                    const resumeAudio = () => {\n                        audioContext.resume();\n                        document.removeEventListener('mousedown', resumeAudio);\n                        document.removeEventListener('touchstart', resumeAudio);\n                    };\n                    document.addEventListener('mousedown', resumeAudio);\n                    document.addEventListener('touchstart', resumeAudio);\n                }\n\n                const stepInits = [\n                    initStep1, initStep2, initStep3, initStep4, initStep5,\n                    initStep6, initStep7, initStep8, initStep9, initStep10\n                ];\n                \n                if (stepInits[currentStep]) {\n                    stepInits[currentStep]();\n                }\n            }\n\n            function getInputPosition(e) {\n                const rect = canvas.getBoundingClientRect();\n                let clientX, clientY;\n                \n                if (e.touches && e.touches.length > 0) {\n                    clientX = e.touches[0].clientX;\n                    clientY = e.touches[0].clientY;\n                } else if (e.changedTouches && e.changedTouches.length > 0) {\n                    clientX = e.changedTouches[0].clientX;\n                    clientY = e.changedTouches[0].clientY;\n                } else {\n                    clientX = e.clientX;\n                    clientY = e.clientY;\n                }\n                \n                const x = (clientX || 0) - rect.left;\n                const y = (clientY || 0) - rect.top;\n                return { x, y };\n            }\n\n            let lastTime = 0;\n            function gameLoop(timestamp) {\n                const deltaTime = Math.min((timestamp - lastTime) / 1000, 0.11);\n                lastTime = timestamp;\n\n                ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n                if (mode === 'edit') {\n                    drawBackground();\n                    ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';\n                    ctx.fillRect(canvas.width / 2 - 250, canvas.height / 2 - 100, 500, 200);\n                    ctx.fillStyle = '#333';\n                    ctx.font = '40px \"Noto Sans TC\", sans-serif';\n                    ctx.textAlign = 'center';\n                    ctx.fillText('Edit Mode', canvas.width / 2, canvas.height / 2);\n                    ctx.font = '32px \"Noto Sans TC\", sans-serif';\n                    ctx.fillText('Adjust settings in the right panel', canvas.width / 2, canvas.height / 2 + 50);\n                    ctx.fillText(`Kitchen: ${window.gameConfig.kitchenStyle.includes('japanese') ? 'Japanese' : 'Modern'}`, canvas.width / 2, canvas.height / 2 + 80);\n                } else {\n                    const stepUpdates = [\n                        updateStep1, updateStep2, () => {}, () => {}, () => {},\n                        () => {}, updateStep7, () => {}, () => {}, () => {}\n                    ];\n                    \n                    const stepRenders = [\n                        renderStep1, renderStep2, renderStep3, renderStep4, renderStep5,\n                        renderStep6, renderStep7, renderStep8, renderStep9, renderStep10\n                    ];\n                    \n                    if (stepUpdates[currentStep]) {\n                        stepUpdates[currentStep](deltaTime);\n                    }\n                    \n                    if (stepRenders[currentStep]) {\n                        stepRenders[currentStep]();\n                    }\n                }\n\n                drawLuxuryFrame();\n                requestAnimationFrame(gameLoop);\n            }\n\n            if (mode === 'play') {\n                backgroundMusic = playMusic('christmas_background_music', 0.5) || playMusic('background_music', 0.5);\n                updateUI();\n                initCurrentStep();\n            }\n\n            requestAnimationFrame(gameLoop);\n        }\n    </script>\n</body>\n</html>", "game_config": "{\n  \"cakeFlavor\": \"vanilla\",\n  \"cakeSize\": 6,\n  \"decorationTheme\": \"christmas\",\n  \"showHints\": true,\n  \"soundVolume\": 50,\n  \"musicVolume\": 30,\n  \"language\": \"en-US\",\n  \"kitchenStyle\": \"christmas\",\n  \"timeOfDay\": \"morning\",\n  \"ingredients\": {\n    \"flour\": {\n      \"target\": 200,\n      \"tolerance\": 5,\n      \"current\": 0\n    },\n    \"sugar\": {\n      \"target\": 150,\n      \"tolerance\": 5,\n      \"current\": 0\n    },\n    \"eggs\": {\n      \"target\": 3,\n      \"tolerance\": 0,\n      \"current\": 0\n    },\n    \"milk\": {\n      \"target\": 100,\n      \"tolerance\": 5,\n      \"current\": 0\n    },\n    \"butter\": {\n      \"target\": 100,\n      \"tolerance\": 5,\n      \"current\": 0\n    },\n    \"bakingPowder\": {\n      \"target\": 10,\n      \"tolerance\": 2,\n      \"current\": 0\n    }\n  }\n}", "game_design_document": "## Core Game Loop and Mechanics\n\n**\u6696\u5fc3\u86cb\u7cd5\u65e5\u8a18**\u662f\u4e00\u6b3e\u6b65\u9a5f\u5f0f\u70d8\u7119\u6a21\u64ec\u904a\u6232\uff0c\u73a9\u5bb6\u5c07\u9ad4\u9a57\u88fd\u4f5c\u751f\u65e5\u86cb\u7cd5\u7684\u5b8c\u6574\u6d41\u7a0b\u3002\u904a\u6232\u63a1\u7528\u6eab\u99a8\u6cbb\u7652\u7684\u98a8\u683c\uff0c\u6c92\u6709\u6642\u9593\u58d3\u529b\uff0c\u8b93\u73a9\u5bb6\u53ef\u4ee5\u653e\u9b06\u5730\u4eab\u53d7\u70d8\u7119\u904e\u7a0b\u3002\n\n### \u904a\u6232\u6d41\u7a0b\n\n\u73a9\u5bb6\u5c07\u4f9d\u5e8f\u5b8c\u6210\u4ee5\u4e0b\u6b65\u9a5f\uff1a\n\n1. **\u6e96\u5099\u6750\u6599\u968e\u6bb5**\n   - \u73a9\u5bb6\u9700\u8981\u5f9e\u5eda\u623f\u6ac3\u5b50\u4e2d\u9078\u64c7\u6b63\u78ba\u7684\u6750\u6599\uff08\u9eb5\u7c89\u3001\u7cd6\u3001\u96de\u86cb\u3001\u725b\u5976\u3001\u5976\u6cb9\u3001\u6ce1\u6253\u7c89\uff09\n   - \u4f7f\u7528\u96fb\u5b50\u79e4\u79e4\u91cf\u6bcf\u7a2e\u6750\u6599\u5230\u6b63\u78ba\u7684\u91cd\u91cf\n   - \u6750\u6599\u6703\u986f\u793a\u76ee\u6a19\u91cd\u91cf\u548c\u7576\u524d\u91cd\u91cf\uff0c\u73a9\u5bb6\u9700\u8981\u9ede\u64ca\u6dfb\u52a0\u6309\u9215\u4f86\u589e\u52a0\u6750\u6599\n   - \u7576\u91cd\u91cf\u5728\u76ee\u6a19\u7bc4\u570d\u5167\uff08\u00b15g\uff09\u6642\u6703\u986f\u793a\u7da0\u8272\u52fe\u9078\u6a19\u8a18\n\n2. **\u6df7\u5408\u9eb5\u7cca\u968e\u6bb5**\n   - \u5c07\u6750\u6599\u4f9d\u5e8f\u52a0\u5165\u652a\u62cc\u7897\u4e2d\n   - \u4f7f\u7528\u624b\u6301\u652a\u62cc\u5668\u652a\u62cc\u9eb5\u7cca\n   - \u73a9\u5bb6\u9700\u8981\u5728\u7897\u4e0a\u4f86\u56de\u6ed1\u52d5\uff08\u79fb\u52d5\u7aef\uff09\u6216\u79fb\u52d5\u6ed1\u9f20\uff08\u684c\u9762\u7aef\uff09\u4f86\u652a\u62cc\n   - \u652a\u62cc\u9032\u5ea6\u689d\u6703\u986f\u793a\u6df7\u5408\u7a0b\u5ea6\uff0c\u9700\u8981\u9054\u5230100%\n   - \u9eb5\u7cca\u7684\u984f\u8272\u548c\u8cea\u5730\u6703\u96a8\u8457\u652a\u62cc\u9010\u6f38\u8b8a\u5316\n\n3. **\u5012\u5165\u6a21\u5177\u968e\u6bb5**\n   - \u5c07\u652a\u62cc\u597d\u7684\u9eb5\u7cca\u5012\u5165\u5713\u5f62\u86cb\u7cd5\u6a21\u5177\n   - \u73a9\u5bb6\u9700\u8981\u50be\u659c\u652a\u62cc\u7897\uff08\u62d6\u52d5\u6216\u50be\u659c\u624b\u52e2\uff09\n   - \u9eb5\u7cca\u6703\u4ee5\u6d41\u9ad4\u52d5\u756b\u7684\u65b9\u5f0f\u6d41\u5165\u6a21\u5177\n   - \u9700\u8981\u5012\u5165\u9069\u91cf\u7684\u9eb5\u7cca\uff08\u7d0480%\u6eff\uff09\n\n4. **\u70d8\u70e4\u968e\u6bb5**\n   - \u5c07\u86cb\u7cd5\u6a21\u5177\u653e\u5165\u70e4\u7bb1\n   - \u8a2d\u5b9a\u6b63\u78ba\u7684\u6eab\u5ea6\uff08180\u00b0C\uff09\u548c\u6642\u9593\uff0830\u5206\u9418\uff09\n   - \u73a9\u5bb6\u53ef\u4ee5\u901a\u904e\u70e4\u7bb1\u7a97\u53e3\u770b\u5230\u86cb\u7cd5\u9010\u6f38\u81a8\u8139\u548c\u8b8a\u8272\n   - \u6709\u6eab\u5ea6\u8a08\u548c\u8a08\u6642\u5668\u986f\u793a\u7576\u524d\u72c0\u614b\n   - \u6642\u9593\u52a0\u901f\u6a5f\u5236\uff1a\u5be6\u969b\u7b49\u5f85\u6642\u9593\u7d0430\u79d2\uff0c\u4f46\u986f\u793a\u70ba30\u5206\u9418\n   - \u86cb\u7cd5\u6703\u7d93\u6b77\uff1a\u751f\u9eb5\u7cca \u2192 \u958b\u59cb\u81a8\u8139 \u2192 \u8868\u9762\u91d1\u9ec3 \u2192 \u5b8c\u6210\n\n5. **\u51b7\u537b\u968e\u6bb5**\n   - \u5c07\u70e4\u597d\u7684\u86cb\u7cd5\u5f9e\u70e4\u7bb1\u53d6\u51fa\n   - \u653e\u5728\u51b7\u537b\u67b6\u4e0a\n   - \u86cb\u7cd5\u6703\u5192\u51fa\u71b1\u6c23\uff0c\u6eab\u5ea6\u8a08\u986f\u793a\u6eab\u5ea6\u9010\u6f38\u4e0b\u964d\n   - \u7b49\u5f85\u7d0415\u79d2\uff08\u904a\u6232\u6642\u959310\u5206\u9418\uff09\n\n6. **\u5207\u7247\u968e\u6bb5**\n   - \u4f7f\u7528\u9577\u5200\u5c07\u86cb\u7cd5\u6a6b\u5207\u6210\u5169\u5c64\n   - \u73a9\u5bb6\u9700\u8981\u6cbf\u8457\u4e2d\u9593\u7684\u865b\u7dda\u5f15\u5c0e\u7dda\u6c34\u5e73\u6ed1\u52d5\u5200\u5b50\n   - \u5207\u5f97\u8d8a\u5e73\u6574\uff0c\u8a55\u5206\u8d8a\u9ad8\n\n7. **\u6253\u767c\u9bae\u5976\u6cb9\u968e\u6bb5**\n   - \u5c07\u9bae\u5976\u6cb9\u5012\u5165\u652a\u62cc\u7897\n   - \u4f7f\u7528\u96fb\u52d5\u652a\u62cc\u5668\u6253\u767c\n   - \u73a9\u5bb6\u9700\u8981\u6301\u7e8c\u652a\u62cc\uff08\u5713\u5468\u904b\u52d5\uff09\n   - \u5976\u6cb9\u6703\u7d93\u6b77\uff1a\u6db2\u614b \u2192 \u958b\u59cb\u8b8a\u7a20 \u2192 \u5f62\u6210\u8edf\u6027\u767c\u6ce1 \u2192 \u786c\u6027\u767c\u6ce1\n   - \u9032\u5ea6\u689d\u986f\u793a\u6253\u767c\u7a0b\u5ea6\n\n8. **\u7d44\u88dd\u86cb\u7cd5\u968e\u6bb5**\n   - \u5728\u5e95\u5c64\u86cb\u7cd5\u4e0a\u5857\u62b9\u9bae\u5976\u6cb9\n   - \u73a9\u5bb6\u4f7f\u7528\u62b9\u5200\uff08\u62d6\u52d5\u624b\u52e2\uff09\u5c07\u5976\u6cb9\u5747\u52fb\u5857\u62b9\n   - \u653e\u4e0a\u7b2c\u4e8c\u5c64\u86cb\u7cd5\n   - \u5728\u6574\u500b\u86cb\u7cd5\u5916\u5c64\u5857\u62b9\u9bae\u5976\u6cb9\n   - \u9700\u8981\u5857\u62b9\u9802\u90e8\u548c\u5074\u9762\n\n9. **\u88dd\u98fe\u968e\u6bb5**\n   - \u9078\u64c7\u88dd\u98fe\u5143\u7d20\uff1a\u8349\u8393\u3001\u85cd\u8393\u3001\u5de7\u514b\u529b\u7247\u3001\u7cd6\u73e0\u3001\u881f\u71ed\n   - \u9ede\u64ca\u6216\u62d6\u52d5\u5c07\u88dd\u98fe\u7269\u653e\u7f6e\u5728\u86cb\u7cd5\u4e0a\n   - \u53ef\u4ee5\u81ea\u7531\u5275\u4f5c\uff0c\u6c92\u6709\u56fa\u5b9a\u8981\u6c42\n   - \u6700\u5f8c\u63d2\u4e0a\u751f\u65e5\u881f\u71ed\n\n10. **\u5b8c\u6210\u6176\u795d**\n    - \u5c55\u793a\u5b8c\u6210\u7684\u86cb\u7cd5\n    - \u9ede\u71c3\u881f\u71ed\uff0c\u64ad\u653e\u751f\u65e5\u5feb\u6a02\u97f3\u6a02\n    - \u986f\u793a\u88fd\u4f5c\u8a55\u5206\uff08\u57fa\u65bc\u5404\u6b65\u9a5f\u7684\u5b8c\u6210\u5ea6\uff09\n    - \u53ef\u4ee5\u62cd\u7167\u4fdd\u5b58\u6216\u91cd\u65b0\u88fd\u4f5c\n\n### \u904a\u6232\u7279\u8272\n\n- **\u771f\u5be6\u6b65\u9a5f**\uff1a\u5b8c\u5168\u6309\u7167\u73fe\u5be6\u4e16\u754c\u7684\u70d8\u7119\u6d41\u7a0b\u8a2d\u8a08\n- **\u8996\u89ba\u56de\u994b**\uff1a\u6bcf\u500b\u6b65\u9a5f\u90fd\u6709\u6e05\u6670\u7684\u8996\u89ba\u8b8a\u5316\u548c\u9032\u5ea6\u63d0\u793a\n- **\u7121\u58d3\u529b\u9ad4\u9a57**\uff1a\u6c92\u6709\u6642\u9593\u9650\u5236\uff0c\u73a9\u5bb6\u53ef\u4ee5\u6309\u81ea\u5df1\u7684\u7bc0\u594f\u9032\u884c\n- **\u6559\u80b2\u610f\u7fa9**\uff1a\u73a9\u5bb6\u53ef\u4ee5\u5b78\u7fd2\u5230\u771f\u5be6\u7684\u70d8\u7119\u77e5\u8b58\n- **\u6210\u5c31\u611f**\uff1a\u5b8c\u6210\u5f8c\u53ef\u4ee5\u770b\u5230\u81ea\u5df1\u89aa\u624b\u88fd\u4f5c\u7684\u7cbe\u7f8e\u86cb\u7cd5\n\n### Win/Lose Conditions\n\n\u9019\u662f\u4e00\u6b3e\u6c92\u6709\u5931\u6557\u7684\u904a\u6232\uff0c\u91cd\u9ede\u5728\u65bc\u9ad4\u9a57\u904e\u7a0b\uff1a\n- **\u5b8c\u6210\u689d\u4ef6**\uff1a\u5b8c\u6210\u6240\u670910\u500b\u6b65\u9a5f\n- **\u8a55\u5206\u7cfb\u7d71**\uff1a\n  - \u6750\u6599\u79e4\u91cf\u6e96\u78ba\u5ea6\uff08\u6bcf\u7a2e\u6750\u65990-10\u5206\uff09\n  - \u652a\u62cc\u5b8c\u6210\u5ea6\uff080-10\u5206\uff09\n  - \u70d8\u70e4\u6eab\u5ea6\u548c\u6642\u9593\u6e96\u78ba\u5ea6\uff080-10\u5206\uff09\n  - \u5207\u7247\u5e73\u6574\u5ea6\uff080-10\u5206\uff09\n  - \u9bae\u5976\u6cb9\u6253\u767c\u7a0b\u5ea6\uff080-10\u5206\uff09\n  - \u88dd\u98fe\u5275\u610f\uff080-10\u5206\uff09\n  - \u7e3d\u5206\uff1a0-70\u5206\n- **\u8a55\u7d1a**\uff1a\n  - 60-70\u5206\uff1a\u5b8c\u7f8e\u86cb\u7cd5\u5e2b\n  - 45-59\u5206\uff1a\u512a\u79c0\u86cb\u7cd5\u5e2b\n  - 30-44\u5206\uff1a\u5408\u683c\u86cb\u7cd5\u5e2b\n  - 0-29\u5206\uff1a\u65b0\u624b\u86cb\u7cd5\u5e2b\n\n## Starting Scope\n\n\u7b2c\u4e00\u7248\u5be6\u73fe\u5c07\u5305\u542b\u5b8c\u6574\u768410\u500b\u6b65\u9a5f\uff0c\u4f46\u6703\u7c21\u5316\u67d0\u4e9b\u7d30\u7bc0\uff1a\n\n**\u7b2c\u4e00\u7248\u5305\u542b\uff1a**\n- \u6240\u670910\u500b\u6838\u5fc3\u6b65\u9a5f\u7684\u57fa\u672c\u5be6\u73fe\n- \u6e05\u6670\u7684UI\u5f15\u5c0e\u548c\u9032\u5ea6\u63d0\u793a\n- \u57fa\u672c\u7684\u8996\u89ba\u56de\u994b\u548c\u52d5\u756b\n- \u7c21\u55ae\u7684\u8a55\u5206\u7cfb\u7d71\n- \u5b8c\u6210\u6176\u795d\u756b\u9762\n\n**\u7b2c\u4e00\u7248\u7c21\u5316\uff1a**\n- \u88dd\u98fe\u9078\u9805\u9650\u5236\u57285-6\u7a2e\u57fa\u672c\u5143\u7d20\n- \u53ea\u88fd\u4f5c\u4e00\u7a2e\u53e3\u5473\u7684\u86cb\u7cd5\uff08\u9999\u8349\u86cb\u7cd5\uff09\n- \u7c21\u5316\u7684\u7269\u7406\u6a21\u64ec\uff08\u9eb5\u7cca\u6d41\u52d5\u3001\u5976\u6cb9\u5857\u62b9\uff09\n\n**\u672a\u4f86\u53ef\u64f4\u5c55\uff1a**\n- \u66f4\u591a\u86cb\u7cd5\u53e3\u5473\uff08\u5de7\u514b\u529b\u3001\u62b9\u8336\u3001\u7d05\u7d72\u7d68\uff09\n- \u66f4\u591a\u88dd\u98fe\u9078\u9805\u548c\u4e3b\u984c\n- \u4e0d\u540c\u5f62\u72c0\u7684\u86cb\u7cd5\uff08\u65b9\u5f62\u3001\u5fc3\u5f62\uff09\n- \u591a\u5c64\u86cb\u7cd5\uff083\u5c64\u6216\u66f4\u591a\uff09\n- \u66f4\u8907\u96dc\u7684\u88dd\u98fe\u6280\u5de7\uff08\u88f1\u82b1\u3001\u7cd6\u971c\u7e6a\u756b\uff09\n- \u6210\u5c31\u7cfb\u7d71\u548c\u6536\u85cf\u518a\n- \u5206\u4eab\u529f\u80fd\n\n## Game Perspective\n\n\u904a\u6232\u63a1\u7528**\u56fa\u5b9a\u8996\u89d2\uff08fixed-screen\uff09**\uff0c\u6a21\u64ec\u73a9\u5bb6\u7ad9\u5728\u5eda\u623f\u5de5\u4f5c\u53f0\u524d\u7684\u7b2c\u4e00\u4eba\u7a31\u8996\u89d2\u3002\n\n- \u6bcf\u500b\u6b65\u9a5f\u90fd\u6709\u5c08\u9580\u7684\u5834\u666f\u8996\u5716\n- \u76f8\u6a5f\u4f4d\u7f6e\u56fa\u5b9a\uff0c\u5c55\u793a\u7576\u524d\u64cd\u4f5c\u5340\u57df\n- \u6240\u6709\u4e92\u52d5\u5143\u7d20\u90fd\u5728\u53ef\u898b\u7bc4\u570d\u5167\n- UI\u5143\u7d20\u56fa\u5b9a\u5728\u87a2\u5e55\u908a\u7de3\uff08\u9802\u90e8\u986f\u793a\u6b65\u9a5f\u9032\u5ea6\uff0c\u5e95\u90e8\u986f\u793a\u64cd\u4f5c\u63d0\u793a\uff09\n\n## Game World and Level Design\n\n\u904a\u6232\u8a2d\u5b9a\u5728\u4e00\u500b\u6eab\u99a8\u7684\u5bb6\u5ead\u5eda\u623f\u4e2d\uff0c\u6574\u500b\u904a\u6232\u5c31\u662f\u4e00\u500b\u5b8c\u6574\u7684\u95dc\u5361\uff08\u88fd\u4f5c\u4e00\u500b\u86cb\u7cd5\uff09\u3002\n\n### \u5834\u666f\u8a2d\u8a08\n\n**\u5eda\u623f\u74b0\u5883\uff1a**\n- \u6eab\u6696\u7684\u6728\u8cea\u5de5\u4f5c\u53f0\n- \u80cc\u666f\u6709\u6ae5\u6ac3\u3001\u70e4\u7bb1\u3001\u51b0\u7bb1\u7b49\u5eda\u623f\u8a2d\u5099\n- \u67d4\u548c\u7684\u81ea\u7136\u5149\u5f9e\u7a97\u6236\u7167\u5165\n- \u7246\u4e0a\u6709\u88dd\u98fe\u756b\u548c\u6642\u9418\n- \u6574\u9ad4\u8272\u8abf\u6eab\u6696\u8212\u9069\uff08\u7c73\u8272\u3001\u6dfa\u6728\u8272\u3001\u67d4\u548c\u7684\u767d\u8272\uff09\n\n**\u5404\u6b65\u9a5f\u5834\u666f\uff1a**\n\n1. **\u6e96\u5099\u6750\u6599\u5834\u666f**\uff1a\u5de5\u4f5c\u53f0\u4e0a\u6709\u96fb\u5b50\u79e4\uff0c\u80cc\u666f\u6709\u6253\u958b\u7684\u6ae5\u6ac3\u986f\u793a\u5404\u7a2e\u6750\u6599\n2. **\u6df7\u5408\u9eb5\u7cca\u5834\u666f**\uff1a\u5de5\u4f5c\u53f0\u4e2d\u592e\u6709\u5927\u652a\u62cc\u7897\uff0c\u65c1\u908a\u6709\u96fb\u52d5\u652a\u62cc\u5668\n3. **\u5012\u5165\u6a21\u5177\u5834\u666f**\uff1a\u5de5\u4f5c\u53f0\u4e0a\u6709\u86cb\u7cd5\u6a21\u5177\uff0c\u652a\u62cc\u7897\u50be\u659c\u5012\u5165\n4. **\u70d8\u70e4\u5834\u666f**\uff1a\u805a\u7126\u5728\u70e4\u7bb1\uff0c\u53ef\u4ee5\u900f\u904e\u73bb\u7483\u7a97\u770b\u5230\u5167\u90e8\n5. **\u51b7\u537b\u5834\u666f**\uff1a\u5de5\u4f5c\u53f0\u4e0a\u6709\u51b7\u537b\u67b6\uff0c\u86cb\u7cd5\u5728\u4e0a\u9762\u5192\u71b1\u6c23\n6. **\u5207\u7247\u5834\u666f**\uff1a\u5de5\u4f5c\u53f0\u4e0a\u6709\u86cb\u7cd5\u548c\u9577\u5200\uff0c\u6709\u5207\u5272\u5f15\u5c0e\u7dda\n7. **\u6253\u767c\u9bae\u5976\u6cb9\u5834\u666f**\uff1a\u5de5\u4f5c\u53f0\u4e0a\u6709\u652a\u62cc\u7897\u548c\u96fb\u52d5\u652a\u62cc\u5668\n8. **\u7d44\u88dd\u5834\u666f**\uff1a\u5de5\u4f5c\u53f0\u4e0a\u6709\u86cb\u7cd5\u5c64\u548c\u62b9\u5200\uff0c\u65c1\u908a\u6709\u9bae\u5976\u6cb9\u7897\n9. **\u88dd\u98fe\u5834\u666f**\uff1a\u5de5\u4f5c\u53f0\u4e0a\u6709\u5b8c\u6574\u86cb\u7cd5\uff0c\u5468\u570d\u6709\u5404\u7a2e\u88dd\u98fe\u6750\u6599\n10. **\u5b8c\u6210\u5834\u666f**\uff1a\u5c55\u793a\u5b8c\u6210\u7684\u86cb\u7cd5\uff0c\u80cc\u666f\u6709\u6176\u795d\u5143\u7d20\uff08\u6c23\u7403\u3001\u5f69\u5e36\uff09\n\n### \u5834\u666f\u8f49\u63db\n\n- \u6bcf\u5b8c\u6210\u4e00\u500b\u6b65\u9a5f\uff0c\u6703\u6709\u7c21\u77ed\u7684\u8f49\u5834\u52d5\u756b\uff08\u6de1\u5165\u6de1\u51fa\uff09\n- \u8f49\u5834\u6642\u986f\u793a\u4e0b\u4e00\u6b65\u9a5f\u7684\u540d\u7a31\u548c\u7c21\u77ed\u8aaa\u660e\n- \u4fdd\u6301\u6d41\u66a2\u7684\u904a\u6232\u9ad4\u9a57\uff0c\u8f49\u5834\u6642\u9593\u7d041\u79d2\n\n## Edit Mode Design\n\nEdit Mode\u8b93\u73a9\u5bb6\u53ef\u4ee5\u81ea\u8a02\u904a\u6232\u7684\u67d0\u4e9b\u5143\u7d20\uff0c\u589e\u52a0\u500b\u4eba\u5316\u9ad4\u9a57\u3002\n\n### \u53ef\u7de8\u8f2f\u5c6c\u6027\n\n**\u86cb\u7cd5\u5ba2\u88fd\u5316\uff1a**\n- **\u86cb\u7cd5\u53e3\u5473**\uff1a\u9999\u8349\uff08\u9810\u8a2d\uff09\u3001\u5de7\u514b\u529b\u3001\u8349\u8393\u3001\u62b9\u8336\n  - \u5f71\u97ff\u86cb\u7cd5\u984f\u8272\u548c\u67d0\u4e9b\u6750\u6599\n- **\u86cb\u7cd5\u5c3a\u5bf8**\uff1a6\u540b\uff08\u9810\u8a2d\uff09\u30018\u540b\u300110\u540b\n  - \u5f71\u97ff\u6750\u6599\u7528\u91cf\u548c\u70d8\u70e4\u6642\u9593\n- **\u88dd\u98fe\u4e3b\u984c**\uff1a\u7d93\u5178\uff08\u9810\u8a2d\uff09\u3001\u5f69\u8679\u3001\u512a\u96c5\u3001\u7ae5\u8da3\n  - \u5f71\u97ff\u53ef\u7528\u7684\u88dd\u98fe\u5143\u7d20\u548c\u984f\u8272\u65b9\u6848\n\n**\u904a\u6232\u8a2d\u5b9a\uff1a**\n- **\u986f\u793a\u63d0\u793a**\uff1a\u958b\u555f\uff08\u9810\u8a2d\uff09/\u95dc\u9589\n  - \u63a7\u5236\u662f\u5426\u986f\u793a\u64cd\u4f5c\u63d0\u793a\u548c\u5f15\u5c0e\u7dda\n- **\u97f3\u6548\u97f3\u91cf**\uff1a0-100%\uff08\u9810\u8a2d50%\uff09\n- **\u97f3\u6a02\u97f3\u91cf**\uff1a0-100%\uff08\u9810\u8a2d30%\uff09\n- **\u8a9e\u8a00**\uff1a\u7e41\u9ad4\u4e2d\u6587\uff08\u9810\u8a2d\uff09\u3001\u82f1\u6587\n\n**\u5eda\u623f\u74b0\u5883\uff1a**\n- **\u5eda\u623f\u98a8\u683c**\uff1a\u73fe\u4ee3\uff08\u9810\u8a2d\uff09\u3001\u9109\u6751\u3001\u5de5\u696d\u98a8\u3001\u65e5\u5f0f\n  - \u6539\u8b8a\u80cc\u666f\u548c\u5de5\u4f5c\u53f0\u7684\u8996\u89ba\u98a8\u683c\n- **\u6642\u9593**\uff1a\u65e9\u6668\uff08\u9810\u8a2d\uff09\u3001\u4e0b\u5348\u3001\u508d\u665a\n  - \u5f71\u97ff\u5149\u7dda\u548c\u7a97\u5916\u666f\u8272\n\n### Edit Mode UI\n\n- Edit Mode\u901a\u904e\u4e3b\u9078\u55ae\u9032\u5165\n- \u4f7f\u7528\u5206\u9801\u5f0f\u4ecb\u9762\u7d44\u7e54\u4e0d\u540c\u985e\u5225\u7684\u8a2d\u5b9a\n- \u6bcf\u500b\u9078\u9805\u90fd\u6709\u9810\u89bd\u5716\u793a\n- \u66f4\u6539\u6703\u5373\u6642\u53cd\u6620\u5728\u904a\u6232\u4e2d\n- \u6709\u300c\u91cd\u7f6e\u70ba\u9810\u8a2d\u300d\u6309\u9215\n\n### \u9650\u5236\n\n- \u6838\u5fc3\u904a\u6232\u6d41\u7a0b\u4e0d\u53ef\u66f4\u6539\uff0810\u500b\u6b65\u9a5f\u9806\u5e8f\u56fa\u5b9a\uff09\n- \u6750\u6599\u6bd4\u4f8b\u7531\u7cfb\u7d71\u81ea\u52d5\u8abf\u6574\uff0c\u78ba\u4fdd\u904a\u6232\u5e73\u8861\n- \u67d0\u4e9b\u9032\u968e\u9078\u9805\u9700\u8981\u5b8c\u6210\u904a\u6232\u4e00\u6b21\u5f8c\u89e3\u9396\n\n## Control Design\n\n\u904a\u6232\u63a1\u7528\u7c21\u55ae\u76f4\u89c0\u7684\u9ede\u64ca/\u89f8\u63a7\u64cd\u4f5c\uff0c\u9069\u5408\u4f11\u9592\u73a9\u5bb6\u3002\n\n### \u79fb\u52d5\u7aef\u63a7\u5236\uff08\u89f8\u63a7\uff09\n\n**\u57fa\u672c\u64cd\u4f5c\uff1a**\n- **\u9ede\u64ca**\uff1a\u9078\u64c7\u7269\u54c1\u3001\u78ba\u8a8d\u64cd\u4f5c\u3001\u9ede\u64ca\u6309\u9215\n- **\u62d6\u52d5**\uff1a\u79fb\u52d5\u7269\u54c1\u3001\u5857\u62b9\u5976\u6cb9\u3001\u88dd\u98fe\u86cb\u7cd5\n- **\u9577\u6309**\uff1a\u6301\u7e8c\u64cd\u4f5c\uff08\u5982\u6301\u7e8c\u6dfb\u52a0\u6750\u6599\uff09\n- **\u6ed1\u52d5**\uff1a\u652a\u62cc\u52d5\u4f5c\u3001\u5207\u86cb\u7cd5\n\n**\u5177\u9ad4\u6b65\u9a5f\u64cd\u4f5c\uff1a**\n\n1. **\u6e96\u5099\u6750\u6599**\uff1a\u9ede\u64ca\u6750\u6599\u9078\u64c7\uff0c\u9577\u6309\u300c\u6dfb\u52a0\u300d\u6309\u9215\u589e\u52a0\u91cd\u91cf\n2. **\u6df7\u5408\u9eb5\u7cca**\uff1a\u5728\u7897\u4e0a\u4f86\u56de\u6ed1\u52d5\u624b\u6307\u9032\u884c\u652a\u62cc\n3. **\u5012\u5165\u6a21\u5177**\uff1a\u62d6\u52d5\u652a\u62cc\u7897\u50be\u659c\u5012\u51fa\u9eb5\u7cca\n4. **\u70d8\u70e4**\uff1a\u9ede\u64ca\u6eab\u5ea6\u548c\u6642\u9593\u6309\u9215\u8abf\u6574\uff0c\u9ede\u64ca\u300c\u958b\u59cb\u70d8\u70e4\u300d\n5. **\u51b7\u537b**\uff1a\u7121\u9700\u64cd\u4f5c\uff0c\u81ea\u52d5\u9032\u884c\n6. **\u5207\u7247**\uff1a\u6cbf\u8457\u5f15\u5c0e\u7dda\u6c34\u5e73\u6ed1\u52d5\u624b\u6307\n7. **\u6253\u767c\u9bae\u5976\u6cb9**\uff1a\u5728\u7897\u4e0a\u756b\u5713\u5708\u652a\u62cc\n8. **\u7d44\u88dd**\uff1a\u62d6\u52d5\u62b9\u5200\u5857\u62b9\u5976\u6cb9\n9. **\u88dd\u98fe**\uff1a\u9ede\u64ca\u9078\u64c7\u88dd\u98fe\u7269\uff0c\u9ede\u64ca\u86cb\u7cd5\u653e\u7f6e\n10. **\u5b8c\u6210**\uff1a\u9ede\u64ca\u881f\u71ed\u9ede\u71c3\n\n### \u684c\u9762\u7aef\u63a7\u5236\uff08\u6ed1\u9f20+\u9375\u76e4\uff09\n\n**\u57fa\u672c\u64cd\u4f5c\uff1a**\n- **\u5de6\u9375\u9ede\u64ca**\uff1a\u9078\u64c7\u7269\u54c1\u3001\u78ba\u8a8d\u64cd\u4f5c\n- **\u5de6\u9375\u62d6\u52d5**\uff1a\u79fb\u52d5\u7269\u54c1\u3001\u5857\u62b9\u3001\u88dd\u98fe\n- **\u6ed1\u9f20\u79fb\u52d5**\uff1a\u652a\u62cc\u3001\u5207\u5272\u52d5\u4f5c\n- **\u6efe\u8f2a**\uff1a\u8abf\u6574\u6578\u503c\uff08\u53ef\u9078\uff09\n\n**\u9375\u76e4\u5feb\u6377\u9375\uff1a**\n- **Space**\uff1a\u78ba\u8a8d/\u7e7c\u7e8c\u4e0b\u4e00\u6b65\n- **ESC**\uff1a\u6253\u958b\u9078\u55ae\n- **\u6578\u5b57\u93751-5**\uff1a\u5feb\u901f\u9078\u64c7\u88dd\u98fe\u7269\uff08\u5728\u88dd\u98fe\u968e\u6bb5\uff09\n\n### UI\u5143\u7d20\n\n**\u56fa\u5b9aUI\uff08\u59cb\u7d42\u986f\u793a\uff09\uff1a**\n- **\u9802\u90e8**\uff1a\n  - \u7576\u524d\u6b65\u9a5f\u540d\u7a31\u548c\u7de8\u865f\uff08\u5982\u300c\u6b65\u9a5f3/10\uff1a\u5012\u5165\u6a21\u5177\u300d\uff09\n  - \u9032\u5ea6\u689d\u986f\u793a\u6574\u9ad4\u5b8c\u6210\u5ea6\n- **\u53f3\u4e0a\u89d2**\uff1a\u9078\u55ae\u6309\u9215\uff08\u66ab\u505c\u3001\u8a2d\u5b9a\u3001\u91cd\u65b0\u958b\u59cb\uff09\n- **\u5e95\u90e8**\uff1a\u7576\u524d\u64cd\u4f5c\u63d0\u793a\u6587\u5b57\n\n**\u4e92\u52d5\u5143\u7d20\uff1a**\n- \u6240\u6709\u53ef\u4e92\u52d5\u7269\u54c1\u6709\u9ad8\u4eae\u908a\u6846\u6216\u767c\u5149\u6548\u679c\n- \u6309\u9215\u6700\u5c0f\u5c3a\u5bf896x96\u50cf\u7d20\uff08\u9069\u5408\u89f8\u63a7\uff09\n- \u91cd\u8981\u6309\u9215\u653e\u5728\u87a2\u5e55\u4e0b\u534a\u90e8\uff08\u62c7\u6307\u53cb\u597d\u5340\u57df\uff09\n\n### \u56de\u994b\u6a5f\u5236\n\n- **\u8996\u89ba\u56de\u994b**\uff1a\n  - \u9ede\u64ca\u6642\u6709\u6309\u9215\u6309\u4e0b\u52d5\u756b\n  - \u5b8c\u6210\u6b65\u9a5f\u6642\u6709\u52fe\u9078\u6a19\u8a18\u548c\u7c92\u5b50\u6548\u679c\n  - \u932f\u8aa4\u64cd\u4f5c\u6642\u7269\u54c1\u8f15\u5fae\u9707\u52d5\n  \n- **\u97f3\u6548\u56de\u994b**\uff1a\n  - \u9ede\u64ca\u97f3\u6548\n  - \u5b8c\u6210\u6b65\u9a5f\u7684\u6210\u529f\u97f3\u6548\n  - \u5404\u7a2e\u64cd\u4f5c\u7684\u64ec\u771f\u97f3\u6548\uff08\u652a\u62cc\u8072\u3001\u70e4\u7bb1\u8072\u7b49\uff09\n\n- **\u89f8\u89ba\u56de\u994b**\uff08\u79fb\u52d5\u7aef\uff09\uff1a\n  - \u91cd\u8981\u64cd\u4f5c\u6642\u7684\u8f15\u5fae\u9707\u52d5\n\n## Art Style and Visual Theme\n\n### \u6574\u9ad4\u98a8\u683c\n\n**\u6eab\u99a8\u6cbb\u7652\u7684\u63d2\u756b\u98a8\u683c**\n- \u67d4\u548c\u7684\u8272\u5f69\u548c\u5713\u6f64\u7684\u9020\u578b\n- \u985e\u4f3c\u624b\u7e6a\u6c34\u5f69\u7684\u8cea\u611f\n- \u7d30\u7bc0\u8c50\u5bcc\u4f46\u4e0d\u904e\u65bc\u8907\u96dc\n- \u6eab\u6696\u3001\u8212\u9069\u3001\u4ee4\u4eba\u653e\u9b06\u7684\u8996\u89ba\u9ad4\u9a57\n\n### \u8272\u5f69\u65b9\u6848\n\n**\u4e3b\u8272\u8abf\uff1a**\n- \u6eab\u6696\u7684\u7c73\u8272\u548c\u5976\u6cb9\u8272\uff08#F5E6D3, #FFF8E7\uff09\n- \u67d4\u548c\u7684\u7c89\u8272\u7cfb\uff08#FFE4E1, #FFC0CB\uff09\n- \u6dfa\u6728\u8272\uff08#D2B48C, #DEB887\uff09\n- \u67d4\u548c\u7684\u767d\u8272\uff08#FFFAF0\uff09\n\n**\u9ede\u7db4\u8272\uff1a**\n- \u8349\u8393\u7d05\uff08#FF6B6B\uff09\n- \u85cd\u8393\u85cd\uff08#4A90E2\uff09\n- \u62b9\u8336\u7da0\uff08#A8D5BA\uff09\n- \u5de7\u514b\u529b\u68d5\uff08#8B4513\uff09\n\n### \u8996\u89ba\u5143\u7d20\u8a2d\u8a08\n\n**\u5eda\u623f\u74b0\u5883\uff1a**\n- \u6728\u8cea\u7d0b\u7406\u7684\u5de5\u4f5c\u53f0\u548c\u6ae5\u6ac3\n- \u67d4\u548c\u7684\u9670\u5f71\u548c\u5149\u7dda\u6548\u679c\n- \u7a97\u6236\u900f\u9032\u7684\u81ea\u7136\u5149\n- \u80cc\u666f\u6709\u8f15\u5fae\u7684\u666f\u6df1\u6a21\u7cca\n\n**\u98df\u6750\u548c\u5de5\u5177\uff1a**\n- \u5beb\u5be6\u4f46\u7565\u5e36\u5361\u901a\u5316\u7684\u98a8\u683c\n- \u6e05\u6670\u7684\u8f2a\u5ed3\u7dda\n- \u9069\u5ea6\u7684\u9ad8\u5149\u548c\u9670\u5f71\n- \u6750\u8cea\u611f\u660e\u986f\uff08\u91d1\u5c6c\u3001\u9676\u74f7\u3001\u98df\u7269\uff09\n\n**\u86cb\u7cd5\u548c\u88dd\u98fe\uff1a**\n- \u9bae\u5976\u6cb9\u6709\u5149\u6fa4\u548c\u8cea\u611f\n- \u6c34\u679c\u8272\u5f69\u9bae\u8c54\u98fd\u6eff\n- \u86cb\u7cd5\u9ad4\u6709\u7d30\u81a9\u7684\u8cea\u5730\n- \u88dd\u98fe\u5143\u7d20\u7cbe\u7dfb\u53ef\u611b\n\n**UI\u5143\u7d20\uff1a**\n- \u5713\u89d2\u77e9\u5f62\u6309\u9215\n- \u67d4\u548c\u7684\u9670\u5f71\u548c\u6f38\u5c64\n- \u6e05\u6670\u6613\u8b80\u7684\u5b57\u9ad4\uff08\u4e2d\u6587\u4f7f\u7528\u5713\u9ad4\uff09\n- \u5716\u793a\u7c21\u6f54\u660e\u77ad\n\n### \u52d5\u756b\u98a8\u683c\n\n- \u6d41\u66a2\u7684\u7de9\u52d5\u52d5\u756b\uff08ease-in-out\uff09\n- \u7269\u54c1\u79fb\u52d5\u6709\u8f15\u5fae\u7684\u5f48\u6027\u6548\u679c\n- \u7c92\u5b50\u6548\u679c\u67d4\u548c\uff08\u661f\u661f\u3001\u611b\u5fc3\u3001\u9583\u5149\uff09\n- \u904e\u6e21\u52d5\u756b\u81ea\u7136\u4e0d\u7a81\u5140\n\n### \u8cc7\u7522\u5c3a\u5bf8\u53c3\u8003\n\n- **\u80cc\u666f\u5834\u666f**\uff1a720x1280\uff08\u5168\u87a2\u5e55\uff09\n- **\u5927\u578b\u7269\u54c1**\uff08\u652a\u62cc\u7897\u3001\u86cb\u7cd5\uff09\uff1a300-400\u50cf\u7d20\u5bec\n- **\u4e2d\u578b\u7269\u54c1**\uff08\u5de5\u5177\u3001\u6750\u6599\uff09\uff1a150-250\u50cf\u7d20\u5bec\n- **\u5c0f\u578b\u88dd\u98fe**\uff08\u6c34\u679c\u3001\u7cd6\u73e0\uff09\uff1a50-100\u50cf\u7d20\u5bec\n- **UI\u6309\u9215**\uff1a\u6700\u5c0f96x96\u50cf\u7d20\n\n### \u4e00\u81f4\u6027\u8981\u6c42\n\n- \u6240\u6709\u8cc7\u7522\u4f7f\u7528\u76f8\u540c\u7684\u5149\u6e90\u65b9\u5411\uff08\u5de6\u4e0a\u65b9\uff09\n- \u7d71\u4e00\u7684\u8f2a\u5ed3\u7dda\u7c97\u7d30\uff082-3\u50cf\u7d20\uff09\n- \u4e00\u81f4\u7684\u9670\u5f71\u98a8\u683c\uff08\u67d4\u548c\u3001\u7565\u5e36\u900f\u660e\uff09\n- \u6240\u6709\u98df\u7269\u8cc7\u7522\u4fdd\u6301\u76f8\u4f3c\u7684\u98fd\u548c\u5ea6\u548c\u4eae\u5ea6\n- \u900f\u660e\u80cc\u666f\uff08PNG\u683c\u5f0f\uff09\u7528\u65bc\u6240\u6709\u975e\u80cc\u666f\u8cc7\u7522\n\n## Audio Design\n\n### \u97f3\u6a02\n\n**\u80cc\u666f\u97f3\u6a02\uff1a**\n- \u8f15\u67d4\u7684\u92fc\u7434\u65cb\u5f8b\u914d\u5408\u5f26\u6a02\n- \u7bc0\u594f\u8212\u7de9\uff0860-80 BPM\uff09\n- \u53ef\u7121\u7e2b\u5faa\u74b0\n- \u71df\u9020\u6eab\u99a8\u3001\u653e\u9b06\u7684\u6c1b\u570d\n- \u97f3\u91cf\u8f03\u4f4e\uff0c\u4e0d\u5e72\u64fe\u904a\u6232\u9ad4\u9a57\n\n**\u7279\u6b8a\u97f3\u6a02\uff1a**\n- \u5b8c\u6210\u6176\u795d\u6642\u64ad\u653e\u300c\u751f\u65e5\u5feb\u6a02\u6b4c\u300d\u7684\u8f15\u5feb\u7248\u672c\n\n### \u97f3\u6548\n\n**\u64cd\u4f5c\u97f3\u6548\uff1a**\n- \u9ede\u64ca\u6309\u9215\uff1a\u6e05\u8106\u7684\u300c\u5494\u300d\u8072\n- \u9078\u64c7\u7269\u54c1\uff1a\u67d4\u548c\u7684\u300c\u53ee\u300d\u8072\n- \u5b8c\u6210\u6b65\u9a5f\uff1a\u6109\u6085\u7684\u300c\u53ee\u9234\u300d\u8072\u914d\u5408\u4e0a\u5347\u97f3\u968e\n\n**\u6b65\u9a5f\u97f3\u6548\uff1a**\n- \u79e4\u91cf\u6750\u6599\uff1a\u6750\u6599\u5012\u5165\u7684\u8072\u97f3\n- \u652a\u62cc\u9eb5\u7cca\uff1a\u96fb\u52d5\u652a\u62cc\u5668\u7684\u55e1\u55e1\u8072\n- \u5012\u5165\u6a21\u5177\uff1a\u6db2\u9ad4\u6d41\u52d5\u8072\n- \u70e4\u7bb1\uff1a\u70e4\u7bb1\u9580\u958b\u95dc\u8072\u3001\u70d8\u70e4\u6642\u7684\u8f15\u5fae\u55e1\u55e1\u8072\n- \u5207\u86cb\u7cd5\uff1a\u5200\u5207\u904e\u86cb\u7cd5\u7684\u8072\u97f3\n- \u6253\u767c\u9bae\u5976\u6cb9\uff1a\u652a\u62cc\u5668\u8072\u97f3\uff08\u97f3\u8abf\u96a8\u9032\u5ea6\u8b8a\u5316\uff09\n- \u5857\u62b9\u5976\u6cb9\uff1a\u67d4\u548c\u7684\u5857\u62b9\u8072\n- \u653e\u7f6e\u88dd\u98fe\uff1a\u8f15\u67d4\u7684\u300c\u5575\u300d\u8072\n\n**\u74b0\u5883\u97f3\u6548\uff1a**\n- \u5eda\u623f\u80cc\u666f\u7684\u8f15\u5fae\u74b0\u5883\u97f3\uff08\u53ef\u9078\uff09\n- \u6642\u9418\u6ef4\u7b54\u8072\uff08\u975e\u5e38\u8f15\u5fae\uff09\n\n### \u97f3\u6548\u7279\u6027\n\n- \u6240\u6709\u97f3\u6548\u90fd\u7d93\u904e\u67d4\u5316\u8655\u7406\uff0c\u907f\u514d\u523a\u8033\n- \u97f3\u6548\u9577\u5ea6\u63a7\u5236\u57280.5-2\u79d2\n- \u652f\u63f4\u97f3\u6548\u91cd\u758a\uff08\u591a\u500b\u64cd\u4f5c\u540c\u6642\u9032\u884c\uff09\n- \u97f3\u91cf\u5e73\u8861\uff0c\u4e0d\u6703\u4e92\u76f8\u5e72\u64fe", "persistent_storage_config": null, "game_metadata": {"game_id": "01M6E7C4TQJZ89M3HZBV31D3CQ", "title": "Christmas Cake Diary: Winter Patisserie", "description": "Bake and decorate a festive Christmas cake in a cozy winter patisserie, then admire your creation to gentle Christmas music. \ud83c\udf84", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_8c472efc-f418-4777-8c3e-dde25a4a345a.webp", "created_at": 1793626347.0, "updated_at": 1793632463699.1638, "art_style": null, "is_remix": true, "base_game_id": "01M52DVAYAX16ZFWG8MX54JZ30"}, "task_plan": null}