{"asset_map": {"collect_sound": {"url": "https://dzmv1mxivz48k.cloudfront.net/images/Google_115298327582608514912/1777281021110_01KQ738D5P2GTTE6AR7J18X9F0.buttonpop.mp3", "type": "audio", "prompt": null, "description": "A light and rewarding pop sound effect for collecting food items in a casual tycoon game.", "subtype": null, "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.0, "loop": null, "animation": null}, "sell_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/sell_sound_37bf31c3-5807-42d3-a4b7-0e1e4cb449e4.mp3", "type": "audio", "prompt": "A high-quality, satisfying cash register 'cha-ching' combined with a light metallic coin jingle. Designed for a restaurant tycoon game, the tone should be cheerful, bright, and rewarding. It features a crisp mechanical 'ding' followed by the subtle sound of coins settling. Clean, professional sound design with a short decay, suitable for frequent UI feedback without becoming grating.", "description": "A rewarding cash register 'cha-ching' sound effect for successful sales and earning money.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.01, "loop": false, "animation": null}, "upgrade_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/upgrade_sound_a3b287cd-3c9a-4794-9526-ecf9671064a8.mp3", "type": "audio", "prompt": "Uplifting 'level up' sound effect for a casual mobile tycoon game. Features a bright, triumphant major-scale chime with a shimmering magical sparkle quality and a rising tonal motif. Sounds rewarding and high-quality, similar to a successful building upgrade or milestone achievement. Clean, crisp, one-shot with a gentle crystalline reverb tail.", "description": "An uplifting and sparkly level-up sound effect for a casual mobile tycoon game, signaling success and achievement.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.16, "loop": false, "animation": null}, "background_music": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/music/background_music_9afaa9ec-97bf-4bb4-8c33-f4bd8864956f.mp3", "type": "audio", "prompt": "Upbeat, cheerful, and lighthearted background music for a casual restaurant tycoon mobile game. The style is a blend of light jazz and Bossa Nova, featuring a bouncy nylon-string guitar rhythm, a soft walking upright bass, gentle shaker percussion, and a melodic vibraphone or electric piano lead. The tempo is a steady 110-120 BPM, creating a pleasant and productive atmosphere. Instrumental only, clean production, designed for seamless looping with a natural flow and no harsh transitions. Positive, inviting, and cozy vibes.", "description": "Upbeat and cheerful Bossa Nova inspired background music for a casual restaurant tycoon game.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 60.02938775510204, "loop": true, "animation": null}, "floor_tile": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/floor_tile_7c0aa055-fc1b-4a29-85c2-4a1ba6cdfd6a.webp", "type": "image", "prompt": "Generate a fully tileable restaurant floor texture with a classic ceramic checkered pattern in a warm tan and beige color scheme. Top-down view, pixel art style to match a casual tycoon game aesthetic. Clean, polished look with subtle shine and reflections. Ensure the texture repeats seamlessly in both horizontal and vertical directions with no visible seams or shadows at the edges. Flat lighting, high resolution, professional game asset.", "description": "A seamless tileable restaurant floor texture in a warm tan/beige checkered pattern for a top-down tycoon game.", "subtype": "ground", "aspect_ratio": [1024, 1024], "compression_suggestion": "original", "duration_secs": null, "loop": null, "animation": null}, "wall_tile": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/wall_tile_359c7012-9c53-44a6-9905-7943c4ae8fdf.webp", "type": "image", "prompt": "Generate a fully tileable red brick wall texture for a restaurant interior, designed to repeat seamlessly in both horizontal and vertical directions. Red brick pattern with light tan or cream mortar lines in a standard running bond pattern. Style should be clean pixel art with a warm, inviting color palette. Flat lighting, no shadows at edges, polished look to match the existing floor_tile. Top-down/orthographic view.", "description": "A seamless tileable red brick wall texture for a diner interior.", "subtype": "ground", "aspect_ratio": [1024, 1024], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "kitchen_floor_tile": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/kitchen_floor_tile_f9ac39b6-a640-4f5f-9018-fef9abfddd04.webp", "type": "image", "prompt": "Generate a fully tileable industrial kitchen floor texture for a fast food restaurant, designed to repeat seamlessly in both horizontal and vertical directions. Professional commercial kitchen style featuring a light gray anti-slip rubber pattern with subtle diamond plate textures and clean grid lines. Top-down orthographic view, clean and minimalist design, consistent lighting and silver-gray color palette. The texture should have subtle darker accents for depth and contrast well with the warm tan dining area. High resolution, no visible seams, game-ready texture.", "description": "A seamless gray industrial kitchen floor texture that tiles in both directions for a top-down restaurant tycoon game.", "subtype": "ground", "aspect_ratio": [1024, 1024], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "paper_money": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/paper_money_94e2dcea-fec2-4fc7-8aac-641a7f498e65.webp", "type": "image", "prompt": "Generate a clear, top-down view pixel art sprite of a stack of green paper money. The asset should feature a crisp dollar bill on top with visible edges of a stack underneath. Clean, polished game-ready asset, vibrant green colors, simple shading, transparent background, high resolution pixel art style to match a casual tycoon aesthetic.", "description": "A top-down pixel art stack of green dollar bills used as a currency pickup in the restaurant tycoon game.", "subtype": "object", "aspect_ratio": [984, 733], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "coin_icon": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/coin_icon_c6b3132a-feba-4279-8fe0-7d5573092daa.webp", "type": "image", "prompt": "Generate a vibrant, high-quality 2D game icon of a green dollar sign ($). The icon should be shiny and polished with professional lighting and shading, suitable for a tycoon game HUD. Top-down perspective, front-view, isolated on a transparent background, clean edges, stylized and bold design.", "description": "A polished green dollar sign icon for the game's currency display.", "subtype": "ui", "aspect_ratio": [597, 843], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "pickup_sound": {"url": "https://dzmv1mxivz48k.cloudfront.net/images/Google_115298327582608514912/1777282180131_01KQ74BS13DFC3NBNEKVA6QNCX.qewqe.mp3", "type": "audio", "prompt": null, "description": "A light and rewarding pop sound effect for collecting items like food or money in a casual tycoon game.", "subtype": null, "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.0, "loop": null, "animation": null}, "stickman-animated_compressed.glb": {"url": "https://dzmv1mxivz48k.cloudfront.net/images/Google_115298327582608514912/1778797075662_01KRM92Q6EVR1PWDCCRAZ78PCY.stickman-animated_compressed.glb", "type": "model_3d", "prompt": null, "description": "Compressed stickman GLB provided by user (for draco-compressed GLB).", "subtype": "glb", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": null, "loop": null, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Fast Food Tycoon 2</title>\n    <!-- <script src=\"loader.js\"></script> -->\n    <style>\n         /* CSS Reset */\n         * {\n             margin: 0;\n             padding: 0;\n             box-sizing: border-box;\n         }\n\n         :root {\n             /* Animation Timings */\n             --transition-fast: 150ms;\n             --transition-normal: 300ms;\n             --transition-slow: 500ms;\n             /* Easing Functions */\n             --ease-out: cubic-bezier(0.0, 0, 0.2, 1);\n             --ease-bounce: cubic-bezier(0.68, -0.6, 0.32, 1.6);\n             /* Spacing Scale (8px grid) */\n             --space-xs: 8px;\n             --space-sm: 16px;\n             --space-md: 24px;\n             --space-lg: 32px;\n             --space-xl: 48px;\n             /* Colors */\n             --color-gold: #FFD700;\n             --color-gold-dark: #DAA520;\n             --color-ui-bg: rgba(255, 255, 255, 0.95);\n             --color-ui-shadow: rgba(0, 0, 0, 0.15);\n             --color-text: #333;\n             --color-text-light: #666;\n             --color-success: #4CAF50;\n             --color-primary: #E74C3C;\n         }\n\n         body, html {\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         }\n\n         #game-world {\n             position: absolute;\n             top: 0;\n             left: 0;\n             width: 100%;\n             height: 100%;\n         }\n\n         /* ============================================\n      HUD STYLES\n      ============================================ */\n         #hud {\n             position: absolute;\n             top: var(--space-sm);\n             right: var(--space-sm);\n             left: auto;\n             display: flex;\n             flex-direction: column;\n             align-items: flex-end;\n             gap: var(--space-xs);\n             pointer-events: none;\n             z-index: 100;\n         }\n\n         /* RTL layout adjustments */\n         body.rtl #hud {\n             right: auto;\n             left: var(--space-sm);\n             align-items: flex-start;\n         }\n\n         body.rtl #vault-toggle,\n         body.rtl #upgrade-toggle {\n             right: auto;\n             left: var(--space-sm);\n         }\n\n         body.rtl #editor-toolbar {\n             left: auto;\n             right: var(--space-sm);\n         }\n\n         body.rtl #zoom-toggle,\n         body.rtl #cam-left-btn,\n         body.rtl #cam-right-btn {\n             left: auto;\n             right: var(--space-sm);\n         }\n\n         body.rtl #cam-left-btn {\n             right: calc(var(--space-sm) + 72px);\n         }\n\n         body.rtl #cam-right-btn {\n             right: calc(var(--space-sm) + 144px);\n         }\n\n         #currency-display {\n             display: flex;\n             align-items: center;\n             gap: 12px;\n             background: var(--color-ui-bg);\n             padding: 14px 28px;\n             border-radius: 50px;\n             box-shadow: 0 4px 16px var(--color-ui-shadow);\n             pointer-events: none;\n         }\n\n         #coin-icon {\n             width: 40px;\n             height: 40px;\n             object-fit: contain;\n         }\n\n         #currency-value {\n             font-size: 36px;\n             font-weight: 700;\n             color: black;\n             min-width: 80px;\n             text-align: center;\n         }\n\n         #stack-display {\n             background: var(--color-ui-bg);\n             padding: 10px 24px;\n             border-radius: 30px;\n             font-size: 26px;\n             font-weight: 600;\n             color: var(--color-text);\n             box-shadow: 0 2px 8px var(--color-ui-shadow);\n             display: flex;\n             align-items: center;\n             gap: 8px;\n         }\n\n         #money-stack-display {\n             background: var(--color-ui-bg);\n             padding: 10px 24px;\n             border-radius: 30px;\n             font-size: 26px;\n             font-weight: 600;\n             color: var(--color-text);\n             box-shadow: 0 2px 8px var(--color-ui-shadow);\n             display: flex;\n             align-items: center;\n             gap: 8px;\n         }\n\n         #cashier-display {\n             background: var(--color-ui-bg);\n             padding: 10px 24px;\n             border-radius: 30px;\n             font-size: 22px;\n             font-weight: 600;\n             color: var(--color-text);\n             box-shadow: 0 2px 8px var(--color-ui-shadow);\n             display: flex;\n             align-items: center;\n             gap: 8px;\n         }\n\n         #cashier-count {\n             color: var(--color-primary);\n             font-weight: 700;\n         }\n\n         #customer-count-label {\n             margin-left: 12px;\n             padding-left: 12px;\n             border-left: 2px solid #ddd;\n         }\n\n         #customer-count {\n             color: #3498DB;\n             font-weight: 700;\n         }\n\n         #cashier-earnings-label {\n             margin-left: 12px;\n             padding-left: 12px;\n             border-left: 2px solid #ddd;\n         }\n\n         #cashier-earnings {\n             color: #27AE60;\n             font-weight: 700;\n         }\n\n         /* ============================================\n      BANK VAULT UI STYLES\n      ============================================ */\n         #vault-display {\n             background: linear-gradient(135deg, rgba(44, 62, 80, 0.95), rgba(52, 73, 94, 0.95));\n             padding: 10px 24px;\n             border-radius: 30px;\n             font-size: 22px;\n             font-weight: 600;\n             color: #fff;\n             box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);\n             display: flex;\n             align-items: center;\n             gap: 8px;\n             border: 2px solid #F1C40F;\n         }\n\n         #vault-balance {\n             color: #F1C40F;\n             font-weight: 700;\n         }\n\n         #vault-panel {\n             position: absolute;\n             bottom: 0;\n             left: 0;\n             right: 0;\n             background: linear-gradient(135deg, rgba(44, 62, 80, 0.98), rgba(52, 73, 94, 0.98));\n             border-radius: 24px 24px 0 0;\n             padding: var(--space-md) var(--space-sm) var(--space-lg);\n             box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.4);\n             z-index: 125;\n             pointer-events: auto;\n             transform: translateY(100%);\n             transition: transform var(--transition-normal) var(--ease-out);\n             max-height: 70%;\n             overflow-y: auto;\n         }\n\n         #vault-panel.open {\n             transform: translateY(0);\n         }\n\n         .vault-header {\n             display: flex;\n             justify-content: space-between;\n             align-items: center;\n             margin-bottom: var(--space-md);\n             padding-bottom: var(--space-sm);\n             border-bottom: 2px solid rgba(241, 196, 15, 0.3);\n         }\n\n         .vault-header-title {\n             font-size: 28px;\n             font-weight: 700;\n             color: #F1C40F;\n             display: flex;\n             align-items: center;\n             gap: 12px;\n         }\n\n         .vault-balance-large {\n             font-size: 36px;\n             font-weight: 800;\n             color: #F1C40F;\n             text-align: center;\n             padding: 20px;\n             background: rgba(0, 0, 0, 0.3);\n             border-radius: 16px;\n             margin-bottom: var(--space-md);\n             text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n         }\n\n         .vault-section-title {\n             font-size: 20px;\n             font-weight: 600;\n             color: #ECF0F1;\n             margin: var(--space-sm) 0;\n             padding-bottom: 8px;\n             border-bottom: 1px solid rgba(255, 255, 255, 0.2);\n         }\n\n         /* ============================================\n      VAULT TABS\n      ============================================ */\n         .vault-tabs {\n             display: flex;\n             gap: 8px;\n             margin-bottom: var(--space-md);\n             padding: 4px;\n             background: rgba(0, 0, 0, 0.3);\n             border-radius: 16px;\n         }\n\n         .vault-tab {\n             flex: 1;\n             padding: 12px 16px;\n             background: transparent;\n             border: none;\n             border-radius: 12px;\n             font-size: 14px;\n             font-weight: 600;\n             color: rgba(236, 240, 241, 0.6);\n             cursor: pointer;\n             transition: all var(--transition-fast) var(--ease-out);\n             display: flex;\n             flex-direction: column;\n             align-items: center;\n             gap: 4px;\n         }\n\n         .vault-tab .tab-icon {\n             font-size: 20px;\n         }\n\n         .vault-tab .tab-label {\n             font-size: 12px;\n             text-transform: uppercase;\n             letter-spacing: 0.5px;\n         }\n\n         .vault-tab:hover {\n             color: rgba(236, 240, 241, 0.9);\n             background: rgba(255, 255, 255, 0.1);\n         }\n\n         .vault-tab.active {\n             background: linear-gradient(135deg, #F1C40F, #D4AC0D);\n             color: #2C3E50;\n             box-shadow: 0 4px 12px rgba(241, 196, 15, 0.4);\n         }\n\n         .vault-tab.active .tab-icon {\n             transform: scale(1.1);\n         }\n\n         .vault-tab-content {\n             display: none;\n             flex-direction: column;\n             gap: var(--space-sm);\n         }\n\n         .vault-tab-content.active {\n             display: flex;\n         }\n\n         .vault-scroll-container {\n             flex: 1;\n             overflow-y: auto;\n             overflow-x: hidden;\n             padding-right: 8px;\n             margin-right: -8px;\n             max-height: calc(70vh - 200px);\n         }\n\n         .vault-scroll-container::-webkit-scrollbar {\n             width: 6px;\n         }\n\n         .vault-scroll-container::-webkit-scrollbar-track {\n             background: rgba(0, 0, 0, 0.2);\n             border-radius: 3px;\n         }\n\n         .vault-scroll-container::-webkit-scrollbar-thumb {\n             background: rgba(241, 196, 15, 0.5);\n             border-radius: 3px;\n         }\n\n         .vault-scroll-container::-webkit-scrollbar-thumb:hover {\n             background: rgba(241, 196, 15, 0.7);\n         }\n\n         .vault-upgrade-list {\n             display: flex;\n             flex-direction: column;\n             gap: var(--space-sm);\n         }\n\n         .vault-upgrade-item {\n             display: flex;\n             justify-content: space-between;\n             align-items: center;\n             padding: var(--space-sm);\n             background: rgba(0, 0, 0, 0.2);\n             border-radius: 16px;\n             min-height: 80px;\n         }\n\n         .vault-upgrade-info {\n             display: flex;\n             flex-direction: column;\n             gap: 4px;\n         }\n\n         .vault-upgrade-name {\n             font-size: 18px;\n             font-weight: 600;\n             color: #ECF0F1;\n         }\n\n         .vault-upgrade-desc {\n             font-size: 14px;\n             color: rgba(236, 240, 241, 0.7);\n         }\n\n         .vault-upgrade-level {\n             font-size: 16px;\n             color: #F1C40F;\n         }\n\n         .vault-upgrade-btn {\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             gap: 8px;\n             background: linear-gradient(135deg, #F1C40F, #D4AC0D);\n             border: none;\n             border-radius: 50px;\n             padding: 14px 24px;\n             min-width: 110px;\n             min-height: 50px;\n             cursor: pointer;\n             transition: transform var(--transition-fast) var(--ease-out), box-shadow var(--transition-fast) var(--ease-out);\n             box-shadow: 0 4px 12px rgba(241, 196, 15, 0.4);\n         }\n\n         .vault-upgrade-btn:hover {\n             transform: scale(1.05);\n             box-shadow: 0 6px 16px rgba(241, 196, 15, 0.5);\n         }\n\n         .vault-upgrade-btn:active {\n             transform: scale(0.95);\n         }\n\n         .vault-upgrade-btn:disabled {\n             background: linear-gradient(135deg, #7F8C8D, #5D6D7E);\n             cursor: not-allowed;\n             box-shadow: none;\n         }\n\n         .vault-upgrade-btn:disabled:hover {\n             transform: none;\n         }\n\n         .vault-cost-value {\n             font-size: 18px;\n             font-weight: 700;\n             color: #2C3E50;\n         }\n\n         /* ============================================\n      STATS & REWARDS STYLES\n      ============================================ */\n         .vault-stats-list {\n             display: flex;\n             flex-direction: column;\n             gap: var(--space-sm);\n         }\n\n         .leaderboard-actions {\n             display: flex;\n             gap: 12px;\n             margin-bottom: var(--space-sm);\n         }\n\n         .leaderboard-btn {\n             flex: 1;\n             min-height: 64px;\n             padding: 16px 20px;\n         }\n\n         .leaderboard-status {\n             font-size: 16px;\n             font-weight: 600;\n             color: #ECF0F1;\n             margin-bottom: 10px;\n             min-height: 24px;\n         }\n\n         .leaderboard-entry {\n             display: flex;\n             justify-content: space-between;\n             align-items: center;\n             padding: 12px 16px;\n             background: rgba(0, 0, 0, 0.2);\n             border-radius: 12px;\n         }\n\n         .leaderboard-rank {\n             font-size: 16px;\n             font-weight: 700;\n             color: #F1C40F;\n             min-width: 56px;\n         }\n\n         .leaderboard-name {\n             flex: 1;\n             font-size: 15px;\n             color: #ECF0F1;\n             margin: 0 10px;\n             white-space: nowrap;\n             overflow: hidden;\n             text-overflow: ellipsis;\n         }\n\n         .leaderboard-score {\n             font-size: 16px;\n             font-weight: 700;\n             color: #2ECC71;\n         }\n\n         .vault-stat-item {\n             display: flex;\n             justify-content: space-between;\n             align-items: center;\n             padding: 12px 16px;\n             background: rgba(0, 0, 0, 0.2);\n             border-radius: 12px;\n         }\n\n         .stat-label {\n             font-size: 16px;\n             font-weight: 600;\n             color: #ECF0F1;\n         }\n\n         .stat-value {\n             font-size: 18px;\n             font-weight: 700;\n             color: #F1C40F;\n         }\n\n         .prestige-rewards-list {\n             display: flex;\n             flex-direction: column;\n             gap: 12px;\n             margin-bottom: var(--space-md);\n         }\n\n         .prestige-reward-item {\n             display: flex;\n             align-items: center;\n             gap: 12px;\n             padding: 12px 16px;\n             background: rgba(155, 89, 182, 0.2);\n             border-radius: 12px;\n             border-left: 4px solid #9B59B6;\n         }\n\n         .reward-icon {\n             font-size: 20px;\n         }\n\n         .reward-text {\n             font-size: 14px;\n             font-weight: 500;\n             color: #ECF0F1;\n         }\n\n         #vault-toggle {\n             position: absolute;\n             bottom: var(--space-sm);\n             right: var(--space-sm);\n             left: auto;\n             width: 96px;\n             height: 96px;\n             border-radius: 50%;\n             background: linear-gradient(135deg, #2C3E50, #34495E);\n             border: 3px solid #F1C40F;\n             cursor: pointer;\n             z-index: 110;\n             pointer-events: auto;\n             display: flex;\n             flex-direction: column;\n             align-items: center;\n             justify-content: center;\n             gap: 4px;\n             box-shadow: 0 4px 20px rgba(44, 62, 80, 0.5);\n             transition: transform var(--transition-fast) var(--ease-out), box-shadow var(--transition-fast) var(--ease-out);\n         }\n\n         #vault-toggle:hover {\n             transform: scale(1.05);\n             box-shadow: 0 6px 24px rgba(44, 62, 80, 0.6);\n         }\n\n         #vault-toggle:active {\n             transform: scale(0.95);\n         }\n\n         #vault-toggle .toggle-icon {\n             font-size: 32px;\n             line-height: 1;\n         }\n\n         #vault-toggle .toggle-label {\n             font-size: 12px;\n             font-weight: 600;\n             color: #F1C40F;\n             text-transform: uppercase;\n             letter-spacing: 0.5px;\n         }\n\n         .vault-close {\n             width: 48px;\n             height: 48px;\n             border-radius: 50%;\n             background: rgba(255, 255, 255, 0.1);\n             border: none;\n             cursor: pointer;\n             font-size: 24px;\n             color: #ECF0F1;\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             transition: background var(--transition-fast) var(--ease-out);\n         }\n\n         .vault-close:hover {\n             background: rgba(255, 255, 255, 0.2);\n         }\n\n         .edit-mode #vault-toggle {\n             display: none;\n         }\n\n         .edit-mode #vault-panel {\n             display: none;\n         }\n\n         /* ============================================\n      PRESTIGE SECTION STYLES\n      ============================================ */\n         .prestige-section {\n             background: linear-gradient(135deg, rgba(155, 89, 182, 0.3), rgba(142, 68, 173, 0.3));\n             border: 2px solid #9B59B6;\n             border-radius: 16px;\n             padding: var(--space-md);\n             margin-bottom: var(--space-md);\n             text-align: center;\n         }\n\n         .prestige-header {\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             gap: 12px;\n             margin-bottom: 12px;\n         }\n\n         .prestige-btn-cost {\n             font-size: 18px;\n             font-weight: 700;\n             color: #F1C40F;\n             background: rgba(0, 0, 0, 0.3);\n             padding: 4px 16px;\n             border-radius: 12px;\n         }\n\n         /* Prestige Notification */\n         #prestige-notification {\n             position: absolute;\n             top: 50%;\n             left: 50%;\n             transform: translate(-50%, -50%) scale(0);\n             background: linear-gradient(135deg, #9B59B6, #8E44AD);\n             padding: 40px 60px;\n             border-radius: 28px;\n             box-shadow: 0 12px 48px rgba(155, 89, 182, 0.6), 0 0 100px rgba(241, 196, 15, 0.3);\n             z-index: 300;\n             pointer-events: none;\n             opacity: 0;\n             transition: transform 0.5s cubic-bezier(0.68, -0.6, 0.32, 1.6), opacity 0.4s ease-out;\n             border: 4px solid #F1C40F;\n             text-align: center;\n         }\n\n         #prestige-notification.show {\n             transform: translate(-50%, -50%) scale(1);\n             opacity: 1;\n         }\n\n         #prestige-notification .notification-icon {\n             font-size: 80px;\n             display: block;\n             margin-bottom: 16px;\n             animation: prestigeStarBig 0.8s ease-out;\n         }\n\n         @keyframes prestigeStarBig {\n             0% {\n                 transform: scale(0) rotate(-180deg);\n             }\n\n             50% {\n                 transform: scale(1.2) rotate(10deg);\n             }\n\n             100% {\n                 transform: scale(1) rotate(0deg);\n             }\n         }\n\n         #prestige-notification .notification-title {\n             font-size: 36px;\n             font-weight: 800;\n             color: #F1C40F;\n             text-transform: uppercase;\n             letter-spacing: 3px;\n             text-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);\n             margin-bottom: 12px;\n         }\n\n         #prestige-notification .notification-subtitle {\n             font-size: 24px;\n             font-weight: 600;\n             color: #2ECC71;\n             margin-bottom: 8px;\n         }\n\n         #prestige-notification .notification-desc {\n             font-size: 16px;\n             color: rgba(255, 255, 255, 0.9);\n         }\n\n         /* ============================================\n      RUSH HOUR INDICATOR STYLES\n      ============================================ */\n         #rush-hour-indicator {\n             display: flex;\n             align-items: center;\n             gap: 10px;\n             background: linear-gradient(135deg, #E74C3C, #C0392B);\n             padding: 12px 24px;\n             border-radius: 30px;\n             font-size: 22px;\n             font-weight: 700;\n             color: #fff;\n             box-shadow: 0 4px 16px rgba(231, 76, 60, 0.5);\n             animation: rushPulse 0.5s ease-in-out infinite alternate;\n         }\n\n         #rush-hour-indicator.hidden {\n             display: none;\n         }\n\n         .rush-icon {\n             font-size: 28px;\n             animation: rushFireAnim 0.3s ease-in-out infinite alternate;\n         }\n\n         #rush-text {\n             text-transform: uppercase;\n             letter-spacing: 1px;\n             text-shadow: 0 2px 4px rgba(0,0,0,0.3);\n         }\n\n         #rush-timer {\n             background: rgba(0,0,0,0.3);\n             padding: 4px 12px;\n             border-radius: 15px;\n             font-size: 18px;\n         }\n\n         @keyframes rushPulse {\n             0% {\n                 transform: scale(1);\n                 box-shadow: 0 4px 16px rgba(231, 76, 60, 0.5);\n             }\n\n             100% {\n                 transform: scale(1.03);\n                 box-shadow: 0 6px 24px rgba(231, 76, 60, 0.7);\n             }\n         }\n\n         @keyframes rushFireAnim {\n             0% {\n                 transform: translateY(0) rotate(-5deg);\n             }\n\n             100% {\n                 transform: translateY(-3px) rotate(5deg);\n             }\n         }\n\n         .stack-label {\n             font-size: 24px;\n         }\n\n         #stack-count {\n             color: var(--color-primary);\n             font-weight: 700;\n         }\n\n         #money-stack-count {\n             color: var(--color-gold-dark);\n             font-weight: 700;\n         }\n\n         /* ============================================\n      UPGRADE DRAWER STYLES\n      ============================================ */\n         #upgrade-toggle {\n             position: absolute;\n             bottom: var(--space-sm);\n             right: var(--space-sm);\n             width: 96px;\n             height: 96px;\n             border-radius: 50%;\n             background: linear-gradient(135deg, #E74C3C, #C0392B);\n             border: none;\n             cursor: pointer;\n             z-index: 110;\n             pointer-events: auto;\n             display: flex;\n             flex-direction: column;\n             align-items: center;\n             justify-content: center;\n             gap: 4px;\n             box-shadow: 0 4px 20px rgba(231, 76, 60, 0.4);\n             transition: transform var(--transition-fast) var(--ease-out), box-shadow var(--transition-fast) var(--ease-out);\n         }\n\n         #upgrade-toggle:hover {\n             transform: scale(1.05);\n             box-shadow: 0 6px 24px rgba(231, 76, 60, 0.5);\n         }\n\n         #upgrade-toggle:active {\n             transform: scale(0.95);\n         }\n\n         #upgrade-toggle .toggle-icon {\n             font-size: 32px;\n             line-height: 1;\n         }\n\n         #upgrade-toggle .toggle-label {\n             font-size: 14px;\n             font-weight: 600;\n             color: #fff;\n             text-transform: uppercase;\n             letter-spacing: 0.5px;\n         }\n\n         #upgrade-panel {\n             position: absolute;\n             bottom: 0;\n             left: 0;\n             right: 0;\n             background: var(--color-ui-bg);\n             border-radius: 24px 24px 0 0;\n             padding: var(--space-md) var(--space-sm) var(--space-lg);\n             box-shadow: 0 -4px 24px var(--color-ui-shadow);\n             z-index: 120;\n             pointer-events: auto;\n             transform: translateY(100%);\n             transition: transform var(--transition-normal) var(--ease-out);\n         }\n\n         #upgrade-panel.open {\n             transform: translateY(0);\n         }\n\n         .upgrade-header {\n             display: flex;\n             justify-content: space-between;\n             align-items: center;\n             margin-bottom: var(--space-md);\n             padding-bottom: var(--space-sm);\n             border-bottom: 2px solid #eee;\n         }\n\n         .upgrade-header-title {\n             font-size: 28px;\n             font-weight: 700;\n             color: var(--color-text);\n         }\n\n         .upgrade-close {\n             width: 48px;\n             height: 48px;\n             border-radius: 50%;\n             background: #f0f0f0;\n             border: none;\n             cursor: pointer;\n             font-size: 24px;\n             color: var(--color-text-light);\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             transition: background var(--transition-fast) var(--ease-out);\n         }\n\n         .upgrade-close:hover {\n             background: #e0e0e0;\n         }\n\n         .upgrade-close:active {\n             background: #d0d0d0;\n         }\n\n         .upgrade-list {\n             display: flex;\n             flex-direction: column;\n             gap: var(--space-sm);\n         }\n\n         .upgrade-item {\n             display: flex;\n             justify-content: space-between;\n             align-items: center;\n             padding: var(--space-sm);\n             background: #f8f9fa;\n             border-radius: 16px;\n             min-height: 96px;\n         }\n\n         .upgrade-info {\n             display: flex;\n             flex-direction: column;\n             gap: 4px;\n         }\n\n         .upgrade-name {\n             font-size: 22px;\n             font-weight: 600;\n             color: var(--color-text);\n         }\n\n         .upgrade-level {\n             font-size: 18px;\n             color: var(--color-text-light);\n         }\n\n         .level-num {\n             color: var(--color-primary);\n             font-weight: 700;\n         }\n\n         .upgrade-btn {\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             gap: 8px;\n             background: linear-gradient(135deg, var(--color-gold), var(--color-gold-dark));\n             border: none;\n             border-radius: 50px;\n             padding: 16px 28px;\n             min-width: 120px;\n             min-height: 56px;\n             cursor: pointer;\n             transition: transform var(--transition-fast) var(--ease-out), box-shadow var(--transition-fast) var(--ease-out);\n             box-shadow: 0 4px 12px rgba(218, 165, 32, 0.4);\n         }\n\n         .upgrade-btn:hover {\n             transform: scale(1.05);\n             box-shadow: 0 6px 16px rgba(218, 165, 32, 0.5);\n         }\n\n         .upgrade-btn:active {\n             transform: scale(0.95);\n         }\n\n         .upgrade-btn:disabled {\n             background: linear-gradient(135deg, #bbb, #999);\n             cursor: not-allowed;\n             box-shadow: none;\n         }\n\n         .upgrade-btn:disabled:hover {\n             transform: none;\n         }\n\n         .cost-value {\n             font-size: 22px;\n             font-weight: 700;\n             color: #fff;\n             text-shadow: 0 1px 2px rgba(0,0,0,0.2);\n         }\n\n         .cost-icon {\n             width: 24px;\n             height: 24px;\n             object-fit: contain;\n         }\n\n         /* ============================================\n      VIRTUAL JOYSTICK STYLES\n      ============================================ */\n         #joystick-zone {\n             position: absolute;\n             bottom: 0;\n             left: 0;\n             width: 50%;\n             height: 40%;\n             z-index: 50;\n             pointer-events: auto;\n         }\n\n         #joystick-base {\n             position: absolute;\n             width: 120px;\n             height: 120px;\n             border-radius: 50%;\n             background: radial-gradient(circle, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.1) 100%);\n             border: 3px solid rgba(255, 255, 255, 0.4);\n             display: flex;\n             justify-content: center;\n             align-items: center;\n             pointer-events: none;\n             bottom: 30px;\n             left: 30px;\n             box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15), inset 0 0 30px rgba(255, 255, 255, 0.1);\n         }\n\n         #joystick-base::before {\n             content: '';\n             position: absolute;\n             width: 40px;\n             height: 40px;\n             border-radius: 50%;\n             border: 2px dashed rgba(255, 255, 255, 0.3);\n         }\n\n         #joystick-stick {\n             width: 50px;\n             height: 50px;\n             border-radius: 50%;\n             background: linear-gradient(145deg, rgba(255, 255, 255, 0.95), rgba(230, 230, 230, 0.9));\n             box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25), inset 0 2px 4px rgba(255, 255, 255, 0.8);\n             transition: transform 0.05s ease-out;\n         }\n\n         #joystick-stick::after {\n             content: '';\n             position: absolute;\n             top: 50%;\n             left: 50%;\n             transform: translate(-50%, -50%);\n             width: 20px;\n             height: 20px;\n             border-radius: 50%;\n             background: radial-gradient(circle, rgba(200, 200, 200, 0.6) 0%, transparent 70%);\n         }\n\n         /* Direction arrows on joystick base */\n         .joystick-arrow {\n             position: absolute;\n             width: 0;\n             height: 0;\n             border-style: solid;\n             opacity: 0.4;\n         }\n\n         .joystick-arrow.up {\n             top: 8px;\n             left: 50%;\n             transform: translateX(-50%);\n             border-width: 0 8px 10px 8px;\n             border-color: transparent transparent rgba(255, 255, 255, 0.8) transparent;\n         }\n\n         .joystick-arrow.down {\n             bottom: 8px;\n             left: 50%;\n             transform: translateX(-50%);\n             border-width: 10px 8px 0 8px;\n             border-color: rgba(255, 255, 255, 0.8) transparent transparent transparent;\n         }\n\n         .joystick-arrow.left {\n             left: 8px;\n             top: 50%;\n             transform: translateY(-50%);\n             border-width: 8px 10px 8px 0;\n             border-color: transparent rgba(255, 255, 255, 0.8) transparent transparent;\n         }\n\n         .joystick-arrow.right {\n             right: 8px;\n             top: 50%;\n             transform: translateY(-50%);\n             border-width: 8px 0 8px 10px;\n             border-color: transparent transparent transparent rgba(255, 255, 255, 0.8);\n         }\n\n         #joystick-base.active {\n             background: radial-gradient(circle, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0.15) 100%);\n             border-color: rgba(255, 255, 255, 0.6);\n         }\n\n         #joystick-base.active #joystick-stick {\n             background: linear-gradient(145deg, #ffffff, #e8e8e8);\n             box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3), inset 0 2px 4px rgba(255, 255, 255, 0.9);\n         }\n\n         /* ============================================\n      COLLECTION PROGRESS BAR\n      ============================================ */\n         #collection-progress {\n             position: absolute;\n             bottom: 200px;\n             left: 50%;\n             transform: translateX(-50%);\n             width: 200px;\n             height: 24px;\n             background: rgba(0, 0, 0, 0.5);\n             border-radius: 12px;\n             z-index: 80;\n             pointer-events: none;\n             border: 3px solid rgba(255, 255, 255, 0.3);\n         }\n\n         #progress-bar {\n             position: absolute;\n             top: 0;\n             left: 0;\n             height: 100%;\n             width: 0%;\n             background: linear-gradient(90deg, var(--color-success), #8BC34A);\n             border-radius: 9px;\n             transition: width 0.1s linear;\n             box-shadow: 0 0 10px rgba(76, 175, 80, 0.5);\n         }\n\n         #collection-progress.hidden {\n             display: none;\n         }\n\n         #collection-label {\n             position: absolute;\n             top: 50%;\n             left: 50%;\n             transform: translate(-50%, -50%);\n             font-size: 14px;\n             font-weight: 700;\n             color: #fff;\n             text-shadow: 0 1px 2px rgba(0,0,0,0.5);\n             z-index: 1;\n         }\n\n         /* ============================================\n      ANIMATIONS\n      ============================================ */\n         @keyframes pulse {\n             0%, 100% {\n                 transform: scale(1);\n             }\n\n             50% {\n                 transform: scale(1.1);\n             }\n         }\n\n         /* ============================================\n      TUTORIAL COMPLETE OVERLAY\n      ============================================ */\n         #tutorial-complete {\n             position: absolute;\n             inset: 0;\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             z-index: 400;\n             pointer-events: none;\n             opacity: 0;\n             transition: opacity 0.4s ease-out;\n         }\n\n         #tutorial-complete.show {\n             opacity: 1;\n         }\n\n         .tutorial-complete-card {\n             background: linear-gradient(135deg, rgba(241, 196, 15, 0.95), rgba(230, 126, 34, 0.95));\n             border-radius: 28px;\n             padding: 32px 48px;\n             text-align: center;\n             box-shadow: 0 12px 48px rgba(0, 0, 0, 0.35);\n             border: 4px solid rgba(255, 255, 255, 0.8);\n             color: #2c3e50;\n             font-weight: 800;\n         }\n\n         .tutorial-complete-title {\n             font-size: 36px;\n             text-transform: uppercase;\n             letter-spacing: 2px;\n         }\n\n         .tutorial-complete-subtitle {\n             margin-top: 8px;\n             font-size: 20px;\n             font-weight: 600;\n             color: rgba(44, 62, 80, 0.85);\n         }\n\n         #confetti-layer {\n             position: absolute;\n             inset: 0;\n             overflow: hidden;\n             pointer-events: none;\n         }\n\n         .confetti-piece {\n             position: absolute;\n             width: 12px;\n             height: 18px;\n             opacity: 0.9;\n             transform: translateY(-10vh) rotate(0deg);\n             animation: confettiFall 1.8s ease-in forwards;\n         }\n\n         @keyframes confettiFall {\n             0% {\n                 transform: translateY(-10vh) rotate(0deg);\n                 opacity: 1;\n             }\n             100% {\n                 transform: translateY(110vh) rotate(540deg);\n                 opacity: 0;\n             }\n         }\n\n         @keyframes float {\n             0%, 100% {\n                 transform: translateY(0);\n             }\n\n             50% {\n                 transform: translateY(-5px);\n             }\n         }\n\n         @keyframes coinPop {\n             0% {\n                 transform: scale(1);\n             }\n\n             50% {\n                 transform: scale(1.2);\n             }\n\n             100% {\n                 transform: scale(1);\n             }\n         }\n\n         .currency-pop {\n             animation: coinPop 0.4s var(--ease-bounce);\n         }\n\n         /* ============================================\n      RESPONSIVE ADJUSTMENTS\n      ============================================ */\n         /* Hide joystick on desktop */\n         .desktop #joystick-zone {\n             display: none;\n         }\n\n         /* Edit mode styles */\n         .edit-mode #upgrade-panel {\n             display: none;\n         }\n\n         .edit-mode #upgrade-toggle {\n             display: none;\n         }\n\n         .edit-mode #joystick-zone {\n             display: none;\n         }\n\n         /* Reduced motion */\n         @media (prefers-reduced-motion: reduce) {\n             #upgrade-panel {\n                 transition: none;\n             }\n\n             .upgrade-btn, #upgrade-toggle {\n                 transition: none;\n             }\n\n             .currency-pop {\n                 animation: none;\n             }\n         }\n\n         /* ============================================\n      LEVEL EDITOR STYLES\n      ============================================ */\n         #editor-toolbar {\n             position: absolute;\n             top: var(--space-sm);\n             left: var(--space-sm);\n             background: var(--color-ui-bg);\n             border-radius: 16px;\n             padding: var(--space-sm);\n             box-shadow: 0 4px 16px var(--color-ui-shadow);\n             z-index: 100;\n             pointer-events: auto;\n             display: none;\n             flex-direction: column;\n             gap: var(--space-xs);\n             min-width: 140px;\n         }\n\n         .edit-mode #editor-toolbar {\n             display: flex;\n         }\n\n         .editor-title {\n             font-size: 18px;\n             font-weight: 700;\n             color: var(--color-text);\n             text-align: center;\n             padding-bottom: var(--space-xs);\n             border-bottom: 2px solid #eee;\n         }\n\n         .editor-buttons {\n             display: flex;\n             flex-direction: column;\n             gap: 8px;\n         }\n\n         .editor-btn {\n             display: flex;\n             align-items: center;\n             gap: 10px;\n             padding: 12px 16px;\n             background: #f0f0f0;\n             border: 2px solid transparent;\n             border-radius: 12px;\n             cursor: pointer;\n             transition: all var(--transition-fast) var(--ease-out);\n             min-height: 52px;\n         }\n\n         .editor-btn:hover {\n             background: #e0e0e0;\n             border-color: var(--color-primary);\n         }\n\n         .editor-btn:active {\n             transform: scale(0.95);\n         }\n\n         .editor-btn .btn-icon {\n             font-size: 24px;\n             line-height: 1;\n         }\n\n         .editor-btn .btn-label {\n             font-size: 16px;\n             font-weight: 600;\n             color: var(--color-text);\n         }\n\n         .editor-hint {\n             font-size: 12px;\n             color: var(--color-text-light);\n             text-align: center;\n             padding-top: var(--space-xs);\n             border-top: 1px solid #eee;\n         }\n\n         /* Selection Info Panel */\n         #selection-info {\n             position: absolute;\n             top: var(--space-sm);\n             right: var(--space-sm);\n             background: var(--color-ui-bg);\n             padding: 12px 20px;\n             border-radius: 30px;\n             box-shadow: 0 4px 16px var(--color-ui-shadow);\n             z-index: 100;\n             pointer-events: auto;\n             display: flex;\n             align-items: center;\n             gap: 12px;\n         }\n\n         #selection-info.hidden {\n             display: none;\n         }\n\n         #selection-name {\n             font-size: 18px;\n             font-weight: 600;\n             color: var(--color-text);\n         }\n         #delete-selected-btn, #copy-selected-btn {\n             width: 36px;\n             height: 36px;\n             border-radius: 50%;\n             background: #f0f0f0;\n             border: none;\n             cursor: pointer;\n             font-size: 18px;\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             transition: all var(--transition-fast) var(--ease-out);\n         }\n\n         .rotate-btn {\n             width: 36px;\n             height: 36px;\n             border-radius: 50%;\n             background: #f0f0f0;\n             border: none;\n             cursor: pointer;\n             font-size: 18px;\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             transition: all var(--transition-fast) var(--ease-out);\n         }\n\n         .rotate-btn:hover {\n             background: #e8f0ff;\n         }\n\n         #delete-selected-btn:hover {\n             background: #ffdddd;\n             color: var(--color-primary);\n         }\n\n         #delete-selected-btn:active, #copy-selected-btn:active {\n             transform: scale(0.9);\n         }\n\n         #copy-selected-btn:hover {\n             background: #ddeeff;\n             color: #3498DB;\n         }\n\n         #delete-selected-btn.hidden, #copy-selected-btn.hidden {\n             display: none;\n         }\n\n         #deselect-btn {\n             width: 36px;\n             height: 36px;\n             border-radius: 50%;\n             background: #f0f0f0;\n             border: none;\n             cursor: pointer;\n             font-size: 18px;\n             color: var(--color-text-light);\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             transition: all var(--transition-fast) var(--ease-out);\n         }\n\n         #deselect-btn:hover {\n             background: #e0e0e0;\n             color: var(--color-primary);\n         }\n\n         /* Edit mode adjustments */\n         .edit-mode #hud {\n             top: auto;\n             bottom: 120px;\n         }\n\n         .edit-mode #currency-display, .edit-mode #stack-display, .edit-mode #money-stack-display {\n             transform: scale(0.8);\n         }\n\n         #zoom-toggle {\n             position: absolute;\n             top: var(--space-sm);\n             left: var(--space-sm);\n             width: 64px;\n             height: 64px;\n             border-radius: 50%;\n             background: var(--color-ui-bg);\n             border: 2px solid #ddd;\n             cursor: pointer;\n             z-index: 110;\n             pointer-events: auto;\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             box-shadow: 0 4px 12px var(--color-ui-shadow);\n             transition: transform 150ms ease-out;\n         }\n\n         #zoom-toggle:hover {\n             transform: scale(1.05);\n         }\n\n         #zoom-toggle:active {\n             transform: scale(0.95);\n         }\n\n         #zoom-toggle .toggle-icon {\n             font-size: 28px;\n             line-height: 1;\n         }\n\n         .edit-mode #zoom-toggle {\n             display: none;\n         }\n\n         #cam-left-btn {\n             position: absolute;\n             top: var(--space-sm);\n             left: calc(var(--space-sm) + 72px);\n             width: 64px;\n             height: 64px;\n             border-radius: 50%;\n             background: var(--color-ui-bg);\n             border: 2px solid #ddd;\n             cursor: pointer;\n             z-index: 110;\n             pointer-events: auto;\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             box-shadow: 0 4px 12px var(--color-ui-shadow);\n             transition: transform 150ms ease-out;\n         }\n\n         #cam-left-btn:hover {\n             transform: scale(1.05);\n         }\n\n         #cam-left-btn:active {\n             transform: scale(0.95);\n         }\n\n         #cam-left-btn .toggle-icon {\n             font-size: 28px;\n             line-height: 1;\n         }\n\n         .edit-mode #cam-left-btn {\n             display: none;\n         }\n\n         #cam-right-btn {\n             position: absolute;\n             top: var(--space-sm);\n             left: calc(var(--space-sm) + 144px);\n             width: 64px;\n             height: 64px;\n             border-radius: 50%;\n             background: var(--color-ui-bg);\n             border: 2px solid #ddd;\n             cursor: pointer;\n             z-index: 110;\n             pointer-events: auto;\n             display: flex;\n             align-items: center;\n             justify-content: center;\n             box-shadow: 0 4px 12px var(--color-ui-shadow);\n             transition: transform 150ms ease-out;\n         }\n\n         #cam-right-btn:hover {\n             transform: scale(1.05);\n         }\n\n         #cam-right-btn:active {\n             transform: scale(0.95);\n         }\n\n         #cam-right-btn .toggle-icon {\n             font-size: 28px;\n             line-height: 1;\n         }\n\n         .edit-mode #cam-right-btn {\n             display: none;\n         }\n\n         /* ============================================\n      HIRE NOTIFICATION STYLES\n      ============================================ */\n         #hire-notification {\n             position: absolute;\n             top: 50%;\n             left: 50%;\n             transform: translate(-50%, -50%) scale(0);\n             background: linear-gradient(135deg, rgba(39, 174, 96, 0.95), rgba(46, 204, 113, 0.95));\n             padding: 24px 40px;\n             border-radius: 20px;\n             box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n             z-index: 200;\n             pointer-events: none;\n             display: flex;\n             flex-direction: column;\n             align-items: center;\n             gap: 12px;\n             opacity: 0;\n             transition: transform 0.4s var(--ease-bounce), opacity 0.3s ease-out;\n         }\n\n         #hire-notification.show {\n             transform: translate(-50%, -50%) scale(1);\n             opacity: 1;\n         }\n\n         #hire-notification .notification-icon {\n             font-size: 64px;\n             animation: notificationBounce 0.6s ease-out;\n         }\n\n         #hire-notification .notification-title {\n             font-size: 28px;\n             font-weight: 700;\n             color: #fff;\n             text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n         }\n\n         #hire-notification .notification-subtitle {\n             font-size: 18px;\n             font-weight: 500;\n             color: rgba(255, 255, 255, 0.9);\n         }\n\n         @keyframes notificationBounce {\n             0% {\n                 transform: scale(0) rotate(-20deg);\n             }\n\n             50% {\n                 transform: scale(1.3) rotate(10deg);\n             }\n\n             70% {\n                 transform: scale(0.9) rotate(-5deg);\n             }\n\n             100% {\n                 transform: scale(1) rotate(0deg);\n             }\n         }\n\n         /* ============================================\n      OFFLINE EARNINGS NOTIFICATION STYLES\n      ============================================ */\n         #offline-earnings-notification {\n             position: absolute;\n             top: 50%;\n             left: 50%;\n             transform: translate(-50%, -50%) scale(0);\n             background: linear-gradient(135deg, rgba(255, 215, 0, 0.98), rgba(255, 193, 7, 0.98));\n             padding: 40px 50px;\n             border-radius: 28px;\n             box-shadow: 0 12px 48px rgba(0, 0, 0, 0.4), 0 0 100px rgba(255, 215, 0, 0.3);\n             z-index: 250;\n             pointer-events: auto;\n             opacity: 0;\n             transition: transform 0.5s cubic-bezier(0.68, -0.6, 0.32, 1.6), opacity 0.4s ease-out;\n             border: 4px solid rgba(255, 255, 255, 0.5);\n         }\n\n         #offline-earnings-notification.show {\n             transform: translate(-50%, -50%) scale(1);\n             opacity: 1;\n         }\n\n         .offline-earnings-content {\n             display: flex;\n             flex-direction: column;\n             align-items: center;\n             gap: 20px;\n         }\n\n         #offline-earnings-notification .notification-icon {\n             font-size: 80px;\n             animation: offlineIconBounce 0.8s ease-out;\n             text-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);\n         }\n\n         #offline-earnings-notification .notification-title {\n             font-size: 36px;\n             font-weight: 800;\n             color: #8B4513;\n             text-shadow: 0 2px 4px rgba(255, 255, 255, 0.5);\n             text-transform: uppercase;\n             letter-spacing: 2px;\n         }\n\n         .offline-earnings-details {\n             display: flex;\n             flex-direction: column;\n             align-items: center;\n             gap: 12px;\n             padding: 20px 0;\n             border-top: 3px solid rgba(139, 69, 19, 0.3);\n             border-bottom: 3px solid rgba(139, 69, 19, 0.3);\n             min-width: 300px;\n         }\n\n         .time-away {\n             font-size: 20px;\n             font-weight: 600;\n             color: rgba(139, 69, 19, 0.9);\n         }\n\n         .earnings-amount {\n             font-size: 28px;\n             font-weight: 800;\n             color: #27AE60;\n             text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n             animation: earningsGlow 1.5s ease-in-out infinite;\n         }\n\n         .collect-btn {\n             padding: 16px 48px;\n             font-size: 24px;\n             font-weight: 700;\n             color: #fff;\n             background: linear-gradient(135deg, #27AE60, #229954);\n             border: none;\n             border-radius: 50px;\n             cursor: pointer;\n             box-shadow: 0 4px 16px rgba(39, 174, 96, 0.4);\n             transition: all 0.2s ease;\n             text-transform: uppercase;\n             letter-spacing: 1px;\n         }\n\n         .collect-btn:hover {\n             transform: scale(1.05);\n             box-shadow: 0 6px 20px rgba(39, 174, 96, 0.5);\n             background: linear-gradient(135deg, #2ECC71, #27AE60);\n         }\n\n         .collect-btn:active {\n             transform: scale(0.95);\n         }\n\n         @keyframes offlineIconBounce {\n             0% {\n                 transform: scale(0) rotate(-45deg);\n             }\n\n             50% {\n                 transform: scale(1.4) rotate(20deg);\n             }\n\n             70% {\n                 transform: scale(0.9) rotate(-10deg);\n             }\n\n             100% {\n                 transform: scale(1) rotate(0deg);\n             }\n         }\n\n         @keyframes earningsGlow {\n             0%, 100% {\n                 transform: scale(1);\n             }\n\n             50% {\n                 transform: scale(1.05);\n             }\n         }\n\n         /* ============================================\n      RUSH HOUR NOTIFICATION STYLES\n      ============================================ */\n         #rush-notification {\n             position: absolute;\n             top: 50%;\n             left: 50%;\n             transform: translate(-50%, -50%) scale(0);\n             background: linear-gradient(135deg, #E74C3C, #C0392B);\n             padding: 30px 50px;\n             border-radius: 24px;\n             box-shadow: 0 8px 40px rgba(231, 76, 60, 0.6);\n             z-index: 200;\n             pointer-events: none;\n             display: flex;\n             flex-direction: column;\n             align-items: center;\n             gap: 12px;\n             opacity: 0;\n             transition: transform 0.5s cubic-bezier(0.68, -0.6, 0.32, 1.6), opacity 0.3s ease-out;\n         }\n\n         #rush-notification.show {\n             transform: translate(-50%, -50%) scale(1);\n             opacity: 1;\n         }\n\n         #rush-notification .notification-icon {\n             font-size: 72px;\n             animation: rushFireAnim 0.2s ease-in-out infinite alternate;\n         }\n\n         #rush-notification .notification-title {\n             font-size: 36px;\n             font-weight: 800;\n             color: #fff;\n             text-transform: uppercase;\n             letter-spacing: 2px;\n             text-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);\n         }\n\n         #rush-notification .notification-subtitle {\n             font-size: 20px;\n             font-weight: 500;\n             color: rgba(255, 255, 255, 0.9);\n         }\n\n         /* ============================================\n      LOADING SCREEN STYLES\n      ============================================ */\n         #loading-screen {\n             position: absolute;\n             top: 0;\n             left: 0;\n             width: 100%;\n             height: 100%;\n             background: #000000;\n             display: flex;\n             flex-direction: column;\n             justify-content: center;\n             align-items: center;\n             z-index: 1000;\n             transition: opacity 0.5s ease-out;\n         }\n\n         #loading-screen.hidden {\n             opacity: 0;\n             pointer-events: none;\n         }\n\n         .loading-content {\n             display: flex;\n             flex-direction: column;\n             align-items: center;\n             gap: 32px;\n         }\n\n         .loading-icon {\n             font-size: 80px;\n             animation: loadingBounce 1s ease-in-out infinite;\n         }\n\n         @keyframes loadingBounce {\n             0%, 100% {\n                 transform: translateY(0) scale(1);\n             }\n\n             50% {\n                 transform: translateY(-15px) scale(1.1);\n             }\n         }\n\n         .loading-title {\n             font-size: 36px;\n             font-weight: 700;\n             color: #FFFFFF;\n             text-align: center;\n         }\n\n         .loading-bar-container {\n             width: 320px;\n             height: 28px;\n             background: rgba(0, 0, 0, 0.1);\n             border-radius: 14px;\n             overflow: visible;\n             box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.15);\n             position: relative;\n             border: 3px solid rgba(255, 255, 255, 0.5);\n             margin: 10px 0;\n         }\n\n         .loading-bar-fill {\n             height: 100%;\n             width: 0%;\n             background: repeating-linear-gradient( 45deg, var(--color-primary), var(--color-primary) 15px, #F39C12 15px, #F39C12 30px );\n             background-size: 42px 42px;\n             border-radius: 10px;\n             transition: width 0.3s ease-out;\n             position: relative;\n             overflow: hidden;\n             animation: moveStripes 1s linear infinite;\n         }\n\n         .loading-bar-fill::after {\n             content: '';\n             position: absolute;\n             top: 0;\n             left: 0;\n             right: 0;\n             bottom: 0;\n             background: linear-gradient( 90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100% );\n             animation: loadingShine 1.5s ease-in-out infinite;\n         }\n\n         .loading-emoji {\n             position: absolute;\n             top: 50%;\n             left: 0%;\n             transform: translate(-50%, -50%);\n             font-size: 40px;\n             transition: left 0.3s ease-out;\n             filter: drop-shadow(0 4px 6px rgba(0,0,0,0.3));\n             z-index: 2;\n             animation: loadingEmojiBounce 0.4s ease-in-out infinite alternate;\n         }\n         @keyframes loadingEmojiBounce {\n             0% {\n                 transform: translate(-50%, -50%) scale(1) rotate(-10deg);\n             }\n\n             100% {\n                 transform: translate(-50%, -65%) scale(1.1) rotate(10deg);\n             }\n         }\n\n         @keyframes moveStripes {\n             0% {\n                 background-position: 0 0;\n             }\n\n             100% {\n                 background-position: 42px 0;\n             }\n         }\n\n         @keyframes loadingShine {\n             0% {\n                 transform: translateX(-100%);\n             }\n\n             100% {\n                 transform: translateX(100%);\n             }\n         }\n\n         .loading-text {\n             font-size: 18px;\n             font-weight: 600;\n             color: #CCCCCC;\n         }\n\n         .loading-tip {\n             font-size: 14px;\n             color: #CCCCCC;\n         }\n    </style>\n  </head>\n  <body>\n    <div id=\"loading-screen\">\n      <div class=\"loading-content\">\n        <div class=\"loading-icon\">\ud83c\udf54</div>\n        <div class=\"loading-title\" id=\"loading-title\" data-i18n=\"loading.title\">Fast Food Tycoon 2</div>\n        <div class=\"loading-bar-container\">\n          <div class=\"loading-bar-fill\" id=\"loading-bar\"></div>\n          <div class=\"loading-emoji\" id=\"loading-emoji\">\ud83c\udf5f</div>\n        </div>\n        <div class=\"loading-text\" id=\"loading-text\" data-i18n=\"loading.text\">Loading...</div>\n        <div class=\"loading-tip\" id=\"loading-tip\" data-i18n=\"loading.tip\">Tip: Cook different foods and sell them to customers!</div>\n      </div>\n    </div>\n    <div id=\"game-world\">\n      <canvas id=\"game-canvas\"></canvas>\n\n      <!-- Mobile Joystick (touch controls) -->\n      <div id=\"joystick-zone\" aria-hidden=\"false\">\n        <div id=\"joystick-base\">\n          <div class=\"joystick-arrow up\"></div>\n          <div class=\"joystick-arrow down\"></div>\n          <div class=\"joystick-arrow left\"></div>\n          <div class=\"joystick-arrow right\"></div>\n          <div id=\"joystick-stick\"></div>\n        </div>\n      </div>\n      <!-- Editor Toolbar (Edit Mode) -->\n      <div id=\"editor-toolbar\">\n        <div class=\"editor-title\" data-i18n=\"editor.title\">Level Editor</div>\n        <div class=\"editor-buttons\">\n          <button class=\"editor-btn\" id=\"add-grill-btn\">\n            <span class=\"btn-icon\">\ud83c\udf73</span>\n            <span class=\"btn-label\" data-i18n=\"editor.addGrill\">Add Grill</span>\n          </button>\n          <button class=\"editor-btn\" id=\"add-fryer-btn\">\n            <span class=\"btn-icon\">\ud83c\udf5f</span>\n            <span class=\"btn-label\" data-i18n=\"editor.addFryer\">Add Fryer</span>\n          </button>\n          <button class=\"editor-btn\" id=\"add-drink-btn\">\n            <span class=\"btn-icon\">\ud83e\udd64</span>\n            <span class=\"btn-label\" data-i18n=\"editor.addDrink\">Add Drink Station</span>\n          </button>\n          <button class=\"editor-btn\" id=\"select-dining-tile-btn\">\n            <span class=\"btn-icon\">\ud83e\ude91</span>\n            <span class=\"btn-label\" data-i18n=\"editor.addDiningTable\">Add Dining Table</span>\n          </button>\n          <button class=\"editor-btn\" id=\"select-self-order-btn\">\n            <span class=\"btn-icon\">\ud83d\udcf1</span>\n            <span class=\"btn-label\" data-i18n=\"editor.editSelfOrder\">Edit Self-Order</span>\n          </button>\n          <div class=\"editor-hint\" data-i18n=\"editor.hint\">Click objects to move them</div>\n        </div>\n      </div>\n      <!-- Selection Info Panel -->\n      <div id=\"selection-info\" class=\"hidden\">\n        <span id=\"selection-name\"></span>\n        <button id=\"copy-selected-btn\" class=\"hidden\" title=\"Duplicate\">\ud83d\udccb</button>\n        <button id=\"delete-selected-btn\" class=\"hidden\" title=\"Delete\">\ud83d\uddd1\ufe0f</button>\n        <button id=\"rotate-left-btn\" class=\"rotate-btn hidden\" title=\"Rotate -90\u00b0\">\u293a</button>\n        <button id=\"rotate-right-btn\" class=\"rotate-btn hidden\" title=\"Rotate +90\u00b0\">\u293b</button>\n        <button id=\"deselect-btn\" title=\"Deselect\">\u2715</button>\n      </div>\n      <!-- Tutorial Hint Overlay -->\n      <div\n        id=\"tutorial-hint\"\n        style=\"position: absolute; top: 15%; left: 50%; transform: translateX(-50%); font-size: 24px; font-weight: bold; color: white; text-shadow: 0px 2px 4px rgba(0,0,0,0.8); background: rgba(0,0,0,0.5); padding: 10px 20px; border-radius: 12px; pointer-events: none; opacity: 0; transition: opacity 0.3s; z-index: 200; text-align: center;\"\n      ></div>\n\n      <div id=\"tutorial-complete\" aria-hidden=\"true\">\n        <div id=\"confetti-layer\"></div>\n      </div>\n      <!-- HUD Overlay -->\n      <div id=\"hud\">\n        <div id=\"currency-display\">\n          <img id=\"coin-icon\" src=\"\" alt=\"$\" />\n          <span id=\"currency-value\">0</span>\n        </div>\n      </div>\n\n      <button id=\"zoom-toggle\" aria-label=\"Toggle Camera Zoom\">\n        <span class=\"toggle-icon\">\ud83d\udd0d</span>\n      </button>\n      <button id=\"cam-left-btn\" aria-label=\"Rotate Camera Left\">\n        <span class=\"toggle-icon\">\u21a9\ufe0f</span>\n      </button>\n      <button id=\"cam-right-btn\" aria-label=\"Rotate Camera Right\">\n        <span class=\"toggle-icon\">\u21aa\ufe0f</span>\n      </button>\n      <!-- Bank Vault Toggle Button -->\n      <button id=\"vault-toggle\" aria-label=\"Open bank vault\">\n        <span class=\"toggle-icon\">\ud83c\udfe6</span>\n        <span class=\"toggle-label\" data-i18n=\"vault.toggle\">Vault</span>\n      </button>\n      <!-- Bank Vault Panel -->\n      <div id=\"vault-panel\" role=\"dialog\" aria-label=\"Bank Vault menu\">\n        <div class=\"vault-header\">\n          <span class=\"vault-header-title\" data-i18n=\"vault.title\">\ud83c\udfe6 Bank Vault</span>\n          <button class=\"vault-close\" aria-label=\"Close vault\">\u2715</button>\n        </div>\n        <div class=\"vault-balance-large\">\ud83d\udcb0 $<span id=\"vault-balance-large\">0</span></div>\n        <div class=\"vault-tabs\">\n          <button class=\"vault-tab active\" data-tab=\"upgrades\">\n            <span class=\"tab-icon\">\ud83d\udcb9</span>\n            <span class=\"tab-label\" data-i18n=\"vault.tabs.upgrades\">Upgrades</span>\n          </button>\n\n          <button class=\"vault-tab\" data-tab=\"stats\">\n            <span class=\"tab-icon\">\ud83d\udcca</span>\n            <span class=\"tab-label\" data-i18n=\"vault.tabs.stats\">Stats</span>\n          </button>\n          <button class=\"vault-tab\" data-tab=\"leaderboard\">\n            <span class=\"tab-icon\">\ud83c\udfc6</span>\n            <span class=\"tab-label\" data-i18n=\"vault.tabs.leaderboard\">Leaderboard</span>\n          </button>\n        </div>\n        <div class=\"vault-scroll-container\">\n          <div class=\"vault-tab-content active\" id=\"vault-tab-upgrades\">\n            <div class=\"vault-section-title\" data-i18n=\"vault.section.upgrades\">Permanent Upgrades</div>\n            <div class=\"vault-upgrade-list\">\n              <div class=\"vault-upgrade-item\" id=\"vault-upgrade-earnings\">\n                <div class=\"vault-upgrade-info\">\n                  <span class=\"vault-upgrade-name\" data-i18n=\"vault.upgrades.earnings.name\">\ud83d\udcb9 Offline Earnings Boost</span>\n                  <span class=\"vault-upgrade-desc\" data-i18n=\"vault.upgrades.earnings.desc\">+25% offline earnings per level</span>\n                  <span class=\"vault-upgrade-level\"> Level <span class=\"level-num\">0</span> </span>\n                </div>\n                <button class=\"vault-upgrade-btn\">\n                  <span class=\"vault-cost-value\">500</span>\n                </button>\n              </div>\n              <div class=\"vault-upgrade-item\" id=\"vault-upgrade-capacity\">\n                <div class=\"vault-upgrade-info\">\n                  <span class=\"vault-upgrade-name\" data-i18n=\"vault.upgrades.capacity.name\">\ud83d\udce6 Storage Expansion</span>\n                  <span class=\"vault-upgrade-desc\" data-i18n=\"vault.upgrades.capacity.desc\">+10 permanent tray capacity</span>\n                  <span class=\"vault-upgrade-level\"> Level <span class=\"level-num\">0</span> </span>\n                </div>\n                <button class=\"vault-upgrade-btn\">\n                  <span class=\"vault-cost-value\">750</span>\n                </button>\n              </div>\n              <div class=\"vault-upgrade-item\" id=\"vault-upgrade-tips\">\n                <div class=\"vault-upgrade-info\">\n                  <span class=\"vault-upgrade-name\" data-i18n=\"vault.upgrades.tips.name\">\u2b50 VIP Magnetism</span>\n                  <span class=\"vault-upgrade-desc\" data-i18n=\"vault.upgrades.tips.desc\">+5% VIP customer chance</span>\n                  <span class=\"vault-upgrade-level\"> Level <span class=\"level-num\">0</span> </span>\n                </div>\n                <button class=\"vault-upgrade-btn\">\n                  <span class=\"vault-cost-value\">1000</span>\n                </button>\n              </div>\n              <div class=\"vault-upgrade-item\" id=\"vault-upgrade-speed\">\n                <div class=\"vault-upgrade-info\">\n                  <span class=\"vault-upgrade-name\" data-i18n=\"vault.upgrades.speed.name\">\u26a1 Hustle Mode</span>\n                  <span class=\"vault-upgrade-desc\" data-i18n=\"vault.upgrades.speed.desc\">+5% permanent move speed</span>\n                  <span class=\"vault-upgrade-level\"> Level <span class=\"level-num\">0</span> </span>\n                </div>\n                <button class=\"vault-upgrade-btn\">\n                  <span class=\"vault-cost-value\">600</span>\n                </button>\n              </div>\n              <div class=\"vault-upgrade-item\" id=\"vault-upgrade-cook\">\n                <div class=\"vault-upgrade-info\">\n                  <span class=\"vault-upgrade-name\" data-i18n=\"vault.upgrades.cook.name\">\ud83d\udd25 Master Chef</span>\n                  <span class=\"vault-upgrade-desc\" data-i18n=\"vault.upgrades.cook.desc\">-5% cooking time per level</span>\n                  <span class=\"vault-upgrade-level\"> Level <span class=\"level-num\">0</span> </span>\n                </div>\n                <button class=\"vault-upgrade-btn\">\n                  <span class=\"vault-cost-value\">800</span>\n                </button>\n              </div>\n              <div class=\"vault-upgrade-item\" id=\"vault-upgrade-serve\">\n                <div class=\"vault-upgrade-info\">\n                  <span class=\"vault-upgrade-name\" data-i18n=\"vault.upgrades.serve.name\">\ud83c\udf7d\ufe0f Quick Service</span>\n                  <span class=\"vault-upgrade-desc\" data-i18n=\"vault.upgrades.serve.desc\">-10% serving time per level</span>\n                  <span class=\"vault-upgrade-level\"> Level <span class=\"level-num\">0</span> </span>\n                </div>\n                <button class=\"vault-upgrade-btn\">\n                  <span class=\"vault-cost-value\">650</span>\n                </button>\n              </div>\n              <div class=\"vault-upgrade-item\" id=\"vault-upgrade-cashpile\">\n                <div class=\"vault-upgrade-info\">\n                  <span class=\"vault-upgrade-name\" data-i18n=\"vault.upgrades.cashpile.name\">\ud83d\udcb0 Cashpile Expansion</span>\n                  <span class=\"vault-upgrade-desc\" data-i18n=\"vault.upgrades.cashpile.desc\">+20 max cashpile capacity</span>\n                  <span class=\"vault-upgrade-level\"> Level <span class=\"level-num\">0</span> </span>\n                </div>\n                <button class=\"vault-upgrade-btn\">\n                  <span class=\"vault-cost-value\">900</span>\n                </button>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"vault-tab-content\" id=\"vault-tab-stats\">\n            <div class=\"vault-section-title\" data-i18n=\"vault.section.stats\">Restaurant Stats</div>\n            <div class=\"vault-stats-list\">\n              <div class=\"vault-stat-item\">\n                <span class=\"stat-label\" data-i18n=\"vault.stats.vaultBalance\">\ud83c\udfe6 Vault Balance</span>\n                <span class=\"stat-value\" id=\"stat-vault-balance\">$0</span>\n              </div>\n              <div class=\"vault-stat-item\">\n                <span class=\"stat-label\" data-i18n=\"vault.stats.prestigeLevel\">\u2b50 Prestige Level</span>\n                <span class=\"stat-value\" id=\"stat-prestige-level\">0</span>\n              </div>\n              <div class=\"vault-stat-item\">\n                <span class=\"stat-label\" data-i18n=\"vault.stats.profitMultiplier\">\ud83d\udcc8 Profit Multiplier</span>\n                <span class=\"stat-value\" id=\"stat-profit-mult\">x1.00</span>\n              </div>\n              <div class=\"vault-stat-item\">\n                <span class=\"stat-label\" data-i18n=\"vault.stats.vipChance\">\ud83d\udc65 VIP Chance</span>\n                <span class=\"stat-value\" id=\"stat-vip-chance\">15%</span>\n              </div>\n              <div class=\"vault-stat-item\">\n                <span class=\"stat-label\" data-i18n=\"vault.stats.moveSpeed\">\ud83c\udfc3 Move Speed</span>\n                <span class=\"stat-value\" id=\"stat-move-speed\">10</span>\n              </div>\n              <div class=\"vault-stat-item\">\n                <span class=\"stat-label\" data-i18n=\"vault.stats.trayCapacity\">\ud83d\udce6 Tray Capacity</span>\n                <span class=\"stat-value\" id=\"stat-tray-capacity\">50</span>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"vault-tab-content\" id=\"vault-tab-leaderboard\">\n            <div class=\"vault-section-title\" data-i18n=\"vault.section.leaderboard\">Top Players</div>\n            <div id=\"leaderboard-actions\" class=\"leaderboard-actions\">\n              <button id=\"leaderboard-submit-btn\" class=\"vault-upgrade-btn leaderboard-btn\" type=\"button\">\n                <span class=\"vault-cost-value\" data-i18n=\"vault.leaderboard.submit\">Submit Score</span>\n              </button>\n              <button id=\"leaderboard-refresh-btn\" class=\"vault-upgrade-btn leaderboard-btn\" type=\"button\">\n                <span class=\"vault-cost-value\" data-i18n=\"vault.leaderboard.refresh\">Refresh</span>\n              </button>\n            </div>\n            <div id=\"leaderboard-status\" class=\"leaderboard-status\" data-i18n=\"vault.leaderboard.loading\">Loading leaderboard...</div>\n            <div id=\"leaderboard-list\" class=\"vault-stats-list\"></div>\n          </div>\n        </div>\n      </div>\n    </div>\n    <script src=\"https://unpkg.com/i18next@21.6.14/i18next.min.js\"></script>\n    <script src=\"https://unpkg.com/i18next-browser-languagedetector@6.1.8/i18nextBrowserLanguageDetector.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js\"></script>\n    <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/DRACOLoader.js\"></script>\n    <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js\"></script>\n    <script src=\"https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/utils/SkeletonUtils.js\"></script>\n    <script>\n      /* ==================================================\n       * RESTAURANT TYCOON - 3D Arcade Idle Game\n       *   player: { color, startX, startZ, baseSpeed, baseCapacity, baseCollectionTime },\n       *   resources: { nodeCount, regenerationTime, nodeRadius, itemValue },\n       *   sellZone: { radius, color },\n       *   upgrades: { capacity: {...}, speed: {...}, collection: {...} },\n       *   currency: number,\n       *   settings: { groundColor, skyColor, musicEnabled }\n       * }\n       * ==================================================\n       */\n\n      // ============================================\n      // GLOBAL VARIABLES\n      // ============================================\n\n      let scene, camera, renderer;\n      let gameMode = 'play';\n      let player,\n        playerStack = [];\n      let moneyStack = [];\n      // Stack of money on player\n      let resourceNodes = [];\n      let groundItems = [];\n      let groundMoneyItems = [];\n      // Money on the ground in money zone\n      let sellZoneMesh, sellZoneGlow;\n      let moneyZoneMesh, moneyZoneGlow;\n      let spendZoneMesh, spendZoneGlow;\n      let cashierStallMesh, cashierStallGlow;\n      let groundMesh;\n\n      // Cashier stall state\n      let cashierBurgerStack = [];\n      // Burgers stored at cashier\n      let customerQueue = [];\n      // Customers waiting in line\n      let customerSpawnTimer = 0;\n      let cashierServeTimer = 0;\n      let cashierServeTimer2 = 0;\n      let isInCashierZone = false;\n      let cashierDepositTimer = 0;\n      const CASHIER_DEPOSIT_DELAY = 0.12;\n\n      function getTotalActiveCustomers() {\n        let total = 0;\n        total += customerQueue.filter((c) => c.state !== 'leaving').length;\n        selfOrderKiosks.forEach((kiosk) => {\n          total += kiosk.queue.length + kiosk.leavers.length;\n        });\n        return total;\n      }\n\n      function isTotalCustomerCapReached() {\n        const maxTotal = window.gameConfig.settings.maxTotalCustomers ?? 40;\n        return getTotalActiveCustomers() >= maxTotal;\n      }\n      // Time between depositing burgers\n      const CUSTOMER_SPACING = 2;\n      // Space between customers in queue\n\n      // Cash pile system (earnings collected at each station)\n      let cashierCashPile = [];\n      // Money pile next to cashier\n      let cashierCashPileMesh, cashierCashPileGlow;\n      // Visual elements\n      let isInCashierCashPileZone = false;\n\n      // Trash bin system\n      let trashBinMesh, trashBinGlow;\n      let isInTrashBinZone = false;\n      let trashBinDisposeTimer = 0;\n      const TRASH_DISPOSE_DELAY = 0.08;\n      // Fast disposal\n\n      // Bank Vault system\n      let bankVaultMesh, bankVaultGlow;\n      let vaultTileMesh, vaultTileGlow;\n      let vaultTileDepositTimer = 0;\n      let bankVaultCashPile = [];\n      // Visual money in vault\n      let isInBankVaultZone = false;\n      let bankVaultDepositTimer = 0;\n      let vaultBalance = 0;\n      // Permanent vault credits\n      const VAULT_DEPOSIT_DELAY = 0.08;\n\n      // Staff hire tile system (Cashier Staff)\n      let staffHireTileMesh, staffHireTileGlow;\n      let staffHireMoneyPile = [];\n      // Money deposited on the tile\n      let isInStaffHireTile = false;\n      let staffHireDepositTimer = 0;\n      let hiredStaffMesh = null;\n      let hiredStaffMesh2 = null;\n      // Visual staff members at cashier\n      let staffWalkingToCashier = null;\n      let staffWalkingToCashier2 = null;\n      // Staff walking animation state\n      let smokeParticles = [];\n      // Smoke particle effects\n      let dustParticles = [];\n      // Dust particles when walking\n      const STAFF_HIRE_DEPOSIT_DELAY = 0.1;\n\n      // Cleaner Staff tile system\n      let cleanerStaffTileMesh, cleanerStaffTileGlow;\n      let isInCleanerStaffTile = false;\n      let cleanerStaffDepositTimer = 0;\n      let hiredCleanerMesh = null;\n      let cleanerStaffAI = null;\n      // AI state for cleaner\n\n      // Runner Staff tile system (collects burgers, delivers to cashier)\n      let runnerStaffTileMesh, runnerStaffTileGlow;\n      let isInRunnerStaffTile = false;\n      let runnerStaffDepositTimer = 0;\n      let hiredRunnerMesh = null;\n      let runnerStaffAI = null;\n      // AI state for runner\n      let runnerBurgerStack = [];\n      // Burgers carried by runner\n\n      // Vault Collector Staff tile system (collects money from cash piles, deposits to vault)\n      let vaultStaffTileMesh, vaultStaffTileGlow;\n      let isInVaultStaffTile = false;\n      let vaultStaffDepositTimer = 0;\n      let hiredVaultCollectorMesh = null;\n      let vaultCollectorAI = null;\n      // AI state for vault collector\n      let vaultCollectorMoneyStack = [];\n      // Money carried by vault collector\n\n      // Dining table system\n      let diningTableMeshes = [];\n      // Array of {mesh, tableData, trashMesh}\n      let diningTableTileMeshes = [];\n      // Array of {mesh, glow, config, index}\n      let isInDiningTableTile = false;\n      let diningTableTileDepositTimer = 0;\n      let nextTableId = 1;\n      const DINING_TABLE_DEPOSIT_DELAY = 0.1;\n      const EATING_TIME = 3;\n      // Seconds customers spend eating\n      const CASH_PILE_COLLECT_DELAY = 0.08;\n      // Time between collecting from cash pile\n      let cashPileCollectTimer = 0;\n\n      // Drive-thru booth system\n      let driveThruBoothMesh, driveThruBoothGlow;\n      let driveThruBurgerStack = [];\n      // Burgers stored at drive-thru\n      let driveThruCarQueue = [];\n      // Cars waiting in line\n      let driveThruCashPile = [];\n      // Money pile at drive-thru\n      let driveThruCashPileMesh, driveThruCashPileGlow;\n      let driveThruCarSpawnTimer = 0;\n      let driveThruServeTimer = 0;\n      let isInDriveThruZone = false;\n      let isInDriveThruCashPileZone = false;\n      let driveThruDepositTimer = 0;\n      let driveThruCashPileCollectTimer = 0;\n      const DRIVE_THRU_DEPOSIT_DELAY = 0.12;\n      const CAR_SPACING = 5;\n      // Space between cars in queue\n\n// Self-order kiosk system (array-based for multiple kiosks)\n      let selfOrderKiosks = []; // { mesh, glow, queue, leavers, spawnTimer, config, id }\n      const SELF_ORDER_SPACING = 2.2;\n      const SELF_ORDER_ORDER_TIME = 4;\n\n      function initSelfOrderKiosks() {\n        const configs = window.gameConfig.selfOrderKiosks || [];\n        selfOrderKiosks = configs.map((config) => ({\n          mesh: null,\n          glow: null,\n          queue: [],\n          leavers: [],\n          spawnTimer: 0,\n          config: config,\n          id: config.id,\n        }));\n      }\n\n      function modifyVaultBalance(amount) {\n        vaultBalance += amount;\n        currentCurrency = vaultBalance;\n        window.gameConfig.bankVault.balance = vaultBalance;\n        window.gameConfig.currency = vaultBalance;\n        updateVaultUI();\n        updateCurrencyUI();\n        updateUpgradeUI();\n      }\n\n      // Runtime state (not persisted)\n      let currentCurrency = 0;\n      let currentStackCount = 0;\n      let currentMoneyCount = 0;\n      // Money currently being carried\n      let incomingBurgers = 0;\n      let incomingMoney = 0;\n      let isCollecting = false;\n      let collectionProgress = 0;\n      let nearestNode = null;\n      let isInSellZone = false;\n      let isInMoneyZone = false;\n      let isInSpendZone = false;\n      let sellTimer = 0;\n      let moneyCollectTimer = 0;\n      let spendTimer = 0;\n      let burgerCollectTimer = 0;\n      // Timer for one-by-one burger collection\n      let cashierEarnings = 0;\n      // Track cashier earnings for display\n\n      // Rush hour system\n      let isRushHour = false;\n      let rushHourTimer = 0;\n      let rushHourCooldownTimer = 0;\n      let rushHourPulseAnim = 0;\n\n      // Input state\n      let inputVector = {\n        x: 0,\n        z: 0,\n      };\n      let keys = {};\n      let joystickActive = false;\n      let joystickTouchId = null;\n      let joystickCenter = {\n        x: 0,\n        y: 0,\n      };\n\n      let isZoomedOut = false;\n      let targetCameraAngle = 0;\n      let currentCameraAngle = 0;\n\n      // Audio\n      let audioContext = null;\n      let audioBuffers = {};\n      let musicSource = null;\n      let musicGainNode = null;\n\n      // 3D Model loading\n      let stickmanModelTemplate = null;\n      let stickmanModelLoaded = false;\n      let gltfLoader = null;\n      let dracoLoader = null;\n\n      let animationMixers = [];\n      // Store animation mixers for all characters\n\n      // Asset cache\n      const assetCache = {};\n\n      // Performance tuning (mobile-first)\n      let isLowPerfDevice = false;\n      const performanceSettings = {\n        pixelRatioCap: 1.5,\n        enableShadows: true,\n        shadowMapSize: 512,\n        maxDustParticles: 7,\n        cityLightUpdateInterval: 0,\n      };\n      let cityLightUpdateTimer = 0;\n\n      // ============================================\n      // INTERNATIONALIZATION (i18next)\n      // ============================================\n      /* global i18next, i18nextBrowserLanguageDetector */\n      let i18nReady = false;\n      const i18nResources = {\n        en: {\n          translation: {\n            loading: {\n              title: 'Fast Food Tycoon',\n              text: 'Loading...',\n              tip: 'Tip: Cook different foods and sell them to customers!',\n              setup: 'Setting up game...',\n              initGraphics: 'Initializing graphics...',\n              building: 'Building restaurant...',\n              loadingModel: 'Loading character model...',\n              grills: 'Setting up grills...',\n              images: 'Loading images...',\n              sounds: 'Loading sounds...',\n              preparing: 'Preparing kitchen...',\n              controls: 'Setting up controls...',\n              finalizing: 'Finalizing...',\n              ready: 'Ready!',\n            },\n            editor: {\n              title: 'Level Editor',\n              addGrill: 'Add Grill',\n              addFryer: 'Add Fryer',\n              addDrink: 'Add Drink Station',\n              addDiningTable: 'Add Dining Table',\n              editSelfOrder: 'Edit Self-Order',\n              hint: 'Click objects to move them',\n            },\n            vault: {\n              toggle: 'Vault',\n              title: '\ud83c\udfe6 Bank Vault',\n              tabs: {\n                upgrades: 'Upgrades',\n                stats: 'Stats',\n                leaderboard: 'Leaderboard',\n              },\n              section: {\n                upgrades: 'Permanent Upgrades',\n                stats: 'Restaurant Stats',\n                leaderboard: 'Top Players',\n              },\n              upgrades: {\n                earnings: {\n                  name: '\ud83d\udcb9 Offline Earnings Boost',\n                  desc: '+25% offline earnings per level',\n                },\n                capacity: {\n                  name: '\ud83d\udce6 Storage Expansion',\n                  desc: '+10 permanent tray capacity',\n                },\n                tips: {\n                  name: '\u2b50 VIP Magnetism',\n                  desc: '+5% VIP customer chance',\n                },\n                speed: {\n                  name: '\u26a1 Hustle Mode',\n                  desc: '+5% permanent move speed',\n                },\n                cook: {\n                  name: '\ud83d\udd25 Master Chef',\n                  desc: '-5% cooking time per level',\n                },\n                serve: {\n                  name: '\ud83c\udf7d\ufe0f Quick Service',\n                  desc: '-10% serving time per level',\n                },\n                cashpile: {\n                  name: '\ud83d\udcb0 Cashpile Expansion',\n                  desc: '+20 max cashpile capacity',\n                },\n              },\n              stats: {\n                vaultBalance: '\ud83c\udfe6 Vault Balance',\n                prestigeLevel: '\u2b50 Prestige Level',\n                profitMultiplier: '\ud83d\udcc8 Profit Multiplier',\n                vipChance: '\ud83d\udc65 VIP Chance',\n                moveSpeed: '\ud83c\udfc3 Move Speed',\n                trayCapacity: '\ud83d\udce6 Tray Capacity',\n              },\n              leaderboard: {\n                submit: 'Submit Score',\n                refresh: 'Refresh',\n                loading: 'Loading leaderboard...',\n                empty: 'No leaderboard entries yet.',\n                error: 'Failed to load leaderboard.',\n                submitting: 'Submitting your score...',\n                submitSuccess: 'Score submitted! Rank: #{{rank}}',\n                submitSuccessNoRank: 'Score submitted successfully!',\n                submitError: 'Could not submit score. {{message}}',\n                unknownPlayer: 'Player'\n              },\n            },\n            tutorial: {\n              cashFull: 'Cash register full! Collect your money!',\n              waitingCollect: 'Customers waiting! Go to the Grill to collect food!',\n              waitingBring: 'Customers waiting! Bring the food to the Cashier counter!',\n              waitingMan: 'Customers waiting! Man the register to serve them!',\n              buildTable: 'Step on the tile to build a Dining Table!',\n              buildGrill: 'Step on the tile to build Grills!',\n              buildCashier: 'Step on the tile to build the Cashier!',\n              collectFood: 'Go to the Grill to collect food!',\n              bringFood: 'Bring the food to the Cashier counter!',\n              hireStaff: 'Hire staff to man the cashier!',\n              cleanTable: 'Wait for customers to eat, then clean their table!',\n              collectEarnings: 'Collect your earnings from the cash pile!',\n              driveThruCollect: 'Drive-thru unlocked! Go to the Grill to collect food!',\n              driveThruDeposit: 'Add burgers to the drive-thru counter!',\n            },\n            notifications: {\n              staffHiredTitle: 'Staff Hired!',\n              cashier: 'Cashier Staff',\n              cleaner: 'Cleaner Staff',\n              runner: 'Runner Staff',\n              vaultCollector: 'Vault Collector',\n              generic: 'Staff Hired!',\n            },\n            rush: {\n              subtitle: 'Customers incoming! +{{bonus}}% tips!',\n            },\n            offline: {\n              timeAway: 'You were away for {{time}}',\n              earnings: 'Your staff deposited ${{amount}} to vault!',\n            },\n          },\n        },\n        es: {\n          translation: {\n            loading: {\n              title: 'Fast Food Tycoon',\n              text: 'Cargando...',\n              tip: 'Consejo: cocina distintos alimentos y v\u00e9ndelos a los clientes.',\n              setup: 'Preparando juego...',\n              initGraphics: 'Inicializando gr\u00e1ficos...',\n              building: 'Construyendo restaurante...',\n              loadingModel: 'Cargando modelo del personaje...',\n              grills: 'Configurando parrillas...',\n              images: 'Cargando im\u00e1genes...',\n              sounds: 'Cargando sonidos...',\n              preparing: 'Preparando cocina...',\n              controls: 'Configurando controles...',\n              finalizing: 'Finalizando...',\n              ready: '\u00a1Listo!',\n            },\n            editor: {\n              title: 'Editor de Nivel',\n              addGrill: 'A\u00f1adir Parrilla',\n              addFryer: 'A\u00f1adir Freidora',\n              addDrink: 'A\u00f1adir Bebidas',\n              addDiningTable: 'A\u00f1adir Mesa',\n              editSelfOrder: 'Editar Auto-Pedido',\n              hint: 'Toca los objetos para moverlos',\n            },\n            vault: {\n              toggle: 'B\u00f3veda',\n              title: '\ud83c\udfe6 B\u00f3veda',\n              tabs: {\n                upgrades: 'Mejoras',\n                stats: 'Estad\u00edsticas',\n              },\n              section: {\n                upgrades: 'Mejoras Permanentes',\n                stats: 'Estad\u00edsticas del Restaurante',\n              },\n              upgrades: {\n                earnings: {\n                  name: '\ud83d\udcb9 Mejora de Ganancias Offline',\n                  desc: '+25% ganancias offline por nivel',\n                },\n                capacity: {\n                  name: '\ud83d\udce6 Expansi\u00f3n de Capacidad',\n                  desc: '+10 capacidad permanente de bandeja',\n                },\n                tips: {\n                  name: '\u2b50 Magnetismo VIP',\n                  desc: '+5% probabilidad de clientes VIP',\n                },\n                speed: {\n                  name: '\u26a1 Modo R\u00e1pido',\n                  desc: '+5% velocidad permanente',\n                },\n                cook: {\n                  name: '\ud83d\udd25 Chef Maestro',\n                  desc: '-5% tiempo de cocci\u00f3n por nivel',\n                },\n                serve: {\n                  name: '\ud83c\udf7d\ufe0f Servicio R\u00e1pido',\n                  desc: '-10% tiempo de servicio por nivel',\n                },\n                cashpile: {\n                  name: '\ud83d\udcb0 Expansi\u00f3n de Caja',\n                  desc: '+20 capacidad m\u00e1xima de efectivo',\n                },\n              },\n              stats: {\n                vaultBalance: '\ud83c\udfe6 Saldo de B\u00f3veda',\n                prestigeLevel: '\u2b50 Nivel de Prestigio',\n                profitMultiplier: '\ud83d\udcc8 Multiplicador de Ganancias',\n                vipChance: '\ud83d\udc65 Probabilidad VIP',\n                moveSpeed: '\ud83c\udfc3 Velocidad',\n                trayCapacity: '\ud83d\udce6 Capacidad de Bandeja',\n              },\n            },\n            tutorial: {\n              cashFull: '\u00a1Caja llena! \u00a1Recoge tu dinero!',\n              waitingCollect: '\u00a1Clientes esperando! Ve a la parrilla por comida.',\n              waitingBring: '\u00a1Clientes esperando! Lleva la comida al cajero.',\n              waitingMan: '\u00a1Clientes esperando! Atiende la caja.',\n              buildTable: 'Pisa la baldosa para construir una mesa.',\n              buildGrill: 'Pisa la baldosa para construir parrillas.',\n              buildCashier: 'Pisa la baldosa para construir el cajero.',\n              collectFood: 'Ve a la parrilla a recoger comida.',\n              bringFood: 'Lleva la comida al cajero.',\n              hireStaff: 'Contrata personal para atender la caja.',\n              cleanTable: 'Espera a que terminen y limpia la mesa.',\n              collectEarnings: 'Recoge tus ganancias de la pila de efectivo.',\n              driveThruCollect: '\u00a1Drive-thru desbloqueado! Ve a la parrilla por comida.',\n              driveThruDeposit: 'A\u00f1ade hamburguesas al mostrador del drive-thru.',\n            },\n            notifications: {\n              staffHiredTitle: '\u00a1Personal Contratado!',\n              cashier: 'Personal de Caja',\n              cleaner: 'Personal de Limpieza',\n              runner: 'Personal Repartidor',\n              vaultCollector: 'Recolector de B\u00f3veda',\n              generic: '\u00a1Personal Contratado!',\n            },\n            rush: {\n              subtitle: '\u00a1Clientes llegando! +{{bonus}}% propinas!',\n            },\n            offline: {\n              timeAway: 'Estuviste ausente por {{time}}',\n              earnings: 'Tu personal deposit\u00f3 ${{amount}} en la b\u00f3veda.',\n            },\n          },\n        },\n        bn: {\n          translation: {\n            loading: {\n              title: '\u09ab\u09be\u09b8\u09cd\u099f \u09ab\u09c1\u09a1 \u099f\u09be\u0987\u0995\u09c1\u09a8',\n              text: '\u09b2\u09cb\u09a1 \u09b9\u099a\u09cd\u099b\u09c7...',\n              tip: '\u099f\u09bf\u09aa: \u09ac\u09bf\u09ad\u09bf\u09a8\u09cd\u09a8 \u0996\u09be\u09ac\u09be\u09b0 \u09b0\u09be\u09a8\u09cd\u09a8\u09be \u0995\u09b0\u09c1\u09a8 \u098f\u09ac\u0982 \u0997\u09cd\u09b0\u09be\u09b9\u0995\u09a6\u09c7\u09b0 \u0995\u09be\u099b\u09c7 \u09ac\u09bf\u0995\u09cd\u09b0\u09bf \u0995\u09b0\u09c1\u09a8!',\n              setup: '\u0997\u09c7\u09ae \u09b8\u09c7\u099f\u0986\u09aa \u09b9\u099a\u09cd\u099b\u09c7...',\n              initGraphics: '\u0997\u09cd\u09b0\u09be\u09ab\u09bf\u0995\u09cd\u09b8 \u09b6\u09c1\u09b0\u09c1 \u09b9\u099a\u09cd\u099b\u09c7...',\n              building: '\u09b0\u09c7\u09b8\u09cd\u099f\u09c1\u09b0\u09c7\u09a8\u09cd\u099f \u09a4\u09c8\u09b0\u09bf \u09b9\u099a\u09cd\u099b\u09c7...',\n              loadingModel: '\u099a\u09b0\u09bf\u09a4\u09cd\u09b0 \u09ae\u09a1\u09c7\u09b2 \u09b2\u09cb\u09a1 \u09b9\u099a\u09cd\u099b\u09c7...',\n              grills: '\u0997\u09cd\u09b0\u09bf\u09b2 \u09b8\u09c7\u099f\u0986\u09aa \u09b9\u099a\u09cd\u099b\u09c7...',\n              images: '\u099b\u09ac\u09bf \u09b2\u09cb\u09a1 \u09b9\u099a\u09cd\u099b\u09c7...',\n              sounds: '\u09b8\u09be\u0989\u09a8\u09cd\u09a1 \u09b2\u09cb\u09a1 \u09b9\u099a\u09cd\u099b\u09c7...',\n              preparing: '\u09b0\u09be\u09a8\u09cd\u09a8\u09be\u0998\u09b0 \u09aa\u09cd\u09b0\u09b8\u09cd\u09a4\u09c1\u09a4 \u09b9\u099a\u09cd\u099b\u09c7...',\n              controls: '\u0995\u09a8\u09cd\u099f\u09cd\u09b0\u09cb\u09b2 \u09b8\u09c7\u099f\u0986\u09aa \u09b9\u099a\u09cd\u099b\u09c7...',\n              finalizing: '\u099a\u09c2\u09a1\u09bc\u09be\u09a8\u09cd\u09a4 \u09aa\u09cd\u09b0\u09b8\u09cd\u09a4\u09c1\u09a4\u09bf...',\n              ready: '\u09aa\u09cd\u09b0\u09b8\u09cd\u09a4\u09c1\u09a4!',\n            },\n            editor: {\n              title: '\u09b2\u09c7\u09ad\u09c7\u09b2 \u098f\u09a1\u09bf\u099f\u09b0',\n              addGrill: '\u0997\u09cd\u09b0\u09bf\u09b2 \u09af\u09cb\u0997 \u0995\u09b0\u09c1\u09a8',\n              addFryer: '\u09ab\u09cd\u09b0\u09be\u09af\u09bc\u09be\u09b0 \u09af\u09cb\u0997 \u0995\u09b0\u09c1\u09a8',\n              addDrink: '\u09a1\u09cd\u09b0\u09bf\u0982\u0995 \u09b8\u09cd\u099f\u09c7\u09b6\u09a8 \u09af\u09cb\u0997 \u0995\u09b0\u09c1\u09a8',\n              addDiningTable: '\u09a1\u09be\u0987\u09a8\u09bf\u0982 \u099f\u09c7\u09ac\u09bf\u09b2 \u09af\u09cb\u0997 \u0995\u09b0\u09c1\u09a8',\n              editSelfOrder: '\u09b8\u09c7\u09b2\u09cd\u09ab-\u0985\u09b0\u09cd\u09a1\u09be\u09b0 \u09b8\u09ae\u09cd\u09aa\u09be\u09a6\u09a8\u09be \u0995\u09b0\u09c1\u09a8',\n              hint: '\u0985\u09ac\u099c\u09c7\u0995\u09cd\u099f \u09b8\u09b0\u09be\u09a4\u09c7 \u099f\u09cd\u09af\u09be\u09aa/\u0995\u09cd\u09b2\u09bf\u0995 \u0995\u09b0\u09c1\u09a8',\n            },\n            vault: {\n              toggle: '\u09ad\u09b2\u09cd\u099f',\n              title: '\ud83c\udfe6 \u09ac\u09cd\u09af\u09be\u0982\u0995 \u09ad\u09b2\u09cd\u099f',\n              tabs: {\n                upgrades: '\u0986\u09aa\u0997\u09cd\u09b0\u09c7\u09a1',\n                stats: '\u09aa\u09b0\u09bf\u09b8\u0982\u0996\u09cd\u09af\u09be\u09a8',\n              },\n              section: {\n                upgrades: '\u09b8\u09cd\u09a5\u09be\u09af\u09bc\u09c0 \u0986\u09aa\u0997\u09cd\u09b0\u09c7\u09a1',\n                stats: '\u09b0\u09c7\u09b8\u09cd\u099f\u09c1\u09b0\u09c7\u09a8\u09cd\u099f\u09c7\u09b0 \u09aa\u09b0\u09bf\u09b8\u0982\u0996\u09cd\u09af\u09be\u09a8',\n              },\n              upgrades: {\n                earnings: {\n                  name: '\ud83d\udcb9 \u0985\u09ab\u09b2\u09be\u0987\u09a8 \u0986\u09af\u09bc\u09c7\u09b0 \u09ac\u09c1\u09b8\u09cd\u099f',\n                  desc: '+25% \u0985\u09ab\u09b2\u09be\u0987\u09a8 \u0986\u09af\u09bc \u09aa\u09cd\u09b0\u09a4\u09bf \u09b2\u09c7\u09ad\u09c7\u09b2',\n                },\n                capacity: {\n                  name: '\ud83d\udce6 \u09b8\u09cd\u099f\u09cb\u09b0\u09c7\u099c \u098f\u0995\u09cd\u09b8\u09aa\u09cd\u09af\u09be\u09a8\u09b6\u09a8',\n                  desc: '+10 \u09b8\u09cd\u09a5\u09be\u09af\u09bc\u09c0 \u099f\u09cd\u09b0\u09c7 \u0995\u09cd\u09af\u09be\u09aa\u09be\u09b8\u09bf\u099f\u09bf',\n                },\n                tips: {\n                  name: '\u2b50 \u09ad\u09bf\u0986\u0987\u09aa\u09bf \u09ae\u09cd\u09af\u09be\u0997\u09a8\u09c7\u099f\u09bf\u099c\u09ae',\n                  desc: '+5% \u09ad\u09bf\u0986\u0987\u09aa\u09bf \u0997\u09cd\u09b0\u09be\u09b9\u0995 \u09b8\u09ae\u09cd\u09ad\u09be\u09ac\u09a8\u09be',\n                },\n                speed: {\n                  name: '\u26a1 \u09b9\u09be\u09b8\u09b2 \u09ae\u09cb\u09a1',\n                  desc: '+5% \u09b8\u09cd\u09a5\u09be\u09af\u09bc\u09c0 \u0997\u09a4\u09bf',\n                },\n                cook: {\n                  name: '\ud83d\udd25 \u09ae\u09be\u09b8\u09cd\u099f\u09be\u09b0 \u09b6\u09c7\u09ab',\n                  desc: '-5% \u09b0\u09be\u09a8\u09cd\u09a8\u09be\u09b0 \u09b8\u09ae\u09af\u09bc \u09aa\u09cd\u09b0\u09a4\u09bf \u09b2\u09c7\u09ad\u09c7\u09b2',\n                },\n                serve: {\n                  name: '\ud83c\udf7d\ufe0f \u09a6\u09cd\u09b0\u09c1\u09a4 \u09b8\u09be\u09b0\u09cd\u09ad\u09bf\u09b8',\n                  desc: '-10% \u09b8\u09be\u09b0\u09cd\u09ad\u09bf\u0982 \u09b8\u09ae\u09af\u09bc \u09aa\u09cd\u09b0\u09a4\u09bf \u09b2\u09c7\u09ad\u09c7\u09b2',\n                },\n                cashpile: {\n                  name: '\ud83d\udcb0 \u0995\u09cd\u09af\u09be\u09b6\u09aa\u09be\u0987\u09b2 \u098f\u0995\u09cd\u09b8\u09aa\u09cd\u09af\u09be\u09a8\u09b6\u09a8',\n                  desc: '+20 \u09b8\u09b0\u09cd\u09ac\u09cb\u099a\u09cd\u099a \u0995\u09cd\u09af\u09be\u09b6\u09aa\u09be\u0987\u09b2 \u0995\u09cd\u09b7\u09ae\u09a4\u09be',\n                },\n              },\n              stats: {\n                vaultBalance: '\ud83c\udfe6 \u09ad\u09b2\u09cd\u099f \u09ac\u09cd\u09af\u09be\u09b2\u09be\u09a8\u09cd\u09b8',\n                prestigeLevel: '\u2b50 \u09aa\u09cd\u09b0\u09c7\u09b8\u09cd\u099f\u09bf\u099c \u09b2\u09c7\u09ad\u09c7\u09b2',\n                profitMultiplier: '\ud83d\udcc8 \u09b2\u09be\u09ad \u0997\u09c1\u09a3\u0995',\n                vipChance: '\ud83d\udc65 \u09ad\u09bf\u0986\u0987\u09aa\u09bf \u09b8\u09ae\u09cd\u09ad\u09be\u09ac\u09a8\u09be',\n                moveSpeed: '\ud83c\udfc3 \u099a\u09b2\u09be\u09b0 \u0997\u09a4\u09bf',\n                trayCapacity: '\ud83d\udce6 \u099f\u09cd\u09b0\u09c7 \u0995\u09cd\u09af\u09be\u09aa\u09be\u09b8\u09bf\u099f\u09bf',\n              },\n            },\n            tutorial: {\n              cashFull: '\u0995\u09cd\u09af\u09be\u09b6 \u09b0\u09c7\u099c\u09bf\u09b8\u09cd\u099f\u09be\u09b0 \u09aa\u09c2\u09b0\u09cd\u09a3! \u099f\u09be\u0995\u09be \u09b8\u0982\u0997\u09cd\u09b0\u09b9 \u0995\u09b0\u09c1\u09a8!',\n              waitingCollect: '\u0997\u09cd\u09b0\u09be\u09b9\u0995 \u0985\u09aa\u09c7\u0995\u09cd\u09b7\u09be \u0995\u09b0\u099b\u09c7! \u0997\u09cd\u09b0\u09bf\u09b2\u09c7 \u0997\u09bf\u09df\u09c7 \u0996\u09be\u09ac\u09be\u09b0 \u09a4\u09c1\u09b2\u09c1\u09a8!',\n              waitingBring: '\u0997\u09cd\u09b0\u09be\u09b9\u0995 \u0985\u09aa\u09c7\u0995\u09cd\u09b7\u09be \u0995\u09b0\u099b\u09c7! \u0996\u09be\u09ac\u09be\u09b0 \u0995\u09cd\u09af\u09be\u09b6\u09bf\u09af\u09bc\u09be\u09b0\u09c7\u09b0 \u0995\u09be\u099b\u09c7 \u09a8\u09bf\u09a8!',\n              waitingMan: '\u0997\u09cd\u09b0\u09be\u09b9\u0995 \u0985\u09aa\u09c7\u0995\u09cd\u09b7\u09be \u0995\u09b0\u099b\u09c7! \u0995\u09be\u0989\u09a8\u09cd\u099f\u09be\u09b0\u09c7 \u09a6\u09be\u0981\u09dc\u09be\u09a8!',\n              buildTable: '\u099f\u09be\u0987\u09b2\u09c7\u09b0 \u0989\u09aa\u09b0 \u09a6\u09be\u0981\u09dc\u09bf\u09df\u09c7 \u09a1\u09be\u0987\u09a8\u09bf\u0982 \u099f\u09c7\u09ac\u09bf\u09b2 \u09ac\u09be\u09a8\u09be\u09a8!',\n              buildGrill: '\u099f\u09be\u0987\u09b2\u09c7\u09b0 \u0989\u09aa\u09b0 \u09a6\u09be\u0981\u09dc\u09bf\u09df\u09c7 \u0997\u09cd\u09b0\u09bf\u09b2 \u09ac\u09be\u09a8\u09be\u09a8!',\n              buildCashier: '\u099f\u09be\u0987\u09b2\u09c7\u09b0 \u0989\u09aa\u09b0 \u09a6\u09be\u0981\u09dc\u09bf\u09df\u09c7 \u0995\u09cd\u09af\u09be\u09b6\u09bf\u09af\u09bc\u09be\u09b0 \u09ac\u09be\u09a8\u09be\u09a8!',\n              collectFood: '\u0997\u09cd\u09b0\u09bf\u09b2 \u09a5\u09c7\u0995\u09c7 \u0996\u09be\u09ac\u09be\u09b0 \u09a4\u09c1\u09b2\u09c1\u09a8!',\n              bringFood: '\u0996\u09be\u09ac\u09be\u09b0 \u0995\u09cd\u09af\u09be\u09b6\u09bf\u09af\u09bc\u09be\u09b0\u09c7\u09b0 \u0995\u09be\u099b\u09c7 \u0986\u09a8\u09c1\u09a8!',\n              hireStaff: '\u0995\u09cd\u09af\u09be\u09b6\u09bf\u09af\u09bc\u09be\u09b0\u09c7 \u09b8\u09cd\u099f\u09be\u09ab \u09a8\u09bf\u09af\u09bc\u09cb\u0997 \u0995\u09b0\u09c1\u09a8!',\n              cleanTable: '\u0997\u09cd\u09b0\u09be\u09b9\u0995 \u0996\u09be\u0993\u09df\u09be \u09b6\u09c7\u09b7 \u0995\u09b0\u09b2\u09c7 \u099f\u09c7\u09ac\u09bf\u09b2 \u09aa\u09b0\u09bf\u09b7\u09cd\u0995\u09be\u09b0 \u0995\u09b0\u09c1\u09a8!',\n              collectEarnings: '\u0995\u09cd\u09af\u09be\u09b6 \u09aa\u09be\u0987\u09b2 \u09a5\u09c7\u0995\u09c7 \u0986\u09df \u09b8\u0982\u0997\u09cd\u09b0\u09b9 \u0995\u09b0\u09c1\u09a8!',\n              driveThruCollect: '\u09a1\u09cd\u09b0\u09be\u0987\u09ad-\u09a5\u09cd\u09b0\u09c1 \u0986\u09a8\u09b2\u0995! \u0997\u09cd\u09b0\u09bf\u09b2\u09c7 \u0997\u09bf\u09df\u09c7 \u0996\u09be\u09ac\u09be\u09b0 \u09a4\u09c1\u09b2\u09c1\u09a8!',\n              driveThruDeposit: '\u09a1\u09cd\u09b0\u09be\u0987\u09ad-\u09a5\u09cd\u09b0\u09c1 \u0995\u09be\u0989\u09a8\u09cd\u099f\u09be\u09b0\u09c7 \u09ac\u09be\u09b0\u09cd\u0997\u09be\u09b0 \u09a6\u09bf\u09a8!',\n            },\n            notifications: {\n              staffHiredTitle: '\u09b8\u09cd\u099f\u09be\u09ab \u09a8\u09bf\u09af\u09bc\u09cb\u0997 \u09b9\u09af\u09bc\u09c7\u099b\u09c7!',\n              cashier: '\u0995\u09cd\u09af\u09be\u09b6\u09bf\u09af\u09bc\u09be\u09b0 \u09b8\u09cd\u099f\u09be\u09ab',\n              cleaner: '\u0995\u09cd\u09b2\u09bf\u09a8\u09be\u09b0 \u09b8\u09cd\u099f\u09be\u09ab',\n              runner: '\u09b0\u09be\u09a8\u09be\u09b0 \u09b8\u09cd\u099f\u09be\u09ab',\n              vaultCollector: '\u09ad\u09b2\u09cd\u099f \u09b8\u0982\u0997\u09cd\u09b0\u09be\u09b9\u0995',\n              generic: '\u09b8\u09cd\u099f\u09be\u09ab \u09a8\u09bf\u09af\u09bc\u09cb\u0997 \u09b9\u09af\u09bc\u09c7\u099b\u09c7!',\n            },\n            rush: {\n              subtitle: '\u0997\u09cd\u09b0\u09be\u09b9\u0995 \u0986\u09b8\u099b\u09c7! +{{bonus}}% \u099f\u09bf\u09aa\u09b8!',\n            },\n            offline: {\n              timeAway: '\u0986\u09aa\u09a8\u09bf {{time}} \u09b8\u09ae\u09df \u09a6\u09c2\u09b0\u09c7 \u099b\u09bf\u09b2\u09c7\u09a8',\n              earnings: '\u0986\u09aa\u09a8\u09be\u09b0 \u09b8\u09cd\u099f\u09be\u09ab \u09ad\u09b2\u09cd\u099f\u09c7 ${{amount}} \u099c\u09ae\u09be \u0995\u09b0\u09c7\u099b\u09c7!',\n            },\n          }\n        },\n        vi: {\n          translation: {\n            loading: {\n              title: '\u00d4ng Tr\u00f9m Th\u1ee9c \u0102n Nhanh',\n              text: '\u0110ang t\u1ea3i...',\n              tip: 'M\u1eb9o: N\u1ea5u c\u00e1c m\u00f3n \u0103n kh\u00e1c nhau v\u00e0 b\u00e1n cho kh\u00e1ch h\u00e0ng!',\n              setup: '\u0110ang thi\u1ebft l\u1eadp tr\u00f2 ch\u01a1i...',\n              initGraphics: '\u0110ang kh\u1edfi t\u1ea1o \u0111\u1ed3 h\u1ecda...',\n              building: '\u0110ang x\u00e2y d\u1ef1ng nh\u00e0 h\u00e0ng...',\n              loadingModel: '\u0110ang t\u1ea3i m\u00f4 h\u00ecnh nh\u00e2n v\u1eadt...',\n              grills: '\u0110ang thi\u1ebft l\u1eadp b\u1ebfp n\u01b0\u1edbng...',\n              images: '\u0110ang t\u1ea3i h\u00ecnh \u1ea3nh...',\n              sounds: '\u0110ang t\u1ea3i \u00e2m thanh...',\n              preparing: '\u0110ang chu\u1ea9n b\u1ecb b\u1ebfp...',\n              controls: '\u0110ang thi\u1ebft l\u1eadp \u0111i\u1ec1u khi\u1ec3n...',\n              finalizing: '\u0110ang ho\u00e0n t\u1ea5t...',\n              ready: 'S\u1eb5n s\u00e0ng!',\n            },\n            editor: {\n              title: 'Tr\u00ecnh ch\u1ec9nh s\u1eeda c\u1ea5p \u0111\u1ed9',\n              addGrill: 'Th\u00eam b\u1ebfp n\u01b0\u1edbng',\n              addFryer: 'Th\u00eam n\u1ed3i chi\u00ean',\n              addDrink: 'Th\u00eam qu\u1ea7y \u0111\u1ed3 u\u1ed1ng',\n              addDiningTable: 'Th\u00eam b\u00e0n \u0103n',\n              editSelfOrder: 'Ch\u1ec9nh s\u1eeda t\u1ef1 \u0111\u1eb7t m\u00f3n',\n              hint: 'Ch\u1ea1m \u0111\u1ec3 di chuy\u1ec3n',\n            },\n            vault: {\n              toggle: 'Kho',\n              title: '\ud83c\udfe6 Kho b\u1ea1c',\n              tabs: {\n                upgrades: 'N\u00e2ng c\u1ea5p',\n                stats: 'Th\u1ed1ng k\u00ea',\n              },\n              section: {\n                upgrades: 'N\u00e2ng c\u1ea5p v\u0129nh vi\u1ec5n',\n                stats: 'Th\u1ed1ng k\u00ea nh\u00e0 h\u00e0ng',\n              },\n              upgrades: {\n                earnings: {\n                  name: '\ud83d\udcb9 T\u0103ng thu nh\u1eadp offline',\n                  desc: '+25% thu nh\u1eadp offline m\u1ed7i c\u1ea5p',\n                },\n                capacity: {\n                  name: '\ud83d\udce6 M\u1edf r\u1ed9ng l\u01b0u tr\u1eef',\n                  desc: '+10 s\u1ee9c ch\u1ee9a khay v\u0129nh vi\u1ec5n',\n                },\n                tips: {\n                  name: '\u2b50 H\u00fat kh\u00e1ch VIP',\n                  desc: '+5% t\u1ec9 l\u1ec7 kh\u00e1ch VIP',\n                },\n                speed: {\n                  name: '\u26a1 Ch\u1ebf \u0111\u1ed9 nhanh',\n                  desc: '+5% t\u1ed1c \u0111\u1ed9 di chuy\u1ec3n v\u0129nh vi\u1ec5n',\n                },\n                cook: {\n                  name: '\ud83d\udd25 \u0110\u1ea7u b\u1ebfp b\u1eadc th\u1ea7y',\n                  desc: '-5% th\u1eddi gian n\u1ea5u m\u1ed7i c\u1ea5p',\n                },\n                serve: {\n                  name: '\ud83c\udf7d\ufe0f Ph\u1ee5c v\u1ee5 nhanh',\n                  desc: '-10% th\u1eddi gian ph\u1ee5c v\u1ee5 m\u1ed7i c\u1ea5p',\n                },\n                cashpile: {\n                  name: '\ud83d\udcb0 M\u1edf r\u1ed9ng ti\u1ec1n m\u1eb7t',\n                  desc: '+20 s\u1ee9c ch\u1ee9a ti\u1ec1n m\u1eb7t t\u1ed1i \u0111a',\n                },\n              },\n              stats: {\n                vaultBalance: '\ud83c\udfe6 S\u1ed1 d\u01b0 kho',\n                prestigeLevel: '\u2b50 C\u1ea5p uy t\u00edn',\n                profitMultiplier: '\ud83d\udcc8 H\u1ec7 s\u1ed1 l\u1ee3i nhu\u1eadn',\n                vipChance: '\ud83d\udc65 T\u1ef7 l\u1ec7 VIP',\n                moveSpeed: '\ud83c\udfc3 T\u1ed1c \u0111\u1ed9 di chuy\u1ec3n',\n                trayCapacity: '\ud83d\udce6 S\u1ee9c ch\u1ee9a khay',\n              },\n            },\n            tutorial: {\n              cashFull: 'M\u00e1y t\u00ednh ti\u1ec1n \u0111\u00e3 \u0111\u1ea7y! H\u00e3y thu ti\u1ec1n!',\n              waitingCollect: 'Kh\u00e1ch \u0111ang ch\u1edd! H\u00e3y \u0111\u1ebfn b\u1ebfp n\u01b0\u1edbng \u0111\u1ec3 l\u1ea5y \u0111\u1ed3 \u0103n!',\n              waitingBring: 'Kh\u00e1ch \u0111ang ch\u1edd! H\u00e3y mang \u0111\u1ed3 \u0103n \u0111\u1ebfn qu\u1ea7y thu ng\u00e2n!',\n              waitingMan: 'Kh\u00e1ch \u0111ang ch\u1edd! H\u00e3y \u0111\u1ee9ng \u1edf qu\u1ea7y thu ng\u00e2n!',\n              buildTable: 'B\u01b0\u1edbc l\u00ean \u00f4 \u0111\u1ec3 x\u00e2y b\u00e0n \u0103n!',\n              buildGrill: 'B\u01b0\u1edbc l\u00ean \u00f4 \u0111\u1ec3 x\u00e2y b\u1ebfp n\u01b0\u1edbng!',\n              buildCashier: 'B\u01b0\u1edbc l\u00ean \u00f4 \u0111\u1ec3 x\u00e2y qu\u1ea7y thu ng\u00e2n!',\n              collectFood: '\u0110\u1ebfn b\u1ebfp n\u01b0\u1edbng \u0111\u1ec3 l\u1ea5y \u0111\u1ed3 \u0103n!',\n              bringFood: 'Mang \u0111\u1ed3 \u0103n \u0111\u1ebfn qu\u1ea7y thu ng\u00e2n!',\n              hireStaff: 'Thu\u00ea nh\u00e2n vi\u00ean \u0111\u1ec3 tr\u1ef1c qu\u1ea7y!',\n              cleanTable: '\u0110\u1ee3i kh\u00e1ch \u0103n xong r\u1ed3i d\u1ecdn b\u00e0n!',\n              collectEarnings: 'Thu ti\u1ec1n t\u1eeb \u0111\u1ed1ng ti\u1ec1n!',\n              driveThruCollect: 'Drive-thru \u0111\u00e3 m\u1edf! H\u00e3y \u0111\u1ebfn b\u1ebfp n\u01b0\u1edbng \u0111\u1ec3 l\u1ea5y \u0111\u1ed3 \u0103n!',\n              driveThruDeposit: 'Th\u00eam burger v\u00e0o qu\u1ea7y drive-thru!',\n            },\n            notifications: {\n              staffHiredTitle: '\u0110\u00e3 thu\u00ea nh\u00e2n vi\u00ean!',\n              cashier: 'Nh\u00e2n vi\u00ean thu ng\u00e2n',\n              cleaner: 'Nh\u00e2n vi\u00ean d\u1ecdn d\u1eb9p',\n              runner: 'Nh\u00e2n vi\u00ean giao h\u00e0ng',\n              vaultCollector: 'Nh\u00e2n vi\u00ean thu kho',\n              generic: '\u0110\u00e3 thu\u00ea nh\u00e2n vi\u00ean!',\n            },\n            rush: {\n              subtitle: 'Kh\u00e1ch \u0111ang \u0111\u1ebfn! +{{bonus}}% ti\u1ec1n tip!',\n            },\n            offline: {\n              timeAway: 'B\u1ea1n \u0111\u00e3 v\u1eafng m\u1eb7t {{time}}',\n              earnings: 'Nh\u00e2n vi\u00ean \u0111\u00e3 g\u1eedi ${{amount}} v\u00e0o kho!',\n            },\n          }\n        },\n        mr: {\n          translation: {\n            loading: {\n              title: '\u092b\u093e\u0938\u094d\u091f \u092b\u0942\u0921 \u091f\u093e\u092f\u0915\u0942\u0928',\n              text: '\u0932\u094b\u0921 \u0915\u0930\u0924 \u0906\u0939\u0947...',\n              tip: '\u091f\u0940\u092a: \u0935\u093f\u0935\u093f\u0927 \u092a\u0926\u093e\u0930\u094d\u0925 \u0936\u093f\u091c\u0935\u093e \u0906\u0923\u093f \u0917\u094d\u0930\u093e\u0939\u0915\u093e\u0902\u0928\u093e \u0935\u093f\u0915\u093e!',\n              setup: '\u0917\u0947\u092e \u0938\u0947\u091f\u0905\u092a \u0939\u094b\u0924 \u0906\u0939\u0947...',\n              initGraphics: '\u0917\u094d\u0930\u093e\u092b\u093f\u0915\u094d\u0938 \u0938\u0941\u0930\u0942 \u0939\u094b\u0924 \u0906\u0939\u0947\u0924...',\n              building: '\u0930\u0947\u0938\u094d\u091f\u0949\u0930\u0902\u091f \u0924\u092f\u093e\u0930 \u0939\u094b\u0924 \u0906\u0939\u0947...',\n              loadingModel: '\u0915\u0945\u0930\u0947\u0915\u094d\u091f\u0930 \u092e\u0949\u0921\u0947\u0932 \u0932\u094b\u0921 \u0939\u094b\u0924 \u0906\u0939\u0947...',\n              grills: '\u0917\u094d\u0930\u093f\u0932 \u0938\u0947\u091f \u0939\u094b\u0924 \u0906\u0939\u0947\u0924...',\n              images: '\u092a\u094d\u0930\u0924\u093f\u092e\u093e \u0932\u094b\u0921 \u0939\u094b\u0924 \u0906\u0939\u0947\u0924...',\n              sounds: '\u0906\u0935\u093e\u091c \u0932\u094b\u0921 \u0939\u094b\u0924 \u0906\u0939\u0947\u0924...',\n              preparing: '\u0915\u093f\u091a\u0928 \u0924\u092f\u093e\u0930 \u0939\u094b\u0924 \u0906\u0939\u0947...',\n              controls: '\u0915\u0902\u091f\u094d\u0930\u094b\u0932\u094d\u0938 \u0938\u0947\u091f \u0939\u094b\u0924 \u0906\u0939\u0947\u0924...',\n              finalizing: '\u0905\u0902\u0924\u093f\u092e \u0924\u092f\u093e\u0930\u0940...',\n              ready: '\u0924\u092f\u093e\u0930!',\n            },\n            editor: {\n              title: '\u0938\u094d\u0924\u0930 \u0938\u0902\u092a\u093e\u0926\u0915',\n              addGrill: '\u0917\u094d\u0930\u093f\u0932 \u091c\u094b\u0921\u093e',\n              addFryer: '\u092b\u094d\u0930\u093e\u092f\u0930 \u091c\u094b\u0921\u093e',\n              addDrink: '\u0921\u094d\u0930\u093f\u0902\u0915 \u0938\u094d\u091f\u0947\u0936\u0928 \u091c\u094b\u0921\u093e',\n              addDiningTable: '\u0921\u093e\u092f\u0928\u093f\u0902\u0917 \u091f\u0947\u092c\u0932 \u091c\u094b\u0921\u093e',\n              editSelfOrder: '\u0938\u0947\u0932\u094d\u092b-\u0911\u0930\u094d\u0921\u0930 \u0938\u0902\u092a\u093e\u0926\u093f\u0924 \u0915\u0930\u093e',\n              hint: '\u0939\u0932\u0935\u0923\u094d\u092f\u093e\u0938\u093e\u0920\u0940 \u091f\u0945\u092a/\u0915\u094d\u0932\u093f\u0915 \u0915\u0930\u093e',\n            },\n            vault: {\n              toggle: '\u0924\u093f\u091c\u094b\u0930\u0940',\n              title: '\ud83c\udfe6 \u092c\u0901\u0915 \u0924\u093f\u091c\u094b\u0930\u0940',\n              tabs: {\n                upgrades: '\u0905\u092a\u0917\u094d\u0930\u0947\u0921',\n                stats: '\u0906\u0915\u0921\u0947\u0935\u093e\u0930\u0940',\n              },\n              section: {\n                upgrades: '\u0915\u093e\u092f\u092e\u0938\u094d\u0935\u0930\u0942\u092a\u0940 \u0905\u092a\u0917\u094d\u0930\u0947\u0921',\n                stats: '\u0930\u0947\u0938\u094d\u091f\u0949\u0930\u0902\u091f \u0906\u0915\u0921\u0947\u0935\u093e\u0930\u0940',\n              },\n              upgrades: {\n                earnings: {\n                  name: '\ud83d\udcb9 \u0911\u092b\u0932\u093e\u0907\u0928 \u0915\u092e\u093e\u0908 \u092c\u0942\u0938\u094d\u091f',\n                  desc: '+25% \u0911\u092b\u0932\u093e\u0907\u0928 \u0915\u092e\u093e\u0908 \u092a\u094d\u0930\u0924\u093f \u0938\u094d\u0924\u0930',\n                },\n                capacity: {\n                  name: '\ud83d\udce6 \u0938\u094d\u091f\u094b\u0930\u0947\u091c \u0935\u093f\u0938\u094d\u0924\u093e\u0930',\n                  desc: '+10 \u0915\u093e\u092f\u092e\u0938\u094d\u0935\u0930\u0942\u092a\u0940 \u091f\u094d\u0930\u0947 \u0915\u094d\u0937\u092e\u0924\u093e',\n                },\n                tips: {\n                  name: '\u2b50 VIP \u0906\u0915\u0930\u094d\u0937\u0923',\n                  desc: '+5% VIP \u0917\u094d\u0930\u093e\u0939\u0915 \u0936\u0915\u094d\u092f\u0924\u093e',\n                },\n                speed: {\n                  name: '\u26a1 \u0935\u0947\u0917 \u092e\u094b\u0921',\n                  desc: '+5% \u0915\u093e\u092f\u092e\u0938\u094d\u0935\u0930\u0942\u092a\u0940 \u0917\u0924\u0940',\n                },\n                cook: {\n                  name: '\ud83d\udd25 \u092e\u093e\u0938\u094d\u091f\u0930 \u0936\u0947\u092b',\n                  desc: '-5% \u0936\u093f\u091c\u0935\u0923\u094d\u092f\u093e\u091a\u093e \u0935\u0947\u0933 \u092a\u094d\u0930\u0924\u093f \u0938\u094d\u0924\u0930',\n                },\n                serve: {\n                  name: '\ud83c\udf7d\ufe0f \u091c\u0932\u0926 \u0938\u0947\u0935\u093e',\n                  desc: '-10% \u0938\u0947\u0935\u093e \u0935\u0947\u0933 \u092a\u094d\u0930\u0924\u093f \u0938\u094d\u0924\u0930',\n                },\n                cashpile: {\n                  name: '\ud83d\udcb0 \u0915\u0945\u0936\u092a\u093e\u0907\u0932 \u0935\u093f\u0938\u094d\u0924\u093e\u0930',\n                  desc: '+20 \u091c\u093e\u0938\u094d\u0924\u0940\u0924 \u091c\u093e\u0938\u094d\u0924 \u0915\u0945\u0936\u092a\u093e\u0907\u0932 \u0915\u094d\u0937\u092e\u0924\u093e',\n                },\n              },\n              stats: {\n                vaultBalance: '\ud83c\udfe6 \u0924\u093f\u091c\u094b\u0930\u0940 \u0936\u093f\u0932\u094d\u0932\u0915',\n                prestigeLevel: '\u2b50 \u092a\u094d\u0930\u0947\u0938\u094d\u091f\u093f\u091c \u0938\u094d\u0924\u0930',\n                profitMultiplier: '\ud83d\udcc8 \u0928\u092b\u093e \u0917\u0941\u0923\u0915',\n                vipChance: '\ud83d\udc65 VIP \u0936\u0915\u094d\u092f\u0924\u093e',\n                moveSpeed: '\ud83c\udfc3 \u0917\u0924\u0940',\n                trayCapacity: '\ud83d\udce6 \u091f\u094d\u0930\u0947 \u0915\u094d\u0937\u092e\u0924\u093e',\n              },\n            },\n            tutorial: {\n              cashFull: '\u0915\u0945\u0936 \u0930\u091c\u093f\u0938\u094d\u091f\u0930 \u092d\u0930\u0932\u0947 \u0906\u0939\u0947! \u0924\u0941\u092e\u091a\u0947 \u092a\u0948\u0938\u0947 \u0917\u094b\u0933\u093e \u0915\u0930\u093e!',\n              waitingCollect: '\u0917\u094d\u0930\u093e\u0939\u0915 \u0935\u093e\u091f \u092a\u093e\u0939\u0924 \u0906\u0939\u0947\u0924! \u0917\u094d\u0930\u093f\u0932\u0915\u0921\u0947 \u091c\u093e\u090a\u0928 \u0905\u0928\u094d\u0928 \u0909\u091a\u0932 \u0915\u0930\u093e!',\n              waitingBring: '\u0917\u094d\u0930\u093e\u0939\u0915 \u0935\u093e\u091f \u092a\u093e\u0939\u0924 \u0906\u0939\u0947\u0924! \u0905\u0928\u094d\u0928 \u0915\u0945\u0936\u093f\u092f\u0930\u0915\u0921\u0947 \u0918\u0947\u090a\u0928 \u091c\u093e!',\n              waitingMan: '\u0917\u094d\u0930\u093e\u0939\u0915 \u0935\u093e\u091f \u092a\u093e\u0939\u0924 \u0906\u0939\u0947\u0924! \u0915\u0945\u0936 \u0915\u093e\u0909\u0902\u091f\u0930 \u0938\u0902\u092d\u093e\u0933\u093e!',\n              buildTable: '\u091f\u093e\u0907\u0932\u0935\u0930 \u0909\u092d\u0947 \u0930\u093e\u0939\u0942\u0928 \u0921\u093e\u092f\u0928\u093f\u0902\u0917 \u091f\u0947\u092c\u0932 \u0924\u092f\u093e\u0930 \u0915\u0930\u093e!',\n              buildGrill: '\u091f\u093e\u0907\u0932\u0935\u0930 \u0909\u092d\u0947 \u0930\u093e\u0939\u0942\u0928 \u0917\u094d\u0930\u093f\u0932 \u0924\u092f\u093e\u0930 \u0915\u0930\u093e!',\n              buildCashier: '\u091f\u093e\u0907\u0932\u0935\u0930 \u0909\u092d\u0947 \u0930\u093e\u0939\u0942\u0928 \u0915\u0945\u0936\u093f\u092f\u0930 \u0924\u092f\u093e\u0930 \u0915\u0930\u093e!',\n              collectFood: '\u0917\u094d\u0930\u093f\u0932\u0915\u0921\u0942\u0928 \u0905\u0928\u094d\u0928 \u0909\u091a\u0932 \u0915\u0930\u093e!',\n              bringFood: '\u0905\u0928\u094d\u0928 \u0915\u0945\u0936\u093f\u092f\u0930\u0915\u0921\u0947 \u0918\u0947\u090a\u0928 \u091c\u093e!',\n              hireStaff: '\u0915\u0945\u0936\u093f\u092f\u0930\u0938\u093e\u0920\u0940 \u0938\u094d\u091f\u093e\u092b \u092d\u093e\u0921\u094d\u092f\u093e\u0928\u0947 \u0918\u094d\u092f\u093e!',\n              cleanTable: '\u0917\u094d\u0930\u093e\u0939\u0915 \u0916\u093e\u090a\u0928 \u091d\u093e\u0932\u094d\u092f\u093e\u0935\u0930 \u091f\u0947\u092c\u0932 \u0938\u093e\u092b \u0915\u0930\u093e!',\n              collectEarnings: '\u0915\u0945\u0936 \u092a\u093e\u0907\u0932\u092e\u0927\u0942\u0928 \u0915\u092e\u093e\u0908 \u0909\u091a\u0932 \u0915\u0930\u093e!',\n              driveThruCollect: '\u0921\u094d\u0930\u093e\u0907\u0935\u094d\u0939-\u0925\u094d\u0930\u0942 \u0905\u0928\u0932\u0949\u0915! \u0917\u094d\u0930\u093f\u0932\u0915\u0921\u0947 \u091c\u093e\u090a\u0928 \u0905\u0928\u094d\u0928 \u0909\u091a\u0932 \u0915\u0930\u093e!',\n              driveThruDeposit: '\u0921\u094d\u0930\u093e\u0907\u0935\u094d\u0939-\u0925\u094d\u0930\u0942 \u0915\u093e\u0909\u0902\u091f\u0930\u0935\u0930 \u092c\u0930\u094d\u0917\u0930 \u0920\u0947\u0935\u093e!',\n            },\n            notifications: {\n              staffHiredTitle: '\u0938\u094d\u091f\u093e\u092b \u0928\u093f\u092f\u0941\u0915\u094d\u0924 \u0915\u0947\u0932\u093e!',\n              cashier: '\u0915\u0945\u0936\u093f\u092f\u0930 \u0938\u094d\u091f\u093e\u092b',\n              cleaner: '\u0915\u094d\u0932\u0940\u0928\u0930 \u0938\u094d\u091f\u093e\u092b',\n              runner: '\u0930\u0928\u0930 \u0938\u094d\u091f\u093e\u092b',\n              vaultCollector: '\u0924\u093f\u091c\u094b\u0930\u0940 \u0915\u0932\u0947\u0915\u094d\u091f\u0930',\n              generic: '\u0938\u094d\u091f\u093e\u092b \u0928\u093f\u092f\u0941\u0915\u094d\u0924 \u0915\u0947\u0932\u093e!',\n            },\n            rush: {\n              subtitle: '\u0917\u094d\u0930\u093e\u0939\u0915 \u092f\u0947\u0924 \u0906\u0939\u0947\u0924! +{{bonus}}% \u091f\u093f\u092a\u094d\u0938!',\n            },\n            offline: {\n              timeAway: '\u0924\u0941\u092e\u094d\u0939\u0940 {{time}} \u0926\u0942\u0930 \u0939\u094b\u0924\u093e',\n              earnings: '\u0924\u0941\u092e\u091a\u094d\u092f\u093e \u0938\u094d\u091f\u093e\u092b\u0928\u0947 \u0924\u093f\u091c\u094b\u0930\u0940\u0924 ${{amount}} \u091c\u092e\u093e \u0915\u0947\u0932\u0947!',\n            },\n          }\n        },\n        ur: {\n          translation: {\n            loading: {\n              title: '\u0641\u0627\u0633\u0679 \u0641\u0648\u0688 \u0679\u0627\u0626\u06a9\u0648\u0646',\n              text: '\u0644\u0648\u0688 \u06c1\u0648 \u0631\u06c1\u0627 \u06c1\u06d2...',\n              tip: '\u0679\u067e: \u0645\u062e\u062a\u0644\u0641 \u06a9\u06be\u0627\u0646\u06d2 \u067e\u06a9\u0627\u0626\u06cc\u06ba \u0627\u0648\u0631 \u06af\u0627\u06c1\u06a9\u0648\u06ba \u06a9\u0648 \u0628\u06cc\u0686\u06cc\u06ba!',\n              setup: '\u06af\u06cc\u0645 \u0633\u06cc\u0679 \u0627\u067e \u06c1\u0648 \u0631\u06c1\u06cc \u06c1\u06d2...',\n              initGraphics: '\u06af\u0631\u0627\u0641\u06a9\u0633 \u0634\u0631\u0648\u0639 \u06c1\u0648 \u0631\u06c1\u06d2 \u06c1\u06cc\u06ba...',\n              building: '\u0631\u06cc\u0633\u062a\u0648\u0631\u0627\u0646 \u062a\u06cc\u0627\u0631 \u06c1\u0648 \u0631\u06c1\u0627 \u06c1\u06d2...',\n              loadingModel: '\u06a9\u0631\u062f\u0627\u0631 \u0645\u0627\u0688\u0644 \u0644\u0648\u0688 \u06c1\u0648 \u0631\u06c1\u0627 \u06c1\u06d2...',\n              grills: '\u06af\u0631\u0650\u0644\u0632 \u0633\u06cc\u0679 \u06c1\u0648 \u0631\u06c1\u06cc \u06c1\u06cc\u06ba...',\n              images: '\u062a\u0635\u0627\u0648\u06cc\u0631 \u0644\u0648\u0688 \u06c1\u0648 \u0631\u06c1\u06cc \u06c1\u06cc\u06ba...',\n              sounds: '\u0622\u0648\u0627\u0632\u06cc\u06ba \u0644\u0648\u0688 \u06c1\u0648 \u0631\u06c1\u06cc \u06c1\u06cc\u06ba...',\n              preparing: '\u06a9\u0686\u0646 \u062a\u06cc\u0627\u0631 \u06c1\u0648 \u0631\u06c1\u0627 \u06c1\u06d2...',\n              controls: '\u06a9\u0646\u0679\u0631\u0648\u0644\u0632 \u0633\u06cc\u0679 \u06c1\u0648 \u0631\u06c1\u06d2 \u06c1\u06cc\u06ba...',\n              finalizing: '\u0622\u062e\u0631\u06cc \u062a\u06cc\u0627\u0631\u06cc...',\n              ready: '\u062a\u06cc\u0627\u0631!',\n            },\n            editor: {\n              title: '\u0644\u06cc\u0648\u0644 \u0627\u06cc\u0688\u06cc\u0679\u0631',\n              addGrill: '\u06af\u0631\u0644 \u0634\u0627\u0645\u0644 \u06a9\u0631\u06cc\u06ba',\n              addFryer: '\u0641\u0631\u0627\u0626\u06cc\u0631 \u0634\u0627\u0645\u0644 \u06a9\u0631\u06cc\u06ba',\n              addDrink: '\u0688\u0631\u0650\u0646\u06a9 \u0627\u0633\u0679\u06cc\u0634\u0646 \u0634\u0627\u0645\u0644 \u06a9\u0631\u06cc\u06ba',\n              addDiningTable: '\u0688\u0627\u0626\u0646\u0646\u06af \u0679\u06cc\u0628\u0644 \u0634\u0627\u0645\u0644 \u06a9\u0631\u06cc\u06ba',\n              editSelfOrder: '\u0633\u06cc\u0644\u0641 \u0622\u0631\u0688\u0631 \u0645\u06cc\u06ba \u062a\u0631\u0645\u06cc\u0645 \u06a9\u0631\u06cc\u06ba',\n              hint: '\u0622\u0628\u062c\u06cc\u06a9\u0679\u0633 \u06a9\u0648 \u0645\u0646\u062a\u0642\u0644 \u06a9\u0631\u0646\u06d2 \u06a9\u06d2 \u0644\u06cc\u06d2 \u0679\u06cc\u067e/\u06a9\u0644\u06a9 \u06a9\u0631\u06cc\u06ba',\n            },\n            vault: {\n              toggle: '\u0648\u0627\u0644\u0679',\n              title: '\ud83c\udfe6 \u0628\u06cc\u0646\u06a9 \u0648\u0627\u0644\u0679',\n              tabs: {\n                upgrades: '\u0627\u067e \u06af\u0631\u06cc\u0688\u0632',\n                stats: '\u0627\u0639\u062f\u0627\u062f \u0648 \u0634\u0645\u0627\u0631',\n              },\n              section: {\n                upgrades: '\u0645\u0633\u062a\u0642\u0644 \u0627\u067e \u06af\u0631\u06cc\u0688\u0632',\n                stats: '\u0631\u06cc\u0633\u062a\u0648\u0631\u0627\u0646 \u06a9\u06cc \u0645\u0639\u0644\u0648\u0645\u0627\u062a',\n              },\n              upgrades: {\n                earnings: {\n                  name: '\ud83d\udcb9 \u0622\u0641 \u0644\u0627\u0626\u0646 \u0622\u0645\u062f\u0646 \u0628\u0648\u0633\u0679',\n                  desc: '+25% \u0622\u0641 \u0644\u0627\u0626\u0646 \u0622\u0645\u062f\u0646 \u0641\u06cc \u0644\u06cc\u0648\u0644',\n                },\n                capacity: {\n                  name: '\ud83d\udce6 \u0627\u0633\u0679\u0648\u0631\u06cc\u062c \u0627\u0636\u0627\u0641\u06c1',\n                  desc: '+10 \u0645\u0633\u062a\u0642\u0644 \u0679\u0631\u06d2 \u06af\u0646\u062c\u0627\u0626\u0634',\n                },\n                tips: {\n                  name: '\u2b50 \u0648\u06cc \u0622\u0626\u06cc \u067e\u06cc \u0645\u06cc\u06af\u0646\u06cc\u0679',\n                  desc: '+5% \u0648\u06cc \u0622\u0626\u06cc \u067e\u06cc \u06af\u0627\u06c1\u06a9 \u0627\u0645\u06a9\u0627\u0646',\n                },\n                speed: {\n                  name: '\u26a1 \u06c1\u0633\u0644 \u0645\u0648\u0688',\n                  desc: '+5% \u0645\u0633\u062a\u0642\u0644 \u0631\u0641\u062a\u0627\u0631',\n                },\n                cook: {\n                  name: '\ud83d\udd25 \u0645\u0627\u0633\u0679\u0631 \u0634\u06cc\u0641',\n                  desc: '-5% \u067e\u06a9\u0627\u0646\u06d2 \u06a9\u0627 \u0648\u0642\u062a \u0641\u06cc \u0644\u06cc\u0648\u0644',\n                },\n                serve: {\n                  name: '\ud83c\udf7d\ufe0f \u062a\u06cc\u0632 \u0633\u0631\u0648\u0633',\n                  desc: '-10% \u0633\u0631\u0648\u0633 \u0648\u0642\u062a \u0641\u06cc \u0644\u06cc\u0648\u0644',\n                },\n                cashpile: {\n                  name: '\ud83d\udcb0 \u06a9\u06cc\u0634 \u067e\u0627\u0626\u0644 \u0627\u0636\u0627\u0641\u06c1',\n                  desc: '+20 \u0632\u06cc\u0627\u062f\u06c1 \u0633\u06d2 \u0632\u06cc\u0627\u062f\u06c1 \u06a9\u06cc\u0634 \u067e\u0627\u0626\u0644 \u06af\u0646\u062c\u0627\u0626\u0634',\n                },\n              },\n              stats: {\n                vaultBalance: '\ud83c\udfe6 \u0648\u0627\u0644\u0679 \u0628\u06cc\u0644\u0646\u0633',\n                prestigeLevel: '\u2b50 \u067e\u0631\u06cc\u0633\u0679\u06cc\u062c \u0644\u06cc\u0648\u0644',\n                profitMultiplier: '\ud83d\udcc8 \u0645\u0646\u0627\u0641\u0639 \u0636\u0631\u0628',\n                vipChance: '\ud83d\udc65 \u0648\u06cc \u0622\u0626\u06cc \u067e\u06cc \u0627\u0645\u06a9\u0627\u0646',\n                moveSpeed: '\ud83c\udfc3 \u0631\u0641\u062a\u0627\u0631',\n                trayCapacity: '\ud83d\udce6 \u0679\u0631\u06d2 \u06af\u0646\u062c\u0627\u0626\u0634',\n              },\n            },\n            tutorial: {\n              cashFull: '\u06a9\u06cc\u0634 \u0631\u062c\u0633\u0679\u0631 \u0628\u06be\u0631 \u06af\u06cc\u0627 \u06c1\u06d2! \u0627\u067e\u0646\u06d2 \u067e\u06cc\u0633\u06d2 \u062c\u0645\u0639 \u06a9\u0631\u06cc\u06ba!',\n              waitingCollect: '\u06af\u0627\u06c1\u06a9 \u0627\u0646\u062a\u0638\u0627\u0631 \u06a9\u0631 \u0631\u06c1\u06d2 \u06c1\u06cc\u06ba! \u06af\u0631\u0644 \u0633\u06d2 \u06a9\u06be\u0627\u0646\u0627 \u0627\u0679\u06be\u0627\u0626\u06cc\u06ba!',\n              waitingBring: '\u06af\u0627\u06c1\u06a9 \u0627\u0646\u062a\u0638\u0627\u0631 \u06a9\u0631 \u0631\u06c1\u06d2 \u06c1\u06cc\u06ba! \u06a9\u06be\u0627\u0646\u0627 \u06a9\u06cc\u0634\u06cc\u0626\u0631 \u062a\u06a9 \u067e\u06c1\u0646\u0686\u0627\u0626\u06cc\u06ba!',\n              waitingMan: '\u06af\u0627\u06c1\u06a9 \u0627\u0646\u062a\u0638\u0627\u0631 \u06a9\u0631 \u0631\u06c1\u06d2 \u06c1\u06cc\u06ba! \u06a9\u0627\u0624\u0646\u0679\u0631 \u0633\u0646\u0628\u06be\u0627\u0644\u06cc\u06ba!',\n              buildTable: '\u0679\u0627\u0626\u0644 \u067e\u0631 \u06a9\u06be\u0691\u06d2 \u06c1\u0648 \u06a9\u0631 \u0688\u0627\u0626\u0646\u0646\u06af \u0679\u06cc\u0628\u0644 \u0628\u0646\u0627\u0626\u06cc\u06ba!',\n              buildGrill: '\u0679\u0627\u0626\u0644 \u067e\u0631 \u06a9\u06be\u0691\u06d2 \u06c1\u0648 \u06a9\u0631 \u06af\u0631\u0644 \u0628\u0646\u0627\u0626\u06cc\u06ba!',\n              buildCashier: '\u0679\u0627\u0626\u0644 \u067e\u0631 \u06a9\u06be\u0691\u06d2 \u06c1\u0648 \u06a9\u0631 \u06a9\u06cc\u0634\u06cc\u0626\u0631 \u0628\u0646\u0627\u0626\u06cc\u06ba!',\n              collectFood: '\u06af\u0631\u0644 \u0633\u06d2 \u06a9\u06be\u0627\u0646\u0627 \u0627\u0679\u06be\u0627\u0626\u06cc\u06ba!',\n              bringFood: '\u06a9\u06be\u0627\u0646\u0627 \u06a9\u06cc\u0634\u06cc\u0626\u0631 \u062a\u06a9 \u0644\u06d2 \u062c\u0627\u0626\u06cc\u06ba!',\n              hireStaff: '\u06a9\u06cc\u0634\u06cc\u0626\u0631 \u06a9\u06d2 \u0644\u06cc\u06d2 \u0627\u0633\u0679\u0627\u0641 \u06c1\u0627\u0626\u0631 \u06a9\u0631\u06cc\u06ba!',\n              cleanTable: '\u06af\u0627\u06c1\u06a9 \u06a9\u06d2 \u06a9\u06be\u0627\u0646\u06d2 \u06a9\u06d2 \u0628\u0639\u062f \u0645\u06cc\u0632 \u0635\u0627\u0641 \u06a9\u0631\u06cc\u06ba!',\n              collectEarnings: '\u06a9\u06cc\u0634 \u067e\u0627\u0626\u0644 \u0633\u06d2 \u0622\u0645\u062f\u0646 \u062c\u0645\u0639 \u06a9\u0631\u06cc\u06ba!',\n              driveThruCollect: '\u0688\u0631\u0627\u0626\u06cc\u0648 \u062a\u06be\u0631\u0648 \u06a9\u06be\u0644 \u06af\u06cc\u0627! \u06af\u0631\u0644 \u0633\u06d2 \u06a9\u06be\u0627\u0646\u0627 \u0627\u0679\u06be\u0627\u0626\u06cc\u06ba!',\n              driveThruDeposit: '\u0688\u0631\u0627\u0626\u06cc\u0648 \u062a\u06be\u0631\u0648 \u06a9\u0627\u0624\u0646\u0679\u0631 \u067e\u0631 \u0628\u0631\u06af\u0631 \u0631\u06a9\u06be\u06cc\u06ba!',\n            },\n            notifications: {\n              staffHiredTitle: '\u0627\u0633\u0679\u0627\u0641 \u06c1\u0627\u0626\u0631 \u06c1\u0648\u06af\u06cc\u0627!',\n              cashier: '\u06a9\u06cc\u0634\u06cc\u0626\u0631 \u0627\u0633\u0679\u0627\u0641',\n              cleaner: '\u0635\u0641\u0627\u0626\u06cc \u0627\u0633\u0679\u0627\u0641',\n              runner: '\u0631\u0646\u0631 \u0627\u0633\u0679\u0627\u0641',\n              vaultCollector: '\u0648\u0627\u0644\u0679 \u06a9\u0644\u06cc\u06a9\u0679\u0631',\n              generic: '\u0627\u0633\u0679\u0627\u0641 \u06c1\u0627\u0626\u0631 \u06c1\u0648\u06af\u06cc\u0627!',\n            },\n            rush: {\n              subtitle: '\u06af\u0627\u06c1\u06a9 \u0622 \u0631\u06c1\u06d2 \u06c1\u06cc\u06ba! +{{bonus}}% \u0679\u067e\u0633!',\n            },\n            offline: {\n              timeAway: '\u0622\u067e {{time}} \u062f\u0648\u0631 \u062a\u06be\u06d2',\n              earnings: '\u0622\u067e \u06a9\u06d2 \u0627\u0633\u0679\u0627\u0641 \u0646\u06d2 \u0648\u0627\u0644\u0679 \u0645\u06cc\u06ba ${{amount}} \u062c\u0645\u0639 \u06a9\u06cc\u0627!',\n            },\n          }\n        },\n        ar: {\n          translation: {\n            loading: {\n              title: '\u0642\u0637\u0628 \u0627\u0644\u0648\u062c\u0628\u0627\u062a \u0627\u0644\u0633\u0631\u064a\u0639\u0629',\n              text: '\u062c\u0627\u0631\u064a \u0627\u0644\u062a\u062d\u0645\u064a\u0644...',\n              tip: '\u0646\u0635\u064a\u062d\u0629: \u0627\u0637\u0628\u062e \u0623\u0637\u0639\u0645\u0629 \u0645\u062e\u062a\u0644\u0641\u0629 \u0648\u0642\u0645 \u0628\u0628\u064a\u0639\u0647\u0627 \u0644\u0644\u0639\u0645\u0644\u0627\u0621!',\n              setup: '\u062c\u0627\u0631\u064d \u0625\u0639\u062f\u0627\u062f \u0627\u0644\u0644\u0639\u0628\u0629...',\n              initGraphics: '\u062c\u0627\u0631\u064d \u062a\u0647\u064a\u0626\u0629 \u0627\u0644\u0631\u0633\u0648\u0645\u0627\u062a...',\n              building: '\u062c\u0627\u0631\u064d \u0628\u0646\u0627\u0621 \u0627\u0644\u0645\u0637\u0639\u0645...',\n              loadingModel: '\u062c\u0627\u0631\u064d \u062a\u062d\u0645\u064a\u0644 \u0646\u0645\u0648\u0630\u062c \u0627\u0644\u0634\u062e\u0635\u064a\u0629...',\n              grills: '\u062c\u0627\u0631\u064d \u062a\u062c\u0647\u064a\u0632 \u0627\u0644\u0634\u0648\u0627\u064a\u0627\u062a...',\n              images: '\u062c\u0627\u0631\u064d \u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0635\u0648\u0631...',\n              sounds: '\u062c\u0627\u0631\u064d \u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0623\u0635\u0648\u0627\u062a...',\n              preparing: '\u062c\u0627\u0631\u064d \u062a\u062c\u0647\u064a\u0632 \u0627\u0644\u0645\u0637\u0628\u062e...',\n              controls: '\u062c\u0627\u0631\u064d \u0625\u0639\u062f\u0627\u062f \u0639\u0646\u0627\u0635\u0631 \u0627\u0644\u062a\u062d\u0643\u0645...',\n              finalizing: '\u062c\u0627\u0631\u064d \u0627\u0644\u0644\u0645\u0633\u0627\u062a \u0627\u0644\u0623\u062e\u064a\u0631\u0629...',\n              ready: '\u062c\u0627\u0647\u0632!',\n            },\n            editor: {\n              title: '\u0645\u062d\u0631\u0631 \u0627\u0644\u0645\u0633\u062a\u0648\u0649',\n              addGrill: '\u0625\u0636\u0627\u0641\u0629 \u0634\u0648\u0627\u064a\u0629',\n              addFryer: '\u0625\u0636\u0627\u0641\u0629 \u0645\u0642\u0644\u0627\u0629',\n              addDrink: '\u0625\u0636\u0627\u0641\u0629 \u0645\u062d\u0637\u0629 \u0645\u0634\u0631\u0648\u0628\u0627\u062a',\n              addDiningTable: '\u0625\u0636\u0627\u0641\u0629 \u0637\u0627\u0648\u0644\u0629 \u0637\u0639\u0627\u0645',\n              editSelfOrder: '\u062a\u0639\u062f\u064a\u0644 \u0627\u0644\u0637\u0644\u0628 \u0627\u0644\u0630\u0627\u062a\u064a',\n              hint: '\u0627\u0636\u063a\u0637/\u0627\u0646\u0642\u0631 \u0644\u062a\u062d\u0631\u064a\u0643 \u0627\u0644\u0639\u0646\u0627\u0635\u0631',\n            },\n            vault: {\n              toggle: '\u0642\u0628\u0648',\n              title: '\ud83c\udfe6 \u0642\u0628\u0648 \u0627\u0644\u0628\u0646\u0643',\n              tabs: {\n                upgrades: '\u062a\u0631\u0642\u064a\u0627\u062a',\n                stats: '\u0625\u062d\u0635\u0627\u0626\u064a\u0627\u062a',\n              },\n              section: {\n                upgrades: '\u062a\u0631\u0642\u064a\u0627\u062a \u062f\u0627\u0626\u0645\u0629',\n                stats: '\u0625\u062d\u0635\u0627\u0626\u064a\u0627\u062a \u0627\u0644\u0645\u0637\u0639\u0645',\n              },\n              upgrades: {\n                earnings: {\n                  name: '\ud83d\udcb9 \u062a\u0639\u0632\u064a\u0632 \u0627\u0644\u0623\u0631\u0628\u0627\u062d \u062f\u0648\u0646 \u0627\u062a\u0635\u0627\u0644',\n                  desc: '+25% \u0623\u0631\u0628\u0627\u062d \u062f\u0648\u0646 \u0627\u062a\u0635\u0627\u0644 \u0644\u0643\u0644 \u0645\u0633\u062a\u0648\u0649',\n                },\n                capacity: {\n                  name: '\ud83d\udce6 \u062a\u0648\u0633\u0639\u0629 \u0627\u0644\u062a\u062e\u0632\u064a\u0646',\n                  desc: '+10 \u0633\u0639\u0629 \u0635\u0648\u0627\u0646\u064a \u062f\u0627\u0626\u0645\u0629',\n                },\n                tips: {\n                  name: '\u2b50 \u062c\u0630\u0628 \u0639\u0645\u0644\u0627\u0621 VIP',\n                  desc: '+5% \u0641\u0631\u0635\u0629 \u0639\u0645\u0644\u0627\u0621 VIP',\n                },\n                speed: {\n                  name: '\u26a1 \u0648\u0636\u0639 \u0627\u0644\u0633\u0631\u0639\u0629',\n                  desc: '+5% \u0633\u0631\u0639\u0629 \u062d\u0631\u0643\u0629 \u062f\u0627\u0626\u0645\u0629',\n                },\n                cook: {\n                  name: '\ud83d\udd25 \u0637\u0627\u0647\u064d \u0645\u062d\u062a\u0631\u0641',\n                  desc: '-5% \u0648\u0642\u062a \u0627\u0644\u0637\u0647\u064a \u0644\u0643\u0644 \u0645\u0633\u062a\u0648\u0649',\n                },\n                serve: {\n                  name: '\ud83c\udf7d\ufe0f \u062e\u062f\u0645\u0629 \u0633\u0631\u064a\u0639\u0629',\n                  desc: '-10% \u0648\u0642\u062a \u0627\u0644\u062e\u062f\u0645\u0629 \u0644\u0643\u0644 \u0645\u0633\u062a\u0648\u0649',\n                },\n                cashpile: {\n                  name: '\ud83d\udcb0 \u062a\u0648\u0633\u0639\u0629 \u0631\u0635\u064a\u062f \u0627\u0644\u0646\u0642\u062f',\n                  desc: '+20 \u0633\u0639\u0629 \u0646\u0642\u062f\u064a\u0629 \u0642\u0635\u0648\u0649',\n                },\n              },\n              stats: {\n                vaultBalance: '\ud83c\udfe6 \u0631\u0635\u064a\u062f \u0627\u0644\u0642\u0628\u0648',\n                prestigeLevel: '\u2b50 \u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u0647\u064a\u0628\u0629',\n                profitMultiplier: '\ud83d\udcc8 \u0645\u0636\u0627\u0639\u0641 \u0627\u0644\u0623\u0631\u0628\u0627\u062d',\n                vipChance: '\ud83d\udc65 \u0641\u0631\u0635\u0629 VIP',\n                moveSpeed: '\ud83c\udfc3 \u0633\u0631\u0639\u0629 \u0627\u0644\u062d\u0631\u0643\u0629',\n                trayCapacity: '\ud83d\udce6 \u0633\u0639\u0629 \u0627\u0644\u0635\u064a\u0646\u064a\u0629',\n              },\n            },\n            tutorial: {\n              cashFull: '\u0622\u0644\u0629 \u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u0646\u0642\u062f \u0645\u0645\u062a\u0644\u0626\u0629! \u0627\u062c\u0645\u0639 \u0623\u0645\u0648\u0627\u0644\u0643!',\n              waitingCollect: '\u0627\u0644\u0639\u0645\u0644\u0627\u0621 \u064a\u0646\u062a\u0638\u0631\u0648\u0646! \u0627\u0630\u0647\u0628 \u0644\u0644\u0634\u0648\u0627\u064a\u0629 \u0644\u062c\u0645\u0639 \u0627\u0644\u0637\u0639\u0627\u0645!',\n              waitingBring: '\u0627\u0644\u0639\u0645\u0644\u0627\u0621 \u064a\u0646\u062a\u0638\u0631\u0648\u0646! \u0623\u062d\u0636\u0631 \u0627\u0644\u0637\u0639\u0627\u0645 \u0625\u0644\u0649 \u0643\u0627\u0648\u0646\u062a\u0631 \u0627\u0644\u0643\u0627\u0634\u064a\u0631!',\n              waitingMan: '\u0627\u0644\u0639\u0645\u0644\u0627\u0621 \u064a\u0646\u062a\u0638\u0631\u0648\u0646! \u062a\u0648\u0644\u0651\u064e\u0649 \u0627\u0644\u0635\u0646\u062f\u0648\u0642 \u0644\u062e\u062f\u0645\u062a\u0647\u0645!',\n              buildTable: '\u0642\u0641 \u0639\u0644\u0649 \u0627\u0644\u0628\u0644\u0627\u0637\u0629 \u0644\u0628\u0646\u0627\u0621 \u0637\u0627\u0648\u0644\u0629 \u0637\u0639\u0627\u0645!',\n              buildGrill: '\u0642\u0641 \u0639\u0644\u0649 \u0627\u0644\u0628\u0644\u0627\u0637\u0629 \u0644\u0628\u0646\u0627\u0621 \u0627\u0644\u0634\u0648\u0627\u064a\u0627\u062a!',\n              buildCashier: '\u0642\u0641 \u0639\u0644\u0649 \u0627\u0644\u0628\u0644\u0627\u0637\u0629 \u0644\u0628\u0646\u0627\u0621 \u0627\u0644\u0643\u0627\u0634\u064a\u0631!',\n              collectFood: '\u0627\u0630\u0647\u0628 \u0625\u0644\u0649 \u0627\u0644\u0634\u0648\u0627\u064a\u0629 \u0644\u062c\u0645\u0639 \u0627\u0644\u0637\u0639\u0627\u0645!',\n              bringFood: '\u0623\u062d\u0636\u0631 \u0627\u0644\u0637\u0639\u0627\u0645 \u0625\u0644\u0649 \u0627\u0644\u0643\u0627\u0634\u064a\u0631!',\n              hireStaff: '\u0639\u064a\u0651\u0646 \u0627\u0644\u0645\u0648\u0638\u0641\u064a\u0646 \u0644\u0625\u062f\u0627\u0631\u0629 \u0627\u0644\u0643\u0627\u0634\u064a\u0631!',\n              cleanTable: '\u0627\u0646\u062a\u0638\u0631 \u062d\u062a\u0649 \u064a\u0646\u062a\u0647\u064a \u0627\u0644\u0639\u0645\u0644\u0627\u0621 \u062b\u0645 \u0646\u0638\u0651\u0641 \u0627\u0644\u0637\u0627\u0648\u0644\u0629!',\n              collectEarnings: '\u0627\u062c\u0645\u0639 \u0623\u0631\u0628\u0627\u062d\u0643 \u0645\u0646 \u0643\u0648\u0645\u0629 \u0627\u0644\u0646\u0642\u0648\u062f!',\n              driveThruCollect: '\u062a\u0645 \u0641\u062a\u062d \u0627\u0644\u0642\u064a\u0627\u062f\u0629! \u0627\u0630\u0647\u0628 \u0625\u0644\u0649 \u0627\u0644\u0634\u0648\u0627\u064a\u0629 \u0644\u062c\u0645\u0639 \u0627\u0644\u0637\u0639\u0627\u0645!',\n              driveThruDeposit: '\u0636\u0639 \u0627\u0644\u0628\u0631\u063a\u0631 \u0641\u064a \u0643\u0627\u0648\u0646\u062a\u0631 \u0627\u0644\u0642\u064a\u0627\u062f\u0629!',\n            },\n            notifications: {\n              staffHiredTitle: '\u062a\u0645 \u062a\u0648\u0638\u064a\u0641 \u0627\u0644\u0645\u0648\u0638\u0641!',\n              cashier: '\u0645\u0648\u0638\u0641 \u0627\u0644\u0643\u0627\u0634\u064a\u0631',\n              cleaner: '\u0645\u0648\u0638\u0641 \u0627\u0644\u062a\u0646\u0638\u064a\u0641',\n              runner: '\u0645\u0648\u0638\u0641 \u0627\u0644\u062a\u0648\u0635\u064a\u0644',\n              vaultCollector: '\u062c\u0627\u0645\u0639 \u0627\u0644\u0642\u0628\u0648',\n              generic: '\u062a\u0645 \u062a\u0648\u0638\u064a\u0641 \u0627\u0644\u0645\u0648\u0638\u0641!',\n            },\n            rush: {\n              subtitle: '\u0627\u0644\u0639\u0645\u0644\u0627\u0621 \u0642\u0627\u062f\u0645\u0648\u0646! +{{bonus}}% \u0628\u0642\u0634\u064a\u0634!',\n            },\n            offline: {\n              timeAway: '\u0643\u0646\u062a \u0628\u0639\u064a\u062f\u064b\u0627 \u0644\u0645\u062f\u0629 {{time}}',\n              earnings: '\u0642\u0627\u0645 \u0637\u0627\u0642\u0645\u0643 \u0628\u0625\u064a\u062f\u0627\u0639 ${{amount}} \u0641\u064a \u0627\u0644\u0642\u0628\u0648!',\n            },\n          }\n        }\n      };\n\n      function t(key, options = {}) {\n        if (typeof i18next !== 'undefined' && i18next) {\n          return i18next.t(key, options);\n        }\n        return key;\n      }\n\n      function applyTranslations() {\n        const nodes = document.querySelectorAll('[data-i18n]');\n        nodes.forEach((el) => {\n          const key = el.getAttribute('data-i18n');\n          if (key) {\n            el.textContent = t(key);\n          }\n        });\n        applyDirectionality();\n      }\n\n      function applyDirectionality() {\n        if (typeof i18next === 'undefined' || !i18next) return;\n        const lang = i18next.resolvedLanguage || i18next.language || 'en';\n        const isRTL = lang === 'ar' || lang === 'ur';\n        document.documentElement.setAttribute('dir', isRTL ? 'rtl' : 'ltr');\n        document.body.classList.toggle('rtl', isRTL);\n      }\n\n      function initI18n() {\n        if (i18nReady || typeof i18next === 'undefined') {\n          return Promise.resolve();\n        }\n        return i18next\n          .use(i18nextBrowserLanguageDetector)\n          .init({\n            resources: i18nResources,\n            fallbackLng: 'en',\n            supportedLngs: ['en', 'es', 'bn', 'vi', 'mr', 'ur', 'ar'],\n            interpolation: { escapeValue: false },\n            detection: {\n              order: ['querystring', 'navigator', 'htmlTag'],\n              lookupQuerystring: 'lang',\n              caches: [],\n            },\n          })\n          .then(() => {\n            i18nReady = true;\n            applyTranslations();\n          })\n          .catch((err) => {\n            lib.log('i18n init failed: ' + err.message);\n          });\n      }\n\n      function setLanguage(lang) {\n        if (typeof i18next === 'undefined' || !i18next) return;\n        i18next.changeLanguage(lang).then(() => {\n          applyTranslations();\n        });\n      }\n\n      // Loading state\n      let loadingProgress = 0;\n      let totalLoadSteps = 0;\n      let completedLoadSteps = 0;\n\n      // Game loop\n      let lastTime = 0;\n      let autosaveTimer = 0;\n\n      // Level Editor state\n      let raycaster = null;\n      let editorSelectedObject = null;\n      let editorIsDragging = false;\n      let editorDragStart = {\n        x: 0,\n        z: 0,\n      };\n      let editorObjectStart = {\n        x: 0,\n        z: 0,\n      };\n      let selectionHighlight = null;\n      let editableObjects = [];\n      // Array of {mesh, type, configKey, data}\n      let nextGrillId = 0;\n\n      let tutorialArrowMesh = null;\n      let tutorialCompleteShown = false;\n      let tutorialCompleteTimer = 0;\n\n      function createTutorialArrow() {\n        const arrowGroup = new THREE.Group();\n\n        // Cone (head)\n        const headGeom = new THREE.ConeGeometry(0.6, 1.2, 16);\n        const headMat = new THREE.MeshBasicMaterial({ color: 0x90ee90 });\n        const head = new THREE.Mesh(headGeom, headMat);\n        head.rotation.x = Math.PI; // point down\n        head.position.y = -0.6;\n        arrowGroup.add(head);\n\n        // Cylinder (body)\n        const bodyGeom = new THREE.CylinderGeometry(0.25, 0.25, 1.5, 16);\n        const body = new THREE.Mesh(bodyGeom, headMat);\n        body.position.y = 0.75;\n        arrowGroup.add(body);\n\n        scene.add(arrowGroup);\n        tutorialArrowMesh = arrowGroup;\n        tutorialArrowMesh.visible = false;\n      }\n\n      // Camera pan state (for editor)\n      let editorIsPanning = false;\n      let editorPanStart = {\n        x: 0,\n        y: 0,\n      };\n      let editorCameraStart = {\n        x: 0,\n        z: 0,\n      };\n      let editorCameraOffset = {\n        x: 0,\n        z: 0,\n      };\n\n      // Constants\n      const PLAYER_HEIGHT = 2;\n      const AUTOSAVE_INTERVAL = 30;\n      // Autosave every 30 seconds\n      const STACK_ITEM_HEIGHT = 0.35;\n      const STACK_ITEM_RADIUS = 0.4;\n      const COLLECTION_RANGE = 3;\n      const SELL_DELAY = 0.15;\n      // Time between selling each item\n      const MONEY_COLLECT_DELAY = 0.1;\n      // Time between collecting money\n      const BURGER_COLLECT_DELAY = 0.12;\n      // Time between collecting burgers\n      const SPEND_DELAY = 0.05;\n      // Time between spending money\n      const MONEY_STACK_HEIGHT = 0.35;\n      // Height of each money item (same as burger)\n      const MAX_MONEY_CAPACITY = 50;\n      // Max money player can carry\n      const MONEY_SIZE = 0.8;\n      // Size of money rectangle (similar to burger)\n      const MONEY_GRID_COLS = 4;\n      // Columns in money zone grid\n      const MONEY_GRID_ROWS = 3;\n      // Rows in money zone grid\n      let moneyGridStacks = [];\n      // Track stacks at each grid position\n\n      // Flying items for juice effects\n      let flyingItems = [];\n      // Items being vacuumed or thrown\n      const VACUUM_SPEED_DEFAULT = 15;\n      // Default speed of vacuum effect\n      const VACUUM_RANGE = 4;\n      // Range to start vacuuming\n      const THROW_SPEED = 20;\n      // Speed of thrown items\n      const THROW_ARC_HEIGHT = 3;\n      // Height of throw arc\n\n      // ============================================\n      // PERFORMANCE OPTIMIZATION - Cached Geometries/Materials\n      // ============================================\n      const cachedGeometries = {};\n      const cachedMaterials = {};\n\n      // Object pools for frequently created/destroyed items\n      const objectPools = {\n        flyingItems: [],\n        smokeParticles: [],\n        groundItems: [],\n        moneyItems: [],\n      };\n      const POOL_MAX_SIZE = 50;\n\n      // Cached DOM elements\n      let cachedDOMElements = {};\n\n      // Reusable vectors to avoid garbage collection\n      const tempVec3 = new THREE.Vector3();\n      const tempVec3_2 = new THREE.Vector3();\n\n      function getCachedGeometry(key, createFn) {\n        if (!cachedGeometries[key]) {\n          cachedGeometries[key] = createFn();\n          cachedGeometries[key].dispose = function () {};\n        }\n        return cachedGeometries[key];\n      }\n\n      function getCachedMaterial(key, createFn) {\n        if (!cachedMaterials[key]) {\n          cachedMaterials[key] = createFn();\n          cachedMaterials[key].dispose = function () {};\n        }\n        return cachedMaterials[key];\n      }\n\n      function cacheDOMElements() {\n        cachedDOMElements = {\n          currencyValue: document.getElementById('currency-value'),\n          stackCount: document.getElementById('stack-count'),\n          stackMax: document.getElementById('stack-max'),\n          moneyCount: document.getElementById('money-stack-count'),\n          moneyMax: document.getElementById('money-stack-max'),\n          cashierCount: document.getElementById('cashier-count'),\n          cashierMax: document.getElementById('cashier-max'),\n          customerCount: document.getElementById('customer-count'),\n          cashierEarnings: document.getElementById('cashier-earnings'),\n          collectionProgress: document.getElementById('collection-progress'),\n          progressBar: document.getElementById('progress-bar'),\n          vaultBalance: document.getElementById('vault-balance'),\n          vaultBalanceLarge: document.getElementById('vault-balance-large'),\n        };\n      }\n\n      // ============================================\n      // SELF-ORDER KIOSK SYSTEM\n      // ============================================\n\nfunction createSelfOrderKiosks() {\n        if (!window.gameConfig.selfOrderKiosks) return;\n\n        window.gameConfig.selfOrderKiosks.forEach((config, index) => {\n          if (config.deleted || config.hidden) return;\n\n          const group = new THREE.Group();\n\n          // Base/Stand\n          const baseGeom = new THREE.BoxGeometry(0.8, 1.2, 0.4);\n          const baseMat = new THREE.MeshStandardMaterial({\n            color: 0xbdc3c7,\n            roughness: 0.6,\n          });\n          const base = new THREE.Mesh(baseGeom, baseMat);\n          base.position.y = 0.6;\n          base.castShadow = true;\n          group.add(base);\n\n          // Screen\n          const screenGeom = new THREE.BoxGeometry(1.2, 1.6, 0.1);\n          const screenMat = new THREE.MeshStandardMaterial({\n            color: 0x34495e,\n            roughness: 0.4,\n          });\n          const screen = new THREE.Mesh(screenGeom, screenMat);\n          screen.position.set(0, 1.6, 0.15);\n          screen.rotation.x = -0.1;\n          screen.castShadow = true;\n          group.add(screen);\n\n          // Glowing display\n          const displayGeom = new THREE.PlaneGeometry(1.0, 1.4);\n          const displayMat = new THREE.MeshStandardMaterial({\n            color: 0xffffff,\n            emissive: 0xecf0f1,\n            emissiveIntensity: 0.4,\n            roughness: 0.2,\n          });\n          const display = new THREE.Mesh(displayGeom, displayMat);\n          display.position.set(0, 1.6, 0.21);\n          display.rotation.x = -0.1;\n          group.add(display);\n\n          // Kiosk \"pay here\" glowing slot\n          const slotGeom = new THREE.BoxGeometry(0.3, 0.05, 0.05);\n          const slotMat = new THREE.MeshStandardMaterial({\n            color: 0x2ecc71,\n            emissive: 0x2ecc71,\n            emissiveIntensity: 0.5,\n          });\n          const slot = new THREE.Mesh(slotGeom, slotMat);\n          slot.position.set(0, 0.8, 0.3);\n          group.add(slot);\n\n          // Order icon floating above (different for each kiosk)\n          const iconCanvas = document.createElement('canvas');\n          iconCanvas.width = 128;\n          iconCanvas.height = 128;\n          const ctx = iconCanvas.getContext('2d');\n          ctx.font = '80px Arial';\n          ctx.textAlign = 'center';\n          ctx.textBaseline = 'middle';\n          ctx.fillText(config.icon || '\ud83d\udcf1', 64, 64);\n          const iconTex = new THREE.CanvasTexture(iconCanvas);\n          iconTex.minFilter = THREE.LinearFilter;\n          const iconMesh = new THREE.Mesh(\n            new THREE.PlaneGeometry(1, 1),\n            new THREE.MeshBasicMaterial({\n              map: iconTex,\n              transparent: true,\n              side: THREE.DoubleSide,\n            })\n          );\n          iconMesh.position.y = 3;\n          group.add(iconMesh);\n          group.userData.icon = iconMesh;\n\n          group.position.set(config.x, 0, config.z);\n          group.rotation.y = THREE.MathUtils.degToRad(config.rotation || 0);\n\n          scene.add(group);\n\n          // Add a subtle glow on the floor\n          const glowGeom = new THREE.RingGeometry(1, 1.5, 32);\n          const glowMat = new THREE.MeshBasicMaterial({\n            color: config.color,\n            transparent: true,\n            opacity: 0.3,\n            side: THREE.DoubleSide,\n          });\n          const glow = new THREE.Mesh(glowGeom, glowMat);\n          glow.rotation.x = -Math.PI / 2;\n          glow.position.set(config.x, 0.02, config.z);\n          scene.add(glow);\n\n          // Store in array\n          if (selfOrderKiosks[index]) {\n            selfOrderKiosks[index].mesh = group;\n            selfOrderKiosks[index].glow = glow;\n          }\n        });\n      }\n\n      function updateSelfOrderKioskPosition(kioskId, x, z) {\n        const kiosk = selfOrderKiosks.find((k) => k.id === kioskId);\n        if (!kiosk || !kiosk.config || !kiosk.mesh) return;\n\n        kiosk.mesh.position.x = x;\n        kiosk.mesh.position.z = z;\n        if (kiosk.glow) {\n          kiosk.glow.position.x = x;\n          kiosk.glow.position.z = z;\n        }\n\n        kiosk.config.x = x;\n        kiosk.config.z = z;\n      }\n\nfunction updateSelfOrderKiosks(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const now = Date.now();\n        const cameraPos = camera.position;\n\n        selfOrderKiosks.forEach((kiosk) => {\n          const config = kiosk.config;\n          if (!config || config.deleted || !kiosk.mesh) return;\n\n          // Spawn logic\n          kiosk.spawnTimer += deltaTime;\n          const spawnRate = getCustomerSpawnRate() * 1.5;\n          if (\n            kiosk.spawnTimer > spawnRate &&\n            kiosk.queue.length < config.maxQueue &&\n            !isTotalCustomerCapReached()\n          ) {\n            kiosk.spawnTimer = 0;\n            const isVIP = Math.random() < (window.gameConfig.vipCustomers?.spawnChance || 0.15);\n            const mesh = createCustomerMesh(isVIP);\n\n            const startX = -RESTAURANT_WIDTH / 2 - 5;\n            const startZ = config.z + 5;\n            mesh.position.set(startX, 0, startZ);\n            scene.add(mesh);\n\n            const rotRad = THREE.MathUtils.degToRad(config.rotation || 0);\n            const dirZ = Math.cos(rotRad);\n            const dirX = Math.sin(rotRad);\n\n            const qPos = kiosk.queue.length;\n            const targetX = config.x + dirX * (2 + qPos * SELF_ORDER_SPACING);\n            const targetZ = config.z + dirZ * (2 + qPos * SELF_ORDER_SPACING);\n\n            kiosk.queue.push({\n              mesh: mesh,\n              state: 'walking',\n              targetX: targetX,\n              targetZ: targetZ,\n              queuePosition: qPos,\n              isVIP: isVIP,\n              orderTime: 0,\n            });\n          }\n\n          // Process Queue\n          for (let i = kiosk.queue.length - 1; i >= 0; i--) {\n            const c = kiosk.queue[i];\n            const mesh = c.mesh;\n            const squish = mesh.userData.squishState || {\n              scaleY: 1,\n              scaleXZ: 1,\n              targetScaleY: 1,\n              targetScaleXZ: 1,\n            };\n\n            if (mesh.userData.emojiMesh) {\n              updateCustomerEmojiVisibility(c, now);\n              mesh.userData.emojiMesh.lookAt(cameraPos);\n            }\n\n            if (c.state === 'walking') {\n              switchAnimation(mesh, 'run');\n              const dx = c.targetX - mesh.position.x;\n              const dz = c.targetZ - mesh.position.z;\n              const distSq = dx * dx + dz * dz;\n\n              if (distSq > 0.05) {\n                const dist = Math.sqrt(distSq);\n                mesh.position.x += (dx / dist) * 5 * deltaTime;\n                mesh.position.z += (dz / dist) * 5 * deltaTime;\n                mesh.rotation.y = Math.atan2(dx, dz);\n\n                const walkCycle = Math.sin(now * 0.012 + i) * 0.5 + 0.5;\n                squish.targetScaleY = 0.9 + walkCycle * 0.2;\n                squish.targetScaleXZ = 1.1 - walkCycle * 0.2;\n              } else {\n                if (c.queuePosition === 0) {\n                  c.state = 'ordering';\n                } else {\n                  c.state = 'waiting';\n                }\n                mesh.rotation.y = THREE.MathUtils.degToRad(config.rotation || 0) + Math.PI;\n                squish.scaleY = 0.8;\n                squish.scaleXZ = 1.2;\n              }\n            } else if (c.state === 'waiting') {\n              switchAnimation(mesh, 'idle');\n              const breathe = Math.sin(now * 0.004 + i * 0.5);\n              squish.targetScaleY = 1 + breathe * 0.04;\n              squish.targetScaleXZ = 1 - breathe * 0.03;\n            } else if (c.state === 'ordering') {\n              switchAnimation(mesh, 'interact');\n              c.orderTime += deltaTime;\n              const interactCycle = Math.sin(now * 0.02);\n              squish.targetScaleY = 1 + interactCycle * 0.05;\n              squish.targetScaleXZ = 1 - interactCycle * 0.05;\n\n              if (c.orderTime >= SELF_ORDER_ORDER_TIME) {\n                createSmokeParticlesAt(config.x, 2, config.z);\n\n                const cashierConfig = window.gameConfig.cashierStall;\n                if (cashierConfig && !cashierConfig.deleted) {\n                  c.state = 'walking';\n                  const queuePosition = customerQueue.filter(\n                    (cq) => cq.state === 'waiting' || cq.state === 'walking'\n                  ).length;\n                  const target = getCustomerQueueTarget(cashierConfig, queuePosition);\n                  c.targetX = target.x;\n                  c.targetZ = target.z;\n                  c.queuePosition = queuePosition;\n                  updateCustomerEmoji(c, c.isVIP ? '\u2b50' : '\ud83d\ude42');\n                  customerQueue.push(kiosk.queue.splice(i, 1)[0]);\n                } else {\n                  const tipMult =\n                    (c.isVIP ? window.gameConfig.vipCustomers?.tipMultiplier || 3 : 1) *\n                    getRushHourTipMultiplier() *\n                    getPrestigeProfitMultiplier();\n                  const itemVal = window.gameConfig.resources.itemValue || 100;\n                  modifyVaultBalance(itemVal * tipMult);\n                  playSound('sell_sound');\n\n                  c.state = 'leaving';\n                  c.targetX = -RESTAURANT_WIDTH / 2 - 5;\n                  c.targetZ = 15;\n                  updateCustomerEmoji(c, c.isVIP ? '\ud83e\udd29' : '\ud83d\ude04');\n                  kiosk.leavers.push(kiosk.queue.splice(i, 1)[0]);\n                }\n\n                for (let j = 0; j < kiosk.queue.length; j++) {\n                  const qc = kiosk.queue[j];\n                  qc.queuePosition--;\n                  const rotRad2 = THREE.MathUtils.degToRad(config.rotation || 0);\n                  qc.targetX = config.x + Math.sin(rotRad2) * (2 + qc.queuePosition * SELF_ORDER_SPACING);\n                  qc.targetZ = config.z + Math.cos(rotRad2) * (2 + qc.queuePosition * SELF_ORDER_SPACING);\n                  qc.state = 'walking';\n                }\n              }\n            }\n\n            const sSpeed = 10 * deltaTime;\n            squish.scaleY += (squish.targetScaleY - squish.scaleY) * sSpeed;\n            squish.scaleXZ += (squish.targetScaleXZ - squish.scaleXZ) * sSpeed;\n            mesh.scale.set(squish.scaleXZ, squish.scaleY, squish.scaleXZ);\n          }\n\n          // Process Leavers\n          for (let i = kiosk.leavers.length - 1; i >= 0; i--) {\n            const c = kiosk.leavers[i];\n            const mesh = c.mesh;\n            const squish = mesh.userData.squishState;\n\n            if (mesh.userData.emojiMesh) {\n              updateCustomerEmojiVisibility(c, now);\n              mesh.userData.emojiMesh.lookAt(cameraPos);\n            }\n\n            switchAnimation(mesh, 'run');\n            const dx = c.targetX - mesh.position.x;\n            const dz = c.targetZ - mesh.position.z;\n            const distSq = dx * dx + dz * dz;\n\n            if (distSq > 1) {\n              const dist = Math.sqrt(distSq);\n              mesh.position.x += (dx / dist) * 6 * deltaTime;\n              mesh.position.z += (dz / dist) * 6 * deltaTime;\n              mesh.rotation.y = Math.atan2(dx, dz);\n\n              const walkCycle = Math.sin(now * 0.012 + i) * 0.5 + 0.5;\n              squish.targetScaleY = 0.9 + walkCycle * 0.2;\n              squish.targetScaleXZ = 1.1 - walkCycle * 0.2;\n\n              const sSpeed = 10 * deltaTime;\n              squish.scaleY += (squish.targetScaleY - squish.scaleY) * sSpeed;\n              squish.scaleXZ += (squish.targetScaleXZ - squish.scaleXZ) * sSpeed;\n              mesh.scale.set(squish.scaleXZ, squish.scaleY, squish.scaleXZ);\n            } else {\n              scene.remove(mesh);\n              mesh.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                  else child.material.dispose();\n                }\n              });\n              kiosk.leavers.splice(i, 1);\n            }\n          }\n\n          // Animate icon\n          if (kiosk.mesh && kiosk.mesh.userData.icon) {\n            kiosk.mesh.userData.icon.position.y = 3 + Math.sin(now * 0.003) * 0.2;\n            kiosk.mesh.userData.icon.lookAt(cameraPos);\n          }\n        });\n      }\n      // ============================================\n      // GENERIC TILE CREATION HELPERS\n      // ============================================\n      class RoundedBoxGeometry extends THREE.BufferGeometry {\n        constructor(width = 1, height = 1, depth = 1, segments = 4, radius = 0.1) {\n          super();\n\n          segments = Math.max(1, Math.floor(segments));\n          radius = Math.min(radius, Math.min(width, height, depth) / 2);\n\n          const shape = new THREE.Shape();\n          const eps = 0.00001;\n          const r = radius - eps;\n\n          // rounded rectangle shape\n          shape.absarc(eps, eps, eps, -Math.PI / 2, -Math.PI, true);\n          shape.absarc(eps, height - r * 2, eps, Math.PI, Math.PI / 2, true);\n          shape.absarc(width - r * 2, height - r * 2, eps, Math.PI / 2, 0, true);\n          shape.absarc(width - r * 2, eps, eps, 0, -Math.PI / 2, true);\n\n          const extrudeSettings = {\n            depth: depth - radius * 2,\n            bevelEnabled: true,\n            bevelSegments: segments,\n            steps: 1,\n            bevelSize: radius,\n            bevelThickness: radius,\n            curveSegments: segments,\n          };\n\n          const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);\n          geometry.center();\n\n          this.copy(geometry);\n        }\n      }\n\n      function createBaseTile(\n        config = {\n          x: 0,\n          y: 0,\n          z: 0,\n          cost: 5,\n          width: 4,\n          depth: 4,\n        }\n      ) {\n        // console.log(config);\n        const tileMesh = new THREE.Group();\n        tileMesh.position.set(config.x, 0, config.z);\n        const color = 0xfdbf78;\n        const sizeW = 4; //config.width || 4;\n        const sizeD = 4; //config.depth || 4;\n\n        // --- WHITE OUTLINE (bottom layer) ---\n        const outline = new THREE.Mesh(\n          new RoundedBoxGeometry(sizeW + 0.3, 0.2, sizeD + 0.3, 3, 0.32),\n          new THREE.MeshBasicMaterial({\n            color: 0xffffff,\n          })\n        );\n\n        outline.position.set(0, 0.02, 0);\n        tileMesh.add(outline);\n\n        // --- MAIN TILE ---\n        const tile = new THREE.Mesh(\n          new RoundedBoxGeometry(sizeW, 0.22, sizeD, 3, 0.32),\n          new THREE.MeshLambertMaterial({\n            color: color,\n          })\n        );\n        tile.position.set(0, 0.02, 0);\n        tileMesh.add(tile);\n\n        // --- PROGRESS LAYER (green fill) ---\n        const progressMesh = new THREE.Mesh(\n          new RoundedBoxGeometry(sizeW, 0.22, sizeD, 3, 0.32),\n          new THREE.MeshLambertMaterial({\n            color: 0x2ecc71,\n            // opacity:0, transparent: true\n          })\n        );\n        const progress = 0;\n        progressMesh.position.set(0, 0.029, 0);\n\n        // store for updates\n        tileMesh.userData.progressMesh = progressMesh;\n        tileMesh.userData.size = sizeW;\n        tileMesh.userData.baseZ = 0;\n\n        tileMesh.add(progressMesh);\n\n        // --- NUMBER TEXT ---\n        const canvas = document.createElement('canvas');\n        canvas.width = 256;\n        canvas.height = 256;\n        const ctx = canvas.getContext('2d');\n\n        ctx.fillStyle = 'white';\n        ctx.font = 'bold 140px Arial';\n        ctx.textAlign = 'center';\n        ctx.textBaseline = 'middle';\n        ctx.fillText(config.cost.toString(), 128, 128);\n\n        const textTexture = new THREE.CanvasTexture(canvas);\n\n        const textMesh = new THREE.Mesh(\n          new THREE.PlaneGeometry(sizeW * 0.6, sizeD * 0.6),\n          new THREE.MeshBasicMaterial({\n            map: textTexture,\n            transparent: true,\n          })\n        );\n\n        textMesh.position.set(-0.003, 0.15, -0.107);\n        textMesh.rotation.x = -Math.PI / 2;\n\n        tileMesh.userData.costCtx = ctx;\n        tileMesh.userData.costCanvas = canvas;\n        tileMesh.userData.costTexture = textTexture;\n        tileMesh.add(textMesh);\n\n        // --- MONEY ICON (simple green rectangle) ---\n        const iconCanvas = document.createElement('canvas');\n        iconCanvas.width = 128;\n        iconCanvas.height = 128;\n        const ictx = iconCanvas.getContext('2d');\n\n        ictx.fillStyle = '#2ecc71';\n        ictx.fillRect(20, 40, 88, 48);\n\n        ictx.fillStyle = '#27ae60';\n        ictx.fillRect(30, 50, 68, 28);\n\n        const iconTexture = new THREE.CanvasTexture(iconCanvas);\n\n        const iconMesh = new THREE.Mesh(\n          new THREE.PlaneGeometry(sizeW * 0.35, sizeW * 0.35),\n          new THREE.MeshBasicMaterial({\n            map: iconTexture,\n            transparent: true,\n          })\n        );\n\n        iconMesh.position.set(0.023, 0.16, 0.602 * 2);\n        iconMesh.rotation.x = -Math.PI / 2;\n        tileMesh.add(iconMesh);\n\n        tileMesh.rotation.y = 0;\n        scene.add(tileMesh);\n\n        tileMesh.userData.targetProgress = progress;\n        lerpTileProgress(tileMesh);\n\n        return {\n          tileMesh,\n          glowMesh: outline,\n        };\n      }\n\n      /**\n       * Creates the cost display with progress bar for unlockable tiles\n       * Used by: grill, fryer, drink station, cashier, trash, fountain, drive-thru, vault\n       */\n      function createTileCostDisplay(tileMesh, config) {}\n\n      /**\n       * Updates the progress bar target for lerping\n       */\n      function updateTileProgress(\n        tileGroup,\n        config,\n        depositedField = 'moneyDeposited',\n        costField = 'cost'\n      ) {\n        if (!tileGroup || !tileGroup.userData) return;\n        const progressMesh = tileGroup.userData.progressMesh;\n        if (!progressMesh) return;\n\n        let progress = Math.min(config[depositedField] / config[costField], 1);\n        progress = Math.max(0, progress);\n\n        tileGroup.userData.targetProgress = progress;\n      }\n\n      /**\n       * lerps the progress bar to its target (call each frame)\n       */\n      function lerpTileProgress(tileGroup) {\n        if (!tileGroup || !tileGroup.userData) return;\n        const progressMesh = tileGroup.userData.progressMesh;\n        if (!progressMesh) return;\n\n        const current = tileGroup.userData.currentProgress || 0;\n        const target = tileGroup.userData.targetProgress || 0;\n        if (Math.abs(target - current) < 0.001) {\n          tileGroup.userData.currentProgress = target;\n          applyProgressMesh(tileGroup, target);\n          return;\n        }\n\n        const newProgress = lerp(current, target, 0.15);\n        tileGroup.userData.currentProgress = newProgress;\n        applyProgressMesh(tileGroup, newProgress);\n      }\n\n      /**\n       * Applies progress value to mesh scale/position\n       */\n      function applyProgressMesh(tileGroup, progress) {\n        if (!tileGroup || !tileGroup.userData) return;\n        const progressMesh = tileGroup.userData.progressMesh;\n        if (!progressMesh) return;\n\n        const fullDepth = tileGroup.userData.size;\n        const baseZ = tileGroup.userData.baseZ;\n\n        progressMesh.scale.z = progress;\n        progressMesh.position.z = baseZ + fullDepth / 2 - (fullDepth * progress) / 2;\n      }\n\n      /**\n       * Resets progress for a tile (e.g. on unlock)\n       */\n      function resetTileProgress(tileGroup) {\n        if (!tileGroup || !tileGroup.userData) return;\n        tileGroup.userData.currentProgress = 0;\n        tileGroup.userData.targetProgress = 0;\n        applyProgressMesh(tileGroup, 0);\n      }\n\n      /**\n       * lerps all tile progress bars (call each frame in game loop)\n       */\n      function lerpTileProgressBars() {\n        const tiles = [\n          fountainTileMesh,\n          fountain2TileMesh,\n          selfOrderKioskTileMesh,\n          driveThruTileMesh,\n          grillTileMesh,\n          cashierTileMesh,\n          trashTileMesh,\n          runnerStaffTileMesh,\n          staffHireTileMesh,\n          cleanerStaffTileMesh,\n        ];\n        for (const tileGroup of tiles) {\n          if (tileGroup && tileGroup.userData && tileGroup.userData.progressMesh) {\n            lerpTileProgress(tileGroup);\n          }\n        }\n\n        diningTableTileMeshes.forEach((e) => lerpTileProgress(e.mesh));\n      }\n\n      /**\n       * Generic cost display update for unlock tiles\n       */\n      function updateUnlockTileCostDisplay(tileGroup, config) {\n        const costCtx = tileGroup.userData.costCtx;\n        const costCanvas = tileGroup.userData.costCanvas;\n        const costTexture = tileGroup.userData.costTexture;\n        if (!costCtx || !costCanvas) return;\n\n        const remainingCost = Math.max(0, config.cost - config.moneyDeposited);\n\n        // console.log({remainingCost});\n\n        costCtx.clearRect(0, 0, 256, 256);\n        costCtx.fillStyle = 'white';\n        costCtx.font = 'bold 140px Arial';\n        costCtx.textAlign = 'center';\n        costCtx.textBaseline = 'middle';\n        costCtx.fillText(formatCurrencyAbbrev(remainingCost), 128, 128);\n        costTexture.needsUpdate = true;\n      }\n\n      /**\n       * Removes all tile visuals (platform, glow, cost display, progress bar)\n       */\n      function destroyTileVisuals(tileMesh, glowMesh) {\n        if (!tileMesh) return;\n\n        scene.remove(tileMesh);\n        if (glowMesh) scene.remove(glowMesh);\n\n        const keys = [\n          'borderMesh',\n          'icon',\n          'progressBg',\n          'progressMesh',\n          'progressFill',\n          'costPlane',\n          'costCanvas',\n          'costCtx',\n          'costTexture',\n        ];\n        keys.forEach((key) => {\n          if (tileMesh.userData[key]) {\n            if (tileMesh.userData[key].geometry) tileMesh.userData[key].geometry.dispose();\n            if (tileMesh.userData[key].material) tileMesh.userData[key].material.dispose();\n            scene.remove(tileMesh.userData[key]);\n          }\n        });\n\n        if (tileMesh.geometry) tileMesh.geometry.dispose();\n        if (tileMesh.material) tileMesh.material.dispose();\n      }\n\n      /**\n       * Check if player is within a tile's zone area\n       */\n      function isPlayerInZone(config, paddingX = 0, paddingZ = 0) {\n        return (\n          Math.abs(player.position.x - config.x) < config.width / 2 + paddingX &&\n          Math.abs(player.position.z - config.z) < config.depth / 2 + paddingZ\n        );\n      }\n\n      // ============================================\n      // LOADING SCREEN\n      // ============================================\n\n      function updateLoadingProgress(step, textKeyOrText) {\n        completedLoadSteps++;\n        const progress = Math.min((completedLoadSteps / totalLoadSteps) * 100, 100);\n\n        const loadingBar = document.getElementById('loading-bar');\n        const loadingEmoji = document.getElementById('loading-emoji');\n        const loadingText = document.getElementById('loading-text');\n\n        if (loadingBar) {\n          loadingBar.style.width = progress + '%';\n        }\n        if (loadingEmoji) {\n          loadingEmoji.style.left = progress + '%';\n        }\n        if (loadingText && textKeyOrText) {\n          const translated = i18nReady ? t(textKeyOrText) : textKeyOrText;\n          loadingText.textContent = translated;\n        }\n      }\n\n      function hideLoadingScreen() {\n        const loadingScreen = document.getElementById('loading-screen');\n        if (loadingScreen) {\n          loadingScreen.classList.add('hidden');\n          // Remove from DOM after animation\n          setTimeout(() => {\n            loadingScreen.style.display = 'none';\n          }, 500);\n        }\n      }\n\n      // ============================================\n      // INITIALIZATION\n      // ============================================\n\n      function initThreeJS() {\n        const canvas = document.getElementById('game-canvas');\n\n        const zoomToggle = document.getElementById('zoom-toggle');\n        if (zoomToggle) {\n          zoomToggle.addEventListener('click', () => {\n            isZoomedOut = !isZoomedOut;\n          });\n        }\n\n        const camLeftBtn = document.getElementById('cam-left-btn');\n        if (camLeftBtn) {\n          camLeftBtn.addEventListener('click', () => {\n            targetCameraAngle -= Math.PI / 2;\n          });\n        }\n\n        const camRightBtn = document.getElementById('cam-right-btn');\n        if (camRightBtn) {\n          camRightBtn.addEventListener('click', () => {\n            targetCameraAngle += Math.PI / 2;\n          });\n        }\n\n        // Create scene\n        scene = new THREE.Scene();\n        scene.background = new THREE.Color(window.gameConfig.settings.skyColor);\n\n        // Create camera (third-person perspective)\n        camera = new THREE.PerspectiveCamera(60, 720 / 1280, 0.1, 1000);\n        camera.position.set(0, 25, 25);\n        camera.lookAt(0, 0, 0);\n\n        // Create renderer\n        renderer = new THREE.WebGLRenderer({\n          canvas,\n          antialias: false,\n          powerPreference: performanceSettings.enableShadows ? 'high-performance' : 'low-power',\n        });\n        // Disabled antialias for mobile perf\n        renderer.setSize(720, 1280);\n        const pixelRatio = Math.min(window.devicePixelRatio || 1, performanceSettings.pixelRatioCap);\n        renderer.setPixelRatio(pixelRatio);\n        // Capped ratio for performance\n        renderer.shadowMap.enabled = performanceSettings.enableShadows;\n        if (performanceSettings.enableShadows) {\n          renderer.shadowMap.type = THREE.PCFShadowMap;\n        }\n        // Changed to PCF for perf\n\n        // Lighting\n        const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);\n        scene.add(ambientLight);\n\n        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);\n        directionalLight.position.set(10, 20, 10);\n        directionalLight.castShadow = performanceSettings.enableShadows;\n        if (performanceSettings.enableShadows) {\n          directionalLight.shadow.mapSize.width = performanceSettings.shadowMapSize;\n          // Reduced shadow resolution\n          directionalLight.shadow.mapSize.height = performanceSettings.shadowMapSize;\n          directionalLight.shadow.camera.near = 0.5;\n          directionalLight.shadow.camera.far = 50;\n          directionalLight.shadow.camera.left = -30;\n          directionalLight.shadow.camera.right = 30;\n          directionalLight.shadow.camera.top = 30;\n          directionalLight.shadow.camera.bottom = -30;\n        }\n        scene.add(directionalLight);\n\n        createTutorialArrow();\n      }\n\n      // Restaurant dimensions\n      const RESTAURANT_WIDTH = 40;\n      // X axis\n      const RESTAURANT_DEPTH = 60;\n      // Z axis\n\n      function createGround() {\n        const halfWidth = RESTAURANT_WIDTH / 2;\n        const halfDepth = RESTAURANT_DEPTH / 2;\n        const kitchenDividerZ = -10;\n        // Kitchen starts at z = -10\n        const kitchenDepth = halfDepth + kitchenDividerZ;\n        // Kitchen area depth\n        const diningDepth = halfDepth - kitchenDividerZ;\n        // Dining area depth\n\n        // Load floor textures from config\n        const floorTextureLoader = new THREE.TextureLoader();\n        const diningFloorAssetId = window.gameConfig.settings.diningFloorAsset || 'floor_tile';\n        const kitchenFloorAssetId =\n          window.gameConfig.settings.kitchenFloorAsset || 'kitchen_floor_tile';\n        const floorAsset = lib.getAsset(diningFloorAssetId);\n        const kitchenFloorAsset = lib.getAsset(kitchenFloorAssetId);\n\n        // Dining floor material\n        let diningFloorMaterial;\n        if (floorAsset && floorAsset.url) {\n          const floorTexture = floorTextureLoader.load(floorAsset.url);\n          floorTexture.wrapS = THREE.RepeatWrapping;\n          floorTexture.wrapT = THREE.RepeatWrapping;\n          floorTexture.repeat.set(RESTAURANT_WIDTH / 8, diningDepth / 8);\n          floorTexture.minFilter = THREE.LinearMipmapLinearFilter;\n          floorTexture.magFilter = THREE.LinearFilter;\n\n          diningFloorMaterial = new THREE.MeshStandardMaterial({\n            map: floorTexture,\n            roughness: 0.7,\n            metalness: 0.1,\n          });\n        } else {\n          diningFloorMaterial = new THREE.MeshStandardMaterial({\n            color: window.gameConfig.settings.groundColor,\n            roughness: 0.8,\n            metalness: 0.1,\n          });\n        }\n\n        // Kitchen floor material\n        let kitchenFloorMaterial;\n        if (kitchenFloorAsset && kitchenFloorAsset.url) {\n          const kitchenTexture = floorTextureLoader.load(kitchenFloorAsset.url);\n          kitchenTexture.wrapS = THREE.RepeatWrapping;\n          kitchenTexture.wrapT = THREE.RepeatWrapping;\n          kitchenTexture.repeat.set(RESTAURANT_WIDTH / 6, kitchenDepth / 6);\n          kitchenTexture.minFilter = THREE.LinearMipmapLinearFilter;\n          kitchenTexture.magFilter = THREE.LinearFilter;\n\n          kitchenFloorMaterial = new THREE.MeshStandardMaterial({\n            map: kitchenTexture,\n            roughness: 0.5,\n            metalness: 0.2,\n          });\n        } else {\n          // Fallback to gray kitchen floor\n          kitchenFloorMaterial = new THREE.MeshStandardMaterial({\n            color: 0x808080,\n            roughness: 0.6,\n            metalness: 0.2,\n          });\n        }\n\n        // Dining floor (front section) - extends from kitchen divider to front wall\n        const fullDiningDepth = halfDepth - kitchenDividerZ;\n        // Full distance from divider to front\n        const diningFloorGeometry = new THREE.PlaneGeometry(RESTAURANT_WIDTH, fullDiningDepth);\n        const diningFloor = new THREE.Mesh(diningFloorGeometry, diningFloorMaterial);\n        diningFloor.rotation.x = -Math.PI / 2;\n        diningFloor.position.z = kitchenDividerZ + fullDiningDepth / 2;\n        diningFloor.receiveShadow = true;\n        scene.add(diningFloor);\n\n        // Kitchen floor (back section)\n        const kitchenFloorGeometry = new THREE.PlaneGeometry(RESTAURANT_WIDTH, kitchenDepth);\n        const kitchenFloor = new THREE.Mesh(kitchenFloorGeometry, kitchenFloorMaterial);\n        kitchenFloor.rotation.x = -Math.PI / 2;\n        kitchenFloor.position.z = -halfDepth + kitchenDepth / 2;\n        kitchenFloor.receiveShadow = true;\n        scene.add(kitchenFloor);\n\n        // Store reference (use dining floor as main)\n        groundMesh = diningFloor;\n\n        // Load wall texture\n        const wallTextureLoader = new THREE.TextureLoader();\n        const wallAsset = lib.getAsset('wall_tile');\n\n        let wallMaterial;\n        const wallHeight = 3;\n\n        if (wallAsset && wallAsset.url) {\n          // Create wall materials with different repeat values for different wall sizes\n          const createWallMaterial = (repeatX, repeatY) => {\n            const wallTexture = wallTextureLoader.load(wallAsset.url);\n            wallTexture.wrapS = THREE.RepeatWrapping;\n            wallTexture.wrapT = THREE.RepeatWrapping;\n            wallTexture.repeat.set(repeatX, repeatY);\n            wallTexture.minFilter = THREE.LinearMipmapLinearFilter;\n            wallTexture.magFilter = THREE.LinearFilter;\n\n            return new THREE.MeshStandardMaterial({\n              map: wallTexture,\n              roughness: 0.7,\n              metalness: 0.1,\n            });\n          };\n          // Back wall (kitchen side)\n          const backWallGeometry = new THREE.BoxGeometry(RESTAURANT_WIDTH, wallHeight, 0.5);\n          const backWallMaterial = createWallMaterial(RESTAURANT_WIDTH / 4, wallHeight / 2);\n          const backWall = new THREE.Mesh(backWallGeometry, backWallMaterial);\n          backWall.position.set(0, wallHeight / 2, -halfDepth);\n          backWall.castShadow = true;\n          backWall.receiveShadow = true;\n          scene.add(backWall);\n\n          // Front wall\n          const frontWallGeometry = new THREE.BoxGeometry(RESTAURANT_WIDTH, wallHeight, 0.5);\n          const frontWallMaterial = createWallMaterial(RESTAURANT_WIDTH / 4, wallHeight / 2);\n          const frontWall = new THREE.Mesh(frontWallGeometry, frontWallMaterial);\n          frontWall.position.set(0, wallHeight / 2, halfDepth);\n          frontWall.castShadow = true;\n          scene.add(frontWall);\n\n          // Left wall - split into two parts with entrance door\n          const entranceOpeningCenter = 10;\n          // Center of entrance (toward front of restaurant)\n          const entranceOpeningWidth = 6;\n          // Width of door opening\n\n          // Left wall - back section (from back to entrance)\n          const leftWallBackLength = halfDepth - entranceOpeningWidth / 2 + entranceOpeningCenter;\n          const leftWallBackGeometry = new THREE.BoxGeometry(0.5, wallHeight, leftWallBackLength);\n          const leftWallBackMat = createWallMaterial(leftWallBackLength / 4, wallHeight / 2);\n          const leftWallBack = new THREE.Mesh(leftWallBackGeometry, leftWallBackMat);\n          leftWallBack.position.set(\n            -halfWidth,\n            wallHeight / 2,\n            -halfDepth + leftWallBackLength / 2\n          );\n          leftWallBack.castShadow = true;\n          scene.add(leftWallBack);\n\n          // Left wall - front section (from entrance to front)\n          const leftWallFrontLength = halfDepth - entranceOpeningWidth / 2 - entranceOpeningCenter;\n          const leftWallFrontGeometry = new THREE.BoxGeometry(0.5, wallHeight, leftWallFrontLength);\n          const leftWallFrontMat = createWallMaterial(leftWallFrontLength / 4, wallHeight / 2);\n          const leftWallFront = new THREE.Mesh(leftWallFrontGeometry, leftWallFrontMat);\n          leftWallFront.position.set(\n            -halfWidth,\n            wallHeight / 2,\n            halfDepth - leftWallFrontLength / 2\n          );\n          leftWallFront.castShadow = true;\n          scene.add(leftWallFront);\n\n          // Create entrance door frame and decorations\n          createEntranceDoor(-halfWidth, entranceOpeningCenter, wallHeight, createWallMaterial);\n\n          // Right wall - split into two parts with opening for drive-thru\n          const driveThruOpeningCenter = 0;\n          // Center of the opening\n          const driveThruOpeningWidth = 12;\n          // Width of opening for drive-thru access\n          const rightWallMaterial = createWallMaterial(RESTAURANT_DEPTH / 4, wallHeight / 2);\n\n          // Right wall - front section (from front to opening)\n          const rightWallFrontLength =\n            halfDepth - driveThruOpeningWidth / 2 - driveThruOpeningCenter;\n          const rightWallFrontGeometry = new THREE.BoxGeometry(\n            0.5,\n            wallHeight,\n            rightWallFrontLength\n          );\n          const rightWallFrontMat = createWallMaterial(rightWallFrontLength / 4, wallHeight / 2);\n          const rightWallFront = new THREE.Mesh(rightWallFrontGeometry, rightWallFrontMat);\n          rightWallFront.position.set(\n            halfWidth,\n            wallHeight / 2,\n            halfDepth - rightWallFrontLength / 2\n          );\n          rightWallFront.castShadow = true;\n          scene.add(rightWallFront);\n\n          // Right wall - back section (from opening to back)\n          const rightWallBackLength =\n            halfDepth - driveThruOpeningWidth / 2 + driveThruOpeningCenter;\n          const rightWallBackGeometry = new THREE.BoxGeometry(0.5, wallHeight, rightWallBackLength);\n          const rightWallBackMat = createWallMaterial(rightWallBackLength / 4, wallHeight / 2);\n          const rightWallBack = new THREE.Mesh(rightWallBackGeometry, rightWallBackMat);\n          rightWallBack.position.set(\n            halfWidth,\n            wallHeight / 2,\n            -halfDepth + rightWallBackLength / 2\n          );\n          rightWallBack.castShadow = true;\n          scene.add(rightWallBack);\n\n          // Create outdoor drive-thru area (asphalt parking lot)\n          const outdoorWidth = 20;\n          const outdoorGeometry = new THREE.PlaneGeometry(outdoorWidth, RESTAURANT_DEPTH);\n          const outdoorFloorColor = window.gameConfig.settings.outdoorFloorColor || '#444444';\n          const outdoorMaterial = new THREE.MeshStandardMaterial({\n            color: outdoorFloorColor,\n            roughness: 0.9,\n            metalness: 0.1,\n          });\n          const outdoorFloor = new THREE.Mesh(outdoorGeometry, outdoorMaterial);\n          outdoorFloor.rotation.x = -Math.PI / 2;\n          outdoorFloor.position.set(halfWidth + outdoorWidth / 2, -0.01, 0);\n          outdoorFloor.receiveShadow = true;\n          scene.add(outdoorFloor);\n        } else {\n          // Fallback to plain color walls\n          wallMaterial = new THREE.MeshStandardMaterial({\n            color: 0x8b4513,\n            roughness: 0.6,\n          });\n\n          // Back wall (kitchen side)\n          const backWallGeometry = new THREE.BoxGeometry(RESTAURANT_WIDTH, wallHeight, 0.5);\n          const backWall = new THREE.Mesh(backWallGeometry, wallMaterial);\n          backWall.position.set(0, wallHeight / 2, -halfDepth);\n          backWall.castShadow = true;\n          backWall.receiveShadow = true;\n          scene.add(backWall);\n\n          // Front wall\n          const frontWallGeometry = new THREE.BoxGeometry(RESTAURANT_WIDTH, wallHeight, 0.5);\n          const frontWall = new THREE.Mesh(frontWallGeometry, wallMaterial);\n          frontWall.position.set(0, wallHeight / 2, halfDepth);\n          frontWall.castShadow = true;\n          scene.add(frontWall);\n\n          // Left wall - split into two parts with entrance door\n          const entranceOpeningCenter = 10;\n          const entranceOpeningWidth = 6;\n\n          // Left wall - back section\n          const leftWallBackLength = halfDepth - entranceOpeningWidth / 2 + entranceOpeningCenter;\n          const leftWallBackGeometry = new THREE.BoxGeometry(0.5, wallHeight, leftWallBackLength);\n          const leftWallBack = new THREE.Mesh(leftWallBackGeometry, wallMaterial);\n          leftWallBack.position.set(\n            -halfWidth,\n            wallHeight / 2,\n            -halfDepth + leftWallBackLength / 2\n          );\n          leftWallBack.castShadow = true;\n          scene.add(leftWallBack);\n\n          // Left wall - front section\n          const leftWallFrontLength = halfDepth - entranceOpeningWidth / 2 - entranceOpeningCenter;\n          const leftWallFrontGeometry = new THREE.BoxGeometry(0.5, wallHeight, leftWallFrontLength);\n          const leftWallFront = new THREE.Mesh(leftWallFrontGeometry, wallMaterial);\n          leftWallFront.position.set(\n            -halfWidth,\n            wallHeight / 2,\n            halfDepth - leftWallFrontLength / 2\n          );\n          leftWallFront.castShadow = true;\n          scene.add(leftWallFront);\n\n          // Create entrance door frame\n          createEntranceDoor(-halfWidth, entranceOpeningCenter, wallHeight, null);\n\n          // Right wall - split into two parts with opening for drive-thru\n          const driveThruOpeningCenter = 0;\n          const driveThruOpeningWidth = 12;\n\n          // Right wall - front section\n          const rightWallFrontLength =\n            halfDepth - driveThruOpeningWidth / 2 - driveThruOpeningCenter;\n          const rightWallFrontGeometry = new THREE.BoxGeometry(\n            0.5,\n            wallHeight,\n            rightWallFrontLength\n          );\n          const rightWallFront = new THREE.Mesh(rightWallFrontGeometry, wallMaterial);\n          rightWallFront.position.set(\n            halfWidth,\n            wallHeight / 2,\n            halfDepth - rightWallFrontLength / 2\n          );\n          rightWallFront.castShadow = true;\n          scene.add(rightWallFront);\n\n          // Right wall - back section\n          const rightWallBackLength =\n            halfDepth - driveThruOpeningWidth / 2 + driveThruOpeningCenter;\n          const rightWallBackGeometry = new THREE.BoxGeometry(0.5, wallHeight, rightWallBackLength);\n          const rightWallBack = new THREE.Mesh(rightWallBackGeometry, wallMaterial);\n          rightWallBack.position.set(\n            halfWidth,\n            wallHeight / 2,\n            -halfDepth + rightWallBackLength / 2\n          );\n          rightWallBack.castShadow = true;\n          scene.add(rightWallBack);\n\n          // Create outdoor drive-thru area (asphalt)\n          const outdoorWidth = 20;\n          const outdoorGeometry = new THREE.PlaneGeometry(outdoorWidth, RESTAURANT_DEPTH);\n          const outdoorFloorColor = window.gameConfig.settings.outdoorFloorColor || '#444444';\n          const outdoorMaterial = new THREE.MeshStandardMaterial({\n            color: outdoorFloorColor,\n            roughness: 0.9,\n            metalness: 0.1,\n          });\n          const outdoorFloor = new THREE.Mesh(outdoorGeometry, outdoorMaterial);\n          outdoorFloor.rotation.x = -Math.PI / 2;\n          outdoorFloor.position.set(halfWidth + outdoorWidth / 2, -0.01, 0);\n          outdoorFloor.receiveShadow = true;\n          scene.add(outdoorFloor);\n        }\n\n        // Kitchen divider line (visual separation)\n        const dividerGeometry = new THREE.BoxGeometry(RESTAURANT_WIDTH - 4, 0.05, 0.3);\n        const dividerMaterial = new THREE.MeshStandardMaterial({\n          color: 0x666666,\n          roughness: 0.3,\n        });\n        const divider = new THREE.Mesh(dividerGeometry, dividerMaterial);\n        divider.position.set(0, 0.03, -10);\n        scene.add(divider);\n\n        // Create bustling city background\n        createCityBackground(halfWidth, halfDepth);\n\n        // Add indoor potted plants\n        createPottedPlants();\n      }\n\n      function createPottedPlants() {\n        const createPlant = (x, z) => {\n          const plantGroup = new THREE.Group();\n\n          // Pot\n          const potGeometry = new THREE.CylinderGeometry(0.6, 0.4, 0.8, 12);\n          const potMaterial = new THREE.MeshStandardMaterial({\n            color: 0x8b4513,\n            // Terracotta\n            roughness: 0.8,\n          });\n          const pot = new THREE.Mesh(potGeometry, potMaterial);\n          pot.position.y = 0.4;\n          pot.castShadow = true;\n          plantGroup.add(pot);\n\n          // Dirt\n          const dirtGeometry = new THREE.CylinderGeometry(0.55, 0.55, 0.1, 12);\n          const dirtMaterial = new THREE.MeshStandardMaterial({\n            color: 0x3e2723,\n            // Dark brown\n            roughness: 0.9,\n          });\n          const dirt = new THREE.Mesh(dirtGeometry, dirtMaterial);\n          dirt.position.y = 0.761;\n          plantGroup.add(dirt);\n\n          // Plant stem\n          const stemGeometry = new THREE.CylinderGeometry(0.05, 0.05, 1, 8);\n          const stemMaterial = new THREE.MeshStandardMaterial({\n            color: 0x2e7d32,\n            // Green\n            roughness: 0.7,\n          });\n          const stem = new THREE.Mesh(stemGeometry, stemMaterial);\n          stem.position.y = 1.3;\n          plantGroup.add(stem);\n\n          // Leaves (using a few spheres to make a bush shape)\n          const leavesMaterial = new THREE.MeshStandardMaterial({\n            color: 0x228b22,\n            // Forest green\n            roughness: 0.8,\n          });\n\n          const bush1 = new THREE.Mesh(new THREE.SphereGeometry(0.7, 12, 12), leavesMaterial);\n          bush1.position.set(0, 1.8, 0);\n          bush1.castShadow = true;\n          plantGroup.add(bush1);\n\n          const bush2 = new THREE.Mesh(new THREE.SphereGeometry(0.5, 10, 10), leavesMaterial);\n          bush2.position.set(0.3, 1.5, 0.3);\n          bush2.castShadow = true;\n          plantGroup.add(bush2);\n\n          const bush3 = new THREE.Mesh(new THREE.SphereGeometry(0.5, 10, 10), leavesMaterial);\n          bush3.position.set(-0.3, 1.6, -0.2);\n          bush3.castShadow = true;\n          plantGroup.add(bush3);\n\n          plantGroup.position.set(x, 0, z);\n          scene.add(plantGroup);\n        };\n        // Front corners\n        createPlant(22, 32);\n        createPlant(-22, 32);\n\n        // Near kitchen divider\n        createPlant(22, -8);\n        createPlant(-22, -8);\n\n        // Around the dining area\n        createPlant(-10, 12);\n        createPlant(10, 12);\n      }\n\n      function createCityBackground(halfWidth, halfDepth) {\n        const cityGroup = new THREE.Group();\n        cityGroup.userData.windowLights = [];\n        // Store window references for animation\n\n        // Building colors (various city building shades)\n        const buildingColors = [\n          0x3d4852, // Dark slate\n          0x4a5568, // Gray\n          0x2d3748, // Dark blue-gray\n          0x1a202c, // Very dark\n          0x553c9a, // Purple tint\n          0x2b4c7e, // Blue tint\n          0x434190, // Indigo\n          0x2c5282, // Blue\n          0x285e61, // Teal\n        ];\n\n        // Window light colors\n        const windowColors = [0xffe066, 0xfff3bf, 0xffffff, 0xffd43b, 0x63e6be];\n\n        // Create buildings around the perimeter of the restaurant\n        function createBuilding(x, z, width, depth, height, colorIndex) {\n          const buildingGroup = new THREE.Group();\n\n          const color = buildingColors[colorIndex % buildingColors.length];\n\n          // Main building structure\n          const buildingGeometry = new THREE.BoxGeometry(width, height, depth);\n          const buildingMaterial = new THREE.MeshStandardMaterial({\n            color: color,\n            roughness: 0.8,\n            metalness: 0.2,\n          });\n          const building = new THREE.Mesh(buildingGeometry, buildingMaterial);\n          building.position.y = height / 2;\n          building.castShadow = true;\n          building.receiveShadow = true;\n          buildingGroup.add(building);\n\n          // Add windows (rows and columns)\n          const windowRows = Math.floor(height / 2.5);\n          const windowCols = Math.floor(width / 2);\n          const windowSize = 0.8;\n          const windowSpacingX = width / (windowCols + 1);\n          const windowSpacingY = 2.2;\n\n          for (let row = 0; row < windowRows; row++) {\n            for (let col = 0; col < windowCols; col++) {\n              // Randomly lit windows\n              const isLit = Math.random() > 0.4;\n              const windowColor = isLit\n                ? windowColors[Math.floor(Math.random() * windowColors.length)]\n                : 0x1a1a2e;\n\n              const windowGeometry = new THREE.BoxGeometry(windowSize, windowSize * 1.2, 0.1);\n              const windowMaterial = new THREE.MeshStandardMaterial({\n                color: windowColor,\n                emissive: isLit ? windowColor : 0x000000,\n                emissiveIntensity: isLit ? 0.6 : 0,\n                roughness: 0.1,\n                metalness: 0.3,\n              });\n              const windowMesh = new THREE.Mesh(windowGeometry, windowMaterial);\n\n              const wx = -width / 2 + windowSpacingX * (col + 1);\n              const wy = 2 + row * windowSpacingY;\n\n              // Front face windows\n              windowMesh.position.set(wx, wy, depth / 2 + 0.05);\n              buildingGroup.add(windowMesh);\n\n              // Track lit windows for animation\n              if (isLit) {\n                cityGroup.userData.windowLights.push({\n                  mesh: windowMesh,\n                  baseColor: windowColor,\n                  flickerRate: 0.002 + Math.random() * 0.005,\n                  flickerOffset: Math.random() * 1000,\n                });\n              }\n\n              // Back face windows (clone)\n              const backWindow = windowMesh.clone();\n              backWindow.position.z = -depth / 2 - 0.05;\n              backWindow.rotation.y = Math.PI;\n              buildingGroup.add(backWindow);\n            }\n          }\n\n          // Rooftop details\n          const roofDetailGeometry = new THREE.BoxGeometry(width * 0.6, 1.5, depth * 0.6);\n          const roofDetailMaterial = new THREE.MeshStandardMaterial({\n            color: 0x1a202c,\n            roughness: 0.7,\n          });\n          const roofDetail = new THREE.Mesh(roofDetailGeometry, roofDetailMaterial);\n          roofDetail.position.y = height + 0.75;\n          buildingGroup.add(roofDetail);\n\n          // Random antenna or AC unit\n          if (Math.random() > 0.5) {\n            const antennaGeometry = new THREE.CylinderGeometry(0.1, 0.1, 3, 8);\n            const antennaMaterial = new THREE.MeshStandardMaterial({\n              color: 0x718096,\n              metalness: 0.6,\n            });\n            const antenna = new THREE.Mesh(antennaGeometry, antennaMaterial);\n            antenna.position.set(width * 0.3, height + 2, 0);\n            buildingGroup.add(antenna);\n\n            // Red blinking light on antenna\n            const lightGeometry = new THREE.SphereGeometry(0.15, 8, 8);\n            const lightMaterial = new THREE.MeshStandardMaterial({\n              color: 0xff0000,\n              emissive: 0xff0000,\n              emissiveIntensity: 1,\n            });\n            const antennaLight = new THREE.Mesh(lightGeometry, lightMaterial);\n            antennaLight.position.set(width * 0.3, height + 3.5, 0);\n            buildingGroup.add(antennaLight);\n\n            cityGroup.userData.windowLights.push({\n              mesh: antennaLight,\n              baseColor: 0xff0000,\n              flickerRate: 0.008,\n              flickerOffset: Math.random() * 1000,\n              isBeacon: true,\n            });\n          }\n\n          buildingGroup.position.set(x, 0, z);\n          return buildingGroup;\n        }\n\n        // Create street/road around restaurant\n        const streetWidth = 8;\n        const streetColor = 0x2d2d2d;\n\n        // Left street (before entrance)\n        const leftStreetGeometry = new THREE.PlaneGeometry(streetWidth, RESTAURANT_DEPTH + 40);\n        const streetMaterial = new THREE.MeshStandardMaterial({\n          color: streetColor,\n          roughness: 0.9,\n        });\n        const leftStreet = new THREE.Mesh(leftStreetGeometry, streetMaterial);\n        leftStreet.rotation.x = -Math.PI / 2;\n        leftStreet.position.set(-halfWidth - streetWidth / 2 - 5, -0.02, 0);\n        leftStreet.receiveShadow = true;\n        cityGroup.add(leftStreet);\n\n        // Back street\n        const backStreetGeometry = new THREE.PlaneGeometry(RESTAURANT_WIDTH + 80, streetWidth);\n        const backStreet = new THREE.Mesh(backStreetGeometry, streetMaterial);\n        backStreet.rotation.x = -Math.PI / 2;\n        backStreet.position.set(0, -0.02, -halfDepth - streetWidth / 2 - 3);\n        backStreet.receiveShadow = true;\n        cityGroup.add(backStreet);\n\n        // Front street (sidewalk area extends)\n        const frontStreetGeometry = new THREE.PlaneGeometry(RESTAURANT_WIDTH + 80, streetWidth);\n        const frontStreet = new THREE.Mesh(frontStreetGeometry, streetMaterial);\n        frontStreet.rotation.x = -Math.PI / 2;\n        frontStreet.position.set(0, -0.02, halfDepth + streetWidth / 2 + 5);\n        frontStreet.receiveShadow = true;\n        cityGroup.add(frontStreet);\n\n        // Add road markings (yellow lines)\n        const lineGeometry = new THREE.BoxGeometry(1.5, 0.02, 0.3);\n        const lineMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffd700,\n          roughness: 0.5,\n        });\n\n        // Back street dashed lines\n        for (let i = -10; i <= 10; i++) {\n          const line = new THREE.Mesh(lineGeometry, lineMaterial);\n          line.position.set(i * 4, 0.01, -halfDepth - streetWidth / 2 - 3);\n          cityGroup.add(line);\n        }\n\n        // Front street dashed lines\n        for (let i = -10; i <= 10; i++) {\n          const line = new THREE.Mesh(lineGeometry, lineMaterial);\n          line.position.set(i * 4, 0.01, halfDepth + streetWidth / 2 + 5);\n          cityGroup.add(line);\n        }\n\n        // Create buildings - BACK ROW (behind restaurant)\n        const backRowZ = -halfDepth - streetWidth - 15;\n        for (let i = 0; i < 8; i++) {\n          const x = -50 + i * 14 + (Math.random() - 0.5) * 3;\n          const height = 15 + Math.random() * 25;\n          const width = 8 + Math.random() * 6;\n          const depth = 8 + Math.random() * 4;\n          const building = createBuilding(x, backRowZ - depth / 2, width, depth, height, i);\n          cityGroup.add(building);\n        }\n\n        // Create buildings - FRONT ROW (across the street in front)\n        const frontRowZ = halfDepth + streetWidth + 18;\n        for (let i = 0; i < 7; i++) {\n          const x = -45 + i * 15 + (Math.random() - 0.5) * 3;\n          const height = 12 + Math.random() * 20;\n          const width = 7 + Math.random() * 5;\n          const depth = 6 + Math.random() * 4;\n          const building = createBuilding(x, frontRowZ + depth / 2, width, depth, height, i + 3);\n          building.rotation.y = Math.PI;\n          // Face toward restaurant\n          cityGroup.add(building);\n        }\n\n        // Create buildings - LEFT SIDE\n        const leftRowX = -halfWidth - streetWidth - 12;\n        for (let i = 0; i < 5; i++) {\n          const z = -40 + i * 18 + (Math.random() - 0.5) * 4;\n          const height = 18 + Math.random() * 22;\n          const width = 10 + Math.random() * 6;\n          const depth = 8 + Math.random() * 4;\n          const building = createBuilding(leftRowX - width / 2, z, width, depth, height, i + 5);\n          building.rotation.y = Math.PI / 2;\n          // Face toward restaurant\n          cityGroup.add(building);\n        }\n\n        // Add street lamps\n        function createStreetLamp(x, z) {\n          const lampGroup = new THREE.Group();\n\n          // Pole\n          const poleGeometry = new THREE.CylinderGeometry(0.15, 0.2, 5, 8);\n          const poleMaterial = new THREE.MeshStandardMaterial({\n            color: 0x333333,\n            metalness: 0.5,\n            roughness: 0.4,\n          });\n          const pole = new THREE.Mesh(poleGeometry, poleMaterial);\n          pole.position.y = 2.5;\n          lampGroup.add(pole);\n\n          // Lamp head\n          const headGeometry = new THREE.BoxGeometry(1, 0.5, 0.5);\n          const headMaterial = new THREE.MeshStandardMaterial({\n            color: 0x444444,\n            metalness: 0.6,\n          });\n          const head = new THREE.Mesh(headGeometry, headMaterial);\n          head.position.y = 5.2;\n          lampGroup.add(head);\n\n          // Light bulb (glowing)\n          const bulbGeometry = new THREE.SphereGeometry(0.3, 12, 12);\n          const bulbMaterial = new THREE.MeshStandardMaterial({\n            color: 0xffe4b5,\n            emissive: 0xffd700,\n            emissiveIntensity: 0.8,\n            roughness: 0.2,\n          });\n          const bulb = new THREE.Mesh(bulbGeometry, bulbMaterial);\n          bulb.position.y = 4.8;\n          lampGroup.add(bulb);\n\n          lampGroup.position.set(x, 0, z);\n          return lampGroup;\n        }\n\n        // Add street lamps along the streets\n        for (let i = -3; i <= 3; i++) {\n          // Back street lamps\n          cityGroup.add(createStreetLamp(i * 15, -halfDepth - streetWidth / 2 - 3 - 3));\n          // Front street lamps\n          cityGroup.add(createStreetLamp(i * 15, halfDepth + streetWidth / 2 + 5 + 3));\n        }\n\n        // Left side lamps\n        for (let i = -2; i <= 2; i++) {\n          cityGroup.add(createStreetLamp(-halfWidth - streetWidth / 2 - 5 - 3, i * 18));\n        }\n\n        // Add some parked cars on the streets for atmosphere\n        function createParkedCar(x, z, rotation, color) {\n          const carGroup = new THREE.Group();\n\n          // Car body\n          const bodyGeometry = new THREE.BoxGeometry(2, 0.7, 4);\n          const bodyMaterial = new THREE.MeshStandardMaterial({\n            color: color,\n            roughness: 0.4,\n            metalness: 0.3,\n          });\n          const body = new THREE.Mesh(bodyGeometry, bodyMaterial);\n          body.position.y = 0.5;\n          carGroup.add(body);\n\n          // Car roof\n          const roofGeometry = new THREE.BoxGeometry(1.6, 0.5, 2);\n          const roof = new THREE.Mesh(roofGeometry, bodyMaterial);\n          roof.position.set(0, 1.1, -0.3);\n          carGroup.add(roof);\n\n          // Windows\n          const windowMaterial = new THREE.MeshStandardMaterial({\n            color: 0x87ceeb,\n            roughness: 0.1,\n            metalness: 0.3,\n            opacity: 0.7,\n            transparent: true,\n          });\n          const frontWindow = new THREE.Mesh(new THREE.BoxGeometry(1.4, 0.4, 0.1), windowMaterial);\n          frontWindow.position.set(0, 1, 0.6);\n          frontWindow.rotation.x = -0.2;\n          carGroup.add(frontWindow);\n\n          carGroup.position.set(x, 0, z);\n          carGroup.rotation.y = rotation;\n          return carGroup;\n        }\n\n        const carColors = [\n          0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6, 0x1abc9c, 0xffffff, 0x2c3e50,\n        ];\n\n        // Add parked cars on back street\n        for (let i = 0; i < 4; i++) {\n          const x = -30 + i * 18 + (Math.random() - 0.5) * 5;\n          const color = carColors[Math.floor(Math.random() * carColors.length)];\n          cityGroup.add(createParkedCar(x, -halfDepth - streetWidth - 1, 0, color));\n        }\n\n        // Add parked cars on front street\n        for (let i = 0; i < 4; i++) {\n          const x = -25 + i * 16 + (Math.random() - 0.5) * 4;\n          const color = carColors[Math.floor(Math.random() * carColors.length)];\n          cityGroup.add(createParkedCar(x, halfDepth + streetWidth + 9, Math.PI, color));\n        }\n\n        // Sky gradient backdrop (far behind everything)\n        const skyGeometry = new THREE.PlaneGeometry(300, 100);\n        const skyMaterial = new THREE.MeshBasicMaterial({\n          color: 0x1a1a2e,\n          // Dark night sky\n          side: THREE.DoubleSide,\n        });\n        // Back sky\n        const backSky = new THREE.Mesh(skyGeometry, skyMaterial);\n        backSky.position.set(0, 30, -120);\n        cityGroup.add(backSky);\n\n        // Front sky\n        const frontSky = new THREE.Mesh(skyGeometry, skyMaterial);\n        frontSky.position.set(0, 30, 120);\n        frontSky.rotation.y = Math.PI;\n        cityGroup.add(frontSky);\n\n        // Left sky\n        const leftSky = new THREE.Mesh(skyGeometry, skyMaterial);\n        leftSky.position.set(-120, 30, 0);\n        leftSky.rotation.y = Math.PI / 2;\n        cityGroup.add(leftSky);\n\n        scene.add(cityGroup);\n\n        // Store reference for animation\n        scene.userData.cityBackground = cityGroup;\n      }\n\n      function updateCityLights(deltaTime) {\n        const city = scene.userData.cityBackground;\n        if (!city || !city.userData.windowLights) return;\n\n        if (performanceSettings.cityLightUpdateInterval > 0) {\n          cityLightUpdateTimer += deltaTime;\n          if (cityLightUpdateTimer < performanceSettings.cityLightUpdateInterval) return;\n          cityLightUpdateTimer = 0;\n        }\n\n        const now = Date.now();\n\n        for (const light of city.userData.windowLights) {\n          if (light.isBeacon) {\n            // Blinking beacon lights\n            const blink = Math.sin(now * light.flickerRate + light.flickerOffset) > 0.3;\n            light.mesh.material.emissiveIntensity = blink ? 1 : 0.1;\n          } else {\n            // Subtle window flickering\n            const flicker = 0.5 + Math.sin(now * light.flickerRate + light.flickerOffset) * 0.15;\n            light.mesh.material.emissiveIntensity = flicker;\n          }\n        }\n      }\n\n      function createEntranceDoor(wallX, entranceZ, wallHeight, createWallMaterial) {\n        const entranceGroup = new THREE.Group();\n\n        // Door frame - vertical posts\n        const framePostGeometry = new THREE.BoxGeometry(0.4, wallHeight + 0.5, 0.6);\n        const framePostMaterial = new THREE.MeshStandardMaterial({\n          color: 0xdaa520,\n          // Golden frame\n          roughness: 0.3,\n          metalness: 0.4,\n        });\n\n        // Left door frame post\n        const leftPost = new THREE.Mesh(framePostGeometry, framePostMaterial);\n        leftPost.position.set(wallX, (wallHeight + 0.5) / 2, entranceZ - 3);\n        leftPost.castShadow = true;\n        scene.add(leftPost);\n\n        // Right door frame post\n        const rightPost = new THREE.Mesh(framePostGeometry, framePostMaterial);\n        rightPost.position.set(wallX, (wallHeight + 0.5) / 2, entranceZ + 3);\n        rightPost.castShadow = true;\n        scene.add(rightPost);\n\n        // Door frame - top beam\n        const frameTopGeometry = new THREE.BoxGeometry(0.5, 0.5, 6.8);\n        const frameTop = new THREE.Mesh(frameTopGeometry, framePostMaterial);\n        frameTop.position.set(wallX, wallHeight + 0.25, entranceZ);\n        frameTop.castShadow = true;\n        scene.add(frameTop);\n\n        // Welcome mat\n        const matGeometry = new THREE.BoxGeometry(3, 0.05, 2);\n        const matMaterial = new THREE.MeshStandardMaterial({\n          color: 0x8b0000,\n          // Dark red mat\n          roughness: 0.9,\n        });\n        const welcomeMat = new THREE.Mesh(matGeometry, matMaterial);\n        welcomeMat.position.set(wallX + 1.5, 0.03, entranceZ);\n        welcomeMat.receiveShadow = true;\n        scene.add(welcomeMat);\n\n        // \"WELCOME\" text area on mat (lighter stripe)\n        const textStripeGeometry = new THREE.BoxGeometry(2.5, 0.06, 0.8);\n        const textStripeMaterial = new THREE.MeshStandardMaterial({\n          color: 0xcd5c5c,\n          // Lighter red\n          roughness: 0.8,\n        });\n        const textStripe = new THREE.Mesh(textStripeGeometry, textStripeMaterial);\n        textStripe.position.set(wallX + 1.5, 0.04, entranceZ);\n        scene.add(textStripe);\n\n        // \"OPEN\" sign above door\n        const signGroup = new THREE.Group();\n\n        // Sign background\n        const signBgGeometry = new THREE.BoxGeometry(0.2, 1, 2.5);\n        const signBgMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2c3e50,\n          roughness: 0.4,\n        });\n        const signBg = new THREE.Mesh(signBgGeometry, signBgMaterial);\n        signBg.position.y = 0;\n        signBg.castShadow = true;\n        signGroup.add(signBg);\n\n        // \"OPEN\" text panel (green neon effect)\n        const openPanelGeometry = new THREE.BoxGeometry(0.1, 0.7, 2);\n        const openPanelMaterial = new THREE.MeshStandardMaterial({\n          color: 0x00ff00,\n          // Bright green\n          emissive: 0x00aa00,\n          emissiveIntensity: 0.5,\n          roughness: 0.2,\n        });\n        const openPanel = new THREE.Mesh(openPanelGeometry, openPanelMaterial);\n        openPanel.position.set(0.1, 0, 0);\n        signGroup.add(openPanel);\n\n        signGroup.position.set(wallX - 0.3, wallHeight + 1, entranceZ);\n        signGroup.rotation.y = Math.PI / 2;\n        scene.add(signGroup);\n\n        // Glass door panels (decorative - suggests automatic doors)\n        const glassMaterial = new THREE.MeshStandardMaterial({\n          color: 0x87ceeb,\n          transparent: true,\n          opacity: 0.3,\n          roughness: 0.1,\n          metalness: 0.3,\n        });\n\n        // Left glass door\n        const leftDoorGeometry = new THREE.BoxGeometry(0.1, wallHeight - 0.3, 2.8);\n        const leftDoor = new THREE.Mesh(leftDoorGeometry, glassMaterial);\n        leftDoor.position.set(wallX, wallHeight / 2, entranceZ - 1.4);\n        scene.add(leftDoor);\n\n        // Right glass door\n        const rightDoor = new THREE.Mesh(leftDoorGeometry, glassMaterial);\n        rightDoor.position.set(wallX, wallHeight / 2, entranceZ + 1.4);\n        scene.add(rightDoor);\n\n        // Door handles\n        const handleGeometry = new THREE.BoxGeometry(0.4, 0.1, 0.1);\n        const handleMaterial = new THREE.MeshStandardMaterial({\n          color: 0xc0c0c0,\n          roughness: 0.2,\n          metalness: 0.8,\n        });\n\n        const leftHandle = new THREE.Mesh(handleGeometry, handleMaterial);\n        leftHandle.position.set(wallX + 0.2, wallHeight / 2, entranceZ - 0.1);\n        scene.add(leftHandle);\n\n        const rightHandle = new THREE.Mesh(handleGeometry, handleMaterial);\n        rightHandle.position.set(wallX + 0.2, wallHeight / 2, entranceZ + 0.1);\n        scene.add(rightHandle);\n\n        // Outdoor sidewalk/entrance area\n        const sidewalkGeometry = new THREE.BoxGeometry(4, 0.1, 8);\n        const sidewalkMaterial = new THREE.MeshStandardMaterial({\n          color: 0x808080,\n          // Gray concrete\n          roughness: 0.9,\n        });\n        const sidewalk = new THREE.Mesh(sidewalkGeometry, sidewalkMaterial);\n        sidewalk.position.set(wallX - 2.5, 0.05, entranceZ);\n        sidewalk.receiveShadow = true;\n        scene.add(sidewalk);\n\n        // Planter boxes on either side of entrance\n        const planterGeometry = new THREE.BoxGeometry(1, 0.8, 1.5);\n        const planterMaterial = new THREE.MeshStandardMaterial({\n          color: 0x8b4513,\n          // Brown terracotta\n          roughness: 0.7,\n        });\n\n        // Left planter\n        const leftPlanter = new THREE.Mesh(planterGeometry, planterMaterial);\n        leftPlanter.position.set(wallX - 1, 0.4, entranceZ - 4);\n        leftPlanter.castShadow = true;\n        scene.add(leftPlanter);\n\n        // Right planter\n        const rightPlanter = new THREE.Mesh(planterGeometry, planterMaterial);\n        rightPlanter.position.set(wallX - 1, 0.4, entranceZ + 4);\n        rightPlanter.castShadow = true;\n        scene.add(rightPlanter);\n\n        // Plants in planters (simple green spheres)\n        const plantGeometry = new THREE.SphereGeometry(0.6, 12, 12);\n        const plantMaterial = new THREE.MeshStandardMaterial({\n          color: 0x228b22,\n          // Forest green\n          roughness: 0.8,\n        });\n\n        const leftPlant = new THREE.Mesh(plantGeometry, plantMaterial);\n        leftPlant.position.set(wallX - 1, 1.1, entranceZ - 4);\n        leftPlant.castShadow = true;\n        scene.add(leftPlant);\n\n        const rightPlant = new THREE.Mesh(plantGeometry, plantMaterial);\n        rightPlant.position.set(wallX - 1, 1.1, entranceZ + 4);\n        rightPlant.castShadow = true;\n        scene.add(rightPlant);\n      }\n\n      function createPlayer() {\n        const config = window.gameConfig.player;\n\n        // Create player using stickman model\n        if (stickmanModelLoaded && stickmanModelTemplate) {\n          player = createStickmanInstance(0xffffff);\n        } else {\n          // Fallback to original player creation if model not loaded\n          player = new THREE.Group();\n\n          // Body (chef uniform)\n          const bodyGeometry = new THREE.CylinderGeometry(0.5, 0.6, 1.4, 16);\n          const bodyMaterial = new THREE.MeshStandardMaterial({\n            color: 0xffffff,\n            roughness: 0.5,\n            metalness: 0.1,\n          });\n          const body = new THREE.Mesh(bodyGeometry, bodyMaterial);\n          body.position.y = 0.7;\n          body.castShadow = true;\n          player.add(body);\n\n          // Apron\n          const apronGeometry = new THREE.BoxGeometry(0.8, 0.8, 0.15);\n          const apronMaterial = new THREE.MeshStandardMaterial({\n            color: config.color,\n            roughness: 0.6,\n          });\n          const apron = new THREE.Mesh(apronGeometry, apronMaterial);\n          apron.position.set(0, 0.5, 0.35);\n          apron.castShadow = true;\n          player.add(apron);\n\n          // Head\n          const headGeometry = new THREE.SphereGeometry(0.45, 16, 16);\n          const headMaterial = new THREE.MeshStandardMaterial({\n            color: 0xffdbac,\n            roughness: 0.6,\n          });\n          const head = new THREE.Mesh(headGeometry, headMaterial);\n          head.position.y = 1.7;\n          head.castShadow = true;\n          player.add(head);\n\n          // Chef hat\n          const hatBaseGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.15, 16);\n          const hatMaterial = new THREE.MeshStandardMaterial({\n            color: 0xffffff,\n            roughness: 0.4,\n          });\n          const hatBase = new THREE.Mesh(hatBaseGeometry, hatMaterial);\n          hatBase.position.y = 2.1;\n          hatBase.castShadow = true;\n          player.add(hatBase);\n\n          const hatTopGeometry = new THREE.CylinderGeometry(0.4, 0.45, 0.5, 16);\n          const hatTop = new THREE.Mesh(hatTopGeometry, hatMaterial);\n          hatTop.position.y = 2.4;\n          hatTop.castShadow = true;\n          player.add(hatTop);\n\n          // Eyes\n          const eyeGeometry = new THREE.SphereGeometry(0.08, 8, 8);\n          const eyeMaterial = new THREE.MeshBasicMaterial({\n            color: 0x333333,\n          });\n\n          const leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial);\n          leftEye.position.set(-0.15, 1.75, 0.35);\n          player.add(leftEye);\n\n          const rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial);\n          rightEye.position.set(0.15, 1.75, 0.35);\n          player.add(rightEye);\n\n          player.userData.squishState = {\n            scaleY: 1,\n            scaleXZ: 1,\n            targetScaleY: 1,\n            targetScaleXZ: 1,\n          };\n        }\n\n        player.position.set(config.startX, 0, config.startZ);\n\n        // Serving tray (held in front) - add to all player types\n        createTray(player, PLAYER_HEIGHT);\n\n        const maxCanvas = document.createElement('canvas');\n        maxCanvas.width = 128;\n        maxCanvas.height = 64;\n        const maxCtx = maxCanvas.getContext('2d');\n        maxCtx.fillStyle = '#FF0000';\n        maxCtx.font = 'bold 40px Arial';\n        maxCtx.textAlign = 'center';\n        maxCtx.textBaseline = 'middle';\n        maxCtx.lineWidth = 4;\n        maxCtx.strokeStyle = '#FFFFFF';\n        maxCtx.strokeText('MAX', 64, 32);\n        maxCtx.fillText('MAX', 64, 32);\n        const maxTex = new THREE.CanvasTexture(maxCanvas);\n        const maxMat = new THREE.SpriteMaterial({\n          map: maxTex,\n          depthTest: false,\n          transparent: true,\n        });\n        const maxSprite = new THREE.Sprite(maxMat);\n        maxSprite.scale.set(1.5, 0.75, 1);\n        maxSprite.position.y = PLAYER_HEIGHT + 1;\n        maxSprite.visible = false;\n        player.add(maxSprite);\n        player.userData.maxSprite = maxSprite;\n\n        scene.add(player);\n      }\n\n      function createSellZone() {\n        const config = window.gameConfig.sellZone;\n        if (!config || config.deleted) return;\n        const x = config.x || 0;\n        const z = config.z || 0;\n\n        // Service counter platform\n        const platformGeometry = new THREE.CylinderGeometry(config.radius, config.radius, 0.3, 32);\n        const platformMaterial = new THREE.MeshStandardMaterial({\n          color: 0x8b4513,\n          roughness: 0.6,\n          metalness: 0.1,\n        });\n        sellZoneMesh = new THREE.Mesh(platformGeometry, platformMaterial);\n        sellZoneMesh.position.set(x, 0.15, z);\n        sellZoneMesh.receiveShadow = true;\n        scene.add(sellZoneMesh);\n\n        // Counter top\n        const counterGeometry = new THREE.CylinderGeometry(\n          config.radius - 0.2,\n          config.radius - 0.2,\n          0.1,\n          32\n        );\n        const counterMaterial = new THREE.MeshStandardMaterial({\n          color: 0xd4d4d4,\n          roughness: 0.3,\n          metalness: 0.5,\n        });\n        const counter = new THREE.Mesh(counterGeometry, counterMaterial);\n        counter.position.set(x, 0.35, z);\n        scene.add(counter);\n\n        // Glowing ring (serving indicator)\n        const ringGeometry = new THREE.RingGeometry(config.radius - 0.3, config.radius, 32);\n        const ringMaterial = new THREE.MeshBasicMaterial({\n          color: config.color,\n          side: THREE.DoubleSide,\n          transparent: true,\n          opacity: 0.6,\n        });\n        sellZoneGlow = new THREE.Mesh(ringGeometry, ringMaterial);\n        sellZoneGlow.rotation.x = -Math.PI / 2;\n        sellZoneGlow.position.set(x, 0.36, z);\n        scene.add(sellZoneGlow);\n\n        // Customer indicator (simple figure waiting)\n        const customerGroup = new THREE.Group();\n\n        // Customer body\n        const custBodyGeometry = new THREE.CylinderGeometry(0.4, 0.5, 1.2, 16);\n        const custBodyMaterial = new THREE.MeshStandardMaterial({\n          color: 0x3498db,\n          roughness: 0.5,\n        });\n        const custBody = new THREE.Mesh(custBodyGeometry, custBodyMaterial);\n        custBody.position.y = 0.6;\n        customerGroup.add(custBody);\n\n        // Customer head\n        const custHeadGeometry = new THREE.SphereGeometry(0.35, 16, 16);\n        const custHeadMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffdbac,\n          roughness: 0.6,\n        });\n        const custHead = new THREE.Mesh(custHeadGeometry, custHeadMaterial);\n        custHead.position.y = 1.5;\n        customerGroup.add(custHead);\n\n        customerGroup.position.set(x, 0.4, z);\n        customerGroup.userData.baseY = 0.4;\n        customerGroup.userData.rotationSpeed = 0;\n        scene.add(customerGroup);\n\n        // Store reference for animation\n        sellZoneMesh.userData.coinIndicator = customerGroup;\n      }\n\n      function createMoneyZone() {\n        const config = window.gameConfig.moneyZone;\n        if (!config || config.deleted) return;\n\n        // Create rounded rectangle platform (like reference image)\n        const platformGroup = new THREE.Group();\n\n        // Main platform - rounded box shape (tan/orange color like reference)\n        const platformGeometry = new THREE.BoxGeometry(config.width, 0.4, config.depth);\n        const platformMaterial = new THREE.MeshStandardMaterial({\n          color: 0xe8c4a0,\n          // Tan/beige color like reference\n          roughness: 0.6,\n          metalness: 0.1,\n        });\n        moneyZoneMesh = new THREE.Mesh(platformGeometry, platformMaterial);\n        moneyZoneMesh.position.set(config.x, 0.2, config.z);\n        moneyZoneMesh.receiveShadow = true;\n        moneyZoneMesh.castShadow = true;\n        scene.add(moneyZoneMesh);\n\n        // Rounded edge effect (darker border)\n        const borderGeometry = new THREE.BoxGeometry(config.width + 0.3, 0.35, config.depth + 0.3);\n        const borderMaterial = new THREE.MeshStandardMaterial({\n          color: 0xd4a574,\n          // Slightly darker tan for border\n          roughness: 0.7,\n          metalness: 0.1,\n        });\n        const border = new THREE.Mesh(borderGeometry, borderMaterial);\n        border.position.set(config.x, 0.175, config.z);\n        border.receiveShadow = true;\n        scene.add(border);\n\n        // Subtle glow underneath\n        const glowGeometry = new THREE.BoxGeometry(config.width + 0.6, 0.05, config.depth + 0.6);\n        const glowMaterial = new THREE.MeshBasicMaterial({\n          color: config.color,\n          transparent: true,\n          opacity: 0.4,\n        });\n        moneyZoneGlow = new THREE.Mesh(glowGeometry, glowMaterial);\n        moneyZoneGlow.position.set(config.x, 0.02, config.z);\n        scene.add(moneyZoneGlow);\n\n        // Icon: Gold coin stack (different from spend zone's gem)\n        const iconGroup = new THREE.Group();\n\n        // Coin stack base\n        const coinGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.15, 16);\n        const coinMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffd700,\n          // Gold\n          roughness: 0.3,\n          metalness: 0.7,\n        });\n\n        // Stack of coins\n        for (let i = 0; i < 3; i++) {\n          const coin = new THREE.Mesh(coinGeometry, coinMaterial);\n          coin.position.y = i * 0.12;\n          coin.rotation.x = Math.PI / 2;\n          coin.castShadow = true;\n          iconGroup.add(coin);\n        }\n\n        // Dollar sign on top coin\n        const dollarGeometry = new THREE.BoxGeometry(0.15, 0.3, 0.05);\n        const dollarMaterial = new THREE.MeshStandardMaterial({\n          color: 0xb8860b,\n          // Dark gold\n          roughness: 0.4,\n        });\n        const dollarSign = new THREE.Mesh(dollarGeometry, dollarMaterial);\n        dollarSign.position.set(0, 0.3, 0.28);\n        iconGroup.add(dollarSign);\n\n        iconGroup.position.set(config.x, 0.5, config.z);\n        iconGroup.userData.baseY = 0.5;\n        scene.add(iconGroup);\n        moneyZoneMesh.userData.signIndicator = iconGroup;\n        moneyZoneMesh.userData.borderMesh = border;\n      }\n\n      function createSpendZone() {\n        const config = window.gameConfig.spendZone;\n        if (!config || config.deleted) return;\n\n        // Create rounded rectangle platform (like reference image)\n        // Main platform - rounded box shape (tan/orange color like reference)\n        const platformGeometry = new THREE.BoxGeometry(config.width, 0.4, config.depth);\n        const platformMaterial = new THREE.MeshStandardMaterial({\n          color: 0xe8c4a0,\n          // Tan/beige color like reference\n          roughness: 0.6,\n          metalness: 0.1,\n        });\n        spendZoneMesh = new THREE.Mesh(platformGeometry, platformMaterial);\n        spendZoneMesh.position.set(config.x, 0.2, config.z);\n        spendZoneMesh.receiveShadow = true;\n        spendZoneMesh.castShadow = true;\n        scene.add(spendZoneMesh);\n\n        // Rounded edge effect (darker border)\n        const borderGeometry = new THREE.BoxGeometry(config.width + 0.3, 0.35, config.depth + 0.3);\n        const borderMaterial = new THREE.MeshStandardMaterial({\n          color: 0xd4a574,\n          // Slightly darker tan for border\n          roughness: 0.7,\n          metalness: 0.1,\n        });\n        const border = new THREE.Mesh(borderGeometry, borderMaterial);\n        border.position.set(config.x, 0.175, config.z);\n        border.receiveShadow = true;\n        scene.add(border);\n\n        // Subtle glow underneath\n        const glowGeometry = new THREE.BoxGeometry(config.width + 0.6, 0.05, config.depth + 0.6);\n        const glowMaterial = new THREE.MeshBasicMaterial({\n          color: config.color,\n          transparent: true,\n          opacity: 0.4,\n        });\n        spendZoneGlow = new THREE.Mesh(glowGeometry, glowMaterial);\n        spendZoneGlow.position.set(config.x, 0.02, config.z);\n        scene.add(spendZoneGlow);\n\n        // Icon: Green gem/diamond (like reference image)\n        const iconGroup = new THREE.Group();\n\n        // Gem shape using octahedron\n        const gemGeometry = new THREE.OctahedronGeometry(0.5, 0);\n        const gemMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2ecc71,\n          // Bright green like reference\n          roughness: 0.2,\n          metalness: 0.5,\n          emissive: 0x1a8f4a,\n          emissiveIntensity: 0.2,\n        });\n        const gem = new THREE.Mesh(gemGeometry, gemMaterial);\n        gem.scale.set(1, 1.2, 1);\n        // Elongate slightly\n        gem.rotation.y = Math.PI / 4;\n        gem.castShadow = true;\n        iconGroup.add(gem);\n\n        iconGroup.position.set(config.x, 0.9, config.z);\n        iconGroup.userData.baseY = 0.9;\n        scene.add(iconGroup);\n\n        // Multiplier number indicator (like \"5x\" in reference)\n        const numberGroup = new THREE.Group();\n\n        // Background pill for number\n        const pillGeometry = new THREE.BoxGeometry(1.2, 0.5, 0.1);\n        const pillMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffffff,\n          roughness: 0.3,\n          metalness: 0.1,\n        });\n        const pill = new THREE.Mesh(pillGeometry, pillMaterial);\n        pill.castShadow = true;\n        numberGroup.add(pill);\n\n        // \"5x\" text using simple box geometry\n        // Number \"5\"\n        const digitMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2ecc71,\n          // Green to match gem\n          roughness: 0.4,\n        });\n\n        // Simplified \"5\" shape\n        const fiveTop = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.06, 0.05), digitMaterial);\n        fiveTop.position.set(-0.2, 0.12, 0.06);\n        numberGroup.add(fiveTop);\n\n        const fiveLeft = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.12, 0.05), digitMaterial);\n        fiveLeft.position.set(-0.3, 0.06, 0.06);\n        numberGroup.add(fiveLeft);\n\n        const fiveMid = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.06, 0.05), digitMaterial);\n        fiveMid.position.set(-0.2, 0, 0.06);\n        numberGroup.add(fiveMid);\n\n        const fiveRight = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.12, 0.05), digitMaterial);\n        fiveRight.position.set(-0.1, -0.06, 0.06);\n        numberGroup.add(fiveRight);\n        const fiveBottom = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.06, 0.05), digitMaterial);\n        fiveBottom.position.set(-0.2, -0.12, 0.06);\n        numberGroup.add(fiveBottom);\n        const xPart1 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.28, 0.05), digitMaterial);\n        xPart1.position.set(0.15, 0, 0.06);\n        xPart1.rotation.z = Math.PI / 4;\n        numberGroup.add(xPart1);\n\n        const xPart2 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.28, 0.05), digitMaterial);\n        xPart2.position.set(0.15, 0, 0.06);\n        xPart2.rotation.z = -Math.PI / 4;\n        numberGroup.add(xPart2);\n\n        numberGroup.position.set(config.x, 0.5, config.z + config.depth / 2 - 0.5);\n        numberGroup.rotation.x = -Math.PI / 6;\n        // Tilt toward camera\n        scene.add(numberGroup);\n\n        spendZoneMesh.userData.gemIndicator = iconGroup;\n        spendZoneMesh.userData.numberIndicator = numberGroup;\n        spendZoneMesh.userData.borderMesh = border;\n      }\n\n      function createCashierStall() {\n        const config = window.gameConfig.cashierStall;\n        if (!config || config.deleted) return;\n\n        // Main cashier counter\n        const counterGroup = new THREE.Group();\n\n        // Counter base (L-shaped serving counter)\n        const counterGeometry = new THREE.BoxGeometry(config.width, 1.2, config.depth);\n        const counterMaterial = new THREE.MeshStandardMaterial({\n          color: config.color,\n          roughness: 0.6,\n          metalness: 0.1,\n        });\n        cashierStallMesh = new THREE.Mesh(counterGeometry, counterMaterial);\n        cashierStallMesh.position.set(config.x, 0.6, config.z);\n        cashierStallMesh.castShadow = true;\n        cashierStallMesh.receiveShadow = true;\n        scene.add(cashierStallMesh);\n        // apply rotation from config if specified (degrees)\n        cashierStallMesh.rotation.y = THREE.MathUtils.degToRad(config.rotation || 0);\n\n        // Counter top (serving surface)\n        const topGeometry = new THREE.BoxGeometry(config.width + 0.2, 0.15, config.depth + 0.2);\n        const topMaterial = new THREE.MeshStandardMaterial({\n          color: 0xe8e8e8,\n          roughness: 0.3,\n          metalness: 0.2,\n        });\n        const counterTop = new THREE.Mesh(topGeometry, topMaterial);\n        counterTop.position.set(config.x, 1.25, config.z);\n        counterTop.castShadow = true;\n        scene.add(counterTop);\n        cashierStallMesh.userData.counterTop = counterTop;\n\n        // Cash register\n        const registerGroup = new THREE.Group();\n\n        // Register body\n        const registerGeometry = new THREE.BoxGeometry(0.8, 0.5, 0.6);\n        const registerMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2c3e50,\n          roughness: 0.4,\n          metalness: 0.3,\n        });\n        const registerBody = new THREE.Mesh(registerGeometry, registerMaterial);\n        registerBody.position.y = 0.25;\n        registerGroup.add(registerBody);\n\n        // Register screen\n        const screenGeometry = new THREE.BoxGeometry(0.5, 0.3, 0.05);\n        const screenMaterial = new THREE.MeshStandardMaterial({\n          color: 0x3498db,\n          emissive: 0x1a5276,\n          emissiveIntensity: 0.3,\n          roughness: 0.2,\n        });\n        const screen = new THREE.Mesh(screenGeometry, screenMaterial);\n        screen.position.set(0, 0.45, 0.25);\n        screen.rotation.x = -0.3;\n        registerGroup.add(screen);\n\n        registerGroup.position.set(config.x + config.width / 2 - 0.8, 1.33, config.z);\n        scene.add(registerGroup);\n        cashierStallMesh.userData.register = registerGroup;\n\n        // Burger display tray area (where burgers stack)\n        const trayGeometry = new THREE.BoxGeometry(2, 0.1, 1.5);\n        const trayMaterial = new THREE.MeshStandardMaterial({\n          color: 0xc0c0c0,\n          roughness: 0.3,\n          metalness: 0.6,\n        });\n        const tray = new THREE.Mesh(trayGeometry, trayMaterial);\n        tray.position.set(config.x - config.width / 2 + 1.5, 1.35, config.z);\n        tray.castShadow = true;\n        scene.add(tray);\n        cashierStallMesh.userData.tray = tray;\n\n        // Glow zone for player deposit area\n        const glowGeometry = new THREE.BoxGeometry(config.width + 1, 0.05, config.depth + 1);\n        const glowMaterial = new THREE.MeshBasicMaterial({\n          color: 0x27ae60,\n          transparent: true,\n          opacity: 0.4,\n        });\n        cashierStallGlow = new THREE.Mesh(glowGeometry, glowMaterial);\n        cashierStallGlow.position.set(config.x, 0.02, config.z - config.depth / 2 - 1);\n        scene.add(cashierStallGlow);\n\n        // Menu board above counter\n        const boardGeometry = new THREE.BoxGeometry(3, 1.5, 0.1);\n        const boardMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2c3e50,\n          roughness: 0.5,\n        });\n        const menuBoard = new THREE.Mesh(boardGeometry, boardMaterial);\n        menuBoard.position.set(config.x, 3, config.z + config.depth / 2 + 0.5);\n        menuBoard.castShadow = true;\n        scene.add(menuBoard);\n\n        // Menu text background (lighter panel)\n        const menuPanelGeometry = new THREE.BoxGeometry(2.6, 1.2, 0.05);\n        const menuPanelMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffd700,\n          roughness: 0.4,\n        });\n        const menuPanel = new THREE.Mesh(menuPanelGeometry, menuPanelMaterial);\n        menuPanel.position.set(config.x, 3, config.z + config.depth / 2 + 0.58);\n        scene.add(menuPanel);\n\n        // Burger icon on menu\n        const menuBurgerGroup = new THREE.Group();\n        const bun = new THREE.Mesh(\n          new THREE.SphereGeometry(0.25, 12, 8, 0, Math.PI * 2, 0, Math.PI / 2),\n          new THREE.MeshStandardMaterial({\n            color: 0xdeb887,\n          })\n        );\n        bun.position.y = 0.15;\n        menuBurgerGroup.add(bun);\n\n        const patty = new THREE.Mesh(\n          new THREE.CylinderGeometry(0.22, 0.22, 0.08, 12),\n          new THREE.MeshStandardMaterial({\n            color: 0x8b4513,\n          })\n        );\n        patty.position.y = 0.05;\n        menuBurgerGroup.add(patty);\n\n        menuBurgerGroup.position.set(config.x, 3, config.z + config.depth / 2 + 0.65);\n        menuBurgerGroup.scale.setScalar(1.2);\n        scene.add(menuBurgerGroup);\n        cashierStallMesh.userData.menuBurger = menuBurgerGroup;\n        cashierStallMesh.userData.menuBoard = menuBoard;\n\n        // Cash pile platform (to the side of cashier)\n        const cashPileX = config.x + config.width / 2 + 3.5;\n        // Right side of cashier\n        const cashPileZ = config.z;\n        const cashPileWidth = 4;\n        const cashPileDepth = 3;\n\n        // Platform for cash pile\n        const cashPileGeometry = new THREE.BoxGeometry(cashPileWidth, 0.3, cashPileDepth);\n        const cashPileMaterial = new THREE.MeshStandardMaterial({\n          color: 0xe8c4a0,\n          roughness: 0.6,\n          metalness: 0.1,\n          opacity: 0,\n          transparent: true,\n        });\n        cashierCashPileMesh = new THREE.Mesh(cashPileGeometry, cashPileMaterial);\n        cashierCashPileMesh.position.set(cashPileX, 0.15, cashPileZ);\n        // cashierCashPileMesh.receiveShadow = true;\n        // cashierCashPileMesh.castShadow = true;\n        scene.add(cashierCashPileMesh);\n\n        // Border for cash pile\n        const cashPileBorderGeometry = new THREE.BoxGeometry(\n          cashPileWidth + 0.3,\n          0.25,\n          cashPileDepth + 0.3\n        );\n        const cashPileBorderMaterial = new THREE.MeshStandardMaterial({\n          color: 0xd4a574,\n          roughness: 0.7,\n        });\n        const cashPileBorder = new THREE.Mesh(cashPileBorderGeometry, cashPileBorderMaterial);\n        cashPileBorder.position.set(cashPileX, 0.125, cashPileZ);\n        // scene.add(cashPileBorder);\n        cashierCashPileMesh.userData.borderMesh = cashPileBorder;\n\n        // Glow for cash pile\n        const cashPileGlowGeometry = new THREE.BoxGeometry(\n          cashPileWidth + 0.6,\n          0.05,\n          cashPileDepth + 0.6\n        );\n        const cashPileGlowMaterial = new THREE.MeshBasicMaterial({\n          color: 0x27ae60,\n          transparent: true,\n          opacity: 0.4,\n        });\n        cashierCashPileGlow = new THREE.Mesh(cashPileGlowGeometry, cashPileGlowMaterial);\n        cashierCashPileGlow.position.set(cashPileX, 0.02, cashPileZ);\n        // scene.add(cashierCashPileGlow);\n\n        // Dollar sign icon above cash pile\n        const dollarIconGroup = new THREE.Group();\n\n        // Dollar sign background circle\n        const circleBg = new THREE.Mesh(\n          new THREE.CylinderGeometry(0.6, 0.6, 0.1, 16),\n          new THREE.MeshStandardMaterial({\n            color: 0x27ae60,\n            roughness: 0.3,\n          })\n        );\n        circleBg.rotation.x = Math.PI / 2;\n        dollarIconGroup.add(circleBg);\n\n        // Dollar sign (simplified)\n        const dollarBarV = new THREE.Mesh(\n          new THREE.BoxGeometry(0.1, 0.5, 0.05),\n          new THREE.MeshStandardMaterial({\n            color: 0xffffff,\n          })\n        );\n        dollarBarV.position.z = 0.08;\n        dollarIconGroup.add(dollarBarV);\n\n        const dollarBarH1 = new THREE.Mesh(\n          new THREE.BoxGeometry(0.3, 0.08, 0.05),\n          new THREE.MeshStandardMaterial({\n            color: 0xffffff,\n          })\n        );\n        dollarBarH1.position.set(0, 0.15, 0.08);\n        dollarIconGroup.add(dollarBarH1);\n\n        const dollarBarH2 = new THREE.Mesh(\n          new THREE.BoxGeometry(0.3, 0.08, 0.05),\n          new THREE.MeshStandardMaterial({\n            color: 0xffffff,\n          })\n        );\n        dollarBarH2.position.set(0, -0.15, 0.08);\n        dollarIconGroup.add(dollarBarH2);\n\n        dollarIconGroup.position.set(cashPileX, 0.8, cashPileZ);\n        dollarIconGroup.userData.baseY = 0.8;\n        // scene.add(dollarIconGroup);\n        cashierCashPileMesh.userData.dollarIcon = dollarIconGroup;\n\n        const maxCanvas = document.createElement('canvas');\n        maxCanvas.width = 128;\n        maxCanvas.height = 64;\n        const maxCtx = maxCanvas.getContext('2d');\n        maxCtx.fillStyle = '#FF0000';\n        maxCtx.font = 'bold 40px Arial';\n        maxCtx.textAlign = 'center';\n        maxCtx.textBaseline = 'middle';\n        maxCtx.lineWidth = 4;\n        maxCtx.strokeStyle = '#FFFFFF';\n        maxCtx.strokeText('MAX', 64, 32);\n        maxCtx.fillText('MAX', 64, 32);\n        const maxTex = new THREE.CanvasTexture(maxCanvas);\n        const maxMat = new THREE.SpriteMaterial({\n          map: maxTex,\n          depthTest: false,\n          transparent: true,\n        });\n        const maxSprite = new THREE.Sprite(maxMat);\n        maxSprite.scale.set(1.5, 0.75, 1);\n        maxSprite.position.set(0, 1.5, 0);\n        maxSprite.visible = false;\n        cashierCashPileMesh.add(maxSprite);\n        cashierCashPileMesh.userData.maxSprite = maxSprite;\n\n        // Store cash pile position in config\n        cashierStallMesh.userData.cashPileX = cashPileX;\n        cashierStallMesh.userData.cashPileZ = cashPileZ;\n        cashierStallMesh.userData.cashPileWidth = cashPileWidth;\n        cashierStallMesh.userData.cashPileDepth = cashPileDepth;\n      }\n\n      function createDriveThruBooth() {\n        const config = window.gameConfig.driveThruBooth;\n        if (!config || config.deleted) return;\n\n        // Main drive-thru booth structure\n        const boothGroup = new THREE.Group();\n\n        // Booth base (elevated window counter)\n        const boothGeometry = new THREE.BoxGeometry(config.width, 2.5, config.depth);\n        const boothMaterial = new THREE.MeshStandardMaterial({\n          color: config.color,\n          roughness: 0.6,\n          metalness: 0.1,\n        });\n        driveThruBoothMesh = new THREE.Mesh(boothGeometry, boothMaterial);\n        driveThruBoothMesh.position.set(config.x, 1.25, config.z);\n        driveThruBoothMesh.castShadow = true;\n        driveThruBoothMesh.receiveShadow = true;\n        scene.add(driveThruBoothMesh);\n        // apply rotation from config if specified (degrees)\n        driveThruBoothMesh.rotation.y = THREE.MathUtils.degToRad(config.rotation || 0);\n\n        // Roof/awning\n        const roofGeometry = new THREE.BoxGeometry(config.width + 2, 0.3, config.depth + 3);\n        const roofMaterial = new THREE.MeshStandardMaterial({\n          color: 0xe74c3c,\n          // Red McDonald's-style roof\n          roughness: 0.5,\n        });\n        const roof = new THREE.Mesh(roofGeometry, roofMaterial);\n        roof.position.set(config.x, 2.65, config.z);\n        roof.castShadow = true;\n        scene.add(roof);\n        driveThruBoothMesh.userData.roof = roof;\n\n        // Service window (opening in the booth)\n        const windowGeometry = new THREE.BoxGeometry(2.5, 1.5, 0.3);\n        const windowMaterial = new THREE.MeshStandardMaterial({\n          color: 0x87ceeb,\n          // Light blue glass\n          roughness: 0.1,\n          metalness: 0.3,\n          transparent: true,\n          opacity: 0.6,\n        });\n        const serviceWindow = new THREE.Mesh(windowGeometry, windowMaterial);\n        serviceWindow.position.set(config.x, 1.8, config.z + config.depth / 2 + 0.15);\n        scene.add(serviceWindow);\n        driveThruBoothMesh.userData.window = serviceWindow;\n\n        // Window frame\n        const frameGeometry = new THREE.BoxGeometry(2.7, 1.7, 0.1);\n        const frameMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffd700,\n          roughness: 0.3,\n        });\n        const windowFrame = new THREE.Mesh(frameGeometry, frameMaterial);\n        windowFrame.position.set(config.x, 1.8, config.z + config.depth / 2 + 0.25);\n        scene.add(windowFrame);\n\n        // Counter/shelf for food\n        const counterGeometry = new THREE.BoxGeometry(2, 0.15, 1);\n        const counterMaterial = new THREE.MeshStandardMaterial({\n          color: 0xe8e8e8,\n          roughness: 0.3,\n          metalness: 0.2,\n        });\n        const counter = new THREE.Mesh(counterGeometry, counterMaterial);\n        counter.position.set(config.x, 1.1, config.z + config.depth / 2 + 0.5);\n        counter.castShadow = true;\n        scene.add(counter);\n        driveThruBoothMesh.userData.counter = counter;\n\n        // Drive-thru sign\n        const signGeometry = new THREE.BoxGeometry(3, 1, 0.2);\n        const signMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffd700,\n          roughness: 0.3,\n          emissive: 0xffaa00,\n          emissiveIntensity: 0.2,\n        });\n        const sign = new THREE.Mesh(signGeometry, signMaterial);\n        sign.position.set(config.x, 3.5, config.z);\n        sign.castShadow = true;\n        scene.add(sign);\n        driveThruBoothMesh.userData.sign = sign;\n\n        // Car icon on sign using emoji\n        const carEmojiCanvas = document.createElement('canvas');\n        carEmojiCanvas.width = 256;\n        carEmojiCanvas.height = 128;\n        const carEmojiCtx = carEmojiCanvas.getContext('2d');\n        carEmojiCtx.font = '80px Arial';\n        carEmojiCtx.textAlign = 'center';\n        carEmojiCtx.textBaseline = 'middle';\n        carEmojiCtx.fillText('\ud83d\ude97', 128, 64);\n\n        const carEmojiTexture = new THREE.CanvasTexture(carEmojiCanvas);\n        carEmojiTexture.minFilter = THREE.LinearFilter;\n\n        const carEmojiGeometry = new THREE.PlaneGeometry(2, 1);\n        const carEmojiMaterial = new THREE.MeshBasicMaterial({\n          map: carEmojiTexture,\n          transparent: true,\n          side: THREE.DoubleSide,\n          depthWrite: false,\n        });\n        const carEmojiMesh = new THREE.Mesh(carEmojiGeometry, carEmojiMaterial);\n        carEmojiMesh.position.set(config.x, 3.5, config.z + 0.15);\n        scene.add(carEmojiMesh);\n        driveThruBoothMesh.userData.carIcon = carEmojiMesh;\n\n        // Order speaker box\n        const speakerGeometry = new THREE.BoxGeometry(0.6, 1.2, 0.4);\n        const speakerMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2c3e50,\n          roughness: 0.5,\n        });\n        const speaker = new THREE.Mesh(speakerGeometry, speakerMaterial);\n        speaker.position.set(config.x - config.width / 2 - 3, 0.6, config.z + 5);\n        speaker.castShadow = true;\n        scene.add(speaker);\n        driveThruBoothMesh.userData.speaker = speaker;\n\n        // Speaker pole\n        const poleGeometry = new THREE.CylinderGeometry(0.1, 0.1, 1.5, 8);\n        const poleMaterial = new THREE.MeshStandardMaterial({\n          color: 0x333333,\n          roughness: 0.6,\n        });\n        const pole = new THREE.Mesh(poleGeometry, poleMaterial);\n        pole.position.set(config.x - config.width / 2 - 3, 0.75, config.z + 5);\n        scene.add(pole);\n\n        // Speaker screen\n        const screenGeometry = new THREE.BoxGeometry(0.4, 0.5, 0.05);\n        const screenMaterial = new THREE.MeshStandardMaterial({\n          color: 0x00ff00,\n          emissive: 0x00aa00,\n          emissiveIntensity: 0.5,\n          roughness: 0.2,\n        });\n        const screen = new THREE.Mesh(screenGeometry, screenMaterial);\n        screen.position.set(config.x - config.width / 2 - 3, 0.9, config.z + 5.23);\n        scene.add(screen);\n\n        // Glow zone for player deposit area (inside restaurant, near the wall opening)\n        const glowGeometry = new THREE.BoxGeometry(config.width + 1, 0.05, config.depth + 1);\n        const glowMaterial = new THREE.MeshBasicMaterial({\n          color: 0xf39c12,\n          transparent: true,\n          opacity: 0.4,\n        });\n        driveThruBoothGlow = new THREE.Mesh(glowGeometry, glowMaterial);\n        // Position the deposit zone inside the restaurant near the wall opening\n        const depositZoneX = RESTAURANT_WIDTH / 2 - 3;\n        // Just inside the right wall\n        driveThruBoothGlow.position.set(depositZoneX, 0.02, config.z);\n        scene.add(driveThruBoothGlow);\n\n        // Create a service window/counter on the inside of the wall for players to interact\n        const insideCounterGeometry = new THREE.BoxGeometry(3, 1.2, 2);\n        const insideCounterMaterial = new THREE.MeshStandardMaterial({\n          color: 0x8b4513,\n          roughness: 0.6,\n        });\n        const insideCounter = new THREE.Mesh(insideCounterGeometry, insideCounterMaterial);\n        insideCounter.position.set(depositZoneX, 0.6, config.z);\n        insideCounter.castShadow = true;\n        scene.add(insideCounter);\n        driveThruBoothMesh.userData.insideCounter = insideCounter;\n\n        // Service window opening through the wall (visual indicator)\n        const windowOpeningGeometry = new THREE.BoxGeometry(0.6, 1.2, 2.5);\n        const windowOpeningMaterial = new THREE.MeshStandardMaterial({\n          color: 0x87ceeb,\n          roughness: 0.1,\n          transparent: true,\n          opacity: 0.4,\n        });\n        const windowOpening = new THREE.Mesh(windowOpeningGeometry, windowOpeningMaterial);\n        windowOpening.position.set(RESTAURANT_WIDTH / 2, 1.2, config.z);\n        scene.add(windowOpening);\n\n        // Road/lane for cars (outside the restaurant)\n        const roadGeometry = new THREE.BoxGeometry(6, 0.1, 90);\n        const roadMaterial = new THREE.MeshStandardMaterial({\n          color: 0x333333,\n          roughness: 0.9,\n        });\n        const road = new THREE.Mesh(roadGeometry, roadMaterial);\n        road.position.set(config.x, 0.05, 0);\n        road.receiveShadow = true;\n        scene.add(road);\n        driveThruBoothMesh.userData.road = road;\n\n        // Road markings (dashed lines)\n        const dashGeometry = new THREE.BoxGeometry(0.3, 0.11, 1);\n        const dashMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffffff,\n          roughness: 0.5,\n        });\n        for (let i = 0; i < 20; i++) {\n          const dash = new THREE.Mesh(dashGeometry, dashMaterial);\n          dash.position.set(config.x - 2.5, 0.06, -40 + i * 4);\n          scene.add(dash);\n\n          const dash2 = new THREE.Mesh(dashGeometry, dashMaterial);\n          dash2.position.set(config.x + 2.5, 0.06, -40 + i * 4);\n          scene.add(dash2);\n        }\n\n        // Cash pile platform for drive-thru earnings (inside restaurant for player access)\n        const dtCashPileX = RESTAURANT_WIDTH / 2 - 6;\n        // Inside restaurant\n        const dtCashPileZ = config.z + 4;\n        const dtCashPileWidth = 3;\n        const dtCashPileDepth = 2.5;\n\n        const dtCashPileGeometry = new THREE.BoxGeometry(dtCashPileWidth, 0.3, dtCashPileDepth);\n        const dtCashPileMaterial = new THREE.MeshStandardMaterial({\n          color: 0xe8c4a0,\n          roughness: 0.6,\n          metalness: 0.1,\n          opacity: 0,\n          transparent: true,\n        });\n        driveThruCashPileMesh = new THREE.Mesh(dtCashPileGeometry, dtCashPileMaterial);\n        driveThruCashPileMesh.position.set(dtCashPileX, 0.15, dtCashPileZ);\n        // driveThruCashPileMesh.receiveShadow = true;\n        // driveThruCashPileMesh.castShadow = true;\n        scene.add(driveThruCashPileMesh);\n\n        // Border for drive-thru cash pile\n        const dtCashBorderGeometry = new THREE.BoxGeometry(\n          dtCashPileWidth + 0.3,\n          0.25,\n          dtCashPileDepth + 0.3\n        );\n        const dtCashBorderMaterial = new THREE.MeshStandardMaterial({\n          color: 0xd4a574,\n          roughness: 0.7,\n        });\n        const dtCashBorder = new THREE.Mesh(dtCashBorderGeometry, dtCashBorderMaterial);\n        dtCashBorder.position.set(dtCashPileX, 0.125, dtCashPileZ);\n        // scene.add(dtCashBorder);\n        driveThruCashPileMesh.userData.borderMesh = dtCashBorder;\n\n        // Glow for drive-thru cash pile\n        const dtCashGlowGeometry = new THREE.BoxGeometry(\n          dtCashPileWidth + 0.6,\n          0.05,\n          dtCashPileDepth + 0.6\n        );\n        const dtCashGlowMaterial = new THREE.MeshBasicMaterial({\n          color: 0xf39c12,\n          transparent: true,\n          opacity: 0.4,\n        });\n        driveThruCashPileGlow = new THREE.Mesh(dtCashGlowGeometry, dtCashGlowMaterial);\n        driveThruCashPileGlow.position.set(dtCashPileX, 0.02, dtCashPileZ);\n        // scene.add(driveThruCashPileGlow);\n\n        const maxCanvas = document.createElement('canvas');\n        maxCanvas.width = 128;\n        maxCanvas.height = 64;\n        const maxCtx = maxCanvas.getContext('2d');\n        maxCtx.fillStyle = '#FF0000';\n        maxCtx.font = 'bold 40px Arial';\n        maxCtx.textAlign = 'center';\n        maxCtx.textBaseline = 'middle';\n        maxCtx.lineWidth = 4;\n        maxCtx.strokeStyle = '#FFFFFF';\n        maxCtx.strokeText('MAX', 64, 32);\n        maxCtx.fillText('MAX', 64, 32);\n        const maxTex = new THREE.CanvasTexture(maxCanvas);\n        const maxMat = new THREE.SpriteMaterial({\n          map: maxTex,\n          depthTest: false,\n          transparent: true,\n        });\n        const maxSprite = new THREE.Sprite(maxMat);\n        maxSprite.scale.set(1.5, 0.75, 1);\n        maxSprite.position.set(0, 1.5, 0);\n        maxSprite.visible = false;\n        driveThruCashPileMesh.add(maxSprite);\n        driveThruCashPileMesh.userData.maxSprite = maxSprite;\n\n        // Store cash pile positions\n        driveThruBoothMesh.userData.cashPileX = dtCashPileX;\n        driveThruBoothMesh.userData.cashPileZ = dtCashPileZ;\n        driveThruBoothMesh.userData.cashPileWidth = dtCashPileWidth;\n        driveThruBoothMesh.userData.cashPileDepth = dtCashPileDepth;\n      }\n\n      function createCarMesh() {\n        const carGroup = new THREE.Group();\n\n        // Random car colors\n        const carColors = [0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6, 0x1abc9c, 0x34495e];\n        const carColor = carColors[Math.floor(Math.random() * carColors.length)];\n\n        // Car body\n        const bodyGeometry = new THREE.BoxGeometry(2, 0.8, 4);\n        const bodyMaterial = new THREE.MeshStandardMaterial({\n          color: carColor,\n          roughness: 0.3,\n          metalness: 0.4,\n        });\n        const body = new THREE.Mesh(bodyGeometry, bodyMaterial);\n        body.position.y = 0.6;\n        body.castShadow = true;\n        carGroup.add(body);\n\n        // Car cabin/roof\n        const cabinGeometry = new THREE.BoxGeometry(1.6, 0.6, 2);\n        const cabinMaterial = new THREE.MeshStandardMaterial({\n          color: carColor,\n          roughness: 0.3,\n          metalness: 0.4,\n        });\n        const cabin = new THREE.Mesh(cabinGeometry, cabinMaterial);\n        cabin.position.set(0, 1.3, -0.3);\n        cabin.castShadow = true;\n        carGroup.add(cabin);\n\n        // Windows\n        const windowMaterial = new THREE.MeshStandardMaterial({\n          color: 0x87ceeb,\n          roughness: 0.1,\n          metalness: 0.3,\n          transparent: true,\n          opacity: 0.7,\n        });\n\n        // Front windshield\n        const frontWindowGeometry = new THREE.BoxGeometry(1.4, 0.5, 0.1);\n        const frontWindow = new THREE.Mesh(frontWindowGeometry, windowMaterial);\n        frontWindow.position.set(0, 1.25, 0.65);\n        frontWindow.rotation.x = -0.3;\n        carGroup.add(frontWindow);\n\n        // Side windows\n        const sideWindowGeometry = new THREE.BoxGeometry(0.1, 0.4, 1.2);\n        const leftWindow = new THREE.Mesh(sideWindowGeometry, windowMaterial);\n        leftWindow.position.set(-0.75, 1.25, -0.3);\n        carGroup.add(leftWindow);\n\n        const rightWindow = new THREE.Mesh(sideWindowGeometry, windowMaterial);\n        rightWindow.position.set(0.75, 1.25, -0.3);\n        carGroup.add(rightWindow);\n\n        // Wheels\n        const wheelGeometry = new THREE.CylinderGeometry(0.35, 0.35, 0.2, 12);\n        const wheelMaterial = new THREE.MeshStandardMaterial({\n          color: 0x222222,\n          roughness: 0.8,\n        });\n\n        const wheelPositions = [\n          [-0.9, 0.35, 1.2],\n          [0.9, 0.35, 1.2],\n          [-0.9, 0.35, -1.2],\n          [0.9, 0.35, -1.2],\n        ];\n\n        wheelPositions.forEach((pos) => {\n          const wheel = new THREE.Mesh(wheelGeometry, wheelMaterial);\n          wheel.position.set(pos[0], pos[1], pos[2]);\n          wheel.rotation.z = Math.PI / 2;\n          wheel.castShadow = true;\n          carGroup.add(wheel);\n        });\n\n        // Headlights\n        const headlightGeometry = new THREE.CylinderGeometry(0.15, 0.15, 0.1, 8);\n        const headlightMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffffaa,\n          emissive: 0xffff00,\n          emissiveIntensity: 0.3,\n          roughness: 0.2,\n        });\n\n        const leftHeadlight = new THREE.Mesh(headlightGeometry, headlightMaterial);\n        leftHeadlight.position.set(-0.6, 0.5, 2);\n        leftHeadlight.rotation.x = Math.PI / 2;\n        carGroup.add(leftHeadlight);\n\n        const rightHeadlight = new THREE.Mesh(headlightGeometry, headlightMaterial);\n        rightHeadlight.position.set(0.6, 0.5, 2);\n        rightHeadlight.rotation.x = Math.PI / 2;\n        carGroup.add(rightHeadlight);\n\n        // Tail lights\n        const tailLightGeometry = new THREE.BoxGeometry(0.3, 0.15, 0.1);\n        const tailLightMaterial = new THREE.MeshStandardMaterial({\n          color: 0xff0000,\n          emissive: 0xff0000,\n          emissiveIntensity: 0.3,\n          roughness: 0.2,\n        });\n\n        const leftTail = new THREE.Mesh(tailLightGeometry, tailLightMaterial);\n        leftTail.position.set(-0.7, 0.5, -2);\n        carGroup.add(leftTail);\n\n        const rightTail = new THREE.Mesh(tailLightGeometry, tailLightMaterial);\n        rightTail.position.set(0.7, 0.5, -2);\n        carGroup.add(rightTail);\n\n        // Initialize squish state\n        carGroup.userData.squishState = {\n          scaleY: 1,\n          scaleXZ: 1,\n          targetScaleY: 1,\n          targetScaleXZ: 1,\n        };\n\n        return carGroup;\n      }\n\n      function spawnDriveThruCar() {\n        const config = window.gameConfig.driveThruBooth;\n        if (!config || config.deleted) return;\n\n        const mesh = createCarMesh();\n\n        // Spawn off-screen from the right\n        const startX = config.x;\n        const startZ = RESTAURANT_DEPTH / 2 + 10;\n\n        mesh.position.set(startX, 0, startZ);\n        mesh.rotation.y = Math.PI;\n        // Face toward the booth\n        scene.add(mesh);\n\n        // Calculate position in queue\n        const queuePosition = driveThruCarQueue.filter(\n          (c) => c.state === 'waiting' || c.state === 'driving'\n        ).length;\n        const targetZ = config.z + config.depth / 2 + 3 + queuePosition * CAR_SPACING;\n\n        const car = {\n          mesh: mesh,\n          state: 'driving',\n          targetZ: targetZ,\n          queuePosition: queuePosition,\n          waitTime: 0,\n          satisfied: false,\n        };\n\n        driveThruCarQueue.push(car);\n      }\n\n      function removeDriveThruCar(car) {\n        car.state = 'leaving';\n        car.targetZ = -RESTAURANT_DEPTH / 2 - 15;\n      }\n\n      function addBurgerToDriveThru(menuItem = null) {\n        const config = window.gameConfig.driveThruBooth;\n        if (driveThruBurgerStack.length >= config.maxBurgers) return false;\n\n        const food = createStackItem(menuItem);\n\n        // Stack burgers in a grid on the counter\n        const stackIndex = driveThruBurgerStack.length;\n        const col = stackIndex % 3;\n        const row = Math.floor((stackIndex % 9) / 3);\n        const layer = Math.floor(stackIndex / 9);\n\n        const counterX = config.x;\n        const counterZ = config.z + config.depth / 2 + 0.5;\n\n        food.position.set(\n          counterX - 0.6 + col * 0.6,\n          1.2 + layer * 0.25,\n          counterZ - 0.3 + row * 0.3\n        );\n\n        food.rotation.y = (Math.random() - 0.5) * 0.3;\n\n        scene.add(food);\n        driveThruBurgerStack.push(food);\n\n        return true;\n      }\n\n      function removeBurgerFromDriveThru() {\n        if (driveThruBurgerStack.length === 0) return null;\n\n        const food = driveThruBurgerStack.pop();\n        const menuItem = food.userData.menuItem;\n        scene.remove(food);\n\n        food.traverse((child) => {\n          if (child.geometry) child.geometry.dispose();\n          if (child.material) {\n            if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n            else child.material.dispose();\n          }\n        });\n\n        return menuItem;\n      }\n\n      function addToDriveThruCashPile() {\n        const cashPileX = driveThruBoothMesh.userData.cashPileX;\n        const cashPileZ = driveThruBoothMesh.userData.cashPileZ;\n        const cashPileWidth = driveThruBoothMesh.userData.cashPileWidth;\n        const cashPileDepth = driveThruBoothMesh.userData.cashPileDepth;\n\n        const stackIndex = driveThruCashPile.length;\n        const cols = 3;\n        const rows = 3;\n        const itemsPerLayer = cols * rows;\n        const col = stackIndex % cols;\n        const row = Math.floor((stackIndex % itemsPerLayer) / cols);\n        const layer = Math.floor(stackIndex / itemsPerLayer);\n\n        const cellWidth = (cashPileWidth - 0.3) / cols;\n        const cellDepth = (cashPileDepth - 0.2) / rows;\n\n        const x = cashPileX - (cashPileWidth - 0.3) / 2 + cellWidth / 2 + col * cellWidth;\n        const z = cashPileZ - (cashPileDepth - 0.2) / 2 + cellDepth / 2 + row * cellDepth;\n        const y = 0.25 + layer * 0.12;\n\n        return {\n          x,\n          y,\n          z,\n        };\n      }\n\n      function createDriveThruCashPileMoney(targetX, targetY, targetZ) {\n        const moneyGroup = new THREE.Group();\n\n        const asset = lib.getAsset('paper_money');\n        if (asset && asset.url) {\n          const geom = getCachedGeometry(\n            'paperMoneyBoxGeom',\n            () => new THREE.BoxGeometry(1.0, 0.15, 0.6)\n          );\n          const topMat = getCachedMaterial(\n            'paperMoneyTopMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                map: new THREE.TextureLoader().load(asset.url),\n                transparent: true,\n                alphaTest: 0.1,\n              })\n          );\n          const sideMat = getCachedMaterial(\n            'paperMoneySideMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x85bb65,\n              })\n          );\n          const mesh = new THREE.Mesh(geom, [sideMat, sideMat, topMat, sideMat, sideMat, sideMat]);\n          mesh.position.y = 0.075;\n          mesh.castShadow = true;\n          moneyGroup.add(mesh);\n        } else {\n          const stackGeom = getCachedGeometry(\n            'cpStackGeom',\n            () => new THREE.BoxGeometry(0.85, 0.1, 0.5)\n          );\n          const stackMat = getCachedMaterial(\n            'moneyStackMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x85bb65,\n                roughness: 0.6,\n                metalness: 0.1,\n              })\n          );\n          const stack = new THREE.Mesh(stackGeom, stackMat);\n          stack.position.y = 0.05;\n          stack.castShadow = true;\n          moneyGroup.add(stack);\n\n          const bandGeom = getCachedGeometry(\n            'cpBandGeom',\n            () => new THREE.BoxGeometry(0.87, 0.05, 0.12)\n          );\n          const bandMat = getCachedMaterial(\n            'moneyBandMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0xffffff,\n                roughness: 0.4,\n              })\n          );\n          const band = new THREE.Mesh(bandGeom, bandMat);\n          band.position.y = 0.05;\n          moneyGroup.add(band);\n        }\n\n        moneyGroup.position.set(targetX, targetY, targetZ);\n        moneyGroup.rotation.y = 0;\n        moneyGroup.castShadow = true;\n\n        moneyGroup.userData.isCashPileMoney = true;\n        moneyGroup.userData.collected = false;\n\n        scene.add(moneyGroup);\n        driveThruCashPile.push(moneyGroup);\n\n        return moneyGroup;\n      }\n\n      function updateDriveThruBooth(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.driveThruBooth;\n        if (!config || config.deleted || !driveThruBoothMesh) return;\n\n        // Check if player is in drive-thru deposit zone\n        const depositZoneX = RESTAURANT_WIDTH / 2 - 3;\n        // Positioned inside the right wall\n        const depositZoneZ = config.z;\n        const inZoneX = Math.abs(player.position.x - depositZoneX) < 2;\n        // Match the glow size\n        const inZoneZ = Math.abs(player.position.z - depositZoneZ) < 2;\n        isInDriveThruZone = inZoneX && inZoneZ;\n\n        // Deposit burgers when in zone\n        if (\n          isInDriveThruZone &&\n          currentStackCount > 0 &&\n          driveThruBurgerStack.length < config.maxBurgers\n        ) {\n          driveThruDepositTimer += deltaTime;\n\n          if (driveThruDepositTimer >= DRIVE_THRU_DEPOSIT_DELAY) {\n            driveThruDepositTimer = 0;\n            window.gameConfig.tutorialHasSeenDriveThruHint = true;\n\n            if (playerStack.length > 0) {\n              const item = playerStack.pop();\n              const menuItem = item.userData.menuItem;\n              player.remove(item);\n              currentStackCount = playerStack.length;\n              updateStackUI();\n\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n\n              const counterX = config.x;\n              const targetPos = {\n                x: counterX,\n                y: 1.3,\n                z: config.z + config.depth / 2 + 0.5,\n              };\n\n              createFlyingItem(\n                startPos,\n                targetPos,\n                'burger',\n                () => {\n                  addBurgerToDriveThru(menuItem);\n                  playSound('pickup_sound');\n                },\n                true\n              );\n\n              item.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                  else child.material.dispose();\n                }\n              });\n            }\n          }\n        } else {\n          driveThruDepositTimer = 0;\n        }\n\n        // Animate glow\n        if (driveThruBoothGlow) {\n          const pulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          driveThruBoothGlow.scale.set(pulse, 1, pulse);\n\n          if (driveThruBurgerStack.length === 0) {\n            driveThruBoothGlow.material.color.setHex(0xe74c3c);\n          } else if (driveThruBurgerStack.length >= config.maxBurgers) {\n            driveThruBoothGlow.material.color.setHex(0xf39c12);\n          } else {\n            driveThruBoothGlow.material.color.setHex(0x27ae60);\n          }\n        }\n\n        // Animate sign/car icon\n        const carIcon = driveThruBoothMesh?.userData?.carIcon;\n        if (carIcon) {\n          carIcon.position.y = 3.5 + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n\n        // Animate cash pile glow\n        if (driveThruCashPileGlow) {\n          const cashPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          driveThruCashPileGlow.scale.set(cashPulse, 1, cashPulse);\n\n          if (driveThruCashPile.length > 0) {\n            driveThruCashPileGlow.material.color.setHex(0xffd700);\n            driveThruCashPileGlow.material.opacity = 0.6;\n          } else {\n            driveThruCashPileGlow.material.color.setHex(0xf39c12);\n            driveThruCashPileGlow.material.opacity = 0.3;\n          }\n        }\n\n        const maxMoney =\n          (config.maxMoney || 50) + (window.gameConfig.vaultUpgrades?.cashpile || 0) * 20;\n        if (driveThruCashPileMesh && driveThruCashPileMesh.userData.maxSprite) {\n          const isMax = driveThruCashPile.length >= maxMoney;\n          driveThruCashPileMesh.userData.maxSprite.visible = isMax;\n          if (isMax) {\n            const layer = Math.floor(driveThruCashPile.length / 9);\n            driveThruCashPileMesh.userData.maxSprite.position.y =\n              0.5 + layer * 0.12 + Math.sin(Date.now() * 0.005) * 0.1;\n          }\n        }\n      }\n\n      function updateDriveThruCashPile(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        if (\n          !window.gameConfig.driveThruBooth ||\n          window.gameConfig.driveThruBooth.deleted ||\n          !driveThruBoothMesh\n        )\n          return;\n\n        const cashPileX = driveThruBoothMesh.userData.cashPileX;\n        const cashPileZ = driveThruBoothMesh.userData.cashPileZ;\n        const cashPileWidth = driveThruBoothMesh.userData.cashPileWidth || 3;\n        const cashPileDepth = driveThruBoothMesh.userData.cashPileDepth || 2.5;\n\n        const playerX = player.position.x;\n        const playerY = player.position.y;\n        const playerZ = player.position.z;\n\n        // Check if player is in drive-thru cash pile zone\n        const inZoneX = Math.abs(playerX - cashPileX) < cashPileWidth / 2 + 1;\n        const inZoneZ = Math.abs(playerZ - cashPileZ) < cashPileDepth / 2 + 1;\n        isInDriveThruCashPileZone = inZoneX && inZoneZ;\n\n        if (isInDriveThruCashPileZone && driveThruCashPile.length > 0) {\n          let collectedCount = 0;\n          while (driveThruCashPile.length > 0) {\n            const money = driveThruCashPile.pop();\n\n            const startPos = {\n              x: money.position.x,\n              y: money.position.y + 0.3,\n              z: money.position.z,\n            };\n\n            const targetPos = {\n              x: playerX,\n              y: playerY + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n              z: playerZ,\n            };\n\n            const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n\n            createFlyingItem(\n              startPos,\n              targetPos,\n              'money',\n              () => {\n                modifyVaultBalance(moneyValue);\n                if (collectedCount === 0) playSound('pickup_sound');\n              },\n              true\n            );\n\n            scene.remove(money);\n            money.traverse((child) => {\n              if (child.geometry) child.geometry.dispose();\n              if (child.material) {\n                if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                else child.material.dispose();\n              }\n            });\n            collectedCount++;\n          }\n          if (collectedCount > 0) playSound('pickup_sound');\n        }\n      }\n\n      function updateDriveThruCarQueue(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.driveThruBooth;\n        if (!config || config.deleted) return;\n\n        // Spawn new cars\n        driveThruCarSpawnTimer += deltaTime;\n        let activeCars = driveThruCarQueue.filter(\n          (c) => c.state === 'waiting' || c.state === 'driving'\n        ).length;\n\n        const currentSpawnRate = isRushHour ? config.carSpawnRate / 2 : config.carSpawnRate;\n        const maxCars = isRushHour ? config.maxCars + 2 : config.maxCars;\n\n        if (driveThruCarSpawnTimer >= currentSpawnRate && activeCars < maxCars) {\n          driveThruCarSpawnTimer = 0;\n          spawnDriveThruCar();\n        }\n\n        const now = Date.now();\n\n        // Update each car\n        for (let i = driveThruCarQueue.length - 1; i >= 0; i--) {\n          const car = driveThruCarQueue[i];\n          const mesh = car.mesh;\n          const squish = mesh.userData.squishState;\n\n          if (car.state === 'driving') {\n            const dz = car.targetZ - mesh.position.z;\n\n            if (Math.abs(dz) > 0.1) {\n              const speed = 8;\n              mesh.position.z += Math.sign(dz) * speed * deltaTime;\n\n              // Squish while moving\n              const moveCycle = Math.sin(now * 0.008) * 0.5 + 0.5;\n              squish.targetScaleY = 0.95 + moveCycle * 0.1;\n              squish.targetScaleXZ = 1.05 - moveCycle * 0.1;\n            } else {\n              car.state = 'waiting';\n              mesh.position.z = car.targetZ;\n              // Landing squish\n              squish.scaleY = 0.9;\n              squish.scaleXZ = 1.1;\n            }\n          } else if (car.state === 'waiting') {\n            // Idle bounce\n            mesh.position.y = Math.sin(now * 0.003 + i) * 0.02;\n\n            const breathe = Math.sin(now * 0.004 + i * 0.5);\n            squish.targetScaleY = 1 + breathe * 0.02;\n            squish.targetScaleXZ = 1 - breathe * 0.015;\n\n            car.waitTime += deltaTime;\n          } else if (car.state === 'served') {\n            car.waitTime += deltaTime;\n\n            if (car.waitTime > 0.5) {\n              removeDriveThruCar(car);\n            }\n          } else if (car.state === 'leaving') {\n            const dz = car.targetZ - mesh.position.z;\n            const speed = 12;\n            mesh.position.z += Math.sign(dz) * speed * deltaTime;\n\n            // Fast exit squish\n            squish.targetScaleY = 0.9;\n            squish.targetScaleXZ = 1.1;\n\n            if (mesh.position.z < -RESTAURANT_DEPTH / 2 - 10) {\n              scene.remove(mesh);\n              mesh.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                  else child.material.dispose();\n                }\n              });\n              driveThruCarQueue.splice(i, 1);\n            }\n          }\n\n          // Apply squish\n          const squishSpeed = 10 * deltaTime;\n          squish.scaleY += (squish.targetScaleY - squish.scaleY) * squishSpeed;\n          squish.scaleXZ += (squish.targetScaleXZ - squish.scaleXZ) * squishSpeed;\n          mesh.scale.set(squish.scaleXZ, squish.scaleY, squish.scaleXZ);\n        }\n\n        // Serve first waiting car\n        const canServe =\n          isInDriveThruZone ||\n          (window.gameConfig.staffHireTile && window.gameConfig.staffHireTile.staffHired);\n\n        let firstWaitingCar = null;\n        for (let i = 0; i < driveThruCarQueue.length; i++) {\n          if (\n            driveThruCarQueue[i].state === 'waiting' &&\n            driveThruCarQueue[i].queuePosition === 0\n          ) {\n            firstWaitingCar = driveThruCarQueue[i];\n            break;\n          }\n        }\n\n        const maxMoney =\n          (config.maxMoney || 50) + (window.gameConfig.vaultUpgrades?.cashpile || 0) * 20;\n        if (\n          firstWaitingCar &&\n          driveThruBurgerStack.length > 0 &&\n          canServe &&\n          driveThruCashPile.length < maxMoney\n        ) {\n          driveThruServeTimer += deltaTime;\n\n          // Calculate dynamic serve time based on vault upgrade\n          const vaultUpgrades = window.gameConfig.vaultUpgrades || {};\n          const serveBonus = 1 - (vaultUpgrades.serve || 0) * 0.1;\n          const currentServeTime = Math.max(0.1, config.serveTime * serveBonus);\n\n          if (driveThruServeTimer >= currentServeTime) {\n            driveThruServeTimer = 0;\n\n            const servedMenuItem = removeBurgerFromDriveThru();\n\n            firstWaitingCar.state = 'served';\n            firstWaitingCar.waitTime = 0;\n            firstWaitingCar.satisfied = true;\n\n            // Calculate payment\n            const vipConfig = window.gameConfig.vipCustomers || {\n              tipMultiplier: 3,\n            };\n            const rushMultiplier = getRushHourTipMultiplier();\n            const prestigeMultiplier = getPrestigeProfitMultiplier();\n            const tipMultiplier = rushMultiplier * prestigeMultiplier;\n            const basePrice =\n              servedMenuItem && servedMenuItem.value\n                ? servedMenuItem.value\n                : window.gameConfig.resources.itemValue;\n            const itemPrice = basePrice * tipMultiplier;\n            const paperMoneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n            const billCount = Math.ceil(itemPrice / paperMoneyValue);\n\n            // Spawn money bills\n            for (let billIndex = 0; billIndex < billCount; billIndex++) {\n              setTimeout(() => {\n                const cashPilePos = addToDriveThruCashPile();\n\n                const offsetX = (Math.random() - 0.5) * 0.5;\n                const offsetZ = (Math.random() - 0.5) * 0.5;\n\n                const moneyStartPos = {\n                  x: firstWaitingCar.mesh.position.x + offsetX,\n                  y: 1.5 + billIndex * 0.15,\n                  z: firstWaitingCar.mesh.position.z + offsetZ,\n                };\n                const moneyTargetPos = {\n                  x: cashPilePos.x,\n                  y: cashPilePos.y,\n                  z: cashPilePos.z,\n                };\n\n                createFlyingItem(\n                  moneyStartPos,\n                  moneyTargetPos,\n                  'money',\n                  () => {\n                    createDriveThruCashPileMoney(cashPilePos.x, cashPilePos.y, cashPilePos.z);\n                  },\n                  true\n                );\n\n                if (billIndex === 0) {\n                  playSound('sell_sound');\n                }\n              }, billIndex * 80);\n            }\n\n            // Move other cars forward\n            for (let j = 0; j < driveThruCarQueue.length; j++) {\n              const c = driveThruCarQueue[j];\n              if (c !== firstWaitingCar && (c.state === 'waiting' || c.state === 'driving')) {\n                c.queuePosition--;\n                c.targetZ = config.z + config.depth / 2 + 3 + c.queuePosition * CAR_SPACING;\n                c.state = 'driving';\n              }\n            }\n          }\n        } else {\n          driveThruServeTimer = 0;\n        }\n      }\n\n      function createTrashBin() {\n        const config = window.gameConfig.trashBin;\n        if (!config || config.deleted) return;\n\n        const x = config.x || 18;\n        const z = config.z || 5;\n        const radius = config.radius || 2;\n\n        // Create simple bucket trash bin\n        const binGroup = new THREE.Group();\n\n        // Simple bucket body (wider at top, narrower at bottom)\n        const binGeometry = new THREE.CylinderGeometry(1.0, 0.7, 1.8, 16);\n        const binMaterial = new THREE.MeshStandardMaterial({\n          color: 0x808080,\n          // Simple gray\n          roughness: 0.7,\n          metalness: 0.1,\n        });\n        const binBody = new THREE.Mesh(binGeometry, binMaterial);\n        binBody.position.y = 0.9;\n        binBody.castShadow = true;\n        binBody.receiveShadow = true;\n        binGroup.add(binBody);\n\n        // Inner darkness (to show it's hollow)\n        const innerGeometry = new THREE.CylinderGeometry(0.9, 0.6, 0.1, 16);\n        const innerMaterial = new THREE.MeshStandardMaterial({\n          color: 0x333333,\n          roughness: 0.9,\n        });\n        const inner = new THREE.Mesh(innerGeometry, innerMaterial);\n        inner.position.y = 1.764;\n        binGroup.add(inner);\n\n        binGroup.position.set(x, 0, z);\n        scene.add(binGroup);\n        // apply rotation from config if specified (degrees)\n        binGroup.rotation.y = THREE.MathUtils.degToRad(config.rotation || 0);\n        trashBinMesh = binGroup;\n\n        // Glow ring around bin\n        const glowGeometry = new THREE.RingGeometry(radius - 0.3, radius, 32);\n        const glowMaterial = new THREE.MeshBasicMaterial({\n          color: config.color || 0x7f8c8d,\n          side: THREE.DoubleSide,\n          transparent: true,\n          opacity: 0.5,\n        });\n        trashBinGlow = new THREE.Mesh(glowGeometry, glowMaterial);\n        trashBinGlow.rotation.x = -Math.PI / 2;\n        trashBinGlow.position.set(x, 0.02, z);\n        scene.add(trashBinGlow);\n\n        // Create floating \ud83d\uddd1\ufe0f emoji using canvas texture\n        const emojiCanvas = document.createElement('canvas');\n        emojiCanvas.width = 128;\n        emojiCanvas.height = 128;\n        const emojiCtx = emojiCanvas.getContext('2d');\n        emojiCtx.font = '100px Arial';\n        emojiCtx.textAlign = 'center';\n        emojiCtx.textBaseline = 'middle';\n        emojiCtx.fillText('\ud83d\uddd1\ufe0f', 64, 64);\n\n        const emojiTexture = new THREE.CanvasTexture(emojiCanvas);\n        emojiTexture.minFilter = THREE.LinearFilter;\n\n        const emojiGeometry = new THREE.PlaneGeometry(1.5, 1.5);\n        const emojiMaterial = new THREE.MeshBasicMaterial({\n          map: emojiTexture,\n          transparent: true,\n          side: THREE.DoubleSide,\n          depthWrite: false,\n        });\n        const emojiMesh = new THREE.Mesh(emojiGeometry, emojiMaterial);\n        emojiMesh.position.set(x, 2.5, z);\n        emojiMesh.userData.baseY = 2.5;\n        scene.add(emojiMesh);\n\n        trashBinMesh.userData.trashIcon = emojiMesh;\n      }\n\n      function createBankVault() {\n        const config = window.gameConfig.bankVault;\n        if (!config || config.deleted) return;\n\n        // Only show vault if unlocked\n        const vaultTileConfig = window.gameConfig.vaultTile;\n        if (vaultTileConfig && !vaultTileConfig.unlocked) return;\n\n        const vaultToggle = document.getElementById('vault-toggle');\n        if (vaultToggle) vaultToggle.style.display = 'flex';\n\n        const x = config.x;\n        const z = config.z;\n\n        // Main vault building\n        const vaultGroup = new THREE.Group();\n\n        // Vault base (solid foundation)\n        const baseGeometry = new THREE.BoxGeometry(config.width + 0.5, 0.3, config.depth + 0.5);\n        const baseMaterial = new THREE.MeshStandardMaterial({\n          color: 0x1a252f,\n          roughness: 0.3,\n          metalness: 0.5,\n        });\n        const base = new THREE.Mesh(baseGeometry, baseMaterial);\n        base.position.y = 0.15;\n        base.castShadow = true;\n        base.receiveShadow = true;\n        vaultGroup.add(base);\n\n        // Vault body (main structure)\n        const bodyGeometry = new THREE.BoxGeometry(config.width, 2.5, config.depth);\n        const bodyMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2c3e50,\n          roughness: 0.4,\n          metalness: 0.3,\n        });\n        const body = new THREE.Mesh(bodyGeometry, bodyMaterial);\n        body.position.y = 1.55;\n        body.castShadow = true;\n        body.receiveShadow = true;\n        vaultGroup.add(body);\n\n        // Vault door (circular, bank-style)\n        const doorGeometry = new THREE.CylinderGeometry(1.2, 1.2, 0.3, 32);\n        const doorMaterial = new THREE.MeshStandardMaterial({\n          color: 0x7f8c8d,\n          roughness: 0.2,\n          metalness: 0.8,\n        });\n        const door = new THREE.Mesh(doorGeometry, doorMaterial);\n        door.rotation.x = Math.PI / 2;\n        door.position.set(0, 1.5, config.depth / 2 + 0.1);\n        door.castShadow = true;\n        vaultGroup.add(door);\n\n        // Door handle (wheel)\n        const handleGeometry = new THREE.TorusGeometry(0.5, 0.08, 8, 16);\n        const handleMaterial = new THREE.MeshStandardMaterial({\n          color: 0xf1c40f,\n          roughness: 0.2,\n          metalness: 0.7,\n        });\n        const handle = new THREE.Mesh(handleGeometry, handleMaterial);\n        handle.position.set(0, 1.5, config.depth / 2 + 0.3);\n        handle.castShadow = true;\n        vaultGroup.add(handle);\n        vaultGroup.userData.handle = handle;\n\n        // Handle spokes\n        for (let i = 0; i < 4; i++) {\n          const spokeGeometry = new THREE.BoxGeometry(0.08, 0.9, 0.08);\n          const spoke = new THREE.Mesh(spokeGeometry, handleMaterial);\n          spoke.rotation.z = (i * Math.PI) / 2;\n          spoke.position.set(0, 1.5, config.depth / 2 + 0.3);\n          vaultGroup.add(spoke);\n        }\n\n        // Vault roof\n        const roofGeometry = new THREE.BoxGeometry(config.width + 0.3, 0.2, config.depth + 0.3);\n        const roofMaterial = new THREE.MeshStandardMaterial({\n          color: 0x1a252f,\n          roughness: 0.3,\n          metalness: 0.4,\n        });\n        const roof = new THREE.Mesh(roofGeometry, roofMaterial);\n        roof.position.y = 2.9;\n        roof.castShadow = true;\n        vaultGroup.add(roof);\n\n        // Gold bars decoration on top\n        const goldBarGeometry = new THREE.BoxGeometry(0.3, 0.15, 0.6);\n        const goldBarMaterial = new THREE.MeshStandardMaterial({\n          color: 0xf1c40f,\n          roughness: 0.2,\n          metalness: 0.8,\n          emissive: 0xf1c40f,\n          emissiveIntensity: 0.1,\n        });\n\n        for (let i = 0; i < 3; i++) {\n          const goldBar = new THREE.Mesh(goldBarGeometry, goldBarMaterial);\n          goldBar.position.set(-0.5 + i * 0.5, 3.1, 0);\n          goldBar.castShadow = true;\n          vaultGroup.add(goldBar);\n        }\n\n        // \"BANK\" sign\n        const signGeometry = new THREE.BoxGeometry(2, 0.6, 0.1);\n        const signMaterial = new THREE.MeshStandardMaterial({\n          color: 0xf1c40f,\n          roughness: 0.3,\n          emissive: 0xf1c40f,\n          emissiveIntensity: 0.2,\n        });\n        const sign = new THREE.Mesh(signGeometry, signMaterial);\n        sign.position.set(0, 3.5, config.depth / 2);\n        sign.castShadow = true;\n        vaultGroup.add(sign);\n\n        vaultGroup.position.set(x, 0, z);\n        scene.add(vaultGroup);\n        // apply rotation from config if specified (degrees)\n        vaultGroup.rotation.y = THREE.MathUtils.degToRad(config.rotation || 0);\n        bankVaultMesh = vaultGroup;\n\n        // Glow zone for player deposit area\n        const glowGeometry = new THREE.BoxGeometry(config.width + 2, 0.05, config.depth + 2);\n        const glowMaterial = new THREE.MeshBasicMaterial({\n          color: 0xf1c40f,\n          transparent: true,\n          opacity: 0.4,\n        });\n        bankVaultGlow = new THREE.Mesh(glowGeometry, glowMaterial);\n        bankVaultGlow.position.set(x, 0.02, z + config.depth / 2 + 1.5);\n        scene.add(bankVaultGlow);\n\n        // Create floating \ud83c\udfe6 emoji\n        const emojiCanvas = document.createElement('canvas');\n        emojiCanvas.width = 128;\n        emojiCanvas.height = 128;\n        const emojiCtx = emojiCanvas.getContext('2d');\n        emojiCtx.font = '90px Arial';\n        emojiCtx.textAlign = 'center';\n        emojiCtx.textBaseline = 'middle';\n        emojiCtx.fillText('\ud83c\udfe6', 64, 64);\n\n        const emojiTexture = new THREE.CanvasTexture(emojiCanvas);\n        emojiTexture.minFilter = THREE.LinearFilter;\n\n        const emojiGeometry = new THREE.PlaneGeometry(2, 2);\n        const emojiMaterial = new THREE.MeshBasicMaterial({\n          map: emojiTexture,\n          transparent: true,\n          side: THREE.DoubleSide,\n          depthWrite: false,\n        });\n        const emojiMesh = new THREE.Mesh(emojiGeometry, emojiMaterial);\n        emojiMesh.position.set(x, 4.5, z);\n        emojiMesh.userData.baseY = 4.5;\n        scene.add(emojiMesh);\n\n        bankVaultMesh.userData.vaultIcon = emojiMesh;\n      }\n\n      function createStaffHireTile() {\n        const config = window.gameConfig.staffHireTile;\n        if (!config || config.deleted) return;\n\n        config.cost = config.hireCost;\n\n        // Use shared createBaseTile function\n        const result = createBaseTile(config);\n        staffHireTileMesh = result.tileMesh;\n        staffHireTileGlow = result.glowMesh;\n\n        // Update progress and cost display based on current deposit\n        createTileCostDisplay(staffHireTileMesh, config);\n        updateStaffHireProgress();\n        updateStaffHireCostDisplay();\n\n        // If staff already hired, create hired staff visual at cashier\n        if (config.staffHired) {\n          createHiredStaffAtCashier();\n        }\n      }\n\n      function updateStaffHireProgress() {\n        const config = window.gameConfig.staffHireTile;\n        if (!config || !staffHireTileMesh) return;\n        const extraCost = config.extraHireCost || Math.max(150, Math.round(config.hireCost * 1.5));\n        const targetCost = config.staffHired ? extraCost : config.hireCost;\n        const tempConfig = { ...config, cost: targetCost, moneyDeposited: config.moneyDeposited || 0 };\n        updateTileProgress(staffHireTileMesh, tempConfig);\n      }\n\n      function updateStaffHireCostDisplay() {\n        const config = window.gameConfig.staffHireTile;\n        if (!config || !staffHireTileMesh) return;\n        const extraCost = config.extraHireCost || Math.max(150, Math.round(config.hireCost * 1.5));\n        const targetCost = config.staffHired ? extraCost : config.hireCost;\n        const tempConfig = { ...config, cost: targetCost, moneyDeposited: config.moneyDeposited || 0 };\n        updateUnlockTileCostDisplay(staffHireTileMesh, tempConfig);\n      }\n\n      function createHiredStaffAtCashier() {\n        const cashierConfig = window.gameConfig.cashierStall;\n        if (!cashierConfig || cashierConfig.deleted) return;\n\n        // Remove existing staff if any\n        if (hiredStaffMesh) {\n          scene.remove(hiredStaffMesh);\n          hiredStaffMesh = null;\n        }\n        if (hiredStaffMesh2) {\n          scene.remove(hiredStaffMesh2);\n          hiredStaffMesh2 = null;\n        }\n\n        // Create staff member using the same model as player (green apron for hired status)\n        const staffGroup = createStaffMesh(0x27ae60);\n\n        // Position behind cashier counter (left side)\n        staffGroup.position.set(\n          cashierConfig.x - 1.4,\n          0,\n          cashierConfig.z - cashierConfig.depth / 2 - 1.2\n        );\n        staffGroup.rotation.y = 0;\n        // Face customers\n\n        scene.add(staffGroup);\n        hiredStaffMesh = staffGroup;\n\n        // Optional second cashier staff\n        if (window.gameConfig.staffHireTile && window.gameConfig.staffHireTile.extraStaffHired) {\n          const staffGroup2 = createStaffMesh(0x27ae60);\n          staffGroup2.position.set(\n            cashierConfig.x + 1.4,\n            0,\n            cashierConfig.z - cashierConfig.depth / 2 - 1.2\n          );\n          staffGroup2.rotation.y = 0;\n          scene.add(staffGroup2);\n          hiredStaffMesh2 = staffGroup2;\n        }\n\n        // Update staff icon on hire tile to show hired\n        if (staffHireTileMesh && staffHireTileMesh.userData.staffIcon) {\n          const icon = staffHireTileMesh.userData.staffIcon;\n          // Change body color to green\n          if (icon.children[0]) {\n            icon.children[0].material.color.setHex(0x27ae60);\n          }\n          // Remove dollar sign if exists\n          if (icon.children.length > 2) {\n            icon.remove(icon.children[2]);\n          }\n        }\n      }\n\n      // ============================================\n      // CLEANER STAFF TILE\n      // ============================================\n\n      function createCleanerStaffTile() {\n        const config = window.gameConfig.cleanerStaffTile;\n        if (!config || config.deleted) return;\n\n        config.cost = config.hireCost;\n\n        // Use shared createBaseTile function\n        const result = createBaseTile(config);\n        cleanerStaffTileMesh = result.tileMesh;\n        cleanerStaffTileGlow = result.glowMesh;\n\n        // Create cost display\n        createTileCostDisplay(cleanerStaffTileMesh, config);\n\n        // Update progress and cost display\n        updateCleanerStaffProgress();\n        updateCleanerStaffCostDisplay();\n\n        // If staff already hired, create hired cleaner\n        if (config.staffHired) {\n          createHiredCleaner();\n        }\n      }\n\n      function updateCleanerStaffProgress() {\n        const config = window.gameConfig.cleanerStaffTile;\n        if (!config || !cleanerStaffTileMesh) return;\n\n        updateTileProgress(cleanerStaffTileMesh, config);\n      }\n\n      function updateCleanerStaffCostDisplay() {\n        const config = window.gameConfig.cleanerStaffTile;\n        if (!config || !cleanerStaffTileMesh) return;\n\n        updateUnlockTileCostDisplay(cleanerStaffTileMesh, config);\n      }\n\n      function toRadians(degrees) {\n        return degrees * (Math.PI / 180);\n      }\n\n      function createBroom(staffGroup) {\n        const broomGroup = new THREE.Group();\n        const broomHandle = new THREE.Mesh(\n          new THREE.CylinderGeometry(0.05, 0.05, 1.5, 8),\n          new THREE.MeshStandardMaterial({\n            color: 0x8b4513,\n          })\n        );\n        broomHandle.position.set(0.03, 1.035, 0);\n        broomHandle.rotation.z = toRadians(-7.25);\n        broomGroup.add(broomHandle);\n\n        const broomHead = new THREE.Mesh(\n          new THREE.BoxGeometry(0.4, 0.2, 0.15),\n          new THREE.MeshStandardMaterial({\n            color: 0xdaa520,\n          })\n        );\n        broomHead.position.set(0.016, 0.278, 0);\n        broomHead.scale.set(1, 2.605, 1);\n        broomGroup.add(broomHead);\n\n        broomGroup.position.set(0.083, 0, 0.14);\n        staffGroup.add(broomGroup);\n\n        // const hand = staffGroup.getObjectByName('hand_l');\n        // hand.add(broomGroup);\n\n        // broomGroup.position.set(-0.128,-1.789,0);\n      }\n\n      function createHiredCleaner() {\n        if (hiredCleanerMesh) {\n          scene.remove(hiredCleanerMesh);\n          hiredCleanerMesh = null;\n        }\n\n        const staffGroup = createStaffMesh(0x3498db);\n        // Blue uniform for cleaner\n\n        // Add broom to cleaner\n        createBroom(staffGroup);\n\n        // Start at center of restaurant\n        staffGroup.position.set(0, 0, 15);\n\n        scene.add(staffGroup);\n        hiredCleanerMesh = staffGroup;\n\n        // Initialize AI\n        cleanerStaffAI = {\n          state: 'idle',\n          targetTable: null,\n          idleTimer: 0,\n          walkSpeed: 4,\n        };\n      }\n\n      // ============================================\n      // RUNNER STAFF TILE\n      // ============================================\n\n      function createRunnerStaffTile() {\n        const config = window.gameConfig.runnerStaffTile;\n        if (!config || config.deleted) return;\n\n        config.cost = config.hireCost;\n\n        // Use shared createBaseTile function\n        const result = createBaseTile(config);\n        runnerStaffTileMesh = result.tileMesh;\n        runnerStaffTileGlow = result.glowMesh;\n\n        // Create cost display\n        createTileCostDisplay(runnerStaffTileMesh, config);\n\n        // Update progress and cost display\n        updateRunnerStaffProgress();\n        updateRunnerStaffCostDisplay();\n\n        // If staff already hired, create hired runner\n        if (config.staffHired) {\n          createHiredRunner();\n        }\n      }\n\n      function updateRunnerStaffProgress() {\n        const config = window.gameConfig.runnerStaffTile;\n        if (!config || !runnerStaffTileMesh) return;\n        console.log('updateRunnerStaffProgress');\n        updateTileProgress(runnerStaffTileMesh, config);\n      }\n\n      function updateRunnerStaffCostDisplay() {\n        const config = window.gameConfig.runnerStaffTile;\n        if (!config || !runnerStaffTileMesh) return;\n        console.log('updateRunnerStaffCostDisplay');\n        updateUnlockTileCostDisplay(runnerStaffTileMesh, config);\n      }\n\n      function createTray(staffGroup, PLAYER_HEIGHT) {\n        const tray = new THREE.Mesh(\n          new THREE.CylinderGeometry(0.5, 0.5, 0.06, 16),\n          new THREE.MeshStandardMaterial({\n            color: 0xc0c0c0,\n            metalness: 0.7,\n            roughness: 0.3,\n          })\n        );\n        tray.position.set(0, PLAYER_HEIGHT - 0.1, 1);\n        tray.castShadow = true;\n\n        staffGroup.add(tray);\n        staffGroup.userData.tray = tray;\n      }\n\n      function updateTray(staffGroup, stackPile) {\n        staffGroup.userData.tray.visible = stackPile.length > 0;\n      }\n\n      function createHiredRunner() {\n        if (hiredRunnerMesh) {\n          scene.remove(hiredRunnerMesh);\n          hiredRunnerMesh = null;\n        }\n\n        const staffGroup = createStaffMesh(0xe67e22);\n        // Orange uniform for runner\n\n        // Add tray to runner\n        createTray(staffGroup, PLAYER_HEIGHT);\n\n        // Start at a grill area\n        staffGroup.position.set(-10, 0, -25);\n\n        scene.add(staffGroup);\n        hiredRunnerMesh = staffGroup;\n\n        // Initialize AI\n        runnerStaffAI = {\n          state: 'idle',\n          targetGrill: null,\n          idleTimer: 0,\n          walkSpeed: 5,\n          carryingBurgers: 0,\n          maxCapacity: 5,\n          collectTimer: 0,\n        };\n\n        runnerBurgerStack = [];\n      }\n\n      // ============================================\n      // VAULT COLLECTOR STAFF TILE\n      // ============================================\n\n      function createVaultStaffTile() {\n        return;\n      }\n\n      function updateVaultStaffProgress() {\n        return;\n      }\n\n      function updateVaultStaffCostDisplay() {\n        return;\n      }\n\n      function createHiredVaultCollector() {\n        return;\n      }\n\n      function destroyVaultStaffTileVisuals() {\n        return;\n      }\n\n      // ============================================\n      // SHARED STAFF HELPERS\n      // ============================================\n\n      function createStaffMesh(apronColor) {\n        const staffGroup = createStickmanInstance(apronColor);\n        return staffGroup;\n      }\n\n      function createStaffTileCostDisplay(tileMesh, config, type) {\n        return;\n      }\n\n      function updateStaffTileCostDisplayGeneric(tileMesh, config) {\n        const costCanvas = tileMesh.userData.costCanvas;\n        const costCtx = tileMesh.userData.costCtx;\n        const costTexture = tileMesh.userData.costTexture;\n        const costPlane = tileMesh.userData.costPlane;\n\n        if (!costCanvas || !costCtx || !costTexture || !costPlane) return;\n\n        costCtx.clearRect(0, 0, costCanvas.width, costCanvas.height);\n\n        if (config.staffHired) {\n          costCtx.fillStyle = '#27AE60';\n          costCtx.font = 'bold 48px Arial';\n          costCtx.textAlign = 'center';\n          costCtx.textBaseline = 'middle';\n          costCtx.fillText('HIRED!', costCanvas.width / 2, costCanvas.height / 2);\n        } else {\n          const remainingCost = Math.max(0, config.hireCost - config.moneyDeposited);\n\n          costCtx.fillStyle = 'rgba(0, 0, 0, 0.7)';\n          const pillWidth = 200;\n          const pillHeight = 80;\n          const pillX = (costCanvas.width - pillWidth) / 2;\n          const pillY = (costCanvas.height - pillHeight) / 2;\n          costCtx.beginPath();\n          costCtx.roundRect(pillX, pillY, pillWidth, pillHeight, 20);\n          costCtx.fill();\n\n          const progress = config.moneyDeposited / config.hireCost;\n          if (progress >= 0.75) {\n            costCtx.fillStyle = '#27AE60';\n          } else if (progress >= 0.5) {\n            costCtx.fillStyle = '#F39C12';\n          } else {\n            costCtx.fillStyle = '#FFD700';\n          }\n\n          costCtx.font = 'bold 56px Arial';\n          costCtx.textAlign = 'center';\n          costCtx.textBaseline = 'middle';\n          costCtx.fillText(\n            '$' + formatCurrencyAbbrev(remainingCost),\n            costCanvas.width / 2,\n            costCanvas.height / 2\n          );\n        }\n\n        costTexture.needsUpdate = true;\n        if (costPlane) {\n          costPlane.position.x = config.x;\n          costPlane.position.z = config.z;\n        }\n\n        if (config.staffHired) {\n          setTimeout(() => {\n            if (costPlane) costPlane.visible = false;\n          }, 2000);\n        }\n      }\n\n      function createDiningTable(tableConfig, index) {\n        const tableGroup = new THREE.Group();\n\n        // Table top\n        const tableTopGeometry = new THREE.BoxGeometry(2.5, 0.15, 2);\n        const tableTopMaterial = new THREE.MeshStandardMaterial({\n          color: 0x8b4513,\n          roughness: 0.6,\n          metalness: 0.1,\n        });\n        const tableTop = new THREE.Mesh(tableTopGeometry, tableTopMaterial);\n        tableTop.position.y = 1;\n        tableTop.castShadow = true;\n        tableTop.receiveShadow = true;\n        tableGroup.add(tableTop);\n\n        // Table legs\n        const legGeometry = new THREE.BoxGeometry(0.15, 1, 0.15);\n        const legMaterial = new THREE.MeshStandardMaterial({\n          color: 0x5d3a1a,\n          roughness: 0.7,\n        });\n\n        const legPositions = [\n          [-1, 0.5, -0.7],\n          [1, 0.5, -0.7],\n          [-1, 0.5, 0.7],\n          [1, 0.5, 0.7],\n        ];\n\n        legPositions.forEach((pos) => {\n          const leg = new THREE.Mesh(legGeometry, legMaterial);\n          leg.position.set(pos[0], pos[1], pos[2]);\n          leg.castShadow = true;\n          tableGroup.add(leg);\n        });\n\n        // Two chairs\n        for (let side = -1; side <= 1; side += 2) {\n          const chairGroup = new THREE.Group();\n\n          // Seat\n          const seatGeometry = new THREE.BoxGeometry(0.8, 0.1, 0.8);\n          const seatMaterial = new THREE.MeshStandardMaterial({\n            color: 0xa0522d,\n            roughness: 0.6,\n          });\n          const seat = new THREE.Mesh(seatGeometry, seatMaterial);\n          seat.position.y = 0.5;\n          seat.castShadow = true;\n          chairGroup.add(seat);\n\n          // Backrest\n          const backGeometry = new THREE.BoxGeometry(0.8, 0.6, 0.1);\n          const back = new THREE.Mesh(backGeometry, seatMaterial);\n          back.position.set(0, 0.85, -0.35 * side);\n          back.castShadow = true;\n          chairGroup.add(back);\n\n          // Chair legs\n          const chairLegGeometry = new THREE.BoxGeometry(0.08, 0.5, 0.08);\n          const chairLegPositions = [\n            [-0.3, 0.25, -0.3],\n            [0.3, 0.25, -0.3],\n            [-0.3, 0.25, 0.3],\n            [0.3, 0.25, 0.3],\n          ];\n\n          chairLegPositions.forEach((pos) => {\n            const chairLeg = new THREE.Mesh(chairLegGeometry, legMaterial);\n            chairLeg.position.set(pos[0], pos[1], pos[2]);\n            chairLeg.castShadow = true;\n            chairGroup.add(chairLeg);\n          });\n\n          chairGroup.position.set(0, 0, side * 1.5);\n          chairGroup.rotation.y = side > 0 ? Math.PI : 0;\n          tableGroup.add(chairGroup);\n        }\n\n        tableGroup.position.set(tableConfig.x, 0, tableConfig.z);\n        scene.add(tableGroup);\n\n        // Create trash mesh (hidden by default)\n        const trashGroup = new THREE.Group();\n\n        // Food wrapper\n        const wrapperGeometry = new THREE.BoxGeometry(0.4, 0.1, 0.3);\n        const wrapperMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffe4b5,\n          roughness: 0.8,\n        });\n        const wrapper = new THREE.Mesh(wrapperGeometry, wrapperMaterial);\n        wrapper.position.set(-0.3, 1.1, 0);\n        wrapper.rotation.y = Math.random() * 0.5;\n        trashGroup.add(wrapper);\n\n        // Crumpled napkin\n        const napkinGeometry = new THREE.SphereGeometry(0.15, 8, 8);\n        const napkinMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffffff,\n          roughness: 0.9,\n        });\n        const napkin = new THREE.Mesh(napkinGeometry, napkinMaterial);\n        napkin.position.set(0.2, 1.1, 0.2);\n        napkin.scale.set(1, 0.6, 1);\n        trashGroup.add(napkin);\n\n        // Empty cup\n        const cupGeometry = new THREE.CylinderGeometry(0.12, 0.1, 0.3, 12);\n        const cupMaterial = new THREE.MeshStandardMaterial({\n          color: 0xe74c3c,\n          roughness: 0.4,\n        });\n        const cup = new THREE.Mesh(cupGeometry, cupMaterial);\n        cup.position.set(0.4, 1.15, -0.2);\n        cup.rotation.x = Math.PI / 8;\n        trashGroup.add(cup);\n\n        trashGroup.position.set(tableConfig.x, 0, tableConfig.z);\n        trashGroup.visible = tableConfig.hasTrash || false;\n        scene.add(trashGroup);\n\n        diningTableMeshes.push({\n          mesh: tableGroup,\n          trashMesh: trashGroup,\n          tableData: tableConfig,\n          index: index,\n        });\n\n        return tableGroup;\n      }\n\n      function createDiningTableTile(index) {\n        const config = window.gameConfig.diningTableTiles[index];\n        if (!config || config.deleted) return;\n\n        // Set exponential cost based on number of active dining tables\n        const numTables = window.gameConfig.diningTables\n          ? window.gameConfig.diningTables.length\n          : 0;\n        config.tableCost = Math.floor(100 * Math.pow(1.5, numTables));\n        config.cost = config.tableCost;\n\n        const { tileMesh, glowMesh } = createBaseTile(config);\n\n        const tileEntry = {\n          mesh: tileMesh,\n          glow: glowMesh,\n          config: config,\n          index: index,\n        };\n        diningTableTileMeshes.push(tileEntry);\n\n        updateDiningTableTileProgress(index);\n        updateDiningTableTileCostDisplay(index);\n\n        return tileEntry;\n      }\n\n      function updateDiningTableTileProgress(index) {\n        const tileEntry = diningTableTileMeshes.find((t) => t.index === index);\n        if (!tileEntry) return;\n        const config = tileEntry.config;\n        const diningTableTileMesh = tileEntry.mesh;\n\n        updateTileProgress(diningTableTileMesh, config, 'moneyDeposited', 'tableCost');\n        return;\n      }\n\n      function updateDiningTableTileCostDisplay(index) {\n        const tileEntry = diningTableTileMeshes.find((t) => t.index === index);\n        if (!tileEntry) return;\n        const config = tileEntry.config;\n        const diningTableTileMesh = tileEntry.mesh;\n\n        updateUnlockTileCostDisplay(diningTableTileMesh, config);\n        return;\n      }\n\n      function spawnNewDiningTable(index) {\n        const tileEntry = diningTableTileMeshes.find((t) => t.index === index);\n        if (!tileEntry) return;\n        const tileConfig = tileEntry.config;\n\n        // Spawn table at the tile's position\n        const x = tileConfig.x;\n        const z = tileConfig.z;\n\n        const newTable = {\n          id: `table_${nextTableId++}`,\n          x: x,\n          z: z,\n          occupied: false,\n          hasTrash: false,\n        };\n\n        window.gameConfig.diningTables.push(newTable);\n        const tableIndex = window.gameConfig.diningTables.length - 1;\n        createDiningTable(newTable, tableIndex);\n\n        // Register the new table as editable in edit mode\n        if (gameMode === 'edit') {\n          const tableEntry = diningTableMeshes[diningTableMeshes.length - 1];\n          const newEditable = {\n            mesh: tableEntry.mesh,\n            type: 'diningTable',\n            configKey: `diningTables.${tableIndex}`,\n            tableIndex: tableIndex,\n            name: `Dining Table ${tableIndex + 1}`,\n            getData: () => ({\n              x: tableEntry.tableData.x,\n              z: tableEntry.tableData.z,\n            }),\n            setPosition: (newX, newZ) => {\n              tableEntry.mesh.position.x = newX;\n              tableEntry.mesh.position.z = newZ;\n              tableEntry.tableData.x = newX;\n              tableEntry.tableData.z = newZ;\n              if (tableEntry.trashMesh) {\n                tableEntry.trashMesh.position.x = newX;\n                tableEntry.trashMesh.position.z = newZ;\n              }\n              window.gameConfig.diningTables[tableIndex].x = newX;\n              window.gameConfig.diningTables[tableIndex].z = newZ;\n            },\n          };\n          editableObjects.push(newEditable);\n        }\n\n        // Create smoke explosion effect at tile location\n        createSmokeParticlesAt(x, 0.5, z);\n        playSound('upgrade_sound');\n\n        // Destroy the tile visuals\n        destroyDiningTableTileVisuals(index);\n      }\n\n      function destroyDiningTableTileVisuals(index) {\n        const entryIndex = diningTableTileMeshes.findIndex((t) => t.index === index);\n        if (entryIndex === -1) return;\n        const tileEntry = diningTableTileMeshes[entryIndex];\n        const diningTableTileMesh = tileEntry.mesh;\n        const diningTableTileGlow = tileEntry.glow;\n\n        destroyTileVisuals(diningTableTileMesh, diningTableTileGlow);\n\n        // Mark as deleted in config\n        window.gameConfig.diningTableTiles[index].deleted = true;\n\n        // Remove from editable objects\n        const editIndex = editableObjects.findIndex(\n          (obj) => obj.type === 'diningTableTile' && obj.tileIndex === index\n        );\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        diningTableTileMeshes.splice(entryIndex, 1);\n      }\n\n      function getDepositAmount(totalCost, moneyValue, totalDeposited) {\n        const tenPercent = Math.max(moneyValue, Math.ceil(totalCost * 0.1));\n        return vaultBalance >= tenPercent\n          ? tenPercent\n          : Math.min(moneyValue, vaultBalance, totalCost - totalDeposited);\n      }\n\n      function updateDiningTableTile(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        if (!window.gameConfig.diningTableTiles) return;\n\n        let isPlayerInAnyTile = false;\n\n        for (let i = 0; i < window.gameConfig.diningTableTiles.length; i++) {\n          const config = window.gameConfig.diningTableTiles[i];\n          if (!config || config.deleted) continue;\n\n          const tileEntry = diningTableTileMeshes.find((t) => t.index === i);\n          if (!tileEntry) continue;\n          const diningTableTileMesh = tileEntry.mesh;\n          const diningTableTileGlow = tileEntry.glow;\n\n          // Check if player is in dining table tile zone\n          const inThisTile = isPlayerInZone(config);\n\n          const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n          const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n\n          if (inThisTile) {\n            isPlayerInAnyTile = true;\n\n            // Deposit money when in zone\n            if (vaultBalance > 0 && totalDeposited < config.tableCost) {\n              diningTableTileDepositTimer += deltaTime;\n\n              if (diningTableTileDepositTimer >= DINING_TABLE_DEPOSIT_DELAY) {\n                diningTableTileDepositTimer = 0;\n\n                const depositAmount = getDepositAmount(\n                  config.tableCost,\n                  moneyValue,\n                  totalDeposited\n                );\n\n                if (depositAmount > 0) {\n                  modifyVaultBalance(-depositAmount);\n                  config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n\n                  // Throw money toward tile\n                  const startPos = {\n                    x: player.position.x,\n                    y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                    z: player.position.z,\n                  };\n\n                  const targetPos = {\n                    x: config.x,\n                    y: 0.5,\n                    z: config.z,\n                  };\n\n                  createFlyingItem(\n                    startPos,\n                    targetPos,\n                    'money',\n                    () => {\n                      config.moneyInFlight = Math.max(\n                        0,\n                        (config.moneyInFlight || 0) - depositAmount\n                      );\n                      config.moneyDeposited += depositAmount;\n\n                      playSound('collect_sound');\n                      updateDiningTableTileProgress(i);\n                      updateDiningTableTileCostDisplay(i);\n\n                      // Check if enough to spawn new table\n                      if (config.moneyDeposited >= config.tableCost) {\n                        spawnNewDiningTable(i);\n                      }\n                    },\n                    true\n                  );\n                }\n              }\n            }\n          }\n\n          // Animate glow\n          if (diningTableTileGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004 + i) * 0.05;\n            diningTableTileGlow.scale.set(pulseScale, 1, pulseScale);\n          }\n        }\n\n        if (!isPlayerInAnyTile) {\n          diningTableTileDepositTimer = 0;\n        }\n      }\n\n      function findAvailableDiningTable() {\n        for (let i = 0; i < diningTableMeshes.length; i++) {\n          const table = diningTableMeshes[i];\n          if (!table.tableData.occupied && !table.tableData.hasTrash) {\n            return table;\n          }\n        }\n        return null;\n      }\n\n      function cleanTrashFromTable(tableData) {\n        tableData.hasTrash = false;\n\n        if (!window.gameConfig.tutorialHasCleanedTable) {\n          window.gameConfig.tutorialHasCleanedTable = true;\n          window.gameConfig.tutorialHasCollectedMoney = false;\n        }\n\n        // Update config\n        const configTable = window.gameConfig.diningTables.find((t) => t.id === tableData.id);\n        if (configTable) {\n          configTable.hasTrash = false;\n        }\n\n        // Hide trash mesh\n        const tableEntry = diningTableMeshes.find((t) => t.tableData.id === tableData.id);\n        if (tableEntry && tableEntry.trashMesh) {\n          tableEntry.trashMesh.visible = false;\n        }\n      }\n\n      function updateTrashCollection(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        // Check if player is near any table with trash\n        for (const tableEntry of diningTableMeshes) {\n          if (tableEntry.tableData.hasTrash) {\n            const dx = player.position.x - tableEntry.tableData.x;\n            const dz = player.position.z - tableEntry.tableData.z;\n            const dist = Math.sqrt(dx * dx + dz * dz);\n\n            if (dist < 2.5) {\n              // Auto-clean trash when player is near\n              cleanTrashFromTable(tableEntry.tableData);\n              playSound('pickup_sound');\n            }\n          }\n        }\n      }\n\n      function createCustomerMesh(isVIP = false) {\n        let customerGroup;\n\n        // Determine customer color\n        let customerColor;\n        if (isVIP) {\n          // VIP customers have gold/purple luxury colors\n          const vipColors = [0xffd700, 0x9b59b6, 0xe91e63, 0x8e44ad];\n          customerColor = vipColors[Math.floor(Math.random() * vipColors.length)];\n        } else {\n          const customerColors = [0x3498db, 0xe74c3c, 0x9b59b6, 0x1abc9c, 0xf39c12, 0x2ecc71];\n          customerColor = customerColors[Math.floor(Math.random() * customerColors.length)];\n        }\n\n        // Use stickman model if loaded\n        if (stickmanModelLoaded && stickmanModelTemplate) {\n          customerGroup = createStickmanInstance(customerColor);\n\n          // Remove any McDonald's hat that was added by createStickmanInstance\n          const mcHat = customerGroup.userData.mcHat;\n          if (mcHat) {\n            const head = customerGroup.getObjectByName('head_leaf');\n            head.remove(mcHat);\n            customerGroup.userData.mcHat = null;\n          }\n\n          // Add appropriate headwear\n          if (isVIP) {\n            // VIP gets a crown!\n            const crown = createCrownHat();\n            const head = customerGroup.getObjectByName('head_leaf');\n            crown.position.y = (0.6 + 0.3) / 2;\n            crown.scale.setScalar(0.5);\n            head.add(crown);\n            // customerGroup.add(crown);\n            customerGroup.userData.crown = crown;\n          }\n          // Regular customers get no hat (just natural stickman)\n        } else {\n          // Fallback to original customer mesh if model not loaded\n          customerGroup = new THREE.Group();\n\n          // Body\n          const bodyGeometry = new THREE.CylinderGeometry(0.4, 0.5, 1.2, 16);\n          const bodyMaterial = new THREE.MeshStandardMaterial({\n            color: customerColor,\n            roughness: isVIP ? 0.3 : 0.5,\n            metalness: isVIP ? 0.3 : 0,\n          });\n          const body = new THREE.Mesh(bodyGeometry, bodyMaterial);\n          body.position.y = 0.6;\n          body.castShadow = true;\n          customerGroup.add(body);\n\n          // Head\n          const headGeometry = new THREE.SphereGeometry(0.35, 16, 16);\n          const headMaterial = new THREE.MeshStandardMaterial({\n            color: 0xffdbac,\n            roughness: 0.6,\n          });\n          const head = new THREE.Mesh(headGeometry, headMaterial);\n          head.position.y = 1.5;\n          head.castShadow = true;\n          customerGroup.add(head);\n\n          // Eyes\n          const eyeGeometry = new THREE.SphereGeometry(0.06, 8, 8);\n          const eyeMaterial = new THREE.MeshBasicMaterial({\n            color: 0x333333,\n          });\n\n          const leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial);\n          leftEye.position.set(-0.12, 1.55, 0.28);\n          customerGroup.add(leftEye);\n\n          const rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial);\n          rightEye.position.set(0.12, 1.55, 0.28);\n          customerGroup.add(rightEye);\n\n          // Hair/accessory based on VIP status\n          if (isVIP) {\n            // VIP Crown!\n            const crown = createCrownHat();\n            crown.position.y = 1.85;\n            crown.scale.setScalar(0.9);\n            customerGroup.add(crown);\n            customerGroup.userData.crown = crown;\n          } else {\n            // Regular customer hair styles\n            const hairStyle = Math.floor(Math.random() * 3);\n            const hairMaterial = new THREE.MeshStandardMaterial({\n              color: [0x4a3728, 0x8b4513, 0x2c2c2c, 0xdaa520][Math.floor(Math.random() * 4)],\n              roughness: 0.8,\n            });\n\n            if (hairStyle === 0) {\n              // Short hair\n              const hairGeometry = new THREE.SphereGeometry(\n                0.38,\n                16,\n                8,\n                0,\n                Math.PI * 2,\n                0,\n                Math.PI / 2\n              );\n              const hair = new THREE.Mesh(hairGeometry, hairMaterial);\n              hair.position.y = 1.6;\n              customerGroup.add(hair);\n            } else if (hairStyle === 1) {\n              // Cap\n              const capGeometry = new THREE.CylinderGeometry(0.38, 0.4, 0.15, 16);\n              const cap = new THREE.Mesh(capGeometry, hairMaterial);\n              cap.position.y = 1.8;\n              customerGroup.add(cap);\n\n              const brimGeometry = new THREE.BoxGeometry(0.4, 0.05, 0.4);\n              const brim = new THREE.Mesh(brimGeometry, hairMaterial);\n              brim.position.set(0, 1.75, 0.2);\n              customerGroup.add(brim);\n            } else {\n              // Top hat\n              const hatGeometry = new THREE.CylinderGeometry(0.25, 0.25, 0.4, 16);\n              const hat = new THREE.Mesh(hatGeometry, hairMaterial);\n              hat.position.y = 1.9;\n              customerGroup.add(hat);\n\n              const brimGeometry = new THREE.CylinderGeometry(0.4, 0.4, 0.05, 16);\n              const brim = new THREE.Mesh(brimGeometry, hairMaterial);\n              brim.position.y = 1.7;\n              customerGroup.add(brim);\n            }\n          }\n        }\n\n        // Create emoji display above head (VIP gets star emoji by default)\n        const emojiMesh = createCustomerEmoji(isVIP ? '\u2b50' : '\ud83d\ude10');\n        emojiMesh.position.set(0, isVIP ? 2.7 : 2.5, 0);\n        customerGroup.add(emojiMesh);\n        customerGroup.userData.emojiMesh = emojiMesh;\n\n        // Create burger holder (hidden initially)\n        const burgerHolder = createCustomerBurger();\n        burgerHolder.position.set(0.5, 1.0, 0.3);\n        burgerHolder.visible = false;\n\n        const hand = customerGroup.getObjectByName('hand_l');\n        hand.add(burgerHolder);\n        burgerHolder.position.set(0, 0, 0);\n        burgerHolder.rotation.set(toRadians(90), 0, 0);\n\n        // customerGroup.add(burgerHolder);\n        customerGroup.userData.burgerMesh = burgerHolder;\n\n        // Initialize squish state for squishy animation\n        customerGroup.userData.squishState = {\n          scaleY: 1,\n          scaleXZ: 1,\n          targetScaleY: 1,\n          targetScaleXZ: 1,\n        };\n        customerGroup.userData.isVIP = isVIP;\n\n        return customerGroup;\n      }\n\n      function createCustomerEmoji(emoji) {\n        const canvas = document.createElement('canvas');\n        canvas.width = 128;\n        canvas.height = 128;\n        const ctx = canvas.getContext('2d');\n\n        // Draw emoji\n        ctx.font = '100px Arial';\n        ctx.textAlign = 'center';\n        ctx.textBaseline = 'middle';\n        ctx.fillText(emoji, 64, 64);\n\n        const texture = new THREE.CanvasTexture(canvas);\n        texture.minFilter = THREE.LinearFilter;\n\n        const geometry = new THREE.PlaneGeometry(1, 1);\n        const material = new THREE.MeshBasicMaterial({\n          map: texture,\n          transparent: true,\n          side: THREE.DoubleSide,\n          depthWrite: false,\n        });\n\n        const mesh = new THREE.Mesh(geometry, material);\n        mesh.userData.canvas = canvas;\n        mesh.userData.ctx = ctx;\n        mesh.userData.texture = texture;\n        mesh.userData.currentEmoji = emoji;\n        mesh.userData.lastEmojiChange = Date.now();\n        mesh.visible = true;\n\n        return mesh;\n      }\n\n      function updateCustomerEmoji(customer, emoji) {\n        const emojiMesh = customer.mesh.userData.emojiMesh;\n        if (!emojiMesh || emojiMesh.userData.currentEmoji === emoji) return;\n\n        const ctx = emojiMesh.userData.ctx;\n        const canvas = emojiMesh.userData.canvas;\n        const texture = emojiMesh.userData.texture;\n\n        // Clear and redraw\n        ctx.clearRect(0, 0, canvas.width, canvas.height);\n        ctx.font = '100px Arial';\n        ctx.textAlign = 'center';\n        ctx.textBaseline = 'middle';\n        ctx.fillText(emoji, 64, 64);\n\n        texture.needsUpdate = true;\n        emojiMesh.userData.currentEmoji = emoji;\n        emojiMesh.userData.lastEmojiChange = Date.now();\n        emojiMesh.visible = true;\n      }\n\n      function updateCustomerEmojiVisibility(customer, nowMs) {\n        const emojiMesh = customer.mesh.userData.emojiMesh;\n        if (!emojiMesh) return;\n        const lastChange = emojiMesh.userData.lastEmojiChange || nowMs;\n        emojiMesh.visible = nowMs - lastChange <= 2000;\n      }\n\n      function createCustomerBurger() {\n        const burgerGroup = new THREE.Group();\n\n        // Bottom bun\n        const bottomBunGeometry = new THREE.CylinderGeometry(0.25, 0.28, 0.08, 12);\n        const bunMaterial = new THREE.MeshStandardMaterial({\n          color: 0xdeb887,\n          roughness: 0.7,\n        });\n        const bottomBun = new THREE.Mesh(bottomBunGeometry, bunMaterial);\n        bottomBun.position.y = 0.04;\n        burgerGroup.add(bottomBun);\n\n        // Patty\n        const pattyGeometry = new THREE.CylinderGeometry(0.22, 0.22, 0.06, 12);\n        const pattyMaterial = new THREE.MeshStandardMaterial({\n          color: 0x8b4513,\n          roughness: 0.8,\n        });\n        const patty = new THREE.Mesh(pattyGeometry, pattyMaterial);\n        patty.position.y = 0.11;\n        burgerGroup.add(patty);\n\n        // Cheese\n        const cheeseGeometry = new THREE.BoxGeometry(0.35, 0.02, 0.35);\n        const cheeseMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffa500,\n          roughness: 0.5,\n        });\n        const cheese = new THREE.Mesh(cheeseGeometry, cheeseMaterial);\n        cheese.position.y = 0.15;\n        cheese.rotation.y = Math.PI / 4;\n        burgerGroup.add(cheese);\n\n        // Top bun\n        const topBunGeometry = new THREE.SphereGeometry(\n          0.25,\n          12,\n          8,\n          0,\n          Math.PI * 2,\n          0,\n          Math.PI / 2\n        );\n        const topBun = new THREE.Mesh(topBunGeometry, bunMaterial);\n        topBun.position.y = 0.18;\n        burgerGroup.add(topBun);\n\n        burgerGroup.scale.setScalar(0.8);\n        return burgerGroup;\n      }\n\n      function getCustomerQueueTarget(config, queuePosition) {\n        let tx = config.x;\n        let tz = config.z + config.depth / 2 + 2 + queuePosition * CUSTOMER_SPACING;\n        const maxZ = (RESTAURANT_DEPTH / 2) - 3; // Keep away from front wall\n        if (tz > maxZ) {\n          const diff = tz - maxZ;\n          tz = maxZ;\n          const dir = config.x < 0 ? 1 : -1;\n          tx += diff * dir; // Curve the line along the wall\n        }\n        return { x: tx, z: tz };\n      }\n\n      function spawnCustomer() {\n        const config = window.gameConfig.cashierStall;\n        if (!config || config.deleted) return;\n\n        // Determine if this is a VIP customer\n        const vipConfig = window.gameConfig.vipCustomers || {\n          spawnChance: 0.15,\n        };\n        const isVIP = Math.random() < vipConfig.spawnChance;\n\n        const mesh = createCustomerMesh(isVIP);\n\n        // Spawn off-screen to the left\n        const startX = -RESTAURANT_WIDTH / 2 - 5;\n        const startZ = config.z + config.depth / 2 + 2;\n\n        mesh.position.set(startX, 0, startZ);\n        scene.add(mesh);\n\n        // Calculate position in queue\n        const queuePosition = customerQueue.filter(\n          (c) => c.state === 'waiting' || c.state === 'walking'\n        ).length;\n        const target = getCustomerQueueTarget(config, queuePosition);\n\n        const customer = {\n          mesh: mesh,\n          state: 'walking',\n          targetX: target.x,\n          targetZ: target.z,\n          queuePosition: queuePosition,\n          waitTime: 0,\n          satisfied: false,\n          isVIP: isVIP,\n        };\n\n        customerQueue.push(customer);\n      }\n\n      function removeCustomer(customer) {\n        customer.state = 'walkingToDoor';\n        customer.targetX = -RESTAURANT_WIDTH / 2 + 2;\n        customer.targetZ = 10;\n      }\n\n      function addBurgerToCashier(menuItem = null) {\n        const config = window.gameConfig.cashierStall;\n        if (cashierBurgerStack.length >= config.maxBurgers) return false;\n\n        const food = createStackItem(menuItem);\n\n        // Stack burgers in a grid pattern on the tray\n        const stackIndex = cashierBurgerStack.length;\n        const col = stackIndex % 4;\n        const row = Math.floor((stackIndex % 12) / 4);\n        const layer = Math.floor(stackIndex / 12);\n\n        const trayX = config.x - config.width / 2 + 1.5;\n        const trayZ = config.z;\n\n        food.position.set(trayX - 0.7 + col * 0.5, 1.4 + layer * 0.25, trayZ - 0.5 + row * 0.5);\n\n        // Slight random rotation\n        food.rotation.y = (Math.random() - 0.5) * 0.3;\n\n        scene.add(food);\n        cashierBurgerStack.push(food);\n\n        return true;\n      }\n\n      function removeBurgerFromCashier() {\n        if (cashierBurgerStack.length === 0) return null;\n\n        const food = cashierBurgerStack.pop();\n        const menuItem = food.userData.menuItem;\n        scene.remove(food);\n\n        // Dispose\n        food.traverse((child) => {\n          if (child.geometry) child.geometry.dispose();\n          if (child.material) {\n            if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n            else child.material.dispose();\n          }\n        });\n\n        return menuItem;\n        // Return the menu item for value calculation\n      }\n\n      function initMoneyGrid() {\n        // Initialize grid tracking for neat money stacking\n        moneyGridStacks = [];\n        for (let i = 0; i < MONEY_GRID_COLS * MONEY_GRID_ROWS; i++) {\n          moneyGridStacks.push({\n            count: 0,\n            bills: [],\n          });\n        }\n      }\n\n      function getGridPosition(gridIndex) {\n        const config = window.gameConfig.moneyZone;\n        const col = gridIndex % MONEY_GRID_COLS;\n        const row = Math.floor(gridIndex / MONEY_GRID_COLS);\n\n        // Tighter grid spacing for neat rectangular alignment\n        const cellWidth = (config.width - 0.6) / MONEY_GRID_COLS;\n        const cellDepth = (config.depth - 0.4) / MONEY_GRID_ROWS;\n\n        const x = config.x - (config.width - 0.6) / 2 + cellWidth / 2 + col * cellWidth;\n        const z = config.z - (config.depth - 0.4) / 2 + cellDepth / 2 + row * cellDepth;\n\n        return {\n          x,\n          z,\n        };\n      }\n\n      function findBestGridSlot() {\n        // Find grid slot with lowest stack count for even distribution\n        let minCount = Infinity;\n        let bestIndex = 0;\n\n        for (let i = 0; i < moneyGridStacks.length; i++) {\n          if (moneyGridStacks[i].count < minCount) {\n            minCount = moneyGridStacks[i].count;\n            bestIndex = i;\n          }\n        }\n\n        return bestIndex;\n      }\n\n      function createMoneyItem(position) {\n        // Create a rectangular money bundle that fills grid neatly\n        const moneyGroup = new THREE.Group();\n\n        const asset = lib.getAsset('paper_money');\n        if (asset && asset.url) {\n          const geom = getCachedGeometry(\n            'paperMoneyBoxGeom',\n            () => new THREE.BoxGeometry(1.0, 0.15, 0.6)\n          );\n          const topMat = getCachedMaterial(\n            'paperMoneyTopMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                map: new THREE.TextureLoader().load(asset.url),\n                transparent: true,\n                alphaTest: 0.1,\n              })\n          );\n          const sideMat = getCachedMaterial(\n            'paperMoneySideMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x85bb65,\n              })\n          );\n          const mesh = new THREE.Mesh(geom, [sideMat, sideMat, topMat, sideMat, sideMat, sideMat]);\n          mesh.position.y = 0.075;\n          mesh.castShadow = true;\n          moneyGroup.add(mesh);\n        } else {\n          // Base plate (rectangular to match money)\n          const plateGeom = getCachedGeometry(\n            'moneyPlateGeom',\n            () => new THREE.BoxGeometry(1.1, 0.06, 0.7)\n          );\n          const plateMat = getCachedMaterial(\n            'moneyPlateMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x2c3e50,\n                roughness: 0.3,\n                metalness: 0.1,\n              })\n          );\n          const plate = new THREE.Mesh(plateGeom, plateMat);\n          plate.position.y = 0.03;\n          plate.castShadow = true;\n          plate.receiveShadow = true;\n          moneyGroup.add(plate);\n\n          // Stack of dollar bills on plate - rectangular to fill grid\n          const stackGeom = getCachedGeometry(\n            'moneyStackGeom',\n            () => new THREE.BoxGeometry(1.0, 0.12, 0.6)\n          );\n          const stackMat = getCachedMaterial(\n            'moneyStackMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x85bb65,\n                roughness: 0.6,\n                metalness: 0.1,\n              })\n          );\n          const stack = new THREE.Mesh(stackGeom, stackMat);\n          stack.position.y = 0.12;\n          stack.castShadow = true;\n          moneyGroup.add(stack);\n\n          // White band around money stack\n          const bandGeom = getCachedGeometry(\n            'moneyBandGeom',\n            () => new THREE.BoxGeometry(1.02, 0.07, 0.15)\n          );\n          const bandMat = getCachedMaterial(\n            'moneyBandMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0xffffff,\n                roughness: 0.4,\n              })\n          );\n          const band = new THREE.Mesh(bandGeom, bandMat);\n          band.position.y = 0.12;\n          moneyGroup.add(band);\n\n          // Dollar sign on top\n          const signGeom = getCachedGeometry(\n            'moneySignGeom',\n            () => new THREE.BoxGeometry(0.12, 0.02, 0.2)\n          );\n          const signMat = getCachedMaterial(\n            'moneySignMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x2d5016,\n                roughness: 0.4,\n              })\n          );\n          const sign = new THREE.Mesh(signGeom, signMat);\n          sign.position.y = 0.19;\n          moneyGroup.add(sign);\n        }\n\n        moneyGroup.position.copy(position);\n        moneyGroup.position.y = 0.1;\n        moneyGroup.rotation.y = 0;\n        // No rotation - aligned to grid\n\n        // Physics properties\n        moneyGroup.userData.velocity = {\n          x: 0,\n          z: 0,\n        };\n        moneyGroup.userData.collected = false;\n        moneyGroup.userData.gridIndex = -1;\n\n        scene.add(moneyGroup);\n        groundMoneyItems.push(moneyGroup);\n\n        return moneyGroup;\n      }\n\n      function createMoneyStackItem() {\n        // Create a money bundle for stacking - rectangular and aligned\n        const moneyGroup = new THREE.Group();\n\n        const asset = lib.getAsset('paper_money');\n        if (asset && asset.url) {\n          const geom = getCachedGeometry(\n            'paperMoneyBoxGeomStack',\n            () => new THREE.BoxGeometry(0.8, 0.1, 0.5)\n          );\n          const topMat = getCachedMaterial(\n            'paperMoneyTopMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                map: new THREE.TextureLoader().load(asset.url),\n                transparent: true,\n                alphaTest: 0.1,\n              })\n          );\n          const sideMat = getCachedMaterial(\n            'paperMoneySideMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x85bb65,\n              })\n          );\n          const mesh = new THREE.Mesh(geom, [sideMat, sideMat, topMat, sideMat, sideMat, sideMat]);\n          mesh.position.y = 0.05;\n          mesh.castShadow = true;\n          moneyGroup.add(mesh);\n        } else {\n          // Stack of dollar bills - rectangular shape\n          const stackGeom = getCachedGeometry(\n            'mStackGeom',\n            () => new THREE.BoxGeometry(0.7, 0.1, 0.4)\n          );\n          const stackMat = getCachedMaterial(\n            'moneyStackMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x85bb65,\n                roughness: 0.6,\n                metalness: 0.1,\n              })\n          );\n          const stack = new THREE.Mesh(stackGeom, stackMat);\n          stack.position.y = 0.05;\n          stack.castShadow = true;\n          moneyGroup.add(stack);\n\n          // White band around money\n          const bandGeom = getCachedGeometry(\n            'mBandGeom',\n            () => new THREE.BoxGeometry(0.72, 0.05, 0.12)\n          );\n          const bandMat = getCachedMaterial(\n            'moneyBandMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0xffffff,\n                roughness: 0.4,\n              })\n          );\n          const band = new THREE.Mesh(bandGeom, bandMat);\n          band.position.y = 0.05;\n          moneyGroup.add(band);\n\n          // Top layer (slightly different shade)\n          const topGeom = getCachedGeometry(\n            'mTopGeom',\n            () => new THREE.BoxGeometry(0.65, 0.025, 0.38)\n          );\n          const topMat = getCachedMaterial(\n            'mTopMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x6b8e4e,\n                roughness: 0.5,\n              })\n          );\n          const top = new THREE.Mesh(topGeom, topMat);\n          top.position.y = 0.11;\n          top.castShadow = true;\n          moneyGroup.add(top);\n        }\n\n        // No rotation - aligned neatly\n        moneyGroup.rotation.y = 0;\n        moneyGroup.castShadow = true;\n\n        return moneyGroup;\n      }\n\n      function addToMoneyStack() {\n        if (currentMoneyCount >= MAX_MONEY_CAPACITY) return false;\n\n        // Position money on top of burger stack (same stacking style)\n        const burgerStackHeight = playerStack.length * STACK_ITEM_HEIGHT;\n        const stackY = PLAYER_HEIGHT + burgerStackHeight + moneyStack.length * MONEY_STACK_HEIGHT;\n        const money = createMoneyStackItem();\n        money.position.set(0, stackY, 0);\n        // Same position as burgers\n        player.add(money);\n        moneyStack.push(money);\n        currentMoneyCount = moneyStack.length;\n        updateMoneyStackUI();\n        playSound('pickup_sound');\n        return true;\n      }\n\n      function removeFromMoneyStack() {\n        if (moneyStack.length > 0) {\n          const item = moneyStack.pop();\n          player.remove(item);\n\n          item.traverse((child) => {\n            if (child.geometry) child.geometry.dispose();\n            if (child.material) {\n              if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n              else child.material.dispose();\n            }\n          });\n\n          currentMoneyCount = moneyStack.length;\n          updateMoneyStackUI();\n          return true;\n        }\n        return false;\n      }\n\n      // New helper function to calculate position without modifying the array\n      function getCashPilePositionAtIndex(index) {\n        const cashPileX = cashierStallMesh.userData.cashPileX;\n        const cashPileZ = cashierStallMesh.userData.cashPileZ;\n        const cashPileWidth = cashierStallMesh.userData.cashPileWidth;\n        const cashPileDepth = cashierStallMesh.userData.cashPileDepth;\n\n        // Calculate position in tight grid pattern\n        const cols = 4;\n        const rows = 4;\n        const itemsPerLayer = cols * rows;\n        const col = index % cols;\n        const row = Math.floor((index % itemsPerLayer) / cols);\n        const layer = Math.floor(index / itemsPerLayer);\n\n        // Tight grid spacing to fill the tile\n        const cellWidth = (cashPileWidth - 0.4) / cols;\n        const cellDepth = (cashPileDepth - 0.2) / rows;\n\n        const x = cashPileX - (cashPileWidth - 0.4) / 2 + cellWidth / 2 + col * cellWidth;\n        const z = cashPileZ - (cashPileDepth - 0.2) / 2 + cellDepth / 2 + row * cellDepth;\n        const y = 0.25 + layer * 0.12;\n\n        return { x, y, z };\n      }\n\n      // Cash pile functions for cashier earnings\n      function createCashPileMoney(targetX, targetY, targetZ) {\n        // Create a money bundle for the cash pile - rectangular to fill grid neatly\n        const moneyGroup = new THREE.Group();\n\n        const asset = lib.getAsset('paper_money');\n        if (asset && asset.url) {\n          const geom = getCachedGeometry(\n            'paperMoneyBoxGeom',\n            () => new THREE.BoxGeometry(1.0, 0.15, 0.6)\n          );\n          const topMat = getCachedMaterial(\n            'paperMoneyTopMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                map: new THREE.TextureLoader().load(asset.url),\n                transparent: true,\n                alphaTest: 0.1,\n              })\n          );\n          const sideMat = getCachedMaterial(\n            'paperMoneySideMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x85bb65,\n              })\n          );\n          const mesh = new THREE.Mesh(geom, [sideMat, sideMat, topMat, sideMat, sideMat, sideMat]);\n          mesh.position.y = 0.075;\n          mesh.castShadow = true;\n          moneyGroup.add(mesh);\n        } else {\n          // Stack of dollar bills - rectangular shape to fit grid\n          const stackGeom = getCachedGeometry(\n            'cpStackGeom',\n            () => new THREE.BoxGeometry(0.85, 0.1, 0.5)\n          );\n          const stackMat = getCachedMaterial(\n            'moneyStackMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0x85bb65,\n                roughness: 0.6,\n                metalness: 0.1,\n              })\n          );\n          const stack = new THREE.Mesh(stackGeom, stackMat);\n          stack.position.y = 0.05;\n          stack.castShadow = true;\n          moneyGroup.add(stack);\n\n          // White band around money\n          const bandGeom = getCachedGeometry(\n            'cpBandGeom',\n            () => new THREE.BoxGeometry(0.87, 0.05, 0.12)\n          );\n          const bandMat = getCachedMaterial(\n            'moneyBandMat',\n            () =>\n              new THREE.MeshStandardMaterial({\n                color: 0xffffff,\n                roughness: 0.4,\n              })\n          );\n          const band = new THREE.Mesh(bandGeom, bandMat);\n          band.position.y = 0.05;\n          moneyGroup.add(band);\n        }\n\n        moneyGroup.position.set(targetX, targetY, targetZ);\n        moneyGroup.rotation.y = 0;\n        // No rotation - aligned to grid\n        moneyGroup.castShadow = true;\n\n        // Mark as cash pile money\n        moneyGroup.userData.isCashPileMoney = true;\n        moneyGroup.userData.collected = false;\n        moneyGroup.userData.beingVacuumed = false;\n\n        scene.add(moneyGroup);\n        cashierCashPile.push(moneyGroup);\n\n        return moneyGroup;\n      }\n\n      function addToCashierCashPile() {\n        const cashPileX = cashierStallMesh.userData.cashPileX;\n        const cashPileZ = cashierStallMesh.userData.cashPileZ;\n        const cashPileWidth = cashierStallMesh.userData.cashPileWidth;\n        const cashPileDepth = cashierStallMesh.userData.cashPileDepth;\n\n        // Calculate position in tight grid pattern - money fills tile neatly\n        const stackIndex = cashierCashPile.length;\n        const cols = 4;\n        const rows = 4;\n        const itemsPerLayer = cols * rows;\n        const col = stackIndex % cols;\n        const row = Math.floor((stackIndex % itemsPerLayer) / cols);\n        const layer = Math.floor(stackIndex / itemsPerLayer);\n\n        // Tight grid spacing to fill the tile\n        const cellWidth = (cashPileWidth - 0.4) / cols;\n        const cellDepth = (cashPileDepth - 0.2) / rows;\n\n        const x = cashPileX - (cashPileWidth - 0.4) / 2 + cellWidth / 2 + col * cellWidth;\n        const z = cashPileZ - (cashPileDepth - 0.2) / 2 + cellDepth / 2 + row * cellDepth;\n        const y = 0.25 + layer * 0.12;\n\n        return {\n          x,\n          y,\n          z,\n        };\n      }\n\n      function removeFromCashierCashPile() {\n        if (cashierCashPile.length === 0) return null;\n\n        const money = cashierCashPile.pop();\n        scene.remove(money);\n\n        money.traverse((child) => {\n          if (child.geometry) child.geometry.dispose();\n          if (child.material) {\n            if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n            else child.material.dispose();\n          }\n        });\n\n        cashierEarnings = cashierCashPile.length;\n        updateCashierUI();\n\n        return true;\n      }\n\n      function createResourceNode(index, total) {\n        // Get position from config\n        const grillConfig = window.gameConfig.grills[index];\n        if (!grillConfig) return;\n\n        const x = grillConfig.x;\n        const z = grillConfig.z;\n        const equipmentType = grillConfig.equipmentType || 'grill';\n\n        const node = {\n          group: new THREE.Group(),\n          position: {\n            x,\n            z,\n          },\n          isReady: true,\n          regenTimer: 0,\n          grillMesh: null,\n          emptyMesh: null,\n          equipmentType: equipmentType,\n        };\n\n        node.group.position.set(x, 0, z);\n\n        // Create equipment based on type\n        node.grillMesh = new THREE.Group();\n        node.emptyMesh = new THREE.Group();\n\n        if (equipmentType === 'grill') {\n          createGrillEquipment(node);\n        } else if (equipmentType === 'fryer') {\n          createFryerEquipment(node);\n        } else if (equipmentType === 'drink_station') {\n          createDrinkStationEquipment(node);\n        } else {\n          // Default to grill\n          createGrillEquipment(node);\n        }\n\n        node.emptyMesh.visible = false;\n        node.group.add(node.grillMesh);\n        node.group.add(node.emptyMesh);\n\n        // Make equipment face forward (toward customers)\n        // allow saved rotation on grills (degrees) or default to facing forward\n        const grillCfg =\n          (window.gameConfig && window.gameConfig.grills && window.gameConfig.grills[index]) ||\n          null;\n        const grillRot =\n          (grillCfg && (grillCfg.rotation || grillCfg.rotationDeg || grillCfg.rotationY)) || 180;\n        node.group.rotation.y = THREE.MathUtils.degToRad(grillRot);\n\n        scene.add(node.group);\n        resourceNodes.push(node);\n      }\n\n      function createGrillEquipment(node) {\n        // Grill base/table\n        const tableGeometry = new THREE.BoxGeometry(2, 1, 1.5);\n        const tableMaterial = new THREE.MeshStandardMaterial({\n          color: 0x606060,\n          roughness: 0.4,\n          metalness: 0.6,\n        });\n        const table = new THREE.Mesh(tableGeometry, tableMaterial);\n        table.position.y = 0.5;\n        table.castShadow = true;\n        table.receiveShadow = true;\n        node.grillMesh.add(table);\n\n        // Grill top surface (flat top grill)\n        const grillTopGeometry = new THREE.BoxGeometry(1.8, 0.1, 1.3);\n        const grillTopMaterial = new THREE.MeshStandardMaterial({\n          color: 0x333333,\n          roughness: 0.7,\n          metalness: 0.3,\n        });\n        const grillTop = new THREE.Mesh(grillTopGeometry, grillTopMaterial);\n        grillTop.position.y = 1.05;\n        grillTop.castShadow = true;\n        node.grillMesh.add(grillTop);\n\n        // Burger patties on grill\n        const burgerColors = [0xcd853f, 0xd2691e, 0xb8860b];\n        for (let i = 0; i < 4; i++) {\n          const burgerGeometry = new THREE.CylinderGeometry(0.25, 0.25, 0.15, 12);\n          const burgerMaterial = new THREE.MeshStandardMaterial({\n            color: burgerColors[i % burgerColors.length],\n            roughness: 0.8,\n          });\n          const burger = new THREE.Mesh(burgerGeometry, burgerMaterial);\n          burger.position.set((i % 2) * 0.6 - 0.3, 1.15, Math.floor(i / 2) * 0.5 - 0.25);\n          burger.castShadow = true;\n          node.grillMesh.add(burger);\n        }\n\n        // Steam particles\n        for (let i = 0; i < 3; i++) {\n          const steamGeometry = new THREE.CylinderGeometry(0.05, 0.1, 0.4, 8);\n          const steamMaterial = new THREE.MeshBasicMaterial({\n            color: 0xffffff,\n            transparent: true,\n            opacity: 0.4,\n          });\n          const steam = new THREE.Mesh(steamGeometry, steamMaterial);\n          steam.position.set((i - 1) * 0.4, 1.5 + Math.random() * 0.3, 0);\n          steam.userData.steamOffset = i * 0.5;\n          node.grillMesh.add(steam);\n        }\n\n        // Empty state\n        const emptyTable = new THREE.Mesh(tableGeometry, tableMaterial);\n        emptyTable.position.y = 0.5;\n        emptyTable.castShadow = true;\n        node.emptyMesh.add(emptyTable);\n\n        const emptyGrillTop = new THREE.Mesh(grillTopGeometry, grillTopMaterial);\n        emptyGrillTop.position.y = 1.05;\n        emptyGrillTop.castShadow = true;\n        node.emptyMesh.add(emptyGrillTop);\n      }\n\n      function createFryerEquipment(node) {\n        // Fryer base (stainless steel look)\n        const baseGeometry = new THREE.BoxGeometry(2, 1.2, 1.5);\n        const baseMaterial = new THREE.MeshStandardMaterial({\n          color: 0xc0c0c0,\n          roughness: 0.3,\n          metalness: 0.7,\n        });\n        const base = new THREE.Mesh(baseGeometry, baseMaterial);\n        base.position.y = 0.6;\n        base.castShadow = true;\n        base.receiveShadow = true;\n        node.grillMesh.add(base);\n\n        // Oil vat (darker inside)\n        const vatGeometry = new THREE.BoxGeometry(1.6, 0.4, 1.2);\n        const vatMaterial = new THREE.MeshStandardMaterial({\n          color: 0xdaa520,\n          // Golden oil color\n          roughness: 0.2,\n          metalness: 0.1,\n        });\n        const vat = new THREE.Mesh(vatGeometry, vatMaterial);\n        vat.position.y = 1.04;\n        node.grillMesh.add(vat);\n\n        // Fry baskets with fries\n        for (let b = 0; b < 2; b++) {\n          const basketGroup = new THREE.Group();\n\n          // Basket wire frame (simplified as box outline)\n          const basketGeometry = new THREE.BoxGeometry(0.5, 0.6, 0.45);\n          const basketMaterial = new THREE.MeshStandardMaterial({\n            color: 0x808080,\n            roughness: 0.4,\n            metalness: 0.6,\n            wireframe: true,\n          });\n          const basket = new THREE.Mesh(basketGeometry, basketMaterial);\n          basket.position.y = 0.3;\n          basketGroup.add(basket);\n\n          // Fries in basket\n          for (let i = 0; i < 8; i++) {\n            const fryGeometry = new THREE.BoxGeometry(0.05, 0.3 + Math.random() * 0.1, 0.05);\n            const fryMaterial = new THREE.MeshStandardMaterial({\n              color: 0xffd700,\n              roughness: 0.6,\n            });\n            const fry = new THREE.Mesh(fryGeometry, fryMaterial);\n            fry.position.set(\n              (Math.random() - 0.5) * 0.35,\n              0.4 + Math.random() * 0.1,\n              (Math.random() - 0.5) * 0.3\n            );\n            fry.rotation.x = (Math.random() - 0.5) * 0.3;\n            fry.rotation.z = (Math.random() - 0.5) * 0.3;\n            basketGroup.add(fry);\n          }\n\n          basketGroup.position.set(b === 0 ? -0.4 : 0.4, 1.0, 0);\n          node.grillMesh.add(basketGroup);\n        }\n\n        // Bubbling effect (small spheres)\n        for (let i = 0; i < 5; i++) {\n          const bubbleGeometry = new THREE.SphereGeometry(0.05, 8, 8);\n          const bubbleMaterial = new THREE.MeshBasicMaterial({\n            color: 0xffffcc,\n            transparent: true,\n            opacity: 0.6,\n          });\n          const bubble = new THREE.Mesh(bubbleGeometry, bubbleMaterial);\n          bubble.position.set(\n            (Math.random() - 0.5) * 1.4,\n            1.15 + Math.random() * 0.1,\n            (Math.random() - 0.5) * 1\n          );\n          bubble.userData.steamOffset = i * 0.3;\n          node.grillMesh.add(bubble);\n        }\n\n        // Empty state\n        const emptyBase = new THREE.Mesh(baseGeometry, baseMaterial);\n        emptyBase.position.y = 0.6;\n        emptyBase.castShadow = true;\n        node.emptyMesh.add(emptyBase);\n\n        const emptyVatMaterial = new THREE.MeshStandardMaterial({\n          color: 0xb8860b,\n          // Darker oil when empty\n          roughness: 0.3,\n          metalness: 0.1,\n        });\n        const emptyVat = new THREE.Mesh(vatGeometry, emptyVatMaterial);\n        emptyVat.position.y = 1.04;\n        node.emptyMesh.add(emptyVat);\n      }\n\n      function createDrinkStationEquipment(node) {\n        // Drink dispenser base\n        const baseGeometry = new THREE.BoxGeometry(2.2, 0.8, 1);\n        const baseMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2c3e50,\n          roughness: 0.3,\n          metalness: 0.4,\n        });\n        const base = new THREE.Mesh(baseGeometry, baseMaterial);\n        base.position.y = 0.4;\n        base.castShadow = true;\n        base.receiveShadow = true;\n        node.grillMesh.add(base);\n\n        // Dispenser unit (tall back panel)\n        const dispenserGeometry = new THREE.BoxGeometry(2, 1.5, 0.4);\n        const dispenserMaterial = new THREE.MeshStandardMaterial({\n          color: 0x34495e,\n          roughness: 0.4,\n          metalness: 0.3,\n        });\n        const dispenser = new THREE.Mesh(dispenserGeometry, dispenserMaterial);\n        dispenser.position.set(0, 1.35, -0.3);\n        dispenser.castShadow = true;\n        node.grillMesh.add(dispenser);\n\n        // Drink brand panels (colorful)\n        const brandColors = [0xe74c3c, 0x3498db, 0x27ae60, 0xf39c12];\n        for (let i = 0; i < 4; i++) {\n          const brandGeometry = new THREE.BoxGeometry(0.4, 0.5, 0.05);\n          const brandMaterial = new THREE.MeshStandardMaterial({\n            color: brandColors[i],\n            roughness: 0.3,\n            emissive: brandColors[i],\n            emissiveIntensity: 0.2,\n          });\n          const brand = new THREE.Mesh(brandGeometry, brandMaterial);\n          brand.position.set(-0.75 + i * 0.5, 1.5, -0.05);\n          node.grillMesh.add(brand);\n        }\n\n        // Dispenser nozzles\n        for (let i = 0; i < 4; i++) {\n          const nozzleGeometry = new THREE.CylinderGeometry(0.05, 0.08, 0.15, 8);\n          const nozzleMaterial = new THREE.MeshStandardMaterial({\n            color: 0x808080,\n            roughness: 0.2,\n            metalness: 0.8,\n          });\n          const nozzle = new THREE.Mesh(nozzleGeometry, nozzleMaterial);\n          nozzle.position.set(-0.75 + i * 0.5, 0.95, 0.2);\n          node.grillMesh.add(nozzle);\n        }\n\n        // Cups ready to be filled\n        for (let i = 0; i < 3; i++) {\n          const cupGeometry = new THREE.CylinderGeometry(0.12, 0.1, 0.35, 12);\n          const cupMaterial = new THREE.MeshStandardMaterial({\n            color: 0xe74c3c,\n            roughness: 0.4,\n          });\n          const cup = new THREE.Mesh(cupGeometry, cupMaterial);\n          cup.position.set(-0.5 + i * 0.5, 0.95, 0.3);\n          cup.castShadow = true;\n          node.grillMesh.add(cup);\n\n          // Lid\n          const lidGeometry = new THREE.CylinderGeometry(0.13, 0.13, 0.03, 12);\n          const lidMaterial = new THREE.MeshStandardMaterial({\n            color: 0xffffff,\n            roughness: 0.3,\n          });\n          const lid = new THREE.Mesh(lidGeometry, lidMaterial);\n          lid.position.set(-0.5 + i * 0.5, 1.15, 0.3);\n          node.grillMesh.add(lid);\n\n          // Straw\n          const strawGeometry = new THREE.CylinderGeometry(0.02, 0.02, 0.2, 8);\n          const strawMaterial = new THREE.MeshStandardMaterial({\n            color: 0xffffff,\n            roughness: 0.4,\n          });\n          const straw = new THREE.Mesh(strawGeometry, strawMaterial);\n          straw.position.set(-0.5 + i * 0.5, 1.25, 0.3);\n          node.grillMesh.add(straw);\n        }\n\n        // Ice cubes indicator\n        for (let i = 0; i < 4; i++) {\n          const iceGeometry = new THREE.BoxGeometry(0.08, 0.08, 0.08);\n          const iceMaterial = new THREE.MeshStandardMaterial({\n            color: 0xadd8e6,\n            transparent: true,\n            opacity: 0.7,\n            roughness: 0.1,\n          });\n          const ice = new THREE.Mesh(iceGeometry, iceMaterial);\n          ice.position.set((Math.random() - 0.5) * 1.5, 1.0, 0.3);\n          ice.rotation.set(Math.random(), Math.random(), Math.random());\n          ice.userData.steamOffset = i * 0.4;\n          node.grillMesh.add(ice);\n        }\n\n        // Empty state\n        const emptyBase = new THREE.Mesh(baseGeometry, baseMaterial);\n        emptyBase.position.y = 0.4;\n        emptyBase.castShadow = true;\n        node.emptyMesh.add(emptyBase);\n\n        const emptyDispenser = new THREE.Mesh(dispenserGeometry, dispenserMaterial);\n        emptyDispenser.position.set(0, 1.35, -0.3);\n        emptyDispenser.castShadow = true;\n        node.emptyMesh.add(emptyDispenser);\n\n        // Empty brand panels (dimmed)\n        for (let i = 0; i < 4; i++) {\n          const brandGeometry = new THREE.BoxGeometry(0.4, 0.5, 0.05);\n          const brandMaterial = new THREE.MeshStandardMaterial({\n            color: brandColors[i],\n            roughness: 0.5,\n            emissive: brandColors[i],\n            emissiveIntensity: 0.05,\n          });\n          const brand = new THREE.Mesh(brandGeometry, brandMaterial);\n          brand.position.set(-0.75 + i * 0.5, 1.5, -0.05);\n          node.emptyMesh.add(brand);\n        }\n      }\n\n      // Get a random menu item from config\n      function getRandomMenuItem() {\n        const menuItems = window.gameConfig.menuItems;\n        if (!menuItems || menuItems.length === 0) {\n          // Fallback to burger\n          return {\n            id: 'burger',\n            name: 'Burger',\n            emoji: '\ud83c\udf54',\n            value: 100,\n            bunColor: '#DEB887',\n            pattyColor: '#8B4513',\n            toppingColor: '#32CD32',\n            accentColor: '#FFA500',\n            equipmentType: 'grill',\n          };\n        }\n        return menuItems[Math.floor(Math.random() * menuItems.length)];\n      }\n\n      // Get menu items for a specific equipment type\n      function getMenuItemsForEquipment(equipmentType) {\n        const menuItems = window.gameConfig.menuItems;\n        if (!menuItems || menuItems.length === 0) return [];\n        return menuItems.filter((item) => item.equipmentType === equipmentType);\n      }\n\n      // Get a random menu item for a specific equipment type\n      function getRandomMenuItemForEquipment(equipmentType) {\n        const items = getMenuItemsForEquipment(equipmentType);\n        if (items.length === 0) {\n          // Fallback based on equipment type\n          if (equipmentType === 'grill') {\n            return {\n              id: 'burger',\n              name: 'Burger',\n              emoji: '\ud83c\udf54',\n              value: 100,\n              bunColor: '#DEB887',\n              pattyColor: '#8B4513',\n              toppingColor: '#32CD32',\n              accentColor: '#FFA500',\n              equipmentType: 'grill',\n            };\n          } else if (equipmentType === 'fryer') {\n            return {\n              id: 'fries',\n              name: 'Fries',\n              emoji: '\ud83c\udf5f',\n              value: 60,\n              bunColor: '#FFD700',\n              pattyColor: '#DAA520',\n              toppingColor: '#FF0000',\n              accentColor: '#FFFFFF',\n              equipmentType: 'fryer',\n            };\n          } else if (equipmentType === 'drink_station') {\n            return {\n              id: 'drink',\n              name: 'Drink',\n              emoji: '\ud83e\udd64',\n              value: 40,\n              bunColor: '#E74C3C',\n              pattyColor: '#8B4513',\n              toppingColor: '#FFFFFF',\n              accentColor: '#3498DB',\n              equipmentType: 'drink_station',\n            };\n          }\n          return getRandomMenuItem();\n        }\n        return items[Math.floor(Math.random() * items.length)];\n      }\n\n      function createStackItem(menuItem = null) {\n        // Get menu item (random if not specified)\n        const item = menuItem || getRandomMenuItem();\n        const foodGroup = new THREE.Group();\n\n        // Store item data for value calculation\n        foodGroup.userData.menuItem = item;\n\n        // Create food based on type\n        const bunColor = new THREE.Color(item.bunColor);\n        const pattyColor = new THREE.Color(item.pattyColor);\n        const toppingColor = new THREE.Color(item.toppingColor);\n        const accentColor = new THREE.Color(item.accentColor);\n\n        if (item.id === 'burger' || item.id === 'bigmac' || item.id === 'mcchicken') {\n          // Classic burger / Big Mac / McChicken\n          const bottomBun = new THREE.Mesh(\n            new THREE.CylinderGeometry(STACK_ITEM_RADIUS, STACK_ITEM_RADIUS * 1.1, 0.1, 12),\n            new THREE.MeshStandardMaterial({\n              color: bunColor,\n              roughness: 0.7,\n            })\n          );\n          bottomBun.position.y = 0.05;\n          foodGroup.add(bottomBun);\n\n          const patty = new THREE.Mesh(\n            new THREE.CylinderGeometry(STACK_ITEM_RADIUS * 0.9, STACK_ITEM_RADIUS * 0.9, 0.08, 12),\n            new THREE.MeshStandardMaterial({\n              color: pattyColor,\n              roughness: 0.8,\n            })\n          );\n          patty.position.y = 0.14;\n          foodGroup.add(patty);\n\n          const cheese = new THREE.Mesh(\n            new THREE.BoxGeometry(STACK_ITEM_RADIUS * 1.4, 0.03, STACK_ITEM_RADIUS * 1.4),\n            new THREE.MeshStandardMaterial({\n              color: accentColor,\n              roughness: 0.5,\n            })\n          );\n          cheese.position.y = 0.19;\n          cheese.rotation.y = Math.PI / 4;\n          foodGroup.add(cheese);\n\n          const lettuce = new THREE.Mesh(\n            new THREE.CylinderGeometry(STACK_ITEM_RADIUS * 1.05, STACK_ITEM_RADIUS, 0.04, 12),\n            new THREE.MeshStandardMaterial({\n              color: toppingColor,\n              roughness: 0.8,\n            })\n          );\n          lettuce.position.y = 0.23;\n          foodGroup.add(lettuce);\n\n          const topBun = new THREE.Mesh(\n            new THREE.SphereGeometry(STACK_ITEM_RADIUS, 12, 8, 0, Math.PI * 2, 0, Math.PI / 2),\n            new THREE.MeshStandardMaterial({\n              color: bunColor,\n              roughness: 0.7,\n            })\n          );\n          topBun.position.y = 0.27;\n          foodGroup.add(topBun);\n\n          // Big Mac gets extra patty and bun\n          if (item.id === 'bigmac') {\n            const middleBun = new THREE.Mesh(\n              new THREE.CylinderGeometry(\n                STACK_ITEM_RADIUS * 0.95,\n                STACK_ITEM_RADIUS * 0.95,\n                0.06,\n                12\n              ),\n              new THREE.MeshStandardMaterial({\n                color: bunColor,\n                roughness: 0.7,\n              })\n            );\n            middleBun.position.y = 0.32;\n            foodGroup.add(middleBun);\n\n            const patty2 = new THREE.Mesh(\n              new THREE.CylinderGeometry(\n                STACK_ITEM_RADIUS * 0.85,\n                STACK_ITEM_RADIUS * 0.85,\n                0.07,\n                12\n              ),\n              new THREE.MeshStandardMaterial({\n                color: pattyColor,\n                roughness: 0.8,\n              })\n            );\n            patty2.position.y = 0.38;\n            foodGroup.add(patty2);\n          }\n        } else if (item.id === 'fries') {\n          // French fries container\n          const container = new THREE.Mesh(\n            new THREE.BoxGeometry(STACK_ITEM_RADIUS * 1.2, 0.3, STACK_ITEM_RADIUS * 0.8),\n            new THREE.MeshStandardMaterial({\n              color: 0xcc0000,\n              roughness: 0.5,\n            })\n          );\n          container.position.y = 0.15;\n          foodGroup.add(container);\n\n          // Fries sticking out\n          for (let i = 0; i < 8; i++) {\n            const fry = new THREE.Mesh(\n              new THREE.BoxGeometry(0.06, 0.25 + Math.random() * 0.1, 0.06),\n              new THREE.MeshStandardMaterial({\n                color: bunColor,\n                roughness: 0.6,\n              })\n            );\n            fry.position.set(\n              (Math.random() - 0.5) * STACK_ITEM_RADIUS * 0.8,\n              0.35 + Math.random() * 0.1,\n              (Math.random() - 0.5) * STACK_ITEM_RADIUS * 0.5\n            );\n            fry.rotation.x = (Math.random() - 0.5) * 0.3;\n            fry.rotation.z = (Math.random() - 0.5) * 0.3;\n            foodGroup.add(fry);\n          }\n        } else if (item.id === 'nuggets') {\n          // Chicken nuggets box\n          const box = new THREE.Mesh(\n            new THREE.BoxGeometry(STACK_ITEM_RADIUS * 1.4, 0.2, STACK_ITEM_RADIUS * 1),\n            new THREE.MeshStandardMaterial({\n              color: 0xcc0000,\n              roughness: 0.5,\n            })\n          );\n          box.position.y = 0.1;\n          foodGroup.add(box);\n\n          // Nuggets (rounded rectangles)\n          for (let i = 0; i < 6; i++) {\n            const nuggetGeom = new THREE.SphereGeometry(0.12, 8, 6);\n            nuggetGeom.scale(1, 0.5, 0.8);\n            const nugget = new THREE.Mesh(\n              nuggetGeom,\n              new THREE.MeshStandardMaterial({\n                color: pattyColor,\n                roughness: 0.7,\n              })\n            );\n            nugget.position.set(((i % 3) - 1) * 0.18, 0.25, (Math.floor(i / 3) - 0.5) * 0.18);\n            nugget.rotation.y = Math.random() * 0.5;\n            nugget.castShadow = true;\n            foodGroup.add(nugget);\n          }\n        } else if (item.id === 'drink') {\n          // Drink cup\n          const cupGeometry = new THREE.CylinderGeometry(\n            STACK_ITEM_RADIUS * 0.7,\n            STACK_ITEM_RADIUS * 0.5,\n            0.5,\n            12\n          );\n          const cupMaterial = new THREE.MeshStandardMaterial({\n            color: pattyColor,\n            roughness: 0.4,\n          });\n          const cup = new THREE.Mesh(cupGeometry, cupMaterial);\n          cup.position.y = 0.25;\n          cup.castShadow = true;\n          foodGroup.add(cup);\n\n          // Cup lid\n          const lidGeometry = new THREE.CylinderGeometry(\n            STACK_ITEM_RADIUS * 0.72,\n            STACK_ITEM_RADIUS * 0.72,\n            0.05,\n            12\n          );\n          const lidMaterial = new THREE.MeshStandardMaterial({\n            color: toppingColor,\n            roughness: 0.3,\n          });\n          const lid = new THREE.Mesh(lidGeometry, lidMaterial);\n          lid.position.y = 0.53;\n          foodGroup.add(lid);\n\n          // Straw\n          const strawGeometry = new THREE.CylinderGeometry(0.03, 0.03, 0.3, 8);\n          const strawMaterial = new THREE.MeshStandardMaterial({\n            color: 0xffffff,\n            roughness: 0.4,\n          });\n          const straw = new THREE.Mesh(strawGeometry, strawMaterial);\n          straw.position.y = 0.65;\n          foodGroup.add(straw);\n\n          // Brand stripe on cup\n          const stripeGeometry = new THREE.CylinderGeometry(\n            STACK_ITEM_RADIUS * 0.71,\n            STACK_ITEM_RADIUS * 0.52,\n            0.15,\n            12\n          );\n          const stripeMaterial = new THREE.MeshStandardMaterial({\n            color: accentColor,\n            roughness: 0.4,\n          });\n          const stripe = new THREE.Mesh(stripeGeometry, stripeMaterial);\n          stripe.position.y = 0.25;\n          foodGroup.add(stripe);\n        } else if (item.id === 'mcflurry') {\n          // McFlurry cup\n          const cupGeometry = new THREE.CylinderGeometry(\n            STACK_ITEM_RADIUS * 0.8,\n            STACK_ITEM_RADIUS * 0.6,\n            0.4,\n            12\n          );\n          const cupMaterial = new THREE.MeshStandardMaterial({\n            color: toppingColor,\n            roughness: 0.3,\n          });\n          const cup = new THREE.Mesh(cupGeometry, cupMaterial);\n          cup.position.y = 0.2;\n          cup.castShadow = true;\n          foodGroup.add(cup);\n\n          // Ice cream swirl on top\n          const iceCreamGeometry = new THREE.SphereGeometry(STACK_ITEM_RADIUS * 0.6, 12, 12);\n          const iceCreamMaterial = new THREE.MeshStandardMaterial({\n            color: bunColor,\n            roughness: 0.4,\n          });\n          const iceCream = new THREE.Mesh(iceCreamGeometry, iceCreamMaterial);\n          iceCream.position.y = 0.45;\n          iceCream.scale.y = 0.7;\n          foodGroup.add(iceCream);\n\n          // Toppings (chocolate/oreo pieces)\n          for (let i = 0; i < 8; i++) {\n            const toppingGeom = new THREE.BoxGeometry(0.06, 0.04, 0.06);\n            const topping = new THREE.Mesh(\n              toppingGeom,\n              new THREE.MeshStandardMaterial({\n                color: pattyColor,\n                roughness: 0.6,\n              })\n            );\n            const angle = (i / 8) * Math.PI * 2;\n            const radius = 0.15 + Math.random() * 0.1;\n            topping.position.set(\n              Math.cos(angle) * radius,\n              0.5 + Math.random() * 0.1,\n              Math.sin(angle) * radius\n            );\n            topping.rotation.set(Math.random(), Math.random(), Math.random());\n            foodGroup.add(topping);\n          }\n\n          // Spoon\n          const spoonHandle = new THREE.Mesh(\n            new THREE.BoxGeometry(0.04, 0.25, 0.02),\n            new THREE.MeshStandardMaterial({\n              color: 0xffffff,\n              roughness: 0.3,\n            })\n          );\n          spoonHandle.position.set(0.2, 0.55, 0);\n          spoonHandle.rotation.z = 0.3;\n          foodGroup.add(spoonHandle);\n        } else {\n          // Default fallback - simple burger\n          const bottomBun = new THREE.Mesh(\n            new THREE.CylinderGeometry(STACK_ITEM_RADIUS, STACK_ITEM_RADIUS * 1.1, 0.1, 12),\n            new THREE.MeshStandardMaterial({\n              color: bunColor,\n              roughness: 0.7,\n            })\n          );\n          bottomBun.position.y = 0.05;\n          foodGroup.add(bottomBun);\n\n          const patty = new THREE.Mesh(\n            new THREE.CylinderGeometry(STACK_ITEM_RADIUS * 0.9, STACK_ITEM_RADIUS * 0.9, 0.08, 12),\n            new THREE.MeshStandardMaterial({\n              color: pattyColor,\n              roughness: 0.8,\n            })\n          );\n          patty.position.y = 0.14;\n          foodGroup.add(patty);\n\n          const topBun = new THREE.Mesh(\n            new THREE.SphereGeometry(STACK_ITEM_RADIUS, 12, 8, 0, Math.PI * 2, 0, Math.PI / 2),\n            new THREE.MeshStandardMaterial({\n              color: bunColor,\n              roughness: 0.7,\n            })\n          );\n          topBun.position.y = 0.2;\n          foodGroup.add(topBun);\n        }\n\n        // Add slight random rotation for variety\n        foodGroup.rotation.y = (Math.random() - 0.5) * 0.5;\n        foodGroup.castShadow = true;\n\n        return foodGroup;\n      }\n\n      function createGroundItem(position, menuItem = null) {\n        // Create a plate with food for ground pickup\n        const itemGroup = new THREE.Group();\n\n        // Get menu item (random if not specified)\n        const item = menuItem || getRandomMenuItem();\n        itemGroup.userData.menuItem = item;\n\n        const bunColor = new THREE.Color(item.bunColor);\n        const pattyColor = new THREE.Color(item.pattyColor);\n        const toppingColor = new THREE.Color(item.toppingColor);\n        const accentColor = item.accentColor ? new THREE.Color(item.accentColor) : bunColor;\n\n        // Plate (not for drinks)\n        if (item.id !== 'drink' && item.id !== 'mcflurry') {\n          const plate = new THREE.Mesh(\n            new THREE.CylinderGeometry(0.5, 0.45, 0.08, 16),\n            new THREE.MeshStandardMaterial({\n              color: 0xffffff,\n              roughness: 0.3,\n              metalness: 0.1,\n            })\n          );\n          plate.position.y = 0.04;\n          plate.castShadow = true;\n          plate.receiveShadow = true;\n          itemGroup.add(plate);\n        }\n\n        // Create food item based on type\n        if (item.id === 'burger' || item.id === 'bigmac' || item.id === 'mcchicken') {\n          const burger = new THREE.Mesh(\n            new THREE.CylinderGeometry(0.3, 0.35, 0.25, 10),\n            new THREE.MeshStandardMaterial({\n              color: bunColor,\n              roughness: 0.7,\n            })\n          );\n          burger.position.y = 0.2;\n          burger.castShadow = true;\n          itemGroup.add(burger);\n\n          const patty = new THREE.Mesh(\n            new THREE.CylinderGeometry(0.32, 0.32, 0.06, 10),\n            new THREE.MeshStandardMaterial({\n              color: pattyColor,\n              roughness: 0.8,\n            })\n          );\n          patty.position.y = 0.18;\n          patty.castShadow = true;\n          itemGroup.add(patty);\n\n          // Big Mac extra layer\n          if (item.id === 'bigmac') {\n            const extraPatty = new THREE.Mesh(\n              new THREE.CylinderGeometry(0.28, 0.28, 0.05, 10),\n              new THREE.MeshStandardMaterial({\n                color: pattyColor,\n                roughness: 0.8,\n              })\n            );\n            extraPatty.position.y = 0.28;\n            itemGroup.add(extraPatty);\n          }\n        } else if (item.id === 'fries') {\n          const container = new THREE.Mesh(\n            new THREE.BoxGeometry(0.35, 0.2, 0.25),\n            new THREE.MeshStandardMaterial({\n              color: 0xcc0000,\n              roughness: 0.5,\n            })\n          );\n          container.position.y = 0.18;\n          itemGroup.add(container);\n\n          // Fries\n          for (let i = 0; i < 5; i++) {\n            const fry = new THREE.Mesh(\n              new THREE.BoxGeometry(0.04, 0.18, 0.04),\n              new THREE.MeshStandardMaterial({\n                color: bunColor,\n                roughness: 0.6,\n              })\n            );\n            fry.position.set((Math.random() - 0.5) * 0.25, 0.32, (Math.random() - 0.5) * 0.15);\n            fry.rotation.x = (Math.random() - 0.5) * 0.3;\n            itemGroup.add(fry);\n          }\n        } else if (item.id === 'nuggets') {\n          // Nugget box\n          const box = new THREE.Mesh(\n            new THREE.BoxGeometry(0.4, 0.12, 0.3),\n            new THREE.MeshStandardMaterial({\n              color: 0xcc0000,\n              roughness: 0.5,\n            })\n          );\n          box.position.y = 0.12;\n          itemGroup.add(box);\n\n          // Nuggets\n          for (let i = 0; i < 4; i++) {\n            const nuggetGeom = new THREE.SphereGeometry(0.08, 6, 4);\n            nuggetGeom.scale(1, 0.5, 0.8);\n            const nugget = new THREE.Mesh(\n              nuggetGeom,\n              new THREE.MeshStandardMaterial({\n                color: pattyColor,\n                roughness: 0.7,\n              })\n            );\n            nugget.position.set(((i % 2) - 0.5) * 0.15, 0.22, (Math.floor(i / 2) - 0.5) * 0.12);\n            nugget.castShadow = true;\n            itemGroup.add(nugget);\n          }\n        } else if (item.id === 'drink') {\n          // Drink cup (no plate)\n          const cup = new THREE.Mesh(\n            new THREE.CylinderGeometry(0.18, 0.14, 0.4, 12),\n            new THREE.MeshStandardMaterial({\n              color: pattyColor,\n              roughness: 0.4,\n            })\n          );\n          cup.position.y = 0.2;\n          cup.castShadow = true;\n          itemGroup.add(cup);\n\n          // Lid\n          const lid = new THREE.Mesh(\n            new THREE.CylinderGeometry(0.19, 0.19, 0.04, 12),\n            new THREE.MeshStandardMaterial({\n              color: toppingColor,\n              roughness: 0.3,\n            })\n          );\n          lid.position.y = 0.42;\n          itemGroup.add(lid);\n\n          // Straw\n          const straw = new THREE.Mesh(\n            new THREE.CylinderGeometry(0.02, 0.02, 0.2, 8),\n            new THREE.MeshStandardMaterial({\n              color: 0xffffff,\n              roughness: 0.4,\n            })\n          );\n          straw.position.y = 0.52;\n          itemGroup.add(straw);\n\n          // Brand stripe\n          const stripe = new THREE.Mesh(\n            new THREE.CylinderGeometry(0.185, 0.145, 0.12, 12),\n            new THREE.MeshStandardMaterial({\n              color: accentColor,\n              roughness: 0.4,\n            })\n          );\n          stripe.position.y = 0.2;\n          itemGroup.add(stripe);\n        } else if (item.id === 'mcflurry') {\n          // McFlurry cup (no plate)\n          const cup = new THREE.Mesh(\n            new THREE.CylinderGeometry(0.2, 0.15, 0.3, 12),\n            new THREE.MeshStandardMaterial({\n              color: toppingColor,\n              roughness: 0.3,\n            })\n          );\n          cup.position.y = 0.15;\n          cup.castShadow = true;\n          itemGroup.add(cup);\n\n          // Ice cream\n          const iceCream = new THREE.Mesh(\n            new THREE.SphereGeometry(0.15, 10, 10),\n            new THREE.MeshStandardMaterial({\n              color: bunColor,\n              roughness: 0.4,\n            })\n          );\n          iceCream.position.y = 0.35;\n          iceCream.scale.y = 0.7;\n          itemGroup.add(iceCream);\n\n          // Toppings\n          for (let i = 0; i < 5; i++) {\n            const topping = new THREE.Mesh(\n              new THREE.BoxGeometry(0.04, 0.03, 0.04),\n              new THREE.MeshStandardMaterial({\n                color: pattyColor,\n                roughness: 0.6,\n              })\n            );\n            const angle = (i / 5) * Math.PI * 2;\n            topping.position.set(Math.cos(angle) * 0.1, 0.38, Math.sin(angle) * 0.1);\n            itemGroup.add(topping);\n          }\n        } else {\n          // Default fallback - burger\n          const burger = new THREE.Mesh(\n            new THREE.CylinderGeometry(0.3, 0.35, 0.25, 10),\n            new THREE.MeshStandardMaterial({\n              color: bunColor,\n              roughness: 0.7,\n            })\n          );\n          burger.position.y = 0.2;\n          burger.castShadow = true;\n          itemGroup.add(burger);\n        }\n\n        itemGroup.position.copy(position);\n        itemGroup.position.y = 0.1;\n        itemGroup.rotation.y = Math.random() * Math.PI * 2;\n\n        // Add physics-like properties\n        itemGroup.userData.velocity = {\n          x: (Math.random() - 0.5) * 3,\n          z: (Math.random() - 0.5) * 3,\n        };\n        itemGroup.userData.collected = false;\n\n        scene.add(itemGroup);\n        groundItems.push(itemGroup);\n\n        return itemGroup;\n      }\n\n      function addToStack(menuItem = null) {\n        const stackY = PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT;\n        const food = createStackItem(menuItem);\n        food.position.y = stackY;\n        player.add(food);\n        playerStack.push(food);\n        currentStackCount = playerStack.length;\n        updateStackUI();\n        playSound('pickup_sound');\n      }\n\n      function removeFromStack() {\n        if (playerStack.length > 0) {\n          const item = playerStack.pop();\n          player.remove(item);\n\n          // Dispose of all children geometries and materials\n          item.traverse((child) => {\n            if (child.geometry) child.geometry.dispose();\n            if (child.material) {\n              if (Array.isArray(child.material)) {\n                child.material.forEach((mat) => mat.dispose());\n              } else {\n                child.material.dispose();\n              }\n            }\n          });\n\n          currentStackCount = playerStack.length;\n          updateStackUI();\n          return true;\n        }\n        return false;\n      }\n\n      // ============================================\n      // 3D MODEL LOADING\n      // ============================================\n\n      async function loadStickmanModel() {\n        // Support asset IDs from the host asset map as well as full URLs.\n        let STICKMAN_MODEL_URL = window.gameConfig.stickmanModelURL;\n\n        // If the config value looks like an asset id (no protocol), try resolving via lib.getAsset\n        try {\n          if (STICKMAN_MODEL_URL && typeof STICKMAN_MODEL_URL === 'string' && !/^https?:\\/\\//i.test(STICKMAN_MODEL_URL)) {\n            const assetInfo = lib.getAsset(STICKMAN_MODEL_URL);\n            if (assetInfo && assetInfo.url) {\n              STICKMAN_MODEL_URL = assetInfo.url;\n            }\n          }\n        } catch (e) {\n          lib.log('Error resolving stickman asset id: ' + (e && e.message ? e.message : e));\n        }\n\n        return new Promise((resolve, reject) => {\n          if (stickmanModelLoaded && stickmanModelTemplate) {\n            resolve(stickmanModelTemplate);\n            return;\n          }\n\n          if (!gltfLoader) {\n            gltfLoader = new THREE.GLTFLoader();\n          }\n          if (!dracoLoader) {\n            dracoLoader = new THREE.DRACOLoader();\n            dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');\n          }\n          gltfLoader.setDRACOLoader(dracoLoader);\n\n          gltfLoader.load(\n            STICKMAN_MODEL_URL,\n            (gltf) => {\n              stickmanModelTemplate = gltf;\n              stickmanModelLoaded = true;\n              lib.log('Stickman model loaded successfully');\n              resolve(gltf);\n            },\n            (progress) => {\n              // Loading progress\n            },\n            (error) => {\n              lib.log('Error loading stickman model: ' + (error && error.message ? error.message : error));\n              reject(error);\n            }\n          );\n        });\n      }\n\n      function createCrownHat() {\n        // Create a royal crown for VIP customers\n        const crownGroup = new THREE.Group();\n\n        const crownMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffd700,\n          roughness: 0.2,\n          metalness: 0.8,\n          emissive: 0xffaa00,\n          emissiveIntensity: 0.3,\n        });\n\n        // Crown base\n        const crownBase = new THREE.Mesh(\n          new THREE.CylinderGeometry(0.35, 0.38, 0.18, 16),\n          crownMaterial\n        );\n        crownBase.position.y = 0;\n        crownBase.castShadow = true;\n        crownGroup.add(crownBase);\n\n        // Crown points (5 tall points around the rim)\n        for (let i = 0; i < 5; i++) {\n          const angle = (i / 5) * Math.PI * 2;\n          const point = new THREE.Mesh(new THREE.ConeGeometry(0.1, 0.35, 8), crownMaterial);\n          point.position.set(Math.cos(angle) * 0.28, 0.25, Math.sin(angle) * 0.28);\n          point.castShadow = true;\n          crownGroup.add(point);\n        }\n\n        // Gems on each point (alternating red and blue)\n        const gemColors = [0xe74c3c, 0x3498db, 0xe74c3c, 0x9b59b6, 0x2ecc71];\n        for (let i = 0; i < 5; i++) {\n          const angle = (i / 5) * Math.PI * 2;\n          const gemMaterial = new THREE.MeshStandardMaterial({\n            color: gemColors[i],\n            roughness: 0.1,\n            metalness: 0.5,\n            emissive: gemColors[i],\n            emissiveIntensity: 0.4,\n          });\n          const gem = new THREE.Mesh(new THREE.OctahedronGeometry(0.06, 0), gemMaterial);\n          gem.position.set(Math.cos(angle) * 0.28, 0.45, Math.sin(angle) * 0.28);\n          gem.rotation.z = Math.PI / 4;\n          crownGroup.add(gem);\n        }\n\n        // Center large gem on front\n        const centerGemMaterial = new THREE.MeshStandardMaterial({\n          color: 0xe74c3c,\n          roughness: 0.1,\n          metalness: 0.5,\n          emissive: 0xe74c3c,\n          emissiveIntensity: 0.5,\n        });\n        const centerGem = new THREE.Mesh(new THREE.OctahedronGeometry(0.1, 0), centerGemMaterial);\n        centerGem.position.set(0, 0.1, 0.35);\n        centerGem.rotation.z = Math.PI / 4;\n        crownGroup.add(centerGem);\n\n        return crownGroup;\n      }\n\n      function createMcDonaldsHat(isPlayer = false) {\n        // Create a McDonald's crew visor cap\n        const hatGroup = new THREE.Group();\n\n        // Main cap body (red)\n        const capGeometry = new THREE.SphereGeometry(0.35, 16, 8, 0, Math.PI * 2, 0, Math.PI / 2);\n        const capMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2b2428,\n          // McDonald's red\n          roughness: 0.5,\n          metalness: 0.1,\n        });\n        const cap = new THREE.Mesh(capGeometry, capMaterial);\n        cap.scale.set(1, 0.6, 1);\n        cap.position.y = 0;\n        cap.position.z = -0.067;\n        cap.castShadow = true;\n        hatGroup.add(cap);\n\n        // Visor/brim (also red)\n        const visorGeometry = new THREE.CylinderGeometry(\n          0.4,\n          0.45,\n          0.06,\n          16,\n          1,\n          false,\n          -Math.PI / 2,\n          Math.PI\n        );\n        const visorMaterial = new THREE.MeshStandardMaterial({\n          color: 0x2b2428,\n          roughness: 0.4,\n          metalness: 0.1,\n        });\n        const visor = new THREE.Mesh(visorGeometry, visorMaterial);\n        visor.rotation.x = THREE.MathUtils.degToRad(8.37);\n        visor.rotation.z = THREE.MathUtils.degToRad(180);\n        visor.position.set(0, -0.023, 0.098);\n        visor.scale.set(0.692, 1, 0.776);\n        visor.castShadow = true;\n        hatGroup.add(visor);\n\n        // Golden arches \"M\" logo\n        const archMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffc72c,\n          // McDonald's gold\n          roughness: 0.3,\n          metalness: 0.4,\n          emissive: 0xffa500,\n          emissiveIntensity: 0.2,\n        });\n\n        // Left arch of M\n        const leftArch = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.15, 0.02), archMaterial);\n        leftArch.position.set(-0.08, 0.05, 0.32);\n        leftArch.rotation.z = -0.2;\n        hatGroup.add(leftArch);\n\n        // Right arch of M\n        const rightArch = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.15, 0.02), archMaterial);\n        rightArch.position.set(0.08, 0.05, 0.32);\n        rightArch.rotation.z = 0.2;\n        hatGroup.add(rightArch);\n\n        // Middle connection\n        const middleBar = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.06, 0.02), archMaterial);\n        middleBar.position.set(0, 0.02, 0.32);\n        hatGroup.add(middleBar);\n\n        hatGroup.position.set(0, 0, 0.082);\n        hatGroup.scale.set(0.75, 0.75, 0.75);\n        return hatGroup;\n      }\n\n      // function createStickmanInstance(color = 0xFFFFFF) {\n      function createStickmanInstance() {\n        if (!stickmanModelLoaded || !stickmanModelTemplate) {\n          // Fallback to simple mesh if model not loaded\n          return createFallbackCharacter(color);\n        }\n\n        const color = 0xfff0ff;\n\n        const characterGroup = new THREE.Group();\n\n        // Clone the model\n        const model =\n          typeof THREE.SkeletonUtils !== 'undefined'\n            ? THREE.SkeletonUtils.clone(stickmanModelTemplate.scene)\n            : stickmanModelTemplate.scene.clone();\n\n        // Auto-scale to height of ~2.0\n        const box = new THREE.Box3().setFromObject(stickmanModelTemplate.scene);\n        const size = box.getSize(new THREE.Vector3());\n        if (size.y > 0.1) {\n          const targetScale = 3.0 / size.y;\n          model.scale.set(targetScale, targetScale, targetScale);\n        } else {\n          model.scale.set(1.5, 1.5, 1.5);\n        }\n        model.position.y = -0.15;\n\n        // Apply color tint to the model\n        model.traverse((child) => {\n          if (child.isMesh) {\n            child.castShadow = true;\n            child.receiveShadow = true;\n            // Create a reliable material to avoid black rendering\n            child.material = new THREE.MeshStandardMaterial({\n              color: color === 0xffffff ? 0xdddddd : color,\n              roughness: 0.6,\n              metalness: 0.1,\n              skinning: !!child.isSkinnedMesh,\n            });\n          }\n        });\n\n        characterGroup.add(model);\n\n        // Add McDonald's hat for staff members (not player)\n        if (color !== 0xffffff) {\n          const mcHat = createMcDonaldsHat();\n          // mcHat.position.y = 2.4*1.75; // Position on top of head\n          // characterGroup.add(mcHat);\n          characterGroup.userData.mcHat = mcHat;\n\n          // find the head bone or mesh\n          const head = model.getObjectByName('head_leaf');\n          // name depends on your model\n          // console.log(head);\n          // attach hat to head\n          if (head) {\n            head.add(mcHat);\n\n            // position hat relative to head\n            mcHat.position.set(0, 0.3, 0);\n          }\n        }\n\n        // Setup animation mixer if animations exist\n        let mixer = null;\n        const animActions = {};\n\n        if (stickmanModelTemplate.animations && stickmanModelTemplate.animations.length > 0) {\n          mixer = new THREE.AnimationMixer(model);\n          mixer.timeScale = 3.0;\n          const clips = stickmanModelTemplate.animations;\n\n          // Find and store specific animation clips by name\n          clips.forEach((clip) => {\n            const clipName = clip.name.toLowerCase();\n            // console.log(clipName);\n            const action = mixer.clipAction(clip);\n\n            // idle_foldarms_loop\n            // idle_loop\n            // interact\n            // jog_fwd_loop\n            // walk_carry_loop\n            // walk_formal_loop\n            if (clipName.includes('idle_foldarms_loop') || clipName.includes('idle_foldarms')) {\n              animActions.idleFoldArms = action;\n            } else if (clipName.includes('idle_loop')) {\n              animActions.idle = action;\n            } else if (clipName.includes('walk_carry_loop') || clipName.includes('walk_carry')) {\n              animActions.walkCarry = action;\n            } else if (clipName.includes('jog_fwd_loop')) {\n              animActions.run = action;\n            } else if (clipName.includes('walk_formal_loop') || clipName.includes('walk_formal')) {\n              animActions.walk = action;\n            } else if (\n              clipName.includes('interact') ||\n              clipName.includes('action') ||\n              clipName.includes('work')\n            ) {\n              animActions.interact = action;\n            }\n          });\n\n          // Fallback: if specific animations not found, use first clip for all\n          if (!animActions.idle && clips.length > 0) {\n            animActions.idle = mixer.clipAction(clips[0]);\n          }\n          if (!animActions.run) {\n            animActions.run = animActions.idle;\n          }\n          if (!animActions.walk) {\n            animActions.walk = animActions.run || animActions.idle;\n          }\n          if (!animActions.interact) {\n            animActions.interact = animActions.idle;\n          }\n          if (!animActions.walkCarry) {\n            animActions.walkCarry = animActions.run || animActions.idle;\n          }\n          if (!animActions.idleFoldArms) {\n            animActions.idleFoldArms = animActions.idle;\n          }\n\n          // Start with idle animation\n          if (animActions.idle) {\n            animActions.idle.play();\n          }\n\n          animationMixers.push(mixer);\n          characterGroup.userData.mixer = mixer;\n          characterGroup.userData.animations = clips;\n          characterGroup.userData.animActions = animActions;\n          characterGroup.userData.currentAnim = 'idle';\n        }\n\n        // Initialize squish state for squishy animation\n        characterGroup.userData.squishState = {\n          scaleY: 1,\n          scaleXZ: 1,\n          targetScaleY: 1,\n          targetScaleXZ: 1,\n        };\n        characterGroup.userData.model = model;\n\n        return characterGroup;\n      }\n\n      function switchAnimation(characterGroup, animName) {\n        if (!characterGroup || !characterGroup.userData.animActions) return;\n\n        const actions = characterGroup.userData.animActions;\n        const currentAnim = characterGroup.userData.currentAnim;\n\n        if (currentAnim === animName) return;\n        // Already playing this animation\n\n        const newAction = actions[animName];\n        const oldAction = actions[currentAnim];\n\n        if (!newAction) return;\n\n        // Crossfade to new animation\n        if (oldAction) {\n          newAction.reset();\n          newAction.setEffectiveTimeScale(1);\n          newAction.setEffectiveWeight(1);\n          newAction.crossFadeFrom(oldAction, 0.2, true);\n          newAction.play();\n        } else {\n          newAction.play();\n        }\n\n        characterGroup.userData.currentAnim = animName;\n      }\n\n      function createFallbackCharacter(color = 0xffffff) {\n        // Fallback simple character if model fails to load\n        const characterGroup = new THREE.Group();\n\n        // Body\n        const bodyGeometry = new THREE.CylinderGeometry(0.45, 0.55, 1.3, 16);\n        const bodyMaterial = new THREE.MeshStandardMaterial({\n          color: color,\n          roughness: 0.5,\n          metalness: 0.1,\n        });\n        const body = new THREE.Mesh(bodyGeometry, bodyMaterial);\n        body.position.y = 0.65;\n        body.castShadow = true;\n        characterGroup.add(body);\n\n        // Head\n        const headGeometry = new THREE.SphereGeometry(0.4, 16, 16);\n        const headMaterial = new THREE.MeshStandardMaterial({\n          color: 0xffdbac,\n          roughness: 0.6,\n        });\n        const head = new THREE.Mesh(headGeometry, headMaterial);\n        head.position.y = 1.6;\n        head.castShadow = true;\n        characterGroup.add(head);\n\n        // Eyes\n        const eyeGeometry = new THREE.SphereGeometry(0.07, 8, 8);\n        const eyeMaterial = new THREE.MeshBasicMaterial({\n          color: 0x333333,\n        });\n\n        const leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial);\n        leftEye.position.set(-0.14, 1.65, 0.32);\n        characterGroup.add(leftEye);\n\n        const rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial);\n        rightEye.position.set(0.14, 1.65, 0.32);\n        characterGroup.add(rightEye);\n\n        characterGroup.userData.squishState = {\n          scaleY: 1,\n          scaleXZ: 1,\n          targetScaleY: 1,\n          targetScaleXZ: 1,\n        };\n\n        return characterGroup;\n      }\n\n      function updateAnimationMixers(deltaTime) {\n        for (let i = 0; i < animationMixers.length; i++) {\n          if (animationMixers[i]) {\n            animationMixers[i].update(deltaTime);\n          }\n        }\n      }\n\n      // ============================================\n      // AUDIO SYSTEM\n      // ============================================\n\n      async function initAudio() {\n        try {\n          audioContext = new (window.AudioContext || window.webkitAudioContext)();\n\n          // Create gain node for music\n          musicGainNode = audioContext.createGain();\n          musicGainNode.connect(audioContext.destination);\n          musicGainNode.gain.value = 0.3;\n          // Audio assets will be lazy-loaded when played\n        } catch (e) {\n          lib.log('Audio initialization failed: ' + e.message);\n        }\n      }\n\n      let lastCollectSoundTime = 0;\n      let collectSoundPitch = 1.0;\n\n      async function playSound(soundId) {\n        if (!audioContext) return;\n\n        // console.log(soundId);\n        // Lazy load audio if not already in buffer\n        if (!audioBuffers[soundId]) {\n          const asset = lib.getAsset(soundId);\n          if (asset && asset.url) {\n            try {\n              const response = await fetch(asset.url);\n              const arrayBuffer = await response.arrayBuffer();\n              audioBuffers[soundId] = await audioContext.decodeAudioData(arrayBuffer);\n            } catch (e) {\n              lib.log('Failed to load audio: ' + soundId);\n              return;\n            }\n          } else {\n            lib.log('Audio asset not found: ' + soundId);\n            return;\n          }\n        }\n\n        try {\n          if (audioContext.state === 'suspended') {\n            audioContext.resume();\n          }\n\n          const source = audioContext.createBufferSource();\n          source.buffer = audioBuffers[soundId];\n\n          if (soundId === 'pickup_sound') {\n              const pitchVariance = window.gameConfig.settings.collectSoundPitchVariance ?? 0;\n              if (pitchVariance > 0) {\n                  const randomOffset = (Math.random() * 2 - 1) * pitchVariance;\n                  source.playbackRate.value = Math.max(0.5, 1 + randomOffset);\n              }\n          }\n\n          if (soundId === 'collect_sound') {\n            const now = Date.now();\n            if (now - lastCollectSoundTime < 1000) {\n              collectSoundPitch = Math.min(3.0, collectSoundPitch + 0.1);\n            } else {\n              collectSoundPitch = 1.0;\n            }\n            lastCollectSoundTime = now;\n            source.playbackRate.value = collectSoundPitch;\n          }\n\n          const gainNode = audioContext.createGain();\n          gainNode.gain.value = 0.5;\n\n          source.connect(gainNode);\n          gainNode.connect(audioContext.destination);\n          source.start(0);\n        } catch (e) {\n          // Silently fail\n        }\n      }\n\n      async function playMusic() {\n        if (!audioContext || !window.gameConfig.settings.musicEnabled) return;\n\n        // Lazy load music if not already in buffer\n        if (!audioBuffers['background_music']) {\n          const asset = lib.getAsset('background_music');\n          if (asset && asset.url) {\n            try {\n              const response = await fetch(asset.url);\n              const arrayBuffer = await response.arrayBuffer();\n              audioBuffers['background_music'] = await audioContext.decodeAudioData(arrayBuffer);\n            } catch (e) {\n              lib.log('Failed to load background music: ' + e);\n              return;\n            }\n          } else {\n            lib.log('Background music asset not found.');\n            return;\n          }\n        }\n\n        try {\n          if (audioContext.state === 'suspended') {\n            audioContext.resume();\n          }\n\n          if (musicSource) {\n            musicSource.stop();\n          }\n\n          musicSource = audioContext.createBufferSource();\n          musicSource.buffer = audioBuffers['background_music'];\n          musicSource.loop = true;\n          musicSource.connect(musicGainNode);\n          musicSource.start(0);\n        } catch (e) {\n          lib.log('Failed to play music: ' + e.message);\n        }\n      }\n\n      function stopMusic() {\n        if (musicSource) {\n          try {\n            musicSource.stop();\n          } catch (e) {}\n          musicSource = null;\n        }\n      }\n\n      // ============================================\n      // INPUT HANDLING\n      // ============================================\n\n      function setupKeyboardInput() {\n        window.addEventListener('keydown', (e) => {\n          if (gameMode === 'edit') return;\n          const key = e.key.toLowerCase();\n          if (\n            ['w', 'a', 's', 'd', 'arrowup', 'arrowdown', 'arrowleft', 'arrowright'].includes(key)\n          ) {\n            e.preventDefault();\n            keys[key] = true;\n          }\n        });\n\n        window.addEventListener('keyup', (e) => {\n          const key = e.key.toLowerCase();\n          keys[key] = false;\n        });\n\n        window.addEventListener('blur', () => {\n          keys = {};\n          inputVector = {\n            x: 0,\n            z: 0,\n          };\n        });\n      }\n\n      function setupTouchInput() {\n        const joystickZone = document.getElementById('joystick-zone');\n        const joystickBase = document.getElementById('joystick-base');\n        const joystickStick = document.getElementById('joystick-stick');\n\n        const maxRadius = 35;\n        // Max movement radius for stick\n        const deadZone = 8;\n\n        // Get joystick center position (fixed position)\n        function getJoystickCenter() {\n          const rect = joystickBase.getBoundingClientRect();\n          return {\n            x: rect.left + rect.width / 2,\n            y: rect.top + rect.height / 2,\n          };\n        }\n\n        joystickZone.addEventListener('touchstart', (e) => {\n          if (gameMode === 'edit') return;\n          e.preventDefault();\n\n          const touch = e.changedTouches[0];\n          joystickTouchId = touch.identifier;\n          joystickActive = true;\n\n          // Use fixed joystick center\n          joystickCenter = getJoystickCenter();\n\n          // Add active class for visual feedback\n          joystickBase.classList.add('active');\n\n          // Calculate initial stick position\n          const dx = touch.clientX - joystickCenter.x;\n          const dy = touch.clientY - joystickCenter.y;\n          const distance = Math.sqrt(dx * dx + dy * dy);\n\n          let clampedX = dx;\n          let clampedY = dy;\n\n          if (distance > maxRadius) {\n            clampedX = (dx / distance) * maxRadius;\n            clampedY = (dy / distance) * maxRadius;\n          }\n\n          joystickStick.style.transform = `translate(${clampedX}px, ${clampedY}px)`;\n\n          // Calculate input vector\n          if (distance > deadZone) {\n            const normalizedDistance = Math.min((distance - deadZone) / (maxRadius - deadZone), 1);\n            const angle = Math.atan2(dy, dx);\n            inputVector.x = Math.cos(angle) * normalizedDistance;\n            inputVector.z = Math.sin(angle) * normalizedDistance;\n          }\n        });\n\n        joystickZone.addEventListener('touchmove', (e) => {\n          if (!joystickActive || gameMode === 'edit') return;\n          e.preventDefault();\n\n          let touch = null;\n          for (let i = 0; i < e.changedTouches.length; i++) {\n            if (e.changedTouches[i].identifier === joystickTouchId) {\n              touch = e.changedTouches[i];\n              break;\n            }\n          }\n\n          if (!touch) return;\n\n          const dx = touch.clientX - joystickCenter.x;\n          const dy = touch.clientY - joystickCenter.y;\n          const distance = Math.sqrt(dx * dx + dy * dy);\n\n          let clampedX = dx;\n          let clampedY = dy;\n\n          if (distance > maxRadius) {\n            clampedX = (dx / distance) * maxRadius;\n            clampedY = (dy / distance) * maxRadius;\n          }\n\n          joystickStick.style.transform = `translate(${clampedX}px, ${clampedY}px)`;\n\n          // Calculate input vector with dead zone\n          if (distance > deadZone) {\n            const normalizedDistance = Math.min((distance - deadZone) / (maxRadius - deadZone), 1);\n            const angle = Math.atan2(dy, dx);\n            inputVector.x = Math.cos(angle) * normalizedDistance;\n            inputVector.z = Math.sin(angle) * normalizedDistance;\n          } else {\n            inputVector.x = 0;\n            inputVector.z = 0;\n          }\n        });\n\n        const endTouch = (e) => {\n          for (let i = 0; i < e.changedTouches.length; i++) {\n            if (e.changedTouches[i].identifier === joystickTouchId) {\n              joystickActive = false;\n              joystickTouchId = null;\n              joystickBase.classList.remove('active');\n              joystickStick.style.transform = 'translate(0, 0)';\n              inputVector.x = 0;\n              inputVector.z = 0;\n              break;\n            }\n          }\n        };\n        joystickZone.addEventListener('touchend', endTouch);\n        joystickZone.addEventListener('touchcancel', endTouch);\n      }\n\n      function updateInputFromKeyboard() {\n        if (gameMode === 'edit') return;\n\n        let kx = 0,\n          kz = 0;\n\n        if (keys['w'] || keys['arrowup']) kz -= 1;\n        if (keys['s'] || keys['arrowdown']) kz += 1;\n        if (keys['a'] || keys['arrowleft']) kx -= 1;\n        if (keys['d'] || keys['arrowright']) kx += 1;\n\n        // Normalize diagonal movement\n        if (kx !== 0 || kz !== 0) {\n          const length = Math.sqrt(kx * kx + kz * kz);\n          inputVector.x = kx / length;\n          inputVector.z = kz / length;\n        } else if (!joystickActive) {\n          inputVector.x = 0;\n          inputVector.z = 0;\n        }\n      }\n\n      // ============================================\n      // UI UPDATES\n      // ============================================\n\n      function lerp(a, b, t) {\n        return a + (b - a) * t;\n      }\n\n      function formatCurrencyAbbrev(value) {\n        const num = Number(value) || 0;\n        const sign = num < 0 ? '-' : '';\n        const abs = Math.abs(num);\n        if (abs < 1000) return sign + Math.floor(abs).toString();\n        const units = [\n          {\n            v: 1e12,\n            s: 'T',\n          },\n          {\n            v: 1e9,\n            s: 'B',\n          },\n          {\n            v: 1e6,\n            s: 'M',\n          },\n        ];\n        for (const u of units) {\n          if (abs >= u.v) {\n            const scaled = abs / u.v;\n            const decimals = scaled >= 10 ? 0 : 1;\n            return sign + scaled.toFixed(decimals).replace(/\\.0$/, '') + u.s;\n          }\n        }\n        return sign + Math.floor(abs).toString();\n      }\n\n      function updateCurrencyUI() {\n        const el = cachedDOMElements.currencyValue;\n        if (!el) return;\n        el.textContent = formatCurrencyAbbrev(currentCurrency);\n        el.classList.remove('currency-pop');\n        void el.offsetWidth;\n        // Trigger reflow\n        el.classList.add('currency-pop');\n      }\n\n      function updateStackUI() {\n        const count = cachedDOMElements.stackCount;\n        const max = cachedDOMElements.stackMax;\n        if (count) count.textContent = currentStackCount;\n        if (max) max.textContent = getMaxCapacity();\n      }\n\n      function updateMoneyStackUI() {\n        const count = cachedDOMElements.moneyCount;\n        const max = cachedDOMElements.moneyMax;\n        if (count) count.textContent = currentMoneyCount;\n        if (max) max.textContent = MAX_MONEY_CAPACITY;\n      }\n\n      function updateCashierUI() {\n        const cashierCount = cachedDOMElements.cashierCount;\n        const cashierMax = cachedDOMElements.cashierMax;\n        const customerCountEl = cachedDOMElements.customerCount;\n        const cashierEarningsEl = cachedDOMElements.cashierEarnings;\n\n        if (cashierCount && cashierMax && window.gameConfig.cashierStall) {\n          cashierCount.textContent = cashierBurgerStack.length;\n          cashierMax.textContent = window.gameConfig.cashierStall.maxBurgers;\n        }\n\n        if (customerCountEl) {\n          let waitingCount = 0;\n          for (let i = 0; i < customerQueue.length; i++) {\n            const state = customerQueue[i].state;\n            if (state === 'waiting' || state === 'walking') waitingCount++;\n          }\n          customerCountEl.textContent = waitingCount;\n        }\n\n        if (cashierEarningsEl) {\n          cashierEarningsEl.textContent = cashierCashPile.length;\n        }\n      }\n\n      function updateVaultUI() {\n        const balanceEl = cachedDOMElements.vaultBalance;\n        const balanceLargeEl = cachedDOMElements.vaultBalanceLarge;\n\n        if (balanceEl) {\n          balanceEl.textContent = formatCurrencyAbbrev(vaultBalance);\n        }\n        if (balanceLargeEl) {\n          balanceLargeEl.textContent = formatCurrencyAbbrev(vaultBalance);\n        }\n\n        // Update vault upgrade buttons\n        updateVaultUpgradeUI();\n\n        // Update prestige UI\n        updatePrestigeUI();\n      }\n\n      function updatePrestigeUI() {\n        const prestige = window.gameConfig.prestige || {\n          level: 0,\n          baseCost: 5000,\n          costMultiplier: 2.5,\n          profitBonus: 0.1,\n        };\n\n        const levelEl = document.getElementById('prestige-level');\n        const bonusEl = document.getElementById('prestige-bonus');\n        const costEl = document.getElementById('prestige-cost');\n        const btnEl = document.getElementById('prestige-btn');\n\n        if (levelEl) {\n          levelEl.textContent = prestige.level;\n        }\n\n        if (bonusEl) {\n          const totalBonus = Math.round(prestige.level * prestige.profitBonus * 100);\n          bonusEl.textContent = totalBonus;\n        }\n\n        const prestigeCost = Math.floor(\n          prestige.baseCost * Math.pow(prestige.costMultiplier, prestige.level)\n        );\n\n        if (costEl) {\n          costEl.textContent = formatCurrencyAbbrev(prestigeCost);\n        }\n\n        if (btnEl) {\n          btnEl.disabled = vaultBalance < prestigeCost;\n        }\n      }\n\n      function getPrestigeProfitMultiplier() {\n        const prestige = window.gameConfig.prestige || {\n          level: 0,\n          profitBonus: 0.1,\n        };\n        return 1 + prestige.level * prestige.profitBonus;\n      }\n\n      function performPrestige() {\n        if (gameMode === 'edit') return;\n\n        const prestige = window.gameConfig.prestige || {\n          level: 0,\n          baseCost: 5000,\n          costMultiplier: 2.5,\n          profitBonus: 0.1,\n        };\n        const prestigeCost = Math.floor(\n          prestige.baseCost * Math.pow(prestige.costMultiplier, prestige.level)\n        );\n\n        if (vaultBalance < prestigeCost) {\n          return;\n        }\n\n        // Deduct cost from vault\n        modifyVaultBalance(-prestigeCost);\n\n        // Increment prestige level (this persists)\n        prestige.level++;\n        window.gameConfig.prestige = prestige;\n\n        // Show prestige notification\n        showPrestigeNotification(prestige.level);\n\n        // Close vault panel\n        const vaultPanel = document.getElementById('vault-panel');\n        if (vaultPanel) {\n          vaultPanel.classList.remove('open');\n        }\n\n        // Reset everything after a brief delay for the notification\n        setTimeout(() => {\n          resetForPrestige();\n        }, 1500);\n      }\n\n      function showPrestigeNotification(newLevel) {\n        const notification = document.getElementById('prestige-notification');\n        if (!notification) return;\n\n        const bonusPercent = Math.round(\n          newLevel * (window.gameConfig.prestige.profitBonus || 0.1) * 100\n        );\n        const subtitleEl = notification.querySelector('.notification-subtitle');\n        if (subtitleEl) {\n          subtitleEl.textContent = `+${bonusPercent}% Profit Bonus!`;\n        }\n\n        notification.classList.add('show');\n        playSound('upgrade_sound');\n\n        // Hide after delay\n        setTimeout(() => {\n          notification.classList.remove('show');\n        }, 3000);\n      }\n\n      function resetForPrestige() {\n        // ====== RESET CURRENCY ======\n        currentCurrency = 0;\n        window.gameConfig.currency = 0;\n        window.gameConfig.tutorialStep = 0;\n        tutorialCompleteShown = false;\n        tutorialCompleteTimer = 0;\n        const tutorialOverlay = document.getElementById('tutorial-complete');\n        if (tutorialOverlay) tutorialOverlay.classList.remove('show');\n\n        // Reset vault balance to 0 (prestige cost already deducted)\n        vaultBalance = 0;\n        window.gameConfig.bankVault.balance = 0;\n\n        // ====== RESET UPGRADES ======\n        window.gameConfig.upgrades.capacity.level = 0;\n        window.gameConfig.upgrades.speed.level = 0;\n        window.gameConfig.upgrades.collection.level = 0;\n\n        // Reset vault upgrades\n        window.gameConfig.vaultUpgrades = {\n          earnings: 0,\n          capacity: 0,\n          tips: 0,\n          speed: 0,\n          cook: 0,\n          serve: 0,\n          cashpile: 0,\n        };\n\n        // ====== RESET CASHIER TILE ======\n        if (window.gameConfig.cashierTile) {\n          window.gameConfig.cashierTile.unlocked = false;\n          window.gameConfig.cashierTile.moneyDeposited = 0;\n        }\n\n        // ====== RESET GRILL TILE ======\n        if (window.gameConfig.grillTile) {\n          window.gameConfig.grillTile.unlocked = false;\n          window.gameConfig.grillTile.moneyDeposited = 0;\n        }\n        if (window.gameConfig.driveThruTile) {\n          window.gameConfig.driveThruTile.unlocked = false;\n          window.gameConfig.driveThruTile.moneyDeposited = 0;\n        }\n        if (window.gameConfig.trashTile) {\n          window.gameConfig.trashTile.unlocked = false;\n          window.gameConfig.trashTile.moneyDeposited = 0;\n        }\n        if (window.gameConfig.vaultTile) {\n          window.gameConfig.vaultTile.unlocked = false;\n          window.gameConfig.vaultTile.moneyDeposited = 0;\n\n          // Close vault panel if it's open\n          const vaultPanel = document.getElementById('vault-panel');\n          if (vaultPanel) vaultPanel.classList.remove('open');\n        }\n\n        if (window.gameConfig.fountainTile) {\n          window.gameConfig.fountainTile.unlocked = false;\n          window.gameConfig.fountainTile.moneyDeposited = 0;\n        }\n        if (fountainMesh) {\n          scene.remove(fountainMesh);\n          fountainMesh = null;\n        }\n\n        // ====== RESET STAFF ======\n        // Cashier staff\n        if (window.gameConfig.staffHireTile) {\n          window.gameConfig.staffHireTile.staffHired = false;\n          window.gameConfig.staffHireTile.moneyDeposited = 0;\n        }\n        if (hiredStaffMesh) {\n          scene.remove(hiredStaffMesh);\n          hiredStaffMesh = null;\n        }\n\n        // Cleaner staff\n        if (window.gameConfig.cleanerStaffTile) {\n          window.gameConfig.cleanerStaffTile.staffHired = false;\n          window.gameConfig.cleanerStaffTile.moneyDeposited = 0;\n        }\n        if (hiredCleanerMesh) {\n          scene.remove(hiredCleanerMesh);\n          hiredCleanerMesh = null;\n        }\n        cleanerStaffAI = null;\n\n        // Vault collector staff\n        if (window.gameConfig.vaultStaffTile) {\n          window.gameConfig.vaultStaffTile.staffHired = false;\n          window.gameConfig.vaultStaffTile.moneyDeposited = 0;\n        }\n        if (hiredVaultCollectorMesh) {\n          scene.remove(hiredVaultCollectorMesh);\n          hiredVaultCollectorMesh = null;\n        }\n        vaultCollectorAI = null;\n        vaultCollectorMoneyStack.forEach((b) => {\n          if (b.parent) b.parent.remove(b);\n        });\n        vaultCollectorMoneyStack = [];\n\n        // ====== RESET STAFF ======\n        // Cashier staff\n        if (window.gameConfig.staffHireTile) {\n          window.gameConfig.staffHireTile.staffHired = false;\n          window.gameConfig.staffHireTile.extraStaffHired = false;\n          window.gameConfig.staffHireTile.moneyDeposited = 0;\n        }\n        if (hiredStaffMesh) {\n          scene.remove(hiredStaffMesh);\n          hiredStaffMesh = null;\n        }\n        if (hiredStaffMesh2) {\n          scene.remove(hiredStaffMesh2);\n          hiredStaffMesh2 = null;\n        }\n\n        // ====== RESET PLAYER STACK ======\n        playerStack.forEach((item) => {\n          item.traverse((child) => {\n            if (child.geometry) child.geometry.dispose();\n            if (child.material) {\n              if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n              else child.material.dispose();\n            }\n          });\n        });\n        playerStack = [];\n        currentStackCount = 0;\n\n        moneyStack.forEach((item) => {\n          player.remove(item);\n          item.traverse((child) => {\n            if (child.geometry) child.geometry.dispose();\n            if (child.material) {\n              if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n              else child.material.dispose();\n            }\n          });\n        });\n        moneyStack = [];\n        currentMoneyCount = 0;\n\n        // ====== RESET CASHIER ======\n        cashierBurgerStack.forEach((b) => scene.remove(b));\n        cashierBurgerStack = [];\n        cashierCashPile.forEach((m) => scene.remove(m));\n        cashierCashPile = [];\n        cashierEarnings = 0;\n        cashierServeTimer = 0;\n        cashierServeTimer2 = 0;\n\n        // Clear customers\n        customerQueue.forEach((c) => scene.remove(c.mesh));\n        customerQueue = [];\n\n// ====== RESET DRIVE-THRU ======\n        driveThruBurgerStack.forEach((b) => scene.remove(b));\n        driveThruBurgerStack = [];\n        driveThruCashPile.forEach((m) => scene.remove(m));\n        driveThruCashPile = [];\n        driveThruCarQueue.forEach((c) => scene.remove(c.mesh));\n        driveThruCarQueue = [];\n\n        // ====== RESET SELF-ORDER KIOSKS ======\n        selfOrderKiosks.forEach((kiosk) => {\n          kiosk.queue.forEach((c) => scene.remove(c.mesh));\n          kiosk.queue = [];\n          kiosk.leavers.forEach((c) => scene.remove(c.mesh));\n          kiosk.leavers = [];\n        });\n\n        // ====== RESET GROUND ITEMS ======\n        groundItems.forEach((item) => scene.remove(item));\n        groundItems = [];\n        groundMoneyItems.forEach((item) => scene.remove(item));\n        groundMoneyItems = [];\n        flyingItems.forEach((item) => scene.remove(item.mesh));\n        flyingItems = [];\n\n        // ====== RESET MONEY GRID ======\n        initMoneyGrid();\n\n        // ====== RESET DINING TABLE TILES ======\n        if (window.gameConfig.diningTableTiles) {\n          window.gameConfig.diningTableTiles.forEach((tile, index) => {\n            tile.moneyDeposited = 0;\n            tile.deleted = false;\n          });\n        }\n\n        // ====== GENERATE NEW LEVEL LAYOUT ======\n        generateNewLevelLayout();\n\n        // ====== MOVE PLAYER TO START ======\n        player.position.set(window.gameConfig.player.startX, 0, window.gameConfig.player.startZ);\n\n        // ====== UPDATE ALL UI ======\n        updateCurrencyUI();\n        updateStackUI();\n        updateMoneyStackUI();\n        updateCashierUI();\n        updateVaultUI();\n        updateUpgradeUI();\n\n        // Save immediately\n        performAutosave();\n\n        playSound('upgrade_sound');\n      }\n\n      function generateNewLevelLayout() {\n        const prestige = window.gameConfig.prestige || {\n          level: 0,\n        };\n        const prestigeLevel = prestige.level;\n\n        // Check if we have a saved layout for this prestige level\n        let currentPrestigeLayouts = window.gameConfig.prestigeLayouts || [];\n        let layoutToLoad = null;\n\n        if (currentPrestigeLayouts.length > 0) {\n          // Find layout for current prestige level, or the highest available if we exceeded\n          let targetLevel = prestigeLevel + 1;\n          // Level 1 is prestige 0, level 2 is prestige 1, etc.\n\n          let matchingLayout = currentPrestigeLayouts.find((l) => l.level === targetLevel);\n          if (matchingLayout) {\n            layoutToLoad = matchingLayout;\n          } else {\n            // Fallback to highest level layout available\n            let maxLevel = -1;\n            let maxLayout = null;\n            currentPrestigeLayouts.forEach((l) => {\n              if (l.level > maxLevel) {\n                maxLevel = l.level;\n                maxLayout = l;\n              }\n            });\n            layoutToLoad = maxLayout;\n          }\n        }\n\n        if (layoutToLoad) {\n          lib.log('Loading saved layout for Prestige ' + prestigeLevel);\n          applyPrestigeLayout(layoutToLoad);\n        } else {\n          // Fallback to randomized layout if no saved layout\n          lib.log('Generating random layout for Prestige ' + prestigeLevel);\n\n          // Randomize equipment positions in the kitchen area\n          const grills = window.gameConfig.grills;\n          const kitchenMinZ = -30;\n          const kitchenMaxZ = -18;\n          const kitchenMinX = -20;\n          const kitchenMaxX = 14;\n\n          // Randomize each grill position\n          grills.forEach((grill, index) => {\n            const newX = kitchenMinX + Math.random() * (kitchenMaxX - kitchenMinX);\n            const newZ = kitchenMinZ + Math.random() * (kitchenMaxZ - kitchenMinZ);\n\n            grill.x = newX;\n            grill.z = newZ;\n\n            // Update the 3D mesh position\n            if (resourceNodes[index]) {\n              resourceNodes[index].group.position.set(newX, 0, newZ);\n              resourceNodes[index].position.x = newX;\n              resourceNodes[index].position.z = newZ;\n              resourceNodes[index].group.rotation.y = Math.PI;\n\n              // Reset to ready state\n              resourceNodes[index].isReady = true;\n              resourceNodes[index].regenTimer = 0;\n              if (resourceNodes[index].grillMesh) {\n                resourceNodes[index].grillMesh.visible = true;\n                resourceNodes[index].grillMesh.scale.setScalar(1);\n              }\n              if (resourceNodes[index].emptyMesh) {\n                resourceNodes[index].emptyMesh.visible = false;\n              }\n            }\n          });\n\n          // Randomize cashier position (in the middle-front area)\n          const cashierConfig = window.gameConfig.cashierStall;\n          if (cashierConfig && !cashierConfig.deleted) {\n            const newCashierX = -8 + Math.random() * 16;\n            const newCashierZ = 2 + Math.random() * 8;\n\n            cashierConfig.x = newCashierX;\n            cashierConfig.z = newCashierZ;\n\n            if (cashierStallMesh) {\n              updateCashierStallPosition(newCashierX, newCashierZ);\n            }\n\n            if (window.gameConfig.cashierTile) {\n              window.gameConfig.cashierTile.x = newCashierX;\n              window.gameConfig.cashierTile.z = newCashierZ;\n              if (cashierTileMesh) {\n                updateCashierTilePosition(newCashierX, newCashierZ);\n              } else if (!window.gameConfig.cashierTile.unlocked) {\n                createCashierTile();\n              }\n            }\n          }\n\n          if (window.gameConfig.grillTile && !window.gameConfig.grillTile.deleted) {\n            window.gameConfig.grillTile.x = -8 + Math.random() * 16;\n            window.gameConfig.grillTile.z = -12 + Math.random() * 4;\n\n            if (!grillTileMesh) {\n              createGrillTile();\n            } else {\n              updateGrillTilePosition(window.gameConfig.grillTile.x, window.gameConfig.grillTile.z);\n              updateGrillTileProgress();\n              updateGrillTileCostDisplay();\n            }\n          }\n          if (window.gameConfig.staffHireTile && !window.gameConfig.staffHireTile.deleted) {\n            window.gameConfig.staffHireTile.x = -10 + Math.random() * 6;\n            window.gameConfig.staffHireTile.z = 2 + Math.random() * 8;\n\n            // Recreate the tile visually\n            if (!staffHireTileMesh) {\n              createStaffHireTile();\n            } else {\n              updateStaffHireTilePosition(\n                window.gameConfig.staffHireTile.x,\n                window.gameConfig.staffHireTile.z\n              );\n              updateStaffHireProgress();\n              updateStaffHireCostDisplay();\n            }\n          }\n\n          if (window.gameConfig.cleanerStaffTile && !window.gameConfig.cleanerStaffTile.deleted) {\n            window.gameConfig.cleanerStaffTile.x = -18 + Math.random() * 8;\n            window.gameConfig.cleanerStaffTile.z = 2 + Math.random() * 6;\n\n            if (!cleanerStaffTileMesh) {\n              createCleanerStaffTile();\n            } else {\n              updateCleanerStaffTilePosition(\n                window.gameConfig.cleanerStaffTile.x,\n                window.gameConfig.cleanerStaffTile.z\n              );\n              updateCleanerStaffProgress();\n              updateCleanerStaffCostDisplay();\n            }\n          }\n\n          if (window.gameConfig.runnerStaffTile && !window.gameConfig.runnerStaffTile.deleted) {\n            window.gameConfig.runnerStaffTile.x = 10 + Math.random() * 8;\n            window.gameConfig.runnerStaffTile.z = -8 + Math.random() * 6;\n\n            if (!runnerStaffTileMesh) {\n              createRunnerStaffTile();\n            } else {\n              updateRunnerStaffTilePosition(\n                window.gameConfig.runnerStaffTile.x,\n                window.gameConfig.runnerStaffTile.z\n              );\n              updateRunnerStaffProgress();\n              updateRunnerStaffCostDisplay();\n            }\n          }\n\n          if (window.gameConfig.vaultStaffTile && !window.gameConfig.vaultStaffTile.deleted) {\n            window.gameConfig.vaultStaffTile.x = 16 + Math.random() * 6;\n            window.gameConfig.vaultStaffTile.z = -24 + Math.random() * 8;\n\n            if (!vaultStaffTileMesh) {\n              createVaultStaffTile();\n            } else {\n              updateVaultStaffTilePosition(\n                window.gameConfig.vaultStaffTile.x,\n                window.gameConfig.vaultStaffTile.z\n              );\n              updateVaultStaffProgress();\n              updateVaultStaffCostDisplay();\n            }\n          }\n\n          // Randomize dining table tile positions\n          if (window.gameConfig.diningTableTiles) {\n            window.gameConfig.diningTableTiles.forEach((tile, index) => {\n              tile.x = -18 + (index % 4) * 10 + Math.random() * 4;\n              tile.z = 18 + Math.floor(index / 4) * 8 + Math.random() * 4;\n              tile.moneyDeposited = 0;\n              tile.deleted = false;\n\n              // Update visual if exists\n              const tileEntry = diningTableTileMeshes.find((t) => t.index === index);\n              if (tileEntry) {\n                updateDiningTableTilePosition(tile.x, tile.z, index);\n                updateDiningTableTileProgress(index);\n                updateDiningTableTileCostDisplay(index);\n              } else {\n                // Recreate the tile\n                createDiningTableTile(index);\n              }\n            });\n          }\n\n          // Randomize trash bin position\n          if (window.gameConfig.trashBin && !window.gameConfig.trashBin.deleted && trashBinMesh) {\n            const newTrashX = 6 + Math.random() * 10;\n            const newTrashZ = -14 + Math.random() * 6;\n            updateTrashBinPosition(newTrashX, newTrashZ);\n          }\n// Reposition self-order kiosks\n        if (window.gameConfig.selfOrderKiosks) {\n          window.gameConfig.selfOrderKiosks.forEach((config) => {\n            const kiosk = selfOrderKiosks.find(k => k.id === config.id);\n            if (kiosk && kiosk.mesh && !config.deleted) {\n              const newKioskX = config.x + (Math.random() - 0.5) * 2;\n              const newKioskZ = config.z + (Math.random() - 0.5) * 2;\n              updateSelfOrderKioskPosition(config.id, newKioskX, newKioskZ);\n            }\n          });\n        }\n      }\n        // Increase difficulty slightly with prestige (e.g., higher costs)\n        const costMultiplier = 1 + prestigeLevel * 0.15;\n        // 15% more expensive per prestige\n\n        if (window.gameConfig.staffHireTile) {\n          window.gameConfig.staffHireTile.hireCost = Math.floor(100 * costMultiplier);\n        }\n        if (window.gameConfig.cleanerStaffTile) {\n          window.gameConfig.cleanerStaffTile.hireCost = Math.floor(100 * costMultiplier);\n        }\n        if (window.gameConfig.runnerStaffTile) {\n          window.gameConfig.runnerStaffTile.hireCost = Math.floor(100 * costMultiplier);\n        }\n        if (window.gameConfig.vaultStaffTile) {\n          window.gameConfig.vaultStaffTile.hireCost = Math.floor(150 * costMultiplier);\n        }\n        if (window.gameConfig.diningTableTiles) {\n          window.gameConfig.diningTableTiles.forEach((tile) => {\n            tile.tableCost = Math.floor(300 * costMultiplier);\n          });\n        }\n\n        lib.log('New level layout generated for Prestige ' + prestigeLevel);\n      }\n\n      function applyPrestigeLayout(layoutData) {\n        if (!layoutData) return;\n\n        // Apply Grills\n        if (layoutData.grills && Array.isArray(layoutData.grills)) {\n          window.gameConfig.grills = JSON.parse(JSON.stringify(layoutData.grills));\n          layoutData.grills.forEach((grillData, index) => {\n            if (resourceNodes[index]) {\n              resourceNodes[index].group.position.set(grillData.x, 0, grillData.z);\n              resourceNodes[index].position.x = grillData.x;\n              resourceNodes[index].position.z = grillData.z;\n              resourceNodes[index].group.rotation.y = Math.PI;\n              resourceNodes[index].equipmentType = grillData.equipmentType || 'grill';\n              resourceNodes[index].isReady = true;\n              resourceNodes[index].regenTimer = 0;\n\n              // We'd ideally recreate the grill mesh if equipment type changed,\n              // but for simplicity we'll just update position\n              if (resourceNodes[index].grillMesh) {\n                resourceNodes[index].grillMesh.visible = true;\n                resourceNodes[index].grillMesh.scale.setScalar(1);\n              }\n              if (resourceNodes[index].emptyMesh) {\n                resourceNodes[index].emptyMesh.visible = false;\n              }\n            } else {\n              // Create new resource node\n              createResourceNodeAt(grillData.x, grillData.z, index, grillData.equipmentType);\n            }\n          });\n\n          // Remove excess resource nodes if the layout has fewer\n          while (resourceNodes.length > layoutData.grills.length) {\n            const node = resourceNodes.pop();\n            scene.remove(node.group);\n          }\n        }\n\n        // Apply Cashier\n        if (layoutData.cashierStall) {\n          window.gameConfig.cashierStall = JSON.parse(JSON.stringify(layoutData.cashierStall));\n          if (cashierStallMesh)\n            updateCashierStallPosition(layoutData.cashierStall.x, layoutData.cashierStall.z);\n        }\n        if (layoutData.cashierTile) {\n          window.gameConfig.cashierTile = JSON.parse(JSON.stringify(layoutData.cashierTile));\n          window.gameConfig.cashierTile.moneyDeposited = 0;\n          window.gameConfig.cashierTile.unlocked = false;\n          if (!cashierTileMesh) createCashierTile();\n          else {\n            updateCashierTilePosition(layoutData.cashierTile.x, layoutData.cashierTile.z);\n            updateCashierTileProgress();\n            updateCashierTileCostDisplay();\n          }\n        }\n        if (layoutData.grillTile) {\n          window.gameConfig.grillTile = JSON.parse(JSON.stringify(layoutData.grillTile));\n          window.gameConfig.grillTile.moneyDeposited = 0;\n          window.gameConfig.grillTile.unlocked = false;\n          if (!grillTileMesh) createGrillTile();\n          else {\n            updateGrillTilePosition(layoutData.grillTile.x, layoutData.grillTile.z);\n            updateGrillTileProgress();\n            updateGrillTileCostDisplay();\n          }\n        }\n\n        // Apply Staff Tiles\n        if (layoutData.staffHireTile) {\n          window.gameConfig.staffHireTile = JSON.parse(JSON.stringify(layoutData.staffHireTile));\n          window.gameConfig.staffHireTile.moneyDeposited = 0;\n          window.gameConfig.staffHireTile.staffHired = false;\n          if (!staffHireTileMesh) createStaffHireTile();\n          else {\n            updateStaffHireTilePosition(layoutData.staffHireTile.x, layoutData.staffHireTile.z);\n            updateStaffHireProgress();\n            updateStaffHireCostDisplay();\n          }\n        }\n\n        if (layoutData.cleanerStaffTile) {\n          window.gameConfig.cleanerStaffTile = JSON.parse(\n            JSON.stringify(layoutData.cleanerStaffTile)\n          );\n          window.gameConfig.cleanerStaffTile.moneyDeposited = 0;\n          window.gameConfig.cleanerStaffTile.staffHired = false;\n          if (!cleanerStaffTileMesh) createCleanerStaffTile();\n          else {\n            updateCleanerStaffTilePosition(\n              layoutData.cleanerStaffTile.x,\n              layoutData.cleanerStaffTile.z\n            );\n            updateCleanerStaffProgress();\n            updateCleanerStaffCostDisplay();\n          }\n        }\n\n        if (layoutData.runnerStaffTile) {\n          window.gameConfig.runnerStaffTile = JSON.parse(\n            JSON.stringify(layoutData.runnerStaffTile)\n          );\n          window.gameConfig.runnerStaffTile.moneyDeposited = 0;\n          window.gameConfig.runnerStaffTile.staffHired = false;\n          if (!runnerStaffTileMesh) createRunnerStaffTile();\n          else {\n            updateRunnerStaffTilePosition(\n              layoutData.runnerStaffTile.x,\n              layoutData.runnerStaffTile.z\n            );\n            updateRunnerStaffProgress();\n            updateRunnerStaffCostDisplay();\n          }\n        }\n\n        if (layoutData.vaultStaffTile) {\n          window.gameConfig.vaultStaffTile = JSON.parse(JSON.stringify(layoutData.vaultStaffTile));\n          window.gameConfig.vaultStaffTile.moneyDeposited = 0;\n          window.gameConfig.vaultStaffTile.staffHired = false;\n          if (!vaultStaffTileMesh) createVaultStaffTile();\n          else {\n            updateVaultStaffTilePosition(layoutData.vaultStaffTile.x, layoutData.vaultStaffTile.z);\n            updateVaultStaffProgress();\n            updateVaultStaffCostDisplay();\n          }\n        }\n\n        // Apply Dining Table Tiles\n        if (layoutData.diningTableTiles) {\n          window.gameConfig.diningTableTiles = JSON.parse(\n            JSON.stringify(layoutData.diningTableTiles)\n          );\n          layoutData.diningTableTiles.forEach((tileData, index) => {\n            window.gameConfig.diningTableTiles[index].moneyDeposited = 0;\n            window.gameConfig.diningTableTiles[index].deleted = false;\n            const tileEntry = diningTableTileMeshes.find((t) => t.index === index);\n            if (tileEntry) {\n              updateDiningTableTilePosition(tileData.x, tileData.z, index);\n              updateDiningTableTileProgress(index);\n              updateDiningTableTileCostDisplay(index);\n            } else {\n              createDiningTableTile(index);\n            }\n          });\n\n          // Remove excess table tiles\n          for (\n            let i = diningTableTileMeshes.length - 1;\n            i >= layoutData.diningTableTiles.length;\n            i--\n          ) {\n            destroyDiningTableTileVisuals(i);\n          }\n        }\n\n        // Apply Trash Bin\n        if (layoutData.trashBin) {\n          window.gameConfig.trashBin = JSON.parse(JSON.stringify(layoutData.trashBin));\n          if (trashBinMesh) updateTrashBinPosition(layoutData.trashBin.x, layoutData.trashBin.z);\n        }\n\n        // Apply Vault\n        if (layoutData.vaultTile) {\n          window.gameConfig.vaultTile = JSON.parse(JSON.stringify(layoutData.vaultTile));\n          window.gameConfig.vaultTile.moneyDeposited = 0;\n          window.gameConfig.vaultTile.unlocked = false;\n          if (!vaultTileMesh) createVaultTile();\n          else {\n            updateVaultTilePosition(layoutData.vaultTile.x, layoutData.vaultTile.z);\n            updateVaultTileProgress();\n            updateVaultTileCostDisplay();\n          }\n        }\n\n        if (layoutData.fountainTile) {\n          window.gameConfig.fountainTile = JSON.parse(JSON.stringify(layoutData.fountainTile));\n          window.gameConfig.fountainTile.moneyDeposited = 0;\n          window.gameConfig.fountainTile.unlocked = false;\n          if (!fountainTileMesh) createFountainTile();\n          else {\n            updateFountainTilePosition(layoutData.fountainTile.x, layoutData.fountainTile.z);\n            updateFountainTileProgress();\n            updateFountainTileCostDisplay();\n          }\n        }\n\n        if (layoutData.bankVault) {\n          window.gameConfig.bankVault = JSON.parse(JSON.stringify(layoutData.bankVault));\n          if (bankVaultMesh)\n            updateBankVaultPosition(layoutData.bankVault.x, layoutData.bankVault.z);\n        }\n\n        if (layoutData.fountain) {\n          window.gameConfig.fountain = JSON.parse(JSON.stringify(layoutData.fountain));\n          if (fountainMesh) updateFountainPosition(layoutData.fountain.x, layoutData.fountain.z);\n        }\n        if (layoutData.driveThruTile) {\n          window.gameConfig.driveThruTile = JSON.parse(JSON.stringify(layoutData.driveThruTile));\n          window.gameConfig.driveThruTile.moneyDeposited = 0;\n          window.gameConfig.driveThruTile.unlocked = false;\n          if (!driveThruTileMesh) createDriveThruTile();\n          else {\n            updateDriveThruTilePosition(layoutData.driveThruTile.x, layoutData.driveThruTile.z);\n            updateDriveThruTileProgress();\n            updateDriveThruTileCostDisplay();\n          }\n        }\n        if (layoutData.trashTile) {\n          window.gameConfig.trashTile = JSON.parse(JSON.stringify(layoutData.trashTile));\n          window.gameConfig.trashTile.moneyDeposited = 0;\n          window.gameConfig.trashTile.unlocked = false;\n          if (!trashTileMesh) createTrashTile();\n          else {\n            updateTrashTilePosition(layoutData.trashTile.x, layoutData.trashTile.z);\n            updateTrashTileProgress();\n            updateTrashTileCostDisplay();\n          }\n        }\nif (layoutData.selfOrderKiosks) {\n          window.gameConfig.selfOrderKiosks = JSON.parse(JSON.stringify(layoutData.selfOrderKiosks));\n          createSelfOrderKiosks();\n        }\n      }\n      function updateVaultUpgradeUI() {\n        const upgrades = window.gameConfig.vaultUpgrades || {};\n\n        // Offline Earnings Boost\n        const earningsEl = document.getElementById('vault-upgrade-earnings');\n        if (earningsEl) {\n          const level = upgrades.earnings || 0;\n          const cost = Math.floor(500 * Math.pow(1.8, level));\n          earningsEl.querySelector('.level-num').textContent = level;\n          earningsEl.querySelector('.vault-cost-value').textContent = cost;\n          earningsEl.querySelector('.vault-upgrade-btn').disabled = vaultBalance < cost;\n        }\n\n        // Storage Expansion\n        const capacityEl = document.getElementById('vault-upgrade-capacity');\n        if (capacityEl) {\n          const level = upgrades.capacity || 0;\n          const cost = Math.floor(750 * Math.pow(1.7, level));\n          capacityEl.querySelector('.level-num').textContent = level;\n          capacityEl.querySelector('.vault-cost-value').textContent = cost;\n          capacityEl.querySelector('.vault-upgrade-btn').disabled = vaultBalance < cost;\n        }\n\n        // VIP Magnetism\n        const tipsEl = document.getElementById('vault-upgrade-tips');\n        if (tipsEl) {\n          const level = upgrades.tips || 0;\n          const cost = Math.floor(1000 * Math.pow(2, level));\n          tipsEl.querySelector('.level-num').textContent = level;\n          tipsEl.querySelector('.vault-cost-value').textContent = cost;\n          tipsEl.querySelector('.vault-upgrade-btn').disabled = vaultBalance < cost || level >= 10;\n          // Max 50% bonus\n        }\n\n        // Hustle Mode\n        const speedEl = document.getElementById('vault-upgrade-speed');\n        if (speedEl) {\n          const level = upgrades.speed || 0;\n          const cost = Math.floor(600 * Math.pow(1.6, level));\n          speedEl.querySelector('.level-num').textContent = level;\n          speedEl.querySelector('.vault-cost-value').textContent = cost;\n          speedEl.querySelector('.vault-upgrade-btn').disabled = vaultBalance < cost || level >= 10;\n          // Max 50% bonus\n        }\n\n        // Master Chef\n        const cookEl = document.getElementById('vault-upgrade-cook');\n        if (cookEl) {\n          const level = upgrades.cook || 0;\n          const cost = Math.floor(800 * Math.pow(1.7, level));\n          cookEl.querySelector('.level-num').textContent = level;\n          cookEl.querySelector('.vault-cost-value').textContent = cost;\n          cookEl.querySelector('.vault-upgrade-btn').disabled = vaultBalance < cost || level >= 10;\n        }\n\n        // Quick Service\n        const serveEl = document.getElementById('vault-upgrade-serve');\n        if (serveEl) {\n          const level = upgrades.serve || 0;\n          const cost = Math.floor(650 * Math.pow(1.6, level));\n          serveEl.querySelector('.level-num').textContent = level;\n          serveEl.querySelector('.vault-cost-value').textContent = cost;\n          serveEl.querySelector('.vault-upgrade-btn').disabled = vaultBalance < cost || level >= 10;\n        }\n\n        // Cashpile Expansion\n        const cashpileEl = document.getElementById('vault-upgrade-cashpile');\n        if (cashpileEl) {\n          const level = upgrades.cashpile || 0;\n          const cost = Math.floor(900 * Math.pow(1.6, level));\n          cashpileEl.querySelector('.level-num').textContent = level;\n          cashpileEl.querySelector('.vault-cost-value').textContent = cost;\n          cashpileEl.querySelector('.vault-upgrade-btn').disabled =\n            vaultBalance < cost || level >= 10;\n        }\n      }\n\n      function purchaseVaultUpgrade(type) {\n        if (gameMode === 'edit') return;\n\n        if (!window.gameConfig.vaultUpgrades) {\n          window.gameConfig.vaultUpgrades = {\n            earnings: 0,\n            capacity: 0,\n            tips: 0,\n            speed: 0,\n          };\n        }\n\n        const upgrades = window.gameConfig.vaultUpgrades;\n        let cost = 0;\n\n        switch (type) {\n          case 'earnings':\n            cost = Math.floor(500 * Math.pow(1.8, upgrades.earnings || 0));\n            if (vaultBalance >= cost) {\n              modifyVaultBalance(-cost);\n              upgrades.earnings = (upgrades.earnings || 0) + 1;\n            }\n            break;\n          case 'capacity':\n            cost = Math.floor(750 * Math.pow(1.7, upgrades.capacity || 0));\n            if (vaultBalance >= cost) {\n              modifyVaultBalance(-cost);\n              upgrades.capacity = (upgrades.capacity || 0) + 1;\n              updateStackUI();\n              // Capacity changed\n            }\n            break;\n          case 'tips':\n            const tipsLevel = upgrades.tips || 0;\n            if (tipsLevel >= 10) return;\n            // Max level\n            cost = Math.floor(1000 * Math.pow(2, tipsLevel));\n            if (vaultBalance >= cost) {\n              modifyVaultBalance(-cost);\n              upgrades.tips = tipsLevel + 1;\n              // Update VIP spawn chance\n              if (!window.gameConfig.vipCustomers) window.gameConfig.vipCustomers = {};\n              window.gameConfig.vipCustomers.spawnChance = 0.15 + upgrades.tips * 0.05;\n            }\n            break;\n          case 'speed':\n            const speedLevel = upgrades.speed || 0;\n            if (speedLevel >= 10) return;\n            // Max level\n            cost = Math.floor(600 * Math.pow(1.6, speedLevel));\n            if (vaultBalance >= cost) {\n              modifyVaultBalance(-cost);\n              upgrades.speed = speedLevel + 1;\n            }\n            break;\n          case 'cook':\n            const cookLevel = upgrades.cook || 0;\n            if (cookLevel >= 10) return;\n            cost = Math.floor(800 * Math.pow(1.7, cookLevel));\n            if (vaultBalance >= cost) {\n              modifyVaultBalance(-cost);\n              upgrades.cook = cookLevel + 1;\n              // Update regen time dynamically if needed\n            }\n            break;\n          case 'serve':\n            const serveLevel = upgrades.serve || 0;\n            if (serveLevel >= 10) return;\n            cost = Math.floor(650 * Math.pow(1.6, serveLevel));\n            if (vaultBalance >= cost) {\n              modifyVaultBalance(-cost);\n              upgrades.serve = serveLevel + 1;\n              // Update serve time dynamically if needed\n            }\n            break;\n          case 'cashpile':\n            const cashpileLevel = upgrades.cashpile || 0;\n            if (cashpileLevel >= 10) return;\n            cost = Math.floor(900 * Math.pow(1.6, cashpileLevel));\n            if (vaultBalance >= cost) {\n              modifyVaultBalance(-cost);\n              upgrades.cashpile = cashpileLevel + 1;\n            }\n            break;\n        }\n\n        if (cost > 0) {\n          window.gameConfig.bankVault.balance = vaultBalance;\n          playSound('upgrade_sound');\n          updateVaultUI();\n        }\n      }\n\n      // Staff hire notification\n      function showHireNotification(staffType) {\n        const notification = document.getElementById('hire-notification');\n        if (!notification) return;\n\n        const iconEl = notification.querySelector('.notification-icon');\n        const titleEl = notification.querySelector('.notification-title');\n        const subtitleEl = notification.querySelector('.notification-subtitle');\n\n        // Set content based on staff type\n        switch (staffType) {\n          case 'cashier':\n            iconEl.textContent = '\ud83c\udfea';\n            titleEl.textContent = t('notifications.staffHiredTitle');\n            subtitleEl.textContent = t('notifications.cashier');\n            break;\n          case 'cleaner':\n            iconEl.textContent = '\ud83e\uddf9';\n            titleEl.textContent = t('notifications.staffHiredTitle');\n            subtitleEl.textContent = t('notifications.cleaner');\n            break;\n          case 'runner':\n            iconEl.textContent = '\ud83c\udfc3';\n            titleEl.textContent = t('notifications.staffHiredTitle');\n            subtitleEl.textContent = t('notifications.runner');\n            break;\n          case 'vaultCollector':\n            iconEl.textContent = '\ud83d\udcb0';\n            titleEl.textContent = t('notifications.staffHiredTitle');\n            subtitleEl.textContent = t('notifications.vaultCollector');\n            break;\n          default:\n            iconEl.textContent = '\ud83d\udc68\u200d\ud83c\udf73';\n            titleEl.textContent = t('notifications.staffHiredTitle');\n            subtitleEl.textContent = staffType || t('notifications.generic');\n        }\n\n        // Show notification\n        notification.classList.add('show');\n\n        // Hide after delay\n        setTimeout(() => {\n          notification.classList.remove('show');\n        }, 2500);\n      }\n\n      function updateUpgradeUI() {\n        const config = window.gameConfig.upgrades;\n\n        // Capacity upgrade\n        const capacityEl = document.getElementById('upgrade-capacity');\n        if (!capacityEl) return;\n        // UI has been removed\n\n        const capacityCost = Math.floor(\n          config.capacity.baseCost * Math.pow(config.capacity.costMultiplier, config.capacity.level)\n        );\n        capacityEl.querySelector('.level-num').textContent = config.capacity.level;\n        capacityEl.querySelector('.cost-value').textContent = formatCurrencyAbbrev(capacityCost);\n        capacityEl.querySelector('.upgrade-btn').disabled = currentCurrency < capacityCost;\n\n        // Speed upgrade\n        const speedEl = document.getElementById('upgrade-speed');\n        const speedCost = Math.floor(\n          config.speed.baseCost * Math.pow(config.speed.costMultiplier, config.speed.level)\n        );\n        speedEl.querySelector('.level-num').textContent = config.speed.level;\n        speedEl.querySelector('.cost-value').textContent = formatCurrencyAbbrev(speedCost);\n        speedEl.querySelector('.upgrade-btn').disabled = currentCurrency < speedCost;\n\n        // Collection upgrade\n        const collectionEl = document.getElementById('upgrade-collection');\n        const collectionCost = Math.floor(\n          config.collection.baseCost *\n            Math.pow(config.collection.costMultiplier, config.collection.level)\n        );\n        collectionEl.querySelector('.level-num').textContent = config.collection.level;\n        collectionEl.querySelector('.cost-value').textContent =\n          formatCurrencyAbbrev(collectionCost);\n        collectionEl.querySelector('.upgrade-btn').disabled = currentCurrency < collectionCost;\n      }\n\n      function setupUpgradeButtons() {\n        // Setup vault panel (main upgrade UI)\n        setupVaultPanel();\n      }\n\n      function setupVaultPanel() {\n        const vaultToggle = document.getElementById('vault-toggle');\n        const vaultPanel = document.getElementById('vault-panel');\n        const vaultClose = vaultPanel.querySelector('.vault-close');\n\n        if (vaultToggle && vaultPanel) {\n          vaultToggle.addEventListener('click', () => {\n            vaultPanel.classList.add('open');\n            updateVaultUI();\n            updateStatsUI();\n            refreshLeaderboard(10);\n          });\n\n          vaultClose.addEventListener('click', () => {\n            vaultPanel.classList.remove('open');\n          });\n\n          // Close when clicking outside\n          vaultPanel.addEventListener('click', (e) => {\n            if (e.target === vaultPanel) {\n              vaultPanel.classList.remove('open');\n            }\n          });\n\n          // Setup tabs\n          const tabs = vaultPanel.querySelectorAll('.vault-tab');\n          const contents = vaultPanel.querySelectorAll('.vault-tab-content');\n\n          tabs.forEach((tab) => {\n            tab.addEventListener('click', () => {\n              // Remove active class from all tabs and contents\n              tabs.forEach((t) => t.classList.remove('active'));\n              contents.forEach((c) => c.classList.remove('active'));\n\n              // Add active class to clicked tab and corresponding content\n              tab.classList.add('active');\n              const targetId = 'vault-tab-' + tab.dataset.tab;\n              const targetContent = document.getElementById(targetId);\n              if (targetContent) {\n                targetContent.classList.add('active');\n              }\n\n              if (tab.dataset.tab === 'stats') {\n                updateStatsUI();\n              }\n              if (tab.dataset.tab === 'leaderboard') {\n                refreshLeaderboard(10);\n              }\n            });\n          });\n\n          // Setup leaderboard actions\n          const leaderboardSubmitBtn = document.getElementById('leaderboard-submit-btn');\n          const leaderboardRefreshBtn = document.getElementById('leaderboard-refresh-btn');\n          if (leaderboardSubmitBtn) {\n            leaderboardSubmitBtn.addEventListener('click', async () => {\n              await submitCurrentScoreToLeaderboard();\n            });\n          }\n          if (leaderboardRefreshBtn) {\n            leaderboardRefreshBtn.addEventListener('click', async () => {\n              await refreshLeaderboard(10);\n            });\n          }\n\n          // Setup vault upgrade buttons\n          const earningsBtn = document.querySelector('#vault-upgrade-earnings .vault-upgrade-btn');\n          const capacityBtn = document.querySelector('#vault-upgrade-capacity .vault-upgrade-btn');\n          const tipsBtn = document.querySelector('#vault-upgrade-tips .vault-upgrade-btn');\n          const speedBtn = document.querySelector('#vault-upgrade-speed .vault-upgrade-btn');\n          const cookBtn = document.querySelector('#vault-upgrade-cook .vault-upgrade-btn');\n          const serveBtn = document.querySelector('#vault-upgrade-serve .vault-upgrade-btn');\n          const cashpileBtn = document.querySelector('#vault-upgrade-cashpile .vault-upgrade-btn');\n\n          if (earningsBtn)\n            earningsBtn.addEventListener('click', () => purchaseVaultUpgrade('earnings'));\n          if (capacityBtn)\n            capacityBtn.addEventListener('click', () => purchaseVaultUpgrade('capacity'));\n          if (tipsBtn) tipsBtn.addEventListener('click', () => purchaseVaultUpgrade('tips'));\n          if (speedBtn) speedBtn.addEventListener('click', () => purchaseVaultUpgrade('speed'));\n          if (cookBtn) cookBtn.addEventListener('click', () => purchaseVaultUpgrade('cook'));\n          if (serveBtn) serveBtn.addEventListener('click', () => purchaseVaultUpgrade('serve'));\n          if (cashpileBtn)\n            cashpileBtn.addEventListener('click', () => purchaseVaultUpgrade('cashpile'));\n\n          // Setup prestige button\n          const prestigeBtn = document.getElementById('prestige-btn');\n          if (prestigeBtn) {\n            prestigeBtn.addEventListener('click', () => performPrestige());\n          }\n        }\n      }\n\n      function updateStatsUI() {\n        const vaultBalanceEl = document.getElementById('stat-vault-balance');\n        const prestigeLevelEl = document.getElementById('stat-prestige-level');\n        const profitMultEl = document.getElementById('stat-profit-mult');\n        const vipChanceEl = document.getElementById('stat-vip-chance');\n        const moveSpeedEl = document.getElementById('stat-move-speed');\n        const trayCapacityEl = document.getElementById('stat-tray-capacity');\n\n        if (vaultBalanceEl) vaultBalanceEl.textContent = '$' + formatCurrencyAbbrev(vaultBalance);\n        if (prestigeLevelEl) {\n          const prestige = window.gameConfig.prestige || {\n            level: 0,\n          };\n          prestigeLevelEl.textContent = prestige.level;\n        }\n        if (profitMultEl) {\n          const prestigeMult = getPrestigeProfitMultiplier();\n          const rushMult = getRushHourTipMultiplier();\n          const totalMult = prestigeMult * rushMult;\n          profitMultEl.textContent = 'x' + totalMult.toFixed(2);\n        }\n        if (vipChanceEl) {\n          const upgrades = window.gameConfig.vaultUpgrades || {};\n          const baseChance = 15;\n          const upgradeBonus = (upgrades.tips || 0) * 5;\n          vipChanceEl.textContent = baseChance + upgradeBonus + '%';\n        }\n        if (moveSpeedEl) {\n          moveSpeedEl.textContent = Math.round(getMoveSpeed());\n        }\n        if (trayCapacityEl) {\n          trayCapacityEl.textContent = getMaxCapacity();\n        }\n      }\n\n      function setLeaderboardStatus(messageKey, options = {}) {\n        const statusEl = document.getElementById('leaderboard-status');\n        if (!statusEl) return;\n        if (options.raw) {\n          statusEl.textContent = options.raw;\n        } else {\n          statusEl.textContent = t(messageKey, options);\n        }\n      }\n\n      function renderLeaderboardEntries(entries) {\n        const listEl = document.getElementById('leaderboard-list');\n        if (!listEl) return;\n        listEl.innerHTML = '';\n\n        if (!entries || entries.length === 0) {\n          setLeaderboardStatus('vault.leaderboard.empty');\n          return;\n        }\n\n        entries.forEach((entry, index) => {\n          const row = document.createElement('div');\n          row.className = 'leaderboard-entry';\n\n          const rankEl = document.createElement('span');\n          rankEl.className = 'leaderboard-rank';\n          rankEl.textContent = `#${index + 1}`;\n\n          const nameEl = document.createElement('span');\n          nameEl.className = 'leaderboard-name';\n          nameEl.textContent = entry.username || t('vault.leaderboard.unknownPlayer');\n\n          const scoreEl = document.createElement('span');\n          scoreEl.className = 'leaderboard-score';\n          scoreEl.textContent = formatCurrencyAbbrev(entry.score || 0);\n\n          row.appendChild(rankEl);\n          row.appendChild(nameEl);\n          row.appendChild(scoreEl);\n          listEl.appendChild(row);\n        });\n      }\n\n      async function refreshLeaderboard(numEntries = 10) {\n        setLeaderboardStatus('vault.leaderboard.loading');\n        try {\n          const response = await lib.getTopNEntriesFromLeaderboard(numEntries);\n          renderLeaderboardEntries(response.entries || []);\n          if (response.entries && response.entries.length > 0) {\n            setLeaderboardStatus('', { raw: '' });\n          }\n        } catch (error) {\n          const msg = error && error.message ? error.message : '';\n          setLeaderboardStatus('vault.leaderboard.error', { raw: `${t('vault.leaderboard.error')} ${msg}`.trim() });\n          lib.log(`Failed to fetch leaderboard: ${msg}`);\n        }\n      }\n\n      async function submitCurrentScoreToLeaderboard() {\n        const score = Number.isFinite(vaultBalance) ? vaultBalance : currentCurrency;\n        setLeaderboardStatus('vault.leaderboard.submitting');\n        try {\n          const response = await lib.addPlayerScoreToLeaderboard(score, 10);\n          if (response && response.success) {\n            renderLeaderboardEntries(response.entries || []);\n            if (response.userRank !== null && response.userRank !== undefined) {\n              setLeaderboardStatus('vault.leaderboard.submitSuccess', { rank: response.userRank });\n            } else {\n              setLeaderboardStatus('vault.leaderboard.submitSuccessNoRank');\n            }\n          } else {\n            setLeaderboardStatus('vault.leaderboard.error');\n          }\n        } catch (error) {\n          const msg = error && error.message ? error.message : '';\n          setLeaderboardStatus('vault.leaderboard.submitError', { message: msg || 'Unknown error' });\n          lib.log(`Failed to submit leaderboard score: ${msg}`);\n        }\n      }\n\n      function purchaseUpgrade(type) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.upgrades[type];\n        const cost = Math.floor(config.baseCost * Math.pow(config.costMultiplier, config.level));\n\n        if (currentCurrency >= cost) {\n          modifyVaultBalance(-cost);\n          window.gameConfig.upgrades[type].level++;\n\n          playSound('upgrade_sound');\n          updateStackUI();\n        }\n      }\n\n      function showCollectionProgress(progress) {\n        const progressEl = cachedDOMElements.collectionProgress;\n        const progressBar = cachedDOMElements.progressBar;\n        if (!progressEl || !progressBar) return;\n\n        if (progress > 0 && progress < 1) {\n          progressEl.classList.remove('hidden');\n          progressBar.style.width = progress * 100 + '%';\n        } else {\n          progressEl.classList.add('hidden');\n        }\n      }\n\n      // ============================================\n      // GAME LOGIC\n      // ============================================\n\n      function getMaxCapacity() {\n        const config = window.gameConfig;\n        let baseCapacity =\n          config.player.baseCapacity +\n          config.upgrades.capacity.level * config.upgrades.capacity.bonusPerLevel;\n\n        // Add vault capacity bonus\n        const vaultUpgrades = config.vaultUpgrades || {};\n        const vaultCapacityBonus = (vaultUpgrades.capacity || 0) * 10;\n\n        return baseCapacity + vaultCapacityBonus;\n      }\n\n      function getMoveSpeed() {\n        const config = window.gameConfig;\n        let baseSpeed =\n          config.player.baseSpeed +\n          config.upgrades.speed.level * config.upgrades.speed.bonusPerLevel;\n\n        // Add vault speed bonus (5% per level)\n        const vaultUpgrades = config.vaultUpgrades || {};\n        const vaultSpeedMultiplier = 1 + (vaultUpgrades.speed || 0) * 0.05;\n\n        let finalSpeed = baseSpeed * vaultSpeedMultiplier;\n        if (currentStackCount >= getMaxCapacity()) {\n          finalSpeed *= 0.5; // 50% slower when carrying max stack of burgers\n        }\n        return finalSpeed;\n      }\n\n      function getCollectionTime() {\n        const config = window.gameConfig;\n        const reduction =\n          config.upgrades.collection.level * config.upgrades.collection.reductionPerLevel;\n\n        // Add vault cook time reduction (5% per level)\n        const vaultUpgrades = config.vaultUpgrades || {};\n        const vaultCookBonus = 1 - (vaultUpgrades.cook || 0) * 0.05;\n\n        return Math.max(0.1, (config.player.baseCollectionTime - reduction) * vaultCookBonus);\n      }\n\n      function getVacuumSpeed() {\n        return window.gameConfig.settings.vacuumSpeed || VACUUM_SPEED_DEFAULT;\n      }\n\n      // Optimized distance calculation (avoiding sqrt when possible)\n      function distanceSquared(x1, z1, x2, z2) {\n        const dx = x1 - x2;\n        const dz = z1 - z2;\n        return dx * dx + dz * dz;\n      }\n\n      function distance(x1, z1, x2, z2) {\n        return Math.sqrt(distanceSquared(x1, z1, x2, z2));\n      }\n\n      // Squish animation state\n      let playerSquishState = {\n        scaleY: 1,\n        scaleXZ: 1,\n        targetScaleY: 1,\n        targetScaleXZ: 1,\n        wasMoving: false,\n      };\n\n      let tutorialHintTimer = 0;\n      let hintVisible = false;\n\n      function getUnlockOrder() {\n        return (\n          window.gameConfig.unlockOrder || [\n            'diningTable',\n            'grillTile',\n            'cashierTile',\n            'trashTile',\n            'staffHireTile',\n            'driveThruTile',\n            'vaultTile',\n            'cleanerStaffTile',\n            'runnerStaffTile',\n            'vaultStaffTile',\n            'fountainTile',\n          ]\n        );\n      }\n\n      function isTileUnlockedByKey(key) {\n        switch (key) {\n          case 'diningTable':\n            return window.gameConfig.diningTables && window.gameConfig.diningTables.length > 0;\n          case 'grillTile':\n            return window.gameConfig.grillTile && window.gameConfig.grillTile.unlocked;\n          case 'cashierTile':\n            return window.gameConfig.cashierTile && window.gameConfig.cashierTile.unlocked;\n          case 'trashTile':\n            return window.gameConfig.trashTile && window.gameConfig.trashTile.unlocked;\n          case 'staffHireTile':\n            return window.gameConfig.staffHireTile && window.gameConfig.staffHireTile.staffHired;\n          case 'driveThruTile':\n            return window.gameConfig.driveThruTile && window.gameConfig.driveThruTile.unlocked;\n          case 'vaultTile':\n            return window.gameConfig.vaultTile && window.gameConfig.vaultTile.unlocked;\n          case 'cleanerStaffTile':\n            return (\n              window.gameConfig.cleanerStaffTile && window.gameConfig.cleanerStaffTile.staffHired\n            );\n          case 'runnerStaffTile':\n            return (\n              window.gameConfig.runnerStaffTile && window.gameConfig.runnerStaffTile.staffHired\n            );\n          case 'vaultStaffTile':\n            return window.gameConfig.vaultStaffTile && window.gameConfig.vaultStaffTile.staffHired;\n          case 'fountainTile':\n            return window.gameConfig.fountainTile && window.gameConfig.fountainTile.unlocked;\n          case 'selfOrderKioskTile':\n            return (\n              window.gameConfig.selfOrderKioskTile && window.gameConfig.selfOrderKioskTile.unlocked\n            );\n          default:\n            return false;\n        }\n      }\n\n      function getUnlockStepFromOrder() {\n        const order = getUnlockOrder();\n        let step = 0;\n        for (let i = 0; i < order.length; i++) {\n          if (isTileUnlockedByKey(order[i])) {\n            step = i + 1;\n          } else {\n            break;\n          }\n        }\n        return step;\n      }\n\n      function shouldShowUnlockTile(key) {\n        const order = getUnlockOrder();\n        const index = order.indexOf(key);\n        if (index === -1) return false;\n        const step = window.gameConfig.tutorialStep || 0;\n        return index <= step;\n      }\n\n      function checkTutorialProgression() {\n        if (gameMode === 'edit') return;\n\n        const step = getUnlockStepFromOrder();\n        window.gameConfig.tutorialStep = step;\n\n        if (\n          shouldShowUnlockTile('grillTile') &&\n          !grillTileMesh &&\n          window.gameConfig.grillTile &&\n          !window.gameConfig.grillTile.unlocked &&\n          !window.gameConfig.grillTile.deleted\n        ) {\n          createGrillTile();\n          playSound('upgrade_sound');\n        }\n\n        if (\n          shouldShowUnlockTile('cashierTile') &&\n          !cashierTileMesh &&\n          window.gameConfig.cashierTile &&\n          !window.gameConfig.cashierTile.unlocked &&\n          !window.gameConfig.cashierTile.deleted\n        ) {\n          createCashierTile();\n          playSound('upgrade_sound');\n        }\n\n        if (shouldShowUnlockTile('trashTile')) {\n          if (\n            !trashTileMesh &&\n            window.gameConfig.trashTile &&\n            !window.gameConfig.trashTile.unlocked &&\n            !window.gameConfig.trashTile.deleted\n          ) {\n            createTrashTile();\n          }\n          if (\n            !staffHireTileMesh &&\n            window.gameConfig.staffHireTile &&\n            !window.gameConfig.staffHireTile.staffHired &&\n            !window.gameConfig.staffHireTile.deleted\n          ) {\n            createStaffHireTile();\n          }\n          if (window.gameConfig.diningTableTiles) {\n            let tableTileCreated = false;\n            window.gameConfig.diningTableTiles.forEach((tile, index) => {\n              if (index > 0 && !tile.deleted && !tableTileCreated) {\n                if (!diningTableTileMeshes.find((t) => t.index === index)) {\n                  createDiningTableTile(index);\n                }\n                tableTileCreated = true;\n              }\n            });\n          }\n        }\n\n        if (shouldShowUnlockTile('driveThruTile')) {\n          if (\n            !driveThruTileMesh &&\n            window.gameConfig.driveThruTile &&\n            !window.gameConfig.driveThruTile.unlocked &&\n            !window.gameConfig.driveThruTile.deleted\n          ) {\n            createDriveThruTile();\n            playSound('upgrade_sound');\n          }\n          if (\n            !vaultTileMesh &&\n            window.gameConfig.vaultTile &&\n            !window.gameConfig.vaultTile.unlocked &&\n            !window.gameConfig.vaultTile.deleted\n          ) {\n            createVaultTile();\n            playSound('upgrade_sound');\n          }\n          if (\n            !cleanerStaffTileMesh &&\n            window.gameConfig.cleanerStaffTile &&\n            !window.gameConfig.cleanerStaffTile.staffHired &&\n            !window.gameConfig.cleanerStaffTile.deleted\n          ) {\n            createCleanerStaffTile();\n          }\n          if (\n            !runnerStaffTileMesh &&\n            window.gameConfig.runnerStaffTile &&\n            !window.gameConfig.runnerStaffTile.staffHired &&\n            !window.gameConfig.runnerStaffTile.deleted\n          ) {\n            createRunnerStaffTile();\n          }\n          if (\n            !vaultStaffTileMesh &&\n            window.gameConfig.vaultStaffTile &&\n            !window.gameConfig.vaultStaffTile.staffHired &&\n            !window.gameConfig.vaultStaffTile.deleted\n          ) {\n            createVaultStaffTile();\n          }\n          if (\n            window.gameConfig.fountainTile &&\n            shouldShowUnlockTile('fountainTile') &&\n            !window.gameConfig.fountainTile.unlocked &&\n            !window.gameConfig.fountainTile.deleted &&\n            !fountainTileMesh\n          ) {\n            createFountainTile();\n          }\n          if (\n            window.gameConfig.selfOrderKioskTile &&\n            shouldShowUnlockTile('selfOrderKioskTile') &&\n            !window.gameConfig.selfOrderKioskTile.unlocked &&\n            !window.gameConfig.selfOrderKioskTile.deleted &&\n            !selfOrderKioskTileMesh\n          ) {\n            createSelfOrderKioskTile();\n          }\n        }\n      }\n\n      function getCashierHeldUpReason() {\n        if (window.gameConfig.cashierStall && !window.gameConfig.cashierStall.deleted) {\n          let firstWaiting = null;\n          for (let i = 0; i < customerQueue.length; i++) {\n            if (customerQueue[i].state === 'waiting' && customerQueue[i].queuePosition === 0) {\n              firstWaiting = customerQueue[i];\n              break;\n            }\n          }\n          if (firstWaiting && firstWaiting.waitTime > 5) {\n            const config = window.gameConfig.cashierStall;\n            const maxMoney =\n              (config.maxMoney || 50) + (window.gameConfig.vaultUpgrades?.cashpile || 0) * 20;\n            const staffHired =\n              window.gameConfig.staffHireTile && window.gameConfig.staffHireTile.staffHired;\n\n            if (cashierCashPile.length >= maxMoney) {\n              return 'cash';\n            } else if (cashierBurgerStack.length === 0) {\n              return 'food';\n            } else if (!staffHired && !isInCashierZone) {\n              return 'man';\n            }\n          }\n        }\n        return null;\n      }\n\n      function updateTutorialArrow(deltaTime) {\n        if (gameMode === 'edit') {\n          if (tutorialArrowMesh) tutorialArrowMesh.visible = false;\n          return;\n        }\n        if (!tutorialArrowMesh) return;\n\n        let targetPos = null;\n        let targetYOffset = 3;\n\n        const cashierHeldUpReason = getCashierHeldUpReason();\n\n        const table0 = window.gameConfig.diningTables && window.gameConfig.diningTables.length > 0;\n        const grillUnlocked = window.gameConfig.grillTile && window.gameConfig.grillTile.unlocked;\n        const cashierUnlocked =\n          window.gameConfig.cashierTile && window.gameConfig.cashierTile.unlocked;\n        const cashierStocked =\n          cashierBurgerStack.length > 0 || window.gameConfig.tutorialHasStockedCashier;\n        const staffHired =\n          window.gameConfig.staffHireTile && window.gameConfig.staffHireTile.staffHired;\n        const hasCleanedTable = window.gameConfig.tutorialHasCleanedTable || false;\n        const hasCollectedMoney = window.gameConfig.tutorialHasCollectedMoney || false;\n\n        if (cashierHeldUpReason === 'cash') {\n          if (cashierStallMesh && cashierStallMesh.userData) {\n            targetPos = {\n              x: cashierStallMesh.userData.cashPileX,\n              z: cashierStallMesh.userData.cashPileZ,\n            };\n          }\n        } else if (cashierHeldUpReason === 'food') {\n          if (currentStackCount > 0) {\n            if (cashierStallMesh) {\n              targetPos = { x: cashierStallMesh.position.x, z: cashierStallMesh.position.z };\n              targetYOffset = 4;\n            }\n          } else {\n            let readyGrill = null;\n            for (const node of resourceNodes) {\n              if (node.isReady) {\n                readyGrill = node;\n                break;\n              }\n            }\n            if (readyGrill) {\n              targetPos = { x: readyGrill.position.x, z: readyGrill.position.z };\n            } else if (resourceNodes.length > 0 && resourceNodes[0].grillMesh) {\n              targetPos = { x: resourceNodes[0].position.x, z: resourceNodes[0].position.z };\n            }\n          }\n        } else if (cashierHeldUpReason === 'man') {\n          if (cashierStallMesh) {\n            targetPos = { x: cashierStallMesh.position.x, z: cashierStallMesh.position.z - 2 };\n          }\n        } else if (!table0) {\n          if (window.gameConfig.diningTableTiles.length > 0) {\n            targetPos = {\n              x: window.gameConfig.diningTableTiles[0].x,\n              z: window.gameConfig.diningTableTiles[0].z,\n            };\n          }\n        } else if (!grillUnlocked) {\n          if (window.gameConfig.grillTile) {\n            targetPos = { x: window.gameConfig.grillTile.x, z: window.gameConfig.grillTile.z };\n          }\n        } else if (!cashierUnlocked) {\n          if (window.gameConfig.cashierTile) {\n            targetPos = { x: window.gameConfig.cashierTile.x, z: window.gameConfig.cashierTile.z };\n          }\n        } else if (!cashierStocked) {\n          if (currentStackCount > 0) {\n            if (cashierStallMesh) {\n              targetPos = { x: cashierStallMesh.position.x, z: cashierStallMesh.position.z };\n              targetYOffset = 4;\n            }\n          } else {\n            if (resourceNodes.length > 0 && resourceNodes[0].grillMesh) {\n              targetPos = { x: resourceNodes[0].position.x, z: resourceNodes[0].position.z };\n            }\n          }\n        } else if (!staffHired) {\n          if (staffHireTileMesh) {\n            targetPos = { x: staffHireTileMesh.position.x, z: staffHireTileMesh.position.z };\n          }\n        } else if (!hasCleanedTable) {\n          let dirtyTable = null;\n          for (const table of diningTableMeshes) {\n            if (table.tableData.hasTrash) {\n              dirtyTable = table;\n              break;\n            }\n          }\n          if (dirtyTable) {\n            targetPos = { x: dirtyTable.tableData.x, z: dirtyTable.tableData.z };\n            targetYOffset = 3;\n          }\n        } else if (!hasCollectedMoney) {\n          if (cashierCashPile.length > 0) {\n            if (cashierStallMesh && cashierStallMesh.userData) {\n              targetPos = {\n                x: cashierStallMesh.userData.cashPileX,\n                z: cashierStallMesh.userData.cashPileZ,\n              };\n            }\n          }\n        } else if (window.gameConfig.driveThruTile && window.gameConfig.driveThruTile.unlocked && !window.gameConfig.tutorialHasSeenDriveThruHint) {\n          if (currentStackCount > 0) {\n            if (driveThruBoothMesh) {\n              const depositZoneX = RESTAURANT_WIDTH / 2 - 3;\n              targetPos = { x: depositZoneX, z: driveThruBoothMesh.position.z };\n              targetYOffset = 3;\n            }\n          } else {\n            let readyGrill = null;\n            for (const node of resourceNodes) {\n              if (node.isReady) {\n                readyGrill = node;\n                break;\n              }\n            }\n            if (readyGrill) {\n              targetPos = { x: readyGrill.position.x, z: readyGrill.position.z };\n            } else if (resourceNodes.length > 0 && resourceNodes[0].grillMesh) {\n              targetPos = { x: resourceNodes[0].position.x, z: resourceNodes[0].position.z };\n            }\n          }\n        }\n\n        if (targetPos) {\n          tutorialArrowMesh.visible = true;\n          tutorialArrowMesh.position.x = targetPos.x;\n          tutorialArrowMesh.position.z = targetPos.z;\n          tutorialArrowMesh.position.y = targetYOffset + Math.sin(Date.now() * 0.005) * 0.5;\n        } else {\n          tutorialArrowMesh.visible = false;\n        }\n      }\n\n      function showTutorialComplete() {\n        if (tutorialCompleteShown) return;\n        tutorialCompleteShown = true;\n        tutorialCompleteTimer = 2.5;\n        window.gameConfig.tutorialCompleted = true;\n\n        const overlay = document.getElementById('tutorial-complete');\n        if (overlay) {\n          overlay.classList.add('show');\n        }\n\n        if (window.gameConfig.selfOrderKioskTiles) {\n          window.gameConfig.selfOrderKioskTiles.forEach((tile) => {\n            if (tile) tile.hidden = false;\n          });\n          createSelfOrderKioskTiles();\n        }\n\n        spawnConfettiBurst(80);\n        playSound('upgrade_sound');\n      }\n\n      function spawnConfettiBurst(count = 60) {\n        const layer = document.getElementById('confetti-layer');\n        if (!layer) return;\n        const colors = ['#F1C40F', '#E74C3C', '#2ECC71', '#3498DB', '#9B59B6', '#F39C12'];\n        for (let i = 0; i < count; i++) {\n          const piece = document.createElement('div');\n          piece.className = 'confetti-piece';\n          piece.style.left = Math.random() * 100 + '%';\n          piece.style.background = colors[Math.floor(Math.random() * colors.length)];\n          piece.style.transform = `translateY(-10vh) rotate(${Math.random() * 360}deg)`;\n          piece.style.animationDelay = Math.random() * 0.3 + 's';\n          piece.style.width = 8 + Math.random() * 8 + 'px';\n          piece.style.height = 12 + Math.random() * 10 + 'px';\n          layer.appendChild(piece);\n          piece.addEventListener('animationend', () => piece.remove());\n        }\n      }\n\n      function updateTutorialHint(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const cashierHeldUpReason = getCashierHeldUpReason();\n\n        const table0 = window.gameConfig.diningTables && window.gameConfig.diningTables.length > 0;\n        const grillUnlocked = window.gameConfig.grillTile && window.gameConfig.grillTile.unlocked;\n        const cashierUnlocked =\n          window.gameConfig.cashierTile && window.gameConfig.cashierTile.unlocked;\n        const cashierStocked =\n          cashierBurgerStack.length > 0 || window.gameConfig.tutorialHasStockedCashier;\n        const staffHired =\n          window.gameConfig.staffHireTile && window.gameConfig.staffHireTile.staffHired;\n        const hasCleanedTable = window.gameConfig.tutorialHasCleanedTable || false;\n        const hasCollectedMoney = window.gameConfig.tutorialHasCollectedMoney || false;\n\n        let hintText = '';\n\n        if (cashierHeldUpReason === 'cash') {\n          hintText = t('tutorial.cashFull');\n        } else if (cashierHeldUpReason === 'food') {\n          if (currentStackCount === 0) hintText = t('tutorial.waitingCollect');\n          else hintText = t('tutorial.waitingBring');\n        } else if (cashierHeldUpReason === 'man') {\n          hintText = t('tutorial.waitingMan');\n        } else if (!table0) {\n          hintText = t('tutorial.buildTable');\n        } else if (!grillUnlocked) {\n          hintText = t('tutorial.buildGrill');\n        } else if (!cashierUnlocked) {\n          hintText = t('tutorial.buildCashier');\n        } else if (!cashierStocked) {\n          if (currentStackCount === 0) hintText = t('tutorial.collectFood');\n          else hintText = t('tutorial.bringFood');\n        } else if (!staffHired) {\n          hintText = t('tutorial.hireStaff');\n        } else if (!hasCleanedTable) {\n          hintText = t('tutorial.cleanTable');\n        } else if (!hasCollectedMoney) {\n          hintText = t('tutorial.collectEarnings');\n        } else if (window.gameConfig.driveThruTile && window.gameConfig.driveThruTile.unlocked && !window.gameConfig.tutorialHasSeenDriveThruHint) {\n          if (currentStackCount === 0) {\n            hintText = t('tutorial.driveThruCollect');\n          } else {\n            hintText = t('tutorial.driveThruDeposit');\n          }\n        } else {\n          showTutorialComplete();\n        }\n\n        const hintEl = document.getElementById('tutorial-hint');\n        if (hintEl) {\n          if (hintText) {\n            hintEl.textContent = hintText;\n            hintEl.style.opacity = 1;\n          } else {\n            hintEl.style.opacity = 0;\n          }\n        }\n      }\n\n      function updatePlayer(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        updateInputFromKeyboard();\n\n        const speed = getMoveSpeed();\n\n        const s = Math.sin(targetCameraAngle);\n        const c = Math.cos(targetCameraAngle);\n        const rotatedX = inputVector.x * c + inputVector.z * s;\n        const rotatedZ = -inputVector.x * s + inputVector.z * c;\n\n        const moveX = rotatedX * speed * deltaTime;\n        const moveZ = rotatedZ * speed * deltaTime;\n        const isMoving = moveX !== 0 || moveZ !== 0;\n\n        if (isMoving) {\n          // Update position\n          player.position.x += moveX;\n          player.position.z += moveZ;\n\n          // Clamp to rectangular restaurant bounds\n          const halfWidth = RESTAURANT_WIDTH / 2 - 1;\n          const halfDepth = RESTAURANT_DEPTH / 2 - 1;\n          player.position.x = Math.max(-halfWidth, Math.min(halfWidth, player.position.x));\n          player.position.z = Math.max(-halfDepth, Math.min(halfDepth, player.position.z));\n\n          // Rotate player to face movement direction\n          const targetAngle = Math.atan2(moveX, moveZ);\n          player.rotation.y = targetAngle;\n\n          // Squishy walk cycle - squash and stretch with movement\n          const walkCycle = Math.sin(Date.now() * 0.015) * 0.5 + 0.5;\n          // 0 to 1\n          playerSquishState.targetScaleY = 0.92 + walkCycle * 0.16;\n          // 0.92 to 1.08\n          playerSquishState.targetScaleXZ = 1.08 - walkCycle * 0.16;\n          // 1.08 to 0.92\n\n          // Trigger squish when starting to move\n          if (!playerSquishState.wasMoving) {\n            playerSquishState.scaleY = 0.85;\n            playerSquishState.scaleXZ = 1.15;\n          }\n        } else {\n          // Idle breathing/bounce animation\n          const breathe = Math.sin(Date.now() * 0.003);\n          playerSquishState.targetScaleY = 1 + breathe * 0.03;\n          playerSquishState.targetScaleXZ = 1 - breathe * 0.02;\n\n          // Trigger squish when stopping\n          if (playerSquishState.wasMoving) {\n            playerSquishState.scaleY = 1.12;\n            playerSquishState.scaleXZ = 0.88;\n          }\n        }\n\n        playerSquishState.wasMoving = isMoving;\n\n        // Switch player animation based on state\n        const isCarrying = currentStackCount > 0 || currentMoneyCount > 0;\n        if (isMoving) {\n          switchAnimation(player, isCarrying ? 'walkCarry' : 'run');\n        } else if (\n          isCollecting ||\n          isInCashierZone ||\n          isInSellZone ||\n          isInMoneyZone ||\n          isInSpendZone\n        ) {\n          switchAnimation(player, 'interact');\n        } else {\n          switchAnimation(player, 'idle');\n        }\n\n        // Smooth squish interpolation (springy feel)\n        const squishSpeed = 12 * deltaTime;\n        playerSquishState.scaleY +=\n          (playerSquishState.targetScaleY - playerSquishState.scaleY) * squishSpeed;\n        playerSquishState.scaleXZ +=\n          (playerSquishState.targetScaleXZ - playerSquishState.scaleXZ) * squishSpeed;\n\n        // Apply squish to player\n        player.scale.set(\n          playerSquishState.scaleXZ,\n          playerSquishState.scaleY,\n          playerSquishState.scaleXZ\n        );\n\n        // Spawn dust particles when walking\n        if (isMoving && Math.random() < 0.3) {\n          // 30% chance each frame\n          spawnDustParticle(player.position.x, 0, player.position.z, moveX, moveZ);\n        }\n\n        // Bobbing animation for stack (burgers and money)\n        const bobAmount = Math.sin(Date.now() * 0.005) * 0.02;\n        playerStack.forEach((item, i) => {\n          item.position.y = PLAYER_HEIGHT + i * STACK_ITEM_HEIGHT + bobAmount * (i + 1) * 0.5;\n          item.position.z = 1;\n        });\n\n        updateTray(player, playerStack);\n\n        // Update money stack positions (on top of burgers)\n        const burgerStackHeight = playerStack.length * STACK_ITEM_HEIGHT;\n        moneyStack.forEach((money, i) => {\n          money.position.y =\n            PLAYER_HEIGHT +\n            burgerStackHeight +\n            i * MONEY_STACK_HEIGHT +\n            bobAmount * (playerStack.length + i + 1) * 0.5;\n        });\n\n        if (player.userData.maxSprite) {\n          const isBurgerMax = currentStackCount >= getMaxCapacity();\n          const isMoneyMax = currentMoneyCount >= MAX_MONEY_CAPACITY;\n          if (isBurgerMax || isMoneyMax) {\n            player.userData.maxSprite.visible = true;\n            const totalStackCount = playerStack.length + moneyStack.length;\n            const stackY =\n              PLAYER_HEIGHT + burgerStackHeight + moneyStack.length * MONEY_STACK_HEIGHT;\n            player.userData.maxSprite.position.y =\n              stackY + 0.6 + bobAmount * (totalStackCount + 2) * 0.5;\n          } else {\n            player.userData.maxSprite.visible = false;\n          }\n        }\n      }\n\n      function updateCamera() {\n        if (gameMode === 'edit') {\n          // In edit mode, use camera offset from panning - higher and wider view\n          const targetX = editorCameraOffset.x;\n          const targetZ = editorCameraOffset.z + 35;\n          const targetY = 50;\n          // Higher view for editing full restaurant\n\n          camera.position.x += (targetX - camera.position.x) * 0.1;\n          camera.position.z += (targetZ - camera.position.z) * 0.1;\n          camera.position.y += (targetY - camera.position.y) * 0.1;\n\n          camera.lookAt(editorCameraOffset.x, 0, editorCameraOffset.z);\n        } else {\n          // Follow player directly with smooth interpolation\n          currentCameraAngle += (targetCameraAngle - currentCameraAngle) * 0.1;\n\n          const distance = isZoomedOut ? 30 : 18;\n          const targetX = player.position.x + Math.sin(currentCameraAngle) * distance;\n          const targetZ = player.position.z + Math.cos(currentCameraAngle) * distance;\n          // Camera behind player\n          const targetY = isZoomedOut ? 40 : 22;\n          // Camera height\n\n          // Smooth camera follow (higher value = more responsive)\n          const followSpeed = 0.08;\n          camera.position.x += (targetX - camera.position.x) * followSpeed;\n          camera.position.z += (targetZ - camera.position.z) * followSpeed;\n          camera.position.y += (targetY - camera.position.y) * followSpeed;\n\n          // Look at player position\n          camera.lookAt(player.position.x, player.position.y + 2, player.position.z);\n        }\n      }\n\n      function updateResourceNodes(deltaTime) {\n        const config = window.gameConfig.resources;\n        const playerX = player.position.x;\n        const playerZ = player.position.z;\n        const rangeSquared = COLLECTION_RANGE * COLLECTION_RANGE;\n\n        nearestNode = null;\n        let nearestDistSq = Infinity;\n        const now = Date.now();\n\n        for (let i = 0; i < resourceNodes.length; i++) {\n          const node = resourceNodes[i];\n\n          // Animate steam on ready grills (throttled)\n          if (node.isReady && node.grillMesh) {\n            const children = node.grillMesh.children;\n            for (let j = 0; j < children.length; j++) {\n              const child = children[j];\n              if (child.userData.steamOffset !== undefined) {\n                child.position.y = 1.5 + Math.sin(now * 0.003 + child.userData.steamOffset) * 0.2;\n                child.material.opacity =\n                  0.3 + Math.sin(now * 0.002 + child.userData.steamOffset) * 0.2;\n              }\n            }\n          }\n\n          // Check regeneration\n          if (!node.isReady) {\n            node.regenTimer -= deltaTime;\n\n            // Visual regeneration progress (food cooking)\n            const progress = 1 - node.regenTimer / config.regenerationTime;\n            if (node.grillMesh) {\n              node.grillMesh.scale.setScalar(Math.min(progress, 1));\n            }\n\n            if (node.regenTimer <= 0) {\n              node.isReady = true;\n              if (node.grillMesh) {\n                node.grillMesh.visible = true;\n                node.grillMesh.scale.setScalar(1);\n              }\n              if (node.emptyMesh) {\n                node.emptyMesh.visible = false;\n              }\n            }\n          }\n\n          // Find nearest ready node (use squared distance to avoid sqrt)\n          if (node.isReady && gameMode === 'play') {\n            const distSq = distanceSquared(playerX, playerZ, node.position.x, node.position.z);\n\n            if (distSq < rangeSquared && distSq < nearestDistSq) {\n              nearestDistSq = distSq;\n              nearestNode = node;\n            }\n          }\n        }\n      }\n\n      function updateCollection(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const maxCapacity = getMaxCapacity();\n\n        if (nearestNode && currentStackCount < maxCapacity) {\n          isCollecting = true;\n          collectionProgress += deltaTime / getCollectionTime();\n\n          if (collectionProgress >= 1) {\n            collectionProgress = 0;\n\n            // Deplete equipment and spawn food items on ground\n            if (nearestNode.isReady) {\n              nearestNode.isReady = false;\n              nearestNode.regenTimer = window.gameConfig.resources.regenerationTime;\n              if (nearestNode.grillMesh) {\n                nearestNode.grillMesh.visible = false;\n                nearestNode.grillMesh.scale.setScalar(0);\n              }\n              if (nearestNode.emptyMesh) {\n                nearestNode.emptyMesh.visible = true;\n              }\n\n              // Get the equipment type and spawn appropriate food\n              const equipmentType = nearestNode.equipmentType || 'grill';\n              const itemsToSpawn = 3 + Math.floor(Math.random() * 2);\n              // 3-4 items\n              for (let i = 0; i < itemsToSpawn; i++) {\n                const angle = (i / itemsToSpawn) * Math.PI * 2;\n                const distance = 1.5 + Math.random() * 1;\n                const spawnPos = new THREE.Vector3(\n                  nearestNode.position.x + Math.cos(angle) * distance,\n                  0,\n                  nearestNode.position.z + Math.sin(angle) * distance\n                );\n                // Spawn food that matches this equipment type\n                const menuItem = getRandomMenuItemForEquipment(equipmentType);\n                createGroundItem(spawnPos, menuItem);\n              }\n\n              playSound('pickup_sound');\n            }\n          }\n\n          showCollectionProgress(collectionProgress);\n        } else {\n          isCollecting = false;\n          collectionProgress = 0;\n          showCollectionProgress(0);\n        }\n      }\n\n      function updateSelling(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.sellZone;\n        if (!config || config.deleted) return;\n        const sellRadius = config.radius;\n        const sellX = config.x || 0;\n        const sellZ = config.z || 0;\n        const dx = player.position.x - sellX;\n        const dz = player.position.z - sellZ;\n        const dist = Math.sqrt(dx * dx + dz * dz);\n\n        isInSellZone = dist < sellRadius;\n\n        if (isInSellZone && currentStackCount > 0) {\n          sellTimer += deltaTime;\n\n          if (sellTimer >= SELL_DELAY) {\n            sellTimer = 0;\n\n            if (playerStack.length > 0) {\n              // Remove from stack visually\n              const item = playerStack.pop();\n              player.remove(item);\n              currentStackCount = playerStack.length;\n              updateStackUI();\n\n              // Throw item toward sell zone with arc\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n\n              const targetPos = {\n                x: sellX,\n                y: 1,\n                z: sellZ,\n              };\n\n              // Create flying burger that spawns money when it arrives\n              const flyingBurger = createFlyingItem(\n                startPos,\n                targetPos,\n                'burger',\n                () => {\n                  // Spawn money in the money zone when burger arrives\n                  const moneyConfig = window.gameConfig.moneyZone;\n                  const gridIndex = findBestGridSlot();\n                  const gridPos = getGridPosition(gridIndex);\n                  const stackHeight = moneyGridStacks[gridIndex].count * 0.25;\n\n                  // Throw money from sell zone to money zone\n                  const moneyStartPos = {\n                    x: sellX,\n                    y: 1.5,\n                    z: sellZ,\n                  };\n                  const moneyTargetPos = {\n                    x: gridPos.x,\n                    y: 0.25 + stackHeight,\n                    z: gridPos.z,\n                  };\n\n                  createFlyingItem(\n                    moneyStartPos,\n                    moneyTargetPos,\n                    'money',\n                    () => {\n                      // Create actual money item when it lands\n                      const bill = createMoneyItem(new THREE.Vector3(gridPos.x, 0, gridPos.z));\n                      bill.position.y = 0.25 + stackHeight;\n                      bill.userData.gridIndex = gridIndex;\n\n                      moneyGridStacks[gridIndex].count++;\n                      moneyGridStacks[gridIndex].bills.push(bill);\n                    },\n                    true\n                  );\n\n                  playSound('sell_sound');\n                },\n                true\n              );\n\n              // Dispose of original item\n              item.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) {\n                    child.material.forEach((mat) => mat.dispose());\n                  } else {\n                    child.material.dispose();\n                  }\n                }\n              });\n            }\n          }\n        } else {\n          sellTimer = 0;\n        }\n\n        // Animate sell zone\n        if (sellZoneGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.003) * 0.05;\n          sellZoneGlow.scale.setScalar(pulseScale);\n        }\n\n        // Animate coin indicator\n        if (sellZoneMesh) {\n          const coinIndicator = sellZoneMesh.userData.coinIndicator;\n          if (coinIndicator) {\n            coinIndicator.rotation.y += deltaTime * 2;\n            coinIndicator.position.y =\n              coinIndicator.userData.baseY + Math.sin(Date.now() * 0.002) * 0.3;\n          }\n        }\n      }\n\n      function updateGroundItems(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const maxCapacity = getMaxCapacity();\n        const vacuumRangeSq = VACUUM_RANGE * VACUUM_RANGE;\n        const playerX = player.position.x;\n        const playerY = player.position.y;\n        const playerZ = player.position.z;\n        const halfWidth = RESTAURANT_WIDTH / 2 - 1;\n        const halfDepth = RESTAURANT_DEPTH / 2 - 1;\n\n        // Find the nearest uncollected ground item within range\n        let nearestItem = null;\n        let nearestDistSq = Infinity;\n\n        for (let i = groundItems.length - 1; i >= 0; i--) {\n          const item = groundItems[i];\n\n          // Clean up collected items\n          if (item.userData.collected) {\n            scene.remove(item);\n            item.traverse((child) => {\n              if (child.geometry) child.geometry.dispose();\n              if (child.material) {\n                if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                else child.material.dispose();\n              }\n            });\n            groundItems.splice(i, 1);\n            continue;\n          }\n\n          // Apply simple physics (slow down and settle)\n          const vel = item.userData.velocity;\n          vel.x *= 0.95;\n          vel.z *= 0.95;\n\n          item.position.x += vel.x * deltaTime;\n          item.position.z += vel.z * deltaTime;\n\n          // Clamp to bounds\n          if (Math.abs(item.position.x) > halfWidth) {\n            item.position.x = Math.sign(item.position.x) * halfWidth;\n            vel.x = 0;\n          }\n          if (Math.abs(item.position.z) > halfDepth) {\n            item.position.z = Math.sign(item.position.z) * halfDepth;\n            vel.z = 0;\n          }\n\n          // Find nearest item to collect (one at a time)\n          if (!item.userData.beingVacuumed && currentStackCount + incomingBurgers < maxCapacity) {\n            const dxPlayer = playerX - item.position.x;\n            const dzPlayer = playerZ - item.position.z;\n            const itemDistSq = dxPlayer * dxPlayer + dzPlayer * dzPlayer;\n\n            if (itemDistSq < vacuumRangeSq && itemDistSq < nearestDistSq) {\n              nearestDistSq = itemDistSq;\n              nearestItem = item;\n            }\n          }\n        }\n\n        // Collect one item at a time with delay (like depositing)\n        if (nearestItem && currentStackCount + incomingBurgers < maxCapacity) {\n          burgerCollectTimer += deltaTime;\n\n          if (burgerCollectTimer >= BURGER_COLLECT_DELAY) {\n            burgerCollectTimer = 0;\n\n            // Mark as being collected\n            nearestItem.userData.beingVacuumed = true;\n            const menuItem = nearestItem.userData.menuItem;\n            // Get the menu item\n            incomingBurgers++;\n\n            // Create flying item toward player\n            const startPos = {\n              x: nearestItem.position.x,\n              y: nearestItem.position.y + 0.3,\n              z: nearestItem.position.z,\n            };\n\n            const targetPos = {\n              x: playerX,\n              y: playerY + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n              z: playerZ,\n            };\n\n            createFlyingItem(\n              startPos,\n              targetPos,\n              'burger',\n              () => {\n                nearestItem.userData.collected = true;\n                incomingBurgers--;\n                addToStack(menuItem);\n                // Pass the menu item\n              },\n              true\n            );\n\n            // Hide the original item immediately\n            nearestItem.visible = false;\n\n            playSound('pickup_sound');\n          }\n        } else {\n          burgerCollectTimer = 0;\n        }\n      }\n\n      function updateMoneyZone(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.moneyZone;\n        if (!config || config.deleted) return;\n\n        const playerX = player.position.x;\n        const playerY = player.position.y;\n        const playerZ = player.position.z;\n\n        // Check if player is in money zone (rectangular area)\n        const inZoneX = Math.abs(playerX - config.x) < config.width / 2;\n        const inZoneZ = Math.abs(playerZ - config.z) < config.depth / 2;\n        isInMoneyZone = inZoneX && inZoneZ;\n\n        const vacuumRangeSq = (VACUUM_RANGE + 2) * (VACUUM_RANGE + 2);\n\n        // Clean up collected money items (handled immediately now, so we don't need the loop for vacuuming items)\n        for (let i = groundMoneyItems.length - 1; i >= 0; i--) {\n          const item = groundMoneyItems[i];\n          if (item.userData.collected) {\n            scene.remove(item);\n            item.traverse((child) => {\n              if (child.geometry) child.geometry.dispose();\n              if (child.material) {\n                if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                else child.material.dispose();\n              }\n            });\n            groundMoneyItems.splice(i, 1);\n          }\n        }\n\n        if (isInMoneyZone && groundMoneyItems.length > 0) {\n          let collectedCount = 0;\n          while (groundMoneyItems.length > 0) {\n            const money = groundMoneyItems.pop();\n            incomingMoney++;\n\n            const startPos = {\n              x: money.position.x,\n              y: money.position.y + 0.3,\n              z: money.position.z,\n            };\n\n            const targetPos = {\n              x: playerX,\n              y: playerY + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n              z: playerZ,\n            };\n\n            const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n            createFlyingItem(\n              startPos,\n              targetPos,\n              'money',\n              () => {\n                modifyVaultBalance(moneyValue);\n                if (collectedCount === 0) playSound('pickup_sound');\n              },\n              true\n            );\n\n            const gridIndex = money.userData.gridIndex;\n            if (gridIndex >= 0 && gridIndex < moneyGridStacks.length) {\n              const stack = moneyGridStacks[gridIndex];\n              const billIndex = stack.bills.indexOf(money);\n              if (billIndex > -1) {\n                stack.bills.splice(billIndex, 1);\n                stack.count = Math.max(0, stack.count - 1);\n              }\n            }\n\n            scene.remove(money);\n            money.traverse((child) => {\n              if (child.geometry) child.geometry.dispose();\n              if (child.material) {\n                if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                else child.material.dispose();\n              }\n            });\n            collectedCount++;\n          }\n          if (collectedCount > 0) playSound('pickup_sound');\n        }\n\n        // Animate money zone glow\n        if (moneyZoneGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.03;\n          moneyZoneGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n\n        // Animate coin indicator\n        const signIndicator = moneyZoneMesh.userData.signIndicator;\n        if (signIndicator) {\n          signIndicator.position.y =\n            signIndicator.userData.baseY + Math.sin(Date.now() * 0.003) * 0.15;\n          signIndicator.rotation.y += deltaTime * 1.2;\n        }\n      }\n\n      function updateCashierStall(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.cashierStall;\n        if (!config || config.deleted || !cashierStallMesh) return;\n\n        // Check if player is in cashier deposit zone (behind counter)\n        const depositZoneZ = config.z - config.depth / 2 - 1;\n        const inZoneX = Math.abs(player.position.x - config.x) < config.width / 2 + 0.5;\n        const inZoneZ = Math.abs(player.position.z - depositZoneZ) < 1.5;\n        isInCashierZone = inZoneX && inZoneZ;\n\n        // Deposit burgers when in zone\n        if (\n          isInCashierZone &&\n          currentStackCount > 0 &&\n          cashierBurgerStack.length < config.maxBurgers\n        ) {\n          cashierDepositTimer += deltaTime;\n\n          if (cashierDepositTimer >= CASHIER_DEPOSIT_DELAY) {\n            cashierDepositTimer = 0;\n            window.gameConfig.tutorialHasStockedCashier = true;\n\n            if (playerStack.length > 0) {\n              // Remove from player stack\n              const item = playerStack.pop();\n              const menuItem = item.userData.menuItem;\n              // Get the menu item data\n              player.remove(item);\n              currentStackCount = playerStack.length;\n              updateStackUI();\n\n              // Throw burger to cashier tray\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n\n              const trayX = config.x - config.width / 2 + 1.5;\n              const targetPos = {\n                x: trayX,\n                y: 1.5,\n                z: config.z,\n              };\n\n              createFlyingItem(\n                startPos,\n                targetPos,\n                'burger',\n                () => {\n                  addBurgerToCashier(menuItem);\n                  // Pass the menu item\n                  playSound('pickup_sound');\n                  updateCashierUI();\n                },\n                true\n              );\n\n              // Dispose of original item\n              item.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) {\n                    child.material.forEach((mat) => mat.dispose());\n                  } else {\n                    child.material.dispose();\n                  }\n                }\n              });\n            }\n          }\n        } else {\n          cashierDepositTimer = 0;\n        }\n\n        // Animate glow\n        if (cashierStallGlow) {\n          const pulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          cashierStallGlow.scale.set(pulse, 1, pulse);\n\n          // Change color based on state\n          if (cashierBurgerStack.length === 0) {\n            cashierStallGlow.material.color.setHex(0xe74c3c);\n            // Red when empty\n          } else if (cashierBurgerStack.length >= config.maxBurgers) {\n            cashierStallGlow.material.color.setHex(0xf39c12);\n            // Orange when full\n          } else {\n            cashierStallGlow.material.color.setHex(0x27ae60);\n            // Green when accepting\n          }\n        }\n\n        // Animate menu burger\n        const menuBurger = cashierStallMesh.userData.menuBurger;\n        if (menuBurger) {\n          menuBurger.rotation.y += deltaTime * 1.5;\n        }\n\n        // Animate hired cashier staff with squishy feel\n        if (hiredStaffMesh && hiredStaffMesh.userData.squishState) {\n          const squish = hiredStaffMesh.userData.squishState;\n          const now = Date.now();\n\n          // Idle breathing/working animation\n          const breathe = Math.sin(now * 0.004);\n\n          // When serving customers, add extra bounce\n          if (cashierServeTimer > 0 && cashierServeTimer < 0.3) {\n            squish.targetScaleY = 0.9 + Math.sin(now * 0.02) * 0.1;\n            squish.targetScaleXZ = 1.1 - Math.sin(now * 0.02) * 0.1;\n            switchAnimation(hiredStaffMesh, 'interact');\n          } else {\n            squish.targetScaleY = 1 + breathe * 0.04;\n            squish.targetScaleXZ = 1 - breathe * 0.03;\n            switchAnimation(hiredStaffMesh, 'idle');\n          }\n\n          // Apply squish\n          const squishSpeed = 10 * deltaTime;\n          squish.scaleY += (squish.targetScaleY - squish.scaleY) * squishSpeed;\n          squish.scaleXZ += (squish.targetScaleXZ - squish.scaleXZ) * squishSpeed;\n          hiredStaffMesh.scale.set(squish.scaleXZ, squish.scaleY, squish.scaleXZ);\n\n          // Subtle idle bob\n          hiredStaffMesh.position.y = Math.sin(now * 0.003) * 0.02;\n        }\n\n        if (hiredStaffMesh2 && hiredStaffMesh2.userData.squishState) {\n          const squish = hiredStaffMesh2.userData.squishState;\n          const now = Date.now();\n          const breathe = Math.sin(now * 0.004 + 1.7);\n\n          if (cashierServeTimer2 > 0 && cashierServeTimer2 < 0.3) {\n            squish.targetScaleY = 0.9 + Math.sin(now * 0.02) * 0.1;\n            squish.targetScaleXZ = 1.1 - Math.sin(now * 0.02) * 0.1;\n            switchAnimation(hiredStaffMesh2, 'interact');\n          } else {\n            squish.targetScaleY = 1 + breathe * 0.04;\n            squish.targetScaleXZ = 1 - breathe * 0.03;\n            switchAnimation(hiredStaffMesh2, 'idle');\n          }\n\n          const squishSpeed = 10 * deltaTime;\n          squish.scaleY += (squish.targetScaleY - squish.scaleY) * squishSpeed;\n          squish.scaleXZ += (squish.targetScaleXZ - squish.scaleXZ) * squishSpeed;\n          hiredStaffMesh2.scale.set(squish.scaleXZ, squish.scaleY, squish.scaleXZ);\n\n          hiredStaffMesh2.position.y = Math.sin(now * 0.003 + 1.2) * 0.02;\n        }\n\n        // Animate cash pile glow and icon\n        if (cashierCashPileGlow) {\n          const cashPilePulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          cashierCashPileGlow.scale.set(cashPilePulse, 1, cashPilePulse);\n\n          // Change glow color based on whether there's money to collect\n          if (cashierCashPile.length > 0) {\n            cashierCashPileGlow.material.color.setHex(0xffd700);\n            // Gold when has money\n            cashierCashPileGlow.material.opacity = 0.6;\n          } else {\n            cashierCashPileGlow.material.color.setHex(0x27ae60);\n            // Green when empty\n            cashierCashPileGlow.material.opacity = 0.3;\n          }\n        }\n\n        const maxMoney =\n          (config.maxMoney || 50) + (window.gameConfig.vaultUpgrades?.cashpile || 0) * 20;\n        if (cashierCashPileMesh && cashierCashPileMesh.userData.maxSprite) {\n          const isMax = cashierCashPile.length >= maxMoney;\n          cashierCashPileMesh.userData.maxSprite.visible = isMax;\n          if (isMax) {\n            const layer = Math.floor(cashierCashPile.length / 16);\n            cashierCashPileMesh.userData.maxSprite.position.y =\n              0.5 + layer * 0.12 + Math.sin(Date.now() * 0.005) * 0.1;\n          }\n        }\n\n        // Animate dollar icon\n        const dollarIcon = cashierCashPileMesh?.userData?.dollarIcon;\n        if (dollarIcon) {\n          dollarIcon.position.y = dollarIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          dollarIcon.rotation.y += deltaTime * 1.2;\n        }\n      }\n\n      function updateCashierCashPile(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        if (\n          !window.gameConfig.cashierStall ||\n          window.gameConfig.cashierStall.deleted ||\n          !cashierStallMesh\n        )\n          return;\n\n        const cashPileX = cashierStallMesh?.userData?.cashPileX;\n        const cashPileZ = cashierStallMesh?.userData?.cashPileZ;\n        const cashPileWidth = cashierStallMesh?.userData?.cashPileWidth || 4;\n        const cashPileDepth = cashierStallMesh?.userData?.cashPileDepth || 3;\n\n        if (cashPileX === undefined) return;\n\n        const playerX = player.position.x;\n        const playerY = player.position.y;\n        const playerZ = player.position.z;\n\n        // Check if player is in cash pile zone\n        const inZoneX = Math.abs(playerX - cashPileX) < cashPileWidth / 2 + 1;\n        const inZoneZ = Math.abs(playerZ - cashPileZ) < cashPileDepth / 2 + 1;\n        isInCashierCashPileZone = inZoneX && inZoneZ;\n\n        if (isInCashierCashPileZone && cashierCashPile.length > 0) {\n          if (!window.gameConfig.tutorialHasCollectedMoney) {\n            window.gameConfig.tutorialHasCollectedMoney = true;\n            performAutosave().catch((err) => lib.log('Autosave error: ' + err.message));\n          }\n          let collectedCount = 0;\n          while (cashierCashPile.length > 0) {\n            const money = cashierCashPile.pop();\n            incomingMoney++;\n\n            const startPos = {\n              x: money.position.x,\n              y: money.position.y + 0.3,\n              z: money.position.z,\n            };\n\n            const targetPos = {\n              x: playerX,\n              y: playerY + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n              z: playerZ,\n            };\n\n            const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n            createFlyingItem(\n              startPos,\n              targetPos,\n              'money',\n              () => {\n                modifyVaultBalance(moneyValue);\n                // To avoid deafening sounds, only play once per batch when it arrives\n                if (collectedCount === 0) playSound('pickup_sound');\n              },\n              true\n            );\n\n            scene.remove(money);\n            money.traverse((child) => {\n              if (child.geometry) child.geometry.dispose();\n              if (child.material) {\n                if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                else child.material.dispose();\n              }\n            });\n            collectedCount++;\n          }\n\n          cashierEarnings = cashierCashPile.length;\n          updateCashierUI();\n          if (collectedCount > 0) playSound('pickup_sound');\n        }\n      }\n\n      // ============================================\n      // RUSH HOUR SYSTEM\n      // ============================================\n\n      function updateRushHour(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const rushConfig = window.gameConfig.rushHour;\n        if (!rushConfig || !rushConfig.enabled) return;\n\n        if (isRushHour) {\n          // Rush hour is active\n          rushHourTimer -= deltaTime;\n          rushHourPulseAnim += deltaTime;\n\n          // Update UI timer\n          const timerEl = document.getElementById('rush-timer');\n          if (timerEl) {\n            timerEl.textContent = Math.ceil(rushHourTimer) + 's';\n          }\n\n          if (rushHourTimer <= 0) {\n            // End rush hour\n            isRushHour = false;\n            rushHourCooldownTimer = rushConfig.cooldown;\n            hideRushHourIndicator();\n          }\n        } else {\n          // Cooldown between rush hours\n          rushHourCooldownTimer -= deltaTime;\n\n          if (rushHourCooldownTimer <= 0) {\n            // Start rush hour!\n            isRushHour = true;\n            rushHourTimer = rushConfig.duration;\n            rushHourPulseAnim = 0;\n            showRushHourNotification();\n            showRushHourIndicator();\n          }\n        }\n      }\n\n      function showRushHourIndicator() {\n        const indicator = document.getElementById('rush-hour-indicator');\n        if (indicator) {\n          indicator.classList.remove('hidden');\n        }\n      }\n\n      function hideRushHourIndicator() {\n        const indicator = document.getElementById('rush-hour-indicator');\n        if (indicator) {\n          indicator.classList.add('hidden');\n        }\n      }\n\n      function showRushHourNotification() {\n        const notification = document.getElementById('rush-notification');\n        if (!notification) return;\n\n        // Update subtitle with actual tip bonus\n        const rushConfig = window.gameConfig.rushHour;\n        const bonusPercent = Math.round((rushConfig.tipBonus - 1) * 100);\n        const subtitle = notification.querySelector('.notification-subtitle');\n        if (subtitle) {\n          subtitle.textContent = t('rush.subtitle', { bonus: bonusPercent });\n        }\n\n        notification.classList.add('show');\n        playSound('upgrade_sound');\n\n        // Hide after delay\n        setTimeout(() => {\n          notification.classList.remove('show');\n        }, 2500);\n      }\n\n      function getCustomerSpawnRate() {\n        const config = window.gameConfig.cashierStall;\n        const rushConfig = window.gameConfig.rushHour;\n        const staffConfig = window.gameConfig.staffHireTile;\n\n        let spawnRate = config.customerSpawnRate;\n        \n        if (staffConfig && staffConfig.extraStaffHired) {\n            spawnRate = 0.5; // 2 customers per second\n        } else if (staffConfig && staffConfig.staffHired) {\n            spawnRate = 1.0; // 1 customer per second\n        }\n\n        // Faster spawns during rush hour\n        if (isRushHour && rushConfig && rushConfig.enabled) {\n          spawnRate = spawnRate / rushConfig.spawnMultiplier;\n        }\n\n        return spawnRate;\n      }\n\n      function getRushHourTipMultiplier() {\n        const rushConfig = window.gameConfig.rushHour;\n\n        if (isRushHour && rushConfig && rushConfig.enabled) {\n          return rushConfig.tipBonus;\n        }\n\n        return 1;\n      }\n\n      function updateCustomerQueue(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.cashierStall;\n        if (!config || config.deleted || !cashierStallMesh) return;\n\n        // Spawn new customers (affected by rush hour)\n        customerSpawnTimer += deltaTime;\n        let activeCustomers = 0;\n        for (let i = 0; i < customerQueue.length; i++) {\n          const state = customerQueue[i].state;\n          if (state === 'waiting' || state === 'walking') activeCustomers++;\n        }\n\n        // Use dynamic spawn rate (faster during rush hour)\n        const currentSpawnRate = getCustomerSpawnRate();\n        const staffConfig = window.gameConfig.staffHireTile;\n        const baseMax = (staffConfig && staffConfig.extraStaffHired) ? config.maxCustomers * 2 : config.maxCustomers;\n        const maxCustomers = isRushHour ? baseMax + 3 : baseMax;\n        // More customers during rush\n\n        if (\n          customerSpawnTimer >= currentSpawnRate &&\n          activeCustomers < maxCustomers &&\n          !isTotalCustomerCapReached()\n        ) {\n          customerSpawnTimer = 0;\n          spawnCustomer();\n        }\n\n        const now = Date.now();\n        const cameraPos = camera.position;\n\n        // Update each customer\n        for (let i = customerQueue.length - 1; i >= 0; i--) {\n          const customer = customerQueue[i];\n          const mesh = customer.mesh;\n          const state = customer.state;\n\n          // Make emoji always face camera\n          const emojiMesh = mesh.userData.emojiMesh;\n          if (emojiMesh) {\n            updateCustomerEmojiVisibility(customer, now);\n            emojiMesh.lookAt(cameraPos);\n            emojiMesh.position.y = 2.5 * (1 + 0.5 + 0.125) + Math.sin(now * 0.005 + i) * 0.1;\n          }\n\n          // Get or create squish state\n          const squish = mesh.userData.squishState || {\n            scaleY: 1,\n            scaleXZ: 1,\n            targetScaleY: 1,\n            targetScaleXZ: 1,\n          };\n\n          if (state === 'walking') {\n            switchAnimation(mesh, 'walk');\n            const dx = customer.targetX - mesh.position.x;\n            const dz = customer.targetZ - mesh.position.z;\n            const distSq = dx * dx + dz * dz;\n\n            if (distSq > 0.01) {\n              const dist = Math.sqrt(distSq);\n              const speed = 5;\n              mesh.position.x += (dx / dist) * speed * deltaTime;\n              mesh.position.z += (dz / dist) * speed * deltaTime;\n              mesh.rotation.y = Math.atan2(dx, dz);\n              mesh.position.y = Math.abs(Math.sin(now * 0.01)) * 0.1;\n\n              // Squishy walk animation\n              const walkCycle = Math.sin(now * 0.012 + i) * 0.5 + 0.5;\n              squish.targetScaleY = 0.9 + walkCycle * 0.2;\n              squish.targetScaleXZ = 1.1 - walkCycle * 0.2;\n            } else {\n              customer.state = 'waiting';\n              mesh.position.y = 0;\n              mesh.rotation.y = -Math.PI;\n              // Landing squish\n              squish.scaleY = 0.8;\n              squish.scaleXZ = 1.2;\n            }\n          } else if (state === 'waiting') {\n            switchAnimation(mesh, 'idleFoldArms');\n            mesh.position.y = Math.sin(now * 0.003 + i) * 0.05;\n            // Idle breathing squish\n            const breathe = Math.sin(now * 0.004 + i * 0.5);\n            squish.targetScaleY = 1 + breathe * 0.04;\n            squish.targetScaleXZ = 1 - breathe * 0.03;\n\n            customer.waitTime += deltaTime;\n            // VIP customers use different emojis\n            const isVIP = customer.isVIP;\n            if (customer.waitTime > 10) {\n              updateCustomerEmoji(customer, isVIP ? '\ud83d\ude24' : '\ud83d\ude20');\n              // mesh.rotation.y = -Math.PI + Math.sin(now * 0.008) * 0.3;\n            } else if (customer.waitTime > 5) {\n              updateCustomerEmoji(customer, isVIP ? '\ud83d\ude44' : '\ud83d\ude10');\n              // mesh.rotation.y = -Math.PI + Math.sin(now * 0.005) * 0.2;\n            } else {\n              updateCustomerEmoji(customer, isVIP ? '\u2b50' : '\ud83d\ude42');\n            }\n          } else if (state === 'served') {\n            switchAnimation(mesh, 'idle');\n            customer.waitTime += deltaTime;\n\n            if (mesh.userData.burgerMesh) {\n              mesh.userData.burgerMesh.visible = true;\n            }\n            updateCustomerEmoji(customer, customer.isVIP ? '\ud83e\udd29' : '\ud83e\udd24');\n\n            if (customer.waitTime > 0.3) {\n              if (customer.assignedTable) {\n                customer.state = 'walkingToTable';\n                customer.targetX = customer.assignedTable.tableData.x;\n                customer.targetZ = customer.assignedTable.tableData.z + 1.5;\n              } else {\n                removeCustomer(customer);\n              }\n            }\n          } else if (state === 'walkingToTable') {\n            switchAnimation(mesh, 'walk');\n            updateCustomerEmoji(customer, customer.isVIP ? '\ud83e\udd29' : '\ud83d\ude0a');\n\n            const isCarryingOrder =\n              mesh.userData && mesh.userData.burgerMesh && mesh.userData.burgerMesh.visible;\n            if (!isCarryingOrder) {\n              switchAnimation(mesh, 'walk');\n            }\n\n            const dx = customer.targetX - mesh.position.x;\n            const dz = customer.targetZ - mesh.position.z;\n            const distSq = dx * dx + dz * dz;\n\n            if (distSq > 0.09) {\n              const dist = Math.sqrt(distSq);\n              const speed = 4;\n              mesh.position.x += (dx / dist) * speed * deltaTime;\n              mesh.position.z += (dz / dist) * speed * deltaTime;\n              mesh.rotation.y = Math.atan2(dx, dz);\n              mesh.position.y = Math.abs(Math.sin(now * 0.01)) * 0.1;\n            } else {\n              customer.state = 'eating';\n              customer.eatingTime = 0;\n              mesh.position.y = 0;\n              mesh.rotation.y = Math.PI;\n\n              if (mesh.userData.burgerMesh) {\n                const hand = mesh.getObjectByName('hand_l');\n                hand.remove(mesh.userData.burgerMesh);\n                const head = mesh.getObjectByName('head');\n                head.add(mesh.userData.burgerMesh);\n                mesh.userData.burgerMesh.position.set(0, 0, 0.2);\n                mesh.userData.burgerMesh.rotation.set(0, 0, 0);\n                // mesh.userData.burgerMesh.position.set(0, 1.2, -0.8);\n              }\n            }\n          } else if (state === 'eating') {\n            switchAnimation(mesh, 'idle');\n            updateCustomerEmoji(customer, customer.isVIP ? '\ud83d\udc51' : '\ud83d\ude0b');\n            customer.eatingTime += deltaTime;\n            mesh.position.y = Math.sin(now * 0.008) * 0.03;\n\n            if (mesh.userData.burgerMesh) {\n              const eatProgress = customer.eatingTime / EATING_TIME;\n              const burgerScale = Math.max(0.1, 0.8 * (1 - eatProgress));\n              mesh.userData.burgerMesh.scale.setScalar(burgerScale);\n              // mesh.userData.burgerMesh.position.y = 1.2 + Math.sin(now * 0.02) * 0.05;\n            }\n\n            if (customer.eatingTime >= EATING_TIME) {\n              if (mesh.userData.burgerMesh) {\n                mesh.userData.burgerMesh.visible = false;\n              }\n\n              updateCustomerEmoji(customer, customer.isVIP ? '\ud83c\udf1f' : '\ud83d\ude04');\n\n              if (customer.assignedTable) {\n                const shouldLeaveTrash = window.gameConfig.settings.customersLeaveTrash !== false;\n\n                if (shouldLeaveTrash) {\n                  customer.assignedTable.tableData.hasTrash = true;\n\n                  const configTable = window.gameConfig.diningTables.find(\n                    (t) => t.id === customer.assignedTable.tableData.id\n                  );\n                  if (configTable) {\n                    configTable.hasTrash = true;\n                  }\n\n                  if (customer.assignedTable.trashMesh) {\n                    customer.assignedTable.trashMesh.visible = true;\n                  }\n                }\n\n                customer.assignedTable.tableData.occupied = false;\n\n                const configTable = window.gameConfig.diningTables.find(\n                  (t) => t.id === customer.assignedTable.tableData.id\n                );\n                if (configTable) {\n                  configTable.occupied = false;\n                }\n              }\n\n              customer.state = 'walkingToDoor';\n              customer.targetX = -RESTAURANT_WIDTH / 2 + 2;\n              customer.targetZ = 10;\n              customer.leaveEmoji = customer.isVIP ? '\ud83c\udf1f' : '\ud83d\ude04';\n            }\n          } else if (state === 'walkingToDoor') {\n            switchAnimation(mesh, 'walk');\n            if (customer.leaveEmoji) {\n              updateCustomerEmoji(customer, customer.leaveEmoji);\n            }\n\n            const dx = customer.targetX - mesh.position.x;\n            const dz = customer.targetZ - mesh.position.z;\n            const distSq = dx * dx + dz * dz;\n\n            if (distSq > 1) {\n              const dist = Math.sqrt(distSq);\n              const speed = 5;\n              mesh.position.x += (dx / dist) * speed * deltaTime;\n              mesh.position.z += (dz / dist) * speed * deltaTime;\n              mesh.rotation.y = Math.atan2(dx, dz);\n              mesh.position.y = Math.abs(Math.sin(now * 0.01)) * 0.1;\n\n              if (squish) {\n                const walkCycle = Math.sin(now * 0.012 + i) * 0.5 + 0.5;\n                squish.targetScaleY = 0.9 + walkCycle * 0.2;\n                squish.targetScaleXZ = 1.1 - walkCycle * 0.2;\n              }\n            } else {\n              customer.state = 'leaving';\n              customer.targetX = -RESTAURANT_WIDTH / 2 - 10;\n            }\n          } else if (state === 'leaving') {\n            switchAnimation(mesh, 'walk');\n            if (customer.leaveEmoji) {\n              updateCustomerEmoji(customer, customer.leaveEmoji);\n            } else {\n              updateCustomerEmoji(customer, customer.isVIP ? '\ud83d\ude12' : '\ud83d\ude1e');\n            }\n\n            const dx = customer.targetX - mesh.position.x;\n            const speed = 6;\n            mesh.position.x += Math.sign(dx) * speed * deltaTime;\n            mesh.rotation.y = dx > 0 ? Math.PI / 2 : -Math.PI / 2;\n            mesh.position.y = Math.abs(Math.sin(now * 0.01)) * 0.1;\n\n            if (Math.abs(mesh.position.x) > RESTAURANT_WIDTH / 2 + 5) {\n              scene.remove(mesh);\n              mesh.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                  else child.material.dispose();\n                }\n              });\n              customerQueue.splice(i, 1);\n            }\n          }\n\n          // Apply squishy interpolation to customer\n          if (squish) {\n            const squishSpeed = 10 * deltaTime;\n            squish.scaleY += (squish.targetScaleY - squish.scaleY) * squishSpeed;\n            squish.scaleXZ += (squish.targetScaleXZ - squish.scaleXZ) * squishSpeed;\n            mesh.scale.set(squish.scaleXZ, squish.scaleY, squish.scaleXZ);\n          }\n        }\n\n        // Serve first waiting customer\n        const staffHired = staffConfig && staffConfig.staffHired;\n        const staffHired2 = staffConfig && staffConfig.extraStaffHired;\n        const canServe = isInCashierZone || staffHired || staffHired2;\n\n        const availableTableForServing = findAvailableDiningTable();\n\n        const waitingCustomers = customerQueue\n          .filter((c) => c.state === 'waiting')\n          .sort((a, b) => a.queuePosition - b.queuePosition);\n        const firstWaiting = waitingCustomers[0] || null;\n        const secondWaiting = waitingCustomers[1] || null;\n\n        const servedCustomers = new Set();\n\n        const maxMoney =\n          (config.maxMoney || 50) + (window.gameConfig.vaultUpgrades?.cashpile || 0) * 20;\n\n        const vaultUpgrades = window.gameConfig.vaultUpgrades || {};\n        const serveBonus = 1 - (vaultUpgrades.serve || 0) * 0.1;\n        const currentServeTime = Math.max(0.1, config.serveTime * serveBonus);\n\n        if (\n          firstWaiting &&\n          cashierBurgerStack.length > 0 &&\n          canServe &&\n          cashierCashPile.length < maxMoney\n        ) {\n          cashierServeTimer += deltaTime;\n\n          if (cashierServeTimer >= currentServeTime) {\n            cashierServeTimer = 0;\n\n            const servedMenuItem = removeBurgerFromCashier();\n\n            if (availableTableForServing) {\n              firstWaiting.assignedTable = availableTableForServing;\n              availableTableForServing.tableData.occupied = true;\n\n              const configTable = window.gameConfig.diningTables.find(\n                (t) => t.id === availableTableForServing.tableData.id\n              );\n              if (configTable) configTable.occupied = true;\n            }\n\n            firstWaiting.state = 'served';\n            firstWaiting.waitTime = 0;\n            firstWaiting.satisfied = true;\n            servedCustomers.add(firstWaiting);\n\n            updateCashierUI();\n\n            // Calculate how many paper money bills the customer pays\n            if (!firstWaiting.alreadyPaid) {\n              // Use the served item's value, VIP multiplier, and rush hour bonus\n              const vipConfig = window.gameConfig.vipCustomers || {\n                tipMultiplier: 3,\n              };\n              const vipMultiplier = firstWaiting.isVIP ? vipConfig.tipMultiplier : 1;\n              const rushMultiplier = getRushHourTipMultiplier();\n              const prestigeMultiplier = getPrestigeProfitMultiplier();\n              const tipMultiplier = vipMultiplier * rushMultiplier * prestigeMultiplier;\n              // Use the menu item's value, or fallback to default\n              const basePrice =\n                servedMenuItem && servedMenuItem.value\n                  ? servedMenuItem.value\n                  : window.gameConfig.resources.itemValue;\n              const itemPrice = basePrice * tipMultiplier;\n              const paperMoneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n              const billCount = Math.ceil(itemPrice / paperMoneyValue);\n\n              // Reserve positions upfront before any flying starts\n              const reservedPositions = [];\n              const baseIndex = cashierCashPile.length;\n              for (let billIndex = 0; billIndex < billCount; billIndex++) {\n                const position = getCashPilePositionAtIndex(baseIndex + billIndex);\n                reservedPositions.push(position);\n              }\n\n              let completedCount = 0; // Track how many bills have landed\n\n              // Spawn multiple paper money bills with staggered timing\n              for (let billIndex = 0; billIndex < billCount; billIndex++) {\n                setTimeout(() => {\n                  const cashPilePos = reservedPositions[billIndex]; // Use reserved position\n\n                  // Slightly randomize start position for each bill\n                  const offsetX = (Math.random() - 0.5) * 0.5;\n                  const offsetZ = (Math.random() - 0.5) * 0.5;\n\n                  const moneyStartPos = {\n                    x: firstWaiting.mesh.position.x + offsetX,\n                    y: 1.5 + billIndex * 0.15,\n                    z: firstWaiting.mesh.position.z + offsetZ,\n                  };\n                  const moneyTargetPos = {\n                    x: cashPilePos.x,\n                    y: cashPilePos.y,\n                    z: cashPilePos.z,\n                  };\n\n                  createFlyingItem(\n                    moneyStartPos,\n                    moneyTargetPos,\n                    'money',\n                    () => {\n                      createCashPileMoney(cashPilePos.x, cashPilePos.y, cashPilePos.z);\n                      completedCount++;\n                      cashierEarnings = completedCount; // Use completedCount instead of array length\n                      updateCashierUI();\n                    },\n                    true\n                  );\n\n                  if (billIndex === 0) {\n                    playSound('sell_sound');\n                  }\n                }, billIndex * 80);\n              }\n            } else {\n              updateCashierUI();\n            }\n          }\n        } else {\n          cashierServeTimer = 0;\n        }\n\n        const secondStaffActive = staffHired2;\n        if (\n          secondStaffActive &&\n          secondWaiting &&\n          cashierBurgerStack.length > 0 &&\n          canServe &&\n          cashierCashPile.length < maxMoney\n        ) {\n          cashierServeTimer2 += deltaTime;\n\n          if (cashierServeTimer2 >= currentServeTime) {\n            cashierServeTimer2 = 0;\n            const servedMenuItem = removeBurgerFromCashier();\n\n            if (availableTableForServing) {\n              secondWaiting.assignedTable = availableTableForServing;\n              availableTableForServing.tableData.occupied = true;\n\n              const configTable = window.gameConfig.diningTables.find(\n                (t) => t.id === availableTableForServing.tableData.id\n              );\n              if (configTable) configTable.occupied = true;\n            }\n\n            secondWaiting.state = 'served';\n            secondWaiting.waitTime = 0;\n            secondWaiting.satisfied = true;\n            servedCustomers.add(secondWaiting);\n\n            updateCashierUI();\n\n            if (!secondWaiting.alreadyPaid) {\n              const vipConfig = window.gameConfig.vipCustomers || {\n                tipMultiplier: 3,\n              };\n              const vipMultiplier = secondWaiting.isVIP ? vipConfig.tipMultiplier : 1;\n              const rushMultiplier = getRushHourTipMultiplier();\n              const prestigeMultiplier = getPrestigeProfitMultiplier();\n              const tipMultiplier = vipMultiplier * rushMultiplier * prestigeMultiplier;\n              const basePrice =\n                servedMenuItem && servedMenuItem.value\n                  ? servedMenuItem.value\n                  : window.gameConfig.resources.itemValue;\n              const itemPrice = basePrice * tipMultiplier;\n              const paperMoneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n              const billCount = Math.ceil(itemPrice / paperMoneyValue);\n\n              const reservedPositions = [];\n              const baseIndex = cashierCashPile.length;\n              for (let billIndex = 0; billIndex < billCount; billIndex++) {\n                const position = getCashPilePositionAtIndex(baseIndex + billIndex);\n                reservedPositions.push(position);\n              }\n\n              let completedCount = 0;\n              for (let billIndex = 0; billIndex < billCount; billIndex++) {\n                setTimeout(() => {\n                  const cashPilePos = reservedPositions[billIndex];\n                  const offsetX = (Math.random() - 0.5) * 0.5;\n                  const offsetZ = (Math.random() - 0.5) * 0.5;\n\n                  const moneyStartPos = {\n                    x: secondWaiting.mesh.position.x + offsetX,\n                    y: 1.5 + billIndex * 0.15,\n                    z: secondWaiting.mesh.position.z + offsetZ,\n                  };\n                  const moneyTargetPos = {\n                    x: cashPilePos.x,\n                    y: cashPilePos.y,\n                    z: cashPilePos.z,\n                  };\n\n                  createFlyingItem(\n                    moneyStartPos,\n                    moneyTargetPos,\n                    'money',\n                    () => {\n                      createCashPileMoney(cashPilePos.x, cashPilePos.y, cashPilePos.z);\n                      completedCount++;\n                      cashierEarnings = completedCount;\n                      updateCashierUI();\n                    },\n                    true\n                  );\n\n                  if (billIndex === 0) {\n                    playSound('sell_sound');\n                  }\n                }, billIndex * 80);\n              }\n            }\n          }\n        } else {\n          cashierServeTimer2 = 0;\n        }\n\n        if (servedCustomers.size > 0) {\n          const remaining = customerQueue\n            .filter((c) => (c.state === 'waiting' || c.state === 'walking') && !servedCustomers.has(c))\n            .sort((a, b) => a.queuePosition - b.queuePosition);\n\n          for (let j = 0; j < remaining.length; j++) {\n            const c = remaining[j];\n            c.queuePosition = j;\n            const target = getCustomerQueueTarget(config, c.queuePosition);\n            c.targetX = target.x;\n            c.targetZ = target.z;\n            c.state = 'walking';\n          }\n        }\n      }\n\n      function updateSpendZone(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.spendZone;\n        if (!config || config.deleted) return;\n\n        // Check if player is in spend zone (rectangular area)\n        isInSpendZone = isPlayerInZone(config);\n\n        // Spend money when in zone\n        if (isInSpendZone && vaultBalance > 0) {\n          spendTimer += deltaTime;\n\n          if (spendTimer >= SPEND_DELAY) {\n            spendTimer = 0;\n\n            if (moneyStack.length > 0) {\n              // Remove from stack visually\n              const item = moneyStack.pop();\n              player.remove(item);\n              currentMoneyCount = moneyStack.length;\n              updateMoneyStackUI();\n\n              // Throw money toward spend zone gem\n              const startPos = {\n                x: player.position.x,\n                y:\n                  player.position.y +\n                  PLAYER_HEIGHT +\n                  playerStack.length * STACK_ITEM_HEIGHT +\n                  moneyStack.length * MONEY_STACK_HEIGHT,\n                z: player.position.z,\n              };\n\n              const targetPos = {\n                x: config.x,\n                y: 0.9,\n                // Gem height\n                z: config.z,\n              };\n\n              // Create flying money\n              createFlyingItem(\n                startPos,\n                targetPos,\n                'money',\n                () => {\n                  // Add to actual currency when money lands\n                  modifyVaultBalance(window.gameConfig.settings.paperMoneyValue || 10);\n                  playSound('sell_sound');\n                },\n                true\n              );\n\n              // Dispose of original item\n              item.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                  else child.material.dispose();\n                }\n              });\n            }\n          }\n        } else {\n          spendTimer = 0;\n        }\n\n        // Animate spend zone glow\n        if (spendZoneGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.005) * 0.04;\n          spendZoneGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n\n        // Animate gem indicator (bobbing and rotating)\n        const gemIndicator = spendZoneMesh.userData.gemIndicator;\n        if (gemIndicator) {\n          gemIndicator.position.y =\n            gemIndicator.userData.baseY + Math.sin(Date.now() * 0.003) * 0.15;\n          gemIndicator.rotation.y += deltaTime * 1.5;\n        }\n      }\n\n      function updateTrashBin(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.trashBin;\n        if (!config || config.deleted || !trashBinMesh) return;\n\n        const binX = config.x || 18;\n        const binZ = config.z || 5;\n        const binRadius = config.radius || 2;\n\n        // Check if player is near trash bin\n        const dx = player.position.x - binX;\n        const dz = player.position.z - binZ;\n        const dist = Math.sqrt(dx * dx + dz * dz);\n\n        isInTrashBinZone = dist < binRadius;\n\n        // Dispose of items when in zone (burgers first, then money)\n        if (isInTrashBinZone && currentStackCount > 0) {\n          trashBinDisposeTimer += deltaTime;\n\n          if (trashBinDisposeTimer >= TRASH_DISPOSE_DELAY) {\n            trashBinDisposeTimer = 0;\n\n            // Dispose of burgers first\n            if (playerStack.length > 0) {\n              const item = playerStack.pop();\n              player.remove(item);\n              currentStackCount = playerStack.length;\n              updateStackUI();\n\n              // Throw item into trash bin\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n\n              const targetPos = {\n                x: binX,\n                y: 2.5,\n                // Into the bin\n                z: binZ,\n              };\n\n              createFlyingItem(\n                startPos,\n                targetPos,\n                'burger',\n                () => {\n                  // Item destroyed - maybe play a sound\n                  playSound('pickup_sound');\n                },\n                true\n              );\n\n              // Dispose of original item\n              item.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) {\n                    child.material.forEach((mat) => mat.dispose());\n                  } else {\n                    child.material.dispose();\n                  }\n                }\n              });\n            }\n          }\n        } else {\n          trashBinDisposeTimer = 0;\n        }\n\n        // Animate trash bin glow\n        if (trashBinGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          trashBinGlow.scale.setScalar(pulseScale);\n\n          // Change color based on player having items\n          if (currentStackCount > 0 || currentMoneyCount > 0) {\n            trashBinGlow.material.color.setHex(0xe74c3c);\n            // Red when player has items\n            trashBinGlow.material.opacity = 0.7;\n          } else {\n            trashBinGlow.material.color.setHex(0x7f8c8d);\n            // Gray when empty\n            trashBinGlow.material.opacity = 0.4;\n          }\n        }\n\n        // Animate trash icon (emoji always faces camera)\n        const trashIcon = trashBinMesh && trashBinMesh.userData && trashBinMesh.userData.trashIcon;\n        if (trashIcon) {\n          trashIcon.position.y = trashIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          trashIcon.lookAt(camera.position);\n        }\n      }\n\n      function updateBankVault(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.bankVault;\n        if (!config || config.deleted || !bankVaultMesh) return;\n\n        const vaultX = config.x;\n        const vaultZ = config.z + config.depth / 2 + 1.5;\n        // Deposit zone in front\n\n        // Check if player is in vault deposit zone\n        const inZoneX = Math.abs(player.position.x - vaultX) < config.width / 2 + 1;\n        const inZoneZ = Math.abs(player.position.z - vaultZ) < 1.5;\n        isInBankVaultZone = inZoneX && inZoneZ;\n\n        // Deposit money into vault when in zone\n        if (isInBankVaultZone && currentMoneyCount > 0) {\n          bankVaultDepositTimer += deltaTime;\n\n          if (bankVaultDepositTimer >= VAULT_DEPOSIT_DELAY) {\n            bankVaultDepositTimer = 0;\n\n            if (moneyStack.length > 0) {\n              const item = moneyStack.pop();\n              player.remove(item);\n              currentMoneyCount = moneyStack.length;\n              updateMoneyStackUI();\n\n              // Throw money toward vault\n              const startPos = {\n                x: player.position.x,\n                y:\n                  player.position.y +\n                  PLAYER_HEIGHT +\n                  playerStack.length * STACK_ITEM_HEIGHT +\n                  moneyStack.length * MONEY_STACK_HEIGHT,\n                z: player.position.z,\n              };\n\n              const targetPos = {\n                x: config.x,\n                y: 1.5,\n                z: config.z + config.depth / 2,\n              };\n\n              createFlyingItem(\n                startPos,\n                targetPos,\n                'money',\n                () => {\n                  // Add to vault balance\n                  const depositValue = window.gameConfig.settings.paperMoneyValue || 10;\n                  vaultBalance += depositValue;\n                  window.gameConfig.bankVault.balance = vaultBalance;\n                  updateVaultUI();\n                  playSound('sell_sound');\n                },\n                true\n              );\n\n              // Dispose of original item\n              item.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                  else child.material.dispose();\n                }\n              });\n            }\n          }\n        } else {\n          bankVaultDepositTimer = 0;\n        }\n\n        // Animate vault glow\n        if (bankVaultGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          bankVaultGlow.scale.set(pulseScale, 1, pulseScale);\n\n          // Gold glow when player has money\n          if (currentMoneyCount > 0) {\n            bankVaultGlow.material.color.setHex(0xf1c40f);\n            bankVaultGlow.material.opacity = 0.6;\n          } else {\n            bankVaultGlow.material.color.setHex(0xbdc3c7);\n            bankVaultGlow.material.opacity = 0.3;\n          }\n        }\n\n        // Animate vault icon\n        const vaultIcon = bankVaultMesh?.userData?.vaultIcon;\n        if (vaultIcon) {\n          vaultIcon.position.y = vaultIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.15;\n          vaultIcon.lookAt(camera.position);\n        }\n\n        // Rotate vault door handle slowly\n        const handle = bankVaultMesh?.userData?.handle;\n        if (handle) {\n          handle.rotation.z += deltaTime * 0.5;\n        }\n      }\n\n      function updateSmokeParticles(deltaTime) {\n        for (let i = smokeParticles.length - 1; i >= 0; i--) {\n          const particle = smokeParticles[i];\n\n          particle.life -= deltaTime;\n\n          if (particle.life <= 0) {\n            scene.remove(particle.mesh);\n            particle.mesh.geometry.dispose();\n            particle.mesh.material.dispose();\n            smokeParticles.splice(i, 1);\n            continue;\n          }\n\n          const mesh = particle.mesh;\n          mesh.position.y += particle.velocityY * deltaTime;\n          mesh.position.x += particle.velocityX * deltaTime;\n          mesh.position.z += particle.velocityZ * deltaTime;\n\n          const lifeRatio = particle.life / particle.maxLife;\n          const scale = 1 + (1 - lifeRatio) * 2;\n          mesh.scale.setScalar(scale);\n          mesh.material.opacity = lifeRatio * 0.6;\n        }\n      }\n\n      function updateDustParticles(deltaTime) {\n        for (let i = dustParticles.length - 1; i >= 0; i--) {\n          const particle = dustParticles[i];\n\n          particle.life -= deltaTime;\n\n          if (particle.life <= 0) {\n            scene.remove(particle.mesh);\n            particle.mesh.geometry.dispose();\n            particle.mesh.material.dispose();\n            dustParticles.splice(i, 1);\n            continue;\n          }\n\n          const mesh = particle.mesh;\n          mesh.position.y += particle.velocityY * deltaTime;\n          mesh.position.x += particle.velocityX * deltaTime;\n          mesh.position.z += particle.velocityZ * deltaTime;\n\n          // Slow down horizontal movement\n          particle.velocityX *= 0.95;\n          particle.velocityZ *= 0.95;\n\n          const lifeRatio = particle.life / particle.maxLife;\n          const scale = particle.baseScale * (0.5 + lifeRatio * 0.5);\n          mesh.scale.setScalar(scale);\n          mesh.material.opacity = lifeRatio * 0.5;\n        }\n      }\n\n      function spawnDustParticle(x, y, z, moveX, moveZ) {\n        // Limit max dust particles for performance\n        if (dustParticles.length > performanceSettings.maxDustParticles) return;\n\n        const dustGeom = getCachedGeometry('dustGeom', () => new THREE.SphereGeometry(0.15, 6, 6));\n        const dustMat = new THREE.MeshBasicMaterial({\n          color: 0xffffff,\n          // Tan/beige color matching floor\n          transparent: true,\n          opacity: 0.5,\n          depthWrite: false,\n        });\n        const mesh = new THREE.Mesh(dustGeom, dustMat);\n\n        // Spawn at player's feet with slight random offset\n        mesh.position.set(\n          x + (Math.random() - 0.5) * 0.5,\n          y + 0.1 + Math.random() * 0.2,\n          z + (Math.random() - 0.5) * 0.5\n        );\n\n        const baseScale = 0.6 + Math.random() * 0.4;\n        mesh.scale.setScalar(baseScale);\n\n        scene.add(mesh);\n\n        const maxLife = 0.4 + Math.random() * 0.3;\n        dustParticles.push({\n          mesh: mesh,\n          life: maxLife,\n          maxLife: maxLife,\n          baseScale: baseScale * 2.5,\n          velocityY: 0.5 + Math.random() * 0.5,\n          // Dust moves opposite to player movement direction\n          velocityX: -moveX * 0.3 + (Math.random() - 0.5) * 0.5,\n          velocityZ: -moveZ * 0.3 + (Math.random() - 0.5) * 0.5,\n        });\n      }\n\n      function createSmokeParticlesAt(x, y, z) {\n        const particleCount = 15;\n        const geom = getCachedGeometry('smokeGeom', () => new THREE.SphereGeometry(0.5, 8, 8));\n        const mat = getCachedMaterial(\n          'smokeMat',\n          () =>\n            new THREE.MeshBasicMaterial({\n              color: 0xcccccc,\n              transparent: true,\n              opacity: 0.6,\n            })\n        );\n\n        for (let i = 0; i < particleCount; i++) {\n          const mesh = new THREE.Mesh(geom, mat);\n\n          // Random offset from center\n          const angle = Math.random() * Math.PI * 2;\n          const radius = Math.random() * 1.5;\n          mesh.position.set(\n            x + Math.cos(angle) * radius,\n            y + Math.random() * 0.5,\n            z + Math.sin(angle) * radius\n          );\n\n          // Randomize scale a bit\n          mesh.scale.setScalar(0.8 + Math.random() * 0.6);\n\n          scene.add(mesh);\n\n          const maxLife = 1 + Math.random() * 0.5;\n          smokeParticles.push({\n            mesh: mesh,\n            life: maxLife,\n            maxLife: maxLife,\n            velocityY: 2 + Math.random() * 2,\n            velocityX: (Math.random() - 0.5) * 2,\n            velocityZ: (Math.random() - 0.5) * 2,\n          });\n        }\n      }\n\n      function spawnCashierAtTileAndWalk(startX, startZ) {\n        const cashierConfig = window.gameConfig.cashierStall;\n        if (!cashierConfig || cashierConfig.deleted) return;\n\n        // Create staff member using the same model as player (green apron for hired status)\n        const staffGroup = createStaffMesh(0x27ae60);\n\n        // Position at tile\n        staffGroup.position.set(startX, 0, startZ);\n\n        scene.add(staffGroup);\n\n        // Set up walking animation state\n        const targetX = cashierConfig.x - 1.4;\n        const targetZ = cashierConfig.z - cashierConfig.depth / 2 - 1.2;\n\n        staffWalkingToCashier = {\n          mesh: staffGroup,\n          startX: startX,\n          startZ: startZ,\n          targetX: targetX,\n          targetZ: targetZ,\n          progress: 0,\n          walkSpeed: 3,\n        };\n      }\n\n      function spawnSecondCashierAtTileAndWalk(startX, startZ) {\n        const cashierConfig = window.gameConfig.cashierStall;\n        if (!cashierConfig || cashierConfig.deleted) return;\n\n        const staffGroup = createStaffMesh(0x27ae60);\n        staffGroup.position.set(startX, 0, startZ);\n        scene.add(staffGroup);\n\n        const targetX = cashierConfig.x + 1.4;\n        const targetZ = cashierConfig.z - cashierConfig.depth / 2 - 1.2;\n\n        staffWalkingToCashier2 = {\n          mesh: staffGroup,\n          startX: startX,\n          startZ: startZ,\n          targetX: targetX,\n          targetZ: targetZ,\n          progress: 0,\n          walkSpeed: 3,\n        };\n      }\n\n      function updateStaffWalking(deltaTime) {\n        const updateWalk = (staff, assignSlot) => {\n          if (!staff) return;\n\n          // Calculate distance to target\n          const dx = staff.targetX - staff.mesh.position.x;\n          const dz = staff.targetZ - staff.mesh.position.z;\n          const dist = Math.sqrt(dx * dx + dz * dz);\n\n          if (dist < 0.3) {\n            // Arrived at destination\n            staff.mesh.position.set(staff.targetX, 0, staff.targetZ);\n            staff.mesh.rotation.y = 0;\n            // Face customers\n            switchAnimation(staff.mesh, 'idle');\n\n            if (assignSlot === 1) {\n              hiredStaffMesh = staff.mesh;\n              staffWalkingToCashier = null;\n            } else if (assignSlot === 2) {\n              hiredStaffMesh2 = staff.mesh;\n              staffWalkingToCashier2 = null;\n            }\n\n            // Update the staff icon on hire tile if it still exists\n            if (staffHireTileMesh && staffHireTileMesh.userData.staffIcon) {\n              const icon = staffHireTileMesh.userData.staffIcon;\n              if (icon.children[0]) {\n                icon.children[0].material.color.setHex(0x27ae60);\n              }\n            }\n\n            return;\n          }\n\n          // Move toward target\n          const speed = staff.walkSpeed;\n          const moveX = (dx / dist) * speed * deltaTime;\n          const moveZ = (dz / dist) * speed * deltaTime;\n\n          staff.mesh.position.x += moveX;\n          staff.mesh.position.z += moveZ;\n\n          // Face movement direction\n          staff.mesh.rotation.y = Math.atan2(dx, dz);\n          switchAnimation(staff.mesh, 'walk');\n\n          // Walking bob animation\n          staff.mesh.position.y = Math.abs(Math.sin(Date.now() * 0.015)) * 0.1;\n        };\n\n        updateWalk(staffWalkingToCashier, 1);\n        updateWalk(staffWalkingToCashier2, 2);\n      }\n\n      function destroyStaffHireTileVisuals() {\n        if (!staffHireTileMesh) return;\n\n        // Remove all tile visual elements\n        scene.remove(staffHireTileMesh);\n        scene.remove(staffHireTileGlow);\n\n        // Mark as deleted in config\n        window.gameConfig.staffHireTile.deleted = true;\n\n        // Remove from editable objects\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'staffHireTile');\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        staffHireTileMesh = null;\n        staffHireTileGlow = null;\n      }\n\n      function destroyStaffHireTileVisualsKeepConfig() {\n        if (!staffHireTileMesh) return;\n\n        scene.remove(staffHireTileMesh);\n        scene.remove(staffHireTileGlow);\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'staffHireTile');\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        staffHireTileMesh = null;\n        staffHireTileGlow = null;\n      }\n\n      function updateStaffHireTile(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.staffHireTile;\n        if (!config || config.deleted || !staffHireTileMesh) return;\n\n        const extraCost = config.extraHireCost || Math.max(150, Math.round(config.hireCost * 1.5));\n        const hasBaseStaff = !!config.staffHired;\n        const hasExtraStaff = !!config.extraStaffHired;\n        const targetCost = hasBaseStaff ? extraCost : config.hireCost;\n\n        // Check if player is in staff hire tile zone\n        isInStaffHireTile = isPlayerInZone(config);\n\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n\n        // Deposit money when in zone\n        if (isInStaffHireTile && vaultBalance > 0 && totalDeposited < targetCost) {\n          staffHireDepositTimer += deltaTime;\n\n          if (staffHireDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            staffHireDepositTimer = 0;\n\n            const depositAmount = getDepositAmount(targetCost, moneyValue, totalDeposited);\n\n            if (depositAmount > 0) {\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n              if (moneyStack.length > 0) {\n                // Remove from stack visually (legacy)\n                const item = moneyStack.pop();\n                if (item) {\n                  player.remove(item);\n                  item.traverse((child) => {\n                    if (child.geometry) child.geometry.dispose();\n                    if (child.material) {\n                      if (Array.isArray(child.material)) {\n                        child.material.forEach((mat) => mat.dispose());\n                      } else {\n                        child.material.dispose();\n                      }\n                    }\n                  });\n                }\n                currentMoneyCount = moneyStack.length;\n                updateMoneyStackUI();\n              }\n\n              // Throw money toward staff hire tile\n              const startPos = {\n                x: player.position.x,\n                y:\n                  player.position.y +\n                  PLAYER_HEIGHT +\n                  playerStack.length * STACK_ITEM_HEIGHT +\n                  moneyStack.length * MONEY_STACK_HEIGHT,\n                z: player.position.z,\n              };\n\n              const targetPos = {\n                x: config.x,\n                y: 0.5,\n                z: config.z,\n              };\n\n              // Create flying money\n              createFlyingItem(\n                startPos,\n                targetPos,\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (hasExtraStaff && hasBaseStaff) return;\n\n                  // Add to deposit count\n                  config.moneyDeposited += depositAmount;\n                  window.gameConfig.staffHireTile.moneyDeposited = config.moneyDeposited;\n\n                  playSound('collect_sound');\n                  updateStaffHireProgress();\n                  updateStaffHireCostDisplay();\n\n                  // Check if enough money deposited to hire\n                  if (config.moneyDeposited >= targetCost) {\n                    if (!hasBaseStaff) {\n                      window.gameConfig.staffHireTile.staffHired = true;\n                      config.staffHired = true;\n                      playSound('upgrade_sound');\n\n                      // Show hire notification\n                      showHireNotification('cashier');\n\n                      // Create smoke particles at the tile\n                      createSmokeParticlesAt(config.x, 0.5, config.z);\n\n                      // Spawn cashier at tile location and start walking\n                      spawnCashierAtTileAndWalk(config.x, config.z);\n\n                      config.moneyDeposited = 0;\n                      window.gameConfig.staffHireTile.moneyDeposited = 0;\n                      updateStaffHireProgress();\n                      updateStaffHireCostDisplay();\n                    } else if (!hasExtraStaff) {\n                      window.gameConfig.staffHireTile.extraStaffHired = true;\n                      config.extraStaffHired = true;\n                      playSound('upgrade_sound');\n                      showHireNotification('cashier');\n                      createSmokeParticlesAt(config.x, 0.5, config.z);\n                      spawnSecondCashierAtTileAndWalk(config.x, config.z);\n\n                      config.moneyDeposited = 0;\n                      window.gameConfig.staffHireTile.moneyDeposited = 0;\n                      updateStaffHireProgress();\n                      updateStaffHireCostDisplay();\n\n                      destroyStaffHireTileVisualsKeepConfig();\n                    }\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          staffHireDepositTimer = 0;\n        }\n\n        // Animate staff hire tile glow\n        if (staffHireTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          staffHireTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n\n        // Animate staff icon\n        const staffIcon = staffHireTileMesh?.userData?.staffIcon;\n        if (staffIcon) {\n          staffIcon.position.y = staffIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      let cashierTileMesh, cashierTileGlow;\n      let cashierTileDepositTimer = 0;\n\n      let grillTileMesh, grillTileGlow;\n      let grillTileDepositTimer = 0;\n\n      let driveThruTileMesh, driveThruTileGlow;\n      let driveThruTileDepositTimer = 0;\n\n      let trashTileMesh, trashTileGlow;\n      let trashTileDepositTimer = 0;\n\n      let fountainTileMesh, fountainTileGlow;\n      let fountainTileDepositTimer = 0;\n      let fountain2TileMesh, fountain2TileGlow;\n      let fountain2TileDepositTimer = 0;\n      let fountainMesh;\n      let fountainMesh2;\n\n      let selfOrderKioskTileMesh, selfOrderKioskTileGlow;\n      let selfOrderKioskTileDepositTimer = 0;\n      let selfOrderKioskTileMeshes = [];\n\n      function createFountainTile() {\n        const config = window.gameConfig.fountainTile;\n        if (!config || config.deleted || config.unlocked) return;\n\n        const result = createBaseTile(config);\n        fountainTileMesh = result.tileMesh;\n        fountainTileGlow = result.glowMesh;\n\n        createTileCostDisplay(fountainTileMesh, config);\n        updateFountainTileProgress();\n        updateFountainTileCostDisplay();\n      }\n\n      function updateFountainTileProgress() {\n        const config = window.gameConfig.fountainTile;\n        if (!config || !fountainTileMesh) return;\n        updateTileProgress(fountainTileMesh, config);\n      }\n\n      function updateFountainTileCostDisplay() {\n        const config = window.gameConfig.fountainTile;\n        if (!config || !fountainTileMesh) return;\n        updateUnlockTileCostDisplay(fountainTileMesh, config);\n      }\n\n      function destroyFountainTileVisuals() {\n        destroyTileVisuals(fountainTileMesh, fountainTileGlow);\n        fountainTileMesh = null;\n        fountainTileGlow = null;\n      }\n\n      function updateFountainTile(deltaTime) {\n        if (gameMode === 'edit') return;\n        const config = window.gameConfig.fountainTile;\n        if (!config || config.deleted || config.unlocked || !fountainTileMesh) return;\n        const inZone = isPlayerInZone(config);\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n        if (inZone && vaultBalance > 0 && totalDeposited < config.cost) {\n          fountainTileDepositTimer += deltaTime;\n          if (fountainTileDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            fountainTileDepositTimer = 0;\n            const depositAmount = getDepositAmount(config.cost, moneyValue, totalDeposited);\n            if (depositAmount > 0) {\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.unlocked) return;\n                  config.moneyDeposited += depositAmount;\n                  playSound('collect_sound');\n                  updateFountainTileProgress();\n                  updateFountainTileCostDisplay();\n                  if (config.moneyDeposited >= config.cost) {\n                    config.unlocked = true;\n                    playSound('upgrade_sound');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    destroyFountainTileVisuals();\n                    createFountain();\n                    resetTileProgress(fountainTileMesh);\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          fountainTileDepositTimer = 0;\n        }\n        if (fountainTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          fountainTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n        const icon = fountainTileMesh?.userData?.icon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      function createFountain2Tile() {\n        const config = window.gameConfig.fountain2Tile;\n        if (!config || config.deleted || config.unlocked) return;\n\n        const result = createBaseTile(config);\n        fountain2TileMesh = result.tileMesh;\n        fountain2TileGlow = result.glowMesh;\n\n        createTileCostDisplay(fountain2TileMesh, config);\n        updateFountain2TileProgress();\n        updateFountain2TileCostDisplay();\n      }\n\n      function updateFountain2TileProgress() {\n        const config = window.gameConfig.fountain2Tile;\n        if (!config || !fountain2TileMesh) return;\n        updateTileProgress(fountain2TileMesh, config);\n      }\n\n      function updateFountain2TileCostDisplay() {\n        const config = window.gameConfig.fountain2Tile;\n        if (!config || !fountain2TileMesh) return;\n        updateUnlockTileCostDisplay(fountain2TileMesh, config);\n      }\n\n      function destroyFountain2TileVisuals() {\n        destroyTileVisuals(fountain2TileMesh, fountain2TileGlow);\n        fountain2TileMesh = null;\n        fountain2TileGlow = null;\n      }\n\n      function updateFountain2Tile(deltaTime) {\n        if (gameMode === 'edit') return;\n        const config = window.gameConfig.fountain2Tile;\n        if (!config || config.deleted || config.unlocked || !fountain2TileMesh) return;\n        const inZone = isPlayerInZone(config);\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n        if (inZone && vaultBalance > 0 && totalDeposited < config.cost) {\n          fountain2TileDepositTimer += deltaTime;\n          if (fountain2TileDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            fountain2TileDepositTimer = 0;\n            const depositAmount = getDepositAmount(config.cost, moneyValue, totalDeposited);\n            if (depositAmount > 0) {\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.unlocked) return;\n                  config.moneyDeposited += depositAmount;\n                  playSound('collect_sound');\n                  updateFountain2TileProgress();\n                  updateFountain2TileCostDisplay();\n                  if (config.moneyDeposited >= config.cost) {\n                    config.unlocked = true;\n                    playSound('upgrade_sound');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    destroyFountain2TileVisuals();\n                    createFountain2();\n                    resetTileProgress(fountain2TileMesh);\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          fountain2TileDepositTimer = 0;\n        }\n        if (fountain2TileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          fountain2TileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n        const icon = fountain2TileMesh?.userData?.icon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      function createFountainFromConfig(config) {\n        if (!config || config.deleted) return null;\n\n        const x = config.x || -18;\n        const z = config.z || -5;\n        const radius = config.radius || 2.5;\n\n        const fountainGroup = new THREE.Group();\n\n        const poolGeo = new THREE.CylinderGeometry(radius, radius, 0.4, 32);\n        const poolMat = new THREE.MeshStandardMaterial({\n          color: 0xdddddd,\n          roughness: 0.8,\n        });\n        const pool = new THREE.Mesh(poolGeo, poolMat);\n        pool.position.y = 0.1;\n        pool.castShadow = true;\n        pool.receiveShadow = true;\n        fountainGroup.add(pool);\n\n        const waterGeo = new THREE.CylinderGeometry(radius - 0.3, radius - 0.3, 0.35, 32);\n        const waterMat = new THREE.MeshStandardMaterial({\n          color: 0x3498db,\n          roughness: 0.1,\n          transparent: true,\n          opacity: 0.8,\n        });\n        const water = new THREE.Mesh(waterGeo, waterMat);\n        water.position.y = 0.22;\n        fountainGroup.add(water);\n\n        const pillarGeo = new THREE.CylinderGeometry(0.5, 0.8, 2, 16);\n        const pillarMat = new THREE.MeshStandardMaterial({\n          color: 0xeeeeee,\n          roughness: 0.7,\n        });\n        const pillar = new THREE.Mesh(pillarGeo, pillarMat);\n        pillar.position.y = 1;\n        pillar.castShadow = true;\n        fountainGroup.add(pillar);\n\n        const bowlGeo = new THREE.CylinderGeometry(1.2, 0.5, 0.3, 16);\n        const bowl = new THREE.Mesh(bowlGeo, pillarMat);\n        bowl.position.y = 2;\n        bowl.castShadow = true;\n        fountainGroup.add(bowl);\n\n        const spoutGeo = new THREE.CylinderGeometry(0.1, 0.4, 1.2, 16);\n        const spoutMat = new THREE.MeshStandardMaterial({\n          color: 0x87ceeb,\n          transparent: true,\n          opacity: 0.6,\n        });\n        const spout = new THREE.Mesh(spoutGeo, spoutMat);\n        spout.position.y = 2.6;\n        fountainGroup.add(spout);\n\n        fountainGroup.position.set(x, 0, z);\n        scene.add(fountainGroup);\n\n        fountainGroup.userData.water = water;\n        fountainGroup.userData.spout = spout;\n\n        return fountainGroup;\n      }\n\n      function createFountain() {\n        const config = window.gameConfig.fountain;\n        fountainMesh = createFountainFromConfig(config);\n      }\n\n      function createFountain2() {\n        const config = window.gameConfig.fountain2;\n        fountainMesh2 = createFountainFromConfig(config);\n      }\n\n      function destroySelfOrderKioskTileVisuals() {\n        destroyTileVisuals(selfOrderKioskTileMesh, selfOrderKioskTileGlow);\n        selfOrderKioskTileMesh = null;\n        selfOrderKioskTileGlow = null;\n      }\n\n      function createSelfOrderKioskTile() {\n        const config = window.gameConfig.selfOrderKioskTile;\n        if (!config || config.deleted || config.unlocked) return;\n\n        const result = createBaseTile(config);\n        selfOrderKioskTileMesh = result.tileMesh;\n        selfOrderKioskTileGlow = result.glowMesh;\n\n        createTileCostDisplay(selfOrderKioskTileMesh, config);\n        updateTileProgress(selfOrderKioskTileMesh, config);\n        updateUnlockTileCostDisplay(selfOrderKioskTileMesh, config);\n      }\n\n      function createSelfOrderKioskTiles() {\n        const configs = window.gameConfig.selfOrderKioskTiles || [];\n        selfOrderKioskTileMeshes = [];\n        configs.forEach((config, index) => {\n          if (!config || config.deleted || config.unlocked || config.hidden) return;\n          const result = createBaseTile(config);\n          const tileEntry = {\n            mesh: result.tileMesh,\n            glow: result.glowMesh,\n            config,\n            index,\n          };\n          selfOrderKioskTileMeshes.push(tileEntry);\n          createTileCostDisplay(tileEntry.mesh, config);\n          updateTileProgress(tileEntry.mesh, config);\n          updateUnlockTileCostDisplay(tileEntry.mesh, config);\n        });\n      }\n\n      function updateSelfOrderKioskTileProgress(index) {\n        const tileEntry = selfOrderKioskTileMeshes.find((t) => t.index === index);\n        if (!tileEntry) return;\n        updateTileProgress(tileEntry.mesh, tileEntry.config);\n      }\n\n      function updateSelfOrderKioskTileCostDisplay(index) {\n        const tileEntry = selfOrderKioskTileMeshes.find((t) => t.index === index);\n        if (!tileEntry) return;\n        updateUnlockTileCostDisplay(tileEntry.mesh, tileEntry.config);\n      }\n\n      function destroySelfOrderKioskTileVisualsByIndex(index) {\n        const entryIndex = selfOrderKioskTileMeshes.findIndex((t) => t.index === index);\n        if (entryIndex === -1) return;\n        const tileEntry = selfOrderKioskTileMeshes[entryIndex];\n        destroyTileVisuals(tileEntry.mesh, tileEntry.glow);\n        if (window.gameConfig.selfOrderKioskTiles && window.gameConfig.selfOrderKioskTiles[index]) {\n          window.gameConfig.selfOrderKioskTiles[index].deleted = true;\n        }\n        const editIndex = editableObjects.findIndex(\n          (obj) => obj.type === 'selfOrderKioskTile' && obj.tileIndex === index\n        );\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n        selfOrderKioskTileMeshes.splice(entryIndex, 1);\n      }\n\n      function updateSelfOrderKioskTile(deltaTime) {\n        if (gameMode === 'edit') return;\n        const config = window.gameConfig.selfOrderKioskTile;\n        if (!config || config.deleted || config.unlocked || !selfOrderKioskTileMesh) return;\n        if (!window.gameConfig.tutorialCompleted) return;\n        const inZone = isPlayerInZone(config);\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n        if (inZone && vaultBalance > 0 && totalDeposited < config.cost) {\n          selfOrderKioskTileDepositTimer += deltaTime;\n          if (selfOrderKioskTileDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            selfOrderKioskTileDepositTimer = 0;\n            const depositAmount = getDepositAmount(config.cost, moneyValue, totalDeposited);\n            if (depositAmount > 0) {\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.unlocked) return;\n                  config.moneyDeposited += depositAmount;\n                  playSound('collect_sound');\n                  updateTileProgress(selfOrderKioskTileMesh, config);\n                  updateUnlockTileCostDisplay(selfOrderKioskTileMesh, config);\n                  if (config.moneyDeposited >= config.cost) {\n                    config.unlocked = true;\n                    window.gameConfig.selfOrderKioskTile.unlocked = true;\n                    playSound('upgrade_sound');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    destroySelfOrderKioskTileVisuals();\n                    createSelfOrderKiosks();\n                    resetTileProgress(selfOrderKioskTileMesh);\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          selfOrderKioskTileDepositTimer = 0;\n        }\n        if (selfOrderKioskTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          selfOrderKioskTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n        const icon = selfOrderKioskTileMesh?.userData?.icon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      function updateSelfOrderKioskTiles(deltaTime) {\n        if (gameMode === 'edit') return;\n        if (!window.gameConfig.tutorialCompleted) return;\n        const configs = window.gameConfig.selfOrderKioskTiles || [];\n        let isPlayerInAnyTile = false;\n\n        configs.forEach((config, index) => {\n          if (!config || config.deleted || config.unlocked || config.hidden) return;\n          const tileEntry = selfOrderKioskTileMeshes.find((t) => t.index === index);\n          if (!tileEntry) return;\n\n          const inZone = isPlayerInZone(config);\n          const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n          const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n\n          if (inZone && vaultBalance > 0 && totalDeposited < config.cost) {\n            isPlayerInAnyTile = true;\n            selfOrderKioskTileDepositTimer += deltaTime;\n            if (selfOrderKioskTileDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n              selfOrderKioskTileDepositTimer = 0;\n              const depositAmount = getDepositAmount(config.cost, moneyValue, totalDeposited);\n              if (depositAmount > 0) {\n                modifyVaultBalance(-depositAmount);\n                config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n                const startPos = {\n                  x: player.position.x,\n                  y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                  z: player.position.z,\n                };\n                createFlyingItem(\n                  startPos,\n                  {\n                    x: config.x,\n                    y: 0.5,\n                    z: config.z,\n                  },\n                  'money',\n                  () => {\n                    config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                    if (config.unlocked) return;\n                    config.moneyDeposited += depositAmount;\n                    playSound('collect_sound');\n                    updateSelfOrderKioskTileProgress(index);\n                    updateSelfOrderKioskTileCostDisplay(index);\n                    if (config.moneyDeposited >= config.cost) {\n                      config.unlocked = true;\n                      if (window.gameConfig.selfOrderKioskTiles && window.gameConfig.selfOrderKioskTiles[index]) {\n                        window.gameConfig.selfOrderKioskTiles[index].unlocked = true;\n                      }\n                      const kioskConfig = window.gameConfig.selfOrderKiosks?.find((k) => k.id === config.kioskId);\n                      if (kioskConfig) {\n                        kioskConfig.deleted = false;\n                        kioskConfig.x = config.x;\n                        kioskConfig.z = config.z;\n                      }\n                      playSound('upgrade_sound');\n                      createSmokeParticlesAt(config.x, 0.5, config.z);\n                      destroySelfOrderKioskTileVisualsByIndex(index);\n                      createSelfOrderKiosks();\n                      if (kioskConfig) {\n                        updateSelfOrderKioskPosition(kioskConfig.id, kioskConfig.x, kioskConfig.z);\n                      }\n                    }\n                  },\n                  true\n                );\n              }\n            }\n          }\n\n          if (tileEntry.glow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004 + index) * 0.05;\n            tileEntry.glow.scale.set(pulseScale, 1, pulseScale);\n          }\n          const icon = tileEntry.mesh?.userData?.icon;\n          if (icon) {\n            icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003 + index) * 0.1;\n          }\n        });\n\n        if (!isPlayerInAnyTile) {\n          selfOrderKioskTileDepositTimer = 0;\n        }\n      }\n\n      function updateFountainAnimation() {\n        const now = Date.now();\n        if (fountainMesh && fountainMesh.userData.spout) {\n          fountainMesh.userData.spout.scale.y = 1 + Math.sin(now * 0.01) * 0.2;\n          fountainMesh.userData.spout.scale.x = 1 + Math.cos(now * 0.015) * 0.1;\n          fountainMesh.userData.spout.scale.z = 1 + Math.cos(now * 0.015) * 0.1;\n          fountainMesh.userData.water.position.y = 0.22 + Math.sin(now * 0.005) * 0.02;\n        }\n        if (fountainMesh2 && fountainMesh2.userData.spout) {\n          fountainMesh2.userData.spout.scale.y = 1 + Math.sin(now * 0.01) * 0.2;\n          fountainMesh2.userData.spout.scale.x = 1 + Math.cos(now * 0.015) * 0.1;\n          fountainMesh2.userData.spout.scale.z = 1 + Math.cos(now * 0.015) * 0.1;\n          fountainMesh2.userData.water.position.y = 0.22 + Math.sin(now * 0.005) * 0.02;\n        }\n      }\n\n      function updateFountainTilePosition(x, z) {\n        const config = window.gameConfig.fountainTile;\n        if (!config || !fountainTileMesh) return;\n        fountainTileMesh.position.x = x;\n        fountainTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateFountain2TilePosition(x, z) {\n        const config = window.gameConfig.fountain2Tile;\n        if (!config || !fountain2TileMesh) return;\n        fountain2TileMesh.position.x = x;\n        fountain2TileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateSelfOrderKioskTilePosition(x, z) {\n        const config = window.gameConfig.selfOrderKioskTile;\n        if (!config || !selfOrderKioskTileMesh) return;\n        selfOrderKioskTileMesh.position.x = x;\n        selfOrderKioskTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateFountainPosition(x, z) {\n        const config = window.gameConfig.fountain;\n        if (!config || !fountainMesh) return;\n        fountainMesh.position.x = x;\n        fountainMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateFountain2Position(x, z) {\n        const config = window.gameConfig.fountain2;\n        if (!config || !fountainMesh2) return;\n        fountainMesh2.position.x = x;\n        fountainMesh2.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function createDriveThruTile() {\n        const config = window.gameConfig.driveThruTile;\n        if (!config || config.deleted || config.unlocked) return;\n\n        const result = createBaseTile(config);\n        driveThruTileMesh = result.tileMesh;\n        driveThruTileGlow = result.glowMesh;\n\n        createTileCostDisplay(driveThruTileMesh, config);\n        updateDriveThruTileProgress();\n        updateDriveThruTileCostDisplay();\n      }\n\n      function updateDriveThruTileProgress() {\n        const config = window.gameConfig.driveThruTile;\n        if (!config || !driveThruTileMesh) return;\n        updateTileProgress(driveThruTileMesh, config);\n      }\n\n      function updateDriveThruTileCostDisplay() {\n        const config = window.gameConfig.driveThruTile;\n        if (!config || !driveThruTileMesh) return;\n        updateUnlockTileCostDisplay(driveThruTileMesh, config);\n      }\n\n      function destroyDriveThruTileVisuals() {\n        destroyTileVisuals(driveThruTileMesh, driveThruTileGlow);\n        driveThruTileMesh = null;\n        driveThruTileGlow = null;\n      }\n\n      function updateDriveThruTile(deltaTime) {\n        if (gameMode === 'edit') return;\n        const config = window.gameConfig.driveThruTile;\n        if (!config || config.deleted || config.unlocked || !driveThruTileMesh) return;\n        const inZone = isPlayerInZone(config);\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n        if (inZone && vaultBalance > 0 && totalDeposited < config.cost) {\n          driveThruTileDepositTimer += deltaTime;\n          if (driveThruTileDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            driveThruTileDepositTimer = 0;\n            const depositAmount = getDepositAmount(config.cost, moneyValue, totalDeposited);\n            if (depositAmount > 0) {\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.unlocked) return;\n                  config.moneyDeposited += depositAmount;\n                  window.gameConfig.driveThruTile.moneyDeposited = config.moneyDeposited;\n                  playSound('collect_sound');\n                  updateDriveThruTileProgress();\n                  updateDriveThruTileCostDisplay();\n                  if (config.moneyDeposited >= config.cost) {\n                    config.unlocked = true;\n                    window.gameConfig.driveThruTile.unlocked = true;\n                    window.gameConfig.tutorialHasSeenDriveThruHint = false;\n                    playSound('upgrade_sound');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    destroyDriveThruTileVisuals();\n                    createDriveThruBooth();\n                    resetTileProgress(driveThruTileMesh);\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          driveThruTileDepositTimer = 0;\n        }\n        if (driveThruTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          driveThruTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n        const icon = driveThruTileMesh?.userData?.icon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      function createGrillTile() {\n        const config = window.gameConfig.grillTile;\n        if (!config || config.deleted || config.unlocked) return;\n\n        const result = createBaseTile(config);\n        grillTileMesh = result.tileMesh;\n        grillTileGlow = result.glowMesh;\n\n        createTileCostDisplay(grillTileMesh, config);\n        updateGrillTileProgress();\n        updateGrillTileCostDisplay();\n      }\n\n      function updateGrillTileProgress() {\n        const config = window.gameConfig.grillTile;\n        if (!config || !grillTileMesh) return;\n        updateTileProgress(grillTileMesh, config);\n      }\n\n      function updateGrillTileCostDisplay() {\n        const config = window.gameConfig.grillTile;\n        if (!config || !grillTileMesh) return;\n        updateUnlockTileCostDisplay(grillTileMesh, config);\n      }\n\n      function destroyGrillTileVisuals() {\n        destroyTileVisuals(grillTileMesh, grillTileGlow);\n        grillTileMesh = null;\n        grillTileGlow = null;\n      }\n\n      function updateGrillTile(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.grillTile;\n        if (!config || config.deleted || config.unlocked || !grillTileMesh) return;\n\n        const inZone = isPlayerInZone(config);\n\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n\n        if (inZone && vaultBalance > 0 && totalDeposited < config.cost) {\n          grillTileDepositTimer += deltaTime;\n          if (grillTileDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            grillTileDepositTimer = 0;\n            if (vaultBalance > 0) {\n              const depositAmount = getDepositAmount(config.cost, moneyValue, totalDeposited);\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.unlocked) return;\n                  config.moneyDeposited += depositAmount;\n                  window.gameConfig.grillTile.moneyDeposited = config.moneyDeposited;\n                  playSound('collect_sound');\n                  updateGrillTileProgress();\n                  updateGrillTileCostDisplay();\n\n                  if (config.moneyDeposited >= config.cost) {\n                    config.unlocked = true;\n                    window.gameConfig.grillTile.unlocked = true;\n                    playSound('upgrade_sound');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    destroyGrillTileVisuals();\n\n                    const grillCount = window.gameConfig.grills.length;\n                    for (let i = 0; i < grillCount; i++) {\n                      createResourceNode(i, grillCount);\n                    }\n                    resetTileProgress(grillTileMesh);\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          grillTileDepositTimer = 0;\n        }\n\n        if (grillTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          grillTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n\n        const icon = grillTileMesh?.userData?.icon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      function createCashierTile() {\n        const config = window.gameConfig.cashierTile;\n        if (!config || config.deleted || config.unlocked) return;\n\n        const result = createBaseTile(config);\n        cashierTileMesh = result.tileMesh;\n        cashierTileGlow = result.glowMesh;\n\n        createTileCostDisplay(cashierTileMesh, config);\n        updateCashierTileProgress();\n        updateCashierTileCostDisplay();\n      }\n\n      function updateCashierTileProgress() {\n        const config = window.gameConfig.cashierTile;\n        if (!config || !cashierTileMesh) return;\n        updateTileProgress(cashierTileMesh, config);\n      }\n\n      function updateCashierTileCostDisplay() {\n        const config = window.gameConfig.cashierTile;\n        if (!config || !cashierTileMesh) return;\n        updateUnlockTileCostDisplay(cashierTileMesh, config);\n      }\n\n      function destroyCashierTileVisuals() {\n        destroyTileVisuals(cashierTileMesh, cashierTileGlow);\n        cashierTileMesh = null;\n        cashierTileGlow = null;\n      }\n\n      function updateCashierTile(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.cashierTile;\n        if (!config || config.deleted || config.unlocked || !cashierTileMesh) return;\n\n        const inZone = isPlayerInZone(config);\n\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n\n        if (inZone && vaultBalance > 0 && totalDeposited < config.cost) {\n          cashierTileDepositTimer += deltaTime;\n          if (cashierTileDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            cashierTileDepositTimer = 0;\n            if (vaultBalance > 0) {\n              const depositAmount = getDepositAmount(config.cost, moneyValue, totalDeposited);\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.unlocked) return;\n                  config.moneyDeposited += depositAmount;\n                  window.gameConfig.cashierTile.moneyDeposited = config.moneyDeposited;\n                  playSound('collect_sound');\n                  updateCashierTileProgress();\n                  updateCashierTileCostDisplay();\n\n                  if (config.moneyDeposited >= config.cost) {\n                    config.unlocked = true;\n                    window.gameConfig.cashierTile.unlocked = true;\n                    playSound('upgrade_sound');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    destroyCashierTileVisuals();\n                    createCashierStall();\n                    resetTileProgress(cashierTileMesh);\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          cashierTileDepositTimer = 0;\n        }\n\n        if (cashierTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          cashierTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n\n        const icon = cashierTileMesh?.userData?.icon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      // ============================================\n      // CLEANER STAFF AI\n      // ============================================\n\n      function updateCleanerStaffTile(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.cleanerStaffTile;\n        if (!config || config.deleted || config.staffHired || !cleanerStaffTileMesh) return;\n\n        // Check if player is in tile zone\n        isInCleanerStaffTile = isPlayerInZone(config);\n\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n\n        // Deposit money when in zone\n        if (isInCleanerStaffTile && vaultBalance > 0 && totalDeposited < config.hireCost) {\n          cleanerStaffDepositTimer += deltaTime;\n\n          if (cleanerStaffDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            cleanerStaffDepositTimer = 0;\n\n            const depositAmount = getDepositAmount(config.hireCost, moneyValue, totalDeposited);\n            if (depositAmount > 0) {\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.staffHired) return;\n                  config.moneyDeposited += depositAmount;\n                  window.gameConfig.cleanerStaffTile.moneyDeposited = config.moneyDeposited;\n\n                  playSound('collect_sound');\n                  updateCleanerStaffProgress();\n                  updateCleanerStaffCostDisplay();\n\n                  if (config.moneyDeposited >= config.hireCost) {\n                    window.gameConfig.cleanerStaffTile.staffHired = true;\n                    config.staffHired = true;\n                    playSound('upgrade_sound');\n                    showHireNotification('cleaner');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    createHiredCleaner();\n                    destroyCleanerStaffTileVisuals();\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          cleanerStaffDepositTimer = 0;\n        }\n\n        // Animate glow\n        if (cleanerStaffTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          cleanerStaffTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n\n        const icon = cleanerStaffTileMesh?.userData?.staffIcon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      function destroyCleanerStaffTileVisuals() {\n        if (!cleanerStaffTileMesh) return;\n\n        scene.remove(cleanerStaffTileMesh);\n        scene.remove(cleanerStaffTileGlow);\n\n        ['borderMesh', 'staffIcon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n          const obj = cleanerStaffTileMesh.userData[key];\n          if (obj) scene.remove(obj);\n        });\n\n        window.gameConfig.cleanerStaffTile.deleted = true;\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'cleanerStaffTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n\n        cleanerStaffTileMesh = null;\n        cleanerStaffTileGlow = null;\n      }\n\n      function updateCleanerAI(deltaTime) {\n        if (!hiredCleanerMesh || !cleanerStaffAI) return;\n\n        const ai = cleanerStaffAI;\n        const squish = hiredCleanerMesh.userData.squishState || {\n          scaleY: 1,\n          scaleXZ: 1,\n          targetScaleY: 1,\n          targetScaleXZ: 1,\n        };\n        const now = Date.now();\n\n        switch (ai.state) {\n          case 'idle':\n            // Switch to idle animation\n            switchAnimation(hiredCleanerMesh, 'idle');\n\n            // Look for tables with trash\n            ai.idleTimer += deltaTime;\n            if (ai.idleTimer > 0.5) {\n              ai.idleTimer = 0;\n\n              // Find table with trash\n              for (const tableEntry of diningTableMeshes) {\n                if (tableEntry.tableData.hasTrash) {\n                  ai.targetTable = tableEntry;\n                  ai.state = 'walkingToTable';\n                  // Start moving squish\n                  squish.scaleY = 0.85;\n                  squish.scaleXZ = 1.15;\n                  break;\n                }\n              }\n            }\n\n            // Idle breathing animation\n            const breathe = Math.sin(now * 0.003);\n            squish.targetScaleY = 1 + breathe * 0.04;\n            squish.targetScaleXZ = 1 - breathe * 0.03;\n            hiredCleanerMesh.position.y = Math.sin(now * 0.003) * 0.03;\n            break;\n\n          case 'walkingToTable':\n            // Switch to run animation\n            switchAnimation(hiredCleanerMesh, 'run');\n\n            if (!ai.targetTable || !ai.targetTable.tableData.hasTrash) {\n              ai.state = 'idle';\n              ai.targetTable = null;\n              break;\n            }\n\n            const tableX = ai.targetTable.tableData.x;\n            const tableZ = ai.targetTable.tableData.z;\n            const dx = tableX - hiredCleanerMesh.position.x;\n            const dz = tableZ - hiredCleanerMesh.position.z;\n            const dist = Math.sqrt(dx * dx + dz * dz);\n\n            if (dist < 2) {\n              // Arrived - clean table, landing squish\n              ai.state = 'cleaning';\n              ai.cleanTimer = 0;\n              squish.scaleY = 0.8;\n              squish.scaleXZ = 1.2;\n            } else {\n              // Walk toward table with squishy walk\n              hiredCleanerMesh.position.x += (dx / dist) * ai.walkSpeed * deltaTime;\n              hiredCleanerMesh.position.z += (dz / dist) * ai.walkSpeed * deltaTime;\n              hiredCleanerMesh.rotation.y = Math.atan2(dx, dz);\n\n              // Squishy walk cycle\n              const walkCycle = Math.sin(now * 0.012) * 0.5 + 0.5;\n              squish.targetScaleY = 0.88 + walkCycle * 0.24;\n              squish.targetScaleXZ = 1.12 - walkCycle * 0.24;\n\n              // Walking bob animation\n              hiredCleanerMesh.position.y = Math.abs(Math.sin(now * 0.015)) * 0.1;\n            }\n            break;\n\n          case 'cleaning':\n            // Switch to interact animation\n            switchAnimation(hiredCleanerMesh, 'interact');\n\n            ai.cleanTimer = (ai.cleanTimer || 0) + deltaTime;\n\n            // Cleaning animation - rotate back and forth with squish\n            hiredCleanerMesh.rotation.y = Math.sin(now * 0.01) * 0.3;\n\n            // Rhythmic cleaning squish\n            const cleanCycle = Math.sin(now * 0.015);\n            squish.targetScaleY = 1 + cleanCycle * 0.08;\n            squish.targetScaleXZ = 1 - cleanCycle * 0.06;\n\n            if (ai.cleanTimer > 1.5) {\n              // Done cleaning\n              cleanTrashFromTable(ai.targetTable.tableData);\n              playSound('pickup_sound');\n              // Celebration bounce\n              squish.scaleY = 1.15;\n              squish.scaleXZ = 0.85;\n              ai.state = 'idle';\n              ai.targetTable = null;\n            }\n            break;\n        }\n\n        // Apply squish interpolation\n        const squishSpeed = 10 * deltaTime;\n        squish.scaleY += (squish.targetScaleY - squish.scaleY) * squishSpeed;\n        squish.scaleXZ += (squish.targetScaleXZ - squish.scaleXZ) * squishSpeed;\n        hiredCleanerMesh.scale.set(squish.scaleXZ, squish.scaleY, squish.scaleXZ);\n      }\n\n      // ============================================\n      // RUNNER STAFF AI\n      // ============================================\n\n      function updateRunnerStaffTile(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.runnerStaffTile;\n        if (!config || config.deleted || config.staffHired || !runnerStaffTileMesh) return;\n\n        // Check if player is in tile zone\n        isInRunnerStaffTile = isPlayerInZone(config);\n\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n\n        // Deposit money when in zone\n        if (isInRunnerStaffTile && vaultBalance > 0 && totalDeposited < config.hireCost) {\n          runnerStaffDepositTimer += deltaTime;\n\n          if (runnerStaffDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            runnerStaffDepositTimer = 0;\n\n            const depositAmount = getDepositAmount(config.hireCost, moneyValue, totalDeposited);\n            if (depositAmount > 0) {\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.staffHired) return;\n                  config.moneyDeposited += depositAmount;\n                  window.gameConfig.runnerStaffTile.moneyDeposited = config.moneyDeposited;\n\n                  playSound('collect_sound');\n                  updateRunnerStaffProgress();\n                  updateRunnerStaffCostDisplay();\n\n                  if (config.moneyDeposited >= config.hireCost) {\n                    window.gameConfig.runnerStaffTile.staffHired = true;\n                    config.staffHired = true;\n                    playSound('upgrade_sound');\n                    showHireNotification('runner');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    createHiredRunner();\n                    destroyRunnerStaffTileVisuals();\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          runnerStaffDepositTimer = 0;\n        }\n\n        // Animate glow\n        if (runnerStaffTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          runnerStaffTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n\n        const icon = runnerStaffTileMesh?.userData?.staffIcon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      function destroyRunnerStaffTileVisuals() {\n        if (!runnerStaffTileMesh) return;\n\n        scene.remove(runnerStaffTileMesh);\n        scene.remove(runnerStaffTileGlow);\n\n        ['borderMesh', 'staffIcon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n          const obj = runnerStaffTileMesh.userData[key];\n          if (obj) scene.remove(obj);\n        });\n\n        window.gameConfig.runnerStaffTile.deleted = true;\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'runnerStaffTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n\n        runnerStaffTileMesh = null;\n        runnerStaffTileGlow = null;\n      }\n\n      function updateRunnerAI(deltaTime) {\n        if (!hiredRunnerMesh || !runnerStaffAI) return;\n\n        const ai = runnerStaffAI;\n        const cashierConfig = window.gameConfig.cashierStall;\n        const squish = hiredRunnerMesh.userData.squishState || {\n          scaleY: 1,\n          scaleXZ: 1,\n          targetScaleY: 1,\n          targetScaleXZ: 1,\n        };\n        const now = Date.now();\n\n        switch (ai.state) {\n          case 'idle':\n            // Switch to idle animation\n            switchAnimation(hiredRunnerMesh, 'idle');\n\n            ai.idleTimer += deltaTime;\n            if (ai.idleTimer > 0.3) {\n              ai.idleTimer = 0;\n\n              // If carrying burgers at capacity, go to cashier\n              if (ai.carryingBurgers >= ai.maxCapacity) {\n                ai.state = 'walkingToCashier';\n                squish.scaleY = 0.85;\n                squish.scaleXZ = 1.15;\n              } else {\n                // First priority: collect ground items if any exist\n                let foundGroundItem = false;\n                if (groundItems.length > 0) {\n                  let nearestItem = null;\n                  let nearestDist = Infinity;\n\n                  for (const item of groundItems) {\n                    if (item.userData.collected || item.userData.beingVacuumed) continue;\n                    const dx = item.position.x - hiredRunnerMesh.position.x;\n                    const dz = item.position.z - hiredRunnerMesh.position.z;\n                    const dist = Math.sqrt(dx * dx + dz * dz);\n                    if (dist < nearestDist) {\n                      nearestDist = dist;\n                      nearestItem = item;\n                    }\n                  }\n\n                  if (nearestItem) {\n                    ai.targetItem = nearestItem;\n                    ai.state = 'walkingToItem';\n                    squish.scaleY = 0.85;\n                    squish.scaleXZ = 1.15;\n                    foundGroundItem = true;\n                  }\n                }\n\n                // Second priority: find a ready grill to use\n                if (!foundGroundItem) {\n                  let nearestGrill = null;\n                  let nearestDist = Infinity;\n\n                  for (const node of resourceNodes) {\n                    if (!node.isReady) continue;\n                    const dx = node.position.x - hiredRunnerMesh.position.x;\n                    const dz = node.position.z - hiredRunnerMesh.position.z;\n                    const dist = Math.sqrt(dx * dx + dz * dz);\n                    if (dist < nearestDist) {\n                      nearestDist = dist;\n                      nearestGrill = node;\n                    }\n                  }\n\n                  if (nearestGrill) {\n                    ai.targetGrill = nearestGrill;\n                    ai.state = 'walkingToGrill';\n                    squish.scaleY = 0.85;\n                    squish.scaleXZ = 1.15;\n                  } else if (ai.carryingBurgers > 0) {\n                    // No grills ready but carrying burgers - deliver them\n                    ai.state = 'walkingToCashier';\n                    squish.scaleY = 0.85;\n                    squish.scaleXZ = 1.15;\n                  }\n                }\n              }\n            }\n\n            // Idle breathing\n            const breathe = Math.sin(now * 0.003);\n            squish.targetScaleY = 1 + breathe * 0.04;\n            squish.targetScaleXZ = 1 - breathe * 0.03;\n            hiredRunnerMesh.position.y = Math.sin(now * 0.003) * 0.03;\n            break;\n\n          case 'walkingToGrill':\n            // Switch to run/carry animation\n            switchAnimation(hiredRunnerMesh, ai.carryingBurgers > 0 ? 'walkCarry' : 'run');\n\n            if (!ai.targetGrill || !ai.targetGrill.isReady) {\n              ai.state = 'idle';\n              ai.targetGrill = null;\n              break;\n            }\n\n            const grillX = ai.targetGrill.position.x;\n            const grillZ = ai.targetGrill.position.z;\n            const dxg = grillX - hiredRunnerMesh.position.x;\n            const dzg = grillZ - hiredRunnerMesh.position.z;\n            const distg = Math.sqrt(dxg * dxg + dzg * dzg);\n\n            if (distg < 2.5) {\n              // Arrived at grill - start cooking/collecting\n              ai.state = 'usingGrill';\n              ai.collectTimer = 0;\n              squish.scaleY = 0.8;\n              squish.scaleXZ = 1.2;\n            } else {\n              hiredRunnerMesh.position.x += (dxg / distg) * ai.walkSpeed * deltaTime;\n              hiredRunnerMesh.position.z += (dzg / distg) * ai.walkSpeed * deltaTime;\n              hiredRunnerMesh.rotation.y = Math.atan2(dxg, dzg);\n\n              // Squishy walk\n              const walkCycle = Math.sin(now * 0.014) * 0.5 + 0.5;\n              squish.targetScaleY = 0.88 + walkCycle * 0.24;\n              squish.targetScaleXZ = 1.12 - walkCycle * 0.24;\n              hiredRunnerMesh.position.y = Math.abs(Math.sin(now * 0.015)) * 0.1;\n            }\n            break;\n\n          case 'usingGrill':\n            // Switch to interact animation\n            switchAnimation(hiredRunnerMesh, 'interact');\n\n            if (!ai.targetGrill || !ai.targetGrill.isReady) {\n              ai.state = 'idle';\n              ai.targetGrill = null;\n              break;\n            }\n\n            ai.collectTimer += deltaTime;\n\n            // Show collection progress (using same time as player)\n            const collectionTime = getCollectionTime();\n\n            // Working squish\n            const workCycle = Math.sin(now * 0.02);\n            squish.targetScaleY = 1 + workCycle * 0.06;\n            squish.targetScaleXZ = 1 - workCycle * 0.05;\n\n            if (ai.collectTimer >= collectionTime) {\n              // Deplete the equipment and spawn food items\n              const node = ai.targetGrill;\n              const equipmentType = node.equipmentType || 'grill';\n              node.isReady = false;\n              node.regenTimer = window.gameConfig.resources.regenerationTime;\n              if (node.grillMesh) {\n                node.grillMesh.visible = false;\n                node.grillMesh.scale.setScalar(0);\n              }\n              if (node.emptyMesh) {\n                node.emptyMesh.visible = true;\n              }\n\n              // Spawn food items on the ground near the equipment\n              const itemsToSpawn = 3 + Math.floor(Math.random() * 2);\n              // 3-4 items\n              for (let i = 0; i < itemsToSpawn; i++) {\n                const angle = (i / itemsToSpawn) * Math.PI * 2;\n                const distance = 1.5 + Math.random() * 1;\n                const spawnPos = new THREE.Vector3(\n                  node.position.x + Math.cos(angle) * distance,\n                  0,\n                  node.position.z + Math.sin(angle) * distance\n                );\n                // Spawn food that matches this equipment type\n                const menuItem = getRandomMenuItemForEquipment(equipmentType);\n                createGroundItem(spawnPos, menuItem);\n              }\n\n              playSound('pickup_sound');\n              squish.scaleY = 1.15;\n              squish.scaleXZ = 0.85;\n              ai.targetGrill = null;\n              ai.state = 'idle';\n              // Will immediately find the ground items\n            }\n            break;\n\n          case 'walkingToItem':\n            // Switch to run/carry animation\n            switchAnimation(hiredRunnerMesh, ai.carryingBurgers > 0 ? 'walkCarry' : 'run');\n\n            if (!ai.targetItem || ai.targetItem.userData.collected) {\n              ai.state = 'idle';\n              ai.targetItem = null;\n              break;\n            }\n\n            const itemX = ai.targetItem.position.x;\n            const itemZ = ai.targetItem.position.z;\n            const dxi = itemX - hiredRunnerMesh.position.x;\n            const dzi = itemZ - hiredRunnerMesh.position.z;\n            const disti = Math.sqrt(dxi * dxi + dzi * dzi);\n\n            if (disti < 1.5) {\n              // Collect the item\n              const menuItem = ai.targetItem.userData.menuItem;\n              ai.targetItem.userData.collected = true;\n              ai.carryingBurgers++;\n              addBurgerToRunnerStack(menuItem);\n              // Pass menu item\n              playSound('pickup_sound');\n              // Collection bounce\n              squish.scaleY = 1.2;\n              squish.scaleXZ = 0.8;\n\n              if (ai.carryingBurgers >= ai.maxCapacity) {\n                ai.state = 'walkingToCashier';\n              } else {\n                ai.state = 'idle';\n              }\n              ai.targetItem = null;\n            } else {\n              hiredRunnerMesh.position.x += (dxi / disti) * ai.walkSpeed * deltaTime;\n              hiredRunnerMesh.position.z += (dzi / disti) * ai.walkSpeed * deltaTime;\n              hiredRunnerMesh.rotation.y = Math.atan2(dxi, dzi);\n\n              // Squishy walk\n              const walkCycle = Math.sin(now * 0.014) * 0.5 + 0.5;\n              squish.targetScaleY = 0.88 + walkCycle * 0.24;\n              squish.targetScaleXZ = 1.12 - walkCycle * 0.24;\n              hiredRunnerMesh.position.y = Math.abs(Math.sin(now * 0.015)) * 0.1;\n            }\n            break;\n\n          case 'walkingToCashier':\n            // Switch to run/carry animation\n            switchAnimation(hiredRunnerMesh, ai.carryingBurgers > 0 ? 'walkCarry' : 'run');\n\n            if (!cashierConfig || cashierConfig.deleted) {\n              ai.state = 'idle';\n              break;\n            }\n\n            const cashierX = cashierConfig.x;\n            const cashierZ = cashierConfig.z - cashierConfig.depth / 2 - 2;\n            const dxc = cashierX - hiredRunnerMesh.position.x;\n            const dzc = cashierZ - hiredRunnerMesh.position.z;\n            const distc = Math.sqrt(dxc * dxc + dzc * dzc);\n\n            if (distc < 2) {\n              // Deposit burgers\n              ai.state = 'depositing';\n              ai.depositTimer = 0;\n              squish.scaleY = 0.8;\n              squish.scaleXZ = 1.2;\n            } else {\n              hiredRunnerMesh.position.x += (dxc / distc) * ai.walkSpeed * deltaTime;\n              hiredRunnerMesh.position.z += (dzc / distc) * ai.walkSpeed * deltaTime;\n              hiredRunnerMesh.rotation.y = Math.atan2(dxc, dzc);\n\n              // Squishy walk\n              const walkCycle = Math.sin(now * 0.014) * 0.5 + 0.5;\n              squish.targetScaleY = 0.88 + walkCycle * 0.24;\n              squish.targetScaleXZ = 1.12 - walkCycle * 0.24;\n              hiredRunnerMesh.position.y = Math.abs(Math.sin(now * 0.015)) * 0.1;\n            }\n            break;\n\n          case 'depositing':\n            // Switch to interact animation\n            switchAnimation(hiredRunnerMesh, 'interact');\n\n            ai.depositTimer += deltaTime;\n\n            // Depositing rhythm squish\n            const depositCycle = Math.sin(now * 0.02);\n            squish.targetScaleY = 1 + depositCycle * 0.05;\n            squish.targetScaleXZ = 1 - depositCycle * 0.04;\n\n            if (ai.depositTimer >= 0.15) {\n              ai.depositTimer = 0;\n\n              if (ai.carryingBurgers > 0 && cashierBurgerStack.length < cashierConfig.maxBurgers) {\n                ai.carryingBurgers--;\n                const menuItem = removeBurgerFromRunnerStack();\n                addBurgerToCashier(menuItem);\n                // Pass menu item\n                playSound('pickup_sound');\n                updateCashierUI();\n                // Quick deposit bounce\n                squish.scaleY = 1.1;\n                squish.scaleXZ = 0.9;\n              }\n\n              if (ai.carryingBurgers === 0) {\n                ai.state = 'idle';\n              }\n            }\n            break;\n        }\n\n        updateTray(hiredRunnerMesh, runnerBurgerStack);\n\n        // Apply squish interpolation\n        const squishSpeed = 10 * deltaTime;\n        squish.scaleY += (squish.targetScaleY - squish.scaleY) * squishSpeed;\n        squish.scaleXZ += (squish.targetScaleXZ - squish.scaleXZ) * squishSpeed;\n        hiredRunnerMesh.scale.set(squish.scaleXZ, squish.scaleY, squish.scaleXZ);\n      }\n\n      function addBurgerToRunnerStack(menuItem = null) {\n        if (!hiredRunnerMesh) return;\n\n        const food = createStackItem(menuItem);\n        const stackY = PLAYER_HEIGHT + runnerBurgerStack.length * STACK_ITEM_HEIGHT;\n        food.position.set(0, stackY, 1);\n        hiredRunnerMesh.add(food);\n        runnerBurgerStack.push(food);\n      }\n\n      function removeBurgerFromRunnerStack() {\n        if (runnerBurgerStack.length > 0) {\n          const item = runnerBurgerStack.pop();\n          const menuItem = item.userData.menuItem;\n          hiredRunnerMesh.remove(item);\n          item.traverse((child) => {\n            if (child.geometry) child.geometry.dispose();\n            if (child.material) {\n              if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n              else child.material.dispose();\n            }\n          });\n          return menuItem;\n          // Return menu item for value tracking\n        }\n        return null;\n      }\n\n      // ============================================\n      // VAULT COLLECTOR STAFF TILE UPDATE\n      // ============================================\n\n      function updateVaultStaffTile(deltaTime) {\n        if (gameMode === 'edit') return;\n\n        const config = window.gameConfig.vaultStaffTile;\n        if (!config || config.deleted || config.staffHired || !vaultStaffTileMesh) return;\n\n        // Check if player is in tile zone\n        isInVaultStaffTile = isPlayerInZone(config);\n\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n\n        // Deposit money when in zone\n        if (isInVaultStaffTile && vaultBalance > 0 && totalDeposited < config.hireCost) {\n          vaultStaffDepositTimer += deltaTime;\n\n          if (vaultStaffDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            vaultStaffDepositTimer = 0;\n\n            const depositAmount = getDepositAmount(config.hireCost, moneyValue, totalDeposited);\n            if (depositAmount > 0) {\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.staffHired) return;\n                  config.moneyDeposited += depositAmount;\n                  window.gameConfig.vaultStaffTile.moneyDeposited = config.moneyDeposited;\n\n                  playSound('collect_sound');\n                  updateVaultStaffProgress();\n                  updateVaultStaffCostDisplay();\n\n                  if (config.moneyDeposited >= config.hireCost) {\n                    window.gameConfig.vaultStaffTile.staffHired = true;\n                    config.staffHired = true;\n                    playSound('upgrade_sound');\n                    showHireNotification('vaultCollector');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    createHiredVaultCollector();\n                    destroyVaultStaffTileVisuals();\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          vaultStaffDepositTimer = 0;\n        }\n\n        // Animate glow\n        if (vaultStaffTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          vaultStaffTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n\n        const icon = vaultStaffTileMesh?.userData?.staffIcon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      function updateVaultCollectorAI(deltaTime) {\n        if (!hiredVaultCollectorMesh || !vaultCollectorAI) return;\n\n        const ai = vaultCollectorAI;\n        const vaultConfig = window.gameConfig.bankVault;\n        const squish = hiredVaultCollectorMesh.userData.squishState || {\n          scaleY: 1,\n          scaleXZ: 1,\n          targetScaleY: 1,\n          targetScaleXZ: 1,\n        };\n        const now = Date.now();\n\n        switch (ai.state) {\n          case 'idle':\n            // Switch to idle animation\n            switchAnimation(hiredVaultCollectorMesh, 'idle');\n\n            ai.idleTimer += deltaTime;\n            if (ai.idleTimer > 0.5) {\n              ai.idleTimer = 0;\n\n              // If carrying money at capacity or no more to collect, go to vault\n              if (ai.carryingMoney >= ai.maxCapacity) {\n                ai.state = 'walkingToVault';\n                squish.scaleY = 0.85;\n                squish.scaleXZ = 1.15;\n              } else {\n                // Find cash pile with money to collect\n                let targetPile = null;\n\n                // Check cashier cash pile\n                if (cashierCashPile.length > 0) {\n                  targetPile = 'cashier';\n                } // Check drive-thru cash pile\n                else if (driveThruCashPile.length > 0) {\n                  targetPile = 'driveThru';\n                }\n\n                if (targetPile) {\n                  ai.targetCashPile = targetPile;\n                  ai.state = 'walkingToCashPile';\n                  squish.scaleY = 0.85;\n                  squish.scaleXZ = 1.15;\n                } else if (ai.carryingMoney > 0) {\n                  // No more money to collect but carrying some - deposit\n                  ai.state = 'walkingToVault';\n                  squish.scaleY = 0.85;\n                  squish.scaleXZ = 1.15;\n                }\n              }\n            }\n\n            // Idle breathing\n            const breathe = Math.sin(now * 0.003);\n            squish.targetScaleY = 1 + breathe * 0.04;\n            squish.targetScaleXZ = 1 - breathe * 0.03;\n            hiredVaultCollectorMesh.position.y = Math.sin(now * 0.003) * 0.03;\n            break;\n\n          case 'walkingToCashPile':\n            // Switch to run/carry animation\n            switchAnimation(hiredVaultCollectorMesh, ai.carryingMoney > 0 ? 'walkCarry' : 'run');\n\n            let targetX, targetZ;\n\n            if (ai.targetCashPile === 'cashier' && cashierStallMesh) {\n              targetX = cashierStallMesh.userData.cashPileX;\n              targetZ = cashierStallMesh.userData.cashPileZ;\n            } else if (ai.targetCashPile === 'driveThru' && driveThruBoothMesh) {\n              targetX = driveThruBoothMesh.userData.cashPileX;\n              targetZ = driveThruBoothMesh.userData.cashPileZ;\n            } else {\n              ai.state = 'idle';\n              ai.targetCashPile = null;\n              break;\n            }\n\n            const dxp = targetX - hiredVaultCollectorMesh.position.x;\n            const dzp = targetZ - hiredVaultCollectorMesh.position.z;\n            const distp = Math.sqrt(dxp * dxp + dzp * dzp);\n\n            if (distp < 2) {\n              // Arrived at cash pile - start collecting\n              ai.state = 'collecting';\n              ai.collectTimer = 0;\n              squish.scaleY = 0.8;\n              squish.scaleXZ = 1.2;\n            } else {\n              hiredVaultCollectorMesh.position.x += (dxp / distp) * ai.walkSpeed * deltaTime;\n              hiredVaultCollectorMesh.position.z += (dzp / distp) * ai.walkSpeed * deltaTime;\n              hiredVaultCollectorMesh.rotation.y = Math.atan2(dxp, dzp);\n\n              // Squishy walk\n              const walkCycle = Math.sin(now * 0.014) * 0.5 + 0.5;\n              squish.targetScaleY = 0.88 + walkCycle * 0.24;\n              squish.targetScaleXZ = 1.12 - walkCycle * 0.24;\n              hiredVaultCollectorMesh.position.y = Math.abs(Math.sin(now * 0.015)) * 0.1;\n            }\n            break;\n\n          case 'collecting':\n            // Switch to interact animation\n            switchAnimation(hiredVaultCollectorMesh, 'interact');\n\n            ai.collectTimer += deltaTime;\n\n            // Collecting rhythm squish\n            const collectCycle = Math.sin(now * 0.02);\n            squish.targetScaleY = 1 + collectCycle * 0.06;\n            squish.targetScaleXZ = 1 - collectCycle * 0.05;\n\n            if (ai.collectTimer >= 0.2) {\n              ai.collectTimer = 0;\n\n              let collectedMoney = false;\n\n              if (ai.targetCashPile === 'cashier' && cashierCashPile.length > 0) {\n                // Collect from cashier cash pile\n                const money = cashierCashPile.pop();\n                scene.remove(money);\n                money.traverse((child) => {\n                  if (child.geometry) child.geometry.dispose();\n                  if (child.material) {\n                    if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                    else child.material.dispose();\n                  }\n                });\n                ai.carryingMoney++;\n                addMoneyToVaultCollectorStack();\n                collectedMoney = true;\n                cashierEarnings = cashierCashPile.length;\n                updateCashierUI();\n              } else if (ai.targetCashPile === 'driveThru' && driveThruCashPile.length > 0) {\n                // Collect from drive-thru cash pile\n                const money = driveThruCashPile.pop();\n                scene.remove(money);\n                money.traverse((child) => {\n                  if (child.geometry) child.geometry.dispose();\n                  if (child.material) {\n                    if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                    else child.material.dispose();\n                  }\n                });\n                ai.carryingMoney++;\n                addMoneyToVaultCollectorStack();\n                collectedMoney = true;\n              }\n\n              if (collectedMoney) {\n                playSound('pickup_sound');\n                squish.scaleY = 1.15;\n                squish.scaleXZ = 0.85;\n              }\n\n              // Check if should continue or go deposit\n              const currentPileEmpty =\n                (ai.targetCashPile === 'cashier' && cashierCashPile.length === 0) ||\n                (ai.targetCashPile === 'driveThru' && driveThruCashPile.length === 0);\n\n              if (ai.carryingMoney >= ai.maxCapacity || currentPileEmpty) {\n                if (ai.carryingMoney > 0) {\n                  ai.state = 'walkingToVault';\n                } else {\n                  ai.state = 'idle';\n                }\n                ai.targetCashPile = null;\n              }\n            }\n            break;\n\n          case 'walkingToVault':\n            // Switch to run/carry animation\n            switchAnimation(hiredVaultCollectorMesh, ai.carryingMoney > 0 ? 'walkCarry' : 'run');\n\n            if (!vaultConfig || vaultConfig.deleted) {\n              ai.state = 'idle';\n              break;\n            }\n\n            const vaultX = vaultConfig.x;\n            const vaultZ = vaultConfig.z + vaultConfig.depth / 2 + 1.5;\n            const dxv = vaultX - hiredVaultCollectorMesh.position.x;\n            const dzv = vaultZ - hiredVaultCollectorMesh.position.z;\n            const distv = Math.sqrt(dxv * dxv + dzv * dzv);\n\n            if (distv < 2) {\n              // Deposit money in vault\n              ai.state = 'depositing';\n              ai.depositTimer = 0;\n              squish.scaleY = 0.8;\n              squish.scaleXZ = 1.2;\n            } else {\n              hiredVaultCollectorMesh.position.x += (dxv / distv) * ai.walkSpeed * deltaTime;\n              hiredVaultCollectorMesh.position.z += (dzv / distv) * ai.walkSpeed * deltaTime;\n              hiredVaultCollectorMesh.rotation.y = Math.atan2(dxv, dzv);\n\n              // Squishy walk\n              const walkCycle = Math.sin(now * 0.014) * 0.5 + 0.5;\n              squish.targetScaleY = 0.88 + walkCycle * 0.24;\n              squish.targetScaleXZ = 1.12 - walkCycle * 0.24;\n              hiredVaultCollectorMesh.position.y = Math.abs(Math.sin(now * 0.015)) * 0.1;\n            }\n            break;\n\n          case 'depositing':\n            // Switch to interact animation\n            switchAnimation(hiredVaultCollectorMesh, 'interact');\n\n            ai.depositTimer += deltaTime;\n\n            // Depositing rhythm squish\n            const depositCycle = Math.sin(now * 0.02);\n            squish.targetScaleY = 1 + depositCycle * 0.05;\n            squish.targetScaleXZ = 1 - depositCycle * 0.04;\n\n            if (ai.depositTimer >= 0.12) {\n              ai.depositTimer = 0;\n\n              if (ai.carryingMoney > 0) {\n                ai.carryingMoney--;\n                removeMoneyFromVaultCollectorStack();\n\n                // Add to vault balance\n                const depositValue = window.gameConfig.settings.paperMoneyValue || 10;\n                modifyVaultBalance(depositValue);\n\n                playSound('pickup_sound');\n                squish.scaleY = 1.1;\n                squish.scaleXZ = 0.9;\n              }\n\n              if (ai.carryingMoney === 0) {\n                ai.state = 'idle';\n              }\n            }\n            break;\n        }\n\n        // Apply squish interpolation\n        const squishSpeed = 10 * deltaTime;\n        squish.scaleY += (squish.targetScaleY - squish.scaleY) * squishSpeed;\n        squish.scaleXZ += (squish.targetScaleXZ - squish.scaleXZ) * squishSpeed;\n        hiredVaultCollectorMesh.scale.set(squish.scaleXZ, squish.scaleY, squish.scaleXZ);\n      }\n\n      function addMoneyToVaultCollectorStack() {\n        if (!hiredVaultCollectorMesh) return;\n\n        const money = createMoneyStackItem();\n        const stackY = PLAYER_HEIGHT + vaultCollectorMoneyStack.length * MONEY_STACK_HEIGHT;\n        money.position.set(0, stackY, 0);\n        hiredVaultCollectorMesh.add(money);\n        vaultCollectorMoneyStack.push(money);\n      }\n\n      function removeMoneyFromVaultCollectorStack() {\n        if (vaultCollectorMoneyStack.length > 0) {\n          const item = vaultCollectorMoneyStack.pop();\n          hiredVaultCollectorMesh.remove(item);\n          item.traverse((child) => {\n            if (child.geometry) child.geometry.dispose();\n            if (child.material) {\n              if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n              else child.material.dispose();\n            }\n          });\n        }\n      }\n\n      // ============================================\n      // FLYING ITEMS SYSTEM (Vacuum & Throw Effects)\n      // ============================================\n\n      function createFlyingItem(sourcePos, targetPos, itemType, onComplete, throwDirection = null) {\n        // Create visual for flying item based on type\n        let visual;\n\n        if (itemType === 'burger') {\n          visual = createStackItem();\n        } else if (itemType === 'money') {\n          visual = createMoneyStackItem();\n        }\n\n        if (!visual) return;\n\n        visual.position.set(sourcePos.x, sourcePos.y, sourcePos.z);\n        scene.add(visual);\n\n        const flyingItem = {\n          mesh: visual,\n          startPos: {\n            x: sourcePos.x,\n            y: sourcePos.y,\n            z: sourcePos.z,\n          },\n          targetPos: {\n            x: targetPos.x,\n            y: targetPos.y,\n            z: targetPos.z,\n          },\n          progress: 0,\n          itemType: itemType,\n          onComplete: onComplete,\n          isThrow: throwDirection !== null,\n          // true = throw arc, false = vacuum\n          throwDirection: throwDirection,\n        };\n\n        flyingItems.push(flyingItem);\n        return flyingItem;\n      }\n\n      function updateFlyingItems(deltaTime) {\n        const playerX = player.position.x;\n        const playerY = player.position.y;\n        const playerZ = player.position.z;\n        const stackHeight = playerStack.length * STACK_ITEM_HEIGHT;\n        const moneyHeight = moneyStack.length * MONEY_STACK_HEIGHT;\n\n        for (let i = flyingItems.length - 1; i >= 0; i--) {\n          const item = flyingItems[i];\n          const mesh = item.mesh;\n\n          // Get current target position\n          let targetX, targetY, targetZ;\n\n          if (item.isThrow) {\n            targetX = item.targetPos.x;\n            targetY = item.targetPos.y;\n            targetZ = item.targetPos.z;\n          } else if (item.itemType === 'burger') {\n            targetX = playerX;\n            targetY = playerY + PLAYER_HEIGHT + stackHeight;\n            targetZ = playerZ;\n          } else {\n            targetX = playerX;\n            targetY = playerY + PLAYER_HEIGHT + stackHeight + moneyHeight;\n            targetZ = playerZ;\n          }\n\n          if (item.isThrow) {\n            item.progress += deltaTime * (THROW_SPEED / 10);\n\n            if (item.progress >= 1) {\n              mesh.position.set(targetX, targetY, targetZ);\n              if (item.onComplete) item.onComplete();\n\n              scene.remove(mesh);\n              mesh.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                  else child.material.dispose();\n                }\n              });\n              flyingItems.splice(i, 1);\n            } else {\n              const t = item.progress;\n              const easeT = t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;\n\n              mesh.position.x = item.startPos.x + (targetX - item.startPos.x) * easeT;\n              mesh.position.z = item.startPos.z + (targetZ - item.startPos.z) * easeT;\n\n              const arcProgress = Math.sin(t * Math.PI);\n              const baseY = item.startPos.y + (targetY - item.startPos.y) * easeT;\n              mesh.position.y = baseY + arcProgress * THROW_ARC_HEIGHT;\n\n              mesh.rotation.y += deltaTime * 10;\n              mesh.rotation.x += deltaTime * 5;\n            }\n          } else {\n            const dx = targetX - mesh.position.x;\n            const dy = targetY - mesh.position.y;\n            const dz = targetZ - mesh.position.z;\n            const distSq = dx * dx + dy * dy + dz * dz;\n\n            if (distSq < 0.25) {\n              // 0.5 squared\n              if (item.onComplete) item.onComplete();\n\n              scene.remove(mesh);\n              mesh.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                  else child.material.dispose();\n                }\n              });\n              flyingItems.splice(i, 1);\n            } else {\n              const dist = Math.sqrt(distSq);\n              const speed = getVacuumSpeed() * (1 + (1 - Math.min(dist / VACUUM_RANGE, 1)) * 2);\n              const moveAmount = (speed * deltaTime) / dist;\n\n              mesh.position.x += dx * moveAmount;\n              mesh.position.y += dy * moveAmount;\n              mesh.position.z += dz * moveAmount;\n\n              const scale = Math.max(0.3, dist / VACUUM_RANGE);\n              mesh.scale.setScalar(scale);\n\n              mesh.rotation.y += deltaTime * 8;\n            }\n          }\n        }\n      }\n\n      function throwItemToZone(itemType, targetPos, onComplete) {\n        // Get player world position for the item\n        const startPos = {\n          x: player.position.x,\n          y: player.position.y + PLAYER_HEIGHT + 0.5,\n          z: player.position.z,\n        };\n\n        createFlyingItem(startPos, targetPos, itemType, onComplete, true);\n      }\n\n      // ============================================\n      // GAME LOOP\n      // ============================================\n\n      function gameLoop(timestamp) {\n        const deltaTime = Math.min((timestamp - lastTime) / 1000, 0.11);\n        lastTime = timestamp;\n\n        if (gameMode === 'play') {\n          // Update autosave timer\n          if (window.gameConfig.settings.autosaveEnabled) {\n            autosaveTimer += deltaTime;\n            if (autosaveTimer >= AUTOSAVE_INTERVAL) {\n              autosaveTimer = 0;\n              performAutosave().catch((err) => lib.log('Autosave error: ' + err.message));\n            }\n          }\n          // Update animation mixers for 3D models\n          updateAnimationMixers(deltaTime);\n\n          updatePlayer(deltaTime);\n          updateResourceNodes(deltaTime);\n          updateCollection(deltaTime);\n          updateGroundItems(deltaTime);\n          updateSelling(deltaTime);\n          updateMoneyZone(deltaTime);\n          updateSpendZone(deltaTime);\n          updateStaffHireTile(deltaTime);\n          updateCleanerStaffTile(deltaTime);\n          updateRunnerStaffTile(deltaTime);\n          updateVaultStaffTile(deltaTime);\n          updateCashierTile(deltaTime);\n          updateCleanerAI(deltaTime);\n          updateRunnerAI(deltaTime);\n          updateVaultCollectorAI(deltaTime);\n          updateCashierStall(deltaTime);\n          updateCashierCashPile(deltaTime);\n          updateGrillTile(deltaTime);\n          updateDriveThruTile(deltaTime);\n          updateTrashTile(deltaTime);\n          updateVaultTile(deltaTime);\n          updateFountainTile(deltaTime);\n          updateFountain2Tile(deltaTime);\n          updateFountainAnimation();\n          updateSelfOrderKioskTile(deltaTime);\n          updateSelfOrderKioskTiles(deltaTime);\n          updateDriveThruBooth(deltaTime);\n          updateDriveThruCashPile(deltaTime);\n          updateDriveThruCarQueue(deltaTime);\n          updateSelfOrderKiosks(deltaTime);\n          updateRushHour(deltaTime);\n          updateCustomerQueue(deltaTime);\n          updateDiningTableTile(deltaTime);\n          updateTrashCollection(deltaTime);\n          updateTrashBin(deltaTime);\n          updateBankVault(deltaTime);\n          updateFlyingItems(deltaTime);\n          updateSmokeParticles(deltaTime);\n          updateDustParticles(deltaTime);\n          updateStaffWalking(deltaTime);\n          updateCityLights(deltaTime);\n          lerpTileProgressBars();\n          checkTutorialProgression();\n          updateTutorialArrow(deltaTime);\n          updateTutorialHint(deltaTime);\n\n          if (tutorialCompleteShown) {\n            tutorialCompleteTimer -= deltaTime;\n            if (tutorialCompleteTimer <= 0) {\n              const overlay = document.getElementById('tutorial-complete');\n              if (overlay) overlay.classList.remove('show');\n            }\n          }\n        } else {\n          // Edit mode - still update visuals\n          updateResourceNodes(deltaTime);\n          updateEditorSelection(deltaTime);\n          updateCityLights(deltaTime);\n\n          // Animate sell zone\n          if (sellZoneGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.003) * 0.05;\n            sellZoneGlow.scale.setScalar(pulseScale);\n          }\n\n          if (sellZoneMesh) {\n            const coinIndicator = sellZoneMesh.userData.coinIndicator;\n            if (coinIndicator) {\n              coinIndicator.rotation.y += deltaTime * 2;\n              coinIndicator.position.y =\n                coinIndicator.userData.baseY + Math.sin(Date.now() * 0.002) * 0.3;\n            }\n          }\n\n          // Animate money zone\n          if (moneyZoneGlow) {\n            const moneyPulse = 1 + Math.sin(Date.now() * 0.004) * 0.03;\n            moneyZoneGlow.scale.set(moneyPulse, 1, moneyPulse);\n          }\n\n// Animate spend zone\n          if (spendZoneGlow) {\n            const spendPulse = 1 + Math.sin(Date.now() * 0.005) * 0.04;\n            spendZoneGlow.scale.set(spendPulse, 1, spendPulse);\n          }\n\n          // Animate self-order kiosks\n          selfOrderKiosks.forEach((kiosk) => {\n            if (kiosk.glow) {\n              const pulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n              kiosk.glow.scale.set(pulse, 1, pulse);\n            }\n            if (kiosk.mesh && kiosk.mesh.userData.icon) {\n              kiosk.mesh.userData.icon.position.y = 3 + Math.sin(Date.now() * 0.003) * 0.2;\n              kiosk.mesh.userData.icon.lookAt(camera.position);\n            }\n          });\n\n          // Animate cashier zone\n          if (cashierStallGlow) {\n            const cashierPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            cashierStallGlow.scale.set(cashierPulse, 1, cashierPulse);\n          }\n\n          // Animate cash pile zone\n          if (cashierCashPileGlow) {\n            const cashPilePulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            cashierCashPileGlow.scale.set(cashPilePulse, 1, cashPilePulse);\n          }\n\n          // Animate dollar icon on cash pile\n          const dollarIcon = cashierCashPileMesh?.userData?.dollarIcon;\n          if (dollarIcon) {\n            dollarIcon.position.y = dollarIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n            dollarIcon.rotation.y += deltaTime * 1.2;\n          }\n\n          // Animate drive-thru booth\n          if (driveThruBoothGlow) {\n            const dtPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            driveThruBoothGlow.scale.set(dtPulse, 1, dtPulse);\n          }\n\n          if (driveThruCashPileGlow) {\n            const dtCashPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            driveThruCashPileGlow.scale.set(dtCashPulse, 1, dtCashPulse);\n          }\n\n          const carIcon = driveThruBoothMesh?.userData?.carIcon;\n          if (carIcon) {\n            carIcon.position.y = 3.5 + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          // Animate menu burger\n          const menuBurger = cashierStallMesh?.userData?.menuBurger;\n          if (menuBurger) {\n            menuBurger.rotation.y += deltaTime * 1.5;\n          }\n\n          // Animate staff hire tile\n          if (staffHireTileGlow) {\n            const staffPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            staffHireTileGlow.scale.set(staffPulse, 1, staffPulse);\n          }\n\n          const staffIcon = staffHireTileMesh?.userData?.staffIcon;\n          if (staffIcon) {\n            staffIcon.position.y = staffIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          // Animate dining table tiles\n          diningTableTileMeshes.forEach((tileEntry, i) => {\n            const diningTableTileGlow = tileEntry.glow;\n            const diningTableTileMesh = tileEntry.mesh;\n\n            if (diningTableTileGlow) {\n              const diningPulse = 1 + Math.sin(Date.now() * 0.004 + i) * 0.05;\n              diningTableTileGlow.scale.set(diningPulse, 1, diningPulse);\n            }\n\n            const tableIcon = diningTableTileMesh?.userData?.tableIcon;\n            if (tableIcon) {\n              tableIcon.position.y =\n                tableIcon.userData.baseY + Math.sin(Date.now() * 0.003 + i) * 0.1;\n              tableIcon.rotation.y += deltaTime * 0.5;\n            }\n          });\n\n          // Animate trash bin\n          if (trashBinGlow) {\n            const trashPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            trashBinGlow.scale.setScalar(trashPulse);\n          }\n\n          const editTrashIcon = trashBinMesh?.userData?.trashIcon;\n          if (editTrashIcon) {\n            editTrashIcon.position.y =\n              editTrashIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n            editTrashIcon.lookAt(camera.position);\n          }\n\n          // Animate bank vault\n          if (bankVaultGlow) {\n            const vaultPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            bankVaultGlow.scale.set(vaultPulse, 1, vaultPulse);\n          }\n\n          const vaultIcon = bankVaultMesh?.userData?.vaultIcon;\n          if (vaultIcon) {\n            vaultIcon.position.y = vaultIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.15;\n            vaultIcon.lookAt(camera.position);\n          }\n\n          const vaultHandle = bankVaultMesh?.userData?.handle;\n          if (vaultHandle) {\n            vaultHandle.rotation.z += deltaTime * 0.5;\n          }\n\n          // Animate cleaner staff tile\n          if (cleanerStaffTileGlow) {\n            const cleanerPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            cleanerStaffTileGlow.scale.set(cleanerPulse, 1, cleanerPulse);\n          }\n\n          const cleanerIcon =\n            cleanerStaffTileMesh &&\n            cleanerStaffTileMesh.userData &&\n            cleanerStaffTileMesh.userData.staffIcon;\n          if (cleanerIcon) {\n            cleanerIcon.position.y =\n              cleanerIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          // Animate runner staff tile\n          if (runnerStaffTileGlow) {\n            const runnerPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            runnerStaffTileGlow.scale.set(runnerPulse, 1, runnerPulse);\n          }\n\n          const runnerIcon =\n            runnerStaffTileMesh &&\n            runnerStaffTileMesh.userData &&\n            runnerStaffTileMesh.userData.staffIcon;\n          if (runnerIcon) {\n            runnerIcon.position.y = runnerIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          // Animate vault staff tile\n          if (vaultStaffTileGlow) {\n            const vaultStaffPulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            vaultStaffTileGlow.scale.set(vaultStaffPulse, 1, vaultStaffPulse);\n          }\n\n          const vaultStaffIcon =\n            vaultStaffTileMesh &&\n            vaultStaffTileMesh.userData &&\n            vaultStaffTileMesh.userData.staffIcon;\n          if (vaultStaffIcon) {\n            vaultStaffIcon.position.y =\n              vaultStaffIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          // Animate cashier tile\n          if (cashierTileGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            cashierTileGlow.scale.set(pulseScale, 1, pulseScale);\n          }\n          const cashierTileIcon = cashierTileMesh?.userData?.icon;\n          if (cashierTileIcon) {\n            cashierTileIcon.position.y =\n              cashierTileIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          // Animate grill tile\n          if (grillTileGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            grillTileGlow.scale.set(pulseScale, 1, pulseScale);\n          }\n          const grillTileIcon = grillTileMesh?.userData?.icon;\n          if (grillTileIcon) {\n            grillTileIcon.position.y =\n              grillTileIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          if (driveThruTileGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            driveThruTileGlow.scale.set(pulseScale, 1, pulseScale);\n          }\n          const driveThruTileIcon = driveThruTileMesh?.userData?.icon;\n          if (driveThruTileIcon) {\n            driveThruTileIcon.position.y =\n              driveThruTileIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          if (trashTileGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            trashTileGlow.scale.set(pulseScale, 1, pulseScale);\n          }\n          const trashTileIcon = trashTileMesh?.userData?.icon;\n          if (trashTileIcon) {\n            trashTileIcon.position.y =\n              trashTileIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          if (vaultTileGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            vaultTileGlow.scale.set(pulseScale, 1, pulseScale);\n          }\n          const vaultTileIcon = vaultTileMesh?.userData?.icon;\n          if (vaultTileIcon) {\n            vaultTileIcon.position.y =\n              vaultTileIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          updateFountainAnimation();\n          if (fountainTileGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            fountainTileGlow.scale.set(pulseScale, 1, pulseScale);\n          }\n          const fountainTileIcon = fountainTileMesh?.userData?.icon;\n          if (fountainTileIcon) {\n            fountainTileIcon.position.y =\n              fountainTileIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          if (fountain2TileGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            fountain2TileGlow.scale.set(pulseScale, 1, pulseScale);\n          }\n          const fountain2TileIcon = fountain2TileMesh?.userData?.icon;\n          if (fountain2TileIcon) {\n            fountain2TileIcon.position.y =\n              fountain2TileIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          if (selfOrderKioskTileGlow) {\n            const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            selfOrderKioskTileGlow.scale.set(pulseScale, 1, pulseScale);\n          }\n          const selfOrderKioskTileIcon = selfOrderKioskTileMesh?.userData?.icon;\n          if (selfOrderKioskTileIcon) {\n            selfOrderKioskTileIcon.position.y =\n              selfOrderKioskTileIcon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n          }\n\n          selfOrderKioskTileMeshes.forEach((tileEntry, index) => {\n            if (tileEntry.glow) {\n              const pulseScale = 1 + Math.sin(Date.now() * 0.004 + index) * 0.05;\n              tileEntry.glow.scale.set(pulseScale, 1, pulseScale);\n            }\n            const icon = tileEntry.mesh?.userData?.icon;\n            if (icon) {\n              icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003 + index) * 0.1;\n            }\n          });\n        }\n\n        // Animate self-order kiosks (fallback for non-array contexts)\n        selfOrderKiosks.forEach((kiosk) => {\n          if (kiosk.mesh && kiosk.mesh.userData.icon) {\n            kiosk.mesh.userData.icon.position.y = 3 + Math.sin(Date.now() * 0.003) * 0.2;\n            kiosk.mesh.userData.icon.lookAt(camera.position);\n          }\n          if (kiosk.glow) {\n            const pulse = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n            kiosk.glow.scale.set(pulse, 1, pulse);\n          }\n        });\n\n        updateCamera();\n        renderer.render(scene, camera);\n\n        requestAnimationFrame(gameLoop);\n      }\n\n      // ============================================\n      // LEVEL EDITOR SYSTEM\n      // ============================================\n\n      function initLevelEditor() {\n        if (gameMode !== 'edit') return;\n\n        raycaster = new THREE.Raycaster();\n\n        // Create selection highlight ring\n        const highlightGeometry = new THREE.RingGeometry(1.5, 1.8, 32);\n        const highlightMaterial = new THREE.MeshBasicMaterial({\n          color: 0x3498db,\n          side: THREE.DoubleSide,\n          transparent: true,\n          opacity: 0.8,\n        });\n        selectionHighlight = new THREE.Mesh(highlightGeometry, highlightMaterial);\n        selectionHighlight.rotation.x = -Math.PI / 2;\n        selectionHighlight.position.y = 0.1;\n        selectionHighlight.visible = false;\n        scene.add(selectionHighlight);\n\n        // Register editable objects\n        registerEditableObjects();\n\n        // Setup editor input handlers\n        setupEditorInput();\n        setupEditorToolbar();\n      }\n\n      function registerEditableObjects() {\n        editableObjects = [];\n\n        // Register player\n        editableObjects.push({\n          mesh: player,\n          type: 'player',\n          configKey: 'player',\n          name: 'Chef (Player Start)',\n          getData: () => ({\n            x: player.position.x,\n            z: player.position.z,\n          }),\n          setPosition: (x, z) => {\n            player.position.x = x;\n            player.position.z = z;\n            window.gameConfig.player.startX = x;\n            window.gameConfig.player.startZ = z;\n          },\n        });\n\n        // Register sell zone\n        if (window.gameConfig.sellZone && !window.gameConfig.sellZone.deleted) {\n          editableObjects.push({\n            mesh: sellZoneMesh,\n            type: 'sellZone',\n            configKey: 'sellZone',\n            name: 'Service Counter',\n            getData: () => ({\n              x: sellZoneMesh.position.x,\n              z: sellZoneMesh.position.z,\n              radius: window.gameConfig.sellZone.radius,\n            }),\n            setPosition: (x, z) => {\n              sellZoneMesh.position.x = x;\n              sellZoneMesh.position.z = z;\n              sellZoneGlow.position.x = x;\n              sellZoneGlow.position.z = z;\n              // Move customer indicator too\n              const customer = sellZoneMesh.userData.coinIndicator;\n              if (customer) {\n                customer.position.x = x;\n                customer.position.z = z;\n              }\n              // Move counter top\n              scene.children.forEach((child) => {\n                if (\n                  child.geometry &&\n                  child.geometry.type === 'CylinderGeometry' &&\n                  child.position.y === 0.35 &&\n                  child !== sellZoneMesh\n                ) {\n                  child.position.x = x;\n                  child.position.z = z;\n                }\n              });\n              window.gameConfig.sellZone.x = x;\n              window.gameConfig.sellZone.z = z;\n            },\n          });\n        }\n\n        // Register money zone\n        if (window.gameConfig.moneyZone && !window.gameConfig.moneyZone.deleted) {\n          editableObjects.push({\n            mesh: moneyZoneMesh,\n            type: 'moneyZone',\n            configKey: 'moneyZone',\n            name: 'Money Collection Zone',\n            getData: () => ({\n              x: window.gameConfig.moneyZone.x,\n              z: window.gameConfig.moneyZone.z,\n              width: window.gameConfig.moneyZone.width,\n              depth: window.gameConfig.moneyZone.depth,\n            }),\n            setPosition: (x, z) => {\n              updateMoneyZonePosition(x, z);\n            },\n          });\n        }\n\n        // Register spend zone\n        if (window.gameConfig.spendZone && !window.gameConfig.spendZone.deleted) {\n          editableObjects.push({\n            mesh: spendZoneMesh,\n            type: 'spendZone',\n            configKey: 'spendZone',\n            name: 'Spend/Deposit Zone',\n            getData: () => ({\n              x: window.gameConfig.spendZone.x,\n              z: window.gameConfig.spendZone.z,\n              width: window.gameConfig.spendZone.width,\n              depth: window.gameConfig.spendZone.depth,\n            }),\n            setPosition: (x, z) => {\n              updateSpendZonePosition(x, z);\n            },\n          });\n        }\n\n        // Register cashier tile\n        if (\n          window.gameConfig.cashierTile &&\n          !window.gameConfig.cashierTile.deleted &&\n          cashierTileMesh\n        ) {\n          editableObjects.push({\n            mesh: cashierTileMesh,\n            type: 'cashierTile',\n            configKey: 'cashierTile',\n            name: 'Cashier Unlock Tile',\n            getData: () => ({\n              x: window.gameConfig.cashierTile.x,\n              z: window.gameConfig.cashierTile.z,\n              width: window.gameConfig.cashierTile.width,\n              depth: window.gameConfig.cashierTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateCashierTilePosition(x, z);\n            },\n          });\n        }\n\n        // Register grill tile\n        if (window.gameConfig.grillTile && !window.gameConfig.grillTile.deleted && grillTileMesh) {\n          editableObjects.push({\n            mesh: grillTileMesh,\n            type: 'grillTile',\n            configKey: 'grillTile',\n            name: 'Grill Unlock Tile',\n            getData: () => ({\n              x: window.gameConfig.grillTile.x,\n              z: window.gameConfig.grillTile.z,\n              width: window.gameConfig.grillTile.width,\n              depth: window.gameConfig.grillTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateGrillTilePosition(x, z);\n            },\n          });\n        }\n\n        // Register cashier stall\n        if (window.gameConfig.cashierStall && !window.gameConfig.cashierStall.deleted) {\n          editableObjects.push({\n            mesh: cashierStallMesh,\n            type: 'cashierStall',\n            configKey: 'cashierStall',\n            name: 'Cashier Counter',\n            getData: () => ({\n              x: window.gameConfig.cashierStall.x,\n              z: window.gameConfig.cashierStall.z,\n              width: window.gameConfig.cashierStall.width,\n              depth: window.gameConfig.cashierStall.depth,\n            }),\n            setPosition: (x, z) => {\n              updateCashierStallPosition(x, z);\n            },\n          });\n        }\n\n        // Register staff hire tile\n        if (window.gameConfig.staffHireTile && !window.gameConfig.staffHireTile.deleted) {\n          editableObjects.push({\n            mesh: staffHireTileMesh,\n            type: 'staffHireTile',\n            configKey: 'staffHireTile',\n            name: 'Cashier Staff Tile',\n            getData: () => ({\n              x: window.gameConfig.staffHireTile.x,\n              z: window.gameConfig.staffHireTile.z,\n              width: window.gameConfig.staffHireTile.width,\n              depth: window.gameConfig.staffHireTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateStaffHireTilePosition(x, z);\n            },\n          });\n        }\n\n        // Register cleaner staff tile\n        if (\n          window.gameConfig.cleanerStaffTile &&\n          !window.gameConfig.cleanerStaffTile.deleted &&\n          cleanerStaffTileMesh\n        ) {\n          editableObjects.push({\n            mesh: cleanerStaffTileMesh,\n            type: 'cleanerStaffTile',\n            configKey: 'cleanerStaffTile',\n            name: 'Cleaner Staff Tile',\n            getData: () => ({\n              x: window.gameConfig.cleanerStaffTile.x,\n              z: window.gameConfig.cleanerStaffTile.z,\n              width: window.gameConfig.cleanerStaffTile.width,\n              depth: window.gameConfig.cleanerStaffTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateCleanerStaffTilePosition(x, z);\n            },\n          });\n        }\n\n        // Register runner staff tile\n        if (\n          window.gameConfig.runnerStaffTile &&\n          !window.gameConfig.runnerStaffTile.deleted &&\n          runnerStaffTileMesh\n        ) {\n          editableObjects.push({\n            mesh: runnerStaffTileMesh,\n            type: 'runnerStaffTile',\n            configKey: 'runnerStaffTile',\n            name: 'Runner Staff Tile',\n            getData: () => ({\n              x: window.gameConfig.runnerStaffTile.x,\n              z: window.gameConfig.runnerStaffTile.z,\n              width: window.gameConfig.runnerStaffTile.width,\n              depth: window.gameConfig.runnerStaffTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateRunnerStaffTilePosition(x, z);\n            },\n          });\n        }\n\n        // Register vault staff tile\n        if (\n          window.gameConfig.vaultStaffTile &&\n          !window.gameConfig.vaultStaffTile.deleted &&\n          vaultStaffTileMesh\n        ) {\n          editableObjects.push({\n            mesh: vaultStaffTileMesh,\n            type: 'vaultStaffTile',\n            configKey: 'vaultStaffTile',\n            name: 'Vault Staff Tile',\n            getData: () => ({\n              x: window.gameConfig.vaultStaffTile.x,\n              z: window.gameConfig.vaultStaffTile.z,\n              width: window.gameConfig.vaultStaffTile.width,\n              depth: window.gameConfig.vaultStaffTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateVaultStaffTilePosition(x, z);\n            },\n          });\n        }\n\n        // Register trash bin\n        if (window.gameConfig.trashBin && !window.gameConfig.trashBin.deleted) {\n          editableObjects.push({\n            mesh: trashBinMesh,\n            type: 'trashBin',\n            configKey: 'trashBin',\n            name: 'Trash Bin',\n            getData: () => ({\n              x: window.gameConfig.trashBin.x,\n              z: window.gameConfig.trashBin.z,\n              radius: window.gameConfig.trashBin.radius,\n            }),\n            setPosition: (x, z) => {\n              updateTrashBinPosition(x, z);\n            },\n          });\n        }\n\n        // Register bank vault\n        if (window.gameConfig.bankVault && !window.gameConfig.bankVault.deleted && bankVaultMesh) {\n          editableObjects.push({\n            mesh: bankVaultMesh,\n            type: 'bankVault',\n            configKey: 'bankVault',\n            name: 'Bank Vault',\n            getData: () => ({\n              x: window.gameConfig.bankVault.x,\n              z: window.gameConfig.bankVault.z,\n              width: window.gameConfig.bankVault.width,\n              depth: window.gameConfig.bankVault.depth,\n            }),\n            setPosition: (x, z) => {\n              updateBankVaultPosition(x, z);\n            },\n          });\n        }\n\n        // Register drive-thru booth\n        if (\n          window.gameConfig.driveThruTile &&\n          !window.gameConfig.driveThruTile.deleted &&\n          driveThruTileMesh\n        ) {\n          editableObjects.push({\n            mesh: driveThruTileMesh,\n            type: 'driveThruTile',\n            configKey: 'driveThruTile',\n            name: 'Drive-Thru Unlock Tile',\n            getData: () => ({\n              x: window.gameConfig.driveThruTile.x,\n              z: window.gameConfig.driveThruTile.z,\n              width: window.gameConfig.driveThruTile.width,\n              depth: window.gameConfig.driveThruTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateDriveThruTilePosition(x, z);\n            },\n          });\n        }\n\n        if (window.gameConfig.trashTile && !window.gameConfig.trashTile.deleted && trashTileMesh) {\n          editableObjects.push({\n            mesh: trashTileMesh,\n            type: 'trashTile',\n            configKey: 'trashTile',\n            name: 'Trash Bin Unlock Tile',\n            getData: () => ({\n              x: window.gameConfig.trashTile.x,\n              z: window.gameConfig.trashTile.z,\n              width: window.gameConfig.trashTile.width,\n              depth: window.gameConfig.trashTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateTrashTilePosition(x, z);\n            },\n          });\n        }\n\n        if (window.gameConfig.vaultTile && !window.gameConfig.vaultTile.deleted && vaultTileMesh) {\n          editableObjects.push({\n            mesh: vaultTileMesh,\n            type: 'vaultTile',\n            configKey: 'vaultTile',\n            name: 'Bank Vault Unlock Tile',\n            getData: () => ({\n              x: window.gameConfig.vaultTile.x,\n              z: window.gameConfig.vaultTile.z,\n              width: window.gameConfig.vaultTile.width,\n              depth: window.gameConfig.vaultTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateVaultTilePosition(x, z);\n            },\n          });\n        }\n\n        if (\n          window.gameConfig.fountainTile &&\n          !window.gameConfig.fountainTile.deleted &&\n          fountainTileMesh\n        ) {\n          editableObjects.push({\n            mesh: fountainTileMesh,\n            type: 'fountainTile',\n            configKey: 'fountainTile',\n            name: 'Fountain Unlock Tile',\n            getData: () => ({\n              x: window.gameConfig.fountainTile.x,\n              z: window.gameConfig.fountainTile.z,\n              width: window.gameConfig.fountainTile.width,\n              depth: window.gameConfig.fountainTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateFountainTilePosition(x, z);\n            },\n          });\n        }\n\n        if (\n          window.gameConfig.fountain2Tile &&\n          !window.gameConfig.fountain2Tile.deleted &&\n          fountain2TileMesh\n        ) {\n          editableObjects.push({\n            mesh: fountain2TileMesh,\n            type: 'fountain2Tile',\n            configKey: 'fountain2Tile',\n            name: 'Fountain 2 Unlock Tile',\n            getData: () => ({\n              x: window.gameConfig.fountain2Tile.x,\n              z: window.gameConfig.fountain2Tile.z,\n              width: window.gameConfig.fountain2Tile.width,\n              depth: window.gameConfig.fountain2Tile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateFountain2TilePosition(x, z);\n            },\n          });\n        }\n\n        if (\n          window.gameConfig.selfOrderKioskTile &&\n          !window.gameConfig.selfOrderKioskTile.deleted &&\n          selfOrderKioskTileMesh\n        ) {\n          editableObjects.push({\n            mesh: selfOrderKioskTileMesh,\n            type: 'selfOrderKioskTile',\n            configKey: 'selfOrderKioskTile',\n            name: 'Self-Order Kiosk Unlock Tile',\n            getData: () => ({\n              x: window.gameConfig.selfOrderKioskTile.x,\n              z: window.gameConfig.selfOrderKioskTile.z,\n              width: window.gameConfig.selfOrderKioskTile.width,\n              depth: window.gameConfig.selfOrderKioskTile.depth,\n            }),\n            setPosition: (x, z) => {\n              updateSelfOrderKioskTilePosition(x, z);\n            },\n          });\n        }\n\n        if (window.gameConfig.selfOrderKioskTiles) {\n          window.gameConfig.selfOrderKioskTiles.forEach((config, index) => {\n            if (config.deleted || config.hidden) return;\n            const tileEntry = selfOrderKioskTileMeshes.find((t) => t.index === index);\n            if (!tileEntry) return;\n            editableObjects.push({\n              mesh: tileEntry.mesh,\n              type: 'selfOrderKioskTile',\n              configKey: `selfOrderKioskTiles.${index}`,\n              tileIndex: index,\n              name: `Self-Order Kiosk Tile ${index + 1}`,\n              getData: () => ({\n                x: config.x,\n                z: config.z,\n                width: config.width,\n                depth: config.depth,\n              }),\n              setPosition: (x, z) => {\n                tileEntry.mesh.position.x = x;\n                tileEntry.mesh.position.z = z;\n                config.x = x;\n                config.z = z;\n              },\n            });\n          });\n        }\n\n        if (window.gameConfig.fountain && !window.gameConfig.fountain.deleted && fountainMesh) {\n          editableObjects.push({\n            mesh: fountainMesh,\n            type: 'fountain',\n            configKey: 'fountain',\n            name: 'Fountain',\n            getData: () => ({\n              x: window.gameConfig.fountain.x,\n              z: window.gameConfig.fountain.z,\n              radius: window.gameConfig.fountain.radius,\n            }),\n            setPosition: (x, z) => {\n              updateFountainPosition(x, z);\n            },\n          });\n        }\n\n        if (window.gameConfig.fountain2 && !window.gameConfig.fountain2.deleted && fountainMesh2) {\n          editableObjects.push({\n            mesh: fountainMesh2,\n            type: 'fountain2',\n            configKey: 'fountain2',\n            name: 'Fountain 2',\n            getData: () => ({\n              x: window.gameConfig.fountain2.x,\n              z: window.gameConfig.fountain2.z,\n              radius: window.gameConfig.fountain2.radius,\n            }),\n            setPosition: (x, z) => {\n              updateFountain2Position(x, z);\n            },\n          });\n        }\n        if (\n          window.gameConfig.driveThruBooth &&\n          !window.gameConfig.driveThruBooth.deleted &&\n          driveThruBoothMesh\n        ) {\n          editableObjects.push({\n            mesh: driveThruBoothMesh,\n            type: 'driveThruBooth',\n            configKey: 'driveThruBooth',\n            name: 'Drive-Thru Booth',\n            getData: () => ({\n              x: window.gameConfig.driveThruBooth.x,\n              z: window.gameConfig.driveThruBooth.z,\n              width: window.gameConfig.driveThruBooth.width,\n              depth: window.gameConfig.driveThruBooth.depth,\n            }),\n            setPosition: (x, z) => {\n              updateDriveThruBoothPosition(x, z);\n            },\n          });\n        }\n\n        // Register self-order kiosk\n// Register self-order kiosks\n        if (window.gameConfig.selfOrderKiosks) {\n          window.gameConfig.selfOrderKiosks.forEach((config, index) => {\n            const kiosk = selfOrderKiosks.find((k) => k.id === config.id);\n            if (config && !config.deleted && kiosk && kiosk.mesh) {\n              editableObjects.push({\n                mesh: kiosk.mesh,\n                type: 'selfOrderKiosk',\n                configKey: `selfOrderKiosks.${index}`,\n                kioskId: config.id,\n                name: config.icon ? `Kiosk ${config.icon}` : `Self-Order Kiosk ${index + 1}`,\n                getData: () => ({\n                  x: config.x,\n                  z: config.z,\n                }),\n                setPosition: (x, z) => {\n                  updateSelfOrderKioskPosition(config.id, x, z);\n                },\n              });\n            }\n          });\n        }\n\n        // Register dining table tiles\n        if (window.gameConfig.diningTableTiles) {\n          window.gameConfig.diningTableTiles.forEach((config, index) => {\n            if (!config.deleted) {\n              const tileEntry = diningTableTileMeshes.find((t) => t.index === index);\n              if (tileEntry) {\n                editableObjects.push({\n                  mesh: tileEntry.mesh,\n                  type: 'diningTableTile',\n                  configKey: `diningTableTiles.${index}`,\n                  tileIndex: index,\n                  name: `Dining Table Tile ${index + 1}`,\n                  getData: () => ({\n                    x: config.x,\n                    z: config.z,\n                    width: config.width,\n                    depth: config.depth,\n                  }),\n                  setPosition: (x, z) => {\n                    updateDiningTableTilePosition(x, z, index);\n                  },\n                });\n              }\n            }\n          });\n        }\n\n        // Register existing dining tables\n        diningTableMeshes.forEach((tableEntry, index) => {\n          editableObjects.push({\n            mesh: tableEntry.mesh,\n            type: 'diningTable',\n            configKey: `diningTables.${index}`,\n            tableIndex: index,\n            name: `Dining Table ${index + 1}`,\n            getData: () => ({\n              x: tableEntry.tableData.x,\n              z: tableEntry.tableData.z,\n            }),\n            setPosition: (x, z) => {\n              tableEntry.mesh.position.x = x;\n              tableEntry.mesh.position.z = z;\n              tableEntry.tableData.x = x;\n              tableEntry.tableData.z = z;\n              if (tableEntry.trashMesh) {\n                tableEntry.trashMesh.position.x = x;\n                tableEntry.trashMesh.position.z = z;\n              }\n              window.gameConfig.diningTables[index].x = x;\n              window.gameConfig.diningTables[index].z = z;\n            },\n          });\n        });\n\n        // Register grills\n        resourceNodes.forEach((node, index) => {\n          const grillConfig = window.gameConfig.grills[index];\n          if (grillConfig) {\n            editableObjects.push({\n              mesh: node.group,\n              type: 'grill',\n              configKey: `grills.${index}`,\n              grillIndex: index,\n              grillId: grillConfig.id,\n              name: `Grill ${index + 1}`,\n              getData: () => ({\n                x: node.position.x,\n                z: node.position.z,\n              }),\n              setPosition: (x, z) => {\n                node.group.position.x = x;\n                node.group.position.z = z;\n                node.position.x = x;\n                node.position.z = z;\n                // Update rotation to face center\n                node.group.rotation.y = Math.atan2(-x, -z);\n                // Update config\n                window.gameConfig.grills[index].x = x;\n                window.gameConfig.grills[index].z = z;\n              },\n            });\n          }\n        });\n\n        // Calculate next grill ID\n        nextGrillId = window.gameConfig.grills.length;\n      }\n\n      function updateMoneyZonePosition(x, z) {\n        const config = window.gameConfig.moneyZone;\n\n        moneyZoneMesh.position.x = x;\n        moneyZoneMesh.position.z = z;\n        moneyZoneGlow.position.x = x;\n        moneyZoneGlow.position.z = z;\n\n        // Move border\n        const border = moneyZoneMesh.userData.borderMesh;\n        if (border) {\n          border.position.x = x;\n          border.position.z = z;\n        }\n\n        // Move coin indicator\n        const sign = moneyZoneMesh.userData.signIndicator;\n        if (sign) {\n          sign.position.x = x;\n          sign.position.z = z;\n        }\n\n        window.gameConfig.moneyZone.x = x;\n        window.gameConfig.moneyZone.z = z;\n      }\n\n      function updateSpendZonePosition(x, z) {\n        const config = window.gameConfig.spendZone;\n\n        spendZoneMesh.position.x = x;\n        spendZoneMesh.position.z = z;\n        spendZoneGlow.position.x = x;\n        spendZoneGlow.position.z = z;\n\n        // Move border\n        const border = spendZoneMesh.userData.borderMesh;\n        if (border) {\n          border.position.x = x;\n          border.position.z = z;\n        }\n\n        // Move gem indicator\n        const gem = spendZoneMesh.userData.gemIndicator;\n        if (gem) {\n          gem.position.x = x;\n          gem.position.z = z;\n        }\n\n        // Move number indicator\n        const number = spendZoneMesh.userData.numberIndicator;\n        if (number) {\n          number.position.x = x;\n          number.position.z = z + config.depth / 2 - 0.5;\n        }\n\n        window.gameConfig.spendZone.x = x;\n        window.gameConfig.spendZone.z = z;\n      }\n\n      function updateTrashBinPosition(x, z) {\n        if (!trashBinMesh) return;\n\n        trashBinMesh.position.x = x;\n        trashBinMesh.position.z = z;\n\n        if (trashBinGlow) {\n          trashBinGlow.position.x = x;\n          trashBinGlow.position.z = z;\n        }\n\n        // Move trash icon\n        const trashIcon = trashBinMesh.userData.trashIcon;\n        if (trashIcon) {\n          trashIcon.position.x = x;\n          trashIcon.position.z = z;\n        }\n\n        window.gameConfig.trashBin.x = x;\n        window.gameConfig.trashBin.z = z;\n      }\n\n      function updateBankVaultPosition(x, z) {\n        const config = window.gameConfig.bankVault;\n        if (!config || !bankVaultMesh) return;\n\n        bankVaultMesh.position.x = x;\n        bankVaultMesh.position.z = z;\n\n        if (bankVaultGlow) {\n          bankVaultGlow.position.x = x;\n          bankVaultGlow.position.z = z + config.depth / 2 + 1.5;\n        }\n\n        // Move vault icon\n        const vaultIcon = bankVaultMesh.userData.vaultIcon;\n        if (vaultIcon) {\n          vaultIcon.position.x = x;\n          vaultIcon.position.z = z;\n        }\n\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateDriveThruBoothPosition(x, z) {\n        const config = window.gameConfig.driveThruBooth;\n        if (!config || !driveThruBoothMesh) return;\n\n        driveThruBoothMesh.position.x = x;\n        driveThruBoothMesh.position.z = z;\n\n        if (driveThruBoothGlow) {\n          const depositZoneX = RESTAURANT_WIDTH / 2 - 3;\n          driveThruBoothGlow.position.x = depositZoneX;\n          driveThruBoothGlow.position.z = z;\n        }\n\n        // Move inside counter\n        const insideCounter = driveThruBoothMesh.userData.insideCounter;\n        if (insideCounter) {\n          const depositZoneX = RESTAURANT_WIDTH / 2 - 3;\n          insideCounter.position.x = depositZoneX;\n          insideCounter.position.z = z;\n        }\n\n        // Move roof\n        const roof = driveThruBoothMesh.userData.roof;\n        if (roof) {\n          roof.position.x = x;\n          roof.position.z = z;\n        }\n\n        // Move window\n        const serviceWindow = driveThruBoothMesh.userData.window;\n        if (serviceWindow) {\n          serviceWindow.position.x = x;\n          serviceWindow.position.z = z + config.depth / 2 + 0.15;\n        }\n\n        // Move counter\n        const counter = driveThruBoothMesh.userData.counter;\n        if (counter) {\n          counter.position.x = x;\n          counter.position.z = z + config.depth / 2 + 0.5;\n        }\n\n        // Move sign\n        const sign = driveThruBoothMesh.userData.sign;\n        if (sign) {\n          sign.position.x = x;\n          sign.position.z = z;\n        }\n\n        // Move car icon\n        const carIcon = driveThruBoothMesh.userData.carIcon;\n        if (carIcon) {\n          carIcon.position.x = x;\n          carIcon.position.z = z + 0.15;\n        }\n\n        // Move speaker\n        const speaker = driveThruBoothMesh.userData.speaker;\n        if (speaker) {\n          speaker.position.x = x - config.width / 2 - 3;\n          speaker.position.z = z + 5;\n        }\n\n        // Move road\n        const road = driveThruBoothMesh.userData.road;\n        if (road) {\n          road.position.x = x;\n          road.position.z = 0;\n          // Road stays centered along Z\n        }\n\n        // Move cash pile (inside restaurant)\n        const newCashPileX = RESTAURANT_WIDTH / 2 - 6;\n        const newCashPileZ = z + 4;\n        if (driveThruCashPileMesh) {\n          driveThruCashPileMesh.position.x = newCashPileX;\n          driveThruCashPileMesh.position.z = newCashPileZ;\n        }\n        if (driveThruCashPileGlow) {\n          driveThruCashPileGlow.position.x = newCashPileX;\n          driveThruCashPileGlow.position.z = newCashPileZ;\n        }\n        const cashBorder = driveThruCashPileMesh?.userData?.borderMesh;\n        if (cashBorder) {\n          cashBorder.position.x = newCashPileX;\n          cashBorder.position.z = newCashPileZ;\n        }\n\n        // Update stored positions\n        driveThruBoothMesh.userData.cashPileX = newCashPileX;\n        driveThruBoothMesh.userData.cashPileZ = newCashPileZ;\n\n        window.gameConfig.driveThruBooth.x = x;\n        window.gameConfig.driveThruBooth.z = z;\n      }\n\n      function updateDriveThruTilePosition(x, z) {\n        const config = window.gameConfig.driveThruTile;\n        if (!config || !driveThruTileMesh) return;\n        driveThruTileMesh.position.x = x;\n        driveThruTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateTrashTilePosition(x, z) {\n        const config = window.gameConfig.trashTile;\n        if (!config || !trashTileMesh) return;\n        trashTileMesh.position.x = x;\n        trashTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function deleteTrashTile() {\n        if (window.gameConfig.trashTile) window.gameConfig.trashTile.deleted = true;\n        if (trashTileMesh) {\n          scene.remove(trashTileMesh);\n          ['borderMesh', 'icon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n            const obj = trashTileMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n        }\n        if (trashTileGlow) scene.remove(trashTileGlow);\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'trashTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n        trashTileMesh = null;\n        trashTileGlow = null;\n        deselectObject();\n      }\n\n      function duplicateTrashTile(editable) {\n        lib.log('Trash Tile can only have one instance');\n      }\n\n      function createTrashTile() {\n        const config = window.gameConfig.trashTile;\n        if (!config || config.deleted || config.unlocked) return;\n\n        const result = createBaseTile(config);\n        trashTileMesh = result.tileMesh;\n        trashTileGlow = result.glowMesh;\n\n        createTileCostDisplay(trashTileMesh, config);\n        updateTrashTileProgress();\n        updateTrashTileCostDisplay();\n      }\n\n      function updateTrashTileProgress() {\n        const config = window.gameConfig.trashTile;\n        if (!config || !trashTileMesh) return;\n        updateTileProgress(trashTileMesh, config);\n      }\n\n      function updateTrashTileCostDisplay() {\n        const config = window.gameConfig.trashTile;\n        if (!config || !trashTileMesh) return;\n        updateUnlockTileCostDisplay(trashTileMesh, config);\n      }\n\n      function createVaultTile() {\n        return;\n      }\n\n      function updateVaultTileProgress() {\n        return;\n      }\n\n      function updateVaultTileCostDisplay() {\n        return;\n      }\n\n      function updateVaultTileCostDisplay() {\n        return;\n      }\n\n      function destroyVaultTileVisuals() {\n        return;\n      }\n\n      function updateVaultTile(deltaTime) {\n        return;\n      }\n\n      function destroyTrashTileVisuals() {\n        destroyTileVisuals(trashTileMesh, trashTileGlow);\n        trashTileMesh = null;\n        trashTileGlow = null;\n      }\n\n      function updateTrashTile(deltaTime) {\n        if (gameMode === 'edit') return;\n        const config = window.gameConfig.trashTile;\n        if (!config || config.deleted || config.unlocked || !trashTileMesh) return;\n        const inZone = isPlayerInZone(config);\n        const moneyValue = window.gameConfig.settings.paperMoneyValue || 10;\n        const totalDeposited = config.moneyDeposited + (config.moneyInFlight || 0);\n        if (inZone && vaultBalance > 0 && totalDeposited < config.cost) {\n          trashTileDepositTimer += deltaTime;\n          if (trashTileDepositTimer >= STAFF_HIRE_DEPOSIT_DELAY) {\n            trashTileDepositTimer = 0;\n            const depositAmount = getDepositAmount(config.cost, moneyValue, totalDeposited);\n            if (depositAmount > 0) {\n              modifyVaultBalance(-depositAmount);\n              config.moneyInFlight = (config.moneyInFlight || 0) + depositAmount;\n              const startPos = {\n                x: player.position.x,\n                y: player.position.y + PLAYER_HEIGHT + playerStack.length * STACK_ITEM_HEIGHT,\n                z: player.position.z,\n              };\n              createFlyingItem(\n                startPos,\n                {\n                  x: config.x,\n                  y: 0.5,\n                  z: config.z,\n                },\n                'money',\n                () => {\n                  config.moneyInFlight = Math.max(0, (config.moneyInFlight || 0) - depositAmount);\n                  if (config.unlocked) return;\n                  config.moneyDeposited += depositAmount;\n                  window.gameConfig.trashTile.moneyDeposited = config.moneyDeposited;\n                  playSound('collect_sound');\n                  updateTrashTileProgress();\n                  updateTrashTileCostDisplay();\n                  if (config.moneyDeposited >= config.cost) {\n                    config.unlocked = true;\n                    window.gameConfig.trashTile.unlocked = true;\n                    playSound('upgrade_sound');\n                    createSmokeParticlesAt(config.x, 0.5, config.z);\n                    destroyTrashTileVisuals();\n                    createTrashBin();\n                    resetTileProgress(trashTileMesh);\n                  }\n                },\n                true\n              );\n            }\n          }\n        } else {\n          trashTileDepositTimer = 0;\n        }\n        if (trashTileGlow) {\n          const pulseScale = 1 + Math.sin(Date.now() * 0.004) * 0.05;\n          trashTileGlow.scale.set(pulseScale, 1, pulseScale);\n        }\n        const icon = trashTileMesh?.userData?.icon;\n        if (icon) {\n          icon.position.y = icon.userData.baseY + Math.sin(Date.now() * 0.003) * 0.1;\n        }\n      }\n\n      function updateCashierTilePosition(x, z) {\n        const config = window.gameConfig.cashierTile;\n        if (!config || !cashierTileMesh) return;\n\n        cashierTileMesh.position.x = x;\n        cashierTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateGrillTilePosition(x, z) {\n        const config = window.gameConfig.grillTile;\n        if (!config || !grillTileMesh) return;\n\n        grillTileMesh.position.x = x;\n        grillTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateVaultTilePosition(x, z) {\n        const config = window.gameConfig.vaultTile;\n        if (!config || !vaultTileMesh) return;\n        vaultTileMesh.position.x = x;\n        vaultTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function deleteVaultTile() {\n        if (window.gameConfig.vaultTile) window.gameConfig.vaultTile.deleted = true;\n        if (vaultTileMesh) {\n          scene.remove(vaultTileMesh);\n          ['borderMesh', 'icon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n            const obj = vaultTileMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n        }\n        if (vaultTileGlow) scene.remove(vaultTileGlow);\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'vaultTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n        vaultTileMesh = null;\n        vaultTileGlow = null;\n        deselectObject();\n      }\n\n      function duplicateVaultTile(editable) {\n        lib.log('Vault Tile can only have one instance');\n      }\n\n      function deleteFountainTile() {\n        if (window.gameConfig.fountainTile) window.gameConfig.fountainTile.deleted = true;\n        if (fountainTileMesh) {\n          scene.remove(fountainTileMesh);\n          ['borderMesh', 'icon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n            const obj = fountainTileMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n        }\n        if (fountainTileGlow) scene.remove(fountainTileGlow);\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'fountainTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n        fountainTileMesh = null;\n        fountainTileGlow = null;\n        deselectObject();\n      }\n\n      function duplicateFountainTile(editable) {\n        lib.log('Fountain Tile can only have one instance');\n      }\n\n      function deleteFountain2Tile() {\n        if (window.gameConfig.fountain2Tile) window.gameConfig.fountain2Tile.deleted = true;\n        if (fountain2TileMesh) {\n          scene.remove(fountain2TileMesh);\n          ['borderMesh', 'icon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n            const obj = fountain2TileMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n        }\n        if (fountain2TileGlow) scene.remove(fountain2TileGlow);\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'fountain2Tile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n        fountain2TileMesh = null;\n        fountain2TileGlow = null;\n        deselectObject();\n      }\n\n      function duplicateFountain2Tile(editable) {\n        lib.log('Fountain 2 Tile can only have one instance');\n      }\n\n      function deleteFountain() {\n        if (window.gameConfig.fountain) window.gameConfig.fountain.deleted = true;\n        if (fountainMesh) {\n          scene.remove(fountainMesh);\n        }\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'fountain');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n        fountainMesh = null;\n        deselectObject();\n      }\n\n      function duplicateFountain(editable) {\n        lib.log('Fountain can only have one instance');\n      }\n\n      function deleteFountain2() {\n        if (window.gameConfig.fountain2) window.gameConfig.fountain2.deleted = true;\n        if (fountainMesh2) {\n          scene.remove(fountainMesh2);\n        }\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'fountain2');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n        fountainMesh2 = null;\n        deselectObject();\n      }\n\n      function duplicateFountain2(editable) {\n        lib.log('Fountain 2 can only have one instance');\n      }\n\n      function deleteSelfOrderKioskTile() {\n        if (window.gameConfig.selfOrderKioskTile)\n          window.gameConfig.selfOrderKioskTile.deleted = true;\n        if (selfOrderKioskTileMesh) {\n          scene.remove(selfOrderKioskTileMesh);\n          ['borderMesh', 'icon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n            const obj = selfOrderKioskTileMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n        }\n        if (selfOrderKioskTileGlow) scene.remove(selfOrderKioskTileGlow);\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'selfOrderKioskTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n        selfOrderKioskTileMesh = null;\n        selfOrderKioskTileGlow = null;\n        deselectObject();\n      }\n\n      function deleteSelfOrderKiosk(editable) {\n        // Find kiosk ID from editable object or use selected\n        const kioskId = editable?.kioskId;\n\n        if (kioskId !== undefined) {\n          const kiosk = selfOrderKiosks.find((k) => k.id === kioskId);\n          if (kiosk) {\n            // Mark as deleted in config\n            if (kiosk.config) kiosk.config.deleted = true;\n\n            // Remove mesh\n            if (kiosk.mesh) {\n              scene.remove(kiosk.mesh);\n              kiosk.mesh.traverse((child) => {\n                if (child.geometry) child.geometry.dispose();\n                if (child.material) {\n                  if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n                  else child.material.dispose();\n                }\n              });\n            }\n            if (kiosk.glow) scene.remove(kiosk.glow);\n\n            // Clear queue\n            kiosk.queue.forEach((c) => scene.remove(c.mesh));\n            kiosk.queue = [];\n            kiosk.leavers.forEach((c) => scene.remove(c.mesh));\n            kiosk.leavers = [];\n\n            kiosk.mesh = null;\n            kiosk.glow = null;\n          }\n        }\n\n        // Remove from editable objects\n        if (editable) {\n          const editIndex = editableObjects.findIndex((obj) => obj.configKey === editable.configKey);\n          if (editIndex > -1) editableObjects.splice(editIndex, 1);\n        }\n        deselectObject();\n      }\n\n      function duplicateSelfOrderKiosk(editable) {\n        lib.log('Kiosks cannot be duplicated');\n      }\n\n      function duplicateSelfOrderKioskTile(editable) {\n        lib.log('Self-Order Kiosk Tile can only have one instance');\n      }\n\n      function duplicateSelfOrderKioskTiles(editable) {\n        lib.log('Self-Order Kiosk Tiles cannot be duplicated');\n      }\n\n      function updateCashierStallPosition(x, z) {\n        const config = window.gameConfig.cashierStall;\n        cashierStallMesh.position.x = x;\n        cashierStallMesh.position.z = z;\n\n        // Move glow\n        cashierStallGlow.position.x = x;\n        cashierStallGlow.position.z = z - config.depth / 2 - 1;\n\n        // Move counter top\n        const counterTop = cashierStallMesh.userData.counterTop;\n        if (counterTop) {\n          counterTop.position.x = x;\n          counterTop.position.z = z;\n        }\n\n        // Move register\n        const register = cashierStallMesh.userData.register;\n        if (register) {\n          register.position.x = x + config.width / 2 - 0.8;\n          register.position.z = z;\n        }\n\n        // Move tray\n        const tray = cashierStallMesh.userData.tray;\n        if (tray) {\n          tray.position.x = x - config.width / 2 + 1.5;\n          tray.position.z = z;\n        }\n\n        // Move menu board\n        const menuBoard = cashierStallMesh.userData.menuBoard;\n        if (menuBoard) {\n          menuBoard.position.x = x;\n          menuBoard.position.z = z + config.depth / 2 + 0.5;\n        }\n\n        // Move menu burger\n        const menuBurger = cashierStallMesh.userData.menuBurger;\n        if (menuBurger) {\n          menuBurger.position.x = x;\n          menuBurger.position.z = z + config.depth / 2 + 0.65;\n        }\n\n        window.gameConfig.cashierStall.x = x;\n        window.gameConfig.cashierStall.z = z;\n      }\n\n      function updateStaffHireTilePosition(x, z) {\n        const config = window.gameConfig.staffHireTile;\n        if (!config || !staffHireTileMesh) return;\n        staffHireTileMesh.position.x = x;\n        staffHireTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateCleanerStaffTilePosition(x, z) {\n        const config = window.gameConfig.cleanerStaffTile;\n        if (!config || !cleanerStaffTileMesh) return;\n        cleanerStaffTileMesh.position.x = x;\n        cleanerStaffTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateRunnerStaffTilePosition(x, z) {\n        const config = window.gameConfig.runnerStaffTile;\n        if (!config || !runnerStaffTileMesh) return;\n        runnerStaffTileMesh.position.x = x;\n        runnerStaffTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateVaultStaffTilePosition(x, z) {\n        const config = window.gameConfig.vaultStaffTile;\n        if (!config || !vaultStaffTileMesh) return;\n        vaultStaffTileMesh.position.x = x;\n        vaultStaffTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function updateDiningTableTilePosition(x, z, index) {\n        const tileEntry = diningTableTileMeshes.find((t) => t.index === index);\n        if (!tileEntry) return;\n        const config = window.gameConfig.diningTableTiles[index];\n        const diningTableTileMesh = tileEntry.mesh;\n\n        diningTableTileMesh.position.x = x;\n        diningTableTileMesh.position.z = z;\n        config.x = x;\n        config.z = z;\n      }\n\n      function setupEditorInput() {\n        const canvas = document.getElementById('game-canvas');\n\n        // Mouse events\n        canvas.addEventListener('mousedown', onEditorPointerDown);\n        canvas.addEventListener('mousemove', onEditorPointerMove);\n        canvas.addEventListener('mouseup', onEditorPointerUp);\n\n        // Right-click context menu prevention (for camera pan)\n        canvas.addEventListener('contextmenu', (e) => {\n          if (gameMode === 'edit') {\n            e.preventDefault();\n          }\n        });\n\n        // Touch events\n        canvas.addEventListener('touchstart', onEditorTouchStart, {\n          passive: false,\n        });\n        canvas.addEventListener('touchmove', onEditorTouchMove, {\n          passive: false,\n        });\n        canvas.addEventListener('touchend', onEditorTouchEnd);\n      }\n\n      function getPointerPosition(event) {\n        const canvas = document.getElementById('game-canvas');\n        const rect = canvas.getBoundingClientRect();\n\n        let clientX, clientY;\n        if (event.touches) {\n          clientX = event.touches[0].clientX;\n          clientY = event.touches[0].clientY;\n        } else {\n          clientX = event.clientX;\n          clientY = event.clientY;\n        }\n\n        return {\n          x: ((clientX - rect.left) / rect.width) * 2 - 1,\n          y: -((clientY - rect.top) / rect.height) * 2 + 1,\n        };\n      }\n\n      function getWorldPosition(pointerPos) {\n        raycaster.setFromCamera(new THREE.Vector2(pointerPos.x, pointerPos.y), camera);\n\n        // Create a plane at y=0 to intersect with\n        const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);\n        const intersection = new THREE.Vector3();\n        raycaster.ray.intersectPlane(plane, intersection);\n\n        return intersection;\n      }\n\n      function onEditorPointerDown(event) {\n        if (gameMode !== 'edit') return;\n\n        // Right-click starts camera panning\n        if (event.button === 2) {\n          editorIsPanning = true;\n          editorPanStart = {\n            x: event.clientX,\n            y: event.clientY,\n          };\n          editorCameraStart = {\n            x: editorCameraOffset.x,\n            z: editorCameraOffset.z,\n          };\n          return;\n        }\n\n        // Left-click for object selection/dragging\n        if (event.button !== 0) return;\n\n        const pointerPos = getPointerPosition(event);\n        raycaster.setFromCamera(new THREE.Vector2(pointerPos.x, pointerPos.y), camera);\n\n        // Get all meshes from editable objects\n        const meshes = editableObjects.map((obj) => obj.mesh);\n        const intersects = raycaster.intersectObjects(meshes, true);\n\n        if (intersects.length > 0) {\n          // Find which editable object was clicked\n          let clickedObject = intersects[0].object;\n\n          // Traverse up to find the root editable object\n          while (\n            clickedObject.parent &&\n            !editableObjects.find((obj) => obj.mesh === clickedObject)\n          ) {\n            clickedObject = clickedObject.parent;\n          }\n\n          const editable = editableObjects.find((obj) => obj.mesh === clickedObject);\n          if (editable) {\n            selectObject(editable);\n\n            // Start dragging\n            editorIsDragging = true;\n            const worldPos = getWorldPosition(pointerPos);\n            editorDragStart = {\n              x: worldPos.x,\n              z: worldPos.z,\n            };\n            editorObjectStart = editable.getData();\n            // If rotation buttons exist, ensure they are visible for this selection\n            const rotLbtn = document.getElementById('rotate-left-btn');\n            const rotRbtn = document.getElementById('rotate-right-btn');\n            if (rotLbtn && rotRbtn && editable.type !== 'player') {\n              rotLbtn.classList.remove('hidden');\n              rotRbtn.classList.remove('hidden');\n            }\n          }\n        } else {\n          // Clicked on empty space - deselect\n          deselectObject();\n        }\n      }\n\n      function onEditorPointerMove(event) {\n        if (gameMode !== 'edit') return;\n\n        // Handle camera panning with right-drag\n        if (editorIsPanning) {\n          const dx = event.clientX - editorPanStart.x;\n          const dy = event.clientY - editorPanStart.y;\n\n          // Convert screen delta to world delta (scale based on camera distance)\n          const panSpeed = 0.05;\n          editorCameraOffset.x = editorCameraStart.x - dx * panSpeed;\n          editorCameraOffset.z = editorCameraStart.z - dy * panSpeed;\n\n          // Clamp camera offset to rectangular restaurant bounds\n          const maxOffsetX = RESTAURANT_WIDTH / 2;\n          const maxOffsetZ = RESTAURANT_DEPTH / 2;\n          editorCameraOffset.x = Math.max(-maxOffsetX, Math.min(maxOffsetX, editorCameraOffset.x));\n          editorCameraOffset.z = Math.max(-maxOffsetZ, Math.min(maxOffsetZ, editorCameraOffset.z));\n          return;\n        }\n\n        // Handle object dragging\n        if (!editorIsDragging || !editorSelectedObject) return;\n\n        const pointerPos = getPointerPosition(event);\n        const worldPos = getWorldPosition(pointerPos);\n\n        // Calculate new position\n        const dx = worldPos.x - editorDragStart.x;\n        const dz = worldPos.z - editorDragStart.z;\n\n        let newX = editorObjectStart.x + dx;\n        let newZ = editorObjectStart.z + dz;\n\n        // Apply grid snapping (1 unit grid)\n        const gridSize = 1;\n        newX = Math.round(newX / gridSize) * gridSize;\n        newZ = Math.round(newZ / gridSize) * gridSize;\n\n        // Clamp to rectangular restaurant bounds\n        const halfWidth = RESTAURANT_WIDTH / 2 - 2;\n        const halfDepth = RESTAURANT_DEPTH / 2 - 2;\n        newX = Math.max(-halfWidth, Math.min(halfWidth, newX));\n        newZ = Math.max(-halfDepth, Math.min(halfDepth, newZ));\n\n        // Update position\n        editorSelectedObject.setPosition(newX, newZ);\n        updateSelectionHighlight();\n      }\n\n      function onEditorPointerUp(event) {\n        editorIsDragging = false;\n        editorIsPanning = false;\n      }\n\n      function onEditorTouchStart(event) {\n        if (gameMode !== 'edit') return;\n        event.preventDefault();\n        onEditorPointerDown(event);\n      }\n\n      function onEditorTouchMove(event) {\n        if (gameMode !== 'edit') return;\n        event.preventDefault();\n        onEditorPointerMove(event);\n      }\n\n      function onEditorTouchEnd(event) {\n        onEditorPointerUp(event);\n      }\n\n      function selectObject(editable) {\n        editorSelectedObject = editable;\n        updateSelectionHighlight();\n        showSelectionInfo(editable);\n        openEditMenu(editable);\n      }\n\n      function deselectObject() {\n        editorSelectedObject = null;\n        if (selectionHighlight) {\n          selectionHighlight.visible = false;\n        }\n        hideSelectionInfo();\n        lib.editMenu.close();\n      }\n\n      function formatTimeAway(seconds) {\n        const hours = Math.floor(seconds / 3600);\n        const minutes = Math.floor((seconds % 3600) / 60);\n\n        if (hours > 0 && minutes > 0) {\n          return `${hours} hour${hours > 1 ? 's' : ''} ${minutes} minute${minutes > 1 ? 's' : ''}`;\n        } else if (hours > 0) {\n          return `${hours} hour${hours > 1 ? 's' : ''}`;\n        } else if (minutes > 0) {\n          return `${minutes} minute${minutes > 1 ? 's' : ''}`;\n        } else {\n          return 'a few moments';\n        }\n      }\n\n      function showOfflineEarningsPopup(timeAway, earnings) {\n        const notification = document.getElementById('offline-earnings-notification');\n        const timeAwayEl = document.getElementById('offline-time-away');\n        const earningsAmountEl = document.getElementById('offline-earnings-amount');\n        const collectBtn = document.getElementById('collect-offline-btn');\n\n        if (!notification || !timeAwayEl || !earningsAmountEl || !collectBtn) return;\n\n        const awayText = t('offline.timeAway', { time: formatTimeAway(timeAway) });\n        const earningsText = t('offline.earnings', { amount: formatCurrencyAbbrev(earnings) });\n\n        // Format and set content - earnings go to vault\n        timeAwayEl.textContent = awayText;\n        earningsAmountEl.textContent = earningsText;\n\n        // Show notification\n        notification.classList.add('show');\n        playSound('sell_sound');\n\n        // Setup collect button (just closes popup, money already in vault)\n        const handleCollect = () => {\n          notification.classList.remove('show');\n          playSound('upgrade_sound');\n          updateVaultUI();\n          collectBtn.removeEventListener('click', handleCollect);\n        };\n        collectBtn.addEventListener('click', handleCollect);\n      }\n\n      function calculateAndAwardOfflineEarnings(savedState) {\n        const now = Date.now();\n        const lastPlayed = savedState.lastPlayedTime;\n\n        // Calculate elapsed time in seconds\n        let elapsedSeconds = (now - lastPlayed) / 1000;\n\n        // Cap at 8 hours (28800 seconds)\n        const maxOfflineTime = 8 * 60 * 60;\n        elapsedSeconds = Math.min(elapsedSeconds, maxOfflineTime);\n\n        // Only award if away for more than 5 minutes\n        if (elapsedSeconds < 300) return;\n\n        // Check if any staff is hired\n        const hasCashierStaff = savedState.staffHireTile && savedState.staffHireTile.staffHired;\n        const hasCleanerStaff =\n          savedState.cleanerStaffTile && savedState.cleanerStaffTile.staffHired;\n        const hasRunnerStaff = savedState.runnerStaffTile && savedState.runnerStaffTile.staffHired;\n\n        if (!hasCashierStaff && !hasCleanerStaff && !hasRunnerStaff) {\n          return;\n          // No staff, no offline earnings\n        }\n\n        // Calculate earnings\n        let earningsPerSecond = 0;\n\n        // Base rate when cashier is hired: ~$17/second (50% of normal $33/second rate)\n        // This is based on: 1 customer every 3 seconds, ~$100 per burger, with 50% offline efficiency\n        if (hasCashierStaff) {\n          const burgerPrice = window.gameConfig.resources.itemValue || 100;\n          const offlineEfficiency = 0.5;\n          // 50% of normal rate\n          const customersPerSecond = 1 / 3;\n          // 1 customer every 3 seconds\n          earningsPerSecond += burgerPrice * customersPerSecond * offlineEfficiency;\n        }\n\n        // Bonus for having runner (helps collect burgers faster)\n        if (hasRunnerStaff) {\n          earningsPerSecond *= 1.2;\n          // 20% boost\n        }\n\n        // Bonus for having cleaner (keeps tables clean for more customers)\n        if (hasCleanerStaff) {\n          earningsPerSecond *= 1.1;\n          // 10% boost\n        }\n\n        // Apply vault offline earnings boost (25% per level)\n        const vaultUpgrades = savedState.vaultUpgrades || window.gameConfig.vaultUpgrades || {};\n        const offlineEarningsBoost = 1 + (vaultUpgrades.earnings || 0) * 0.25;\n\n        // Apply prestige multiplier\n        const prestige = savedState.prestige ||\n          window.gameConfig.prestige || {\n            level: 0,\n            profitBonus: 0.1,\n          };\n        const prestigeBoost = 1 + prestige.level * (prestige.profitBonus || 0.1);\n\n        earningsPerSecond *= offlineEarningsBoost * prestigeBoost;\n\n        const totalEarnings = earningsPerSecond * elapsedSeconds;\n\n        // Award earnings TO VAULT instead of currency\n        if (totalEarnings > 0) {\n          // Add to vault balance\n          modifyVaultBalance(totalEarnings);\n\n          // Show popup\n          showOfflineEarningsPopup(elapsedSeconds, totalEarnings);\n        }\n      }\n\n      function updateSelectionHighlight() {\n        if (!editorSelectedObject || !selectionHighlight) return;\n\n        const pos = editorSelectedObject.getData();\n        selectionHighlight.position.x = pos.x;\n        selectionHighlight.position.z = pos.z;\n        selectionHighlight.visible = true;\n\n        // Adjust size based on object type\n        let size = 2;\n        if (editorSelectedObject.type === 'sellZone') {\n          size = window.gameConfig.sellZone.radius + 1;\n        } else if (editorSelectedObject.type === 'moneyZone') {\n          size =\n            Math.max(window.gameConfig.moneyZone.width, window.gameConfig.moneyZone.depth) / 2 + 1;\n        } else if (editorSelectedObject.type === 'spendZone') {\n          size =\n            Math.max(window.gameConfig.spendZone.width, window.gameConfig.spendZone.depth) / 2 + 1;\n        } else if (editorSelectedObject.type === 'cashierStall') {\n          size =\n            Math.max(window.gameConfig.cashierStall.width, window.gameConfig.cashierStall.depth) /\n              2 +\n            1;\n        } else if (editorSelectedObject.type === 'staffHireTile') {\n          size =\n            Math.max(window.gameConfig.staffHireTile.width, window.gameConfig.staffHireTile.depth) /\n              2 +\n            1;\n        } else if (\n          editorSelectedObject.type === 'cleanerStaffTile' &&\n          window.gameConfig.cleanerStaffTile\n        ) {\n          size =\n            Math.max(\n              window.gameConfig.cleanerStaffTile.width,\n              window.gameConfig.cleanerStaffTile.depth\n            ) /\n              2 +\n            1;\n        } else if (\n          editorSelectedObject.type === 'runnerStaffTile' &&\n          window.gameConfig.runnerStaffTile\n        ) {\n          size =\n            Math.max(\n              window.gameConfig.runnerStaffTile.width,\n              window.gameConfig.runnerStaffTile.depth\n            ) /\n              2 +\n            1;\n        } else if (editorSelectedObject.type === 'trashBin') {\n          size = window.gameConfig.trashBin.radius + 0.5;\n        } else if (\n          editorSelectedObject.type === 'driveThruBooth' &&\n          window.gameConfig.driveThruBooth\n        ) {\n          size =\n            Math.max(\n              window.gameConfig.driveThruBooth.width,\n              window.gameConfig.driveThruBooth.depth\n            ) /\n              2 +\n            2;\n        } else if (editorSelectedObject.type === 'diningTableTile') {\n          size =\n            Math.max(\n              window.gameConfig.diningTableTile.width,\n              window.gameConfig.diningTableTile.depth\n            ) /\n              2 +\n            1;\n        } else if (editorSelectedObject.type === 'diningTable') {\n          size = 2.5;\n        } else if (editorSelectedObject.type === 'bankVault' && window.gameConfig.bankVault) {\n          size =\n            Math.max(window.gameConfig.bankVault.width, window.gameConfig.bankVault.depth) / 2 +\n            1.5;\n        } else if (editorSelectedObject.type === 'fountainTile' && window.gameConfig.fountainTile) {\n          size =\n            Math.max(window.gameConfig.fountainTile.width, window.gameConfig.fountainTile.depth) /\n              2 +\n            1;\n        } else if (\n          editorSelectedObject.type === 'fountain2Tile' &&\n          window.gameConfig.fountain2Tile\n        ) {\n          size =\n            Math.max(window.gameConfig.fountain2Tile.width, window.gameConfig.fountain2Tile.depth) /\n              2 +\n            1;\n        } else if (editorSelectedObject.type === 'fountain' && window.gameConfig.fountain) {\n          size = window.gameConfig.fountain.radius + 1.5;\n        } else if (editorSelectedObject.type === 'fountain2' && window.gameConfig.fountain2) {\n          size = window.gameConfig.fountain2.radius + 1.5;\n        } else if (\n          editorSelectedObject.type === 'selfOrderKioskTile' &&\n          window.gameConfig.selfOrderKioskTile\n        ) {\n          size =\n            Math.max(\n              window.gameConfig.selfOrderKioskTile.width,\n              window.gameConfig.selfOrderKioskTile.depth\n            ) /\n              2 +\n            1;\n        } else if (editorSelectedObject.type === 'grill') {\n          size = 2;\n        } else if (editorSelectedObject.type === 'player') {\n          size = 1.5;\n        }\n\n        selectionHighlight.scale.setScalar(size / 1.5);\n      }\n\n      function updateEditorSelection(deltaTime) {\n        if (!selectionHighlight || !selectionHighlight.visible) return;\n\n        // Pulse animation\n        const pulse = 1 + Math.sin(Date.now() * 0.005) * 0.1;\n        const baseScale = selectionHighlight.scale.x;\n        selectionHighlight.material.opacity = 0.5 + Math.sin(Date.now() * 0.003) * 0.3;\n      }\n\n      function showSelectionInfo(editable) {\n        const infoEl = document.getElementById('selection-info');\n        const nameEl = document.getElementById('selection-name');\n        const deleteBtn = document.getElementById('delete-selected-btn');\n        const copyBtn = document.getElementById('copy-selected-btn');\n        const rotL = document.getElementById('rotate-left-btn');\n        const rotR = document.getElementById('rotate-right-btn');\n\n        nameEl.textContent = editable.name;\n        infoEl.classList.remove('hidden');\n\n        // Show delete/copy buttons for all deletable objects (everything except player)\n        if (editable.type !== 'player') {\n          deleteBtn.classList.remove('hidden');\n          copyBtn.classList.remove('hidden');\n        } else {\n          deleteBtn.classList.add('hidden');\n          copyBtn.classList.add('hidden');\n        }\n\n        // Show rotate controls for editable objects (not the player)\n        if (rotL && rotR) {\n          if (editable.type !== 'player') {\n            rotL.classList.remove('hidden');\n            rotR.classList.remove('hidden');\n          } else {\n            rotL.classList.add('hidden');\n            rotR.classList.add('hidden');\n          }\n        }\n      }\n\n      function hideSelectionInfo() {\n        const infoEl = document.getElementById('selection-info');\n        const deleteBtn = document.getElementById('delete-selected-btn');\n        const copyBtn = document.getElementById('copy-selected-btn');\n        const rotL = document.getElementById('rotate-left-btn');\n        const rotR = document.getElementById('rotate-right-btn');\n\n        infoEl.classList.add('hidden');\n        deleteBtn.classList.add('hidden');\n        copyBtn.classList.add('hidden');\n        if (rotL) rotL.classList.add('hidden');\n        if (rotR) rotR.classList.add('hidden');\n      }\n\nfunction openEditMenu(editable) {\n        const params = {};\n\n        if (editable.type === 'selfOrderKiosk') {\n          const kiosk = selfOrderKiosks.find(k => k.id === editable.kioskId);\n          const config = kiosk?.config;\n          params['Color'] = {\n            key: `gameConfig.selfOrderKiosks.${editable.kioskId}.color`,\n            type: 'color',\n            onChange: (value) => {\n              if (config) config.color = value;\n              if (kiosk?.glow) kiosk.glow.material.color.set(value);\n            },\n          };\n        } else if (editable.type === 'player') {\n          params['Apron Color'] = {\n            key: 'gameConfig.player.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.player.color = value;\n              // Update apron mesh\n              if (player.children[1]) {\n                player.children[1].material.color.set(value);\n              }\n            },\n          };\n          params['Base Speed'] = {\n            key: 'gameConfig.player.baseSpeed',\n            type: 'slider',\n            min: 5,\n            max: 20,\n            step: 1,\n          };\n          params['Base Capacity'] = {\n            key: 'gameConfig.player.baseCapacity',\n            type: 'slider',\n            min: 5,\n            max: 50,\n            step: 5,\n            onChange: (value) => {\n              window.gameConfig.player.baseCapacity = value;\n              updateStackUI();\n            },\n          };\n        } else if (editable.type === 'sellZone') {\n          params['Radius'] = {\n            key: 'gameConfig.sellZone.radius',\n            type: 'slider',\n            min: 3,\n            max: 12,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.sellZone.radius = value;\n              rebuildSellZone();\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.sellZone.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.sellZone.color = value;\n              sellZoneGlow.material.color.set(value);\n            },\n          };\n        } else if (editable.type === 'moneyZone') {\n          params['Width'] = {\n            key: 'gameConfig.moneyZone.width',\n            type: 'slider',\n            min: 4,\n            max: 16,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.moneyZone.width = value;\n              rebuildMoneyZone();\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.moneyZone.depth',\n            type: 'slider',\n            min: 4,\n            max: 16,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.moneyZone.depth = value;\n              rebuildMoneyZone();\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.moneyZone.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.moneyZone.color = value;\n              moneyZoneGlow.material.color.set(value);\n            },\n          };\n        } else if (editable.type === 'spendZone') {\n          params['Width'] = {\n            key: 'gameConfig.spendZone.width',\n            type: 'slider',\n            min: 4,\n            max: 16,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.spendZone.width = value;\n              rebuildSpendZone();\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.spendZone.depth',\n            type: 'slider',\n            min: 4,\n            max: 16,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.spendZone.depth = value;\n              rebuildSpendZone();\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.spendZone.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.spendZone.color = value;\n              spendZoneGlow.material.color.set(value);\n            },\n          };\n        } else if (editable.type === 'grill') {\n          params['Cook Time'] = {\n            key: 'gameConfig.resources.regenerationTime',\n            type: 'slider',\n            min: 1,\n            max: 10,\n            step: 0.5,\n          };\n          params['Item Value'] = {\n            key: 'gameConfig.resources.itemValue',\n            type: 'slider',\n            min: 5,\n            max: 50,\n            step: 5,\n          };\n        } else if (editable.type === 'cashierTile') {\n          params['Unlock Cost'] = {\n            key: 'gameConfig.cashierTile.cost',\n            type: 'slider',\n            min: 50,\n            max: 1000,\n            step: 50,\n            onChange: (value) => {\n              window.gameConfig.cashierTile.cost = value;\n              updateCashierTileProgress();\n            },\n          };\n          params['Width'] = {\n            key: 'gameConfig.cashierTile.width',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.cashierTile.width = value;\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.cashierTile.depth',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.cashierTile.depth = value;\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.cashierTile.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.cashierTile.color = value;\n              if (cashierTileGlow) {\n                cashierTileGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'trashTile') {\n          params['Unlock Cost'] = {\n            key: 'gameConfig.trashTile.cost',\n            type: 'slider',\n            min: 50,\n            max: 2000,\n            step: 50,\n            onChange: (value) => {\n              window.gameConfig.trashTile.cost = value;\n              updateTrashTileProgress();\n              updateTrashTileCostDisplay();\n            },\n          };\n          params['Width'] = {\n            key: 'gameConfig.trashTile.width',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.trashTile.width = value;\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.trashTile.depth',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.trashTile.depth = value;\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.trashTile.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.trashTile.color = value;\n              if (trashTileGlow) {\n                trashTileGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'vaultTile') {\n          params['Unlock Cost'] = {\n            key: 'gameConfig.vaultTile.cost',\n            type: 'slider',\n            min: 500,\n            max: 5000,\n            step: 100,\n            onChange: (value) => {\n              window.gameConfig.vaultTile.cost = value;\n              updateVaultTileProgress();\n              updateVaultTileCostDisplay();\n            },\n          };\n          params['Width'] = {\n            key: 'gameConfig.vaultTile.width',\n            type: 'slider',\n            min: 3,\n            max: 10,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.vaultTile.width = value;\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.vaultTile.depth',\n            type: 'slider',\n            min: 3,\n            max: 10,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.vaultTile.depth = value;\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.vaultTile.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.vaultTile.color = value;\n              if (vaultTileGlow) {\n                vaultTileGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'fountainTile') {\n          params['Unlock Cost'] = {\n            key: 'gameConfig.fountainTile.cost',\n            type: 'slider',\n            min: 50,\n            max: 5000,\n            step: 50,\n            onChange: (value) => {\n              window.gameConfig.fountainTile.cost = value;\n              updateTileProgress(fountainTileMesh, window.gameConfig.fountainTile);\n              updateUnlockTileCostDisplay(fountainTileMesh, window.gameConfig.fountainTile);\n            },\n          };\n          params['Width'] = {\n            key: 'gameConfig.fountainTile.width',\n            type: 'slider',\n            min: 3,\n            max: 10,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.fountainTile.width = value;\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.fountainTile.depth',\n            type: 'slider',\n            min: 3,\n            max: 10,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.fountainTile.depth = value;\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.fountainTile.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.fountainTile.color = value;\n              if (fountainTileGlow) {\n                fountainTileGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'fountain2Tile') {\n          params['Unlock Cost'] = {\n            key: 'gameConfig.fountain2Tile.cost',\n            type: 'slider',\n            min: 50,\n            max: 5000,\n            step: 50,\n            onChange: (value) => {\n              window.gameConfig.fountain2Tile.cost = value;\n              updateTileProgress(fountain2TileMesh, window.gameConfig.fountain2Tile);\n              updateUnlockTileCostDisplay(fountain2TileMesh, window.gameConfig.fountain2Tile);\n            },\n          };\n          params['Width'] = {\n            key: 'gameConfig.fountain2Tile.width',\n            type: 'slider',\n            min: 3,\n            max: 10,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.fountain2Tile.width = value;\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.fountain2Tile.depth',\n            type: 'slider',\n            min: 3,\n            max: 10,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.fountain2Tile.depth = value;\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.fountain2Tile.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.fountain2Tile.color = value;\n              if (fountain2TileGlow) {\n                fountain2TileGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'selfOrderKioskTile') {\n          params['Unlock Cost'] = {\n            key: 'gameConfig.selfOrderKioskTile.cost',\n            type: 'slider',\n            min: 50,\n            max: 5000,\n            step: 50,\n            onChange: (value) => {\n              window.gameConfig.selfOrderKioskTile.cost = value;\n              updateTileProgress(selfOrderKioskTileMesh, window.gameConfig.selfOrderKioskTile);\n              updateUnlockTileCostDisplay(\n                selfOrderKioskTileMesh,\n                window.gameConfig.selfOrderKioskTile\n              );\n            },\n          };\n          params['Width'] = {\n            key: 'gameConfig.selfOrderKioskTile.width',\n            type: 'slider',\n            min: 3,\n            max: 10,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.selfOrderKioskTile.width = value;\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.selfOrderKioskTile.depth',\n            type: 'slider',\n            min: 3,\n            max: 10,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.selfOrderKioskTile.depth = value;\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.selfOrderKioskTile.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.selfOrderKioskTile.color = value;\n              if (selfOrderKioskTileGlow) {\n                selfOrderKioskTileGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'fountain') {\n          params['Radius'] = {\n            key: 'gameConfig.fountain.radius',\n            type: 'slider',\n            min: 1,\n            max: 5,\n            step: 0.5,\n            onChange: (value) => {\n              window.gameConfig.fountain.radius = value;\n              if (fountainMesh) {\n                scene.remove(fountainMesh);\n              }\n              createFountain();\n              const fountainEditable = editableObjects.find((obj) => obj.type === 'fountain');\n              if (fountainEditable) {\n                fountainEditable.mesh = fountainMesh;\n              }\n              updateSelectionHighlight();\n            },\n          };\n        } else if (editable.type === 'fountain2') {\n          params['Radius'] = {\n            key: 'gameConfig.fountain2.radius',\n            type: 'slider',\n            min: 1,\n            max: 5,\n            step: 0.5,\n            onChange: (value) => {\n              window.gameConfig.fountain2.radius = value;\n              if (fountainMesh2) {\n                scene.remove(fountainMesh2);\n              }\n              createFountain2();\n              const fountainEditable = editableObjects.find((obj) => obj.type === 'fountain2');\n              if (fountainEditable) {\n                fountainEditable.mesh = fountainMesh2;\n              }\n              updateSelectionHighlight();\n            },\n          };\n        } else if (editable.type === 'driveThruTile') {\n          params['Unlock Cost'] = {\n            key: 'gameConfig.driveThruTile.cost',\n            type: 'slider',\n            min: 50,\n            max: 2000,\n            step: 50,\n            onChange: (value) => {\n              window.gameConfig.driveThruTile.cost = value;\n              updateDriveThruTileProgress();\n            },\n          };\n          params['Width'] = {\n            key: 'gameConfig.driveThruTile.width',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.driveThruTile.width = value;\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.driveThruTile.depth',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.driveThruTile.depth = value;\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.driveThruTile.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.driveThruTile.color = value;\n              if (driveThruTileGlow) {\n                driveThruTileGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'cashierStall') {\n          params['Max Burgers'] = {\n            key: 'gameConfig.cashierStall.maxBurgers',\n            type: 'slider',\n            min: 10,\n            max: 50,\n            step: 5,\n          };\n          params['Max Money'] = {\n            key: 'gameConfig.cashierStall.maxMoney',\n            type: 'slider',\n            min: 10,\n            max: 200,\n            step: 10,\n          };\n          params['Serve Time'] = {\n            key: 'gameConfig.cashierStall.serveTime',\n            type: 'slider',\n            min: 0.3,\n            max: 2,\n            step: 0.1,\n          };\n          params['Customer Spawn Rate'] = {\n            key: 'gameConfig.cashierStall.customerSpawnRate',\n            type: 'slider',\n            min: 1,\n            max: 5,\n            step: 0.5,\n          };\n          params['Max Customers'] = {\n            key: 'gameConfig.cashierStall.maxCustomers',\n            type: 'slider',\n            min: 3,\n            max: 10,\n            step: 1,\n          };\n        } else if (editable.type === 'driveThruBooth') {\n          params['Max Burgers'] = {\n            key: 'gameConfig.driveThruBooth.maxBurgers',\n            type: 'slider',\n            min: 5,\n            max: 30,\n            step: 5,\n          };\n          params['Max Money'] = {\n            key: 'gameConfig.driveThruBooth.maxMoney',\n            type: 'slider',\n            min: 10,\n            max: 200,\n            step: 10,\n          };\n          params['Serve Time'] = {\n            key: 'gameConfig.driveThruBooth.serveTime',\n            type: 'slider',\n            min: 0.5,\n            max: 3,\n            step: 0.1,\n          };\n          params['Car Spawn Rate'] = {\n            key: 'gameConfig.driveThruBooth.carSpawnRate',\n            type: 'slider',\n            min: 2,\n            max: 8,\n            step: 0.5,\n          };\n          params['Max Cars'] = {\n            key: 'gameConfig.driveThruBooth.maxCars',\n            type: 'slider',\n            min: 2,\n            max: 8,\n            step: 1,\n          };\n        } else if (editable.type === 'staffHireTile') {\n          params['Hire Cost'] = {\n            key: 'gameConfig.staffHireTile.hireCost',\n            type: 'slider',\n            min: 100,\n            max: 2000,\n            step: 100,\n            onChange: (value) => {\n              window.gameConfig.staffHireTile.hireCost = value;\n              updateStaffHireProgress();\n            },\n          };\n          params['Width'] = {\n            key: 'gameConfig.staffHireTile.width',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.staffHireTile.width = value;\n              rebuildStaffHireTile();\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.staffHireTile.depth',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.staffHireTile.depth = value;\n              rebuildStaffHireTile();\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.staffHireTile.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.staffHireTile.color = value;\n              if (staffHireTileGlow) {\n                staffHireTileGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'vaultStaffTile') {\n          params['Hire Cost'] = {\n            key: 'gameConfig.vaultStaffTile.hireCost',\n            type: 'slider',\n            min: 100,\n            max: 2000,\n            step: 100,\n            onChange: (value) => {\n              window.gameConfig.vaultStaffTile.hireCost = value;\n              updateVaultStaffProgress();\n            },\n          };\n          params['Width'] = {\n            key: 'gameConfig.vaultStaffTile.width',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.vaultStaffTile.width = value;\n              rebuildVaultStaffTile();\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.vaultStaffTile.depth',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.vaultStaffTile.depth = value;\n              rebuildVaultStaffTile();\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.vaultStaffTile.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.vaultStaffTile.color = value;\n              if (vaultStaffTileGlow) {\n                vaultStaffTileGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'trashBin') {\n          params['Radius'] = {\n            key: 'gameConfig.trashBin.radius',\n            type: 'slider',\n            min: 1,\n            max: 5,\n            step: 0.5,\n            onChange: (value) => {\n              window.gameConfig.trashBin.radius = value;\n              rebuildTrashBin();\n            },\n          };\n          params['Color'] = {\n            key: 'gameConfig.trashBin.color',\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.trashBin.color = value;\n              if (trashBinGlow) {\n                trashBinGlow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'diningTableTile') {\n          const index = editable.tileIndex;\n          params['Table Cost'] = {\n            key: `gameConfig.diningTableTiles.${index}.tableCost`,\n            type: 'slider',\n            min: 100,\n            max: 1000,\n            step: 50,\n            onChange: (value) => {\n              window.gameConfig.diningTableTiles[index].tableCost = value;\n              updateDiningTableTileProgress(index);\n              updateDiningTableTileCostDisplay(index);\n            },\n          };\n          params['Width'] = {\n            key: `gameConfig.diningTableTiles.${index}.width`,\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.diningTableTiles[index].width = value;\n              rebuildDiningTableTile(index);\n            },\n          };\n          params['Depth'] = {\n            key: `gameConfig.diningTableTiles.${index}.depth`,\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.diningTableTiles[index].depth = value;\n              rebuildDiningTableTile(index);\n            },\n          };\n          params['Color'] = {\n            key: `gameConfig.diningTableTiles.${index}.color`,\n            type: 'color',\n            onChange: (value) => {\n              window.gameConfig.diningTableTiles[index].color = value;\n              const tileEntry = diningTableTileMeshes.find((t) => t.index === index);\n              if (tileEntry && tileEntry.glow) {\n                tileEntry.glow.material.color.set(value);\n              }\n            },\n          };\n        } else if (editable.type === 'diningTable') {\n          // Dining tables don't have many editable properties, just position\n        } else if (editable.type === 'bankVault') {\n          params['Width'] = {\n            key: 'gameConfig.bankVault.width',\n            type: 'slider',\n            min: 4,\n            max: 10,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.bankVault.width = value;\n              // Would need rebuild but keeping simple for now\n            },\n          };\n          params['Depth'] = {\n            key: 'gameConfig.bankVault.depth',\n            type: 'slider',\n            min: 3,\n            max: 8,\n            step: 1,\n            onChange: (value) => {\n              window.gameConfig.bankVault.depth = value;\n            },\n          };\n        }\n\n        const menuOptions = {\n          name: editable.name,\n          params: params,\n        };\n\n        // Add copy/delete for grills\n        if (editable.type === 'grill') {\n          menuOptions.onCopy = () => duplicateGrill(editable);\n          menuOptions.onDelete = () => deleteGrill(editable);\n        }\n\n        lib.editMenu.open(menuOptions);\n      }\n\n      function setupEditorToolbar() {\n        const addGrillBtn = document.getElementById('add-grill-btn');\n        const addFryerBtn = document.getElementById('add-fryer-btn');\n        const addDrinkBtn = document.getElementById('add-drink-btn');\n        const addMoneyZoneBtn = document.getElementById('add-money-zone-btn');\n        const addSpendZoneBtn = document.getElementById('add-spend-zone-btn');\n        const selectSelfOrderBtn = document.getElementById('select-self-order-btn');\n        const deselectBtn = document.getElementById('deselect-btn');\n        const deleteBtn = document.getElementById('delete-selected-btn');\n        const copyBtn = document.getElementById('copy-selected-btn');\n\n        if (addGrillBtn) {\n          addGrillBtn.addEventListener('click', () => addNewEquipment('grill'));\n        }\n        if (addFryerBtn) {\n          addFryerBtn.addEventListener('click', () => addNewEquipment('fryer'));\n        }\n        if (addDrinkBtn) {\n          addDrinkBtn.addEventListener('click', () => addNewEquipment('drink_station'));\n        }\n\n        if (addMoneyZoneBtn) {\n          addMoneyZoneBtn.addEventListener('click', () => {\n            // Focus on money zone\n            const moneyZoneEditable = editableObjects.find((obj) => obj.type === 'moneyZone');\n            if (moneyZoneEditable) {\n              selectObject(moneyZoneEditable);\n            }\n          });\n        }\n\n        if (addSpendZoneBtn) {\n          addSpendZoneBtn.addEventListener('click', () => {\n            // Focus on spend zone\n            const spendZoneEditable = editableObjects.find((obj) => obj.type === 'spendZone');\n            if (spendZoneEditable) {\n              selectObject(spendZoneEditable);\n            }\n          });\n        }\n\n        if (selectSelfOrderBtn) {\n          selectSelfOrderBtn.addEventListener('click', () => {\n            const selfOrderEditable = editableObjects.find((obj) => obj.type === 'selfOrderKiosk');\n            if (selfOrderEditable) {\n              selectObject(selfOrderEditable);\n            }\n          });\n        }\n\n        const selectCashierBtn = document.getElementById('select-cashier-btn');\n        if (selectCashierBtn) {\n          selectCashierBtn.addEventListener('click', () => {\n            // Focus on cashier stall\n            const cashierEditable = editableObjects.find((obj) => obj.type === 'cashierStall');\n            if (cashierEditable) {\n              selectObject(cashierEditable);\n            }\n          });\n        }\n\n        const selectStaffTileBtn = document.getElementById('select-staff-tile-btn');\n        if (selectStaffTileBtn) {\n          selectStaffTileBtn.addEventListener('click', () => {\n            // Focus on staff hire tile\n            const staffTileEditable = editableObjects.find((obj) => obj.type === 'staffHireTile');\n            if (staffTileEditable) {\n              selectObject(staffTileEditable);\n            }\n          });\n        }\n\n        const selectDiningTileBtn = document.getElementById('select-dining-tile-btn');\n        if (selectDiningTileBtn) {\n          selectDiningTileBtn.addEventListener('click', () => {\n            // First check if there's any active tile to focus on\n            const activeTile = editableObjects.find((obj) => obj.type === 'diningTableTile');\n            if (activeTile) {\n              selectObject(activeTile);\n            } else {\n              // If no active tile exists, create a new one\n              addNewDiningTableTile();\n            }\n          });\n        }\n\n        const selectTrashBinBtn = document.getElementById('select-trash-bin-btn');\n        if (selectTrashBinBtn) {\n          selectTrashBinBtn.addEventListener('click', () => {\n            // Focus on trash bin\n            const trashBinEditable = editableObjects.find((obj) => obj.type === 'trashBin');\n            if (trashBinEditable) {\n              selectObject(trashBinEditable);\n            }\n          });\n        }\n\n        // Cleaner staff tile button\n        const selectCleanerTileBtn = document.getElementById('select-cleaner-tile-btn');\n        if (selectCleanerTileBtn) {\n          selectCleanerTileBtn.addEventListener('click', () => {\n            const cleanerTileEditable = editableObjects.find(\n              (obj) => obj.type === 'cleanerStaffTile'\n            );\n            if (cleanerTileEditable) {\n              selectObject(cleanerTileEditable);\n            }\n          });\n        }\n\n        // Runner staff tile button\n        const selectRunnerTileBtn = document.getElementById('select-runner-tile-btn');\n        if (selectRunnerTileBtn) {\n          selectRunnerTileBtn.addEventListener('click', () => {\n            const runnerTileEditable = editableObjects.find(\n              (obj) => obj.type === 'runnerStaffTile'\n            );\n            if (runnerTileEditable) {\n              selectObject(runnerTileEditable);\n            }\n          });\n        }\n\n        // Drive-thru booth button\n        const selectDriveThruBtn = document.getElementById('select-drive-thru-btn');\n        if (selectDriveThruBtn) {\n          selectDriveThruBtn.addEventListener('click', () => {\n            const driveThruEditable = editableObjects.find((obj) => obj.type === 'driveThruBooth');\n            if (driveThruEditable) {\n              selectObject(driveThruEditable);\n            }\n          });\n        }\n\n        // Bank vault button\n        const selectVaultBtn = document.getElementById('select-vault-btn');\n        if (selectVaultBtn) {\n          selectVaultBtn.addEventListener('click', () => {\n            const vaultEditable = editableObjects.find((obj) => obj.type === 'bankVault');\n            if (vaultEditable) {\n              selectObject(vaultEditable);\n            }\n          });\n        }\n\n        // Vault staff tile button\n        const selectVaultStaffBtn = document.getElementById('select-vault-staff-btn');\n        if (selectVaultStaffBtn) {\n          selectVaultStaffBtn.addEventListener('click', () => {\n            const vaultStaffEditable = editableObjects.find((obj) => obj.type === 'vaultStaffTile');\n            if (vaultStaffEditable) {\n              selectObject(vaultStaffEditable);\n            }\n          });\n        }\n\n        // Prestige Layouts UI\n        let currentEditPrestigeLevel = 1;\n        const layoutLevelDisplay = document.getElementById('layout-level-display');\n        const prevLayoutBtn = document.getElementById('prev-layout-btn');\n        const nextLayoutBtn = document.getElementById('next-layout-btn');\n        const saveLayoutBtn = document.getElementById('save-layout-btn');\n        const loadLayoutBtn = document.getElementById('load-layout-btn');\n\n        function updateLayoutDisplay() {\n          if (layoutLevelDisplay)\n            layoutLevelDisplay.textContent = `Lvl ${currentEditPrestigeLevel}`;\n        }\n\n        if (prevLayoutBtn) {\n          prevLayoutBtn.addEventListener('click', () => {\n            if (currentEditPrestigeLevel > 1) {\n              currentEditPrestigeLevel--;\n              updateLayoutDisplay();\n            }\n          });\n        }\n\n        if (nextLayoutBtn) {\n          nextLayoutBtn.addEventListener('click', () => {\n            currentEditPrestigeLevel++;\n            updateLayoutDisplay();\n          });\n        }\n\n        if (saveLayoutBtn) {\n          saveLayoutBtn.addEventListener('click', () => {\n            const currentLayouts = window.gameConfig.prestigeLayouts\n              ? JSON.parse(JSON.stringify(window.gameConfig.prestigeLayouts))\n              : [];\n\n            // Collect current layout data\n            const layoutData = {\n              level: currentEditPrestigeLevel,\n              grills: JSON.parse(JSON.stringify(window.gameConfig.grills || [])),\n              cashierStall: JSON.parse(JSON.stringify(window.gameConfig.cashierStall || {})),\n              cashierTile: window.gameConfig.cashierTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.cashierTile))\n                : null,\n              grillTile: window.gameConfig.grillTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.grillTile))\n                : null,\n              staffHireTile: window.gameConfig.staffHireTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.staffHireTile))\n                : null,\n              cleanerStaffTile: window.gameConfig.cleanerStaffTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.cleanerStaffTile))\n                : null,\n              runnerStaffTile: window.gameConfig.runnerStaffTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.runnerStaffTile))\n                : null,\n              vaultStaffTile: window.gameConfig.vaultStaffTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.vaultStaffTile))\n                : null,\n              diningTableTiles: window.gameConfig.diningTableTiles\n                ? JSON.parse(JSON.stringify(window.gameConfig.diningTableTiles))\n                : null,\n              trashBin: window.gameConfig.trashBin\n                ? JSON.parse(JSON.stringify(window.gameConfig.trashBin))\n                : null,\n              vaultTile: window.gameConfig.vaultTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.vaultTile))\n                : null,\n              fountainTile: window.gameConfig.fountainTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.fountainTile))\n                : null,\nbankVault: window.gameConfig.bankVault\n                ? JSON.parse(JSON.stringify(window.gameConfig.bankVault))\n                : null,\n              fountain: window.gameConfig.fountain\n                ? JSON.parse(JSON.stringify(window.gameConfig.fountain))\n                : null,\n              selfOrderKiosks: window.gameConfig.selfOrderKiosks\n                ? JSON.parse(JSON.stringify(window.gameConfig.selfOrderKiosks))\n                : null,\n              driveThruTile: window.gameConfig.driveThruTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.driveThruTile))\n                : null,\n              trashTile: window.gameConfig.trashTile\n                ? JSON.parse(JSON.stringify(window.gameConfig.trashTile))\n                : null,\n            };\n\n            // Update or add\n            const existingIndex = currentLayouts.findIndex(\n              (l) => l.level === currentEditPrestigeLevel\n            );\n            if (existingIndex >= 0) {\n              currentLayouts[existingIndex] = layoutData;\n            } else {\n              currentLayouts.push(layoutData);\n            }\n\n            window.gameConfig.prestigeLayouts = currentLayouts;\n\n            lib.log(`Saved Prestige Level ${currentEditPrestigeLevel} Layout`);\n\n            // Show visual feedback on button\n            const originalText = saveLayoutBtn.innerHTML;\n            saveLayoutBtn.innerHTML =\n              '<span class=\"btn-icon\">\u2705</span><span class=\"btn-label\">Saved!</span>';\n            setTimeout(() => {\n              saveLayoutBtn.innerHTML = originalText;\n            }, 1500);\n          });\n        }\n\n        if (loadLayoutBtn) {\n          loadLayoutBtn.addEventListener('click', () => {\n            const layouts = window.gameConfig.prestigeLayouts || [];\n            const layoutData = layouts.find((l) => l.level === currentEditPrestigeLevel);\n\n            if (layoutData) {\n              applyPrestigeLayout(layoutData);\n              registerEditableObjects();\n              lib.log(`Loaded Prestige Level ${currentEditPrestigeLevel} Layout`);\n            } else {\n              lib.log(`No saved layout for Prestige Level ${currentEditPrestigeLevel}`);\n            }\n          });\n        }\n\n        if (deselectBtn) {\n          deselectBtn.addEventListener('click', () => deselectObject());\n        }\n\n        // Delete selected object button\n        if (deleteBtn) {\n          deleteBtn.addEventListener('click', () => {\n            if (editorSelectedObject) {\n              deleteSelectedObject(editorSelectedObject);\n            }\n          });\n        }\n\n        // Copy/duplicate selected object button\n        if (copyBtn) {\n          copyBtn.addEventListener('click', () => {\n            if (editorSelectedObject) {\n              duplicateSelectedObject(editorSelectedObject);\n            }\n          });\n        }\n      }\n\n      function deleteSelectedObject(editable) {\n        switch (editable.type) {\n          case 'grill':\n            deleteGrill(editable);\n            break;\n          case 'sellZone':\n            deleteSellZone();\n            break;\n          case 'moneyZone':\n            deleteMoneyZone();\n            break;\n          case 'spendZone':\n            deleteSpendZone();\n            break;\n          case 'cashierStall':\n            deleteCashierStall();\n            break;\n          case 'staffHireTile':\n            deleteStaffHireTile();\n            break;\n          case 'cleanerStaffTile':\n            deleteCleanerStaffTile();\n            break;\n          case 'runnerStaffTile':\n            deleteRunnerStaffTile();\n            break;\n          case 'vaultStaffTile':\n            deleteVaultStaffTile();\n            break;\n          case 'trashBin':\n            deleteTrashBin();\n            break;\n          case 'driveThruBooth':\n            deleteDriveThruBooth();\n            break;\n          case 'diningTableTile':\n            deleteDiningTableTile();\n            break;\n          case 'driveThruTile':\n            deleteDriveThruTile();\n            break;\n          case 'trashTile':\n            deleteTrashTile();\n            break;\n          case 'vaultTile':\n            deleteVaultTile();\n            break;\n          case 'fountainTile':\n            deleteFountainTile();\n            break;\n          case 'fountain2Tile':\n            deleteFountain2Tile();\n            break;\n          case 'fountain':\n            deleteFountain();\n            break;\n          case 'fountain2':\n            deleteFountain2();\n            break;\n          case 'selfOrderKioskTile':\n            deleteSelfOrderKioskTile();\n            break;\ncase 'selfOrderKiosk':\n            deleteSelfOrderKiosk(editable);\n            break;\n          case 'diningTable':\n            deleteDiningTable(editable);\n            break;\n          default:\n            lib.log('Cannot delete this object type');\n        }\n      }\n\n      function duplicateSelectedObject(editable) {\n        switch (editable.type) {\n          case 'grill':\n            duplicateGrill(editable);\n            break;\n          case 'sellZone':\n            duplicateSellZone(editable);\n            break;\n          case 'moneyZone':\n            duplicateMoneyZone(editable);\n            break;\n          case 'spendZone':\n            duplicateSpendZone(editable);\n            break;\n          case 'cashierStall':\n            duplicateCashierStall(editable);\n            break;\n          case 'staffHireTile':\n            duplicateStaffHireTile(editable);\n            break;\n          case 'cleanerStaffTile':\n            duplicateCleanerStaffTile(editable);\n            break;\n          case 'runnerStaffTile':\n            duplicateRunnerStaffTile(editable);\n            break;\n          case 'vaultStaffTile':\n            duplicateVaultStaffTile(editable);\n            break;\n          case 'trashBin':\n            duplicateTrashBin(editable);\n            break;\n          case 'driveThruBooth':\n            duplicateDriveThruBooth(editable);\n            break;\n          case 'diningTableTile':\n            duplicateDiningTableTile(editable);\n            break;\n          case 'driveThruTile':\n            duplicateDriveThruTile(editable);\n            break;\n          case 'trashTile':\n            duplicateTrashTile(editable);\n            break;\n          case 'vaultTile':\n            duplicateVaultTile(editable);\n            break;\n          case 'selfOrderKioskTile':\n            if (editable.configKey && String(editable.configKey).startsWith('selfOrderKioskTiles.')) {\n              if (typeof duplicateSelfOrderKioskTiles === 'function') {\n                duplicateSelfOrderKioskTiles(editable);\n              }\n            } else {\n              duplicateSelfOrderKioskTile(editable);\n            }\n            break;\n          case 'diningTable':\n            duplicateDiningTable(editable);\n            break;\n        }\n      }\n\n      function deleteSellZone() {\n        if (window.gameConfig.sellZone) window.gameConfig.sellZone.deleted = true;\n\n        // Remove sell zone from scene\n        scene.remove(sellZoneMesh);\n        scene.remove(sellZoneGlow);\n\n        const customer = sellZoneMesh.userData.coinIndicator;\n        if (customer) scene.remove(customer);\n\n        // Find and remove counter top\n        scene.children.forEach((child) => {\n          if (\n            child.geometry &&\n            child.geometry.type === 'CylinderGeometry' &&\n            child.position.y === 0.35\n          ) {\n            scene.remove(child);\n          }\n        });\n\n        // Remove from editableObjects\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'sellZone');\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        sellZoneMesh = null;\n        sellZoneGlow = null;\n\n        deselectObject();\n      }\n\n      function deleteMoneyZone() {\n        if (window.gameConfig.moneyZone) window.gameConfig.moneyZone.deleted = true;\n\n        scene.remove(moneyZoneMesh);\n        scene.remove(moneyZoneGlow);\n\n        const border = moneyZoneMesh.userData.borderMesh;\n        if (border) scene.remove(border);\n\n        const sign = moneyZoneMesh.userData.signIndicator;\n        if (sign) scene.remove(sign);\n\n        // Remove ground money items\n        groundMoneyItems.forEach((item) => {\n          scene.remove(item);\n        });\n        groundMoneyItems = [];\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'moneyZone');\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        moneyZoneMesh = null;\n        moneyZoneGlow = null;\n\n        deselectObject();\n      }\n\n      function deleteSpendZone() {\n        if (window.gameConfig.spendZone) window.gameConfig.spendZone.deleted = true;\n\n        scene.remove(spendZoneMesh);\n        scene.remove(spendZoneGlow);\n\n        const border = spendZoneMesh.userData.borderMesh;\n        if (border) scene.remove(border);\n\n        const gem = spendZoneMesh.userData.gemIndicator;\n        if (gem) scene.remove(gem);\n\n        const number = spendZoneMesh.userData.numberIndicator;\n        if (number) scene.remove(number);\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'spendZone');\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        spendZoneMesh = null;\n        spendZoneGlow = null;\n\n        deselectObject();\n      }\n\n      function deleteCashierStall() {\n        if (window.gameConfig.cashierStall) window.gameConfig.cashierStall.deleted = true;\n\n        scene.remove(cashierStallMesh);\n        scene.remove(cashierStallGlow);\n        scene.remove(cashierCashPileMesh);\n        scene.remove(cashierCashPileGlow);\n\n        const counterTop = cashierStallMesh.userData.counterTop;\n        if (counterTop) scene.remove(counterTop);\n\n        const register = cashierStallMesh.userData.register;\n        if (register) scene.remove(register);\n\n        const tray = cashierStallMesh.userData.tray;\n        if (tray) scene.remove(tray);\n\n        const menuBoard = cashierStallMesh.userData.menuBoard;\n        if (menuBoard) scene.remove(menuBoard);\n\n        const menuBurger = cashierStallMesh.userData.menuBurger;\n        if (menuBurger) scene.remove(menuBurger);\n\n        const dollarIcon = cashierCashPileMesh?.userData?.dollarIcon;\n        if (dollarIcon) scene.remove(dollarIcon);\n\n        const cashPileBorder = cashierCashPileMesh?.userData?.borderMesh;\n        if (cashPileBorder) scene.remove(cashPileBorder);\n\n        // Clear cashier state\n        cashierBurgerStack.forEach((b) => scene.remove(b));\n        cashierBurgerStack = [];\n        cashierCashPile.forEach((m) => scene.remove(m));\n        cashierCashPile = [];\n        customerQueue.forEach((c) => scene.remove(c.mesh));\n        customerQueue = [];\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'cashierStall');\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        cashierStallMesh = null;\n        cashierStallGlow = null;\n        cashierCashPileMesh = null;\n        cashierCashPileGlow = null;\n\n        deselectObject();\n      }\n\n      function duplicateSellZone(editable) {\n        lib.log('Service Counter can only have one instance');\n      }\n\n      function duplicateMoneyZone(editable) {\n        lib.log('Money Zone can only have one instance');\n      }\n\n      function duplicateSpendZone(editable) {\n        lib.log('Spend Zone can only have one instance');\n      }\n\n      function duplicateCashierStall(editable) {\n        lib.log('Cashier can only have one instance');\n      }\n\n      function deleteStaffHireTile() {\n        if (window.gameConfig.staffHireTile) window.gameConfig.staffHireTile.deleted = true;\n\n        scene.remove(staffHireTileMesh);\n        scene.remove(staffHireTileGlow);\n\n        const border =\n          staffHireTileMesh && staffHireTileMesh.userData && staffHireTileMesh.userData.borderMesh;\n        if (border) scene.remove(border);\n\n        const staffIcon =\n          staffHireTileMesh && staffHireTileMesh.userData && staffHireTileMesh.userData.staffIcon;\n        if (staffIcon) scene.remove(staffIcon);\n\n        const progressBg =\n          staffHireTileMesh && staffHireTileMesh.userData && staffHireTileMesh.userData.progressBg;\n        if (progressBg) scene.remove(progressBg);\n\n        const progressFill =\n          staffHireTileMesh &&\n          staffHireTileMesh.userData &&\n          staffHireTileMesh.userData.progressFill;\n        if (progressFill) scene.remove(progressFill);\n\n        const costPlane =\n          staffHireTileMesh && staffHireTileMesh.userData && staffHireTileMesh.userData.costPlane;\n        if (costPlane) scene.remove(costPlane);\n\n        // Remove hired staff if exists\n        if (hiredStaffMesh) {\n          scene.remove(hiredStaffMesh);\n          hiredStaffMesh = null;\n        }\n        if (hiredStaffMesh2) {\n          scene.remove(hiredStaffMesh2);\n          hiredStaffMesh2 = null;\n        }\n\n        // Clear money pile on tile\n        staffHireMoneyPile.forEach((m) => scene.remove(m));\n        staffHireMoneyPile = [];\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'staffHireTile');\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        staffHireTileMesh = null;\n        staffHireTileGlow = null;\n\n        deselectObject();\n      }\n\n      function duplicateStaffHireTile(editable) {\n        lib.log('Staff Hire Tile can only have one instance');\n      }\n\n      function deleteCleanerStaffTile() {\n        if (window.gameConfig.cleanerStaffTile) window.gameConfig.cleanerStaffTile.deleted = true;\n\n        if (cleanerStaffTileMesh) {\n          scene.remove(cleanerStaffTileMesh);\n          ['borderMesh', 'staffIcon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n            const obj = cleanerStaffTileMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n        }\n        if (cleanerStaffTileGlow) scene.remove(cleanerStaffTileGlow);\n\n        if (hiredCleanerMesh) {\n          scene.remove(hiredCleanerMesh);\n          hiredCleanerMesh = null;\n        }\n        cleanerStaffAI = null;\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'cleanerStaffTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n\n        cleanerStaffTileMesh = null;\n        cleanerStaffTileGlow = null;\n\n        deselectObject();\n      }\n\n      function duplicateCleanerStaffTile(editable) {\n        lib.log('Cleaner Staff Tile can only have one instance');\n      }\n\n      function deleteRunnerStaffTile() {\n        if (window.gameConfig.runnerStaffTile) window.gameConfig.runnerStaffTile.deleted = true;\n\n        if (runnerStaffTileMesh) {\n          scene.remove(runnerStaffTileMesh);\n          ['borderMesh', 'staffIcon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n            const obj = runnerStaffTileMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n        }\n        if (runnerStaffTileGlow) scene.remove(runnerStaffTileGlow);\n\n        if (hiredRunnerMesh) {\n          scene.remove(hiredRunnerMesh);\n          hiredRunnerMesh = null;\n        }\n        runnerStaffAI = null;\n        runnerBurgerStack.forEach((b) => {\n          if (b.parent) b.parent.remove(b);\n        });\n        runnerBurgerStack = [];\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'runnerStaffTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n\n        runnerStaffTileMesh = null;\n        runnerStaffTileGlow = null;\n\n        deselectObject();\n      }\n\n      function duplicateRunnerStaffTile(editable) {\n        lib.log('Runner Staff Tile can only have one instance');\n      }\n\n      function deleteVaultStaffTile() {\n        if (window.gameConfig.vaultStaffTile) window.gameConfig.vaultStaffTile.deleted = true;\n\n        if (vaultStaffTileMesh) {\n          scene.remove(vaultStaffTileMesh);\n          ['borderMesh', 'staffIcon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n            const obj = vaultStaffTileMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n        }\n        if (vaultStaffTileGlow) scene.remove(vaultStaffTileGlow);\n\n        if (hiredVaultCollectorMesh) {\n          scene.remove(hiredVaultCollectorMesh);\n          hiredVaultCollectorMesh = null;\n        }\n        vaultCollectorAI = null;\n        vaultCollectorMoneyStack.forEach((b) => {\n          if (b.parent) b.parent.remove(b);\n        });\n        vaultCollectorMoneyStack = [];\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'vaultStaffTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n\n        vaultStaffTileMesh = null;\n        vaultStaffTileGlow = null;\n\n        deselectObject();\n      }\n\n      function duplicateVaultStaffTile(editable) {\n        lib.log('Vault Staff Tile can only have one instance');\n      }\n\n      function deleteTrashBin() {\n        if (window.gameConfig.trashBin) window.gameConfig.trashBin.deleted = true;\n\n        if (trashBinMesh) {\n          // Remove trash icon\n          const trashIcon = trashBinMesh.userData.trashIcon;\n          if (trashIcon) scene.remove(trashIcon);\n\n          scene.remove(trashBinMesh);\n        }\n        if (trashBinGlow) scene.remove(trashBinGlow);\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'trashBin');\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        trashBinMesh = null;\n        trashBinGlow = null;\n\n        deselectObject();\n      }\n\n      function duplicateTrashBin(editable) {\n        lib.log('Trash Bin can only have one instance');\n      }\n\n      function deleteDriveThruTile() {\n        if (window.gameConfig.driveThruTile) window.gameConfig.driveThruTile.deleted = true;\n        if (driveThruTileMesh) {\n          scene.remove(driveThruTileMesh);\n          ['borderMesh', 'icon', 'progressBg', 'progressFill', 'costPlane'].forEach((key) => {\n            const obj = driveThruTileMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n        }\n        if (driveThruTileGlow) scene.remove(driveThruTileGlow);\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'driveThruTile');\n        if (editIndex > -1) editableObjects.splice(editIndex, 1);\n        driveThruTileMesh = null;\n        driveThruTileGlow = null;\n        deselectObject();\n      }\n\n      function duplicateDriveThruTile(editable) {\n        lib.log('Drive-Thru Tile can only have one instance');\n      }\n\n      function deleteDriveThruBooth() {\n        if (window.gameConfig.driveThruBooth) window.gameConfig.driveThruBooth.deleted = true;\n\n        if (driveThruBoothMesh) {\n          // Remove all attached elements\n          ['roof', 'window', 'counter', 'sign', 'carIcon', 'speaker', 'road'].forEach((key) => {\n            const obj = driveThruBoothMesh.userData[key];\n            if (obj) scene.remove(obj);\n          });\n          scene.remove(driveThruBoothMesh);\n        }\n        if (driveThruBoothGlow) scene.remove(driveThruBoothGlow);\n\n        // Remove cash pile\n        if (driveThruCashPileMesh) {\n          const border = driveThruCashPileMesh.userData.borderMesh;\n          if (border) scene.remove(border);\n          scene.remove(driveThruCashPileMesh);\n        }\n        if (driveThruCashPileGlow) scene.remove(driveThruCashPileGlow);\n\n        // Clear burger stack and cash pile\n        driveThruBurgerStack.forEach((b) => scene.remove(b));\n        driveThruBurgerStack = [];\n        driveThruCashPile.forEach((m) => scene.remove(m));\n        driveThruCashPile = [];\n        driveThruCarQueue.forEach((c) => scene.remove(c.mesh));\n        driveThruCarQueue = [];\n\n        const editIndex = editableObjects.findIndex((obj) => obj.type === 'driveThruBooth');\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        driveThruBoothMesh = null;\n        driveThruBoothGlow = null;\n        driveThruCashPileMesh = null;\n        driveThruCashPileGlow = null;\n\n        deselectObject();\n      }\n\n      function duplicateDriveThruBooth(editable) {\n        lib.log('Drive-Thru Booth can only have one instance');\n      }\n\n      function deleteDiningTableTile(editable) {\n        if (!editable) return;\n        const index = editable.tileIndex;\n        destroyDiningTableTileVisuals(index);\n        deselectObject();\n      }\n\n      function deleteDiningTable(editable) {\n        const index = editable.tableIndex;\n        const tableEntry = diningTableMeshes[index];\n\n        if (!tableEntry) return;\n\n        // Remove from scene\n        scene.remove(tableEntry.mesh);\n        scene.remove(tableEntry.trashMesh);\n\n        tableEntry.mesh.traverse((child) => {\n          if (child.geometry) child.geometry.dispose();\n          if (child.material) {\n            if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n            else child.material.dispose();\n          }\n        });\n        tableEntry.trashMesh.traverse((child) => {\n          if (child.geometry) child.geometry.dispose();\n          if (child.material) {\n            if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());\n            else child.material.dispose();\n          }\n        });\n\n        // Remove from arrays\n        diningTableMeshes.splice(index, 1);\n        window.gameConfig.diningTables.splice(index, 1);\n\n        // Remove from editableObjects\n        const editIndex = editableObjects.indexOf(editable);\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        // Update indices for remaining tables\n        editableObjects.forEach((obj) => {\n          if (obj.type === 'diningTable' && obj.tableIndex > index) {\n            obj.tableIndex--;\n            obj.name = `Dining Table ${obj.tableIndex + 1}`;\n          }\n        });\n\n        deselectObject();\n      }\n\n      function duplicateDiningTable(editable) {\n        const data = editable.getData();\n        const offset = 4;\n        const x = data.x + offset;\n        const z = data.z;\n\n        const newTable = {\n          id: `table_${nextTableId++}`,\n          x: x,\n          z: z,\n          occupied: false,\n          hasTrash: false,\n        };\n\n        window.gameConfig.diningTables.push(newTable);\n        const index = window.gameConfig.diningTables.length - 1;\n        createDiningTable(newTable, index);\n\n        // Register as editable\n        const tableEntry = diningTableMeshes[diningTableMeshes.length - 1];\n        const newEditable = {\n          mesh: tableEntry.mesh,\n          type: 'diningTable',\n          configKey: `diningTables.${index}`,\n          tableIndex: index,\n          name: `Dining Table ${index + 1}`,\n          getData: () => ({\n            x: tableEntry.tableData.x,\n            z: tableEntry.tableData.z,\n          }),\n          setPosition: (newX, newZ) => {\n            tableEntry.mesh.position.x = newX;\n            tableEntry.mesh.position.z = newZ;\n            tableEntry.tableData.x = newX;\n            tableEntry.tableData.z = newZ;\n            if (tableEntry.trashMesh) {\n              tableEntry.trashMesh.position.x = newX;\n              tableEntry.trashMesh.position.z = newZ;\n            }\n            window.gameConfig.diningTables[index].x = newX;\n            window.gameConfig.diningTables[index].z = newZ;\n          },\n        };\n        editableObjects.push(newEditable);\n\n        // Select the new table\n        selectObject(newEditable);\n      }\n\n      function rebuildStaffHireTile() {\n        const config = window.gameConfig.staffHireTile;\n        if (!config || !staffHireTileMesh) return;\n\n        // Update main platform geometry\n        staffHireTileMesh.geometry.dispose();\n        staffHireTileMesh.geometry = new THREE.BoxGeometry(config.width, 0.4, config.depth);\n\n        // Update border\n        const border = staffHireTileMesh.userData.borderMesh;\n        if (border) {\n          border.geometry.dispose();\n          border.geometry = new THREE.BoxGeometry(config.width + 0.3, 0.35, config.depth + 0.3);\n        }\n\n        // Update glow\n        staffHireTileGlow.geometry.dispose();\n        staffHireTileGlow.geometry = new THREE.BoxGeometry(\n          config.width + 0.6,\n          0.05,\n          config.depth + 0.6\n        );\n\n        // Update progress bar position\n        const progressBg = staffHireTileMesh.userData.progressBg;\n        if (progressBg) {\n          progressBg.geometry.dispose();\n          progressBg.geometry = new THREE.BoxGeometry(config.width - 0.5, 0.1, 0.4);\n          progressBg.position.z = config.z + config.depth / 2 - 0.5;\n        }\n\n        updateStaffHireProgress();\n        updateStaffHireCostDisplay();\n        updateSelectionHighlight();\n      }\n\n      function rebuildVaultStaffTile() {\n        const config = window.gameConfig.vaultStaffTile;\n        if (!config || !vaultStaffTileMesh) return;\n\n        // Update main platform geometry\n        vaultStaffTileMesh.geometry.dispose();\n        vaultStaffTileMesh.geometry = new THREE.BoxGeometry(config.width, 0.4, config.depth);\n\n        // Update border\n        const border = vaultStaffTileMesh.userData.borderMesh;\n        if (border) {\n          border.geometry.dispose();\n          border.geometry = new THREE.BoxGeometry(config.width + 0.3, 0.35, config.depth + 0.3);\n        }\n\n        // Update glow\n        vaultStaffTileGlow.geometry.dispose();\n        vaultStaffTileGlow.geometry = new THREE.BoxGeometry(\n          config.width + 0.6,\n          0.05,\n          config.depth + 0.6\n        );\n\n        // Update progress bar position\n        const progressBg = vaultStaffTileMesh.userData.progressBg;\n        if (progressBg) {\n          progressBg.geometry.dispose();\n          progressBg.geometry = new THREE.BoxGeometry(config.width - 0.5, 0.1, 0.4);\n          progressBg.position.z = config.z + config.depth / 2 - 0.5;\n        }\n\n        updateVaultStaffProgress();\n        updateVaultStaffCostDisplay();\n        updateSelectionHighlight();\n      }\n\n      function duplicateDiningTableTile(editable) {\n        const data = editable.getData();\n        const offset = 4;\n        const x = data.x + offset;\n        const z = data.z;\n\n        // Copy the config\n        const sourceConfig = window.gameConfig.diningTableTiles[editable.tileIndex];\n        const newConfig = {\n          ...sourceConfig,\n          x: x,\n          z: z,\n          moneyDeposited: 0,\n          deleted: false,\n        };\n\n        window.gameConfig.diningTableTiles.push(newConfig);\n        const index = window.gameConfig.diningTableTiles.length - 1;\n\n        const tileEntry = createDiningTableTile(index);\n\n        // Register as editable\n        const newEditable = {\n          mesh: tileEntry.mesh,\n          type: 'diningTableTile',\n          configKey: `diningTableTiles.${index}`,\n          tileIndex: index,\n          name: `Dining Table Tile ${index + 1}`,\n          getData: () => ({\n            x: newConfig.x,\n            z: newConfig.z,\n            width: newConfig.width,\n            depth: newConfig.depth,\n          }),\n          setPosition: (newX, newZ) => {\n            updateDiningTableTilePosition(newX, newZ, index);\n          },\n        };\n        editableObjects.push(newEditable);\n        selectObject(newEditable);\n      }\n\n      function addNewDiningTableTile() {\n        const x = 0;\n        const z = 16;\n\n        // Create new config based on default\n        const newConfig = {\n          x: x,\n          z: z,\n          width: 4,\n          depth: 4,\n          tableCost: 300,\n          color: '#3498DB',\n          moneyDeposited: 0,\n          deleted: false,\n        };\n\n        if (!window.gameConfig.diningTableTiles) {\n          window.gameConfig.diningTableTiles = [];\n        }\n\n        window.gameConfig.diningTableTiles.push(newConfig);\n        const index = window.gameConfig.diningTableTiles.length - 1;\n\n        const tileEntry = createDiningTableTile(index);\n\n        // Register as editable\n        const newEditable = {\n          mesh: tileEntry.mesh,\n          type: 'diningTableTile',\n          configKey: `diningTableTiles.${index}`,\n          tileIndex: index,\n          name: `Dining Table Tile ${index + 1}`,\n          getData: () => ({\n            x: newConfig.x,\n            z: newConfig.z,\n            width: newConfig.width,\n            depth: newConfig.depth,\n          }),\n          setPosition: (newX, newZ) => {\n            updateDiningTableTilePosition(newX, newZ, index);\n          },\n        };\n        editableObjects.push(newEditable);\n        selectObject(newEditable);\n      }\n\n      function rebuildDiningTableTile(index) {\n        const tileEntry = diningTableTileMeshes.find((t) => t.index === index);\n        if (!tileEntry) return;\n        const config = window.gameConfig.diningTableTiles[index];\n        const diningTableTileMesh = tileEntry.mesh;\n        const diningTableTileGlow = tileEntry.glow;\n\n        // Update main platform geometry\n        diningTableTileMesh.geometry.dispose();\n        diningTableTileMesh.geometry = new THREE.BoxGeometry(config.width, 0.4, config.depth);\n\n        // Update border\n        const border = diningTableTileMesh.userData.borderMesh;\n        if (border) {\n          border.geometry.dispose();\n          border.geometry = new THREE.BoxGeometry(config.width + 0.3, 0.35, config.depth + 0.3);\n        }\n\n        // Update glow\n        diningTableTileGlow.geometry.dispose();\n        diningTableTileGlow.geometry = new THREE.BoxGeometry(\n          config.width + 0.6,\n          0.05,\n          config.depth + 0.6\n        );\n\n        // Update progress bar position\n        const progressBg = diningTableTileMesh.userData.progressBg;\n        if (progressBg) {\n          progressBg.geometry.dispose();\n          progressBg.geometry = new THREE.BoxGeometry(config.width - 0.5, 0.1, 0.4);\n          progressBg.position.z = config.z + config.depth / 2 - 0.5;\n        }\n\n        updateDiningTableTileProgress(index);\n        updateDiningTableTileCostDisplay(index);\n        updateSelectionHighlight();\n      }\n\n      function addNewEquipment(equipmentType = 'grill') {\n        // Find a position for the new equipment in the kitchen area (back of restaurant)\n        const x = (Math.random() - 0.5) * (RESTAURANT_WIDTH - 10);\n        const z = -RESTAURANT_DEPTH / 2 + 5 + Math.random() * 10;\n        // Kitchen area at back\n\n        // Add to config\n        const equipmentId = `${equipmentType}_${nextGrillId++}`;\n        window.gameConfig.grills.push({\n          id: equipmentId,\n          x,\n          z,\n          equipmentType: equipmentType,\n        });\n\n        // Create the equipment\n        const index = resourceNodes.length;\n        createResourceNodeAt(x, z, index, equipmentType);\n\n        // Register as editable\n        const node = resourceNodes[resourceNodes.length - 1];\n        const typeName =\n          equipmentType === 'drink_station'\n            ? 'Drink Station'\n            : equipmentType.charAt(0).toUpperCase() + equipmentType.slice(1);\n        const newEditable = {\n          mesh: node.group,\n          type: 'grill',\n          configKey: `grills.${index}`,\n          grillIndex: index,\n          grillId: equipmentId,\n          name: `${typeName} ${index + 1}`,\n          getData: () => ({\n            x: node.position.x,\n            z: node.position.z,\n          }),\n          setPosition: (newX, newZ) => {\n            node.group.position.x = newX;\n            node.group.position.z = newZ;\n            node.position.x = newX;\n            node.position.z = newZ;\n            node.group.rotation.y = Math.atan2(-newX, -newZ);\n            window.gameConfig.grills[index].x = newX;\n            window.gameConfig.grills[index].z = newZ;\n          },\n        };\n        editableObjects.push(newEditable);\n\n        // Select the new equipment\n        selectObject(newEditable);\n      }\n\n      function duplicateGrill(editable) {\n        const data = editable.getData();\n        const offset = 3;\n        const x = data.x + offset;\n        const z = data.z + offset;\n\n        // Get the equipment type from the original grill\n        const originalGrillConfig = window.gameConfig.grills[editable.grillIndex];\n        const equipmentType = originalGrillConfig?.equipmentType || 'grill';\n\n        // Add to config with same equipment type\n        const grillId = `${equipmentType}_${nextGrillId++}`;\n        window.gameConfig.grills.push({\n          id: grillId,\n          x,\n          z,\n          equipmentType: equipmentType,\n        });\n\n        // Create the equipment\n        const index = resourceNodes.length;\n        createResourceNodeAt(x, z, index, equipmentType);\n\n        // Register as editable\n        const node = resourceNodes[resourceNodes.length - 1];\n        const typeName =\n          equipmentType === 'drink_station'\n            ? 'Drink Station'\n            : equipmentType.charAt(0).toUpperCase() + equipmentType.slice(1);\n        const newEditable = {\n          mesh: node.group,\n          type: 'grill',\n          configKey: `grills.${index}`,\n          grillIndex: index,\n          grillId: grillId,\n          name: `${typeName} ${index + 1}`,\n          getData: () => ({\n            x: node.position.x,\n            z: node.position.z,\n          }),\n          setPosition: (newX, newZ) => {\n            node.group.position.x = newX;\n            node.group.position.z = newZ;\n            node.position.x = newX;\n            node.position.z = newZ;\n            node.group.rotation.y = Math.atan2(-newX, -newZ);\n            window.gameConfig.grills[index].x = newX;\n            window.gameConfig.grills[index].z = newZ;\n          },\n        };\n        editableObjects.push(newEditable);\n\n        // Select the new equipment\n        selectObject(newEditable);\n      }\n\n      function deleteGrill(editable) {\n        if (resourceNodes.length <= 1) {\n          lib.log('Cannot delete the last grill!');\n          return;\n        }\n\n        const index = editable.grillIndex;\n        const node = resourceNodes[index];\n\n        // Remove from scene\n        scene.remove(node.group);\n        node.group.traverse((child) => {\n          if (child.geometry) child.geometry.dispose();\n          if (child.material) {\n            if (Array.isArray(child.material)) {\n              child.material.forEach((mat) => mat.dispose());\n            } else {\n              child.material.dispose();\n            }\n          }\n        });\n\n        // Remove from arrays\n        resourceNodes.splice(index, 1);\n        window.gameConfig.grills.splice(index, 1);\n\n        // Remove from editableObjects\n        const editIndex = editableObjects.indexOf(editable);\n        if (editIndex > -1) {\n          editableObjects.splice(editIndex, 1);\n        }\n\n        // Update indices for remaining grills\n        editableObjects.forEach((obj) => {\n          if (obj.type === 'grill' && obj.grillIndex > index) {\n            obj.grillIndex--;\n            obj.name = `Grill ${obj.grillIndex + 1}`;\n          }\n        });\n\n        // Deselect\n        deselectObject();\n      }\n\n      function createResourceNodeAt(x, z, index, equipmentType = 'grill') {\n        const node = {\n          group: new THREE.Group(),\n          position: {\n            x,\n            z,\n          },\n          isReady: true,\n          regenTimer: 0,\n          grillMesh: null,\n          emptyMesh: null,\n          equipmentType: equipmentType,\n        };\n\n        node.group.position.set(x, 0, z);\n\n        // Create equipment based on type\n        node.grillMesh = new THREE.Group();\n        node.emptyMesh = new THREE.Group();\n\n        if (equipmentType === 'grill') {\n          createGrillEquipment(node);\n        } else if (equipmentType === 'fryer') {\n          createFryerEquipment(node);\n        } else if (equipmentType === 'drink_station') {\n          createDrinkStationEquipment(node);\n        } else {\n          // Default to grill\n          createGrillEquipment(node);\n        }\n\n        node.emptyMesh.visible = false;\n        node.group.add(node.grillMesh);\n        node.group.add(node.emptyMesh);\n\n        // Face forward (toward customers)\n        // allow saved rotation on grills (degrees) or default to facing forward\n        const grillCfg =\n          (window.gameConfig && window.gameConfig.grills && window.gameConfig.grills[index]) ||\n          null;\n        const grillRot =\n          (grillCfg && (grillCfg.rotation || grillCfg.rotationDeg || grillCfg.rotationY)) || 180;\n        node.group.rotation.y = THREE.MathUtils.degToRad(grillRot);\n\n        scene.add(node.group);\n        resourceNodes.push(node);\n      }\n\n      function rebuildSellZone() {\n        // Store references to cleanup\n        const oldCustomer = sellZoneMesh.userData.coinIndicator;\n\n        // Remove old meshes\n        scene.remove(sellZoneMesh);\n        scene.remove(sellZoneGlow);\n        if (oldCustomer) scene.remove(oldCustomer);\n\n        // Find and remove counter top\n        scene.children.forEach((child) => {\n          if (\n            child.geometry &&\n            child.geometry.type === 'CylinderGeometry' &&\n            child.position.y === 0.35\n          ) {\n            scene.remove(child);\n          }\n        });\n\n        // Recreate\n        createSellZone();\n\n        // Re-register editable\n        const sellEditable = editableObjects.find((obj) => obj.type === 'sellZone');\n        if (sellEditable) {\n          sellEditable.mesh = sellZoneMesh;\n        }\n\n        updateSelectionHighlight();\n      }\n\n      function rebuildMoneyZone() {\n        const config = window.gameConfig.moneyZone;\n\n        // Update main platform geometry\n        moneyZoneMesh.geometry.dispose();\n        moneyZoneMesh.geometry = new THREE.BoxGeometry(config.width, 0.4, config.depth);\n\n        // Update border\n        const border = moneyZoneMesh.userData.borderMesh;\n        if (border) {\n          border.geometry.dispose();\n          border.geometry = new THREE.BoxGeometry(config.width + 0.3, 0.35, config.depth + 0.3);\n        }\n\n        // Update glow\n        moneyZoneGlow.geometry.dispose();\n        moneyZoneGlow.geometry = new THREE.BoxGeometry(\n          config.width + 0.6,\n          0.05,\n          config.depth + 0.6\n        );\n\n        updateSelectionHighlight();\n        initMoneyGrid();\n      }\n\n      function rebuildSpendZone() {\n        const config = window.gameConfig.spendZone;\n\n        // Update main platform geometry\n        spendZoneMesh.geometry.dispose();\n        spendZoneMesh.geometry = new THREE.BoxGeometry(config.width, 0.4, config.depth);\n\n        // Update border\n        const border = spendZoneMesh.userData.borderMesh;\n        if (border) {\n          border.geometry.dispose();\n          border.geometry = new THREE.BoxGeometry(config.width + 0.3, 0.35, config.depth + 0.3);\n        }\n\n        // Update glow\n        spendZoneGlow.geometry.dispose();\n        spendZoneGlow.geometry = new THREE.BoxGeometry(\n          config.width + 0.6,\n          0.05,\n          config.depth + 0.6\n        );\n\n        // Update number indicator position\n        const number = spendZoneMesh.userData.numberIndicator;\n        if (number) {\n          number.position.z = config.z + config.depth / 2 - 0.5;\n        }\n\n        updateSelectionHighlight();\n      }\n\n      function rebuildTrashBin() {\n        const config = window.gameConfig.trashBin;\n        if (!config || !trashBinGlow) return;\n\n        // Update glow radius\n        trashBinGlow.geometry.dispose();\n        trashBinGlow.geometry = new THREE.RingGeometry(config.radius - 0.3, config.radius, 32);\n\n        updateSelectionHighlight();\n      }\n\n      // ============================================\n      // INITIALIZATION\n      // ============================================\n\n      function loadAssetImages() {\n        // Load coin icon for UI\n        const coinAsset = lib.getAsset('coin_icon');\n        if (coinAsset && coinAsset.url) {\n          document.getElementById('coin-icon').src = coinAsset.url;\n          document.querySelectorAll('.cost-icon').forEach((el) => {\n            el.src = coinAsset.url;\n          });\n        }\n      }\n\n      async function loadGameState() {\n        try {\n          const response = await lib.getUserGameState();\n          if (response.state) {\n            const savedState = response.state;\n\n            // Restore currency\n            if (savedState.currency !== undefined) {\n              currentCurrency = savedState.currency;\n              window.gameConfig.currency = savedState.currency;\n            }\n\n            // Restore staff hire states\n            if (savedState.staffHireTile) {\n              if (savedState.staffHireTile.staffHired !== undefined) {\n                window.gameConfig.staffHireTile.staffHired = savedState.staffHireTile.staffHired;\n              }\n              if (savedState.staffHireTile.extraStaffHired !== undefined) {\n                window.gameConfig.staffHireTile.extraStaffHired =\n                  savedState.staffHireTile.extraStaffHired;\n              }\n              if (savedState.staffHireTile.staffHired) {\n                createHiredStaffAtCashier();\n                if (staffHireTileMesh && !window.gameConfig.staffHireTile.extraStaffHired) {\n                  destroyStaffHireTileVisualsKeepConfig();\n                }\n              }\n              if (savedState.staffHireTile.moneyDeposited !== undefined) {\n                window.gameConfig.staffHireTile.moneyDeposited =\n                  savedState.staffHireTile.moneyDeposited;\n              }\n            }\n\n            if (savedState.cleanerStaffTile) {\n              if (savedState.cleanerStaffTile.staffHired !== undefined) {\n                window.gameConfig.cleanerStaffTile.staffHired =\n                  savedState.cleanerStaffTile.staffHired;\n                if (savedState.cleanerStaffTile.staffHired) {\n                  createHiredCleaner();\n                  if (cleanerStaffTileMesh) {\n                    destroyCleanerStaffTileVisuals();\n                  }\n                }\n              }\n              if (savedState.cleanerStaffTile.moneyDeposited !== undefined) {\n                window.gameConfig.cleanerStaffTile.moneyDeposited =\n                  savedState.cleanerStaffTile.moneyDeposited;\n              }\n            }\n            if (savedState.runnerStaffTile) {\n              if (savedState.runnerStaffTile.staffHired !== undefined) {\n                window.gameConfig.runnerStaffTile.staffHired =\n                  savedState.runnerStaffTile.staffHired;\n                if (savedState.runnerStaffTile.staffHired) {\n                  createHiredRunner();\n                  if (runnerStaffTileMesh) {\n                    destroyRunnerStaffTileVisuals();\n                  }\n                }\n              }\n              if (savedState.runnerStaffTile.moneyDeposited !== undefined) {\n                window.gameConfig.runnerStaffTile.moneyDeposited =\n                  savedState.runnerStaffTile.moneyDeposited;\n              }\n            }\n            if (savedState.cashierTile) {\n              if (savedState.cashierTile.unlocked !== undefined) {\n                window.gameConfig.cashierTile.unlocked = savedState.cashierTile.unlocked;\n                if (savedState.cashierTile.unlocked) {\n                  if (cashierTileMesh) {\n                    destroyCashierTileVisuals();\n                    createCashierStall();\n                  }\n                }\n              }\n              if (savedState.cashierTile.moneyDeposited !== undefined) {\n                window.gameConfig.cashierTile.moneyDeposited =\n                  savedState.cashierTile.moneyDeposited;\n              }\n            }\n\n            if (savedState.driveThruTile) {\n              if (savedState.driveThruTile.unlocked !== undefined) {\n                window.gameConfig.driveThruTile.unlocked = savedState.driveThruTile.unlocked;\n                if (savedState.driveThruTile.unlocked) {\n                  if (driveThruTileMesh) {\n                    destroyDriveThruTileVisuals();\n                    createDriveThruBooth();\n                  }\n                }\n              }\n              if (savedState.driveThruTile.moneyDeposited !== undefined) {\n                window.gameConfig.driveThruTile.moneyDeposited =\n                  savedState.driveThruTile.moneyDeposited;\n              }\n            }\n            if (savedState.trashTile) {\n              if (savedState.trashTile.unlocked !== undefined) {\n                window.gameConfig.trashTile.unlocked = savedState.trashTile.unlocked;\n                if (savedState.trashTile.unlocked) {\n                  if (trashTileMesh) {\n                    destroyTrashTileVisuals();\n                    createTrashBin();\n                  }\n                }\n              }\n              if (savedState.trashTile.moneyDeposited !== undefined) {\n                window.gameConfig.trashTile.moneyDeposited = savedState.trashTile.moneyDeposited;\n              }\n            }\n\n            if (savedState.vaultTile) {\n              if (savedState.vaultTile.unlocked !== undefined) {\n                window.gameConfig.vaultTile.unlocked = savedState.vaultTile.unlocked;\n                if (savedState.vaultTile.unlocked) {\n                  if (vaultTileMesh) {\n                    destroyVaultTileVisuals();\n                    createBankVault();\n                  }\n                }\n              }\n              if (savedState.vaultTile.moneyDeposited !== undefined) {\n                window.gameConfig.vaultTile.moneyDeposited = savedState.vaultTile.moneyDeposited;\n              }\n            }\n\n            if (savedState.fountainTile) {\n              if (savedState.fountainTile.unlocked !== undefined) {\n                window.gameConfig.fountainTile.unlocked = savedState.fountainTile.unlocked;\n                if (savedState.fountainTile.unlocked) {\n                  if (fountainTileMesh) {\n                    destroyFountainTileVisuals();\n                    createFountain();\n                  }\n                }\n              }\n              if (savedState.fountainTile.moneyDeposited !== undefined) {\n                window.gameConfig.fountainTile.moneyDeposited =\n                  savedState.fountainTile.moneyDeposited;\n              }\n            }\n\n            if (savedState.fountain2Tile) {\n              if (savedState.fountain2Tile.unlocked !== undefined) {\n                window.gameConfig.fountain2Tile.unlocked = savedState.fountain2Tile.unlocked;\n                if (savedState.fountain2Tile.unlocked) {\n                  if (fountain2TileMesh) {\n                    destroyFountain2TileVisuals();\n                    createFountain2();\n                  }\n                }\n              }\n              if (savedState.fountain2Tile.moneyDeposited !== undefined) {\n                window.gameConfig.fountain2Tile.moneyDeposited =\n                  savedState.fountain2Tile.moneyDeposited;\n              }\n            }\n\n            if (savedState.grillTile) {\n              if (savedState.grillTile.unlocked !== undefined) {\n                window.gameConfig.grillTile.unlocked = savedState.grillTile.unlocked;\n                if (savedState.grillTile.unlocked) {\n                  if (grillTileMesh) {\n                    destroyGrillTileVisuals();\n                    const grillCount = window.gameConfig.grills.length;\n                    for (let i = 0; i < grillCount; i++) {\n                      createResourceNode(i, grillCount);\n                    }\n                  }\n                }\n              }\n              if (savedState.grillTile.moneyDeposited !== undefined) {\n                window.gameConfig.grillTile.moneyDeposited = savedState.grillTile.moneyDeposited;\n              }\n            }\n\n            if (savedState.vaultStaffTile) {\n              if (savedState.vaultStaffTile.staffHired !== undefined) {\n                window.gameConfig.vaultStaffTile.staffHired = savedState.vaultStaffTile.staffHired;\n                if (savedState.vaultStaffTile.staffHired) {\n                  createHiredVaultCollector();\n                  if (vaultStaffTileMesh) {\n                    destroyVaultStaffTileVisuals();\n                  }\n                }\n              }\n              if (savedState.vaultStaffTile.moneyDeposited !== undefined) {\n                window.gameConfig.vaultStaffTile.moneyDeposited =\n                  savedState.vaultStaffTile.moneyDeposited;\n              }\n            }\n\n            // Restore dining tables\n            if (savedState.diningTables && Array.isArray(savedState.diningTables)) {\n              window.gameConfig.diningTables = savedState.diningTables;\n              // Tables will be created in the main initialization\n            }\n\n            // Restore dining table tiles money deposited and deleted state\n            if (savedState.diningTableTiles && Array.isArray(savedState.diningTableTiles)) {\n              savedState.diningTableTiles.forEach((tileState, index) => {\n                if (window.gameConfig.diningTableTiles[index]) {\n                  if (tileState.moneyDeposited !== undefined) {\n                    window.gameConfig.diningTableTiles[index].moneyDeposited =\n                      tileState.moneyDeposited;\n                  }\n                  if (tileState.deleted !== undefined) {\n                    window.gameConfig.diningTableTiles[index].deleted = tileState.deleted;\n                    if (tileState.deleted) {\n                      destroyDiningTableTileVisuals(index);\n                    }\n                  }\n                }\n              });\n            }\n\n            // Restore vault balance and upgrades\n            if (savedState.vaultBalance !== undefined) {\n              vaultBalance = savedState.vaultBalance;\n              currentCurrency = vaultBalance;\n              window.gameConfig.currency = vaultBalance;\n              if (window.gameConfig.bankVault) {\n                window.gameConfig.bankVault.balance = vaultBalance;\n              }\n            }\n\n            if (savedState.vaultUpgrades) {\n              window.gameConfig.vaultUpgrades = savedState.vaultUpgrades;\n              // Apply VIP bonus from vault upgrades\n              if (savedState.vaultUpgrades.tips) {\n                if (!window.gameConfig.vipCustomers) window.gameConfig.vipCustomers = {};\n                window.gameConfig.vipCustomers.spawnChance =\n                  0.15 + savedState.vaultUpgrades.tips * 0.05;\n              }\n            }\n\n            // Restore prestige\n            if (savedState.prestige) {\n              if (!window.gameConfig.prestige) {\n                window.gameConfig.prestige = {\n                  baseCost: 5000,\n                  costMultiplier: 2.5,\n                  profitBonus: 0.1,\n                };\n              }\n              window.gameConfig.prestige.level = savedState.prestige.level || 0;\n            }\n\nif (savedState.selfOrderKiosks) {\n              window.gameConfig.selfOrderKiosks.forEach((config, index) => {\n                if (savedState.selfOrderKiosks[index] && savedState.selfOrderKiosks[index].deleted !== undefined) {\n                  config.deleted = savedState.selfOrderKiosks[index].deleted;\n                  if (config.deleted && selfOrderKiosks[index] && selfOrderKiosks[index].mesh) {\n                    deleteSelfOrderKiosk({ configKey: `selfOrderKiosk_${index}`, config: config });\n                  }\n                }\n              });\n            }\n\n            // Calculate offline earnings (after vault balance is restored)\n            if (savedState.lastPlayedTime) {\n              calculateAndAwardOfflineEarnings(savedState);\n            }\n\n            lib.log('Game state loaded successfully');\n\n            // Update UI after loading\n            updateCurrencyUI();\n            updateUpgradeUI();\n            updateStackUI();\n            updateVaultUI();\n            if (staffHireTileMesh) {\n              updateStaffHireProgress();\n              updateStaffHireCostDisplay();\n            }\n            if (cleanerStaffTileMesh) {\n              updateCleanerStaffProgress();\n              updateCleanerStaffCostDisplay();\n            }\n            if (runnerStaffTileMesh) {\n              updateRunnerStaffProgress();\n              updateRunnerStaffCostDisplay();\n            }\n            if (vaultStaffTileMesh) {\n              updateVaultStaffProgress();\n              updateVaultStaffCostDisplay();\n            }\n            if (cashierTileMesh) {\n              updateCashierTileProgress();\n              updateCashierTileCostDisplay();\n            }\n            if (vaultTileMesh) {\n              updateVaultTileProgress();\n              updateVaultTileCostDisplay();\n            }\n            if (grillTileMesh) {\n              updateGrillTileProgress();\n              updateGrillTileCostDisplay();\n            }\n            diningTableTileMeshes.forEach((tileEntry, index) => {\n              updateDiningTableTileProgress(index);\n              updateDiningTableTileCostDisplay(index);\n            });\n          }\n        } catch (error) {\n          lib.log('Failed to load game state: ' + error.message);\n        }\n      }\n\n      async function performAutosave() {\n        try {\n          // Validate and sanitize all data before saving\n          const saveData = {\n            currency: Number.isFinite(currentCurrency) ? currentCurrency : 0,\n            lastPlayedTime: Date.now(),\n            upgrades: {\n              capacity: window.gameConfig.upgrades?.capacity?.level || 0,\n              speed: window.gameConfig.upgrades?.speed?.level || 0,\n              collection: window.gameConfig.upgrades?.collection?.level || 0,\n            },\n            cashierTile: {\n              unlocked: window.gameConfig.cashierTile?.unlocked || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.cashierTile?.moneyDeposited)\n                ? window.gameConfig.cashierTile.moneyDeposited\n                : 0,\n            },\n            grillTile: {\n              unlocked: window.gameConfig.grillTile?.unlocked || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.grillTile?.moneyDeposited)\n                ? window.gameConfig.grillTile.moneyDeposited\n                : 0,\n            },\n            driveThruTile: {\n              unlocked: window.gameConfig.driveThruTile?.unlocked || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.driveThruTile?.moneyDeposited)\n                ? window.gameConfig.driveThruTile.moneyDeposited\n                : 0,\n            },\n            trashTile: {\n              unlocked: window.gameConfig.trashTile?.unlocked || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.trashTile?.moneyDeposited)\n                ? window.gameConfig.trashTile.moneyDeposited\n                : 0,\n            },\n            vaultTile: {\n              unlocked: window.gameConfig.vaultTile?.unlocked || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.vaultTile?.moneyDeposited)\n                ? window.gameConfig.vaultTile.moneyDeposited\n                : 0,\n            },\n            fountainTile: {\n              unlocked: window.gameConfig.fountainTile?.unlocked || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.fountainTile?.moneyDeposited)\n                ? window.gameConfig.fountainTile.moneyDeposited\n                : 0,\n            },\n            fountain2Tile: {\n              unlocked: window.gameConfig.fountain2Tile?.unlocked || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.fountain2Tile?.moneyDeposited)\n                ? window.gameConfig.fountain2Tile.moneyDeposited\n                : 0,\n            },\n            staffHireTile: {\n              staffHired: window.gameConfig.staffHireTile?.staffHired || false,\n              extraStaffHired: window.gameConfig.staffHireTile?.extraStaffHired || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.staffHireTile?.moneyDeposited)\n                ? window.gameConfig.staffHireTile.moneyDeposited\n                : 0,\n            },\n            cleanerStaffTile: {\n              staffHired: window.gameConfig.cleanerStaffTile?.staffHired || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.cleanerStaffTile?.moneyDeposited)\n                ? window.gameConfig.cleanerStaffTile.moneyDeposited\n                : 0,\n            },\n            runnerStaffTile: {\n              staffHired: window.gameConfig.runnerStaffTile?.staffHired || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.runnerStaffTile?.moneyDeposited)\n                ? window.gameConfig.runnerStaffTile.moneyDeposited\n                : 0,\n            },\n            vaultStaffTile: {\n              staffHired: window.gameConfig.vaultStaffTile?.staffHired || false,\n              moneyDeposited: Number.isFinite(window.gameConfig.vaultStaffTile?.moneyDeposited)\n                ? window.gameConfig.vaultStaffTile.moneyDeposited\n                : 0,\n            },\n            diningTables: Array.isArray(window.gameConfig.diningTables)\n              ? window.gameConfig.diningTables.map((table) => ({\n                  id: table.id || '',\n                  x: Number.isFinite(table.x) ? table.x : 0,\n                  z: Number.isFinite(table.z) ? table.z : 0,\n                  occupied: !!table.occupied,\n                  hasTrash: !!table.hasTrash,\n                }))\n              : [],\n            diningTableTiles: Array.isArray(window.gameConfig.diningTableTiles)\n              ? window.gameConfig.diningTableTiles.map((tile) => ({\n                  moneyDeposited: Number.isFinite(tile.moneyDeposited) ? tile.moneyDeposited : 0,\n                  deleted: !!tile.deleted,\n                }))\n              : [],\n            vaultBalance: Number.isFinite(vaultBalance) ? vaultBalance : 0,\n            vaultUpgrades: window.gameConfig.vaultUpgrades || {\n              earnings: 0,\n              capacity: 0,\n              tips: 0,\n              speed: 0,\n              cook: 0,\n              serve: 0,\n              cashpile: 0,\n            },\nprestige: {\n              level: window.gameConfig.prestige?.level || 0,\n              profitBonus: window.gameConfig.prestige?.profitBonus || 0.1,\n            },\n            selfOrderKiosks: window.gameConfig.selfOrderKiosks\n              ? window.gameConfig.selfOrderKiosks.map((k) => ({ deleted: k.deleted || false }))\n              : [],\n          };\n\n          // Await the save operation\n          await lib.saveUserGameState(saveData);\n          lib.log('Game autosaved successfully');\n        } catch (error) {\n          lib.log('Autosave failed: ' + error.message);\n        }\n      }\n\n      function initGameState() {\n        currentCurrency = window.gameConfig.currency || 0;\n        currentStackCount = 0;\n        currentMoneyCount = 0;\n        incomingBurgers = 0;\n        incomingMoney = 0;\n        playerStack = [];\n        moneyStack = [];\n        cashierBurgerStack = [];\n        cashierCashPile = [];\n        cashierEarnings = 0;\n        customerQueue = [];\n        customerSpawnTimer = 0;\n        cashierServeTimer = 0;\n\n// Initialize drive-thru state\n        driveThruBurgerStack = [];\n        driveThruCashPile = [];\n        driveThruCarQueue = [];\n        driveThruCarSpawnTimer = 0;\n        driveThruServeTimer = 0;\n\n        // Initialize self-order kiosks\n        initSelfOrderKiosks();\n\n        // Initialize money grid tracking\n        initMoneyGrid();\n\n        // Give starting cash if configured\n        const startingCash = window.gameConfig.settings.startingCash || 0;\n        if (startingCash > 0 && vaultBalance === 0) {\n          modifyVaultBalance(startingCash);\n        }\n\n        updateCurrencyUI();\n        updateStackUI();\n        updateMoneyStackUI();\n        updateCashierUI();\n        updateUpgradeUI();\n      }\n\n      function setupGameParameters() {\n        lib.showGameParameters({\n          name: 'Game Settings',\n          params: {\n            'Player Color': {\n              key: 'gameConfig.player.color',\n              type: 'color',\n              onChange: (value) => {\n                window.gameConfig.player.color = value;\n                if (player && player.children[0]) {\n                  player.children[0].material.color.set(value);\n                }\n              },\n            },\n            'Extra Cashier Hire Cost': {\n              key: 'gameConfig.staffHireTile.extraHireCost',\n              type: 'slider',\n              min: 150,\n              max: 2000,\n              step: 50,\n              onChange: (value) => {\n                if (!window.gameConfig.staffHireTile) window.gameConfig.staffHireTile = {};\n                window.gameConfig.staffHireTile.extraHireCost = value;\n                if (staffHireTileMesh) {\n                  updateStaffHireProgress();\n                  updateStaffHireCostDisplay();\n                }\n              },\n            },\n            'Base Speed': {\n              key: 'gameConfig.player.baseSpeed',\n              type: 'slider',\n              min: 5,\n              max: 20,\n              step: 1,\n              onChange: (value) => {\n                window.gameConfig.player.baseSpeed = value;\n              },\n            },\n            'Base Capacity': {\n              key: 'gameConfig.player.baseCapacity',\n              type: 'slider',\n              min: 5,\n              max: 50,\n              step: 5,\n              onChange: (value) => {\n                window.gameConfig.player.baseCapacity = value;\n                updateStackUI();\n              },\n            },\n            'Collection Time': {\n              key: 'gameConfig.player.baseCollectionTime',\n              type: 'slider',\n              min: 0.5,\n              max: 3,\n              step: 0.1,\n              onChange: (value) => {\n                window.gameConfig.player.baseCollectionTime = value;\n              },\n            },\n            'Regen Time': {\n              key: 'gameConfig.resources.regenerationTime',\n              type: 'slider',\n              min: 1,\n              max: 10,\n              step: 0.5,\n              onChange: (value) => {\n                window.gameConfig.resources.regenerationTime = value;\n              },\n            },\n            'Burger Price': {\n              key: 'gameConfig.resources.itemValue',\n              type: 'slider',\n              min: 5,\n              max: 100,\n              step: 5,\n              onChange: (value) => {\n                window.gameConfig.resources.itemValue = value;\n              },\n            },\n            'Paper Money Value': {\n              key: 'gameConfig.settings.paperMoneyValue',\n              type: 'slider',\n              min: 5,\n              max: 50,\n              step: 5,\n              onChange: (value) => {\n                window.gameConfig.settings.paperMoneyValue = value;\n              },\n            },\n            'Vacuum Speed': {\n              key: 'gameConfig.settings.vacuumSpeed',\n              type: 'slider',\n              min: 5,\n              max: 30,\n              step: 1,\n              onChange: (value) => {\n                window.gameConfig.settings.vacuumSpeed = value;\n              },\n            },\n            'Cashier Tile Cost': {\n              key: 'gameConfig.cashierTile.cost',\n              type: 'slider',\n              min: 50,\n              max: 1000,\n              step: 50,\n              onChange: (value) => {\n                window.gameConfig.cashierTile.cost = value;\n                if (cashierTileMesh) {\n                  updateCashierTileProgress();\n                  updateCashierTileCostDisplay();\n                }\n              },\n            },\n            'Grill Tile Cost': {\n              key: 'gameConfig.grillTile.cost',\n              type: 'slider',\n              min: 50,\n              max: 1000,\n              step: 50,\n              onChange: (value) => {\n                window.gameConfig.grillTile.cost = value;\n                if (grillTileMesh) {\n                  updateGrillTileProgress();\n                  updateGrillTileCostDisplay();\n                }\n              },\n            },\n            'Drive-Thru Tile Cost': {\n              key: 'gameConfig.driveThruTile.cost',\n              type: 'slider',\n              min: 50,\n              max: 2000,\n              step: 50,\n              onChange: (value) => {\n                window.gameConfig.driveThruTile.cost = value;\n                if (driveThruTileMesh) {\n                  updateDriveThruTileProgress();\n                  updateDriveThruTileCostDisplay();\n                }\n              },\n            },\n            'Trash Tile Cost': {\n              key: 'gameConfig.trashTile.cost',\n              type: 'slider',\n              min: 50,\n              max: 1000,\n              step: 50,\n              onChange: (value) => {\n                window.gameConfig.trashTile.cost = value;\n                if (trashTileMesh) {\n                  updateTrashTileProgress();\n                  updateTrashTileCostDisplay();\n                }\n              },\n            },\n            'Vault Tile Cost': {\n              key: 'gameConfig.vaultTile.cost',\n              type: 'slider',\n              min: 500,\n              max: 5000,\n              step: 100,\n              onChange: (value) => {\n                window.gameConfig.vaultTile.cost = value;\n                if (vaultTileMesh) {\n                  updateVaultTileProgress();\n                  updateVaultTileCostDisplay();\n                }\n              },\n            },\n            'Cashier Staff Price': {\n              key: 'gameConfig.staffHireTile.hireCost',\n              type: 'slider',\n              min: 100,\n              max: 2000,\n              step: 50,\n              onChange: (value) => {\n                window.gameConfig.staffHireTile.hireCost = value;\n                if (staffHireTileMesh) {\n                  updateStaffHireProgress();\n                  updateStaffHireCostDisplay();\n                }\n              },\n            },\n            'Cleaner Staff Price': {\n              key: 'gameConfig.cleanerStaffTile.hireCost',\n              type: 'slider',\n              min: 100,\n              max: 2000,\n              step: 50,\n              onChange: (value) => {\n                window.gameConfig.cleanerStaffTile.hireCost = value;\n                if (cleanerStaffTileMesh) {\n                  updateCleanerStaffProgress();\n                  updateCleanerStaffCostDisplay();\n                }\n              },\n            },\n            'Runner Staff Price': {\n              key: 'gameConfig.runnerStaffTile.hireCost',\n              type: 'slider',\n              min: 100,\n              max: 2000,\n              step: 50,\n              onChange: (value) => {\n                window.gameConfig.runnerStaffTile.hireCost = value;\n                if (runnerStaffTileMesh) {\n                  updateRunnerStaffProgress();\n                  updateRunnerStaffCostDisplay();\n                }\n              },\n            },\n            'Vault Staff Price': {\n              key: 'gameConfig.vaultStaffTile.hireCost',\n              type: 'slider',\n              min: 100,\n              max: 2000,\n              step: 50,\n              onChange: (value) => {\n                window.gameConfig.vaultStaffTile.hireCost = value;\n                if (vaultStaffTileMesh) {\n                  updateVaultStaffProgress();\n                  updateVaultStaffCostDisplay();\n                }\n              },\n            },\n            'Prestige Level': {\n              key: 'gameConfig.prestige.level',\n              type: 'slider',\n              min: 0,\n              max: 10,\n              step: 1,\n              onChange: (value) => {\n                if (!window.gameConfig.prestige)\n                  window.gameConfig.prestige = {\n                    baseCost: 5000,\n                    costMultiplier: 2.5,\n                    profitBonus: 0.1,\n                  };\n                window.gameConfig.prestige.level = value;\n                updatePrestigeUI();\n              },\n            },\n            'Table Price': {\n              key: 'gameConfig.settings.defaultTableCost',\n              type: 'slider',\n              min: 100,\n              max: 1000,\n              step: 50,\n              onChange: (value) => {\n                window.gameConfig.settings.defaultTableCost = value;\n                // Update all existing dining table tiles\n                if (window.gameConfig.diningTableTiles) {\n                  window.gameConfig.diningTableTiles.forEach((tile, index) => {\n                    if (!tile.deleted) {\n                      tile.tableCost = value;\n                      updateDiningTableTileProgress(index);\n                      updateDiningTableTileCostDisplay(index);\n                    }\n                  });\n                }\n              },\n            },\n            'Music Enabled': {\n              key: 'gameConfig.settings.musicEnabled',\n              type: 'checkbox',\n              onChange: (value) => {\n                window.gameConfig.settings.musicEnabled = value;\n                if (value) {\n                  playMusic();\n                } else {\n                  stopMusic();\n                }\n              },\n            },\n            'Autosave Enabled': {\n              key: 'gameConfig.settings.autosaveEnabled',\n              type: 'checkbox',\n              onChange: (value) => {\n                window.gameConfig.settings.autosaveEnabled = value;\n              },\n            },\n            'Customers Leave Trash': {\n              key: 'gameConfig.settings.customersLeaveTrash',\n              type: 'checkbox',\n              onChange: (value) => {\n                window.gameConfig.settings.customersLeaveTrash = value;\n              },\n            },\n            'VIP Spawn Chance': {\n              key: 'gameConfig.vipCustomers.spawnChance',\n              type: 'slider',\n              min: 0,\n              max: 0.5,\n              step: 0.05,\n              onChange: (value) => {\n                if (!window.gameConfig.vipCustomers) window.gameConfig.vipCustomers = {};\n                window.gameConfig.vipCustomers.spawnChance = value;\n              },\n            },\n            'VIP Tip Multiplier': {\n              key: 'gameConfig.vipCustomers.tipMultiplier',\n              type: 'slider',\n              min: 1,\n              max: 5,\n              step: 0.5,\n              onChange: (value) => {\n                if (!window.gameConfig.vipCustomers) window.gameConfig.vipCustomers = {};\n                window.gameConfig.vipCustomers.tipMultiplier = value;\n              },\n            },\n            'Rush Hour Enabled': {\n              key: 'gameConfig.rushHour.enabled',\n              type: 'checkbox',\n              onChange: (value) => {\n                if (!window.gameConfig.rushHour) window.gameConfig.rushHour = {};\n                window.gameConfig.rushHour.enabled = value;\n                if (!value) {\n                  isRushHour = false;\n                  hideRushHourIndicator();\n                }\n              },\n            },\n            'Rush Duration (sec)': {\n              key: 'gameConfig.rushHour.duration',\n              type: 'slider',\n              min: 15,\n              max: 60,\n              step: 5,\n              onChange: (value) => {\n                if (!window.gameConfig.rushHour) window.gameConfig.rushHour = {};\n                window.gameConfig.rushHour.duration = value;\n              },\n            },\n            'Rush Cooldown (sec)': {\n              key: 'gameConfig.rushHour.cooldown',\n              type: 'slider',\n              min: 30,\n              max: 120,\n              step: 10,\n              onChange: (value) => {\n                if (!window.gameConfig.rushHour) window.gameConfig.rushHour = {};\n                window.gameConfig.rushHour.cooldown = value;\n              },\n            },\n            'Rush Spawn Speed': {\n              key: 'gameConfig.rushHour.spawnMultiplier',\n              type: 'slider',\n              min: 1.5,\n              max: 4,\n              step: 0.5,\n              onChange: (value) => {\n                if (!window.gameConfig.rushHour) window.gameConfig.rushHour = {};\n                window.gameConfig.rushHour.spawnMultiplier = value;\n              },\n            },\n            'Rush Tip Bonus': {\n              key: 'gameConfig.rushHour.tipBonus',\n              type: 'slider',\n              min: 1,\n              max: 2,\n              step: 0.1,\n              onChange: (value) => {\n                if (!window.gameConfig.rushHour) window.gameConfig.rushHour = {};\n                window.gameConfig.rushHour.tipBonus = value;\n              },\n            },\n            'Max Total Customers': {\n              key: 'gameConfig.settings.maxTotalCustomers',\n              type: 'slider',\n              min: 20,\n              max: 120,\n              step: 5,\n              onChange: (value) => {\n                window.gameConfig.settings.maxTotalCustomers = value;\n              },\n            },\n            'Dining Floor Tile': {\n              key: 'gameConfig.settings.diningFloorAsset',\n              type: 'string',\n              onChange: (value) => {\n                window.gameConfig.settings.diningFloorAsset = value;\n                lib.log('Dining floor asset changed to: ' + value + ' (reload to see changes)');\n              },\n            },\n            'Kitchen Floor Tile': {\n              key: 'gameConfig.settings.kitchenFloorAsset',\n              type: 'string',\n              onChange: (value) => {\n                window.gameConfig.settings.kitchenFloorAsset = value;\n                lib.log('Kitchen floor asset changed to: ' + value + ' (reload to see changes)');\n              },\n            },\n            'Outdoor Floor Color': {\n              key: 'gameConfig.settings.outdoorFloorColor',\n              type: 'color',\n              onChange: (value) => {\n                window.gameConfig.settings.outdoorFloorColor = value;\n                lib.log('Outdoor floor color changed to: ' + value + ' (reload to see changes)');\n              },\n            },\n            'Starting Cash': {\n              key: 'gameConfig.settings.startingCash',\n              type: 'slider',\n              min: 0,\n              max: 10000,\n              step: 100,\n              onChange: (value) => {\n                window.gameConfig.settings.startingCash = value;\n                if (gameMode === 'edit' || vaultBalance === 0) {\n                  vaultBalance = value;\n                  currentCurrency = value;\n                  if (window.gameConfig.bankVault) window.gameConfig.bankVault.balance = value;\n                  window.gameConfig.currency = value;\n                  updateVaultUI();\n                  updateCurrencyUI();\n                  updateUpgradeUI();\n                }\n              },\n            },\n          },\n        });\n      }\n\n      async function run(mode) {\n        lib.log('run() called. Mode: ' + mode);\n        gameMode = mode;\n\n        // Initialize loading progress (count total steps)\n        totalLoadSteps = 10;\n        // Adjust based on actual steps\n        completedLoadSteps = 0;\n\n        await initI18n();\n\n        // Setup game parameters UI\n        setupGameParameters();\n        updateLoadingProgress(1, 'loading.setup');\n\n        // Detect device type\n        const isTouchDevice = lib.shouldUseTouchControls();\n        isLowPerfDevice = !!isTouchDevice;\n        performanceSettings.pixelRatioCap = isLowPerfDevice ? 1 : 1.5;\n        performanceSettings.enableShadows = !isLowPerfDevice;\n        performanceSettings.shadowMapSize = isLowPerfDevice ? 256 : 512;\n        performanceSettings.maxDustParticles = isLowPerfDevice ? 4 : 7;\n        performanceSettings.cityLightUpdateInterval = isLowPerfDevice ? 0.25 : 0;\n        if (!isTouchDevice) {\n          document.body.classList.add('desktop');\n        }\n\n        // Apply mode-specific styles\n        if (mode === 'edit') {\n          document.body.classList.add('edit-mode');\n        } else {\n          document.body.classList.remove('edit-mode');\n        }\n\n        // Initialize Three.js\n        updateLoadingProgress(2, 'loading.initGraphics');\n        initThreeJS();\n\n        // Create game world\n        updateLoadingProgress(3, 'loading.building');\n        createGround();\n        createSellZone();\n        createMoneyZone();\n        createSpendZone();\n\n        // Load 3D stickman model before creating characters or loading state\n        updateLoadingProgress(4, 'loading.loadingModel');\n        try {\n          await loadStickmanModel();\n        } catch (e) {\n          lib.log('Failed to load stickman model, using fallback: ' + e.message);\n        }\n\n        // Load saved game state first (play mode only)\n        if (mode === 'play') {\n          await loadGameState();\n        }\n\n        const step = window.gameConfig.tutorialStep || 0;\n\n        if (\n          window.gameConfig.cashierTile &&\n          (!window.gameConfig.cashierTile.unlocked || mode === 'edit')\n        ) {\n          if (step >= 2 || mode === 'edit') createCashierTile();\n        }\n        if (\n          !window.gameConfig.cashierTile ||\n          window.gameConfig.cashierTile.unlocked ||\n          mode === 'edit'\n        ) {\n          createCashierStall();\n        }\n\n        if (\n          window.gameConfig.driveThruTile &&\n          (!window.gameConfig.driveThruTile.unlocked || mode === 'edit')\n        ) {\n          if (step >= 4 || mode === 'edit') createDriveThruTile();\n        }\n        if (\n          !window.gameConfig.driveThruTile ||\n          window.gameConfig.driveThruTile.unlocked ||\n          mode === 'edit'\n        ) {\n          createDriveThruBooth();\n        }\n\n        if (step >= 3 || mode === 'edit') createStaffHireTile();\n        if (step >= 4 || mode === 'edit') createCleanerStaffTile();\n        if (step >= 4 || mode === 'edit') createRunnerStaffTile();\n        if (step >= 4 || mode === 'edit') createVaultStaffTile();\n\n        if (\n          window.gameConfig.trashTile &&\n          (!window.gameConfig.trashTile.unlocked || mode === 'edit')\n        ) {\n          if (step >= 3 || mode === 'edit') createTrashTile();\n        }\n        if (\n          !window.gameConfig.trashTile ||\n          window.gameConfig.trashTile.unlocked ||\n          mode === 'edit'\n        ) {\n          createTrashBin();\n        }\n\n        if (\n          window.gameConfig.vaultTile &&\n          (!window.gameConfig.vaultTile.unlocked || mode === 'edit')\n        ) {\n          if (step >= 4 || mode === 'edit') createVaultTile();\n        }\n        if (\n          !window.gameConfig.vaultTile ||\n          window.gameConfig.vaultTile.unlocked ||\n          mode === 'edit'\n        ) {\n          createBankVault();\n        }\n\n        if (!window.gameConfig.fountainTile) {\n          window.gameConfig.fountainTile = {\n            x: -18,\n            z: -5,\n            width: 4,\n            depth: 4,\n            cost: 1000,\n            color: '#3498DB',\n            unlocked: false,\n            moneyDeposited: 0,\n            deleted: false,\n          };\n        }\n        if (!window.gameConfig.fountain) {\n          window.gameConfig.fountain = {\n            x: -18,\n            z: -5,\n            radius: 2.5,\n            deleted: false,\n          };\n        }\n        if (!window.gameConfig.fountain2) {\n          window.gameConfig.fountain2 = {\n            x: -12,\n            z: -3,\n            radius: 2.5,\n            deleted: false,\n          };\n        }\n        if (!window.gameConfig.fountain2Tile) {\n          window.gameConfig.fountain2Tile = {\n            x: -12,\n            z: -3,\n            width: 4,\n            depth: 4,\n            cost: 1000,\n            color: '#3498DB',\n            unlocked: false,\n            moneyDeposited: 0,\n            deleted: false,\n          };\n        }\n\n        if (\n          window.gameConfig.fountainTile &&\n          (!window.gameConfig.fountainTile.unlocked || mode === 'edit')\n        ) {\n          if (shouldShowUnlockTile('fountainTile') || mode === 'edit') createFountainTile();\n        }\n        if (\n          window.gameConfig.fountain2Tile &&\n          (!window.gameConfig.fountain2Tile.unlocked || mode === 'edit')\n        ) {\n          if (shouldShowUnlockTile('fountainTile') || mode === 'edit') createFountain2Tile();\n        }\n        if (\n          !window.gameConfig.fountainTile ||\n          window.gameConfig.fountainTile.unlocked ||\n          mode === 'edit'\n        ) {\n          createFountain();\n        }\n        if (\n          !window.gameConfig.fountain2Tile ||\n          window.gameConfig.fountain2Tile.unlocked ||\n          mode === 'edit'\n        ) {\n          createFountain2();\n        }\n\n        if (!window.gameConfig.selfOrderKioskTile) {\n          window.gameConfig.selfOrderKioskTile = {\n            x: -12,\n            z: 8,\n            width: 4,\n            depth: 4,\n            cost: 500,\n            color: '#2C3E50',\n            unlocked: false,\n            moneyDeposited: 0,\n            deleted: false,\n          };\n        }\n\n        // Initialize staff hire tile settings\n        if (window.gameConfig.staffHireTile) {\n          if (!window.gameConfig.staffHireTile.extraHireCost) {\n            window.gameConfig.staffHireTile.extraHireCost = Math.max(\n              150,\n              Math.round(window.gameConfig.staffHireTile.hireCost * 1.5)\n            );\n          }\n          if (window.gameConfig.staffHireTile.extraStaffHired === undefined) {\n            window.gameConfig.staffHireTile.extraStaffHired = false;\n          }\n        }\n\n        // Initialize cashier serve timers\n        cashierServeTimer2 = 0;\n\n        initSelfOrderKiosks();\n        createSelfOrderKiosks();\n        if (mode === 'edit') {\n          window.gameConfig.selfOrderKioskTiles.forEach((tile) => {\n            if (tile) tile.hidden = false;\n          });\n        } else if (!window.gameConfig.tutorialCompleted) {\n          window.gameConfig.selfOrderKioskTiles.forEach((tile) => {\n            if (tile) tile.hidden = true;\n          });\n        }\n        if (window.gameConfig.tutorialCompleted || mode === 'edit') {\n          createSelfOrderKioskTiles();\n        }\n\n        // Always show vault toggle\n        const vaultToggle = document.getElementById('vault-toggle');\n        if (vaultToggle) vaultToggle.style.display = 'flex';\n\n        // Create existing dining table tiles from config\n        if (window.gameConfig.diningTableTile && !window.gameConfig.diningTableTiles) {\n          window.gameConfig.diningTableTiles = [window.gameConfig.diningTableTile];\n          delete window.gameConfig.diningTableTile;\n        }\n\n        if (window.gameConfig.diningTableTiles) {\n          let tableTileCreated = false;\n          window.gameConfig.diningTableTiles.forEach((tile, index) => {\n            if (!tile.deleted) {\n              if (mode === 'edit' || (!tableTileCreated && (index === 0 || step >= 3))) {\n                createDiningTableTile(index);\n                if (mode !== 'edit') tableTileCreated = true;\n              }\n            }\n          });\n        }\n\n        // Create existing dining tables from config\n        if (window.gameConfig.diningTables) {\n          window.gameConfig.diningTables.forEach((table, index) => {\n            createDiningTable(table, index);\n          });\n          nextTableId = window.gameConfig.diningTables.length;\n        }\n\n        createPlayer();\n\n        // Create resource nodes from config\n        updateLoadingProgress(5, 'loading.grills');\n        if (\n          window.gameConfig.grillTile &&\n          (!window.gameConfig.grillTile.unlocked || mode === 'edit')\n        ) {\n          if (step >= 1 || mode === 'edit') createGrillTile();\n        }\n        if (\n          !window.gameConfig.grillTile ||\n          window.gameConfig.grillTile.unlocked ||\n          mode === 'edit'\n        ) {\n          const grillCount = window.gameConfig.grills.length;\n          for (let i = 0; i < grillCount; i++) {\n            createResourceNode(i, grillCount);\n          }\n        }\n\n        // Load assets\n        updateLoadingProgress(6, 'loading.images');\n        loadAssetImages();\n\n        // Initialize audio\n        updateLoadingProgress(6, 'loading.sounds');\n        await initAudio();\n\n        // Cache DOM elements for performance\n        cacheDOMElements();\n\n        // Initialize game state\n        updateLoadingProgress(7, 'loading.preparing');\n        initGameState();\n\n        // Setup input\n        updateLoadingProgress(8, 'loading.controls');\n        setupKeyboardInput();\n        if (isTouchDevice) {\n          try {\n            setupTouchInput();\n          } catch (e) {\n            lib.log('setupTouchInput failed: ' + (e && e.message ? e.message : e));\n          }\n        }\n\n        // Setup upgrade buttons\n        updateLoadingProgress(9, 'loading.finalizing');\n        setupUpgradeButtons();\n\n        // Initialize level editor (for edit mode)\n        if (mode === 'edit') {\n          initLevelEditor();\n        }\n\n        // Start music in play mode\n        if (mode === 'play' && window.gameConfig.settings.musicEnabled) {\n          // Start music on first user interaction\n          const startMusic = () => {\n            playMusic();\n            document.removeEventListener('touchstart', startMusic);\n            document.removeEventListener('click', startMusic);\n            document.removeEventListener('keydown', startMusic);\n          };\n          document.addEventListener('touchstart', startMusic, {\n            once: true,\n          });\n          document.addEventListener('click', startMusic, {\n            once: true,\n          });\n          document.addEventListener('keydown', startMusic, {\n            once: true,\n          });\n        }\n\n        // Complete loading\n        updateLoadingProgress(10, 'loading.ready');\n\n        // Hide loading screen after a brief delay\n        setTimeout(() => {\n          hideLoadingScreen();\n        }, 300);\n\n        // Start game loop\n        lastTime = performance.now();\n        requestAnimationFrame(gameLoop);\n      }\n\n      // Helper: persist rotation value into window.gameConfig by a configKey path (e.g. 'grills.0' or 'cashierStall')\n      function setConfigRotationByKey(configKey, degrees) {\n        try {\n          const parts = String(configKey).split('.');\n          let obj = window.gameConfig;\n          for (let i = 0; i < parts.length; i++) {\n            const part = parts[i];\n            if (i === parts.length - 1) {\n              if (obj[part] === undefined) obj[part] = {};\n              obj[part].rotation = degrees;\n            } else {\n              if (obj[part] === undefined) obj[part] = {};\n              obj = obj[part];\n            }\n          }\n        } catch (e) {\n          lib.log('Failed to set config rotation: ' + e.message);\n        }\n      }\n\n      // Rotate currently selected editable object by degrees (snaps to 90\u00b0)\n      function rotateSelectedByDegrees(deltaDeg) {\n        if (!editorSelectedObject || !editorSelectedObject.mesh) return;\n        const mesh = editorSelectedObject.mesh;\n        // current rotation in degrees normalized\n        let currentDeg = THREE.MathUtils.radToDeg(mesh.rotation.y) || 0;\n        // normalize to nearest integer to avoid floating drift\n        currentDeg = Math.round(currentDeg);\n        let newDeg = currentDeg + deltaDeg;\n        // snap to 90-degree increments\n        newDeg = Math.round(newDeg / 90) * 90;\n        mesh.rotation.y = THREE.MathUtils.degToRad(newDeg);\n        // persist to config if possible\n        if (editorSelectedObject.configKey) {\n          setConfigRotationByKey(editorSelectedObject.configKey, newDeg);\n        } else if (editorSelectedObject.type && editorSelectedObject.getData) {\n          // try to find matching entry in gameConfig by equality of position (best-effort fallback)\n          try {\n            const data = editorSelectedObject.getData();\n            for (const key in window.gameConfig) {\n              if (!window.gameConfig.hasOwnProperty(key)) continue;\n              const entry = window.gameConfig[key];\n              if (entry && typeof entry === 'object') {\n                if (entry.x === data.x && entry.z === data.z) {\n                  entry.rotation = newDeg;\n                  break;\n                }\n              }\n            }\n          } catch (e) {}\n        }\n        updateSelectionHighlight();\n      }\n\n      // Wire rotate buttons and keyboard shortcut (editor mode only) if DOM exists\n      (function wireRotationControls() {\n        // Buttons may not exist in non-edit mode, so guard selections\n        const rotL = document.getElementById('rotate-left-btn');\n        const rotR = document.getElementById('rotate-right-btn');\n        if (rotL) {\n          rotL.addEventListener('click', () => rotateSelectedByDegrees(-90));\n        }\n        if (rotR) {\n          rotR.addEventListener('click', () => rotateSelectedByDegrees(90));\n        }\n        // Keyboard 'R' rotates +90 (edit mode only)\n        window.addEventListener('keydown', (e) => {\n          if (gameMode !== 'edit') return;\n          if (e.key.toLowerCase() === 'r') {\n            rotateSelectedByDegrees(90);\n          }\n        });\n      })();\n\n      // Expose run function to window for iframe runtime\n      if (typeof window !== 'undefined') {\n        window.run = run;\n      }\n    </script>\n  </body>\n</html>\n", "game_config": "{\n  \"stickmanModelURL\": \"stickman-animated_compressed.glb\",\n  \"player\": {\n    \"color\": \"#E74C3C\",\n    \"startX\": -16,\n    \"startZ\": 13,\n    \"baseSpeed\": 10,\n    \"baseCapacity\": 10,\n    \"baseCollectionTime\": 0.5\n  },\n  \"resources\": {\n    \"regenerationTime\": 4,\n    \"itemValue\": 20\n  },\n  \"grills\": [\n    {\n      \"id\": \"grill_1\",\n      \"x\": -5,\n      \"z\": -23,\n      \"equipmentType\": \"grill\",\n      \"rotation\": -180\n    },\n    {\n      \"id\": \"grill_2\",\n      \"x\": -2,\n      \"z\": -23,\n      \"equipmentType\": \"grill\",\n      \"rotation\": -180\n    },\n    {\n      \"id\": \"grill_3\",\n      \"x\": 1,\n      \"z\": -23,\n      \"equipmentType\": \"grill\",\n      \"rotation\": -180\n    },\n    {\n      \"id\": \"fryer_0\",\n      \"x\": 7,\n      \"z\": -23,\n      \"equipmentType\": \"fryer\",\n      \"rotation\": -360\n    },\n    {\n      \"id\": \"fryer_1\",\n      \"x\": 4,\n      \"z\": -23,\n      \"equipmentType\": \"fryer\",\n      \"rotation\": -180\n    },\n    {\n      \"id\": \"drink_0\",\n      \"x\": 7,\n      \"z\": -7,\n      \"equipmentType\": \"drink_station\",\n      \"rotation\": 270\n    },\n    {\n      \"id\": \"drink_1\",\n      \"x\": 7,\n      \"z\": -11,\n      \"equipmentType\": \"drink_station\",\n      \"rotation\": 630\n    },\n    {\n      \"id\": \"grill_5\",\n      \"x\": -8,\n      \"z\": -23,\n      \"equipmentType\": \"grill\",\n      \"rotation\": -180\n    },\n    {\n      \"id\": \"drink_station_10\",\n      \"x\": -7,\n      \"z\": -7,\n      \"equipmentType\": \"drink_station\",\n      \"rotation\": -270\n    },\n    {\n      \"id\": \"drink_station_11\",\n      \"x\": -7,\n      \"z\": -11,\n      \"equipmentType\": \"drink_station\",\n      \"rotation\": -270\n    }\n  ],\n  \"sellZone\": {\n    \"x\": 9.841845960759247,\n    \"z\": -9.208578872021114,\n    \"radius\": 6,\n    \"color\": \"#27AE60\",\n    \"deleted\": true\n  },\n  \"moneyZone\": {\n    \"x\": 12,\n    \"z\": 20,\n    \"width\": 10,\n    \"depth\": 8,\n    \"color\": \"#FFD700\",\n    \"deleted\": true\n  },\n  \"spendZone\": {\n    \"x\": -11.488736004568842,\n    \"z\": 20.131738840172076,\n    \"width\": 10,\n    \"depth\": 8,\n    \"color\": \"#E74C3C\",\n    \"deleted\": true\n  },\n  \"upgrades\": {\n    \"capacity\": {\n      \"level\": 0,\n      \"baseCost\": 50,\n      \"costMultiplier\": 1.5,\n      \"bonusPerLevel\": 5\n    },\n    \"speed\": {\n      \"level\": 0,\n      \"baseCost\": 75,\n      \"costMultiplier\": 1.6,\n      \"bonusPerLevel\": 2\n    },\n    \"collection\": {\n      \"level\": 0,\n      \"baseCost\": 100,\n      \"costMultiplier\": 1.7,\n      \"reductionPerLevel\": 0.15\n    }\n  },\n  \"currency\": 9500,\n  \"settings\": {\n    \"groundColor\": \"#D4A574\",\n    \"skyColor\": \"#FFF5E6\",\n    \"musicEnabled\": true,\n    \"customersLeaveTrash\": true,\n    \"autosaveEnabled\": true,\n    \"defaultTableCost\": 50,\n    \"paperMoneyValue\": 10,\n    \"vacuumSpeed\": 0,\n    \"diningFloorAsset\": \"floor_tile\",\n    \"kitchenFloorAsset\": \"kitchen_floor_tile\",\n    \"outdoorFloorColor\": \"#4d4e46\",\n    \"startingCash\": 1000,\n    \"collectSoundPitchVariance\": 0.1,\n    \"bugFix\": false,\n    \"maxTotalCustomers\": 40\n  },\n  \"cashierStall\": {\n    \"x\": 0,\n    \"z\": -3,\n    \"width\": 8,\n    \"depth\": 4,\n    \"maxBurgers\": 100,\n    \"maxMoney\": 100,\n    \"serveTime\": 0.8,\n    \"customerSpawnRate\": 2.5,\n    \"maxCustomers\": 6,\n    \"color\": \"#8B4513\"\n  },\n  \"staffHireTile\": {\n    \"x\": 5,\n    \"z\": -15,\n    \"width\": 4,\n    \"depth\": 4,\n    \"hireCost\": 100,\n    \"color\": \"#9B59B6\",\n    \"staffHired\": false,\n    \"moneyDeposited\": 0,\n    \"cost\": 100\n  },\n  \"diningTables\": [],\n  \"diningTableTile\": \"__DELETE__\",\n  \"diningTableTiles\": [\n    {\n      \"width\": 4,\n      \"x\": -15.016490604550816,\n      \"z\": 16.133036187743055,\n      \"tableCost\": 100,\n      \"depth\": 4,\n      \"moneyDeposited\": 0,\n      \"color\": \"#3498DB\",\n      \"deleted\": false,\n      \"cost\": 100\n    },\n    {\n      \"width\": 4,\n      \"x\": -15.14183591719429,\n      \"z\": 23.898387879930507,\n      \"tableCost\": 100,\n      \"depth\": 4,\n      \"moneyDeposited\": 0,\n      \"color\": \"#3498DB\",\n      \"deleted\": false,\n      \"cost\": 100\n    },\n    {\n      \"width\": 4,\n      \"x\": 6.132096347865142,\n      \"z\": 16.127572994534983,\n      \"tableCost\": 100,\n      \"depth\": 4,\n      \"moneyDeposited\": 0,\n      \"color\": \"#3498DB\",\n      \"deleted\": false,\n      \"cost\": 100\n    },\n    {\n      \"width\": 4,\n      \"x\": -8.667152265934797,\n      \"z\": 23.31763681926224,\n      \"tableCost\": 100,\n      \"depth\": 4,\n      \"moneyDeposited\": 0,\n      \"color\": \"#3498DB\",\n      \"deleted\": false,\n      \"cost\": 100\n    },\n    {\n      \"width\": 4,\n      \"x\": -8.545166447414058,\n      \"z\": 16.01429074427949,\n      \"tableCost\": 100,\n      \"depth\": 4,\n      \"moneyDeposited\": 0,\n      \"color\": \"#3498DB\",\n      \"deleted\": false,\n      \"cost\": 100\n    },\n    {\n      \"width\": 4,\n      \"x\": 5.955364751646173,\n      \"z\": 22.87584780489358,\n      \"tableCost\": 100,\n      \"depth\": 4,\n      \"moneyDeposited\": 0,\n      \"color\": \"#3498DB\",\n      \"deleted\": false,\n      \"cost\": 100\n    },\n    {\n      \"width\": 4,\n      \"x\": 11.896793010569025,\n      \"z\": 16.391015807013854,\n      \"tableCost\": 100,\n      \"depth\": 4,\n      \"moneyDeposited\": 0,\n      \"color\": \"#3498DB\",\n      \"deleted\": false,\n      \"cost\": 100\n    },\n    {\n      \"width\": 4,\n      \"x\": 11.994271840852896,\n      \"z\": 22.772683039660294,\n      \"tableCost\": 100,\n      \"depth\": 4,\n      \"moneyDeposited\": 0,\n      \"color\": \"#3498DB\",\n      \"deleted\": false,\n      \"cost\": 100\n    }\n  ],\n  \"trashBin\": {\n    \"x\": 12,\n    \"z\": -20,\n    \"radius\": 2,\n    \"color\": \"#7F8C8D\"\n  },\n  \"cleanerStaffTile\": {\n    \"x\": -1,\n    \"z\": 21,\n    \"width\": 4,\n    \"depth\": 4,\n    \"hireCost\": 150,\n    \"color\": \"#3498DB\",\n    \"staffHired\": false,\n    \"moneyDeposited\": 0,\n    \"cost\": 150\n  },\n  \"runnerStaffTile\": {\n    \"color\": \"#E67E22\",\n    \"depth\": 4,\n    \"hireCost\": 300,\n    \"moneyDeposited\": 0,\n    \"staffHired\": false,\n    \"width\": 4,\n    \"x\": -5,\n    \"z\": -15,\n    \"cost\": 300\n  },\n  \"vipCustomers\": {\n    \"spawnChance\": 0.15,\n    \"tipMultiplier\": 3,\n    \"color\": \"#FFD700\"\n  },\n  \"rushHour\": {\n    \"enabled\": true,\n    \"duration\": 30,\n    \"cooldown\": 60,\n    \"spawnMultiplier\": 2.5,\n    \"tipBonus\": 1.5\n  },\n  \"menuItems\": [\n    {\n      \"id\": \"burger\",\n      \"name\": \"Burger\",\n      \"emoji\": \"\\ud83c\\udf54\",\n      \"value\": 100,\n      \"bunColor\": \"#DEB887\",\n      \"pattyColor\": \"#8B4513\",\n      \"toppingColor\": \"#32CD32\",\n      \"accentColor\": \"#FFA500\",\n      \"equipmentType\": \"grill\"\n    },\n    {\n      \"id\": \"bigmac\",\n      \"name\": \"Big Mac\",\n      \"emoji\": \"\\ud83c\\udf54\",\n      \"value\": 150,\n      \"bunColor\": \"#DEB887\",\n      \"pattyColor\": \"#8B4513\",\n      \"toppingColor\": \"#32CD32\",\n      \"accentColor\": \"#FFA500\",\n      \"equipmentType\": \"grill\"\n    },\n    {\n      \"id\": \"mcchicken\",\n      \"name\": \"McChicken\",\n      \"emoji\": \"\\ud83c\\udf57\",\n      \"value\": 120,\n      \"bunColor\": \"#F5DEB3\",\n      \"pattyColor\": \"#D4A574\",\n      \"toppingColor\": \"#90EE90\",\n      \"accentColor\": \"#FFFFFF\",\n      \"equipmentType\": \"grill\"\n    },\n    {\n      \"id\": \"fries\",\n      \"name\": \"Fries\",\n      \"emoji\": \"\\ud83c\\udf5f\",\n      \"value\": 60,\n      \"bunColor\": \"#FFD700\",\n      \"pattyColor\": \"#DAA520\",\n      \"toppingColor\": \"#FF0000\",\n      \"accentColor\": \"#FFFFFF\",\n      \"equipmentType\": \"fryer\"\n    },\n    {\n      \"id\": \"nuggets\",\n      \"name\": \"Nuggets\",\n      \"emoji\": \"\\ud83c\\udf57\",\n      \"value\": 80,\n      \"bunColor\": \"#DEB887\",\n      \"pattyColor\": \"#D4A574\",\n      \"toppingColor\": \"#FFD700\",\n      \"accentColor\": \"#CC0000\",\n      \"equipmentType\": \"fryer\"\n    },\n    {\n      \"id\": \"drink\",\n      \"name\": \"Drink\",\n      \"emoji\": \"\\ud83e\\udd64\",\n      \"value\": 40,\n      \"bunColor\": \"#FFFFFF\",\n      \"pattyColor\": \"#E74C3C\",\n      \"toppingColor\": \"#FFFFFF\",\n      \"accentColor\": \"#3498DB\",\n      \"equipmentType\": \"drink_station\"\n    },\n    {\n      \"id\": \"mcflurry\",\n      \"name\": \"McFlurry\",\n      \"emoji\": \"\\ud83c\\udf66\",\n      \"value\": 70,\n      \"bunColor\": \"#FFFAF0\",\n      \"pattyColor\": \"#3C2415\",\n      \"toppingColor\": \"#FFFFFF\",\n      \"accentColor\": \"#87CEEB\",\n      \"equipmentType\": \"drink_station\"\n    }\n  ],\n  \"driveThruBooth\": {\n    \"x\": 25,\n    \"z\": 0,\n    \"width\": 6,\n    \"depth\": 4,\n    \"maxBurgers\": 20,\n    \"serveTime\": 1,\n    \"carSpawnRate\": 4,\n    \"maxCars\": 4,\n    \"color\": \"#8B4513\"\n  },\n  \"bankVault\": {\n    \"x\": 19.156143651649913,\n    \"z\": -28,\n    \"width\": 5,\n    \"depth\": 4,\n    \"color\": \"#2C3E50\",\n    \"balance\": 9500\n  },\n  \"vaultUpgrades\": {\n    \"earnings\": 0,\n    \"capacity\": 0,\n    \"tips\": 0,\n    \"speed\": 0\n  },\n  \"vaultStaffTile\": {\n    \"x\": 20.765021614900473,\n    \"z\": -20.270689447321523,\n    \"width\": 4,\n    \"depth\": 4,\n    \"hireCost\": 100,\n    \"color\": \"#F1C40F\",\n    \"staffHired\": false,\n    \"moneyDeposited\": 0,\n    \"deleted\": true\n  },\n  \"prestige\": {\n    \"level\": 0,\n    \"profitBonus\": 0.1,\n    \"baseCost\": 5000,\n    \"costMultiplier\": 2.5\n  },\n  \"prestigeLayouts\": [\n    {\n      \"level\": 2,\n      \"grills\": [\n        {\n          \"id\": \"grill_1\",\n          \"x\": -12,\n          \"z\": -28,\n          \"equipmentType\": \"grill\"\n        },\n        {\n          \"id\": \"grill_2\",\n          \"x\": -6,\n          \"z\": -28,\n          \"equipmentType\": \"grill\"\n        },\n        {\n          \"id\": \"grill_3\",\n          \"x\": 0,\n          \"z\": -28,\n          \"equipmentType\": \"grill\"\n        },\n        {\n          \"id\": \"fryer_0\",\n          \"x\": 9.189076158220818,\n          \"z\": -27.99915651314427,\n          \"equipmentType\": \"fryer\"\n        },\n        {\n          \"id\": \"fryer_1\",\n          \"x\": 12,\n          \"z\": -28,\n          \"equipmentType\": \"fryer\"\n        },\n        {\n          \"id\": \"fryer_2\",\n          \"x\": 7.483807071767375,\n          \"z\": -25.629013524533505,\n          \"equipmentType\": \"fryer\"\n        },\n        {\n          \"id\": \"drink_0\",\n          \"x\": -15,\n          \"z\": -22,\n          \"equipmentType\": \"drink_station\"\n        },\n        {\n          \"id\": \"drink_1\",\n          \"x\": -9,\n          \"z\": -22,\n          \"equipmentType\": \"drink_station\"\n        },\n        {\n          \"id\": \"grill_4\",\n          \"x\": -3,\n          \"z\": -22,\n          \"equipmentType\": \"grill\"\n        },\n        {\n          \"id\": \"grill_5\",\n          \"x\": 3,\n          \"z\": -22,\n          \"equipmentType\": \"grill\"\n        }\n      ],\n      \"cashierStall\": {\n        \"x\": 0.28906841226326585,\n        \"z\": 5.04635963040284,\n        \"width\": 8,\n        \"depth\": 4,\n        \"maxBurgers\": 30,\n        \"serveTime\": 0.8,\n        \"customerSpawnRate\": 2.5,\n        \"maxCustomers\": 6,\n        \"color\": \"#8B4513\"\n      },\n      \"staffHireTile\": {\n        \"x\": -7.0955394260919995,\n        \"z\": 4.739915362829798,\n        \"width\": 4,\n        \"depth\": 4,\n        \"hireCost\": 100,\n        \"color\": \"#9B59B6\",\n        \"staffHired\": false,\n        \"moneyDeposited\": 0\n      },\n      \"cleanerStaffTile\": {\n        \"x\": -14.864825954115048,\n        \"z\": 4.336336887646887,\n        \"width\": 4,\n        \"depth\": 4,\n        \"hireCost\": 100,\n        \"color\": \"#3498DB\",\n        \"staffHired\": false,\n        \"moneyDeposited\": 0\n      },\n      \"runnerStaffTile\": {\n        \"color\": \"#E67E22\",\n        \"depth\": 4,\n        \"hireCost\": 100,\n        \"moneyDeposited\": 0,\n        \"staffHired\": false,\n        \"width\": 4,\n        \"x\": 15,\n        \"z\": -5\n      },\n      \"vaultStaffTile\": {\n        \"x\": 20.765021614900473,\n        \"z\": -20.270689447321523,\n        \"width\": 4,\n        \"depth\": 4,\n        \"hireCost\": 150,\n        \"color\": \"#F1C40F\",\n        \"staffHired\": false,\n        \"moneyDeposited\": 0\n      },\n      \"diningTableTiles\": [\n        {\n          \"width\": 4,\n          \"x\": -17.227867093078277,\n          \"z\": 26.46764854710877,\n          \"tableCost\": 300,\n          \"depth\": 4,\n          \"moneyDeposited\": 0,\n          \"color\": \"#3498DB\"\n        },\n        {\n          \"width\": 4,\n          \"x\": 12.466602103796491,\n          \"z\": 26.608752185146525,\n          \"tableCost\": 300,\n          \"depth\": 4,\n          \"moneyDeposited\": 0,\n          \"color\": \"#3498DB\",\n          \"deleted\": false\n        },\n        {\n          \"width\": 4,\n          \"x\": -1.3055000718761216,\n          \"z\": 26.166043916084593,\n          \"tableCost\": 300,\n          \"depth\": 4,\n          \"moneyDeposited\": 0,\n          \"color\": \"#3498DB\",\n          \"deleted\": false\n        },\n        {\n          \"width\": 4,\n          \"x\": 5.950127325117245,\n          \"z\": 26.665006903200993,\n          \"tableCost\": 300,\n          \"depth\": 4,\n          \"moneyDeposited\": 0,\n          \"color\": \"#3498DB\",\n          \"deleted\": false\n        },\n        {\n          \"width\": 4,\n          \"x\": -9.710922262460203,\n          \"z\": 26.42274786429538,\n          \"tableCost\": 300,\n          \"depth\": 4,\n          \"moneyDeposited\": 0,\n          \"color\": \"#3498DB\",\n          \"deleted\": false\n        }\n      ],\n      \"trashBin\": {\n        \"x\": 8.718287561121713,\n        \"z\": -13.340571517218429,\n        \"radius\": 2,\n        \"color\": \"#7F8C8D\"\n      },\n      \"bankVault\": {\n        \"x\": 19.156143651649913,\n        \"z\": -30.93629744607111,\n        \"width\": 5,\n        \"depth\": 4,\n        \"color\": \"#2C3E50\",\n        \"balance\": 0\n      }\n    }\n  ],\n  \"cashierTile\": {\n    \"x\": 0,\n    \"cost\": 150,\n    \"color\": \"#3498DB\",\n    \"moneyDeposited\": 0,\n    \"z\": -6,\n    \"unlocked\": false,\n    \"width\": 8,\n    \"depth\": 4,\n    \"deleted\": false\n  },\n  \"grillTile\": {\n    \"cost\": 100,\n    \"deleted\": false,\n    \"x\": 0,\n    \"color\": \"#E74C3C\",\n    \"width\": 8,\n    \"z\": -15,\n    \"depth\": 4,\n    \"moneyDeposited\": 0,\n    \"unlocked\": false\n  },\n  \"driveThruTile\": {\n    \"width\": 6,\n    \"color\": \"#8B4513\",\n    \"deleted\": false,\n    \"depth\": 4,\n    \"x\": 18,\n    \"unlocked\": false,\n    \"moneyDeposited\": 0,\n    \"cost\": 1000,\n    \"z\": -0.41427041514123175\n  },\n  \"trashTile\": {\n    \"color\": \"#7F8C8D\",\n    \"z\": -20,\n    \"depth\": 4,\n    \"width\": 4,\n    \"deleted\": false,\n    \"cost\": 10,\n    \"x\": 12,\n    \"unlocked\": false,\n    \"moneyDeposited\": 0\n  },\n  \"vaultTile\": {\n    \"deleted\": true,\n    \"width\": 5,\n    \"unlocked\": false,\n    \"cost\": 500,\n    \"x\": 19.15,\n    \"z\": -28,\n    \"moneyDeposited\": 0,\n    \"depth\": 4,\n    \"color\": \"#F1C40F\"\n  },\n  \"fountainTile\": {\n    \"x\": -14,\n    \"z\": -3,\n    \"width\": 4,\n    \"depth\": 4,\n    \"cost\": 1000,\n    \"color\": \"#3498DB\",\n    \"unlocked\": false,\n    \"moneyDeposited\": 0,\n    \"deleted\": false\n  },\n  \"fountain\": {\n    \"x\": -14,\n    \"z\": -3,\n    \"radius\": 2.5,\n    \"deleted\": false\n  },\n  \"unlockOrder\": [\n    \"diningTable\",\n    \"grillTile\",\n    \"cashierTile\",\n    \"trashTile\",\n    \"staffHireTile\",\n    \"driveThruTile\",\n    \"cleanerStaffTile\",\n    \"fountainTile\",\n    \"selfOrderKioskTile\"\n  ],\n  \"selfOrderKiosk\": {\n    \"x\": -11,\n    \"z\": 5,\n    \"width\": 4,\n    \"depth\": 3,\n    \"color\": \"#2C3E50\",\n    \"maxQueue\": 4,\n    \"rotation\": 0,\n    \"deleted\": false\n  },\n  \"selfOrderKioskTile\": {\n    \"x\": -11,\n    \"z\": 5,\n    \"width\": 4,\n    \"depth\": 4,\n    \"cost\": 500,\n    \"color\": \"#2C3E50\",\n    \"unlocked\": false,\n    \"moneyDeposited\": 0,\n    \"deleted\": true\n  },\n  \"fountain2\": {\n    \"x\": 13,\n    \"z\": -8,\n    \"radius\": 2.5,\n    \"deleted\": false\n  },\n  \"fountain2Tile\": {\n    \"x\": 13,\n    \"z\": -8,\n    \"width\": 4,\n    \"depth\": 4,\n    \"cost\": 1000,\n    \"color\": \"#3498DB\",\n    \"unlocked\": false,\n    \"moneyDeposited\": 0,\n    \"deleted\": false\n  },\n  \"selfOrderKiosks\": [\n    {\n      \"id\": 0,\n      \"x\": -11,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 3,\n      \"color\": \"#2C3E50\",\n      \"icon\": \"\\ud83d\\udcf1\",\n      \"maxQueue\": 4,\n      \"rotation\": 0,\n      \"deleted\": true\n    },\n    {\n      \"id\": 1,\n      \"x\": -5,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 3,\n      \"color\": \"#E74C3C\",\n      \"icon\": \"\\ud83c\\udf54\",\n      \"maxQueue\": 4,\n      \"rotation\": 0,\n      \"deleted\": true\n    },\n    {\n      \"id\": 2,\n      \"x\": 1,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 3,\n      \"color\": \"#3498DB\",\n      \"icon\": \"\\ud83c\\udf55\",\n      \"maxQueue\": 4,\n      \"rotation\": 0,\n      \"deleted\": true\n    },\n    {\n      \"id\": 3,\n      \"x\": 7,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 3,\n      \"color\": \"#27AE60\",\n      \"icon\": \"\\ud83c\\udf5c\",\n      \"maxQueue\": 4,\n      \"rotation\": 0,\n      \"deleted\": true\n    },\n    {\n      \"id\": 4,\n      \"x\": 13,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 3,\n      \"color\": \"#F39C12\",\n      \"icon\": \"\\ud83c\\udf2e\",\n      \"maxQueue\": 4,\n      \"rotation\": 0,\n      \"deleted\": true\n    }\n  ],\n  \"selfOrderKioskTiles\": [\n    {\n      \"kioskId\": 0,\n      \"x\": -11,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 4,\n      \"cost\": 500,\n      \"color\": \"#2C3E50\",\n      \"unlocked\": false,\n      \"moneyDeposited\": 0,\n      \"deleted\": false,\n      \"hidden\": false\n    },\n    {\n      \"kioskId\": 1,\n      \"x\": -5,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 4,\n      \"cost\": 500,\n      \"color\": \"#E74C3C\",\n      \"unlocked\": false,\n      \"moneyDeposited\": 0,\n      \"deleted\": false,\n      \"hidden\": false\n    },\n    {\n      \"kioskId\": 2,\n      \"x\": 6,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 4,\n      \"cost\": 500,\n      \"color\": \"#3498DB\",\n      \"unlocked\": false,\n      \"moneyDeposited\": 0,\n      \"deleted\": false,\n      \"hidden\": false\n    },\n    {\n      \"kioskId\": 3,\n      \"x\": -17,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 4,\n      \"cost\": 500,\n      \"color\": \"#27AE60\",\n      \"unlocked\": false,\n      \"moneyDeposited\": 0,\n      \"deleted\": false,\n      \"hidden\": false\n    },\n    {\n      \"kioskId\": 4,\n      \"x\": 12,\n      \"z\": 5,\n      \"width\": 4,\n      \"depth\": 4,\n      \"cost\": 500,\n      \"color\": \"#F39C12\",\n      \"unlocked\": false,\n      \"moneyDeposited\": 0,\n      \"deleted\": false,\n      \"hidden\": false\n    }\n  ]\n}", "game_design_document": "", "persistent_storage_config": {"leaderboard": {"sort_ascending": false}}, "game_metadata": {"game_id": "01KTEAT9BMFWNWTTRVT0P9WGHX", "title": "Fast food Tycoon", "description": "You\u2019ll step into the shoes of a fast-food manager building a world-class burger empire from the ground up.", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_f82e01b3-85b2-49f4-8fe6-a9c90cd9d74b.webp", "created_at": 1780745058.0, "updated_at": 1780745114601.0251, "art_style": null, "is_remix": true, "base_game_id": "01KP1CZS66H55NYADBJER1XSPB"}, "task_plan": null}