{"TotalCount":95,"Files":[{"Ident":"playnplay.chain_reaction","Path":".obj/__compiler_extra.cs","FileName":"__compiler_extra.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"global using static Sandbox.Internal.GlobalGameNamespace;\r\nglobal using Microsoft.AspNetCore.Components;\r\nglobal using Microsoft.AspNetCore.Components.Rendering;\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonTitle\u0022, \u0022Chain Reaction\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonIdent\u0022, \u0022chain_reaction\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022OrgIdent\u0022, \u0022playnplay\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022Ident\u0022, \u0022playnplay.chain_reaction\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineVersion\u0022, \u002228\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineMinorVersion\u0022, \u00221\u0022 )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \u0022.NETCoreApp,Version=v9.0\u0022, FrameworkDisplayName = \u0022.NET 9.0\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022CompileTime\u0022, \u00222026-08-25T16:50:49.1757035Z\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\u00220.0.120.0\u0022)]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\u00220.0.120.0\u0022)]"},{"Ident":"playnplay.chain_reaction","Path":"Game/2D/MusicManager.cs","FileName":"MusicManager.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"using Sandbox;\r\n\r\npublic sealed class MusicManager : Component\r\n{\r\n    public static MusicManager Instance { get; private set; }\r\n\r\n    SoundHandle _menuMusic;\r\n    SoundHandle _windMusic;\r\n    SoundHandle _gameMusic;\r\n\r\n    // Tracked explicitly rather than inferred from SoundHandle.IsValid(), because\r\n    // a handle stopped with a fade is not reliably invalid straight away \u2014 and\r\n    // guessing wrong here means the menu comes back silent.\r\n    bool _menuPlaying;\r\n\r\n    protected override void OnStart()\r\n    {\r\n        Instance = this;\r\n\r\n        // Subscribe to game events\r\n        if (ChainReactionGame.Instance != null)\r\n        {\r\n            ChainReactionGame.Instance.OnGameStarted  \u002B= OnGameStarted;\r\n            ChainReactionGame.Instance.OnGameReturned \u002B= OnGameReturned;\r\n        }\r\n\r\n        StartMenuLoops(.25f);\r\n    }\r\n\r\n    void StartMenuLoops(float volume)\r\n    {\r\n        _menuMusic   = Sound.Play(\u0022phonk_menu_music\u0022, volume);\r\n        _windMusic   = Sound.Play(\u0022wind_blowing_loop\u0022, volume);\r\n        _menuPlaying = true;\r\n    }\r\n\r\n    void StopMenuLoops(float fade)\r\n    {\r\n        if (_menuMusic.IsValid()) _menuMusic.Stop(fade);\r\n        if (_windMusic.IsValid()) _windMusic.Stop(fade);\r\n        _menuPlaying = false;\r\n    }\r\n\r\n    void OnGameStarted()\r\n    {\r\n        // Reactor mode is meant to be left running for a long time \u2014 the combat\r\n        // track would grate after ten minutes, so it keeps the calmer menu loop.\r\n        if (ChainReactionGame.Instance?.Mode == GameMode.Idle)\r\n        {\r\n            if (_gameMusic.IsValid()) _gameMusic.Stop(.5f);\r\n\r\n            if (!_menuPlaying) StartMenuLoops(.18f);\r\n            else if (_menuMusic.IsValid()) _menuMusic.Volume = .18f;\r\n\r\n            return;\r\n        }\r\n\r\n        StopMenuLoops(.5f);\r\n\r\n        // Only start game music if not already playing\r\n        if (!_gameMusic.IsValid())\r\n            _gameMusic = Sound.Play(\u0022abydos_game\u0022);\r\n    }\r\n\r\n    public void StopMenuMusic() =\u003E StopMenuLoops(0f);\r\n\r\n    public void PlayMenuMusic() =\u003E StartMenuLoops(.25f);\r\n\r\n    void OnGameReturned()\r\n    {\r\n        // Reactor mode never starts the combat track, so this handle can still be\r\n        // default-constructed here \u2014 calling Stop() on that throws.\r\n        if (_gameMusic.IsValid()) _gameMusic.Stop(.5f);\r\n\r\n        // Coming back from Reactor mode the menu loop never stopped \u2014 restore\r\n        // its volume instead of stacking a second copy on top of it.\r\n        if (_menuPlaying)\r\n        {\r\n            if (_menuMusic.IsValid()) _menuMusic.Volume = .3f;\r\n            return;\r\n        }\r\n\r\n        StartMenuLoops(.3f);\r\n    }\r\n}\r\n"},{"Ident":"playnplay.chain_reaction","Path":"Game/2D/UI/MainMenuTutorial.razor","FileName":"MainMenuTutorial.razor","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"@using System;\n@using Sandbox;\n@using Sandbox.Game.Menu\n@using Sandbox.UI;\n@inherits PanelComponent\n@namespace Sandbox\n\n\u003Croot\u003E\n    @if(_active)\n    {\n\t\t@* \u2500\u2500 Debug card \u2500\u2500 *@\n\t\t@if(_debugMode)\n\t\t{\n\t\t\t\u003Cdiv class=\u0022tut-debug-card\u0022\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-title\u0022\u003EMM TUT \u2014 Step @_step\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\n\t\t\t\t\t\u003Cspan\u003EBL: @_debugBL.ToString(\u0022F1\u0022)%\u003C/span\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBL -= 1f; StateHasChanged(); })\u003E\u25C0\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBL \u002B= 1f; StateHasChanged(); })\u003E\u25B6\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\n\t\t\t\t\t\u003Cspan\u003EBY: @_debugBY.ToString(\u0022F0\u0022)px\u003C/span\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBY -= 10f; StateHasChanged(); })\u003E\u25B2\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBY \u002B= 10f; StateHasChanged(); })\u003E\u25BC\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\n\t\t\t\t\t\u003Cspan\u003EBR: @_debugBR.ToString(\u0022F1\u0022)%\u003C/span\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBR -= 1f; StateHasChanged(); })\u003E\u25C0\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBR \u002B= 1f; StateHasChanged(); })\u003E\u25B6\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\n\t\t\t\t\t\u003Cspan\u003EHX: @_debugHX.ToString(\u0022F1\u0022)%\u003C/span\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHX -= 1f; StateHasChanged(); })\u003E\u25C0\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHX \u002B= 1f; StateHasChanged(); })\u003E\u25B6\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\n\t\t\t\t\t\u003Cspan\u003EHY: @_debugHY.ToString(\u0022F0\u0022)px\u003C/span\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHY -= 10f; StateHasChanged(); })\u003E\u25B2\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHY \u002B= 10f; StateHasChanged(); })\u003E\u25BC\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\n\t\t\t\t\t\u003Cspan\u003EHW: @_debugHW.ToString(\u0022F0\u0022)px\u003C/span\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHW -= 10f; StateHasChanged(); })\u003E\u25C0\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHW \u002B= 10f; StateHasChanged(); })\u003E\u25B6\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\n\t\t\t\t\t\u003Cspan\u003EHH: @_debugHH.ToString(\u0022F0\u0022)px\u003C/span\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHH -= 10f; StateHasChanged(); })\u003E\u25B2\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHH \u002B= 10f; StateHasChanged(); })\u003E\u25BC\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cbutton class=\u0022tut-debug-log\u0022 onclick=@LogCurrentValues\u003E\uD83D\uDCCB Log\u003C/button\u003E\n\t\t\t\u003C/div\u003E\n\t\t}\n\n\t\t@* \u2500\u2500 Dim layer \u2500\u2500 *@\n\t\t\u003Cdiv class=\u0022tut-dim\u0022\u003E\u003C/div\u003E\n\n\t\t@* \u2500\u2500 Highlight \u2500\u2500 *@\n\t\t@if(_step \u003C _steps.Length \u0026\u0026 _steps[_step].HW \u003E 0)\n\t\t{\n\t\t\tvar s = _steps[_step];\n\t\t\tfloat hx = _debugHX != 0f ? _debugHX : s.HX;\n\t\t\tfloat hy = _debugHY != 0f ? _debugHY : s.HY;\n\t\t\tfloat hw = _debugHW != 0f ? _debugHW : s.HW;\n\t\t\tfloat hh = _debugHH != 0f ? _debugHH : s.HH;\n\t\t\t\u003Cdiv class=\u0022tut-highlight\u0022\n\t\t\t\t\tstyle=\u0022left:@hx.ToString(\u0022F1\u0022)%;top:@hy.ToString(\u0022F0\u0022)px;width:@hw.ToString(\u0022F0\u0022)px;height:@hh.ToString(\u0022F0\u0022)px;\u0022\u003E\n\t\t\t\u003C/div\u003E\n\t\t}\n\n\t\t@* \u2500\u2500 Instruction box \u2500\u2500 *@\n\t\t@if(_step \u003C _steps.Length)\n\t\t{\n\t\t\tvar s = _steps[_step];\n\t\t\tfloat bl = _debugBL != 0f ? _debugBL : s.BL;\n\t\t\tfloat br = _debugBR != 0f ? _debugBR : s.BR;\n\t\t\tfloat by = _debugBY != 0f ? _debugBY : s.BY;\n\n\t\t\tfloat boxWidthPct = s.BW \u003E 0f ? (s.BW / 1920f * 100f) : (380f / 1920f * 100f);\n\n\t\t\tstring posStyle = br != 0f\n\t\t\t\t? $\u0022left:{(100f - br - boxWidthPct).ToString(\u0022F1\u0022)}%;\u0022\n\t\t\t\t: $\u0022left:{bl.ToString(\u0022F1\u0022)}%;\u0022;\n\t\t\tstring boxStyle = $\u0022{posStyle}top:{by.ToString(\u0022F0\u0022)}px;{(s.BW \u003E 0f ? $\u0022width:{s.BW.ToString(\u0022F0\u0022)}px;\u0022 : \u0022\u0022)}\u0022;\n\n\t\t\t\u003Cdiv class=\u0022tut-box\u0022 style=\u0022@boxStyle\u0022\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-step-count\u0022\u003E@(_step \u002B 1) / @_steps.Length\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-title\u0022\u003E@s.Title\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022tut-text\u0022\u003E@s.Text\u003C/div\u003E\n\n\t\t\t\t@* Action hint \u2014 shown when player must act *@\n\t\t\t\t@if(s.Trigger == MMTutTrigger.HeartPurchased)\n\t\t\t\t{\n\t\t\t\t\t\u003Cdiv class=\u0022tut-action-hint\u0022\u003E\uD83D\uDC46 @s.ActionHint\u003C/div\u003E\n\t\t\t\t}\n\n\t\t\t\t@* Next button \u2014 shown on Any steps *@\n\t\t\t\t@if(s.Trigger == MMTutTrigger.Any)\n\t\t\t\t{\n\t\t\t\t\t\u003Cbutton class=\u0022tut-btn-next\u0022 onclick=@NextStep\u003EGot it \u2192\u003C/button\u003E\n\t\t\t\t}\n\n\t\t\t\t@* Final step \u2014 Play button *@\n\t\t\t\t@if(s.Trigger == MMTutTrigger.Done)\n\t\t\t\t{\n\t\t\t\t\t\u003Cbutton class=\u0022tut-btn-next\u0022 onclick=@OnPlayAndComplete\u003E\u25B6 Let\u0027s play!\u003C/button\u003E\n\t\t\t\t}\n\t\t\t\u003C/div\u003E\n\t\t}\n    }\n\u003C/root\u003E\n\n@code {\n    public static MainMenuTutorial Instance { get; private set; }\n    public bool IsActive =\u003E _active;\n\n    bool _active = false;\n\tpublic int Step =\u003E _step;\n    int  _step   = 0;\n\n    // \u2500\u2500 Debug mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 //\n    const bool _debugMode = false; // set false before release\n    float _debugBL = 0f;\n    float _debugBR = 0f;\n    float _debugBY = 0f;\n    float _debugHX = 0f;\n    float _debugHY = 0f;\n    float _debugHW = 0f;\n    float _debugHH = 0f;\n\n    void LogCurrentValues()\n    {\n        Log.Info($\u0022// MM Step {_step} \u2014 {_steps[_step].Title}\u0022);\n        Log.Info($\u0022BL={_debugBL:F1}f, BY={_debugBY:F0}f, BR={_debugBR:F1}f,\u0022);\n        Log.Info($\u0022HX={_debugHX:F1}f, HY={_debugHY:F0}f, HW={_debugHW:F0}f, HH={_debugHH:F0}f\u0022);\n    }\n\n    // \u2500\u2500 Trigger types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 //\n    public enum MMTutTrigger\n    {\n        Any,           // advances via Got it button\n        HeartPurchased, // advances when player buys Heart consumable\n        Done,          // final step \u2014 shows Play button\n    }\n\n    // \u2500\u2500 Step data \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 //\n    public struct MMTutStep\n    {\n        public string Title;\n        public string Text;\n        public string ActionHint;\n        public MMTutTrigger Trigger;\n        public float HX, HY, HW, HH; // highlight\n        public float BL, BR, BY, BW;  // box position\n    }\n\n    // \u2500\u2500 Steps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 //\n    // Positions are placeholder \u2014 tune with debug card in-game\n    static readonly MMTutStep[] _steps = new MMTutStep[]\n    {\n        // Step 1 \u2014 Welcome\n        new MMTutStep {\n            Title   = \u0022Welcome to Chain Reaction!\u0022,\n            Text    = \u0022Here\u0027s a quick tour of how the game works. It\u0027ll take 30 seconds.\u0022,\n            Trigger = MMTutTrigger.Any,\n            HW = 0,\n            BL = 47f, BY = 370f\n        },\n\n        // Step 2 \u2014 Play button\n        new MMTutStep {\n            Title   = \u0022Play runs to earn \uD83D\uDCA0 Fragments\u0022,\n            Text    = \u0022Fragments are the game\u0027s currency. The further you go each run, the more you earn.\u0022,\n            Trigger = MMTutTrigger.Any,\n            HW = 0,\n            BL = 47f, BY = 370f\n        },\n\n        // Step 3 \u2014 Shop \u2192 Modifiers\n        new MMTutStep {\n            Title   = \u0022The Shop \u2014 Modifiers \uD83C\uDFB2\u0022,\n            Text    = \u0022Spend fragments to unlock Modifiers. These permanently strengthen every run you play. Unlock once, keep forever.\u0022,\n            Trigger = MMTutTrigger.Any,\n            HX = 40f, HY = 200f, HW = 110f, HH = 40f,\n            BR = 61f, BY = 120f\n        },\n\n        // Step 4 \u2014 Consumables explanation\n        new MMTutStep {\n            Title   = \u0022Consumables \uD83C\uDF81\u0022,\n            Text    = \u0022Consumables are single-use items you bring into a run. Buy them once, use them at any time during a run. A bar will appear in-game when you own one.\u0022,\n            Trigger = MMTutTrigger.Any,\n            HX = 46f, HY = 200f, HW = 130f, HH = 40f,\n            BR = 61f, BY = 110\n        },\n\n        // Step 5 \u2014 Buy Heart (action step)\n        new MMTutStep {\n            Title   = \u0022Get yourself a Heart \u2764\uFE0F\u0022,\n            Text    = \u0022We\u0027ve added 100 \uD83D\uDCA0 to your balance. Buy the Heart consumable \u2014 it gives you an extra life mid-run when you need it most.\u0022,\n            ActionHint = \u0022Click the buy button on the Heart card\u0022,\n            Trigger = MMTutTrigger.HeartPurchased,\n            HX = 65f, HY = 860f, HW = 80f, HH = 70f,\n            BR = 7f, BY = 800f\n        },\n\n        // Step 6 \u2014 Pet tab\n        new MMTutStep {\n            Title = \u0022Your secret package \uD83D\uDCE6\u0022,\n            Text = \u0022Play 3 runs to unlock what\u0027s inside. This section is for cosmetic skins \u2014 coming soon.\u0022,\n    \t\tTrigger = MMTutTrigger.Any,\n            HX = 53f, HY = 360f, HW = 70f, HH = 40f,\n            BR = 50f, BY = 340f\n        },\n\n        // Step 7 \u2014 Bombs\n        new MMTutStep {\n            Title   = \u0022Unlock new Bombs \uD83D\uDCA3\u0022,\n            Text    = \u0022Different bomb types open up new chain reaction strategies. Mix and match to find your playstyle.\u0022,\n            Trigger = MMTutTrigger.Any,\n            HX = 57f, HY = 220f, HW = 90f, HH = 40f,\n            BR = 5f, BY = 130f\n        },\n\n        // Step 8 \u2014 Leaderboard\n        new MMTutStep {\n            Title   = \u0022Compete Globally \uD83C\uDFC5\u0022,\n            Text    = \u0022The leaderboard tracks your best score, chain reactions, and pet level. How far can you go?\u0022,\n            Trigger = MMTutTrigger.Any,\n            HX = 75f, HY = 400f, HW = 10f, HH = 10f,\n            BR = 5f, BY = 160f\n        },\n\n        // Step 9 \u2014 Done\n        new MMTutStep {\n            Title   = \u0022You\u0027re ready. \uD83D\uDCA5\u0022,\n            Text    = \u0022Play runs \u2192 earn fragments \u2192 unlock things \u2192 become stronger. Good luck!\u0022,\n            Trigger = MMTutTrigger.Done,\n\t\t\tBR = 20f, BY = 370f\n        },\n    };\n\n    // \u2500\u2500 Lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 //\n    protected override void OnStart()\n    {\n        Instance = this;\n        _active  = false;\n    }\n\n    public void Activate(PlayerSave save)\n    {\n        //var save = ChainReactionGame.Save ?? PlayerSave.Load();\n        if (save?.MenuTutorialCompleted == true) return;\n\n        _step   = save?.MenuTutorialStep ?? 0;\n        _active = true;\n        ResetDebug();\n\n\t\t// If resuming at step 4 (Buy Heart) but heart already purchased, skip it\n\t\tif (_step == 4 \u0026\u0026 (save?.GetConsumableCount(ConsumableId.Heart) ?? 0) \u003E 0)\n\t\t\t_step = 5;\n\n        // Step 3 requires shop/mods to be visible \u2014 navigate there\n        ApplySideEffect(_step);\n        StateHasChanged();\n    }\n\n    // \u2500\u2500 Called from MainMenu when Heart is bought \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 //\n    public void OnHeartPurchased()\n    {\n        if (!_active || _step \u003E= _steps.Length) return;\n        if (_steps[_step].Trigger != MMTutTrigger.HeartPurchased) return;\n        NextStep();\n    }\n\n    // \u2500\u2500 Step advancement \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 //\n  \tvoid NextStep() \n\t{\n\t\t_step\u002B\u002B;\n\t\tResetDebug();\n\n\t\tif (_step \u003E= _steps.Length)\n\t\t{\n\t\t\tComplete();\n\t\t\treturn;\n\t\t}\n\n\t\t// Persist current step so we can resume after crash - save tuto step\n\t\tvar save = PlayerSave.Load();\n\t\tif (save != null) \n\t\t{ \n\t\t\tsave.MenuTutorialStep = _step; \n\t\t\tsave.Save(); \n\t\t}\n\n\t\tApplySideEffect(_step);\n\t\tSound.Play(\u0022UI_Button\u0022);\n\t\tStateHasChanged();\n}\n\n    // \u2500\u2500 Side effects \u2014 navigate tabs automatically \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 //\n    void ApplySideEffect(int step)\n    {\n        var mm = MainMenu.Instance;\n        if (mm == null) return;\n\n        switch(step)\n        {\n            case 2: // Show shop modifiers\n                mm.SetTabPublic(\u0022shop\u0022);\n                mm.SetShopTabPublic(\u0022mods\u0022);\n                // Grant frags on step before heart purchase\n                break;\n\n            case 3: // Show consumables\n                mm.SetTabPublic(\u0022shop\u0022);\n                mm.SetShopTabPublic(\u0022consumables\u0022);\n                break;\n\n\t\t    case 4: // Buy Heart \u2014 shop consumables \u002B grant frags if not already granted\n            var save = ChainReactionGame.Save ?? PlayerSave.Load();\n            if (save != null \u0026\u0026 !save.TutorialHeartFragsGranted)\n            {\n                save.TotalFragments \u002B= 100;\n                save.TutorialHeartFragsGranted = true;\n                save.Save();\n            }\n            mm.SetTabPublic(\u0022shop\u0022);\n            mm.SetShopTabPublic(\u0022consumables\u0022);\n            mm.ReloadSave();\n            break;\n\t\t\n            case 5: // Pet tab\n\t\t\t    mm.SetTabPublic(\u0022shop\u0022);\n                mm.SetShopTabPublic(\u0022pets\u0022);\n                break;\n\n            case 6: // Bombs\n                mm.SetTabPublic(\u0022shop\u0022);\n                mm.SetShopTabPublic(\u0022bombs\u0022);\n                break;\n\n            case 7: // Leaderboard\n                mm.SetTabPublic(\u0022lb\u0022);\n                break;\n\t\t\tcase 8: // Done \u2014 no navigation needed\n            break;\n        }\n    }\n\n    void Skip() =\u003E Complete();\n\n    void OnPlayAndComplete()\n    {\n        Complete();\n        MainMenu.Instance?.StartGamePublic();\n    }\n\n    void Complete()\n    {\n        _active = false;\n\n\t\t// Get save from MainMenu directly\n\t\tvar mm = MainMenu.Instance;\n\n\t\t// just use ChainReactionGame.Save or load fresh\n\t\tvar save = PlayerSave.Load();\n\n\t\tif (save != null)\n\t\t{\n\t\t\tsave.MenuTutorialCompleted = true;\n\t\t\tsave.MenuTutorialStep = 0;\n\t\t\tsave.Save();\n\t\t}\n\t\tStateHasChanged();\n    }\n\n    void ResetDebug()\n    {\n        _debugBL = 0f; _debugBR = 0f; _debugBY = 0f;\n        _debugHX = 0f; _debugHY = 0f; _debugHW = 0f; _debugHH = 0f;\n    }\n\n\tpublic void ForceReset()\n\t{\n\t\t_active = false;\n\t\t_step   = 0;\n\t\tResetDebug();\n\t\tStateHasChanged();\n\t}\n\n    protected override int BuildHash() =\u003E\n        System.HashCode.Combine(_active, _step, _debugBL, _debugBY, _debugHX, _debugHY, _debugHW, _debugHH);\n}"},{"Ident":"playnplay.chain_reaction","Path":"Game/Idle/UI/IdleHud.razor","FileName":"IdleHud.razor","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"@using Sandbox;\n@using Sandbox.UI;\n@using System;\n@using System.Linq;\n@using System.Collections.Generic;\n@inherits PanelComponent\n@namespace Sandbox\n\n@*\n\tIMPORTANT \u2014 s\u0026box text rendering quirk.\n\n\tRazor compiles a literal containing non-ASCII characters (emoji, \u00D7, \u00B7, \u2014)\n\tinto AddMarkupContent, and plain-ASCII literals into AddContent. The panel\n\trenderer hoists every AddMarkupContent node ahead of every AddContent node,\n\tso \u0022@A \u00D7 @B\u0022 comes out as \u0022\u00D7AB\u0022.\n\n\tThe rule this file follows: an element holds EITHER pure literal text OR a\n\tsingle @expression. Anything composed goes through a helper below.\n*@\n\n\u003Croot\u003E\n@if ( Visible )\n{\n\t\u003Cdiv class=\u0022idle-root\u0022\u003E\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 AMBIENT BACKDROP \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *@\n\t\t\u003Cdiv class=\u0022idle-bg\u0022\u003E\u003C/div\u003E\n\t\t\u003Cdiv class=\u0022idle-bg-glow @GlowClass()\u0022\u003E\u003C/div\u003E\n\n\t\t@for ( int i = 0; i \u003C 12; i\u002B\u002B )\n\t\t{\n\t\t\tfloat pLeft = (i * 8.3f) % 100f;\n\t\t\tfloat pDur  = 10f \u002B (i * 0.9f) % 8f;\n\t\t\tfloat pDel  = (i * 1.4f) % 12f;\n\t\t\tint   pSz   = (i % 4 == 0) ? 4 : 2;\n\t\t\tstring pCol = (i % 3 == 0) ? \u0022rgba(94,234,212,0.45)\u0022 : \u0022rgba(96,165,250,0.30)\u0022;\n\t\t\t\u003Cdiv class=\u0022idle-particle\u0022\n\t\t\t\t style=\u0022left:@pLeft.ToString(\u0022F1\u0022)%;width:@(pSz)px;height:@(pSz)px;background-color:@pCol;animation-duration:@pDur.ToString(\u0022F1\u0022)s;animation-delay:@pDel.ToString(\u0022F1\u0022)s;\u0022\u003E\u003C/div\u003E\n\t\t}\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 TOP BAR \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *@\n\t\t\u003Cdiv class=\u0022idle-top @TutDim(\u0022top\u0022)\u0022\u003E\n\t\t\t\u003Cbutton class=\u0022idle-back\u0022 onclick=@OnBack\u003E\u2302\u003C/button\u003E\n\t\t\t\u003Cbutton class=\u0022idle-help @HelpClass()\u0022 onclick=@OnStartTutorial\u003E?\u003C/button\u003E\n\n\t\t\t\u003Cdiv class=\u0022idle-brand\u0022\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-brand-title\u0022\u003ETHE REACTOR\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-brand-sub\u0022\u003Echain reactions, forever\u003C/div\u003E\n\t\t\t\u003C/div\u003E\n\n\t\t\t\u003Cdiv class=\u0022idle-currencies\u0022\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-cur cores\u0022\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-icon\u0022\u003E\u269B\uFE0F\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-val\u0022\u003E@CoresLabel()\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-lbl\u0022\u003ECores\u003C/span\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-cur rate\u0022\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-icon\u0022\u003E\uD83D\uDCC8\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-val\u0022\u003E@RateLabel()\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-lbl\u0022\u003Eper sec\u003C/span\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-cur flux\u0022\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-icon\u0022\u003E\uD83D\uDD37\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-val\u0022\u003E@FluxLabel()\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-lbl\u0022\u003EFlux\u003C/span\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-cur frag\u0022\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-icon\u0022\u003E\uD83D\uDCA0\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-val\u0022\u003E@FragLabel()\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-cur-lbl\u0022\u003EFrags\u003C/span\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\u003C/div\u003E\n\t\t\u003C/div\u003E\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 BODY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *@\n\t\t@* Hidden behind a modal \u2014 the panel renderer draws emoji glyphs above\n\t\t   overlay panels, so the board has to actually leave the tree. *@\n\t\t@if ( !ModalOpen )\n\t\t{\n\t\t\u003Cdiv class=\u0022idle-body\u0022\u003E\n\n\t\t\t@* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 LEFT COLUMN \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *@\n\t\t\t\u003Cdiv class=\u0022idle-col idle-left @TutDim(\u0022left\u0022)\u0022\u003E\n\n\t\t\t\t@* \u2500\u2500 Daily contracts \u2500\u2500\n\t\t\t\t   First card in the column on purpose: it is the only part of\n\t\t\t\t   the mode with a deadline, so it has to be the first thing a\n\t\t\t\t   returning player\u0027s eye lands on. *@\n\t\t\t\t\u003Cdiv class=\u0022idle-card idle-card-contracts @ContractsCardClass()\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card-hd row\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-card-hd-txt\u0022\u003EDAILY CONTRACTS\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-card-hd-side\u0022\u003E@ContractResetLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\t@for ( int ci = 0; ci \u003C IdleContracts.DailyCount; ci\u002B\u002B )\n\t\t\t\t\t{\n\t\t\t\t\t\tint slot = ci;\n\t\t\t\t\t\tvar contract = IdleContracts.At( S, slot );\n\t\t\t\t\t\tif ( contract != null )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-contract @ContractClass( slot )\u0022 onclick=@(() =\u003E OnClaimContract( slot ))\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-contract-icon\u0022\u003E@ContractIcon( slot )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-contract-body\u0022\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-contract-top\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-contract-name\u0022\u003E@contract.Name\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-contract-reward\u0022\u003E@ContractRewardLabel( slot )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-contract-desc\u0022\u003E@ContractDescLabel( slot )\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-contract-track\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-contract-fill\u0022 style=\u0022width:@Pct( IdleContracts.Fraction( S, slot ) )%;\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-contract-prog\u0022\u003E@ContractProgLabel( slot )\u003C/div\u003E\n\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card-note\u0022\u003E@ContractFootLabel()\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-card idle-card-output\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card-hd\u0022\u003EOUTPUT\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-stat-row\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-lbl\u0022\u003ECores / sec\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-val hot\u0022\u003E@RateLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-stat-row\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-lbl\u0022\u003EGlobal multiplier\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-val\u0022\u003E@GlobalMultLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-stat-row\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-lbl\u0022\u003EChain exponent\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-val\u0022\u003E@ExponentLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-stat-row\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-lbl\u0022\u003ELast chain\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-val\u0022\u003E@LastChainLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-stat-row\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-lbl\u0022\u003EBest chain\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-val\u0022\u003E@BestChainLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-stat-row\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-lbl\u0022\u003EBoard\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-val\u0022\u003E@BoardLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-stat-row\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-lbl\u0022\u003EFeed line\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-val @FeedClass()\u0022\u003E@FeedLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t@* \u2500\u2500 Next goal \u2500\u2500\n\t\t\t\t   The Reactor Log is four clicks away in the right panel, which\n\t\t\t\t   means the long-tail goals are invisible exactly when they are\n\t\t\t\t   supposed to be pulling. This pins the closest one. *@\n\t\t\t\t@if ( NextGoal != null )\n\t\t\t\t{\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card idle-card-goal\u0022\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-card-hd\u0022\u003ENEXT GOAL\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-goal-row\u0022\u003E\n\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-goal-icon\u0022\u003E@NextGoal.Icon\u003C/span\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-goal-body\u0022\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-goal-name\u0022\u003E@NextGoal.Name\u003C/div\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-goal-desc\u0022\u003E@NextGoal.Desc\u003C/div\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-bar-track\u0022\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-bar-fill goal\u0022 style=\u0022width:@Pct( NextGoal.Fraction( S ) )%;\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-goal-foot\u0022\u003E\n\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-goal-prog\u0022\u003E@NextGoalProgLabel()\u003C/span\u003E\n\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-goal-reward\u0022\u003E@NextGoalRewardLabel()\u003C/span\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t}\n\n\t\t\t\t@if ( ShowBoosts )\n\t\t\t\t{\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card idle-card-active\u0022\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-card-hd\u0022\u003EACTIVE\u003C/div\u003E\n\t\t\t\t\t\t@if ( R.SurgeActive )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-boost surge\u0022\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-boost-icon\u0022\u003E\u26A1\u003C/span\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-boost-lbl\u0022\u003E@SurgeBoostLabel()\u003C/span\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-boost-time\u0022\u003E@SecondsLabel( R.SurgeTimeLeft )\u003C/span\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@foreach ( var boost in R.Boosts )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar b = boost;\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-boost\u0022\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-boost-icon\u0022\u003E@b.Icon\u003C/span\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-boost-lbl\u0022\u003E@BoostLabel( b )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-boost-time\u0022\u003E@SecondsLabel( b.SecondsLeft )\u003C/span\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t}\n\n\t\t\t\t\u003Cdiv class=\u0022idle-card idle-card-condenser\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card-hd\u0022\u003ECONDENSER\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-bar-track\u0022\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-bar-fill frag\u0022 style=\u0022width:@Pct( R.FragmentProgressFraction )%;\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card-note\u0022\u003E@CondenserLabel()\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-stat-row\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-lbl\u0022\u003EToday\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-stat-val @CapClass()\u0022\u003E@FragTodayLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-card\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card-hd\u0022\u003EMANIFEST\u003C/div\u003E\n\t\t\t\t\t@for ( int ti = 0; ti \u003C IdleBalance.Tiers.Count; ti\u002B\u002B )\n\t\t\t\t\t{\n\t\t\t\t\t\tvar tier = IdleBalance.Tiers[ti];\n\t\t\t\t\t\tbool got = R.TierUnlocked( ti );\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-tier @(got ? \u0022on\u0022 : \u0022\u0022)\u0022\u003E\n\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-tier-icon\u0022\u003E@tier.Icon\u003C/span\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-tier-body\u0022\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-tier-name\u0022\u003E@tier.Name\u003C/div\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-tier-blurb\u0022\u003E@TierBlurb( ti, got )\u003C/div\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t}\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t@* Rank card \u2014 neighbours, not a top-N. See RebuildRankRows(). *@\n\t\t\t\t\u003Cdiv class=\u0022idle-card\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card-hd\u0022\u003EREACTOR RANK\u003C/div\u003E\n\t\t\t\t\t@if ( _rankRows.Count == 0 )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-card-note\u0022\u003E@RankEmptyLabel()\u003C/div\u003E\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t@foreach ( var row in _rankRows )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar rw = row;\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-rank-row @RankRowClass( rw )\u0022\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-rank-pos\u0022\u003E@rw.RankLabel\u003C/span\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-rank-name\u0022\u003E@rw.Name\u003C/span\u003E\n\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-rank-val\u0022\u003E@rw.ValueLabel\u003C/span\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-card-note\u0022\u003E@GapLabel()\u003C/div\u003E\n\t\t\t\t\t\t@if ( _rankClimb \u003E 0 )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-card-note up\u0022\u003E@ClimbLabel()\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\u003C/div\u003E\n\n\t\t\t@* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 CENTER COLUMN \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *@\n\t\t\t\u003Cdiv class=\u0022idle-col idle-center\u0022\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-board-wrap @TutDim(\u0022board\u0022)\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-board @SurgeClass() @CascadeClass() @BoardShake()\u0022\u003E\n\t\t\t\t\t\t@for ( int r = 0; r \u003C R.Size; r\u002B\u002B )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-row\u0022\u003E\n\t\t\t\t\t\t\t\t@for ( int c = 0; c \u003C R.Size; c\u002B\u002B )\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tint row = r;\n\t\t\t\t\t\t\t\t\tint col = c;\n\t\t\t\t\t\t\t\t\tvar cell = R.Board[row, col];\n\t\t\t\t\t\t\t\t\tbool golden = R.GoldenActive \u0026\u0026 R.GoldenRow == row \u0026\u0026 R.GoldenCol == col;\n\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-cell @CellClass( cell, golden )\u0022\n\t\t\t\t\t\t\t\t\t\t style=\u0022width:@(CellPx)px;height:@(CellPx)px;\u0022\n\t\t\t\t\t\t\t\t\t\t onclick=@(() =\u003E OnCellClick( row, col ))\u003E\n\n\t\t\t\t\t\t\t\t\t\t@if ( golden )\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-golden\u0022\u003E\uD83C\uDF1F\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-golden-timer\u0022\u003E@GoldenTimerLabel()\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t@if ( !golden \u0026\u0026 cell.HasChest )\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-chest\u0022 style=\u0022@ChestStyle( cell )\u0022\u003E@ChestGlyph( cell )\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t@if ( !golden \u0026\u0026 cell.HasChest \u0026\u0026 cell.MaxHits \u003E 1 )\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-hits\u0022\u003E@cell.HitsLeft\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t@if ( cell.HasDrone )\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-drone\u0022 style=\u0022@DroneStyle( cell )\u0022\u003E\uD83D\uDCA3\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\t@foreach ( var floater in _floaters )\n\t\t\t\t\t{\n\t\t\t\t\t\tvar f = floater;\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-float @f.Cls\u0022\n\t\t\t\t\t\t\t style=\u0022left:@f.X.ToString(\u0022F1\u0022)%;top:@f.Y.ToString(\u0022F1\u0022)%;opacity:@f.Alpha.ToString(\u0022F2\u0022);transform:translateY(@f.Rise.ToString(\u0022F0\u0022)px) scale(@f.Scale.ToString(\u0022F2\u0022));\u0022\u003E\n\t\t\t\t\t\t\t@f.Text\n\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t}\n\n\t\t\t\t\t@if ( _calloutLife \u003E 0f )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-callout @_calloutCls\u0022 style=\u0022opacity:@_calloutLife.ToString(\u0022F2\u0022);\u0022\u003E@_calloutText\u003C/div\u003E\n\t\t\t\t\t}\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-controls @TutDim(\u0022controls\u0022)\u0022\u003E\n\n\t\t\t\t\t\u003Cbutton class=\u0022idle-overload @OverloadClass()\u0022 onclick=@OnOverload\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-overload-fill\u0022 style=\u0022width:@OverloadFillPct()%;\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-overload-label\u0022\u003E@OverloadLabel()\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-overload-sub\u0022\u003E@OverloadSubLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/button\u003E\n\n\t\t\t\t\t\u003Cdiv class=\u0022idle-surge-wrap\u0022\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-surge-track @SurgeReadyClass()\u0022\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-surge-fill\u0022 style=\u0022width:@Pct( R.Charge )%;\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-surge-lbl\u0022\u003E@SurgeLabel()\u003C/span\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t@if ( R.SurgeReady )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\u003Cbutton class=\u0022idle-surge-btn\u0022 onclick=@OnSurge\u003E@UnleashLabel()\u003C/button\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-meltdown-strip @MeltdownStripClass() @TutDim(\u0022meltdown\u0022)\u0022\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-melt-icon\u0022\u003E\u2622\uFE0F\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-melt-text\u0022\u003E@MeltdownLabel()\u003C/span\u003E\n\t\t\t\t\t@if ( R.CanMeltdown )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cbutton class=\u0022idle-melt-btn\u0022 onclick=@OnOpenMeltdown\u003EREVIEW\u003C/button\u003E\n\t\t\t\t\t}\n\t\t\t\t\t@if ( !R.CanMeltdown )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-bar\u0022\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-fill\u0022 style=\u0022width:@Pct( R.MeltdownProgress )%;\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t}\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t@* Only appears once the perk tree is most of the way bought \u2014 the\n\t\t\t\t   layer is meaningless before that and just clutters the column. *@\n\t\t\t\t@if ( R.SingularityVisible )\n\t\t\t\t{\n\t\t\t\t\t\u003Cdiv class=\u0022idle-sing-strip @SingStripClass() @TutDim(\u0022meltdown\u0022)\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-melt-icon\u0022\u003E\u2734\uFE0F\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-melt-text\u0022\u003E@SingularityLabel()\u003C/span\u003E\n\t\t\t\t\t\t@if ( R.CanSingularity )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\u003Cbutton class=\u0022idle-sing-btn\u0022 onclick=@OnOpenSingularity\u003ECOLLAPSE\u003C/button\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if ( !R.CanSingularity )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-bar\u0022\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-sing-fill\u0022 style=\u0022width:@Pct( R.SingularityProgress )%;\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t}\n\t\t\t\u003C/div\u003E\n\n\t\t\t@* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 RIGHT COLUMN \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *@\n\t\t\t\u003Cdiv class=\u0022idle-col idle-right @TutDim(\u0022right\u0022)\u0022\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-tabs\u0022\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-tab @TabClass(\u0022reactor\u0022)\u0022 onclick=@(() =\u003E SetTab(\u0022reactor\u0022))\u003E\u2699\uFE0F\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-tab @TabClass(\u0022chains\u0022)\u0022  onclick=@(() =\u003E SetTab(\u0022chains\u0022))\u003E\u26D3\uFE0F\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-tab @TabClass(\u0022systems\u0022)\u0022 onclick=@(() =\u003E SetTab(\u0022systems\u0022))\u003E\uD83D\uDEE0\uFE0F\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-tab fluxtab @TabClass(\u0022flux\u0022)\u0022 onclick=@(() =\u003E SetTab(\u0022flux\u0022))\u003E\uD83D\uDD37\u003C/button\u003E\n\t\t\t\t\t@if ( R.SingularityVisible )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cbutton class=\u0022idle-tab singtab @TabClass(\u0022powers\u0022)\u0022 onclick=@(() =\u003E SetTab(\u0022powers\u0022))\u003E\u2734\uFE0F\u003C/button\u003E\n\t\t\t\t\t}\n\t\t\t\t\t\u003Cbutton class=\u0022idle-tab @TabClass(\u0022log\u0022)\u0022 onclick=@(() =\u003E SetTab(\u0022log\u0022))\u003E\uD83D\uDCDC\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t@if ( IsUpgradeTab )\n\t\t\t\t{\n\t\t\t\t\t\u003Cdiv class=\u0022idle-buyrow\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-buyrow-lbl\u0022\u003EBUY\u003C/span\u003E\n\t\t\t\t\t\t@foreach ( var amount in BuyAmounts )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint amt = amount;\n\t\t\t\t\t\t\t\u003Cbutton class=\u0022idle-buy-amt @BuyAmtClass( amt )\u0022 onclick=@(() =\u003E SetBuyAmount( amt ))\u003E@BuyAmountLabel( amt )\u003C/button\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t}\n\n\t\t\t\t\u003Cdiv class=\u0022idle-panel\u0022\u003E\n\n\t\t\t\t\t@if ( IsUpgradeTab )\n\t\t\t\t\t{\n\t\t\t\t\t\t@foreach ( var upgrade in IdleUpgradeCatalog.InCategory( CurrentCategory ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar up = upgrade;\n\t\t\t\t\t\t\tbool revealed = R.IsRevealed( up );\n\t\t\t\t\t\t\tint  lvl      = S.Level( up.Id );\n\t\t\t\t\t\t\tbool maxed    = lvl \u003E= up.MaxLevel;\n\t\t\t\t\t\t\tint  count    = revealed ? BuyCount( up.Id ) : 0;\n\t\t\t\t\t\t\tdouble cost   = (!revealed || maxed) ? 0 : R.CostFor( up.Id, count );\n\t\t\t\t\t\t\tbool afford   = revealed \u0026\u0026 !maxed \u0026\u0026 count \u003E 0 \u0026\u0026 Cores \u003E= cost;\n\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up @UpgradeClass( revealed, maxed, afford )\u0022 onclick=@(() =\u003E OnBuy( up.Id ))\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-icon\u0022\u003E@UpgradeIcon( up, revealed )\u003C/div\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-body\u0022\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-top\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-name\u0022\u003E@UpgradeName( up, revealed )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-lvl\u0022\u003E@LevelLabel( up, lvl, revealed )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-desc\u0022\u003E@UpgradeDesc( up, revealed )\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t@if ( revealed )\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-bottom\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-effect\u0022\u003E@up.Effect( lvl )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-cost @MaxedClass( maxed )\u0022\u003E@CostLabel( maxed, cost, count )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t@if ( _tab == \u0022flux\u0022 )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-flux-head\u0022\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-flux-amount\u0022\u003E@FluxHeadLabel()\u003C/div\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-flux-sub\u0022\u003E@FluxSubLabel()\u003C/div\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\t\t@foreach ( var perk in VisiblePerks() )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar p      = perk;\n\t\t\t\t\t\t\tint lvl    = S.PerkLevel( p.Id );\n\t\t\t\t\t\t\tbool maxed = lvl \u003E= p.MaxLevel;\n\t\t\t\t\t\t\tint cost   = p.CostAt( lvl );\n\t\t\t\t\t\t\tbool afford = !maxed \u0026\u0026 S.Flux \u003E= cost;\n\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up perk @UpgradeClass( true, maxed, afford )\u0022 onclick=@(() =\u003E OnBuyPerk( p.Id ))\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-icon\u0022\u003E@p.Icon\u003C/div\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-body\u0022\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-top\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-name\u0022\u003E@p.Name\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-lvl\u0022\u003E@PerkLevelLabel( p, lvl )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-desc\u0022\u003E@p.Desc\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-bottom\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-effect\u0022\u003E@p.Effect( lvl )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-cost flux @MaxedClass( maxed )\u0022\u003E@PerkCostLabel( maxed, cost )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t@if ( _tab == \u0022powers\u0022 )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-flux-head sing\u0022\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-flux-amount\u0022\u003E@ShardHeadLabel()\u003C/div\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-flux-sub\u0022\u003E@ShardSubLabel()\u003C/div\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\t\t@foreach ( var power in IdleUpgradeCatalog.Powers )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar pw     = power;\n\t\t\t\t\t\t\tint lvl    = S.PowerLevel( pw.Id );\n\t\t\t\t\t\t\tbool maxed = lvl \u003E= pw.MaxLevel;\n\t\t\t\t\t\t\tint cost   = pw.CostAt( lvl );\n\t\t\t\t\t\t\tbool afford = !maxed \u0026\u0026 S.Shards \u003E= cost;\n\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up power @UpgradeClass( true, maxed, afford )\u0022 onclick=@(() =\u003E OnBuyPower( pw.Id ))\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-icon\u0022\u003E@pw.Icon\u003C/div\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-body\u0022\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-top\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-name\u0022\u003E@pw.Name\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-lvl\u0022\u003E@PowerLevelLabel( pw, lvl )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-desc\u0022\u003E@pw.Desc\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-bottom\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-effect\u0022\u003E@pw.Effect( lvl )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-cost shard @MaxedClass( maxed )\u0022\u003E@PowerCostLabel( maxed, cost )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t@if ( _tab == \u0022log\u0022 )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-flux-head\u0022\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-flux-amount\u0022\u003E@LogHeadLabel()\u003C/div\u003E\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-flux-sub\u0022\u003EEach one pays out once.\u003C/div\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\t\t@foreach ( var achievement in IdleAchievements.All )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar a = achievement;\n\t\t\t\t\t\t\tbool done = S.HasAchievement( a.Id );\n\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up ach @(done ? \u0022done\u0022 : \u0022\u0022)\u0022\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-icon\u0022\u003E@AchIcon( a, done )\u003C/div\u003E\n\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-body\u0022\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-top\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-up-name\u0022\u003E@a.Name\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cspan class=\u0022idle-ach-reward\u0022\u003E@RewardLabel( a )\u003C/span\u003E\n\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-up-desc\u0022\u003E@a.Desc\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t@if ( !done )\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-ach-bar\u0022\u003E\n\t\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-ach-fill\u0022 style=\u0022width:@Pct( a.Fraction( S ) )%;\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t\t\u003Cdiv class=\u0022idle-ach-prog\u0022\u003E@ProgressLabel( a )\u003C/div\u003E\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\u003C/div\u003E\n\t\t\t\u003C/div\u003E\n\t\t\u003C/div\u003E\n\t\t}\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 TOASTS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *@\n\t\t\u003Cdiv class=\u0022idle-toasts\u0022\u003E\n\t\t\t@foreach ( var toast in _toasts )\n\t\t\t{\n\t\t\t\tvar t = toast;\n\t\t\t\t\u003Cdiv class=\u0022idle-toast @t.Cls\u0022 style=\u0022opacity:@t.Alpha.ToString(\u0022F2\u0022);\u0022\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-toast-icon\u0022\u003E@t.Icon\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-toast-text\u0022\u003E@t.Text\u003C/span\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t}\n\t\t\u003C/div\u003E\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 WELCOME BACK OVERLAY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *@\n\t\t@if ( R.OfflinePending )\n\t\t{\n\t\t\t\u003Cdiv class=\u0022idle-modal-bg\u0022\u003E\u003C/div\u003E\n\t\t\t\u003Cdiv class=\u0022idle-modal offline\u0022\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-eyebrow\u0022\u003EREACTOR REPORT\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-title\u0022\u003EWelcome back\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-sub\u0022\u003E@OfflineSubLabel()\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-offline-haul\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-offline-amount\u0022\u003E@OfflineAmountLabel()\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-offline-lbl\u0022\u003ECORES\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t@if ( R.PendingOfflineFrags \u003E 0 )\n\t\t\t\t{\n\t\t\t\t\t\u003Cdiv class=\u0022idle-offline-extra\u0022\u003E@OfflineFragLabel()\u003C/div\u003E\n\t\t\t\t}\n\n\t\t\t\t@if ( OfflineTankFull )\n\t\t\t\t{\n\t\t\t\t\t\u003Cdiv class=\u0022idle-offline-warn\u0022\u003E@OfflineWarnLabel()\u003C/div\u003E\n\t\t\t\t}\n\n\t\t\t\t\u003Cbutton class=\u0022idle-modal-btn\u0022 onclick=@OnClaimOffline\u003ECOLLECT\u003C/button\u003E\n\t\t\t\u003C/div\u003E\n\t\t}\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 DAILY STREAK OVERLAY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *@\n\t\t@if ( !R.OfflinePending \u0026\u0026 !_showIntro \u0026\u0026 R.DailyPending )\n\t\t{\n\t\t\t\u003Cdiv class=\u0022idle-modal-bg\u0022\u003E\u003C/div\u003E\n\t\t\t\u003Cdiv class=\u0022idle-modal daily\u0022\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-eyebrow\u0022\u003EDAILY CYCLE\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-title\u0022\u003E@DailyTitleLabel()\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-sub\u0022\u003ECome back tomorrow to keep the streak climbing. Day 7 pays eight times.\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-streak-row\u0022\u003E\n\t\t\t\t\t@for ( int d = 1; d \u003C= 7; d\u002B\u002B )\n\t\t\t\t\t{\n\t\t\t\t\t\tint day = d;\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-streak-pip @StreakPipClass( day )\u0022\u003E@day\u003C/div\u003E\n\t\t\t\t\t}\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-daily-reward\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-daily-mult\u0022\u003E@DailyMultLabel()\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-daily-lbl\u0022\u003E@DailyRewardLabel()\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cbutton class=\u0022idle-modal-btn\u0022 onclick=@OnClaimDaily\u003ECLAIM\u003C/button\u003E\n\t\t\t\u003C/div\u003E\n\t\t}\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 MELTDOWN MODAL \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *@\n\t\t@if ( !R.OfflinePending \u0026\u0026 !_showIntro \u0026\u0026 !R.DailyPending \u0026\u0026 _showMeltdown )\n\t\t{\n\t\t\t\u003Cdiv class=\u0022idle-modal-bg\u0022\u003E\u003C/div\u003E\n\t\t\t\u003Cdiv class=\u0022idle-modal meltdown\u0022\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-eyebrow\u0022\u003EPRESTIGE\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-title\u0022\u003EMELTDOWN\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-sub\u0022\u003ECollapse this cycle into permanent Flux. Every Flux you have ever earned adds income forever, and Flux buys perks that survive every reset.\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-melt-payout\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-payout-val\u0022\u003E@MeltPayoutLabel()\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-payout-lbl\u0022\u003EFLUX\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t@* The Flux number alone does not answer the only question that\n\t\t\t\t   matters at this button \u2014 \u0022am I actually going to be faster\n\t\t\t\t   afterwards\u0022. This does. *@\n\t\t\t\t\u003Cdiv class=\u0022idle-melt-gain\u0022\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-melt-gain-from\u0022\u003E@MeltMultBeforeLabel()\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-melt-gain-arrow\u0022\u003E\u2192\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-melt-gain-to\u0022\u003E@MeltMultAfterLabel()\u003C/span\u003E\n\t\t\t\t\t\u003Cspan class=\u0022idle-melt-gain-lbl\u0022\u003Epermanent income\u003C/span\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-melt-cols\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-col keep\u0022\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-col-hd\u0022\u003EKEPT\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EFlux and every perk\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EReactor Log entries\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003ERecords and Fragments\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EKickstart head start\u003C/div\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-col lose\u0022\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-col-hd\u0022\u003EBURNED\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003E@BurnedCoresLabel()\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EAll Core upgrades\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EReactor Frame size\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EUnlocked chest tiers\u003C/div\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-btns\u0022\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-modal-btn ghost\u0022 onclick=@OnCloseMeltdown\u003ENot yet\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-modal-btn danger\u0022 onclick=@OnMeltdown\u003EMELT DOWN\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\u003C/div\u003E\n\t\t}\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 SINGULARITY MODAL \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *@\n\t\t@if ( !R.OfflinePending \u0026\u0026 !_showIntro \u0026\u0026 !R.DailyPending \u0026\u0026 !_showMeltdown \u0026\u0026 _showSingularity )\n\t\t{\n\t\t\t\u003Cdiv class=\u0022idle-modal-bg\u0022\u003E\u003C/div\u003E\n\t\t\t\u003Cdiv class=\u0022idle-modal singularity\u0022\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-eyebrow\u0022\u003ESECOND PRESTIGE\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-title\u0022\u003ESINGULARITY\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-sub\u0022\u003ECollapse your entire Flux career into Shards. Shards buy Powers \u2014 and Powers change how a cycle runs, not just how much it pays.\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-melt-payout\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-sing-payout-val\u0022\u003E@SingPayoutLabel()\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-payout-lbl\u0022\u003ESHARDS\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-melt-cols\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-col keep\u0022\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-col-hd\u0022\u003EKEPT\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EShards and every Power\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EReactor Log entries\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003ERecords, Fragments, pet\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003E@ShardKeepLabel()\u003C/div\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-col lose\u0022\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-col-hd\u0022\u003ECOLLAPSED\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003E@BurnedFluxLabel()\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EEvery Flux perk\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EAll Core upgrades\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022idle-melt-line\u0022\u003EThis cycle\u0027s Cores\u003C/div\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-btns\u0022\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-modal-btn ghost\u0022 onclick=@OnCloseSingularity\u003ENot yet\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-modal-btn sing\u0022 onclick=@OnSingularity\u003ECOLLAPSE\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\u003C/div\u003E\n\t\t}\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 INTRO MODAL \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *@\n\t\t@if ( !R.OfflinePending \u0026\u0026 _showIntro )\n\t\t{\n\t\t\t\u003Cdiv class=\u0022idle-modal-bg\u0022\u003E\u003C/div\u003E\n\t\t\t\u003Cdiv class=\u0022idle-modal intro\u0022\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-eyebrow\u0022\u003EHOW IT WORKS\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-title\u0022\u003ETHE REACTOR\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-sub\u0022\u003EA board that plays itself. Chests drop, drones bomb them, chain reactions pay Cores. You never lose here \u2014 you only grow.\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-intro-steps\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-intro-step\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-intro-num\u0022\u003E1\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-intro-txt\u0022\u003ESpend Cores on the right. Longer chains are worth far more than richer chests.\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-intro-step\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-intro-num\u0022\u003E2\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-intro-txt\u0022\u003EHit OVERLOAD, or press SPACE, for a burst. It charges the Surge meter \u2014 ten times output when you unleash it.\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-intro-step\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-intro-num\u0022\u003E3\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-intro-txt\u0022\u003EIt keeps running while you are on the menu or another screen. Close the game and it stops.\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-intro-step\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-intro-num\u0022\u003E4\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022idle-intro-txt\u0022\u003ECores condense into Fragments, which feed your companion for a permanent output bonus. Meltdown turns a whole cycle into Flux.\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\u003Cdiv class=\u0022idle-modal-btns\u0022\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-modal-btn ghost\u0022 onclick=@OnCloseIntro\u003EI\u0027ll figure it out\u003C/button\u003E\n\t\t\t\t\t\u003Cbutton class=\u0022idle-modal-btn\u0022 onclick=@OnIntroTutorial\u003ESHOW ME HOW\u003C/button\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\u003C/div\u003E\n\t\t}\n\n\t\t@* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 GUIDED WALKTHROUGH \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\t\t   Coach marks over the live HUD. The board keeps running underneath \u2014\n\t\t   an idle game that freezes for its own tutorial teaches the wrong\n\t\t   thing about what the game is. Everything here is measured from the\n\t\t   real panels at runtime, so it cannot drift out of sync with layout.\n\t\t*@\n\t\t@if ( TutorialActive )\n\t\t{\n\t\t\t\u003Cdiv class=\u0022rt-layer\u0022\u003E\n\n\t\t\t\t@* Four quads around the spotlight, rather than one dim sheet with\n\t\t\t\t   a hole \u2014 the panel renderer has no masking, so this is how you\n\t\t\t\t   get a real cut-out that still swallows clicks everywhere else. *@\n\t\t\t\t\u003Cdiv class=\u0022rt-dim\u0022 style=\u0022@DimTopStyle()\u0022\u003E\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022rt-dim\u0022 style=\u0022@DimBottomStyle()\u0022\u003E\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022rt-dim\u0022 style=\u0022@DimLeftStyle()\u0022\u003E\u003C/div\u003E\n\t\t\t\t\u003Cdiv class=\u0022rt-dim\u0022 style=\u0022@DimRightStyle()\u0022\u003E\u003C/div\u003E\n\n\t\t\t\t@if ( TutHasSpot )\n\t\t\t\t{\n\t\t\t\t\t\u003Cdiv class=\u0022rt-ring\u0022 style=\u0022@SpotStyle()\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022rt-spot\u0022 style=\u0022@SpotStyle()\u0022\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022rt-corner tl\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022rt-corner tr\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022rt-corner bl\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t\u003Cdiv class=\u0022rt-corner br\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t}\n\n\t\t\t\t@* The class alternates per step purely so the entrance animation\n\t\t\t\t   replays \u2014 a stable class string would only ever play once. *@\n\t\t\t\t\u003Cdiv class=\u0022rt-card @TutCardAnimClass()\u0022 style=\u0022@CardStyle()\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022rt-card-top\u0022\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022rt-eyebrow\u0022\u003EHOW TO PLAY\u003C/span\u003E\n\t\t\t\t\t\t\u003Cspan class=\u0022rt-count\u0022\u003E@TutCountLabel()\u003C/span\u003E\n\t\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\t\u003Cdiv class=\u0022rt-title\u0022\u003E@TutTitleLabel()\u003C/div\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022rt-text\u0022\u003E@TutTextLabel()\u003C/div\u003E\n\n\t\t\t\t\t\u003Cdiv class=\u0022rt-dots\u0022\u003E\n\t\t\t\t\t\t@for ( int di = 0; di \u003C IdleTutorial.Count; di\u002B\u002B )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\u003Cdiv class=\u0022rt-dot @DotClass( di )\u0022\u003E\u003C/div\u003E\n\t\t\t\t\t\t}\n\t\t\t\t\t\u003C/div\u003E\n\n\t\t\t\t\t@if ( TutWaitingOnAction )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cdiv class=\u0022rt-hint\u0022\u003E\n\t\t\t\t\t\t\t\u003Cspan class=\u0022rt-hint-pip\u0022\u003E\uD83D\uDC46\u003C/span\u003E\n\t\t\t\t\t\t\t\u003Cspan class=\u0022rt-hint-txt\u0022\u003E@TutHintLabel()\u003C/span\u003E\n\t\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\t}\n\t\t\t\t\t@if ( !TutWaitingOnAction )\n\t\t\t\t\t{\n\t\t\t\t\t\t\u003Cbutton class=\u0022rt-next\u0022 onclick=@OnTutNext\u003E@TutNextLabel()\u003C/button\u003E\n\t\t\t\t\t}\n\n\t\t\t\t\t\u003Cdiv class=\u0022rt-foot\u0022\u003E\n\t\t\t\t\t\t\u003Cbutton class=\u0022rt-back @TutBackClass()\u0022 onclick=@OnTutBack\u003EBack\u003C/button\u003E\n\t\t\t\t\t\t\u003Cbutton class=\u0022rt-skip\u0022 onclick=@OnTutSkip\u003ESkip the tour\u003C/button\u003E\n\t\t\t\t\t\u003C/div\u003E\n\t\t\t\t\u003C/div\u003E\n\t\t\t\u003C/div\u003E\n\t\t}\n\t\u003C/div\u003E\n}\n\u003C/root\u003E\n\n@code {\n\tpublic static IdleHud Instance { get; private set; }\n\n\tIdleReactor       R    =\u003E IdleReactor.Instance;\n\tChainReactionGame Game =\u003E ChainReactionGame.Instance;\n\tIdleSave          S    =\u003E ChainReactionGame.Save?.Idle;\n\n\tbool Visible =\u003E Game?.GameStarted == true\n\t\t\t\t \u0026\u0026 Game?.Mode == GameMode.Idle\n\t\t\t\t \u0026\u0026 R != null \u0026\u0026 S != null;\n\n\tdouble Cores =\u003E S?.Cores ?? 0;\n\n\tbool ModalOpen  =\u003E R.OfflinePending || R.DailyPending || _showMeltdown || _showSingularity || _showIntro;\n\tbool ShowBoosts =\u003E R.Boosts.Count \u003E 0 || R.SurgeActive;\n\tbool OfflineTankFull =\u003E R.PendingOfflineSeconds \u003E R.OfflineCapHours * 3600f;\n\n\t// \u2500\u2500 UI state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tstring _tab       = \u0022reactor\u0022;\n\tint    _buyAmount = 1;                      // -1 == MAX\n\tbool   _showMeltdown;\n\tbool   _showSingularity;\n\tbool   _showIntro;\n\tbool   _subscribed;\n\tbool   _wasVisible;\n\n\tstatic readonly int[] BuyAmounts = { 1, 10, 25, -1 };\n\n\tbool IsUpgradeTab =\u003E _tab == \u0022reactor\u0022 || _tab == \u0022chains\u0022 || _tab == \u0022systems\u0022;\n\n\tIdleUpgradeCategory CurrentCategory =\u003E _tab switch\n\t{\n\t\t\u0022reactor\u0022 =\u003E IdleUpgradeCategory.Reactor,\n\t\t\u0022chains\u0022  =\u003E IdleUpgradeCategory.Chains,\n\t\t_         =\u003E IdleUpgradeCategory.Systems,\n\t};\n\n\t// \u2500\u2500 Floating numbers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tclass Floater\n\t{\n\t\tpublic string Text;\n\t\tpublic string Cls;\n\t\tpublic float X, Y;\n\t\tpublic float Life, MaxLife;\n\t\tpublic float Alpha =\u003E MathF.Min( 1f, Life / 0.35f );\n\t\tpublic float Rise  =\u003E -(MaxLife - Life) * 42f;\n\t\tpublic float Scale =\u003E 0.85f \u002B MathF.Min( 0.35f, (MaxLife - Life) * 1.2f );\n\t}\n\treadonly List\u003CFloater\u003E _floaters = new();\n\n\tclass ToastMsg\n\t{\n\t\tpublic string Icon;\n\t\tpublic string Text;\n\t\tpublic string Cls;\n\t\tpublic float Life;\n\t\tpublic float Alpha =\u003E MathF.Min( 1f, Life / 0.4f );\n\t}\n\treadonly List\u003CToastMsg\u003E _toasts = new();\n\n\tstring _calloutText = \u0022\u0022;\n\tstring _calloutCls  = \u0022\u0022;\n\tfloat  _calloutLife;\n\n\t// Board shake is kept local rather than going through ScreenShaker so the\n\t// class string stays stable between rebuilds \u2014 a changing class would\n\t// restart the animation on every repaint and read as a vibration.\n\tstring _shakeCls = \u0022\u0022;\n\tfloat  _shakeUntil;\n\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\t// Guided walkthrough\n\t//\n\t// Coach marks measured off the real panels every frame, so the spotlight\n\t// cannot drift when the board resizes, the window changes shape, or a card\n\t// appears above the one being pointed at. The previous tutorial in this\n\t// codebase hard-coded pixel rectangles and needed a debug nudge UI to keep\n\t// them honest; this one has nothing to keep honest.\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\tbool  _tutRunning;\n\tint   _tutStep;\n\tfloat _tutStepAt;          // Time.Now the step opened, for the action grace period\n\n\t// Spotlight rect and card position, in design pixels relative to the root.\n\tfloat _spotX, _spotY, _spotW, _spotH;\n\tfloat _cardX, _cardY;\n\tfloat _rootW = 1920f, _rootH = 1080f;\n\tbool  _spotFound;\n\n\t// Measured on the frame after the card first renders; the fallback only\n\t// ever shows for that single frame.\n\tfloat _cardW = 380f, _cardH = 240f;\n\n\tconst float CardGap    = 26f;   // spotlight \u2192 card\n\tconst float EdgePad    = 20f;   // card \u2192 screen edge\n\tconst float ActionGrace = 0.45f; // the click that opened a step cannot also close it\n\n\t/// \u003Csummary\u003E\n\t/// Set by the menu\u0027s \u0022How to play\u0022 button. Consumed on the frame the HUD\n\t/// becomes visible, since the tour points at panels that do not exist until\n\t/// then.\n\t/// \u003C/summary\u003E\n\tpublic static bool PendingTutorial { get; set; }\n\n\tpublic bool TutorialActive =\u003E _tutRunning \u0026\u0026 Visible \u0026\u0026 !ModalOpen;\n\n\tbool TutHasSpot =\u003E _spotFound \u0026\u0026 _spotW \u003E 1f \u0026\u0026 _spotH \u003E 1f;\n\n\tIdleTutStep CurrentTutStep\n\t\t=\u003E _tutStep \u003E= 0 \u0026\u0026 _tutStep \u003C IdleTutorial.Count ? IdleTutorial.Steps[_tutStep] : null;\n\n\tbool TutWaitingOnAction\n\t\t=\u003E CurrentTutStep != null \u0026\u0026 CurrentTutStep.Trigger != IdleTutTrigger.Button;\n\n\tpublic void StartTutorial()\n\t{\n\t\t_tutRunning = true;\n\t\t_tutStep    = 0;\n\t\t_tutStepAt  = Time.Now;\n\t\t_showIntro  = false;\n\t\t_spotFound  = false;\n\t\tMeasureTutorial();\n\t\tStateHasChanged();\n\t}\n\n\tvoid EndTutorial( bool completed )\n\t{\n\t\t_tutRunning = false;\n\t\tif ( S != null )\n\t\t{\n\t\t\tS.TutorialOffered = true;\n\t\t\tif ( completed ) S.TutorialDone = true;\n\t\t\tChainReactionGame.Save?.Save();\n\t\t}\n\t\tStateHasChanged();\n\t}\n\n\tvoid OnTutNext()\n\t{\n\t\tSound.Play( \u0022UI_Button\u0022 );\n\t\tAdvanceTutorial();\n\t}\n\n\tvoid OnTutBack()\n\t{\n\t\tif ( _tutStep \u003C= 0 ) return;\n\t\tSound.Play( \u0022UI_Button\u0022 );\n\t\t_tutStep--;\n\t\t_tutStepAt = Time.Now;\n\t\tMeasureTutorial();\n\t\tStateHasChanged();\n\t}\n\n\tvoid OnTutSkip()\n\t{\n\t\tSound.Play( \u0022UI_Button\u0022 );\n\t\tEndTutorial( completed: true );\n\t}\n\n\tvoid AdvanceTutorial()\n\t{\n\t\t_tutStep\u002B\u002B;\n\t\t_tutStepAt = Time.Now;\n\n\t\tif ( _tutStep \u003E= IdleTutorial.Count )\n\t\t{\n\t\t\tSound.Play( \u0022PlayButton\u0022 );\n\t\t\tEndTutorial( completed: true );\n\t\t\treturn;\n\t\t}\n\n\t\tMeasureTutorial();\n\t\tStateHasChanged();\n\t}\n\n\t/// \u003Csummary\u003E\n\t/// Satisfies a step that is waiting on the player doing something. The grace\n\t/// period stops the very click that advanced onto a step from also\n\t/// advancing off it \u2014 buying an upgrade lands on the \u0022buy an upgrade\u0022 step\n\t/// otherwise, and the player never reads it.\n\t/// \u003C/summary\u003E\n\tvoid TutSatisfy( IdleTutTrigger trigger )\n\t{\n\t\tif ( !_tutRunning ) return;\n\n\t\tvar step = CurrentTutStep;\n\t\tif ( step == null || step.Trigger != trigger ) return;\n\t\tif ( Time.Now - _tutStepAt \u003C ActionGrace ) return;\n\n\t\tSound.Play( \u0022Notification_1\u0022 );\n\t\tAdvanceTutorial();\n\t}\n\n\t/// \u003Csummary\u003ERecursive class lookup rather than Descendants, so the first match\n\t/// found is the shallowest one \u2014 an inner panel sharing a class cannot win.\u003C/summary\u003E\n\tstatic Sandbox.UI.Panel FindByClass( Sandbox.UI.Panel from, string cls )\n\t{\n\t\tif ( from == null || string.IsNullOrEmpty( cls ) ) return null;\n\t\tif ( from.HasClass( cls ) ) return from;\n\n\t\tforeach ( var child in from.Children )\n\t\t{\n\t\t\tvar hit = FindByClass( child, cls );\n\t\t\tif ( hit != null ) return hit;\n\t\t}\n\t\treturn null;\n\t}\n\n\tvoid MeasureTutorial()\n\t{\n\t\tvar root = Panel;\n\t\tif ( root == null ) return;\n\n\t\tfloat scale = root.ScaleFromScreen;\n\t\tvar   rr    = root.Box.Rect;\n\n\t\t_rootW = MathF.Max( 320f, rr.Width  * scale );\n\t\t_rootH = MathF.Max( 240f, rr.Height * scale );\n\n\t\tvar step = CurrentTutStep;\n\t\t_spotFound = false;\n\n\t\tif ( step != null \u0026\u0026 !string.IsNullOrEmpty( step.Target ) )\n\t\t{\n\t\t\tvar target = FindByClass( root, step.Target );\n\t\t\tif ( target != null )\n\t\t\t{\n\t\t\t\tvar tr = target.Box.Rect;\n\t\t\t\tif ( tr.Width \u003E 1f \u0026\u0026 tr.Height \u003E 1f )\n\t\t\t\t{\n\t\t\t\t\t_spotX = (tr.Left - rr.Left) * scale - step.Pad;\n\t\t\t\t\t_spotY = (tr.Top  - rr.Top ) * scale - step.Pad;\n\t\t\t\t\t_spotW = tr.Width  * scale \u002B step.Pad * 2f;\n\t\t\t\t\t_spotH = tr.Height * scale \u002B step.Pad * 2f;\n\n\t\t\t\t\t// A column that runs off the bottom of the viewport reports a\n\t\t\t\t\t// rect taller than the screen; clamping keeps the dim quads\n\t\t\t\t\t// from collapsing to nothing.\n\t\t\t\t\t_spotX = Math.Clamp( _spotX, 0f, _rootW - 8f );\n\t\t\t\t\t_spotY = Math.Clamp( _spotY, 0f, _rootH - 8f );\n\t\t\t\t\t_spotW = Math.Clamp( _spotW, 8f, _rootW - _spotX );\n\t\t\t\t\t_spotH = Math.Clamp( _spotH, 8f, _rootH - _spotY );\n\n\t\t\t\t\t_spotFound = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// The card measures itself once it exists; until then the constructor\n\t\t// defaults are close enough that nothing visibly jumps.\n\t\tvar card = FindByClass( root, \u0022rt-card\u0022 );\n\t\tif ( card != null )\n\t\t{\n\t\t\tvar cr = card.Box.Rect;\n\t\t\tif ( cr.Width \u003E 1f \u0026\u0026 cr.Height \u003E 1f )\n\t\t\t{\n\t\t\t\t_cardW = cr.Width  * scale;\n\t\t\t\t_cardH = cr.Height * scale;\n\t\t\t}\n\t\t}\n\n\t\tPlaceCard( step );\n\t}\n\n\tvoid PlaceCard( IdleTutStep step )\n\t{\n\t\tif ( step == null || !TutHasSpot )\n\t\t{\n\t\t\t_cardX = (_rootW - _cardW) * 0.5f;\n\t\t\t_cardY = (_rootH - _cardH) * 0.5f;\n\t\t\treturn;\n\t\t}\n\n\t\tfloat cx = _spotX \u002B _spotW * 0.5f;\n\t\tfloat cy = _spotY \u002B _spotH * 0.5f;\n\n\t\tvar place = step.Place;\n\n\t\t// Fall back to the other side when the requested one has no room. This\n\t\t// is what makes the script survive an ultrawide or a small window\n\t\t// without every step needing a per-aspect-ratio override.\n\t\tif ( place == IdleTutPlace.Right \u0026\u0026 _spotX \u002B _spotW \u002B CardGap \u002B _cardW \u003E _rootW - EdgePad )\n\t\t\tplace = IdleTutPlace.Left;\n\t\telse if ( place == IdleTutPlace.Left \u0026\u0026 _spotX - CardGap - _cardW \u003C EdgePad )\n\t\t\tplace = IdleTutPlace.Right;\n\t\telse if ( place == IdleTutPlace.Above \u0026\u0026 _spotY - CardGap - _cardH \u003C EdgePad )\n\t\t\tplace = IdleTutPlace.Below;\n\t\telse if ( place == IdleTutPlace.Below \u0026\u0026 _spotY \u002B _spotH \u002B CardGap \u002B _cardH \u003E _rootH - EdgePad )\n\t\t\tplace = IdleTutPlace.Above;\n\n\t\tswitch ( place )\n\t\t{\n\t\t\tcase IdleTutPlace.Left:\n\t\t\t\t_cardX = _spotX - CardGap - _cardW;\n\t\t\t\t_cardY = cy - _cardH * 0.5f;\n\t\t\t\tbreak;\n\n\t\t\tcase IdleTutPlace.Right:\n\t\t\t\t_cardX = _spotX \u002B _spotW \u002B CardGap;\n\t\t\t\t_cardY = cy - _cardH * 0.5f;\n\t\t\t\tbreak;\n\n\t\t\tcase IdleTutPlace.Above:\n\t\t\t\t_cardX = cx - _cardW * 0.5f;\n\t\t\t\t_cardY = _spotY - CardGap - _cardH;\n\t\t\t\tbreak;\n\n\t\t\tcase IdleTutPlace.Below:\n\t\t\t\t_cardX = cx - _cardW * 0.5f;\n\t\t\t\t_cardY = _spotY \u002B _spotH \u002B CardGap;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t_cardX = (_rootW - _cardW) * 0.5f;\n\t\t\t\t_cardY = (_rootH - _cardH) * 0.5f;\n\t\t\t\tbreak;\n\t\t}\n\n\t\t_cardX = Math.Clamp( _cardX, EdgePad, MathF.Max( EdgePad, _rootW - _cardW - EdgePad ) );\n\t\t_cardY = Math.Clamp( _cardY, EdgePad, MathF.Max( EdgePad, _rootH - _cardH - EdgePad ) );\n\t}\n\n\t// \u2500\u2500 Inline styles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t// Ints only. A decimal separator from a French locale would be a comma and\n\t// would silently kill the whole declaration.\n\n\tstatic string Px( float v ) =\u003E ((int)MathF.Round( v )).ToString();\n\n\tstring SpotStyle()\n\t\t=\u003E $\u0022left:{Px( _spotX )}px;top:{Px( _spotY )}px;width:{Px( _spotW )}px;height:{Px( _spotH )}px;\u0022;\n\n\tstring CardStyle() =\u003E $\u0022left:{Px( _cardX )}px;top:{Px( _cardY )}px;\u0022;\n\n\t// With no spotlight the top quad covers everything and the other three\n\t// collapse \u2014 which is exactly the plain dim sheet a centred step wants.\n\tstring DimTopStyle()\n\t\t=\u003E TutHasSpot\n\t\t\t? $\u0022left:0px;top:0px;width:{Px( _rootW )}px;height:{Px( _spotY )}px;\u0022\n\t\t\t: $\u0022left:0px;top:0px;width:{Px( _rootW )}px;height:{Px( _rootH )}px;\u0022;\n\n\tstring DimBottomStyle()\n\t{\n\t\tif ( !TutHasSpot ) return \u0022width:0px;height:0px;\u0022;\n\t\tfloat top = _spotY \u002B _spotH;\n\t\treturn $\u0022left:0px;top:{Px( top )}px;width:{Px( _rootW )}px;height:{Px( _rootH - top )}px;\u0022;\n\t}\n\n\tstring DimLeftStyle()\n\t\t=\u003E TutHasSpot\n\t\t\t? $\u0022left:0px;top:{Px( _spotY )}px;width:{Px( _spotX )}px;height:{Px( _spotH )}px;\u0022\n\t\t\t: \u0022width:0px;height:0px;\u0022;\n\n\tstring DimRightStyle()\n\t{\n\t\tif ( !TutHasSpot ) return \u0022width:0px;height:0px;\u0022;\n\t\tfloat left = _spotX \u002B _spotW;\n\t\treturn $\u0022left:{Px( left )}px;top:{Px( _spotY )}px;width:{Px( _rootW - left )}px;height:{Px( _spotH )}px;\u0022;\n\t}\n\n\t// \u2500\u2500 Labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tstring TutTitleLabel() =\u003E CurrentTutStep?.Title ?? \u0022\u0022;\n\tstring TutTextLabel()  =\u003E CurrentTutStep?.Text  ?? \u0022\u0022;\n\tstring TutHintLabel()  =\u003E CurrentTutStep?.Hint  ?? \u0022\u0022;\n\tstring TutCountLabel() =\u003E $\u0022{_tutStep \u002B 1} / {IdleTutorial.Count}\u0022;\n\n\tstring TutNextLabel()\n\t\t=\u003E _tutStep \u003E= IdleTutorial.Count - 1 ? \u0022RUN THE REACTOR\u0022 : \u0022GOT IT\u0022;\n\n\tstring TutBackClass() =\u003E _tutStep \u003C= 0 ? \u0022hidden\u0022 : \u0022\u0022;\n\n\tstring TutCardAnimClass() =\u003E (_tutStep % 2) == 0 ? \u0022in-a\u0022 : \u0022in-b\u0022;\n\n\t/// \u003Csummary\u003E\n\t/// Dims a HUD block that is not the subject of the current step.\n\t///\n\t/// The dim quads are click-blockers and a background scrim; they cannot do\n\t/// this job on their own, because this renderer draws emoji above overlay\n\t/// panels and every chest, icon and tab glyph would shine straight through\n\t/// the scrim at full brightness. Ancestor opacity takes the glyphs with it.\n\t/// \u003C/summary\u003E\n\tstring TutDim( string region )\n\t{\n\t\tif ( !TutorialActive ) return \u0022\u0022;\n\n\t\tstring want = CurrentTutStep?.Region ?? \u0022\u0022;\n\t\tif ( string.IsNullOrEmpty( want ) ) return \u0022\u0022;   // centred step dims nothing\n\n\t\treturn region == want ? \u0022\u0022 : \u0022tut-dim\u0022;\n\t}\n\n\tstring DotClass( int i )\n\t{\n\t\tif ( i == _tutStep ) return \u0022on\u0022;\n\t\treturn i \u003C _tutStep ? \u0022done\u0022 : \u0022\u0022;\n\t}\n\n\tstring HelpClass() =\u003E _tutRunning ? \u0022on\u0022 : \u0022\u0022;\n\n\tint CellPx =\u003E R == null ? 92 : R.Size switch\n\t{\n\t\t5 =\u003E 96,\n\t\t6 =\u003E 84,\n\t\t7 =\u003E 74,\n\t\t8 =\u003E 66,\n\t\t_ =\u003E 59,\n\t};\n\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\t// Reactor rank card\n\t//\n\t// Deliberately NOT a top-N board. A top-N motivates whoever is already in\n\t// it and is scenery for everyone else; the crossable goal is the gap to the\n\t// player directly above, so the card shows a window around the local player\n\t// (two up, you, one down).\n\t//\n\t// The board snapshot is stale between refreshes but S.AllTimeCores is live,\n\t// so the local row and the gap are always computed from the local value.\n\t// That makes the gap tick down while the player watches, which is the whole\n\t// point of the card.\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\tclass RankRow\n\t{\n\t\tpublic string RankLabel;\n\t\tpublic string Name;\n\t\tpublic string ValueLabel;\n\t\tpublic bool   IsMe;\n\t}\n\treadonly List\u003CRankRow\u003E _rankRows = new();\n\n\tSandbox.Services.Leaderboards.Board2 _rankBoard;\n\tbool  _rankLoading;\n\tbool  _rankFetched;\n\tfloat _rankNextFetch;\n\tint    _myRank;       // 1-based, 0 == not on the board\n\tdouble _nextUpValue;  // stat value of the player directly above, 0 == none above\n\tint   _rankClimb;     // places gained since the last session\n\tint   _rankTotalPlayers; // Board2.TotalEntries \u2014 real population, not window size\n\n\tconst float  RankRefreshSeconds = 90f;\n\t// Same stat the main-menu leaderboard ranks on, so the two can never disagree.\n\tstatic string RankStat =\u003E CommunityGoal.CoresStat;\n\n\t/// \u003Csummary\u003E\n\t/// Network call \u2014 only ever driven by the throttle in OnUpdate, never from\n\t/// the render path. BuildHash changes many times a second, so fetching from\n\t/// there would hammer the backend into rate-limiting us.\n\t/// \u003C/summary\u003E\n\tasync System.Threading.Tasks.Task LoadRank()\n\t{\n\t\tif ( _rankLoading ) return;\n\t\t_rankLoading = true;\n\n\t\ttry\n\t\t{\n\t\t\tvar b = Sandbox.Services.Leaderboards.GetFromStat( RankStat );\n\t\t\tb.SetAggregationMax();\n\t\t\tb.SetSortDescending();\n\n\t\t\t// CenterOnMe is exactly what this card wants: Board2 returns the\n\t\t\t// entries surrounding a target player, so asking for a small window\n\t\t\t// centred on us gives the neighbours directly. No manual slicing.\n\t\t\tb.CenterOnMe();\n\t\t\tb.MaxEntries = 5;\n\n\t\t\tawait b.Refresh();\n\t\t\t_rankBoard = b;\n\t\t\t_rankTotalPlayers = (int)b.TotalEntries;\n\n\t\t\tLog.Info( $\u0022Rank \u0027{RankStat}\u0027 \u2014 window: {b.Entries?.Length ?? -1}, board total: {b.TotalEntries}\u0022 );\n\t\t}\n\t\tcatch ( Exception e )\n\t\t{\n\t\t\tLog.Warning( $\u0022Reactor rank: {e.Message}\u0022 );\n\t\t}\n\n\t\t_rankLoading = false;\n\t\t_rankFetched = true;\n\t\tRebuildRankRows();\n\t\tStateHasChanged();\n\t}\n\n\tvoid RebuildRankRows()\n\t{\n\t\t_rankRows.Clear();\n\t\t_myRank      = 0;\n\t\t_nextUpValue = 0;\n\t\t_rankClimb   = 0;\n\n\t\tvar entries = _rankBoard?.Entries;\n\t\tif ( entries == null || entries.Length == 0 ) return;\n\n\t\t// CenterOnMe means these indices are window-relative, NOT board-relative,\n\t\t// so ranks must come from Entry.Rank. Using index\u002B1 here would label the\n\t\t// window \u0022#1..#5\u0022 no matter where the player actually sits.\n\t\tlong myId  = Sandbox.Connection.Local?.SteamId ?? 0;\n\t\tint  meIdx = -1;\n\t\tfor ( int i = 0; i \u003C entries.Length; i\u002B\u002B )\n\t\t{\n\t\t\tif ( entries[i].SteamId == myId ) { meIdx = i; break; }\n\t\t}\n\n\t\tif ( meIdx \u003C 0 )\n\t\t{\n\t\t\t// No entry for us yet \u2014 show the window we were given so there is\n\t\t\t// still a named target to chase rather than an empty card.\n\t\t\tfor ( int i = 0; i \u003C entries.Length; i\u002B\u002B )\n\t\t\t\t_rankRows.Add( MakeRankRow( entries[i], false ) );\n\n\t\t\t_nextUpValue = entries[entries.Length - 1].Value;\n\t\t\treturn;\n\t\t}\n\n\t\t_myRank = (int)entries[meIdx].Rank;\n\n\t\tint start = Math.Max( 0, meIdx - 2 );\n\t\tint end   = Math.Min( entries.Length - 1, meIdx \u002B 1 );\n\t\tfor ( int i = start; i \u003C= end; i\u002B\u002B )\n\t\t\t_rankRows.Add( MakeRankRow( entries[i], i == meIdx ) );\n\n\t\tif ( meIdx \u003E 0 ) _nextUpValue = entries[meIdx - 1].Value;\n\n\t\t// Places gained since we last recorded a rank. Persisting this is what\n\t\t// turns offline production into \u0022I moved up while I was away\u0022.\n\t\tint prev = S?.LastReactorRank ?? 0;\n\t\tif ( prev \u003E 0 \u0026\u0026 _myRank \u003E 0 \u0026\u0026 _myRank \u003C prev ) _rankClimb = prev - _myRank;\n\n\t\tif ( S != null \u0026\u0026 _myRank \u003E 0 \u0026\u0026 S.LastReactorRank != _myRank )\n\t\t{\n\t\t\tS.LastReactorRank = _myRank;\n\t\t\tChainReactionGame.Save?.Save();\n\t\t}\n\t}\n\n\tRankRow MakeRankRow( Sandbox.Services.Leaderboards.Board2.Entry e, bool isMe )\n\t{\n\t\t// The snapshot lags our own earnings, so never show the player a number\n\t\t// lower than what they have actually banked.\n\t\tdouble val = isMe\n\t\t\t? Math.Max( e.Value, S?.AllTimeCores ?? 0 )\n\t\t\t: e.Value;\n\n\t\treturn new RankRow\n\t\t{\n\t\t\tRankLabel  = \u0022#\u0022 \u002B e.Rank.ToString(),\n\t\t\tName       = isMe ? \u0022you\u0022 : RankName( e ),\n\t\t\tValueLabel = IdleFormat.Short( val ),\n\t\t\tIsMe       = isMe,\n\t\t};\n\t}\n\n\tstring RankName( Sandbox.Services.Leaderboards.Board2.Entry e )\n\t{\n\t\tif ( !string.IsNullOrWhiteSpace( e.DisplayName ) ) return e.DisplayName;\n\t\ttry\n\t\t{\n\t\t\tvar friend = new Sandbox.Friend( e.SteamId );\n\t\t\tif ( !string.IsNullOrWhiteSpace( friend.Name ) ) return friend.Name;\n\t\t}\n\t\tcatch { }\n\t\treturn \u0022Anonymous\u0022;\n\t}\n\n\tstring RankRowClass( RankRow r ) =\u003E r.IsMe ? \u0022me\u0022 : \u0022\u0022;\n\n\tstring RankEmptyLabel()\n\t{\n\t\tif ( !_rankFetched ) return \u0022Reading the board\u0022;\n\t\treturn \u0022Produce Cores to enter the rankings\u0022;\n\t}\n\n\tstring GapLabel()\n\t{\n\t\tif ( _nextUpValue \u003C= 0 ) return \u0022Top of the board\u0022;\n\n\t\tdouble mine = S?.AllTimeCores ?? 0;\n\t\tdouble gap  = _nextUpValue - mine;\n\n\t\tif ( gap \u003C= 0 ) return \u0022Ahead \u2014 board updates shortly\u0022;\n\t\treturn IdleFormat.Short( gap ) \u002B \u0022 Cores to pass\u0022;\n\t}\n\n\tstring ClimbLabel()\n\t{\n\t\tif ( _rankClimb == 1 ) return \u0022Up 1 place since last time\u0022;\n\t\treturn \u0022Up \u0022 \u002B _rankClimb.ToString() \u002B \u0022 places since last time\u0022;\n\t}\n\n\t// Lifecycle\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\tprotected override void OnStart()\n\t{\n\t\tInstance = this;\n\t}\n\n\tprotected override void OnUpdate()\n\t{\n\t\tif ( !_subscribed \u0026\u0026 R != null ) Subscribe();\n\n\t\tbool vis = Visible;\n\t\tif ( vis != _wasVisible )\n\t\t{\n\t\t\t_wasVisible = vis;\n\t\t\tif ( vis )\n\t\t\t{\n\t\t\t\t_floaters.Clear();\n\t\t\t\t_toasts.Clear();\n\t\t\t\t_calloutLife  = 0f;\n\t\t\t\t_shakeUntil   = 0f;\n\t\t\t\t_showMeltdown    = false;\n\t\t\t\t_showSingularity = false;\n\n\t\t\t\t// Offered on the walkthrough flag rather than IntroShown, so a\n\t\t\t\t// save from before the walkthrough existed still gets asked\n\t\t\t\t// once instead of being silently skipped past it.\n\t\t\t\t_showIntro    = S != null \u0026\u0026 !S.TutorialDone \u0026\u0026 !S.TutorialOffered;\n\t\t\t\t_tutRunning   = false;\n\t\t\t\t_tab          = \u0022reactor\u0022;\n\t\t\t\t_rankNextFetch = 0f;   // refetch on entering idle mode\n\t\t\t\t_nextGoalAt    = 0f;\n\t\t\t\tRefreshNextGoal();\n\n\t\t\t\tif ( PendingTutorial )\n\t\t\t\t{\n\t\t\t\t\tPendingTutorial = false;\n\t\t\t\t\tStartTutorial();\n\t\t\t\t}\n\t\t\t}\n\t\t\tStateHasChanged();\n\t\t}\n\n\t\tif ( !vis ) return;\n\n\t\t// Wall-clock throttle. Never move this into BuildHash or the render path.\n\t\tif ( RealTime.Now \u003E= _rankNextFetch )\n\t\t{\n\t\t\t_rankNextFetch = RealTime.Now \u002B RankRefreshSeconds;\n\t\t\t_ = LoadRank();\n\t\t}\n\n\t\t// SPACE is the detonate key everywhere else in the game, so it fires the\n\t\t// reactor\u0027s manual burst here too.\n\t\tif ( Input.Pressed( \u0022Jump\u0022 ) \u0026\u0026 R.OverloadReady \u0026\u0026 !ModalOpen )\n\t\t\tR.Overload();\n\n\t\tif ( RealTime.Now \u003E= _nextGoalAt )\n\t\t{\n\t\t\t_nextGoalAt = RealTime.Now \u002B 0.5f;\n\t\t\tRefreshNextGoal();\n\t\t}\n\n\t\t// Re-measured every frame rather than only on step change: the board\n\t\t// resizes on a Reactor Frame purchase, cards appear and disappear in the\n\t\t// left column, and the window can be dragged to another size mid-tour.\n\t\tif ( TutorialActive ) MeasureTutorial();\n\n\t\tfloat dt = Time.Delta;\n\n\t\tfor ( int i = _floaters.Count - 1; i \u003E= 0; i-- )\n\t\t{\n\t\t\t_floaters[i].Life -= dt;\n\t\t\tif ( _floaters[i].Life \u003C= 0f ) _floaters.RemoveAt( i );\n\t\t}\n\n\t\tfor ( int i = _toasts.Count - 1; i \u003E= 0; i-- )\n\t\t{\n\t\t\t_toasts[i].Life -= dt;\n\t\t\tif ( _toasts[i].Life \u003C= 0f ) _toasts.RemoveAt( i );\n\t\t}\n\n\t\tif ( _calloutLife \u003E 0f ) _calloutLife -= dt * 1.4f;\n\t}\n\n\tvoid Subscribe()\n\t{\n\t\tvar r = R;\n\t\tif ( r == null || _subscribed ) return;\n\n\t\tr.OnPop        \u002B= OnPop;\n\t\tr.OnToast      \u002B= OnToastEvent;\n\t\tr.OnDetonation \u002B= OnDetonation;\n\n\t\t// Walkthrough gates \u2014 a step that says \u0022press OVERLOAD\u0022 only closes when\n\t\t// the player actually presses OVERLOAD.\n\t\tr.OnManualOverload \u002B= () =\u003E TutSatisfy( IdleTutTrigger.Overload );\n\t\tr.OnUpgradeBought  \u002B= () =\u003E TutSatisfy( IdleTutTrigger.Buy );\n\t\tr.OnManualCrack    \u002B= () =\u003E TutSatisfy( IdleTutTrigger.CellClick );\n\n\t\t_subscribed = true;\n\t}\n\n\t/// \u003Csummary\u003E\n\t/// Rebuilds only when something on screen would actually differ.\n\t///\n\t/// The first version forced a repaint 15 times a second, which meant\n\t/// rebuilding the board, both side columns and every upgrade row constantly\n\t/// \u2014 that was the source of the stutter. Between detonations almost nothing\n\t/// changes, so hashing real state instead of a clock cuts the repaint rate\n\t/// by roughly an order of magnitude.\n\t/// \u003C/summary\u003E\n\tprotected override int BuildHash()\n\t{\n\t\tif ( !Visible ) return 0;\n\n\t\tvar hc = new HashCode();\n\t\thc.Add( _tab );\n\t\thc.Add( _buyAmount );\n\t\thc.Add( _showMeltdown );\n\t\thc.Add( _showSingularity );\n\t\thc.Add( R?.CascadeActive ?? false );\n\t\thc.Add( S?.Shards ?? 0 );\n\t\thc.Add( S?.ShardsEarned ?? 0 );\n\t\thc.Add( S?.Singularities ?? 0 );\n\t\thc.Add( R?.SingularityVisible ?? false );\n\t\thc.Add( _showIntro );\n\t\thc.Add( ModalOpen );\n\n\t\t// Walkthrough \u2014 geometry is quantised to whole pixels, so a static\n\t\t// spotlight leaves the hash alone and the panel is not rebuilt.\n\t\thc.Add( TutorialActive );\n\t\thc.Add( _tutStep );\n\t\thc.Add( TutHasSpot );\n\t\thc.Add( (int)_spotX ); hc.Add( (int)_spotY );\n\t\thc.Add( (int)_spotW ); hc.Add( (int)_spotH );\n\t\thc.Add( (int)_cardX ); hc.Add( (int)_cardY );\n\n\t\t// Contracts \u2014 progress bars, claim state and the reset countdown\n\t\tfor ( int i = 0; i \u003C IdleContracts.DailyCount; i\u002B\u002B )\n\t\t{\n\t\t\thc.Add( (int)(IdleContracts.Fraction( S, i ) * 200f) );\n\t\t\thc.Add( IdleContracts.IsClaimed( S, i ) );\n\t\t}\n\t\thc.Add( (int)(IdleContracts.SecondsUntilReset() / 60.0) );\n\n\t\tvar goal = NextGoal;\n\t\thc.Add( goal?.Id ?? \u0022\u0022 );\n\t\thc.Add( goal == null ? 0 : (int)(goal.Fraction( S ) * 200f) );\n\n\t\t// Currencies \u2014 quantised to what the labels actually show\n\t\thc.Add( (long)Cores );\n\t\thc.Add( (long)R.Cps );\n\t\thc.Add( S.Flux );\n\t\thc.Add( S.FluxEarned );\n\t\thc.Add( _rankRows.Count );\n\t\thc.Add( _myRank );\n\t\thc.Add( _rankClimb );\n\t\thc.Add( ChainReactionGame.Save?.TotalFragments ?? 0 );\n\t\thc.Add( S.TodayReactorFragments );\n\t\thc.Add( (int)(R.FragmentProgressFraction * 60f) );\n\n\t\t// Live controls\n\t\thc.Add( (int)(R.OverloadCooldownLeft * 10f) );\n\t\thc.Add( (int)(R.Charge * 100f) );\n\t\thc.Add( R.SurgeActive );\n\t\thc.Add( (int)R.SurgeTimeLeft );\n\t\thc.Add( (int)R.GoldenTimeLeft );\n\t\thc.Add( R.Boosts.Count );\n\t\thc.Add( R.Boosts.Count \u003E 0 ? (int)R.Boosts[0].SecondsLeft : 0 );\n\t\thc.Add( R.FeedStarved );\n\n\t\t// Transient effects\n\t\thc.Add( _floaters.Count );\n\t\thc.Add( (int)(_calloutLife * 20f) );\n\t\thc.Add( Time.Now \u003C _shakeUntil ? _shakeCls : \u0022\u0022 );\n\t\thc.Add( _toasts.Count );\n\n\t\t// Board \u2014 chest layout, damage, drones and any live flash\n\t\thc.Add( R.Size );\n\t\tfloat now = Time.Now;\n\t\tbool animating = _floaters.Count \u003E 0 || _calloutLife \u003E 0f || now \u003C _shakeUntil;\n\n\t\tfor ( int r = 0; r \u003C R.Size; r\u002B\u002B )\n\t\t\tfor ( int c = 0; c \u003C R.Size; c\u002B\u002B )\n\t\t\t{\n\t\t\t\tvar cell = R.Board[r, c];\n\t\t\t\thc.Add( (int)cell.Chest );\n\t\t\t\thc.Add( cell.HitsLeft );\n\t\t\t\thc.Add( cell.HasDrone );\n\t\t\t\thc.Add( cell.FlashUntil \u003E now ? cell.FlashKind : 0 );\n\n\t\t\t\tif ( cell.FlashUntil \u003E now ) animating = true;\n\t\t\t\tif ( cell.HasChest \u0026\u0026 now - cell.SpawnAt \u003C 0.25f ) animating = true;\n\t\t\t\tif ( cell.HasDrone \u0026\u0026 now - cell.DroneAt \u003C 0.18f ) animating = true;\n\t\t\t}\n\n\t\thc.Add( R.GoldenRow );\n\t\thc.Add( R.GoldenCol );\n\t\thc.Add( R.LastChain );\n\n\t\t// Anything mid-flight is drawn from inline styles rather than keyframes,\n\t\t// so it needs a clock \u2014 but only while it is actually moving. The rest of\n\t\t// the time this hash is stable and the panel is never rebuilt at all.\n\t\tif ( animating ) hc.Add( (int)(now * 20f) );\n\n\t\treturn hc.ToHashCode();\n\t}\n\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\t// Reactor events\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\tvoid OnPop( int row, int col, double value, bool crit )\n\t{\n\t\tif ( R == null || _floaters.Count \u003E 10 ) return;\n\n\t\tfloat step = 100f / R.Size;\n\t\t_floaters.Add( new Floater {\n\t\t\tText = \u0022\u002B\u0022 \u002B IdleFormat.Short( value ),\n\t\t\tCls  = crit ? \u0022crit\u0022 : \u0022\u0022,\n\t\t\tX = step * col \u002B step * 0.5f,\n\t\t\tY = step * row \u002B step * 0.5f,\n\t\t\tLife = 0.9f,\n\t\t\tMaxLife = 0.9f,\n\t\t} );\n\t}\n\n\tvoid OnToastEvent( string icon, string text, string cls )\n\t{\n\t\t_toasts.Insert( 0, new ToastMsg { Icon = icon, Text = text, Cls = cls, Life = 3.2f } );\n\t\twhile ( _toasts.Count \u003E 4 ) _toasts.RemoveAt( _toasts.Count - 1 );\n\t}\n\n\tvoid OnDetonation( int chain, double payout, bool crit )\n\t{\n\t\tstring shake = crit || chain \u003E= 25 ? \u0022idle-shake-3\u0022\n\t\t\t\t\t : chain \u003E= 12 ? \u0022idle-shake-2\u0022\n\t\t\t\t\t : chain \u003E= 4  ? \u0022idle-shake-1\u0022\n\t\t\t\t\t : \u0022\u0022;\n\n\t\tif ( shake != \u0022\u0022 )\n\t\t{\n\t\t\t_shakeCls   = shake;\n\t\t\t_shakeUntil = Time.Now \u002B (shake == \u0022idle-shake-3\u0022 ? 0.45f : 0.28f);\n\t\t}\n\n\t\tif ( chain \u003C 15 \u0026\u0026 !crit ) return;\n\n\t\tdouble critPower = IdleBalance.CritPower( S?.Level( IdleUpgradeId.CritPower ) ?? 0 );\n\n\t\t_calloutText = crit\n\t\t\t? $\u0022CRIT \u00D7{critPower:F0}  \u00B7  \u002B{IdleFormat.Short( payout )}\u0022\n\t\t\t: $\u0022{chain} CHAIN  \u00B7  \u002B{IdleFormat.Short( payout )}\u0022;\n\n\t\t_calloutCls  = crit ? \u0022crit\u0022 : chain \u003E= 35 ? \u0022mega\u0022 : \u0022big\u0022;\n\t\t_calloutLife = 1f;\n\t}\n\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\t// Input\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\tvoid SetTab( string tab )\n\t{\n\t\tbool changed = _tab != tab;\n\t\t_tab = tab;\n\t\tSound.Play( \u0022UI_Button\u0022 );\n\t\tif ( changed ) TutSatisfy( IdleTutTrigger.TabChanged );\n\t}\n\tvoid SetBuyAmount( int amount ) { _buyAmount = amount; Sound.Play( \u0022UI_Button\u0022 ); }\n\n\tint BuyCount( IdleUpgradeId id )\n\t\t=\u003E _buyAmount \u003C 0 ? Math.Max( 1, R.MaxAffordable( id ) ) : _buyAmount;\n\n\tvoid OnBuy( IdleUpgradeId id )\n\t{\n\t\tif ( R == null ) return;\n\t\tif ( !R.TryBuy( id, BuyCount( id ) ) ) Sound.Play( \u0022Pop_Button_1\u0022 );\n\t}\n\n\tvoid OnBuyPerk( IdlePerkId id )\n\t{\n\t\tif ( R == null ) return;\n\t\tif ( !R.TryBuyPerk( id ) ) Sound.Play( \u0022Pop_Button_1\u0022 );\n\t}\n\n\tvoid OnOverload()  =\u003E R?.Overload();\n\tvoid OnSurge()     =\u003E R?.ActivateSurge();\n\tvoid OnCellClick( int r, int c ) =\u003E R?.ClickCell( r, c );\n\n\tvoid OnClaimOffline() =\u003E R?.ClaimOffline();\n\tvoid OnClaimDaily()   =\u003E R?.ClaimDaily();\n\n\tvoid OnOpenMeltdown()  { _showMeltdown = true;  Sound.Play( \u0022UI_Button\u0022 ); }\n\tvoid OnCloseMeltdown() { _showMeltdown = false; Sound.Play( \u0022UI_Button\u0022 ); }\n\n\tvoid OnOpenSingularity()\n\t{\n\t\t_showSingularity = true;\n\t\tSound.Play( \u0022UI_Button\u0022 );\n\t\tif ( S != null \u0026\u0026 !S.SingularitySeen )\n\t\t{\n\t\t\tS.SingularitySeen = true;\n\t\t\tChainReactionGame.Save?.Save();\n\t\t}\n\t}\n\n\tvoid OnCloseSingularity() { _showSingularity = false; Sound.Play( \u0022UI_Button\u0022 ); }\n\n\tvoid OnSingularity()\n\t{\n\t\t_showSingularity = false;\n\t\tR?.DoSingularity();\n\t\t_tab = \u0022powers\u0022;\n\t}\n\n\tvoid OnBuyPower( IdlePowerId id ) =\u003E R?.BuyPower( id );\n\n\tvoid OnMeltdown()\n\t{\n\t\t_showMeltdown = false;\n\t\tR?.DoMeltdown();\n\t}\n\n\tvoid OnCloseIntro()\n\t{\n\t\t_showIntro = false;\n\t\tif ( S != null )\n\t\t{\n\t\t\tS.IntroShown      = true;\n\t\t\tS.TutorialOffered = true;\n\t\t\tChainReactionGame.Save?.Save();\n\t\t}\n\t\tSound.Play( \u0022PlayButton\u0022 );\n\t}\n\n\tvoid OnIntroTutorial()\n\t{\n\t\tif ( S != null )\n\t\t{\n\t\t\tS.IntroShown = true;\n\t\t\tChainReactionGame.Save?.Save();\n\t\t}\n\t\tSound.Play( \u0022PlayButton\u0022 );\n\t\tStartTutorial();\n\t}\n\n\t/// \u003Csummary\u003EThe ? button in the top bar \u2014 replayable from anywhere, any time.\u003C/summary\u003E\n\tvoid OnStartTutorial()\n\t{\n\t\tSound.Play( \u0022UI_Button\u0022 );\n\t\tif ( _tutRunning ) { EndTutorial( completed: true ); return; }\n\t\tStartTutorial();\n\t}\n\n\tvoid OnClaimContract( int slot )\n\t{\n\t\tif ( R == null ) return;\n\t\tif ( !R.ClaimContract( slot ) ) Sound.Play( \u0022Pop_Button_1\u0022 );\n\t}\n\n\tvoid OnBack()\n\t{\n\t\tSound.Play( \u0022UI_Button\u0022 );\n\t\tGame?.ReturnToMenu();\n\t}\n\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\t// Class helpers\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\tstring Pct( float f ) =\u003E (Math.Clamp( f, 0f, 1f ) * 100f).ToString( \u0022F1\u0022 );\n\n\tstring GlowClass()        =\u003E R.SurgeActive ? \u0022surging\u0022 : \u0022\u0022;\n\tstring SurgeClass()       =\u003E R.SurgeActive ? \u0022surging\u0022 : \u0022\u0022;\n\t// Surge is the louder event and its styles are declared first, so a cascade\n\t// would otherwise paint over it. Let surge own the board while it lasts.\n\tstring CascadeClass()     =\u003E R.CascadeActive \u0026\u0026 !R.SurgeActive ? \u0022cascading\u0022 : \u0022\u0022;\n\tstring SurgeReadyClass()  =\u003E R.SurgeReady ? \u0022ready\u0022 : \u0022\u0022;\n\tstring OverloadClass()    =\u003E R.OverloadReady ? \u0022ready\u0022 : \u0022cooling\u0022;\n\tstring BoardShake()       =\u003E Time.Now \u003C _shakeUntil ? _shakeCls : \u0022\u0022;\n\tstring TabClass( string t )     =\u003E _tab == t ? \u0022on\u0022 : \u0022\u0022;\n\tstring BuyAmtClass( int amt )   =\u003E _buyAmount == amt ? \u0022on\u0022 : \u0022\u0022;\n\tstring MaxedClass( bool maxed ) =\u003E maxed ? \u0022maxed\u0022 : \u0022\u0022;\n\tstring CapClass()  =\u003E R.FragmentsToday \u003E= R.DailyFragmentCap ? \u0022capped\u0022 : \u0022\u0022;\n\tstring FeedClass() =\u003E R.FeedStarved ? \u0022starved\u0022 : \u0022\u0022;\n\tstring MeltdownStripClass() =\u003E R.CanMeltdown ? \u0022ready\u0022 : \u0022\u0022;\n\n\tstring StreakPipClass( int day )\n\t{\n\t\tint streak = S?.DailyStreak ?? 1;\n\t\tif ( day == streak ) return \u0022on today\u0022;\n\t\treturn day \u003C streak ? \u0022on\u0022 : \u0022\u0022;\n\t}\n\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\t// Text helpers \u2014 one composed string per element (see note at top)\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\t// \u2500\u2500 Daily contracts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tstring ContractsCardClass() =\u003E R.ContractReady ? \u0022ready\u0022 : \u0022\u0022;\n\n\tstring ContractClass( int slot )\n\t{\n\t\tif ( IdleContracts.IsClaimed( S, slot ) )  return \u0022claimed\u0022;\n\t\tif ( IdleContracts.IsComplete( S, slot ) ) return \u0022ready\u0022;\n\t\treturn \u0022\u0022;\n\t}\n\n\tstring ContractIcon( int slot )\n\t{\n\t\tif ( IdleContracts.IsClaimed( S, slot ) ) return \u0022\u2705\u0022;\n\t\treturn IdleContracts.At( S, slot )?.Icon ?? \u0022\uD83D\uDCCB\u0022;\n\t}\n\n\tstring ContractDescLabel( int slot )\n\t{\n\t\tvar c = IdleContracts.At( S, slot );\n\t\tif ( c == null ) return \u0022\u0022;\n\t\treturn c.Desc( IdleContracts.Target( S, slot ) );\n\t}\n\n\tstring ContractRewardLabel( int slot )\n\t{\n\t\tif ( IdleContracts.IsClaimed( S, slot ) ) return \u0022CLAIMED\u0022;\n\t\tif ( IdleContracts.IsComplete( S, slot ) ) return \u0022CLAIM \u2192\u0022;\n\t\treturn $\u0022\u002B{IdleContracts.Reward( S, slot )} \uD83D\uDCA0\u0022;\n\t}\n\n\tstring ContractProgLabel( int slot )\n\t{\n\t\tvar c = IdleContracts.At( S, slot );\n\t\tif ( c == null ) return \u0022\u0022;\n\n\t\tdouble cur = Math.Min( IdleContracts.Progress( S, slot ), IdleContracts.Target( S, slot ) );\n\t\tdouble tgt = IdleContracts.Target( S, slot );\n\n\t\tif ( c.Id == \u0022runtime\u0022 )\n\t\t\treturn $\u0022{IdleFormat.Duration( cur )} / {IdleFormat.Duration( tgt )}\u0022;\n\n\t\treturn c.BigNumbers\n\t\t\t? $\u0022{IdleFormat.Short( cur )} / {IdleFormat.Short( tgt )}\u0022\n\t\t\t: $\u0022{(long)cur} / {(long)tgt}\u0022;\n\t}\n\n\tstring ContractResetLabel()\n\t\t=\u003E \u0022resets in \u0022 \u002B IdleFormat.Duration( IdleContracts.SecondsUntilReset() );\n\n\tstring ContractFootLabel()\n\t{\n\t\tint streak = S?.ContractStreak ?? 0;\n\t\tif ( IdleContracts.AllClaimed( S ) )\n\t\t\treturn streak \u003E 1\n\t\t\t\t? $\u0022All three cleared \u00B7 {streak} days running\u0022\n\t\t\t\t: \u0022All three cleared. Back tomorrow.\u0022;\n\n\t\treturn \u0022Clear all three for a \u00D73 boost and double Fragments on the last one.\u0022;\n\t}\n\n\t// \u2500\u2500 Next goal pin \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\t/// \u003Csummary\u003E\n\t/// The unfinished Reactor Log entry closest to completion. Picking by\n\t/// fraction rather than by list order means the pin always shows something\n\t/// nearly done, which is the only version of this card that pulls.\n\t///\n\t/// Cached: BuildHash runs every frame and the markup reads this five more\n\t/// times per rebuild, so recomputing it on every access would walk the\n\t/// achievement list a hundred-odd times a frame for a card that changes\n\t/// once every few minutes.\n\t/// \u003C/summary\u003E\n\tIdleAchievement _nextGoal;\n\tfloat _nextGoalAt;\n\n\tIdleAchievement NextGoal =\u003E _nextGoal;\n\n\tvoid RefreshNextGoal()\n\t{\n\t\tvar s = S;\n\t\tif ( s == null ) { _nextGoal = null; return; }\n\n\t\tIdleAchievement best = null;\n\t\tfloat bestFrac = -1f;\n\n\t\tforeach ( var a in IdleAchievements.All )\n\t\t{\n\t\t\tif ( s.HasAchievement( a.Id ) ) continue;\n\t\t\tfloat f = a.Fraction( s );\n\t\t\tif ( f \u003E bestFrac ) { bestFrac = f; best = a; }\n\t\t}\n\t\t_nextGoal = best;\n\t}\n\n\tstring NextGoalProgLabel()\n\t{\n\t\tvar a = NextGoal;\n\t\tif ( a == null ) return \u0022\u0022;\n\t\treturn ProgressLabel( a );\n\t}\n\n\tstring NextGoalRewardLabel()\n\t{\n\t\tvar a = NextGoal;\n\t\tif ( a == null ) return \u0022\u0022;\n\t\treturn RewardLabel( a );\n\t}\n\n\tstring CoresLabel() =\u003E IdleFormat.Short( Cores );\n\tstring RateLabel()  =\u003E IdleFormat.Short( R.Cps );\n\tstring FluxLabel()  =\u003E (S?.Flux ?? 0).ToString();\n\tstring FragLabel()  =\u003E (ChainReactionGame.Save?.TotalFragments ?? 0).ToString();\n\n\tstring GlobalMultLabel() =\u003E \u0022\u00D7\u0022 \u002B IdleFormat.Short( R.GlobalMultiplier );\n\tstring ExponentLabel()   =\u003E R.ChainExponent.ToString( \u0022F2\u0022 );\n\tstring LastChainLabel()  =\u003E $\u0022{R.LastChain} chests\u0022;\n\tstring BestChainLabel()  =\u003E $\u0022{S.BestChain} chests\u0022;\n\tstring BoardLabel()      =\u003E $\u0022{R.Size} \u00D7 {R.Size}\u0022;\n\n\tstring FeedLabel() =\u003E R.FeedStarved ? \u0022starving\u0022 : \u0022keeping up\u0022;\n\n\tstring SurgeBoostLabel() =\u003E $\u0022SURGE \u00D7{IdleBalance.SurgeMultiplier:F0}\u0022;\n\tstring BoostLabel( IdleReactor.Boost b ) =\u003E $\u0022{b.Label} \u00D7{b.Mult:F1}\u0022;\n\tstring SecondsLabel( float s ) =\u003E $\u0022{s:F0}s\u0022;\n\n\tstring CondenserLabel()\n\t\t=\u003E $\u0022next at {IdleFormat.Short( R.FragmentCost )} Cores \u00B7 price climbs with each one\u0022;\n\n\tstring FragTodayLabel() =\u003E $\u0022{R.FragmentsToday} / {R.DailyFragmentCap}\u0022;\n\n\tstring TierBlurb( int index, bool unlocked )\n\t{\n\t\tvar tier = IdleBalance.Tiers[index];\n\t\tif ( unlocked ) return tier.Blurb;\n\n\t\t// Tier Memory can drop the gate to zero, so read the effective figure\n\t\t// rather than the catalog one.\n\t\tdouble need = IdleBalance.TierRequirement( index, S?.PowerLevel( IdlePowerId.TierSkip ) ?? 0 );\n\t\treturn $\u0022unlocks at {IdleFormat.Short( need )} cycle Cores\u0022;\n\t}\n\n\tstring GoldenTimerLabel() =\u003E R.GoldenTimeLeft.ToString( \u0022F0\u0022 );\n\n\tstring OverloadLabel()\n\t\t=\u003E R.OverloadReady ? \u0022\u26A1 OVERLOAD\u0022 : $\u0022{R.OverloadCooldownLeft:F1}s\u0022;\n\n\tstring OverloadSubLabel() =\u003E $\u0022\u00D7{IdleBalance.OverloadMultiplier:F1} burst \u00B7 SPACE\u0022;\n\tstring OverloadFillPct()  =\u003E ((1f - R.OverloadCooldownFraction) * 100f).ToString( \u0022F0\u0022 );\n\tstring UnleashLabel()     =\u003E $\u0022UNLEASH \u00D7{IdleBalance.SurgeMultiplier:F0}\u0022;\n\n\tstring SurgeLabel()\n\t{\n\t\tif ( R.SurgeActive ) return $\u0022SURGE {R.SurgeTimeLeft:F0}s\u0022;\n\t\tif ( R.SurgeReady )  return \u0022SURGE READY\u0022;\n\t\treturn $\u0022CHARGE {R.Charge * 100f:F0}%\u0022;\n\t}\n\n\tstring MeltdownLabel()\n\t\t=\u003E R.CanMeltdown\n\t\t\t? $\u0022Meltdown available \u2014 \u002B{R.PendingFlux} Flux\u0022\n\t\t\t: $\u0022Meltdown unlocks at {IdleFormat.Short( R.MeltdownRequirement )} cycle Cores \u00B7 {IdleFormat.Short( S.CycleCores )} banked\u0022;\n\n\tstring BuyAmountLabel( int amount ) =\u003E amount \u003C 0 ? \u0022MAX\u0022 : $\u0022\u00D7{amount}\u0022;\n\n\tstring UpgradeClass( bool revealed, bool maxed, bool afford )\n\t{\n\t\tif ( !revealed ) return \u0022locked\u0022;\n\t\tif ( maxed ) return \u0022maxed\u0022;\n\t\treturn afford ? \u0022afford\u0022 : \u0022poor\u0022;\n\t}\n\n\tstring UpgradeIcon( IdleUpgrade up, bool revealed ) =\u003E revealed ? up.Icon : \u0022\uD83D\uDD12\u0022;\n\tstring UpgradeName( IdleUpgrade up, bool revealed ) =\u003E revealed ? up.Name : \u0022Locked\u0022;\n\n\tstring UpgradeDesc( IdleUpgrade up, bool revealed )\n\t\t=\u003E revealed ? up.Desc : $\u0022Reach {IdleFormat.Short( up.RevealAt )} lifetime Cores to reveal.\u0022;\n\n\tstring LevelLabel( IdleUpgrade up, int lvl, bool revealed )\n\t{\n\t\tif ( !revealed ) return \u0022\u2014\u0022;\n\t\treturn up.MaxLevel \u003C 100 ? $\u0022Lv {lvl} / {up.MaxLevel}\u0022 : $\u0022Lv {lvl}\u0022;\n\t}\n\n\tstring CostLabel( bool maxed, double cost, int count )\n\t{\n\t\tif ( maxed ) return \u0022MAXED\u0022;\n\t\treturn count \u003E 1\n\t\t\t? $\u0022{IdleFormat.Short( cost )}  \u00D7{count}\u0022\n\t\t\t: IdleFormat.Short( cost );\n\t}\n\n\tstring PerkLevelLabel( IdlePerk p, int lvl ) =\u003E p.Uncapped ? $\u0022Lv {lvl}\u0022 : $\u0022Lv {lvl} / {p.MaxLevel}\u0022;\n\tstring PerkCostLabel( bool maxed, int cost ) =\u003E maxed ? \u0022MAXED\u0022 : $\u0022\uD83D\uDD37 {cost}\u0022;\n\n\t/// \u003Csummary\u003E\n\t/// Containment Field is deliberately hidden until the player has enough Flux\n\t/// history to understand what a sink is for \u2014 offered at Flux 0 it just reads\n\t/// as a bad first purchase.\n\t/// \u003C/summary\u003E\n\tIEnumerable\u003CIdlePerk\u003E VisiblePerks()\n\t\t=\u003E IdleUpgradeCatalog.Perks.Where( p =\u003E p.RevealAtFluxEarned \u003C= 0\n\t\t\t\t\t\t\t\t\t\t\t || (S?.FluxEarned ?? 0) \u003E= p.RevealAtFluxEarned\n\t\t\t\t\t\t\t\t\t\t\t || (S?.PerkLevel( p.Id ) ?? 0) \u003E 0 );\n\n\tstring PowerLevelLabel( IdlePower p, int lvl ) =\u003E $\u0022Lv {lvl} / {p.MaxLevel}\u0022;\n\tstring PowerCostLabel( bool maxed, int cost ) =\u003E maxed ? \u0022MAXED\u0022 : $\u0022\u2734\uFE0F {cost}\u0022;\n\n\tstring ShardHeadLabel() =\u003E $\u0022\u2734\uFE0F {S.Shards}\u0022;\n\n\tstring ShardSubLabel()\n\t\t=\u003E $\u0022{S.ShardsEarned} earned \u00B7 \u00D7{IdleBalance.ShardMultiplier( S.ShardsEarned ):F2} permanent income\u0022;\n\n\tstring SingStripClass() =\u003E R.CanSingularity ? \u0022ready\u0022 : \u0022\u0022;\n\n\tstring SingularityLabel()\n\t{\n\t\tif ( R.CanSingularity )\n\t\t\treturn $\u0022Singularity available \u2014 \u002B{R.PendingShards} Shards\u0022;\n\n\t\treturn $\u0022Singularity unlocks at {IdleBalance.SingularityRequirement} lifetime Flux \u00B7 {S.FluxEarned} earned\u0022;\n\t}\n\n\tstring SingPayoutLabel()  =\u003E \u0022\u002B\u0022 \u002B R.PendingShards.ToString();\n\tstring BurnedFluxLabel()  =\u003E $\u0022{S.FluxEarned} lifetime Flux\u0022;\n\n\tstring ShardKeepLabel()\n\t{\n\t\tint after = S.ShardsEarned \u002B R.PendingShards;\n\t\treturn $\u0022\u00D7{IdleBalance.ShardMultiplier( after ):F2} income from Shards\u0022;\n\t}\n\n\tstring FluxHeadLabel() =\u003E $\u0022\uD83D\uDD37 {S.Flux}\u0022;\n\n\tstring FluxSubLabel()\n\t\t=\u003E $\u0022{S.FluxEarned} earned \u00B7 \u00D7{IdleBalance.FluxMultiplier( S.FluxEarned ):F2} permanent income\u0022;\n\n\tstring LogHeadLabel() =\u003E $\u0022{IdleAchievements.CompletedCount( S )} / {IdleAchievements.All.Count}\u0022;\n\n\tstring AchIcon( IdleAchievement a, bool done ) =\u003E done ? a.Icon : \u0022\uD83D\uDD18\u0022;\n\n\tstring RewardLabel( IdleAchievement a )\n\t{\n\t\tif ( a.RewardFlux \u003E 0 \u0026\u0026 a.RewardFrags \u003E 0 ) return $\u0022\u002B{a.RewardFlux}\uD83D\uDD37  \u002B{a.RewardFrags}\uD83D\uDCA0\u0022;\n\t\tif ( a.RewardFlux \u003E 0 ) return $\u0022\u002B{a.RewardFlux}\uD83D\uDD37\u0022;\n\t\treturn $\u0022\u002B{a.RewardFrags}\uD83D\uDCA0\u0022;\n\t}\n\n\tstring ProgressLabel( IdleAchievement a )\n\t{\n\t\tdouble cur = a.Progress( S );\n\t\treturn a.BigNumbers\n\t\t\t? $\u0022{IdleFormat.Short( cur )} / {IdleFormat.Short( a.Target )}\u0022\n\t\t\t: $\u0022{(long)cur} / {(long)a.Target}\u0022;\n\t}\n\n\t// \u2500\u2500 Modal labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tstring OfflineSubLabel()\n\t\t=\u003E $\u0022The reactor ran for {IdleFormat.Duration( R.PendingOfflineSeconds )} at {IdleFormat.Pct( R.OfflineEfficiency )} efficiency.\u0022;\n\n\tstring OfflineAmountLabel() =\u003E \u0022\u002B\u0022 \u002B IdleFormat.Short( R.PendingOfflineCores );\n\n\tstring OfflineFragLabel() =\u003E $\u0022condensing {R.PendingOfflineFrags} Fragments\u0022;\n\n\tstring OfflineWarnLabel()\n\t\t=\u003E $\u0022Storage filled up after {R.OfflineCapHours:F0}h. Upgrade Core Storage to bank longer trips.\u0022;\n\n\tstring DailyTitleLabel() =\u003E $\u0022Day {S?.DailyStreak ?? 1}\u0022;\n\tstring DailyMultLabel()  =\u003E $\u0022\u00D7{IdleBalance.DailyReward( S?.DailyStreak ?? 1 ).mult:F1}\u0022;\n\n\tstring DailyRewardLabel()\n\t{\n\t\tvar reward = IdleBalance.DailyReward( S?.DailyStreak ?? 1 );\n\t\treturn $\u0022for {IdleFormat.Duration( reward.seconds )}  \u00B7  \u002B{reward.frags} Fragments\u0022;\n\t}\n\n\tstring MeltPayoutLabel()  =\u003E \u0022\u002B\u0022 \u002B R.PendingFlux;\n\tstring BurnedCoresLabel() =\u003E $\u0022{IdleFormat.Short( Cores )} Cores\u0022;\n\n\tstring MeltMultBeforeLabel()\n\t\t=\u003E \u0022\u00D7\u0022 \u002B IdleBalance.FluxMultiplier( S?.FluxEarned ?? 0 ).ToString( \u0022F2\u0022 );\n\n\tstring MeltMultAfterLabel()\n\t\t=\u003E \u0022\u00D7\u0022 \u002B IdleBalance.FluxMultiplier( (S?.FluxEarned ?? 0) \u002B R.PendingFlux ).ToString( \u0022F2\u0022 );\n\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\t// Cell rendering\n\t// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\tstring CellClass( IdleReactor.Cell cell, bool golden )\n\t{\n\t\tif ( golden ) return \u0022golden\u0022;\n\n\t\tstring cls = cell.HasChest ? \u0022filled\u0022 : \u0022empty\u0022;\n\n\t\tif ( cell.FlashUntil \u003E Time.Now )\n\t\t{\n\t\t\tcls \u002B= cell.FlashKind switch\n\t\t\t{\n\t\t\t\t3 =\u003E \u0022 fx-drone\u0022,\n\t\t\t\t2 =\u003E \u0022 fx-kill\u0022,\n\t\t\t\t_ =\u003E \u0022 fx-blast\u0022,\n\t\t\t};\n\t\t}\n\n\t\tif ( cell.HasDrone ) cls \u002B= \u0022 armed\u0022;\n\n\t\tif ( cell.HasChest )\n\t\t{\n\t\t\tcls \u002B= cell.Chest switch\n\t\t\t{\n\t\t\t\tGridManager.ChestType.Gem       =\u003E \u0022 c-gem\u0022,\n\t\t\t\tGridManager.ChestType.BombChest =\u003E \u0022 c-bomb\u0022,\n\t\t\t\tGridManager.ChestType.Locked    =\u003E \u0022 c-locked\u0022,\n\t\t\t\tGridManager.ChestType.Cursed    =\u003E \u0022 c-cursed\u0022,\n\t\t\t\tGridManager.ChestType.Void      =\u003E \u0022 c-void\u0022,\n\t\t\t\tGridManager.ChestType.Boss      =\u003E \u0022 c-boss\u0022,\n\t\t\t\t_                               =\u003E \u0022 c-normal\u0022,\n\t\t\t};\n\t\t}\n\n\t\treturn cls;\n\t}\n\n\t/// \u003Csummary\u003E\n\t/// Spawn pop, computed here rather than as a keyframe: the panel can rebuild\n\t/// at any moment and a class-driven animation would restart mid-flight.\n\t/// \u003C/summary\u003E\n\tstring ChestStyle( IdleReactor.Cell cell )\n\t{\n\t\tfloat age = Time.Now - cell.SpawnAt;\n\t\tif ( age \u003E= 0.25f || age \u003C 0f ) return \u0022\u0022;\n\n\t\tfloat scale = 0.4f \u002B 0.6f * (age / 0.25f);\n\t\treturn $\u0022transform:scale({scale.ToString( \u0022F2\u0022 )});\u0022;\n\t}\n\n\t/// \u003Csummary\u003EDrone drop \u2014 falls onto the cell as the volley arms.\u003C/summary\u003E\n\tstring DroneStyle( IdleReactor.Cell cell )\n\t{\n\t\tfloat age = Time.Now - cell.DroneAt;\n\t\tif ( age \u003E= 0.18f || age \u003C 0f ) return \u0022\u0022;\n\n\t\tfloat t = age / 0.18f;\n\t\tfloat scale = 2.0f - 1.0f * t;\n\t\treturn $\u0022transform:scale({scale.ToString( \u0022F2\u0022 )});opacity:{t.ToString( \u0022F2\u0022 )};\u0022;\n\t}\n\n\tstring ChestGlyph( IdleReactor.Cell cell ) =\u003E cell.Chest switch\n\t{\n\t\tGridManager.ChestType.BombChest =\u003E \u0022\uD83D\uDCA3\u0022,\n\t\tGridManager.ChestType.Locked    =\u003E \u0022\uD83D\uDD12\u0022,\n\t\tGridManager.ChestType.Cursed    =\u003E \u0022\uD83D\uDC80\u0022,\n\t\tGridManager.ChestType.Void      =\u003E \u0022\uD83C\uDF11\u0022,\n\t\tGridManager.ChestType.Boss      =\u003E \u0022\uD83D\uDC51\u0022,\n\t\tGridManager.ChestType.Gem       =\u003E \u0022\uD83D\uDC8E\u0022,\n\t\t_                               =\u003E \u0022\uD83C\uDF81\u0022,\n\t};\n}\n"},{"Ident":"playnplay.chain_reaction","Path":"styles/form/_switch.scss","FileName":"_switch.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"\r\n$primary: red !default;\r\n$primary-alt: white !default;\r\n\r\n$switch-padding: 6px !default;\r\n\r\n.checkbox.switch\r\n{\r\n\tcursor: pointer;\r\n\r\n\t\u003E .checkmark\r\n\t{\r\n\t\tfont-size: 22px;\r\n\t\tborder: 0px solid $primary;\r\n\t\tborder-radius: 100px;\r\n\t\ttext-align: center;\r\n\t\tjustify-content: center;\r\n\t\talign-items: center;\r\n\t\tcolor: $primary-alt;\r\n\t\tpadding: $switch-padding;\r\n\t\tpadding-right: 32px;\r\n\t\tpadding-left: $switch-padding;\r\n\t\ttransition: all 0.3s ease;\r\n\t\tbackground-color: rgba( $primary, 0.1 );\r\n\r\n\t\t\u003E .handle\r\n\t\t{\r\n\t\t\tbackground-color: $primary-alt;\r\n\t\t\twidth: 20px;\r\n\t\t\theight: 20px;\r\n\t\t\tborder-radius: 100px;\r\n\t\t\tbox-shadow: 2px 2px 12px black;\r\n\t\t}\r\n\t}\r\n\r\n\t\u0026.checked\r\n\t{\r\n\t\t\u003E .checkmark\r\n\t\t{\r\n\t\t\tbackground-color: $primary;\r\n\t\t\tpadding-left: 32px;\r\n\t\t\tpadding-right: $switch-padding;\r\n\t\t}\r\n\t}\r\n\r\n\t\u0026:active\r\n\t{\r\n\t\ttransform: scale( 0.9 );\r\n\t\ttransform-origin: 20px 50%;\r\n\t}\r\n}\r\n"},{"Ident":"playnplay.chain_reaction","Path":"ui/controls/vectorcontrol.cs.scss","FileName":"vectorcontrol.cs.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"VectorControl\r\n{\r\n\tgap: 2px;\r\n\tflex-grow: 1;\r\n}\r\n\r\nVectorControl NumberEntry\r\n{\r\n\tflex-basis: 50%;\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"ui/controls/enumcontrol.cs.scss","FileName":"enumcontrol.cs.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"EnumControl\r\n{\r\n\tgap: 2px;\r\n\tflex-grow: 1;\r\n}\r\n\r\nEnumControl DropDown,\r\nEnumControl ButtonGroup\r\n{\r\n\tborder-radius: 8px;\r\n\tbackground-color: #000a;\r\n\tflex-grow: 1;\r\n}\r\n\r\nEnumControl DropDown\r\n{\r\n\tflex-grow: 1;\r\n\tmin-height: 32px;\r\n}\r\n\r\nEnumControl ButtonGroup\r\n{\r\n\tborder-radius: 12px;\r\n\toverflow: hidden;\r\n\tmin-height: 32px;\r\n\r\n\tButton\r\n\t{\r\n\t\tflex-grow: 1;\r\n\t\tjustify-content: center;\r\n\t\talign-items: center;\r\n\t\tgap: 4px;\r\n\t\tcolor: #aaa;\r\n\t\tfont-size: 1rem;\r\n\t\tcursor: pointer;\r\n\r\n\t\t.icon\r\n\t\t{\r\n\t\t\tcolor: #08f;\r\n\t\t}\r\n\r\n\t\t\u0026:hover\r\n\t\t{\r\n\t\t\tcolor: #ddd;\r\n\r\n\t\t\t.icon\r\n\t\t\t{\r\n\t\t\t\tcolor: #3af;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\u0026:active\r\n\t\t{\r\n\t\t\tbackground-color: #04a;\r\n\t\t\tcolor: white;\r\n\t\t\ttransform: translateX( 1px ) translateY( 1px );\r\n\r\n\t\t\t.icon\r\n\t\t\t{\r\n\t\t\t\tcolor: #fff;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\u0026.active\r\n\t\t{\r\n\t\t\tbackground-color: #08f;\r\n\t\t\tcolor: white;\r\n\t\t\tpointer-events: none;\r\n\r\n\t\t\t.icon\r\n\t\t\t{\r\n\t\t\t\tcolor: #fff;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"ui/controls/color/colorpickercontrol.cs.scss","FileName":"colorpickercontrol.cs.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"ColorPickerControl\r\n{\r\n\tflex-direction: column;\r\n\tflex-shrink: 0;\r\n\tgap: 0.5rem;\r\n\tmargin: 1rem;\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"game/idle/ui/idlehud.razor.scss","FileName":"idlehud.razor.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   THE REACTOR \u2014 Idle mode HUD\n   Palette leans teal/amber against the same deep purple-black the rest of\n   10XPLOSION uses, so it reads as \u0022the same game, different room\u0022.\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\nroot {\n    /* Declared so no panel in this tree ever claims the OS pointer -- a hovered\n       panel outranks Mouse.CursorType, and one that declares nothing still claims\n       the default arrow. GameCursor draws the cursor instead. */\n    cursor: none;\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    pointer-events: none;\n    overflow: hidden;\n    /* Sits above the wave HUD\u0027s layers \u2014 those hide themselves in this mode,\n       but ScreenShaker and PopupLayer share the same ScreenPanel. */\n    z-index: 200;\n}\n\n.idle-root {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    display: flex;\n    flex-direction: column;\n    pointer-events: none;\n}\n\n/* \u2500\u2500 Backdrop \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-bg {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    background-color: #04070d;\n}\n\n.idle-bg-glow {\n    position: absolute;\n    top: -20%; left: 15%;\n    width: 70%;\n    height: 120%;\n    border-radius: 50%;\n    background-color: rgba(20, 130, 130, 0.10);\n    filter: blur(120px);\n    animation: idle-breathe 9s ease-in-out infinite alternate;\n    transition: background-color 0.5s;\n}\n\n.idle-bg-glow.surging {\n    background-color: rgba(251, 146, 60, 0.22);\n}\n\n@keyframes idle-breathe {\n    0%   { transform: scale(1.00); opacity: 0.75; }\n    100% { transform: scale(1.18); opacity: 1.00; }\n}\n\n.idle-particle {\n    position: absolute;\n    bottom: -12px;\n    border-radius: 50%;\n    animation: idle-rise linear infinite;\n}\n\n@keyframes idle-rise {\n    0%   { transform: translateY(0px);      opacity: 0; }\n    12%  {                                  opacity: 1; }\n    100% { transform: translateY(-1180px);  opacity: 0; }\n}\n\n/* \u2500\u2500 Top bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-top {\n    position: relative;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 16px;\n    padding: 12px 22px;\n    flex-shrink: 0;\n    background-color: rgba(6, 12, 20, 0.82);\n    border-bottom: 2px solid rgba(45, 212, 191, 0.18);\n    pointer-events: all;\n    overflow: hidden;\n}\n\n.idle-back {\n    width: 46px;\n    height: 46px;\n    flex-shrink: 0;\n    border-radius: 10px;\n    border: 2px solid rgba(148, 163, 184, 0.28);\n    background-color: rgba(15, 23, 35, 0.9);\n    color: rgba(226, 232, 240, 0.85);\n    font-size: 22px;\n    font-weight: 900;\n    align-items: center;\n    justify-content: center;\n    cursor: none;\n    pointer-events: all;\n    transition: background-color 0.1s, border-color 0.1s;\n}\n.idle-back:hover {\n    background-color: rgba(45, 212, 191, 0.18);\n    border-color: rgba(45, 212, 191, 0.6);\n}\n\n/* Without an explicit no-shrink the currency row squeezes this to nothing and\n   the title wraps one letter per line. */\n.idle-brand {\n    display: flex;\n    flex-direction: column;\n    flex-shrink: 0;\n    flex-grow: 0;\n}\n\n.idle-brand-title {\n    white-space: nowrap;\n    font-size: 22px;\n    font-weight: 900;\n    letter-spacing: 0.24em;\n    color: #5eead4;\n    text-shadow: 0px 0px 18px rgba(45, 212, 191, 0.45);\n}\n\n.idle-brand-sub {\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: 0.16em;\n    text-transform: uppercase;\n    color: rgba(148, 163, 184, 0.55);\n}\n\n.idle-currencies {\n    display: flex;\n    flex-direction: row;\n    gap: 8px;\n    margin-left: auto;\n    flex-shrink: 0;\n    flex-grow: 0;\n}\n\n.idle-cur {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 7px;\n    padding: 7px 13px;\n    border-radius: 10px;\n    background-color: rgba(12, 20, 30, 0.92);\n    border: 2px solid rgba(100, 116, 139, 0.22);\n    width: 172px;\n    flex-shrink: 0;\n    flex-grow: 0;\n    overflow: hidden;\n}\n\n.idle-cur-icon { font-size: 17px; flex-shrink: 0; }\n\n.idle-cur-val {\n    font-size: 19px;\n    font-weight: 900;\n    color: #e2e8f0;\n    white-space: nowrap;\n    flex-shrink: 0;\n}\n\n.idle-cur-lbl {\n    font-size: 9px;\n    font-weight: 800;\n    letter-spacing: 0.06em;\n    text-transform: uppercase;\n    color: rgba(148, 163, 184, 0.55);\n    margin-left: auto;\n    white-space: nowrap;\n    flex-shrink: 0;\n}\n\n.idle-cur.cores { border-color: rgba(45, 212, 191, 0.45); }\n.idle-cur.cores .idle-cur-val { color: #5eead4; }\n.idle-cur.rate  .idle-cur-val { color: #a7f3d0; font-size: 17px; }\n.idle-cur.flux  { border-color: rgba(96, 165, 250, 0.42); width: 132px; }\n.idle-cur.flux  .idle-cur-val { color: #93c5fd; }\n.idle-cur.frag  { border-color: rgba(167, 139, 250, 0.42); width: 152px; }\n.idle-cur.frag  .idle-cur-val { color: #c4b5fd; }\n\n/* \u2500\u2500 Body layout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-body {\n    position: relative;\n    display: flex;\n    flex-direction: row;\n    align-items: stretch;\n    gap: 14px;\n    padding: 14px 20px 16px 20px;\n    flex-grow: 1;\n    flex-shrink: 1;\n    height: 0;          /* lets flex-grow decide the height, so children can scroll */\n    overflow: hidden;\n}\n\n.idle-col { display: flex; flex-direction: column; gap: 12px; }\n\n.idle-left {\n    width: 300px;\n    flex-shrink: 0;\n    overflow-y: scroll;\n    overflow-x: hidden;\n    pointer-events: all;\n}\n\n.idle-right {\n    width: 400px;\n    flex-shrink: 0;\n    overflow: hidden;\n    pointer-events: all;\n}\n\n.idle-center {\n    flex-grow: 1;\n    align-items: center;\n    justify-content: center;\n    gap: 16px;\n    pointer-events: all;\n}\n\n/* \u2500\u2500 Info cards \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-card {\n    background-color: rgba(10, 17, 26, 0.88);\n    border: 2px solid rgba(100, 116, 139, 0.18);\n    border-radius: 12px;\n    padding: 12px 14px;\n    display: flex;\n    flex-direction: column;\n    gap: 6px;\n    flex-shrink: 0;\n}\n\n.idle-card-hd {\n    font-size: 11px;\n    font-weight: 900;\n    letter-spacing: 0.18em;\n    text-transform: uppercase;\n    color: rgba(94, 234, 212, 0.75);\n    margin-bottom: 4px;\n}\n\n.idle-card-note {\n    font-size: 11px;\n    font-weight: 600;\n    color: rgba(148, 163, 184, 0.65);\n}\n.idle-card-note.dim { color: rgba(251, 191, 36, 0.75); }\n\n.idle-stat-row {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: space-between;\n    gap: 8px;\n}\n\n.idle-stat-lbl {\n    font-size: 12px;\n    font-weight: 600;\n    color: rgba(148, 163, 184, 0.75);\n}\n\n.idle-stat-val {\n    font-size: 14px;\n    font-weight: 900;\n    color: #e2e8f0;\n}\n.idle-stat-val.hot    { color: #5eead4; }\n.idle-stat-val.capped  { color: #fbbf24; }\n.idle-stat-val.starved { color: #f87171; }\n\n\n/* \u2500\u2500 Reactor rank card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.idle-rank-row {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 8px;\n    padding: 3px 6px;\n    border-radius: 6px;\n    border: 2px solid rgba(0, 0, 0, 0);\n}\n\n.idle-rank-row.me {\n    background-color: rgba(94, 234, 212, 0.10);\n    border-color: rgba(94, 234, 212, 0.35);\n}\n\n.idle-rank-pos {\n    font-size: 11px;\n    font-weight: 900;\n    color: rgba(148, 163, 184, 0.75);\n    width: 26px;\n    flex-shrink: 0;\n}\n.idle-rank-row.me .idle-rank-pos { color: #5eead4; }\n\n.idle-rank-name {\n    font-size: 12px;\n    font-weight: 700;\n    color: rgba(226, 232, 240, 0.85);\n    flex-grow: 1;\n    text-overflow: ellipsis;\n    overflow: hidden;\n    white-space: nowrap;\n}\n.idle-rank-row.me .idle-rank-name { color: #e2e8f0; }\n\n.idle-rank-val {\n    font-size: 12px;\n    font-weight: 900;\n    color: rgba(148, 163, 184, 0.9);\n    flex-shrink: 0;\n}\n.idle-rank-row.me .idle-rank-val { color: #5eead4; }\n\n.idle-card-note.up {\n    color: rgba(74, 222, 128, 0.9);\n    font-weight: 700;\n}\n.idle-bar-track {\n    width: 100%;\n    height: 10px;\n    border-radius: 5px;\n    background-color: rgba(30, 41, 59, 0.9);\n    overflow: hidden;\n}\n\n.idle-bar-fill {\n    height: 100%;\n    border-radius: 5px;\n    background-color: #5eead4;\n    transition: width 0.2s;\n}\n.idle-bar-fill.frag { background-color: #a78bfa; }\n\n/* Boosts */\n.idle-boost {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 8px;\n    padding: 6px 8px;\n    border-radius: 8px;\n    background-color: rgba(30, 41, 59, 0.7);\n    border-left: 3px solid rgba(94, 234, 212, 0.7);\n}\n.idle-boost.surge {\n    border-left-color: #fb923c;\n    background-color: rgba(120, 53, 15, 0.45);\n}\n.idle-boost-icon { font-size: 15px; }\n.idle-boost-lbl  { font-size: 12px; font-weight: 800; color: #e2e8f0; }\n.idle-boost-time {\n    margin-left: auto;\n    font-size: 11px;\n    font-weight: 900;\n    color: rgba(251, 191, 36, 0.9);\n}\n\n/* Chest tiers */\n.idle-tier {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 9px;\n    padding: 5px 0px;\n    opacity: 0.42;\n}\n.idle-tier.on { opacity: 1; }\n\n.idle-tier-icon { font-size: 20px; width: 26px; text-align: center; }\n.idle-tier-body { display: flex; flex-direction: column; flex-grow: 1; }\n.idle-tier-name { font-size: 12px; font-weight: 900; color: #e2e8f0; }\n.idle-tier-blurb {\n    font-size: 10px;\n    font-weight: 600;\n    color: rgba(148, 163, 184, 0.7);\n}\n.idle-tier.on .idle-tier-name { color: #5eead4; }\n\n/* \u2500\u2500 The board \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-board-wrap {\n    position: relative;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    flex-shrink: 0;\n}\n\n.idle-board {\n    display: flex;\n    flex-direction: column;\n    gap: 5px;\n    padding: 14px;\n    border-radius: 16px;\n    background-color: rgba(6, 14, 22, 0.9);\n    border: 2px solid rgba(45, 212, 191, 0.22);\n    box-shadow: 0px 0px 40px rgba(13, 148, 136, 0.14);\n    transition: border-color 0.3s, box-shadow 0.3s;\n}\n\n.idle-board.surging {\n    border-color: rgba(251, 146, 60, 0.65);\n    box-shadow: 0px 0px 60px rgba(251, 146, 60, 0.35);\n}\n\n.idle-row { display: flex; flex-direction: row; gap: 5px; }\n\n.idle-cell {\n    position: relative;\n    border-radius: 9px;\n    background-color: rgba(17, 27, 40, 0.85);\n    border: 2px solid rgba(71, 85, 105, 0.28);\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    flex-shrink: 0;\n    cursor: none;\n    pointer-events: all;\n    overflow: hidden;\n}\n\n.idle-cell.empty  { background-color: rgba(13, 21, 32, 0.7); }\n.idle-cell.filled { border-color: rgba(148, 163, 184, 0.35); }\n\n.idle-chest {\n    font-size: 30px;\n    text-shadow: 0px 2px 6px rgba(0, 0, 0, 0.6);\n}\n\n/* chest type tinting */\n.idle-cell.c-normal { background-color: rgba(59, 44, 20, 0.55);  border-color: rgba(217, 160, 60, 0.45); }\n.idle-cell.c-gem    { background-color: rgba(14, 60, 78, 0.6);   border-color: rgba(56, 189, 248, 0.55); }\n.idle-cell.c-bomb   { background-color: rgba(70, 24, 24, 0.62);  border-color: rgba(248, 113, 113, 0.6); }\n.idle-cell.c-locked { background-color: rgba(45, 39, 74, 0.65);  border-color: rgba(167, 139, 250, 0.6); }\n.idle-cell.c-cursed { background-color: rgba(56, 20, 52, 0.7);   border-color: rgba(232, 121, 249, 0.65); }\n.idle-cell.c-void   { background-color: rgba(10, 10, 20, 0.95);  border-color: rgba(129, 140, 248, 0.7); }\n.idle-cell.c-boss   { background-color: rgba(80, 55, 8, 0.75);   border-color: rgba(251, 191, 36, 0.85); }\n\n/* detonation feedback */\n.idle-cell.fx-blast {\n    background-color: rgba(94, 234, 212, 0.30);\n    border-color: rgba(153, 246, 228, 0.85);\n}\n.idle-cell.fx-kill {\n    background-color: rgba(251, 191, 36, 0.55);\n    border-color: rgba(254, 240, 138, 0.95);\n}\n.idle-cell.fx-drone {\n    background-color: rgba(248, 113, 113, 0.6);\n    border-color: rgba(254, 202, 202, 0.95);\n}\n\n.idle-hits {\n    position: absolute;\n    bottom: 2px;\n    right: 4px;\n    font-size: 11px;\n    font-weight: 900;\n    color: rgba(255, 255, 255, 0.85);\n    text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.9);\n}\n\n/* A drone sits on its target for a beat before the volley goes off, so the\n   explosion has something to come from. */\n.idle-drone {\n    position: absolute;\n    font-size: 26px;\n    text-shadow: 0px 0px 10px rgba(248, 113, 113, 0.9);\n}\n\n.idle-cell.armed {\n    border-color: rgba(248, 113, 113, 0.95);\n    background-color: rgba(80, 20, 20, 0.55);\n}\n\n/* golden chest */\n.idle-cell.golden {\n    background-color: rgba(120, 82, 6, 0.85);\n    border-color: #fde047;\n    box-shadow: 0px 0px 26px rgba(253, 224, 71, 0.7);\n    animation: idle-golden-pulse 0.55s ease-in-out infinite alternate;\n}\n\n@keyframes idle-golden-pulse {\n    0%   { transform: scale(1.00); }\n    100% { transform: scale(1.10); }\n}\n\n.idle-golden { font-size: 32px; }\n\n.idle-golden-timer {\n    position: absolute;\n    bottom: 1px;\n    font-size: 11px;\n    font-weight: 900;\n    color: #fef9c3;\n}\n\n/* floating values */\n.idle-float {\n    position: absolute;\n    font-size: 17px;\n    font-weight: 900;\n    color: #a7f3d0;\n    text-shadow: 0px 2px 6px rgba(0, 0, 0, 0.9);\n    pointer-events: none;\n}\n.idle-float.crit {\n    font-size: 22px;\n    color: #fde047;\n}\n\n.idle-callout {\n    position: absolute;\n    top: 40%;\n    font-size: 34px;\n    font-weight: 900;\n    letter-spacing: 0.06em;\n    color: #5eead4;\n    text-shadow: 0px 0px 22px rgba(45, 212, 191, 0.8), 0px 3px 8px rgba(0, 0, 0, 0.9);\n    pointer-events: none;\n}\n.idle-callout.big  { color: #a7f3d0; }\n.idle-callout.mega { color: #fbbf24; font-size: 40px; }\n.idle-callout.crit { color: #fb923c; font-size: 44px; }\n\n/* \u2500\u2500 Controls \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-controls {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 16px;\n    flex-shrink: 0;\n}\n\n.idle-overload {\n    position: relative;\n    overflow: hidden;\n    width: 290px;\n    padding-top: 14px;\n    padding-bottom: 14px;\n    border-radius: 14px;\n    border: 3px solid rgba(248, 113, 113, 0.55);\n    background-color: rgba(60, 12, 12, 0.9);\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    gap: 1px;\n    cursor: none;\n    pointer-events: all;\n    transition: transform 0.06s, border-color 0.12s;\n}\n\n.idle-overload.ready {\n    border-color: #f87171;\n    animation: idle-overload-pulse 1.4s ease-in-out infinite alternate;\n}\n.idle-overload.cooling { opacity: 0.62; cursor: none; }\n.idle-overload:active { transform: translateY(2px); }\n\n@keyframes idle-overload-pulse {\n    0%   { box-shadow: 0px 0px 12px rgba(248, 113, 113, 0.25); }\n    100% { box-shadow: 0px 0px 34px rgba(248, 113, 113, 0.65); }\n}\n\n.idle-overload-fill {\n    position: absolute;\n    left: 0; top: 0; bottom: 0;\n    background-color: rgba(248, 113, 113, 0.18);\n    pointer-events: none;\n}\n\n.idle-overload-label {\n    font-size: 20px;\n    font-weight: 900;\n    letter-spacing: 0.14em;\n    color: #fecaca;\n}\n\n.idle-overload-sub {\n    font-size: 10px;\n    font-weight: 800;\n    letter-spacing: 0.14em;\n    text-transform: uppercase;\n    color: rgba(254, 202, 202, 0.6);\n}\n\n.idle-surge-wrap {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n    width: 300px;\n}\n\n.idle-surge-track {\n    position: relative;\n    height: 34px;\n    border-radius: 9px;\n    background-color: rgba(15, 23, 35, 0.92);\n    border: 2px solid rgba(148, 163, 184, 0.24);\n    overflow: hidden;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n}\n.idle-surge-track.ready {\n    border-color: #fb923c;\n    animation: idle-overload-pulse 0.9s ease-in-out infinite alternate;\n}\n\n.idle-surge-fill {\n    position: absolute;\n    left: 0; top: 0; bottom: 0;\n    background-color: rgba(251, 146, 60, 0.42);\n    transition: width 0.2s;\n}\n\n.idle-surge-lbl {\n    position: relative;\n    font-size: 12px;\n    font-weight: 900;\n    letter-spacing: 0.16em;\n    color: #fed7aa;\n}\n\n.idle-surge-btn {\n    padding-top: 10px;\n    padding-bottom: 10px;\n    border-radius: 9px;\n    border: 2px solid #fb923c;\n    background-color: rgba(154, 52, 18, 0.85);\n    color: #ffedd5;\n    font-size: 14px;\n    font-weight: 900;\n    letter-spacing: 0.12em;\n    align-items: center;\n    justify-content: center;\n    cursor: none;\n    pointer-events: all;\n}\n.idle-surge-btn:hover { background-color: rgba(194, 65, 12, 0.95); }\n\n/* \u2500\u2500 Meltdown strip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-meltdown-strip {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 14px;\n    width: 620px;\n    padding: 10px 16px;\n    border-radius: 12px;\n    background-color: rgba(10, 17, 26, 0.85);\n    border: 2px solid rgba(100, 116, 139, 0.18);\n    flex-shrink: 0;\n}\n.idle-meltdown-strip.ready {\n    border-color: rgba(251, 191, 36, 0.7);\n    background-color: rgba(69, 26, 3, 0.6);\n}\n\n.idle-melt-icon { font-size: 20px; flex-shrink: 0; }\n.idle-melt-text {\n    font-size: 12px;\n    font-weight: 700;\n    color: rgba(203, 213, 225, 0.85);\n    flex-grow: 1;\n}\n.idle-meltdown-strip.ready .idle-melt-text { color: #fde68a; font-weight: 900; }\n\n.idle-melt-bar {\n    margin-left: auto;\n    width: 180px;\n    height: 8px;\n    border-radius: 4px;\n    background-color: rgba(30, 41, 59, 0.9);\n    overflow: hidden;\n}\n.idle-melt-fill {\n    height: 100%;\n    background-color: #fbbf24;\n    border-radius: 4px;\n}\n\n.idle-melt-btn {\n    margin-left: auto;\n    padding: 8px 20px;\n    border-radius: 9px;\n    border: 2px solid #fbbf24;\n    background-color: rgba(120, 53, 15, 0.9);\n    color: #fef3c7;\n    font-size: 12px;\n    font-weight: 900;\n    letter-spacing: 0.14em;\n    cursor: none;\n    pointer-events: all;\n}\n.idle-melt-btn:hover { background-color: rgba(180, 83, 9, 0.95); }\n\n/* \u2500\u2500 Right panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-tabs { display: flex; flex-direction: row; gap: 6px; flex-shrink: 0; }\n\n.idle-tab {\n    flex-grow: 1;\n    padding-top: 10px;\n    padding-bottom: 10px;\n    border-radius: 9px;\n    border: 2px solid rgba(100, 116, 139, 0.22);\n    background-color: rgba(12, 20, 30, 0.85);\n    font-size: 18px;\n    align-items: center;\n    justify-content: center;\n    cursor: none;\n    pointer-events: all;\n    opacity: 0.55;\n    transition: opacity 0.1s, border-color 0.1s;\n}\n.idle-tab:hover { opacity: 0.85; }\n.idle-tab.on {\n    opacity: 1;\n    border-color: rgba(45, 212, 191, 0.7);\n    background-color: rgba(13, 40, 44, 0.9);\n}\n.idle-tab.fluxtab.on { border-color: rgba(96, 165, 250, 0.8); background-color: rgba(15, 32, 60, 0.9); }\n\n.idle-buyrow {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 6px;\n    flex-shrink: 0;\n}\n\n.idle-buyrow-lbl {\n    font-size: 10px;\n    font-weight: 900;\n    letter-spacing: 0.16em;\n    color: rgba(148, 163, 184, 0.6);\n    margin-right: 4px;\n}\n\n.idle-buy-amt {\n    padding: 5px 14px;\n    border-radius: 7px;\n    border: 2px solid rgba(100, 116, 139, 0.22);\n    background-color: rgba(12, 20, 30, 0.85);\n    color: rgba(203, 213, 225, 0.7);\n    font-size: 11px;\n    font-weight: 900;\n    cursor: none;\n    pointer-events: all;\n}\n.idle-buy-amt.on {\n    border-color: rgba(45, 212, 191, 0.75);\n    color: #5eead4;\n    background-color: rgba(13, 40, 44, 0.9);\n}\n\n/* height:0 \u002B flex-grow is what makes this actually scroll instead of pushing\n   its content off the bottom of the screen. */\n.idle-panel {\n    flex-grow: 1;\n    flex-shrink: 1;\n    height: 0;\n    overflow-y: scroll;\n    overflow-x: hidden;\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n    padding-right: 8px;\n}\n\n/* \u2500\u2500 Upgrade rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-up {\n    display: flex;\n    flex-direction: row;\n    gap: 12px;\n    padding: 10px 12px;\n    border-radius: 11px;\n    background-color: rgba(10, 17, 26, 0.9);\n    border: 2px solid rgba(100, 116, 139, 0.16);\n    cursor: none;\n    pointer-events: all;\n    flex-shrink: 0;\n    transition: border-color 0.1s, background-color 0.1s, transform 0.06s;\n}\n.idle-up:hover  { background-color: rgba(18, 28, 42, 0.95); }\n.idle-up:active { transform: translateY(1px); }\n\n.idle-up.afford { border-color: rgba(45, 212, 191, 0.55); }\n.idle-up.afford:hover { border-color: rgba(94, 234, 212, 0.95); }\n.idle-up.poor   { opacity: 0.62; }\n.idle-up.maxed  { border-color: rgba(251, 191, 36, 0.45); opacity: 0.9; }\n.idle-up.locked { opacity: 0.35; cursor: none; }\n.idle-up.perk.afford { border-color: rgba(96, 165, 250, 0.7); }\n.idle-up.ach    { cursor: none; }\n.idle-up.ach.done { border-color: rgba(52, 211, 153, 0.5); }\n\n.idle-up-icon {\n    font-size: 26px;\n    width: 34px;\n    flex-shrink: 0;\n    text-align: center;\n    align-items: center;\n    justify-content: center;\n}\n\n.idle-up-body {\n    display: flex;\n    flex-direction: column;\n    gap: 3px;\n    flex-grow: 1;\n}\n\n.idle-up-top {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: space-between;\n    gap: 8px;\n}\n\n.idle-up-name {\n    font-size: 14px;\n    font-weight: 900;\n    color: #e2e8f0;\n}\n\n.idle-up-lvl {\n    font-size: 11px;\n    font-weight: 800;\n    color: rgba(148, 163, 184, 0.7);\n}\n\n.idle-up-desc {\n    font-size: 11px;\n    font-weight: 600;\n    color: rgba(148, 163, 184, 0.72);\n}\n\n.idle-up-bottom {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: space-between;\n    gap: 8px;\n    margin-top: 3px;\n}\n\n.idle-up-effect {\n    font-size: 11px;\n    font-weight: 800;\n    color: rgba(94, 234, 212, 0.85);\n}\n\n.idle-up-cost {\n    font-size: 13px;\n    font-weight: 900;\n    color: #cbd5e1;\n}\n.idle-up.afford .idle-up-cost { color: #5eead4; }\n.idle-up-cost.flux  { color: #93c5fd; }\n.idle-up-cost.maxed { color: #fbbf24; }\n\n/* \u2500\u2500 Flux / log headers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-flux-head {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n    padding: 12px 14px;\n    border-radius: 11px;\n    background-color: rgba(13, 25, 46, 0.9);\n    border: 2px solid rgba(96, 165, 250, 0.3);\n    flex-shrink: 0;\n}\n\n.idle-flux-amount {\n    font-size: 22px;\n    font-weight: 900;\n    color: #93c5fd;\n}\n\n.idle-flux-sub {\n    font-size: 11px;\n    font-weight: 700;\n    color: rgba(148, 163, 184, 0.75);\n}\n\n.idle-ach-reward {\n    font-size: 11px;\n    font-weight: 900;\n    color: rgba(251, 191, 36, 0.9);\n}\n\n.idle-ach-bar {\n    width: 100%;\n    height: 6px;\n    border-radius: 3px;\n    background-color: rgba(30, 41, 59, 0.9);\n    overflow: hidden;\n    margin-top: 4px;\n}\n\n.idle-ach-fill {\n    height: 100%;\n    border-radius: 3px;\n    background-color: rgba(94, 234, 212, 0.8);\n}\n\n.idle-ach-prog {\n    font-size: 10px;\n    font-weight: 800;\n    color: rgba(148, 163, 184, 0.6);\n    margin-top: 2px;\n}\n\n/* \u2500\u2500 Toasts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-toasts {\n    position: absolute;\n    top: 96px;\n    left: 50%;\n    transform: translateX(-50%);\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 6px;\n    pointer-events: none;\n}\n\n.idle-toast {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 9px;\n    padding: 8px 18px;\n    border-radius: 10px;\n    background-color: rgba(8, 14, 22, 0.94);\n    border: 2px solid rgba(100, 116, 139, 0.35);\n}\n\n.idle-toast-icon { font-size: 17px; }\n.idle-toast-text { font-size: 13px; font-weight: 800; color: #e2e8f0; }\n\n.idle-toast.idle-toast-gold        { border-color: rgba(253, 224, 71, 0.8); }\n.idle-toast.idle-toast-chain       { border-color: rgba(94, 234, 212, 0.75); }\n.idle-toast.idle-toast-crit        { border-color: rgba(251, 146, 60, 0.85); }\n.idle-toast.idle-toast-frag        { border-color: rgba(167, 139, 250, 0.8); }\n.idle-toast.idle-toast-flux        { border-color: rgba(96, 165, 250, 0.85); }\n.idle-toast.idle-toast-surge       { border-color: rgba(251, 146, 60, 0.9); }\n.idle-toast.idle-toast-big         { border-color: rgba(52, 211, 153, 0.85); }\n.idle-toast.idle-toast-achievement { border-color: rgba(251, 191, 36, 0.9); }\n.idle-toast.idle-toast-dim         { opacity: 0.6; }\n\n/* \u2500\u2500 Modals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-modal-bg {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    background-color: rgba(2, 4, 8, 0.86);\n    pointer-events: all;\n}\n\n.idle-modal {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    transform: translate(-50%, -50%);\n    width: 640px;\n    max-height: 88%;\n    overflow-y: auto;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 10px;\n    padding: 34px 40px;\n    border-radius: 18px;\n    background-color: rgba(9, 15, 24, 0.98);\n    border: 3px solid rgba(45, 212, 191, 0.45);\n    box-shadow: 0px 0px 70px rgba(13, 148, 136, 0.28);\n    pointer-events: all;\n}\n\n.idle-modal.meltdown { border-color: rgba(251, 191, 36, 0.6); box-shadow: 0px 0px 70px rgba(180, 83, 9, 0.35); }\n.idle-modal.daily    { border-color: rgba(167, 139, 250, 0.6); }\n.idle-modal.intro    { width: 720px; }\n\n.idle-modal-eyebrow {\n    font-size: 11px;\n    font-weight: 900;\n    letter-spacing: 0.30em;\n    text-transform: uppercase;\n    color: rgba(148, 163, 184, 0.55);\n}\n\n.idle-modal-title {\n    font-size: 34px;\n    font-weight: 900;\n    letter-spacing: 0.06em;\n    color: #5eead4;\n    text-shadow: 0px 0px 24px rgba(45, 212, 191, 0.4);\n}\n.idle-modal.meltdown .idle-modal-title { color: #fbbf24; }\n.idle-modal.daily    .idle-modal-title { color: #c4b5fd; }\n\n.idle-modal-sub {\n    font-size: 13px;\n    font-weight: 600;\n    color: rgba(203, 213, 225, 0.75);\n    text-align: center;\n    max-width: 520px;\n    margin-bottom: 8px;\n}\n\n.idle-modal-btn {\n    margin-top: 14px;\n    padding: 14px 40px;\n    border-radius: 11px;\n    border: 2px solid #2dd4bf;\n    background-color: rgba(13, 78, 74, 0.9);\n    color: #ccfbf1;\n    font-size: 16px;\n    font-weight: 900;\n    letter-spacing: 0.16em;\n    align-items: center;\n    justify-content: center;\n    cursor: none;\n    pointer-events: all;\n}\n.idle-modal-btn:hover { background-color: rgba(15, 118, 110, 0.95); }\n\n.idle-modal-btn.ghost {\n    border-color: rgba(148, 163, 184, 0.4);\n    background-color: rgba(30, 41, 59, 0.7);\n    color: rgba(203, 213, 225, 0.85);\n}\n.idle-modal-btn.danger {\n    border-color: #fbbf24;\n    background-color: rgba(146, 64, 14, 0.9);\n    color: #fef3c7;\n}\n.idle-modal-btn.danger:hover { background-color: rgba(180, 83, 9, 0.95); }\n\n.idle-modal-btns { display: flex; flex-direction: row; gap: 14px; }\n\n/* offline report */\n.idle-offline-haul {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 2px;\n    padding: 18px 44px;\n    border-radius: 14px;\n    background-color: rgba(13, 40, 44, 0.7);\n    border: 2px solid rgba(45, 212, 191, 0.35);\n    margin-top: 6px;\n}\n\n.idle-offline-amount {\n    font-size: 46px;\n    font-weight: 900;\n    color: #5eead4;\n    text-shadow: 0px 0px 26px rgba(45, 212, 191, 0.5);\n}\n\n.idle-offline-lbl {\n    font-size: 12px;\n    font-weight: 900;\n    letter-spacing: 0.20em;\n    color: rgba(148, 163, 184, 0.7);\n}\n\n.idle-offline-extra {\n    font-size: 13px;\n    font-weight: 800;\n    color: #c4b5fd;\n    margin-top: 6px;\n}\n\n.idle-offline-warn {\n    font-size: 12px;\n    font-weight: 700;\n    color: rgba(251, 191, 36, 0.9);\n    text-align: center;\n    max-width: 480px;\n    margin-top: 8px;\n}\n\n/* daily */\n.idle-streak-row { display: flex; flex-direction: row; gap: 8px; margin-top: 6px; }\n\n.idle-streak-pip {\n    width: 44px;\n    height: 44px;\n    border-radius: 10px;\n    background-color: rgba(30, 41, 59, 0.8);\n    border: 2px solid rgba(100, 116, 139, 0.25);\n    align-items: center;\n    justify-content: center;\n    font-size: 15px;\n    font-weight: 900;\n    color: rgba(148, 163, 184, 0.6);\n}\n.idle-streak-pip.on {\n    background-color: rgba(76, 29, 149, 0.6);\n    border-color: rgba(167, 139, 250, 0.7);\n    color: #ddd6fe;\n}\n.idle-streak-pip.today {\n    border-color: #fbbf24;\n    color: #fde68a;\n    animation: idle-overload-pulse 1.1s ease-in-out infinite alternate;\n}\n\n.idle-daily-reward {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    margin-top: 14px;\n}\n.idle-daily-mult {\n    font-size: 44px;\n    font-weight: 900;\n    color: #fbbf24;\n}\n.idle-daily-lbl {\n    font-size: 13px;\n    font-weight: 800;\n    color: rgba(203, 213, 225, 0.8);\n}\n\n/* meltdown */\n.idle-melt-payout {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 10px;\n    padding: 14px 38px;\n    border-radius: 14px;\n    background-color: rgba(30, 41, 90, 0.6);\n    border: 2px solid rgba(96, 165, 250, 0.45);\n}\n.idle-melt-payout-val { font-size: 44px; font-weight: 900; color: #93c5fd; }\n.idle-melt-payout-lbl { font-size: 15px; font-weight: 900; color: rgba(148, 163, 184, 0.85); }\n\n.idle-melt-cols {\n    display: flex;\n    flex-direction: row;\n    gap: 16px;\n    margin-top: 14px;\n    width: 100%;\n}\n\n.idle-melt-col {\n    flex-grow: 1;\n    display: flex;\n    flex-direction: column;\n    gap: 4px;\n    padding: 12px 14px;\n    border-radius: 11px;\n    background-color: rgba(15, 23, 35, 0.85);\n    border: 2px solid rgba(100, 116, 139, 0.2);\n}\n.idle-melt-col.keep { border-color: rgba(52, 211, 153, 0.4); }\n.idle-melt-col.lose { border-color: rgba(248, 113, 113, 0.4); }\n\n.idle-melt-col-hd {\n    font-size: 11px;\n    font-weight: 900;\n    letter-spacing: 0.16em;\n    color: rgba(203, 213, 225, 0.8);\n    margin-bottom: 4px;\n}\n\n.idle-melt-line {\n    font-size: 12px;\n    font-weight: 700;\n    color: rgba(148, 163, 184, 0.85);\n}\n\n/* intro */\n.idle-intro-steps {\n    display: flex;\n    flex-direction: column;\n    gap: 12px;\n    margin-top: 10px;\n    width: 100%;\n}\n\n.idle-intro-step {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 14px;\n    padding: 10px 14px;\n    border-radius: 11px;\n    background-color: rgba(15, 23, 35, 0.8);\n    border-left: 3px solid rgba(45, 212, 191, 0.6);\n}\n\n.idle-intro-num {\n    width: 28px;\n    height: 28px;\n    flex-shrink: 0;\n    border-radius: 50%;\n    background-color: rgba(13, 78, 74, 0.9);\n    border: 2px solid rgba(45, 212, 191, 0.6);\n    align-items: center;\n    justify-content: center;\n    font-size: 13px;\n    font-weight: 900;\n    color: #99f6e4;\n}\n\n.idle-intro-txt {\n    font-size: 13px;\n    font-weight: 600;\n    color: rgba(203, 213, 225, 0.9);\n}\n\n\n/* \u2500\u2500 Board shake \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n/* Own keyframes rather than reusing Gamehud\u0027s, so this stylesheet stands\n   on its own if that file ever changes. */\n\n.idle-board.idle-shake-1 { animation: idle-shake-1 0.28s ease-out; }\n.idle-board.idle-shake-2 { animation: idle-shake-2 0.28s ease-out; }\n.idle-board.idle-shake-3 { animation: idle-shake-3 0.45s ease-out; }\n\n@keyframes idle-shake-1 {\n    0%   { transform: translate(0px, 0px); }\n    30%  { transform: translate(-2px, 1px); }\n    60%  { transform: translate(2px, -1px); }\n    100% { transform: translate(0px, 0px); }\n}\n\n@keyframes idle-shake-2 {\n    0%   { transform: translate(0px, 0px) rotate(0deg); }\n    20%  { transform: translate(-4px, 2px) rotate(-0.3deg); }\n    45%  { transform: translate(4px, -3px) rotate(0.3deg); }\n    70%  { transform: translate(-3px, -1px) rotate(-0.2deg); }\n    100% { transform: translate(0px, 0px) rotate(0deg); }\n}\n\n@keyframes idle-shake-3 {\n    0%   { transform: translate(0px, 0px) rotate(0deg) scale(1.00); }\n    15%  { transform: translate(-7px, 4px) rotate(-0.6deg) scale(1.01); }\n    35%  { transform: translate(7px, -5px) rotate(0.6deg) scale(1.02); }\n    55%  { transform: translate(-5px, -3px) rotate(-0.4deg) scale(1.01); }\n    78%  { transform: translate(4px, 2px) rotate(0.3deg) scale(1.00); }\n    100% { transform: translate(0px, 0px) rotate(0deg) scale(1.00); }\n}\n\n/* \u2500\u2500 Singularity \u2014 the second prestige layer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n/* Violet throughout, so nothing on screen can be mistaken for Flux blue or\n   Meltdown amber. The layer is rare enough that it has to read instantly. */\n\n.idle-sing-strip {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 14px;\n    width: 620px;\n    padding: 10px 16px;\n    border-radius: 12px;\n    background-color: rgba(10, 17, 26, 0.85);\n    border: 2px solid rgba(100, 116, 139, 0.18);\n    flex-shrink: 0;\n}\n.idle-sing-strip.ready {\n    border-color: rgba(192, 132, 252, 0.75);\n    background-color: rgba(46, 16, 101, 0.6);\n}\n.idle-sing-strip.ready .idle-melt-text { color: #e9d5ff; font-weight: 900; }\n\n.idle-sing-fill {\n    height: 100%;\n    background-color: #c084fc;\n    border-radius: 4px;\n}\n\n.idle-sing-btn {\n    margin-left: auto;\n    padding: 8px 20px;\n    border-radius: 9px;\n    border: 2px solid #c084fc;\n    background-color: rgba(76, 29, 149, 0.9);\n    color: #f3e8ff;\n    font-size: 12px;\n    font-weight: 900;\n    letter-spacing: 0.14em;\n    cursor: none;\n    pointer-events: all;\n}\n.idle-sing-btn:hover { background-color: rgba(109, 40, 217, 0.95); }\n\n.idle-tab.singtab.on {\n    border-color: rgba(192, 132, 252, 0.8);\n    background-color: rgba(38, 18, 62, 0.9);\n}\n\n.idle-up.power.afford { border-color: rgba(192, 132, 252, 0.7); }\n.idle-up-cost.shard   { color: #d8b4fe; }\n\n.idle-flux-head.sing {\n    background-color: rgba(30, 15, 52, 0.9);\n    border-color: rgba(192, 132, 252, 0.35);\n}\n.idle-flux-head.sing .idle-flux-amount { color: #d8b4fe; }\n\n.idle-sing-payout-val {\n    font-size: 44px;\n    font-weight: 900;\n    color: #d8b4fe;\n}\n\n.idle-modal.singularity {\n    border-color: rgba(192, 132, 252, 0.6);\n    box-shadow: 0px 0px 70px rgba(109, 40, 217, 0.4);\n}\n.idle-modal.singularity .idle-modal-title {\n    color: #d8b4fe;\n    text-shadow: 0px 0px 24px rgba(168, 85, 247, 0.45);\n}\n\n.idle-modal-btn.sing {\n    border-color: #c084fc;\n    background-color: rgba(76, 29, 149, 0.9);\n    color: #f3e8ff;\n}\n.idle-modal-btn.sing:hover { background-color: rgba(109, 40, 217, 0.95); }\n\n/* Board glows violet while a Twin Reactor / Cascade follow-up is in the air,\n   so an extra volley reads as an event rather than as the board stuttering. */\n.idle-board.cascading {\n    border-color: rgba(192, 132, 252, 0.65);\n    box-shadow: 0px 0px 46px rgba(168, 85, 247, 0.35);\n}\n\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   DAILY CONTRACTS\n\n   Violet, because contracts pay Fragments and Fragments are already violet\n   everywhere else in this HUD \u2014 the currency chip, the condenser bar, the\n   toast. Amber belongs to Meltdown and would have read as prestige.\n   Green marks a finished contract, matching the \u0022collect\u0022 green the offline\n   and achievement toasts already use.\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.idle-card-contracts { border-color: rgba(167, 139, 250, 0.28); }\n\n.idle-card-contracts.ready { border-color: rgba(52, 211, 153, 0.5); }\n\n.idle-card-contracts .idle-card-hd { color: rgba(196, 181, 253, 0.85); }\n\n.idle-card-hd.row {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: space-between;\n    gap: 8px;\n}\n\n.idle-card-hd-txt {\n    font-size: 11px;\n    font-weight: 900;\n    letter-spacing: 0.18em;\n    color: rgba(196, 181, 253, 0.85);\n}\n\n/* The parent .idle-card-hd is uppercased; a live countdown reading\n   \u0022RESETS IN 5H 12M\u0022 is a lot louder than it deserves to be. */\n.idle-card-hd-side {\n    font-size: 9px;\n    font-weight: 800;\n    letter-spacing: 0.04em;\n    text-transform: none;\n    color: rgba(148, 163, 184, 0.6);\n}\n\n.idle-contract {\n    display: flex;\n    flex-direction: row;\n    gap: 9px;\n    padding: 7px 8px;\n    border-radius: 9px;\n    background-color: rgba(10, 14, 22, 0.75);\n    border: 2px solid rgba(100, 116, 139, 0.16);\n    cursor: none;\n    pointer-events: all;\n    transition: border-color 0.12s, background-color 0.12s, transform 0.06s;\n}\n.idle-contract:hover { background-color: rgba(20, 26, 38, 0.85); }\n\n.idle-contract.ready {\n    border-color: rgba(52, 211, 153, 0.85);\n    background-color: rgba(6, 46, 38, 0.6);\n    animation: idle-contract-ready 1.1s ease-in-out infinite alternate;\n}\n.idle-contract.ready:active { transform: translateY(1px); }\n\n.idle-contract.claimed { opacity: 0.45; }\n\n@keyframes idle-contract-ready {\n    0%   { box-shadow: 0px 0px 6px rgba(52, 211, 153, 0.18); }\n    100% { box-shadow: 0px 0px 20px rgba(52, 211, 153, 0.50); }\n}\n\n.idle-contract-icon {\n    font-size: 19px;\n    width: 24px;\n    flex-shrink: 0;\n    text-align: center;\n    align-items: center;\n    justify-content: center;\n}\n\n.idle-contract-body {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n    flex-grow: 1;\n}\n\n.idle-contract-top {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: space-between;\n    gap: 6px;\n}\n\n.idle-contract-name {\n    font-size: 12px;\n    font-weight: 900;\n    color: #e2e8f0;\n}\n\n.idle-contract-reward {\n    font-size: 10px;\n    font-weight: 900;\n    color: rgba(196, 181, 253, 0.9);\n    flex-shrink: 0;\n}\n.idle-contract.ready .idle-contract-reward { color: #6ee7b7; }\n.idle-contract.claimed .idle-contract-reward { color: rgba(148, 163, 184, 0.7); }\n\n.idle-contract-desc {\n    font-size: 10px;\n    font-weight: 600;\n    color: rgba(148, 163, 184, 0.72);\n}\n\n.idle-contract-track {\n    width: 100%;\n    height: 5px;\n    border-radius: 3px;\n    background-color: rgba(30, 41, 59, 0.9);\n    overflow: hidden;\n    margin-top: 3px;\n}\n\n/* Same violet as the condenser bar \u2014 both are the road to a Fragment. */\n.idle-contract-fill {\n    height: 100%;\n    border-radius: 3px;\n    background-color: #a78bfa;\n    transition: width 0.25s;\n}\n.idle-contract.ready   .idle-contract-fill { background-color: rgba(52, 211, 153, 0.85); }\n.idle-contract.claimed .idle-contract-fill { background-color: rgba(52, 211, 153, 0.7); }\n\n.idle-contract-prog {\n    font-size: 9px;\n    font-weight: 800;\n    color: rgba(148, 163, 184, 0.55);\n}\n\n.idle-toast.idle-toast-contract { border-color: rgba(167, 139, 250, 0.8); }\n\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   NEXT GOAL PIN\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.idle-card-goal { border-color: rgba(94, 234, 212, 0.22); }\n\n.idle-goal-row {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 9px;\n}\n\n.idle-goal-icon {\n    font-size: 20px;\n    width: 26px;\n    flex-shrink: 0;\n    text-align: center;\n}\n\n.idle-goal-body { display: flex; flex-direction: column; flex-grow: 1; }\n\n.idle-goal-name { font-size: 12px; font-weight: 900; color: #e2e8f0; }\n\n.idle-goal-desc {\n    font-size: 10px;\n    font-weight: 600;\n    color: rgba(148, 163, 184, 0.7);\n}\n\n.idle-bar-fill.goal { background-color: rgba(94, 234, 212, 0.85); }\n\n.idle-goal-foot {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: space-between;\n    gap: 8px;\n}\n\n.idle-goal-prog {\n    font-size: 10px;\n    font-weight: 800;\n    color: rgba(148, 163, 184, 0.6);\n}\n\n.idle-goal-reward {\n    font-size: 10px;\n    font-weight: 900;\n    color: rgba(251, 191, 36, 0.85);\n}\n\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   GUIDED WALKTHROUGH \u2014 coach marks over the live HUD\n\n   The spotlight is a real cut-out: four dim quads that stop at the target\n   rect, so the highlighted control stays fully lit AND fully clickable while\n   everything else eats the click. There is no masking in the panel renderer,\n   which is why it is built this way rather than as one sheet with a hole.\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.rt-layer {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    pointer-events: none;\n    z-index: 400;\n}\n\n/* Background scrim and click blocker. The actual visual dimming of HUD content\n   is done by .tut-dim below \u2014 a scrim alone leaves every emoji punching\n   through it at full brightness. */\n.rt-dim {\n    position: absolute;\n    /* Explicit origin: a collapsed quad is given only a width and a height,\n       and an absolute panel with no anchor falls back to its static position. */\n    left: 0;\n    top: 0;\n    background-color: rgba(2, 5, 10, 0.55);\n    pointer-events: all;\n    z-index: 1;\n}\n\n/* Applied to whichever HUD blocks are not the subject of the current step. */\n.tut-dim {\n    opacity: 0.24;\n    transition: opacity 0.22s;\n}\n\n.rt-ring {\n    position: absolute;\n    border-radius: 18px;\n    border: 2px solid rgba(94, 234, 212, 0.75);\n    pointer-events: none;\n    z-index: 4;\n    animation: rt-ring 1.9s ease-out infinite;\n}\n\n@keyframes rt-ring {\n    0%   { transform: scale(1.00); opacity: 0.85; }\n    65%  { transform: scale(1.07); opacity: 0.00; }\n    100% { transform: scale(1.07); opacity: 0.00; }\n}\n\n.rt-spot {\n    position: absolute;\n    border-radius: 16px;\n    border: 3px solid rgba(94, 234, 212, 0.95);\n    pointer-events: none;\n    z-index: 5;\n    animation: rt-spot 1.5s ease-in-out infinite alternate;\n}\n\n@keyframes rt-spot {\n    0% {\n        border-color: rgba(94, 234, 212, 0.45);\n        box-shadow: 0px 0px 14px rgba(45, 212, 191, 0.25);\n    }\n    100% {\n        border-color: rgba(167, 243, 208, 1.00);\n        box-shadow: 0px 0px 46px rgba(45, 212, 191, 0.70);\n    }\n}\n\n/* Corner brackets \u2014 the bit that makes it read as a targeting reticle rather\n   than as a selected list item. */\n.rt-corner {\n    position: absolute;\n    width: 20px;\n    height: 20px;\n    animation: rt-corner 1.5s ease-in-out infinite alternate;\n}\n\n.rt-corner.tl { top: -7px;    left: -7px;  border-top: 4px solid #fde047; border-left: 4px solid #fde047; }\n.rt-corner.tr { top: -7px;    right: -7px; border-top: 4px solid #fde047; border-right: 4px solid #fde047; }\n.rt-corner.bl { bottom: -7px; left: -7px;  border-bottom: 4px solid #fde047; border-left: 4px solid #fde047; }\n.rt-corner.br { bottom: -7px; right: -7px; border-bottom: 4px solid #fde047; border-right: 4px solid #fde047; }\n\n@keyframes rt-corner {\n    0%   { opacity: 0.55; transform: scale(0.90); }\n    100% { opacity: 1.00; transform: scale(1.00); }\n}\n\n.rt-card {\n    position: absolute;\n    width: 380px;\n    padding: 16px 18px 14px 18px;\n    border-radius: 16px;\n    background-color: rgba(9, 15, 24, 0.98);\n    border: 2px solid rgba(45, 212, 191, 0.45);\n    box-shadow: 0px 0px 50px rgba(13, 148, 136, 0.28);\n    display: flex;\n    flex-direction: column;\n    gap: 7px;\n    pointer-events: all;\n    z-index: 10;\n}\n\n.rt-card.in-a { animation: rt-card-in 0.24s ease-out; }\n.rt-card.in-b { animation: rt-card-in 0.24s ease-out; }\n\n@keyframes rt-card-in {\n    0%   { opacity: 0; transform: scale(0.94) translateY(8px); }\n    100% { opacity: 1; transform: scale(1.00) translateY(0px); }\n}\n\n.rt-card-top {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: space-between;\n    gap: 8px;\n}\n\n.rt-eyebrow {\n    font-size: 9px;\n    font-weight: 900;\n    letter-spacing: 0.28em;\n    color: rgba(94, 234, 212, 0.7);\n}\n\n.rt-count {\n    font-size: 10px;\n    font-weight: 900;\n    letter-spacing: 0.10em;\n    color: rgba(148, 163, 184, 0.55);\n}\n\n.rt-title {\n    font-size: 19px;\n    font-weight: 900;\n    color: #ccfbf1;\n    text-shadow: 0px 0px 18px rgba(45, 212, 191, 0.35);\n}\n\n.rt-text {\n    font-size: 13px;\n    font-weight: 600;\n    line-height: 1.45;\n    color: rgba(203, 213, 225, 0.9);\n}\n\n.rt-dots {\n    display: flex;\n    flex-direction: row;\n    gap: 4px;\n    margin-top: 4px;\n    margin-bottom: 2px;\n}\n\n.rt-dot {\n    width: 16px;\n    height: 4px;\n    border-radius: 2px;\n    background-color: rgba(100, 116, 139, 0.30);\n    transition: background-color 0.2s, width 0.2s;\n}\n.rt-dot.done { background-color: rgba(45, 212, 191, 0.45); }\n.rt-dot.on   { background-color: #5eead4; width: 24px; }\n\n.rt-next {\n    margin-top: 4px;\n    padding-top: 11px;\n    padding-bottom: 11px;\n    border-radius: 10px;\n    border: 2px solid #2dd4bf;\n    background-color: rgba(13, 78, 74, 0.9);\n    color: #ccfbf1;\n    font-size: 13px;\n    font-weight: 900;\n    letter-spacing: 0.16em;\n    align-items: center;\n    justify-content: center;\n    cursor: none;\n    pointer-events: all;\n    transition: background-color 0.1s, transform 0.06s;\n}\n.rt-next:hover  { background-color: rgba(15, 118, 110, 0.95); }\n.rt-next:active { transform: translateY(1px); }\n\n/* Shown instead of the button when the step is gated on an action \u2014 the two\n   are mutually exclusive, so there is no way to click past the doing. */\n.rt-hint {\n    margin-top: 4px;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 9px;\n    padding: 10px 12px;\n    border-radius: 10px;\n    background-color: rgba(69, 26, 3, 0.65);\n    border: 2px solid rgba(251, 191, 36, 0.65);\n    animation: rt-hint 1.0s ease-in-out infinite alternate;\n}\n\n@keyframes rt-hint {\n    0%   { border-color: rgba(251, 191, 36, 0.35); }\n    100% { border-color: rgba(253, 224, 71, 0.95); }\n}\n\n.rt-hint-pip { font-size: 15px; flex-shrink: 0; }\n\n.rt-hint-txt {\n    font-size: 12px;\n    font-weight: 900;\n    letter-spacing: 0.04em;\n    color: #fde68a;\n}\n\n.rt-foot {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: space-between;\n    gap: 8px;\n    margin-top: 2px;\n}\n\n.rt-back, .rt-skip {\n    padding: 5px 10px;\n    border-radius: 7px;\n    border: 2px solid rgba(0, 0, 0, 0);\n    background-color: rgba(0, 0, 0, 0);\n    font-size: 10px;\n    font-weight: 800;\n    letter-spacing: 0.08em;\n    color: rgba(148, 163, 184, 0.6);\n    cursor: none;\n    pointer-events: all;\n    transition: color 0.1s;\n}\n.rt-back:hover, .rt-skip:hover { color: rgba(226, 232, 240, 0.95); }\n.rt-back.hidden { opacity: 0; pointer-events: none; }\n\n\n/* \u2500\u2500 Help button in the top bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-help {\n    width: 46px;\n    height: 46px;\n    flex-shrink: 0;\n    border-radius: 10px;\n    border: 2px solid rgba(148, 163, 184, 0.28);\n    background-color: rgba(15, 23, 35, 0.9);\n    color: rgba(226, 232, 240, 0.85);\n    font-size: 20px;\n    font-weight: 900;\n    align-items: center;\n    justify-content: center;\n    cursor: none;\n    pointer-events: all;\n    transition: background-color 0.1s, border-color 0.1s;\n}\n.idle-help:hover {\n    background-color: rgba(45, 212, 191, 0.18);\n    border-color: rgba(45, 212, 191, 0.6);\n}\n.idle-help.on {\n    border-color: rgba(45, 212, 191, 0.9);\n    background-color: rgba(13, 78, 74, 0.9);\n    color: #5eead4;\n}\n\n\n/* \u2500\u2500 Meltdown income preview \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.idle-melt-gain {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 10px;\n    margin-top: 10px;\n    padding: 9px 18px;\n    border-radius: 11px;\n    background-color: rgba(15, 23, 35, 0.85);\n    border: 2px solid rgba(52, 211, 153, 0.35);\n}\n\n.idle-melt-gain-from {\n    font-size: 18px;\n    font-weight: 900;\n    color: rgba(148, 163, 184, 0.8);\n}\n\n.idle-melt-gain-arrow {\n    font-size: 16px;\n    font-weight: 900;\n    color: rgba(148, 163, 184, 0.6);\n}\n\n.idle-melt-gain-to {\n    font-size: 22px;\n    font-weight: 900;\n    color: #6ee7b7;\n    text-shadow: 0px 0px 16px rgba(52, 211, 153, 0.45);\n}\n\n.idle-melt-gain-lbl {\n    font-size: 11px;\n    font-weight: 800;\n    letter-spacing: 0.12em;\n    text-transform: uppercase;\n    color: rgba(148, 163, 184, 0.65);\n}\n"},{"Ident":"playnplay.chain_reaction","Path":"Game/2D/GameMode.cs","FileName":"GameMode.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"using Sandbox;\r\n\r\npublic enum GameMode\r\n{\r\n    Wave,\r\n    Survival,\r\n    Idle,       // Reactor mode \u2014 the self-running chain reaction economy\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"Game/Idle/IdlePet.cs","FileName":"IdlePet.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"using System;\n\n/// \u003Csummary\u003E\n/// The pet is the Reactor\u0027s Fragment sink.\n///\n/// Cores buy upgrades that reset on Meltdown, and Flux buys perks that never do.\n/// The pet sits outside both: Fragments condense slowly, on a daily ceiling, and\n/// go into a permanent output multiplier that takes weeks rather than hours to\n/// finish. It is the long-horizon goal that survives every reset.\n/// \u003C/summary\u003E\npublic static class IdlePet\n{\n\tpublic const int MaxLevel = 6;\n\n\t/// \u003Csummary\u003EFragments needed to go from \u003Cparamref name=\u0022level\u0022/\u003E to the next one.\u003C/summary\u003E\n\tpublic static int CostToUpgrade( int level ) =\u003E level switch\n\t{\n\t\t1 =\u003E 120,\n\t\t2 =\u003E 300,\n\t\t3 =\u003E 700,\n\t\t4 =\u003E 1_500,\n\t\t5 =\u003E 3_200,\n\t\t_ =\u003E 0,\n\t};\n\n\t/// \u003Csummary\u003ETotal Fragments to take a fresh pet all the way to level 6.\u003C/summary\u003E\n\tpublic static int TotalCost\n\t{\n\t\tget\n\t\t{\n\t\t\tint total = 0;\n\t\t\tfor ( int lvl = 1; lvl \u003C MaxLevel; lvl\u002B\u002B ) total \u002B= CostToUpgrade( lvl );\n\t\t\treturn total;\n\t\t}\n\t}\n\n\tpublic static bool IsMaxed( int level ) =\u003E level \u003E= MaxLevel;\n\n\t/// \u003Csummary\u003EReactor output multiplier granted by the pet, before Pet Link.\u003C/summary\u003E\n\tpublic static double OutputBonus( int level ) =\u003E 0.15 * Math.Max( 0, level - 1 );\n\n\tpublic static string Name( int level ) =\u003E level switch\n\t{\n\t\t1 =\u003E \u0022Mysterious Egg\u0022,\n\t\t2 =\u003E \u0022Cat\u0022,\n\t\t3 =\u003E \u0022Wolf\u0022,\n\t\t4 =\u003E \u0022Tiger\u0022,\n\t\t5 =\u003E \u0022Baby Drake\u0022,\n\t\t_ =\u003E \u0022Dragon\u0022,\n\t};\n\n\tpublic static string Emoji( int level ) =\u003E level switch\n\t{\n\t\t1 =\u003E \u0022\uD83E\uDD5A\u0022,\n\t\t2 =\u003E \u0022\uD83D\uDC31\u0022,\n\t\t3 =\u003E \u0022\uD83D\uDC3A\u0022,\n\t\t4 =\u003E \u0022\uD83D\uDC2F\u0022,\n\t\t5 =\u003E \u0022\uD83D\uDC23\u0022,\n\t\t_ =\u003E \u0022\uD83D\uDC09\u0022,\n\t};\n\n\tpublic static string Blurb( int level ) =\u003E level switch\n\t{\n\t\t1 =\u003E \u0022Something is moving in there. Feed it Fragments and find out what.\u0022,\n\t\t2 =\u003E \u0022It watches the reactor and purrs at every chain.\u0022,\n\t\t3 =\u003E \u0022Faster, hungrier, and it has started organising the drones.\u0022,\n\t\t4 =\u003E \u0022Big enough now to shove chests into the blast radius itself.\u0022,\n\t\t5 =\u003E \u0022Wings, and the beginnings of a temper.\u0022,\n\t\t_ =\u003E \u0022Fully grown. The reactor runs hot around it.\u0022,\n\t};\n}\n"},{"Ident":"playnplay.chain_reaction","Path":"Game/Idle/IdleSave.cs","FileName":"IdleSave.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\n/// \u003Csummary\u003E\n/// Persistent state for Reactor (Idle) mode.\n/// Serialized as a nested object inside PlayerSave \u2014 see PlayerSave.Idle.\n/// Everything here survives a Meltdown except what ResetForMeltdown() clears.\n/// \u003C/summary\u003E\npublic class IdleSave\n{\n\t// \u2500\u2500 Unlock / onboarding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tpublic bool Unlocked      { get; set; } = false;  // granted after the first finished run\n\tpublic bool IntroShown    { get; set; } = false;  // first-entry explainer\n\tpublic bool MeltdownSeen  { get; set; } = false;  // has the prestige panel ever been opened\n\n\t/// \u003Csummary\u003E\n\t/// The guided walkthrough has run to the end, or been skipped. Separate from\n\t/// IntroShown so an existing save \u2014 which already has IntroShown set and has\n\t/// therefore never seen the walkthrough \u2014 still gets offered it once.\n\t/// \u003C/summary\u003E\n\tpublic bool TutorialDone { get; set; } = false;\n\n\t/// \u003Csummary\u003EThe walkthrough has been offered and declined; do not re-prompt.\u003C/summary\u003E\n\tpublic bool TutorialOffered { get; set; } = false;\n\n\t// \u2500\u2500 Currencies \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tpublic double Cores         { get; set; } = 0;   // spendable, wiped by Meltdown\n\tpublic double CycleCores    { get; set; } = 0;   // earned since last Meltdown \u2014 drives Flux payout\n\tpublic double AllTimeCores  { get; set; } = 0;   // never resets\n\tpublic int    Flux          { get; set; } = 0;   // unspent prestige currency\n\tpublic int    FluxEarned    { get; set; } = 0;   // lifetime, for milestones\n\tpublic int    Meltdowns     { get; set; } = 0;\n\tpublic int    LastReactorRank { get; set; } = 0;   // 0 == never ranked; drives the \u0022up N places\u0022 note\n\n\t// \u2500\u2500 Singularity (second prestige) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tpublic int  Shards          { get; set; } = 0;   // unspent, buys Powers\n\tpublic int  ShardsEarned    { get; set; } = 0;   // lifetime \u2014 drives the passive multiplier\n\tpublic int  Singularities   { get; set; } = 0;\n\tpublic bool SingularitySeen { get; set; } = false;  // has the collapse panel ever been opened\n\n\t// \u2500\u2500 Upgrade levels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t// Keys are IdleUpgradeId.ToString(); missing key == level 0.\n\tpublic Dictionary\u003Cstring, int\u003E Upgrades { get; set; } = new();\n\tpublic Dictionary\u003Cstring, int\u003E Perks    { get; set; } = new();\n\tpublic Dictionary\u003Cstring, int\u003E Powers   { get; set; } = new();\n\n\t// \u2500\u2500 Offline bookkeeping \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tpublic long   LastSeenUnix   { get; set; } = 0;\n\tpublic double MeasuredCps    { get; set; } = 0;   // smoothed cores/sec, used for offline payout\n\tpublic double BestCps        { get; set; } = 0;\n\n\t// Pending offline haul, staged so the welcome-back panel can show it\n\tpublic double PendingOfflineCores   { get; set; } = 0;\n\tpublic double PendingOfflineSeconds { get; set; } = 0;\n\tpublic int    PendingOfflineFrags   { get; set; } = 0;\n\n\t// \u2500\u2500 Daily loop \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tpublic string LastDailyDate { get; set; } = \u0022\u0022;\n\tpublic int    DailyStreak   { get; set; } = 0;\n\tpublic bool   DailyClaimed  { get; set; } = false;\n\n\t// \u2500\u2500 Fragment drip (own bucket \u2014 does not touch the pet farm cap) \u2500\u2500\n\tpublic int    TodayReactorFragments { get; set; } = 0;\n\tpublic string LastFragDate          { get; set; } = \u0022\u0022;\n\tpublic double FragProgress          { get; set; } = 0; // cores banked toward the next fragment\n\n\t// \u2500\u2500 Records / milestones \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tpublic int    TotalDetonations { get; set; } = 0;\n\tpublic long   TotalChestsBlown { get; set; } = 0;\n\tpublic int    BestChain        { get; set; } = 0;\n\tpublic double BestSingleBlast  { get; set; } = 0;\n\tpublic int    GoldenGrabbed    { get; set; } = 0;\n\tpublic double SecondsRun       { get; set; } = 0;\n\tpublic List\u003Cstring\u003E Achievements { get; set; } = new();\n\n\t// Counters that exist purely so Daily Contracts have something to watch.\n\t// They are lifetime totals; a contract stores its own baseline at issue.\n\tpublic int TotalOverloads      { get; set; } = 0;\n\tpublic int TotalSurges         { get; set; } = 0;\n\tpublic int TotalUpgradesBought { get; set; } = 0;\n\n\t/// \u003Csummary\u003ELongest chain since the contract set was last issued. Reset with it.\u003C/summary\u003E\n\tpublic int BestChainToday { get; set; } = 0;\n\n\t// \u2500\u2500 Daily contracts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t// Parallel lists rather than a list of objects: the save is plain JSON and\n\t// a schema change to a nested type would break every existing file.\n\tpublic string       ContractDate     { get; set; } = \u0022\u0022;\n\tpublic List\u003Cstring\u003E ContractIds      { get; set; } = new();\n\tpublic List\u003Cdouble\u003E ContractTargets  { get; set; } = new();\n\tpublic List\u003Cdouble\u003E ContractBaseline { get; set; } = new();\n\tpublic List\u003Cbool\u003E   ContractClaimed  { get; set; } = new();\n\n\t/// \u003Csummary\u003EConsecutive days on which all three contracts were cleared.\u003C/summary\u003E\n\tpublic int    ContractStreak      { get; set; } = 0;\n\tpublic string ContractStreakDate  { get; set; } = \u0022\u0022;\n\n\t// \u2500\u2500 Upgrade helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tpublic int Level( IdleUpgradeId id )\n\t\t=\u003E Upgrades.TryGetValue( id.ToString(), out int v ) ? v : 0;\n\n\tpublic void SetLevel( IdleUpgradeId id, int level )\n\t\t=\u003E Upgrades[id.ToString()] = Math.Max( 0, level );\n\n\tpublic int PerkLevel( IdlePerkId id )\n\t\t=\u003E Perks.TryGetValue( id.ToString(), out int v ) ? v : 0;\n\n\tpublic void SetPerkLevel( IdlePerkId id, int level )\n\t\t=\u003E Perks[id.ToString()] = Math.Max( 0, level );\n\n\tpublic int PowerLevel( IdlePowerId id )\n\t\t=\u003E Powers != null \u0026\u0026 Powers.TryGetValue( id.ToString(), out int v ) ? v : 0;\n\n\tpublic void SetPowerLevel( IdlePowerId id, int level )\n\t{\n\t\tPowers ??= new();\n\t\tPowers[id.ToString()] = Math.Max( 0, level );\n\t}\n\n\tpublic bool HasAchievement( string id ) =\u003E Achievements.Contains( id );\n\n\t// \u2500\u2500 Meltdown (prestige) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/// \u003Csummary\u003EWipes the run-scoped economy. Flux, perks, records and achievements stay.\u003C/summary\u003E\n\tpublic void ResetForMeltdown()\n\t{\n\t\tCores      = 0;\n\t\tCycleCores = 0;\n\t\tUpgrades.Clear();\n\t\tMeltdowns\u002B\u002B;\n\n\t\t// Kickstart perk hands back a head start so the early grind shrinks each time\n\t\tint kick = PerkLevel( IdlePerkId.Kickstart );\n\t\tif ( kick \u003E 0 )\n\t\t{\n\t\t\tSetLevel( IdleUpgradeId.ChestValue, kick * 6 );\n\t\t\tSetLevel( IdleUpgradeId.SpawnRate,  kick * 4 );\n\t\t\tSetLevel( IdleUpgradeId.CycleSpeed, kick * 4 );\n\t\t\tSetLevel( IdleUpgradeId.DroneCount, Math.Min( kick, 3 ) );\n\t\t}\n\t}\n\n\t// \u2500\u2500 Singularity (second prestige) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/// \u003Csummary\u003E\n\t/// Collapses everything a Meltdown normally protects \u2014 Flux, lifetime Flux\n\t/// and the whole perk tree. Shards, Powers, records, achievements, Fragments\n\t/// and the pet survive; those are what carry the run forward.\n\t///\n\t/// Meltdowns is deliberately kept: the Reactor Log counts it, and wiping a\n\t/// lifetime tally the player earned honestly reads as a bug, not a cost.\n\t/// \u003C/summary\u003E\n\tpublic void ResetForSingularity()\n\t{\n\t\tFlux       = 0;\n\t\tFluxEarned = 0;\n\t\tPerks.Clear();\n\n\t\t// Kickstart is gone with the perks, so ResetForMeltdown lands us at a\n\t\t// genuine level-zero board \u2014 which is the point of the layer.\n\t\tResetForMeltdown();\n\t\tMeltdowns--;   // ResetForMeltdown counts a Meltdown that did not happen\n\n\t\tSingularities\u002B\u002B;\n\t}\n\n\t// \u2500\u2500 Day rollover for the fragment bucket \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\tpublic void EnsureFragDay()\n\t{\n\t\tstring today = DateTime.UtcNow.ToString( \u0022yyyy-MM-dd\u0022 );\n\t\tif ( LastFragDate == today ) return;\n\t\tLastFragDate = today;\n\t\tTodayReactorFragments = 0;\n\t}\n\n\tpublic int FragmentsToday\n\t{\n\t\tget { EnsureFragDay(); return TodayReactorFragments; }\n\t}\n}\n"},{"Ident":"playnplay.chain_reaction","Path":"styles/form/_dropdown.scss","FileName":"_dropdown.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"\r\n$primary: red !default;\r\n$primary-alt: white !default;\r\n\r\n$switch-padding: 6px !default;\r\n\r\n.button.popupbutton.dropdown\r\n{\r\n\tcursor: pointer;\r\n\ttransition: all .1s ease-out;\r\n\tposition: relative;\r\n\r\n\t\u003E .dropdown_indicator\r\n\t{\r\n\t\tposition: absolute;\r\n\t\tright: 8px;\r\n\t}\r\n\r\n\t\u0026.open\r\n\t{\r\n\t\tborder-bottom-left-radius: 1px;\r\n\t\tborder-bottom-right-radius: 1px;\r\n\t\ttransition: border-radius 0.2s ease-out;\r\n\t}\r\n}\r\n\r\nselect\r\n{\r\n\tmin-height: 40px;\r\n\r\n\t\u003E option\r\n\t{\r\n\t\tdisplay: none;\r\n\t}\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"ui/components/packagecard.razor.scss","FileName":"packagecard.razor.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"@import \u0022/styles/_theme.scss\u0022;\r\n$background-size: 8px;\r\n\r\nPackageCard\r\n{\r\n\tflex-shrink: 0;\r\n\r\n\t\u0026:hover\r\n\t{\r\n\t\tsound-in: \u0022ui.button.over\u0022;\r\n\t}\r\n\r\n\t\u0026:active\r\n\t{\r\n\t\tsound-in: \u0022ui.button.press\u0022;\r\n\t}\r\n\r\n\tflex-direction: column;\r\n\tposition: relative;\r\n\tbackground-color: rgba( $default-950, 0 );\r\n\tborder-radius: $rounding-default;\r\n\ttransition: all 150ms ease;\r\n\tcursor: pointer;\r\n\tz-index: 0;\r\n\theight: 200px;\r\n\r\n\t.image\r\n\t{\r\n\t\tflex-grow: 1;\r\n\t\tflex-shrink: 0;\r\n\t\ttransition: all 150ms ease;\r\n\t\tborder: 1px solid rgba( white, 0.025 );\r\n\t\taspect-ratio: 16 / 9;\r\n\t\tbackground-position: center;\r\n\t\tbackground-size: cover;\r\n\t\tborder-radius: $rounding-small;\r\n\t\tposition: relative;\r\n\t}\r\n\r\n\tcolumn\r\n\t{\r\n\t\tpadding: 8px 2px; // Optically aligned\r\n\t}\r\n\r\n\t\r\n\r\n\t\u0026.list\r\n\t{\r\n\t\tflex-grow: 1;\r\n\t\theight: 64px;\r\n\t\tflex-direction: row;\r\n\t\tgap: 12px;\r\n\t\tpadding: 4px;\r\n\r\n\t\t.inner column\r\n\t\t{\r\n\t\t\tflex-grow: 1;\r\n\t\t}\r\n\r\n\t\t.image\r\n\t\t{\r\n\t\t\theight: 100%;\r\n\t\t\taspect-ratio: 1;\r\n\t\t\tflex-grow: 0;\r\n\t\t\tflex-shrink: 0;\r\n\t\t}\r\n\r\n\t\tcolumn\r\n\t\t{\r\n\t\t\tgap: 3px;\r\n\t\t\tjustify-content: center;\r\n\t\t}\r\n\r\n\t\t.package-title\r\n\t\t{\r\n\t\t\tflex-shrink: 0;\r\n\t\t\tmax-width: 500px;\r\n\t\t}\r\n\r\n\t\t.package-users\r\n\t\t{\r\n\t\t\ttop: 1px;\r\n\t\t\tright: 1px;\r\n\t\t}\r\n\r\n\t\t\u0026:hover\r\n\t\t{\r\n\t\t\tbackground-color: $default-800;\r\n\t\t\ttransform: none;\r\n\r\n\t\t\t\u0026::after\r\n\t\t\t{\r\n\t\t\t\tdisplay: none;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t\u0026.wide\r\n\t{\r\n\t\theight: 250px;\r\n\r\n\t\t.package-title\r\n\t\t{\r\n\t\t\tmax-width: 300px;\r\n\t\t}\r\n\t}\r\n\r\n\t\u0026.small\r\n\t{\r\n\t\theight: 200px;\r\n\t\taspect-ratio: 3/4;\r\n\r\n\t\t.image\r\n\t\t{\r\n\t\t\taspect-ratio: 1;\r\n\t\t}\r\n\r\n\t\t.package-title\r\n\t\t{\r\n\t\t\tmax-width: 140px;\r\n\t\t}\r\n\t}\r\n\r\n\t\u0026.tall\r\n\t{\r\n\t\theight: 400px;\r\n\r\n\t\t.image\r\n\t\t{\r\n\t\t\taspect-ratio: 9/16;\r\n\t\t}\r\n\r\n\t\t.package-title\r\n\t\t{\r\n\t\t\tmax-width: 200px;\r\n\t\t}\r\n\t}\r\n\r\n\t.package-title\r\n\t{\r\n\t\ttext-overflow: ellipsis;\r\n\t\tmax-height: 24px;\r\n\t\tflex-shrink: 1;\r\n\t\tfont-size: 14px;\r\n\t}\r\n\r\n\t.package-users\r\n\t{\r\n\t\tposition: absolute;\r\n\t\tbottom: 4px;\r\n\t\tright: 4px;\r\n\t\tbackground-color: rgba( 10, 40, 10, 0.95 );\r\n\t\tpadding: 3px 5px;\r\n\t\tborder-radius: 2px;\r\n\t\tjustify-content: center;\r\n\t\talign-items: center;\r\n\t\tgap: 3px;\r\n\t\tfont-size: 11px;\r\n\t\tcolor: #def;\r\n\t\tborder: 1px solid #252;\r\n\t\tcolor: #2f3;\r\n\r\n\t\t\u0026:before\r\n\t\t{\r\n\t\t\tcontent: \u0027\u25CF\u0027;\r\n\t\t\tfont-size: 0.5rem;\r\n\t\t}\r\n\t}\r\n\t// Hover effect (not for list)\r\n\t\u0026:not(.list)\r\n\t{\r\n\t\t\u0026::after\r\n\t\t{\r\n\t\t\tcontent: \u0022\u0022;\r\n\t\t\tposition: absolute;\r\n\t\t\ttop: 0;\r\n\t\t\tleft: 0;\r\n\t\t\tbottom: 0;\r\n\t\t\tright: 0;\r\n\t\t\tbackground-color: rgba( $default-950, 0 );\r\n\t\t\ttransition: all 150ms ease;\r\n\t\t\tz-index: -10;\r\n\t\t\tborder-radius: $rounding-large;\r\n\t\t\tpointer-events: none;\r\n\t\t}\r\n\r\n\t\t\u0026:hover\r\n\t\t{\r\n\t\t\ttransform: scale( 1.05 );\r\n\r\n\t\t\t\u0026::after\r\n\t\t\t{\r\n\t\t\t\tbackground-color: $default-800;\r\n\t\t\t\ttop: -$background-size;\r\n\t\t\t\tleft: -$background-size;\r\n\t\t\t\tright: -$background-size;\r\n\t\t\t\tbottom: -$background-size;\r\n\t\t\t\tbox-shadow: 0 0 25px rgba( black, 0.3 );\r\n\t\t\t}\r\n\r\n\t\t\tz-index: 100;\r\n\t\t\tbackground-color: $default-900;\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\r\n.package-card.list packageflairbar\r\n{\r\n\tdisplay: none;\r\n}\r\n\r\n.package-card.list .package-users\r\n{\r\n\tdisplay: none;\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"game/2d/ui/bombwheel.razor.scss","FileName":"bombwheel.razor.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"root {\n    /* Declared so no panel in this tree ever claims the OS pointer -- a hovered\n       panel outranks Mouse.CursorType, and one that declares nothing still claims\n       the default arrow. GameCursor draws the cursor instead. */\n    cursor: none;\n    flex-grow:1; width:100%; height:100%;\n    pointer-events:none; position:relative;\n    overflow:visible;\n}\n\n.mini-hand {\n    position:absolute;\n    bottom:15px;\n    left:0; right:0px;           /* right edge = sidebar width */\n    display:flex; flex-direction:row; gap:14px; align-items:flex-end;\n    justify-content:center;        /* centers within the grid area, not full viewport */\n    pointer-events:all;\n    z-index:30;\n}\n\n.mini-card {\n    width:80px;\n    height: 80px;\n    background-color:rgba(31,27,83,0.85);   /* more opaque \u2014 was barely visible */\n    border:2px solid rgba(255,255,255,0.18);\n    border-radius:16px; padding:16px 10px 12px;\n    display:flex; flex-direction:column; align-items:center; gap:10px;\n    cursor: none; position:relative;\n    transition:background-color 0.1s, border-color 0.1s, transform 0.1s;\n}\n.mini-card:hover  { background-color:rgba(117,110,194,0.75); transform:translateY(-5px); }\n.mini-card.active {\n    background-color:rgba(83,74,183,0.90);\n    border-color:#a78bfa;\n    border-width:3px;\n    transform:translateY(-16px);\n}\n.mini-card.used {\n    background-color:rgba(29,158,117,0.28);\n    border-color:rgba(29,158,117,0.60);\n    opacity:0.75;\n    cursor: none;\n}\n\n.mini-icon { font-size:30px; position: relative; bottom: 4px;}     /* was 40px \u2014 easier to see */\n.mini-name { font-size:13px; font-weight:800; color:rgba(255,255,255,0.80); }\n.mini-card.active .mini-name { color:#fff; font-size:15px; }\n\n.mini-badge {\n    position:absolute; top:5px; right:7px;\n    font-size:12px; font-weight:700;\n    color:rgba(80,220,140,0.95);\n    background-color:rgba(29,158,117,0.40);\n    border-radius:4px; padding:0 5px;\n}\n\n.kb-hint {\n    position:absolute; top:5px; left:7px;\n    font-size:12px; font-weight:800;\n    color:rgba(255,255,255,0.45);\n    background-color:rgba(255,255,255,0.10);\n    border-radius:4px; padding:0 6px;\n}\n.mini-card.active .kb-hint {\n    color:rgba(255,255,255,0.80);\n    background-color:rgba(255,255,255,0.18);\n}\n\n.mini-det-wrap { align-self:flex-end; }\n.mini-det {\n    width:90px;                    /* was 90px */\n    background:linear-gradient(135deg,#7c3aed,#a855f7);\n    border:2px solid rgba(167,139,250,0.40);\n    border-radius:14px; \n    padding:12px 10px 5px;\n    display:flex; flex-direction:column; align-items:center; gap:5px;\n    cursor: none;\n    transition:opacity 0.15s;\n}\n.mini-det-wrap.ready .mini-det { border-color:#a78bfa; }\n.mini-det-wrap.off   .mini-det { opacity:0.25; pointer-events:none; }\n.mini-det-icon  { font-size:28px; }\n.mini-det-label { font-size:13px; font-weight:800; color:#fff; letter-spacing:0.08em; }\n\n/* SCREEN SHAKE */\n.mini-hand.shake-small  { animation: shake-small  0.25s ease-out; }\n.mini-hand.shake-medium { animation: shake-medium 0.35s ease-out; }\n.mini-hand.shake-strong { animation: shake-strong 0.45s ease-out; }\n.mini-hand.shake-insane { animation: shake-insane 0.55s ease-out; }\n\n@keyframes shake-small {\n    0%,100% { transform: translate(0,0); }\n    25%     { transform: translate(-2px, 1px); }\n    50%     { transform: translate(2px, -1px); }\n    75%     { transform: translate(-1px, 2px); }\n}\n@keyframes shake-medium {\n    0%,100% { transform: translate(0,0); }\n    20%     { transform: translate(-4px, 2px) rotate(-0.3deg); }\n    40%     { transform: translate(4px,-3px) rotate(0.3deg); }\n    60%     { transform: translate(-3px, 3px) rotate(-0.2deg); }\n    80%     { transform: translate(3px,-2px) rotate(0.2deg); }\n}\n@keyframes shake-strong {\n    0%,100% { transform: translate(0,0); }\n    15%     { transform: translate(-6px, 3px) rotate(-0.5deg); }\n    30%     { transform: translate(6px,-4px) rotate(0.5deg); }\n    45%     { transform: translate(-5px, 5px) rotate(-0.4deg); }\n    60%     { transform: translate(5px,-4px) rotate(0.4deg); }\n    75%     { transform: translate(-3px, 2px); }\n    90%     { transform: translate(2px,-2px); }\n}\n@keyframes shake-insane {\n    0%,100% { transform: translate(0,0); }\n    10%     { transform: translate(-9px, 5px) rotate(-0.7deg); }\n    20%     { transform: translate(9px,-6px) rotate(0.7deg); }\n    30%     { transform: translate(-8px, 7px) rotate(-0.6deg); }\n    40%     { transform: translate(8px,-6px) rotate(0.6deg); }\n    50%     { transform: translate(-6px, 5px) rotate(-0.4deg); }\n    60%     { transform: translate(6px,-5px) rotate(0.4deg); }\n    70%     { transform: translate(-4px, 3px); }\n    80%     { transform: translate(3px,-3px); }\n    90%     { transform: translate(-1px, 1px); }\n}\n\n/* Lock icon, if random event Bomb jam hit */\n.mini-jam-overlay {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    background-color: rgba(239,68,68,0.25);\n    border-radius: 14px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    font-size: 28px;\n    pointer-events: none;\n}"},{"Ident":"playnplay.chain_reaction","Path":"game/2d/ui/gamehud.razor.scss","FileName":"gamehud.razor.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"root {\n    /* Declared so no panel in this tree ever claims the OS pointer -- a hovered\n       panel outranks Mouse.CursorType, and one that declares nothing still claims\n       the default arrow. GameCursor draws the cursor instead. */\n    cursor: none;\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    pointer-events: none;\n    overflow: hidden;\n}\n\n/* TOP HUD */\n.topbar {\n    position: absolute;\n    top: 0; left: 0; right: 0;\n    height: 90px;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: space-evenly;\n    border-bottom: 1px solid rgba(167,139,250,0.15);\n    z-index: 10;\n    background-color: rgba(18,8,42,0.97);\n    animation: topbar-shift 20s ease-in-out infinite alternate;\n    overflow: hidden;\n    z-index: 11;\n}\n\n@keyframes topbar-shift {\n    0%   { background-color: rgba(18,8,42,0.97); }\n    25%  { background-color: rgba(8,24,48,0.97); }\n    50%  { background-color: rgba(24,8,40,0.97); }\n    75%  { background-color: rgba(8,18,50,0.97); }\n    100% { background-color: rgba(20,6,38,0.97); }\n}\n.hud-block {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 5px;\n}\n.hud-lbl {\n    font-size: 22px;\n    font-weight: 700;\n    letter-spacing: 0.12em;\n    color: rgba(255,255,255,0.30);\n    text-transform: uppercase;\n}\n.hud-val {\n    font-size: 32px;\n    font-weight: 900;\n    color: white;\n}\n.hud-block.wave  .hud-val { color: #a78bfa; }\n.hud-block.score .hud-val { color: #34d399; }\n.hud-block.best  .hud-val { color: #fbbf24; }\n.hud-sep {\n    width: 1px;\n    height: 120px;\n    background-color: rgba(128,95,128,0.07);\n}\n\n/* TIMER */\n.timer-track {\n    position: absolute;\n    top: 90px; left: 0; right: 0;\n    height: 14px;\n    background-color: rgba(255,255,255,0.06);\n    z-index: 10;\n}\n.timer-fill {\n    position: absolute;\n    top: 0; left: 0; bottom: 0;\n    background-color: #34d399;\n    transition: width 0.25s linear, background-color 0.8s ease;\n    min-width: 4px;\n}\n\n.timer-fill.mid {\n    background-color: #a78bfa;           /* half time: purple */\n    transition: width 0.25s linear, background-color 0.6s ease;\n}\n.timer-fill.hurry {\n    background-color: #f59e0b;           /* 30%: amber */\n    transition: width 0.25s linear, background-color 0.5s ease;\n}\n.timer-fill.panic {\n    background-color: #ef4444;           /* 15%: red */\n    transition: width 0.25s linear, background-color 0.3s ease;\n    animation: pulse-bar 0.55s ease-in-out infinite alternate;\n}\n@keyframes pulse-bar {\n    from { opacity: 1.0; }\n    to   { opacity: 0.5; }\n}\n/* \n   Three-stage color shift:\n   full \u2192 hurry  : purple  \u2192 amber   (feels warm, attention-grabbing)\n   hurry \u2192 panic : amber   \u2192 red     (urgent)\n   panic         : red \u002B pulse animation\n*/\n\n/* Challenge sentence \u2014 between timer and grid */\n\n.challenge-bar {\n    position: absolute;\n    top: 104px; \n    left: 0;\n    right: 0px;\n    height: 36px;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: center;\n    gap: 10px;\n    background-color: rgba(13,8,32,0.80);\n    border-bottom: 1px solid rgba(167,139,250,0.12);\n    pointer-events: none;\n    z-index: 9;\n    transition: background-color 0.4s ease;\n}\n.challenge-bar.done {\n    background-color: rgba(16,42,16,0.85);\n    border-bottom-color: rgba(52,211,153,0.25);\n}\n\n.ch-icon {\n    font-size: 16px;\n    color: rgba(167,139,250,0.70);\n}\n.challenge-bar.done .ch-icon {\n    color: #34d399;\n}\n\n.ch-label {\n    font-size: 20px;\n    font-weight: 700;\n    color: rgba(255,255,255,0.55);\n    letter-spacing: 0.04em;\n}\n.challenge-bar.done .ch-label {\n    color: rgba(52,211,153,0.85);\n}\n\n.ch-bonus {\n    font-size: 13px;\n    font-weight: 800;\n    color: rgba(251,191,36,0.60);\n    background-color: rgba(251,191,36,0.08);\n    border-radius: 5px;\n    padding: 2px 7px;\n}\n.challenge-bar.done .ch-bonus {\n    color: rgba(52,211,153,0.80);\n    background-color: rgba(52,211,153,0.10);\n}\n\n/* CHAIN TRACK \u2014 above grid, pointer-events none */\n.chain-track {\n    position: absolute;\n    top: 146px;\n    left: 0;\n    right: 0px;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    pointer-events: none;\n    z-index: 15;\n}\n.chain-track-inner {\n    background-color: rgba(13,8,32,0.75);\n    border-radius: 14px;\n    padding: 10px 20px;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 8px;\n}\n.step {\n    background-color: rgba(255,255,255,0.05);\n    color: rgba(255,255,255,0.25);\n    border-radius: 10px;\n    padding: 6px 17px;\n    font-size: 15px;\n    border: 1.5px solid rgba(255,255,255,0.06);\n    transition: all 0.15s;\n}\n.step.lit {\n    background-color: rgba(167,139,250,0.2);\n    color: #c4b5fd;\n    border-color: #7c3aed;\n    transform: scale(1.15);\n}\n.arr { font-size: 15px; color: rgba(255,255,255,0.15); }\n\n/* SIDEBAR */\n.sidebar {\n    position: absolute;\n    top: 140px; right: 0; bottom: 0;\n    width: 260px;\n    background-color: rgba(18,8,42,0.97);\n    border-left: 2px solid rgba(167,139,250,0.18);\n    display: flex;\n    flex-direction: column;\n    gap: 14px;\n    padding: 16px;\n    pointer-events: all;\n    z-index: 10;\n}\n.divider {\n    height: 1px;\n    background-color: rgba(167,139,250,0.10);\n}\n.sbl {\n    font-size: 12px; font-weight: 800;\n    letter-spacing: 0.2em; text-transform: uppercase;\n    color: rgba(166,154,219,0.5);\n}\n\n/* Legend */\n.legend { display: flex; flex-direction: column; gap: 8px; }\n.leg    { display: flex; flex-direction: row; align-items: center; gap: 6px; font-size: 14px; color: rgba(255,255,255,0.38); font-weight: 600; }\n.dot    { width: 10px; height: 10px; border-radius: 2px; }\n.dot.p0    { background-color: #60a5fa; }\n.dot.p1    { background-color: #34d399; }\n.dot.p2    { background-color: #fb923c; }\n.dot.red   { background-color: #f87171; }\n.dot.chain { background-color: #c4b5fd; }\n\n/* Controls */\n.controls-hint {\n    display: flex; flex-direction: column; gap: 6px;\n    font-size: 13px; color: rgba(255,255,255,0.30); font-weight: 500;\n}\n\n.spacer { flex-grow: 1; }\n\n/* Message bottom bar \u2014 pointer-events none, no text-align */\n.msg {\n    position: absolute;\n    bottom: 8px;\n    left: 12px;\n    background-color: rgba(4,1,12,1.0);\n    border: 1px solid rgba(167,139,250,0.15);\n    border-radius: 8px;\n    padding: 6px 14px;\n    font-size: 18px;\n    color: rgba(255,255,255,0.55);\n    pointer-events: none;\n    font-weight: 600;\n    z-index: 5;\n}\n/* GAME OVER */\n/* \u2500\u2500 Three column layout \u2500\u2500 */\n.ov-bg {\n    position: absolute;\n    top: 0; left: 0;\n    width: 100%;\n    height: 100%;\n    background-color: rgba(4,1,12,1.0);\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: center;\n    gap: 24px;\n    pointer-events: all;\n    z-index: 50;\n    overflow: hidden;\n}\n\n.ov-side-panel {\n    display: flex;\n    flex-direction: column;\n    gap: 16px;\n    width: 220px;\n    pointer-events: none;\n    z-index: 3;\n    max-height: 92vh;\n    overflow-y: auto;\n}\n\n.ov-side-card {\n    position: relative; /* needed for ::before */\n    background-color: rgba(8,3,22,0.90);\n    border: 1px solid rgba(139,92,246,0.18);\n    border-radius: 16px;\n    padding: 16px;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 5px;\n    overflow: hidden;\n}\n\n.ov-side-card.journey {\n    gap: 4px;\n}\n/* Streak card \u2014 orange glow line */\n.ov-side-card.streak::before {\n    background-color: rgba(251,146,60,0.55);\n}\n\n.ov-side-card::before {\n    content: \u0022\u0022;\n    position: absolute;\n    top: -1px; left: 20%; right: 20%;\n    height: 1px;\n    background-color: rgba(139,92,246,0.40);\n    border-radius: 50%;\n}\n\n.ov-side-card.streak {\n    border-color: rgba(251,146,60,0.30);\n    background-color: rgba(251,146,60,0.05);\n}\n\n.ov-side-card-title {\n    font-size: 14px;\n    font-weight: 900;\n    color: rgb(255, 214, 161);\n    letter-spacing: 0.22em;\n    text-transform: uppercase;\n    width: 100%;\n    justify-content: center;\n       margin-bottom: 8px;\n       gap: 3px;\n}\n\n/* \u2500\u2500 Personal best bar \u2500\u2500 */\n.ov-pb-bar-wrap { width: 100%; }\n\n.ov-pb-bar-track {\n    width: 100%;\n    height: 6px;\n    background-color: rgba(255,255,255,0.06);\n    border-radius: 3px;\n    position: relative;\n}\n\n.ov-pb-bar-fill {\n    position: absolute;\n    top: 0; left: 0; bottom: 0;\n    background-color: #a78bfa;\n    border-radius: 3px;\n    min-width: 4px;\n}\n\n.ov-pb-marker {\n    position: absolute;\n    top: -3px;\n    width: 2px;\n    height: 12px;\n    background-color: rgba(251,191,36,0.80);\n    border-radius: 1px;\n}\n\n.ov-pb-scores {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 2px;\n    width: 100%;\n}\n\n.ov-pb-score-current {\n    font-size: 22px;\n    font-weight: 900;\n    color: #a78bfa;\n}\n\n.ov-pb-score-best {\n    font-size: 11px;\n    font-weight: 600;\n    color: rgba(255,255,255,0.30);\n}\n\n.ov-pb-newbest {\n    font-size: 12px;\n    font-weight: 900;\n    color: #fbbf24;\n    background-color: rgba(251,191,36,0.10);\n    border: 1px solid rgba(251,191,36,0.25);\n    border-radius: 8px;\n    padding: 3px 10px;\n}\n\n/* Game over background Squares */\n@keyframes sq-float-1 {\n    0%   { margin-top: 0px;    margin-left: 0px; }\n    25%  { margin-top: -14px;  margin-left: 8px; }\n    50%  { margin-top: -6px;   margin-left: -10px; }\n    75%  { margin-top: 10px;   margin-left: 4px; }\n    100% { margin-top: 0px;    margin-left: 0px; }\n}\n\n@keyframes sq-float-2 {\n    0%   { margin-top: 0px;   margin-left: 0px; }\n    30%  { margin-top: 12px;  margin-left: -8px; }\n    60%  { margin-top: -8px;  margin-left: 12px; }\n    100% { margin-top: 0px;   margin-left: 0px; }\n}\n\n@keyframes sq-pulse {\n    0%,100% { opacity: 0.4; }\n    50%     { opacity: 1.0; }\n}\n\n.ov-bg-squares {\n    position: absolute;\n    top: 0; left: 0;\n    width: 100%;\n    height: 100%;\n    pointer-events: none;\n    z-index: 1;\n}\n\n.ov-bg-sq {\n    position: absolute;\n    border: 1.5px solid;\n}\n\n@keyframes sq-drift-a {\n    0%   { margin-top: 0px;   margin-left: 0px;   opacity: 0.35; }\n    20%  { margin-top: -12px; margin-left: 8px;   opacity: 0.80; }\n    40%  { margin-top: -20px; margin-left: -6px;  opacity: 0.50; }\n    60%  { margin-top: -8px;  margin-left: -14px; opacity: 0.90; }\n    80%  { margin-top: 6px;   margin-left: 4px;   opacity: 0.45; }\n    100% { margin-top: 0px;   margin-left: 0px;   opacity: 0.35; }\n}\n\n@keyframes sq-drift-b {\n    0%   { margin-top: 0px;  margin-left: 0px;  opacity: 0.25; }\n    25%  { margin-top: 14px; margin-left: -10px; opacity: 0.70; }\n    50%  { margin-top: 6px;  margin-left: 16px;  opacity: 0.90; }\n    75%  { margin-top: -10px; margin-left: 8px;  opacity: 0.40; }\n    100% { margin-top: 0px;  margin-left: 0px;   opacity: 0.25; }\n}\n\n.ov-bg-sq.sq-a {\n    animation-name: sq-drift-a;\n    animation-timing-function: ease-in-out;\n    animation-iteration-count: infinite;\n}\n\n.ov-bg-sq.sq-b {\n    animation-name: sq-drift-b;\n    animation-timing-function: ease-in-out;\n    animation-iteration-count: infinite;\n}\n\n/* Alternate float pattern on even squares */\n.ov-bg-sq:nth-child(even) {\n    animation-name: sq-float-2, sq-pulse;\n}\n\n/* \u2500\u2500 Journey \u2500\u2500 */\n.ov-journey-wave {\n    font-size: 32px;\n    font-weight: 900;\n    color: white;\n    gap: 3px;\n}\n\n.ov-journey-sub {\n    font-size: 12px;\n    font-weight: 600;\n    color: rgba(167, 255, 179, 0.719);\n    word-spacing: 1px;\n    width: 100%;\n    display: flex;\n    flex-direction: row;\n    justify-content: space-between;  /* \u2190 pushes label left, value right */\n}\n\n.ov-journey-tag {\n    font-size: 10px;\n    font-weight: 800;\n    color: rgba(255, 255, 255, 0.562);\n    background-color: rgba(255,255,255,0.04);\n    border-radius: 6px;\n    padding: 3px 10px;\n}\n\n.ov-journey-tag.good {\n    color: #34d399;\n    background-color: rgba(52,211,153,0.08);\n}\n\n/* \u2500\u2500 Streak \u2500\u2500 */\n.ov-streak-count {\n    font-size: 48px;\n    font-weight: 900;\n    color: #fb923c;\n}\n\n.ov-streak-label {\n    font-size: 11px;\n    font-weight: 700;\n    color: rgba(255, 199, 125, 0.404);\n}\n\n/* \u2500\u2500 Next unlock \u2500\u2500 */\n.ov-unlock-icon { font-size: 32px; }\n\n.ov-unlock-name {\n    font-size: 13px;\n    font-weight: 800;\n    color: rgba(255,255,255,0.75);\n    width: 100%;\n    text-align: center;\n    justify-content: center;\n    margin-bottom: 2px;\n}\n\n.ov-unlock-cost {\n    font-size: 12px;\n    flex-direction: row;\n    font-weight: 700;\n    color: rgba(129,140,248,0.70);\n    width: 100%;\n    justify-content: center;\n    text-align: center;\n    gap: 3px; /* adds space b/w elements */\n}\n\n.ov-unlock-ready {\n    font-size: 12px;\n    font-weight: 900;\n    color: #34d399;\n}\n\n.ov-unlock-bar-track {\n    width: 100%;\n    height: 4px;\n    background-color: rgba(255,255,255,0.06);\n    border-radius: 2px;\n    position: relative;\n    margin-top: 4px;\n}\n\n.ov-unlock-bar-fill {\n    position: absolute;\n    top: 0; left: 0; bottom: 0;\n    background-color: #818cf8;\n    border-radius: 2px;\n    min-width: 4px;\n}\n\n.ov-particles {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    pointer-events: none;\n    overflow: hidden;\n    z-index: 2;\n}\n.ov-particle {\n    position: absolute;\n    bottom: -20px;\n    opacity: 0;\n    animation-name: particle-rise;\n    animation-timing-function: ease-in;\n    animation-iteration-count: infinite;\n}\n@keyframes particle-rise {\n    0%   { opacity: 0;   transform: translateY(0px); }\n    15%  { opacity: 1; }\n    80%  { opacity: 0.7; transform: translateY(-65vh); }\n    100% { opacity: 0;   transform: translateY(-105vh); }\n}\n\n.ov-box {\n    position: relative;\n    background-color: rgba(8,3,22,0.98);\n    border: 1px solid rgba(139,92,246,0.25);\n    border-radius: 24px;\n    padding: 2rem 2.2rem 2.2rem 2.2rem;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 16px;\n    width: 600px;\n    max-height: 92vh;\n    overflow-y: auto;\n    z-index: 3;\n    box-shadow: 0px 0px 0px 1px rgba(139,92,246,0.08),\n                0px 2px 0px rgba(139,92,246,0.15),\n                0px 24px 60px rgba(0s,0,0,0.80),\n                inset 0px 1px 0px rgba(255,255,255,0.04);\n}\n\n/* Subtle top glow on the box */\n.ov-box::before {\n    content: \u0022\u0022;\n    position: absolute;\n    top: -1px; left: 15%; right: 15%;\n    height: 1px;\n    background-color: rgba(167,139,250,0.50);\n    border-radius: 50%;\n}\n\n.ov-wave-big {\n    font-size: 64px;\n    font-weight: 900;\n    color: #a78bfa;\n    letter-spacing: 0.06em;\n    animation: ov-section-in 0.6s cubic-bezier(0.22,1,0.36,1) 0.3s both;\n}\n\n.ov-stats {\n    display: flex;\n    flex-direction: row;\n    gap: 8px;\n    flex-shrink: 0;\n    width: 100%;\n    justify-content: center;\n}\n\n.ov-stat {\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 5px;\n    background-color: rgba(255,255,255,0.02);\n    border-radius: 14px;\n    padding: 16px 8px 12px 8px;\n    position: relative;\n    overflow: hidden;\n}\n\n.ov-stat::before {\n    content: \u0022\u0022;\n    position: absolute;\n    top: 0; left: 0; right: 0;\n    height: 2px;\n}\n\n.ov-stat-icon { font-size: 18px; opacity: 0.70; }\n\n.ov-stat-val {\n    font-size: 36px;\n    font-weight: 900;\n    letter-spacing: -0.03em;\n    line-height: 1;\n}\n\n.ov-stat-lbl {\n    font-size: 11px;\n    font-weight: 700;\n    color: rgba(255, 255, 255, 0.575);\n    text-transform: uppercase;\n    letter-spacing: 0.14em;\n}\n\n.ov-stat:nth-child(1) {\n    border: 1px solid rgba(52,211,153,0.18);\n    background-color: rgba(52,211,153,0.06);\n}\n.ov-stat:nth-child(2) {\n    border: 1px solid rgba(167,139,250,0.18);\n    background-color: rgba(167,139,250,0.06);\n}\n.ov-stat:nth-child(3) {\n    border: 1px solid rgba(251,191,36,0.18);\n    background-color: rgba(251,191,36,0.06);\n}\n\n/* Color accent per stat \u2014 target each by position */\n.ov-stat:nth-child(1)::before { background-color: #34d399; }\n.ov-stat:nth-child(2)::before { background-color: #a78bfa; }\n.ov-stat:nth-child(3)::before { background-color: #fbbf24; }\n\n.ov-stat:nth-child(1) { border: 1px solid rgba(52,211,153,0.12); }\n.ov-stat:nth-child(2) { border: 1px solid rgba(167,139,250,0.12); }\n.ov-stat:nth-child(3) { border: 1px solid rgba(251,191,36,0.12); }\n\n/* \u2500\u2500 Stat label colors \u2500\u2500 */\n.ov-stat:nth-child(1) .ov-stat-val { color: #6ee7b7; }\n.ov-stat:nth-child(2) .ov-stat-val { color: #d8b4fe; }\n.ov-stat:nth-child(3) .ov-stat-val { color: #fde68a; }\n\n\n/* Background orbs still game over (ov)*/\n@keyframes orb-drift-1 {\n    0%   { margin-left: 0px;   margin-top: 0px; }\n    33%  { margin-left: 18px;  margin-top: -12px; }\n    66%  { margin-left: -10px; margin-top: 20px; }\n    100% { margin-left: 0px;   margin-top: 0px; }\n}\n\n@keyframes orb-drift-2 {\n    0%   { margin-left: 0px;   margin-top: 0px; }\n    40%  { margin-left: -20px; margin-top: 14px; }\n    70%  { margin-left: 12px;  margin-top: -8px; }\n    100% { margin-left: 0px;   margin-top: 0px; }\n}\n\n@keyframes orb-drift-3 {\n    0%   { margin-left: 0px;  margin-top: 0px; }\n    30%  { margin-left: 14px; margin-top: 18px; }\n    60%  { margin-left: -8px; margin-top: -14px; }\n    100% { margin-left: 0px;  margin-top: 0px; }\n}\n\n.ov-orb {\n    position: absolute;\n    border-radius: 50%;\n    pointer-events: none;\n    z-index: 0;\n}\n\n.ov-orb-1 {\n    width: 280px;\n    height: 280px;\n    top: -60px;\n    left: -80px;\n    background-color: rgba(109,40,217,0.06);\n    animation: orb-drift-1 12s ease-in-out infinite;\n}\n\n.ov-orb-2 {\n    width: 200px;\n    height: 200px;\n    bottom: 40px;\n    right: -60px;\n    background-color: rgba(99,102,241,0.05);\n    animation: orb-drift-2 16s ease-in-out infinite;\n}\n\n.ov-orb-3 {\n    width: 150px;\n    height: 150px;\n    top: 40%;\n    left: 30%;\n    background-color: rgba(244,114,182,0.03);\n    animation: orb-drift-3 20s ease-in-out infinite;\n}\n\n/* Game over - secret */\n.ov-secret-title {\n    font-size: 13px;\n    font-weight: 800;\n    color: rgba(244,114,182,0.70);\n    letter-spacing: 0.18em;\n    text-transform: uppercase;\n    animation: ov-in 0.5s ease-out 1.6s both;\n}\n.ov-secrets {\n    display: flex;\n    flex-direction: row;\n    gap: 10px;\n    flex-wrap: wrap;\n    justify-content: center;\n}\n.ov-secret {\n    position: relative;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 6px;\n    background-color: rgba(244,114,182,0.08);\n    border: 1px solid rgba(244,114,182,0.25);\n    border-radius: 14px;\n    padding: 14px 18px;\n    min-width: 80px;\n    overflow: hidden;\n}\n.ov-secret.locked {\n    background-color: rgba(255,255,255,0.03);\n    border-color: rgba(255,255,255,0.08);\n    animation: chest-idle 2s ease-in-out infinite;\n}\n.ov-secret.rare {\n    background-color: rgba(251,191,36,0.10);\n    border-color: rgba(251,191,36,0.45);\n}\n.ov-secret.pop      { animation: chest-pop      0.7s cubic-bezier(0.22,1,0.36,1) forwards; }\n.ov-secret.rare.pop { animation: chest-pop-rare 1.0s cubic-bezier(0.22,1,0.36,1) forwards; }\n.ov-secret-icon { font-size: 28px; color: white; }\n.ov-secret-lbl  { font-size: 13px; font-weight: 800; color: rgba(255,255,255,0.80); }\n.ov-secret.rare .ov-secret-lbl { color: #fbbf24; }\n.ov-secret-shine {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,0.35) 50%, transparent 70%);\n    animation: shine-sweep 0.6s ease-out 0.1s forwards;\n    pointer-events: none;\n}\n\n.ov-section-enter {\n    animation: ov-section-in 0.45s cubic-bezier(0.22,1,0.36,1) forwards;\n}\n\n@keyframes ov-section-in {\n    0%   { margin-top: 24px; }\n    100% { margin-top: 0px; }\n}\n\n/* Clickable secret chests during Game Over */\n.ov-secret.clickable {\n    cursor: none;\n    border-color: rgba(167,139,250,0.55);\n    background-color: rgba(167,139,250,0.10);\n    animation: chest-idle 1.0s ease-in-out infinite;\n    pointer-events: all;\n}\n\n.ov-secret.clickable:hover {\n    background-color: rgba(167,139,250,0.20);\n    border-color: rgba(167,139,250,0.85);\n}\n\n@keyframes chest-idle {\n    0%,100% { transform: rotate(0deg); }\n    25%     { transform: rotate(-2deg) scale(1.02); }\n    75%     { transform: rotate(2deg)  scale(1.02); }\n}\n@keyframes chest-pop {\n    0%   { transform: scale(1.0); }\n    20%  { transform: scale(0.88); }\n    55%  { transform: scale(1.22); }\n    75%  { transform: scale(0.96); }\n    100% { transform: scale(1.0); }\n}\n@keyframes chest-pop-rare {\n    0%   { transform: scale(1.0)  rotate(0deg); }\n    15%  { transform: scale(0.82) rotate(-4deg); }\n    45%  { transform: scale(1.35) rotate(3deg); }\n    65%  { transform: scale(0.93) rotate(-1deg); }\n    82%  { transform: scale(1.06) rotate(0deg); }\n    100% { transform: scale(1.0)  rotate(0deg); }\n}\n@keyframes shine-sweep {\n    0%   { transform: translateX(-200%); }\n    100% { transform: translateX(300%); }\n}\n\n/* Secret section - when clickicking */\n.ov-secret-section {\n    width: 100%;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 12px;\n}\n\n.ov-secret-eyebrow {\n    font-size: 11px;\n    font-weight: 800;\n    color: rgba(244,114,182,0.70);\n    letter-spacing: 0.22em;\n    text-transform: uppercase;\n}\n\n.ov-secret-hint {\n    font-size: 13px;\n    font-weight: 600;\n    color: rgba(255,255,255,0.35);\n}\n\n/* \u2500\u2500 Chest cards \u2500\u2500 */\n\n.ov-chest-cards {\n    display: flex;\n    flex-direction: row;\n    gap: 16px;\n    justify-content: center;\n}\n\n.ov-chest-card {\n    width: 130px;\n    min-height: 150px; /* was height: 150px */\n    border-radius: 16px;\n    border: 1.5px solid rgba(167,139,250,0.15);\n    background-color: rgba(13,6,32,0.90);\n    position: relative;\n    overflow: visible;\n    cursor: none;\n    box-shadow: 0px 4px 0px rgba(0,0,0,0.40);\n}\n\n.ov-chest-card.ready {\n    border-color: rgba(167,139,250,0.55);\n    background-color: rgba(60,20,140,0.25);\n    cursor: none;\n    pointer-events: all;\n    animation: chest-card-ready 1.4s ease-in-out infinite alternate;\n    box-shadow: 0px 4px 0px rgba(0,0,0,0.40),\n                0px 0px 20px rgba(167,139,250,0.15);\n}\n\n.ov-chest-card.ready:hover {\n    border-color: rgba(167,139,250,0.90);\n    background-color: rgba(109,40,217,0.30);\n}\n\n.ov-chest-card.waiting {\n    border-color: rgba(255,255,255,0.06);\n    background-color: rgba(255,255,255,0.02);\n    box-shadow: none;\n}\n\n.ov-chest-card.revealed {\n    border-color: rgba(129,140,248,0.35);\n    background-color: rgba(99,102,241,0.08);\n    box-shadow: 0px 4px 0px rgba(0,0,0,0.30),\n                0px 0px 16px rgba(129,140,248,0.10);\n}\n\n.ov-chest-card.revealed.rare {\n    border-color: rgba(251,191,36,0.50);\n    background-color: rgba(251,191,36,0.08);\n    box-shadow: 0px 4px 0px rgba(0,0,0,0.30),\n                0px 0px 24px rgba(251,191,36,0.12);\n}\n\n.ov-chest-card.pop-in {\n    animation: chest-card-pop 0.7s cubic-bezier(0.22,1,0.36,1) forwards;\n}\n\n.ov-chest-card-inner {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    pointer-events: none;\n}\n\n.ov-chest-emoji {\n    font-size: 48px;\n    animation: chest-card-idle 2.2s ease-in-out infinite alternate;\n}\n\n.ov-chest-card.waiting .ov-chest-emoji {\n    opacity: 0.25;\n    animation: none;\n}\n\n.ov-chest-tap {\n    font-size: 10px;\n    font-weight: 900;\n    color: white;\n    background-color: rgba(167,139,250,0.35);\n    border: 1px solid rgba(167,139,250,0.50);\n    border-radius: 6px;\n    padding: 3px 10px;\n    letter-spacing: 0.12em;\n}\n\n.ov-chest-tap.locked {\n    background-color: rgba(255,255,255,0.05);\n    border-color: rgba(255,255,255,0.08);\n    color: rgba(255,255,255,0.20);\n}\n\n.ov-chest-glow {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    border-radius: 14px;\n    animation: chest-glow-pulse 1.2s ease-in-out infinite alternate;\n    pointer-events: none;\n}\n\n.ov-chest-card.ready {\n    border-color: rgba(167,139,250,0.60);\n    background-color: rgba(109,40,217,0.18);\n    cursor: none;\n    pointer-events: all;\n    animation: chest-card-ready 1.2s ease-in-out infinite alternate;\n}\n\n.ov-chest-reward-icon {\n    font-size: 40px;\n}\n\n.ov-chest-reward-lbl {\n    font-size: 11px;\n    font-weight: 800;\n    color: rgba(255,255,255,0.75);\n    padding: 0px 8px;\n}\n\n/* rare badge */\n.ov-chest-revealed-icon {\n    font-size: 40px;\n}\n\n.ov-chest-revealed-info {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 4px;\n}\n\n.ov-chest-revealed-lbl {\n    font-size: 13px;\n    font-weight: 900;\n    color: white;\n    padding: 0px 4px;\n}\n\n.ov-chest-rare-badge {\n    font-size: 9px;\n    font-weight: 900;\n    color: #fbbf24;\n    background-color: rgba(251,191,36,0.10);\n    border: 1px solid rgba(251,191,36,0.30);\n    border-radius: 20px;\n    padding: 2px 8px;\n    letter-spacing: 0.10em;\n}\n\n.ov-confetti-bit {\n    position: absolute;\n    top: -20px;\n    width: 8px;\n    height: 8px;\n    border-radius: 2px;\n    animation-name: confetti-fly;\n    animation-timing-function: ease-out;\n    animation-fill-mode: forwards;\n    pointer-events: none;\n    z-index: 10;\n}\n\n@keyframes confetti-fly {\n    0%   { opacity: 1; transform: translateY(0px) rotate(0deg) scale(1.2); }\n    30%  { opacity: 1; }\n    100% { opacity: 0; transform: translateY(-140px) rotate(520deg) scale(0.4); }\n}\n\n@keyframes chest-card-ready {\n    0%   { transform: translateY(0px) scale(1.0); }\n    100% { transform: translateY(-5px) scale(1.02); }\n}\n\n@keyframes chest-card-idle {\n    0%   { transform: translateY(0px) rotate(-1.5deg); }\n    100% { transform: translateY(-5px) rotate(1.5deg); }\n}\n\n@keyframes chest-card-pop {\n    0%   { transform: scale(1.0); }\n    20%  { transform: scale(0.85); }\n    55%  { transform: scale(1.25); }\n    75%  { transform: scale(0.96); }\n    100% { transform: scale(1.0); }\n}\n\n@keyframes chest-glow-pulse {\n    0%   { opacity: 0.4; }\n    100% { opacity: 1.0; }\n}\n\n@keyframes chest-shine-sweep {\n    0%   { transform: translateX(-100%); }\n    40%  { transform: translateX(250%); }\n    100% { transform: translateX(250%); }\n}\n\n@keyframes confetti-fly {\n    0%   { opacity: 1; transform: translateY(0px) rotate(0deg) scale(1.0); }\n    60%  { opacity: 1; }\n    100% { opacity: 0; transform: translateY(-90px) rotate(380deg) scale(0.6); }\n}\n\n/* Frags section */\n.ov-frag-section {\n    width: 100%;\n    display: flex;\n     flex-shrink: 0;\n    flex-direction: column;\n    align-items: center;\n    gap: 12px;\n    background-color: rgba(99,102,241,0.05);\n    border: 1px solid rgba(99,102,241,0.18);\n    border-radius: 18px;\n    padding: 20px 22px;\n    position: relative;\n    overflow: hidden;\n}\n\n/* Subtle shimmer across the top */\n.ov-frag-section::before {\n    content: \u0022\u0022;\n    position: absolute;\n    top: 0; left: 0; right: 0;\n    height: 1px;\n    background-color: rgba(129,140,248,0.35);\n}\n\n.ov-frag-breakdown {\n    display: flex;\n    flex-direction: column;\n    gap: 5px;\n    width: 100%;\n}\n\n.ov-frag-line {\n    display: flex;\n    flex-direction: row;\n    justify-content: space-between;\n    align-items: center;\n    padding: 5px 10px;\n    background-color: rgba(255,255,255,0.025);\n    border-radius: 8px;\n    border-left: 2px solid rgba(129,140,248,0.20);\n}\n\n.ov-frag-line-lbl {\n    font-size: 12px;\n    font-weight: 600;\n    color: rgba(255,255,255,0.35);\n}\n\n.ov-frag-line-val {\n    font-size: 13px;\n    font-weight: 900;\n    color: #818cf8;\n}\n\n.ov-frag-label {\n    font-size: 10px;\n    font-weight: 900;\n    color: rgba(129,140,248,0.55);\n    letter-spacing: 0.22em;\n    text-transform: uppercase;\n}\n\n.ov-frag-val {\n    font-size: 52px;\n    font-weight: 900;\n    color: #818cf8;\n    letter-spacing: -0.02em;\n    line-height: 1;\n}\n\n.ov-frag-total {\n    font-size: 12px;\n    font-weight: 700;\n    color: rgba(129,140,248,0.50);\n    border-top: 1px solid rgba(129,140,248,0.10);\n    padding-top: 8px;\n    width: 100%;\n    text-align: center;\n}\n\n.ov-frag-hint {\n    font-size: 11px;\n    color: rgba(255,255,255,0.22);\n    font-weight: 600;\n    letter-spacing: 0.04em;\n}\n\n/**/\n\n.ov-score-section {\n    display: flex;\n    flex-shrink: 0;\n    flex-direction: column;\n    align-items: center;\n    gap: 6px;\n    animation: ov-score-appear 0.6s ease-out forwards;\n}\n\n@keyframes ov-score-appear {\n    0%   { opacity: 0; }\n    100% { opacity: 1; }\n}\n\n.ov-score-label { font-size: 12px; font-weight: 700; color: rgba(255,255,255,0.35); letter-spacing: 0.18em; text-transform: uppercase; }\n.ov-score       { font-size: 62px; font-weight: 900; color: #a78bfa; }\n.ov-score.gold  { color: #fbbf24; }\n.ov-newbest-badge {\n    font-size: 15px;\n    font-weight: 900;\n    color: #fbbf24;\n    background-color: rgba(251,191,36,0.12);\n    border: 1.5px solid rgba(251,191,36,0.40);\n    border-radius: 8px;\n    padding: 4px 14px;\n    letter-spacing: 0.12em;\n    animation: badge-slam 0.5s cubic-bezier(0.22,1,0.36,1) 0.6s both;\n}\n@keyframes badge-slam {\n    0%   { opacity: 0; transform: scale(0.3); }\n    60%  { transform: scale(1.15); }\n    100% { opacity: 1; transform: scale(1.0); }\n}\n.ov-btns {\n    display: flex;\n    flex-direction: row;\n    gap: 12px;\n    flex-shrink: 0; /* never let the buttons get squeezed */\n}\n\n/* \u2500\u2500 Play Again button sweep \u2500\u2500 */\n.ov-btn-wrap {\n    position: relative;\n    overflow: hidden;\n    background-color: #3aed3a;\n    border: 1.5px solid #8bfa9a;\n    border-radius: 12px;\n    color: white;\n    font-size: 30px;\n    font-weight: 800;\n    padding: 15px 40px;\n    cursor: none;\n    pointer-events: all;\n    animation: btn-breathe 2.2s ease-in-out infinite alternate;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n}\n\n@keyframes btn-breathe {\n    0%   { transform: scale(1.0); }\n    100% { transform: scale(1.05); }\n}\n\n.ov-btn-shine {\n    position: absolute;\n    top: 0; bottom: 0;\n    left: -80%;\n    width: 40%;\n    background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.25) 50%, transparent 100%);\n    animation: btn-shine-sweep 3.0s ease-in-out infinite;\n    pointer-events: none;\n}\n\n@keyframes btn-shine-sweep {\n    0%   { left: -80%; }\n    45%  { left: 140%; }\n    100% { left: 140%; }\n}\n\n@keyframes ov-in {\n    0%   { opacity: 0; transform: translateY(12px) scale(0.97); }\n    100% { opacity: 1; transform: translateY(0) scale(1.0); }\n}\n\n.ov-reveal {\n    animation: ov-in 0.4s ease-out both;\n}\n\n/* Main Menu Button */\n.ov-btn-menu {\n    background-color: rgba(255,255,255,0.04);\n    border: 1px solid rgba(255,255,255,0.10);\n    border-radius: 12px;\n    color: rgba(255,255,255,0.40);\n    font-size: 14px;\n    font-weight: 700;\n    padding: 15px 20px;\n    cursor: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n}\n.ov-btn-menu:hover {\n    background-color: rgba(255,255,255,0.08);\n    color: rgba(255,255,255,0.65);\n}\n\n/* -------------------- Modifier strip -----------------------\n* This shows the equipped modifiers as a vertical strip in the sidebar\n* Each item has its icon, name, and a tooltip on hover showing the full description. \n* The green tint matches the \u0022active\u0022 color language used in the shop, \n* creating visual consistency between menu and game.\n\n* Players can now glance at the sidebar mid-run and see exactly what\u0027s buffing them\n* Combined with the boss multiplier suppression warning I added, \n* the player always knows what\u0027s affecting their score\n*/\n.mod-strip {\n    display: flex;\n    flex-direction: column;\n    gap: 6px;\n    margin-bottom: 12px;\n}\n\n.mod-strip-item {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 10px;\n    padding: 8px 12px;\n    background-color: rgba(52,211,153,0.06);\n    border: 1px solid rgba(52,211,153,0.20);\n    border-radius: 8px;\n    transition: background-color 0.2s ease;\n}\n\n.mod-strip-item:hover {\n    background-color: rgba(52,211,153,0.12);\n}\n\n.mod-strip-icon {\n    font-size: 18px;\n    flex-shrink: 0;\n}\n\n.mod-strip-name {\n    font-size: 11px;\n    font-weight: 700;\n    color: rgba(255,255,255,0.75);\n    letter-spacing: 0.04em;\n}\n\n/* \u2500\u2500 Game over reason \u2500\u2500 */\n.ov-reason {\n    animation: ov-section-in 0.5s cubic-bezier(0.22,1,0.36,1) 0.5s both;\n    font-size: 13px;\n    font-weight: 700;\n    color: rgba(255,255,255,0.55);\n    padding: 8px 16px;\n    background-color: rgba(255,255,255,0.04);\n    border: 1px solid rgba(255,255,255,0.08);\n    border-radius: 10px;\n    width: 100%;\n    justify-content: center;\n}\n\n/* ---------------- SCREEN SHAKE ---------------- */\n.hud-overlay {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    pointer-events: none;\n    z-index: 5;\n}\n\n.hud-overlay.shake-small  { animation: shake-small  0.25s ease-out; }\n.hud-overlay.shake-medium { animation: shake-medium 0.35s ease-out; }\n.hud-overlay.shake-strong { animation: shake-strong 0.45s ease-out; }\n.hud-overlay.shake-insane { animation: shake-insane 0.55s ease-out; }\n\n.mini-hand.shake-small  { animation: shake-small  0.25s ease-out; }\n.mini-hand.shake-medium { animation: shake-medium 0.35s ease-out; }\n.mini-hand.shake-strong { animation: shake-strong 0.45s ease-out; }\n.mini-hand.shake-insane { animation: shake-insane 0.55s ease-out; }\n\n@keyframes shake-small {\n    0%,100% { transform: translate(0,0); }\n    25%     { transform: translate(-2px, 1px); }\n    50%     { transform: translate(2px, -1px); }\n    75%     { transform: translate(-1px, 2px); }\n}\n@keyframes shake-medium {\n    0%,100% { transform: translate(0,0); }\n    20%     { transform: translate(-4px, 2px) rotate(-0.3deg); }\n    40%     { transform: translate(4px,-3px) rotate(0.3deg); }\n    60%     { transform: translate(-3px, 3px) rotate(-0.2deg); }\n    80%     { transform: translate(3px,-2px) rotate(0.2deg); }\n}\n@keyframes shake-strong {\n    0%,100% { transform: translate(0,0); }\n    15%     { transform: translate(-6px, 3px) rotate(-0.5deg); }\n    30%     { transform: translate(6px,-4px) rotate(0.5deg); }\n    45%     { transform: translate(-5px, 5px) rotate(-0.4deg); }\n    60%     { transform: translate(5px,-4px) rotate(0.4deg); }\n    75%     { transform: translate(-3px, 2px); }\n    90%     { transform: translate(2px,-2px); }\n}\n@keyframes shake-insane {\n    0%,100% { transform: translate(0,0); }\n    10%     { transform: translate(-9px, 5px) rotate(-0.7deg); }\n    20%     { transform: translate(9px,-6px) rotate(0.7deg); }\n    30%     { transform: translate(-8px, 7px) rotate(-0.6deg); }\n    40%     { transform: translate(8px,-6px) rotate(0.6deg); }\n    50%     { transform: translate(-6px, 5px) rotate(-0.4deg); }\n    60%     { transform: translate(6px,-5px) rotate(0.4deg); }\n    70%     { transform: translate(-4px, 3px); }\n    80%     { transform: translate(3px,-3px); }\n    90%     { transform: translate(-1px, 1px); }\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n/* \u2500\u2500\u2500\u2500\u2500\u2500 Consumable strip \u2014 horizontal bar below challenge bar \u2500\u2500\u2500\u2500\u2500 */\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.consumable-strip {\n    position: absolute;\n    bottom: 120px;         /* sits above the bomb wheel */\n    left: 0;\n    right: 150px;     /* leave room for sidebar */\n    display: flex;\n    flex-direction: row;\n    justify-content: center;\n    gap: 8px;\n    pointer-events: all;\n    z-index: 20;\n}\n\n.consumable-slot {\n    position: relative;\n    left: 70px;\n    width: 54px;\n    height: 54px;\n    border-radius: 10px;\n    border: 1.5px solid rgba(255,255,255,0.10);\n    background-color: rgba(255,255,255,0.04);\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    cursor: none;\n    position: relative;\n}\n\n.consumable-slot.available {\n    border-color: rgba(167,139,250,0.50);\n    background-color: rgba(109,40,217,0.15);\n    cursor: none;\n}\n\n.consumable-slot.available:hover {\n    background-color: rgba(109,40,217,0.30);\n    border-color: rgba(167,139,250,0.80);\n}\n\n.consumable-slot.empty {\n    opacity: 0.25;\n    cursor: none;\n}\n\n.consumable-icon {\n    font-size: 22px;\n    pointer-events: none;\n}\n\n/* Count badge \u2014 bottom right corner of slot */\n.consumable-count {\n    position: absolute;\n    bottom: 2px;\n    right: 4px;\n    font-size: 10px;\n    font-weight: 900;\n    color: rgba(167,139,250,0.95);\n    pointer-events: none;\n}\n\n/* \u2500\u2500 Screen Flash \u2500\u2500 */\n.screen-flash {\n    position: absolute;\n    top: 0; left: 0;\n    width: 2200px;\n    height: 1400px;\n    pointer-events: none;\n    z-index: 100;\n}\n\n.flash-text {\n    position: absolute;\n    top: 485px;        /* roughly vertical center of 1080p */\n    left: 1055px;       /* roughly horizontal center minus half text width */\n    font-size: 72px;\n    font-weight: 900;\n    color: rgba(255,255,255,0.90);\n    pointer-events: none;\n    animation: flash-text-pop 1.2s ease-out forwards;\n        text-shadow:\n        -3px -3px 0 #000,\n         3px -3px 0 #000,\n        -3px  3px 0 #000,\n         3px  3px 0 #000,\n        -4px  0   0 #000,\n         4px  0   0 #000,\n         0   -4px  0 #000,\n         0    4px  0 #000;\n\tz-index: 10;\n}\n\n@keyframes flash-text-pop {\n    0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.6); }\n    12%  { opacity: 1; transform: translate(-50%, -50%) scale(1.05); }\n    70%  { opacity: 1; transform: translate(-50%, -50%) scale(1.0); }\n    100% { opacity: 0; transform: translate(-50%, -50%) scale(0.95); }\n}\n\n@keyframes flash-fade {\n    0%   { opacity: 0; }\n    15%  { opacity: 1; }\n    100% { opacity: 0; }\n}\n\n@keyframes flash-boss-spawn {\n    0%   { opacity: 0; }\n    15%  { opacity: 1; }\n    100% { opacity: 0; }\n}\n\n@keyframes flash-wave {\n    0%   { opacity: 0; }\n    10%  { opacity: 1; }\n    60%  { opacity: 1; }\n    100% { opacity: 0; }\n}\n\n.screen-flash.flash-boss-spawn {\n    background-color: rgba(220, 30, 30, 0.50);\n    animation: flash-boss-spawn 2.5s ease-out forwards;\n}\n\n.screen-flash.flash-boss-death {\n    background-color: rgba(251,191,36,0.55);\n    animation: flash-fade 0.6s ease-out forwards;\n}\n\n.screen-flash.flash-wave-start {\n    background-color: rgba(138, 92, 246, 0.082);  /* was 0.18 \u2014 lighter */\n    animation: flash-wave .9s ease-out forwards;  /* was 2.5s \u2014 faster */\n}\n\n.screen-flash.flash-new-best {\n    background-color: rgba(52,211,153,0.45);\n    animation: flash-fade 1.0s ease-out forwards;\n}\n\n.screen-flash.flash-game-over {\n    background-color: rgba(180,20,20,0.60);\n    animation: flash-fade 0.8s ease-out forwards;\n}\n\n/* ------------------------------------------- */\n/* -------------- Pet Companion -------------- */\n/* ------------------------------------------- */\n\n.hud-pet-wrap {\n    position: absolute;\n    bottom: 55px;\n    left: 15px;\n    width: 220px;\n    height: 870px;\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n    padding: 14px 14px 0px 14px;\n    z-index: 6;\n}\n\n.hud-pet {\n    font-size: 110px;\n    animation: hud-pet-idle 2.5s ease-in-out infinite alternate;\n    opacity: 0.8;\n    margin-bottom: 10px;\n    margin-left: 0px;\n}\n.hud-pet image {\n    width: 110px;\n    height: 110px;\n}\n\n.hud-pet-egg-icon\n{\n    width: 110px;\n    height: 110px;\n}\n\n/* Top section \u2014 modifiers \u002B curse */\n.hud-pet-top {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n    width: 100%;\n}\n\n/* Bottom section \u2014 pet \u002B hearts, pinned to bottom */\n.hud-pet-bottom {\n    margin-top: auto;\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n    width: 100%;\n    font-size: 64px; \n\n}\n\n@keyframes pet-bob {\n    from { transform: translateY(0px); }\n    to   { transform: translateY(-8px); }\n}\n\n.pet-bob {\n    animation: pet-bob 1.4s ease-in-out infinite alternate;\n}\n\n.hud-pet-egg {\n    width: 120px;\n    height: 120px;\n}\n\n.hud-pet-bottom image {\n    width: 134px;\n    height: 134px;\n    margin-left: -10px;\n}\n\n.pet-sbl {\n    font-size: 10px;\n    font-weight: 800;\n    letter-spacing: 0.2em;\n    text-transform: uppercase;\n    color: rgba(166,154,219,0.5);\n}\n\n.hud-pet-card {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    background-color: rgba(18,8,42,0.92);\n    border: 1px solid rgba(167,139,250,0.18);\n    border-radius: 14px 14px 0px 0px;\n    z-index: -1;\n}\n\n@keyframes hud-pet-idle {\n    0%   { transform: translateY(0px) scale(1.0); }\n    100% { transform: translateY(-6px) scale(1.05); }\n}\n\n/* -------------------------------------------------- */\n/* -------------- Hearts/Lives Sidebar -------------- */\n/* -------------------------------------------------- */\n\n.hud-pet-lives {\n    position: absolute;\n    right: 0px;\n    left: 150px;\n    bottom: 10px;\n    display: flex;\n    flex-direction: column;\n    gap: 4px;\n}\n\n.hud-pet-heart {\n    font-size: 40px;\n}\n\n.hud-pet-heart.empty {\n    opacity: 0.20;\n}\n\n/* -------------------------------------------------- */\n/* -------------- BACKGROUND (grid-like) ------------ */\n/* -------------------------------------------------- */\n\n.game-bg-grid {\n    position: absolute;\n    top: 90px; left: 0; right: 0; bottom: 0;\n    pointer-events: none;\n    z-index: 0;\n    overflow: hidden;\n}\n\n.game-bg-cell {\n    position: absolute;\n    width: 232px;\n    height: 232px;\n    border-radius: 10px;\n    border: 2px solid rgba(66, 54, 75, 0.04);\n    z-index: 0;\n}\n\n.game-bg-cell.gbg-idle  { /* inline style from WaveGridColor \u2014 already very dark hex */ }\n.game-bg-cell.gbg-expl  { background-color: rgba(10,4,24,0.95); border-color: rgba(LoadPetLbLoadPetLb251,146,60,0.22); }\n.game-bg-cell.gbg-expl2 { background-color: rgba(10,4,24,0.95); border-color: rgba(251,191,36,0.22); }\n.game-bg-cell.gbg-gold  { background-color: rgba(10,4,24,0.95); border-color: rgba(251,191,36,0.16); }\n.game-bg-cell.gbg-red   { background-color: rgba(10,4,24,0.95); border-color: rgba(248,113,113,0.16); }\n.game-bg-cell.gbg-chain { background-color: rgba(10,4,24,0.95); border-color: rgba(196,181,253,0.14); }\n\n/* SIDEBAR BUTTON (info) */\n.sidebar-toggle {\n    position: absolute;\n    top: 155px;\n    right: 12px;\n    width: 44px;\n    height: 44px;\n    border-radius: 50px;\n    background-color: rgba(180,30,30,0.85);\n    border: 2px solid rgba(255,100,100,0.60);\n    color: white;\n    font-size: 20px;\n    font-weight: 900;\n    pointer-events: all;\n    z-index: 20;\n    cursor: none;\n    box-shadow: 2px 2px 0px rgba(0,0,0,0.60);\n    display: flex;\n    align-items: center;\n    justify-content: center;\n}\n\n.sidebar-toggle:hover {\n    background-color: rgba(220,40,40,0.95);\n    border-color: rgba(255,140,140,0.80);\n}\n\n/**/\n.pet-mod-strip {\n    display: flex;\n    flex-direction: row;\n    gap: 6px;\n    margin-top: 8px;\n}\n\n.pet-mod-item {\n    width: 32px;\n    height: 32px;\n    border-radius: 8px;\n    background-color: rgba(52,211,153,0.10);\n    border: 1px solid rgba(52,211,153,0.25);\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    font-size: 16px;\n}\n\n.pet-curse-warning {\n    font-size: 13px;\n    font-weight: 800;\n    color: rgba(248,113,113,0.85);\n    margin-top: 6px;\n    padding: 4px 8px;\n    background-color: rgba(239,68,68,0.10);\n    border: 1px solid rgba(248,113,113,0.20);\n    border-radius: 6px;\n}\n\n/* Min chest requirement (displayed on left side bar)*/\n.hud-min-chests {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 6px;\n    padding: 4px 23px;\n    background-color: rgba(0,0,0,0.35);\n    border-radius: 6px;\n    margin-top: 4px;\n    justify-content: center;\n}\n\n.hud-min-chests-lbl {\n    font-size: 18px;\n    font-weight: 700;\n    color: rgba(255,255,255,0.55);\n}\n\n.hud-min-chests-val {\n    font-size: 18px;\n    font-weight: 900;\n    color: rgba(251,191,36,0.90);\n}\n\n/* MUSIC DEBUG */\n.mm-dev-music-panel {\n    position: absolute;\n    bottom: 20px;\n    right: 20px;\n    display: flex;\n    flex-direction: column;\n    gap: 4px;\n    padding: 8px;\n    background-color: rgba(4,1,13,0.90);\n    border: 1px dashed rgba(244,114,182,0.30);\n    border-radius: 6px;\n    z-index: 99;\n    pointer-events: all;\n}\n\n.mm-dev-music-label {\n    font-size: 9px;\n    font-weight: 900;\n    color: rgba(244,114,182,0.60);\n    letter-spacing: 0.20em;\n    text-transform: uppercase;\n    margin-bottom: 2px;\n}\n\n.mm-dev-btn {\n    background-color: rgba(244,114,182,0.15);\n    border: 1px solid rgba(244,114,182,0.30);\n    border-radius: 4px;\n    color: rgba(244,114,182,0.85);\n    font-size: 10px;\n    font-weight: 700;\n    padding: 4px 8px;\n    cursor: none;\n    pointer-events: all;\n    letter-spacing: 0.05em;\n}\n\n.mm-dev-btn:hover {\n    background-color: rgba(244,114,182,0.25);\n}\n\n.mm-dev-btn-active {\n    background-color: rgba(52,211,153,0.20);\n    border-color: rgba(52,211,153,0.50);\n    color: rgba(52,211,153,0.90);\n}\n\n/* =============== New SURVIVAL mode =============== */\n.survival-timer {\n    position: absolute;\n    top: 100px;\n    left: 50%;\n    transform: translateX(-50%);\n    width: 160px;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    padding: 8px 16px;\n    background-color: rgba(10,4,28,0.80);\n    border: 2px solid rgba(239,68,68,0.35);\n    border-radius: 10px;\n    box-shadow: 3px 3px 0px rgba(0,0,0,0.55);\n    z-index: 10;\n}\n\n.survival-timer-icon {\n    font-size: 16px;\n}\n\n.survival-timer-val {\n    font-size: 20px;\n    font-weight: 900;\n    color: rgba(251,191,36,0.90);\n    letter-spacing: 0.04em;\n    font-variant-numeric: tabular-nums;\n}\n\n.survival-spawn-track {\n    position: absolute;\n    top: 145px; /* just below the timer pill */\n    left: 50%;\n    transform: translateX(-50%);\n    width: 160px;\n    height: 6px;\n    background-color: rgba(255,255,255,0.08);\n    border-radius: 3px;\n    overflow: hidden;\n    z-index: 10;\n}\n\n.survival-spawn-fill {\n    height: 100%;\n    background-color: rgba(251,191,36,0.85);\n    transition: width 0.1s linear;\n}\n\n/* CAT */\n.mm-pet-hero-emoji image {\n    width: 170px;   /* bump this up to whatever size you want */\n    height: 170px;\n    image-rendering: pixelated; /* keep it crisp if it\u0027s pixel art */\n    margin-left: -10px;\n}\n\n.mm-pet-hero-emoji {\n    position: relative;\n}\n.cat-anchor { position: relative; }\n\n\n\n.cat-speech-bubble {\n    position: absolute;\n    top: -50px;\n    left: 50%;\n    transform: translateX(-50%);\n    background-color: rgba(255,255,255,0.95);\n    color: #1a1030;\n    font-size: 14px;\n    font-weight: 800;\n    padding: 8px 14px;\n    border-radius: 12px;\n    white-space: nowrap;\n    box-shadow: 2px 2px 0px rgba(0,0,0,0.4);\n    z-index: 10;\n    animation: bubble-pop 0.2s ease-out both;\n}\n\n.cat-speech-bubble::after {\n    content: \u0022\u0022;\n    position: absolute;\n    bottom: -6px;\n    left: 50%;\n    width: 12px;\n    height: 12px;\n    background-color: rgba(255,255,255,0.95);\n    transform: translateX(-50%) rotate(45deg);\n}\n\n@keyframes bubble-pop {\n    0%   { opacity: 0; transform: translateX(-50%) scale(0.7); }\n    100% { opacity: 1; transform: translateX(-50%) scale(1.0); }\n}\n\n/* Back button */\n.back-toggle {\n    top: 211px; /* 155 \u002B 44 \u002B 12 gap */\n    background-color: rgba(60,60,180,0.85);\n    border-color: rgba(140,140,255,0.60);\n}\n\n.back-toggle:hover {\n    background-color: rgba(80,80,220,0.95);\n    border-color: rgba(160,160,255,0.80);\n}\n\n.back-confirm-overlay {\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n    background-color: rgba(4,1,13,0.85);\n    z-index: 90;\n    pointer-events: all;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n}\n\n.back-confirm-popup {\n    background-color: rgba(20,8,50,0.98);\n    border: 2px solid rgba(167,139,250,0.55);\n    border-radius: 18px;\n    padding: 32px 40px;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 16px;\n    max-width: 380px;\n    text-align: center;\n    box-shadow: 6px 6px 0px rgba(20,0,60,0.80);\n}\n\n.back-confirm-title { font-size: 22px; font-weight: 900; color: white; }\n.back-confirm-desc { font-size: 13px; color: rgba(255,255,255,0.55); line-height: 1.5; }\n.back-confirm-btns { display: flex; flex-direction: row; gap: 12px; margin-top: 8px; }\n\n.back-confirm-btn {\n    border-radius: 10px;\n    font-size: 14px;\n    font-weight: 800;\n    padding: 12px 20px;\n    cursor: none;\n    pointer-events: all;\n    border: 2px solid transparent;\n}\n\n.back-confirm-btn.cancel { background-color: rgba(52,211,153,0.15); border-color: rgba(52,211,153,0.45); color: #6ee7b7; }\n.back-confirm-btn.confirm { background-color: rgba(239,68,68,0.15); border-color: rgba(239,68,68,0.45); color: #fca5a5; }\n\n\n/* Survival hype track */\n.survival-strip {\n    position: absolute;\n    top: 96px;\n    left: 0;\n    right: 0;\n    height: 44px;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    justify-content: center;\n    gap: 10px;\n    z-index: 10;\n    pointer-events: none;\n}\n\n.survival-timer {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 6px;\n    padding: 5px 12px;\n    background-color: rgba(10,4,28,0.85);\n    border: 2px solid rgba(239,68,68,0.35);\n    border-radius: 8px;\n    flex-shrink: 0;\n}\n.survival-timer-icon { font-size: 14px; }\n.survival-timer-val {\n    font-size: 18px;\n    font-weight: 900;\n    color: rgba(251,191,36,0.9);\n}\n\n.survival-hype-track {\n    position: relative;\n    width: 130px;\n    height: 20px;\n    background-color: rgba(255,255,255,0.08);\n    border: 1px solid rgba(251,146,60,0.35);\n    border-radius: 6px;\n    overflow: hidden;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    flex-shrink: 0;\n}\n.survival-hype-fill {\n    position: absolute;\n    top: 0; left: 0; bottom: 0;\n    background-color: rgba(251,146,60,0.75);\n    transition: width 0.12s linear;\n}\n.survival-hype-track.maxed {\n    border-color: rgba(251,191,36,0.95);\n    animation: hype-pulse 0.5s ease-in-out infinite alternate;\n}\n.survival-hype-track.maxed .survival-hype-fill { background-color: rgba(251,191,36,0.9); }\n.survival-hype-lbl {\n    font-size: 9px;\n    font-weight: 900;\n    letter-spacing: 0.12em;\n    color: rgba(255,255,255,0.9);\n    z-index: 1;\n}\n@keyframes hype-pulse {\n    from { border-color: rgba(251,191,36,0.5); }\n    to   { border-color: rgba(251,191,36,1.0); }\n}\n\n.survival-goal {\n    position: relative;\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    gap: 8px;\n    padding: 5px 12px;\n    background-color: rgba(10,4,28,0.85);\n    border: 1px solid rgba(167,139,250,0.35);\n    border-radius: 8px;\n    flex-shrink: 0;\n}\n.survival-goal-lbl  { font-size: 11px; font-weight: 800; color: rgba(255,255,255,0.85); }\n.survival-goal-prog { font-size: 12px; font-weight: 900; color: #a78bfa; }\n.survival-goal-timer {\n    position: absolute;\n    bottom: 0; left: 0; right: 0;\n    height: 2px;\n    background-color: rgba(255,255,255,0.06);\n}\n.survival-goal-timer-fill {\n    height: 100%;\n    background-color: rgba(167,139,250,0.7);\n    transition: width 0.2s linear;\n}\n\n.survival-spawn-track {\n    width: 90px;\n    height: 6px;\n    background-color: rgba(255,255,255,0.08);\n    border-radius: 3px;\n    overflow: hidden;\n    flex-shrink: 0;\n}\n.survival-spawn-fill {\n    height: 100%;\n    background-color: rgba(251,191,36,0.85);\n    transition: width 0.1s linear;\n}"},{"Ident":"playnplay.chain_reaction","Path":"styles/base/_splitcontainer.scss","FileName":"_splitcontainer.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"\r\n.splitcontainer\r\n{\r\n\tflex-grow: 1;\r\n\r\n\t\u003E .split-left, \u003E .split-right\r\n\t{\r\n\t\tflex-grow: 1;\r\n\t}\r\n\r\n\t\u003E .split-left\r\n\t{\r\n\t\theight: 100%;\r\n\t}\r\n\r\n\t\u003E .splitter\r\n\t{\r\n\t\tflex-grow: 1;\r\n\t\twidth: 8px;\r\n\t\tcursor: ew-resize;\r\n\r\n\t\t\u0026:hover\r\n\t\t{\r\n\t\t}\r\n\t}\r\n\r\n\t\u0026.dragging \u003E .splitter\r\n\t{\r\n\t}\r\n\r\n\t\u0026.vertical\r\n\t{\r\n\t\tflex-direction: column;\r\n\r\n\t\t\u003E .splitter\r\n\t\t{\r\n\t\t\theight: 8px;\r\n\t\t\twidth: 100%;\r\n\t\t\tcursor: ns-resize;\r\n\t\t}\r\n\t}\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"styles/form/_coloreditor.scss","FileName":"_coloreditor.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"\r\n$primary: red !default;\r\n$primary-alt: white !default;\r\n\r\n$switch-padding: 6px !default;\r\n\r\n.coloreditor\r\n{\r\n\tflex-direction: column;\r\n\tmin-width: 250px;\r\n\tmax-width: 250px;\r\n\r\n\t\u003E .canvas\r\n\t{\r\n\t\tborder-top: 1px solid rgba( white, 0.5 );\r\n\t\tflex-grow: 1;\r\n\t\tflex-direction: column;\r\n\t\tpadding: 10px 15px;\r\n\r\n\r\n\t\t\u003E textentry.textentry\r\n\t\t{\r\n\t\t\tflex-grow: 0;\r\n\t\t\tflex-shrink: 0;\r\n\t\t\twidth: auto;\r\n\t\t}\r\n\t}\r\n\r\n\t.slider\r\n\t{\r\n\t\tpadding: 6.5px;\r\n\t}\r\n\r\n\t.slider .track\r\n\t{\r\n\t\theight: 14px;\r\n\t\tborder-radius: 14px;\r\n\t\tleft: 0px;\r\n\t\tright: 0px;\r\n\t\ttransition: all 0.1s ease-out;\r\n\r\n\t\t.inner\r\n\t\t{\r\n\t\t\tdisplay: none;\r\n\t\t}\r\n\t}\r\n\r\n\t.slider:hover, .slider:active\r\n\t{\r\n\t\tpadding: 7px;\r\n\r\n\t\t.track\r\n\t\t{\r\n\t\t\theight: 20px;\r\n\t\t\tborder-radius: 6px;\r\n\t\t}\r\n\r\n\t\t.thumb\r\n\t\t{\r\n\t\t\twidth: 10px;\r\n\t\t\theight: 16px;\r\n\t\t\tmargin-left: 2px;\r\n\t\t}\r\n\t}\r\n\r\n\t.slider .thumb\r\n\t{\r\n\t\twidth: 10px;\r\n\t\theight: 10px;\r\n\t\tbox-shadow: 0px 0px 0px transparent;\r\n\t\tbackground-color: white;\r\n\t\tbackdrop-filter: invert( 1 );\r\n\t\tmargin-left: 1px;\r\n\t\tpointer-events: none;\r\n\t}\r\n\r\n\t.alpha_slider .track\r\n\t{\r\n\t\tbackground-image: linear-gradient( to right, rgba( #ccc, 0.05 ), rgba( #ccc, 0.9 ) );\r\n\t}\r\n\r\n\t.hue .track\r\n\t{\r\n\t\tbackground: linear-gradient(to right, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);\r\n\r\n\t\t.inner\r\n\t\t{\r\n\t\t\tdisplay: none;\r\n\t\t}\r\n\t}\r\n\r\n\t.satval\r\n\t{\r\n\t\tbackground: linear-gradient(to right, white, transparent);\r\n\t\theight: 200px;\r\n\t\tposition: relative;\r\n\r\n\t\t\u0026:active\r\n\t\t{\r\n\t\t\tz-index: 10;\r\n\t\t\tcursor: none;\r\n\t\t}\r\n\r\n\t\t.value_gradient\r\n\t\t{\r\n\t\t\tbackground: linear-gradient(to bottom, black, white);\r\n\t\t\tposition: absolute;\r\n\t\t\ttop: 0;\r\n\t\t\tleft: 0;\r\n\t\t\tright: 0;\r\n\t\t\tbottom: 0;\r\n\t\t\tpointer-events: none;\r\n\t\t\tmix-blend-mode: multiply;\r\n\t\t}\r\n\r\n\t\t.thumb\r\n\t\t{\r\n\t\t\tz-index: 10;\r\n\t\t}\r\n\t}\r\n\r\n\t.presets\r\n\t{\r\n\t\tflex-wrap: wrap;\r\n\t\talign-content: center;\r\n\t\tjustify-content: center;\r\n\t\tpadding: 15px;\r\n\t\tborder-top: 1px solid rgba( white, 0.5 );\r\n\r\n\t\t.preset\r\n\t\t{\r\n\t\t\tbackground-color: white;\r\n\t\t\twidth: 16px;\r\n\t\t\theight: 16px;\r\n\t\t\tmargin: 2px;\r\n\t\t\tborder-radius: 20px;\r\n\t\t\tcursor: pointer;\r\n\t\t\t\r\n\t\t\t\u0026:hover\r\n\t\t\t{\r\n\t\t\t\tbox-shadow: 0px 0px 1px 2px white;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\r\n.coloreditorrgba\r\n{\r\n\tflex-direction: column;\r\n\r\n\t.slider\r\n\t{\r\n\t\tpadding-right: 16px;\r\n\t}\r\n\r\n\t.slider .track\r\n\t{\r\n\t\tbackground-color: transparent;\r\n\t\theight: 14px;\r\n\t\tborder-radius: 14px;\r\n\t\tleft: 0px;\r\n\t\tright: 0px;\r\n\r\n\t\t.inner\r\n\t\t{\r\n\t\t\tdisplay: none;\r\n\t\t}\r\n\t}\r\n\r\n\t.slider .thumb\r\n\t{\r\n\t\twidth: 12px;\r\n\t\theight: 12px;\r\n\t\tbox-shadow: 0px 0px 0px transparent;\r\n\t\tbackground-color: white;\r\n\t\tmargin-left: 2px;\r\n\t\tbackdrop-filter: invert( 1 );\r\n\t}\r\n\r\n\t.red_slider .track\r\n\t{\r\n\t\tbackground-image: linear-gradient( to right, rgba( #e63439, 0.1 ), rgba( #e63439, 0.9 ) );\r\n\t}\r\n\r\n\t.green_slider .track\r\n\t{\r\n\t\tbackground-image: linear-gradient( to right, rgba( #54be35, 0.1 ), rgba( #54be35, 0.9 ) );\r\n\t}\r\n\r\n\t.blue_slider .track\r\n\t{\r\n\t\tbackground-image: linear-gradient( to right, rgba( #3472e6, 0.1 ), rgba( #3472e6, 0.9 ) );\r\n\t}\r\n\r\n\t.alpha_slider .track\r\n\t{\r\n\t\tbackground-image: linear-gradient( to right, rgba( #ccc, 0.05 ), rgba( #ccc, 0.9 ) );\r\n\t}\r\n}\r\n\r\n\r\n.coloreditorhsva\r\n{\r\n\tflex-direction: column;\r\n\r\n\t.slider\r\n\t{\r\n\t\tpadding-right: 16px;\r\n\t}\r\n\r\n\t.slider .track\r\n\t{\r\n\t\tbackground-color: transparent;\r\n\t\theight: 14px;\r\n\t\tborder-radius: 14px;\r\n\t\tleft: 0px;\r\n\t\tright: 0px;\r\n\r\n\t\t.inner\r\n\t\t{\r\n\t\t\tdisplay: none;\r\n\t\t}\r\n\t}\r\n\r\n\t.slider .thumb\r\n\t{\r\n\t\twidth: 12px;\r\n\t\theight: 12px;\r\n\t\tbox-shadow: 0px 0px 0px transparent;\r\n\t\tbackground-color: white;\r\n\t\tmargin-left: 2px;\r\n\t\tbackdrop-filter: invert( 1 );\r\n\t}\r\n\t\t\r\n\t.hue_slider .track\r\n\t{\r\n\t\tbackground-image: linear-gradient( to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00 );\r\n\t}\r\n\r\n\t.alpha_slider .track\r\n\t{\r\n\t\tbackground-image: linear-gradient( to right, rgba( #ccc, 0.05 ), rgba( #ccc, 0.9 ) );\r\n\t}\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"styles/form/_colorproperty.scss","FileName":"_colorproperty.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"\r\n\r\n.colorproperty\r\n{\r\n\talign-items: center;\r\n\tposition: relative;\r\n\r\n\t.colorsquare\r\n\t{\r\n\t\twidth: 20px;\r\n\t\theight: 20px;\r\n\t\tborder-radius: 4px;\r\n\t\tmargin-right: 8px;\r\n\t\tposition: absolute;\r\n\t\tleft: 7px;\r\n\t\tz-index: 1;\r\n\t\tcursor: pointer;\r\n\t}\r\n\r\n\t\u003E .textentry:not( .a.b.c )\r\n\t{\r\n\t\tpadding-left: 36px;\r\n\t}\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"ui/controls/color/coloralphacontrol.cs.scss","FileName":"coloralphacontrol.cs.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":354834,"IsPrivate":false,"Code":"ColorAlphaControl\r\n{\r\n\tgap: 0.5rem;\r\n\tflex-grow: 1;\r\n\tpointer-events: all;\r\n\tbackground: linear-gradient( to right, black, white );\r\n\tborder-radius: 4px;\r\n\tpadding: 2px;\r\n\theight: 12px;\r\n\tposition: relative;\r\n\tcursor: pointer;\r\n\tborder: 1px solid #333;\r\n\r\n\t\u0026:hover\r\n\t{\r\n\t\tborder: 1px solid #08f;\r\n\t}\r\n\r\n\t\u0026:active\r\n\t{\r\n\t\tborder: 1px solid #fff;\r\n\t}\r\n\r\n\t.handle\r\n\t{\r\n\t\ttop: -5px;\r\n\t\tbottom: -5px;\r\n\t\taspect-ratio: 1;\r\n\t\tborder-radius: 100px;\r\n\t\tborder: 2px solid #444;\r\n\t\tposition: absolute;\r\n\t\tbackground-color: white;\r\n\t\tbox-shadow: 2px 2px 16px #000a;\r\n\t\ttransform: translateX( -50% );\r\n\t\tpointer-events: none;\r\n\t}\r\n}\r\n"}]}