{"asset_map": {"sniper_rifle_01": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/sniper_rifle_01_73711ece-f55b-4bc3-a706-6da209b3c077.webp", "type": "image", "prompt": "Generate a low-poly 3D modern tactical sniper rifle for a first-person shooter game. Sleek angular design, minimalist geometry with flat shading. Color palette: dark military gray (#374151) and black (#1F2937) with subtle metallic highlights. Features a tactical scope on top, angular stock, and visible magazine. Orientation: horizontal, angled slightly upward as if held by a player. Lit from top-front-left. High resolution, professional game asset on a transparent background.", "description": "Modern tactical sniper rifle for first-person view, dark military gray with black accents.", "subtype": "object", "aspect_ratio": [1486, 554], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "sniper_rifle_02": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/sniper_rifle_02_c1d09ea5-02d4-4c2c-b9d2-a79504b84ded.webp", "type": "image", "prompt": "Generate a low-poly 3D compact bullpup sniper rifle. Magazine is located behind the trigger. Minimalist geometry with flat shading. Color palette: desert tan (#D97706) with dark brown accents (#78350F). Features a shorter barrel and integrated scope. Orientation: horizontal, angled slightly upward for first-person perspective. Lit from top-front-left. Professional game asset on a transparent background.", "description": "Bullpup sniper rifle for first-person view, desert tan color palette.", "subtype": "object", "aspect_ratio": [1346, 642], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "target_male_01": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/target_male_01_8a416da2-14d8-4926-892a-5c07c145fba8.webp", "type": "image", "prompt": "Generate a low-poly 3D male character model. Full body standing illustration, front-facing neutral stance. Blocky humanoid shape with flat shading. Wearing a bright red jacket (#EF4444) and dark gray pants (#374151). Simple facial features. 1.8m tall proportions with a clearly defined head. Lit from top-front. Clean, professional game asset on a transparent background.", "description": "Main target character in a red jacket for identification.", "subtype": "character", "aspect_ratio": [612, 1413], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "civilian_male_01": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/civilian_male_01_764a6e74-ebb3-4c97-9a8e-6aacd0bd5adc.webp", "type": "image", "prompt": "Generate a low-poly 3D male civilian character model. Full body standing illustration, front-facing neutral stance. Blocky humanoid shape with flat shading. Wearing a blue shirt (#3B82F6) and gray pants (#6B7280). Simple facial features. Consistent style with target_male_01. Lit from top-front. Clean, professional game asset on a transparent background.", "description": "Male civilian NPC in a blue shirt for the rooftop environment.", "subtype": "character", "aspect_ratio": [526, 1248], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "civilian_male_02": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/civilian_male_02_13201c98-bcf3-420b-9f0c-c35a875d0259.webp", "type": "image", "prompt": "Generate a low-poly 3D male civilian character model. Full body standing illustration, front-facing neutral stance. Blocky humanoid shape with flat shading. Wearing a green jacket (#10B981) and brown pants (#92400E). Simple facial features. Consistent style with target_male_01. Lit from top-front. Clean, professional game asset on a transparent background.", "description": "Male civilian NPC in a green jacket for the rooftop environment.", "subtype": "character", "aspect_ratio": [540, 1248], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "civilian_female_01": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/civilian_female_01_a805b7c5-d8af-46de-b92a-f0be7b1e63b2.webp", "type": "image", "prompt": "Generate a low-poly 3D female civilian character model. Full body standing illustration, front-facing neutral stance. Blocky humanoid shape with flat shading. Wearing a yellow shirt (#FBBF24) and dark blue pants (#1E40AF). Slightly longer hair shape. 1.7m tall proportions. Lit from top-front. Clean, professional game asset on a transparent background.", "description": "Female civilian NPC in a yellow shirt for the rooftop environment.", "subtype": "character", "aspect_ratio": [502, 1244], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "rooftop_ac_unit": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/rooftop_ac_unit_9be7ecf5-95f2-4e2a-a699-e10525a7c62c.webp", "type": "image", "prompt": "Generate a low-poly 3D industrial rooftop air conditioning unit. Rectangular box shape with simple ventilation grilles and a circular fan detail on top. Flat shading, weathered metal gray (#9CA3AF) with orange rust accents (#F59E0B). Viewed from a slight isometric-style angle to show front and top. Lit from top-front. Transparent background.", "description": "Industrial air conditioning unit prop for the rooftop map.", "subtype": "object", "aspect_ratio": [884, 927], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "rooftop_water_tank": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/rooftop_water_tank_fd54955f-280a-4704-a5b8-87f6c196fdb3.webp", "type": "image", "prompt": "Generate a low-poly 3D cylindrical water tank on four metal legs. Cylindrical body with a conical top. Flat shading, faded blue-gray (#64748B) with orange rust streaks (#F59E0B). Viewed from a slight angle. Lit from top-front. Clean edges, professional game asset on a transparent background.", "description": "Cylindrical water tank prop for the rooftop map.", "subtype": "object", "aspect_ratio": [659, 1370], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "rooftop_crate": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/rooftop_crate_8bd58df5-b2a2-4411-907a-711211fa750f.webp", "type": "image", "prompt": "Generate a low-poly 3D wooden crate. Simple cube geometry with flat shading. Weathered brown wood (#78350F) with dark brown edge details. Suggestion of planks and metal corner brackets. Viewed from a slight angle to show front and top. Lit from top-front. Transparent background.", "description": "Wooden crate prop for the rooftop map.", "subtype": "object", "aspect_ratio": [874, 902], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "gunshot_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/gunshot_sound_a59abedd-b6b7-45c6-bc13-80fd6074816c.mp3", "type": "audio", "prompt": "Professional modern sniper rifle gunshot, high-caliber single shot, sharp mechanical crack followed by a brief 1-second reverb tail suitable for a city rooftop environment, crisp and punchy, no bullet casing drop sound, high fidelity.", "description": "Powerful sniper rifle shot with a crisp mechanical crack and a brief urban echo tail.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2.48, "loop": false, "animation": null}, "headshot_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/headshot_sound_868cc563-5020-40e4-8683-333c2fafc7bb.mp3", "type": "audio", "prompt": "Satisfying 'headshot' feedback sound, high-pitched metallic 'ping' or 'ding' combined with a subtle fleshy impact thud, rewarding and celebratory UI-style effect, clear and snappy, short decay.", "description": "Rewarding metallic ding to provide positive feedback for a successful headshot.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.23, "loop": false, "animation": null}, "mission_fail_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/mission_fail_sound_eb6e23a0-afad-4055-86f5-3b50161b96ef.mp3", "type": "audio", "prompt": "Mission failure sound effect, a subtle low-frequency descending synth tone, non-aggressive buzzer sound, professional game UI feedback, clear negative reinforcement without being harsh.", "description": "Descending negative tone used when a civilian is hit or time runs out.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.87, "loop": false, "animation": null}, "background_music": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/music/background_music_6a94a290-4ba4-4b42-9ca7-9402fc0ab880.mp3", "type": "audio", "prompt": "Modern cinematic tactical background music, atmospheric ambient pads with a steady but subtle electronic percussion pulse at 90 BPM. Focus on tension and professional stealth vibes, low-profile bass drones, minimal melody, designed for a high-stakes sniper mission, seamless loopable transitions.", "description": "Tense and atmospheric tactical music loop to maintain focus during missions.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 60.02938775510204, "loop": true, "animation": null}, "splash_background": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/splash_background_26560703-4ad9-4fc1-a20f-9bb037edf84c.webp", "type": "image", "prompt": "Generate a 9:16 tall cinematic background of a dark city skyline at dusk for a sniper game. Silhouettes of tall skyscrapers and buildings against a dramatic orange and deep blue sunset sky. Realistic, gritty military shooter aesthetic with moody fog and haze between buildings. High contrast lighting with long shadows, dominant dark navy and black tones with warm amber accents. Professional AAA game quality, high resolution, sharp details, suitable for a title screen overlay.", "description": "A cinematic night-time city skyline at dusk for the game's splash screen background.", "subtype": "background", "aspect_ratio": [1024, 1536], "compression_suggestion": "original", "duration_secs": null, "loop": null, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n<head>\n    <title>QuickScope: Sniper Contracts</title>\n    <style>\n        :root {\n            --transition-fast: 150ms;\n            --transition-normal: 300ms;\n            --transition-slow: 500ms;\n            --ease-out: cubic-bezier(0.0, 0, 0.2, 1);\n            --ease-bounce: cubic-bezier(0.68, -0.6, 0.32, 1.6);\n            --space-xs: 8px;\n            --space-sm: 16px;\n            --space-md: 24px;\n            --space-lg: 32px;\n            --space-xl: 48px;\n            \n            /* Tactical Color Palette */\n            --bg-dark: #0a0c10;\n            --bg-panel: rgba(12, 16, 24, 0.92);\n            --bg-card: rgba(18, 24, 36, 0.95);\n            --border-subtle: rgba(60, 75, 95, 0.4);\n            --border-glow: rgba(80, 100, 130, 0.3);\n            --text-primary: #e8ecf2;\n            --text-secondary: #8896a8;\n            --text-muted: #5a6878;\n            --accent-red: #dc3545;\n            --accent-red-glow: rgba(220, 53, 69, 0.4);\n            --accent-green: #28a745;\n            --accent-green-glow: rgba(40, 167, 69, 0.4);\n            --accent-blue: #3a7bd5;\n            --accent-gold: #d4a54a;\n            --accent-gold-glow: rgba(212, 165, 74, 0.5);\n        }\n\n        body, html {\n            margin: 0;\n            padding: 0;\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            touch-action: none;\n            -ms-touch-action: none;\n            font-family: 'Inter', sans-serif;\n            background: #000;\n        }\n\n        /* Portrait lock overlay */\n        #rotate-overlay {\n            display: none;\n            position: fixed;\n            top: 0; left: 0;\n            width: 100%; height: 100%;\n            background: #0a0c10;\n            z-index: 9999;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            gap: 24px;\n        }\n\n        #rotate-overlay .rotate-icon {\n            font-size: 72px;\n            animation: rotateHint 1.5s ease-in-out infinite;\n        }\n\n        @keyframes rotateHint {\n            0%, 100% { transform: rotate(-10deg); }\n            50% { transform: rotate(10deg); }\n        }\n\n        #rotate-overlay p {\n            font-family: 'Orbitron', monospace;\n            font-size: 18px;\n            color: #8896a8;\n            letter-spacing: 3px;\n            text-align: center;\n            text-transform: uppercase;\n            margin: 0;\n            padding: 0 32px;\n        }\n\n        @media (orientation: landscape) and (max-width: 1024px) {\n            #rotate-overlay { display: flex; }\n            #game-world { display: none; }\n        }\n\n        @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Inter:wght@400;600;700&display=swap');\n\n        #game-world {\n            position: relative;\n            width: 100%;\n            height: 100%;\n        }\n\n        #game-canvas {\n            width: 100%;\n            height: 100%;\n            display: block;\n        }\n\n        .ui-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            z-index: 10;\n        }\n\n        .ui-overlay > * {\n            pointer-events: auto;\n        }\n\n        #crosshair {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            width: 60px;\n            height: 60px;\n            pointer-events: none;\n            transition: transform 0.1s ease;\n        }\n\n        #crosshair::before, #crosshair::after {\n            content: '';\n            position: absolute;\n            background: rgba(200, 210, 220, 0.7);\n        }\n\n        #crosshair::before {\n            top: 50%;\n            left: 0;\n            right: 0;\n            height: 1px;\n            transform: translateY(-50%);\n            box-shadow: 0 0 4px rgba(0,0,0,0.8);\n        }\n\n        #crosshair::after {\n            left: 50%;\n            top: 0;\n            bottom: 0;\n            width: 1px;\n            transform: translateX(-50%);\n            box-shadow: 0 0 4px rgba(0,0,0,0.8);\n        }\n\n        #crosshair .dot {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            width: 6px;\n            height: 6px;\n            background: var(--accent-red);\n            border-radius: 50%;\n            transform: translate(-50%, -50%);\n            box-shadow: 0 0 8px var(--accent-red-glow), 0 0 2px rgba(255,255,255,0.5);\n        }\n\n        #crosshair .ring {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            width: 48px;\n            height: 48px;\n            border: 1px solid rgba(200, 210, 220, 0.4);\n            border-radius: 50%;\n            transform: translate(-50%, -50%);\n        }\n\n        #crosshair .tick {\n            position: absolute;\n            background: rgba(200, 210, 220, 0.5);\n        }\n\n        #crosshair .tick-top { width: 1px; height: 8px; top: 6px; left: 50%; transform: translateX(-50%); }\n        #crosshair .tick-bottom { width: 1px; height: 8px; bottom: 6px; left: 50%; transform: translateX(-50%); }\n        #crosshair .tick-left { width: 8px; height: 1px; left: 6px; top: 50%; transform: translateY(-50%); }\n        #crosshair .tick-right { width: 8px; height: 1px; right: 6px; top: 50%; transform: translateY(-50%); }\n\n        .crosshair-expanded {\n            transform: translate(-50%, -50%) scale(1.5) !important;\n        }\n\n        #hud-top {\n            position: absolute;\n            top: 16px;\n            left: 16px;\n            right: 16px;\n            display: flex;\n            justify-content: space-between;\n            align-items: flex-start;\n            pointer-events: none;\n        }\n\n        .hud-element {\n            background: rgba(12, 16, 24, 0.5);\n            backdrop-filter: blur(20px) saturate(180%);\n            border: 1px solid rgba(255, 255, 255, 0.06);\n            border-radius: 20px;\n            padding: 10px 20px;\n            color: var(--text-primary);\n            font-family: 'Orbitron', monospace;\n            font-size: 28px;\n            text-shadow: 0 2px 8px rgba(0,0,0,0.9);\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255,255,255,0.08);\n        }\n\n        #pause-btn {\n            width: 52px;\n            height: 52px;\n            background: rgba(20, 28, 40, 0.6);\n            backdrop-filter: blur(20px) saturate(180%);\n            border: 1.5px solid rgba(255, 255, 255, 0.08);\n            border-radius: 50%;\n            color: var(--text-secondary);\n            font-size: 16px;\n            cursor: pointer;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            pointer-events: auto;\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255,255,255,0.1), inset 0 -1px 0 rgba(0,0,0,0.2);\n            transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);\n            position: relative;\n            overflow: hidden;\n        }\n\n        #pause-btn::after {\n            content: '';\n            position: absolute;\n            top: 0; left: 0; right: 0;\n            height: 50%;\n            background: linear-gradient(180deg, rgba(255,255,255,0.08) 0%, transparent 100%);\n            border-radius: 50% 50% 0 0;\n            pointer-events: none;\n        }\n\n        #pause-btn:hover {\n            background: rgba(30, 40, 55, 0.7);\n            border-color: rgba(255, 255, 255, 0.15);\n            color: var(--text-primary);\n            transform: scale(1.08);\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 0 20px rgba(80, 120, 180, 0.15);\n        }\n\n        #pause-btn:active {\n            transform: scale(0.92);\n            box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);\n        }\n\n        #target-counter {\n            display: flex;\n            align-items: center;\n            gap: 10px;\n            background: rgba(12, 16, 24, 0.5);\n            backdrop-filter: blur(20px) saturate(180%);\n            padding: 10px 18px;\n            border-radius: 20px;\n            border: 1px solid rgba(255, 255, 255, 0.06);\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255,255,255,0.08);\n        }\n\n        #target-counter .target-icon {\n            width: 36px;\n            height: 36px;\n            background: linear-gradient(145deg, var(--accent-red), #a02030);\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 16px;\n            box-shadow: 0 0 15px var(--accent-red-glow), inset 0 2px 4px rgba(255,255,255,0.2);\n            border: 2px solid rgba(255, 100, 100, 0.3);\n        }\n\n        #target-counter .count {\n            font-family: 'Orbitron', monospace;\n            font-size: 26px;\n            color: var(--text-primary);\n            letter-spacing: 1px;\n        }\n\n        #hud-right {\n            display: flex;\n            flex-direction: column;\n            align-items: flex-end;\n            gap: 8px;\n        }\n\n        #lives-display {\n            display: flex;\n            align-items: center;\n            gap: 10px;\n            background: rgba(12, 16, 24, 0.5);\n            backdrop-filter: blur(20px) saturate(180%);\n            padding: 8px 16px;\n            border-radius: 16px;\n            border: 1px solid rgba(255, 255, 255, 0.06);\n            font-family: 'Orbitron', monospace;\n            font-size: 22px;\n            color: var(--text-primary);\n            box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255,255,255,0.08);\n        }\n\n        #lives-display .label {\n            color: var(--text-muted);\n            font-size: 11px;\n            text-transform: uppercase;\n            letter-spacing: 1px;\n        }\n\n        #lives-display .shield-icon {\n            color: var(--accent-red);\n            text-shadow: 0 0 8px var(--accent-red-glow);\n        }\n\n        #coins-display {\n            display: flex;\n            align-items: center;\n            gap: 10px;\n            background: rgba(12, 16, 24, 0.5);\n            backdrop-filter: blur(20px) saturate(180%);\n            padding: 8px 16px;\n            border-radius: 16px;\n            border: 1px solid rgba(212, 165, 74, 0.15);\n            font-family: 'Orbitron', monospace;\n            font-size: 22px;\n            color: var(--accent-gold);\n            box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35), 0 0 20px rgba(212, 165, 74, 0.08), inset 0 1px 0 rgba(255,255,255,0.08);\n        }\n\n        #coins-display .coin-icon {\n            width: 22px;\n            height: 22px;\n            background: linear-gradient(145deg, #f0c850, #c9a030);\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 12px;\n            box-shadow: 0 0 10px var(--accent-gold-glow), inset 0 2px 4px rgba(255,255,255,0.4);\n            border: 1px solid rgba(255, 220, 100, 0.5);\n        }\n\n        #timer {\n            font-size: 34px;\n            color: var(--text-primary);\n            position: absolute;\n            bottom: 380px;\n            left: 50%;\n            transform: translateX(-50%);\n            background: rgba(12, 16, 24, 0.5);\n            backdrop-filter: blur(20px) saturate(180%);\n            border: 1px solid rgba(255, 255, 255, 0.06);\n            padding: 10px 22px;\n            border-radius: 20px;\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255,255,255,0.08);\n            letter-spacing: 2px;\n        }\n\n        #timer.warning {\n            color: #EF4444;\n            animation: pulse 0.5s infinite alternate;\n        }\n\n        @keyframes pulse {\n            from { opacity: 1; }\n            to { opacity: 0.5; }\n        }\n\n        /* Stability/Breath Bar */\n        #stability-bar-container {\n            display: none;\n        }\n\n        #stability-bar {\n            position: absolute;\n            bottom: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: linear-gradient(to top, #EF4444 0%, #FBBF24 50%, #10B981 100%);\n            transition: height 0.1s ease-out;\n        }\n\n        #stability-bar-marker {\n            position: absolute;\n            left: -6px;\n            width: 20px;\n            height: 4px;\n            background: #fff;\n            border-radius: 2px;\n            box-shadow: 0 0 4px rgba(0,0,0,0.5);\n        }\n\n        /* Scope and action buttons */\n        #aim-joystick {\n            position: absolute;\n            bottom: 180px;\n            left: 20px;\n            width: 140px;\n            height: 140px;\n            border-radius: 50%;\n            background: rgba(15, 22, 35, 0.4);\n            backdrop-filter: blur(12px);\n            border: 1.5px solid rgba(255, 255, 255, 0.06);\n            touch-action: none;\n            pointer-events: auto;\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 0 40px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255,255,255,0.05);\n            z-index: 25;\n        }\n\n        #aim-joystick-knob {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            width: 52px;\n            height: 52px;\n            border-radius: 50%;\n            background: radial-gradient(circle at 35% 35%, rgba(80, 110, 150, 0.7), rgba(40, 55, 80, 0.8) 70%);\n            border: 1.5px solid rgba(120, 160, 220, 0.3);\n            transform: translate(-50%, -50%);\n            box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4), 0 0 20px rgba(60, 120, 200, 0.15), inset 0 1px 2px rgba(255,255,255,0.2);\n            transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);\n        }\n\n        #aim-joystick-knob.active {\n            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5), 0 0 30px rgba(60, 120, 200, 0.3), inset 0 1px 2px rgba(255,255,255,0.3);\n            border-color: rgba(100, 160, 240, 0.5);\n            transform: translate(-50%, -50%) scale(1.05);\n        }\n\n        #aim-joystick-label {\n            position: absolute;\n            bottom: -22px;\n            left: 50%;\n            transform: translateX(-50%);\n            font-family: 'Orbitron', monospace;\n            font-size: 9px;\n            color: var(--text-muted);\n            letter-spacing: 2px;\n            white-space: nowrap;\n        }\n\n        #action-buttons {\n            position: absolute;\n            right: 20px;\n            bottom: 200px;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            gap: 20px;\n            pointer-events: auto;\n            z-index: 25;\n        }\n\n        .action-btn {\n            width: 72px;\n            height: 72px;\n            background: rgba(20, 28, 40, 0.5);\n            backdrop-filter: blur(16px) saturate(150%);\n            border: 1.5px solid rgba(255, 255, 255, 0.08);\n            border-radius: 50%;\n            color: var(--text-secondary);\n            font-size: 22px;\n            cursor: pointer;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            gap: 2px;\n            transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255,255,255,0.1), inset 0 -1px 0 rgba(0,0,0,0.2);\n            position: relative;\n            overflow: hidden;\n        }\n\n        .action-btn::after {\n            content: '';\n            position: absolute;\n            top: 0; left: 0; right: 0;\n            height: 45%;\n            background: linear-gradient(180deg, rgba(255,255,255,0.07) 0%, transparent 100%);\n            border-radius: 50% 50% 40% 40%;\n            pointer-events: none;\n        }\n\n        .action-btn .btn-label {\n            font-family: 'Inter', sans-serif;\n            font-size: 8px;\n            font-weight: 600;\n            letter-spacing: 0.5px;\n            text-transform: uppercase;\n            opacity: 0.7;\n            margin-top: 1px;\n        }\n\n        .action-btn:hover {\n            background: rgba(30, 42, 60, 0.6);\n            border-color: rgba(255, 255, 255, 0.15);\n            transform: scale(1.08);\n            box-shadow: 0 8px 36px rgba(0, 0, 0, 0.5), 0 0 25px rgba(80, 120, 180, 0.12);\n        }\n\n        .action-btn:active {\n            transform: scale(0.88);\n            box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);\n            border-color: rgba(255, 255, 255, 0.2);\n        }\n\n        .action-btn.scope-active {\n            background: rgba(30, 70, 140, 0.5);\n            border-color: rgba(100, 160, 240, 0.4);\n            color: #80b0ff;\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 30px rgba(60, 120, 200, 0.25), inset 0 0 20px rgba(60, 120, 200, 0.1);\n        }\n\n        #shoot-btn {\n            width: 88px;\n            height: 88px;\n            background: radial-gradient(circle at 35% 35%, rgba(220, 70, 80, 0.9), rgba(160, 35, 45, 0.95) 60%, rgba(100, 20, 30, 0.98));\n            border: 2px solid rgba(255, 100, 110, 0.35);\n            border-radius: 50%;\n            color: #fff;\n            font-size: 20px;\n            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5), 0 0 40px var(--accent-red-glow), 0 0 80px rgba(220, 53, 69, 0.15), inset 0 2px 4px rgba(255,255,255,0.2), inset 0 -2px 4px rgba(0,0,0,0.3);\n            position: relative;\n            overflow: hidden;\n        }\n\n        #shoot-btn::before {\n            content: '';\n            position: absolute;\n            top: 8%; left: 15%; right: 15%;\n            height: 35%;\n            background: linear-gradient(180deg, rgba(255,255,255,0.25) 0%, transparent 100%);\n            border-radius: 50%;\n            pointer-events: none;\n        }\n\n        #shoot-btn:hover {\n            background: radial-gradient(circle at 35% 35%, rgba(240, 80, 90, 0.95), rgba(180, 45, 55, 0.98) 60%, rgba(120, 25, 35, 1));\n            transform: scale(1.06);\n            box-shadow: 0 12px 48px rgba(0, 0, 0, 0.6), 0 0 50px var(--accent-red-glow), 0 0 100px rgba(220, 53, 69, 0.2);\n        }\n\n        #shoot-btn:active {\n            transform: scale(0.85);\n            box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5), 0 0 25px var(--accent-red-glow);\n            background: radial-gradient(circle at 50% 50%, rgba(180, 40, 50, 0.95), rgba(120, 20, 30, 0.98));\n        }\n\n        #ammo {\n            font-size: 22px;\n            position: absolute;\n            bottom: 120px;\n            right: 20px;\n            background: rgba(12, 16, 24, 0.5);\n            backdrop-filter: blur(20px) saturate(180%);\n            border: 1px solid rgba(255, 255, 255, 0.06);\n            padding: 10px 18px;\n            border-radius: 20px;\n            color: var(--text-primary);\n            font-family: 'Orbitron', monospace;\n            letter-spacing: 3px;\n            box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255,255,255,0.08);\n            display: flex;\n            align-items: center;\n            gap: 6px;\n        }\n\n        #ammo .bullet {\n            width: 8px;\n            height: 20px;\n            background: linear-gradient(180deg, #c9a030, #8a7020);\n            border-radius: 2px 2px 1px 1px;\n            box-shadow: 0 0 6px rgba(200, 160, 50, 0.4);\n        }\n\n        #ammo .bullet.empty {\n            background: linear-gradient(180deg, #3a4050, #252a35);\n            box-shadow: none;\n        }\n\n        #ammo.reloading {\n            color: #f0a030;\n            border-color: rgba(240, 160, 48, 0.15);\n            animation: reloadPulse 0.8s ease-in-out infinite;\n        }\n\n        @keyframes reloadPulse {\n            0%, 100% { box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35); }\n            50% { box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35), 0 0 20px rgba(240, 160, 48, 0.2); }\n        }\n\n        #hit-marker {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            width: 30px;\n            height: 30px;\n            opacity: 0;\n            pointer-events: none;\n            transition: opacity 0.1s;\n        }\n\n        #hit-marker.show {\n            opacity: 1;\n        }\n\n        #hit-marker::before, #hit-marker::after {\n            content: '';\n            position: absolute;\n            background: white;\n            box-shadow: 0 0 4px rgba(0,0,0,0.8);\n        }\n\n        #hit-marker::before {\n            width: 20px;\n            height: 3px;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%) rotate(45deg);\n        }\n\n        #hit-marker::after {\n            width: 20px;\n            height: 3px;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%) rotate(-45deg);\n        }\n\n        #mission-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            background: linear-gradient(180deg, rgba(8, 12, 18, 0.95) 0%, rgba(12, 18, 28, 0.98) 100%);\n            z-index: 100;\n            pointer-events: auto;\n            backdrop-filter: blur(8px);\n        }\n\n        #mission-overlay h1 {\n            font-family: 'Orbitron', monospace;\n            font-size: 42px;\n            font-weight: 700;\n            color: var(--text-primary);\n            margin: 0 0 16px;\n            text-align: center;\n            letter-spacing: 4px;\n            text-shadow: 0 0 30px rgba(80, 120, 180, 0.4);\n        }\n\n        #mission-overlay h2 {\n            font-family: 'Orbitron', monospace;\n            font-size: 28px;\n            color: var(--accent-blue);\n            margin: 0 0 24px;\n            text-align: center;\n            letter-spacing: 2px;\n        }\n\n        #mission-overlay p {\n            font-family: 'Inter', sans-serif;\n            font-size: 18px;\n            color: var(--text-secondary);\n            margin: 8px 0;\n            text-align: center;\n        }\n\n        .btn {\n            font-family: 'Orbitron', monospace;\n            font-size: 16px;\n            font-weight: 600;\n            letter-spacing: 2px;\n            padding: 16px 40px;\n            border: 1.5px solid rgba(255, 255, 255, 0.08);\n            border-radius: 28px;\n            background: rgba(30, 40, 58, 0.6);\n            backdrop-filter: blur(20px) saturate(180%);\n            color: var(--text-primary);\n            cursor: pointer;\n            margin: 8px;\n            min-width: 220px;\n            min-height: 56px;\n            transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255,255,255,0.1), inset 0 -1px 0 rgba(0,0,0,0.2);\n            text-transform: uppercase;\n            position: relative;\n            overflow: hidden;\n        }\n\n        .btn::after {\n            content: '';\n            position: absolute;\n            top: 0; left: 0; right: 0;\n            height: 50%;\n            background: linear-gradient(180deg, rgba(255,255,255,0.06) 0%, transparent 100%);\n            border-radius: 28px 28px 0 0;\n            pointer-events: none;\n        }\n\n        .btn:hover, .btn:focus-visible {\n            background: rgba(40, 52, 72, 0.7);\n            border-color: rgba(255, 255, 255, 0.15);\n            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5), 0 0 25px rgba(80, 120, 180, 0.12);\n            transform: translateY(-3px) scale(1.02);\n        }\n\n        .btn:active {\n            transform: scale(0.95) translateY(0);\n            box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);\n        }\n\n        .btn.danger {\n            background: rgba(160, 35, 45, 0.5);\n            border-color: rgba(255, 80, 90, 0.2);\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 25px rgba(220, 53, 69, 0.15), inset 0 1px 0 rgba(255,255,255,0.1);\n        }\n\n        .btn.danger:hover {\n            background: rgba(180, 45, 55, 0.6);\n            border-color: rgba(255, 80, 90, 0.3);\n            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5), 0 0 35px rgba(220, 53, 69, 0.2);\n        }\n\n        .btn.success {\n            background: rgba(30, 130, 80, 0.5);\n            border-color: rgba(60, 180, 110, 0.2);\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 25px rgba(40, 167, 69, 0.15), inset 0 1px 0 rgba(255,255,255,0.1);\n        }\n\n        .btn.success:hover {\n            background: rgba(40, 150, 95, 0.6);\n            border-color: rgba(60, 180, 110, 0.3);\n            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5), 0 0 35px rgba(40, 167, 69, 0.2);\n        }\n\n        #rifle-overlay {\n            position: absolute;\n            bottom: 0;\n            right: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            z-index: 4;\n        }\n\n        #rifle-canvas {\n            width: 100%;\n            height: 100%;\n            display: block;\n        }\n\n        /* Scope overlay */\n        #scope-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            opacity: 0;\n            transition: opacity 0.2s ease;\n            z-index: 15;\n        }\n\n        #scope-overlay.active {\n            opacity: 1;\n        }\n\n        #scope-overlay .vignette {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: radial-gradient(circle at center, transparent 30%, rgba(0,0,0,0.95) 70%);\n        }\n\n        #scope-overlay .scope-ring {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            width: 480px;\n            height: 480px;\n            border: 2px solid rgba(20, 25, 35, 0.9);\n            border-radius: 50%;\n            box-shadow: inset 0 0 80px rgba(0,0,0,0.7), 0 0 0 3px rgba(40, 50, 70, 0.5);\n        }\n\n        #scope-overlay .crosshair-h {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            width: 380px;\n            height: 1px;\n            background: linear-gradient(90deg, rgba(30,35,45,0.9) 0%, rgba(30,35,45,0.3) 45%, transparent 48%, transparent 52%, rgba(30,35,45,0.3) 55%, rgba(30,35,45,0.9) 100%);\n        }\n\n        #scope-overlay .crosshair-v {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            width: 1px;\n            height: 380px;\n            background: linear-gradient(180deg, rgba(30,35,45,0.9) 0%, rgba(30,35,45,0.3) 45%, transparent 48%, transparent 52%, rgba(30,35,45,0.3) 55%, rgba(30,35,45,0.9) 100%);\n        }\n\n        #scope-overlay .scope-center-dot {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            width: 4px;\n            height: 4px;\n            background: var(--accent-red);\n            border-radius: 50%;\n            transform: translate(-50%, -50%);\n            box-shadow: 0 0 6px var(--accent-red-glow);\n        }\n\n        #scope-overlay .mil-dots {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n        }\n\n        #scope-overlay .mil-dot {\n            position: absolute;\n            width: 6px;\n            height: 6px;\n            background: rgba(0,0,0,0.7);\n            border-radius: 50%;\n        }\n\n        .rifle-recoil {\n            animation: recoil 0.3s ease-out;\n        }\n\n        @keyframes recoil {\n            0% { transform: translateY(0) rotate(0); }\n            20% { transform: translateY(-15px) rotate(-3deg); }\n            100% { transform: translateY(0) rotate(0); }\n        }\n\n        #muzzle-flash {\n            position: absolute;\n            top: 70%;\n            left: 78%;\n            width: 80px;\n            height: 80px;\n            background: radial-gradient(circle, rgba(255,200,0,0.9) 0%, rgba(255,100,0,0.5) 40%, transparent 70%);\n            border-radius: 50%;\n            opacity: 0;\n            pointer-events: none;\n            transform: translate(-50%, -50%);\n        }\n\n        .flash-show {\n            animation: flash 0.1s ease-out;\n        }\n\n        @keyframes flash {\n            0% { opacity: 1; transform: translate(-50%, -50%) scale(1); }\n            100% { opacity: 0; transform: translate(-50%, -50%) scale(1.5); }\n        }\n\n        #countdown {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            font-family: 'Orbitron', monospace;\n            font-size: 100px;\n            font-weight: 900;\n            color: var(--text-primary);\n            text-shadow: 0 0 40px rgba(60, 100, 180, 0.6), 0 0 80px rgba(60, 100, 180, 0.3);\n            opacity: 0;\n            pointer-events: none;\n            z-index: 50;\n            letter-spacing: 8px;\n        }\n\n        .countdown-show {\n            animation: countPop 0.8s ease-out forwards;\n        }\n\n        @keyframes countPop {\n            0% { opacity: 1; transform: translate(-50%, -50%) scale(2); }\n            80% { opacity: 1; transform: translate(-50%, -50%) scale(0.9); }\n            100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }\n        }\n\n        #target-desc {\n            position: absolute;\n            top: 80px;\n            left: 50%;\n            transform: translateX(-50%);\n            background: rgba(12, 16, 24, 0.6);\n            backdrop-filter: blur(20px) saturate(180%);\n            border: 1px solid rgba(220, 53, 69, 0.2);\n            border-radius: 20px;\n            padding: 12px 28px;\n            color: var(--accent-red);\n            font-family: 'Orbitron', monospace;\n            font-size: 18px;\n            letter-spacing: 1px;\n            white-space: nowrap;\n            pointer-events: none;\n            opacity: 0;\n            transition: opacity 0.3s;\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 20px rgba(220, 53, 69, 0.15), inset 0 1px 0 rgba(255,255,255,0.05);\n            text-transform: uppercase;\n        }\n\n        #target-desc.show {\n            opacity: 1;\n        }\n\n        .screen-shake {\n            animation: shake 0.2s ease-out;\n        }\n\n        @keyframes shake {\n            0% { transform: translate(0, 0); }\n            25% { transform: translate(-3px, 2px); }\n            50% { transform: translate(2px, -3px); }\n            75% { transform: translate(-2px, 1px); }\n            100% { transform: translate(0, 0); }\n        }\n\n        #slow-mo-overlay {\n            position: absolute;\n            top: 0; left: 0; width: 100%; height: 100%;\n            background: rgba(255, 200, 0, 0.1);\n            pointer-events: none;\n            opacity: 0;\n            transition: opacity 0.1s;\n        }\n\n        #slow-mo-overlay.active {\n            opacity: 1;\n        }\n\n        /* Red hit flash */\n        #hit-flash {\n            position: absolute;\n            top: 0; left: 0; width: 100%; height: 100%;\n            background: radial-gradient(circle, rgba(255,0,0,0.3) 0%, transparent 70%);\n            pointer-events: none;\n            opacity: 0;\n            transition: opacity 0.1s;\n            z-index: 12;\n        }\n\n        #hit-flash.active {\n            animation: hitFlashAnim 0.3s ease-out;\n        }\n\n        @keyframes hitFlashAnim {\n            0% { opacity: 1; }\n            100% { opacity: 0; }\n        }\n\n        /* Blood effect */\n        .blood-particle {\n            position: absolute;\n            pointer-events: none;\n            z-index: 50;\n            border-radius: 50%;\n        }\n\n        .blood-splatter {\n            background: radial-gradient(circle, #8B0000 0%, #5c0000 50%, transparent 70%);\n            animation: bloodSplat 0.6s ease-out forwards;\n        }\n\n        .blood-drop {\n            background: linear-gradient(180deg, #a00 0%, #600 100%);\n            box-shadow: 0 0 4px rgba(139, 0, 0, 0.8);\n            animation: bloodDrop 0.8s ease-out forwards;\n        }\n\n        .blood-mist {\n            background: radial-gradient(circle, rgba(139, 0, 0, 0.6) 0%, rgba(100, 0, 0, 0.3) 40%, transparent 70%);\n            animation: bloodMist 0.5s ease-out forwards;\n        }\n\n        @keyframes bloodSplat {\n            0% { \n                opacity: 1; \n                transform: scale(0.2) translate(-50%, -50%); \n            }\n            30% { \n                opacity: 1; \n                transform: scale(1.5) translate(-50%, -50%); \n            }\n            100% { \n                opacity: 0; \n                transform: scale(2) translate(-50%, -50%); \n            }\n        }\n\n        @keyframes bloodDrop {\n            0% { \n                opacity: 1; \n                transform: translate(0, 0) scale(1); \n            }\n            100% { \n                opacity: 0; \n                transform: translate(var(--dx), var(--dy)) scale(0.3); \n            }\n        }\n\n        @keyframes bloodMist {\n            0% { \n                opacity: 0.8; \n                transform: scale(0.5) translate(-50%, -50%); \n            }\n            100% { \n                opacity: 0; \n                transform: scale(3) translate(-50%, -50%); \n            }\n        }\n\n        /* Blood screen flash for headshots */\n        #blood-screen-flash {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            z-index: 13;\n            opacity: 0;\n            background: radial-gradient(circle at center, rgba(139, 0, 0, 0.3) 0%, rgba(80, 0, 0, 0.1) 50%, transparent 70%);\n        }\n\n        #blood-screen-flash.active {\n            animation: bloodScreenFlash 0.4s ease-out forwards;\n        }\n\n        @keyframes bloodScreenFlash {\n            0% { opacity: 1; }\n            100% { opacity: 0; }\n        }\n\n        /* Bullet trail effect */\n        .bullet-trail {\n            position: absolute;\n            pointer-events: none;\n            z-index: 11;\n            animation: bulletTrail 0.3s ease-out forwards;\n        }\n\n        @keyframes bulletTrail {\n            0% { opacity: 1; transform: scaleX(0); }\n            30% { opacity: 1; transform: scaleX(1); }\n            100% { opacity: 0; transform: scaleX(1); }\n        }\n\n        /* Flying bullet projectile */\n        .bullet-projectile {\n            position: absolute;\n            pointer-events: none;\n            z-index: 12;\n            width: 12px;\n            height: 12px;\n            border-radius: 50%;\n            animation: bulletFly 0.15s linear forwards;\n        }\n\n        @keyframes bulletFly {\n            0% { \n                opacity: 1;\n                transform: translate(0, 0) scale(1.5);\n            }\n            100% { \n                opacity: 0.8;\n                transform: translate(var(--tx), var(--ty)) scale(0.5);\n            }\n        }\n\n        /* Bullet streak/tracer */\n        .bullet-streak {\n            position: absolute;\n            pointer-events: none;\n            z-index: 11;\n            height: 3px;\n            border-radius: 2px;\n            animation: streakFly 0.2s ease-out forwards;\n            transform-origin: left center;\n        }\n\n        @keyframes streakFly {\n            0% { \n                opacity: 0;\n                width: 0;\n            }\n            20% {\n                opacity: 1;\n                width: 80px;\n            }\n            100% { \n                opacity: 0;\n                width: 150px;\n            }\n        }\n\n        /* Impact ring effect */\n        .bullet-impact-ring {\n            position: absolute;\n            pointer-events: none;\n            z-index: 51;\n            border-radius: 50%;\n            border: 2px solid;\n            animation: impactRing 0.4s ease-out forwards;\n        }\n\n        @keyframes impactRing {\n            0% { transform: translate(-50%, -50%) scale(0.2); opacity: 1; }\n            100% { transform: translate(-50%, -50%) scale(2); opacity: 0; }\n        }\n\n        /* Bullet smoke/vapor trail */\n        .bullet-vapor {\n            position: absolute;\n            pointer-events: none;\n            z-index: 10;\n            background: radial-gradient(circle, rgba(200,200,200,0.4) 0%, transparent 70%);\n            border-radius: 50%;\n            animation: vaporDissipate 0.6s ease-out forwards;\n        }\n\n        @keyframes vaporDissipate {\n            0% { opacity: 0.6; transform: translate(-50%, -50%) scale(0.5); }\n            100% { opacity: 0; transform: translate(-50%, -50%) scale(2); }\n        }\n\n        /* Bullet hit effect */\n        .bullet-hit-effect {\n            position: absolute;\n            pointer-events: none;\n            z-index: 52;\n            border-radius: 50%;\n            animation: bulletHitExpand 0.5s ease-out forwards;\n        }\n\n        @keyframes bulletHitExpand {\n            0% { transform: translate(-50%, -50%) scale(0.3); opacity: 1; }\n            50% { transform: translate(-50%, -50%) scale(1.5); opacity: 0.8; }\n            100% { transform: translate(-50%, -50%) scale(2.5); opacity: 0; }\n        }\n\n        .bullet-hit-spark {\n            position: absolute;\n            pointer-events: none;\n            z-index: 51;\n            width: 4px;\n            height: 4px;\n            border-radius: 50%;\n            animation: sparkFly 0.4s ease-out forwards;\n        }\n\n        @keyframes sparkFly {\n            0% { opacity: 1; transform: translate(0, 0) scale(1); }\n            100% { opacity: 0; transform: translate(var(--sx), var(--sy)) scale(0.2); }\n        }\n\n        /* Floating damage text */\n        .floating-text {\n            position: absolute;\n            font-family: 'Orbitron', monospace;\n            font-size: 24px;\n            font-weight: 700;\n            color: var(--accent-gold);\n            text-shadow: 0 0 15px var(--accent-gold-glow), 0 2px 6px rgba(0,0,0,0.9);\n            pointer-events: none;\n            z-index: 100;\n            animation: floatUp 1.2s ease-out forwards;\n            letter-spacing: 1px;\n        }\n\n        .floating-text.headshot {\n            font-size: 32px;\n            color: var(--accent-red);\n            text-shadow: 0 0 20px var(--accent-red-glow), 0 2px 6px rgba(0,0,0,0.9);\n        }\n\n        @keyframes floatUp {\n            0% { opacity: 1; transform: translateY(0) scale(1); }\n            50% { opacity: 1; transform: translateY(-40px) scale(1.1); }\n            100% { opacity: 0; transform: translateY(-80px) scale(0.8); }\n        }\n\n        @keyframes toxicDrip {\n            0% { transform: translateY(0) scale(1); opacity: 1; }\n            100% { transform: translateY(40px) scale(0.5); opacity: 0; }\n        }\n\n        /* Fog overlay */\n        #fog-overlay {\n            position: absolute;\n            top: 0; left: 0; width: 100%; height: 100%;\n            background: linear-gradient(180deg, \n                rgba(100, 120, 140, 0.1) 0%,\n                rgba(80, 100, 120, 0.15) 50%,\n                rgba(60, 80, 100, 0.2) 100%);\n            pointer-events: none;\n            z-index: 3;\n        }\n\n        /* Pointer lock hint */\n        #pointer-lock-hint {\n            position: absolute;\n            bottom: 20px;\n            left: 50%;\n            transform: translateX(-50%);\n            background: rgba(10, 14, 22, 0.55);\n            backdrop-filter: blur(16px) saturate(160%);\n            border: 1px solid rgba(255, 255, 255, 0.07);\n            border-radius: 24px;\n            padding: 9px 22px;\n            font-family: 'Inter', sans-serif;\n            font-size: 12px;\n            color: var(--text-muted);\n            letter-spacing: 0.5px;\n            pointer-events: none;\n            white-space: nowrap;\n            box-shadow: 0 4px 20px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.06);\n            transition: opacity 0.4s ease;\n            opacity: 0;\n        }\n\n        #pointer-lock-hint.show {\n            opacity: 1;\n        }\n\n        #pointer-lock-hint kbd {\n            display: inline-block;\n            background: rgba(255,255,255,0.08);\n            border: 1px solid rgba(255,255,255,0.15);\n            border-radius: 5px;\n            padding: 1px 7px;\n            font-family: 'Orbitron', monospace;\n            font-size: 10px;\n            color: var(--text-secondary);\n            letter-spacing: 1px;\n        }\n\n        /* Wind indicator */\n        #wind-indicator {\n            position: absolute;\n            top: 180px;\n            left: 50%;\n            transform: translateX(-50%);\n            background: rgba(12, 16, 24, 0.5);\n            backdrop-filter: blur(16px) saturate(150%);\n            border: 1px solid rgba(255, 255, 255, 0.06);\n            border-radius: 16px;\n            padding: 8px 18px;\n            display: flex;\n            align-items: center;\n            gap: 10px;\n            font-family: 'Orbitron', monospace;\n            font-size: 12px;\n            color: var(--text-secondary);\n            letter-spacing: 1px;\n            opacity: 0;\n            transition: opacity 0.3s;\n            pointer-events: none;\n            box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255,255,255,0.06);\n        }\n\n        #wind-indicator.show {\n            opacity: 1;\n        }\n\n        #wind-indicator .wind-arrow {\n            font-size: 18px;\n            transition: transform 0.3s;\n        }\n\n        /* Night vision filter */\n        #night-vision-overlay {\n            position: absolute;\n            top: 0; left: 0; width: 100%; height: 100%;\n            background: rgba(0, 50, 0, 0.3);\n            mix-blend-mode: multiply;\n            pointer-events: none;\n            opacity: 0;\n            transition: opacity 0.5s;\n            z-index: 4;\n        }\n\n        #night-vision-overlay.active {\n            opacity: 1;\n        }\n\n        #night-vision-overlay::after {\n            content: '';\n            position: absolute;\n            top: 0; left: 0; width: 100%; height: 100%;\n            background: repeating-linear-gradient(\n                0deg,\n                transparent,\n                transparent 2px,\n                rgba(0, 0, 0, 0.1) 2px,\n                rgba(0, 0, 0, 0.1) 4px\n            );\n        }\n\n        /* Combo counter */\n        #combo-counter {\n            position: absolute;\n            top: 75px;\n            right: 16px;\n            background: rgba(160, 35, 45, 0.5);\n            backdrop-filter: blur(16px) saturate(150%);\n            border: 1px solid rgba(255, 80, 90, 0.25);\n            border-radius: 20px;\n            padding: 10px 18px;\n            font-family: 'Orbitron', monospace;\n            font-size: 22px;\n            color: #fff;\n            text-shadow: 0 2px 4px rgba(0,0,0,0.5);\n            opacity: 0;\n            transform: scale(0.8);\n            transition: all 0.2s ease;\n            pointer-events: none;\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 25px rgba(220, 53, 69, 0.2), inset 0 1px 0 rgba(255,255,255,0.1);\n            letter-spacing: 1px;\n        }\n\n        #combo-counter.show {\n            opacity: 1;\n            transform: scale(1);\n        }\n\n        #combo-counter.pulse {\n            animation: comboPulse 0.3s ease;\n        }\n\n        @keyframes comboPulse {\n            0% { transform: scale(1); }\n            50% { transform: scale(1.2); }\n            100% { transform: scale(1); }\n        }\n\n        \n\n        /* Mission modifier badge */\n        #modifier-badge {\n            position: absolute;\n            top: 75px;\n            left: 16px;\n            background: rgba(12, 16, 24, 0.5);\n            backdrop-filter: blur(16px) saturate(150%);\n            border: 1px solid rgba(100, 120, 200, 0.15);\n            border-radius: 16px;\n            padding: 8px 16px;\n            font-family: 'Orbitron', monospace;\n            font-size: 14px;\n            color: var(--text-secondary);\n            text-transform: uppercase;\n            letter-spacing: 2px;\n            opacity: 0;\n            transition: opacity 0.3s;\n            pointer-events: none;\n            box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255,255,255,0.06);\n        }\n\n        #modifier-badge.show {\n            opacity: 1;\n        }\n\n        /* Target escape warning */\n        #escape-warning {\n            position: absolute;\n            top: 150px;\n            left: 50%;\n            transform: translateX(-50%);\n            background: rgba(160, 35, 45, 0.5);\n            backdrop-filter: blur(16px) saturate(150%);\n            border: 1px solid rgba(255, 80, 90, 0.3);\n            border-radius: 20px;\n            padding: 10px 24px;\n            font-family: 'Orbitron', monospace;\n            font-size: 20px;\n            letter-spacing: 2px;\n            color: #fff;\n            opacity: 0;\n            transition: opacity 0.3s;\n            pointer-events: none;\n            animation: warningPulse 0.5s ease-in-out infinite;\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 30px rgba(220, 53, 69, 0.2), inset 0 1px 0 rgba(255,255,255,0.1);\n        }\n\n        #escape-warning.show {\n            opacity: 1;\n        }\n\n        @keyframes warningPulse {\n            0%, 100% { box-shadow: 0 0 10px rgba(239, 68, 68, 0.5); }\n            50% { box-shadow: 0 0 20px rgba(239, 68, 68, 0.8); }\n        }\n\n        /* Darker atmosphere */\n        #game-canvas {\n            filter: contrast(1.05) saturate(0.9);\n        }\n\n        /* Loading Screen - Premium Cinematic */\n        #loading-screen {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: linear-gradient(180deg, #080a0f 0%, #0c1018 30%, #101520 60%, #0a0c10 100%);\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            z-index: 200;\n            transition: opacity 0.8s ease-out;\n            overflow: hidden;\n        }\n\n        #loading-screen.hidden {\n            opacity: 0;\n            pointer-events: none;\n        }\n\n        /* Vignette effect */\n        .loading-vignette {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: radial-gradient(ellipse at center, transparent 30%, rgba(0,0,0,0.7) 100%);\n            pointer-events: none;\n        }\n\n        /* Ambient center glow */\n        .loading-ambient-glow {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            width: 400px;\n            height: 400px;\n            background: radial-gradient(circle, rgba(59, 130, 246, 0.08) 0%, transparent 70%);\n            animation: ambientPulse 4s ease-in-out infinite;\n            pointer-events: none;\n        }\n\n        @keyframes ambientPulse {\n            0%, 100% { opacity: 0.5; transform: translate(-50%, -50%) scale(1); }\n            50% { opacity: 0.8; transform: translate(-50%, -50%) scale(1.1); }\n        }\n\n        /* Fog/dust particles */\n        .loading-particles {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            overflow: hidden;\n        }\n\n        .loading-particle {\n            position: absolute;\n            width: 2px;\n            height: 2px;\n            background: rgba(150, 180, 220, 0.3);\n            border-radius: 50%;\n            animation: floatParticle 20s linear infinite;\n        }\n\n        @keyframes floatParticle {\n            0% { transform: translateY(100vh) translateX(0); opacity: 0; }\n            10% { opacity: 0.6; }\n            90% { opacity: 0.6; }\n            100% { transform: translateY(-20vh) translateX(30px); opacity: 0; }\n        }\n\n        /* Moving fog layer */\n        .loading-fog {\n            position: absolute;\n            bottom: 0;\n            left: 0;\n            width: 200%;\n            height: 50%;\n            background: linear-gradient(0deg, rgba(15, 25, 40, 0.5) 0%, transparent 100%);\n            animation: fogDrift 25s linear infinite;\n            pointer-events: none;\n        }\n\n        @keyframes fogDrift {\n            0% { transform: translateX(0); }\n            50% { transform: translateX(-20%); }\n            100% { transform: translateX(0); }\n        }\n\n        /* Title styling */\n        .loading-title-container {\n            position: relative;\n            z-index: 10;\n            text-align: center;\n            margin-bottom: 32px;\n        }\n\n        #loading-screen h1 {\n            font-family: 'Orbitron', monospace;\n            font-size: 34px;\n            font-weight: 900;\n            color: var(--text-primary);\n            margin: 0 0 6px;\n            text-shadow: \n                0 0 40px rgba(60, 100, 160, 0.4),\n                0 0 80px rgba(60, 100, 160, 0.2),\n                0 2px 4px rgba(0, 0, 0, 0.9);\n            letter-spacing: 8px;\n        }\n\n        #loading-screen h2 {\n            font-family: 'Orbitron', monospace;\n            font-size: 12px;\n            color: var(--text-muted);\n            margin: 0;\n            font-weight: 400;\n            letter-spacing: 8px;\n            text-transform: uppercase;\n        }\n\n        /* Large scope container */\n        .loading-scope {\n            width: 220px;\n            height: 220px;\n            border: 2px solid rgba(100, 130, 170, 0.25);\n            border-radius: 50%;\n            position: relative;\n            margin-bottom: 40px;\n            z-index: 10;\n            animation: scopeBreathing 4s ease-in-out infinite;\n            box-shadow: \n                inset 0 0 60px rgba(0, 0, 0, 0.8),\n                0 0 30px rgba(59, 130, 246, 0.15);\n        }\n\n        @keyframes scopeBreathing {\n            0%, 100% { transform: scale(1); }\n            50% { transform: scale(1.02); }\n        }\n\n        /* Blurred city background inside scope */\n        .scope-bg {\n            position: absolute;\n            top: 4px;\n            left: 4px;\n            right: 4px;\n            bottom: 4px;\n            border-radius: 50%;\n            background: linear-gradient(180deg, \n                rgba(30, 40, 60, 0.9) 0%,\n                rgba(20, 30, 50, 0.95) 50%,\n                rgba(10, 15, 25, 1) 100%);\n            overflow: hidden;\n        }\n\n        .scope-bg::before {\n            content: '';\n            position: absolute;\n            bottom: 30%;\n            left: 10%;\n            right: 10%;\n            height: 40%;\n            background: \n                linear-gradient(90deg, \n                    transparent 5%,\n                    rgba(40, 50, 70, 0.4) 5%, rgba(40, 50, 70, 0.4) 8%,\n                    transparent 8%, transparent 15%,\n                    rgba(35, 45, 65, 0.5) 15%, rgba(35, 45, 65, 0.5) 22%,\n                    transparent 22%, transparent 30%,\n                    rgba(45, 55, 75, 0.4) 30%, rgba(45, 55, 75, 0.4) 35%,\n                    transparent 35%, transparent 45%,\n                    rgba(40, 50, 70, 0.6) 45%, rgba(40, 50, 70, 0.6) 55%,\n                    transparent 55%, transparent 65%,\n                    rgba(35, 45, 65, 0.4) 65%, rgba(35, 45, 65, 0.4) 70%,\n                    transparent 70%, transparent 80%,\n                    rgba(45, 55, 75, 0.5) 80%, rgba(45, 55, 75, 0.5) 88%,\n                    transparent 88%\n                );\n            filter: blur(2px);\n            opacity: 0.7;\n        }\n\n        /* Crosshair lines */\n        .loading-scope::before,\n        .loading-scope::after {\n            content: '';\n            position: absolute;\n            background: rgba(180, 200, 220, 0.5);\n        }\n\n        .loading-scope::before {\n            width: 1px;\n            height: 100%;\n            left: 50%;\n            transform: translateX(-50%);\n        }\n\n        .loading-scope::after {\n            width: 100%;\n            height: 1px;\n            top: 50%;\n            transform: translateY(-50%);\n        }\n\n        /* Inner scope ring */\n        .scope-inner-ring {\n            position: absolute;\n            top: 20px;\n            left: 20px;\n            right: 20px;\n            bottom: 20px;\n            border: 1px solid rgba(100, 130, 170, 0.15);\n            border-radius: 50%;\n        }\n\n        /* Center red dot with glow */\n        .loading-scope .dot {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            width: 8px;\n            height: 8px;\n            background: var(--accent-red);\n            border-radius: 50%;\n            transform: translate(-50%, -50%);\n            animation: dotPulse 2s ease-in-out infinite;\n            box-shadow: \n                0 0 12px var(--accent-red-glow),\n                0 0 24px var(--accent-red-glow),\n                0 0 36px rgba(220, 53, 69, 0.2);\n            z-index: 5;\n        }\n\n        @keyframes dotPulse {\n            0%, 100% { \n                transform: translate(-50%, -50%) scale(1); \n                box-shadow: \n                    0 0 10px rgba(220, 38, 38, 0.8),\n                    0 0 20px rgba(220, 38, 38, 0.5),\n                    0 0 30px rgba(220, 38, 38, 0.3);\n            }\n            50% { \n                transform: translate(-50%, -50%) scale(1.2); \n                box-shadow: \n                    0 0 15px rgba(220, 38, 38, 1),\n                    0 0 30px rgba(220, 38, 38, 0.7),\n                    0 0 45px rgba(220, 38, 38, 0.4);\n            }\n        }\n\n        /* Scope glow ring */\n        .loading-scope .ring {\n            position: absolute;\n            top: -4px;\n            left: -4px;\n            right: -4px;\n            bottom: -4px;\n            border: 2px solid transparent;\n            border-top-color: rgba(59, 130, 246, 0.6);\n            border-radius: 50%;\n            animation: scopeSpin 8s linear infinite;\n        }\n\n        /* Mil dots */\n        .scope-mil-dot {\n            position: absolute;\n            width: 4px;\n            height: 4px;\n            background: rgba(180, 200, 220, 0.4);\n            border-radius: 50%;\n        }\n\n        @keyframes scopeSpin {\n            0% { transform: rotate(0deg); }\n            100% { transform: rotate(360deg); }\n        }\n\n        /* Loading bar container */\n        .loading-bar-container {\n            width: 260px;\n            height: 3px;\n            background: rgba(255, 255, 255, 0.08);\n            border-radius: 2px;\n            overflow: hidden;\n            margin-bottom: 16px;\n            z-index: 10;\n            position: relative;\n        }\n\n        /* Loading bar fill */\n        .loading-bar {\n            height: 100%;\n            width: 0%;\n            background: linear-gradient(90deg, var(--accent-red), #f05050, var(--accent-red));\n            border-radius: 2px;\n            transition: width 0.3s ease-out;\n            box-shadow: 0 0 15px var(--accent-red-glow);\n            position: relative;\n        }\n\n        /* Light sweep effect */\n        .loading-bar::after {\n            content: '';\n            position: absolute;\n            top: 0;\n            left: 0;\n            right: 0;\n            bottom: 0;\n            background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);\n            animation: barSweep 2s ease-in-out infinite;\n        }\n\n        @keyframes barSweep {\n            0% { transform: translateX(-100%); }\n            50%, 100% { transform: translateX(200%); }\n        }\n\n        /* Loading text */\n        #loading-text {\n            font-family: 'Orbitron', monospace;\n            font-size: 11px;\n            color: var(--text-muted);\n            letter-spacing: 4px;\n            text-transform: uppercase;\n            z-index: 10;\n            min-height: 20px;\n        }\n\n        /* Cinematic Splash Screen */\n        #splash-screen {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: #0a0a0f;\n            z-index: 150;\n            overflow: hidden;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n        }\n\n        #splash-screen.hidden {\n            display: none;\n        }\n\n        .splash-bg-layer {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 110%;\n            height: 110%;\n            background-size: cover;\n            background-position: center;\n            animation: splashZoom 20s ease-out forwards;\n            transform: scale(1.1);\n        }\n\n        @keyframes splashZoom {\n            0% { transform: scale(1.15) translateY(0); }\n            100% { transform: scale(1.0) translateY(-10px); }\n        }\n\n        .splash-bg-image {\n            width: 100%;\n            height: 100%;\n            object-fit: cover;\n            filter: brightness(0.6) saturate(0.9);\n        }\n\n        .splash-overlay-gradient {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: linear-gradient(180deg, \n                rgba(10, 10, 20, 0.7) 0%, \n                rgba(10, 10, 20, 0.3) 40%,\n                rgba(10, 10, 20, 0.5) 70%,\n                rgba(10, 10, 20, 0.95) 100%);\n            pointer-events: none;\n        }\n\n        .splash-vignette {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,0.8) 100%);\n            pointer-events: none;\n        }\n\n        .splash-fog {\n            position: absolute;\n            bottom: 0;\n            left: 0;\n            width: 200%;\n            height: 40%;\n            background: linear-gradient(0deg, rgba(20, 30, 50, 0.6) 0%, transparent 100%);\n            animation: fogMove 30s linear infinite;\n            pointer-events: none;\n        }\n\n        @keyframes fogMove {\n            0% { transform: translateX(0); }\n            50% { transform: translateX(-25%); }\n            100% { transform: translateX(0); }\n        }\n\n        .splash-scope-overlay {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            width: 140%;\n            height: 140%;\n            pointer-events: none;\n            opacity: 0.15;\n        }\n\n        .splash-scope-ring {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            width: 800px;\n            height: 800px;\n            border: 2px solid rgba(255,255,255,0.15);\n            border-radius: 50%;\n        }\n\n        .splash-scope-crosshair-h {\n            position: absolute;\n            top: 50%;\n            left: 0;\n            width: 100%;\n            height: 1px;\n            background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.1) 30%, rgba(255,255,255,0.1) 70%, transparent 100%);\n        }\n\n        .splash-scope-crosshair-v {\n            position: absolute;\n            top: 0;\n            left: 50%;\n            width: 1px;\n            height: 100%;\n            background: linear-gradient(180deg, transparent 0%, rgba(255,255,255,0.1) 30%, rgba(255,255,255,0.1) 70%, transparent 100%);\n        }\n\n        .splash-hud-top {\n            position: absolute;\n            top: 20px;\n            left: 20px;\n            right: 20px;\n            display: flex;\n            justify-content: space-between;\n            align-items: flex-start;\n            z-index: 10;\n        }\n\n        .splash-settings-btn {\n            width: 68px;\n            height: 68px;\n            background: linear-gradient(135deg, rgba(40, 50, 70, 0.4), rgba(20, 28, 45, 0.6));\n            border: 2px solid rgba(255, 255, 255, 0.1);\n            border-radius: 18px;\n            color: var(--text-secondary);\n            font-size: 30px;\n            cursor: pointer;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);\n            backdrop-filter: blur(20px) saturate(180%);\n            box-shadow: \n                0 8px 32px rgba(0, 0, 0, 0.4),\n                0 2px 8px rgba(0, 0, 0, 0.2),\n                inset 0 1px 0 rgba(255, 255, 255, 0.1),\n                inset 0 -1px 0 rgba(0, 0, 0, 0.2);\n            position: relative;\n            overflow: hidden;\n        }\n\n        .splash-settings-btn::before {\n            content: '';\n            position: absolute;\n            top: 0;\n            left: 0;\n            right: 0;\n            bottom: 0;\n            background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%);\n            opacity: 0;\n            transition: opacity 0.4s ease;\n        }\n\n        .splash-settings-btn:hover::before {\n            opacity: 1;\n        }\n\n        .splash-settings-btn:hover {\n            transform: translateY(-6px) scale(1.05);\n            border-color: rgba(255, 255, 255, 0.3);\n            box-shadow: \n                0 12px 48px rgba(0, 0, 0, 0.5),\n                0 4px 16px rgba(0, 0, 0, 0.3),\n                inset 0 1px 0 rgba(255, 255, 255, 0.2);\n        }\n\n        .splash-settings-btn:active {\n            transform: translateY(-3px) scale(1.02);\n        }\n\n        /* Shop button - gold accent with modern glow */\n        .splash-settings-btn#splash-shop {\n            background: linear-gradient(135deg, rgba(212, 165, 74, 0.25), rgba(180, 140, 50, 0.35));\n            border-color: rgba(212, 165, 74, 0.5);\n            color: #f0c850;\n            box-shadow: \n                0 8px 32px rgba(0, 0, 0, 0.4),\n                0 0 24px rgba(212, 165, 74, 0.2),\n                inset 0 1px 0 rgba(255, 220, 100, 0.2);\n            animation: shopPulse 3s ease-in-out infinite;\n        }\n\n        @keyframes shopPulse {\n            0%, 100% { \n                box-shadow: \n                    0 8px 32px rgba(0, 0, 0, 0.4),\n                    0 0 20px rgba(212, 165, 74, 0.2),\n                    inset 0 1px 0 rgba(255, 220, 100, 0.2);\n            }\n            50% { \n                box-shadow: \n                    0 8px 32px rgba(0, 0, 0, 0.4),\n                    0 0 32px rgba(212, 165, 74, 0.4),\n                    inset 0 1px 0 rgba(255, 220, 100, 0.3);\n            }\n        }\n\n        .splash-settings-btn#splash-shop::after {\n            content: '';\n            position: absolute;\n            width: 100%;\n            height: 100%;\n            background: radial-gradient(circle, rgba(212, 165, 74, 0.3) 0%, transparent 70%);\n            opacity: 0;\n            transition: opacity 0.4s ease;\n        }\n\n        .splash-settings-btn#splash-shop:hover {\n            background: linear-gradient(135deg, rgba(212, 165, 74, 0.4), rgba(180, 140, 50, 0.5));\n            border-color: rgba(255, 215, 100, 0.7);\n            color: #fff4a3;\n            box-shadow: \n                0 12px 48px rgba(0, 0, 0, 0.5),\n                0 0 48px rgba(212, 165, 74, 0.5),\n                inset 0 1px 0 rgba(255, 220, 100, 0.3);\n            transform: translateY(-6px) scale(1.05) rotate(2deg);\n        }\n\n        .splash-settings-btn#splash-shop:hover::after {\n            opacity: 1;\n        }\n\n        /* Leaderboard button - trophy gold with sparkle */\n        .splash-settings-btn#splash-leaderboard {\n            background: linear-gradient(135deg, rgba(255, 215, 0, 0.25), rgba(218, 165, 32, 0.35));\n            border-color: rgba(255, 215, 0, 0.5);\n            color: #FFD700;\n            box-shadow: \n                0 8px 32px rgba(0, 0, 0, 0.4),\n                0 0 24px rgba(255, 215, 0, 0.25),\n                inset 0 1px 0 rgba(255, 240, 100, 0.2);\n            animation: trophyShine 4s ease-in-out infinite;\n        }\n\n        @keyframes trophyShine {\n            0%, 100% { \n                box-shadow: \n                    0 8px 32px rgba(0, 0, 0, 0.4),\n                    0 0 20px rgba(255, 215, 0, 0.25),\n                    inset 0 1px 0 rgba(255, 240, 100, 0.2);\n            }\n            50% { \n                box-shadow: \n                    0 8px 32px rgba(0, 0, 0, 0.4),\n                    0 0 40px rgba(255, 215, 0, 0.5),\n                    inset 0 1px 0 rgba(255, 240, 100, 0.4);\n            }\n        }\n\n        .splash-settings-btn#splash-leaderboard::after {\n            content: '';\n            position: absolute;\n            top: -50%;\n            left: -50%;\n            width: 200%;\n            height: 200%;\n            background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.3) 50%, transparent 70%);\n            transform: rotate(45deg);\n            animation: leaderboardSparkle 3s linear infinite;\n        }\n\n        @keyframes leaderboardSparkle {\n            0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }\n            100% { transform: translateX(100%) translateY(100%) rotate(45deg); }\n        }\n\n        .splash-settings-btn#splash-leaderboard:hover {\n            background: linear-gradient(135deg, rgba(255, 215, 0, 0.4), rgba(218, 165, 32, 0.5));\n            border-color: rgba(255, 240, 100, 0.8);\n            color: #FFF4A3;\n            box-shadow: \n                0 12px 48px rgba(0, 0, 0, 0.5),\n                0 0 56px rgba(255, 215, 0, 0.6),\n                inset 0 1px 0 rgba(255, 240, 100, 0.4);\n            transform: translateY(-6px) scale(1.05) rotate(-2deg);\n        }\n\n        /* Settings button - blue accent with tech feel */\n        .splash-settings-btn#splash-settings {\n            background: linear-gradient(135deg, rgba(60, 120, 200, 0.25), rgba(40, 80, 160, 0.35));\n            border-color: rgba(80, 140, 220, 0.5);\n            color: #60A5FA;\n            box-shadow: \n                0 8px 32px rgba(0, 0, 0, 0.4),\n                0 0 24px rgba(60, 120, 200, 0.2),\n                inset 0 1px 0 rgba(100, 160, 255, 0.2);\n            animation: settingsSpin 20s linear infinite;\n        }\n\n        @keyframes settingsSpin {\n            0% { transform: rotate(0deg); }\n            100% { transform: rotate(360deg); }\n        }\n\n        .splash-settings-btn#splash-settings::before {\n            content: '';\n            position: absolute;\n            width: 120%;\n            height: 120%;\n            background: conic-gradient(\n                from 0deg,\n                transparent 0deg,\n                rgba(96, 165, 250, 0.4) 90deg,\n                transparent 180deg,\n                rgba(96, 165, 250, 0.4) 270deg,\n                transparent 360deg\n            );\n            animation: settingsRotate 4s linear infinite;\n            opacity: 0;\n            transition: opacity 0.4s ease;\n        }\n\n        @keyframes settingsRotate {\n            0% { transform: rotate(0deg); }\n            100% { transform: rotate(360deg); }\n        }\n\n        .splash-settings-btn#splash-settings:hover {\n            background: linear-gradient(135deg, rgba(60, 120, 200, 0.4), rgba(40, 80, 160, 0.5));\n            border-color: rgba(96, 165, 250, 0.7);\n            color: #93C5FD;\n            box-shadow: \n                0 12px 48px rgba(0, 0, 0, 0.5),\n                0 0 48px rgba(60, 120, 200, 0.5),\n                inset 0 1px 0 rgba(100, 160, 255, 0.3);\n            transform: translateY(-6px) scale(1.05);\n            animation: none;\n        }\n\n        .splash-settings-btn#splash-settings:hover::before {\n            opacity: 1;\n        }\n\n        .splash-stats {\n            display: flex;\n            gap: 12px;\n        }\n\n        .splash-stat {\n            background: var(--bg-panel);\n            border: 1px solid var(--border-subtle);\n            border-radius: 6px;\n            padding: 8px 14px;\n            display: flex;\n            align-items: center;\n            gap: 8px;\n            backdrop-filter: blur(8px);\n            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);\n        }\n\n        .splash-stat-icon {\n            font-size: 16px;\n            opacity: 0.8;\n        }\n\n        .splash-stat-value {\n            font-family: 'Orbitron', monospace;\n            font-size: 14px;\n            color: var(--text-primary);\n            font-weight: 600;\n            letter-spacing: 1px;\n        }\n\n        .splash-stat-coins .splash-stat-value {\n            color: var(--accent-gold);\n        }\n\n        .splash-content {\n            position: relative;\n            z-index: 5;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            text-align: center;\n            margin-top: -60px;\n        }\n\n        .splash-title-glow {\n            position: absolute;\n            width: 400px;\n            height: 120px;\n            background: radial-gradient(ellipse at center, rgba(60, 100, 160, 0.2) 0%, transparent 70%);\n            filter: blur(30px);\n            animation: titleGlow 3s ease-in-out infinite alternate;\n        }\n\n        @keyframes titleGlow {\n            0% { opacity: 0.5; transform: scale(1); }\n            100% { opacity: 0.8; transform: scale(1.1); }\n        }\n\n        .splash-title {\n            font-family: 'Orbitron', monospace;\n            font-size: 48px;\n            font-weight: 900;\n            color: var(--text-primary);\n            letter-spacing: 8px;\n            text-shadow: \n                0 0 30px rgba(80, 120, 180, 0.3),\n                0 4px 8px rgba(0, 0, 0, 0.9),\n                0 0 80px rgba(60, 100, 160, 0.2);\n            margin: 0;\n            position: relative;\n            animation: titleFadeIn 1s ease-out forwards;\n            opacity: 0;\n            transform: translateY(20px);\n        }\n\n        @keyframes titleFadeIn {\n            to { opacity: 1; transform: translateY(0); }\n        }\n\n        .splash-subtitle {\n            font-family: 'Orbitron', monospace;\n            font-size: 16px;\n            font-weight: 400;\n            color: var(--text-muted);\n            letter-spacing: 10px;\n            text-transform: uppercase;\n            margin-top: 12px;\n            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8);\n            animation: titleFadeIn 1s ease-out 0.3s forwards;\n            opacity: 0;\n        }\n\n        .splash-divider {\n            width: 180px;\n            height: 1px;\n            background: linear-gradient(90deg, transparent, var(--border-glow), transparent);\n            margin: 24px 0;\n            animation: titleFadeIn 1s ease-out 0.5s forwards;\n            opacity: 0;\n        }\n\n        .splash-mission-info {\n            font-family: 'Orbitron', monospace;\n            font-size: 12px;\n            color: var(--text-muted);\n            margin-bottom: 8px;\n            animation: titleFadeIn 1s ease-out 0.6s forwards;\n            opacity: 0;\n            letter-spacing: 2px;\n        }\n\n        .splash-bottom {\n            position: absolute;\n            bottom: 60px;\n            left: 50%;\n            transform: translateX(-50%);\n            z-index: 10;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            gap: 16px;\n        }\n\n        .splash-start-btn {\n            font-family: 'Orbitron', monospace;\n            font-size: 18px;\n            font-weight: 700;\n            letter-spacing: 4px;\n            padding: 18px 50px;\n            background: linear-gradient(180deg, rgba(35, 140, 90, 0.95) 0%, rgba(22, 100, 65, 0.98) 100%);\n            border: 1px solid rgba(60, 180, 110, 0.4);\n            border-radius: 6px;\n            color: #fff;\n            cursor: pointer;\n            position: relative;\n            overflow: hidden;\n            box-shadow: \n                0 6px 30px rgba(0, 0, 0, 0.5),\n                0 0 40px var(--accent-green-glow),\n                inset 0 1px 0 rgba(255, 255, 255, 0.15);\n            transition: all 0.2s ease;\n            animation: btnFadeIn 1s ease-out 0.8s forwards;\n            opacity: 0;\n            transform: translateY(20px);\n            text-transform: uppercase;\n        }\n\n        @keyframes btnFadeIn {\n            to { opacity: 1; transform: translateY(0); }\n        }\n\n        .splash-start-btn::before {\n            content: '';\n            position: absolute;\n            top: 0;\n            left: -100%;\n            width: 100%;\n            height: 100%;\n            background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);\n            animation: btnShine 3s ease-in-out infinite;\n        }\n\n        @keyframes btnShine {\n            0% { left: -100%; }\n            50%, 100% { left: 100%; }\n        }\n\n        .splash-start-btn:hover {\n            background: linear-gradient(180deg, rgba(45, 160, 105, 0.95) 0%, rgba(28, 120, 78, 0.98) 100%);\n            box-shadow: \n                0 8px 40px rgba(0, 0, 0, 0.6),\n                0 0 60px var(--accent-green-glow),\n                inset 0 1px 0 rgba(255, 255, 255, 0.2);\n            transform: translateY(-3px);\n        }\n\n        .splash-start-btn:active {\n            transform: translateY(1px) scale(0.97);\n            box-shadow: \n                0 2px 15px rgba(0, 0, 0, 0.5),\n                0 0 25px var(--accent-green-glow),\n                inset 0 2px 4px rgba(0, 0, 0, 0.3);\n        }\n\n        .splash-hint {\n            font-family: 'Inter', sans-serif;\n            font-size: 12px;\n            color: var(--text-muted);\n            letter-spacing: 2px;\n            animation: titleFadeIn 1s ease-out 1s forwards;\n            opacity: 0;\n        }\n\n        .splash-how-to-play {\n            font-family: 'Orbitron', monospace;\n            font-size: 12px;\n            letter-spacing: 3px;\n            padding: 12px 28px;\n            background: linear-gradient(145deg, rgba(35, 45, 60, 0.9), rgba(20, 28, 40, 0.95));\n            border: 1px solid var(--border-subtle);\n            border-radius: 6px;\n            color: var(--text-secondary);\n            cursor: pointer;\n            margin-top: 12px;\n            transition: all 0.2s ease;\n            animation: btnFadeIn 1s ease-out 0.9s forwards;\n            opacity: 0;\n            text-transform: uppercase;\n            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);\n        }\n\n        .splash-how-to-play:hover {\n            background: linear-gradient(145deg, rgba(45, 58, 78, 0.95), rgba(28, 38, 55, 0.98));\n            color: var(--text-primary);\n            border-color: var(--border-glow);\n            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5), 0 0 15px rgba(80, 100, 130, 0.2);\n        }\n\n        /* Tutorial Overlay */\n        #tutorial-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: linear-gradient(180deg, rgba(8, 12, 18, 0.98) 0%, rgba(12, 18, 28, 0.99) 100%);\n            z-index: 160;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: flex-start;\n            padding: 40px 24px;\n            box-sizing: border-box;\n            overflow-y: auto;\n            backdrop-filter: blur(10px);\n        }\n\n        #tutorial-overlay.hidden {\n            display: none;\n        }\n\n        .tutorial-header {\n            text-align: center;\n            margin-bottom: 24px;\n        }\n\n        .tutorial-header h1 {\n            font-family: 'Orbitron', monospace;\n            font-size: 28px;\n            color: var(--accent-blue);\n            margin: 0 0 8px;\n            letter-spacing: 5px;\n            text-shadow: 0 0 30px rgba(60, 120, 200, 0.4);\n        }\n\n        .tutorial-header p {\n            font-family: 'Inter', sans-serif;\n            font-size: 13px;\n            color: var(--text-muted);\n            margin: 0;\n            letter-spacing: 1px;\n        }\n\n        .tutorial-content {\n            width: 100%;\n            max-width: 500px;\n        }\n\n        .tutorial-section {\n            background: var(--bg-card);\n            border: 1px solid var(--border-subtle);\n            border-radius: 8px;\n            padding: 20px;\n            margin-bottom: 16px;\n            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);\n        }\n\n        .tutorial-section h2 {\n            font-family: 'Orbitron', monospace;\n            font-size: 14px;\n            color: var(--accent-gold);\n            margin: 0 0 16px;\n            display: flex;\n            align-items: center;\n            gap: 10px;\n            letter-spacing: 2px;\n        }\n\n        .tutorial-section h2 .icon {\n            font-size: 24px;\n        }\n\n        .tutorial-item {\n            display: flex;\n            align-items: flex-start;\n            gap: 12px;\n            margin-bottom: 12px;\n            padding-bottom: 12px;\n            border-bottom: 1px solid rgba(255, 255, 255, 0.05);\n        }\n\n        .tutorial-item:last-child {\n            margin-bottom: 0;\n            padding-bottom: 0;\n            border-bottom: none;\n        }\n\n        .tutorial-item .key {\n            min-width: 80px;\n            padding: 6px 12px;\n            background: linear-gradient(145deg, rgba(35, 55, 90, 0.8), rgba(25, 40, 70, 0.9));\n            border: 1px solid rgba(60, 100, 160, 0.4);\n            border-radius: 4px;\n            font-family: 'Orbitron', monospace;\n            font-size: 10px;\n            color: var(--accent-blue);\n            text-align: center;\n            white-space: nowrap;\n            letter-spacing: 1px;\n            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n        }\n\n        .tutorial-item .desc {\n            font-family: 'Inter', sans-serif;\n            font-size: 13px;\n            color: var(--text-secondary);\n            line-height: 1.5;\n        }\n\n        .tutorial-warning {\n            background: linear-gradient(145deg, rgba(140, 35, 45, 0.2), rgba(100, 25, 35, 0.25));\n            border: 1px solid rgba(220, 80, 90, 0.3);\n            border-radius: 6px;\n            padding: 16px;\n            margin-top: 8px;\n        }\n\n        .tutorial-warning h3 {\n            font-family: 'Orbitron', monospace;\n            font-size: 12px;\n            color: var(--accent-red);\n            margin: 0 0 12px;\n            display: flex;\n            align-items: center;\n            gap: 8px;\n            letter-spacing: 1px;\n        }\n\n        .tutorial-warning ul {\n            margin: 0;\n            padding-left: 20px;\n        }\n\n        .tutorial-warning li {\n            font-family: 'Inter', sans-serif;\n            font-size: 12px;\n            color: var(--text-secondary);\n            margin-bottom: 6px;\n        }\n\n        .tutorial-tips {\n            background: linear-gradient(145deg, rgba(30, 120, 80, 0.2), rgba(20, 90, 60, 0.25));\n            border: 1px solid rgba(50, 160, 100, 0.3);\n        }\n\n        .tutorial-tips h3 {\n            color: var(--accent-green);\n        }\n\n        .tutorial-close-btn {\n            font-family: 'Orbitron', monospace;\n            font-size: 16px;\n            font-weight: 700;\n            letter-spacing: 3px;\n            padding: 16px 48px;\n            background: linear-gradient(145deg, rgba(35, 140, 90, 0.95), rgba(22, 100, 65, 0.98));\n            border: 1px solid rgba(60, 180, 110, 0.4);\n            border-radius: 6px;\n            color: #fff;\n            cursor: pointer;\n            margin-top: 24px;\n            box-shadow: 0 6px 25px rgba(0, 0, 0, 0.5), 0 0 30px var(--accent-green-glow);\n            transition: all 0.2s ease;\n            text-transform: uppercase;\n        }\n\n        .tutorial-close-btn:hover {\n            background: linear-gradient(145deg, rgba(45, 160, 105, 0.95), rgba(28, 120, 78, 0.98));\n            transform: translateY(-3px);\n            box-shadow: 0 8px 35px rgba(0, 0, 0, 0.6), 0 0 40px var(--accent-green-glow);\n        }\n\n        .tutorial-close-btn:active {\n            transform: translateY(1px);\n            box-shadow: 0 2px 15px rgba(0, 0, 0, 0.5);\n        }\n\n        /* Leaderboard Overlay */\n        #leaderboard-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: linear-gradient(180deg, rgba(8, 12, 18, 0.98) 0%, rgba(12, 18, 28, 0.99) 100%);\n            z-index: 160;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: flex-start;\n            padding: 40px 24px;\n            box-sizing: border-box;\n            overflow-y: auto;\n            backdrop-filter: blur(10px);\n        }\n\n        #leaderboard-overlay.hidden {\n            display: none;\n        }\n\n        .leaderboard-header {\n            text-align: center;\n            margin-bottom: 24px;\n        }\n\n        .leaderboard-header h1 {\n            font-family: 'Orbitron', monospace;\n            font-size: 32px;\n            color: var(--accent-gold);\n            margin: 0 0 8px;\n            letter-spacing: 5px;\n            text-shadow: 0 0 30px var(--accent-gold-glow);\n        }\n\n        .leaderboard-header p {\n            font-family: 'Inter', sans-serif;\n            font-size: 13px;\n            color: var(--text-muted);\n            margin: 0;\n            letter-spacing: 1px;\n        }\n\n        .leaderboard-content {\n            width: 100%;\n            max-width: 600px;\n            background: var(--bg-card);\n            border: 1px solid var(--border-subtle);\n            border-radius: 8px;\n            padding: 20px;\n            box-shadow: 0 6px 30px rgba(0, 0, 0, 0.5);\n        }\n\n        .leaderboard-loading {\n            font-family: 'Orbitron', monospace;\n            font-size: 14px;\n            color: var(--text-secondary);\n            text-align: center;\n            padding: 40px;\n            letter-spacing: 2px;\n        }\n\n        .leaderboard-error {\n            font-family: 'Inter', sans-serif;\n            font-size: 14px;\n            color: var(--accent-red);\n            text-align: center;\n            padding: 40px;\n        }\n\n        .leaderboard-list {\n            display: flex;\n            flex-direction: column;\n            gap: 12px;\n        }\n\n        .leaderboard-entry {\n            display: flex;\n            align-items: center;\n            gap: 16px;\n            padding: 12px 16px;\n            background: linear-gradient(145deg, rgba(30, 38, 52, 0.6), rgba(18, 24, 36, 0.8));\n            border: 1px solid var(--border-subtle);\n            border-radius: 6px;\n            transition: all 0.2s ease;\n        }\n\n        .leaderboard-entry:hover {\n            background: linear-gradient(145deg, rgba(40, 50, 68, 0.7), rgba(25, 32, 48, 0.9));\n            border-color: var(--border-glow);\n            transform: translateX(4px);\n        }\n\n        .leaderboard-entry.user-entry {\n            background: linear-gradient(145deg, rgba(60, 100, 140, 0.3), rgba(40, 70, 110, 0.4));\n            border-color: rgba(80, 140, 220, 0.4);\n            box-shadow: 0 0 20px rgba(60, 120, 200, 0.2);\n        }\n\n        .leaderboard-rank {\n            font-family: 'Orbitron', monospace;\n            font-size: 20px;\n            font-weight: 700;\n            color: var(--text-secondary);\n            min-width: 40px;\n            text-align: center;\n        }\n\n        .leaderboard-rank.top1 {\n            color: #FFD700;\n            text-shadow: 0 0 10px rgba(255, 215, 0, 0.5);\n        }\n\n        .leaderboard-rank.top2 {\n            color: #C0C0C0;\n            text-shadow: 0 0 10px rgba(192, 192, 192, 0.4);\n        }\n\n        .leaderboard-rank.top3 {\n            color: #CD7F32;\n            text-shadow: 0 0 10px rgba(205, 127, 50, 0.4);\n        }\n\n        .leaderboard-profile-pic {\n            width: 48px;\n            height: 48px;\n            border-radius: 50%;\n            border: 2px solid var(--border-subtle);\n            object-fit: cover;\n            background: linear-gradient(145deg, #2a3344, #1a2030);\n        }\n\n        .leaderboard-info {\n            flex: 1;\n            display: flex;\n            flex-direction: column;\n            gap: 4px;\n        }\n\n        .leaderboard-username {\n            font-family: 'Orbitron', monospace;\n            font-size: 16px;\n            font-weight: 600;\n            color: var(--text-primary);\n            letter-spacing: 1px;\n        }\n\n        .leaderboard-user-rank {\n            font-family: 'Inter', sans-serif;\n            font-size: 11px;\n            color: var(--text-muted);\n            letter-spacing: 1px;\n        }\n\n        .leaderboard-score {\n            font-family: 'Orbitron', monospace;\n            font-size: 18px;\n            font-weight: 700;\n            color: var(--accent-gold);\n            text-shadow: 0 0 10px var(--accent-gold-glow);\n            letter-spacing: 1px;\n            display: flex;\n            align-items: center;\n            gap: 6px;\n        }\n\n        .leaderboard-score::before {\n            content: '\ud83e\ude99';\n            font-size: 16px;\n        }\n\n        .leaderboard-empty {\n            font-family: 'Inter', sans-serif;\n            font-size: 14px;\n            color: var(--text-muted);\n            text-align: center;\n            padding: 40px;\n        }\n\n        .splash-settings-btn:active {\n            transform: scale(0.95);\n        }\n\n        .tutorial-close-btn:active {\n            transform: translateY(1px);\n            box-shadow: 0 2px 15px rgba(0, 0, 0, 0.5);\n        }\n\n        /* Shop Overlay */\n        #shop-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: linear-gradient(180deg, rgba(8, 12, 18, 0.98) 0%, rgba(12, 18, 28, 0.99) 100%);\n            z-index: 160;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            padding: 20px 16px;\n            box-sizing: border-box;\n            overflow-y: auto;\n            backdrop-filter: blur(10px);\n        }\n\n        #shop-overlay.hidden {\n            display: none;\n        }\n\n        .shop-header {\n            text-align: center;\n            margin-bottom: 16px;\n            width: 100%;\n        }\n\n        .shop-header h1 {\n            font-family: 'Orbitron', monospace;\n            font-size: 28px;\n            color: var(--accent-gold);\n            margin: 0 0 4px;\n            letter-spacing: 4px;\n            text-shadow: 0 0 30px var(--accent-gold-glow);\n        }\n\n        .shop-coins {\n            font-family: 'Orbitron', monospace;\n            font-size: 18px;\n            color: var(--accent-gold);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 8px;\n        }\n\n        .shop-tabs {\n            display: flex;\n            gap: 8px;\n            margin-bottom: 16px;\n        }\n\n        .shop-tab {\n            font-family: 'Orbitron', monospace;\n            font-size: 12px;\n            letter-spacing: 2px;\n            padding: 10px 20px;\n            background: linear-gradient(145deg, rgba(30, 38, 52, 0.9), rgba(18, 24, 36, 0.95));\n            border: 1px solid var(--border-subtle);\n            border-radius: 6px;\n            color: var(--text-secondary);\n            cursor: pointer;\n            transition: all 0.2s ease;\n            text-transform: uppercase;\n        }\n\n        .shop-tab:hover {\n            background: linear-gradient(145deg, rgba(40, 50, 68, 0.95), rgba(25, 32, 48, 0.98));\n            color: var(--text-primary);\n        }\n\n        .shop-tab.active {\n            background: linear-gradient(145deg, rgba(60, 100, 140, 0.5), rgba(40, 70, 110, 0.6));\n            border-color: rgba(80, 140, 220, 0.5);\n            color: var(--accent-blue);\n            box-shadow: 0 0 15px rgba(60, 120, 200, 0.2);\n        }\n\n        .shop-content {\n            width: 100%;\n            max-width: 600px;\n            flex: 1;\n        }\n\n        .shop-grid {\n            display: grid;\n            grid-template-columns: repeat(2, 1fr);\n            gap: 12px;\n        }\n\n        .shop-item {\n            background: var(--bg-card);\n            border: 1px solid var(--border-subtle);\n            border-radius: 8px;\n            padding: 14px;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            gap: 10px;\n            transition: all 0.2s ease;\n            cursor: pointer;\n        }\n\n        .shop-item:hover {\n            border-color: var(--border-glow);\n            transform: translateY(-2px);\n            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);\n        }\n\n        .shop-item.equipped {\n            border-color: var(--accent-green);\n            box-shadow: 0 0 15px var(--accent-green-glow);\n        }\n\n        .shop-item.locked {\n            opacity: 0.7;\n        }\n\n        .shop-item-preview {\n            width: 80px;\n            height: 50px;\n            border-radius: 4px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            position: relative;\n            overflow: hidden;\n        }\n\n        .shop-item-preview.gun-preview {\n            background: linear-gradient(145deg, rgba(30, 35, 45, 0.9), rgba(20, 25, 35, 0.95));\n        }\n\n        .shop-item-preview .gun-shape {\n            width: 70px;\n            height: 20px;\n            border-radius: 2px;\n            position: relative;\n        }\n\n        .shop-item-preview .gun-accent {\n            position: absolute;\n            left: 15px;\n            top: 50%;\n            transform: translateY(-50%);\n            width: 3px;\n            height: 16px;\n            border-radius: 1px;\n        }\n\n        .shop-item-preview.bullet-preview {\n            background: radial-gradient(circle, rgba(40, 45, 55, 0.9) 0%, rgba(20, 25, 35, 0.95) 100%);\n        }\n\n        .bullet-effect-demo {\n            width: 40px;\n            height: 4px;\n            border-radius: 2px;\n            position: relative;\n        }\n\n        .bullet-effect-demo::after {\n            content: '';\n            position: absolute;\n            right: -10px;\n            top: 50%;\n            transform: translateY(-50%);\n            width: 12px;\n            height: 12px;\n            border-radius: 50%;\n            animation: bulletPulse 1s ease-in-out infinite;\n        }\n\n        @keyframes bulletPulse {\n            0%, 100% { transform: translateY(-50%) scale(0.8); opacity: 0.6; }\n            50% { transform: translateY(-50%) scale(1.2); opacity: 1; }\n        }\n\n        .shop-item-name {\n            font-family: 'Orbitron', monospace;\n            font-size: 11px;\n            color: var(--text-primary);\n            text-align: center;\n            letter-spacing: 1px;\n        }\n\n        .shop-item-price {\n            font-family: 'Orbitron', monospace;\n            font-size: 14px;\n            color: var(--accent-gold);\n            display: flex;\n            align-items: center;\n            gap: 4px;\n        }\n\n        .shop-item-price.owned {\n            color: var(--accent-green);\n        }\n\n        .shop-item-status {\n            font-family: 'Inter', sans-serif;\n            font-size: 10px;\n            color: var(--text-muted);\n            text-transform: uppercase;\n            letter-spacing: 1px;\n        }\n\n        .shop-item-status.equipped-status {\n            color: var(--accent-green);\n        }\n\n        .shop-btn-row {\n            display: flex;\n            gap: 8px;\n            margin-top: 20px;\n        }\n\n        .splash-particles {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            overflow: hidden;\n        }\n\n        .particle {\n            position: absolute;\n            width: 2px;\n            height: 2px;\n            background: rgba(255, 200, 150, 0.4);\n            border-radius: 50%;\n            animation: particleFloat 15s linear infinite;\n        }\n\n        @keyframes particleFloat {\n            0% { transform: translateY(100vh) translateX(0); opacity: 0; }\n            10% { opacity: 1; }\n            90% { opacity: 1; }\n            100% { transform: translateY(-20vh) translateX(50px); opacity: 0; }\n        }\n\n        .results-stats {\n            background: var(--bg-card);\n            border: 1px solid var(--border-subtle);\n            border-radius: 8px;\n            padding: 20px 36px;\n            margin: 16px 0;\n            box-shadow: 0 6px 25px rgba(0, 0, 0, 0.5);\n        }\n\n        .results-stats p {\n            display: flex;\n            justify-content: space-between;\n            gap: 40px;\n            color: var(--text-secondary);\n            font-size: 15px;\n            margin: 10px 0;\n        }\n\n        .results-stats .value {\n            color: var(--accent-blue);\n            font-family: 'Orbitron', monospace;\n            font-weight: 600;\n        }\n    </style>\n</head>\n<body>\n    <div id=\"rotate-overlay\">\n        <div class=\"rotate-icon\">\ud83d\udcf1</div>\n        <p>Please rotate your device to portrait mode</p>\n    </div>\n    <div id=\"game-world\">\n        <canvas id=\"game-canvas\"></canvas>\n        <div class=\"ui-overlay\">\n            <div id=\"hud-top\">\n                <button id=\"pause-btn\">\u275a\u275a</button>\n                <div id=\"target-counter\">\n                    <div class=\"target-icon\">\u2295</div>\n                    <span class=\"count\"><span id=\"targets-hit\">0</span>/<span id=\"targets-total\">1</span></span>\n                </div>\n                <div id=\"hud-right\">\n                    <div id=\"coins-display\"><div class=\"coin-icon\">$</div> <span id=\"coins-amount\">0</span></div>\n                </div>\n            </div>\n            <div id=\"stability-bar-container\">\n                <div id=\"stability-bar\"></div>\n                <div id=\"stability-bar-marker\"></div>\n            </div>\n            <div id=\"timer\" class=\"hud-element\">30</div>\n            <div id=\"ammo\">\u25cf \u25cf \u25cf</div>\n            <div id=\"aim-joystick\">\n                <div id=\"aim-joystick-knob\"></div>\n                <div id=\"aim-joystick-label\">MOVE</div>\n            </div>\n            <div id=\"action-buttons\">\n                \n                <button class=\"action-btn\" id=\"scope-btn\"><span>\u25ce</span><span class=\"btn-label\">Scope</span></button>\n                <button class=\"action-btn\" id=\"shoot-btn\"><span>\u2299</span><span class=\"btn-label\">Fire</span></button>\n            </div>\n            <div id=\"crosshair\">\n                <div class=\"ring\"></div>\n                <div class=\"tick tick-top\"></div>\n                <div class=\"tick tick-bottom\"></div>\n                <div class=\"tick tick-left\"></div>\n                <div class=\"tick tick-right\"></div>\n                <div class=\"dot\"></div>\n            </div>\n            <div id=\"hit-marker\"></div>\n            <div id=\"slow-mo-overlay\"></div>\n            <div id=\"rifle-overlay\"><canvas id=\"rifle-canvas\"></canvas><div id=\"muzzle-flash\"></div></div>\n            <div id=\"scope-overlay\">\n                <div class=\"vignette\"></div>\n                <div class=\"scope-ring\"></div>\n                <div class=\"crosshair-h\"></div>\n                <div class=\"crosshair-v\"></div>\n                <div class=\"scope-center-dot\"></div>\n                <div class=\"mil-dots\">\n                    <div class=\"mil-dot\" style=\"top: 30px; left: -3px;\"></div>\n                    <div class=\"mil-dot\" style=\"top: 60px; left: -3px;\"></div>\n                    <div class=\"mil-dot\" style=\"top: 90px; left: -3px;\"></div>\n                    <div class=\"mil-dot\" style=\"top: -30px; left: -3px;\"></div>\n                </div>\n            </div>\n            <div id=\"countdown\"></div>\n            <div id=\"target-desc\"></div>\n            <div id=\"hit-flash\"></div>\n            <div id=\"blood-screen-flash\"></div>\n            <div id=\"blood-container\"></div>\n            <div id=\"fog-overlay\"></div>\n            <div id=\"night-vision-overlay\"></div>\n            <div id=\"wind-indicator\"><span class=\"wind-arrow\">\u2192</span> <span id=\"wind-text\">WIND: 0</span></div>\n            <div id=\"pointer-lock-hint\">\ud83d\uddb1\ufe0f Click to aim freely &nbsp;|&nbsp; <kbd>Esc</kbd> to release</div>\n            <div id=\"combo-counter\">\ud83d\udd25 COMBO x<span id=\"combo-count\">0</span></div>\n            <div id=\"modifier-badge\"></div>\n            <div id=\"escape-warning\">\u26a0 TARGET ESCAPING</div>\n            \n            <div id=\"mission-overlay\"></div>\n        </div>\n        <div id=\"leaderboard-overlay\" class=\"hidden\">\n            <div class=\"leaderboard-header\">\n                <h1>\ud83c\udfc6 LEADERBOARD</h1>\n                <p>Top Elite Snipers</p>\n            </div>\n            <div class=\"leaderboard-content\" id=\"leaderboard-content\">\n                <div class=\"leaderboard-loading\">Loading rankings...</div>\n            </div>\n            <button class=\"tutorial-close-btn\" id=\"leaderboard-close\">CLOSE</button>\n        </div>\n        <div id=\"shop-overlay\" class=\"hidden\">\n            <div class=\"shop-header\">\n                <h1>\ud83d\uded2 ARMORY</h1>\n                <div class=\"shop-coins\">\ud83e\ude99 <span id=\"shop-coins\">0</span></div>\n            </div>\n            <div class=\"shop-tabs\">\n                <button class=\"shop-tab active\" id=\"tab-guns\">\ud83d\udd2b GUNS</button>\n                <button class=\"shop-tab\" id=\"tab-bullets\">\ud83d\udca5 BULLETS</button>\n            </div>\n            <div class=\"shop-content\" id=\"shop-content\">\n                <!-- Items populated by JS -->\n            </div>\n            <div class=\"shop-btn-row\">\n                <button class=\"tutorial-close-btn\" id=\"shop-close\">BACK</button>\n            </div>\n        </div>\n        <div id=\"tutorial-overlay\" class=\"hidden\">\n            <div class=\"tutorial-header\">\n                <h1>HOW TO PLAY</h1>\n                <p>Master these controls to become an elite sniper</p>\n            </div>\n            <div class=\"tutorial-content\">\n                <div class=\"tutorial-section\">\n                    <h2><span class=\"icon\">\ud83c\udfaf</span> CONTROLS - MOBILE</h2>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">DRAG</span>\n                        <span class=\"desc\">Drag anywhere on screen to aim your rifle</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">\ud83c\udfaf BUTTON</span>\n                        <span class=\"desc\">Tap the shoot button to fire</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">\u2295 BUTTON</span>\n                        <span class=\"desc\">Toggle scope for zoomed precision aiming</span>\n                    </div>\n                </div>\n                <div class=\"tutorial-section\">\n                    <h2><span class=\"icon\">\ud83d\uddb1\ufe0f</span> CONTROLS - PC</h2>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">CLICK GAME</span>\n                        <span class=\"desc\">Click the game area to activate pointer lock (free mouse aim)</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">MOUSE</span>\n                        <span class=\"desc\">Move mouse freely to aim (pointer lock active)</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">LEFT CLICK</span>\n                        <span class=\"desc\">Fire your weapon</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">RIGHT CLICK</span>\n                        <span class=\"desc\">Toggle scope zoom for precision aiming</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">SPACE</span>\n                        <span class=\"desc\">Alternative fire button</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">E / R</span>\n                        <span class=\"desc\">Alternative scope toggle</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">ESC</span>\n                        <span class=\"desc\">Pause the game (also exits pointer lock)</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">WASD</span>\n                        <span class=\"desc\">Move your position on the rooftop</span>\n                    </div>\n                </div>\n                <div class=\"tutorial-section\">\n                    <h2><span class=\"icon\">\ud83d\udccb</span> MISSION OBJECTIVE</h2>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">\ud83d\udd34 TARGET</span>\n                        <span class=\"desc\">Eliminate the target marked with a <span style=\"color:#EF4444;\">red marker</span> above their head. They wear a red jacket.</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">\u23f1\ufe0f TIME</span>\n                        <span class=\"desc\">Complete mission before timer runs out. Target may try to escape!</span>\n                    </div>\n                    <div class=\"tutorial-warning\">\n                        <h3>\u26a0\ufe0f MISSION FAIL CONDITIONS</h3>\n                        <ul>\n                            <li>Shooting an innocent civilian</li>\n                            <li>Missing 3 shots (running out of ammo)</li>\n                            <li>Time runs out before eliminating target</li>\n                            <li>Target escapes the area</li>\n                        </ul>\n                    </div>\n                </div>\n                <div class=\"tutorial-section\">\n                    <h2><span class=\"icon\">\ud83d\udcb0</span> SCORING & BONUSES</h2>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">+100</span>\n                        <span class=\"desc\">Base reward for eliminating target</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">+50</span>\n                        <span class=\"desc\">Headshot bonus (aim for the head!)</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">+25-50</span>\n                        <span class=\"desc\">Speed bonus for quick eliminations</span>\n                    </div>\n                    <div class=\"tutorial-item\">\n                        <span class=\"key\">+40</span>\n                        <span class=\"desc\">Perfect accuracy bonus (no misses)</span>\n                    </div>\n                    <div class=\"tutorial-warning tutorial-tips\">\n                        <h3>\ud83d\udca1 PRO TIPS</h3>\n                        <ul>\n                            <li>Use the scope for long-range precision shots</li>\n                            <li>Wait for a clear shot - patience is key</li>\n                            <li>Headshots trigger slow-motion for style points</li>\n                            <li>Watch for mission modifiers like wind or night ops</li>\n                        </ul>\n                    </div>\n                </div>\n            </div>\n            <button class=\"tutorial-close-btn\" id=\"tutorial-close\">GOT IT!</button>\n        </div>\n        <div id=\"loading-screen\">\n            <div class=\"loading-vignette\"></div>\n            <div class=\"loading-ambient-glow\"></div>\n            <div class=\"loading-fog\"></div>\n            <div class=\"loading-particles\" id=\"loading-particles\"></div>\n            \n            <div class=\"loading-title-container\">\n                <h1>QUICKSCOPE</h1>\n                <h2>Sniper Contracts</h2>\n            </div>\n            \n            <div class=\"loading-scope\">\n                <div class=\"scope-bg\"></div>\n                <div class=\"scope-inner-ring\"></div>\n                <div class=\"ring\"></div>\n                <div class=\"dot\"></div>\n                <div class=\"scope-mil-dot\" style=\"top: 50%; left: 25%; transform: translateY(-50%);\"></div>\n                <div class=\"scope-mil-dot\" style=\"top: 50%; left: 75%; transform: translateY(-50%);\"></div>\n                <div class=\"scope-mil-dot\" style=\"top: 25%; left: 50%; transform: translateX(-50%);\"></div>\n                <div class=\"scope-mil-dot\" style=\"top: 75%; left: 50%; transform: translateX(-50%);\"></div>\n            </div>\n            \n            <div class=\"loading-bar-container\">\n                <div class=\"loading-bar\" id=\"loading-bar\"></div>\n            </div>\n            <p id=\"loading-text\">INITIALIZING...</p>\n        </div>\n        <div id=\"splash-screen\" class=\"hidden\">\n            <div class=\"splash-bg-layer\">\n                <img class=\"splash-bg-image\" id=\"splash-bg-img\" src=\"\" alt=\"\">\n            </div>\n            <div class=\"splash-overlay-gradient\"></div>\n            <div class=\"splash-fog\"></div>\n            <div class=\"splash-vignette\"></div>\n            <div class=\"splash-scope-overlay\">\n                <div class=\"splash-scope-ring\"></div>\n                <div class=\"splash-scope-crosshair-h\"></div>\n                <div class=\"splash-scope-crosshair-v\"></div>\n            </div>\n            <div class=\"splash-particles\" id=\"splash-particles\"></div>\n            <div class=\"splash-hud-top\">\n                <div style=\"display: flex; gap: 12px;\">\n                    <button class=\"splash-settings-btn\" id=\"splash-shop\">\ud83d\uded2</button>\n                    <button class=\"splash-settings-btn\" id=\"splash-leaderboard\">\ud83c\udfc6</button>\n                    <button class=\"splash-settings-btn\" id=\"splash-settings\">\u2699</button>\n                </div>\n                <div class=\"splash-stats\">\n                    <div class=\"splash-stat splash-stat-level\">\n                        <span class=\"splash-stat-icon\">\ud83d\udcca</span>\n                        <span class=\"splash-stat-value\" id=\"splash-level\">Lv.1</span>\n                    </div>\n                    <div class=\"splash-stat splash-stat-coins\">\n                        <span class=\"splash-stat-icon\">\ud83e\ude99</span>\n                        <span class=\"splash-stat-value\" id=\"splash-coins\">0</span>\n                    </div>\n                </div>\n            </div>\n            <div class=\"splash-content\">\n                <div class=\"splash-title-glow\"></div>\n                <h1 class=\"splash-title\">QUICKSCOPE</h1>\n                <p class=\"splash-subtitle\">Sniper Contracts</p>\n                <div class=\"splash-divider\"></div>\n                <p class=\"splash-mission-info\" id=\"splash-mission-info\">MISSION 1 \u2022 CITY ROOFTOP</p>\n            </div>\n            <div class=\"splash-bottom\">\n                <button class=\"splash-start-btn\" id=\"splash-start\">START MISSION</button>\n                <button class=\"splash-how-to-play\" id=\"splash-tutorial\">HOW TO PLAY</button>\n                <p class=\"splash-hint\">Drag to aim \u2022 Tap to shoot</p>\n            </div>\n        </div>\n    </div>\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js\"></script>\n    <script>\n    /*\n     * QuickScope: Sniper Contracts (Lite)\n     * A lightweight first-person sniper game with low-poly 3D environments.\n     * Drag to aim, tap to shoot. Eliminate the marked target among civilians.\n     */\n    async function run(mode) {\n        const gameWorld = document.getElementById('game-world');\n        const canvas = document.getElementById('game-canvas');\n        const overlay = document.querySelector('.ui-overlay');\n        const missionOverlay = document.getElementById('mission-overlay');\n        const timerEl = document.getElementById('timer');\n        const ammoEl = document.getElementById('ammo');\n        const coinsEl = document.getElementById('coins-amount');\n        \n        const targetsHitEl = document.getElementById('targets-hit');\n        const targetsTotalEl = document.getElementById('targets-total');\n        const crosshairEl = document.getElementById('crosshair');\n        const hitMarkerEl = document.getElementById('hit-marker');\n        const rifleCanvas = document.getElementById('rifle-canvas');\n        const muzzleFlash = document.getElementById('muzzle-flash');\n        const countdownEl = document.getElementById('countdown');\n        const targetDescEl = document.getElementById('target-desc');\n        const slowMoOverlay = document.getElementById('slow-mo-overlay');\n        const rifleOverlay = document.getElementById('rifle-overlay');\n        const scopeBtn = document.getElementById('scope-btn');\n        const shootBtn = document.getElementById('shoot-btn');\n        const scopeOverlay = document.getElementById('scope-overlay');\n        const stabilityBar = document.getElementById('stability-bar');\n        const stabilityMarker = document.getElementById('stability-bar-marker');\n        const pauseBtn = document.getElementById('pause-btn');\n        const actionButtons = document.getElementById('action-buttons');\n        const aimJoystick = document.getElementById('aim-joystick');\n        const aimJoystickKnob = document.getElementById('aim-joystick-knob');\n        const hitFlash = document.getElementById('hit-flash');\n        const fogOverlay = document.getElementById('fog-overlay');\n        const nightVisionOverlay = document.getElementById('night-vision-overlay');\n        const windIndicator = document.getElementById('wind-indicator');\n        const windArrow = windIndicator.querySelector('.wind-arrow');\n        const windText = document.getElementById('wind-text');\n        const comboCounter = document.getElementById('combo-counter');\n        const comboCountEl = document.getElementById('combo-count');\n        const modifierBadge = document.getElementById('modifier-badge');\n        const escapeWarning = document.getElementById('escape-warning');\n        \n        const bloodContainer = document.getElementById('blood-container');\n        const bloodScreenFlash = document.getElementById('blood-screen-flash');\n        \n\n        const config = window.gameConfig;\n\n        // Audio context\n        let audioCtx = null;\n        const audioBuffers = {};\n\n        function getAudioContext() {\n            if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();\n            return audioCtx;\n        }\n\n        async function loadAudio(id) {\n            try {\n                const asset = lib.getAsset(id);\n                if (!asset || asset.type !== 'audio') return;\n                const ctx = getAudioContext();\n                const response = await fetch(asset.url);\n                const buffer = await response.arrayBuffer();\n                audioBuffers[id] = await ctx.decodeAudioData(buffer);\n            } catch(e) { lib.log('Audio load error: ' + id); }\n        }\n\n        function playSound(id, volume = 1.0) {\n            try {\n                const ctx = getAudioContext();\n                if (!audioBuffers[id]) return;\n                const source = ctx.createBufferSource();\n                const gain = ctx.createGain();\n                source.buffer = audioBuffers[id];\n                gain.gain.value = volume;\n                source.connect(gain);\n                gain.connect(ctx.destination);\n                source.start(0);\n                return source;\n            } catch(e) {}\n        }\n\n        let bgMusicSource = null;\n        function playBGMusic() {\n            try {\n                const ctx = getAudioContext();\n                if (!audioBuffers['background_music']) return;\n                if (bgMusicSource) { try { bgMusicSource.stop(); } catch(e){} }\n                bgMusicSource = ctx.createBufferSource();\n                const gain = ctx.createGain();\n                bgMusicSource.buffer = audioBuffers['background_music'];\n                bgMusicSource.loop = true;\n                gain.gain.value = (config.settings.musicVolume ?? 50) / 100 * 0.3;\n                bgMusicSource.connect(gain);\n                gain.connect(ctx.destination);\n                bgMusicSource.start(0);\n            } catch(e) {}\n        }\n\n        // Preload images\n        const imageCache = {};\n        function loadImage(id) {\n            return new Promise((resolve) => {\n                const asset = lib.getAsset(id);\n                if (!asset || asset.type !== 'image') { resolve(); return; }\n                const img = new Image();\n                img.crossOrigin = 'anonymous';\n                img.onload = () => { imageCache[id] = img; resolve(); };\n                img.onerror = () => { resolve(); };\n                img.src = asset.url;\n            });\n        }\n\n        // Loading screen elements\n        const loadingScreen = document.getElementById('loading-screen');\n        const loadingBar = document.getElementById('loading-bar');\n        const loadingText = document.getElementById('loading-text');\n        const loadingParticlesContainer = document.getElementById('loading-particles');\n\n        // Create loading particles\n        function createLoadingParticles() {\n            for (let i = 0; i < 30; i++) {\n                const particle = document.createElement('div');\n                particle.className = 'loading-particle';\n                particle.style.left = Math.random() * 100 + '%';\n                particle.style.animationDelay = Math.random() * 20 + 's';\n                particle.style.animationDuration = (15 + Math.random() * 10) + 's';\n                loadingParticlesContainer.appendChild(particle);\n            }\n        }\n        createLoadingParticles();\n\n        // Dynamic loading text\n        const loadingMessages = [\n            'LOADING SNIPER RIFLE...',\n            'PREPARING MISSION...',\n            'CALIBRATING SCOPE...',\n            'ANALYZING TARGET...',\n            'INITIALIZING SYSTEMS...',\n            'LOADING ASSETS...',\n            'CONFIGURING OPTICS...',\n            'SYNCING DATA...'\n        ];\n        let loadingMsgIndex = 0;\n        const loadingTextInterval = setInterval(() => {\n            loadingMsgIndex = (loadingMsgIndex + 1) % loadingMessages.length;\n            loadingText.textContent = loadingMessages[loadingMsgIndex];\n        }, 2000);\n\n        let loadedCount = 0;\n        const totalAssets = 14;\n\n        function updateLoadingProgress(assetName) {\n            loadedCount++;\n            const progress = Math.round((loadedCount / totalAssets) * 100);\n            loadingBar.style.width = progress + '%';\n        }\n\n        // Wrap loaders to track progress\n        async function loadImageWithProgress(id) {\n            await loadImage(id);\n            updateLoadingProgress(id.replace(/_/g, ' '));\n        }\n\n        async function loadAudioWithProgress(id) {\n            await loadAudio(id);\n            updateLoadingProgress(id.replace(/_/g, ' '));\n        }\n\n        // Load all assets\n        await Promise.all([\n            loadImageWithProgress('sniper_rifle_01'),\n            loadImageWithProgress('sniper_rifle_02'),\n            loadImageWithProgress('target_male_01'),\n            loadImageWithProgress('civilian_male_01'),\n            loadImageWithProgress('civilian_male_02'),\n            loadImageWithProgress('civilian_female_01'),\n            loadImageWithProgress('rooftop_ac_unit'),\n            loadImageWithProgress('rooftop_water_tank'),\n            loadImageWithProgress('rooftop_crate'),\n            loadImageWithProgress('splash_background'),\n            loadAudioWithProgress('gunshot_sound'),\n            loadAudioWithProgress('headshot_sound'),\n            loadAudioWithProgress('mission_fail_sound'),\n            loadAudioWithProgress('background_music')\n        ]);\n\n        loadingText.textContent = 'BUILDING WORLD...';\n\n        // 3D Rifle setup\n        const rifleRenderer = new THREE.WebGLRenderer({ canvas: rifleCanvas, antialias: true, alpha: true });\n        rifleRenderer.setSize(gameWorld.clientWidth, gameWorld.clientHeight);\n        rifleRenderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n        rifleRenderer.setClearColor(0x000000, 0);\n\n        const rifleScene = new THREE.Scene();\n        const rifleCamera = new THREE.PerspectiveCamera(50, gameWorld.clientWidth / gameWorld.clientHeight, 0.01, 10);\n        rifleCamera.position.set(0, 0, 0);\n\n        // Rifle lighting\n        const rifleAmbient = new THREE.AmbientLight(0xffffff, 0.6);\n        rifleScene.add(rifleAmbient);\n        const rifleDir = new THREE.DirectionalLight(0xffeedd, 0.8);\n        rifleDir.position.set(1, 2, 1);\n        rifleScene.add(rifleDir);\n        const rifleRim = new THREE.DirectionalLight(0x88aaff, 0.3);\n        rifleRim.position.set(-1, 0, -1);\n        rifleScene.add(rifleRim);\n\n        // Build 3D sniper rifle\n        function build3DRifle() {\n            const rifleGroup = new THREE.Group();\n            \n            // Materials\n            const metalDark = new THREE.MeshStandardMaterial({ color: 0x2a2a2a, metalness: 0.8, roughness: 0.3 });\n            const metalGray = new THREE.MeshStandardMaterial({ color: 0x4a4a4a, metalness: 0.7, roughness: 0.4 });\n            const metalLight = new THREE.MeshStandardMaterial({ color: 0x5a5a5a, metalness: 0.6, roughness: 0.5 });\n            const woodMat = new THREE.MeshStandardMaterial({ color: 0x3d2817, metalness: 0.1, roughness: 0.8 });\n            const scopeGlass = new THREE.MeshStandardMaterial({ color: 0x1a3a5a, metalness: 0.9, roughness: 0.1, transparent: true, opacity: 0.8 });\n            const accentRed = new THREE.MeshStandardMaterial({ color: 0x8B2020, metalness: 0.5, roughness: 0.4 });\n\n            // Main barrel (long cylinder)\n            const barrelGeo = new THREE.CylinderGeometry(0.012, 0.015, 0.5, 8);\n            const barrel = new THREE.Mesh(barrelGeo, metalDark);\n            barrel.rotation.z = Math.PI / 2;\n            barrel.position.set(0.15, 0, 0);\n            rifleGroup.add(barrel);\n\n            // Barrel shroud/handguard\n            const shroudGeo = new THREE.BoxGeometry(0.25, 0.04, 0.045);\n            const shroud = new THREE.Mesh(shroudGeo, metalGray);\n            shroud.position.set(0.02, -0.005, 0);\n            rifleGroup.add(shroud);\n\n            // Shroud vents (decorative cuts)\n            for (let i = 0; i < 4; i++) {\n                const ventGeo = new THREE.BoxGeometry(0.03, 0.05, 0.02);\n                const vent = new THREE.Mesh(ventGeo, metalDark);\n                vent.position.set(-0.06 + i * 0.04, -0.005, 0.018);\n                rifleGroup.add(vent);\n            }\n\n            // Receiver (main body)\n            const receiverGeo = new THREE.BoxGeometry(0.18, 0.055, 0.05);\n            const receiver = new THREE.Mesh(receiverGeo, metalGray);\n            receiver.position.set(-0.15, 0, 0);\n            rifleGroup.add(receiver);\n\n            // Magazine\n            const magGeo = new THREE.BoxGeometry(0.06, 0.1, 0.025);\n            const magazine = new THREE.Mesh(magGeo, metalDark);\n            magazine.position.set(-0.14, -0.07, 0);\n            rifleGroup.add(magazine);\n\n            // Trigger guard\n            const guardGeo = new THREE.TorusGeometry(0.02, 0.004, 6, 8, Math.PI);\n            const guard = new THREE.Mesh(guardGeo, metalLight);\n            guard.rotation.x = Math.PI / 2;\n            guard.rotation.z = Math.PI;\n            guard.position.set(-0.12, -0.035, 0);\n            rifleGroup.add(guard);\n\n            // Pistol grip\n            const gripGeo = new THREE.BoxGeometry(0.035, 0.08, 0.04);\n            const grip = new THREE.Mesh(gripGeo, woodMat);\n            grip.rotation.z = -0.3;\n            grip.position.set(-0.17, -0.06, 0);\n            rifleGroup.add(grip);\n\n            // Stock (buttstock)\n            const stockGeo = new THREE.BoxGeometry(0.15, 0.045, 0.035);\n            const stock = new THREE.Mesh(stockGeo, woodMat);\n            stock.position.set(-0.32, 0.005, 0);\n            rifleGroup.add(stock);\n\n            // Stock end piece\n            const stockEndGeo = new THREE.BoxGeometry(0.025, 0.06, 0.045);\n            const stockEnd = new THREE.Mesh(stockEndGeo, metalDark);\n            stockEnd.position.set(-0.4, 0.005, 0);\n            rifleGroup.add(stockEnd);\n\n            // Cheek rest\n            const cheekGeo = new THREE.BoxGeometry(0.08, 0.015, 0.04);\n            const cheek = new THREE.Mesh(cheekGeo, woodMat);\n            cheek.position.set(-0.28, 0.035, 0);\n            rifleGroup.add(cheek);\n\n            // Scope mount rail\n            const railGeo = new THREE.BoxGeometry(0.12, 0.012, 0.02);\n            const rail = new THREE.Mesh(railGeo, metalLight);\n            rail.position.set(-0.1, 0.035, 0);\n            rifleGroup.add(rail);\n\n            // Scope body\n            const scopeBodyGeo = new THREE.CylinderGeometry(0.018, 0.018, 0.14, 12);\n            const scopeBody = new THREE.Mesh(scopeBodyGeo, metalDark);\n            scopeBody.rotation.z = Math.PI / 2;\n            scopeBody.position.set(-0.1, 0.065, 0);\n            rifleGroup.add(scopeBody);\n\n            // Scope front lens housing\n            const scopeFrontGeo = new THREE.CylinderGeometry(0.022, 0.018, 0.025, 12);\n            const scopeFront = new THREE.Mesh(scopeFrontGeo, metalGray);\n            scopeFront.rotation.z = Math.PI / 2;\n            scopeFront.position.set(-0.02, 0.065, 0);\n            rifleGroup.add(scopeFront);\n\n            // Scope front lens\n            const lensGeo = new THREE.CircleGeometry(0.018, 12);\n            const lens = new THREE.Mesh(lensGeo, scopeGlass);\n            lens.rotation.y = -Math.PI / 2;\n            lens.position.set(-0.005, 0.065, 0);\n            rifleGroup.add(lens);\n\n            // Scope rear lens housing  \n            const scopeRearGeo = new THREE.CylinderGeometry(0.015, 0.018, 0.02, 12);\n            const scopeRear = new THREE.Mesh(scopeRearGeo, metalGray);\n            scopeRear.rotation.z = Math.PI / 2;\n            scopeRear.position.set(-0.18, 0.065, 0);\n            rifleGroup.add(scopeRear);\n\n            // Scope adjustment turrets\n            const turretGeo = new THREE.CylinderGeometry(0.008, 0.008, 0.015, 8);\n            const turretTop = new THREE.Mesh(turretGeo, metalLight);\n            turretTop.position.set(-0.1, 0.088, 0);\n            rifleGroup.add(turretTop);\n            const turretSide = new THREE.Mesh(turretGeo.clone(), metalLight);\n            turretSide.rotation.x = Math.PI / 2;\n            turretSide.position.set(-0.1, 0.065, 0.025);\n            rifleGroup.add(turretSide);\n\n            // Bipod legs (folded)\n            const bipodGeo = new THREE.CylinderGeometry(0.004, 0.004, 0.08, 6);\n            const bipodL = new THREE.Mesh(bipodGeo, metalDark);\n            bipodL.rotation.z = Math.PI / 2 + 0.3;\n            bipodL.position.set(0.08, -0.025, 0.03);\n            rifleGroup.add(bipodL);\n            const bipodR = new THREE.Mesh(bipodGeo.clone(), metalDark);\n            bipodR.rotation.z = Math.PI / 2 + 0.3;\n            bipodR.position.set(0.08, -0.025, -0.03);\n            rifleGroup.add(bipodR);\n\n            // Muzzle brake\n            const muzzleGeo = new THREE.CylinderGeometry(0.018, 0.015, 0.04, 8);\n            const muzzle = new THREE.Mesh(muzzleGeo, metalDark);\n            muzzle.rotation.z = Math.PI / 2;\n            muzzle.position.set(0.42, 0, 0);\n            rifleGroup.add(muzzle);\n\n            // Muzzle brake ports\n            for (let i = 0; i < 3; i++) {\n                const portGeo = new THREE.BoxGeometry(0.008, 0.025, 0.008);\n                const port = new THREE.Mesh(portGeo, metalLight);\n                port.position.set(0.4 + i * 0.012, 0.012, 0);\n                rifleGroup.add(port);\n            }\n\n            // Red accent stripe\n            const accentGeo = new THREE.BoxGeometry(0.003, 0.04, 0.05);\n            const accent = new THREE.Mesh(accentGeo, accentRed);\n            accent.position.set(-0.06, 0, 0);\n            rifleGroup.add(accent);\n\n            // Bolt handle\n            const boltGeo = new THREE.CylinderGeometry(0.006, 0.006, 0.03, 6);\n            const bolt = new THREE.Mesh(boltGeo, metalLight);\n            bolt.rotation.x = Math.PI / 2;\n            bolt.position.set(-0.08, 0.01, 0.04);\n            rifleGroup.add(bolt);\n            const boltKnob = new THREE.Mesh(new THREE.SphereGeometry(0.01, 6, 6), metalLight);\n            boltKnob.position.set(-0.08, 0.01, 0.055);\n            rifleGroup.add(boltKnob);\n\n            const wrapperGroup = new THREE.Group();\n            rifleGroup.rotation.y = Math.PI / 2; // Point rifle straight ahead (-Z)\n            wrapperGroup.add(rifleGroup);\n\n            return wrapperGroup;\n        }\n\n        const rifle3D = build3DRifle();\n        rifle3D.position.set(0.12, -0.18, -0.35);\n        rifle3D.rotation.set(0.05, 0, 0.02);\n        rifleScene.add(rifle3D);\n\n        // Store original rifle materials for skin application\n        const rifleMaterialsMap = new Map();\n        rifle3D.traverse(child => {\n            if (child.isMesh && child.material) {\n                rifleMaterialsMap.set(child.uuid, {\n                    originalColor: child.material.color.getHex(),\n                    type: child.material.color.getHex() === 0x8B2020 ? 'accent' : \n                          (child.material.color.getHex() === 0x2a2a2a || child.material.color.getHex() === 0x4a4a4a) ? 'main' : 'other'\n                });\n            }\n        });\n\n        // Apply gun skin to 3D rifle\n        function applyGunSkin(skinId) {\n            const skin = config.shop.gunSkins.find(g => g.id === skinId);\n            if (!skin) return;\n            \n            const mainColor = new THREE.Color(skin.color);\n            const accentColor = new THREE.Color(skin.accent);\n            \n            // Update rifle materials using stored original data\n            rifle3D.traverse(child => {\n                if (child.isMesh && child.material && rifleMaterialsMap.has(child.uuid)) {\n                    const matData = rifleMaterialsMap.get(child.uuid);\n                    \n                    if (matData.type === 'accent') {\n                        // Accent pieces (red stripe, etc)\n                        child.material.color.copy(accentColor);\n                    } else if (matData.type === 'main') {\n                        // Main body colors\n                        const originalColor = new THREE.Color(matData.originalColor);\n                        const lerpAmount = matData.originalColor === 0x2a2a2a ? 0.8 : 0.6;\n                        child.material.color.copy(originalColor).lerp(mainColor, lerpAmount);\n                    }\n                }\n            });\n            \n            lib.log('Applied gun skin: ' + skinId);\n        }\n\n        // Rifle animation state\n        let rifleRecoil = 0;\n        let rifleTargetX = 0.12;\n        let rifleTargetY = -0.18;\n        let rifleScopeHidden = false;\n\n        // Three.js setup\n        const renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: false, alpha: false });\n        renderer.setSize(gameWorld.clientWidth, gameWorld.clientHeight);\n        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n        renderer.setClearColor(0x1a2634);\n\n        const scene = new THREE.Scene();\n        const camera = new THREE.PerspectiveCamera(60, gameWorld.clientWidth / gameWorld.clientHeight, 0.1, 200);\n        camera.position.set(0, 12, 0);\n\n        // Camera rotation state\n        let cameraYaw = 0;\n        let cameraPitch = -0.1;\n        // Scope smoothing targets\n        let scopeTargetYaw = 0;\n        let scopeTargetPitch = -0.1;\n\n        function updateCamera() {\n            camera.rotation.order = 'YXZ';\n            camera.rotation.y = cameraYaw;\n            camera.rotation.x = cameraPitch;\n        }\n\n        window.addEventListener('resize', () => {\n            renderer.setSize(gameWorld.clientWidth, gameWorld.clientHeight);\n            camera.aspect = gameWorld.clientWidth / gameWorld.clientHeight;\n            camera.updateProjectionMatrix();\n            rifleRenderer.setSize(gameWorld.clientWidth, gameWorld.clientHeight);\n            rifleCamera.aspect = gameWorld.clientWidth / gameWorld.clientHeight;\n            rifleCamera.updateProjectionMatrix();\n        });\n\n        // Lighting - dramatic with enough for PBR materials\n        const ambientLight = new THREE.AmbientLight(0x8899aa, 0.5);\n        scene.add(ambientLight);\n        const dirLight = new THREE.DirectionalLight(0xffeedd, 0.8);\n        dirLight.position.set(5, 10, 3);\n        scene.add(dirLight);\n        \n        // Add subtle rim light for depth on 3D characters\n        const rimLight = new THREE.DirectionalLight(0x4488cc, 0.4);\n        rimLight.position.set(-5, 5, -10);\n        scene.add(rimLight);\n        \n        // Fill light from below to illuminate character faces\n        const fillLight = new THREE.DirectionalLight(0x6688aa, 0.2);\n        fillLight.position.set(0, -3, -5);\n        scene.add(fillLight);\n\n        // Darker sky gradient\n        scene.background = new THREE.Color(0x1a2634);\n        \n        // Add fog for depth\n        scene.fog = new THREE.Fog(0x1a2634, 40, 120);\n\n        // Build city rooftop scene\n        function buildScene() {\n            const sceneObstacles = [];\n            \n            // Player's building (underneath)\n            const playerBuildingGeo = new THREE.BoxGeometry(8, 12, 8);\n            const playerBuildingMat = new THREE.MeshLambertMaterial({ color: 0x3d4654 });\n            const playerBuilding = new THREE.Mesh(playerBuildingGeo, playerBuildingMat);\n            playerBuilding.position.set(0, 6 - 1, 0);\n            scene.add(playerBuilding);\n\n            // Target building (across the gap) - darker concrete\n            const targetBuildingGeo = new THREE.BoxGeometry(24, 10, 18);\n            const targetBuildingMat = new THREE.MeshLambertMaterial({ color: 0x4a5568 });\n            const targetBuilding = new THREE.Mesh(targetBuildingGeo, targetBuildingMat);\n            targetBuilding.position.set(0, 5 - 1, -35);\n            scene.add(targetBuilding);\n\n            // Rooftop floor - concrete texture feel\n            const roofGeo = new THREE.BoxGeometry(24, 0.3, 18);\n            const roofMat = new THREE.MeshLambertMaterial({ color: 0x6b7280 });\n            const roof = new THREE.Mesh(roofGeo, roofMat);\n            roof.position.set(0, 10, -35);\n            scene.add(roof);\n\n            // Edge walls (low parapet) - darker\n            const wallMat = new THREE.MeshLambertMaterial({ color: 0x2d3748 });\n            const wallPositions = [\n                { pos: [0, 10.5, -44], size: [24, 1, 0.3] },\n                { pos: [0, 10.5, -26], size: [24, 1, 0.3] },\n                { pos: [-12, 10.5, -35], size: [0.3, 1, 18] },\n                { pos: [12, 10.5, -35], size: [0.3, 1, 18] }\n            ];\n            wallPositions.forEach(w => {\n                const geo = new THREE.BoxGeometry(...w.size);\n                const mesh = new THREE.Mesh(geo, wallMat);\n                mesh.position.set(...w.pos);\n                scene.add(mesh);\n                sceneObstacles.push(mesh); // Track walls as obstacles\n            });\n\n            // Props on target roof - industrial feel (these block bullets)\n            const propMat1 = new THREE.MeshLambertMaterial({ color: 0x4a5568 });\n            const propMat2 = new THREE.MeshLambertMaterial({ color: 0x374151 });\n            const propPositions = [\n                { pos: [-8, 11, -40], size: [2.5, 1.8, 2.5], mat: propMat1 },\n                { pos: [7, 11.2, -30], size: [1.5, 3, 1.5], mat: propMat2 },\n                { pos: [4, 10.8, -42], size: [1.5, 1.5, 1.5], mat: propMat1 },\n                { pos: [-5, 11.5, -32], size: [1, 3, 1], mat: propMat2 },\n                { pos: [9, 10.7, -38], size: [2, 1.2, 2], mat: propMat1 },\n                { pos: [-9, 10.6, -33], size: [1.8, 1, 1.8], mat: propMat2 },\n                { pos: [0, 10.9, -28], size: [3, 1.5, 1], mat: propMat1 },\n            ];\n            propPositions.forEach(p => {\n                const geo = new THREE.BoxGeometry(...p.size);\n                const mesh = new THREE.Mesh(geo, p.mat);\n                mesh.position.set(...p.pos);\n                scene.add(mesh);\n                sceneObstacles.push(mesh); // Track props as obstacles\n            });\n            \n            // Add some vertical pipes/vents (these also block bullets)\n            const pipeMat = new THREE.MeshLambertMaterial({ color: 0x718096 });\n            const pipePositions = [\n                [-10, 11.5, -41], [10, 11.2, -29], [-6, 11, -28], [8, 11.3, -43]\n            ];\n            pipePositions.forEach(p => {\n                const pipeHeight = 2 + Math.random();\n                const geo = new THREE.CylinderGeometry(0.3, 0.3, pipeHeight, 8); // Slightly larger for collision\n                const mesh = new THREE.Mesh(geo, pipeMat);\n                mesh.position.set(...p);\n                scene.add(mesh);\n                sceneObstacles.push(mesh); // Track pipes as obstacles\n            });\n\n            // Background buildings (distant) - varied heights, darker colors\n            for (let i = 0; i < 12; i++) {\n                const h = 20 + Math.random() * 40;\n                const w = 6 + Math.random() * 10;\n                const d = 6 + Math.random() * 10;\n                const geo = new THREE.BoxGeometry(w, h, d);\n                const brightness = 0.15 + Math.random() * 0.15;\n                const mat = new THREE.MeshLambertMaterial({ color: new THREE.Color().setHSL(0.6, 0.1, brightness) });\n                const mesh = new THREE.Mesh(geo, mat);\n                mesh.position.set(-50 + i * 10 + Math.random() * 6, h/2 - 5, -75 - Math.random() * 40);\n                scene.add(mesh);\n                \n                // Add some lit windows\n                if (Math.random() > 0.5) {\n                    const windowMat = new THREE.MeshBasicMaterial({ color: 0xffeebb, transparent: true, opacity: 0.6 });\n                    for (let j = 0; j < 3; j++) {\n                        const wGeo = new THREE.PlaneGeometry(1, 1.5);\n                        const wMesh = new THREE.Mesh(wGeo, windowMat);\n                        wMesh.position.set(\n                            mesh.position.x + (Math.random() - 0.5) * w * 0.7,\n                            mesh.position.y - h * 0.3 + Math.random() * h * 0.4,\n                            mesh.position.z + d/2 + 0.1\n                        );\n                        scene.add(wMesh);\n                    }\n                }\n            }\n            \n            // Second row of buildings even further\n            for (let i = 0; i < 8; i++) {\n                const h = 30 + Math.random() * 50;\n                const w = 8 + Math.random() * 12;\n                const d = 8 + Math.random() * 12;\n                const geo = new THREE.BoxGeometry(w, h, d);\n                const mat = new THREE.MeshLambertMaterial({ color: new THREE.Color().setHSL(0.6, 0.05, 0.12 + Math.random() * 0.08) });\n                const mesh = new THREE.Mesh(geo, mat);\n                mesh.position.set(-60 + i * 18 + Math.random() * 10, h/2 - 5, -110 - Math.random() * 30);\n                scene.add(mesh);\n            }\n\n            // Ground plane far below - darker\n            const groundGeo = new THREE.PlaneGeometry(300, 300);\n            const groundMat = new THREE.MeshLambertMaterial({ color: 0x1a1f2e });\n            const ground = new THREE.Mesh(groundGeo, groundMat);\n            ground.rotation.x = -Math.PI / 2;\n            ground.position.y = -2;\n            scene.add(ground);\n            \n            // Street level lights (distant glow)\n            const streetLightMat = new THREE.MeshBasicMaterial({ color: 0xffaa44, transparent: true, opacity: 0.4 });\n            for (let i = 0; i < 20; i++) {\n                const lightGeo = new THREE.SphereGeometry(0.5, 8, 8);\n                const lightMesh = new THREE.Mesh(lightGeo, streetLightMat);\n                lightMesh.position.set(\n                    -80 + Math.random() * 160,\n                    -1,\n                    -50 - Math.random() * 80\n                );\n                scene.add(lightMesh);\n            }\n\n            // === ZONE 1: Left Rooftop Building ===\n            const leftBuildingGeo = new THREE.BoxGeometry(16, 8, 14);\n            const leftBuildingMat = new THREE.MeshLambertMaterial({ color: 0x3a4a5c });\n            const leftBuilding = new THREE.Mesh(leftBuildingGeo, leftBuildingMat);\n            leftBuilding.position.set(-23, 4, -36);\n            scene.add(leftBuilding);\n\n            // Left rooftop floor\n            const leftRoofGeo = new THREE.BoxGeometry(16, 0.3, 14);\n            const leftRoofMat = new THREE.MeshLambertMaterial({ color: 0x5a6a7a });\n            const leftRoof = new THREE.Mesh(leftRoofGeo, leftRoofMat);\n            leftRoof.position.set(-23, 8, -36);\n            scene.add(leftRoof);\n\n            // Left rooftop parapet walls\n            const leftWalls = [\n                { pos: [-23, 8.5, -43], size: [16, 1, 0.3] },\n                { pos: [-23, 8.5, -29], size: [16, 1, 0.3] },\n                { pos: [-31, 8.5, -36], size: [0.3, 1, 14] },\n                { pos: [-15, 8.5, -36], size: [0.3, 1, 14] }\n            ];\n            leftWalls.forEach(w => {\n                const geo = new THREE.BoxGeometry(...w.size);\n                const mesh = new THREE.Mesh(geo, wallMat);\n                mesh.position.set(...w.pos);\n                scene.add(mesh);\n                sceneObstacles.push(mesh);\n            });\n\n            // Left rooftop props\n            const leftProps = [\n                { pos: [-28, 9, -40], size: [2, 1.5, 2] },\n                { pos: [-18, 9.2, -32], size: [1.5, 2.5, 1.5] },\n                { pos: [-25, 8.8, -33], size: [1.8, 1.2, 1.8] },\n                { pos: [-20, 9.5, -39], size: [1, 2.8, 1] }\n            ];\n            leftProps.forEach(p => {\n                const geo = new THREE.BoxGeometry(...p.size);\n                const mesh = new THREE.Mesh(geo, propMat1);\n                mesh.position.set(...p.pos);\n                scene.add(mesh);\n                sceneObstacles.push(mesh);\n            });\n\n            // === ZONE 2: Right Rooftop Building ===\n            const rightBuildingGeo = new THREE.BoxGeometry(16, 9, 14);\n            const rightBuildingMat = new THREE.MeshLambertMaterial({ color: 0x445566 });\n            const rightBuilding = new THREE.Mesh(rightBuildingGeo, rightBuildingMat);\n            rightBuilding.position.set(21, 4.5, -34);\n            scene.add(rightBuilding);\n\n            // Right rooftop floor\n            const rightRoofGeo = new THREE.BoxGeometry(16, 0.3, 14);\n            const rightRoofMat = new THREE.MeshLambertMaterial({ color: 0x6a7a8a });\n            const rightRoof = new THREE.Mesh(rightRoofGeo, rightRoofMat);\n            rightRoof.position.set(21, 9, -34);\n            scene.add(rightRoof);\n\n            // Right rooftop parapet walls\n            const rightWalls = [\n                { pos: [21, 9.5, -41], size: [16, 1, 0.3] },\n                { pos: [21, 9.5, -27], size: [16, 1, 0.3] },\n                { pos: [13, 9.5, -34], size: [0.3, 1, 14] },\n                { pos: [29, 9.5, -34], size: [0.3, 1, 14] }\n            ];\n            rightWalls.forEach(w => {\n                const geo = new THREE.BoxGeometry(...w.size);\n                const mesh = new THREE.Mesh(geo, wallMat);\n                mesh.position.set(...w.pos);\n                scene.add(mesh);\n                sceneObstacles.push(mesh);\n            });\n\n            // Right rooftop props (antenna, boxes)\n            const rightProps = [\n                { pos: [25, 9.8, -37], size: [1.2, 2, 1.2] },\n                { pos: [17, 9.5, -30], size: [2, 1.8, 2] },\n                { pos: [23, 9.2, -31], size: [1.5, 1, 3] },\n                { pos: [19, 10, -38], size: [0.8, 3, 0.8] }\n            ];\n            rightProps.forEach(p => {\n                const geo = new THREE.BoxGeometry(...p.size);\n                const mesh = new THREE.Mesh(geo, propMat2);\n                mesh.position.set(...p.pos);\n                scene.add(mesh);\n                sceneObstacles.push(mesh);\n            });\n\n            // === ZONE 3: City Park (at street level) ===\n            // Park ground (green area)\n            const parkGroundGeo = new THREE.BoxGeometry(24, 0.3, 20);\n            const parkGroundMat = new THREE.MeshLambertMaterial({ color: 0x2d5a27 });\n            const parkGround = new THREE.Mesh(parkGroundGeo, parkGroundMat);\n            parkGround.position.set(-20, -1.95, -18);\n            scene.add(parkGround);\n\n            // Park pathways (lighter gray)\n            const pathMat = new THREE.MeshLambertMaterial({ color: 0x8a8a7a });\n            const pathGeo1 = new THREE.BoxGeometry(2, 0.05, 20);\n            const path1 = new THREE.Mesh(pathGeo1, pathMat);\n            path1.position.set(-20, -1.78, -18);\n            scene.add(path1);\n            const pathGeo2 = new THREE.BoxGeometry(24, 0.05, 2);\n            const path2 = new THREE.Mesh(pathGeo2, pathMat);\n            path2.position.set(-20, -1.78, -18);\n            scene.add(path2);\n\n            // Park trees (green cones on brown cylinders)\n            const treeTrunkMat = new THREE.MeshLambertMaterial({ color: 0x4a3020 });\n            const treeLeafMat = new THREE.MeshLambertMaterial({ color: 0x1a6a20 });\n            const treePositions = [\n                [-28, -18], [-24, -22], [-17, -15], [-13, -20], [-26, -14], [-11, -16], [-22, -23], [-15, -23]\n            ];\n            treePositions.forEach(([tx, tz]) => {\n                const trunkGeo = new THREE.CylinderGeometry(0.2, 0.3, 2.5, 6);\n                const trunk = new THREE.Mesh(trunkGeo, treeTrunkMat);\n                trunk.position.set(tx, -0.55, tz);\n                scene.add(trunk);\n                sceneObstacles.push(trunk);\n\n                const leafGeo = new THREE.ConeGeometry(1.5, 3, 6);\n                const leaf = new THREE.Mesh(leafGeo, treeLeafMat);\n                leaf.position.set(tx, 1.5, tz);\n                scene.add(leaf);\n                sceneObstacles.push(leaf);\n            });\n\n            // Park benches\n            const benchMat = new THREE.MeshLambertMaterial({ color: 0x5c3a1a });\n            const benchPositions = [[-23, -16], [-16, -20], [-27, -21]];\n            benchPositions.forEach(([bx, bz]) => {\n                const benchGeo = new THREE.BoxGeometry(2, 0.6, 0.6);\n                const bench = new THREE.Mesh(benchGeo, benchMat);\n                bench.position.set(bx, -1.4, bz);\n                scene.add(bench);\n            });\n\n            // Park lamp posts\n            const lampMat = new THREE.MeshLambertMaterial({ color: 0x3a3a3a });\n            const lampLightMat = new THREE.MeshBasicMaterial({ color: 0xffdd88, transparent: true, opacity: 0.7 });\n            [[-25, -17], [-15, -19], [-20, -22]].forEach(([lx, lz]) => {\n                const poleMat = new THREE.CylinderGeometry(0.1, 0.1, 4, 6);\n                const pole = new THREE.Mesh(poleMat, lampMat);\n                pole.position.set(lx, 0.2, lz);\n                scene.add(pole);\n                const lampGeo = new THREE.SphereGeometry(0.3, 8, 8);\n                const lamp = new THREE.Mesh(lampGeo, lampLightMat);\n                lamp.position.set(lx, 2.4, lz);\n                scene.add(lamp);\n            });\n\n            // === ZONE 4: Lower Terrace (adjacent building, closer) ===\n            const terraceBuildingGeo = new THREE.BoxGeometry(16, 6, 10);\n            const terraceBuildingMat = new THREE.MeshLambertMaterial({ color: 0x4a5060 });\n            const terraceBuilding = new THREE.Mesh(terraceBuildingGeo, terraceBuildingMat);\n            terraceBuilding.position.set(0, 3, -20);\n            scene.add(terraceBuilding);\n\n            // Terrace floor\n            const terraceFloorGeo = new THREE.BoxGeometry(16, 0.3, 10);\n            const terraceFloorMat = new THREE.MeshLambertMaterial({ color: 0x7a6a5a });\n            const terraceFloor = new THREE.Mesh(terraceFloorGeo, terraceFloorMat);\n            terraceFloor.position.set(0, 6, -20);\n            scene.add(terraceFloor);\n\n            // Terrace railing\n            const railMat = new THREE.MeshLambertMaterial({ color: 0x5a6a7a });\n            const terraceRails = [\n                { pos: [0, 6.5, -25], size: [16, 0.8, 0.2] },\n                { pos: [0, 6.5, -15], size: [16, 0.8, 0.2] },\n                { pos: [-8, 6.5, -20], size: [0.2, 0.8, 10] },\n                { pos: [8, 6.5, -20], size: [0.2, 0.8, 10] }\n            ];\n            terraceRails.forEach(r => {\n                const geo = new THREE.BoxGeometry(...r.size);\n                const mesh = new THREE.Mesh(geo, railMat);\n                mesh.position.set(...r.pos);\n                scene.add(mesh);\n                sceneObstacles.push(mesh);\n            });\n\n            // Terrace props (table, chairs, planters)\n            const terraceProps = [\n                { pos: [-4, 6.6, -21], size: [1.5, 0.8, 1.5] },\n                { pos: [3, 6.5, -18], size: [1, 1.2, 1] },\n                { pos: [5, 6.8, -22], size: [0.8, 1.5, 0.8] },\n                { pos: [-6, 6.4, -17], size: [2, 0.6, 1] }\n            ];\n            terraceProps.forEach(p => {\n                const geo = new THREE.BoxGeometry(...p.size);\n                const mesh = new THREE.Mesh(geo, propMat1);\n                mesh.position.set(...p.pos);\n                scene.add(mesh);\n                sceneObstacles.push(mesh);\n            });\n\n            return sceneObstacles;\n        }\n\n        const sceneObstacles = buildScene();\n\n        // Store obstacles for bullet collision detection\n        const obstacles = [...sceneObstacles];\n        \n        scene.updateMatrixWorld(true);\n        const obstacleBoxes = obstacles.map(obs => {\n            if (!obs.geometry.boundingBox) obs.geometry.computeBoundingBox();\n            const box = new THREE.Box3();\n            box.copy(obs.geometry.boundingBox).applyMatrix4(obs.matrixWorld);\n            return box;\n        });\n\n        function addObstacle(mesh) {\n            obstacles.push(mesh);\n            mesh.updateMatrixWorld(true);\n            if (!mesh.geometry.boundingBox) mesh.geometry.computeBoundingBox();\n            const box = new THREE.Box3();\n            box.copy(mesh.geometry.boundingBox).applyMatrix4(mesh.matrixWorld);\n            obstacleBoxes.push(box);\n        }\n\n        // NPC system using fully 3D humanoid models\n        const npcs = [];\n\n        // Clothing color palettes for civilians\n        const civilianOutfits = [\n            { shirt: 0x2266aa, pants: 0x2a2a3a, shoes: 0x1a1a1a, skin: 0xc68642, hair: 0x2a1a0a },\n            { shirt: 0x2d8a4e, pants: 0x3d3d4a, shoes: 0x2a1a0a, skin: 0xe0ac69, hair: 0x4a3520 },\n            { shirt: 0xd4a030, pants: 0x1a2a3a, shoes: 0x3a2a1a, skin: 0xf1c27d, hair: 0x1a0a00 },\n            { shirt: 0x7744aa, pants: 0x2a2a2a, shoes: 0x1a1a1a, skin: 0x8d5524, hair: 0x0a0a0a },\n            { shirt: 0x3388bb, pants: 0x404050, shoes: 0x2a2020, skin: 0xffdbac, hair: 0x5a3a1a },\n            { shirt: 0xcc5533, pants: 0x2a3040, shoes: 0x1a1a2a, skin: 0xc68642, hair: 0x3a2a1a }\n        ];\n        const targetOutfit = { shirt: 0xcc2222, pants: 0x1a1a2a, shoes: 0x0a0a0a, skin: 0xe0ac69, hair: 0x1a0a00 };\n\n        // Build a detailed 3D humanoid model\n        function build3DHuman(outfit) {\n            const group = new THREE.Group();\n\n            // Materials with realistic feel\n            const skinMat = new THREE.MeshStandardMaterial({ color: outfit.skin, roughness: 0.7, metalness: 0.0 });\n            const shirtMat = new THREE.MeshStandardMaterial({ color: outfit.shirt, roughness: 0.8, metalness: 0.0 });\n            const pantsMat = new THREE.MeshStandardMaterial({ color: outfit.pants, roughness: 0.75, metalness: 0.0 });\n            const shoesMat = new THREE.MeshStandardMaterial({ color: outfit.shoes, roughness: 0.6, metalness: 0.1 });\n            const hairMat = new THREE.MeshStandardMaterial({ color: outfit.hair, roughness: 0.9, metalness: 0.0 });\n\n            // Head (sphere, slightly taller than wide)\n            const headGeo = new THREE.SphereGeometry(0.18, 12, 10);\n            headGeo.scale(1, 1.1, 0.95);\n            const head = new THREE.Mesh(headGeo, skinMat);\n            head.position.y = 1.72;\n            group.add(head);\n\n            // Hair (cap on top of head)\n            const hairGeo = new THREE.SphereGeometry(0.19, 10, 8, 0, Math.PI * 2, 0, Math.PI * 0.55);\n            const hair = new THREE.Mesh(hairGeo, hairMat);\n            hair.position.y = 1.75;\n            group.add(hair);\n\n            // Face features\n            const eyeWhiteMat = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.3, metalness: 0.0 });\n            const eyePupilMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.2, metalness: 0.0 });\n            const lipMat = new THREE.MeshStandardMaterial({ color: 0x994444, roughness: 0.6, metalness: 0.0 });\n            const noseMat = new THREE.MeshStandardMaterial({ color: new THREE.Color(outfit.skin).multiplyScalar(0.85), roughness: 0.7, metalness: 0.0 });\n            const browMat = new THREE.MeshStandardMaterial({ color: outfit.hair, roughness: 0.9, metalness: 0.0 });\n\n            // Eyes (white part)\n            const eyeGeo = new THREE.SphereGeometry(0.035, 8, 6);\n            const eyeL = new THREE.Mesh(eyeGeo, eyeWhiteMat);\n            eyeL.position.set(-0.055, 1.74, 0.155);\n            eyeL.scale.set(1, 0.8, 0.6);\n            group.add(eyeL);\n            const eyeR = new THREE.Mesh(eyeGeo.clone(), eyeWhiteMat);\n            eyeR.position.set(0.055, 1.74, 0.155);\n            eyeR.scale.set(1, 0.8, 0.6);\n            group.add(eyeR);\n\n            // Pupils\n            const pupilGeo = new THREE.SphereGeometry(0.018, 6, 6);\n            const pupilL = new THREE.Mesh(pupilGeo, eyePupilMat);\n            pupilL.position.set(-0.055, 1.74, 0.17);\n            group.add(pupilL);\n            const pupilR = new THREE.Mesh(pupilGeo.clone(), eyePupilMat);\n            pupilR.position.set(0.055, 1.74, 0.17);\n            group.add(pupilR);\n\n            // Eyebrows\n            const browGeo = new THREE.BoxGeometry(0.05, 0.012, 0.02);\n            const browL = new THREE.Mesh(browGeo, browMat);\n            browL.position.set(-0.055, 1.78, 0.155);\n            browL.rotation.z = -0.1;\n            group.add(browL);\n            const browR = new THREE.Mesh(browGeo.clone(), browMat);\n            browR.position.set(0.055, 1.78, 0.155);\n            browR.rotation.z = 0.1;\n            group.add(browR);\n\n            // Nose\n            const noseGeo = new THREE.ConeGeometry(0.025, 0.05, 6);\n            const nose = new THREE.Mesh(noseGeo, noseMat);\n            nose.position.set(0, 1.71, 0.17);\n            nose.rotation.x = -Math.PI * 0.35;\n            group.add(nose);\n\n            // Mouth/lips\n            const mouthGeo = new THREE.BoxGeometry(0.05, 0.015, 0.015);\n            const mouth = new THREE.Mesh(mouthGeo, lipMat);\n            mouth.position.set(0, 1.65, 0.16);\n            group.add(mouth);\n\n            // Ears\n            const earGeo = new THREE.SphereGeometry(0.03, 6, 6);\n            const earL = new THREE.Mesh(earGeo, skinMat);\n            earL.position.set(-0.17, 1.72, 0);\n            earL.scale.set(0.5, 0.8, 0.6);\n            group.add(earL);\n            const earR = new THREE.Mesh(earGeo.clone(), skinMat);\n            earR.position.set(0.17, 1.72, 0);\n            earR.scale.set(0.5, 0.8, 0.6);\n            group.add(earR);\n\n            // Neck\n            const neckGeo = new THREE.CylinderGeometry(0.07, 0.08, 0.12, 8);\n            const neck = new THREE.Mesh(neckGeo, skinMat);\n            neck.position.y = 1.52;\n            group.add(neck);\n\n            // Torso (slightly tapered box for realism)\n            const torsoGeo = new THREE.BoxGeometry(0.5, 0.65, 0.28);\n            const torso = new THREE.Mesh(torsoGeo, shirtMat);\n            torso.position.y = 1.15;\n            group.add(torso);\n\n            // Shoulders (rounded caps)\n            const shoulderGeo = new THREE.SphereGeometry(0.1, 8, 6);\n            const shoulderL = new THREE.Mesh(shoulderGeo, shirtMat);\n            shoulderL.position.set(-0.28, 1.4, 0);\n            group.add(shoulderL);\n            const shoulderR = new THREE.Mesh(shoulderGeo.clone(), shirtMat);\n            shoulderR.position.set(0.28, 1.4, 0);\n            group.add(shoulderR);\n\n            // Collar/neckline detail\n            const collarGeo = new THREE.CylinderGeometry(0.12, 0.14, 0.06, 8);\n            const collar = new THREE.Mesh(collarGeo, shirtMat);\n            collar.position.y = 1.47;\n            group.add(collar);\n\n            // Upper arms\n            const upperArmGeo = new THREE.CylinderGeometry(0.07, 0.06, 0.35, 8);\n            upperArmGeo.translate(0, -0.175, 0);\n            const upperArmL = new THREE.Mesh(upperArmGeo, shirtMat);\n            upperArmL.position.set(-0.3, 1.375, 0);\n            upperArmL.rotation.z = 0.1;\n            group.add(upperArmL);\n            const upperArmR = new THREE.Mesh(upperArmGeo.clone(), shirtMat);\n            upperArmR.position.set(0.3, 1.375, 0);\n            upperArmR.rotation.z = -0.1;\n            group.add(upperArmR);\n\n            // Forearms (skin colored)\n            const forearmGeo = new THREE.CylinderGeometry(0.055, 0.05, 0.3, 8);\n            forearmGeo.translate(0, -0.15, 0);\n            const forearmL = new THREE.Mesh(forearmGeo, skinMat);\n            forearmL.position.set(0, -0.32, 0);\n            forearmL.rotation.z = -0.05;\n            upperArmL.add(forearmL);\n            const forearmR = new THREE.Mesh(forearmGeo.clone(), skinMat);\n            forearmR.position.set(0, -0.32, 0);\n            forearmR.rotation.z = 0.05;\n            upperArmR.add(forearmR);\n\n            // Hands\n            const handGeo = new THREE.SphereGeometry(0.05, 6, 6);\n            const handL = new THREE.Mesh(handGeo, skinMat);\n            handL.position.set(0, -0.3, 0);\n            forearmL.add(handL);\n            const handR = new THREE.Mesh(handGeo.clone(), skinMat);\n            handR.position.set(0, -0.3, 0);\n            forearmR.add(handR);\n\n            // Belt\n            const beltGeo = new THREE.BoxGeometry(0.5, 0.06, 0.29);\n            const beltMat = new THREE.MeshStandardMaterial({ color: 0x2a2018, roughness: 0.5, metalness: 0.2 });\n            const belt = new THREE.Mesh(beltGeo, beltMat);\n            belt.position.y = 0.82;\n            group.add(belt);\n\n            // Belt buckle\n            const buckleGeo = new THREE.BoxGeometry(0.06, 0.05, 0.02);\n            const buckleMat = new THREE.MeshStandardMaterial({ color: 0xc0c0c0, roughness: 0.3, metalness: 0.8 });\n            const buckle = new THREE.Mesh(buckleGeo, buckleMat);\n            buckle.position.set(0, 0.82, 0.15);\n            group.add(buckle);\n\n            // Upper legs (thighs)\n            const thighGeo = new THREE.CylinderGeometry(0.09, 0.08, 0.4, 8);\n            thighGeo.translate(0, -0.2, 0);\n            const thighL = new THREE.Mesh(thighGeo, pantsMat);\n            thighL.position.set(-0.12, 0.78, 0);\n            group.add(thighL);\n            const thighR = new THREE.Mesh(thighGeo.clone(), pantsMat);\n            thighR.position.set(0.12, 0.78, 0);\n            group.add(thighR);\n\n            // Lower legs (shins)\n            const shinGeo = new THREE.CylinderGeometry(0.07, 0.06, 0.4, 8);\n            shinGeo.translate(0, -0.2, 0);\n            const shinL = new THREE.Mesh(shinGeo, pantsMat);\n            shinL.position.set(0, -0.36, 0);\n            thighL.add(shinL);\n            const shinR = new THREE.Mesh(shinGeo.clone(), pantsMat);\n            shinR.position.set(0, -0.36, 0);\n            thighR.add(shinR);\n\n            // Shoes (box-like for realistic footwear)\n            const shoeGeo = new THREE.BoxGeometry(0.12, 0.08, 0.22);\n            shoeGeo.translate(0, -0.04, 0.03);\n            const shoeL = new THREE.Mesh(shoeGeo, shoesMat);\n            shoeL.position.set(0, -0.4, 0);\n            shinL.add(shoeL);\n            const shoeR = new THREE.Mesh(shoeGeo.clone(), shoesMat);\n            shoeR.position.set(0, -0.4, 0);\n            shinR.add(shoeR);\n\n            // Store limb references for animation\n            group.userData = {\n                upperArmL, upperArmR, forearmL, forearmR,\n                thighL, thighR, shinL, shinR, head\n            };\n\n            return group;\n        }\n\n        function createNPC(type, startX, startZ, forcedBehavior = null) {\n            const height = 2.0;\n            const width = 0.6;\n\n            // Pick outfit\n            const outfit = type === 'target' ? targetOutfit : civilianOutfits[Math.floor(Math.random() * civilianOutfits.length)];\n            const sprite = build3DHuman(outfit);\n\n            sprite.position.set(startX, currentZone.groundY, startZ);\n            // Face away from player (player is at ~0,12,0) - NPCs are unaware\n            const awayAngle = Math.atan2(startX - camera.position.x, startZ - camera.position.z) + Math.PI + (Math.random() - 0.5) * 1.5;\n            sprite.rotation.y = awayAngle;\n            scene.add(sprite);\n\n            // Shadow under NPC\n            const shadowGeo = new THREE.PlaneGeometry(0.7, 0.5);\n            const shadowMat = new THREE.MeshBasicMaterial({\n                color: 0x000000,\n                transparent: true,\n                opacity: 0.3\n            });\n            const shadow = new THREE.Mesh(shadowGeo, shadowMat);\n            shadow.rotation.x = -Math.PI / 2;\n            shadow.position.set(startX, currentZone.groundY + 0.01, startZ);\n            scene.add(shadow);\n\n            // Red marker for target\n            let marker = null;\n            if (type === 'target') {\n                const markerGeo = new THREE.SphereGeometry(0.15, 8, 8);\n                const markerMat = new THREE.MeshBasicMaterial({ color: 0xEF4444 });\n                marker = new THREE.Mesh(markerGeo, markerMat);\n                marker.position.set(startX, currentZone.groundY + height + 0.4, startZ);\n                scene.add(marker);\n            }\n\n            // Determine speed based on modifiers\n            let baseSpeed = (0.8 + Math.random() * 0.6) * (config.settings.targetSpeed ?? 1);\n            if (type === 'target' && currentModifier === 'running') {\n                baseSpeed *= 2.0;\n            }\n\n            // Assign a behavior: walking, jogging, sleeping, talking\n            const behaviors = ['walking', 'jogging', 'sleeping', 'talking', 'walking', 'jogging'];\n            const behavior = forcedBehavior || (type === 'target'\n                ? (Math.random() > 0.3 ? 'walking' : 'jogging')\n                : behaviors[Math.floor(Math.random() * behaviors.length)]);\n\n            // Adjust speed based on behavior\n            if (behavior === 'jogging') baseSpeed *= 1.8;\n            else if (behavior === 'sleeping' || behavior === 'talking') baseSpeed = 0;\n\n            const npc = {\n                type,\n                sprite,\n                shadow,\n                marker,\n                x: startX,\n                z: startZ,\n                height,\n                width,\n                speed: baseSpeed,\n                baseSpeed: baseSpeed,\n                dirX: (Math.random() - 0.5) * 2,\n                dirZ: (Math.random() - 0.5) * 2,\n                stopTimer: 0,\n                isStopped: behavior === 'sleeping' || behavior === 'talking',\n                alive: true,\n                escaping: false,\n                speedVariationTimer: 0,\n                behavior: behavior,\n                behaviorTimer: 0,\n                talkingPartner: null,\n                animTime: Math.random() * 10 // offset animation phase\n            };\n\n            // Normalize direction\n            const len = Math.sqrt(npc.dirX * npc.dirX + npc.dirZ * npc.dirZ);\n            if (len > 0) {\n                npc.dirX /= len;\n                npc.dirZ /= len;\n            }\n\n            // Pair up talking civilians so they face each other\n            if (type === 'civilian' && behavior === 'talking') {\n                const partner = npcs.find(n => n.alive && n.type === 'civilian' && n.behavior === 'talking' && !n.talkingPartner);\n                if (partner) {\n                    npc.talkingPartner = partner;\n                    partner.talkingPartner = npc;\n                    npc.speed = 0;\n                    partner.speed = 0;\n                    npc.isStopped = true;\n                    partner.isStopped = true;\n                }\n            }\n\n            // Sleeping pose: lay character on side\n            if (behavior === 'sleeping') {\n                sprite.rotation.x = -Math.PI / 2;\n                sprite.position.y = currentZone.groundY + 0.3;\n            }\n\n            npcs.push(npc);\n            return npc;\n        }\n\n        function disposeObject(obj) {\n            if (!obj) return;\n            if (obj.geometry) obj.geometry.dispose();\n            if (obj.material) {\n                if (Array.isArray(obj.material)) {\n                    obj.material.forEach(m => m.dispose());\n                } else {\n                    obj.material.dispose();\n                }\n            }\n            if (obj.children) {\n                obj.children.forEach(disposeObject);\n            }\n        }\n\n        function removeAllNPCs() {\n            npcs.forEach(n => {\n                scene.remove(n.sprite);\n                disposeObject(n.sprite);\n                if (n.shadow) {\n                    scene.remove(n.shadow);\n                    disposeObject(n.shadow);\n                }\n                if (n.marker) {\n                    scene.remove(n.marker);\n                    disposeObject(n.marker);\n                }\n            });\n            npcs.length = 0;\n        }\n\n        // Animate 3D human limbs for walking/jogging/talking\n        function animateHumanLimbs(npc, dt) {\n            const limbs = npc.sprite.userData;\n            if (!limbs || !limbs.upperArmL) return;\n\n            npc.animTime += dt;\n            const t = npc.animTime;\n\n            if (npc.behavior === 'sleeping') {\n                // No limb animation for sleeping\n                return;\n            }\n\n            if (npc.behavior === 'talking' || (npc.isStopped && npc.behavior !== 'sleeping')) {\n                // Gesture animation - subtle arm/hand movement\n                const gestureSpeed = 2.5;\n                limbs.upperArmL.rotation.z = 0.1 + Math.sin(t * gestureSpeed) * 0.15;\n                limbs.upperArmR.rotation.z = -0.1 - Math.sin(t * gestureSpeed + 1) * 0.1;\n                limbs.forearmL.rotation.z = 0.05 + Math.sin(t * gestureSpeed) * 0.1;\n                limbs.forearmR.rotation.z = -0.05 - Math.sin(t * gestureSpeed + 1) * 0.08;\n                // Reset legs and arms\n                limbs.thighL.rotation.x = 0;\n                limbs.thighR.rotation.x = 0;\n                limbs.shinL.rotation.x = 0;\n                limbs.shinR.rotation.x = 0;\n                limbs.upperArmL.rotation.x = 0;\n                limbs.upperArmR.rotation.x = 0;\n                limbs.forearmL.rotation.x = 0;\n                limbs.forearmR.rotation.x = 0;\n                return;\n            }\n\n            if (!npc.isStopped && npc.speed > 0) {\n                // Walk/Jog cycle\n                const animSpeed = npc.behavior === 'jogging' ? 8 : 4.5;\n                const amplitude = npc.behavior === 'jogging' ? 0.5 : 0.3;\n                const armSwing = npc.behavior === 'jogging' ? 0.4 : 0.25;\n\n                // Legs swing forward/backward\n                limbs.thighL.rotation.x = Math.sin(t * animSpeed) * amplitude;\n                limbs.thighR.rotation.x = Math.sin(t * animSpeed + Math.PI) * amplitude;\n                limbs.shinL.rotation.x = -Math.max(0, Math.sin(t * animSpeed + 0.5)) * amplitude * 0.8;\n                limbs.shinR.rotation.x = -Math.max(0, Math.sin(t * animSpeed + Math.PI + 0.5)) * amplitude * 0.8;\n\n                // Arms swing opposite to legs\n                limbs.upperArmL.rotation.x = Math.sin(t * animSpeed + Math.PI) * armSwing;\n                limbs.upperArmR.rotation.x = Math.sin(t * animSpeed) * armSwing;\n                \n                // Elbows bend forward (positive X) when arm swings forward\n                limbs.forearmL.rotation.x = Math.max(0, Math.sin(t * animSpeed + Math.PI)) * armSwing * 1.2;\n                limbs.forearmR.rotation.x = Math.max(0, Math.sin(t * animSpeed)) * armSwing * 1.2;\n\n                // Subtle body bob\n                const bobHeight = npc.behavior === 'jogging' ? 0.06 : 0.03;\n                npc.sprite.position.y = currentZone.groundY + Math.abs(Math.sin(t * animSpeed)) * bobHeight;\n\n                // Slight torso lean for jogging\n                if (npc.behavior === 'jogging') {\n                    limbs.head.rotation.x = -0.05;\n                }\n            } else {\n                // Idle - reset to neutral with slight breathing\n                limbs.thighL.rotation.x = 0;\n                limbs.thighR.rotation.x = 0;\n                limbs.shinL.rotation.x = 0;\n                limbs.shinR.rotation.x = 0;\n                limbs.upperArmL.rotation.x = 0;\n                limbs.upperArmR.rotation.x = 0;\n                limbs.upperArmL.rotation.z = 0.1;\n                limbs.upperArmR.rotation.z = -0.1;\n                limbs.forearmL.rotation.x = 0;\n                limbs.forearmR.rotation.x = 0;\n                limbs.forearmL.rotation.z = -0.05;\n                limbs.forearmR.rotation.z = 0.05;\n                // Breathing\n                npc.sprite.position.y = currentZone.groundY + Math.sin(t * 1.5) * 0.01;\n            }\n        }\n\n        function updateNPCs(dt) {\n            const bounds = currentZone.bounds;\n            const escapeBounds = { minZ: currentZone.bounds.minZ - 7 };\n\n            npcs.forEach(npc => {\n                if (!npc.alive) return;\n\n                npc.stopTimer += dt;\n                npc.speedVariationTimer += dt;\n\n                // Animate limbs\n                animateHumanLimbs(npc, dt);\n\n                // Target escape behavior\n                if (npc.type === 'target' && npc.escaping) {\n                    npc.z -= npc.speed * 1.5 * dt;\n                    npc.sprite.position.z = npc.z;\n                    if (npc.shadow) npc.shadow.position.z = npc.z;\n                    if (npc.marker) npc.marker.position.z = npc.z;\n                    \n                    if (npc.z < escapeBounds.minZ && !killCamActive && gameState !== 'result') {\n                        // Target escaped\n                        npc.alive = false;\n                        scene.remove(npc.sprite);\n                        if (npc.shadow) scene.remove(npc.shadow);\n                        if (npc.marker) scene.remove(npc.marker);\n                        playSound('mission_fail_sound', (config.settings.sfxVolume ?? 70) / 100);\n                        gameState = 'result';\n                        setTimeout(() => endMission(false, 'Target escaped!'), 300);\n                    }\n                    return;\n                }\n\n                // Speed variation for target\n                if (npc.type === 'target' && npc.speedVariationTimer > 3) {\n                    npc.speedVariationTimer = 0;\n                    npc.speed = npc.baseSpeed * (0.7 + Math.random() * 0.6);\n                }\n\n                // Behavior-based movement\n                npc.behaviorTimer += dt;\n\n                // Sleeping NPCs stay still, slight breathing\n                if (npc.behavior === 'sleeping') {\n                    npc.sprite.position.y = currentZone.groundY + 0.3 + Math.sin(npc.behaviorTimer * 0.5) * 0.02;\n                    return;\n                }\n\n                // Talking NPCs face each other and stay in place\n                if (npc.behavior === 'talking') {\n                    if (npc.talkingPartner) {\n                        const targetAngle = Math.atan2(npc.talkingPartner.x - npc.x, npc.talkingPartner.z - npc.z);\n                        const currentAngle = npc.sprite.rotation.y;\n                        const diff = Math.atan2(Math.sin(targetAngle - currentAngle), Math.cos(targetAngle - currentAngle));\n                        npc.sprite.rotation.y += diff * 0.1;\n                        \n                        // Push them closer together if too far\n                        const dx = npc.talkingPartner.x - npc.x;\n                        const dz = npc.talkingPartner.z - npc.z;\n                        const dist = Math.sqrt(dx*dx + dz*dz);\n                        if (dist > 1.5) {\n                            npc.x += (dx/dist) * 0.5 * dt;\n                            npc.z += (dz/dist) * 0.5 * dt;\n                        } else if (dist < 1.0) {\n                            npc.x -= (dx/dist) * 0.5 * dt;\n                            npc.z -= (dz/dist) * 0.5 * dt;\n                        }\n                    }\n                    npc.sprite.position.x = npc.x;\n                    npc.sprite.position.z = npc.z;\n                    return;\n                }\n\n                if (npc.isStopped) {\n                    if (npc.stopTimer > 1 + Math.random() * 2) {\n                        npc.isStopped = false;\n                        npc.stopTimer = 0;\n                        const angle = Math.random() * Math.PI * 2;\n                        npc.dirX = Math.cos(angle);\n                        npc.dirZ = Math.sin(angle);\n                        npc.sprite.rotation.y = Math.atan2(npc.dirX, npc.dirZ);\n                    }\n                    return;\n                }\n\n                // Joggers rarely stop, walkers stop more often\n                const stopChance = npc.behavior === 'jogging' ? 0.003 : 0.015;\n                if (npc.stopTimer > 2 && Math.random() < stopChance) {\n                    npc.isStopped = true;\n                    npc.stopTimer = 0;\n                    return;\n                }\n\n                // Random direction change (joggers change less frequently)\n                const dirChangeChance = npc.behavior === 'jogging' ? 0.004 : 0.008;\n                if (Math.random() < dirChangeChance) {\n                    const angle = (Math.random() - 0.5) * Math.PI * 0.7;\n                    const cos = Math.cos(angle), sin = Math.sin(angle);\n                    const newDirX = npc.dirX * cos - npc.dirZ * sin;\n                    const newDirZ = npc.dirX * sin + npc.dirZ * cos;\n                    npc.dirX = newDirX;\n                    npc.dirZ = newDirZ;\n                }\n\n                // Smoothly rotate towards movement direction\n                const targetRotation = Math.atan2(npc.dirX, npc.dirZ);\n                const currentRotation = npc.sprite.rotation.y;\n                const rotDiff = Math.atan2(Math.sin(targetRotation - currentRotation), Math.cos(targetRotation - currentRotation));\n                npc.sprite.rotation.y += rotDiff * 5 * dt;\n\n                // Calculate next position\n                const newX = npc.x + npc.dirX * npc.speed * dt;\n                const newZ = npc.z + npc.dirZ * npc.speed * dt;\n\n                // Check collision with obstacles\n                let collision = false;\n                const radius = 0.4;\n                for (let i = 0; i < obstacleBoxes.length; i++) {\n                    const box = obstacleBoxes[i];\n                    if (newX > box.min.x - radius && newX < box.max.x + radius &&\n                        newZ > box.min.z - radius && newZ < box.max.z + radius &&\n                        currentZone.groundY + npc.height > box.min.y &&\n                        currentZone.groundY < box.max.y) {\n                        collision = true;\n                        break;\n                    }\n                }\n\n                if (collision) {\n                    // Turn around if hit an obstacle\n                    const angle = Math.PI + (Math.random() - 0.5) * Math.PI * 0.5;\n                    const cos = Math.cos(angle), sin = Math.sin(angle);\n                    const ndx = npc.dirX * cos - npc.dirZ * sin;\n                    const ndz = npc.dirX * sin + npc.dirZ * cos;\n                    npc.dirX = ndx;\n                    npc.dirZ = ndz;\n                } else {\n                    // Apply movement\n                    npc.x = newX;\n                    npc.z = newZ;\n                }\n\n                // Bounce off bounds\n                if (npc.x < bounds.minX) { npc.x = bounds.minX; npc.dirX *= -1; }\n                if (npc.x > bounds.maxX) { npc.x = bounds.maxX; npc.dirX *= -1; }\n                if (npc.z < bounds.minZ) { npc.z = bounds.minZ; npc.dirZ *= -1; }\n                if (npc.z > bounds.maxZ) { npc.z = bounds.maxZ; npc.dirZ *= -1; }\n\n                npc.sprite.position.x = npc.x;\n                npc.sprite.position.z = npc.z;\n                if (npc.shadow) {\n                    npc.shadow.position.x = npc.x;\n                    npc.shadow.position.z = npc.z;\n                }\n                if (npc.marker) {\n                    npc.marker.position.x = npc.x;\n                    npc.marker.position.z = npc.z;\n                }\n            });\n        }\n\n        // Game state\n        let gameState = 'menu'; // menu, countdown, playing, result\n        let missesLeft = config.settings.missLimit ?? 3;\n        let currentSwayX = 0;\n        let currentSwayY = 0;\n        let timeLeft = 30;\n        let coins = 0;\n        let isReloading = false;\n        let canShoot = true;\n        let shotsFired = 0;\n        let shotsHit = 0;\n        let gotHeadshot = false;\n        let missionIndex = 0;\n        let slowMoTimer = 0;\n        let slowMoActive = false;\n        let shakeTimer = 0;\n        let isScoped = false;\n        let stabilityValue = 100;\n        \n        let comboCount = 0;\n        let comboTimer = 0;\n        let currentModifier = null;\n        let windStrength = 0;\n        let windDirection = 0;\n        let targetEscapeTimer = 0;\n        let targetEscaping = false;\n        let aimingTime = 0;\n        let cameraZoomTarget = 60;\n\n        // Kill-cam state\n        let killCamActive = false;\n        let killCamState = 'inactive';\n        let killCamTimer = 0;\n        let killCamBullet = null;\n        let killCamTrailParticles = [];\n        let killCamTarget = null;\n        let killCamStartPos = new THREE.Vector3();\n        let killCamEndPos = new THREE.Vector3();\n        let killCamSavedCameraPos = new THREE.Vector3();\n        let killCamSavedYaw = 0;\n        let killCamSavedPitch = 0;\n        let killCamSavedFOV = 60;\n        let killCamProgress = 0;\n        let killCamDuration = 1.0;\n        let killCamReturnStartPos = new THREE.Vector3();\n        let killCamReturnStartQuat = new THREE.Quaternion();\n        let killCamReturnTimer = 0;\n        let killCamReturnDuration = 0.5;\n\n        // Location zones for different missions\n        const locationZones = [\n            { // Zone 0: Center Rooftop (original)\n                name: 'Center Rooftop',\n                groundY: 10.15,\n                bounds: { minX: -10, maxX: 10, minZ: -43, maxZ: -27 },\n                targetSpawn: { minX: -3, maxX: 3, minZ: -38, maxZ: -30 },\n                civSpawnCenter: { x: 0, z: -35 },\n                initialYaw: 0,\n                initialPitch: -0.1\n            },\n            { // Zone 1: Left Rooftop\n                name: 'Left Rooftop',\n                groundY: 8.15,\n                bounds: { minX: -30, maxX: -16, minZ: -42, maxZ: -30 },\n                targetSpawn: { minX: -27, maxX: -19, minZ: -39, maxZ: -33 },\n                civSpawnCenter: { x: -23, z: -36 },\n                initialYaw: 0.55,\n                initialPitch: -0.05\n            },\n            { // Zone 2: Right Rooftop\n                name: 'Right Rooftop',\n                groundY: 9.15,\n                bounds: { minX: 14, maxX: 28, minZ: -40, maxZ: -28 },\n                targetSpawn: { minX: 16, maxX: 26, minZ: -37, maxZ: -31 },\n                civSpawnCenter: { x: 21, z: -34 },\n                initialYaw: -0.52,\n                initialPitch: -0.06\n            },\n            { // Zone 3: City Park (street level)\n                name: 'City Park',\n                groundY: -1.8,\n                bounds: { minX: -30, maxX: -10, minZ: -26, maxZ: -10 },\n                targetSpawn: { minX: -26, maxX: -14, minZ: -23, maxZ: -13 },\n                civSpawnCenter: { x: -20, z: -18 },\n                initialYaw: 0.85,\n                initialPitch: -0.55\n            },\n            { // Zone 4: Lower Terrace\n                name: 'Lower Terrace',\n                groundY: 6.15,\n                bounds: { minX: -7, maxX: 7, minZ: -24, maxZ: -16 },\n                targetSpawn: { minX: -4, maxX: 4, minZ: -22, maxZ: -18 },\n                civSpawnCenter: { x: 0, z: -20 },\n                initialYaw: 0.05,\n                initialPitch: -0.2\n            }\n        ];\n        // Mission-to-zone mapping (each mission uses a different location)\n        const missionZoneMap = [0, 1, 3, 2, 4, 0, 3, 1, 2, 4];\n        let currentZone = locationZones[0];\n\n        // All initialization complete - hide loading screen\n        clearInterval(loadingTextInterval);\n        loadingText.textContent = 'READY';\n        loadingBar.style.width = '100%';\n        await new Promise(resolve => setTimeout(resolve, 400));\n        loadingScreen.classList.add('hidden');\n        await new Promise(resolve => setTimeout(resolve, 600));\n\n        // Load saved progress\n        let savedState = null;\n        try {\n            savedState = await lib.getUserGameState();\n            if (savedState && savedState.state) {\n                coins = savedState.state.coins || 0;\n                if (savedState.state.missionIndex !== undefined) {\n                    missionIndex = savedState.state.missionIndex;\n                }\n                if (savedState.state.settings) {\n                    config.settings.musicVolume = savedState.state.settings.musicVolume ?? config.settings.musicVolume;\n                    config.settings.sfxVolume = savedState.state.settings.sfxVolume ?? config.settings.sfxVolume;\n                    config.settings.shakeIntensity = savedState.state.settings.shakeIntensity ?? config.settings.shakeIntensity;\n                    config.settings.slowMoDuration = savedState.state.settings.slowMoDuration ?? config.settings.slowMoDuration;\n                }\n                if (savedState.state.shop) {\n                    if (savedState.state.shop.unlockedGuns) {\n                        config.shop.unlockedGuns = savedState.state.shop.unlockedGuns;\n                    }\n                    if (savedState.state.shop.unlockedBullets) {\n                        config.shop.unlockedBullets = savedState.state.shop.unlockedBullets;\n                    }\n                    if (savedState.state.shop.equippedGun) {\n                        config.shop.equippedGun = savedState.state.shop.equippedGun;\n                    }\n                    if (savedState.state.shop.equippedBullet) {\n                        config.shop.equippedBullet = savedState.state.shop.equippedBullet;\n                    }\n                }\n                lib.log('Progress loaded: ' + coins + ' coins, Mission ' + (missionIndex + 1));\n            } else {\n                lib.log('No saved progress found, starting fresh');\n            }\n        } catch(e) {\n            lib.log('Failed to load progress: ' + e.message);\n        }\n        \n        // Apply loaded gun skin immediately\n        if (config.shop.equippedGun && config.shop.equippedGun !== 'default') {\n            applyGunSkin(config.shop.equippedGun);\n        }\n\n        function saveGameState() {\n            try {\n                const stateToSave = {\n                    coins,\n                    missionIndex,\n                    shop: {\n                        unlockedGuns: [...config.shop.unlockedGuns],\n                        unlockedBullets: [...config.shop.unlockedBullets],\n                        equippedGun: config.shop.equippedGun,\n                        equippedBullet: config.shop.equippedBullet\n                    },\n                    settings: {\n                        musicVolume: config.settings.musicVolume,\n                        sfxVolume: config.settings.sfxVolume,\n                        shakeIntensity: config.settings.shakeIntensity,\n                        slowMoDuration: config.settings.slowMoDuration\n                    }\n                };\n                lib.saveUserGameState(stateToSave);\n                lib.log('Progress saved: ' + coins + ' coins, Mission ' + (missionIndex + 1));\n            } catch(e) {\n                lib.log('Failed to save progress: ' + e.message);\n            }\n        }\n\n        function updateCoinsDisplay() {\n            coinsEl.textContent = coins;\n        }\n        \n        updateCoinsDisplay();\n\n        // Touch input\n        const input = {\n            isTouching: false,\n            touchStartX: 0,\n            touchStartY: 0,\n            lastTouchX: 0,\n            lastTouchY: 0,\n            hasMoved: false\n        };\n\n        const sensitivity = 0.0025;\n        const scopedSensitivity = 0.001;\n        const aimAssistStrength = 0.15;\n        const aimAssistRange = 0.08;\n\n        // Scope toggle\n        function toggleScope() {\n            if (killCamActive) return;\n            isScoped = !isScoped;\n            if (isScoped) {\n                // Sync scope targets to current camera before entering scope\n                scopeTargetYaw = cameraYaw;\n                scopeTargetPitch = cameraPitch;\n                scopeOverlay.classList.add('active');\n                scopeBtn.classList.add('scope-active');\n                crosshairEl.style.display = 'none';\n                cameraZoomTarget = 30; // 2x zoom (60 / 30 = 2x magnification)\n                rifleScopeHidden = true;\n                rifleTargetX = 0.8; // Move rifle off screen\n                rifleTargetY = -0.4;\n            } else {\n                // Sync camera back to scope target on exit\n                cameraYaw = scopeTargetYaw;\n                cameraPitch = scopeTargetPitch;\n                scopeOverlay.classList.remove('active');\n                scopeBtn.classList.remove('scope-active');\n                crosshairEl.style.display = 'block';\n                cameraZoomTarget = 60; // Normal FOV\n                rifleScopeHidden = false;\n                rifleTargetX = 0.12;\n                rifleTargetY = -0.18;\n            }\n            camera.updateProjectionMatrix();\n        }\n\n        scopeBtn.addEventListener('click', () => {\n            if (gameState === 'playing') toggleScope();\n        });\n        scopeBtn.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            e.stopPropagation();\n            if (gameState === 'playing') toggleScope();\n        });\n\n        \n\n        // Blood effect system\n        function createBloodEffect(screenX, screenY, isHeadshot = false) {\n            // Clear any existing blood particles\n            bloodContainer.innerHTML = '';\n            \n            // Central splatter\n            const splatter = document.createElement('div');\n            splatter.className = 'blood-particle blood-splatter';\n            splatter.style.left = screenX + 'px';\n            splatter.style.top = screenY + 'px';\n            splatter.style.width = isHeadshot ? '120px' : '80px';\n            splatter.style.height = isHeadshot ? '120px' : '80px';\n            bloodContainer.appendChild(splatter);\n            \n            // Blood mist\n            const mist = document.createElement('div');\n            mist.className = 'blood-particle blood-mist';\n            mist.style.left = screenX + 'px';\n            mist.style.top = screenY + 'px';\n            mist.style.width = isHeadshot ? '200px' : '120px';\n            mist.style.height = isHeadshot ? '200px' : '120px';\n            bloodContainer.appendChild(mist);\n            \n            // Blood droplets spraying outward\n            const dropCount = isHeadshot ? 18 : 10;\n            for (let i = 0; i < dropCount; i++) {\n                const drop = document.createElement('div');\n                drop.className = 'blood-particle blood-drop';\n                \n                // Random size\n                const size = 4 + Math.random() * (isHeadshot ? 12 : 8);\n                drop.style.width = size + 'px';\n                drop.style.height = size + 'px';\n                \n                // Start position\n                drop.style.left = screenX + 'px';\n                drop.style.top = screenY + 'px';\n                \n                // Random direction and distance\n                const angle = Math.random() * Math.PI * 2;\n                const distance = 40 + Math.random() * (isHeadshot ? 120 : 80);\n                const dx = Math.cos(angle) * distance;\n                const dy = Math.sin(angle) * distance + (Math.random() * 30); // Slight downward bias\n                \n                drop.style.setProperty('--dx', dx + 'px');\n                drop.style.setProperty('--dy', dy + 'px');\n                \n                // Stagger animation\n                drop.style.animationDelay = (Math.random() * 0.1) + 's';\n                \n                bloodContainer.appendChild(drop);\n            }\n            \n            // Screen flash for headshots\n            if (isHeadshot) {\n                bloodScreenFlash.classList.remove('active');\n                void bloodScreenFlash.offsetWidth;\n                bloodScreenFlash.classList.add('active');\n            }\n            \n            // Clean up after animations complete\n            setTimeout(() => {\n                bloodContainer.innerHTML = '';\n                bloodScreenFlash.classList.remove('active');\n            }, 1000);\n        }\n\n        // Bullet hit effect based on equipped bullet type\n        function createBulletHitEffect(screenX, screenY, isHeadshot = false) {\n            const bulletId = config.shop.equippedBullet || 'default';\n            const bullet = config.shop.bulletEffects.find(b => b.id === bulletId);\n            \n            // Default hit effect for all bullets (including default)\n            const hitColor = bullet && bullet.hitColor ? bullet.hitColor : '#ffffff';\n            const trailColor = bullet && bullet.trailColor ? bullet.trailColor : '#ffdd88';\n            \n            // Impact flash at center\n            const impactFlash = document.createElement('div');\n            impactFlash.style.position = 'absolute';\n            impactFlash.style.left = screenX + 'px';\n            impactFlash.style.top = screenY + 'px';\n            impactFlash.style.width = isHeadshot ? '60px' : '40px';\n            impactFlash.style.height = isHeadshot ? '60px' : '40px';\n            impactFlash.style.borderRadius = '50%';\n            impactFlash.style.background = `radial-gradient(circle, ${hitColor} 0%, transparent 70%)`;\n            impactFlash.style.boxShadow = `0 0 40px ${hitColor}`;\n            impactFlash.style.pointerEvents = 'none';\n            impactFlash.style.zIndex = '52';\n            impactFlash.style.transform = 'translate(-50%, -50%)';\n            impactFlash.style.animation = 'bulletHitExpand 0.3s ease-out forwards';\n            overlay.appendChild(impactFlash);\n            \n            // Impact rings\n            for (let i = 0; i < (isHeadshot ? 3 : 2); i++) {\n                setTimeout(() => {\n                    const ring = document.createElement('div');\n                    ring.className = 'bullet-impact-ring';\n                    ring.style.left = screenX + 'px';\n                    ring.style.top = screenY + 'px';\n                    ring.style.width = (30 + i * 20) + 'px';\n                    ring.style.height = (30 + i * 20) + 'px';\n                    ring.style.borderColor = hitColor;\n                    ring.style.boxShadow = `0 0 10px ${hitColor}`;\n                    overlay.appendChild(ring);\n                    setTimeout(() => ring.remove(), 400);\n                }, i * 80);\n            }\n            \n            // Sparks flying outward\n            const sparkCount = isHeadshot ? 16 : 10;\n            for (let i = 0; i < sparkCount; i++) {\n                const spark = document.createElement('div');\n                spark.className = 'bullet-hit-spark';\n                spark.style.left = screenX + 'px';\n                spark.style.top = screenY + 'px';\n                spark.style.background = hitColor;\n                spark.style.boxShadow = `0 0 6px ${hitColor}`;\n                \n                const angle = (i / sparkCount) * Math.PI * 2 + Math.random() * 0.3;\n                const distance = 50 + Math.random() * (isHeadshot ? 80 : 50);\n                spark.style.setProperty('--sx', Math.cos(angle) * distance + 'px');\n                spark.style.setProperty('--sy', Math.sin(angle) * distance + 'px');\n                spark.style.animationDelay = (Math.random() * 0.05) + 's';\n                \n                overlay.appendChild(spark);\n            }\n            \n            // Special effects per bullet type\n            if (bulletId === 'fire') {\n                // Fire explosion\n                const fireExplosion = document.createElement('div');\n                fireExplosion.style.position = 'absolute';\n                fireExplosion.style.left = screenX + 'px';\n                fireExplosion.style.top = screenY + 'px';\n                fireExplosion.style.width = isHeadshot ? '100px' : '70px';\n                fireExplosion.style.height = isHeadshot ? '100px' : '70px';\n                fireExplosion.style.borderRadius = '50%';\n                fireExplosion.style.background = 'radial-gradient(circle, #ffaa00 0%, #ff6600 40%, transparent 70%)';\n                fireExplosion.style.boxShadow = '0 0 40px #ff4400';\n                fireExplosion.style.pointerEvents = 'none';\n                fireExplosion.style.zIndex = '51';\n                fireExplosion.style.transform = 'translate(-50%, -50%)';\n                fireExplosion.style.animation = 'bulletHitExpand 0.4s ease-out forwards';\n                overlay.appendChild(fireExplosion);\n                \n                // Fire embers rising\n                for (let i = 0; i < 12; i++) {\n                    const ember = document.createElement('div');\n                    ember.className = 'bullet-hit-spark';\n                    ember.style.left = screenX + (Math.random() - 0.5) * 30 + 'px';\n                    ember.style.top = screenY + 'px';\n                    ember.style.width = (4 + Math.random() * 6) + 'px';\n                    ember.style.height = (4 + Math.random() * 6) + 'px';\n                    ember.style.background = Math.random() > 0.5 ? '#ffaa00' : '#ff6600';\n                    ember.style.boxShadow = '0 0 8px #ff4400';\n                    ember.style.setProperty('--sx', (Math.random() - 0.5) * 100 + 'px');\n                    ember.style.setProperty('--sy', -Math.random() * 100 - 30 + 'px');\n                    ember.style.animationDuration = '0.7s';\n                    overlay.appendChild(ember);\n                }\n                setTimeout(() => fireExplosion.remove(), 400);\n            } else if (bulletId === 'electric') {\n                // Electric discharge\n                for (let i = 0; i < 5; i++) {\n                    const arcX = screenX + (Math.random() - 0.5) * 60;\n                    const arcY = screenY + (Math.random() - 0.5) * 60;\n                    createElectricArc(screenX, screenY, arcX, arcY);\n                }\n                \n                // Electric pulse\n                const pulse = document.createElement('div');\n                pulse.style.position = 'absolute';\n                pulse.style.left = screenX + 'px';\n                pulse.style.top = screenY + 'px';\n                pulse.style.width = '80px';\n                pulse.style.height = '80px';\n                pulse.style.borderRadius = '50%';\n                pulse.style.border = '3px solid #00ffff';\n                pulse.style.boxShadow = '0 0 30px #00ccff, inset 0 0 20px rgba(0,255,255,0.3)';\n                pulse.style.pointerEvents = 'none';\n                pulse.style.zIndex = '51';\n                pulse.style.transform = 'translate(-50%, -50%)';\n                pulse.style.animation = 'impactRing 0.4s ease-out forwards';\n                overlay.appendChild(pulse);\n                setTimeout(() => pulse.remove(), 400);\n            } else if (bulletId === 'plasma') {\n                // Plasma explosion with multiple waves\n                for (let i = 0; i < 4; i++) {\n                    setTimeout(() => {\n                        const wave = document.createElement('div');\n                        wave.className = 'bullet-impact-ring';\n                        wave.style.left = screenX + 'px';\n                        wave.style.top = screenY + 'px';\n                        wave.style.width = (40 + i * 15) + 'px';\n                        wave.style.height = (40 + i * 15) + 'px';\n                        wave.style.borderColor = '#ff00ff';\n                        wave.style.boxShadow = '0 0 20px #ff00ff, inset 0 0 15px rgba(255,0,255,0.4)';\n                        wave.style.borderWidth = '3px';\n                        overlay.appendChild(wave);\n                        setTimeout(() => wave.remove(), 400);\n                    }, i * 60);\n                }\n                \n                // Plasma core\n                const core = document.createElement('div');\n                core.style.position = 'absolute';\n                core.style.left = screenX + 'px';\n                core.style.top = screenY + 'px';\n                core.style.width = '50px';\n                core.style.height = '50px';\n                core.style.borderRadius = '50%';\n                core.style.background = 'radial-gradient(circle, #ff88ff 0%, #ff00ff 50%, transparent 70%)';\n                core.style.boxShadow = '0 0 40px #ff00ff';\n                core.style.pointerEvents = 'none';\n                core.style.zIndex = '51';\n                core.style.transform = 'translate(-50%, -50%)';\n                core.style.animation = 'bulletHitExpand 0.5s ease-out forwards';\n                overlay.appendChild(core);\n                setTimeout(() => core.remove(), 500);\n            } else if (bulletId === 'toxic') {\n                // Toxic splash\n                const splash = document.createElement('div');\n                splash.style.position = 'absolute';\n                splash.style.left = screenX + 'px';\n                splash.style.top = screenY + 'px';\n                splash.style.width = isHeadshot ? '90px' : '60px';\n                splash.style.height = isHeadshot ? '90px' : '60px';\n                splash.style.borderRadius = '50%';\n                splash.style.background = 'radial-gradient(circle, #88ff44 0%, #44ff00 40%, transparent 70%)';\n                splash.style.boxShadow = '0 0 30px #00ff44';\n                splash.style.pointerEvents = 'none';\n                splash.style.zIndex = '51';\n                splash.style.transform = 'translate(-50%, -50%)';\n                splash.style.animation = 'bulletHitExpand 0.4s ease-out forwards';\n                overlay.appendChild(splash);\n                \n                // Toxic droplets\n                for (let i = 0; i < 8; i++) {\n                    setTimeout(() => {\n                        const droplet = document.createElement('div');\n                        droplet.style.position = 'absolute';\n                        droplet.style.left = screenX + (Math.random() - 0.5) * 40 + 'px';\n                        droplet.style.top = screenY + 'px';\n                        droplet.style.width = (6 + Math.random() * 8) + 'px';\n                        droplet.style.height = (6 + Math.random() * 8) + 'px';\n                        droplet.style.borderRadius = '50%';\n                        droplet.style.background = '#44ff00';\n                        droplet.style.boxShadow = '0 0 8px #00ff44';\n                        droplet.style.pointerEvents = 'none';\n                        droplet.style.zIndex = '51';\n                        droplet.style.animation = 'toxicDrip 0.6s ease-in forwards';\n                        overlay.appendChild(droplet);\n                        setTimeout(() => droplet.remove(), 600);\n                    }, i * 40);\n                }\n                setTimeout(() => splash.remove(), 400);\n            }\n            \n            // Cleanup\n            setTimeout(() => {\n                impactFlash.remove();\n                document.querySelectorAll('.bullet-hit-spark').forEach(el => el.remove());\n            }, 700);\n        }\n\n        // Floating text system\n        function showFloatingText(text, isHeadshot = false) {\n            const floatingText = document.createElement('div');\n            floatingText.className = 'floating-text' + (isHeadshot ? ' headshot' : '');\n            floatingText.textContent = text;\n            floatingText.style.top = '45%';\n            floatingText.style.left = '50%';\n            floatingText.style.transform = 'translateX(-50%)';\n            overlay.appendChild(floatingText);\n            setTimeout(() => floatingText.remove(), 1200);\n        }\n\n        // Bullet trail effect from muzzle to crosshair\n        function createBulletTrail() {\n            const bulletId = config.shop.equippedBullet || 'default';\n            const bullet = config.shop.bulletEffects.find(b => b.id === bulletId);\n            \n            // Get colors based on bullet type\n            const trailColor = bullet && bullet.trailColor ? bullet.trailColor : '#ffdd88';\n            const hitColor = bullet && bullet.hitColor ? bullet.hitColor : '#ffffff';\n            \n            // Start position (near muzzle)\n            const startX = gameWorld.clientWidth * 0.78;\n            const startY = gameWorld.clientHeight * 0.72;\n            \n            // End position (crosshair center)\n            const endX = gameWorld.clientWidth / 2;\n            const endY = gameWorld.clientHeight / 2;\n            \n            // Calculate trajectory\n            const dx = endX - startX;\n            const dy = endY - startY;\n            const angle = Math.atan2(dy, dx) * (180 / Math.PI);\n            const distance = Math.sqrt(dx * dx + dy * dy);\n            \n            // Create flying bullet projectile\n            const projectile = document.createElement('div');\n            projectile.className = 'bullet-projectile';\n            projectile.style.left = startX + 'px';\n            projectile.style.top = startY + 'px';\n            projectile.style.setProperty('--tx', dx + 'px');\n            projectile.style.setProperty('--ty', dy + 'px');\n            projectile.style.background = `radial-gradient(circle, ${hitColor} 30%, ${trailColor} 70%)`;\n            projectile.style.boxShadow = `0 0 15px ${trailColor}, 0 0 30px ${trailColor}`;\n            overlay.appendChild(projectile);\n            \n            // Create bullet streak/tracer\n            const streak = document.createElement('div');\n            streak.className = 'bullet-streak';\n            streak.style.left = startX + 'px';\n            streak.style.top = startY + 'px';\n            streak.style.transform = `rotate(${angle}deg)`;\n            streak.style.background = `linear-gradient(90deg, transparent, ${trailColor})`;\n            streak.style.boxShadow = `0 0 8px ${trailColor}`;\n            overlay.appendChild(streak);\n            \n            // Create vapor trail for all bullet types\n            for (let i = 0; i < 5; i++) {\n                setTimeout(() => {\n                    const progress = (i + 1) / 5;\n                    const vaporX = startX + dx * progress * 0.8;\n                    const vaporY = startY + dy * progress * 0.8;\n                    \n                    const vapor = document.createElement('div');\n                    vapor.className = 'bullet-vapor';\n                    vapor.style.left = vaporX + 'px';\n                    vapor.style.top = vaporY + 'px';\n                    vapor.style.width = '30px';\n                    vapor.style.height = '30px';\n                    \n                    if (bullet && bullet.trailColor) {\n                        vapor.style.background = `radial-gradient(circle, ${trailColor}40 0%, transparent 70%)`;\n                    }\n                    \n                    overlay.appendChild(vapor);\n                    setTimeout(() => vapor.remove(), 600);\n                }, i * 20);\n            }\n            \n            // Special effects for different bullet types\n            if (bulletId === 'fire') {\n                // Fire sparks along the trail\n                for (let i = 0; i < 8; i++) {\n                    setTimeout(() => {\n                        const progress = Math.random() * 0.8;\n                        const sparkX = startX + dx * progress;\n                        const sparkY = startY + dy * progress;\n                        createFireSpark(sparkX, sparkY);\n                    }, i * 15);\n                }\n            } else if (bulletId === 'electric') {\n                // Electric arcs\n                createElectricArc(startX, startY, endX, endY);\n            } else if (bulletId === 'plasma') {\n                // Plasma wave effect\n                createPlasmaWave(startX, startY, angle);\n            } else if (bulletId === 'toxic') {\n                // Toxic drips\n                for (let i = 0; i < 4; i++) {\n                    setTimeout(() => {\n                        const progress = 0.2 + Math.random() * 0.5;\n                        const dripX = startX + dx * progress;\n                        const dripY = startY + dy * progress;\n                        createToxicDrip(dripX, dripY);\n                    }, i * 30);\n                }\n            }\n            \n            // Cleanup\n            setTimeout(() => {\n                projectile.remove();\n                streak.remove();\n            }, 200);\n        }\n        \n        // Fire spark helper\n        function createFireSpark(x, y) {\n            const spark = document.createElement('div');\n            spark.className = 'bullet-hit-spark';\n            spark.style.left = x + 'px';\n            spark.style.top = y + 'px';\n            spark.style.width = '6px';\n            spark.style.height = '6px';\n            spark.style.background = Math.random() > 0.5 ? '#ff6600' : '#ffaa00';\n            spark.style.boxShadow = '0 0 8px #ff4400';\n            spark.style.setProperty('--sx', (Math.random() - 0.5) * 40 + 'px');\n            spark.style.setProperty('--sy', (Math.random() - 0.5) * 40 + 'px');\n            overlay.appendChild(spark);\n            setTimeout(() => spark.remove(), 400);\n        }\n        \n        // Electric arc helper\n        function createElectricArc(x1, y1, x2, y2) {\n            const canvas = document.createElement('canvas');\n            canvas.style.position = 'absolute';\n            canvas.style.left = '0';\n            canvas.style.top = '0';\n            canvas.style.width = '100%';\n            canvas.style.height = '100%';\n            canvas.style.pointerEvents = 'none';\n            canvas.style.zIndex = '11';\n            canvas.width = gameWorld.clientWidth;\n            canvas.height = gameWorld.clientHeight;\n            \n            const ctx = canvas.getContext('2d');\n            ctx.strokeStyle = '#00ffff';\n            ctx.lineWidth = 2;\n            ctx.shadowColor = '#00ccff';\n            ctx.shadowBlur = 10;\n            \n            // Draw jagged lightning\n            ctx.beginPath();\n            ctx.moveTo(x1, y1);\n            \n            const segments = 8;\n            for (let i = 1; i < segments; i++) {\n                const t = i / segments;\n                const midX = x1 + (x2 - x1) * t + (Math.random() - 0.5) * 30;\n                const midY = y1 + (y2 - y1) * t + (Math.random() - 0.5) * 30;\n                ctx.lineTo(midX, midY);\n            }\n            ctx.lineTo(x2, y2);\n            ctx.stroke();\n            \n            overlay.appendChild(canvas);\n            \n            // Animate fade out\n            let opacity = 1;\n            const fadeOut = setInterval(() => {\n                opacity -= 0.15;\n                canvas.style.opacity = opacity;\n                if (opacity <= 0) {\n                    clearInterval(fadeOut);\n                    canvas.remove();\n                }\n            }, 30);\n        }\n        \n        // Plasma wave helper\n        function createPlasmaWave(x, y, angle) {\n            for (let i = 0; i < 3; i++) {\n                setTimeout(() => {\n                    const wave = document.createElement('div');\n                    wave.style.position = 'absolute';\n                    wave.style.left = x + 'px';\n                    wave.style.top = y + 'px';\n                    wave.style.width = '40px';\n                    wave.style.height = '40px';\n                    wave.style.borderRadius = '50%';\n                    wave.style.border = '2px solid #ff00ff';\n                    wave.style.boxShadow = '0 0 15px #ff00ff, inset 0 0 10px rgba(255,0,255,0.3)';\n                    wave.style.pointerEvents = 'none';\n                    wave.style.zIndex = '11';\n                    wave.style.animation = 'impactRing 0.3s ease-out forwards';\n                    overlay.appendChild(wave);\n                    setTimeout(() => wave.remove(), 300);\n                }, i * 50);\n            }\n        }\n        \n        // Toxic drip helper\n        function createToxicDrip(x, y) {\n            const drip = document.createElement('div');\n            drip.style.position = 'absolute';\n            drip.style.left = x + 'px';\n            drip.style.top = y + 'px';\n            drip.style.width = '8px';\n            drip.style.height = '8px';\n            drip.style.borderRadius = '50% 50% 50% 50%';\n            drip.style.background = '#44ff00';\n            drip.style.boxShadow = '0 0 10px #00ff44';\n            drip.style.pointerEvents = 'none';\n            drip.style.zIndex = '11';\n            drip.style.animation = 'toxicDrip 0.5s ease-in forwards';\n            overlay.appendChild(drip);\n            setTimeout(() => drip.remove(), 500);\n        }\n\n        // Kill-cam system\n        function createKillCamBullet() {\n            const bulletGroup = new THREE.Group();\n            const bodyGeo = new THREE.CylinderGeometry(0.04, 0.025, 0.2, 8);\n            const bodyMat = new THREE.MeshStandardMaterial({ color: 0xd4a030, metalness: 0.9, roughness: 0.2 });\n            const body = new THREE.Mesh(bodyGeo, bodyMat);\n            body.rotation.x = Math.PI / 2;\n            bulletGroup.add(body);\n            const tipGeo = new THREE.ConeGeometry(0.04, 0.08, 8);\n            const tipMat = new THREE.MeshStandardMaterial({ color: 0xcc8800, metalness: 0.95, roughness: 0.1 });\n            const tip = new THREE.Mesh(tipGeo, tipMat);\n            tip.rotation.x = -Math.PI / 2;\n            tip.position.z = -0.14;\n            bulletGroup.add(tip);\n            const glowGeo = new THREE.SphereGeometry(0.08, 8, 8);\n            const glowMat = new THREE.MeshBasicMaterial({ color: 0xffaa00, transparent: true, opacity: 0.5 });\n            const glow = new THREE.Mesh(glowGeo, glowMat);\n            bulletGroup.add(glow);\n            const coreGeo = new THREE.SphereGeometry(0.03, 6, 6);\n            const coreMat = new THREE.MeshBasicMaterial({ color: 0xffffff });\n            const core = new THREE.Mesh(coreGeo, coreMat);\n            bulletGroup.add(core);\n            return bulletGroup;\n        }\n\n        function startKillCam(targetNPC) {\n            killCamActive = true;\n            killCamState = 'flying';\n            killCamTimer = 0;\n            killCamProgress = 0;\n            killCamTarget = targetNPC;\n            killCamSavedCameraPos.copy(camera.position);\n            killCamSavedYaw = cameraYaw;\n            killCamSavedPitch = cameraPitch;\n            killCamSavedFOV = camera.fov;\n            killCamStartPos.copy(camera.position);\n            const forward = new THREE.Vector3(0, 0, -1);\n            forward.applyQuaternion(camera.quaternion);\n            killCamStartPos.add(forward.clone().multiplyScalar(1.5));\n            killCamStartPos.y -= 0.15;\n            killCamEndPos.set(\n                targetNPC.x,\n                targetNPC.sprite.position.y + targetNPC.height * 0.85,\n                targetNPC.z\n            );\n            killCamBullet = createKillCamBullet();\n            killCamBullet.position.copy(killCamStartPos);\n            killCamBullet.lookAt(killCamEndPos);\n            scene.add(killCamBullet);\n            killCamTrailParticles.forEach(p => scene.remove(p));\n            killCamTrailParticles = [];\n            slowMoActive = true;\n            slowMoTimer = 4;\n            slowMoOverlay.classList.add('active');\n            crosshairEl.style.opacity = '0';\n            rifleOverlay.style.opacity = '0';\n            actionButtons.style.opacity = '0';\n            actionButtons.style.pointerEvents = 'none';\n            camera.fov = 45;\n            camera.updateProjectionMatrix();\n        }\n\n        function updateKillCam(dt) {\n            if (!killCamActive) return;\n            const realDt = dt;\n            if (killCamState === 'flying') {\n                killCamTimer += realDt * 0.5;\n                killCamProgress = Math.min(1, killCamTimer / killCamDuration);\n                const eased = 1 - Math.pow(1 - killCamProgress, 2.5);\n                killCamBullet.position.lerpVectors(killCamStartPos, killCamEndPos, eased);\n                killCamBullet.lookAt(killCamEndPos);\n                const bulletGlow = killCamBullet.children[2];\n                if (bulletGlow) {\n                    bulletGlow.scale.setScalar(1 + Math.sin(killCamTimer * 15) * 0.3);\n                }\n                if (Math.random() < 0.6) {\n                    const trailGeo = new THREE.SphereGeometry(0.03 + Math.random() * 0.03, 4, 4);\n                    const trailMat = new THREE.MeshBasicMaterial({ color: 0xffaa44, transparent: true, opacity: 0.7 });\n                    const trail = new THREE.Mesh(trailGeo, trailMat);\n                    trail.position.copy(killCamBullet.position);\n                    trail.position.x += (Math.random() - 0.5) * 0.12;\n                    trail.position.y += (Math.random() - 0.5) * 0.12;\n                    trail.position.z += (Math.random() - 0.5) * 0.12;\n                    trail.userData.life = 0.6;\n                    trail.userData.startScale = 1;\n                    scene.add(trail);\n                    killCamTrailParticles.push(trail);\n                }\n                if (Math.random() < 0.25) {\n                    const smokeGeo = new THREE.SphereGeometry(0.06, 4, 4);\n                    const smokeMat = new THREE.MeshBasicMaterial({ color: 0x888888, transparent: true, opacity: 0.3 });\n                    const smoke = new THREE.Mesh(smokeGeo, smokeMat);\n                    smoke.position.copy(killCamBullet.position);\n                    smoke.position.x += (Math.random() - 0.5) * 0.08;\n                    smoke.position.y += (Math.random() - 0.5) * 0.08 + 0.05;\n                    smoke.position.z += (Math.random() - 0.5) * 0.08;\n                    smoke.userData.life = 0.8;\n                    smoke.userData.startScale = 0.5;\n                    scene.add(smoke);\n                    killCamTrailParticles.push(smoke);\n                }\n                killCamTrailParticles = killCamTrailParticles.filter(p => {\n                    p.userData.life -= realDt * 0.5;\n                    p.material.opacity = Math.max(0, p.userData.life * 1.2);\n                    p.scale.multiplyScalar(0.96);\n                    if (p.userData.life <= 0) { \n                        scene.remove(p); \n                        disposeObject(p);\n                        return false; \n                    }\n                    return true;\n                });\n                const bulletDir = killCamEndPos.clone().sub(killCamStartPos).normalize();\n                const camBehind = bulletDir.clone().multiplyScalar(-2.5);\n                camBehind.y += 0.6;\n                const sideVec = new THREE.Vector3(-bulletDir.z, 0, bulletDir.x).normalize();\n                camBehind.add(sideVec.multiplyScalar(0.6 * Math.sin(killCamTimer * 0.8)));\n                camera.position.copy(killCamBullet.position).add(camBehind);\n                const lookTarget = killCamEndPos.clone();\n                lookTarget.lerp(killCamBullet.position, 0.2);\n                camera.lookAt(lookTarget);\n                camera.fov = 45 - eased * 10;\n                camera.updateProjectionMatrix();\n                if (killCamProgress >= 1) {\n                    killCamState = 'impact';\n                    killCamTimer = 0;\n                    onKillCamImpact();\n                }\n            } else if (killCamState === 'impact') {\n                killCamTimer += realDt;\n                const shakeIntensity = Math.max(0, 1 - killCamTimer * 3);\n                camera.position.x += (Math.random() - 0.5) * 0.08 * shakeIntensity;\n                camera.position.y += (Math.random() - 0.5) * 0.05 * shakeIntensity;\n                camera.lookAt(killCamEndPos);\n                killCamTrailParticles = killCamTrailParticles.filter(p => {\n                    p.userData.life -= realDt;\n                    p.material.opacity = Math.max(0, p.userData.life);\n                    p.scale.multiplyScalar(0.94);\n                    if (p.userData.life <= 0) { \n                        scene.remove(p); \n                        disposeObject(p);\n                        return false; \n                    }\n                    return true;\n                });\n                if (killCamTimer > 0.6) {\n                    killCamState = 'returning';\n                    killCamTimer = 0;\n                    killCamReturnTimer = 0;\n                    killCamReturnStartPos.copy(camera.position);\n                    killCamReturnStartQuat.copy(camera.quaternion);\n                }\n            } else if (killCamState === 'returning') {\n                killCamReturnTimer += realDt * 1.5;\n                const t = Math.min(1, killCamReturnTimer / killCamReturnDuration);\n                const eased = t * t * (3 - 2 * t);\n                camera.position.lerpVectors(killCamReturnStartPos, killCamSavedCameraPos, eased);\n                const targetQuat = new THREE.Quaternion();\n                const tempCam = new THREE.Object3D();\n                tempCam.rotation.order = 'YXZ';\n                tempCam.rotation.y = killCamSavedYaw;\n                tempCam.rotation.x = killCamSavedPitch;\n                tempCam.updateMatrixWorld();\n                targetQuat.setFromEuler(tempCam.rotation);\n                camera.quaternion.slerpQuaternions(killCamReturnStartQuat, targetQuat, eased);\n                camera.fov = 35 + (killCamSavedFOV - 35) * eased;\n                camera.updateProjectionMatrix();\n                killCamTrailParticles = killCamTrailParticles.filter(p => {\n                    p.userData.life -= realDt;\n                    p.material.opacity = Math.max(0, p.userData.life);\n                    if (p.userData.life <= 0) { \n                        scene.remove(p); \n                        disposeObject(p);\n                        return false; \n                    }\n                    return true;\n                });\n                if (t >= 1) {\n                    endKillCam();\n                }\n            }\n        }\n\n        function onKillCamImpact() {\n            if (killCamTarget) {\n                killCamTarget.alive = false;\n                scene.remove(killCamTarget.sprite);\n                disposeObject(killCamTarget.sprite);\n                if (killCamTarget.shadow) {\n                    scene.remove(killCamTarget.shadow);\n                    disposeObject(killCamTarget.shadow);\n                }\n                if (killCamTarget.marker) {\n                    scene.remove(killCamTarget.marker);\n                    disposeObject(killCamTarget.marker);\n                }\n            }\n            if (killCamBullet) {\n                scene.remove(killCamBullet);\n                disposeObject(killCamBullet);\n                killCamBullet = null;\n            }\n            playSound('headshot_sound', (config.settings.sfxVolume ?? 70) / 100);\n            const centerX = gameWorld.clientWidth / 2;\n            const centerY = gameWorld.clientHeight / 2;\n            createBloodEffect(centerX, centerY, true);\n            createBulletHitEffect(centerX, centerY, true);\n            showFloatingText('HEADSHOT +150', true);\n            targetsHitEl.textContent = '1';\n            shakeTimer = 0.4;\n        }\n\n        function endKillCam() {\n            killCamActive = false;\n            killCamState = 'inactive';\n            if (killCamBullet) { \n                scene.remove(killCamBullet); \n                disposeObject(killCamBullet);\n                killCamBullet = null; \n            }\n            killCamTrailParticles.forEach(p => {\n                scene.remove(p);\n                disposeObject(p);\n            });\n            killCamTrailParticles = [];\n            camera.position.copy(killCamSavedCameraPos);\n            cameraYaw = killCamSavedYaw;\n            cameraPitch = killCamSavedPitch;\n            camera.fov = killCamSavedFOV;\n            camera.updateProjectionMatrix();\n            updateCamera();\n            crosshairEl.style.opacity = '1';\n            rifleOverlay.style.opacity = '1';\n            actionButtons.style.opacity = '1';\n            actionButtons.style.pointerEvents = 'auto';\n            slowMoActive = false;\n            slowMoOverlay.classList.remove('active');\n            killCamTarget = null;\n            setTimeout(() => { endMission(true); }, 200);\n        }\n\n        // Update combo display\n        function updateCombo(hit = true) {\n            if (hit) {\n                comboCount++;\n                comboTimer = 5;\n                comboCountEl.textContent = comboCount;\n                comboCounter.classList.add('show');\n                comboCounter.classList.remove('pulse');\n                void comboCounter.offsetWidth;\n                comboCounter.classList.add('pulse');\n            } else {\n                comboCount = 0;\n                comboCounter.classList.remove('show');\n            }\n        }\n\n        // Mission modifiers\n        const modifiers = ['none', 'wind', 'running', 'night', 'limited'];\n        \n        function applyModifier(mod) {\n            currentModifier = mod;\n            modifierBadge.classList.remove('show');\n            windIndicator.classList.remove('show');\n            nightVisionOverlay.classList.remove('active');\n            \n            switch(mod) {\n                case 'wind':\n                    windStrength = 2 + Math.random() * 3;\n                    windDirection = Math.random() > 0.5 ? 1 : -1;\n                    windIndicator.classList.add('show');\n                    windArrow.style.transform = `rotate(${windDirection > 0 ? 0 : 180}deg)`;\n                    windText.textContent = `WIND: ${windStrength.toFixed(1)}`;\n                    modifierBadge.textContent = '\ud83d\udca8 WINDY CONDITIONS';\n                    modifierBadge.classList.add('show');\n                    break;\n                case 'running':\n                    modifierBadge.textContent = '\ud83c\udfc3 TARGET RUNNING';\n                    modifierBadge.classList.add('show');\n                    break;\n                case 'night':\n                    nightVisionOverlay.classList.add('active');\n                    modifierBadge.textContent = '\ud83c\udf19 NIGHT OPS';\n                    modifierBadge.classList.add('show');\n                    break;\n                case 'limited':\n                    missesLeft = 2;\n                    modifierBadge.textContent = '\u26a0\ufe0f LIMITED AMMO';\n                    modifierBadge.classList.add('show');\n                    break;\n            }\n        }\n\n        // Shoot button\n        shootBtn.addEventListener('click', () => {\n            if (gameState === 'playing' && canShoot && !isReloading) shoot();\n        });\n        shootBtn.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            e.stopPropagation();\n            if (gameState === 'playing' && canShoot && !isReloading) shoot();\n        });\n\n        // Pause button\n        pauseBtn.addEventListener('click', () => {\n            if (gameState === 'playing') {\n                showPauseMenu();\n            }\n        });\n\n        function showPauseMenu() {\n            gameState = 'paused';\n            missionOverlay.style.display = 'flex';\n            missionOverlay.innerHTML = `\n                <h1 style=\"font-family:'Orbitron',monospace;font-size:40px;color:#60A5FA;\">PAUSED</h1>\n                <div style=\"margin-top:32px;\">\n                    <button class=\"btn success\" id=\"btn-resume\">RESUME</button>\n                    <button class=\"btn\" id=\"btn-restart\" style=\"margin-top:8px;\">RESTART</button>\n                    <button class=\"btn danger\" id=\"btn-quit\" style=\"margin-top:8px;\">QUIT</button>\n                </div>\n            `;\n            document.getElementById('btn-resume').addEventListener('click', () => {\n                gameState = 'playing';\n                missionOverlay.style.display = 'none';\n                requestPointerLock();\n            });\n            document.getElementById('btn-restart').addEventListener('click', () => {\n                startMission();\n                requestPointerLock();\n            });\n            document.getElementById('btn-quit').addEventListener('click', () => showMenu());\n        }\n\n        // Joystick state\n        let joystickActive = false;\n        let joystickTouchId = null;\n        const joystickRadius = 45; // max distance knob can move\n        let joystickNormX = 0;\n        let joystickNormY = 0;\n        const playerMoveSpeed = 4; // units per second\n        const playerBounds = { minX: -3.8, maxX: 3.8, minZ: -3.8, maxZ: 3.8 };\n\n        aimJoystick.addEventListener('touchstart', (e) => {\n            e.preventDefault();\n            e.stopPropagation();\n            if (gameState !== 'playing') return;\n            const touch = e.touches[0];\n            joystickActive = true;\n            joystickTouchId = touch.identifier;\n            aimJoystickKnob.classList.add('active');\n        }, { passive: false });\n\n        aimJoystick.addEventListener('touchmove', (e) => {\n            e.preventDefault();\n            e.stopPropagation();\n            if (!joystickActive) return;\n            for (let i = 0; i < e.touches.length; i++) {\n                if (e.touches[i].identifier === joystickTouchId) {\n                    const touch = e.touches[i];\n                    const rect = aimJoystick.getBoundingClientRect();\n                    const centerX = rect.left + rect.width / 2;\n                    const centerY = rect.top + rect.height / 2;\n                    let dx = touch.clientX - centerX;\n                    let dy = touch.clientY - centerY;\n                    const dist = Math.sqrt(dx * dx + dy * dy);\n                    if (dist > joystickRadius) {\n                        dx = (dx / dist) * joystickRadius;\n                        dy = (dy / dist) * joystickRadius;\n                    }\n                    aimJoystickKnob.style.transform = `translate(calc(-50% + ${dx}px), calc(-50% + ${dy}px))`;\n                    // Store normalized joystick values for player movement\n                    joystickNormX = dx / joystickRadius;\n                    joystickNormY = dy / joystickRadius;\n                    break;\n                }\n            }\n        }, { passive: false });\n\n        aimJoystick.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            e.stopPropagation();\n            joystickActive = false;\n            joystickTouchId = null;\n            joystickNormX = 0;\n            joystickNormY = 0;\n            aimJoystickKnob.style.transform = 'translate(-50%, -50%)';\n            aimJoystickKnob.classList.remove('active');\n        }, { passive: false });\n\n        canvas.addEventListener('touchstart', (e) => {\n            e.preventDefault();\n            if (gameState !== 'playing') return;\n            const touch = e.touches[0];\n            input.isTouching = true;\n            input.touchStartX = touch.clientX;\n            input.touchStartY = touch.clientY;\n            input.lastTouchX = touch.clientX;\n            input.lastTouchY = touch.clientY;\n            input.hasMoved = false;\n        }, { passive: false });\n\n        canvas.addEventListener('touchmove', (e) => {\n            e.preventDefault();\n            if (!input.isTouching || gameState !== 'playing') return;\n            const touch = e.touches[0];\n            const dx = touch.clientX - input.lastTouchX;\n            const dy = touch.clientY - input.lastTouchY;\n\n            const totalMove = Math.sqrt(\n                Math.pow(touch.clientX - input.touchStartX, 2) +\n                Math.pow(touch.clientY - input.touchStartY, 2)\n            );\n            if (totalMove > 10) input.hasMoved = true;\n\n            if (isScoped) {\n                scopeTargetYaw -= dx * scopedSensitivity;\n                scopeTargetPitch -= dy * scopedSensitivity;\n                scopeTargetPitch = Math.max(-0.8, Math.min(0.3, scopeTargetPitch));\n                scopeTargetYaw = Math.max(-1.2, Math.min(1.2, scopeTargetYaw));\n            } else {\n                cameraYaw -= dx * sensitivity;\n                cameraPitch -= dy * sensitivity;\n                cameraPitch = Math.max(-0.8, Math.min(0.3, cameraPitch));\n                cameraYaw = Math.max(-1.2, Math.min(1.2, cameraYaw));\n            }\n\n            input.lastTouchX = touch.clientX;\n            input.lastTouchY = touch.clientY;\n            \n            // Apply aim assist\n            applyAimAssist();\n        }, { passive: false });\n\n        canvas.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            if (gameState !== 'playing') return;\n            input.isTouching = false;\n            input.hasMoved = false;\n        }, { passive: false });\n        \n        // Aim assist function - subtly pulls crosshair toward nearby targets\n        function applyAimAssist() {\n            if (gameState !== 'playing') return;\n            \n            const raycaster = new THREE.Raycaster();\n            camera.updateMatrixWorld();\n            raycaster.setFromCamera(new THREE.Vector2(0, 0), camera);\n            const ray = raycaster.ray;\n            \n            let closestTarget = null;\n            let closestAngle = Infinity;\n            \n            for (const npc of npcs) {\n                if (!npc.alive || npc.type !== 'target') continue;\n                \n                // Get direction to target (center of body)\n                const targetPos = new THREE.Vector3(npc.x, npc.sprite.position.y + npc.height * 0.5, npc.z);\n                const toTarget = targetPos.clone().sub(ray.origin).normalize();\n                \n                // Calculate angle between aim and target\n                const angle = ray.direction.angleTo(toTarget);\n                \n                if (angle < aimAssistRange && angle < closestAngle) {\n                    closestAngle = angle;\n                    closestTarget = { pos: targetPos, dir: toTarget };\n                }\n            }\n            \n            // Subtle pull toward target\n            if (closestTarget && closestAngle > 0.01) {\n                const pullStrength = aimAssistStrength * (1 - closestAngle / aimAssistRange);\n                \n                // Calculate adjustment needed\n                const currentDir = ray.direction.clone();\n                const targetDir = closestTarget.dir.clone();\n                \n                // Apply subtle yaw/pitch adjustment\n                const yawDiff = Math.atan2(targetDir.x, -targetDir.z) - Math.atan2(currentDir.x, -currentDir.z);\n                const pitchDiff = Math.asin(targetDir.y) - Math.asin(currentDir.y);\n                \n                cameraYaw += yawDiff * pullStrength * 0.3;\n                cameraPitch += pitchDiff * pullStrength * 0.3;\n                \n                // Clamp values\n                cameraPitch = Math.max(-0.8, Math.min(0.3, cameraPitch));\n                cameraYaw = Math.max(-1.2, Math.min(1.2, cameraYaw));\n            }\n        }\n\n        // Pointer Lock for desktop\n        let pointerLocked = false;\n\n        function requestPointerLock() {\n            if (!pointerLocked && document.pointerLockElement !== canvas) {\n                try { canvas.requestPointerLock(); } catch(e) {}\n            }\n        }\n\n        const pointerLockHint = document.getElementById('pointer-lock-hint');\n\n        document.addEventListener('pointerlockchange', () => {\n            pointerLocked = document.pointerLockElement === canvas;\n            if (pointerLocked) {\n                pointerLockHint.classList.remove('show');\n            } else if (gameState === 'playing') {\n                pointerLockHint.classList.add('show');\n            }\n        });\n\n        // Mouse controls with pointer lock support\n        canvas.addEventListener('mousedown', (e) => {\n            if (gameState === 'playing' || gameState === 'edit') {\n                if (!pointerLocked) {\n                    requestPointerLock();\n                    return;\n                }\n                if (e.button === 0 && canShoot && !isReloading && gameState === 'playing') {\n                    shoot();\n                }\n                if (e.button === 2 && gameState === 'playing') {\n                    toggleScope();\n                }\n            }\n        });\n\n        canvas.addEventListener('mousemove', (e) => {\n            if (gameState !== 'playing' && gameState !== 'edit') return;\n            if (pointerLocked) {\n                const dx = e.movementX || 0;\n                const dy = e.movementY || 0;\n                if (isScoped) {\n                    scopeTargetYaw -= dx * scopedSensitivity;\n                    scopeTargetPitch -= dy * scopedSensitivity;\n                    scopeTargetPitch = Math.max(-0.8, Math.min(0.3, scopeTargetPitch));\n                    scopeTargetYaw = Math.max(-1.2, Math.min(1.2, scopeTargetYaw));\n                } else {\n                    cameraYaw -= dx * sensitivity;\n                    cameraPitch -= dy * sensitivity;\n                    cameraPitch = Math.max(-0.8, Math.min(0.3, cameraPitch));\n                    cameraYaw = Math.max(-1.2, Math.min(1.2, cameraYaw));\n                }\n                applyAimAssist();\n            } else {\n                // Fallback drag aiming without pointer lock\n                if (!input.isTouching) return;\n                const dx = e.clientX - input.lastTouchX;\n                const dy = e.clientY - input.lastTouchY;\n                cameraYaw -= dx * (isScoped ? scopedSensitivity : sensitivity);\n                cameraPitch -= dy * (isScoped ? scopedSensitivity : sensitivity);\n                cameraPitch = Math.max(-0.8, Math.min(0.3, cameraPitch));\n                cameraYaw = Math.max(-1.2, Math.min(1.2, cameraYaw));\n                input.lastTouchX = e.clientX;\n                input.lastTouchY = e.clientY;\n                applyAimAssist();\n            }\n        });\n\n        canvas.addEventListener('mouseup', (e) => {\n            if (!pointerLocked) {\n                input.isTouching = false;\n            }\n        });\n\n        // Right click for scope toggle\n        canvas.addEventListener('contextmenu', (e) => {\n            e.preventDefault();\n            if (gameState === 'playing' && !pointerLocked) toggleScope();\n        });\n\n        // Keyboard controls\n        const keysDown = {};\n        document.addEventListener('keydown', (e) => {\n            keysDown[e.code] = true;\n            if (gameState === 'playing') {\n                // Space to shoot\n                if (e.code === 'Space' && canShoot && !isReloading) {\n                    e.preventDefault();\n                    shoot();\n                }\n                // E or R to toggle scope\n                if (e.code === 'KeyE' || e.code === 'KeyR') {\n                    e.preventDefault();\n                    toggleScope();\n                }\n            }\n            // ESC to pause (also exits pointer lock)\n            if (e.code === 'Escape' && gameState === 'playing') {\n                e.preventDefault();\n                if (pointerLocked) {\n                    document.exitPointerLock();\n                }\n                showPauseMenu();\n            }\n        });\n        document.addEventListener('keyup', (e) => {\n            keysDown[e.code] = false;\n        });\n\n        // Shooting\n        function shoot() {\n            if (!canShoot || isReloading || killCamActive) return;\n\n            canShoot = false;\n            isReloading = true;\n            shotsFired++;\n\n            // Play sound\n            playSound('gunshot_sound', (config.settings.sfxVolume ?? 70) / 100);\n\n            // Advance tutorial if waiting for shoot\n            advanceTutorialOnShoot();\n\n            // Muzzle flash\n            muzzleFlash.classList.add('flash-show');\n            setTimeout(() => muzzleFlash.classList.remove('flash-show'), 100);\n            \n            // Bullet trail effect\n            createBulletTrail();\n\n            // Raycast from crosshair (BEFORE RECOIL) - pinpoint accuracy\n            const raycaster = new THREE.Raycaster();\n            updateCamera();\n            camera.updateMatrixWorld();\n            \n            // Shots go exactly where crosshair points - no wind deviation on aim\n            const ndcX = 0;\n            const ndcY = 0;\n            raycaster.setFromCamera(new THREE.Vector2(ndcX, ndcY), camera);\n            const ray = raycaster.ray;\n\n            // Check for obstacle collision first (bullets can't penetrate objects)\n            let closestObstacleDist = Infinity;\n            const obstacleIntersects = raycaster.intersectObjects(obstacles, false);\n            if (obstacleIntersects.length > 0) {\n                closestObstacleDist = obstacleIntersects[0].distance;\n            }\n\n            // Recoil with camera kick - 3D rifle recoil\n            rifleRecoil = 1.0;\n            \n            // Camera recoil\n            cameraPitch += 0.03;\n            cameraPitch = Math.min(0.3, cameraPitch);\n\n            // Screen shake\n            if (config.settings.shakeIntensity > 0) {\n                shakeTimer = 0.25;\n            }\n\n            // Crosshair expand\n            crosshairEl.classList.add('crosshair-expanded');\n            setTimeout(() => crosshairEl.classList.remove('crosshair-expanded'), 150);\n\n            // Check hits against NPCs\n            let hitNPC = null;\n            let isHeadshot = false;\n            let minHitDist = Infinity;\n\n            for (const npc of npcs) {\n                if (!npc.alive) continue;\n\n                const O = ray.origin;\n                const D = ray.direction;\n\n                const dx = npc.x - O.x;\n                const dz = npc.z - O.z;\n                const dirLenSq = D.x * D.x + D.z * D.z;\n                \n                if (dirLenSq < 0.0001) continue;\n\n                let t = (dx * D.x + dz * D.z) / dirLenSq;\n                if (t < 0) continue;\n\n                const hitX = O.x + D.x * t;\n                const hitY = O.y + D.y * t;\n                const hitZ = O.z + D.z * t;\n\n                const distXZ = Math.sqrt((hitX - npc.x) ** 2 + (hitZ - npc.z) ** 2);\n                \n                // 3D model: origin at feet, so bottom=groundY, top=groundY+height\n                const npcBottom = npc.sprite.position.y;\n                const npcTop = npc.sprite.position.y + npc.height;\n                \n                const hitRadius = 0.5; // Generous hitbox for 3D model width\n                \n                if (distXZ < hitRadius && hitY > npcBottom - 0.3 && hitY < npcTop + 0.3) {\n                    const distToPlayer = t;\n                    // Only register hit if no obstacle is closer (bullets don't penetrate)\n                    if (distToPlayer < minHitDist && distToPlayer < closestObstacleDist) {\n                        minHitDist = distToPlayer;\n                        hitNPC = npc;\n                        \n                        // Check for headshot - head is top ~25% of model\n                        let headY = npcTop - npc.height * 0.2;\n                        if (npc.behavior === 'sleeping') {\n                            isHeadshot = false; \n                        } else {\n                            isHeadshot = hitY > headY;\n                        }\n                    }\n                }\n            }\n\n            if (hitNPC) {\n                shotsHit++;\n                // Hit marker\n                hitMarkerEl.classList.add('show');\n                setTimeout(() => hitMarkerEl.classList.remove('show'), 200);\n\n                // Red hit flash\n                hitFlash.classList.add('active');\n                setTimeout(() => hitFlash.classList.remove('active'), 300);\n\n                // Check for headshot kill-cam on target\n                if (hitNPC.type === 'target' && isHeadshot) {\n                    gotHeadshot = true;\n                    updateCombo(true);\n                    startKillCam(hitNPC);\n                    return;\n                }\n                \n                // Blood effect at center of screen (for non kill-cam hits)\n                const centerX = gameWorld.clientWidth / 2;\n                const centerY = gameWorld.clientHeight / 2;\n                createBloodEffect(centerX, centerY, isHeadshot);\n                createBulletHitEffect(centerX, centerY, isHeadshot);\n\n                if (hitNPC.type === 'target') {\n                    // Body shot on target - trigger slow-mo bullet time\n                    gotHeadshot = false;\n                    updateCombo(true);\n                    slowMoActive = true;\n                    slowMoTimer = config.settings.slowMoDuration || 0.5;\n                    slowMoOverlay.classList.add('active');\n                    hitNPC.alive = false;\n                    scene.remove(hitNPC.sprite);\n                    disposeObject(hitNPC.sprite);\n                    if (hitNPC.shadow) {\n                        scene.remove(hitNPC.shadow);\n                        disposeObject(hitNPC.shadow);\n                    }\n                    if (hitNPC.marker) {\n                        scene.remove(hitNPC.marker);\n                        disposeObject(hitNPC.marker);\n                    }\n                    showFloatingText('+100');\n                    targetsHitEl.textContent = '1';\n                    gameState = 'result';\n                    setTimeout(() => { endMission(true); }, 600);\n                } else {\n                    // Shot civilian - mission fail\n                    hitNPC.alive = false;\n                    scene.remove(hitNPC.sprite);\n                    disposeObject(hitNPC.sprite);\n                    if (hitNPC.shadow) {\n                        scene.remove(hitNPC.shadow);\n                        disposeObject(hitNPC.shadow);\n                    }\n                    if (hitNPC.marker) {\n                        scene.remove(hitNPC.marker);\n                        disposeObject(hitNPC.marker);\n                    }\n                    playSound('mission_fail_sound', (config.settings.sfxVolume ?? 70) / 100);\n                    showFloatingText('CIVILIAN HIT!', true);\n                    gameState = 'result';\n                    setTimeout(() => endMission(false, 'Civilian killed!'), 500);\n                }\n            } else {\n                // Miss\n                missesLeft--;\n                updateCombo(false);\n                updateAmmoDisplay();\n                if (missesLeft <= 0) {\n                    playSound('mission_fail_sound', (config.settings.sfxVolume ?? 70) / 100);\n                    gameState = 'result';\n                    setTimeout(() => endMission(false, 'Out of ammo!'), 500);\n                }\n            }\n\n            // Reload\n            updateAmmoDisplay();\n            const reloadTime = 1200;\n            setTimeout(() => {\n                isReloading = false;\n                canShoot = true;\n                updateAmmoDisplay();\n            }, reloadTime);\n        }\n\n        function updateAmmoDisplay() {\n            if (isReloading) {\n                ammoEl.innerHTML = '<span style=\"color:#f0a030;\">RELOADING...</span>';\n                ammoEl.classList.add('reloading');\n            } else {\n                let bulletsHTML = '';\n                for (let i = 0; i < 3; i++) {\n                    if (i < missesLeft) {\n                        bulletsHTML += '<div class=\"bullet\"></div>';\n                    } else {\n                        bulletsHTML += '<div class=\"bullet empty\"></div>';\n                    }\n                }\n                ammoEl.innerHTML = bulletsHTML;\n                ammoEl.classList.remove('reloading');\n            }\n            \n        }\n\n        function updateStabilityBar() {\n            stabilityBar.style.height = stabilityValue + '%';\n            // Position marker based on stability\n            const containerHeight = 200;\n            const markerY = containerHeight * (1 - stabilityValue / 100);\n            stabilityMarker.style.top = markerY + 'px';\n        }\n\n        // Mission management\n        const missions = config.missions;\n        const splashScreen = document.getElementById('splash-screen');\n        const splashBgImg = document.getElementById('splash-bg-img');\n        const splashCoins = document.getElementById('splash-coins');\n        const splashLevel = document.getElementById('splash-level');\n        const splashMissionInfo = document.getElementById('splash-mission-info');\n        const splashStartBtn = document.getElementById('splash-start');\n        const splashSettingsBtn = document.getElementById('splash-settings');\n        const splashParticles = document.getElementById('splash-particles');\n        const tutorialOverlay = document.getElementById('tutorial-overlay');\n        const tutorialCloseBtn = document.getElementById('tutorial-close');\n        const splashTutorialBtn = document.getElementById('splash-tutorial');\n        const leaderboardOverlay = document.getElementById('leaderboard-overlay');\n        const leaderboardCloseBtn = document.getElementById('leaderboard-close');\n        const leaderboardContent = document.getElementById('leaderboard-content');\n        const splashLeaderboardBtn = document.getElementById('splash-leaderboard');\n\n        // Set splash background image\n        if (imageCache['splash_background']) {\n            splashBgImg.src = imageCache['splash_background'].src;\n        }\n\n        // Create floating particles\n        function createSplashParticles() {\n            splashParticles.innerHTML = '';\n            for (let i = 0; i < 20; i++) {\n                const particle = document.createElement('div');\n                particle.className = 'particle';\n                particle.style.left = Math.random() * 100 + '%';\n                particle.style.animationDelay = Math.random() * 15 + 's';\n                particle.style.animationDuration = (10 + Math.random() * 10) + 's';\n                splashParticles.appendChild(particle);\n            }\n        }\n\n        function showMenu() {\n            gameState = 'menu';\n            missionOverlay.style.display = 'none';\n            actionButtons.style.display = 'none';\n            rifleOverlay.style.display = 'none';\n            \n            // Apply equipped gun skin\n            if (config.shop.equippedGun && config.shop.equippedGun !== 'default') {\n                applyGunSkin(config.shop.equippedGun);\n            }\n            \n            // Update splash screen info\n            splashCoins.textContent = coins;\n            const playerLevel = Math.floor(coins / 500) + 1;\n            splashLevel.textContent = 'Lv.' + playerLevel;\n            \n            const missionNames = ['TUTORIAL', 'FIRST CONTRACT', 'CROWDED SCENE', 'RUSH HOUR', 'QUICK STRIKE', 'PRESSURE TEST', 'HIGH STAKES', 'ELITE CONTRACT', 'MASTER SNIPER', 'FINAL CONTRACT'];\n            const zoneIdx = missionZoneMap[missionIndex % missionZoneMap.length];\n            const zoneName = locationZones[zoneIdx].name.toUpperCase();\n            splashMissionInfo.textContent = `MISSION ${missionIndex + 1}/10 \u2022 ${zoneName}`;\n            \n            // Show splash screen\n            splashScreen.classList.remove('hidden');\n            createSplashParticles();\n            \n            // Play ambient music at low volume\n            playBGMusic();\n        }\n\n        // Splash screen buttons\n        splashStartBtn.addEventListener('click', () => {\n            splashScreen.classList.add('hidden');\n            startMission();\n            requestPointerLock();\n        });\n        splashStartBtn.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            splashScreen.classList.add('hidden');\n            startMission();\n        });\n\n        splashSettingsBtn.addEventListener('click', () => {\n            showSettingsMenu();\n        });\n\n        // Leaderboard button handlers\n        splashLeaderboardBtn.addEventListener('click', () => {\n            showLeaderboard();\n        });\n        splashLeaderboardBtn.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            showLeaderboard();\n        });\n\n        leaderboardCloseBtn.addEventListener('click', () => {\n            hideLeaderboard();\n        });\n        leaderboardCloseBtn.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            hideLeaderboard();\n        });\n\n        // Shop elements\n        const shopOverlay = document.getElementById('shop-overlay');\n        const shopContent = document.getElementById('shop-content');\n        const shopCoinsEl = document.getElementById('shop-coins');\n        const shopCloseBtn = document.getElementById('shop-close');\n        const tabGuns = document.getElementById('tab-guns');\n        const tabBullets = document.getElementById('tab-bullets');\n        const splashShopBtn = document.getElementById('splash-shop');\n        \n        let currentShopTab = 'guns';\n\n        // Shop button handlers\n        splashShopBtn.addEventListener('click', () => {\n            showShop();\n        });\n        splashShopBtn.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            showShop();\n        });\n\n        shopCloseBtn.addEventListener('click', () => {\n            hideShop();\n        });\n        shopCloseBtn.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            hideShop();\n        });\n\n        tabGuns.addEventListener('click', () => {\n            currentShopTab = 'guns';\n            tabGuns.classList.add('active');\n            tabBullets.classList.remove('active');\n            renderShopItems();\n        });\n\n        tabBullets.addEventListener('click', () => {\n            currentShopTab = 'bullets';\n            tabBullets.classList.add('active');\n            tabGuns.classList.remove('active');\n            renderShopItems();\n        });\n\n        function showShop() {\n            shopOverlay.classList.remove('hidden');\n            shopCoinsEl.textContent = coins;\n            renderShopItems();\n        }\n\n        function hideShop() {\n            shopOverlay.classList.add('hidden');\n        }\n\n        function renderShopItems() {\n            const shop = config.shop;\n            let html = '<div class=\"shop-grid\">';\n            \n            if (currentShopTab === 'guns') {\n                shop.gunSkins.forEach(gun => {\n                    const isUnlocked = shop.unlockedGuns.includes(gun.id);\n                    const isEquipped = shop.equippedGun === gun.id;\n                    const canAfford = coins >= gun.price;\n                    \n                    html += `\n                        <div class=\"shop-item ${isEquipped ? 'equipped' : ''} ${!isUnlocked ? 'locked' : ''}\" \n                             data-id=\"${gun.id}\" data-type=\"gun\" data-price=\"${gun.price}\">\n                            <div class=\"shop-item-preview gun-preview\">\n                                <div class=\"gun-shape\" style=\"background: ${gun.color};\">\n                                    <div class=\"gun-accent\" style=\"background: ${gun.accent};\"></div>\n                                </div>\n                            </div>\n                            <div class=\"shop-item-name\">${gun.name}</div>\n                            ${isUnlocked \n                                ? `<div class=\"shop-item-status ${isEquipped ? 'equipped-status' : ''}\">${isEquipped ? '\u2713 EQUIPPED' : 'OWNED'}</div>`\n                                : `<div class=\"shop-item-price ${canAfford ? '' : 'locked'}\">\ud83e\ude99 ${gun.price}</div>`\n                            }\n                        </div>\n                    `;\n                });\n            } else {\n                shop.bulletEffects.forEach(bullet => {\n                    const isUnlocked = shop.unlockedBullets.includes(bullet.id);\n                    const isEquipped = shop.equippedBullet === bullet.id;\n                    const canAfford = coins >= bullet.price;\n                    const trailColor = bullet.trailColor || '#888888';\n                    const hitColor = bullet.hitColor || '#ffffff';\n                    \n                    html += `\n                        <div class=\"shop-item ${isEquipped ? 'equipped' : ''} ${!isUnlocked ? 'locked' : ''}\"\n                             data-id=\"${bullet.id}\" data-type=\"bullet\" data-price=\"${bullet.price}\">\n                            <div class=\"shop-item-preview bullet-preview\">\n                                <div class=\"bullet-effect-demo\" style=\"background: linear-gradient(90deg, transparent, ${trailColor});\">\n                                    <style>\n                                        .shop-item[data-id=\"${bullet.id}\"] .bullet-effect-demo::after {\n                                            background: ${hitColor};\n                                            box-shadow: 0 0 10px ${hitColor};\n                                        }\n                                    </style>\n                                </div>\n                            </div>\n                            <div class=\"shop-item-name\">${bullet.name}</div>\n                            ${isUnlocked \n                                ? `<div class=\"shop-item-status ${isEquipped ? 'equipped-status' : ''}\">${isEquipped ? '\u2713 EQUIPPED' : 'OWNED'}</div>`\n                                : `<div class=\"shop-item-price ${canAfford ? '' : 'locked'}\">\ud83e\ude99 ${bullet.price}</div>`\n                            }\n                        </div>\n                    `;\n                });\n            }\n            \n            html += '</div>';\n            shopContent.innerHTML = html;\n            \n            // Add click handlers to items\n            document.querySelectorAll('.shop-item').forEach(item => {\n                item.addEventListener('click', () => handleShopItemClick(item));\n            });\n        }\n\n        function handleShopItemClick(item) {\n            const id = item.dataset.id;\n            const type = item.dataset.type;\n            const price = parseInt(item.dataset.price);\n            const shop = config.shop;\n            \n            if (type === 'gun') {\n                const isUnlocked = shop.unlockedGuns.includes(id);\n                if (isUnlocked) {\n                    // Equip\n                    shop.equippedGun = id;\n                    config.shop = shop;\n                    applyGunSkin(id);\n                    // Save equipped gun\n                    saveGameState();\n                    lib.log('Equipped gun: ' + id);\n                } else if (coins >= price) {\n                    // Purchase\n                    coins -= price;\n                    shop.unlockedGuns.push(id);\n                    shop.equippedGun = id;\n                    config.shop = shop;\n                    updateCoinsDisplay();\n                    shopCoinsEl.textContent = coins;\n                    applyGunSkin(id);\n                    saveGameState();\n                    lib.log('Purchased and equipped gun: ' + id + ' for ' + price + ' coins');\n                } else {\n                    lib.log('Not enough coins to purchase ' + id);\n                    return;\n                }\n            } else {\n                const isUnlocked = shop.unlockedBullets.includes(id);\n                if (isUnlocked) {\n                    // Equip\n                    shop.equippedBullet = id;\n                    config.shop = shop;\n                    // Save equipped bullet\n                    saveGameState();\n                    lib.log('Equipped bullet: ' + id);\n                } else if (coins >= price) {\n                    // Purchase\n                    coins -= price;\n                    shop.unlockedBullets.push(id);\n                    shop.equippedBullet = id;\n                    config.shop = shop;\n                    updateCoinsDisplay();\n                    shopCoinsEl.textContent = coins;\n                    saveGameState();\n                    lib.log('Purchased and equipped bullet: ' + id + ' for ' + price + ' coins');\n                } else {\n                    lib.log('Not enough coins to purchase ' + id);\n                    return;\n                }\n            }\n            \n            renderShopItems();\n        }\n\n\n        // Tutorial button handlers\n        splashTutorialBtn.addEventListener('click', () => {\n            showTutorial();\n        });\n        splashTutorialBtn.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            showTutorial();\n        });\n\n        tutorialCloseBtn.addEventListener('click', () => {\n            hideTutorial();\n        });\n        tutorialCloseBtn.addEventListener('touchend', (e) => {\n            e.preventDefault();\n            hideTutorial();\n        });\n\n        function showTutorial() {\n            tutorialOverlay.classList.remove('hidden');\n        }\n\n        function hideTutorial() {\n            tutorialOverlay.classList.add('hidden');\n        }\n\n        // Leaderboard functions\n        async function showLeaderboard() {\n            leaderboardOverlay.classList.remove('hidden');\n            leaderboardContent.innerHTML = '<div class=\"leaderboard-loading\">Loading rankings...</div>';\n            \n            try {\n                lib.log('Fetching leaderboard...');\n                const response = await lib.getTopNEntriesFromLeaderboard(10);\n                lib.log('Leaderboard fetched successfully. Entries: ' + response.entries.length);\n                displayLeaderboard(response.entries, response.userRank);\n            } catch (error) {\n                lib.log(`Failed to fetch leaderboard: ${error.message}`);\n                leaderboardContent.innerHTML = '<div class=\"leaderboard-error\">Failed to load leaderboard. Please try again later.</div>';\n            }\n        }\n\n        function displayLeaderboard(entries, userRank) {\n            if (!entries || entries.length === 0) {\n                leaderboardContent.innerHTML = '<div class=\"leaderboard-empty\">No entries yet. Be the first to set a score!</div>';\n                return;\n            }\n\n            let html = '<div class=\"leaderboard-list\">';\n            \n            entries.forEach((entry, index) => {\n                const rank = index + 1;\n                const isUserEntry = userRank === rank;\n                const rankClass = rank === 1 ? 'top1' : rank === 2 ? 'top2' : rank === 3 ? 'top3' : '';\n                \n                html += `\n                    <div class=\"leaderboard-entry ${isUserEntry ? 'user-entry' : ''}\">\n                        <div class=\"leaderboard-rank ${rankClass}\">#${rank}</div>\n                        <img class=\"leaderboard-profile-pic\" src=\"${entry.profilePicture || 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22%3E%3Crect fill=%22%232a3344%22 width=%22100%22 height=%22100%22/%3E%3Ctext x=%2250%22 y=%2250%22 font-size=%2240%22 text-anchor=%22middle%22 dy=%22.3em%22 fill=%22%23ffffff%22%3E${entry.username.charAt(0).toUpperCase()}%3C/text%3E%3C/svg%3E'}\" alt=\"${entry.username}\" onerror=\"this.src='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22%3E%3Crect fill=%22%232a3344%22 width=%22100%22 height=%22100%22/%3E%3Ctext x=%2250%22 y=%2250%22 font-size=%2240%22 text-anchor=%22middle%22 dy=%22.3em%22 fill=%22%23ffffff%22%3E${entry.username.charAt(0).toUpperCase()}%3C/text%3E%3C/svg%3E'\">\n                        <div class=\"leaderboard-info\">\n                            <div class=\"leaderboard-username\">${entry.username}${isUserEntry ? ' (You)' : ''}</div>\n                            ${isUserEntry && userRank ? `<div class=\"leaderboard-user-rank\">YOUR RANK</div>` : ''}\n                        </div>\n                        <div class=\"leaderboard-score\">${entry.score.toLocaleString()}</div>\n                    </div>\n                `;\n            });\n            \n            html += '</div>';\n            \n            // Show user's rank if they're not in top 10\n            if (userRank && userRank > 10) {\n                html += `\n                    <div style=\"margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--border-subtle);\">\n                        <div class=\"leaderboard-entry user-entry\">\n                            <div class=\"leaderboard-rank\">#${userRank}</div>\n                            <img class=\"leaderboard-profile-pic\" src=\"data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22%3E%3Crect fill=%22%232a3344%22 width=%22100%22 height=%22100%22/%3E%3Ctext x=%2250%22 y=%2250%22 font-size=%2240%22 text-anchor=%22middle%22 dy=%22.3em%22 fill=%22%23ffffff%22%3EYou%3C/text%3E%3C/svg%3E\" alt=\"You\">\n                            <div class=\"leaderboard-info\">\n                                <div class=\"leaderboard-username\">You</div>\n                                <div class=\"leaderboard-user-rank\">YOUR RANK</div>\n                            </div>\n                            <div class=\"leaderboard-score\">${coins.toLocaleString()}</div>\n                        </div>\n                    </div>\n                `;\n            }\n            \n            leaderboardContent.innerHTML = html;\n        }\n\n        function hideLeaderboard() {\n            leaderboardOverlay.classList.add('hidden');\n        }\n\n        async function submitScoreToLeaderboard(score) {\n            try {\n                lib.log('Submitting score to leaderboard: ' + score);\n                const response = await lib.addPlayerScoreToLeaderboard(score, 10);\n                if (response.success) {\n                    lib.log(`Score submitted successfully! Your rank: #${response.userRank || 'N/A'}`);\n                    return response;\n                }\n                return response;\n            } catch (error) {\n                lib.log(`Failed to submit score to leaderboard: ${error.message}`);\n                // Game continues even if leaderboard fails (e.g., guest users)\n                return null;\n            }\n        }\n\n        function showSettingsMenu() {\n            splashScreen.classList.add('hidden');\n            gameState = 'settings';\n            missionOverlay.style.display = 'flex';\n            missionOverlay.innerHTML = `\n                <h1 style=\"font-family:'Orbitron',monospace;font-size:32px;color:#60A5FA;margin-bottom:24px;\">SETTINGS</h1>\n                <div style=\"background:rgba(0,0,0,0.5);padding:24px 32px;border-radius:8px;border:1px solid rgba(255,255,255,0.1);min-width:300px;\">\n                    <div style=\"margin:16px 0;\">\n                        <label style=\"color:#9CA3AF;font-size:14px;display:block;margin-bottom:8px;\">Music Volume: <span id=\"music-val\">${config.settings.musicVolume}</span>%</label>\n                        <input type=\"range\" id=\"music-slider\" min=\"0\" max=\"100\" step=\"10\" value=\"${config.settings.musicVolume}\" style=\"width:100%;accent-color:#60A5FA;cursor:pointer;\">\n                    </div>\n                    <div style=\"margin:16px 0;\">\n                        <label style=\"color:#9CA3AF;font-size:14px;display:block;margin-bottom:8px;\">SFX Volume: <span id=\"sfx-val\">${config.settings.sfxVolume}</span>%</label>\n                        <input type=\"range\" id=\"sfx-slider\" min=\"0\" max=\"100\" step=\"10\" value=\"${config.settings.sfxVolume}\" style=\"width:100%;accent-color:#60A5FA;cursor:pointer;\">\n                    </div>\n                    <div style=\"margin:16px 0;\">\n                        <label style=\"color:#9CA3AF;font-size:14px;display:block;margin-bottom:8px;\">Screen Shake: <span id=\"shake-val\">${config.settings.shakeIntensity}</span>%</label>\n                        <input type=\"range\" id=\"shake-slider\" min=\"0\" max=\"100\" step=\"10\" value=\"${config.settings.shakeIntensity}\" style=\"width:100%;accent-color:#60A5FA;cursor:pointer;\">\n                    </div>\n                    <div style=\"margin:16px 0;\">\n                        <label style=\"color:#9CA3AF;font-size:14px;display:block;margin-bottom:8px;\">Slow-Mo Duration: <span id=\"slowmo-val\">${config.settings.slowMoDuration}</span>s</label>\n                        <input type=\"range\" id=\"slowmo-slider\" min=\"0.2\" max=\"0.6\" step=\"0.1\" value=\"${config.settings.slowMoDuration}\" style=\"width:100%;accent-color:#60A5FA;cursor:pointer;\">\n                    </div>\n                </div>\n                <div style=\"margin-top:24px;\">\n                    <button class=\"btn success\" id=\"btn-save-settings\">SAVE</button>\n                    <button class=\"btn\" id=\"btn-back\" style=\"margin-top:8px;\">BACK</button>\n                </div>\n            `;\n            \n            // Music volume slider\n            const musicSlider = document.getElementById('music-slider');\n            const musicVal = document.getElementById('music-val');\n            musicSlider.addEventListener('input', (e) => {\n                musicVal.textContent = e.target.value;\n            });\n            \n            // SFX volume slider\n            const sfxSlider = document.getElementById('sfx-slider');\n            const sfxVal = document.getElementById('sfx-val');\n            sfxSlider.addEventListener('input', (e) => {\n                sfxVal.textContent = e.target.value;\n            });\n            \n            // Shake intensity slider\n            const shakeSlider = document.getElementById('shake-slider');\n            const shakeVal = document.getElementById('shake-val');\n            shakeSlider.addEventListener('input', (e) => {\n                shakeVal.textContent = e.target.value;\n            });\n            \n            // Slow-mo duration slider\n            const slowmoSlider = document.getElementById('slowmo-slider');\n            const slowmoVal = document.getElementById('slowmo-val');\n            slowmoSlider.addEventListener('input', (e) => {\n                slowmoVal.textContent = e.target.value;\n            });\n            \n            // Save button\n            document.getElementById('btn-save-settings').addEventListener('click', () => {\n                config.settings.musicVolume = parseInt(musicSlider.value);\n                config.settings.sfxVolume = parseInt(sfxSlider.value);\n                config.settings.shakeIntensity = parseInt(shakeSlider.value);\n                config.settings.slowMoDuration = parseFloat(slowmoSlider.value);\n                \n                // Update background music volume if playing\n                if (bgMusicSource) {\n                    playBGMusic();\n                }\n                \n                // Save settings to persistent storage\n                saveGameState();\n                lib.log('Settings saved');\n                \n                missionOverlay.style.display = 'none';\n                showMenu();\n            });\n            \n            document.getElementById('btn-back').addEventListener('click', () => {\n                missionOverlay.style.display = 'none';\n                showMenu();\n            });\n        }\n\n        function startMission() {\n            const mission = missions[missionIndex];\n            missesLeft = config.settings.missLimit ?? 3;\n            timeLeft = mission.timeLimit || 35;\n            isReloading = false;\n            canShoot = true;\n            shotsFired = 0;\n            shotsHit = 0;\n            gotHeadshot = false;\n            slowMoActive = false;\n            slowMoTimer = 0;\n            isScoped = false;\n            stabilityValue = 100;\n            comboCount = 0;\n            comboTimer = 0;\n            targetEscapeTimer = 0;\n            targetEscaping = false;\n            aimingTime = 0;\n            windStrength = 0;\n            cameraZoomTarget = 60;\n            pointerLockHint.classList.remove('show');\n\n            // Hide splash screen\n            splashScreen.classList.add('hidden');\n\n            // Reset UI states\n            comboCounter.classList.remove('show');\n            escapeWarning.classList.remove('show');\n            \n            \n\n            // Apply random modifier for missions after tutorial\n            if (missionIndex > 0 && Math.random() > 0.4) {\n                const availableModifiers = modifiers.filter(m => m !== 'none');\n                applyModifier(availableModifiers[Math.floor(Math.random() * availableModifiers.length)]);\n            } else {\n                applyModifier('none');\n            }\n\n            // Reset scope state\n            scopeOverlay.classList.remove('active');\n            scopeBtn.classList.remove('scope-active');\n            rifleScopeHidden = false;\n            rifleTargetX = 0.12;\n            rifleTargetY = -0.18;\n            crosshairEl.style.display = 'block';\n            camera.fov = 60;\n            camera.updateProjectionMatrix();\n\n            // Set zone based on mission\n            const zoneIndex = missionZoneMap[missionIndex % missionZoneMap.length];\n            currentZone = locationZones[zoneIndex];\n\n            cameraYaw = currentZone.initialYaw;\n            cameraPitch = currentZone.initialPitch;\n            \n            // Position player for best view of the zone\n            if (currentZone.name === 'City Park') {\n                camera.position.set(-3.5, 12, -3.5);\n            } else if (currentZone.name === 'Left Rooftop') {\n                camera.position.set(-3.5, 12, -3.5);\n            } else if (currentZone.name === 'Right Rooftop') {\n                camera.position.set(3.5, 12, -3.5);\n            } else {\n                camera.position.set(0, 12, 0);\n            }\n\n            removeAllNPCs();\n            updateAmmoDisplay();\n            updateStabilityBar();\n            targetsHitEl.textContent = '0';\n            targetsTotalEl.textContent = '1';\n\n            // Spawn NPCs in the current zone\n            const civCount = mission.civilianCount || 5;\n            const zone = currentZone;\n\n            // Spawn target within zone's target spawn area\n            const targetX = zone.targetSpawn.minX + Math.random() * (zone.targetSpawn.maxX - zone.targetSpawn.minX);\n            const targetZ = zone.targetSpawn.minZ + Math.random() * (zone.targetSpawn.maxZ - zone.targetSpawn.minZ);\n            createNPC('target', targetX, targetZ);\n\n            // Spawn civilians spread around zone center\n            const zoneWidth = (zone.bounds.maxX - zone.bounds.minX) * 0.4;\n            const zoneDepth = (zone.bounds.maxZ - zone.bounds.minZ) * 0.4;\n            for (let i = 0; i < civCount; i++) {\n                const angle = (i / civCount) * Math.PI * 2;\n                const civX = zone.civSpawnCenter.x + Math.cos(angle) * zoneWidth * (0.5 + Math.random() * 0.5);\n                const civZ = zone.civSpawnCenter.z + Math.sin(angle) * zoneDepth * (0.5 + Math.random() * 0.5);\n                // Clamp to bounds\n                const clampedX = Math.max(zone.bounds.minX + 1, Math.min(zone.bounds.maxX - 1, civX));\n                const clampedZ = Math.max(zone.bounds.minZ + 1, Math.min(zone.bounds.maxZ - 1, civZ));\n                createNPC('civilian', clampedX, clampedZ);\n            }\n\n\n            // Show target description\n            const descriptions = [\n                '\ud83c\udfaf Target: Red jacket, dark pants',\n                '\ud83c\udfaf Target: Male in red clothing',\n                '\ud83c\udfaf Eliminate: Red jacket suspect'\n            ];\n            targetDescEl.textContent = descriptions[Math.floor(Math.random() * descriptions.length)];\n            targetDescEl.classList.add('show');\n            actionButtons.style.display = 'flex';\n\n            // Show briefing then countdown\n            gameState = 'countdown';\n            rifleOverlay.style.display = 'flex';\n\n            // Skip briefing - jump straight into action\n            missionOverlay.style.display = 'none';\n            doCountdown(3);\n            \n            // Show quick visual cues instead of briefing\n            setTimeout(() => {\n                const zone = currentZone;\n                targetDescEl.textContent = '\ud83c\udfaf ELIMINATE THE RED JACKET TARGET';\n                targetDescEl.classList.add('show');\n                setTimeout(() => {\n                    targetDescEl.textContent = `\ud83d\udccd ${zone.name.toUpperCase()} \u2022 ${missions[missionIndex].timeLimit}s`;\n                    setTimeout(() => {\n                        if (currentModifier && currentModifier !== 'none') {\n                            const modLabels = { wind: '\ud83d\udca8 WINDY', running: '\ud83c\udfc3 FAST TARGET', night: '\ud83c\udf19 NIGHT OPS', limited: '\u26a0\ufe0f LOW AMMO' };\n                            targetDescEl.textContent = modLabels[currentModifier] || '';\n                            setTimeout(() => {\n                                targetDescEl.textContent = '\ud83c\udfaf Target: Red jacket, dark pants';\n                            }, 2000);\n                        } else {\n                            targetDescEl.textContent = '\ud83c\udfaf Target: Red jacket, dark pants';\n                        }\n                    }, 2000);\n                }, 2000);\n            }, 500);\n        }\n\n        // Interactive Tutorial System\n        let tutorialStep = 0;\n        let tutorialActive = false;\n        const tutorialSteps = [\n            { msg: '\\u{1F44B} Welcome, Sniper! This is your training mission.\\nLet\\'s learn the basics.', action: 'wait', delay: 3500 },\n            { msg: '\\u{1F3AF} AIMING: Drag on screen (mobile) or move mouse (desktop).\\nClick to enable pointer lock for free aim.', action: 'aim', hint: 'Move your crosshair around' },\n            { msg: '\\u{1F534} See the red marker above? That\\'s your TARGET.\\nThey wear a RED JACKET. Never shoot civilians!', action: 'wait', delay: 4500 },\n            { msg: '\\u{1F4A5} SHOOTING: Tap \\u2299 button (mobile) or LEFT CLICK (desktop).\\nTry firing a shot now!', action: 'shoot' },\n            { msg: '\\u25CE SCOPE: Tap \\u25CE button or RIGHT CLICK to zoom in.\\nTry toggling your scope!', action: 'scope' },\n            { msg: '\\u{1F3AE} MOVEMENT: Use joystick (mobile) or the bottom-right pad to reposition yourself.', action: 'wait', delay: 3500 },\n            { msg: '\\u26A0\\uFE0F FAIL CONDITIONS:\\n\\u2022 Miss 3 shots\\n\\u2022 Hit a civilian\\n\\u2022 Time runs out\\n\\u2022 Target escapes', action: 'wait', delay: 5000 },\n            { msg: '\\u{1F4B0} BONUSES: Headshot +50 | Quick kill +50 | Perfect accuracy +40\\nNow eliminate the target! Good luck!', action: 'complete' }\n        ];\n\n        function startTutorialMission() {\n            tutorialActive = true;\n            tutorialStep = 0;\n            showTutorialStep();\n        }\n\n        function showTutorialStep() {\n            if (tutorialStep >= tutorialSteps.length) {\n                tutorialActive = false;\n                targetDescEl.textContent = '\\u{1F3AF} Eliminate the target!';\n                targetDescEl.style.whiteSpace = '';\n                targetDescEl.style.fontSize = '';\n                targetDescEl.style.maxWidth = '';\n                targetDescEl.style.textAlign = '';\n                targetDescEl.classList.add('show');\n                return;\n            }\n            const step = tutorialSteps[tutorialStep];\n            targetDescEl.style.whiteSpace = 'pre-line';\n            targetDescEl.style.fontSize = '12px';\n            targetDescEl.style.maxWidth = '380px';\n            targetDescEl.style.textAlign = 'center';\n            targetDescEl.textContent = step.msg;\n            targetDescEl.classList.add('show');\n\n            if (step.action === 'wait') {\n                setTimeout(() => {\n                    tutorialStep++;\n                    showTutorialStep();\n                }, step.delay || 3000);\n            } else if (step.action === 'aim') {\n                const startYaw = cameraYaw;\n                const checkAim = setInterval(() => {\n                    if (Math.abs(cameraYaw - startYaw) > 0.08) {\n                        clearInterval(checkAim);\n                        tutorialStep++;\n                        showTutorialStep();\n                    }\n                }, 200);\n            } else if (step.action === 'shoot') {\n                // Advance handled in shoot() via advanceTutorialOnShoot\n            } else if (step.action === 'scope') {\n                const checkScope = setInterval(() => {\n                    if (isScoped) {\n                        clearInterval(checkScope);\n                        setTimeout(() => {\n                            tutorialStep++;\n                            showTutorialStep();\n                        }, 1500);\n                    }\n                }, 200);\n            } else if (step.action === 'complete') {\n                tutorialActive = false;\n                setTimeout(() => {\n                    targetDescEl.textContent = '\\u{1F3AF} Now eliminate the target!';\n                    targetDescEl.style.whiteSpace = '';\n                    targetDescEl.style.fontSize = '';\n                    targetDescEl.style.maxWidth = '';\n                    targetDescEl.style.textAlign = '';\n                }, 5000);\n            }\n        }\n\n        function advanceTutorialOnShoot() {\n            if (tutorialActive && tutorialSteps[tutorialStep] && tutorialSteps[tutorialStep].action === 'shoot') {\n                tutorialStep++;\n                showTutorialStep();\n            }\n        }\n\n        function showMissionBriefing(callback) {\n            const mission = missions[missionIndex];\n            const zoneIndex = missionZoneMap[missionIndex % missionZoneMap.length];\n            const zone = locationZones[zoneIndex];\n            \n            // Find target NPC to get position info\n            const targetNPC = npcs.find(n => n.type === 'target');\n            let posHint = '';\n            let compassDir = '\u2014';\n            let compassDeg = 0;\n            if (targetNPC) {\n                const zoneCenterX = (zone.bounds.minX + zone.bounds.maxX) / 2;\n                const zoneCenterZ = (zone.bounds.minZ + zone.bounds.maxZ) / 2;\n                const relX = targetNPC.x - zoneCenterX;\n                const relZ = targetNPC.z - zoneCenterZ;\n                \n                let dirH = relX > 1 ? 'East' : relX < -1 ? 'West' : 'Center';\n                let dirV = relZ < -1 ? 'North' : relZ > 1 ? 'South' : 'Mid';\n                posHint = `${dirH} ${dirV}`;\n                \n                const angle = Math.atan2(relX, -relZ) * (180 / Math.PI);\n                compassDeg = Math.round((angle + 360) % 360);\n                if (angle >= -22.5 && angle < 22.5) compassDir = 'N';\n                else if (angle >= 22.5 && angle < 67.5) compassDir = 'NE';\n                else if (angle >= 67.5 && angle < 112.5) compassDir = 'E';\n                else if (angle >= 112.5 && angle < 157.5) compassDir = 'SE';\n                else if (angle >= 157.5 || angle < -157.5) compassDir = 'S';\n                else if (angle >= -157.5 && angle < -112.5) compassDir = 'SW';\n                else if (angle >= -112.5 && angle < -67.5) compassDir = 'W';\n                else compassDir = 'NW';\n            }\n\n            const pitchHint = zone.initialPitch < -0.2 ? 'Below' : zone.initialPitch < -0.05 ? 'Slightly Below' : 'Eye Level';\n            const dist = targetNPC ? Math.round(Math.sqrt(Math.pow(targetNPC.x - camera.position.x, 2) + Math.pow(targetNPC.z - camera.position.z, 2))) : '?';\n\n            // Modifier badge\n            let modifierHTML = '';\n            if (currentModifier && currentModifier !== 'none') {\n                const modData = { \n                    wind: { icon: '\ud83d\udca8', label: 'WIND ACTIVE', color: '#60A5FA', bg: 'rgba(96,165,250,0.12)', border: 'rgba(96,165,250,0.3)' },\n                    running: { icon: '\ud83c\udfc3', label: 'FAST TARGET', color: '#f59e0b', bg: 'rgba(245,158,11,0.12)', border: 'rgba(245,158,11,0.3)' },\n                    night: { icon: '\ud83c\udf19', label: 'NIGHT OPS', color: '#a78bfa', bg: 'rgba(167,139,250,0.12)', border: 'rgba(167,139,250,0.3)' },\n                    limited: { icon: '\u26a0\ufe0f', label: 'LOW AMMO', color: '#ef4444', bg: 'rgba(239,68,68,0.12)', border: 'rgba(239,68,68,0.3)' }\n                };\n                const mod = modData[currentModifier];\n                if (mod) modifierHTML = `<div class=\"briefing-modifier\" style=\"background:${mod.bg};border:1px solid ${mod.border};color:${mod.color};\"><span>${mod.icon}</span><span>${mod.label}</span></div>`;\n            }\n\n            // Threat level\n            const threatLevel = Math.min(5, Math.ceil((missionIndex + 1) / 2));\n            const threatDots = Array.from({length: 5}, (_, i) => \n                `<span style=\"width:8px;height:8px;border-radius:50%;background:${i < threatLevel ? 'var(--accent-red)' : 'rgba(255,255,255,0.1)'};box-shadow:${i < threatLevel ? '0 0 8px var(--accent-red-glow)' : 'none'};display:inline-block;\"></span>`\n            ).join('');\n\n            missionOverlay.style.display = 'flex';\n            missionOverlay.style.padding = '0';\n            missionOverlay.style.overflowY = 'auto';\n            missionOverlay.innerHTML = `\n                <style>\n                    .briefing-container { width:100%;max-width:480px;padding:28px 20px;box-sizing:border-box;display:flex;flex-direction:column;align-items:center;gap:0;animation:briefingSlideIn 0.7s cubic-bezier(0.16,1,0.3,1) forwards;opacity:0;transform:translateY(30px); }\n                    @keyframes briefingSlideIn { to { opacity:1;transform:translateY(0); } }\n                    .briefing-header { text-align:center;margin-bottom:20px; }\n                    .briefing-mission-badge { display:inline-flex;align-items:center;gap:8px;background:linear-gradient(135deg,rgba(60,120,200,0.15),rgba(40,80,160,0.25));border:1px solid rgba(80,140,220,0.4);border-radius:20px;padding:6px 18px;margin-bottom:12px; }\n                    .briefing-mission-badge span { font-family:'Orbitron',monospace;font-size:10px;color:var(--accent-blue);letter-spacing:3px; }\n                    .briefing-title { font-family:'Orbitron',monospace;font-size:28px;font-weight:900;color:var(--text-primary);margin:0 0 6px;letter-spacing:3px;text-shadow:0 0 40px rgba(80,120,180,0.3),0 2px 4px rgba(0,0,0,0.8); }\n                    .briefing-subtitle { font-family:'Inter',sans-serif;font-size:12px;color:var(--text-muted);letter-spacing:1px;margin:0; }\n                    .briefing-modifier { display:inline-flex;align-items:center;gap:6px;border-radius:16px;padding:5px 14px;margin-top:10px;font-family:'Orbitron',monospace;font-size:9px;letter-spacing:2px; }\n                    .briefing-intel-card { width:100%;background:linear-gradient(160deg,rgba(14,18,28,0.98),rgba(10,14,22,0.99));border:1px solid rgba(80,100,130,0.25);border-radius:16px;padding:0;margin-bottom:16px;overflow:hidden;position:relative;box-shadow:0 16px 48px rgba(0,0,0,0.6),0 0 1px rgba(255,255,255,0.1); }\n                    .briefing-intel-header { display:flex;align-items:center;gap:12px;padding:16px 20px;background:linear-gradient(90deg,rgba(220,53,69,0.08),transparent);border-bottom:1px solid rgba(220,53,69,0.15); }\n                    .briefing-intel-icon { width:38px;height:38px;border-radius:10px;background:linear-gradient(145deg,rgba(220,53,69,0.25),rgba(160,30,40,0.35));border:1px solid rgba(220,53,69,0.4);display:flex;align-items:center;justify-content:center;font-size:18px;box-shadow:0 0 20px rgba(220,53,69,0.2); }\n                    .briefing-intel-title { font-family:'Orbitron',monospace;font-size:12px;color:var(--accent-red);letter-spacing:3px;margin:0; }\n                    .briefing-intel-sub { font-family:'Inter',sans-serif;font-size:9px;color:var(--text-muted);letter-spacing:1px;margin:2px 0 0; }\n                    .briefing-grid { display:grid;grid-template-columns:1fr 1fr;gap:1px;background:rgba(60,75,95,0.2);padding:1px; }\n                    .briefing-grid-cell { background:rgba(10,14,22,0.95);padding:14px 12px;text-align:center;transition:background 0.3s; }\n                    .briefing-grid-cell:hover { background:rgba(20,28,40,0.95); }\n                    .briefing-grid-label { font-family:'Inter',sans-serif;font-size:8px;color:var(--text-muted);letter-spacing:2px;text-transform:uppercase;margin:0 0 6px;display:flex;align-items:center;justify-content:center;gap:4px; }\n                    .briefing-grid-value { font-family:'Orbitron',monospace;font-size:14px;font-weight:700;margin:0;letter-spacing:1px; }\n                    .briefing-target-section { padding:16px 20px;border-top:1px solid rgba(60,75,95,0.2);background:linear-gradient(180deg,rgba(220,53,69,0.04),transparent); }\n                    .briefing-target-row { display:flex;align-items:center;gap:12px;margin-bottom:10px; }\n                    .briefing-target-dot { width:10px;height:10px;border-radius:50%;background:var(--accent-red);box-shadow:0 0 12px var(--accent-red-glow);animation:dotPulse 2s ease-in-out infinite;flex-shrink:0; }\n                    .briefing-target-text { font-family:'Inter',sans-serif;font-size:12px;color:var(--text-secondary);line-height:1.5; }\n                    .briefing-target-pos { display:inline-block;background:rgba(212,165,74,0.12);border:1px solid rgba(212,165,74,0.25);border-radius:8px;padding:3px 10px;font-family:'Orbitron',monospace;font-size:10px;color:var(--accent-gold);letter-spacing:1px;margin-top:6px; }\n                    .briefing-threat-row { display:flex;align-items:center;gap:8px;margin-top:8px;padding-top:10px;border-top:1px solid rgba(60,75,95,0.15); }\n                    .briefing-threat-label { font-family:'Orbitron',monospace;font-size:8px;color:var(--text-muted);letter-spacing:2px; }\n                    .briefing-threat-dots { display:flex;gap:4px; }\n                    .briefing-stats-row { width:100%;display:flex;gap:8px;margin-bottom:16px; }\n                    .briefing-stat-card { flex:1;background:rgba(12,16,24,0.85);backdrop-filter:blur(12px);border:1px solid rgba(60,75,95,0.25);border-radius:12px;padding:14px 8px;text-align:center;box-shadow:0 4px 16px rgba(0,0,0,0.3);transition:transform 0.2s,box-shadow 0.2s; }\n                    .briefing-stat-card:hover { transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,0.4); }\n                    .briefing-stat-icon { font-size:22px;margin-bottom:6px; }\n                    .briefing-stat-value { font-family:'Orbitron',monospace;font-size:18px;font-weight:700;color:var(--text-primary);margin:0 0 2px; }\n                    .briefing-stat-label { font-family:'Inter',sans-serif;font-size:8px;color:var(--text-muted);letter-spacing:2px;text-transform:uppercase;margin:0; }\n                    .briefing-engage-btn { font-family:'Orbitron',monospace;font-size:16px;font-weight:700;letter-spacing:4px;padding:18px 56px;border:none;border-radius:12px;background:linear-gradient(180deg,rgba(34,197,94,0.9) 0%,rgba(22,163,74,0.95) 100%);color:#fff;cursor:pointer;position:relative;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,0.5),0 0 40px rgba(34,197,94,0.25),inset 0 1px 0 rgba(255,255,255,0.2);transition:all 0.2s ease;text-transform:uppercase; }\n                    .briefing-engage-btn:hover { transform:translateY(-3px) scale(1.02);box-shadow:0 12px 40px rgba(0,0,0,0.6),0 0 60px rgba(34,197,94,0.35); }\n                    .briefing-engage-btn:active { transform:translateY(1px) scale(0.97);box-shadow:0 2px 12px rgba(0,0,0,0.5); }\n                    .briefing-engage-btn::before { content:'';position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,0.15),transparent);animation:btnShine 2.5s ease-in-out infinite; }\n                    .briefing-scanline { position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(255,255,255,0.008) 2px,rgba(255,255,255,0.008) 4px);opacity:0.5; }\n                    .briefing-corner { position:absolute;width:12px;height:12px; }\n                    .briefing-corner-tl { top:8px;left:8px;border-top:2px solid rgba(80,140,220,0.4);border-left:2px solid rgba(80,140,220,0.4); }\n                    .briefing-corner-tr { top:8px;right:8px;border-top:2px solid rgba(80,140,220,0.4);border-right:2px solid rgba(80,140,220,0.4); }\n                    .briefing-corner-bl { bottom:8px;left:8px;border-bottom:2px solid rgba(80,140,220,0.4);border-left:2px solid rgba(80,140,220,0.4); }\n                    .briefing-corner-br { bottom:8px;right:8px;border-bottom:2px solid rgba(80,140,220,0.4);border-right:2px solid rgba(80,140,220,0.4); }\n                </style>\n                <div class=\"briefing-container\">\n                    <div class=\"briefing-header\">\n                        <div class=\"briefing-mission-badge\"><span>\u25c6 MISSION ${missionIndex + 1} / 10</span></div>\n                        <h1 class=\"briefing-title\">${mission.name.toUpperCase()}</h1>\n                        <p class=\"briefing-subtitle\">${mission.description}</p>\n                        ${modifierHTML}\n                    </div>\n\n                    <div class=\"briefing-intel-card\">\n                        <div class=\"briefing-scanline\"></div>\n                        <div class=\"briefing-corner briefing-corner-tl\"></div>\n                        <div class=\"briefing-corner briefing-corner-tr\"></div>\n                        <div class=\"briefing-corner briefing-corner-bl\"></div>\n                        <div class=\"briefing-corner briefing-corner-br\"></div>\n                        \n                        <div class=\"briefing-intel-header\">\n                            <div class=\"briefing-intel-icon\">\ud83c\udfaf</div>\n                            <div>\n                                <p class=\"briefing-intel-title\">TACTICAL INTEL</p>\n                                <p class=\"briefing-intel-sub\">CLASSIFIED \u2022 PRIORITY ALPHA</p>\n                            </div>\n                        </div>\n\n                        <div class=\"briefing-grid\">\n                            <div class=\"briefing-grid-cell\">\n                                <p class=\"briefing-grid-label\">\ud83d\udccd Zone</p>\n                                <p class=\"briefing-grid-value\" style=\"color:var(--accent-blue);\">${zone.name}</p>\n                            </div>\n                            <div class=\"briefing-grid-cell\">\n                                <p class=\"briefing-grid-label\">\ud83d\udccf Distance</p>\n                                <p class=\"briefing-grid-value\" style=\"color:var(--text-primary);\">${dist}m</p>\n                            </div>\n                            <div class=\"briefing-grid-cell\">\n                                <p class=\"briefing-grid-label\">\ud83e\udded Bearing</p>\n                                <p class=\"briefing-grid-value\" style=\"color:var(--accent-gold);\">${compassDir} \u00b7 ${compassDeg}\u00b0</p>\n                            </div>\n                            <div class=\"briefing-grid-cell\">\n                                <p class=\"briefing-grid-label\">\u2195 Elevation</p>\n                                <p class=\"briefing-grid-value\" style=\"color:var(--text-primary);\">${pitchHint}</p>\n                            </div>\n                        </div>\n\n                        <div class=\"briefing-target-section\">\n                            <div class=\"briefing-target-row\">\n                                <div class=\"briefing-target-dot\"></div>\n                                <div>\n                                    <p class=\"briefing-target-text\" style=\"margin:0;\"><strong style=\"color:var(--accent-red);\">Target:</strong> Male, red jacket, dark pants</p>\n                                    <p class=\"briefing-target-text\" style=\"margin:4px 0 0;font-size:11px;color:var(--text-muted);\">Red marker above head \u2022 Moving</p>\n                                </div>\n                            </div>\n                            <div class=\"briefing-target-pos\">\ud83d\udccc POSITION: ${posHint || 'SCANNING...'}</div>\n                            <div class=\"briefing-threat-row\">\n                                <span class=\"briefing-threat-label\">THREAT</span>\n                                <div class=\"briefing-threat-dots\">${threatDots}</div>\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"briefing-stats-row\">\n                        <div class=\"briefing-stat-card\">\n                            <div class=\"briefing-stat-icon\">\u23f1</div>\n                            <p class=\"briefing-stat-value\">${mission.timeLimit}s</p>\n                            <p class=\"briefing-stat-label\">Time Limit</p>\n                        </div>\n                        <div class=\"briefing-stat-card\">\n                            <div class=\"briefing-stat-icon\">\ud83d\udc65</div>\n                            <p class=\"briefing-stat-value\">${mission.civilianCount}</p>\n                            <p class=\"briefing-stat-label\">Civilians</p>\n                        </div>\n                        <div class=\"briefing-stat-card\">\n                            <div class=\"briefing-stat-icon\">\ud83d\udca5</div>\n                            <p class=\"briefing-stat-value\">${missesLeft}</p>\n                            <p class=\"briefing-stat-label\">Max Shots</p>\n                        </div>\n                    </div>\n\n                    <button class=\"briefing-engage-btn\" id=\"btn-begin\">ENGAGE</button>\n                </div>\n            `;\n            \n            document.getElementById('btn-begin').addEventListener('click', () => {\n                missionOverlay.style.display = 'none';\n                missionOverlay.style.padding = '';\n                missionOverlay.style.overflowY = '';\n                callback();\n            });\n        }\n\n        function doCountdown(n) {\n            if (n <= 0) {\n                gameState = 'playing';\n                playBGMusic();\n                // Show pointer lock hint for PC users\n                if (!pointerLocked) pointerLockHint.classList.add('show');\n                // Start interactive tutorial for first mission\n                if (missionIndex === 0) {\n                    startTutorialMission();\n                }\n                return;\n            }\n            countdownEl.textContent = n;\n            countdownEl.classList.remove('countdown-show');\n            void countdownEl.offsetWidth;\n            countdownEl.classList.add('countdown-show');\n            setTimeout(() => doCountdown(n - 1), 800);\n        }\n\n        async function endMission(success, failReason) {\n            gameState = 'result';\n            slowMoActive = false;\n            slowMoOverlay.classList.remove('active');\n            pointerLockHint.classList.remove('show');\n            targetDescEl.classList.remove('show');\n            escapeWarning.classList.remove('show');\n            \n            comboCounter.classList.remove('show');\n            modifierBadge.classList.remove('show');\n            windIndicator.classList.remove('show');\n            nightVisionOverlay.classList.remove('active');\n\n            if (bgMusicSource) { try { bgMusicSource.stop(); } catch(e){} bgMusicSource = null; }\n\n            let earnedCoins = 0;\n            let bonusDetails = [];\n            \n            if (success) {\n                // Base reward\n                earnedCoins = 100;\n                bonusDetails.push(['Base Reward', 100]);\n                \n                // Headshot bonus\n                if (gotHeadshot) {\n                    earnedCoins += 50;\n                    bonusDetails.push(['Headshot', 50]);\n                }\n                \n                // Speed bonus\n                if (timeLeft > 20) {\n                    earnedCoins += 50;\n                    bonusDetails.push(['Quick Kill', 50]);\n                } else if (timeLeft > 10) {\n                    earnedCoins += 25;\n                    bonusDetails.push(['Speed Bonus', 25]);\n                }\n                \n                // Accuracy bonus\n                if (shotsFired === shotsHit && shotsFired > 0) {\n                    earnedCoins += 40;\n                    bonusDetails.push(['Perfect Accuracy', 40]);\n                }\n                \n                // Combo bonus\n                if (comboCount > 1) {\n                    const comboBonus = comboCount * 20;\n                    earnedCoins += comboBonus;\n                    bonusDetails.push([`Combo x${comboCount}`, comboBonus]);\n                }\n                \n                // Modifier bonus\n                if (currentModifier && currentModifier !== 'none') {\n                    earnedCoins += 30;\n                    bonusDetails.push(['Challenge Bonus', 30]);\n                }\n                \n                coins += earnedCoins;\n                updateCoinsDisplay();\n\n                // Save progress first\n                saveGameState();\n                lib.log('Mission completed! Earned ' + earnedCoins + ' coins. Total: ' + coins);\n\n                // Submit score to leaderboard\n                try {\n                    const leaderboardResponse = await submitScoreToLeaderboard(coins);\n                    if (leaderboardResponse && leaderboardResponse.success) {\n                        lib.log('Leaderboard updated! Your rank: #' + leaderboardResponse.userRank);\n                    }\n                } catch (e) {\n                    lib.log('Leaderboard submission failed (non-critical): ' + e.message);\n                }\n            }\n\n            missionOverlay.style.display = 'flex';\n            rifleOverlay.style.display = 'none';\n            actionButtons.style.display = 'none';\n\n            if (success) {\n                const bonusHTML = bonusDetails.map(([label, value]) => \n                    `<p style=\"margin:4px 0;font-size:16px;\"><span style=\"color:#9CA3AF;\">${label}</span> <span class=\"value\">+${value}</span></p>`\n                ).join('');\n                \n                missionOverlay.innerHTML = `\n                    <h1 style=\"font-family:'Orbitron',monospace;font-size:36px;color:#10B981;\">TARGET ELIMINATED</h1>\n                    <div class=\"results-stats\" style=\"min-width:280px;\">\n                        <p>Time Left <span class=\"value\">${timeLeft.toFixed(1)}s</span></p>\n                        <p>Accuracy <span class=\"value\">${shotsFired > 0 ? Math.round(shotsHit/shotsFired*100) : 0}%</span></p>\n                        <p>Headshot <span class=\"value\" style=\"color:${gotHeadshot ? '#10B981' : '#EF4444'}\">${gotHeadshot ? '\u2713 YES' : '\u2717 NO'}</span></p>\n                        <div style=\"border-top:1px solid rgba(255,255,255,0.1);margin:12px 0;padding-top:12px;\">\n                            ${bonusHTML}\n                        </div>\n                        <p style=\"font-size:20px;margin-top:12px;border-top:1px solid rgba(255,255,255,0.2);padding-top:12px;\">Total <span class=\"value\" style=\"color:#FFD700;font-size:24px;\">+${earnedCoins}</span></p>\n                    </div>\n                    <div style=\"margin-top:24px;\">\n                        ${missionIndex < missions.length - 1 ? '<button class=\"btn success\" id=\"btn-next\">NEXT MISSION</button>' : '<button class=\"btn success\" id=\"btn-next\">REPLAY</button>'}\n                        <button class=\"btn\" id=\"btn-menu\" style=\"margin-top:8px;\">MENU</button>\n                    </div>\n                `;\n                document.getElementById('btn-next').addEventListener('click', () => {\n                    missionOverlay.style.display = 'none';\n                    if (missionIndex < missions.length - 1) missionIndex++;\n                    else missionIndex = 0;\n                    startMission();\n                    requestPointerLock();\n                });\n                document.getElementById('btn-menu').addEventListener('click', () => {\n                    missionOverlay.style.display = 'none';\n                    showMenu();\n                });\n            } else {\n                missionOverlay.innerHTML = `\n                    <h1 style=\"font-family:'Orbitron',monospace;font-size:36px;color:#EF4444;\">MISSION FAILED</h1>\n                    <p style=\"font-size:22px;color:#F87171;margin:16px 0;\">${failReason || 'Time expired!'}</p>\n                    <div class=\"results-stats\" style=\"background:rgba(239,68,68,0.1);border-color:rgba(239,68,68,0.3);\">\n                        <p>Shots Fired <span class=\"value\">${shotsFired}</span></p>\n                        <p>Time Remaining <span class=\"value\">${Math.max(0, timeLeft).toFixed(1)}s</span></p>\n                    </div>\n                    <div style=\"margin-top:32px;\">\n                        <button class=\"btn danger\" id=\"btn-retry\">RETRY MISSION</button>\n                        <button class=\"btn\" id=\"btn-menu\" style=\"margin-top:8px;\">MENU</button>\n                    </div>\n                `;\n                document.getElementById('btn-retry').addEventListener('click', () => {\n                    missionOverlay.style.display = 'none';\n                    startMission();\n                    requestPointerLock();\n                });\n                document.getElementById('btn-menu').addEventListener('click', () => {\n                    missionOverlay.style.display = 'none';\n                    showMenu();\n                });\n            }\n        }\n\n        // Show game parameters\n        lib.showGameParameters({\n            name: 'QuickScope Settings',\n            params: {\n                'Mission Time': { key: 'settings.missionTime', type: 'slider', min: 20, max: 40, step: 5 },\n                'Civilian Count': { key: 'settings.civilianCount', type: 'slider', min: 3, max: 6, step: 1 },\n                'Target Speed': { key: 'settings.targetSpeed', type: 'slider', min: 0.5, max: 1.5, step: 0.1 },\n                'Miss Limit': { key: 'settings.missLimit', type: 'slider', min: 2, max: 5, step: 1 },\n                'Shake Intensity': { key: 'settings.shakeIntensity', type: 'slider', min: 0, max: 100, step: 10 },\n                'Music Volume': { key: 'settings.musicVolume', type: 'slider', min: 0, max: 100, step: 10 },\n                'SFX Volume': { key: 'settings.sfxVolume', type: 'slider', min: 0, max: 100, step: 10 },\n                'Slow-Mo Duration': { key: 'settings.slowMoDuration', type: 'slider', min: 0.2, max: 0.6, step: 0.1 }\n            }\n        });\n\n        // Edit mode\n        if (mode === 'edit') {\n            gameState = 'edit';\n            missionOverlay.style.display = 'none';\n            rifleOverlay.style.display = 'flex';\n            crosshairEl.style.display = 'block';\n            actionButtons.style.display = 'flex';\n            timerEl.textContent = '30';\n            updateAmmoDisplay();\n            updateStabilityBar();\n\n            // Spawn some NPCs for preview (use default zone 0)\n            currentZone = locationZones[0];\n            createNPC('target', 0, -35, 'walking');\n            createNPC('civilian', -5, -32, 'jogging');\n            createNPC('civilian', 4, -38, 'sleeping');\n            createNPC('civilian', -3, -40, 'talking');\n            createNPC('civilian', -1, -40, 'talking');\n\n            // Allow camera panning in edit mode too\n            canvas.addEventListener('touchstart', (e) => {\n                e.preventDefault();\n                const touch = e.touches[0];\n                input.isTouching = true;\n                input.lastTouchX = touch.clientX;\n                input.lastTouchY = touch.clientY;\n            }, { passive: false });\n\n            canvas.addEventListener('touchmove', (e) => {\n                e.preventDefault();\n                if (!input.isTouching) return;\n                const touch = e.touches[0];\n                const dx = touch.clientX - input.lastTouchX;\n                const dy = touch.clientY - input.lastTouchY;\n                cameraYaw -= dx * sensitivity;\n                cameraPitch -= dy * sensitivity;\n                cameraPitch = Math.max(-0.8, Math.min(0.3, cameraPitch));\n                cameraYaw = Math.max(-1.2, Math.min(1.2, cameraYaw));\n                input.lastTouchX = touch.clientX;\n                input.lastTouchY = touch.clientY;\n            }, { passive: false });\n\n            canvas.addEventListener('touchend', (e) => {\n                e.preventDefault();\n                input.isTouching = false;\n            }, { passive: false });\n\n            canvas.addEventListener('mousedown', (e) => {\n                input.isTouching = true;\n                input.lastTouchX = e.clientX;\n                input.lastTouchY = e.clientY;\n            });\n            canvas.addEventListener('mousemove', (e) => {\n                if (!input.isTouching) return;\n                const dx = e.clientX - input.lastTouchX;\n                const dy = e.clientY - input.lastTouchY;\n                cameraYaw -= dx * sensitivity;\n                cameraPitch -= dy * sensitivity;\n                cameraPitch = Math.max(-0.8, Math.min(0.3, cameraPitch));\n                cameraYaw = Math.max(-1.2, Math.min(1.2, cameraYaw));\n                input.lastTouchX = e.clientX;\n                input.lastTouchY = e.clientY;\n            });\n            canvas.addEventListener('mouseup', () => { input.isTouching = false; });\n        } else {\n            // Play mode - show menu\n            showMenu();\n        }\n\n        // Crosshair sway\n        let swayTime = 0;\n        const stabilityLevel = config.progression.stabilityLevel || 0;\n        const swayAmounts = [6, 4, 2, 1];\n        const swayAmount = swayAmounts[Math.min(stabilityLevel, 3)];\n\n        // Game loop\n        let lastTime = performance.now();\n\n        function gameLoop(timestamp) {\n            let dt = (timestamp - lastTime) / 1000;\n            lastTime = timestamp;\n            dt = Math.min(dt, 0.11);\n\n            // Slow motion\n            let effectiveDt = dt;\n            if (slowMoActive) {\n                slowMoTimer -= dt;\n                effectiveDt = dt * 0.25;\n                if (slowMoTimer <= 0) {\n                    slowMoActive = false;\n                    slowMoOverlay.classList.remove('active');\n                }\n            }\n\n            // Update camera (skip during kill-cam)\n            if (!killCamActive) {\n                // Smooth scope aiming - lerp camera toward scope targets when scoped\n                if (isScoped) {\n                    const scopeLerp = 1 - Math.pow(0.05, dt);\n                    cameraYaw += (scopeTargetYaw - cameraYaw) * scopeLerp;\n                    cameraPitch += (scopeTargetPitch - cameraPitch) * scopeLerp;\n                }\n                updateCamera();\n            }\n\n            // Update kill-cam\n            updateKillCam(dt);\n            \n            // Smooth camera FOV transitions (skip during kill-cam which manages its own FOV)\n            if (!killCamActive && camera.fov !== cameraZoomTarget) {\n                camera.fov += (cameraZoomTarget - camera.fov) * 0.15;\n                if (Math.abs(camera.fov - cameraZoomTarget) < 0.5) camera.fov = cameraZoomTarget;\n                camera.updateProjectionMatrix();\n            }\n\n            \n\n            // Stable crosshair - no sway\n            currentSwayX = 0;\n            currentSwayY = 0;\n            crosshairEl.style.marginLeft = '0px';\n            crosshairEl.style.marginTop = '0px';\n\n            // Screen shake\n            if (shakeTimer > 0) {\n                shakeTimer -= dt;\n                const intensity = (config.settings.shakeIntensity ?? 50) / 100;\n                const shakeX = (Math.random() - 0.5) * 6 * intensity;\n                const shakeY = (Math.random() - 0.5) * 6 * intensity;\n                gameWorld.style.transform = `translate(${shakeX}px, ${shakeY}px)`;\n                if (shakeTimer <= 0) gameWorld.style.transform = '';\n            }\n\n            // Game state updates\n            if (gameState === 'playing') {\n                \n                // Target escape timer (after 20 seconds)\n                targetEscapeTimer += effectiveDt;\n                if (targetEscapeTimer > 18 && !targetEscaping) {\n                    escapeWarning.classList.add('show');\n                }\n                if (targetEscapeTimer > 22 && !targetEscaping) {\n                    targetEscaping = true;\n                    npcs.forEach(npc => {\n                        if (npc.type === 'target' && npc.alive) {\n                            npc.escaping = true;\n                        }\n                    });\n                }\n                \n                // Combo timer\n                if (comboTimer > 0) {\n                    comboTimer -= dt;\n                    if (comboTimer <= 0) {\n                        comboCount = 0;\n                        comboCounter.classList.remove('show');\n                    }\n                }\n            }\n\n            // Move player with joystick or WASD keys (skip during kill-cam)\n            let moveInputX = killCamActive ? 0 : joystickNormX;\n            let moveInputY = killCamActive ? 0 : joystickNormY;\n            if (!killCamActive) {\n                if (keysDown['KeyW'] || keysDown['ArrowUp']) moveInputY = -1;\n                if (keysDown['KeyS'] || keysDown['ArrowDown']) moveInputY = 1;\n                if (keysDown['KeyA'] || keysDown['ArrowLeft']) moveInputX = -1;\n                if (keysDown['KeyD'] || keysDown['ArrowRight']) moveInputX = 1;\n            }\n\n            if ((gameState === 'playing' || gameState === 'edit') && (moveInputX !== 0 || moveInputY !== 0)) {\n                const moveX = moveInputX * playerMoveSpeed * effectiveDt;\n                const moveZ = moveInputY * playerMoveSpeed * effectiveDt;\n                // Apply movement relative to camera facing direction\n                const cosYaw = Math.cos(cameraYaw);\n                const sinYaw = Math.sin(cameraYaw);\n                const worldMoveX = moveX * cosYaw + moveZ * sinYaw;\n                const worldMoveZ = -moveX * sinYaw + moveZ * cosYaw;\n                camera.position.x += worldMoveX;\n                camera.position.z += worldMoveZ;\n                // Clamp to player rooftop bounds\n                camera.position.x = Math.max(playerBounds.minX, Math.min(playerBounds.maxX, camera.position.x));\n                camera.position.z = Math.max(playerBounds.minZ, Math.min(playerBounds.maxZ, camera.position.z));\n            }\n\n            // Update NPCs\n            if (gameState === 'playing' || gameState === 'edit') {\n                updateNPCs(effectiveDt);\n            }\n\n            // Timer\n            if (gameState === 'playing' && !killCamActive) {\n                timeLeft -= effectiveDt;\n                timerEl.textContent = Math.ceil(timeLeft);\n                if (timeLeft <= 5) timerEl.classList.add('warning');\n                else timerEl.classList.remove('warning');\n\n                if (timeLeft <= 0) {\n                    playSound('mission_fail_sound', (config.settings.sfxVolume ?? 70) / 100);\n                    gameState = 'result';\n                    endMission(false, 'Time expired!');\n                }\n            }\n\n            // Animate 3D rifle\n            if (rifleRecoil > 0) {\n                rifleRecoil -= dt * 4;\n                if (rifleRecoil < 0) rifleRecoil = 0;\n            }\n            \n            // Smooth rifle position transitions\n            rifle3D.position.x += (rifleTargetX - rifle3D.position.x) * 0.15;\n            rifle3D.position.y += (rifleTargetY - rifle3D.position.y) * 0.15;\n            \n            // Apply recoil to rifle\n            const recoilAmount = rifleRecoil * 0.08;\n            rifle3D.position.z = -0.35 + recoilAmount;\n            rifle3D.rotation.x = 0.05 + recoilAmount * 0.8;\n            \n            // Subtle idle sway\n            if (gameState === 'playing' || gameState === 'edit') {\n                const idleTime = timestamp * 0.001;\n                rifle3D.rotation.y = Math.sin(idleTime * 0.8) * 0.003;\n                rifle3D.rotation.z = 0.02 + Math.cos(idleTime * 0.6) * 0.002;\n            }\n\n            // Render main scene\n            renderer.render(scene, camera);\n            \n            // Render rifle overlay (hide during kill-cam)\n            if (!killCamActive) {\n                rifleRenderer.render(rifleScene, rifleCamera);\n            }\n            \n            requestAnimationFrame(gameLoop);\n        }\n\n        requestAnimationFrame(gameLoop);\n    }\n    </script>\n</body>\n</html>", "game_config": "{\n  \"settings\": {\n    \"missionTime\": 30,\n    \"civilianCount\": 4,\n    \"targetSpeed\": 1.0,\n    \"missLimit\": 3,\n    \"shakeIntensity\": 50,\n    \"musicVolume\": 50,\n    \"sfxVolume\": 70,\n    \"slowMoDuration\": 0.5\n  },\n  \"missions\": [\n    {\n      \"id\": 1,\n      \"name\": \"Tutorial\",\n      \"description\": \"Learn the basics on the center rooftop\",\n      \"timeLimit\": 45,\n      \"civilianCount\": 2,\n      \"targetMoving\": true\n    },\n    {\n      \"id\": 2,\n      \"name\": \"First Contract\",\n      \"description\": \"Your first real mission\",\n      \"timeLimit\": 40,\n      \"civilianCount\": 3,\n      \"targetMoving\": true\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Crowded Scene\",\n      \"description\": \"More civilians, stay focused\",\n      \"timeLimit\": 38,\n      \"civilianCount\": 4,\n      \"targetMoving\": true\n    },\n    {\n      \"id\": 4,\n      \"name\": \"Rush Hour\",\n      \"description\": \"Heavy foot traffic\",\n      \"timeLimit\": 35,\n      \"civilianCount\": 5,\n      \"targetMoving\": true\n    },\n    {\n      \"id\": 5,\n      \"name\": \"Quick Strike\",\n      \"description\": \"Time is running out\",\n      \"timeLimit\": 32,\n      \"civilianCount\": 5,\n      \"targetMoving\": true\n    },\n    {\n      \"id\": 6,\n      \"name\": \"Pressure Test\",\n      \"description\": \"Less time, more civilians\",\n      \"timeLimit\": 30,\n      \"civilianCount\": 6,\n      \"targetMoving\": true\n    },\n    {\n      \"id\": 7,\n      \"name\": \"High Stakes\",\n      \"description\": \"Expert territory begins\",\n      \"timeLimit\": 28,\n      \"civilianCount\": 6,\n      \"targetMoving\": true\n    },\n    {\n      \"id\": 8,\n      \"name\": \"Elite Contract\",\n      \"description\": \"Maximum crowd density\",\n      \"timeLimit\": 26,\n      \"civilianCount\": 7,\n      \"targetMoving\": true\n    },\n    {\n      \"id\": 9,\n      \"name\": \"Master Sniper\",\n      \"description\": \"Near impossible conditions\",\n      \"timeLimit\": 24,\n      \"civilianCount\": 8,\n      \"targetMoving\": true\n    },\n    {\n      \"id\": 10,\n      \"name\": \"Final Contract\",\n      \"description\": \"The ultimate challenge\",\n      \"timeLimit\": 20,\n      \"civilianCount\": 8,\n      \"targetMoving\": true\n    }\n  ],\n  \"progression\": {\n    \"equippedRifle\": \"sniper_rifle_01\",\n    \"unlockedRifles\": [\n      \"sniper_rifle_01\"\n    ],\n    \"stabilityLevel\": 0\n  },\n  \"shop\": {\n    \"gunSkins\": [\n      {\n        \"id\": \"default\",\n        \"name\": \"Standard Issue\",\n        \"price\": 0,\n        \"color\": \"#2a2a2a\",\n        \"accent\": \"#8B2020\"\n      },\n      {\n        \"id\": \"gold\",\n        \"name\": \"Golden Elite\",\n        \"price\": 500,\n        \"color\": \"#c9a030\",\n        \"accent\": \"#FFD700\"\n      },\n      {\n        \"id\": \"arctic\",\n        \"name\": \"Arctic Camo\",\n        \"price\": 350,\n        \"color\": \"#e8f0f5\",\n        \"accent\": \"#88ccff\"\n      },\n      {\n        \"id\": \"crimson\",\n        \"name\": \"Crimson Shadow\",\n        \"price\": 400,\n        \"color\": \"#4a1515\",\n        \"accent\": \"#ff3333\"\n      },\n      {\n        \"id\": \"neon\",\n        \"name\": \"Neon Viper\",\n        \"price\": 600,\n        \"color\": \"#1a1a2e\",\n        \"accent\": \"#00ff88\"\n      }\n    ],\n    \"bulletEffects\": [\n      {\n        \"id\": \"default\",\n        \"name\": \"Standard\",\n        \"price\": 0,\n        \"trailColor\": null,\n        \"hitColor\": \"#ffffff\"\n      },\n      {\n        \"id\": \"fire\",\n        \"name\": \"Incendiary\",\n        \"price\": 300,\n        \"trailColor\": \"#ff6600\",\n        \"hitColor\": \"#ff4400\"\n      },\n      {\n        \"id\": \"electric\",\n        \"name\": \"Electric\",\n        \"price\": 350,\n        \"trailColor\": \"#00ccff\",\n        \"hitColor\": \"#00ffff\"\n      },\n      {\n        \"id\": \"toxic\",\n        \"name\": \"Toxic\",\n        \"price\": 400,\n        \"trailColor\": \"#44ff00\",\n        \"hitColor\": \"#00ff44\"\n      },\n      {\n        \"id\": \"plasma\",\n        \"name\": \"Plasma\",\n        \"price\": 500,\n        \"trailColor\": \"#ff00ff\",\n        \"hitColor\": \"#ff44ff\"\n      }\n    ],\n    \"unlockedGuns\": [\n      \"default\"\n    ],\n    \"unlockedBullets\": [\n      \"default\"\n    ],\n    \"equippedGun\": \"default\",\n    \"equippedBullet\": \"default\"\n  }\n}", "game_design_document": "## Core Game Loop and Mechanics\n\n**Moment-to-Moment Gameplay:**\nPlayers experience a streamlined first-person sniper experience where they:\n1. Enter a mission and immediately see their rifle and crosshair from first-person perspective\n2. Drag on screen to aim the camera/crosshair at targets in the environment\n3. Tap to fire a single shot with satisfying feedback (recoil animation, screen shake, hit marker)\n4. Wait 1.5 seconds for reload before firing again\n5. Identify and eliminate the main target (marked with subtle red indicator) among 3-6 walking civilian NPCs\n6. Complete the mission within 20-40 seconds\n\n**Core Feedback Loop:**\n- **Shooting Satisfaction:** Small recoil animation, screen shake, and hit markers provide immediate tactile feedback\n- **Headshot Reward:** Landing a headshot triggers 0.4 second slow-motion effect with special sound, creating a \"wow\" moment\n- **Precision Pressure:** Limited ammo (3 misses = fail) and collateral damage consequences (wrong target = fail) create tension\n- **Time Pressure:** 20-40 second mission timer keeps gameplay fast and focused\n\n**Progression:**\n- Earn coins for completing missions\n- Bonus coins for headshots and fast completion times\n- Unlock 5 visually distinct sniper rifles (cosmetic only)\n- Upgrade three core stats: Stability (reduces sway), Zoom (increases magnification), Reload Speed (reduces 1.5s delay)\n\n**Win Conditions:**\n- Successfully eliminate the main target within the time limit\n- Avoid shooting civilians\n- Stay within the 3-miss limit\n\n**Lose Conditions:**\n- Shoot a civilian (instant fail)\n- Miss 3 shots (instant fail)\n- Time runs out before eliminating target\n\n**Replayability Drivers:**\n- 3-star rating system based on time and accuracy\n- Combo bonuses for consecutive headshots\n- Endless mode where targets spawn continuously for high score chasing\n- Global leaderboard for competitive players\n\n## Starting Scope\n\n**First Implementation (Minimum Viable Product):**\n\nThe initial version will include:\n\n1. **Core Shooting Mechanics:**\n   - First-person camera with visible rifle model\n   - Drag-to-aim camera control\n   - Tap-to-shoot with 1.5 second reload\n   - Recoil animation, screen shake, hit markers\n   - 0.4 second slow-motion on headshot with special sound\n\n2. **Single Map:**\n   - City rooftop environment only (desert outpost for later)\n   - Low-poly 3D models with simple textures\n   - Static lighting, no dynamic effects\n\n3. **Basic Mission Structure:**\n   - 3 pre-designed missions on city rooftop\n   - 1 main target with subtle red marker\n   - 3-6 walking civilian NPCs with simple AI\n   - 30 second time limit per mission\n   - Fail conditions: wrong target shot or 3 misses\n\n4. **Simple Progression:**\n   - Coin earning system (base reward + headshot bonus)\n   - 2 unlockable rifles (starter + 1 unlock)\n   - Single upgrade path: Stability only (no zoom or reload upgrades yet)\n\n5. **Minimal Audio:**\n   - Gunshot sound\n   - Headshot sound\n   - Fail sound\n   - Simple background music loop\n\n6. **Basic UI:**\n   - Crosshair\n   - Ammo counter (3 bullets before fail)\n   - Timer\n   - Mission complete/fail screens\n   - Simple main menu\n\n**Excluded from First Implementation (Future Iterations):**\n- Desert outpost map\n- Additional rifles (only 2 total in v1)\n- Zoom and reload speed upgrades\n- 3-star rating system\n- Combo system\n- Endless mode\n- Leaderboards\n- Additional missions beyond the initial 3\n\n**Why This Scope:**\nThis starting scope delivers a complete, playable, and polished core experience that demonstrates all the key mechanics (aiming, shooting, target identification, time pressure) while keeping development focused. Players can experience the satisfying shooting feel, complete missions, and unlock one new rifle. This provides enough content to validate the core gameplay loop before expanding with additional maps, modes, and progression systems.\n\n## Game Perspective\n\n**3d-first-person** - The game uses a first-person 3D perspective where the player sees through the sniper's eyes. The view includes:\n- The player's rifle model visible in the lower portion of the screen\n- A crosshair in the center for aiming\n- The 3D environment (rooftop or outpost) with targets moving through it\n- Camera controlled by dragging to look around and aim\n\nThis perspective is ideal for:\n- Creating immersion and tension as a sniper\n- Providing clear line-of-sight gameplay\n- Making aiming and target identification the primary skill\n- Delivering satisfying shooting feedback directly in the player's view\n\n## Game World and Level Design\n\n**City Rooftop Map (First Implementation):**\n\nThe city rooftop is a small, contained 3D environment designed for quick missions:\n\n**Layout:**\n- Player positioned on one rooftop (sniper position) looking across to an adjacent building rooftop\n- Distance between buildings: approximately 30-50 meters (close enough for clear target visibility)\n- Target rooftop is a rectangular space approximately 20m x 15m\n- Simple low-poly building geometry with flat surfaces and minimal detail\n- A few basic props on target rooftop: air conditioning units, water tanks, ventilation boxes (provide visual interest and occasional cover for targets)\n\n**Lighting:**\n- Static, baked lighting with a daytime setting\n- Soft ambient light to ensure all targets are clearly visible\n- No dynamic shadows or time-of-day changes\n- Simple directional light creating subtle depth without performance cost\n\n**Visual Style:**\n- Low-poly 3D models with flat-shaded or simple textured surfaces\n- Minimal polygon count (buildings are basic boxes with edge details)\n- Clear color separation: buildings in neutral grays/browns, targets in distinct clothing colors\n- No complex materials or reflections\n\n**Target Movement Area:**\n- Targets spawn at edges of the rooftop and walk predetermined paths\n- Paths are simple loops or back-and-forth routes across the rooftop\n- Civilians follow similar patterns but with different starting positions\n- Movement is slow and predictable (walking speed only)\n\n**Camera Position:**\n- Player's sniper position is fixed (no movement, only camera rotation)\n- Elevated slightly above target rooftop for clear sightlines\n- Positioned to give good coverage of the entire target area\n\n**Desert Outpost Map (Future Implementation):**\n- Similar structure: player in one position, targets in a contained area\n- Sandy/desert color palette with simple structures (crates, tents, barriers)\n- Slightly different layout to provide variety while maintaining same gameplay principles\n\n**Technical Considerations:**\n- Total polygon count kept under 10,000 for entire scene\n- All textures are simple, low-resolution (256x256 or 512x512)\n- No transparent materials except for simple alpha cutouts\n- Minimal draw calls through batched geometry\n- Optimized for mobile rendering with WebGL\n\n## Edit Mode Design\n\nEdit mode allows players to customize visual and difficulty aspects of their game without breaking core mechanics.\n\n**Customizable Properties:**\n\n1. **Rifle Appearance:**\n   - Color/skin of currently equipped rifle\n   - Crosshair style (3-4 preset options: dot, cross, circle)\n   - Crosshair color\n\n2. **Mission Parameters:**\n   - Mission time limit (20-40 seconds, slider)\n   - Number of civilian NPCs (3-6, slider)\n   - Target movement speed (0.5x to 1.5x normal, slider)\n\n3. **Visual Settings:**\n   - Background music volume (0-100%)\n   - Sound effects volume (0-100%)\n   - Screen shake intensity (0-100%)\n   - Slow-motion duration on headshot (0.2-0.6 seconds)\n\n4. **Difficulty Modifiers:**\n   - Miss limit before fail (2-5 shots)\n   - Target marker visibility (subtle, medium, obvious)\n\n**Constraints:**\n\n- Rifle appearance changes are cosmetic only (no stat changes in edit mode)\n- Mission time cannot go below 20 seconds (ensures missions remain completable)\n- Civilian count minimum of 3 (ensures challenge exists)\n- Movement speed cannot exceed 1.5x (prevents impossible difficulty)\n- Miss limit cannot be set to 1 (too punishing)\n- All sliders have reasonable min/max values to prevent broken gameplay\n\n**UI Design:**\n- Simple tabbed interface: Visuals | Difficulty | Audio\n- Each setting has a clear label and preview\n- \"Reset to Default\" button for each category\n- Changes apply immediately in edit mode preview\n- \"Play\" button to test changes in actual gameplay\n\n**What's NOT Editable:**\n- Core shooting mechanics (reload time, recoil)\n- Map layouts or geometry\n- AI behavior patterns\n- Progression system (coins, unlocks)\n- Rifle stats (stability, zoom, reload - these are upgrade-based only)\n\nThis edit mode design keeps customization simple and focused on player preference rather than complex game balance, ensuring the core experience remains intact while allowing personalization.\n\n## Control Design\n\n**Genre & Camera:** 3D First-Person Sniper (fixed position, camera-only aiming)\n\n**Mobile Controls (Primary):**\n\n1. **Aiming (Drag):**\n   - Touch and drag anywhere on screen to rotate camera/aim\n   - Direct touch-to-look mapping (no virtual joystick needed)\n   - Smooth camera rotation with configurable sensitivity\n   - No dead zones - immediate response to touch movement\n   - Camera bounds: ~180\u00b0 horizontal, ~60\u00b0 vertical (prevents looking too far up/down)\n\n2. **Shooting (Tap):**\n   - Tap anywhere on screen to fire (while not dragging)\n   - Distinguish between drag (aim) and tap (shoot) by movement threshold\n   - If touch moves less than 10 pixels before release = shoot\n   - If touch moves more than 10 pixels = aim only, no shoot on release\n   - Visual feedback: crosshair briefly expands on tap\n\n3. **Reload:**\n   - Automatic after each shot (1.5 second delay)\n   - No manual reload input needed\n   - Visual indicator: ammo counter shows \"Reloading...\" during delay\n\n**Desktop Controls (Secondary):**\n\n1. **Aiming (Mouse):**\n   - Mouse movement rotates camera (pointer lock enabled during gameplay)\n   - Same camera bounds as mobile\n   - Configurable mouse sensitivity\n   - Pointer lock released when pausing or showing menus\n\n2. **Shooting (Mouse Click):**\n   - Left mouse button to fire\n   - Right mouse button for alternative zoom (if zoom upgrade unlocked)\n\n3. **Keyboard (Optional):**\n   - ESC to pause/menu\n   - R to manually trigger reload (still takes 1.5 seconds)\n   - Space bar as alternative fire button\n\n**Input State Management:**\n\n```javascript\n// Track input state\nconst input = {\n  isTouching: false,\n  touchStartX: 0,\n  touchStartY: 0,\n  lastTouchX: 0,\n  lastTouchY: 0,\n  hasMoved: false,\n  isReloading: false,\n  canShoot: true\n};\n\n// Touch start\ncanvas.addEventListener('touchstart', (e) => {\n  e.preventDefault();\n  const touch = e.touches[0];\n  input.isTouching = true;\n  input.touchStartX = touch.clientX;\n  input.touchStartY = touch.clientY;\n  input.lastTouchX = touch.clientX;\n  input.lastTouchY = touch.clientY;\n  input.hasMoved = false;\n});\n\n// Touch move (aiming)\ncanvas.addEventListener('touchmove', (e) => {\n  e.preventDefault();\n  if (!input.isTouching) return;\n  \n  const touch = e.touches[0];\n  const deltaX = touch.clientX - input.lastTouchX;\n  const deltaY = touch.clientY - input.lastTouchY;\n  \n  // Check if moved beyond threshold\n  const totalMovement = Math.sqrt(\n    Math.pow(touch.clientX - input.touchStartX, 2) +\n    Math.pow(touch.clientY - input.touchStartY, 2)\n  );\n  \n  if (totalMovement > 10) {\n    input.hasMoved = true;\n  }\n  \n  // Rotate camera\n  camera.rotation.y -= deltaX * 0.002; // Horizontal\n  camera.rotation.x -= deltaY * 0.002; // Vertical\n  \n  // Clamp vertical rotation\n  camera.rotation.x = Math.max(-0.5, Math.min(0.5, camera.rotation.x));\n  \n  input.lastTouchX = touch.clientX;\n  input.lastTouchY = touch.clientY;\n});\n\n// Touch end (shooting)\ncanvas.addEventListener('touchend', (e) => {\n  e.preventDefault();\n  \n  // Only shoot if didn't move much and can shoot\n  if (!input.hasMoved && input.canShoot && !input.isReloading) {\n    shoot();\n  }\n  \n  input.isTouching = false;\n  input.hasMoved = false;\n});\n\n// Desktop mouse (pointer lock)\ncanvas.addEventListener('click', () => {\n  canvas.requestPointerLock();\n});\n\ndocument.addEventListener('pointerlockchange', () => {\n  if (document.pointerLockElement === canvas) {\n    // Pointer locked - enable mouse look\n    document.addEventListener('mousemove', onMouseMove);\n    document.addEventListener('mousedown', onMouseDown);\n  } else {\n    // Pointer unlocked - disable mouse look\n    document.removeEventListener('mousemove', onMouseMove);\n    document.removeEventListener('mousedown', onMouseDown);\n  }\n});\n\nfunction onMouseMove(e) {\n  camera.rotation.y -= e.movementX * 0.002;\n  camera.rotation.x -= e.movementY * 0.002;\n  camera.rotation.x = Math.max(-0.5, Math.min(0.5, camera.rotation.x));\n}\n\nfunction onMouseDown(e) {\n  if (e.button === 0 && input.canShoot && !input.isReloading) {\n    shoot();\n  }\n}\n\n// Shooting function\nfunction shoot() {\n  input.canShoot = false;\n  input.isReloading = true;\n  \n  // Fire bullet, check hit, play effects\n  performShot();\n  \n  // Reload delay\n  setTimeout(() => {\n    input.isReloading = false;\n    input.canShoot = true;\n  }, 1500);\n}\n```\n\n**Key Features:**\n- **Simple & Intuitive:** Drag to aim, tap to shoot - no complex button layouts\n- **Responsive:** Immediate camera response to touch/mouse input\n- **Forgiving:** 10-pixel threshold prevents accidental shots while aiming\n- **Automatic Reload:** No manual reload needed, keeps controls minimal\n- **Pointer Lock:** Desktop gets immersive mouse look during gameplay\n- **Clear Feedback:** Visual indicators for reloading state and ammo count\n\n**Performance Considerations:**\n- Prevent default on all touch events to avoid scrolling/zooming\n- Clear input state on window blur\n- Release pointer lock when showing menus/pause screens\n- Throttle camera updates if needed for low-end devices\n\nThis control scheme prioritizes simplicity and responsiveness, perfect for quick mobile sessions while also supporting desktop play.\n\n## Art Style and Visual Theme\n\n**Overall Art Direction:**\nLow-poly 3D with a clean, modern aesthetic. The game uses simple geometric shapes, flat or minimally textured surfaces, and a limited color palette to ensure fast loading, smooth performance, and clear visual hierarchy. The style is functional and professional rather than stylized or cartoonish.\n\n**Visual Style:**\n- **Low-Poly 3D:** All models use minimal polygons (characters: 200-500 polys, buildings: 100-300 polys)\n- **Flat Shading:** Models use flat shading or very simple gradients for depth\n- **Simple Textures:** Solid colors or basic patterns, no complex materials\n- **Clean Lines:** Sharp edges and clear silhouettes for easy target identification\n- **Minimal Effects:** Simple particle effects for muzzle flash and hit impacts only\n\n**Color Palette:**\n\n*City Rooftop Map:*\n- Buildings: Neutral grays (#6B7280, #9CA3AF), concrete whites (#E5E7EB)\n- Accents: Dark metal (#374151), rust/orange details (#F59E0B)\n- Sky: Clear blue gradient (#3B82F6 to #60A5FA)\n- Main Target: Red clothing accent (#EF4444) with neutral base\n- Civilians: Varied muted colors (blues, greens, browns) to distinguish from target\n\n*Desert Outpost Map (Future):*\n- Environment: Sandy yellows (#F59E0B), tan (#D97706), dusty browns (#92400E)\n- Structures: Weathered wood (#78350F), canvas tents (#FEF3C7)\n- Sky: Warm orange-yellow gradient (#FBBF24 to #FCD34D)\n- Target/Civilian colors adjusted for desert visibility\n\n**Asset Sizes & Scale:**\n- Character models: ~1.8m tall in world space\n- Rifle model: Scaled to fill lower 20% of screen in first-person view\n- Buildings: 3-4 story height (~12-15m)\n- Props (AC units, crates): 1-2m scale\n- UI elements: Minimum 96x96 pixels for touch targets\n\n**Viewing Angles:**\n- First-person perspective for player view\n- Character models designed to be viewed from medium distance (30-50m)\n- All models have consistent front-facing orientation\n- Rifle model optimized for first-person view (detailed front, simplified sides)\n\n**Lighting & Mood:**\n- Static baked lighting with single directional light source\n- Soft ambient light ensures no harsh shadows obscure targets\n- Daytime setting with clear visibility\n- No dynamic lighting, fog, or atmospheric effects\n- Simple skybox or gradient background\n\n**Technical Rendering Approach:**\n\n*Generated Assets:*\n- Character models (main target, civilians) - need variety in appearance\n- Rifle models (5 variants) - visually distinct designs\n- Environmental props (AC units, crates, barrels) - add visual interest\n\n*Code-Rendered Elements:*\n- Buildings (simple box geometry with code-generated materials)\n- Skybox/background (CSS gradient or simple WebGL shader)\n- UI elements (crosshair, HUD, menus) - CSS/Canvas\n- Particle effects (muzzle flash, hit sparks) - simple WebGL particles\n- Ground planes (flat colored geometry)\n\n**Consistency Requirements:**\n- All character models share same poly count range and shading style\n- All rifles follow same design language (military/tactical aesthetic)\n- Props maintain consistent level of detail\n- Color palette remains cohesive across all assets\n- Lighting angle consistent across all generated models (top-down-front light)\n\n**Special Visual Requirements:**\n- Transparent backgrounds on all character and prop models (PNG with alpha)\n- Main target needs subtle red indicator (glowing outline or marker above head)\n- Hit markers need high contrast against all backgrounds\n- Crosshair must be visible against varied backgrounds (white with black outline)\n\n**Performance Targets:**\n- Total scene polygon count: <10,000 triangles\n- Texture memory: <16MB total\n- Draw calls: <20 per frame\n- Target 60 FPS on mid-range mobile devices (iPhone 11, Samsung Galaxy S10)\n\nThis art direction ensures the game loads quickly, runs smoothly on low-end devices, and maintains crystal-clear visual communication for gameplay while still looking polished and professional.\n\n## Additional Game-Specific Elements\n\n**Mission Structure & Progression:**\n\n*Mission Types (First Implementation):*\n1. **Tutorial Mission:** Single stationary target, no civilians, 40 seconds\n2. **Mission 1:** Moving target, 3 civilians, 35 seconds\n3. **Mission 2:** Moving target, 5 civilians, 30 seconds\n\n*Mission Flow:*\n- Brief text overlay: \"Eliminate: [Target Description]\" (e.g., \"Red jacket, black pants\")\n- 3-2-1 countdown\n- Mission starts, timer begins\n- Target and civilians spawn and begin walking\n- Player must identify and shoot correct target\n- Mission ends on success/fail with results screen\n\n*Results Screen:*\n- Time taken\n- Accuracy (shots fired vs hits)\n- Headshot bonus (if applicable)\n- Coins earned\n- \"Next Mission\" or \"Retry\" buttons\n\n**Target Identification System:**\n\nThe main target is distinguished by:\n1. **Subtle Red Marker:** Small red diamond or circle floating above target's head (not intrusive)\n2. **Clothing Description:** Brief text at mission start describes target's appearance\n3. **Behavior:** Target may have slightly different walking pattern (optional, not in first implementation)\n\nCivilians are visually similar but lack the red marker and don't match the description exactly.\n\n**Shooting Mechanics Details:**\n\n*Bullet Physics:*\n- Instant hitscan (no bullet travel time in first implementation)\n- Raycast from camera center through crosshair\n- Check collision with character models\n- Headshot detection: hit on upper 20% of character model\n- Body shot: hit on remaining 80% of character model\n\n*Hit Detection:*\n- Headshot: Instant kill, triggers slow-motion, bonus coins\n- Body shot: Instant kill (no health system), standard coins\n- Miss: Counts toward 3-miss limit\n- Wrong target: Instant mission fail\n\n*Visual Feedback Sequence:*\n1. Player taps to shoot\n2. Muzzle flash appears on rifle (0.1s)\n3. Screen shakes (0.2s, subtle)\n4. Recoil animation: rifle kicks up slightly (0.3s)\n5. If hit: Hit marker appears at crosshair (white X, 0.2s)\n6. If headshot: Time slows to 0.3x speed for 0.4s, special sound plays\n7. Target falls/disappears (simple fade out, 0.5s)\n8. Reload delay begins (1.5s, ammo counter shows \"Reloading...\")\n\n**AI Behavior (Simple Implementation):**\n\nEach NPC (target and civilians) has:\n- Spawn point at edge of rooftop\n- Predetermined path (straight line or simple loop)\n- Walking speed: 1.5 m/s\n- Random stop chance: 10% every 2 seconds, stops for 1-2 seconds\n- Random direction change: 5% every 3 seconds, rotates 90-180 degrees\n- No collision detection with other NPCs (can overlap)\n- No reaction to player or gunshots\n\n*Path Examples:*\n- Walk from left edge to right edge, turn around, repeat\n- Walk in rectangular loop around center of rooftop\n- Walk from back to front, stop, walk back\n\n**Coin Economy:**\n\n*Earning Coins:*\n- Mission completion: 100 coins\n- Headshot bonus: +50 coins\n- Fast completion bonus: +25 coins if under 15 seconds\n- Accuracy bonus: +25 coins if no misses\n\n*Spending Coins:*\n- Unlock Rifle 2: 500 coins\n- Unlock Rifle 3: 1000 coins (future)\n- Unlock Rifle 4: 1500 coins (future)\n- Unlock Rifle 5: 2000 coins (future)\n- Stability Upgrade Level 1: 300 coins\n- Stability Upgrade Level 2: 600 coins\n- Stability Upgrade Level 3: 900 coins\n\n**Upgrade System:**\n\n*Stability (First Implementation):*\n- Level 0 (default): Crosshair sway \u00b15 pixels in random direction every 0.5s\n- Level 1: Crosshair sway \u00b13 pixels\n- Level 2: Crosshair sway \u00b11 pixel\n- Level 3: No sway (perfectly stable)\n\n*Future Upgrades:*\n- Zoom: Increases FOV narrowing (closer view of targets)\n- Reload Speed: Reduces 1.5s delay to 1.2s, 1.0s, 0.8s\n\n**UI/HUD Elements:**\n\n*In-Game HUD:*\n- Crosshair (center screen, always visible)\n- Ammo counter (top-right): \"3\" or \"Reloading...\"\n- Timer (top-center): Countdown from mission time\n- Target marker (above target's head): Small red diamond\n- Hit marker (center, on hit): White X, fades quickly\n\n*Main Menu:*\n- \"Play\" button (large, center)\n- \"Endless Mode\" button (locked until 3 missions complete)\n- \"Upgrades\" button (opens upgrade shop)\n- \"Settings\" button (audio, sensitivity)\n- Coin counter (top-right corner)\n\n*Upgrade Shop:*\n- Grid of rifle icons (locked/unlocked)\n- Selected rifle displayed large with stats\n- Upgrade buttons for Stability (with level indicators)\n- \"Equip\" button for unlocked rifles\n- Back button\n\n**Performance Optimization Strategies:**\n\n1. **Object Pooling:** Reuse character models instead of creating/destroying\n2. **LOD System:** Not needed due to small scene and low poly counts\n3. **Culling:** Frustum culling for objects outside camera view\n4. **Batching:** Combine static geometry (buildings) into single draw call\n5. **Texture Atlas:** Pack all textures into single atlas to reduce draw calls\n6. **Simplified Physics:** No physics engine, just simple raycasting\n7. **Audio Management:** Preload all sounds, limit concurrent sounds to 3\n\n**Accessibility Considerations:**\n\n- Colorblind mode: Change red target marker to yellow or blue (future)\n- Adjustable crosshair size (future)\n- Adjustable UI scale (future)\n- Subtitles for any audio cues (not needed in first implementation)\n\nThis comprehensive design ensures all team members understand the complete game vision while maintaining focus on the lightweight, fast-loading, satisfying shooting experience that defines QuickScope: Sniper Contracts (Lite).", "persistent_storage_config": {"leaderboard": {"sort_ascending": false}}, "game_metadata": {"game_id": "01KT448BSNVCPTQWBHP47HME0F", "title": "QuickScope: Sniper Contracts (Lite)", "description": "**Description:** Drag to aim, tap to shoot. Eliminate targets in tense 20-40 second missions with satisfying recoil and slow-mo headshots!", "thumbnail_url": "https://dzmv1mxivz48k.cloudfront.net/images/38d1c330-8001-705e-1320-9c0ce9ba8e94/1780866532427_01KTHYNH2B3R9GKNRF19DJ93FF.b_Creat_a_military_rel (2) (1).gif", "created_at": 1780402969, "updated_at": 1780866553416.277, "art_style": null}, "task_plan": null}