{"TotalCount":92,"Files":[{"Ident":"playnplay.chain_reaction","Path":".obj/__compiler_extra.cs","FileName":"__compiler_extra.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":343168,"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-19T13:11:37.0448314Z\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\u00220.0.111.0\u0022)]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\u00220.0.111.0\u0022)]"},{"Ident":"playnplay.chain_reaction","Path":"Game/2D/MusicManager.cs","FileName":"MusicManager.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":343168,"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/Idle/UI/IdleHud.razor","FileName":"IdleHud.razor","PackageType":"game","CodeKind":"Game","AssetVersionId":343168,"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\u0022\u003E\n\t\t\t\u003Cbutton class=\u0022idle-back\u0022 onclick=@OnBack\u003E\u2302\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\u0022\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\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@if ( ShowBoosts )\n\t\t\t\t{\n\t\t\t\t\t\u003Cdiv class=\u0022idle-card\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\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@foreach ( var t in IdleBalance.Tiers )\n\t\t\t\t\t{\n\t\t\t\t\t\tvar tier = t;\n\t\t\t\t\t\tbool got = S.CycleCores \u003E= tier.Requires;\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( tier, 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\u0022\u003E\n\t\t\t\t\t\u003Cdiv class=\u0022idle-board @SurgeClass() @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\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()\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\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\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\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 IdleUpgradeCatalog.Perks )\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 == \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\u003EReactor Log \u2014 every entry 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\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\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\u003Cbutton class=\u0022idle-modal-btn\u0022 onclick=@OnCloseIntro\u003ESTART THE REACTOR\u003C/button\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 || _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   _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\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.FluxEarned 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\tlong  _nextUpValue;   // stat value of the player directly above, 0 == none above\n\tint   _rankClimb;     // places gained since the last session\n\n\tconst float  RankRefreshSeconds = 90f;\n\tconst string RankStat  = \u0022chain_reaction_reactor\u0022;\n\tconst string GameIdent = \u0022playnplay.chain_reaction\u0022;\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( GameIdent, RankStat );\n\t\t\tb.SetAggregationMax();\n\t\t\tb.SetSortDescending();\n\t\t\tb.MaxEntries = 100;\n\t\t\tawait b.Refresh();\n\t\t\t_rankBoard = b;\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\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\t// Not on the board yet \u2014 show the bottom of it, so there is still a\n\t\t// named target to chase rather than an empty card.\n\t\tif ( meIdx \u003C 0 )\n\t\t{\n\t\t\tint from = Math.Max( 0, entries.Length - 2 );\n\t\t\tfor ( int i = from; i \u003C entries.Length; i\u002B\u002B )\n\t\t\t\t_rankRows.Add( MakeRankRow( entries[i], i \u002B 1, false ) );\n\n\t\t\t_nextUpValue = (long)entries[entries.Length - 1].Value;\n\t\t\treturn;\n\t\t}\n\n\t\t_myRank = meIdx \u002B 1;\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 \u002B 1, i == meIdx ) );\n\n\t\tif ( meIdx \u003E 0 ) _nextUpValue = (long)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 \u003C prev ) _rankClimb = prev - _myRank;\n\n\t\tif ( S != null \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, int rank, 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\tlong val = isMe\n\t\t\t? Math.Max( (long)e.Value, S?.FluxEarned ?? 0 )\n\t\t\t: (long)e.Value;\n\n\t\treturn new RankRow\n\t\t{\n\t\t\tRankLabel  = \u0022#\u0022 \u002B rank.ToString(),\n\t\t\tName       = isMe ? \u0022you\u0022 : RankName( e ),\n\t\t\tValueLabel = val.ToString( \u0022N0\u0022 ),\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 \u0022Earn Flux 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\tlong mine = S?.FluxEarned ?? 0;\n\t\tlong gap  = _nextUpValue - mine;\n\n\t\tif ( gap \u003C= 0 ) return \u0022Ahead \u2014 board updates shortly\u0022;\n\t\treturn gap.ToString( \u0022N0\u0022 ) \u002B \u0022 Flux 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_showIntro    = S != null \u0026\u0026 !S.IntroShown;\n\t\t\t\t_tab          = \u0022reactor\u0022;\n\t\t\t\t_rankNextFetch = 0f;   // refetch on entering idle mode\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\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_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( _showIntro );\n\t\thc.Add( ModalOpen );\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 )       { _tab = tab; Sound.Play( \u0022UI_Button\u0022 ); }\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 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\tChainReactionGame.Save?.Save();\n\t\t}\n\t\tSound.Play( \u0022PlayButton\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\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\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( IdleBalance.ChestTier tier, bool unlocked )\n\t\t=\u003E unlocked ? tier.Blurb : $\u0022unlocks at {IdleFormat.Short( tier.Requires )} cycle Cores\u0022;\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( IdleBalance.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 $\u0022Lv {lvl} / {p.MaxLevel}\u0022;\n\tstring PerkCostLabel( bool maxed, int cost ) =\u003E maxed ? \u0022MAXED\u0022 : $\u0022\uD83D\uDD37 {cost}\u0022;\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 \u2014 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\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":343168,"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":343168,"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":343168,"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":343168,"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":343168,"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"},{"Ident":"playnplay.chain_reaction","Path":"Game/2D/UI/MainMenuTutorial.razor","FileName":"MainMenuTutorial.razor","PackageType":"game","CodeKind":"Game","AssetVersionId":343168,"Code":"@using System;\r\n@using Sandbox;\r\n@using Sandbox.Game.Menu\r\n@using Sandbox.UI;\r\n@inherits PanelComponent\r\n@namespace Sandbox\r\n\r\n\u003Croot\u003E\r\n    @if(_active)\r\n    {\r\n\t\t@* \u2500\u2500 Debug card \u2500\u2500 *@\r\n\t\t@if(_debugMode)\r\n\t\t{\r\n\t\t\t\u003Cdiv class=\u0022tut-debug-card\u0022\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-title\u0022\u003EMM TUT \u2014 Step @_step\u003C/div\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\r\n\t\t\t\t\t\u003Cspan\u003EBL: @_debugBL.ToString(\u0022F1\u0022)%\u003C/span\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBL -= 1f; StateHasChanged(); })\u003E\u25C0\u003C/button\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBL \u002B= 1f; StateHasChanged(); })\u003E\u25B6\u003C/button\u003E\r\n\t\t\t\t\u003C/div\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\r\n\t\t\t\t\t\u003Cspan\u003EBY: @_debugBY.ToString(\u0022F0\u0022)px\u003C/span\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBY -= 10f; StateHasChanged(); })\u003E\u25B2\u003C/button\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBY \u002B= 10f; StateHasChanged(); })\u003E\u25BC\u003C/button\u003E\r\n\t\t\t\t\u003C/div\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\r\n\t\t\t\t\t\u003Cspan\u003EBR: @_debugBR.ToString(\u0022F1\u0022)%\u003C/span\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBR -= 1f; StateHasChanged(); })\u003E\u25C0\u003C/button\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugBR \u002B= 1f; StateHasChanged(); })\u003E\u25B6\u003C/button\u003E\r\n\t\t\t\t\u003C/div\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\r\n\t\t\t\t\t\u003Cspan\u003EHX: @_debugHX.ToString(\u0022F1\u0022)%\u003C/span\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHX -= 1f; StateHasChanged(); })\u003E\u25C0\u003C/button\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHX \u002B= 1f; StateHasChanged(); })\u003E\u25B6\u003C/button\u003E\r\n\t\t\t\t\u003C/div\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\r\n\t\t\t\t\t\u003Cspan\u003EHY: @_debugHY.ToString(\u0022F0\u0022)px\u003C/span\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHY -= 10f; StateHasChanged(); })\u003E\u25B2\u003C/button\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHY \u002B= 10f; StateHasChanged(); })\u003E\u25BC\u003C/button\u003E\r\n\t\t\t\t\u003C/div\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\r\n\t\t\t\t\t\u003Cspan\u003EHW: @_debugHW.ToString(\u0022F0\u0022)px\u003C/span\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHW -= 10f; StateHasChanged(); })\u003E\u25C0\u003C/button\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHW \u002B= 10f; StateHasChanged(); })\u003E\u25B6\u003C/button\u003E\r\n\t\t\t\t\u003C/div\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-debug-row\u0022\u003E\r\n\t\t\t\t\t\u003Cspan\u003EHH: @_debugHH.ToString(\u0022F0\u0022)px\u003C/span\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHH -= 10f; StateHasChanged(); })\u003E\u25B2\u003C/button\u003E\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-debug-btn\u0022 onclick=@(() =\u003E { _debugHH \u002B= 10f; StateHasChanged(); })\u003E\u25BC\u003C/button\u003E\r\n\t\t\t\t\u003C/div\u003E\r\n\t\t\t\t\u003Cbutton class=\u0022tut-debug-log\u0022 onclick=@LogCurrentValues\u003E\uD83D\uDCCB Log\u003C/button\u003E\r\n\t\t\t\u003C/div\u003E\r\n\t\t}\r\n\r\n\t\t@* \u2500\u2500 Dim layer \u2500\u2500 *@\r\n\t\t\u003Cdiv class=\u0022tut-dim\u0022\u003E\u003C/div\u003E\r\n\r\n\t\t@* \u2500\u2500 Highlight \u2500\u2500 *@\r\n\t\t@if(_step \u003C _steps.Length \u0026\u0026 _steps[_step].HW \u003E 0)\r\n\t\t{\r\n\t\t\tvar s = _steps[_step];\r\n\t\t\tfloat hx = _debugHX != 0f ? _debugHX : s.HX;\r\n\t\t\tfloat hy = _debugHY != 0f ? _debugHY : s.HY;\r\n\t\t\tfloat hw = _debugHW != 0f ? _debugHW : s.HW;\r\n\t\t\tfloat hh = _debugHH != 0f ? _debugHH : s.HH;\r\n\t\t\t\u003Cdiv class=\u0022tut-highlight\u0022\r\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\r\n\t\t\t\u003C/div\u003E\r\n\t\t}\r\n\r\n\t\t@* \u2500\u2500 Instruction box \u2500\u2500 *@\r\n\t\t@if(_step \u003C _steps.Length)\r\n\t\t{\r\n\t\t\tvar s = _steps[_step];\r\n\t\t\tfloat bl = _debugBL != 0f ? _debugBL : s.BL;\r\n\t\t\tfloat br = _debugBR != 0f ? _debugBR : s.BR;\r\n\t\t\tfloat by = _debugBY != 0f ? _debugBY : s.BY;\r\n\r\n\t\t\tfloat boxWidthPct = s.BW \u003E 0f ? (s.BW / 1920f * 100f) : (380f / 1920f * 100f);\r\n\r\n\t\t\tstring posStyle = br != 0f\r\n\t\t\t\t? $\u0022left:{(100f - br - boxWidthPct).ToString(\u0022F1\u0022)}%;\u0022\r\n\t\t\t\t: $\u0022left:{bl.ToString(\u0022F1\u0022)}%;\u0022;\r\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;\r\n\r\n\t\t\t\u003Cdiv class=\u0022tut-box\u0022 style=\u0022@boxStyle\u0022\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-step-count\u0022\u003E@(_step \u002B 1) / @_steps.Length\u003C/div\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-title\u0022\u003E@s.Title\u003C/div\u003E\r\n\t\t\t\t\u003Cdiv class=\u0022tut-text\u0022\u003E@s.Text\u003C/div\u003E\r\n\r\n\t\t\t\t@* Action hint \u2014 shown when player must act *@\r\n\t\t\t\t@if(s.Trigger == MMTutTrigger.HeartPurchased)\r\n\t\t\t\t{\r\n\t\t\t\t\t\u003Cdiv class=\u0022tut-action-hint\u0022\u003E\uD83D\uDC46 @s.ActionHint\u003C/div\u003E\r\n\t\t\t\t}\r\n\r\n\t\t\t\t@* Next button \u2014 shown on Any steps *@\r\n\t\t\t\t@if(s.Trigger == MMTutTrigger.Any)\r\n\t\t\t\t{\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-btn-next\u0022 onclick=@NextStep\u003EGot it \u2192\u003C/button\u003E\r\n\t\t\t\t}\r\n\r\n\t\t\t\t@* Final step \u2014 Play button *@\r\n\t\t\t\t@if(s.Trigger == MMTutTrigger.Done)\r\n\t\t\t\t{\r\n\t\t\t\t\t\u003Cbutton class=\u0022tut-btn-next\u0022 onclick=@OnPlayAndComplete\u003E\u25B6 Let\u0027s play!\u003C/button\u003E\r\n\t\t\t\t}\r\n\t\t\t\u003C/div\u003E\r\n\t\t}\r\n    }\r\n\u003C/root\u003E\r\n\r\n@code {\r\n    public static MainMenuTutorial Instance { get; private set; }\r\n    public bool IsActive =\u003E _active;\r\n\r\n    bool _active = false;\r\n\tpublic int Step =\u003E _step;\r\n    int  _step   = 0;\r\n\r\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 //\r\n    const bool _debugMode = false; // set false before release\r\n    float _debugBL = 0f;\r\n    float _debugBR = 0f;\r\n    float _debugBY = 0f;\r\n    float _debugHX = 0f;\r\n    float _debugHY = 0f;\r\n    float _debugHW = 0f;\r\n    float _debugHH = 0f;\r\n\r\n    void LogCurrentValues()\r\n    {\r\n        Log.Info($\u0022// MM Step {_step} \u2014 {_steps[_step].Title}\u0022);\r\n        Log.Info($\u0022BL={_debugBL:F1}f, BY={_debugBY:F0}f, BR={_debugBR:F1}f,\u0022);\r\n        Log.Info($\u0022HX={_debugHX:F1}f, HY={_debugHY:F0}f, HW={_debugHW:F0}f, HH={_debugHH:F0}f\u0022);\r\n    }\r\n\r\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 //\r\n    public enum MMTutTrigger\r\n    {\r\n        Any,           // advances via Got it button\r\n        HeartPurchased, // advances when player buys Heart consumable\r\n        Done,          // final step \u2014 shows Play button\r\n    }\r\n\r\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 //\r\n    public struct MMTutStep\r\n    {\r\n        public string Title;\r\n        public string Text;\r\n        public string ActionHint;\r\n        public MMTutTrigger Trigger;\r\n        public float HX, HY, HW, HH; // highlight\r\n        public float BL, BR, BY, BW;  // box position\r\n    }\r\n\r\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 //\r\n    // Positions are placeholder \u2014 tune with debug card in-game\r\n    static readonly MMTutStep[] _steps = new MMTutStep[]\r\n    {\r\n        // Step 1 \u2014 Welcome\r\n        new MMTutStep {\r\n            Title   = \u0022Welcome to 10XPLOSION!\u0022,\r\n            Text    = \u0022Here\u0027s a quick tour of how the game works. It\u0027ll take 30 seconds.\u0022,\r\n            Trigger = MMTutTrigger.Any,\r\n            HW = 0,\r\n            BL = 47f, BY = 370f\r\n        },\r\n\r\n        // Step 2 \u2014 Play button\r\n        new MMTutStep {\r\n            Title   = \u0022Play runs to earn \uD83D\uDCA0 Fragments\u0022,\r\n            Text    = \u0022Fragments are the game\u0027s currency. The further you go each run, the more you earn.\u0022,\r\n            Trigger = MMTutTrigger.Any,\r\n            HW = 0,\r\n            BL = 47f, BY = 370f\r\n        },\r\n\r\n        // Step 3 \u2014 Shop \u2192 Modifiers\r\n        new MMTutStep {\r\n            Title   = \u0022The Shop \u2014 Modifiers \uD83C\uDFB2\u0022,\r\n            Text    = \u0022Spend fragments to unlock Modifiers. These permanently strengthen every run you play. Unlock once, keep forever.\u0022,\r\n            Trigger = MMTutTrigger.Any,\r\n            HX = 40f, HY = 200f, HW = 110f, HH = 40f,\r\n            BR = 61f, BY = 120f\r\n        },\r\n\r\n        // Step 4 \u2014 Consumables explanation\r\n        new MMTutStep {\r\n            Title   = \u0022Consumables \uD83C\uDF81\u0022,\r\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,\r\n            Trigger = MMTutTrigger.Any,\r\n            HX = 46f, HY = 200f, HW = 130f, HH = 40f,\r\n            BR = 61f, BY = 110\r\n        },\r\n\r\n        // Step 5 \u2014 Buy Heart (action step)\r\n        new MMTutStep {\r\n            Title   = \u0022Get yourself a Heart \u2764\uFE0F\u0022,\r\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,\r\n            ActionHint = \u0022Click the buy button on the Heart card\u0022,\r\n            Trigger = MMTutTrigger.HeartPurchased,\r\n            HX = 65f, HY = 860f, HW = 80f, HH = 70f,\r\n            BR = 7f, BY = 800f\r\n        },\r\n\r\n        // Step 6 \u2014 Pet tab\r\n        new MMTutStep {\r\n            Title = \u0022Your secret package \uD83D\uDCE6\u0022,\r\n            Text = \u0022Play 3 runs to unlock what\u0027s inside. This section is for cosmetic skins \u2014 coming soon.\u0022,\r\n    \t\tTrigger = MMTutTrigger.Any,\r\n            HX = 53f, HY = 360f, HW = 70f, HH = 40f,\r\n            BR = 50f, BY = 340f\r\n        },\r\n\r\n        // Step 7 \u2014 Bombs\r\n        new MMTutStep {\r\n            Title   = \u0022Unlock new Bombs \uD83D\uDCA3\u0022,\r\n            Text    = \u0022Different bomb types open up new chain reaction strategies. Mix and match to find your playstyle.\u0022,\r\n            Trigger = MMTutTrigger.Any,\r\n            HX = 57f, HY = 220f, HW = 90f, HH = 40f,\r\n            BR = 5f, BY = 130f\r\n        },\r\n\r\n        // Step 8 \u2014 Leaderboard\r\n        new MMTutStep {\r\n            Title   = \u0022Compete Globally \uD83C\uDFC5\u0022,\r\n            Text    = \u0022The leaderboard tracks your best score, chain reactions, and pet level. How far can you go?\u0022,\r\n            Trigger = MMTutTrigger.Any,\r\n            HX = 75f, HY = 400f, HW = 10f, HH = 10f,\r\n            BR = 5f, BY = 160f\r\n        },\r\n\r\n        // Step 9 \u2014 Done\r\n        new MMTutStep {\r\n            Title   = \u0022You\u0027re ready. \uD83D\uDCA5\u0022,\r\n            Text    = \u0022Play runs \u2192 earn fragments \u2192 unlock things \u2192 become stronger. Good luck!\u0022,\r\n            Trigger = MMTutTrigger.Done,\r\n\t\t\tBR = 20f, BY = 370f\r\n        },\r\n    };\r\n\r\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 //\r\n    protected override void OnStart()\r\n    {\r\n        Instance = this;\r\n        _active  = false;\r\n    }\r\n\r\n    public void Activate(PlayerSave save)\r\n    {\r\n        //var save = ChainReactionGame.Save ?? PlayerSave.Load();\r\n        if (save?.MenuTutorialCompleted == true) return;\r\n\r\n        _step   = save?.MenuTutorialStep ?? 0;\r\n        _active = true;\r\n        ResetDebug();\r\n\r\n\t\t// If resuming at step 4 (Buy Heart) but heart already purchased, skip it\r\n\t\tif (_step == 4 \u0026\u0026 (save?.GetConsumableCount(ConsumableId.Heart) ?? 0) \u003E 0)\r\n\t\t\t_step = 5;\r\n\r\n        // Step 3 requires shop/mods to be visible \u2014 navigate there\r\n        ApplySideEffect(_step);\r\n        StateHasChanged();\r\n    }\r\n\r\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 //\r\n    public void OnHeartPurchased()\r\n    {\r\n        if (!_active || _step \u003E= _steps.Length) return;\r\n        if (_steps[_step].Trigger != MMTutTrigger.HeartPurchased) return;\r\n        NextStep();\r\n    }\r\n\r\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 //\r\n  \tvoid NextStep() \r\n\t{\r\n\t\t_step\u002B\u002B;\r\n\t\tResetDebug();\r\n\r\n\t\tif (_step \u003E= _steps.Length)\r\n\t\t{\r\n\t\t\tComplete();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// Persist current step so we can resume after crash - save tuto step\r\n\t\tvar save = PlayerSave.Load();\r\n\t\tif (save != null) \r\n\t\t{ \r\n\t\t\tsave.MenuTutorialStep = _step; \r\n\t\t\tsave.Save(); \r\n\t\t}\r\n\r\n\t\tApplySideEffect(_step);\r\n\t\tSound.Play(\u0022UI_Button\u0022);\r\n\t\tStateHasChanged();\r\n}\r\n\r\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 //\r\n    void ApplySideEffect(int step)\r\n    {\r\n        var mm = MainMenu.Instance;\r\n        if (mm == null) return;\r\n\r\n        switch(step)\r\n        {\r\n            case 2: // Show shop modifiers\r\n                mm.SetTabPublic(\u0022shop\u0022);\r\n                mm.SetShopTabPublic(\u0022mods\u0022);\r\n                // Grant frags on step before heart purchase\r\n                break;\r\n\r\n            case 3: // Show consumables\r\n                mm.SetTabPublic(\u0022shop\u0022);\r\n                mm.SetShopTabPublic(\u0022consumables\u0022);\r\n                break;\r\n\r\n\t\t    case 4: // Buy Heart \u2014 shop consumables \u002B grant frags if not already granted\r\n            var save = ChainReactionGame.Save ?? PlayerSave.Load();\r\n            if (save != null \u0026\u0026 !save.TutorialHeartFragsGranted)\r\n            {\r\n                save.TotalFragments \u002B= 100;\r\n                save.TutorialHeartFragsGranted = true;\r\n                save.Save();\r\n            }\r\n            mm.SetTabPublic(\u0022shop\u0022);\r\n            mm.SetShopTabPublic(\u0022consumables\u0022);\r\n            mm.ReloadSave();\r\n            break;\r\n\t\t\r\n            case 5: // Pet tab\r\n\t\t\t    mm.SetTabPublic(\u0022shop\u0022);\r\n                mm.SetShopTabPublic(\u0022pets\u0022);\r\n                break;\r\n\r\n            case 6: // Bombs\r\n                mm.SetTabPublic(\u0022shop\u0022);\r\n                mm.SetShopTabPublic(\u0022bombs\u0022);\r\n                break;\r\n\r\n            case 7: // Leaderboard\r\n                mm.SetTabPublic(\u0022lb\u0022);\r\n                break;\r\n\t\t\tcase 8: // Done \u2014 no navigation needed\r\n            break;\r\n        }\r\n    }\r\n\r\n    void Skip() =\u003E Complete();\r\n\r\n    void OnPlayAndComplete()\r\n    {\r\n        Complete();\r\n        MainMenu.Instance?.StartGamePublic();\r\n    }\r\n\r\n    void Complete()\r\n    {\r\n        _active = false;\r\n\r\n\t\t// Get save from MainMenu directly\r\n\t\tvar mm = MainMenu.Instance;\r\n\r\n\t\t// just use ChainReactionGame.Save or load fresh\r\n\t\tvar save = PlayerSave.Load();\r\n\r\n\t\tif (save != null)\r\n\t\t{\r\n\t\t\tsave.MenuTutorialCompleted = true;\r\n\t\t\tsave.MenuTutorialStep = 0;\r\n\t\t\tsave.Save();\r\n\t\t}\r\n\t\tStateHasChanged();\r\n    }\r\n\r\n    void ResetDebug()\r\n    {\r\n        _debugBL = 0f; _debugBR = 0f; _debugBY = 0f;\r\n        _debugHX = 0f; _debugHY = 0f; _debugHW = 0f; _debugHH = 0f;\r\n    }\r\n\r\n\tpublic void ForceReset()\r\n\t{\r\n\t\t_active = false;\r\n\t\t_step   = 0;\r\n\t\tResetDebug();\r\n\t\tStateHasChanged();\r\n\t}\r\n\r\n    protected override int BuildHash() =\u003E\r\n        System.HashCode.Combine(_active, _step, _debugBL, _debugBY, _debugHX, _debugHY, _debugHW, _debugHH);\r\n}"},{"Ident":"playnplay.chain_reaction","Path":"Game/2D/GameMode.cs","FileName":"GameMode.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":343168,"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":343168,"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":343168,"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// \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 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\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// \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 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 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":343168,"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":343168,"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":343168,"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":343168,"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":343168,"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":343168,"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":343168,"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":343168,"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"}]}