{"asset_map": {"tic_tac_toe_x_piece": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/tic_tac_toe_x_piece_77c63f5d-6c6f-4b00-bfb2-042e0364d57e.webp", "type": "image", "prompt": "Generate a bold, stylized 'X' symbol for a game. Flat design with a subtle gradient from bright red (#E74C3C) to darker red (#C0392B). Rounded ends, slight 3D depth effect with a soft drop shadow. Top-down view, centered, thick lines, professional clean game asset, transparent background.", "description": "A bold red 'X' game piece with rounded edges and subtle 3D depth for Tic-Tac-Toe.", "subtype": "object", "aspect_ratio": [681, 710], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "tic_tac_toe_o_piece": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/tic_tac_toe_o_piece_b9f8dd7a-c523-42e6-82ee-72d305986dab.webp", "type": "image", "prompt": "Generate a bold, stylized 'O' symbol for a game. Flat design with a subtle gradient from bright blue (#3498DB) to darker blue (#2980B9). Thick ring with rounded edges, slight 3D depth effect with a soft drop shadow. Top-down view, centered, professional clean game asset, transparent background.", "description": "A thick blue 'O' game piece with a subtle 3D ring effect for Tic-Tac-Toe.", "subtype": "object", "aspect_ratio": [693, 748], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "checkers_red_piece": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/checkers_red_piece_6227abf6-43a1-4890-9363-7b066c4fc981.webp", "type": "image", "prompt": "Generate a classic red checker game piece. Flat design with subtle 3D appearance, circular disc viewed from slightly above. Gradient from bright red (#E74C3C) at the top to darker red (#C0392B) at the bottom. Subtle highlight on the top edge and soft shadow underneath. Clean smooth edges, transparent background.", "description": "A classic red circular checker disc with subtle highlights and depth.", "subtype": "character", "aspect_ratio": [760, 568], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "checkers_black_piece": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/checkers_black_piece_48371a2a-dc66-4b5e-92df-2a1baee124e9.webp", "type": "image", "prompt": "Generate a classic black checker game piece. Flat design with subtle 3D appearance, circular disc viewed from slightly above. Gradient from dark gray (#34495E) to near-black (#2C3E50). Subtle highlight on the top edge and soft shadow underneath. Clean smooth edges, transparent background.", "description": "A classic black/dark-gray circular checker disc with subtle highlights.", "subtype": "character", "aspect_ratio": [839, 590], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "checkers_red_king": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/checkers_red_king_3a7de53d-6b65-4847-9c2d-f83790e4beb8.webp", "type": "image", "prompt": "Modify the checkers_red_piece to include a small, stylized golden crown icon (#F39C12) centered on top. The crown should be simple with a subtle shine. Keep the red disc base identical in style. Professional game asset, transparent background.", "description": "A red checker piece featuring a golden crown to indicate a King piece.", "subtype": "character", "aspect_ratio": [1113, 825], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "checkers_black_king": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/checkers_black_king_59026615-4926-4b20-9a1b-b67c294c7f23.webp", "type": "image", "prompt": "Modify the checkers_black_piece to include a small, stylized golden crown icon (#F39C12) centered on top. The crown should be simple with a subtle shine. Keep the black disc base identical in style. Professional game asset, transparent background.", "description": "A black checker piece featuring a golden crown to indicate a King piece.", "subtype": "character", "aspect_ratio": [1183, 831], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "ludo_token_red": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/ludo_token_red_3b62d8c1-6300-4def-8854-e8dd709bb02c.webp", "type": "image", "prompt": "Generate a red Ludo game token. Flat design with a pawn/cone shape, viewed from slightly above. Bright red (#E74C3C) to dark red (#C0392B) gradient. Rounded top, circular base, subtle 3D depth and soft shadow. Friendly and playful board game piece, transparent background.", "description": "A red cone-shaped Ludo pawn viewed from a slight top-down angle.", "subtype": "character", "aspect_ratio": [508, 798], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "ludo_token_blue": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/ludo_token_blue_9c14af11-2627-4241-96dd-b874e7dc4891.webp", "type": "image", "prompt": "Generate a blue Ludo game token. Flat design with a pawn/cone shape, viewed from slightly above. Bright blue (#3498DB) to dark blue (#2980B9) gradient. Rounded top, circular base, subtle 3D depth and soft shadow. Friendly and playful board game piece, transparent background.", "description": "A blue cone-shaped Ludo pawn viewed from a slight top-down angle.", "subtype": "character", "aspect_ratio": [519, 820], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "dice_1": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dice_1_e9ef1e4e-eef5-428c-b264-29e619491df5.webp", "type": "image", "prompt": "Generate a six-sided die showing one black dot. Flat design, white/cream cube (#F5F5DC) with rounded corners. Viewed from a slight angle to show 3D depth. Subtle shading on edges, soft drop shadow, transparent background.", "description": "A clean white 3D die showing the number one.", "subtype": "object", "aspect_ratio": [684, 738], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dice_2": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dice_2_2bc96a04-9070-42b8-8ac2-9ce64e3bd030.webp", "type": "image", "prompt": "Generate a six-sided die showing two black dots arranged diagonally. Match the style, color, and angle of dice_1 perfectly. White/cream cube with rounded corners, subtle depth, transparent background.", "description": "A clean white 3D die showing the number two.", "subtype": "object", "aspect_ratio": [938, 1023], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dice_3": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dice_3_f87fc12c-405d-4e41-9f74-58db47616af0.webp", "type": "image", "prompt": "Generate a six-sided die showing three black dots arranged diagonally. Match the style, color, and angle of dice_1 perfectly. White/cream cube with rounded corners, subtle depth, transparent background.", "description": "A clean white 3D die showing the number three.", "subtype": "object", "aspect_ratio": [947, 1024], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dice_4": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dice_4_f93ffdac-e121-4e1c-a727-9b9cd6047d2c.webp", "type": "image", "prompt": "Generate a six-sided die showing four black dots in the corners. Match the style, color, and angle of dice_1 perfectly. White/cream cube with rounded corners, subtle depth, transparent background.", "description": "A clean white 3D die showing the number four.", "subtype": "object", "aspect_ratio": [948, 1023], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dice_5": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dice_5_5bbee670-b20f-4168-a5c3-9f1c01113828.webp", "type": "image", "prompt": "Generate a six-sided die showing five black dots (corners and center). Match the style, color, and angle of dice_1 perfectly. White/cream cube with rounded corners, subtle depth, transparent background.", "description": "A clean white 3D die showing the number five.", "subtype": "object", "aspect_ratio": [924, 1003], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "dice_6": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/dice_6_7b0f621e-986e-4f42-b292-5be47d96d629.webp", "type": "image", "prompt": "Generate a six-sided die showing six black dots in two columns. Match the style, color, and angle of dice_1 perfectly. White/cream cube with rounded corners, subtle depth, transparent background.", "description": "A clean white 3D die showing the number six.", "subtype": "object", "aspect_ratio": [948, 1023], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "game_card_tictactoe": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/game_card_tictactoe_8e2c6a7d-deaf-4e71-a60a-d9dfd1ca6d6f.webp", "type": "image", "prompt": "Generate a 256x256 icon for a game menu representing Tic-Tac-Toe. Flat design showing a 3x3 grid with red 'X' and blue 'O' pieces from tic_tac_toe_x_piece and tic_tac_toe_o_piece. Background is a soft cream-to-white gradient. Clean, modern, and inviting.", "description": "Selection menu card for Tic-Tac-Toe showing a grid and pieces.", "subtype": "ui", "aspect_ratio": [1024, 1024], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "game_card_checkers": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/game_card_checkers_8c5740f9-afba-4455-bd7a-311328241d3d.webp", "type": "image", "prompt": "Generate a 256x256 icon for a game menu representing Checkers. Flat design showing a portion of an 8x8 checkerboard in perspective with red and black pieces from checkers_red_piece and checkers_black_piece. Background is a soft cream-to-white gradient.", "description": "Selection menu card for Checkers showing a board and pieces.", "subtype": "ui", "aspect_ratio": [1024, 1024], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "game_card_ludo": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/game_card_ludo_a222dd1e-971a-4411-8b35-f3872fd0686d.webp", "type": "image", "prompt": "Generate a 256x256 icon for a game menu representing Ludo. Flat design showing a cross-shaped Ludo path with red and blue tokens from ludo_token_red and ludo_token_blue. Include a die. Cheerful, vibrant colors, soft cream gradient background.", "description": "Selection menu card for Ludo showing the board and tokens.", "subtype": "ui", "aspect_ratio": [1024, 1024], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "wood_board_texture": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/wood_board_texture_4526bd84-b73e-4323-8ca8-3e83b6379b26.webp", "type": "image", "prompt": "Generate a fully tileable light oak wood texture. Warm natural brown tones (#D4A574 to #C19A6B), subtle grain patterns, clean and modern. Designed to repeat seamlessly in both horizontal and vertical directions without visible seams. Suitable for a board game surface.", "description": "Seamless light wood texture for board game backgrounds.", "subtype": "ground", "aspect_ratio": [1536, 1024], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "move_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/move_sound_ea234ad1-f7e4-44a0-9637-d7bdd4a1e2b1.mp3", "type": "audio", "prompt": "A short, clean, and satisfying 'click' or 'tap' sound effect of a wooden game piece being placed firmly on a polished wooden surface. Warm, organic tone, pleasant and non-metallic. Suitable for frequent use in a board game, high-quality tactile feedback.", "description": "Short, tactile click sound of a game piece being placed on a board.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.61, "loop": false, "animation": null}, "capture_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/capture_sound_7c289e23-971c-4a9a-8503-60f542da3359.mp3", "type": "audio", "prompt": "A short, impactful 'thunk' or 'pop' sound effect for a game capture event. Slightly heavier and more resonant than a standard move, conveying a sense of removal and reward. Clean, wooden texture, medium-pitched, satisfying physical impact.", "description": "Impactful 'thunk' sound for capturing an opponent's piece.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.89, "loop": false, "animation": null}, "win_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/win_sound_d36142dc-827b-4f7a-bf98-43a5d5680bd4.mp3", "type": "audio", "prompt": "An upbeat, cheerful victory fanfare jingle. Bright and positive major-key tones using a clean glockenspiel or light synth bells. Rising melody, celebratory and accomplished atmosphere, high-quality audio, ends on a clear resolved note.", "description": "Short upbeat victory jingle for winning a match.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 3, "loop": false, "animation": null}, "lose_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/lose_sound_278c00b1-6d41-43ea-9191-f84f3a9cabb1.mp3", "type": "audio", "prompt": "A short, gentle sequence of soft descending musical tones. Sympathetic and mellow 'game over' sound, using a warm Rhodes piano or soft woodwind. Not harsh or punishing, slightly melancholic but encouraging, resolving softly.", "description": "Soft descending tones to indicate a loss without being discouraging.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 3, "loop": false, "animation": null}, "dice_roll_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/dice_roll_sound_febb698d-f158-476d-a821-8ae501d82dca.mp3", "type": "audio", "prompt": "A quick rattling and tumbling sound of a single plastic die rolling on a wooden table. Ends with a sharp, gentle 'clack' as it settles. High-frequency detail, physical and realistic, conveys motion and chance.", "description": "Physical sound of a die rattling and settling on a table.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.9, "loop": false, "animation": null}, "button_click_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/button_click_sound_19fd4005-cbd0-43f6-94bf-01bb2023d825.mp3", "type": "audio", "prompt": "A very short, snappy, modern UI click sound effect. Minimalist and clean, mid-to-high frequency, subtle and pleasant. Designed for rapid interface interaction without fatigue, high-quality digital feedback.", "description": "Clean and modern UI button feedback sound.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.5, "loop": false, "animation": null}, "ludo_token_green": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/ludo_token_green_4956a7dd-be30-46f3-8368-8e5c1d17d467.webp", "type": "image", "prompt": "Generate a professional game asset of a green cone-shaped Ludo pawn. The pawn should be viewed from a slight top-down perspective, featuring smooth surfaces, subtle 3D depth, and soft highlights. Ensure the lighting, material finish, and artistic style perfectly match the red ludo token in image 1. High resolution, clean edges, transparent background.", "description": "A green cone-shaped Ludo pawn viewed from a slight top-down angle, matching the existing tokens.", "subtype": "object", "aspect_ratio": [792, 1246], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "ludo_token_yellow": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/ludo_token_yellow_6c65858d-192a-461c-98f8-7ea480eb8af3.webp", "type": "image", "prompt": "Generate a yellow cone-shaped Ludo pawn game piece, viewed from a slight top-down angle. Use a clean, polished 3D style with subtle highlights and soft shadows to match the lighting of ludo_token_red. High-resolution game asset on a transparent background.", "description": "A yellow cone-shaped Ludo pawn viewed from a top-down angle, matching the existing token set.", "subtype": "object", "aspect_ratio": [792, 1246], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "game_card_chess": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/game_card_chess_3bea9778-5541-4ce6-aca9-8cfc9797fd9c.webp", "type": "image", "prompt": "Generate a premium game selection menu card for Chess. The image should feature a stylish wooden chess board with iconic pieces, specifically a Knight and a King. The style must match the existing game cards like `game_card_tictactoe` and `game_card_checkers`, featuring a clean, rounded appearance, professional lighting, and subtle shadows. The perspective should be a polished 3D-angled view. High resolution, high quality, game-ready UI element.", "description": "A premium selection menu card for Chess featuring a knight and king on a wooden board.", "subtype": "ui", "aspect_ratio": [855, 803], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "game_card_connectfour": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/game_card_connectfour_0944d48a-84bf-4b24-a733-293c5f444ec5.webp", "type": "image", "prompt": "Generate a clean, modern 256x256 game menu card for Connect Four. Use a flat design style showing a bright blue vertical grid with red and yellow circular discs slotted into columns. The background should be a soft cream-to-white gradient. Maintain a consistent aesthetic and perspective to match the style of image 1 and image 2. Professional quality, centered composition, inviting look.", "description": "A modern menu icon for Connect Four featuring a blue grid with red and yellow discs.", "subtype": "ui", "aspect_ratio": [1024, 1024], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Boardverse: All-Star Classic Games</title>\n    <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n    <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n    <link href=\"https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n    <style>\n        /* CSS Reset */\n        *, *::before, *::after {\n            box-sizing: border-box;\n            margin: 0;\n            padding: 0;\n        }\n\n        /* Root Variables */\n        :root {\n            /* Colors */\n            --primary-dark: #1A237E;\n            --accent-orange: #FF6D00;\n            --bg-cream: #FFF9E1;\n            --bg-light: #F0F4C3;\n            --text-dark: #1A237E;\n            --text-light: #5C6BC0;\n            --red-bright: #FF1744;\n            --green-bright: #00E676;\n            --yellow-bright: #FFEA00;\n            --blue-bright: #2979FF;\n            --white: #FFFFFF;\n            --shadow: rgba(0, 0, 0, 0.15);\n            --shadow-strong: rgba(0, 0, 0, 0.3);\n\n            /* UI Elements */\n            --card-radius: 20px;\n            --button-radius: 12px;\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: 'Fredoka', sans-serif;\n            background-color: var(--bg-cream);\n        }\n\n        #game-world {\n            position: relative;\n            width: 100%;\n            height: 100%;\n            background: radial-gradient(circle at center, #FFFDE7 0%, #F0F4C3 100%);\n        }\n\n        /* Main Menu Styles */\n        .menu-container {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            overflow-y: auto;\n            padding: var(--space-md) var(--space-sm);\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n        }\n\n        .menu-header {\n            text-align: center;\n            padding: var(--space-xl) 0 var(--space-md);\n            margin-bottom: var(--space-lg);\n        }\n\n        .menu-title {\n            font-size: 82px;\n            font-weight: 700;\n            color: var(--primary-dark);\n            text-shadow: 4px 4px 0px rgba(255, 255, 255, 1), 10px 10px 20px var(--shadow);\n            letter-spacing: -2px;\n            margin-bottom: var(--space-xs);\n            animation: slideUp 0.8s var(--ease-bounce);\n        }\n\n        .menu-subtitle {\n            font-size: 24px;\n            color: var(--text-light);\n            font-weight: 600;\n            background: var(--white);\n            padding: 8px 30px;\n            border-radius: 40px;\n            box-shadow: 0 6px 20px var(--shadow);\n            animation: slideUp 1s var(--ease-bounce);\n        }\n\n        .games-grid {\n            display: grid;\n            grid-template-columns: 1fr;\n            gap: var(--space-md);\n            padding-bottom: var(--space-xl);\n            width: 100%;\n            max-width: 480px;\n        }\n\n        .game-card {\n            background: var(--white);\n            border-radius: var(--card-radius);\n            padding: var(--space-sm);\n            box-shadow: 0 10px 30px var(--shadow);\n            cursor: pointer;\n            transition: all 0.4s var(--ease-bounce);\n            display: flex;\n            align-items: center;\n            gap: var(--space-md);\n            border: 4px solid transparent;\n            animation: fadeIn 0.5s ease-out both;\n        }\n\n        .game-card:nth-child(1) { animation-delay: 0.1s; }\n        .game-card:nth-child(2) { animation-delay: 0.2s; }\n        .game-card:nth-child(3) { animation-delay: 0.3s; }\n        .game-card:nth-child(4) { animation-delay: 0.4s; }\n        .game-card-hot-icon {\n            position: absolute;\n            top: 10px;\n            right: 10px;\n            font-size: 30px;\n            animation: pulse 1.5s infinite;\n            text-shadow: 0 0 8px rgba(255, 109, 0, 0.7);\n        }\n\n        .game-card:hover {\n            border-color: var(--accent-orange);\n            transform: translateY(-8px) scale(1.02);\n            box-shadow: 0 20px 40px rgba(255, 109, 0, 0.2);\n        }\n\n        .game-card:active {\n            transform: scale(0.95);\n        }\n\n        .game-card:hover {\n            border-color: var(--accent-orange);\n            transform: translateY(-4px);\n        }\n\n        .game-card-icon {\n            width: 120px;\n            height: 120px;\n            border-radius: 16px;\n            object-fit: cover;\n            box-shadow: 0 4px 12px var(--shadow);\n        }\n\n        .game-card-info {\n            flex: 1;\n        }\n\n        .game-card-title {\n            font-size: 28px;\n            font-weight: 700;\n            color: var(--primary-dark);\n            margin-bottom: 4px;\n        }\n\n        .game-card-desc {\n            font-size: 16px;\n            color: var(--text-light);\n            line-height: 1.2;\n        }\n\n        /* Ludo Star Style Dice - 2D version */\n        .ludo-die {\n            position: absolute;\n            width: 90px;\n            height: 90px;\n            background: #ffffff;\n            border-radius: 18px;\n            box-shadow: 0 6px 12px var(--shadow-strong), inset 0 -4px 0 rgba(0,0,0,0.1);\n            display: grid;\n            grid-template-columns: repeat(3, 1fr);\n            grid-template-rows: repeat(3, 1fr);\n            padding: 14px;\n            gap: 6px;\n            cursor: pointer;\n            z-index: 20;\n            border: 3px solid #f0f0f0;\n            transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n            opacity: 0.6;\n            pointer-events: none;\n        }\n\n        .die-dot {\n            background: #2c3e50;\n            border-radius: 50%;\n            width: 100%;\n            height: 100%;\n            visibility: hidden;\n            box-shadow: inset 0 2px 2px rgba(0,0,0,0.3);\n        }\n\n        .ludo-die.active-roll {\n            animation: die-pulse 1.5s infinite;\n            border-color: #ffffff;\n            box-shadow: 0 0 20px rgba(0,0,0,0.2), 0 10px 25px rgba(0,0,0,0.15);\n            opacity: 1;\n            pointer-events: auto;\n            transform: scale(1.1);\n        }\n\n        @keyframes die-pulse {\n            0%, 100% { transform: scale(1.1); box-shadow: 0 10px 25px rgba(0,0,0,0.15); }\n            50% { transform: scale(1.15); box-shadow: 0 15px 35px rgba(0,0,0,0.25); }\n        }\n\n        .ludo-die.rolling {\n            animation: shake 0.1s infinite;\n        }\n\n        #die-red { bottom: 60px; left: 60px; border-bottom: 8px solid var(--red-bright); }\n        #die-green { top: 160px; left: 60px; border-top: 8px solid var(--green-bright); }\n        #die-yellow { top: 160px; right: 60px; border-top: 8px solid var(--yellow-bright); }\n        #die-blue { bottom: 60px; right: 60px; border-bottom: 8px solid var(--blue-bright); }\n\n        /* Turn Indicator */\n        .turn-indicator {\n            position: absolute;\n            top: 110px;\n            left: 50%;\n            transform: translateX(-50%);\n            background: rgba(255, 255, 255, 0.95);\n            padding: 10px 24px;\n            border-radius: 40px;\n            box-shadow: 0 4px 12px var(--shadow-strong);\n            font-size: 22px;\n            font-weight: 700;\n            z-index: 10;\n            border: 3px solid var(--primary-dark);\n            white-space: nowrap;\n            text-transform: uppercase;\n            letter-spacing: 1px;\n        }\n\n        /* Game Screen Styles */\n        .game-screen {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: var(--bg-cream);\n            display: none;\n            flex-direction: column;\n            z-index: 100;\n        }\n\n        .game-screen.active {\n            display: flex;\n            animation: fadeIn 0.3s ease-out;\n        }\n\n        .game-header {\n            display: flex;\n            justify-content: space-between;\n            align-items: center;\n            padding: 20px;\n            background: var(--white);\n            box-shadow: 0 2px 10px var(--shadow);\n            z-index: 10;\n        }\n\n        .game-header-left {\n            display: flex;\n            align-items: center;\n            gap: 15px;\n        }\n\n        .back-btn {\n            background: var(--primary-dark);\n            color: white;\n            border: none;\n            width: 50px;\n            height: 50px;\n            border-radius: 50%;\n            font-size: 24px;\n            cursor: pointer;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            box-shadow: 0 4px 0 var(--shadow-strong);\n        }\n\n        .back-btn:active {\n            transform: translateY(2px);\n            box-shadow: 0 2px 0 var(--shadow-strong);\n        }\n\n        .game-title {\n            font-size: 32px;\n            font-weight: 700;\n            color: var(--primary-dark);\n        }\n\n        .header-btn {\n            background: var(--bg-light);\n            border: none;\n            padding: 10px 15px;\n            border-radius: 12px;\n            font-size: 24px;\n            cursor: pointer;\n            box-shadow: 0 4px 0 var(--shadow);\n        }\n\n        .game-content {\n            flex: 1;\n            position: relative;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            overflow: hidden;\n        }\n\n        /* Modals */\n        .mode-selection, .result-modal {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0, 0, 0, 0.7);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            z-index: 200;\n            backdrop-filter: blur(5px);\n        }\n\n        .mode-modal, .result-content {\n            background: var(--white);\n            padding: 40px;\n            border-radius: 30px;\n            text-align: center;\n            width: 80%;\n            max-width: 400px;\n            box-shadow: 0 20px 40px rgba(0,0,0,0.4);\n            animation: slideUp 0.4s var(--ease-bounce);\n        }\n\n        .mode-modal-title, .result-title {\n            font-size: 36px;\n            margin-bottom: 30px;\n            color: var(--primary-dark);\n        }\n\n        .mode-button, .result-button {\n            display: block;\n            width: 100%;\n            padding: 15px;\n            margin-bottom: 15px;\n            border-radius: 15px;\n            border: none;\n            font-family: 'Fredoka', sans-serif;\n            font-size: 22px;\n            font-weight: 600;\n            cursor: pointer;\n            transition: transform 0.1s;\n        }\n\n        .mode-button:active, .result-button:active {\n            transform: scale(0.95);\n        }\n\n        .mode-button {\n            background: var(--accent-orange);\n            color: white;\n            box-shadow: 0 6px 0 #E65100;\n        }\n\n        .mode-button.secondary {\n            background: var(--blue-bright);\n            box-shadow: 0 6px 0 #1565C0;\n        }\n\n        .result-button.primary {\n            background: var(--green-bright);\n            color: white;\n            box-shadow: 0 6px 0 #00C853;\n        }\n\n        .result-button.secondary {\n            background: var(--bg-light);\n            color: var(--text-dark);\n            box-shadow: 0 6px 0 #C0CA33;\n        }\n\n        .result-title.win { color: var(--green-bright); }\n        .result-title.lose { color: var(--red-bright); }\n\n        /* General Utilities */\n        :root {\n            --space-xs: 8px;\n            --space-sm: 16px;\n            --space-md: 24px;\n            --space-lg: 32px;\n            --space-xl: 48px;\n            --transition-fast: 0.1s;\n            --transition-normal: 0.3s;\n            --ease-out: cubic-bezier(0.25, 1, 0.5, 1);\n            --ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);\n        }\n\n        .edit-panel.hidden {\n            display: none;\n        }\n\n        .edit-section {\n            margin-bottom: var(--space-md);\n        }\n\n        .edit-section-title {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 20px;\n            font-weight: 600;\n            color: var(--primary-dark);\n            margin-bottom: var(--space-sm);\n        }\n\n        .edit-options {\n            display: grid;\n            grid-template-columns: repeat(3, 1fr);\n            gap: var(--space-xs);\n        }\n\n        .edit-option {\n            aspect-ratio: 1;\n            background: var(--bg-light);\n            border: 3px solid transparent;\n            border-radius: 12px;\n            cursor: pointer;\n            transition: border-color var(--transition-fast) var(--ease-out);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 14px;\n            font-weight: 600;\n            color: var(--text-dark);\n            text-align: center;\n            padding: var(--space-xs);\n            pointer-events: auto;\n        }\n\n        .edit-option.selected {\n            border-color: var(--accent-orange);\n            background: #FFF5F0;\n        }\n\n        .edit-option:active {\n            transform: scale(0.95);\n        }\n\n        /* Animations */\n        @keyframes fadeIn {\n            from { opacity: 0; }\n            to { opacity: 1; }\n        }\n\n        /* Loading Screen */\n        #loading-screen {\n            position: fixed;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: var(--primary-dark);\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            z-index: 9999;\n            transition: opacity 0.5s ease-out;\n        }\n\n        .loader-logo {\n            font-size: 64px;\n            color: var(--white);\n            margin-bottom: 40px;\n            text-shadow: 0 4px 20px rgba(0,0,0,0.3);\n            animation: pulse 2s infinite ease-in-out;\n        }\n\n        .loader-container {\n            width: 280px;\n            height: 12px;\n            background: rgba(255, 255, 255, 0.2);\n            border-radius: 6px;\n            overflow: hidden;\n            margin-bottom: 16px;\n        }\n\n        #loader-bar {\n            width: 0%;\n            height: 100%;\n            background: var(--accent-orange);\n            transition: width 0.3s ease-out;\n        }\n\n        #loading-text {\n            color: var(--white);\n            font-size: 18px;\n            font-weight: 500;\n        }\n\n        @keyframes slideUp {\n            from { transform: translateY(20px); opacity: 0; }\n            to { transform: translateY(0); opacity: 1; }\n        }\n\n        @keyframes pulse {\n            0%, 100% { transform: scale(1); }\n            50% { transform: scale(1.1); }\n        }\n\n        @keyframes shake {\n            0%, 100% { transform: translateX(0); }\n            25% { transform: translateX(-5px); }\n            75% { transform: translateX(5px); }\n        }\n\n        /* Reduced Motion */\n        @media (prefers-reduced-motion: reduce) {\n            * {\n                animation-duration: 0.01ms !important;\n                animation-iteration-count: 1 !important;\n                transition-duration: 0.01ms !important;\n            }\n        }\n    </style>\n</head>\n<body>\n    <div id=\"loading-screen\">\n        <div class=\"loader-logo\">\ud83c\udfb2 Boardverse</div>\n        <div class=\"loader-container\">\n            <div id=\"loader-bar\"></div>\n        </div>\n        <div id=\"loading-text\">Loading Assets (0%)</div>\n    </div>\n    <div id=\"game-world\">\n        <!-- Main Menu -->\n        <div id=\"main-menu\" class=\"menu-container\">\n            <div class=\"menu-header\">\n                <h1 class=\"menu-title\">Boardverse</h1>\n                <p class=\"menu-subtitle\">All-Star Classic Games</p>\n            </div>\n            <div class=\"games-grid\" id=\"games-grid\"></div>\n        </div>\n\n        <!-- Settings Button -->\n        <button class=\"settings-btn\" id=\"settings-btn\" style=\"display: none;\">\u2699\ufe0f</button>\n\n        <!-- Game Screens (dynamically created) -->\n    </div>\n\n    <script>\n        // ===========================\n        // GAME OVERVIEW\n        // ===========================\n        // Boardverse is a collection of classic board games featuring:\n        // 1. Tic-Tac-Toe: 3x3 grid with X and O\n        // 2. Checkers: 8x8 board with mandatory captures\n        // 3. Ludo (Simplified): 2 players, circular path, dice rolling\n        // Each game supports vs AI and vs Friend modes with smooth animations\n\n        let currentMode = 'play';\n        let audioContext = null;\n        let audioBuffers = {};\n        let masterVolume = 1.0;\n\n        // Asset cache\n        const assetCache = {};\n\n        async function run(mode) {\n            currentMode = mode;\n            const gameWorld = document.getElementById('game-world');\n\n            // Show loading screen\n            const loadingScreen = document.getElementById('loading-screen');\n            if (loadingScreen) {\n                loadingScreen.style.opacity = '1';\n                loadingScreen.style.display = 'flex';\n            }\n\n            // Initialize audio context\n            if (!audioContext) {\n                audioContext = new (window.AudioContext || window.webkitAudioContext)();\n            }\n\n            // Preload all assets\n            await preloadAssets([\n                'tic_tac_toe_x_piece',\n                'tic_tac_toe_o_piece',\n                'checkers_red_piece',\n                'checkers_black_piece',\n                'checkers_red_king',\n                'checkers_black_king',\n                'ludo_token_red',\n                'ludo_token_blue',\n                'ludo_token_green',\n                'ludo_token_yellow',\n                'dice_1', 'dice_2', 'dice_3', 'dice_4', 'dice_5', 'dice_6',\n                'game_card_tictactoe',\n                'game_card_checkers',\n                'game_card_ludo',\n                'game_card_chess',\n                'game_card_connectfour',\n                'wood_board_texture',\n                'move_sound',\n                'capture_sound',\n                'win_sound',\n                'lose_sound',\n                'dice_roll_sound',\n                'button_click_sound'\n            ]);\n\n            // Initialize game configuration if needed\n            if (!window.gameConfig) {\n                window.gameConfig = getDefaultConfig();\n            }\n\n            // Show game parameters\n            lib.showGameParameters({\n                name: 'Game Settings',\n                params: {\n                    'Sound Volume': {\n                        key: 'settings.soundVolume',\n                        type: 'slider',\n                        min: 0,\n                        max: 100,\n                        step: 10,\n                        onChange: (value) => {\n                            window.gameConfig.settings.soundVolume = value;\n                            masterVolume = value / 100;\n                        }\n                    },\n                    'Animation Speed': {\n                        key: 'settings.animationSpeed',\n                        type: 'dropdown',\n                        options: [\n                            { label: 'Slow', value: 'slow' },\n                            { label: 'Normal', value: 'normal' },\n                            { label: 'Fast', value: 'fast' }\n                        ]\n                    },\n                    'Show Valid Moves': {\n                        key: 'settings.showValidMoves',\n                        type: 'checkbox'\n                    }\n                }\n            });\n\n            // Update master volume\n            masterVolume = window.gameConfig.settings.soundVolume / 100;\n\n            // Setup main menu\n            setupMainMenu();\n\n            // Setup game selection\n            setupGameSelection();\n\n            // Hide loading screen\n            if (loadingScreen) {\n                loadingScreen.style.opacity = '0';\n                setTimeout(() => {\n                    loadingScreen.style.display = 'none';\n                }, 500);\n            }\n\n            if (mode === 'edit') {\n                // Edit mode: Show customization options\n                document.getElementById('settings-btn').style.display = 'block';\n                setupEditMode();\n            } else {\n                // Play mode: Normal gameplay\n                document.getElementById('settings-btn').style.display = 'none';\n            }\n        }\n\n        function getDefaultConfig() {\n            return {\n                settings: {\n                    soundVolume: 70,\n                    animationSpeed: 'normal',\n                    showValidMoves: true\n                },\n                themes: {\n                    tictactoe: 'classic',\n                    checkers: 'classic',\n                    ludo: 'classic'\n                }\n            };\n        }\n\n        async function preloadAssets(assetIds) {\n            let loadedCount = 0;\n            const total = assetIds.length;\n            const bar = document.getElementById('loader-bar');\n            const text = document.getElementById('loading-text');\n\n            const updateProgress = () => {\n                loadedCount++;\n                const percentage = Math.floor((loadedCount / total) * 100);\n                if (bar) bar.style.width = `${percentage}%`;\n                if (text) text.textContent = `Loading Assets (${percentage}%)`;\n            };\n\n            const promises = assetIds.map(async (id) => {\n                const assetInfo = lib.getAsset(id);\n                if (!assetInfo) {\n                    updateProgress();\n                    return;\n                }\n\n                if (assetInfo.type === 'image') {\n                    return new Promise((resolve, reject) => {\n                        const img = new Image();\n                        img.onload = () => {\n                            assetCache[id] = { img, info: assetInfo };\n                            updateProgress();\n                            resolve();\n                        };\n                        img.onerror = () => {\n                            lib.log(`Failed to load image: ${id}`);\n                            updateProgress();\n                            resolve(); // Continue even if asset fails\n                        };\n                        img.src = assetInfo.url;\n                    });\n                } else if (assetInfo.type === 'audio') {\n                    try {\n                        const response = await fetch(assetInfo.url);\n                        const arrayBuffer = await response.arrayBuffer();\n                        const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n                        audioBuffers[id] = audioBuffer;\n                        updateProgress();\n                    } catch (error) {\n                        lib.log(`Failed to load audio: ${id}`);\n                        updateProgress();\n                    }\n                } else {\n                    updateProgress();\n                }\n            });\n\n            await Promise.all(promises);\n        }\n\n        function playSound(soundId) {\n            if (!audioContext) {\n                audioContext = new (window.AudioContext || window.webkitAudioContext)();\n            }\n            if (audioContext.state === 'suspended') {\n                audioContext.resume();\n            }\n\n            // Always trigger crisp synthesized audio for instant, satisfying responsiveness\n            playSynthesizedSound(soundId);\n\n            // Also play the preloaded high quality audio asset buffer if loaded\n            if (audioBuffers[soundId] && masterVolume > 0) {\n                try {\n                    const source = audioContext.createBufferSource();\n                    const gainNode = audioContext.createGain();\n\n                    source.buffer = audioBuffers[soundId];\n                    gainNode.gain.value = masterVolume * 0.7; // Blend nicely with synth\n\n                    source.connect(gainNode);\n                    gainNode.connect(audioContext.destination);\n\n                    source.start(0);\n                } catch (e) {\n                    lib.log(`Error playing buffer: ${soundId}`);\n                }\n            }\n        }\n\n        function playSynthesizedSound(type) {\n            if (!audioContext || masterVolume === 0) return;\n            \n            const ctx = audioContext;\n            const now = ctx.currentTime;\n\n            switch (type) {\n                case 'button_click_sound': {\n                    // Quick high-pitch modern interface click\n                    const osc = ctx.createOscillator();\n                    const gain = ctx.createGain();\n                    osc.type = 'sine';\n                    osc.frequency.setValueAtTime(900, now);\n                    osc.frequency.exponentialRampToValueAtTime(200, now + 0.08);\n                    \n                    gain.gain.setValueAtTime(0.18 * masterVolume, now);\n                    gain.gain.exponentialRampToValueAtTime(0.001, now + 0.08);\n                    \n                    osc.connect(gain);\n                    gain.connect(ctx.destination);\n                    osc.start(now);\n                    osc.stop(now + 0.08);\n                    break;\n                }\n                case 'move_sound': {\n                    // Solid tactile wood/plastic slide and drop click\n                    const osc1 = ctx.createOscillator();\n                    const osc2 = ctx.createOscillator();\n                    const gain = ctx.createGain();\n                    \n                    osc1.type = 'triangle';\n                    osc1.frequency.setValueAtTime(180, now);\n                    osc1.frequency.exponentialRampToValueAtTime(60, now + 0.12);\n                    \n                    osc2.type = 'sine';\n                    osc2.frequency.setValueAtTime(350, now);\n                    osc2.frequency.exponentialRampToValueAtTime(120, now + 0.08);\n\n                    gain.gain.setValueAtTime(0.4 * masterVolume, now);\n                    gain.gain.exponentialRampToValueAtTime(0.001, now + 0.12);\n                    \n                    osc1.connect(gain);\n                    osc2.connect(gain);\n                    gain.connect(ctx.destination);\n                    \n                    osc1.start(now);\n                    osc2.start(now);\n                    osc1.stop(now + 0.12);\n                    osc2.stop(now + 0.12);\n                    break;\n                }\n                case 'capture_sound': {\n                    // Powerful heavy impact capture block with reverb/vibration feel\n                    const osc1 = ctx.createOscillator();\n                    const osc2 = ctx.createOscillator();\n                    const gain = ctx.createGain();\n                    \n                    osc1.type = 'sawtooth';\n                    osc1.frequency.setValueAtTime(130, now);\n                    osc1.frequency.exponentialRampToValueAtTime(25, now + 0.22);\n                    \n                    osc2.type = 'triangle';\n                    osc2.frequency.setValueAtTime(190, now);\n                    osc2.frequency.exponentialRampToValueAtTime(35, now + 0.22);\n                    \n                    gain.gain.setValueAtTime(0.35 * masterVolume, now);\n                    gain.gain.exponentialRampToValueAtTime(0.001, now + 0.25);\n                    \n                    osc1.connect(gain);\n                    osc2.connect(gain);\n                    gain.connect(ctx.destination);\n                    \n                    osc1.start(now);\n                    osc2.start(now);\n                    osc1.stop(now + 0.25);\n                    osc2.stop(now + 0.25);\n                    break;\n                }\n                case 'dice_roll_sound': {\n                    // Multiple random physical tumbling rattle effects\n                    for (let i = 0; i < 7; i++) {\n                        const t = now + i * 0.08;\n                        const osc = ctx.createOscillator();\n                        const gain = ctx.createGain();\n                        osc.type = i % 2 === 0 ? 'sine' : 'triangle';\n                        osc.frequency.setValueAtTime(200 + Math.random() * 250, t);\n                        \n                        gain.gain.setValueAtTime(0.14 * masterVolume, t);\n                        gain.gain.exponentialRampToValueAtTime(0.001, t + 0.07);\n                        \n                        osc.connect(gain);\n                        gain.connect(ctx.destination);\n                        osc.start(t);\n                        osc.stop(t + 0.07);\n                    }\n                    break;\n                }\n                case 'win_sound': {\n                    // Majestic sparkling happy victory arpeggio chord\n                    const notes = [261.63, 329.63, 392.00, 523.25, 659.25, 783.99]; // C4, E4, G4, C5, E5, G5\n                    notes.forEach((freq, i) => {\n                        const t = now + i * 0.1;\n                        const osc = ctx.createOscillator();\n                        const gain = ctx.createGain();\n                        \n                        osc.type = 'sine';\n                        osc.frequency.setValueAtTime(freq, t);\n                        \n                        gain.gain.setValueAtTime(0.22 * masterVolume, t);\n                        gain.gain.exponentialRampToValueAtTime(0.001, t + 0.35);\n                        \n                        osc.connect(gain);\n                        gain.connect(ctx.destination);\n                        osc.start(t);\n                        osc.stop(t + 0.35);\n                    });\n                    break;\n                }\n                case 'lose_sound': {\n                    // Disappointed sliding down sad whistle\n                    const notes = [440.00, 392.00, 349.23, 293.66]; // A4, G4, F4, D4\n                    notes.forEach((freq, i) => {\n                        const t = now + i * 0.14;\n                        const osc = ctx.createOscillator();\n                        const gain = ctx.createGain();\n                        \n                        osc.type = 'triangle';\n                        osc.frequency.setValueAtTime(freq, t);\n                        osc.frequency.linearRampToValueAtTime(freq - 30, t + 0.25);\n                        \n                        gain.gain.setValueAtTime(0.2 * masterVolume, t);\n                        gain.gain.exponentialRampToValueAtTime(0.001, t + 0.28);\n                        \n                        osc.connect(gain);\n                        gain.connect(ctx.destination);\n                        osc.start(t);\n                        osc.stop(t + 0.28);\n                    });\n                    break;\n                }\n            }\n        }\n\n        async function setupMainMenu() {\n            const gamesGrid = document.getElementById('games-grid');\n            gamesGrid.innerHTML = '';\n\n            const games = [\n                {\n                    id: 'tictactoe',\n                    title: 'Tic-Tac-Toe',\n                    desc: 'Classic 3-in-a-row strategy.',\n                    icon: 'game_card_tictactoe'\n                },\n                {\n                    id: 'checkers',\n                    title: 'Checkers',\n                    desc: 'Classic capture strategy.',\n                    icon: 'game_card_checkers'\n                },\n                {\n                    id: 'ludo',\n                    title: 'Ludo Star',\n                    desc: 'The ultimate Ludo experience.',\n                    icon: 'game_card_ludo'\n                },\n                {\n                    id: 'chess',\n                    title: 'Chess',\n                    desc: 'The ultimate game of strategy.',\n                    icon: 'game_card_chess'\n                },\n                {\n                    id: 'connectfour',\n                    title: 'Connect 4',\n                    desc: 'Drop and line up 4 discs.',\n                    icon: 'game_card_connectfour'\n                }\n            ];\n\n            let topPlayedGames = [];\n            try {\n                const response = await lib.getTopNEntriesFromLeaderboard(5); // Get top 5 played games\n                if (response.entries) {\n                    topPlayedGames = response.entries.map(entry => entry.leaderboardId);\n                }\n            } catch (error) {\n                lib.log(`Failed to fetch top played games: ${error.message}`);\n            }\n\n            games.forEach(game => {\n                const asset = assetCache[game.icon];\n                if (!asset || !asset.img) {\n                    lib.log(`Warning: Asset ${game.icon} not found for game ${game.id}`);\n                    return;\n                }\n\n                const card = document.createElement('div');\n                card.className = 'game-card';\n                card.style.position = 'relative';\n                card.innerHTML = `\n                    <img src=\"${asset.img.src}\" alt=\"${game.title}\" class=\"game-card-icon\">\n                    <div class=\"game-card-info\">\n                        <h3 class=\"game-card-title\">${game.title}</h3>\n                        <p class=\"game-card-desc\">${game.desc}</p>\n                    </div>\n                    ${topPlayedGames.includes(game.id) ? '<span class=\"game-card-hot-icon\">\ud83d\udd25</span>' : ''}\n                `;\n\n                card.addEventListener('click', () => {\n                    playSound('button_click_sound');\n                    showGameModeSelection(game.id);\n                });\n\n                gamesGrid.appendChild(card);\n            });\n        }\n\n        function setupGameSelection() {\n            // Game screens are created dynamically when a game is selected\n        }\n\n        function showGameModeSelection(gameId) {\n            // Create mode selection modal\n            const modal = document.createElement('div');\n            modal.className = 'mode-selection';\n            modal.innerHTML = `\n                <div class=\"mode-modal\">\n                    <h2 class=\"mode-modal-title\">Choose Mode</h2>\n                    <button class=\"mode-button\" data-mode=\"ai\">vs AI</button>\n                    <button class=\"mode-button secondary\" data-mode=\"friend\">vs Friend</button>\n                </div>\n            `;\n\n            document.getElementById('game-world').appendChild(modal);\n\n            modal.addEventListener('click', (e) => {\n                if (e.target.classList.contains('mode-button')) {\n                    playSound('button_click_sound');\n                    const mode = e.target.dataset.mode;\n                    modal.remove();\n                    startGame(gameId, mode);\n                } else if (e.target === modal) {\n                    modal.remove();\n                }\n            });\n        }\n\n        async function startGame(gameId, vsMode) {\n            // Hide main menu\n            document.getElementById('main-menu').style.display = 'none';\n\n            // Increment play count for the game\n            try {\n                await lib.addPlayerScoreToLeaderboard(1, 10, gameId); // Score is 1 for one play, get top 10 entries\n            } catch (error) {\n                lib.log(`Failed to update leaderboard for ${gameId}: ${error.message}`);\n            }\n\n            // Create or show game screen\n            let gameScreen = document.getElementById(`game-${gameId}`);\n            if (!gameScreen) {\n                gameScreen = createGameScreen(gameId);\n            }\n\n            gameScreen.classList.add('active');\n\n            // Initialize the game\n            if (gameId === 'tictactoe') {\n                initTicTacToe(gameScreen, vsMode);\n            } else if (gameId === 'checkers') {\n                initCheckers(gameScreen, vsMode);\n            } else if (gameId === 'ludo') {\n                initLudo(gameScreen, vsMode);\n            } else if (gameId === 'chess') {\n                initChess(gameScreen, vsMode);\n            } else if (gameId === 'connectfour') {\n                initConnectFour(gameScreen, vsMode);\n            }\n        }\n\n        function createGameScreen(gameId) {\n            const titles = {\n                tictactoe: 'Tic-Tac-Toe',\n                checkers: 'Checkers',\n                ludo: 'Ludo Star',\n                chess: 'Chess',\n                connectfour: 'Connect 4'\n            };\n\n            const screen = document.createElement('div');\n            screen.id = `game-${gameId}`;\n            screen.className = 'game-screen';\n            screen.innerHTML = `\n                <div class=\"game-header\">\n                    <div class=\"game-header-left\">\n                        <button class=\"back-btn\">\u2190</button>\n                        <h2 class=\"game-title\">${titles[gameId] || 'Board Game'}</h2>\n                    </div>\n                    <div class=\"game-header-right\">\n                        <button class=\"header-btn restart-btn\">\u21bb</button>\n                    </div>\n                </div>\n                <div class=\"game-content\">\n                    <canvas id=\"canvas-${gameId}\"></canvas>\n                    <div class=\"turn-indicator\" id=\"turn-${gameId}\"></div>\n                </div>\n            `;\n\n            document.getElementById('game-world').appendChild(screen);\n\n            // Setup back button\n            screen.querySelector('.back-btn').addEventListener('click', () => {\n                playSound('button_click_sound');\n                returnToMenu(screen);\n            });\n\n            return screen;\n        }\n\n        function returnToMenu(gameScreen) {\n            gameScreen.classList.remove('active');\n            document.getElementById('main-menu').style.display = 'flex';\n\n            // Clean up game state\n            const canvas = gameScreen.querySelector('canvas');\n            const ctx = canvas.getContext('2d');\n            ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n            // Remove any modals\n            gameScreen.querySelectorAll('.result-modal, .mode-selection').forEach(el => el.remove());\n        }\n\n        // ===========================\n        // TIC-TAC-TOE\n        // ===========================\n\n        function initTicTacToe(gameScreen, vsMode) {\n            const canvas = gameScreen.querySelector('canvas');\n            const ctx = canvas.getContext('2d');\n            const turnIndicator = gameScreen.querySelector('.turn-indicator');\n\n            // Game state\n            let board = Array(9).fill(null);\n            let currentPlayer = 'X'; \n            let gameOver = false;\n            let isAITurn = false;\n\n            // Canvas setup\n            const boardSize = Math.min(window.innerWidth - 32, 500);\n            canvas.width = boardSize;\n            canvas.height = boardSize;\n            const cellSize = boardSize / 3;\n\n            // Setup restart button\n            gameScreen.querySelector('.restart-btn').onclick = () => {\n                playSound('button_click_sound');\n                resetGame();\n            };\n\n            function resetGame() {\n                board = Array(9).fill(null);\n                currentPlayer = 'X';\n                gameOver = false;\n                isAITurn = false;\n                drawBoard();\n                updateTurnIndicator();\n\n                const modal = gameScreen.querySelector('.result-modal');\n                if (modal) modal.remove();\n            }\n\n            function drawBoard() {\n                ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n                // Draw Grid\n                ctx.strokeStyle = '#2C3E50';\n                ctx.lineWidth = 10;\n                ctx.lineCap = 'round';\n\n                for (let i = 1; i < 3; i++) {\n                    // Vertical\n                    ctx.beginPath();\n                    ctx.moveTo(i * cellSize, 20);\n                    ctx.lineTo(i * cellSize, boardSize - 20);\n                    ctx.stroke();\n                    // Horizontal\n                    ctx.beginPath();\n                    ctx.moveTo(20, i * cellSize);\n                    ctx.lineTo(boardSize - 20, i * cellSize);\n                    ctx.stroke();\n                }\n\n                // Draw Pieces\n                board.forEach((cell, index) => {\n                    if (cell) {\n                        const row = Math.floor(index / 3);\n                        const col = index % 3;\n                        const x = col * cellSize + cellSize / 2;\n                        const y = row * cellSize + cellSize / 2;\n\n                        const assetId = cell === 'X' ? 'tic_tac_toe_x_piece' : 'tic_tac_toe_o_piece';\n                        const asset = assetCache[assetId];\n                        const pieceSize = cellSize * 0.7;\n                        \n                        if (asset) {\n                            ctx.drawImage(asset.img, x - pieceSize / 2, y - pieceSize / 2, pieceSize, pieceSize);\n                        } else {\n                            // Fallback\n                            ctx.fillStyle = cell === 'X' ? '#FF1744' : '#2979FF';\n                            ctx.font = `bold ${cellSize * 0.6}px Arial`;\n                            ctx.textAlign = 'center';\n                            ctx.textBaseline = 'middle';\n                            ctx.fillText(cell, x, y);\n                        }\n                    }\n                });\n            }\n\n            function updateTurnIndicator() {\n                if (gameOver) {\n                    turnIndicator.textContent = 'Game Over';\n                    return;\n                }\n                const name = vsMode === 'ai' && currentPlayer === 'O' ? 'AI' : (vsMode === 'friend' ? `Player ${currentPlayer}` : 'Your');\n                turnIndicator.textContent = `${name}'s Turn`;\n                turnIndicator.style.color = currentPlayer === 'X' ? '#FF1744' : '#2979FF';\n                turnIndicator.style.borderColor = currentPlayer === 'X' ? '#FF1744' : '#2979FF';\n            }\n\n            function handleClick(e) {\n                if (gameOver || isAITurn) return;\n                \n                const rect = canvas.getBoundingClientRect();\n                const clientX = e.clientX || (e.touches && e.touches[0] ? e.touches[0].clientX : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientX : 0));\n                const clientY = e.clientY || (e.touches && e.touches[0] ? e.touches[0].clientY : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientY : 0));\n                const x = clientX - rect.left;\n                const y = clientY - rect.top;\n                \n                const col = Math.floor(x / cellSize);\n                const row = Math.floor(y / cellSize);\n\n                if (row < 0 || row >= 3 || col < 0 || col >= 3) return;\n                \n                const index = row * 3 + col;\n\n                if (board[index] === null) {\n                    makeMove(index);\n                }\n            }\n\n            function makeMove(index) {\n                board[index] = currentPlayer;\n                playSound('move_sound');\n                drawBoard();\n\n                const winner = checkWinner();\n                if (winner) {\n                    gameOver = true;\n                    setTimeout(() => showResult(winner), 300);\n                } else if (board.every(cell => cell !== null)) {\n                    gameOver = true;\n                    setTimeout(() => showResult('draw'), 300);\n                } else {\n                    currentPlayer = currentPlayer === 'X' ? 'O' : 'X';\n                    updateTurnIndicator();\n                    if (vsMode === 'ai' && currentPlayer === 'O') {\n                        isAITurn = true;\n                        setTimeout(aiMove, 600);\n                    }\n                }\n            }\n\n            function aiMove() {\n                if (gameOver) return;\n                const emptyIndices = board.map((c, i) => c === null ? i : null).filter(i => i !== null);\n                if (emptyIndices.length > 0) {\n                    const randomIndex = emptyIndices[Math.floor(Math.random() * emptyIndices.length)];\n                    makeMove(randomIndex);\n                }\n                isAITurn = false;\n            }\n\n            function checkWinner() {\n                const lines = [\n                    [0, 1, 2], [3, 4, 5], [6, 7, 8], // rows\n                    [0, 3, 6], [1, 4, 7], [2, 5, 8], // cols\n                    [0, 4, 8], [2, 4, 6]             // diags\n                ];\n                for (const [a, b, c] of lines) {\n                    if (board[a] && board[a] === board[b] && board[a] === board[c]) return board[a];\n                }\n                return null;\n            }\n\n            function showResult(winner) {\n                const isWin = vsMode === 'ai' ? winner === 'X' : true;\n                const title = winner === 'draw' ? \"It's a Draw!\" : (vsMode === 'ai' ? (isWin ? \"You Win!\" : \"AI Wins!\") : `Player ${winner} Wins!`);\n                \n                const modal = document.createElement('div');\n                modal.className = 'result-modal';\n                modal.innerHTML = `\n                    <div class=\"result-content\">\n                        <h2 class=\"result-title ${winner === 'draw' ? '' : (isWin ? 'win' : 'lose')}\">${title}</h2>\n                        <div class=\"result-buttons\">\n                            <button class=\"result-button primary\">Play Again</button>\n                            <button class=\"result-button secondary\">Menu</button>\n                        </div>\n                    </div>\n                `;\n                gameScreen.appendChild(modal);\n\n                modal.querySelector('.result-button.primary').onclick = () => { playSound('button_click_sound'); modal.remove(); resetGame(); };\n                modal.querySelector('.result-button.secondary').onclick = () => { playSound('button_click_sound'); modal.remove(); returnToMenu(gameScreen); };\n                \n                if (winner === 'draw') playSound('button_click_sound');\n                else playSound(isWin ? 'win_sound' : 'lose_sound');\n            }\n\n            canvas.addEventListener('mousedown', handleClick);\n            canvas.addEventListener('touchstart', (e) => { e.preventDefault(); handleClick(e); });\n            resetGame();\n        }\n        // ===========================\n        // CHECKERS\n        // ===========================\n\n        function initCheckers(gameScreen, vsMode) {\n            const canvas = gameScreen.querySelector('canvas');\n            const ctx = canvas.getContext('2d');\n            const turnIndicator = gameScreen.querySelector('.turn-indicator');\n\n            let board = [];\n            let currentPlayer = 'red';\n            let selectedPiece = null;\n            let validMoves = [];\n            let gameOver = false;\n            let isAITurn = false;\n            let mustCapture = false;\n\n            const boardSize = Math.min(window.innerWidth - 32, 600);\n            const cellSize = boardSize / 8;\n\n            canvas.width = boardSize;\n            canvas.height = boardSize;\n            canvas.style.position = 'absolute';\n            canvas.style.top = '50%';\n            canvas.style.left = '50%';\n            canvas.style.transform = 'translate(-50%, -50%)';\n\n            gameScreen.querySelector('.restart-btn').onclick = function() {\n                playSound('button_click_sound');\n                resetGame();\n            };\n\n            function resetGame() {\n                board = initializeBoard();\n                currentPlayer = 'red';\n                selectedPiece = null;\n                validMoves = [];\n                gameOver = false;\n                isAITurn = false;\n                mustCapture = false;\n                drawBoard();\n                updateTurnIndicator();\n                const modal = gameScreen.querySelector('.result-modal');\n                if (modal) modal.remove();\n            }\n\n            function initializeBoard() {\n                const newBoard = [];\n                for (let row = 0; row < 8; row++) {\n                    newBoard[row] = [];\n                    for (let col = 0; col < 8; col++) {\n                        if ((row + col) % 2 === 1) {\n                            if (row < 3) newBoard[row][col] = { color: 'black', king: false };\n                            else if (row > 4) newBoard[row][col] = { color: 'red', king: false };\n                            else newBoard[row][col] = null;\n                        } else {\n                            newBoard[row][col] = null;\n                        }\n                    }\n                }\n                return newBoard;\n            }\n\n            function drawBoard() {\n                ctx.clearRect(0, 0, canvas.width, canvas.height);\n                for (let row = 0; row < 8; row++) {\n                    for (let col = 0; col < 8; col++) {\n                        ctx.fillStyle = (row + col) % 2 === 0 ? '#E8D4B0' : '#8B4513';\n                        ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);\n                    }\n                }\n                if (selectedPiece) {\n                    ctx.fillStyle = 'rgba(255, 215, 0, 0.4)';\n                    ctx.fillRect(selectedPiece.col * cellSize, selectedPiece.row * cellSize, cellSize, cellSize);\n                }\n                if (window.gameConfig.settings.showValidMoves) {\n                    for (let i = 0; i < validMoves.length; i++) {\n                        ctx.fillStyle = 'rgba(50, 205, 50, 0.3)';\n                        ctx.fillRect(validMoves[i].col * cellSize, validMoves[i].row * cellSize, cellSize, cellSize);\n                    }\n                }\n                for (let row = 0; row < 8; row++) {\n                    for (let col = 0; col < 8; col++) {\n                        const piece = board[row][col];\n                        if (piece) {\n                            const assetId = piece.king ? (piece.color === 'red' ? 'checkers_red_king' : 'checkers_black_king') : (piece.color === 'red' ? 'checkers_red_piece' : 'checkers_black_piece');\n                            const asset = assetCache[assetId];\n                            if (asset) {\n                                const pieceSize = cellSize * 0.8;\n                                ctx.drawImage(asset.img, col * cellSize + cellSize / 2 - pieceSize / 2, row * cellSize + cellSize / 2 - pieceSize / 2, pieceSize, pieceSize);\n                            }\n                        }\n                    }\n                }\n            }\n\n            function updateTurnIndicator() {\n                if (gameOver) { turnIndicator.textContent = 'Game Over'; } else {\n                    const isAI = vsMode === 'ai' && currentPlayer === 'black';\n                    const name = isAI ? 'AI' : (vsMode === 'friend' ? (currentPlayer === 'red' ? 'Player 1' : 'Player 2') : 'Your');\n                    turnIndicator.textContent = name + \"'s Turn\";\n                    turnIndicator.style.color = currentPlayer === 'red' ? '#FF1744' : '#333333';\n                    turnIndicator.style.borderColor = currentPlayer === 'red' ? '#FF1744' : '#333333';\n                }\n            }\n\n            function handleClick(e) {\n                if (gameOver || isAITurn) return;\n                const rect = canvas.getBoundingClientRect();\n                const clientX = e.clientX || (e.touches && e.touches[0] ? e.touches[0].clientX : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientX : 0));\n                const clientY = e.clientY || (e.touches && e.touches[0] ? e.touches[0].clientY : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientY : 0));\n                const x = clientX - rect.left;\n                const y = clientY - rect.top;\n                const col = Math.floor(x / cellSize);\n                const row = Math.floor(y / cellSize);\n                if (row < 0 || row >= 8 || col < 0 || col >= 8) return;\n                \n                if (selectedPiece) {\n                    let move = null;\n                    for (let i = 0; i < validMoves.length; i++) {\n                        if (validMoves[i].row === row && validMoves[i].col === col) { move = validMoves[i]; break; }\n                    }\n                    if (move) {\n                        makeMove(selectedPiece, move);\n                        selectedPiece = null;\n                        validMoves = [];\n                    } else {\n                        const piece = board[row][col];\n                        if (piece && piece.color === currentPlayer) selectPiece(row, col);\n                        else { selectedPiece = null; validMoves = []; }\n                    }\n                } else {\n                    const piece = board[row][col];\n                    if (piece && piece.color === currentPlayer) selectPiece(row, col);\n                }\n                drawBoard();\n            }\n\n            function selectPiece(row, col) {\n                selectedPiece = { row: row, col: col };\n                validMoves = getValidMoves(row, col);\n                if (mustCapture) {\n                    const onlyCaptures = [];\n                    for (let i = 0; i < validMoves.length; i++) { if (validMoves[i].capture) onlyCaptures.push(validMoves[i]); }\n                    validMoves = onlyCaptures;\n                }\n                playSound('button_click_sound');\n            }\n\n            function getValidMoves(row, col) {\n                const piece = board[row][col];\n                if (!piece) return [];\n                const moves = [];\n                const dirs = piece.king ? [-1, 1] : (piece.color === 'red' ? [-1] : [1]);\n                for (let i = 0; i < dirs.length; i++) {\n                    const dir = dirs[i];\n                    for (let side = -1; side <= 1; side += 2) {\n                        const jumpR = row + dir * 2; const jumpC = col + side * 2;\n                        const midR = row + dir; const midC = col + side;\n                        if (jumpR >= 0 && jumpR < 8 && jumpC >= 0 && jumpC < 8) {\n                            const target = board[midR][midC]; const dest = board[jumpR][jumpC];\n                            if (target && target.color !== piece.color && !dest) {\n                                moves.push({ row: jumpR, col: jumpC, capture: { row: midR, col: midC } });\n                            }\n                        }\n                    }\n                }\n                if (moves.length === 0 && !mustCapture) {\n                    for (let i = 0; i < dirs.length; i++) {\n                        const dir = dirs[i];\n                        for (let side = -1; side <= 1; side += 2) {\n                            const newR = row + dir; const newC = col + side;\n                            if (newR >= 0 && newR < 8 && newC >= 0 && newC < 8 && !board[newR][newC]) {\n                                moves.push({ row: newR, col: newC, capture: null });\n                            }\n                        }\n                    }\n                }\n                return moves;\n            }\n\n            function makeMove(from, to) {\n                const piece = board[from.row][from.col];\n                board[to.row][to.col] = piece;\n                board[from.row][from.col] = null;\n                let captured = false;\n                if (to.capture) { board[to.capture.row][to.capture.col] = null; playSound('capture_sound'); captured = true; }\n                else playSound('move_sound');\n                if (!piece.king) {\n                    if ((piece.color === 'red' && to.row === 0) || (piece.color === 'black' && to.row === 7)) piece.king = true;\n                }\n                if (captured) {\n                    const next = getValidMoves(to.row, to.col);\n                    const captures = [];\n                    for (let i = 0; i < next.length; i++) { if (next[i].capture) captures.push(next[i]); }\n                    if (captures.length > 0) {\n                        selectedPiece = { row: to.row, col: to.col };\n                        validMoves = captures;\n                        mustCapture = true;\n                        drawBoard();\n                        return;\n                    }\n                }\n                mustCapture = false;\n                const winner = checkWinner();\n                if (winner) { gameOver = true; setTimeout(showResult, 300); return; }\n                currentPlayer = currentPlayer === 'red' ? 'black' : 'red';\n                mustCapture = checkMustCapture();\n                updateTurnIndicator();\n                drawBoard();\n                if (vsMode === 'ai' && currentPlayer === 'black') {\n                    isAITurn = true;\n                    setTimeout(function() { aiMove(); isAITurn = false; }, 800);\n                }\n            }\n\n            function checkMustCapture() {\n                for (let r = 0; r < 8; r++) {\n                    for (let c = 0; c < 8; c++) {\n                        const p = board[r][c];\n                        if (p && p.color === currentPlayer) {\n                            const m = getValidMoves(r, c);\n                            for (let i = 0; i < m.length; i++) { if (m[i].capture) return true; }\n                        }\n                    }\n                }\n                return false;\n            }\n\n            function aiMove() {\n                const all = [];\n                for (let r = 0; r < 8; r++) {\n                    for (let c = 0; c < 8; c++) {\n                        const p = board[r][c];\n                        if (p && p.color === 'black') {\n                            const m = getValidMoves(r, c);\n                            for (let i = 0; i < m.length; i++) { all.push({ from: { row: r, col: c }, to: m[i], score: evaluateMove(r, c, m[i]) }); }\n                        }\n                    }\n                }\n                if (all.length === 0) return;\n                const caps = [];\n                for (let i = 0; i < all.length; i++) { if (all[i].to.capture) caps.push(all[i]); }\n                const pool = caps.length > 0 ? caps : all;\n                pool.sort(function(a, b) { return b.score - a.score; });\n                const best = pool[0];\n                selectPiece(best.from.row, best.from.col);\n                setTimeout(function() { makeMove(best.from, best.to); }, 300);\n            }\n\n            function evaluateMove(fr, fc, m) {\n                let s = 0; if (m.capture) s += 10;\n                s += (7 - m.row);\n                const p = board[fr][fc];\n                if (p.king) s += 5; if (!p.king && m.row === 7) s += 8;\n                return s + (7 - (Math.abs(m.row - 3.5) + Math.abs(m.col - 3.5)));\n            }\n\n            function checkWinner() {\n                let r = 0, b = 0;\n                for (let i = 0; i < 8; i++) { for (let j = 0; j < 8; j++) {\n                    const p = board[i][j]; if (p) { if (p.color === 'red') r++; else b++; }\n                }}\n                if (r === 0) return 'black'; if (b === 0) return 'red'; return null;\n            }\n\n            function showResult() {\n                const w = checkWinner();\n                const isWin = w === 'red';\n                const title = vsMode === 'ai' ? (isWin ? \"You Win!\" : \"AI Wins!\") : (\"Player \" + (w === 'red' ? \"1\" : \"2\") + \" Wins!\");\n                const modal = document.createElement('div');\n                modal.className = 'result-modal';\n                modal.innerHTML = '<div class=\"result-content\"><h2 class=\"result-title ' + (isWin ? 'win' : 'lose') + '\">' + title + '</h2><p class=\"result-message\">Great game!</p><div class=\"result-buttons\"><button class=\"result-button primary\">Play Again</button><button class=\"result-button secondary\">Menu</button></div></div>';\n                gameScreen.appendChild(modal);\n                modal.querySelector('.result-button.primary').onclick = function() { playSound('button_click_sound'); modal.remove(); resetGame(); };\n                modal.querySelector('.result-button.secondary').onclick = function() { playSound('button_click_sound'); modal.remove(); returnToMenu(gameScreen); };\n                playSound(isWin ? 'win_sound' : 'lose_sound');\n            }\n\n            canvas.addEventListener('mousedown', handleClick);\n            canvas.addEventListener('touchstart', (e) => { e.preventDefault(); handleClick(e); });\n            resetGame();\n        }\n\n        // ===========================\n        // CHESS\n        // ===========================\n\n        function initChess(gameScreen, vsMode) {\n            const canvas = gameScreen.querySelector('canvas');\n            const ctx = canvas.getContext('2d');\n            const turnIndicator = gameScreen.querySelector('.turn-indicator');\n\n            const boardSize = Math.min(window.innerWidth - 32, 600);\n            const cellSize = boardSize / 8;\n            canvas.width = boardSize;\n            canvas.height = boardSize;\n\n            let board = [];\n            let currentPlayer = 'white';\n            let selectedPiece = null;\n            let validMoves = [];\n            let gameOver = false;\n            let isAITurn = false;\n\n            const pieces = {\n                'white': { 'p': '\u2659', 'r': '\u2656', 'n': '\u2658', 'b': '\u2657', 'q': '\u2655', 'k': '\u2654' },\n                'black': { 'p': '\u265f', 'r': '\u265c', 'n': '\u265e', 'b': '\u265d', 'q': '\u265b', 'k': '\u265a' }\n            };\n\n            gameScreen.querySelector('.restart-btn').onclick = () => {\n                playSound('button_click_sound');\n                resetGame();\n            };\n\n            function resetGame() {\n                board = initializeBoard();\n                currentPlayer = 'white';\n                selectedPiece = null;\n                validMoves = [];\n                gameOver = false;\n                isAITurn = false;\n                drawBoard();\n                updateTurnIndicator();\n                const modal = gameScreen.querySelector('.result-modal');\n                if (modal) modal.remove();\n            }\n\n            function initializeBoard() {\n                const b = Array(8).fill(null).map(() => Array(8).fill(null));\n                const layout = ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'];\n                \n                for (let i = 0; i < 8; i++) {\n                    b[0][i] = { type: layout[i], color: 'black' };\n                    b[1][i] = { type: 'p', color: 'black' };\n                    b[6][i] = { type: 'p', color: 'white' };\n                    b[7][i] = { type: layout[i], color: 'white' };\n                }\n                return b;\n            }\n\n            function drawBoard() {\n                ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n                for (let r = 0; r < 8; r++) {\n                    for (let c = 0; c < 8; c++) {\n                        ctx.fillStyle = (r + c) % 2 === 0 ? '#EEEED2' : '#769656';\n                        ctx.fillRect(c * cellSize, r * cellSize, cellSize, cellSize);\n\n                        if (selectedPiece && selectedPiece.r === r && selectedPiece.c === c) {\n                            ctx.fillStyle = 'rgba(255, 255, 0, 0.4)';\n                            ctx.fillRect(c * cellSize, r * cellSize, cellSize, cellSize);\n                        }\n\n                        if (validMoves.some(m => m.r === r && m.c === c)) {\n                            ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';\n                            ctx.beginPath();\n                            ctx.arc(c * cellSize + cellSize/2, r * cellSize + cellSize/2, cellSize/6, 0, Math.PI*2);\n                            ctx.fill();\n                        }\n\n                        const piece = board[r][c];\n                        if (piece) {\n                            ctx.fillStyle = piece.color === 'white' ? '#FFFFFF' : '#000000';\n                            ctx.font = `${cellSize * 0.75}px Arial`;\n                            ctx.textAlign = 'center';\n                            ctx.textBaseline = 'middle';\n                            ctx.shadowBlur = 2;\n                            ctx.shadowColor = 'rgba(0,0,0,0.5)';\n                            ctx.fillText(pieces[piece.color][piece.type], c * cellSize + cellSize / 2, r * cellSize + cellSize / 2);\n                            ctx.shadowBlur = 0;\n                        }\n                    }\n                }\n            }\n\n            function updateTurnIndicator() {\n                if (gameOver) {\n                    turnIndicator.textContent = 'Checkmate!';\n                    return;\n                }\n                const name = vsMode === 'ai' && currentPlayer === 'black' ? 'AI' : (vsMode === 'friend' ? `Player ${currentPlayer === 'white' ? '1' : '2'}` : 'Your');\n                turnIndicator.textContent = `${name}'s Turn`;\n                turnIndicator.style.color = currentPlayer === 'white' ? '#333' : '#000';\n                turnIndicator.style.borderColor = currentPlayer === 'white' ? '#AAA' : '#000';\n            }\n\n            function handleClick(e) {\n                if (gameOver || isAITurn) return;\n                const rect = canvas.getBoundingClientRect();\n                const clientX = e.clientX || (e.touches && e.touches[0] ? e.touches[0].clientX : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientX : 0));\n                const clientY = e.clientY || (e.touches && e.touches[0] ? e.touches[0].clientY : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientY : 0));\n                const x = clientX - rect.left;\n                const y = clientY - rect.top;\n                const c = Math.floor(x / cellSize);\n                const r = Math.floor(y / cellSize);\n\n                if (r < 0 || r >= 8 || c < 0 || c >= 8) return;\n\n                if (selectedPiece) {\n                    const move = validMoves.find(m => m.r === r && m.c === c);\n                    if (move) {\n                        makeMove(selectedPiece, { r, c });\n                        selectedPiece = null;\n                        validMoves = [];\n                    } else {\n                        const piece = board[r][c];\n                        if (piece && piece.color === currentPlayer) {\n                            selectedPiece = { r, c };\n                            validMoves = getValidMoves(r, c);\n                        } else {\n                            selectedPiece = null;\n                            validMoves = [];\n                        }\n                    }\n                } else {\n                    const piece = board[r][c];\n                    if (piece && piece.color === currentPlayer) {\n                        selectedPiece = { r, c };\n                        validMoves = getValidMoves(r, c);\n                    }\n                }\n                drawBoard();\n            }\n\n            function getValidMoves(r, c) {\n                const piece = board[r][c];\n                if (!piece) return [];\n                const moves = [];\n                const type = piece.type;\n                const color = piece.color;\n\n                const addMove = (nr, nc) => {\n                    if (nr >= 0 && nr < 8 && nc >= 0 && nc < 8) {\n                        if (!board[nr][nc] || board[nr][nc].color !== color) {\n                            moves.push({ r: nr, c: nc });\n                            return !board[nr][nc];\n                        }\n                    }\n                    return false;\n                };\n\n                if (type === 'p') {\n                    const dir = color === 'white' ? -1 : 1;\n                    if (r + dir >= 0 && r + dir < 8 && !board[r + dir][c]) {\n                        moves.push({ r: r + dir, c: c });\n                        if ((color === 'white' && r === 6) || (color === 'black' && r === 1)) {\n                            if (!board[r + 2 * dir][c]) moves.push({ r: r + 2 * dir, c: c });\n                        }\n                    }\n                    for (let side of [-1, 1]) {\n                        if (r + dir >= 0 && r + dir < 8 && c + side >= 0 && c + side < 8) {\n                            const target = board[r + dir][c + side];\n                            if (target && target.color !== color) moves.push({ r: r + dir, c: c + side });\n                        }\n                    }\n                } else if (type === 'r' || type === 'q') {\n                    const dirs = [[0, 1], [0, -1], [1, 0], [-1, 0]];\n                    for (let [dr, dc] of dirs) {\n                        for (let i = 1; i < 8; i++) {\n                            if (!addMove(r + dr * i, c + dc * i)) break;\n                        }\n                    }\n                }\n                if (type === 'b' || type === 'q') {\n                    const dirs = [[1, 1], [1, -1], [-1, 1], [-1, -1]];\n                    for (let [dr, dc] of dirs) {\n                        for (let i = 1; i < 8; i++) {\n                            if (!addMove(r + dr * i, c + dc * i)) break;\n                        }\n                    }\n                } else if (type === 'n') {\n                    const lMoves = [[2, 1], [2, -1], [-2, 1], [-2, -1], [1, 2], [1, -2], [-1, 2], [-1, -2]];\n                    for (let [dr, dc] of lMoves) addMove(r + dr, c + dc);\n                } else if (type === 'k') {\n                    for (let dr = -1; dr <= 1; dr++) {\n                        for (let dc = -1; dc <= 1; dc++) {\n                            if (dr !== 0 || dc !== 0) addMove(r + dr, c + dc);\n                        }\n                    }\n                }\n                return moves;\n            }\n\n            function makeMove(from, to) {\n                const piece = board[from.r][from.c];\n                const target = board[to.r][to.c];\n                \n                if (target) playSound('capture_sound');\n                else playSound('move_sound');\n\n                board[to.r][to.c] = piece;\n                board[from.r][from.c] = null;\n\n                // Promotion\n                if (piece.type === 'p' && (to.r === 0 || to.r === 7)) {\n                    piece.type = 'q';\n                }\n\n                if (target && target.type === 'k') {\n                    gameOver = true;\n                    setTimeout(() => showResult(piece.color), 300);\n                    return;\n                }\n\n                currentPlayer = currentPlayer === 'white' ? 'black' : 'white';\n                updateTurnIndicator();\n\n                if (vsMode === 'ai' && currentPlayer === 'black') {\n                    isAITurn = true;\n                    setTimeout(aiMove, 800);\n                }\n            }\n\n            function aiMove() {\n                if (gameOver) return;\n                const allMoves = [];\n                for (let r = 0; r < 8; r++) {\n                    for (let c = 0; c < 8; c++) {\n                        const p = board[r][c];\n                        if (p && p.color === 'black') {\n                            const moves = getValidMoves(r, c);\n                            moves.forEach(m => {\n                                let score = 0;\n                                const target = board[m.r][m.c];\n                                if (target) {\n                                    const values = { p: 1, n: 3, b: 3, r: 5, q: 9, k: 100 };\n                                    score = values[target.type] * 10;\n                                }\n                                allMoves.push({ from: { r, c }, to: m, score: score + Math.random() });\n                            });\n                        }\n                    }\n                }\n\n                if (allMoves.length > 0) {\n                    allMoves.sort((a, b) => b.score - a.score);\n                    makeMove(allMoves[0].from, allMoves[0].to);\n                }\n                isAITurn = false;\n                drawBoard();\n            }\n\n            function showResult(winner) {\n                const isWin = vsMode === 'ai' ? winner === 'white' : true;\n                const title = vsMode === 'ai' ? (isWin ? \"You Win!\" : \"AI Wins!\") : `Player ${winner === 'white' ? '1' : '2'} Wins!`;\n                \n                const modal = document.createElement('div');\n                modal.className = 'result-modal';\n                modal.innerHTML = `\n                    <div class=\"result-content\">\n                        <h2 class=\"result-title ${isWin ? 'win' : 'lose'}\">${title}</h2>\n                        <div class=\"result-buttons\">\n                            <button class=\"result-button primary\">Play Again</button>\n                            <button class=\"result-button secondary\">Menu</button>\n                        </div>\n                    </div>\n                `;\n                gameScreen.appendChild(modal);\n                modal.querySelector('.result-button.primary').onclick = () => { playSound('button_click_sound'); modal.remove(); resetGame(); };\n                modal.querySelector('.result-button.secondary').onclick = () => { playSound('button_click_sound'); modal.remove(); returnToMenu(gameScreen); };\n                playSound(isWin ? 'win_sound' : 'lose_sound');\n            }\n\n            canvas.addEventListener('mousedown', handleClick);\n            canvas.addEventListener('touchstart', (e) => { e.preventDefault(); handleClick(e); });\n            resetGame();\n        }\n\n        // ===========================\n        // LUDO (Standard Ludo Star Style)\n        // ===========================\n\n        function initLudo(gameScreen, vsMode) {\n            const canvas = gameScreen.querySelector('canvas');\n            const ctx = canvas.getContext('2d');\n            const turnIndicator = gameScreen.querySelector('.turn-indicator');\n            const GRID_SIZE = 15;\n            const boardSize = Math.min(window.innerWidth - 32, 720);\n            const cellSize = boardSize / GRID_SIZE;\n\n            canvas.width = boardSize;\n            canvas.height = boardSize;\n            \n            const COLORS = {\n                red: '#FF1744',\n                green: '#00E676',\n                yellow: '#FFEA00',\n                blue: '#2979FF',\n                bg: '#FFFFFF',\n                board: '#F5F5F5',\n                text: '#1A237E'\n            };\n\n            let currentPlayer = 'red';\n            const players = ['red', 'green', 'yellow', 'blue'];\n            let diceValue = 0;\n            let canRoll = true;\n            let gameOver = false;\n            let isAITurn = false;\n            let isAnimating = false; // Prevent any interaction during animation\n\n            // Tokens for 4 players\n            let tokens = {\n                red: Array(4).fill(0).map(() => ({p: -1, f: false})),\n                green: Array(4).fill(0).map(() => ({p: -1, f: false})),\n                yellow: Array(4).fill(0).map(() => ({p: -1, f: false})),\n                blue: Array(4).fill(0).map(() => ({p: -1, f: false}))\n            };\n\n            // Main Path (52 steps)\n            const mainPath = [\n                {r: 6, c: 1}, {r: 6, c: 2}, {r: 6, c: 3}, {r: 6, c: 4}, {r: 6, c: 5},\n                {r: 5, c: 6}, {r: 4, c: 6}, {r: 3, c: 6}, {r: 2, c: 6}, {r: 1, c: 6}, {r: 0, c: 6},\n                {r: 0, c: 7}, {r: 0, c: 8},\n                {r: 1, c: 8}, {r: 2, c: 8}, {r: 3, c: 8}, {r: 4, c: 8}, {r: 5, c: 8},\n                {r: 6, c: 9}, {r: 6, c: 10}, {r: 6, c: 11}, {r: 6, c: 12}, {r: 6, c: 13}, {r: 6, c: 14},\n                {r: 7, c: 14}, {r: 8, c: 14},\n                {r: 8, c: 13}, {r: 8, c: 12}, {r: 8, c: 11}, {r: 8, c: 10}, {r: 8, c: 9},\n                {r: 9, c: 8}, {r: 10, c: 8}, {r: 11, c: 8}, {r: 12, c: 8}, {r: 13, c: 8}, {r: 14, c: 8},\n                {r: 14, c: 7}, {r: 14, c: 6},\n                {r: 13, c: 6}, {r: 12, c: 6}, {r: 11, c: 6}, {r: 10, c: 6}, {r: 9, c: 6},\n                {r: 8, c: 5}, {r: 8, c: 4}, {r: 8, c: 3}, {r: 8, c: 2}, {r: 8, c: 1}, {r: 8, c: 0},\n                {r: 7, c: 0}, {r: 6, c: 0}\n            ];\n\n            // Re-mapped start indices to match Ludo Star: Red is Bottom Left\n            const startIndices = { \n                green: 0,   // Top-Left (0,0)\n                yellow: 13, // Top-Right (0,9)\n                blue: 26,   // Bottom-Right (9,9)\n                red: 39     // Bottom-Left (9,0)\n            };\n            \n            const homePaths = {\n                green: [{r: 7, c: 1}, {r: 7, c: 2}, {r: 7, c: 3}, {r: 7, c: 4}, {r: 7, c: 5}],\n                yellow: [{r: 1, c: 7}, {r: 2, c: 7}, {r: 3, c: 7}, {r: 4, c: 7}, {r: 5, c: 7}],\n                blue: [{r: 7, c: 13}, {r: 7, c: 12}, {r: 7, c: 11}, {r: 7, c: 10}, {r: 7, c: 9}],\n                red: [{r: 13, c: 7}, {r: 12, c: 7}, {r: 11, c: 7}, {r: 10, c: 7}, {r: 9, c: 7}]\n            };\n\n            const safeSquares = [\n                {r: 6, c: 1}, {r: 2, c: 6}, {r: 1, c: 8}, {r: 6, c: 13}, \n                {r: 8, c: 13}, {r: 12, c: 8}, {r: 13, c: 6}, {r: 8, c: 1}\n            ];\n\n            // Create 4 dice buttons for Ludo Star style - 2D dots version\n            const diceContainer = gameScreen.querySelector('.game-content');\n            players.forEach(p => {\n                let d = document.getElementById(`die-${p}`);\n                if (!d) {\n                    d = document.createElement('div');\n                    d.id = `die-${p}`;\n                    d.className = 'ludo-die';\n                    // Create 9 dots for the die face\n                    for (let i = 0; i < 9; i++) {\n                        const dot = document.createElement('div');\n                        dot.className = 'die-dot';\n                        d.appendChild(dot);\n                    }\n                    diceContainer.appendChild(d);\n                }\n                d.onclick = () => { \n                    if (canRoll && !gameOver && !isAITurn && !isAnimating && currentPlayer === p) rollDice(p); \n                };\n                updateDieUI(p, 1);\n            });\n\n            gameScreen.querySelector('.restart-btn').onclick = function() { playSound('button_click_sound'); resetGame(); };\n\n            function updateDieUI(player, value) {\n                const die = document.getElementById(`die-${player}`);\n                if (!die) return;\n                const dots = die.querySelectorAll('.die-dot');\n                const patterns = {\n                    1: [4],\n                    2: [0, 8],\n                    3: [0, 4, 8],\n                    4: [0, 2, 6, 8],\n                    5: [0, 2, 4, 6, 8],\n                    6: [0, 3, 6, 2, 5, 8]\n                };\n                dots.forEach((dot, i) => {\n                    dot.style.visibility = patterns[value].includes(i) ? 'visible' : 'hidden';\n                });\n            }\n\n            function resetGame() {\n                tokens = {\n                    red: Array(4).fill(0).map(() => ({p: -1, f: false})),\n                    green: Array(4).fill(0).map(() => ({p: -1, f: false})),\n                    yellow: Array(4).fill(0).map(() => ({p: -1, f: false})),\n                    blue: Array(4).fill(0).map(() => ({p: -1, f: false}))\n                };\n                currentPlayer = 'red'; diceValue = 0; canRoll = true; gameOver = false; isAITurn = false; isAnimating = false;\n                \n                players.forEach(p => {\n                    const d = document.getElementById(`die-${p}`);\n                    d.classList.remove('active-roll');\n                    updateDieUI(p, 1);\n                });\n                document.getElementById(`die-${currentPlayer}`).classList.add('active-roll');\n\n                drawBoard(); updateTurnIndicator();\n                const m = gameScreen.querySelector('.result-modal'); if (m) m.remove();\n            }\n\n            function rollDice(player) {\n                canRoll = false;\n                isAnimating = true;\n                const die = document.getElementById(`die-${player}`);\n                die.classList.remove('active-roll');\n                die.classList.add('rolling');\n                playSound('dice_roll_sound');\n                \n                let rolls = 0;\n                const interval = setInterval(() => {\n                    const rnd = Math.floor(Math.random() * 6) + 1;\n                    updateDieUI(player, rnd);\n                    if (++rolls >= 12) {\n                        clearInterval(interval);\n                        die.classList.remove('rolling');\n                        diceValue = Math.floor(Math.random() * 6) + 1;\n                        updateDieUI(player, diceValue);\n                        isAnimating = false;\n                        handleDiceRoll();\n                    }\n                }, 60);\n            }\n\n            function handleDiceRoll() {\n                const valid = getValidMoves();\n                if (valid.length === 0) {\n                    setTimeout(switchPlayer, 800);\n                } else if (vsMode === 'ai' && currentPlayer !== 'red') {\n                    // AI Logic: pick best token to move\n                    isAITurn = true;\n                    setTimeout(() => {\n                        const moves = getValidMoves();\n                        if (moves.length === 0) {\n                            switchPlayer();\n                            return;\n                        }\n                        // Priority: move tokens on board before coming out of base\n                        const onBoard = moves.filter(m => tokens[currentPlayer][m.idx].p !== -1);\n                        const move = onBoard.length > 0 ? onBoard[0] : moves[0];\n                        if (move) {\n                            animateMove(move.idx);\n                        } else {\n                            switchPlayer();\n                        }\n                    }, 800);\n                }\n                // For human player, wait for token click\n                drawBoard();\n            }\n\n            function getValidMoves() {\n                const moves = [];\n                const tks = tokens[currentPlayer];\n                for (let i = 0; i < tks.length; i++) {\n                    if (tks[i].f) continue;\n                    if (tks[i].p === -1) { if (diceValue === 6) moves.push({idx: i}); }\n                    else if (tks[i].p + diceValue <= 57) moves.push({idx: i});\n                }\n                return moves;\n            }\n\n            function animateMove(idx) {\n                isAnimating = true;\n                const t = tokens[currentPlayer][idx];\n                const isBasePiece = t.p === -1;\n                // Capture dice value NOW to prevent any race condition\n                const moveAmount = diceValue; \n\n                if (isBasePiece) {\n                    // Rolling 6 allows moving from base (-1) to start (0)\n                    t.p = 0;\n                    playSound('move_sound');\n                    drawBoard();\n                    setTimeout(() => {\n                        isAnimating = false;\n                        finalizeMove(idx);\n                    }, 200);\n                } else {\n                    // Regular move on board - move exactly moveAmount steps\n                    let stepsRemaining = moveAmount;\n                    const step = () => {\n                        if (stepsRemaining > 0 && t.p < 57) {\n                            t.p++;\n                            stepsRemaining--;\n                            playSound('move_sound');\n                            drawBoard();\n                            setTimeout(step, 150);\n                        } else {\n                            isAnimating = false;\n                            finalizeMove(idx);\n                        }\n                    };\n                    step();\n                }\n            }\n\n            function finalizeMove(idx) {\n                const t = tokens[currentPlayer][idx];\n                let captured = false;\n\n                if (t.p === 57) {\n                    t.f = true;\n                    playSound('win_sound');\n                    captured = true;\n                } else if (t.p < 52) {\n                    const pos = getGridCoords(currentPlayer, t.p);\n                    if (pos) {\n                        const isSafe = safeSquares.some(s => s.r === pos.r && s.c === pos.c);\n                        \n                        if (!isSafe) {\n                            players.forEach(p => {\n                                if (p === currentPlayer) return;\n                                tokens[p].forEach(ot => {\n                                    if (ot.p !== -1 && ot.p < 52 && !ot.f) {\n                                        const opos = getGridCoords(p, ot.p);\n                                        if (opos && opos.r === pos.r && opos.c === pos.c) {\n                                            ot.p = -1;\n                                            playSound('capture_sound');\n                                            captured = true;\n                                        }\n                                    }\n                                });\n                            });\n                        }\n                    }\n                }\n\n                if (checkWinner()) {\n                    gameOver = true;\n                    setTimeout(showResult, 500);\n                    return;\n                }\n\n                if (diceValue === 6 || captured) {\n                    // Extra turn - same player rolls again\n                    canRoll = true;\n                    document.getElementById(`die-${currentPlayer}`).classList.add('active-roll');\n                    updateTurnIndicator();\n                    drawBoard();\n                    if (vsMode === 'ai' && currentPlayer !== 'red') {\n                        // AI gets another roll after a delay\n                        setTimeout(() => rollDice(currentPlayer), 1000);\n                    }\n                } else {\n                    setTimeout(switchPlayer, 500);\n                }\n                drawBoard();\n            }\n\n            function getGridCoords(player, p) {\n                if (p === -1) return null;\n                if (p < 52) {\n                    const idx = (p + startIndices[player]) % 52;\n                    return mainPath[idx];\n                }\n                // Home stretch\n                const pathIdx = p - 52;\n                const path = homePaths[player];\n                if (pathIdx >= 0 && pathIdx < path.length) {\n                    return path[pathIdx];\n                }\n                // Center/Finish\n                if (p === 57) return { r: 7, c: 7 };\n                return null;\n            }\n\n            function switchPlayer() {\n                const curIdx = players.indexOf(currentPlayer);\n                currentPlayer = players[(curIdx + 1) % players.length];\n                canRoll = true;\n                diceValue = 0;\n                isAnimating = false;\n                \n                players.forEach(p => document.getElementById(`die-${p}`).classList.remove('active-roll'));\n                document.getElementById(`die-${currentPlayer}`).classList.add('active-roll');\n\n                updateTurnIndicator();\n                drawBoard();\n                if (vsMode === 'ai' && currentPlayer !== 'red') {\n                    isAITurn = true;\n                    setTimeout(() => rollDice(currentPlayer), 600);\n                } else {\n                    isAITurn = false;\n                }\n            }\n\n            function checkWinner() {\n                return tokens[currentPlayer].every(t => t.f);\n            }\n\n            function drawBoard() {\n                ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n                // Draw Wood Background for the whole canvas area\n                if (assetCache['wood_board_texture']) {\n                    const pattern = ctx.createPattern(assetCache['wood_board_texture'].img, 'repeat');\n                    ctx.fillStyle = pattern;\n                    ctx.fillRect(0, 0, canvas.width, canvas.height);\n                }\n\n                // Draw Base Backgrounds with Ludo Star vibe (gradients and shadows)\n                drawBase(0, 0, COLORS.green, 'green'); \n                drawBase(9, 0, COLORS.red, 'red');   \n                drawBase(0, 9, COLORS.yellow, 'yellow'); \n                drawBase(9, 9, COLORS.blue, 'blue');   \n\n                // Draw Paths\n                for (let r = 0; r < GRID_SIZE; r++) {\n                    for (let c = 0; c < GRID_SIZE; c++) {\n                        if ((r >= 6 && r <= 8) || (c >= 6 && c <= 8)) {\n                            if (r >= 6 && r <= 8 && c >= 6 && c <= 8) {\n                                if (r === 7 && c === 7) drawCenter();\n                                continue;\n                            }\n\n                            let cellColor = COLORS.bg;\n                            \n                            // Get home stretch squares\n                            homePaths.green.forEach(s => { if(r === s.r && c === s.c) cellColor = COLORS.green; });\n                            homePaths.yellow.forEach(s => { if(r === s.r && c === s.c) cellColor = COLORS.yellow; });\n                            homePaths.blue.forEach(s => { if(r === s.r && c === s.c) cellColor = COLORS.blue; });\n                            homePaths.red.forEach(s => { if(r === s.r && c === s.c) cellColor = COLORS.red; });\n\n                            // Start squares\n                            if (r === 6 && c === 1) cellColor = COLORS.green;\n                            if (r === 1 && c === 8) cellColor = COLORS.yellow;\n                            if (r === 8 && c === 13) cellColor = COLORS.blue;\n                            if (r === 13 && c === 6) cellColor = COLORS.red;\n\n                            ctx.save();\n                            ctx.fillStyle = cellColor;\n                            ctx.beginPath();\n                            const margin = 1;\n                            ctx.roundRect(c * cellSize + margin, r * cellSize + margin, cellSize - margin * 2, cellSize - margin * 2, 4);\n                            ctx.fill();\n                            \n                            // Add inner shadow to path cells\n                            ctx.strokeStyle = 'rgba(0,0,0,0.05)';\n                            ctx.lineWidth = 1;\n                            ctx.stroke();\n                            ctx.restore();\n                        }\n                    }\n                }\n\n                // Draw Safe Stars\n                safeSquares.forEach(s => {\n                    ctx.save();\n                    ctx.fillStyle = 'rgba(0,0,0,0.15)';\n                    ctx.font = `bold ${cellSize * 0.8}px Arial`;\n                    ctx.textAlign = 'center';\n                    ctx.textBaseline = 'middle';\n                    ctx.fillText('\u2605', s.c * cellSize + cellSize / 2, s.r * cellSize + cellSize / 2 + 2);\n                    ctx.restore();\n                });\n\n                // Draw Tokens\n                players.forEach(color => {\n                    tokens[color].forEach((t, i) => {\n                        if (t.f) return;\n                        let pos;\n                        if (t.p === -1) {\n                            const offset = color === 'red' ? {r: 10.5, c: 1.5} : (color === 'green' ? {r: 1.5, c: 1.5} : (color === 'yellow' ? {r: 1.5, c: 10.5} : {r: 10.5, c: 10.5}));\n                            pos = { r: offset.r + Math.floor(i / 2) * 2, c: offset.c + (i % 2) * 2 };\n                        } else {\n                            pos = getGridCoords(color, t.p);\n                        }\n\n                        const assetId = `ludo_token_${color}`;\n                        const asset = assetCache[assetId];\n                        const size = cellSize * 1.4;\n                        \n                        if (asset) {\n                            ctx.save();\n                            ctx.shadowBlur = 10;\n                            ctx.shadowColor = 'rgba(0,0,0,0.5)';\n                            ctx.shadowOffsetY = 4;\n                            ctx.drawImage(asset.img, pos.c * cellSize + cellSize / 2 - size / 2, pos.r * cellSize + cellSize / 2 - size * 0.95, size, size);\n                            ctx.restore();\n                        }\n\n                        // Highlight valid moves with a pulse\n                        if (!canRoll && currentPlayer === color && !isAITurn) {\n                            if (getValidMoves().some(m => m.idx === i)) {\n                                ctx.save();\n                                const glow = Math.sin(Date.now() / 150) * 8 + 10;\n                                ctx.strokeStyle = '#FFFFFF';\n                                ctx.lineWidth = 4;\n                                ctx.shadowBlur = glow;\n                                ctx.shadowColor = 'white';\n                                ctx.beginPath();\n                                ctx.arc(pos.c * cellSize + cellSize / 2, pos.r * cellSize + cellSize / 2, cellSize * 0.5, 0, Math.PI * 2);\n                                ctx.stroke();\n                                ctx.restore();\n                            }\n                        }\n                    });\n                });\n            }\n\n            function drawBase(r, c, color, colorName) {\n                ctx.save();\n                // Outer glow if current player\n                if (currentPlayer === colorName) {\n                    ctx.shadowBlur = 30;\n                    ctx.shadowColor = color;\n                }\n\n                // Base Outer with rounded corners and gradient\n                const baseGrd = ctx.createLinearGradient(c * cellSize, r * cellSize, (c+6) * cellSize, (r+6) * cellSize);\n                baseGrd.addColorStop(0, color);\n                baseGrd.addColorStop(1, adjustColor(color, -20));\n                ctx.fillStyle = baseGrd;\n                \n                ctx.beginPath();\n                ctx.roundRect(c * cellSize + 4, r * cellSize + 4, cellSize * 6 - 8, cellSize * 6 - 8, 30);\n                ctx.fill();\n                ctx.restore();\n\n                // Inner glossy area\n                ctx.save();\n                const innerGrd = ctx.createLinearGradient(c * cellSize + cellSize, r * cellSize + cellSize, c * cellSize + cellSize * 5, r * cellSize + cellSize * 5);\n                innerGrd.addColorStop(0, 'rgba(255,255,255,1)');\n                innerGrd.addColorStop(1, 'rgba(255,255,255,0.8)');\n                ctx.fillStyle = innerGrd;\n                ctx.beginPath();\n                ctx.roundRect(c * cellSize + cellSize, r * cellSize + cellSize, cellSize * 4, cellSize * 4, 20);\n                ctx.fill();\n                ctx.restore();\n\n                // Token slots with better visuals\n                for (let i = 0; i < 4; i++) {\n                    const tr = r + 1.5 + Math.floor(i / 2) * 2;\n                    const tc = c + 1.5 + (i % 2) * 2;\n                    \n                    ctx.save();\n                    ctx.fillStyle = '#E0E0E0';\n                    ctx.beginPath();\n                    ctx.arc(tc * cellSize + cellSize / 2, tr * cellSize + cellSize / 2, cellSize * 0.8, 0, Math.PI * 2);\n                    ctx.fill();\n                    \n                    ctx.fillStyle = color;\n                    ctx.globalAlpha = 0.3;\n                    ctx.beginPath();\n                    ctx.arc(tc * cellSize + cellSize / 2, tr * cellSize + cellSize / 2, cellSize * 0.6, 0, Math.PI * 2);\n                    ctx.fill();\n                    ctx.restore();\n                }\n            }\n\n            function adjustColor(hex, amt) {\n                let usePound = false;\n                if (hex[0] === \"#\") { hex = hex.slice(1); usePound = true; }\n                let num = parseInt(hex, 16);\n                let r = (num >> 16) + amt;\n                if (r > 255) r = 255; else if (r < 0) r = 0;\n                let b = ((num >> 8) & 0x00FF) + amt;\n                if (b > 255) b = 255; else if (b < 0) b = 0;\n                let g = (num & 0x0000FF) + amt;\n                if (g > 255) g = 255; else if (g < 0) g = 0;\n                return (usePound ? \"#\" : \"\") + (g | (b << 8) | (r << 16)).toString(16).padStart(6, '0');\n            }\n\n            function drawCenter() {\n                const cx = 7.5 * cellSize;\n                const cy = 7.5 * cellSize;\n                const s = 1.5 * cellSize;\n                \n                ctx.save();\n                ctx.shadowBlur = 10;\n                ctx.shadowColor = 'rgba(0,0,0,0.3)';\n                \n                // Redo center correctly to match player start/home positions\n                // Standard: BL: Red, TL: Green, TR: Yellow, BR: Blue\n                \n                // Left Triangle (Green)\n                ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(cx - s, cy - s); ctx.lineTo(cx - s, cy + s); ctx.fillStyle = COLORS.green; ctx.fill();\n                // Top Triangle (Yellow)\n                ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(cx - s, cy - s); ctx.lineTo(cx + s, cy - s); ctx.fillStyle = COLORS.yellow; ctx.fill();\n                // Right Triangle (Blue)\n                ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(cx + s, cy - s); ctx.lineTo(cx + s, cy + s); ctx.fillStyle = COLORS.blue; ctx.fill();\n                // Bottom Triangle (Red)\n                ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(cx - s, cy + s); ctx.lineTo(cx + s, cy + s); ctx.fillStyle = COLORS.red; ctx.fill();\n                \n                ctx.restore();\n            }\n\n            function updateTurnIndicator() {\n                if (gameOver) { turnIndicator.textContent = ''; return; }\n                const isPlayer = currentPlayer === 'red';\n                const name = isPlayer ? 'YOUR' : `${currentPlayer.toUpperCase()}'S`;\n                turnIndicator.textContent = `${name} TURN`;\n                turnIndicator.style.color = COLORS[currentPlayer];\n                turnIndicator.style.borderColor = COLORS[currentPlayer];\n                if (canRoll && isPlayer) turnIndicator.textContent += ' - TAP DICE!';\n            }\n\n            function handleClick(e) {\n                if (gameOver || isAITurn || isAnimating || canRoll) return;\n                const rect = canvas.getBoundingClientRect();\n                const clientX = e.clientX || (e.touches && e.touches[0] ? e.touches[0].clientX : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientX : 0));\n                const clientY = e.clientY || (e.touches && e.touches[0] ? e.touches[0].clientY : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientY : 0));\n                const x = clientX - rect.left;\n                const y = clientY - rect.top;\n                const c = Math.floor(x / cellSize);\n                const r = Math.floor(y / cellSize);\n\n                const valid = getValidMoves();\n                for (let i = 0; i < valid.length; i++) {\n                    const t = tokens[currentPlayer][valid[i].idx];\n                    let pos;\n                    if (t.p === -1) {\n                        const offset = currentPlayer === 'red' ? {r: 10.5, c: 1.5} : (currentPlayer === 'green' ? {r: 1.5, c: 1.5} : (currentPlayer === 'yellow' ? {r: 1.5, c: 10.5} : {r: 10.5, c: 10.5}));\n                        pos = { r: offset.r + Math.floor(valid[i].idx / 2) * 2, c: offset.c + (valid[i].idx % 2) * 2 };\n                    } else {\n                        pos = getGridCoords(currentPlayer, t.p);\n                    }\n                    \n                    if (pos) {\n                        const dist = Math.sqrt(Math.pow(pos.c - c, 2) + Math.pow(pos.r - r, 2));\n                        if (dist < 1.5) {\n                            animateMove(valid[i].idx);\n                            return;\n                        }\n                    }\n                }\n            }\n\n            function showResult() {\n                const winner = currentPlayer;\n                const isWin = winner === 'red';\n                const modal = document.createElement('div');\n                modal.className = 'result-modal';\n                modal.innerHTML = `\n                    <div class=\"result-content\">\n                        <h2 class=\"result-title ${isWin ? 'win' : 'lose'}\">${winner.toUpperCase()} WINS!</h2>\n                        <div class=\"result-buttons\">\n                            <button class=\"result-button primary\">Rematch</button>\n                            <button class=\"result-button secondary\">Menu</button>\n                        </div>\n                    </div>\n                `;\n                gameScreen.appendChild(modal);\n                modal.querySelector('.result-button.primary').onclick = () => { playSound('button_click_sound'); modal.remove(); resetGame(); };\n                modal.querySelector('.result-button.secondary').onclick = () => { playSound('button_click_sound'); modal.remove(); returnToMenu(gameScreen); };\n                playSound(isWin ? 'win_sound' : 'lose_sound');\n            }\n\n            canvas.addEventListener('mousedown', handleClick);\n            canvas.addEventListener('touchstart', (e) => { e.preventDefault(); handleClick(e); });\n\n            const anim = () => {\n                if (!gameOver && gameScreen.classList.contains('active')) {\n                    drawBoard();\n                    requestAnimationFrame(anim);\n                }\n            };\n            requestAnimationFrame(anim);\n            resetGame();\n        }\n\n        // ===========================\n        // CONNECT FOUR\n        // ===========================\n\n        function initConnectFour(gameScreen, vsMode) {\n            const canvas = gameScreen.querySelector('canvas');\n            const ctx = canvas.getContext('2d');\n            const turnIndicator = gameScreen.querySelector('.turn-indicator');\n\n            const boardWidth = Math.min(window.innerWidth - 32, 550);\n            const boardHeight = boardWidth * (6 / 7);\n            canvas.width = boardWidth;\n            canvas.height = boardHeight;\n\n            const colWidth = boardWidth / 7;\n            const rowHeight = boardHeight / 6;\n\n            // Game state\n            let board = Array(6).fill(null).map(() => Array(7).fill(null));\n            let currentPlayer = 'red'; // 'red' or 'yellow'\n            let gameOver = false;\n            let isAITurn = false;\n            let fallingPiece = null; // { col, startY, currentY, targetY, color }\n\n            // Setup restart button\n            gameScreen.querySelector('.restart-btn').onclick = () => {\n                playSound('button_click_sound');\n                resetGame();\n            };\n\n            function resetGame() {\n                board = Array(6).fill(null).map(() => Array(7).fill(null));\n                currentPlayer = 'red';\n                gameOver = false;\n                isAITurn = false;\n                fallingPiece = null;\n                \n                updateTurnIndicator();\n                \n                const modal = gameScreen.querySelector('.result-modal');\n                if (modal) modal.remove();\n            }\n\n            function drawPiece(col, rowY, color) {\n                const cx = col * colWidth + colWidth / 2;\n                const cy = rowY * rowHeight + rowHeight / 2;\n                const radius = Math.min(colWidth, rowHeight) * 0.38;\n\n                ctx.save();\n                ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';\n                ctx.shadowBlur = 5;\n                ctx.shadowOffsetY = 3;\n\n                const gradient = ctx.createRadialGradient(\n                    cx - radius * 0.3, cy - radius * 0.3, radius * 0.1,\n                    cx, cy, radius\n                );\n\n                if (color === 'red') {\n                    gradient.addColorStop(0, '#FF8A80');\n                    gradient.addColorStop(0.4, '#FF1744');\n                    gradient.addColorStop(1, '#B71C1C');\n                } else {\n                    gradient.addColorStop(0, '#FFE082');\n                    gradient.addColorStop(0.4, '#FFEA00');\n                    gradient.addColorStop(1, '#F57F17');\n                }\n\n                ctx.fillStyle = gradient;\n                ctx.beginPath();\n                ctx.arc(cx, cy, radius, 0, Math.PI * 2);\n                ctx.fill();\n\n                // Inner detail ring\n                ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';\n                ctx.lineWidth = 2.5;\n                ctx.beginPath();\n                ctx.arc(cx, cy, radius * 0.6, 0, Math.PI * 2);\n                ctx.stroke();\n\n                ctx.restore();\n            }\n\n            function drawBoard() {\n                ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n                // Draw background wood board texture\n                if (assetCache['wood_board_texture']) {\n                    ctx.drawImage(assetCache['wood_board_texture'].img, 0, 0, canvas.width, canvas.height);\n                } else {\n                    ctx.fillStyle = '#FFF9E1';\n                    ctx.fillRect(0, 0, canvas.width, canvas.height);\n                }\n\n                // Draw falling piece behind the front board\n                if (fallingPiece) {\n                    drawPiece(fallingPiece.col, fallingPiece.currentY, fallingPiece.color);\n                }\n\n                // Draw static pieces behind the front board\n                for (let r = 0; r < 6; r++) {\n                    for (let c = 0; c < 7; c++) {\n                        if (board[r][c]) {\n                            drawPiece(c, r, board[r][c]);\n                        }\n                    }\n                }\n\n                // Draw the blue front board with cut-out holes using 'evenodd' rule\n                ctx.save();\n                ctx.fillStyle = '#2979FF'; // Vibrant blue board\n                ctx.beginPath();\n                // Clockwise outer rectangle\n                ctx.rect(0, 0, canvas.width, canvas.height);\n                \n                // Counter-clockwise circular holes\n                for (let r = 0; r < 6; r++) {\n                    for (let c = 0; c < 7; c++) {\n                        const cx = c * colWidth + colWidth / 2;\n                        const cy = r * rowHeight + rowHeight / 2;\n                        const radius = Math.min(colWidth, rowHeight) * 0.35;\n                        ctx.moveTo(cx + radius, cy);\n                        ctx.arc(cx, cy, radius, 0, Math.PI * 2, true);\n                    }\n                }\n                ctx.fill('evenodd');\n                ctx.restore();\n\n                // Clean 3D outline borders\n                ctx.strokeStyle = '#1565C0';\n                ctx.lineWidth = 6;\n                ctx.strokeRect(3, 3, canvas.width - 6, canvas.height - 6);\n\n                // Highlight winning line if exist\n                const winState = checkWinner();\n                if (winState) {\n                    ctx.save();\n                    ctx.strokeStyle = '#FFD700'; // Golden highlight line\n                    ctx.lineWidth = 10;\n                    ctx.lineCap = 'round';\n                    ctx.shadowBlur = 15;\n                    ctx.shadowColor = '#FFD700';\n                    ctx.beginPath();\n                    const startCell = winState.line[0];\n                    const endCell = winState.line[3];\n                    ctx.moveTo(startCell[1] * colWidth + colWidth/2, startCell[0] * rowHeight + rowHeight/2);\n                    ctx.lineTo(endCell[1] * colWidth + colWidth/2, endCell[0] * rowHeight + rowHeight/2);\n                    ctx.stroke();\n                    ctx.restore();\n                }\n            }\n\n            function updateTurnIndicator() {\n                if (gameOver) {\n                    turnIndicator.textContent = 'Game Over';\n                    return;\n                }\n                const isAI = vsMode === 'ai' && currentPlayer === 'yellow';\n                const name = isAI ? 'AI' : (vsMode === 'friend' ? (currentPlayer === 'red' ? 'Player 1 (Red)' : 'Player 2 (Yellow)') : 'Your');\n                turnIndicator.textContent = `${name}'s Turn`;\n                turnIndicator.style.color = currentPlayer === 'red' ? '#FF1744' : '#FFD600';\n                turnIndicator.style.borderColor = currentPlayer === 'red' ? '#FF1744' : '#FFEA00';\n            }\n\n            function getLowestEmptyRow(col) {\n                for (let r = 5; r >= 0; r--) {\n                    if (board[r][col] === null) {\n                        return r;\n                    }\n                }\n                return -1;\n            }\n\n            function handleClick(e) {\n                if (gameOver || isAITurn || fallingPiece) return;\n\n                const rect = canvas.getBoundingClientRect();\n                const clientX = e.clientX || (e.touches && e.touches[0] ? e.touches[0].clientX : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientX : 0));\n                const clientY = e.clientY || (e.touches && e.touches[0] ? e.touches[0].clientY : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientY : 0));\n                const x = clientX - rect.left;\n                \n                const col = Math.floor(x / colWidth);\n                if (col < 0 || col >= 7) return;\n\n                const row = getLowestEmptyRow(col);\n                if (row !== -1) {\n                    dropPiece(col, row);\n                }\n            }\n\n            function dropPiece(col, row) {\n                if (row === undefined) row = getLowestEmptyRow(col);\n                if (row === -1) return;\n\n                fallingPiece = {\n                    col: col,\n                    startY: -1,\n                    currentY: -1,\n                    targetY: row,\n                    color: currentPlayer\n                };\n\n                playSound('move_sound');\n            }\n\n            function checkWinner() {\n                // Horizontal\n                for (let r = 0; r < 6; r++) {\n                    for (let c = 0; c < 4; c++) {\n                        if (board[r][c] && board[r][c] === board[r][c+1] && board[r][c] === board[r][c+2] && board[r][c] === board[r][c+3]) {\n                            return { winner: board[r][c], line: [[r, c], [r, c+1], [r, c+2], [r, c+3]] };\n                        }\n                    }\n                }\n                // Vertical\n                for (let r = 0; r < 3; r++) {\n                    for (let c = 0; c < 7; c++) {\n                        if (board[r][c] && board[r][c] === board[r+1][c] && board[r][c] === board[r+2][c] && board[r][c] === board[r+3][c]) {\n                            return { winner: board[r][c], line: [[r, c], [r+1, c], [r+2, c], [r+3, c]] };\n                        }\n                    }\n                }\n                // Diagonal positive slope\n                for (let r = 3; r < 6; r++) {\n                    for (let c = 0; c < 4; c++) {\n                        if (board[r][c] && board[r][c] === board[r-1][c+1] && board[r][c] === board[r-2][c+2] && board[r][c] === board[r-3][c+3]) {\n                            return { winner: board[r][c], line: [[r, c], [r-1, c+1], [r-2, c+2], [r-3, c+3]] };\n                        }\n                    }\n                }\n                // Diagonal negative slope\n                for (let r = 0; r < 3; r++) {\n                    for (let c = 0; c < 4; c++) {\n                        if (board[r][c] && board[r][c] === board[r+1][c+1] && board[r][c] === board[r+2][c+2] && board[r][c] === board[r+3][c+3]) {\n                            return { winner: board[r][c], line: [[r, c], [r+1, c+1], [r+2, c+2], [r+3, c+3]] };\n                        }\n                    }\n                }\n                return null;\n            }\n\n            function isBoardFull() {\n                for (let c = 0; c < 7; c++) {\n                    if (board[0][c] === null) return false;\n                }\n                return true;\n            }\n\n            function aiMove() {\n                if (gameOver || fallingPiece) return;\n\n                // 1. Check if AI can win immediately\n                for (let c = 0; c < 7; c++) {\n                    const r = getLowestEmptyRow(c);\n                    if (r !== -1) {\n                        board[r][c] = 'yellow';\n                        const win = checkWinner();\n                        board[r][c] = null;\n                        if (win && win.winner === 'yellow') {\n                            dropPiece(c, r);\n                            return;\n                        }\n                    }\n                }\n\n                // 2. Check if player would win immediately, block them\n                for (let c = 0; c < 7; c++) {\n                    const r = getLowestEmptyRow(c);\n                    if (r !== -1) {\n                        board[r][c] = 'red';\n                        const win = checkWinner();\n                        board[r][c] = null;\n                        if (win && win.winner === 'red') {\n                            dropPiece(c, r);\n                            return;\n                        }\n                    }\n                }\n\n                // 3. Strategic moves: prefer center column, check for opponent setups\n                const preferredCols = [3, 2, 4, 1, 5, 0, 6];\n                for (let c of preferredCols) {\n                    const r = getLowestEmptyRow(c);\n                    if (r !== -1) {\n                        // Avoid setting up a win for the player on the cell above\n                        if (r > 0) {\n                            board[r][c] = 'yellow';\n                            board[r-1][c] = 'red';\n                            const trap = checkWinner();\n                            board[r-1][c] = null;\n                            board[r][c] = null;\n                            if (trap && trap.winner === 'red') {\n                                continue; // Avoid this move\n                            }\n                        }\n                        dropPiece(c, r);\n                        return;\n                    }\n                }\n\n                // 4. Fallback if preferred have traps\n                for (let c of preferredCols) {\n                    const r = getLowestEmptyRow(c);\n                    if (r !== -1) {\n                        dropPiece(c, r);\n                        return;\n                    }\n                }\n            }\n\n            function showResult(winState) {\n                const winner = winState === 'draw' ? 'draw' : winState.winner;\n                const isWin = vsMode === 'ai' ? winner === 'red' : true;\n                \n                let title = '';\n                if (winner === 'draw') {\n                    title = \"It's a Draw!\";\n                } else if (vsMode === 'ai') {\n                    title = isWin ? \"You Win!\" : \"AI Wins!\";\n                } else {\n                    title = winner === 'red' ? \"Player 1 (Red) Wins!\" : \"Player 2 (Yellow) Wins!\";\n                }\n\n                const modal = document.createElement('div');\n                modal.className = 'result-modal';\n                modal.innerHTML = `\n                    <div class=\"result-content\">\n                        <h2 class=\"result-title ${winner === 'draw' ? '' : (isWin ? 'win' : 'lose')}\">${title}</h2>\n                        <div class=\"result-buttons\">\n                            <button class=\"result-button primary\">Play Again</button>\n                            <button class=\"result-button secondary\">Menu</button>\n                        </div>\n                    </div>\n                `;\n                gameScreen.appendChild(modal);\n                modal.querySelector('.result-button.primary').onclick = () => { playSound('button_click_sound'); modal.remove(); resetGame(); };\n                modal.querySelector('.result-button.secondary').onclick = () => { playSound('button_click_sound'); modal.remove(); returnToMenu(gameScreen); };\n                \n                if (winner === 'draw') {\n                    playSound('button_click_sound');\n                } else {\n                    playSound(isWin ? 'win_sound' : 'lose_sound');\n                }\n            }\n\n            canvas.addEventListener('mousedown', handleClick);\n            canvas.addEventListener('touchstart', (e) => { e.preventDefault(); handleClick(e); });\n\n            // Smooth animation loop\n            const speed = 0.25;\n            const anim = () => {\n                if (gameOver || !gameScreen.classList.contains('active')) return;\n\n                if (fallingPiece) {\n                    fallingPiece.currentY += speed;\n                    if (fallingPiece.currentY >= fallingPiece.targetY) {\n                        // Snap to final position\n                        board[fallingPiece.targetY][fallingPiece.col] = fallingPiece.color;\n                        fallingPiece = null;\n                        playSound('capture_sound'); // Solid placement click!\n\n                        // Check outcome\n                        const win = checkWinner();\n                        if (win) {\n                            gameOver = true;\n                            setTimeout(() => showResult(win), 300);\n                        } else if (isBoardFull()) {\n                            gameOver = true;\n                            setTimeout(() => showResult('draw'), 300);\n                        } else {\n                            // Alternate turns\n                            currentPlayer = currentPlayer === 'red' ? 'yellow' : 'red';\n                            updateTurnIndicator();\n\n                            if (vsMode === 'ai' && currentPlayer === 'yellow') {\n                                isAITurn = true;\n                                setTimeout(() => {\n                                    aiMove();\n                                    isAITurn = false;\n                                }, 600);\n                            }\n                        }\n                    }\n                }\n\n                drawBoard();\n                requestAnimationFrame(anim);\n            };\n\n            requestAnimationFrame(anim);\n            resetGame();\n        }\n\n        // ===========================\n        // EDIT MODE\n        // ===========================\n\n        function setupEditMode() {\n            const settingsBtn = document.getElementById('settings-btn');\n\n            settingsBtn.onclick = () => {\n                playSound('button_click_sound');\n                showEditPanel();\n            };\n        }\n\n        function showEditPanel() {\n            // Remove existing panel\n            const existing = document.querySelector('.edit-panel');\n            if (existing) {\n                existing.remove();\n                return;\n            }\n\n            const panel = document.createElement('div');\n            panel.className = 'edit-panel';\n            panel.innerHTML = `\n                <div class=\"edit-section\">\n                    <h3 class=\"edit-section-title\">Visual Themes</h3>\n                    <div class=\"edit-options\">\n                        <div class=\"edit-option ${window.gameConfig.themes.tictactoe === 'classic' ? 'selected' : ''}\" data-theme=\"classic\">\n                            Classic\n                        </div>\n                        <div class=\"edit-option ${window.gameConfig.themes.tictactoe === 'neon' ? 'selected' : ''}\" data-theme=\"neon\">\n                            Neon\n                        </div>\n                        <div class=\"edit-option ${window.gameConfig.themes.tictactoe === 'ocean' ? 'selected' : ''}\" data-theme=\"ocean\">\n                            Ocean\n                        </div>\n                    </div>\n                </div>\n            `;\n\n            document.getElementById('game-world').appendChild(panel);\n\n            // Handle theme selection\n            panel.querySelectorAll('.edit-option').forEach(option => {\n                option.addEventListener('click', () => {\n                    playSound('button_click_sound');\n                    const theme = option.dataset.theme;\n                    window.gameConfig.themes.tictactoe = theme;\n                    window.gameConfig.themes.checkers = theme;\n                    window.gameConfig.themes.ludo = theme;\n\n                    panel.querySelectorAll('.edit-option').forEach(o => o.classList.remove('selected'));\n                    option.classList.add('selected');\n                });\n            });\n        }\n    </script>\n</body>\n</html>", "game_config": "{\"settings\": {\"soundVolume\": 70, \"animationSpeed\": \"normal\", \"showValidMoves\": true}, \"themes\": {\"tictactoe\": \"classic\", \"checkers\": \"classic\", \"ludo\": \"classic\"}}", "game_design_document": "## Core Game Loop and Mechanics\n\n**Boardverse** is a board game collection hub that provides players with instant access to multiple classic games through an elegant menu system. The core experience flows as follows:\n\n1. **Game Selection**: Players start at a visually appealing main menu displaying all available board games as attractive cards/tiles\n2. **Game Setup**: After selecting a game, players choose their opponent (vs AI or vs Friend on same device) and any game-specific settings\n3. **Gameplay**: Players engage in turn-based gameplay with clear visual feedback, move validation, and game state tracking\n4. **Game Completion**: When a game ends, players see the result (win/lose/draw) and can return to the main menu or play again\n5. **Progression**: Players unlock new board themes and piece designs by playing games\n\n**What makes it compelling:**\n- Variety: Multiple games in one package keeps the experience fresh\n- Accessibility: Familiar games with intuitive touch controls\n- Polish: Smooth animations, satisfying feedback, and beautiful visuals\n- Social: Pass-and-play multiplayer for local competition\n- Progression: Unlockable cosmetics reward continued play\n\n## Starting Scope\n\nFor the initial implementation, we'll focus on creating a **polished, playable prototype** with the following:\n\n**Phase 1 (Initial Creation):**\n- Main menu with game selection grid\n- **3 fully playable games**: Tic-Tac-Toe, Checkers, and a simplified Ludo (2 players, single lap around board)\n- Each game includes:\n  - vs AI opponent (simple but competent)\n  - vs Friend (pass-and-play on same device)\n  - Clear win/lose/draw detection\n  - Smooth animations and visual feedback\n- One default board theme for each game\n- Basic sound effects (move, capture, win/lose)\n- Return to menu functionality\n\n**Future Phases (Post-Launch Iterations):**\n- Phase 2: Add Chess with full rules and AI\n- Phase 3: Add Carrom (physics-based)\n- Phase 4: Add Reversi/Othello\n- Phase 5: Add Snakes & Ladders\n- Phase 6: Unlockable board themes and piece designs\n- Phase 7: Game statistics and achievements\n- Phase 8: Difficulty levels for AI opponents\n\n## Game Perspective\n\n**Fixed-screen, top-down perspective** for all games. Each board game is displayed from a bird's eye view, centered on screen, with the game board taking up most of the viewport. UI elements (turn indicator, menu button, score) are positioned around the board edges.\n\nThe main menu uses a **scrollable grid layout** showing game cards in portrait orientation.\n\n## Game World and Level Design\n\n### Main Menu\n- **Layout**: Vertical scrolling grid of game cards (2 columns on mobile)\n- **Game Cards**: Each card shows:\n  - Game title\n  - Attractive icon/preview of the game board\n  - Brief description (e.g., \"Classic strategy for two\")\n  - \"Play\" button\n- **Header**: \"Boardverse\" title with settings icon\n- **Visual Style**: Clean, modern interface with subtle animations on card hover/tap\n\n### Individual Game Screens\n\nEach game has a consistent layout structure:\n- **Game Board**: Centered, taking up ~70% of screen height\n- **Top Bar**: Current player indicator, turn counter\n- **Bottom Bar**: Menu button, restart button\n- **Side Areas**: Used for captured pieces (Checkers) or player info\n\n**Tic-Tac-Toe:**\n- 3\u00d73 grid with clear cell boundaries\n- X and O symbols appear with animation\n- Winning line highlighted when game ends\n- Simple, clean aesthetic\n\n**Checkers:**\n- 8\u00d78 board with alternating dark/light squares\n- 12 pieces per player (red vs black)\n- Pieces have subtle 3D appearance\n- Valid moves highlighted when piece selected\n- Captured pieces shown in side areas\n- Jump sequences animated smoothly\n- King pieces have crown indicator\n\n**Ludo (Simplified):**\n- Square board with circular path (40 spaces)\n- 2 players (red vs blue) with 2 tokens each\n- Home bases in opposite corners\n- Single die shown in center when rolling\n- Token movement animated along path\n- Safe spaces marked distinctly\n- Finish zone for each player\n\n## Edit Mode Design\n\nEdit mode allows players to customize the visual appearance of games without breaking core gameplay rules.\n\n**Customizable Properties:**\n\n1. **Board Themes** (per game):\n   - Color schemes (e.g., \"Classic Wood\", \"Neon Glow\", \"Ocean Blue\")\n   - Board textures and patterns\n   - Grid line styles\n\n2. **Piece Designs** (per game):\n   - Piece shapes and styles\n   - Color customization within player color constraints\n   - Animation styles (smooth, bouncy, instant)\n\n3. **Global Settings**:\n   - Sound effects volume\n   - Music volume\n   - Animation speed (slow, normal, fast)\n   - Show valid moves toggle\n\n**Edit Mode UI:**\n- Accessed via settings icon in main menu\n- Tabbed interface: \"Themes\", \"Pieces\", \"Settings\"\n- Visual previews of each option\n- \"Apply\" and \"Reset to Default\" buttons\n\n**Constraints:**\n- Game rules cannot be modified (board size, piece movement, win conditions)\n- Player colors must remain distinguishable\n- Unlockable themes start locked (shown with lock icon)\n- At least one theme/design must always be available\n\n## Control Design\n\n**Genre:** Turn-based board games (casual, strategic)\n\n**Control Scheme:** Tap/Click-based interaction (simple and intuitive)\n\n### Mobile (Touch) Controls\n\n**Main Menu:**\n- **Tap** on game card to select game\n- **Scroll** vertically to see all games\n- **Tap** settings icon for edit mode\n\n**In-Game:**\n- **Tap** on piece to select it\n- **Tap** on valid destination to move piece\n- **Tap** die to roll (Ludo)\n- **Tap** menu button to return to main menu\n- **Tap** restart button to restart current game\n\n**Visual Feedback:**\n- Selected pieces highlighted with glow/outline\n- Valid move destinations shown with subtle indicators\n- Invalid taps trigger gentle shake animation\n- Smooth animations for all piece movements\n\n### Desktop (Keyboard/Mouse) Controls\n\n**Main Menu:**\n- **Click** on game card to select game\n- **Scroll** with mouse wheel to see all games\n- **Click** settings icon for edit mode\n\n**In-Game:**\n- **Click** on piece to select it\n- **Click** on valid destination to move piece\n- **Click** die to roll (Ludo)\n- **Click** menu button to return to main menu\n- **Click** restart button to restart current game\n- **ESC key** to return to main menu\n\n**Hover States:**\n- Pieces show subtle highlight on hover\n- Valid destinations show on piece hover\n- Buttons show hover effects\n\n### Implementation Notes\n- All interactions are single-click/tap (no drag-and-drop to keep it simple)\n- Clear visual feedback for every interaction\n- Prevent accidental double-taps with cooldown\n- Disable input during animations\n- Clear all input states when switching between games\n\n## AI Opponent Design\n\nEach game includes a simple but competent AI opponent:\n\n**Tic-Tac-Toe AI:**\n- Checks for immediate wins\n- Blocks opponent's winning moves\n- Takes center if available\n- Takes corners over edges\n- Random valid move as fallback\n\n**Checkers AI:**\n- Prioritizes captures (mandatory in checkers)\n- Evaluates board position (piece count, king count, position strength)\n- Looks 2-3 moves ahead for simple tactics\n- Protects back row until necessary\n- Advances pieces toward king row\n\n**Ludo AI:**\n- Prioritizes getting pieces out of home\n- Moves piece closest to finish when safe\n- Avoids risky moves when possible\n- Captures opponent pieces when opportunity arises\n- Balances aggression with safety\n\n**AI Behavior:**\n- Adds 0.5-1 second delay before moving (feels more natural)\n- Shows \"thinking\" indicator during delay\n- Makes moves that feel fair but challenging\n\n## Art Style and Visual Theme\n\n**Overall Aesthetic:** Modern, clean, and colorful with a touch of playfulness. The art style should feel polished and premium while remaining accessible and friendly.\n\n**Visual Direction:**\n- **Style**: Flat design with subtle gradients and shadows for depth\n- **Color Palette**: Vibrant but not overwhelming\n  - Primary: Deep blue (#2C3E50) for UI elements\n  - Accent: Warm orange (#E67E22) for highlights and CTAs\n  - Game-specific colors: Traditional board game colors (red, black, blue, yellow)\n  - Background: Soft cream (#F5F5DC) or light gray (#ECEFF1)\n- **Typography**: Bold, rounded sans-serif for titles; clean sans-serif for body text\n- **Mood**: Inviting, nostalgic, sophisticated casual\n\n**Asset Approach:**\n- **Generated Assets**: Game pieces, dice, board textures, game card icons\n- **Code-Rendered**: Game boards (grids, paths), UI elements (buttons, bars), simple shapes\n- **Consistency**: All generated assets should share the same flat-design-with-subtle-depth aesthetic\n\n**Technical Specifications:**\n- Game boards: Rendered with Canvas/CSS for precision and flexibility\n- Game pieces: Generated as PNG images with transparent backgrounds\n- UI elements: CSS-styled for crisp rendering at any size\n- Animations: CSS transitions and Canvas animations for smooth 60fps performance\n\n**Asset Sizes:**\n- Game pieces: 128\u00d7128px (scaled as needed)\n- Game card icons: 256\u00d7256px\n- Board textures: 512\u00d7512px (tileable)\n- Dice: 128\u00d7128px per face", "persistent_storage_config": {"leaderboard": {"sort_ascending": false}}, "game_metadata": {"game_id": "01KT6W51CPG6E0AH8X3TJ3GYHT", "title": "Boardverse: All-Star Classic Games", "description": "**Description:** The ultimate collection of classic board games! Play Chess, Ludo, Tic-Tac-Toe, Checkers, and more in one beautiful hub with friends or AI.", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_a07c9894-1986-42ee-89aa-d8636f70c75c.webp", "created_at": 1780494959, "updated_at": 1781537926669.7922, "art_style": null}, "task_plan": null}