/* LOOPLIGHT v4 — all styles (extracted from index.html for maintainability) */
    /* ==================================================================
       THE LAYER SCALE. PHASE 5.2.4J.
       ==================================================================
       Every full-screen overlay names its layer here instead of inventing a
       number. The 5.2.4J owner test found what ad-hoc numbers cost: the sync
       dialog was given 120 and #confirmModal had been sitting at 50 since v4,
       so the UPDATE AVAILABLE confirmation opened UNDERNEATH the sync dialog's
       backdrop - invisible, unclickable, and with its promise left pending
       for ever. Nothing was broken in either dialog; they simply did not
       know about each other.

       The order is the order things answer each other in:

         dialog   a decision about the page            (sync adoption/conflict)
         confirm  a decision about THAT decision       (always on top of it)
         toast    transient, pointer-events:none       (must stay legible)
         gate     "this device is too small"           (nothing else matters)
         boot     the loader                           (nothing exists yet)

       A dialog that opens on top of another must also make the one below
       INERT - see askConfirm in game.js - so focus and Escape belong to
       exactly one layer at a time. */
    :root{
      --z-dialog:120;
      --z-confirm:130;
      --z-toast:140;
      --z-gate:190;
      --z-boot:200;

      --bg:#08091a;
      --panel:rgba(11,15,43,.78);
      --glass:rgba(13,18,52,.62);
      --line:rgba(255,255,255,.12);
      --text:#f8fbff;
      --muted:#aeb8d8;
      --cyan:#78f8ff;
      --pink:#ff66d4;
      --gold:#ffd56a;
      --green:#5dff9f;
      --purple:#b16dff;
      --danger:#ff5c7a;
      /* v5.1 design tokens — spacing, radius and motion scales used by all
         new components (legacy rules migrate opportunistically) */
      --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px;
      --r-sm:10px; --r-md:14px; --r-lg:20px;
      --t-fast:.15s; --t-med:.28s;
      --safe-bottom: env(safe-area-inset-bottom, 0px);
      --safe-top: env(safe-area-inset-top, 0px);
      --safe-left: env(safe-area-inset-left, 0px);
      --safe-right: env(safe-area-inset-right, 0px);
      --vh100: 100vh;
    }
    @supports (height: 100dvh){ :root{ --vh100: 100dvh; } }
    *{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
    html,body{
      margin:0;width:100%;height:100%;overflow:hidden;
      background:var(--bg);color:var(--text);
      font-family:Inter,ui-rounded,"SF Pro Rounded",system-ui,-apple-system,Segoe UI,sans-serif;
      touch-action:none;user-select:none;-webkit-user-select:none;
    }
    button{font-family:inherit;cursor:pointer;touch-action:manipulation;color:inherit}
    input{font-family:inherit}
    button:focus-visible,input:focus-visible,[tabindex]:focus-visible{
      outline:2px solid var(--cyan);outline-offset:2px;border-radius:12px}
    #gameWrap{position:fixed;inset:0;overflow:hidden;background:
      radial-gradient(circle at 50% 20%,#1b2052 0,#090b20 38%,#050611 100%)}
    canvas#game{position:absolute;inset:0;width:100%;height:100%;display:block}

    /* ---------- HUD ---------- */
    /* Set while a menu screen is open over a paused run - see showScreen().
       visibility rather than display so the run lifecycle keeps sole ownership
       of #hud.style.display and nothing here can strand the HUD hidden. */
    #hud.hudMuted{visibility:hidden}
    #hud{position:absolute;inset:0;pointer-events:none;
      padding:calc(8px + var(--safe-top)) calc(10px + var(--safe-right)) calc(8px + var(--safe-bottom)) calc(10px + var(--safe-left));}
    .topbar{display:flex;justify-content:space-between;align-items:flex-start;gap:6px}
    .chip{
      min-width:0;padding:5px 10px;border:1px solid var(--line);border-radius:12px;
      background:rgba(5,8,28,.55);backdrop-filter:blur(10px);
      box-shadow:0 6px 20px rgba(0,0,0,.2)
    }
    .chip .label{display:block;color:var(--muted);font-size:8.5px;font-weight:800;letter-spacing:.13em}
    .chip .value{display:block;margin-top:1px;font-size:clamp(15px,4.5vw,20px);font-weight:900;line-height:1;font-variant-numeric:tabular-nums}
    .chip .value.coinVal{color:var(--gold);font-size:clamp(12px,3.6vw,15px)}
    .leftchips{display:flex;flex-direction:column;gap:4px;align-items:flex-start}
    .chip.mini{padding:3px 8px}
    .chip.mini .label{font-size:7.5px}
    .chip.mini .value{font-size:12px}
    #score.bump{animation:scoreBump .25s}
    @keyframes scoreBump{40%{transform:scale(1.28);color:var(--gold)}}
    .rightchips{display:flex;gap:6px}
    #comboTag{display:inline-block;margin-left:5px;font-size:12px;font-weight:900;color:var(--gold);
      text-shadow:0 0 10px rgba(255,213,106,.8);vertical-align:2px;opacity:0;transition:opacity .2s}
    #comboTag.on{opacity:1}
    #comboTag.bump{animation:scoreBump .3s}
    /* PHASE 5.2.3: the LIVES chip answers when a life is lost. Short, and it
       borrows the beat the score already uses so the HUD has one vocabulary
       for "this number just moved". */
    .chip.lifeLost{animation:lifeLostPulse .42s}
    @keyframes lifeLostPulse{
      0%{transform:scale(1)}
      22%{transform:scale(1.14);box-shadow:0 0 0 2px rgba(255,92,122,.55),0 0 22px rgba(255,92,122,.5)}
      100%{transform:scale(1)}}
    #loopFill{height:100%;width:0%;border-radius:inherit;background:linear-gradient(90deg,var(--cyan),var(--pink));box-shadow:0 0 12px var(--pink)}
    #loopMeter.warn #loopFill{background:linear-gradient(90deg,var(--pink),#ff9a6a);box-shadow:0 0 16px #ff8f7a}
    @keyframes cdPop{0%{transform:translateX(-50%) scale(1.7)}100%{transform:translateX(-50%) scale(1)}}
    .buff{display:flex;align-items:center;gap:4px;padding:3px 8px;border-radius:999px;border:1px solid var(--line);
      background:rgba(5,8,28,.6);font-size:10.5px;font-weight:900;letter-spacing:.05em;backdrop-filter:blur(8px)}
    .buff i{font-style:normal;font-size:11px}
    .buff.expiring{animation:buffBlink .5s infinite alternate}
    @keyframes buffBlink{to{opacity:.45}}
    #pulseBtn{
      pointer-events:auto;position:absolute;
      right:calc(12px + var(--safe-right));bottom:calc(14px + var(--safe-bottom));
      width:clamp(62px,17vw,80px);height:clamp(62px,17vw,80px);border-radius:50%;border:1px solid rgba(255,255,255,.25);
      color:white;font-weight:950;letter-spacing:.08em;font-size:11px;
      background:radial-gradient(circle at 35% 25%,rgba(255,255,255,.35),rgba(120,248,255,.18) 28%,rgba(255,102,212,.12) 60%,rgba(5,7,25,.8));
      box-shadow:0 0 0 5px rgba(120,248,255,.06),0 10px 30px rgba(0,0,0,.35),inset 0 0 24px rgba(120,248,255,.2);
      opacity:.5;transform:scale(.96);transition:.2s
    }
    #pulseBtn.ready{opacity:1;transform:scale(1);animation:pulseReady 1.1s infinite alternate}
    #pulseBtn.tokenReady{opacity:.92}
    #pulseBtn:active{transform:scale(.9)}
    #pulseBtn.full{animation:pulseFull .5s}
    @keyframes pulseFull{35%{transform:scale(1.16);box-shadow:0 0 34px var(--cyan),inset 0 0 34px rgba(255,102,212,.4)}}
    @keyframes pulseReady{to{box-shadow:0 0 0 10px rgba(120,248,255,.13),0 0 26px rgba(120,248,255,.5),inset 0 0 28px rgba(255,102,212,.25)}}
    #chargeRing{position:absolute;inset:6px;border-radius:50%;border:4px solid rgba(255,255,255,.1)}
    #chargeArc{position:absolute;inset:6px;border-radius:50%;background:conic-gradient(var(--cyan) 0deg,transparent 0deg);mask:radial-gradient(transparent 55%,#000 58%);-webkit-mask:radial-gradient(transparent 55%,#000 58%)}
    #tokenBadge{position:absolute;top:-4px;left:-4px;display:flex;gap:3px;pointer-events:none}
    #tokenBadge span{width:12px;height:12px;border-radius:50%;background:radial-gradient(circle at 35% 30%,#fff,var(--gold) 55%,#a06900);
      box-shadow:0 0 8px var(--gold);border:1px solid rgba(255,255,255,.5)}
    #pauseBtn{
      position:absolute;left:calc(12px + var(--safe-left));bottom:calc(16px + var(--safe-bottom));pointer-events:auto;
      width:40px;height:40px;border-radius:13px;background:rgba(5,8,28,.55);color:#fff;
      border:1px solid var(--line);font-size:15px;font-weight:900;backdrop-filter:blur(10px)
    }
    #keysHint{position:absolute;left:50%;bottom:calc(16px + var(--safe-bottom));transform:translateX(-50%);
      font-size:10px;font-weight:800;letter-spacing:.12em;color:#8e99c4;text-shadow:0 2px 8px #000;
      opacity:0;transition:opacity .6s;pointer-events:none;white-space:nowrap}
    #keysHint.show{opacity:.8}

    /* landscape phone */
    @media (orientation:landscape) and (max-height:520px){
      #hud{padding-top:calc(5px + var(--safe-top))}
            #pulseBtn{width:60px;height:60px;bottom:calc(10px + var(--safe-bottom))}
      #pauseBtn{width:36px;height:36px;bottom:auto;top:calc(6px + var(--safe-top));left:calc(10px + var(--safe-left))}
      .topbar{margin-left:52px}
      #keysHint{display:none}
    }

    /* ---------- Screens ---------- */
    .screen{
      position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
      padding:calc(14px + var(--safe-top)) 14px calc(14px + var(--safe-bottom));
      background:radial-gradient(circle at 50% 18%,rgba(35,43,105,.9),rgba(6,7,22,.975) 54%,rgba(4,5,14,.995));
      z-index:10;transition:opacity .28s,visibility .28s;overflow:hidden
    }
    .screen.hidden{opacity:0;visibility:hidden;pointer-events:none}
    .screen.withNav{padding-bottom:calc(76px + var(--safe-bottom))}
    .card{width:min(94vw,500px);max-height:100%;overflow-y:auto;overflow-x:hidden;text-align:center;
      scrollbar-width:thin;scrollbar-color:rgba(120,248,255,.25) transparent;
      padding:4px 2px;-webkit-overflow-scrolling:touch}
    .card::-webkit-scrollbar,.homeBody::-webkit-scrollbar{width:5px}
    .card::-webkit-scrollbar-thumb,.homeBody::-webkit-scrollbar-thumb{background:rgba(120,248,255,.25);border-radius:99px}
    .card::-webkit-scrollbar-track,.homeBody::-webkit-scrollbar-track{background:transparent}
    .card.wide{width:min(96vw,760px)}
    .screen:not(.hidden) .card{animation:cardIn .34s cubic-bezier(.2,.9,.3,1)}
    @keyframes cardIn{from{opacity:0;transform:translateY(14px) scale(.985)}to{opacity:1;transform:none}}
    .logo{
      font-size:clamp(38px,11vw,66px);line-height:.9;font-weight:1000;letter-spacing:-.06em;
      background:linear-gradient(100deg,#fff 10%,var(--cyan) 47%,var(--pink) 90%);
      -webkit-background-clip:text;background-clip:text;color:transparent;
      filter:drop-shadow(0 0 24px rgba(120,248,255,.18))
    }
    .tag{margin-top:6px;color:#d4d9f3;font-weight:850;letter-spacing:.18em;font-size:10.5px}
    .mainBtn{
      width:100%;border:0;border-radius:17px;padding:15px 18px;color:#07101d;
      background:linear-gradient(100deg,var(--cyan),#b5ffdc 50%,var(--gold));
      font-size:15px;font-weight:1000;letter-spacing:.06em;box-shadow:0 12px 32px rgba(120,248,255,.2);
      transition:transform .12s, box-shadow .12s
    }
    .mainBtn:active{transform:scale(.965);box-shadow:0 6px 18px rgba(120,248,255,.3)}
    @media (hover:hover){ .mainBtn:hover{box-shadow:0 14px 40px rgba(120,248,255,.34);transform:translateY(-1px)} }
    .secondaryBtn{
      margin-top:8px;width:100%;border:1px solid var(--line);border-radius:17px;padding:12px 18px;
      color:#fff;background:rgba(255,255,255,.05);font-weight:900;font-size:13px;letter-spacing:.05em;
      transition:transform .12s, background .12s
    }
    .secondaryBtn:active{transform:scale(.97);background:rgba(255,255,255,.11)}
    @media (hover:hover){ .secondaryBtn:hover{background:rgba(255,255,255,.1)} }
    .menuRow{display:grid;grid-template-columns:1fr 1fr;gap:8px}
    .menuRow .secondaryBtn{margin-top:8px}
    .small{font-size:11.5px;color:var(--muted);line-height:1.45}
    .backBtn{margin-top:12px}
    .screenTitle{font-size:clamp(24px,6.5vw,36px);font-weight:1000;letter-spacing:-.03em;margin:2px 0 4px;
      background:linear-gradient(100deg,#fff 10%,var(--cyan) 60%,var(--pink));-webkit-background-clip:text;background-clip:text;color:transparent}
    .dangerBtn{border-color:rgba(255,92,122,.5);color:#ffb6c4}

    /* currency bar shown at top of menu sub-screens */
    .curBar{display:flex;justify-content:center;gap:8px;margin:2px 0 8px}
    .curPill{display:flex;align-items:center;gap:5px;padding:5px 12px;border-radius:999px;border:1px solid var(--line);
      background:var(--glass);backdrop-filter:blur(8px);font-weight:900;font-size:12.5px;font-variant-numeric:tabular-nums}
    .curPill .ic{width:13px;height:13px;border-radius:50%;flex:none}
    .curPill.coins{color:var(--gold)} .curPill.coins .ic{background:radial-gradient(circle at 35% 30%,#fff,var(--gold) 55%,#a06900);box-shadow:0 0 8px var(--gold)}
    .curPill.gems{color:#c9a4ff} .curPill.gems .ic{border-radius:3px;transform:rotate(45deg);background:linear-gradient(135deg,#e6d4ff,var(--purple) 70%);box-shadow:0 0 8px var(--purple)}

    /* ---------- HOME SCREEN ---------- */
    #homeScreen{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;
      padding:calc(10px + var(--safe-top)) calc(12px + var(--safe-right)) calc(74px + var(--safe-bottom)) calc(12px + var(--safe-left))}
    .homeTop{display:flex;justify-content:space-between;align-items:center;gap:8px;flex:none}
    .topChip{display:flex;align-items:center;gap:7px;padding:6px 10px;border-radius:14px;border:1px solid var(--line);
      background:var(--glass);backdrop-filter:blur(10px);box-shadow:0 6px 18px rgba(0,0,0,.25);
      transition:transform .12s,border-color .2s;min-height:38px}
    .topChip:active{transform:scale(.95)}
    @media (hover:hover){ .topChip:hover{border-color:rgba(120,248,255,.5)} }
    .profileChip{max-width:44vw;text-align:left}
    .profileChip .avatarDot{width:26px;height:26px;border-radius:50%;flex:none;
      background:radial-gradient(circle at 35% 30%,#fff,var(--cyan) 60%,#0a4a55);box-shadow:0 0 10px var(--cyan)}
    .profileChip span{display:flex;flex-direction:column;min-width:0}
    .profileChip b{font-size:12px;letter-spacing:.05em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .profileChip i{font-style:normal;font-size:8.5px;font-weight:900;letter-spacing:.12em;color:var(--cyan)}
    .topRight{display:flex;gap:6px;align-items:center}
    .curChip{font-weight:900;font-size:13px;font-variant-numeric:tabular-nums;gap:5px}
    .curChip .ic{width:12px;height:12px;border-radius:50%;flex:none}
    .curChip.coin{color:var(--gold)} .curChip.coin .ic{background:radial-gradient(circle at 35% 30%,#fff,var(--gold) 55%,#a06900);box-shadow:0 0 8px var(--gold)}
    .curChip.gem{color:#c9a4ff} .curChip.gem .ic{border-radius:3px;transform:rotate(45deg);background:linear-gradient(135deg,#e6d4ff,var(--purple) 70%);box-shadow:0 0 8px var(--purple)}
    .iconBtn{position:relative;width:38px;height:38px;border-radius:13px;border:1px solid var(--line);flex:none;
      background:var(--glass);backdrop-filter:blur(10px);font-size:15px;color:#dfe6ff;
      display:flex;align-items:center;justify-content:center;transition:transform .12s,border-color .2s}
    .iconBtn:active{transform:scale(.92)}
    @media (hover:hover){ .iconBtn:hover{border-color:rgba(120,248,255,.5)} }
    .iconBtn .dot{position:absolute;top:5px;right:5px;width:8px;height:8px;border-radius:50%;
      background:var(--gold);box-shadow:0 0 8px var(--gold);display:none}
    .iconBtn .dot.on{display:block;animation:dotPulse 1.4s infinite alternate}
    @keyframes dotPulse{to{transform:scale(1.35)}}
    .iconBtn.muted{color:#67719c}
    .iconBtn.muted:after{content:"";position:absolute;width:22px;height:2px;background:#67719c;transform:rotate(-40deg);border-radius:2px}

    .homeBody{flex:1;min-height:0;display:flex;flex-direction:column;gap:6px;overflow-y:auto;overflow-x:hidden;
      scrollbar-width:thin;-webkit-overflow-scrolling:touch;margin-top:6px}
    .heroPane{flex:none;text-align:center;position:relative}
    .heroPane .logo{font-size:clamp(30px,8vw,46px)}
    #heroStage{position:relative;width:min(46vw,190px);aspect-ratio:1;margin:2px auto 0;touch-action:none}
    #heroCanvas{width:100%;height:100%;display:block}
    .plate{display:inline-flex;align-items:center;gap:6px;margin-top:0;padding:5px 16px;border-radius:999px;
      border:1px solid var(--line);background:var(--glass);backdrop-filter:blur(8px);
      font-weight:900;font-size:12.5px;letter-spacing:.09em}
    .plate.neon{border-color:var(--cyan);box-shadow:0 0 14px rgba(120,248,255,.35);color:var(--cyan)}
    .plate.gold{border-color:var(--gold);box-shadow:0 0 14px rgba(255,213,106,.35);color:var(--gold)}
    .plate.glass{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.3)}
    .plate.danger{border-color:var(--danger);box-shadow:0 0 14px rgba(255,92,122,.35);color:#ffb6c4}
    .plate.prism{border-color:transparent;background:linear-gradient(100deg,rgba(120,248,255,.2),rgba(255,102,212,.2),rgba(255,213,106,.2));box-shadow:0 0 16px rgba(197,140,255,.3)}
    .levelBar{position:relative;width:min(70vw,240px);height:14px;margin:8px auto 0;border-radius:999px;
      border:1px solid var(--line);background:rgba(0,0,0,.4);overflow:hidden}
    .levelBar div{height:100%;width:0%;background:linear-gradient(90deg,var(--cyan),var(--green));box-shadow:0 0 10px var(--cyan);transition:width .5s}
    .levelBar span{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
      font-size:8.5px;font-weight:900;letter-spacing:.12em;color:#eaf6ff;text-shadow:0 1px 4px #000}

    .playPane{flex:none;width:min(94vw,420px);margin:4px auto 0}
    .modeCarousel{display:flex;align-items:stretch;gap:7px}
    .carArrow{width:40px;flex:none;border-radius:15px;border:1px solid var(--line);background:var(--glass);
      backdrop-filter:blur(8px);color:#cfe0ff;font-size:22px;font-weight:900;transition:transform .12s,border-color .2s}
    .carArrow:active{transform:scale(.9)}
    @media (hover:hover){ .carArrow:hover{border-color:rgba(120,248,255,.55);color:#fff} }
    .modeCard{flex:1;min-width:0;position:relative;border-radius:18px;border:1px solid var(--line);
      background:linear-gradient(160deg,rgba(120,248,255,.08),var(--glass) 45%);backdrop-filter:blur(10px);
      padding:12px 12px 10px;text-align:center;overflow:hidden;touch-action:pan-y}
    .modeCard.anim-left{animation:modeSlideL .28s}
    .modeCard.anim-right{animation:modeSlideR .28s}
    @keyframes modeSlideL{from{opacity:0;transform:translateX(26px)}to{opacity:1;transform:none}}
    @keyframes modeSlideR{from{opacity:0;transform:translateX(-26px)}to{opacity:1;transform:none}}
    .modeBadge{position:absolute;top:8px;right:-1px;padding:3px 9px;border-radius:8px 0 0 8px;font-size:8px;
      font-weight:1000;letter-spacing:.1em;color:#1d0710;background:var(--danger);display:none}
    .modeBadge.show{display:block}
    .modeCard i{font-style:normal;font-size:22px;display:block;line-height:1}
    .modeCard b{display:block;font-size:17px;letter-spacing:.08em;margin-top:3px}
    .modeCard>span.desc{display:block;font-size:10.5px;color:var(--muted);line-height:1.4;margin-top:3px;min-height:29px}
    .modeMeta{display:flex;justify-content:center;gap:12px;margin-top:6px;font-size:10px;font-weight:900;letter-spacing:.08em}
    .modeMeta .mBest{color:var(--cyan)} .modeMeta .mMult{color:var(--gold)}
    /* ===== PHASE 5.2.3 · LOCKED MODES =====================================
       A locked mode is still browsable - the icon, the name and the rule stay
       readable, because a player deciding whether to chase something needs to
       know what it is. What changes is that it is visibly not yet theirs and
       it says what is missing. */
    .modeCard.locked{border-color:rgba(255,255,255,.14)}
    .modeCard.locked i,.modeCard.locked b{opacity:.62}
    .modeCard.locked>span.desc{opacity:.5}
    .modeCard.locked .modeMeta{opacity:.45}
    .modeLock{display:none;margin-top:8px;padding-top:8px;border-top:1px solid rgba(255,255,255,.1)}
    .modeLock.show{display:block}
    .modeLock b{display:block;font-size:9.5px;font-weight:1000;letter-spacing:.2em;color:var(--gold);opacity:1}
    .modeLock span{display:block;font-size:10.5px;color:var(--muted);line-height:1.4;margin-top:3px}
    .modeLock i{display:block;font-style:normal;font-size:11px;font-weight:900;letter-spacing:.06em;
      color:#eaf2ff;margin-top:4px;font-variant-numeric:tabular-nums}
    /* PLAY says what it will do. A disabled button that still reads PLAY is a
       button the player will keep pressing. */
    .mainBtn.isLocked{opacity:.5;cursor:not-allowed;filter:saturate(.35)}

    /* ===== PHASE 5.2.3 · NEW MODE UNLOCKED ================================
       Shown once, on returning Home, then persisted as seen. A card rather
       than a takeover: the player has just finished a run and is on their way
       somewhere. */
    .modeUnlockCard{position:absolute;left:50%;top:64px;transform:translate(-50%,-8px);z-index:40;
      min-width:min(300px,74vw);padding:12px 20px 13px;border-radius:16px;text-align:center;
      background:linear-gradient(160deg,rgba(255,213,106,.16),rgba(13,18,52,.96) 55%);
      border:1px solid rgba(255,213,106,.42);box-shadow:0 18px 54px rgba(0,0,0,.55);
      opacity:0;pointer-events:none;transition:opacity .22s ease,transform .22s ease}
    .modeUnlockCard.show{opacity:1;transform:translate(-50%,0)}
    .modeUnlockCard i{display:block;font-style:normal;font-size:9px;font-weight:1000;letter-spacing:.22em;color:var(--gold)}
    .modeUnlockCard b{display:block;font-size:20px;font-weight:1000;letter-spacing:.1em;margin-top:5px}
    .modeUnlockCard span{display:block;font-size:10.5px;color:var(--muted);line-height:1.45;margin-top:5px}

    /* ===== PHASE 5.2.3 · THE GLOBAL ACCOUNT ===============================
       The player chip already existed; these two states are all that is new.
       Signed out it says GUEST / SIGN IN in the space the level tag used, so
       Home gains no new furniture. */
    .profileChip.signedIn b{color:var(--cyan)}
    .profileChip.signedOut i{color:var(--gold);font-weight:900;letter-spacing:.1em}
    .profileChip.signedOut b{opacity:.82}

    /* The account section on the Profile page. Plain rows and the game's own
       stat boxes - it is part of Profile, not a web form dropped into it. */
    .profAccount{margin-top:12px;padding:14px 16px 15px;border-radius:16px;
      border:1px solid var(--line);background:rgba(5,8,28,.5)}
    .profAccount.hidden{display:none}
    /* PHASE 5.2.3 FINAL: WHOSE PROFILE IS THIS?
       The identity card led with the CHARACTER name (NOVA) while the player's
       own LOOPLIGHT name sat in 10px rows at the bottom of the page. The
       person comes first; the character they fly is a property of them. */
    .profPlayerName{display:block;font-size:22px;font-weight:1000;letter-spacing:.05em;color:#eaf2ff;
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .profHandle{display:block;font-size:10px;font-weight:900;letter-spacing:.14em;color:var(--cyan);margin-top:2px}
    .profCharLine{display:block;font-size:10.5px;color:var(--muted);margin-top:6px}
    /* While an account owns the name, the device-name control is not shown as
       a large dead input and a dead primary button - it is one line saying
       where the name comes from. The data behind it is untouched. */
    .deviceNameRow.accountOwned{display:none}

    .acctHead{font-size:9.5px;font-weight:1000;letter-spacing:.2em;color:var(--gold);
      margin:2px 0 8px}
    .acctHead+.acctHead,.acctBr+.acctHead,.acctRow+.acctHead{margin-top:16px}
    .acctRow{display:flex;justify-content:space-between;align-items:baseline;gap:12px;
      padding:6px 0;border-bottom:1px solid rgba(255,255,255,.06)}
    .acctRow span{font-size:10px;font-weight:800;letter-spacing:.12em;color:var(--muted)}
    .acctRow b{font-size:13px;font-weight:900;letter-spacing:.05em;color:#eaf2ff;
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .acctBr{margin-top:4px;min-height:34px;color:var(--muted);font-size:11px}
    .acctBr .statList{margin-top:0}
    .acctNote{margin-top:10px;opacity:.75;line-height:1.5}
    .acctActions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
    .acctActions button{flex:1 1 auto;min-width:132px;margin:0}
    .secondaryBtn.quiet{opacity:.72}
    .secondaryBtn.quiet:hover{opacity:1}

    #modeDots{display:flex;justify-content:center;gap:6px;margin:8px 0}
    #modeDots i{width:7px;height:7px;border-radius:50%;background:rgba(255,255,255,.18);transition:.2s}
    #modeDots i.on{background:var(--cyan);box-shadow:0 0 8px var(--cyan)}
    .loadoutBtn{display:flex;align-items:center;justify-content:center;gap:8px}
    .loadoutBtn .bIc{font-style:normal;color:var(--purple)}
    .homeLinks{display:flex;justify-content:center;gap:14px;margin-top:9px}
    /* PHASE 5.2.3 FINAL: "Board name: PLAYER EDIT" read as three words, with
       the only control on the line indistinguishable from the sentence around
       it. It keeps its light weight and gains an edge, so it is findable as a
       button without shouting on a screen that already has a primary action. */
    .miniLink{border:1px solid rgba(255,255,255,.16);border-radius:99px;background:none;
      color:var(--muted);font-size:10.5px;font-weight:900;letter-spacing:.1em;padding:3px 10px;
      margin-left:6px;vertical-align:middle}
    .miniLink:active{color:#fff;border-color:var(--cyan)}
    @media (hover:hover){ .miniLink:hover{color:var(--cyan)} }

    /* ---------- BOTTOM NAV ---------- */
    #menuNav{position:absolute;left:50%;bottom:calc(8px + var(--safe-bottom));transform:translateX(-50%);
      z-index:30;display:flex;gap:2px;padding:5px;border-radius:19px;border:1px solid var(--line);
      background:rgba(7,10,32,.82);backdrop-filter:blur(16px);box-shadow:0 14px 40px rgba(0,0,0,.5);
      transition:opacity .25s,visibility .25s}
    #menuNav.hidden{opacity:0;visibility:hidden;pointer-events:none}
    .navBtn{display:flex;flex-direction:column;align-items:center;gap:1px;min-width:50px;padding:6px 6px 5px;
      border:0;border-radius:14px;background:none;color:#8e99c4;transition:.15s}
    .navBtn i{font-style:normal;font-size:16px;line-height:1}
    .navBtn span{font-size:8px;font-weight:900;letter-spacing:.08em}
    .navBtn.active{color:#07101d;background:linear-gradient(100deg,var(--cyan),#b5ffdc);box-shadow:0 4px 16px rgba(120,248,255,.35)}
    .navBtn:not(.active):active{color:#fff;transform:scale(.94)}
    @media (hover:hover){ .navBtn:not(.active):hover{color:#e8f4ff} }
    @media (max-width:370px){ .navBtn{min-width:44px;padding:5px 4px 4px} }

    /* compact top bar on narrow phones so nothing clips */
    @media (max-width:400px){
      .topRight{gap:4px}
      .iconBtn{width:34px;height:34px;font-size:13px;border-radius:11px}
      .curChip{font-size:11.5px;padding:5px 7px;gap:4px}
      .curChip .ic{width:10px;height:10px}
      .profileChip{max-width:34vw;padding:5px 8px;gap:5px}
      .profileChip .avatarDot{width:22px;height:22px}
      .profileChip b{font-size:11px}
    }

    /* ---------- grids & cards (characters / customize / shop) ---------- */
    .skinGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:8px;margin:10px 0}
    .skinCard{position:relative;border:1px solid var(--line);border-radius:15px;background:rgba(255,255,255,.045);
      padding:9px 5px 8px;text-align:center;color:#fff;transition:transform .12s,border-color .2s,box-shadow .2s}
    .skinCard:active{transform:scale(.95)}
    @media (hover:hover){ .skinCard:hover{border-color:rgba(120,248,255,.4)} }
    .skinCard canvas{width:58px;height:58px;display:block;margin:0 auto}
    .skinCard b{display:block;font-size:11px;letter-spacing:.08em;margin-top:3px}
    .skinCard span{display:block;font-size:9px;color:var(--muted);margin-top:2px;line-height:1.25;min-height:22px}
    /* An offer, not a requirement: MY PHOTO invites the player to do
       something now, so it must not wear the hatched locked treatment that
       means "you cannot have this yet". */
    .skinCard.actionCard{border-style:dashed;border-color:rgba(120,248,255,.34);
      background:rgba(120,248,255,.05)}
    .skinCard.actionCard span{color:var(--cyan);opacity:.9;font-weight:800}
    .skinCard.selected{border-color:var(--cyan);box-shadow:0 0 0 1px var(--cyan),0 0 22px rgba(120,248,255,.35)}
    .skinCard.selected:after{content:"EQUIPPED";position:absolute;top:6px;right:6px;font-size:7px;font-weight:900;
      letter-spacing:.08em;color:#07101d;background:var(--cyan);border-radius:99px;padding:2px 5px}
    .skinCard.owned:not(.selected):before{content:"OWNED";position:absolute;top:6px;right:6px;font-size:7px;font-weight:900;
      letter-spacing:.08em;color:var(--muted);border:1px solid var(--line);border-radius:99px;padding:2px 5px}
    .skinCard.newItem b:after{content:"NEW";margin-left:4px;font-size:7px;color:#07101d;background:var(--gold);
      border-radius:99px;padding:1px 4px;vertical-align:2px}
    /* PHASE 5.2.3 FINAL: A LOCKED CARD MUST NOT READ AS AN OWNED ONE.
       Before this, the only difference between DRONE (locked) and every card
       around it was a 9px padlock in the corner - same background, same
       border, same text weight - so a locked item looked like something you
       already had. It is now visibly not yours, and the requirement is
       promoted out of description weight to the line that matters. */
    .skinCard.locked{opacity:.72;border-color:rgba(255,255,255,.09);
      background:repeating-linear-gradient(135deg,rgba(255,255,255,.022) 0 6px,transparent 6px 12px),rgba(0,0,0,.28)}
    .skinCard.locked canvas{filter:grayscale(.92) brightness(.5)}
    .skinCard.locked b{opacity:.72}
    .skinCard.locked span{color:var(--gold);opacity:.92;font-weight:800}
    .skinCard .lockTag{position:absolute;top:6px;right:6px;font-size:10px;opacity:.85}

    /* NEW sits beside OWNED and was competing with it. Quieter: it is a
       nudge, not a status. */
    .skinCard.newItem b:after{opacity:.9}
    .priceTag{display:inline-flex;align-items:center;gap:4px;margin-top:4px;padding:2px 8px;border-radius:99px;
      font-size:9.5px;font-weight:900;letter-spacing:.04em}
    .priceTag.coins{color:var(--gold);border:1px solid rgba(255,213,106,.4);background:rgba(255,213,106,.08)}
    .priceTag.gems{color:#c9a4ff;border:1px solid rgba(177,109,255,.4);background:rgba(177,109,255,.1)}

    /* customizer */
    #custPreviewWrap{position:relative;width:min(42vw,170px);aspect-ratio:1;margin:0 auto}
    #custPreview{width:100%;height:100%;display:block}
    .catTabsWrap{position:relative}
    .catTabsWrap::after{content:"";position:absolute;top:0;right:0;bottom:0;width:26px;pointer-events:none;
      background:linear-gradient(90deg,transparent,rgba(6,7,22,.9));border-radius:0 var(--r-sm) var(--r-sm) 0;
      opacity:1;transition:opacity var(--t-fast)}
    .catTabsWrap.atEnd::after{opacity:0}
    .catTabs{display:flex;gap:5px;overflow-x:auto;padding:4px 26px 6px 2px;scrollbar-width:none;
      -webkit-overflow-scrolling:touch;scroll-snap-type:x proximity}
    .catTabs::-webkit-scrollbar{display:none}
    .catTabs .catTab{scroll-snap-align:start}
    .catTab{flex:none;border:1px solid var(--line);background:rgba(255,255,255,.05);color:var(--muted);
      border-radius:99px;padding:7px 12px;font-weight:900;font-size:10px;letter-spacing:.07em;white-space:nowrap}
    .catTab.active{color:#07101d;background:var(--cyan);border-color:transparent}
    .filterRow{display:flex;gap:5px;justify-content:center;flex-wrap:wrap;margin:4px 0 2px}
    .filterBtn{border:1px solid var(--line);background:none;color:var(--muted);border-radius:99px;
      padding:4px 10px;font-weight:900;font-size:9px;letter-spacing:.07em}
    .filterBtn.active{color:var(--cyan);border-color:var(--cyan)}
    .custActions{display:flex;gap:6px;margin-top:8px}
    .custActions .secondaryBtn{margin-top:0;padding:10px 8px;font-size:11px}
    .presetRow{display:flex;gap:6px;margin-top:6px}
    .presetBtn{flex:1;border:1px solid var(--line);background:rgba(255,255,255,.05);color:var(--muted);
      border-radius:13px;padding:9px 4px;font-weight:900;font-size:10px;letter-spacing:.06em}
    .presetBtn.filled{color:#fff;border-color:rgba(120,248,255,.4)}

    /* upgrades */
    .upList{display:flex;flex-direction:column;gap:8px;margin:10px 0;text-align:left}
    .upRow{border:1px solid var(--line);border-radius:16px;background:rgba(255,255,255,.045);padding:11px 12px}
    .upHead{display:flex;align-items:center;gap:9px}
    .upIc{width:34px;height:34px;flex:none;display:flex;align-items:center;justify-content:center;border-radius:50%;
      font-size:14px;font-weight:900;border:1.5px solid;background:rgba(6,10,32,.7)}
    .upHead b{font-size:13px;letter-spacing:.06em}
    .upHead .lvl{margin-left:auto;font-size:10px;font-weight:900;color:var(--muted);letter-spacing:.08em}
    .upPips{display:flex;gap:3px;margin:8px 0 6px}
    .upPips i{flex:1;height:6px;border-radius:99px;background:rgba(255,255,255,.1)}
    .upPips i.on{background:linear-gradient(90deg,var(--cyan),var(--green));box-shadow:0 0 6px var(--cyan)}
    .upStats{font-size:10.5px;color:var(--muted);line-height:1.5}
    .upStats b{color:#fff}
    .upStats .next{color:var(--green)}
    .upBuy{margin-top:8px;width:100%;border:0;border-radius:12px;padding:10px;font-weight:1000;font-size:12px;
      letter-spacing:.05em;color:#07101d;background:linear-gradient(100deg,var(--gold),#ffe9a8);transition:transform .12s}
    .upBuy:active{transform:scale(.97)}
    .upBuy:disabled{background:rgba(255,255,255,.08);color:var(--muted);cursor:default}
    .upBuy.maxed{background:linear-gradient(100deg,var(--green),#b5ffdc);}

    /* shop */
    .tabs{display:flex;gap:6px;margin:10px 0}
    .tabs.scroll{overflow-x:auto;scrollbar-width:none;padding-bottom:4px}
    .tabs.scroll::-webkit-scrollbar{display:none}
    .tabBtn{flex:1;border:1px solid var(--line);background:rgba(255,255,255,.05);color:var(--muted);
      border-radius:12px;padding:9px 8px;font-weight:900;font-size:10.5px;letter-spacing:.07em;white-space:nowrap}
    .tabs.scroll .tabBtn{flex:none}
    .tabBtn.active{color:#07101d;background:linear-gradient(100deg,var(--cyan),#b5ffdc);border-color:transparent}
    .shopNote{margin:8px 0;padding:10px 12px;border-radius:13px;border:1px solid rgba(255,213,106,.3);
      background:rgba(255,213,106,.06);color:#ffe9b3;font-size:11px;font-weight:700;line-height:1.45;text-align:left}
    .shopNote.dev{border-color:rgba(93,255,159,.4);background:rgba(93,255,159,.07);color:#b5ffdc}
    .gemProduct{display:flex;align-items:center;gap:10px;padding:11px 13px;border:1px solid var(--line);
      border-radius:15px;background:rgba(255,255,255,.045);margin-bottom:7px;text-align:left}
    .gemProduct .gIc{width:34px;height:34px;flex:none;display:flex;align-items:center;justify-content:center;font-size:17px}
    .gemProduct div{flex:1;min-width:0}
    .gemProduct b{display:block;font-size:12.5px;letter-spacing:.04em}
    .gemProduct span{display:block;font-size:10px;color:var(--muted);margin-top:1px}
    .gemProduct button{border:0;border-radius:11px;padding:9px 13px;font-weight:1000;font-size:11px;color:#07101d;
      background:linear-gradient(100deg,var(--purple),#e6d4ff);white-space:nowrap}
    .gemProduct button:disabled{background:rgba(255,255,255,.08);color:var(--muted);cursor:default}

    /* challenges */
    .chList{display:flex;flex-direction:column;gap:7px;margin:8px 0;text-align:left}
    .chRow{border:1px solid var(--line);border-radius:14px;background:rgba(255,255,255,.045);padding:10px 12px}
    .chRow b{display:block;font-size:12px;letter-spacing:.03em}
    /* PHASE 5.2.3 FINAL: A BAR AT 0% MUST STILL LOOK LIKE A BAR.
       The track was near-black inside a near-black card, so a challenge with
       no progress appeared to have no progress element at all - the player
       could not tell an empty bar from a missing one. */
    .chBar{position:relative;height:8px;border-radius:99px;background:rgba(255,255,255,.09);
      border:1px solid rgba(255,255,255,.14);overflow:hidden;margin:7px 0 5px}
    .chBar div{height:100%;background:linear-gradient(90deg,var(--cyan),var(--green));box-shadow:0 0 8px var(--cyan);transition:width .4s}
    .chRow.done .chBar div{background:linear-gradient(90deg,var(--gold),#ffe9a8)}
    .chFoot{display:flex;align-items:center;justify-content:space-between;gap:8px}
    .chFoot span{font-size:9.5px;color:var(--muted);font-weight:800;letter-spacing:.04em}
    .chFoot .rew{color:var(--gold)}
    .claimBtn{border:0;border-radius:10px;padding:7px 14px;font-weight:1000;font-size:10.5px;letter-spacing:.05em;
      color:#07101d;background:linear-gradient(100deg,var(--gold),#ffe9a8)}
    /* Styled as a button, does nothing when pressed. It is a STATUS, so it
       stops pretending to be a control. */
    .claimBtn:disabled{background:transparent;color:var(--muted);border:1px dashed rgba(255,255,255,.16);
      font-weight:800;letter-spacing:.1em}
    .claimBtn.claimed{background:rgba(93,255,159,.15);color:var(--green);border:1px solid rgba(93,255,159,.4)}
    .chHead{display:flex;align-items:center;justify-content:space-between;margin-top:10px}
    .chHead b{font-size:12px;letter-spacing:.14em;color:#d7dcff}
    .chHead span{font-size:9.5px;color:var(--muted);font-weight:800}

    /* leaderboard */
    .lbList{text-align:left;display:flex;flex-direction:column;gap:6px;min-height:120px}
    .lbRow{display:flex;align-items:center;gap:9px;padding:9px 11px;border:1px solid var(--line);
      border-radius:13px;background:rgba(255,255,255,.04)}
    .lbRow.me{border-color:var(--gold);background:rgba(255,213,106,.08);box-shadow:0 0 16px rgba(255,213,106,.15)}
    .lbRank{width:22px;font-weight:1000;font-size:14px;color:var(--muted);text-align:center}
    .lbRow:nth-child(1) .lbRank{color:var(--gold)}
    .lbRow canvas{width:30px;height:30px;flex:none}
    .lbName{flex:1;min-width:0;font-weight:900;font-size:13px;letter-spacing:.04em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .lbMeta{display:block;font-size:9px;color:var(--muted);font-weight:700;letter-spacing:.05em}
    .lbScore{font-weight:1000;font-size:17px;color:var(--cyan)}
    .lbBoost{font-size:8px;font-weight:900;color:var(--purple);border:1px solid rgba(177,109,255,.4);
      border-radius:99px;padding:1px 5px;margin-left:4px;vertical-align:2px}
    .statList{text-align:left;display:grid;grid-template-columns:1fr 1fr;gap:7px}
    .statBox{padding:11px;border:1px solid var(--line);border-radius:13px;background:rgba(255,255,255,.04)}
    .statBox b{display:block;font-size:17px}
    .statBox span{font-size:8.5px;color:var(--muted);font-weight:900;letter-spacing:.1em}
    .emptyNote{padding:26px 10px;color:var(--muted);font-size:12px;font-weight:700;text-align:center}
    /* An empty board, stated rather than whispered. Sized to hold the space
       it is standing in instead of floating in the middle of it. */
    .lbEmpty{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;
      min-height:min(46vh,340px);padding:26px 18px;border:1px dashed rgba(255,255,255,.12);
      border-radius:18px;background:rgba(255,255,255,.02)}
    .lbEmptyMark{font-style:normal;font-size:26px;color:var(--gold);opacity:.55}
    .lbEmpty b{display:block;margin-top:10px;font-size:13px;font-weight:1000;letter-spacing:.16em;color:#eaf2ff}
    .lbEmpty span{display:block;margin-top:7px;max-width:46ch;font-size:11.5px;line-height:1.6;color:var(--muted)}

    /* PHASE 5.2.3 FINAL: Settings' BACK reads as the page's exit, not as the
       next item in the first column.
       It was already a sibling of the section grid rather than a cell in it -
       the problem was purely adjacency: left-aligned and hard under the
       GRAPHICS card, with the same gap as the cards use between themselves,
       it looked like part of that card. A rule above it and real separation
       say otherwise. */
    body.desktop #settingsScreen .backBtn{margin-top:22px;padding-top:0}
    body.desktop #settingsScreen #settingsList{padding-bottom:18px;border-bottom:1px solid rgba(255,255,255,.08)}

    /* settings */
    .setRow{display:flex;align-items:center;justify-content:space-between;gap:12px;
      padding:11px 13px;border:1px solid var(--line);border-radius:14px;background:rgba(255,255,255,.04);
      margin-bottom:7px;text-align:left}
    .setRow label{font-weight:900;font-size:12.5px;letter-spacing:.04em}
    .setRow .sub{display:block;font-size:9.5px;color:var(--muted);font-weight:700;margin-top:2px;letter-spacing:.03em}
    input[type=range]{-webkit-appearance:none;appearance:none;width:46%;height:6px;border-radius:99px;
      background:linear-gradient(90deg,var(--cyan) var(--fill,50%),rgba(255,255,255,.14) var(--fill,50%));outline:none}
    input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:20px;height:20px;border-radius:50%;
      background:#fff;box-shadow:0 0 10px var(--cyan);border:0}
    input[type=range]::-moz-range-thumb{width:20px;height:20px;border-radius:50%;background:#fff;box-shadow:0 0 10px var(--cyan);border:0}
    .toggle{position:relative;width:46px;height:26px;border-radius:99px;border:1px solid var(--line);
      background:rgba(255,255,255,.09);transition:.2s;flex:none}
    .toggle:after{content:"";position:absolute;top:2px;left:2px;width:20px;height:20px;border-radius:50%;
      background:#aab;transition:.2s}
    .toggle.on{background:rgba(120,248,255,.3);border-color:var(--cyan)}
    .toggle.on:after{left:22px;background:#fff;box-shadow:0 0 10px var(--cyan)}
    .segment{display:flex;gap:4px;flex:none}
    .segBtn{border:1px solid var(--line);background:rgba(255,255,255,.05);color:var(--muted);border-radius:10px;
      padding:7px 9px;font-weight:900;font-size:9.5px;letter-spacing:.05em}
    .segBtn.active{color:#07101d;background:var(--cyan);border-color:transparent}
    #importArea{width:100%;min-height:90px;border-radius:12px;border:1px solid var(--line);background:rgba(0,0,0,.4);
      color:#fff;font-size:10px;font-family:monospace;padding:8px;margin-top:8px;resize:vertical}

    /* how to play */
    .howList{text-align:left;display:flex;flex-direction:column;gap:7px;margin:12px 0}
    .howRow{display:flex;gap:10px;align-items:flex-start;padding:10px 12px;border:1px solid var(--line);
      border-radius:14px;background:rgba(255,255,255,.04)}
    .howRow .ic{width:30px;height:30px;flex:none;display:flex;align-items:center;justify-content:center;
      border-radius:10px;font-size:15px;background:rgba(120,248,255,.1);border:1px solid rgba(120,248,255,.25)}
    .howRow div b{display:block;font-size:12.5px;margin-bottom:2px}
    .howRow div span{font-size:11px;color:var(--muted);line-height:1.4}
    .puIc{border-radius:50%!important}

    /* loadout */
    .boostGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:8px;margin:10px 0}
    .boostCard{position:relative;border:1px solid var(--line);border-radius:15px;background:rgba(255,255,255,.045);
      padding:12px 8px 10px;text-align:center;color:#fff;transition:transform .12s,border-color .2s}
    .boostCard:active{transform:scale(.96)}
    .boostCard .bSym{display:flex;align-items:center;justify-content:center;width:36px;height:36px;margin:0 auto 6px;
      border-radius:50%;border:1.5px solid;font-size:14px;font-weight:900;background:rgba(6,10,32,.7)}
    .boostCard b{display:block;font-size:11px;letter-spacing:.06em}
    .boostCard span{display:block;font-size:9px;color:var(--muted);margin-top:3px;line-height:1.35;min-height:33px}
    .boostCard.selected{border-color:var(--purple);box-shadow:0 0 0 1px var(--purple),0 0 22px rgba(177,109,255,.3)}
    .boostCard.selected:after{content:"SELECTED";position:absolute;top:6px;right:6px;font-size:7px;font-weight:900;
      color:#fff;background:var(--purple);border-radius:99px;padding:2px 5px}
    .boostCard .freeTag{position:absolute;top:6px;left:6px;font-size:7px;font-weight:900;color:#07101d;
      background:var(--green);border-radius:99px;padding:2px 5px}
    .boostWarn{margin-top:6px;font-size:10px;color:#c9a4ff;font-weight:800;letter-spacing:.04em}

    /* results */
    #endTitle{font-size:clamp(30px,8.5vw,48px);font-weight:1000;letter-spacing:-.04em}
    #endTitle.newbest{background:linear-gradient(100deg,var(--gold),#fff 50%,var(--gold));-webkit-background-clip:text;background-clip:text;color:transparent;
      animation:bestGlow 1.2s infinite alternate}
    @keyframes bestGlow{from{filter:drop-shadow(0 0 6px rgba(255,213,106,.4))}to{filter:drop-shadow(0 0 22px rgba(255,213,106,.9))}}
    #endModeTag{display:inline-block;margin-top:4px;padding:3px 12px;border-radius:99px;border:1px solid var(--line);
      font-size:9.5px;font-weight:900;letter-spacing:.12em;color:#d7dcff}
    #endPreview{width:64px;height:64px;flex:none}
    .endScoreBig{font-size:clamp(38px,11vw,58px);font-weight:1000;line-height:1;font-variant-numeric:tabular-nums}
    .endScoreBig span{display:block;font-size:9px;font-weight:900;letter-spacing:.16em;color:var(--muted)}
    .resultGrid{display:grid;grid-template-columns:repeat(4,1fr);gap:7px;margin:12px 0 8px}
    .result{padding:10px 4px;border:1px solid var(--line);border-radius:14px;background:rgba(255,255,255,.045)}
    .result b{display:block;font-size:clamp(15px,4.6vw,21px)}.result span{font-size:8px;color:var(--muted);font-weight:900;letter-spacing:.1em}
      font-size:12px;font-weight:900;letter-spacing:.04em}
    .rewardLine .rv{font-variant-numeric:tabular-nums}
    .rewardLine.coins .rv{color:var(--gold)} .rewardLine.gems .rv{color:#c9a4ff} .rewardLine.xp .rv{color:var(--green)}
    .xpMini{position:relative;height:10px;border-radius:99px;background:rgba(0,0,0,.4);border:1px solid var(--line);overflow:hidden;margin-top:6px}
    .xpMini div{height:100%;width:0%;background:linear-gradient(90deg,var(--cyan),var(--green));transition:width .6s}
    #levelUpTag,#dailyDoneTag{display:none;margin-top:6px;font-size:11px;font-weight:1000;letter-spacing:.1em;color:var(--green);text-shadow:0 0 12px rgba(93,255,159,.6)}
    #levelUpTag.show,#dailyDoneTag.show{display:block;animation:cardIn .4s}
    /* 5.3.1: the Daily objective is the one run outcome the player was told
       about BEFORE playing ("TODAY'S OBJECTIVE: SCORE n"), and the result
       screen never said they had met it - the 60 coins and 2 gems were folded
       silently into the reward strip. Same banner as LEVEL UP, in Daily's
       gold, so it needs no new component and no new idea. */
    #dailyDoneTag{color:var(--gold);text-shadow:0 0 12px rgba(255,213,106,.6)}
    #unlockRow{display:none;margin:8px 0;padding:10px;border-radius:15px;border:1px solid rgba(120,248,255,.35);
      background:rgba(120,248,255,.06)}
    #unlockRow.show{display:block;animation:cardIn .4s}
    #unlockRow .uTitle{font-size:9.5px;font-weight:900;letter-spacing:.14em;color:var(--cyan)}
    #unlockItems{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-top:6px}
    .unlockCard{display:flex;flex-direction:column;align-items:center;gap:2px;width:74px}
    .unlockCard canvas{width:44px;height:44px}
    .unlockCard b{font-size:8.5px;letter-spacing:.05em}
    .unlockCard span{font-size:7.5px;color:var(--muted)}
    #endChallenges{margin:8px 0;text-align:left}
    #nameEntry{display:none;margin:6px 0 10px;padding:13px;border-radius:16px;border:1px solid rgba(255,213,106,.4);
      background:rgba(255,213,106,.07)}
    #nameEntry.show{display:block}
    #nameEntry .small{color:#ffe9b3;font-weight:800;letter-spacing:.08em}
    #nameForm{display:flex;gap:7px;margin-top:9px}
    #nameInput{flex:1;min-width:0;border-radius:12px;border:1px solid var(--line);background:rgba(0,0,0,.4);
      color:#fff;font-weight:900;font-size:15px;padding:11px 12px;letter-spacing:.05em;text-transform:uppercase;outline:none}
    #nameInput:focus{border-color:var(--gold)}
    #nameSave{border:0;border-radius:12px;padding:0 16px;font-weight:1000;color:#07101d;background:var(--gold);font-size:13px}
    .endBtnRow{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-top:8px}
    .endBtnRow .secondaryBtn{margin-top:0;padding:10px 6px;font-size:10.5px}

    /* image editor */
    #imgStage{position:relative;width:min(74vw,280px);aspect-ratio:1;margin:10px auto;border-radius:18px;
      border:1px dashed rgba(120,248,255,.4);overflow:hidden;background:rgba(0,0,0,.35);touch-action:none}
    #imgCanvas{width:100%;height:100%;display:block;cursor:grab}
    .imgTools{display:flex;flex-direction:column;gap:8px;margin:8px 0}
    .imgToolRow{display:flex;align-items:center;gap:8px;justify-content:center}
    .imgToolRow label{font-size:9.5px;font-weight:900;letter-spacing:.1em;color:var(--muted);width:52px;text-align:right}
    .imgToolRow input[type=range]{flex:1;max-width:220px;width:auto}
    .glowDot{width:26px;height:26px;border-radius:50%;border:2px solid transparent;flex:none}
    .glowDot.sel{border-color:#fff;box-shadow:0 0 10px currentColor}
    .imgPrivacy{margin-top:8px;font-size:10px;color:var(--muted);line-height:1.5}

    /* profile */
    #profileNameInput{width:100%;border-radius:13px;border:1px solid var(--line);background:rgba(0,0,0,.4);
      color:#fff;font-weight:900;font-size:16px;padding:12px;letter-spacing:.05em;text-transform:uppercase;
      outline:none;text-align:center;margin:8px 0}
    #profileNameInput:focus{border-color:var(--cyan)}

    /* tutorial */
    #tutorialBox{position:absolute;left:50%;bottom:calc(84px + var(--safe-bottom));transform:translateX(-50%);
      width:min(90vw,420px);padding:13px 15px;border-radius:16px;border:1px solid rgba(120,248,255,.4);
      background:rgba(6,10,32,.88);backdrop-filter:blur(12px);z-index:5;pointer-events:none;
      text-align:center;box-shadow:0 10px 40px rgba(0,0,0,.5),0 0 30px rgba(120,248,255,.12);
      opacity:0;transition:opacity .3s;display:none}
    #tutorialBox.show{opacity:1;display:block}
    #tutorialBox b{display:block;font-size:14px;letter-spacing:.05em;color:var(--cyan);margin-bottom:3px}
    #tutorialBox span{font-size:12px;color:#dbe3ff;line-height:1.4}
    #tutStep{position:absolute;top:-9px;left:14px;font-size:9px;font-weight:900;letter-spacing:.1em;
      background:var(--cyan);color:#07101d;border-radius:99px;padding:2px 8px}

    #toast{position:absolute;left:50%;top:calc(74px + var(--safe-top));transform:translateX(-50%) translateY(-8px);
      padding:9px 16px;border-radius:99px;border:1px solid rgba(255,213,106,.55);
      /* PHASE 5.2.3 FINAL: OPAQUE AND ELEVATED.
         At .85 alpha and z-index 40 this both sat ON TOP of the Style
         category tabs and got washed out BEHIND the Challenges notice - the
         same element reading as merged into the page on one screen and buried
         under it on another. A toast is the one thing that has to be legible
         wherever it lands, so it is solid, shadowed and above the page. */
      background:#1c1604;box-shadow:0 10px 30px rgba(0,0,0,.6),0 0 0 1px rgba(0,0,0,.35);
      color:#ffe9b3;font-weight:900;font-size:12px;letter-spacing:.06em;z-index:var(--z-toast);opacity:0;transition:.3s;pointer-events:none;
      white-space:nowrap;max-width:92vw;overflow:hidden;text-overflow:ellipsis}
    #toast.show{opacity:1;transform:translateX(-50%) translateY(0)}

    /* confirm modal */
    /* POSITION FIXED, NOT ABSOLUTE, and above every dialog it can be raised
       from. A confirmation always ANSWERS the layer beneath it, so it has to
       out-rank that layer; `absolute` also tied it to an ancestor box, which
       makes a z-index comparison against a `fixed` overlay meaningless. See
       the layer scale at the top of this file. */
    #confirmModal{position:fixed;inset:0;z-index:var(--z-confirm);display:flex;align-items:center;justify-content:center;
      background:rgba(2,3,10,.7);backdrop-filter:blur(6px);
      /* SHOWING IS INSTANT; ONLY HIDING WAITS. Phase 5.2.4J: this transitioned
         visibility over .2s in both directions, so for 200ms after the dialog
         was shown it still COMPUTED visibility:hidden - and focus() on an
         element the engine believes is invisible is ignored outright, without
         an error. The dialog appeared with focus left on whatever was behind
         it. Delaying only the hide keeps the fade-out and makes the dialog
         focusable the instant it is shown. */
      transition:opacity .2s,visibility 0s}
    #confirmModal.hidden{opacity:0;visibility:hidden;pointer-events:none;
      transition:opacity .2s,visibility 0s .2s}
    .modalCard{width:min(88vw,340px);border-radius:20px;border:1px solid var(--line);background:rgba(10,14,40,.96);
      padding:20px 18px 16px;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.6);animation:cardIn .25s}
    .modalCard b{display:block;font-size:16px;letter-spacing:.05em}
    .modalCard p{margin:8px 0 4px;font-size:12px;color:var(--muted);line-height:1.5}
    #confirmPrice{display:none;margin:8px auto 2px}
    #confirmPrice.show{display:inline-flex}
    .modalBtns{display:flex;gap:8px;margin-top:12px}
    .modalBtns button{flex:1;border-radius:13px;padding:12px;font-weight:1000;font-size:12.5px;letter-spacing:.05em;border:0}
    #confirmCancel{background:rgba(255,255,255,.08);color:#fff;border:1px solid var(--line)}
    #confirmOk{background:linear-gradient(100deg,var(--cyan),#b5ffdc);color:#07101d}

    /* ---------- DESKTOP LAYOUT ---------- */
    @media (min-width:900px) and (min-height:560px){
      .homeBody{flex-direction:row;align-items:center;justify-content:center;gap:5vw;overflow:visible}
      .heroPane{flex:0 1 380px}
      .heroPane .logo{font-size:56px}
      #heroStage{width:240px}
      .playPane{flex:0 1 400px;margin:0}
      .card.wide{width:min(90vw,860px)}
      #menuNav{gap:6px;padding:6px}
      .navBtn{flex-direction:row;gap:7px;min-width:0;padding:9px 15px}
      .navBtn span{font-size:10px}
    }

    @media (max-height:640px){
      .heroPane .logo{font-size:clamp(24px,6vw,32px)}
      #heroStage{width:min(32vw,120px)}
      .levelBar{margin-top:5px}
      .modeCard>span.desc{min-height:0}
    }

    /* reduced motion (system or in-game setting via body.rm) */
    @media (prefers-reduced-motion:reduce){
      #pulseBtn.ready,#endTitle.newbest,.iconBtn .dot.on,.buff.expiring{animation:none}
      .screen:not(.hidden) .card,.modeCard.anim-left,.modeCard.anim-right{animation:none}
    }
    body.rm #pulseBtn.ready,body.rm #endTitle.newbest,body.rm .iconBtn .dot.on,body.rm .buff.expiring{animation:none}
    body.rm .screen:not(.hidden) .card,body.rm .modeCard.anim-left,body.rm .modeCard.anim-right{animation:none}

    /* ==================== v4 ADDITIONS ==================== */

    /* grouped currencies + quieter secondary icons in the top bar */
    .curGroup{display:flex;gap:0;border:1px solid var(--line);border-radius:14px;background:var(--glass);
      backdrop-filter:blur(10px);overflow:hidden}
    .curGroup .curChip{border:0;border-radius:0;background:none;display:flex;align-items:center;gap:5px;
      padding:6px 10px;min-height:38px;font-weight:900;font-size:13px;font-variant-numeric:tabular-nums}
    .curGroup .curChip + .curChip{border-left:1px solid var(--line)}
    .curGroup .curChip .ic{width:12px;height:12px;border-radius:50%;flex:none}
    .iconBtn.quiet{opacity:.72}
    .iconBtn.quiet:hover{opacity:1}

    /* update pill */
    #updatePill{position:absolute;left:50%;top:calc(56px + var(--safe-top));transform:translateX(-50%);
      z-index:35;border:1px solid rgba(93,255,159,.5);background:rgba(10,30,18,.9);color:#b5ffdc;
      font-weight:900;font-size:11px;letter-spacing:.08em;border-radius:99px;padding:8px 16px;
      box-shadow:0 8px 30px rgba(0,0,0,.5),0 0 18px rgba(93,255,159,.25);cursor:pointer}

    /* quick goal panel */
    #goalPanel{display:flex;flex-direction:column;gap:5px;margin-top:8px}
    .goalRow{position:relative;display:flex;align-items:center;gap:8px;width:100%;text-align:left;
      border:1px solid var(--line);border-radius:13px;background:var(--glass);backdrop-filter:blur(8px);
      padding:8px 11px 12px;color:#dfe6ff;font-size:11px;font-weight:800;letter-spacing:.02em;
      transition:transform .12s,border-color .2s}
    .goalRow:active{transform:scale(.98)}
    @media (hover:hover){ .goalRow:hover{border-color:rgba(120,248,255,.45)} }
    .goalRow i{font-style:normal;color:var(--cyan);font-size:13px;flex:none}
    .goalRow span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .goalRow.hot{border-color:rgba(255,213,106,.55)}
    .goalRow.hot i{color:var(--gold)}
    .goalBar{position:absolute;left:11px;right:11px;bottom:5px;height:3px;border-radius:99px;background:rgba(255,255,255,.08)}
    .goalBar div{height:100%;border-radius:inherit;background:linear-gradient(90deg,var(--cyan),var(--green));box-shadow:0 0 6px var(--cyan)}
    .goalRow.hot .goalBar div{background:linear-gradient(90deg,var(--gold),#ffe9a8)}

    /* mastery chips on character cards + strips */
    .masteryChip{position:absolute;top:6px;left:6px;font-size:7.5px;font-weight:900;letter-spacing:.06em;
      color:#ffd56a;border:1px solid rgba(255,213,106,.45);border-radius:99px;padding:2px 5px;background:rgba(255,213,106,.08)}
    .masteryChip.maxed{color:#07101d;background:var(--gold);border-color:transparent}
    .mBar{height:3px;border-radius:99px;background:rgba(255,255,255,.08);margin-top:5px;overflow:hidden}
    .mBar div{height:100%;background:linear-gradient(90deg,var(--gold),#ffe9a8);box-shadow:0 0 5px var(--gold)}
    .mStrip{display:grid;grid-template-columns:repeat(auto-fill,minmax(64px,1fr));gap:6px;margin:8px 0}
    .mCell{position:relative;border:1px solid var(--line);border-radius:12px;background:rgba(255,255,255,.04);
      padding:6px 4px;text-align:center}
    .mCell canvas{width:36px;height:36px;display:block;margin:0 auto}
    .mCell b{display:block;font-size:9px;letter-spacing:.06em;color:#ffd56a;margin-top:2px}
    .mCell.maxed{border-color:rgba(255,213,106,.55);box-shadow:0 0 12px rgba(255,213,106,.15)}

    /* collection book */
    .collHead{display:flex;align-items:center;gap:10px;margin:6px 0 4px}
    .collHead b{font-size:22px;color:var(--cyan);font-variant-numeric:tabular-nums}
    .collBar{flex:1;height:10px;border-radius:99px;border:1px solid var(--line);background:rgba(0,0,0,.4);overflow:hidden}
    .collBar div{height:100%;background:linear-gradient(90deg,var(--cyan),var(--pink));box-shadow:0 0 10px var(--cyan);transition:width .5s}
    .collHead .small{flex:none}

    /* challenge codes */
    .codeBox{margin:10px 0;padding:11px 13px;border:1px solid var(--line);border-radius:15px;
      background:rgba(255,255,255,.045);text-align:left}
    .codeBox label{font-weight:900;letter-spacing:.12em;font-size:9.5px;color:var(--muted)}
    .codeRow{display:flex;gap:7px;margin-top:7px;align-items:center}
    #challengeInput{flex:1;min-width:0;border-radius:11px;border:1px solid var(--line);background:rgba(0,0,0,.4);
      color:#fff;font-weight:900;font-size:13px;padding:10px 11px;letter-spacing:.08em;text-transform:uppercase;
      outline:none;font-family:monospace}
    #challengeInput:focus{border-color:var(--purple)}
    .codeVal{flex:1;min-width:0;font-family:monospace;font-size:13px;letter-spacing:.06em;color:var(--cyan);
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

    /* results v4 */
    .resultGrid{grid-template-columns:repeat(4,1fr)}
    #routeMapWrap{margin:8px 0;padding:10px;border:1px solid var(--line);border-radius:15px;background:rgba(255,255,255,.035)}
    #routeMap{width:100%;height:auto;display:block;border-radius:10px}
    .routeLegend{margin-top:6px;font-size:9.5px}
    .routeLegend i{font-style:normal;font-weight:900}

    /* ---------- accessibility: UI & text scale ---------- */
    body.ui-small .mainBtn{padding:12px 16px;font-size:13.5px}
    body.ui-small .secondaryBtn{padding:10px 14px;font-size:11.5px}
    body.ui-small .iconBtn{width:32px;height:32px;font-size:12px}
    body.ui-large .mainBtn{padding:18px 20px;font-size:17px}
    body.ui-large .secondaryBtn{padding:15px 20px;font-size:14.5px}
    body.ui-large .iconBtn{width:44px;height:44px;font-size:17px}
    body.ui-large .navBtn{min-width:54px;padding:8px 8px 7px}
    body.ui-large #pulseBtn{width:clamp(72px,19vw,92px);height:clamp(72px,19vw,92px)}
    body.txt-large .small{font-size:13px}
    body.txt-large .tip span,body.txt-large .howRow div span{font-size:12.5px}
    body.txt-large .skinCard span{font-size:10.5px}
    body.txt-large .setRow .sub{font-size:11px}
    body.txt-xlarge .small{font-size:14.5px}
    body.txt-xlarge .tip span,body.txt-xlarge .howRow div span{font-size:14px}
    body.txt-xlarge .skinCard span{font-size:12px}
    body.txt-xlarge .setRow .sub{font-size:12.5px}

    /* ---------- accessibility: left-handed controls ---------- */
    body.lh #pulseBtn{right:auto;left:calc(12px + var(--safe-left))}
    body.lh #pauseBtn{left:auto;right:calc(12px + var(--safe-right))}
    @media (orientation:landscape) and (max-height:520px){
      body.lh #pauseBtn{left:auto;right:calc(10px + var(--safe-right))}
    }

    /* ---------- accessibility: high contrast ---------- */
    body.hc .screen{background:radial-gradient(circle at 50% 18%,rgba(20,24,60,.92),rgba(3,4,12,.97) 54%,rgba(2,2,8,.99))}
    body.hc .chip,body.hc .buff{background:rgba(0,0,0,.8);border-color:rgba(255,255,255,.35)}
    body.hc .small{color:#cfd6ee}
    body.hc .skinCard,body.hc .chRow,body.hc .setRow,body.hc .upRow{border-color:rgba(255,255,255,.28);background:rgba(0,0,0,.45)}

    /* ==================== v5 ADDITIONS ==================== */


    /* ---------- v5 results: replay-first ---------- */
    .endHero{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:8px}
    #endPreview{width:64px;height:64px;flex:none}
    .endHeroMid{text-align:left;min-width:0}
    .endScoreBig #finalScore{display:block;font-size:inherit;color:#fff}
    .endTimeLine{margin:1px 0 0}
    .endTimeLine b{color:var(--cyan)}
    .rewardStrip{position:relative;display:flex;flex-wrap:wrap;justify-content:center;gap:6px;
      margin:10px 0 6px;padding:8px 30px 8px 10px;border-radius:14px;
      border:1px solid rgba(255,213,106,.28);background:rgba(255,213,106,.05)}
    .rChip{display:inline-flex;align-items:center;gap:5px;font-weight:900;font-size:12px;
      font-variant-numeric:tabular-nums;letter-spacing:.03em}
    .rChip .ic{width:11px;height:11px;border-radius:50%;flex:none}
    .rChip.coins{color:var(--gold)} .rChip.coins .ic{background:radial-gradient(circle at 35% 30%,#fff,var(--gold) 55%,#a06900);box-shadow:0 0 6px var(--gold)}
    .rChip.xp{color:var(--green)}
    .rChip.mastery{color:#ffe9a8} .rChip.mastery i{font-style:normal;font-size:9px;letter-spacing:.08em;color:var(--muted)}
    .rChip.gems{color:#c9a4ff} .rChip.gems .ic{border-radius:3px;transform:rotate(45deg);background:linear-gradient(135deg,#e6d4ff,var(--purple) 70%);box-shadow:0 0 6px var(--purple)}
    #skipRewardsBtn{position:absolute;top:4px;right:8px;color:var(--muted);font-size:9px}
    .mainBtn.playAgain{margin-top:10px;padding:17px 18px;font-size:16px}
    .endBtnRow.two{grid-template-columns:1fr 1fr}
    #endNameLine{margin:10px 0 0;letter-spacing:.04em}
    #endNameLine b{color:var(--cyan)}
    #nameEntry{display:none;margin:6px 0}
    #nameEntry.show{display:block}
    .detailsToggle{margin-top:10px;letter-spacing:.1em;font-size:11.5px;color:var(--muted)}
    #runDetails{animation:cardIn .25s}
    body.rm #runDetails{animation:none}
    #unlockRow{margin:8px 0 0}
    #unlockItems{flex-wrap:nowrap;overflow-x:auto;justify-content:flex-start;
      scrollbar-width:thin;padding-bottom:4px;-webkit-overflow-scrolling:touch}
    .unlockCard{flex:none}

    /* ---------- v5 tutorial: card that collapses into a pill ---------- */
    #tutorialBox{pointer-events:auto}
    #tutorialBox.low{bottom:calc(84px + var(--safe-bottom));top:auto}
    #tutorialBox.high{top:calc(96px + var(--safe-top));bottom:auto}
    #tutorialBox.pill{width:auto;max-width:88vw;padding:7px 40px 7px 14px;border-radius:99px}
    #tutorialBox.pill b{display:inline;font-size:11.5px;margin-right:6px}
    #tutorialBox.pill span{display:inline;font-size:10.5px}
    #tutorialBox.pill #tutStep{display:none}
    #tutSkip{position:absolute;right:8px;top:50%;transform:translateY(-50%);
      border:0;background:none;color:var(--muted);font-weight:900;font-size:9px;
      letter-spacing:.08em;padding:6px}
    #tutorialBox:not(.pill) #tutSkip{top:auto;bottom:4px;transform:none}
    @media (hover:hover){ #tutSkip:hover{color:var(--cyan)} }

    /* ---------- v5 onboarding: progressive disclosure ---------- */
    body.onb0 .navBtn[data-nav="upgrades"],body.onb0 .navBtn[data-nav="shop"],body.onb0 .navBtn[data-nav="lb"],
    body.onb0 #challengeLink,body.onb0 #statsLink,body.onb0 #challengesLink,body.onb0 #challengeBtn,
    body.onb0 #goalPanel,body.onb0 .curGroup,body.onb0 .levelBar,
    body.onb0 .navBtn[data-nav="chars"],body.onb0 .navBtn[data-nav="custom"],
    body.onb0 #loadoutBtn,body.onb0 #modeDots,body.onb0 .modeMeta .mMult{display:none}
    body.onb1 .navBtn[data-nav="upgrades"],body.onb1 .navBtn[data-nav="shop"],body.onb1 .navBtn[data-nav="lb"],
    body.onb1 #challengeLink,body.onb1 #statsLink,body.onb1 #challengesLink,body.onb1 #challengeBtn,
    body.onb1 #goalPanel,body.onb1 .levelBar{display:none}
    body.onb2 .navBtn[data-nav="upgrades"],body.onb2 .navBtn[data-nav="shop"],
    body.onb2 #challengeLink{display:none}
    body.onb0 .carArrow,body.onb1 .carArrow{visibility:hidden}

    /* ---------- v5 arena frame (desktop/tablet: capped playfield) ---------- */
    /* (drawn on canvas — no DOM element needed) */

    /* ---------- iPhone: prevent overscroll/pull-to-refresh ---------- */
    html{overscroll-behavior:none}
    body.onb0 #menuNav{display:none}   /* tier 0: a lone HOME button helps nobody */

    /* ==================== v5.1: CENTRE HUD STACK ==================== */
    #hudCenter{
      position:absolute;left:50%;top:calc(6px + var(--safe-top));transform:translateX(-50%);
      display:flex;flex-direction:column;align-items:center;gap:3px;
      width:min(92vw,420px);pointer-events:none;z-index:6}
    #loopMeter{width:min(34vw,180px);height:7px;border:1px solid var(--line);border-radius:999px;
      background:rgba(0,0,0,.35);overflow:hidden;flex:none}
    #loopFill{height:100%;width:0%;border-radius:inherit;background:linear-gradient(90deg,var(--cyan),var(--pink));box-shadow:0 0 12px var(--pink)}
    #loopMeter.warn #loopFill{background:linear-gradient(90deg,var(--pink),#ff9a6a);box-shadow:0 0 16px #ff8f7a}
    #loopText{font-size:8.5px;font-weight:900;letter-spacing:.16em;color:#d7dcff;text-shadow:0 2px 8px #000;white-space:nowrap}
    /* loop 3-2-1 overlays the meter/label region — no layout shift */
    #countdown{position:absolute;top:-2px;left:50%;transform:translateX(-50%);
      font-size:19px;font-weight:1000;color:#ffb1c8;text-shadow:0 0 14px rgba(255,102,150,.8);
      opacity:0;pointer-events:none}
    #countdown.show{opacity:1;animation:cdPop .32s}
    @keyframes cdPop{0%{transform:translateX(-50%) scale(1.6)}100%{transform:translateX(-50%) scale(1)}}
    body.rm #countdown.show{animation:none}
    /* announcement rail: compact, plated, reserved height (no layout jumps) */
    #announce{
      min-height:30px;max-width:100%;display:flex;flex-direction:column;align-items:center;
      justify-content:center;padding:4px 14px;border-radius:12px;text-align:center;
      background:rgba(4,6,20,.78);border:1px solid rgba(255,255,255,.16);
      backdrop-filter:blur(8px);box-shadow:0 6px 20px rgba(0,0,0,.4);
      opacity:0;transform:translateY(-4px);
      transition:opacity var(--t-fast,.2s),transform var(--t-fast,.2s)}
    #announce.show{opacity:1;transform:none}
    body.rm #announce{transform:none}
    #announce b{display:block;max-width:min(88vw,400px);font-size:clamp(12.5px,3.6vw,17px);
      font-weight:1000;letter-spacing:.03em;line-height:1.25;text-shadow:0 2px 8px #000;
      white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    #announce span{display:block;max-width:min(88vw,400px);margin-top:1px;
      font-size:clamp(9px,2.6vw,11px);font-weight:800;letter-spacing:.1em;color:#cfe6ff;
      white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    #announce.small{background:rgba(4,6,20,.6)}
    #announce.small b{font-size:clamp(11px,3vw,13.5px)}
    body.hc #announce{background:rgba(0,0,0,.94);border:2px solid rgba(255,255,255,.5)}
    #buffBar{display:flex;gap:5px;flex-wrap:wrap;justify-content:center;max-width:92vw;min-height:22px}

    /* ==================== v5.1: SAFE RESUME COUNTDOWN ==================== */
    /* (drawn on canvas — CSS only dims interaction hints) */


    /* ================= v5.2 — fresh-player intro strip ================= */
    #freshIntro{display:none;margin:10px auto 2px;text-align:center}
    body.onb0 #freshIntro{display:block}
    .fiTag{margin:0 0 7px;font-size:10px;font-weight:1000;letter-spacing:.22em;color:#ffb1e5;
      text-shadow:0 0 14px rgba(255,102,212,.5)}
    .fiSteps{display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap}
    .fiSteps span{display:inline-flex;flex-direction:column;align-items:center;gap:2px;
      padding:7px 10px;border:1px solid var(--line);border-radius:var(--r-md);
      background:rgba(255,255,255,.04);min-width:64px}
    .fiSteps i{font-style:normal;font-size:14px;color:var(--cyan)}
    .fiSteps b{font-size:7.5px;letter-spacing:.12em;color:var(--muted)}
    .fiSteps .fiArrow{border:0;background:none;min-width:0;padding:0;color:rgba(255,255,255,.3);
      font-size:12px;font-weight:900;flex-direction:row}

    /* ================= v5.2 — menu transitions & press feedback ================= */
    .screen{transform:translateY(0);transition:opacity var(--t-med),visibility var(--t-med),transform var(--t-med)}
    .screen.hidden{transform:translateY(10px)}
    body.rm .screen{transform:none !important;transition:opacity .15s,visibility .15s}
    .mainBtn:active{transform:scale(.965);filter:brightness(1.15)}
    .secondaryBtn:active,.tabBtn:active,.segBtn:active,.navBtn:active{transform:scale(.96)}
    .skinCard:active,.featHero:active,.modeCard:active{transform:scale(.982)}
    body.rm .mainBtn:active,body.rm .secondaryBtn:active,body.rm .skinCard:active{transform:none}
    @media (hover:hover){
      .skinCard:hover{border-color:rgba(120,248,255,.4)}
      .mainBtn:hover{filter:brightness(1.12)}
      .secondaryBtn:hover,.tabBtn:hover{border-color:rgba(120,248,255,.45);color:#eaffff}
    }
    /* selected-character idle bob (visual only) */
    @keyframes cdIdle{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}
    #cdPreview{animation:cdIdle 3.2s ease-in-out infinite}
    body.rm #cdPreview{animation:none}

    /* ================= v5.2 — dev-only future-mode card ================= */
    #futureCard{display:flex;flex-direction:column;gap:2px;margin:8px auto 0;padding:9px 12px;
      border:1px dashed rgba(177,109,255,.5);border-radius:var(--r-md);background:rgba(177,109,255,.07);
      color:#c9a4ff;font-size:9px;font-weight:900;letter-spacing:.1em;max-width:280px}
    #futureCard b{font-size:11px;letter-spacing:.16em}

    /* ================= v5.1 — Characters detail panel ================= */
    .charsLayout{display:flex;flex-direction:column;gap:var(--sp-2)}
    #charDetail{display:flex;gap:var(--sp-3);align-items:center;text-align:left;
      border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--sp-3);
      background:linear-gradient(160deg,rgba(120,248,255,.07),rgba(255,102,212,.05)),rgba(255,255,255,.04)}
    #cdPreview{width:110px;height:110px;flex:none;border-radius:var(--r-md);
      background:rgba(4,6,20,.5);border:1px solid var(--line)}
    .cdInfo{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px;align-items:flex-start}
    .cdInfo b{font-size:15px;letter-spacing:.08em}
    .cdInfo .small{margin:0;text-align:left}
    #cdMastery{width:100%;display:flex;flex-direction:column;gap:3px;margin-top:2px}
    #cdMasteryLabel{font-size:10px;font-weight:900;letter-spacing:.08em;color:#e8d9ae}
    #cdMasteryNext{margin:0;text-align:left;font-size:9px}
    .mBar.big{height:6px;margin-top:0;width:100%}
    .masteredBadge{display:inline-block;padding:2px 9px;border-radius:99px;background:var(--gold);
      color:#07101d;font-size:10px;font-weight:1000;letter-spacing:.1em;box-shadow:0 0 12px rgba(255,213,106,.55)}
    .cdEquipBtn{margin-top:4px;padding:9px 22px;font-size:11px;min-height:44px}
    .cdEquipBtn:disabled{opacity:.55;cursor:default;transform:none}
    .skinCard.inspecting{border-color:rgba(120,248,255,.65);box-shadow:0 0 0 1px rgba(120,248,255,.35)}
    @media (min-width:900px){
      .charsLayout{flex-direction:row;align-items:flex-start}
      #charDetail{flex-direction:column;width:250px;flex:none;position:sticky;top:0;text-align:center}
      #cdPreview{width:200px;height:200px}
      .cdInfo{align-items:center;text-align:center}
      .cdInfo .small,#cdMasteryNext{text-align:center}
      .charsLayout .skinGrid{flex:1;margin:0}
    }

    /* ================= v5.1 — Settings sections ================= */
    .setSection{border:1px solid var(--line);border-radius:var(--r-md);margin:var(--sp-2) 0;
      background:rgba(255,255,255,.03);overflow:hidden}
    .setHead{display:flex;width:100%;align-items:center;justify-content:space-between;border:0;
      background:rgba(255,255,255,.04);padding:12px 14px;min-height:44px;
      font-weight:1000;font-size:11.5px;letter-spacing:.14em;color:#d7dcff}
    .setHead i{font-style:normal;transition:transform var(--t-med);color:var(--muted)}
    .setSection.open .setHead i{transform:rotate(180deg)}
    .setBody{display:none;padding:2px 10px 10px}
    .setSection.open .setBody{display:block}
    .setBody .setRow{border:0;border-bottom:1px solid rgba(255,255,255,.06);border-radius:0;
      background:transparent;margin:0;padding:10px 4px}
    .setBody .setRow:last-child{border-bottom:0}
    body.hc .setSection{border-color:rgba(255,255,255,.28);background:rgba(0,0,0,.45)}

    /* ================= v5.1 — Challenges tabs & summary ================= */
    .chSummary{display:flex;flex-direction:column;gap:2px;border:1px solid rgba(255,213,106,.25);
      border-radius:var(--r-md);background:rgba(255,213,106,.06);padding:9px 12px;margin:6px 0 2px}
    .chSummary b{font-size:12px;letter-spacing:.06em;color:var(--gold)}
    .chSummary span{font-size:9.5px;color:var(--muted);font-weight:800;letter-spacing:.05em}
    .claimBtn.claimAll{display:block;width:100%;margin:4px 0 8px;padding:11px;font-size:11px;min-height:44px}
    .chRow.quiet{opacity:.55}
    .chList{display:flex;flex-direction:column;gap:7px;margin:6px 0}

    /* ================= v5.1 — Shop featured hero ================= */
    .featHero{display:flex;width:100%;gap:var(--sp-3);align-items:center;text-align:left;margin:6px 0;
      border:1px solid rgba(255,213,106,.4);border-radius:var(--r-lg);padding:var(--sp-3);
      background:linear-gradient(135deg,rgba(255,213,106,.12),rgba(255,102,212,.07) 55%,rgba(120,248,255,.06)),rgba(8,10,28,.65);
      transition:transform var(--t-fast),box-shadow var(--t-fast)}
    .featHero:active{transform:scale(.985)}
    .featHero canvas{width:96px;height:96px;flex:none;border-radius:var(--r-md);
      background:rgba(4,6,20,.5);border:1px solid var(--line)}
    .fhInfo{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;align-items:flex-start}
    .fhInfo i{font-style:normal;font-size:8.5px;font-weight:1000;letter-spacing:.2em;color:var(--gold)}
    .fhInfo b{font-size:14px;letter-spacing:.06em}
    .fhInfo span{font-size:9.5px;color:var(--muted);font-weight:700}
    @media (min-width:900px){
      .featHero canvas{width:130px;height:130px}
      .skinGrid{grid-template-columns:repeat(auto-fill,minmax(118px,1fr))}
    }

    /* ================= v5.1 — desktop home scale-up ================= */
    @media (min-width:1200px){
      .card{width:min(94vw,560px)}
      .card.wide{width:min(94vw,860px)}
    }


    /* =====================================================================
       v5.3 BROWSER EDITION — desktop application shell
       Everything here is scoped to body.desktop (set by the game when the
       viewport is a real desktop window). The phone layout above is left
       untouched, so the preserved mobile build still renders exactly as it
       did. No global zoom/scale is used anywhere: this is a real layout.
       ===================================================================== */
    :root{
      --nav-w: 212px;
      --shell-gutter: clamp(18px, 2.2vw, 44px);
      --content-max: 1480px;
      --panel-1: rgba(12,16,44,.72);
      --panel-2: rgba(255,255,255,.045);
    }
    .deskOnly{display:none}
    body.desktop .deskOnly{display:flex}

    /* ---------- left sidebar navigation ---------- */
    body.desktop #menuNav{
      position:fixed;left:0;top:0;bottom:0;transform:none;
      width:var(--nav-w);flex-direction:column;align-items:stretch;gap:2px;
      padding:18px 12px 14px;border-radius:0;border:0;border-right:1px solid var(--line);
      background:linear-gradient(180deg,rgba(10,14,38,.96),rgba(6,8,26,.96));
      box-shadow:8px 0 40px rgba(0,0,0,.45);backdrop-filter:blur(18px);
      overflow-y:auto;overflow-x:hidden;z-index:40}
    body.desktop .navBrand{display:flex;flex-direction:column;gap:2px;padding:0 8px 14px;margin-bottom:8px;
      border-bottom:1px solid rgba(255,255,255,.07)}
    body.desktop .navBrand b{font-size:19px;font-weight:1000;letter-spacing:-.02em;
      background:linear-gradient(100deg,#fff 10%,var(--cyan) 60%,var(--pink));
      -webkit-background-clip:text;background-clip:text;color:transparent}
    body.desktop .navBrand i{font-style:normal;font-size:7.5px;font-weight:900;letter-spacing:.22em;color:var(--muted)}
    body.desktop .navGroup{display:flex;flex-direction:column;gap:2px}
    body.desktop .navGroup.deskOnly{margin-top:12px;padding-top:12px;border-top:1px solid rgba(255,255,255,.07)}
    body.desktop .navBtn{flex-direction:row;justify-content:flex-start;gap:11px;width:100%;
      min-width:0;min-height:42px;padding:9px 12px;border-radius:11px;text-align:left;
      transition:background var(--t-fast),color var(--t-fast),transform var(--t-fast)}
    body.desktop .navBtn i{font-size:15px;width:20px;text-align:center;flex:none}
    body.desktop .navBtn span{font-size:11px;letter-spacing:.09em;white-space:nowrap}
    body.desktop .navBtn:hover{background:rgba(120,248,255,.09);color:#eaffff}
    body.desktop .navBtn.active{background:linear-gradient(90deg,rgba(120,248,255,.20),rgba(120,248,255,.05));
      color:#fff;box-shadow:inset 3px 0 0 var(--cyan)}
    body.desktop .navBtn:focus-visible{outline:2px solid var(--cyan);outline-offset:-2px}
    body.desktop .navFoot{margin-top:auto;flex-direction:column;gap:8px;padding-top:14px;
      border-top:1px solid rgba(255,255,255,.07)}
    body.desktop .navMini{display:flex;align-items:center;gap:9px;width:100%;min-height:38px;padding:8px 12px;
      border:1px solid var(--line);border-radius:11px;background:rgba(255,255,255,.04);
      color:var(--muted);font-size:10px;font-weight:900;letter-spacing:.1em}
    body.desktop .navMini:hover{border-color:rgba(120,248,255,.45);color:#eaffff}
    body.desktop .navMini i{font-style:normal;font-size:14px}
    body.desktop .navVer{font-size:8px;font-weight:800;letter-spacing:.08em;color:rgba(174,184,216,.55);padding:0 12px}

    /* ---------- screens fill the shell beside the sidebar ---------- */
    body.desktop .screen{
      align-items:flex-start;justify-content:center;
      padding:clamp(20px,2.6vh,40px) var(--shell-gutter) clamp(20px,2.6vh,40px)
              calc(var(--nav-w) + var(--shell-gutter));
      overflow-y:auto}
    body.desktop .screen.withNav{padding-bottom:clamp(20px,2.6vh,40px)}
    body.desktop .card{width:min(100%,var(--content-max));max-height:none;overflow:visible;
      padding:0 2px 8px;text-align:left}
    body.desktop .card.wide{width:min(100%,var(--content-max))}
    body.desktop .screenTitle{font-size:clamp(30px,2.6vw,44px);text-align:left;margin:0 0 4px}
    body.desktop .card > .small{text-align:left;max-width:78ch}
    body.desktop .curBar{justify-content:flex-start}
    body.desktop .backBtn{width:auto;min-width:190px;margin-top:18px}
    /* a screen header row: title left, currencies right */
    body.desktop .card > .curBar{position:absolute;right:var(--shell-gutter);top:clamp(20px,2.6vh,40px)}
    /* NOTE: .screen stays position:absolute (inset:0) - making it relative
       stacks the screens in flow and pushes them off the viewport. */

    /* ---------- HOME: three deliberate columns ---------- */
    body.desktop #homeScreen{padding-top:clamp(16px,2vh,28px)}
    body.desktop .homeTop{position:absolute;top:clamp(14px,1.8vh,26px);right:var(--shell-gutter);
      left:calc(var(--nav-w) + var(--shell-gutter));z-index:5}
    body.desktop .homeBody{display:grid;overflow:visible;
      grid-template-columns:minmax(300px,1.02fr) minmax(340px,1.25fr) minmax(280px,.92fr);
      gap:clamp(18px,2vw,46px);align-items:center;
      width:min(100%,var(--content-max));margin:0 auto;
      min-height:calc(100vh - 150px);padding-top:56px}
    body.desktop .heroPane{grid-column:2;grid-row:1;flex:none;display:flex;flex-direction:column;
      align-items:center;gap:10px}
    body.desktop .playPane{grid-column:1;grid-row:1;width:100%;margin:0;display:flex;
      flex-direction:column;gap:10px}
    body.desktop .statPane{grid-column:3;grid-row:1;display:flex;flex-direction:column;gap:12px}
    body.desktop .heroPane .logo{font-size:clamp(38px,3.6vw,62px);line-height:.95}
    body.desktop .heroPane .tag{font-size:clamp(10px,.85vw,13px);letter-spacing:.3em}
    body.desktop #heroStage{width:min(30vw,380px);aspect-ratio:1}
    body.desktop .modeCarousel{width:100%}
    body.desktop .modeCard{padding:16px 18px}
    body.desktop .modeCard b{font-size:clamp(20px,1.5vw,26px)}
    body.desktop #startBtn{font-size:clamp(20px,1.6vw,28px);padding:clamp(16px,1.5vh,24px) 28px;
      letter-spacing:.06em}
    body.desktop #startBtn:hover{filter:brightness(1.12) saturate(1.05)}
    body.desktop .homeLinks{justify-content:flex-start;flex-wrap:wrap}

    /* desktop-only right dock: profile, progress, challenges */
    .statPane{display:none}
    body.desktop .statPane{display:flex}
    .dockCard{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel-1);
      padding:14px 16px;display:flex;flex-direction:column;gap:9px}
    .dockCard h4{margin:0;font-size:9px;font-weight:1000;letter-spacing:.2em;color:var(--muted)}
    .dockRow{display:flex;align-items:center;justify-content:space-between;gap:10px;
      font-size:11px;font-weight:800;letter-spacing:.04em}
    .dockRow span{color:var(--muted)}
    .dockRow b{font-size:14px}
    .dockBig{display:flex;align-items:baseline;gap:8px}
    .dockBig b{font-size:30px;font-weight:1000;letter-spacing:-.02em}
    .dockBig i{font-style:normal;font-size:9px;font-weight:900;letter-spacing:.16em;color:var(--muted)}
    /* PHASE 5.2.3 FINAL: visible with nothing in it. The same fix .chBar got
       never reached here, so three daily challenges at 0/1 rendered as three
       barely-there grey lines on the home screen. A progress bar has to be
       legible as a bar before it has any progress in it. */
    .dockBar{height:6px;border-radius:99px;background:rgba(255,255,255,.09);
      border:1px solid rgba(255,255,255,.14);overflow:hidden}
    .dockBar div{height:100%;background:linear-gradient(90deg,var(--cyan),var(--pink));border-radius:99px}
    .dockChal{display:flex;flex-direction:column;gap:3px}
    .dockChal .dcName{font-size:10.5px;font-weight:800;color:#dfe6ff}
    .dockEmpty{font-size:10px;color:var(--muted);font-weight:700}

    /* ---------- HEROES ---------- */
    body.desktop .charsLayout{display:grid;grid-template-columns:minmax(280px,340px) 1fr;
      gap:clamp(18px,1.8vw,34px);align-items:start}
    body.desktop #charDetail{position:sticky;top:0;width:100%;flex-direction:column;text-align:center;
      padding:20px;gap:14px}
    body.desktop #cdPreview{width:min(18vw,240px);height:min(18vw,240px)}
    body.desktop .cdInfo{align-items:center;text-align:center;gap:6px}
    body.desktop .cdInfo b{font-size:20px}
    body.desktop .charsLayout .skinGrid{margin:0;
      grid-template-columns:repeat(auto-fill,minmax(clamp(132px,10vw,168px),1fr));gap:12px}
    body.desktop .skinCard{padding:12px 10px 11px}
    body.desktop .skinCard canvas{width:88px;height:88px}
    body.desktop .skinCard b{font-size:12px}

    /* ---------- STYLE / CUSTOMIZE ---------- */
    /* STYLE: preview column on the left, category browser on the right.
       Every element gets its OWN grid cell - sharing a cell stacks them. */
    body.desktop #customScreen .card{display:grid;
      grid-template-columns:minmax(280px,340px) minmax(0,1fr);
      grid-template-rows:auto auto auto 1fr auto auto auto;
      grid-template-areas:
        "title   title"
        "preview tabs"
        "preview filters"
        "preview grid"
        "actions grid"
        "presets grid"
        "note    grid";
      gap:clamp(12px,1.2vw,22px) clamp(18px,1.8vw,34px);align-items:start}
    body.desktop #customScreen .screenTitle{grid-area:title}
    body.desktop #custPreviewWrap{grid-area:preview;width:100%;max-width:340px;aspect-ratio:1;
      border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel-1);padding:16px}
    body.desktop #customScreen .catTabsWrap{grid-area:tabs}
    body.desktop #customScreen .filterRow{grid-area:filters;justify-content:flex-start}
    body.desktop #customScreen #custGrid{grid-area:grid;align-self:start;margin:0;
      grid-template-columns:repeat(auto-fill,minmax(clamp(126px,9.5vw,158px),1fr))}
    body.desktop #customScreen .custActions{grid-area:actions;display:flex;gap:10px}
    body.desktop #customScreen .presetRow{grid-area:presets}
    body.desktop #customScreen .card > .small{grid-area:note;margin:0;text-align:left}
    body.desktop #customScreen #collectionLink{grid-area:note;align-self:end;margin-top:44px}
    body.desktop #customScreen .catTabs{flex-wrap:wrap;overflow:visible;padding-right:2px}
    body.desktop #customScreen .catTabsWrap::after{display:none}

    /* ---------- POWER / UPGRADES ---------- */
    body.desktop #upgradeList{display:grid;
      grid-template-columns:repeat(auto-fit,minmax(clamp(300px,23vw,380px),1fr));
      gap:14px;align-items:start}
    body.desktop .upRow{padding:16px 18px}
    body.desktop .upRow .upBuy{width:100%;margin-top:10px}
    /* P7 (5.3.1): MEMORY BREAKER is the only upgrade whose line names a
       FUTURE level ("Next change · level 5: …"), so its stat text is about
       twice the length of every sibling and ran to within 49-67px of the
       card edge while the others sat 157-378px clear. Capping the MEASURE
       lets that one line wrap where every other line is unaffected - the
       alternative, shrinking the type, would punish five cards to fix one.
       No text is truncated and no wording changed. */
    body.desktop .upRow .upStats{max-width:31ch}
    body.desktop .claimBtn.claimAll{grid-column:1/-1}

    /* ---------- SHOP ---------- */
    body.desktop .skinGrid{grid-template-columns:repeat(auto-fill,minmax(clamp(150px,11vw,196px),1fr));gap:14px}
    body.desktop .featHero{padding:20px;gap:22px;align-items:center}
    body.desktop .featHero canvas{width:clamp(130px,11vw,170px);height:clamp(130px,11vw,170px)}
    body.desktop .fhInfo b{font-size:clamp(18px,1.5vw,24px)}
    body.desktop .fhInfo span{font-size:11px}
    body.desktop #shopTabs{flex-wrap:wrap}

    /* ---------- RANKINGS ---------- */
    body.desktop #lbContent{max-width:1000px}
    body.desktop .lbRow{padding:12px 16px;gap:14px}
    body.desktop .lbRow canvas{width:38px;height:38px}
    body.desktop .lbRow .lbName{font-size:15px}
    body.desktop .lbRow .lbScore{font-size:20px}
    body.desktop #lbTabs{max-width:1000px}

    /* ---------- PROFILE dashboard ---------- */
    body.desktop .statBox{padding:16px 14px}
    body.desktop .statBox b{font-size:24px}

    /* ---------- SETTINGS ---------- */
    body.desktop #settingsList{display:grid;grid-template-columns:repeat(auto-fit,minmax(clamp(340px,26vw,440px),1fr));
      gap:14px;align-items:start}
    body.desktop .setSection{margin:0}
    body.desktop .setSection.wideSection{grid-column:1/-1}
    body.desktop .setRow{padding:12px 6px}

    /* ---------- CHALLENGES ---------- */
    body.desktop #chList{display:grid;grid-template-columns:repeat(auto-fill,minmax(clamp(280px,21vw,360px),1fr));
      gap:12px}
    body.desktop #chSummary{max-width:640px}

    /* ---------- HOW TO PLAY / COLLECTION ---------- */
    body.desktop .howList{columns:2;column-gap:22px}
    body.desktop .howList > *{break-inside:avoid;margin-bottom:8px}
    body.desktop .collGrid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}

    /* ---------- PAUSE + GAME OVER as centred desktop overlays ---------- */
    body.desktop #pauseScreen,body.desktop #endScreen,body.desktop #loadoutScreen,
    body.desktop #challengeScreen,body.desktop #imageScreen{
      align-items:center;padding-left:var(--shell-gutter);
      background:radial-gradient(circle at 50% 40%,rgba(12,16,48,.86),rgba(4,5,16,.93) 60%,rgba(3,4,12,.96))}
    body.desktop #pauseScreen .card,body.desktop #endScreen .card,
    body.desktop #loadoutScreen .card,body.desktop #challengeScreen .card,
    body.desktop #imageScreen .card{width:min(100%,660px);text-align:center}
    body.desktop #pauseScreen .screenTitle,body.desktop #endScreen .screenTitle{text-align:center}
    body.desktop #endScreen .card{width:min(100%,760px)}
    body.desktop .pauseKeys{display:flex;justify-content:center;gap:14px;flex-wrap:wrap;margin-top:12px;
      font-size:10px;font-weight:800;letter-spacing:.08em;color:var(--muted)}
    body.desktop .pauseKeys kbd{display:inline-block;padding:2px 7px;margin-right:5px;border-radius:6px;
      border:1px solid var(--line);background:rgba(255,255,255,.06);color:#dfe6ff;font-family:inherit}

    /* ---------- HUD on desktop ---------- */
    body.desktop #hud{padding:18px 24px}
    body.desktop .topbar .chip{padding:9px 14px}
    body.desktop .topbar .chip .value{font-size:22px}
    body.desktop .topbar .chip .label{font-size:8.5px}
    body.desktop #hudCenter{width:min(46vw,560px);top:14px}
    body.desktop #loopMeter{width:min(20vw,260px);height:8px}
    body.desktop #announce{min-height:34px}
    body.desktop #fsBtn{position:absolute;right:24px;top:auto;bottom:26px;width:46px;height:46px;
      display:flex;align-items:center;justify-content:center;border-radius:14px;
      border:1px solid var(--line);background:rgba(7,10,32,.7);color:var(--muted);
      font-size:17px;pointer-events:auto;backdrop-filter:blur(8px)}
    body.desktop #fsBtn:hover{border-color:rgba(120,248,255,.5);color:#eaffff}
    body.desktop #pauseBtn{width:46px;height:46px;font-size:17px}
    #ctrlHint{display:none}
    body.desktop #ctrlHint{display:block;position:absolute;left:50%;transform:translateX(-50%);
      bottom:18px;font-size:9.5px;font-weight:900;letter-spacing:.16em;color:rgba(174,184,216,.6);
      text-shadow:0 2px 10px #000;transition:opacity .5s;pointer-events:none}
    body.desktop #ctrlHint.fade{opacity:0}
    body.desktop #keysHint{display:none}

    /* ---------- boot loader ---------- */
    #bootLoader{position:fixed;inset:0;z-index:var(--z-boot);display:flex;align-items:center;justify-content:center;
      background:radial-gradient(circle at 50% 40%,#141a44,#080a1e 62%,#05060f);
      transition:opacity .45s ease,visibility .45s}
    #bootLoader.gone{opacity:0;visibility:hidden;pointer-events:none}
    .blInner{display:flex;flex-direction:column;align-items:center;gap:12px;text-align:center;padding:20px}
    .blLogo{font-size:clamp(34px,5vw,60px);font-weight:1000;letter-spacing:-.03em;
      background:linear-gradient(100deg,#fff 10%,var(--cyan) 55%,var(--pink));
      -webkit-background-clip:text;background-clip:text;color:transparent}
    .blTag{font-size:9px;font-weight:900;letter-spacing:.34em;color:var(--muted)}
    .blBar{width:min(58vw,260px);height:4px;border-radius:99px;background:rgba(255,255,255,.1);overflow:hidden;margin-top:6px}
    #blFill{height:100%;width:8%;border-radius:99px;background:linear-gradient(90deg,var(--cyan),var(--pink));
      transition:width .35s ease}
    .blTip{margin:6px 0 0;font-size:11px;color:var(--muted);font-weight:700;max-width:34ch}
    .blErr{display:none;flex-direction:column;gap:10px;align-items:center;margin-top:10px}
    .blErr b{font-size:12px;color:#ffb6c4}
    #bootLoader.stalled .blErr{display:flex}

    /* ---------- small-viewport gate ---------- */
    #smallGate{position:fixed;inset:0;z-index:var(--z-gate);display:none;align-items:center;justify-content:center;
      padding:22px;background:radial-gradient(circle at 50% 35%,#131a42,#070a1c 60%,#05060f)}
    body.smallGate #smallGate{display:flex}
    .sgCard{max-width:420px;text-align:center;display:flex;flex-direction:column;gap:12px;
      border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel-1);padding:24px 20px}
    .sgLogo{font-size:34px;font-weight:1000;letter-spacing:-.03em;
      background:linear-gradient(100deg,#fff 10%,var(--cyan) 55%,var(--pink));
      -webkit-background-clip:text;background-clip:text;color:transparent}
    .sgCard b{font-size:13px;letter-spacing:.02em}
    .sgCard p{margin:0;font-size:11.5px;color:var(--muted);line-height:1.55}
    .sgWhyText{display:none;border-top:1px solid var(--line);padding-top:12px}
    .sgCard.open .sgWhyText{display:block}

    /* ACCOUNT PROGRESS UNAVAILABLE (Phase 5.2.4L). Same gate layer and card as
       #smallGate - this is the other thing that must stop the game before it
       starts, so it looks and behaves like it rather than inventing a second
       vocabulary. */
    #acctGate{position:fixed;inset:0;z-index:var(--z-gate);display:none;align-items:center;justify-content:center;
      padding:22px;background:radial-gradient(circle at 50% 35%,#131a42,#070a1c 60%,#05060f)}
    body.acctGate #acctGate{display:flex}

    /* ---------- desktop density tuning ---------- */
    @media (min-width:1700px){
      body.desktop{--content-max:1560px}
      body.desktop .skinCard canvas{width:100px;height:100px}
    }
    @media (min-width:2200px){
      body.desktop{--nav-w:236px;--content-max:1720px}
      body.desktop .screenTitle{font-size:46px}
    }
    /* short laptops: tighten vertical rhythm so nothing is clipped */
    @media (max-height:800px){
      body.desktop .homeBody{min-height:0;padding-top:48px;gap:clamp(14px,1.4vw,28px)}
      body.desktop #heroStage{width:min(24vw,290px)}
      body.desktop #startBtn{padding:14px 24px;font-size:22px}
      body.desktop .dockCard{padding:11px 13px;gap:7px}
    }
    @media (max-height:700px){
      body.desktop #heroStage{width:min(20vw,230px)}
      body.desktop .heroPane .logo{font-size:clamp(30px,2.8vw,44px)}
      body.desktop .screen{padding-top:16px;padding-bottom:16px}
    }

    /* ---------- v5.3 home: controls card + vertical balance ---------- */
    .ctrlCard{display:none}
    body.desktop .ctrlCard{display:flex;flex-direction:column;gap:7px;margin-top:4px;
      border:1px solid var(--line);border-radius:var(--r-md);background:var(--panel-2);padding:12px 14px}
    body.desktop .ctrlCard h4{margin:0;font-size:8.5px;font-weight:1000;letter-spacing:.2em;color:var(--muted)}
    .ckRow{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:10px;font-weight:800;color:var(--muted)}
    .ckRow kbd{display:inline-block;min-width:22px;text-align:center;padding:3px 7px;border-radius:6px;
      border:1px solid var(--line);background:rgba(255,255,255,.07);color:#dfe6ff;
      font-family:inherit;font-size:9.5px;font-weight:900}
    body.desktop .homeBody{align-content:center;min-height:calc(100vh - 128px);padding-top:44px}
    body.desktop #heroStage{width:min(32vw,440px)}
    body.desktop .modeCard{padding:20px}
    body.desktop .modeCard .desc{font-size:12px;line-height:1.5}
    body.desktop .levelBar{width:min(30vw,380px)}

    /* ---------- v5.3 home: beat the ID-scoped mobile padding ---------- */
    body.desktop #homeScreen{
      padding:clamp(14px,1.8vh,26px) var(--shell-gutter) clamp(18px,2.2vh,32px)
              calc(var(--nav-w) + var(--shell-gutter));
      justify-content:flex-start;align-items:stretch}
    body.desktop .homeTop{position:static;display:flex;justify-content:flex-end;gap:10px;
      margin-bottom:2px;left:auto;right:auto;top:auto}
    body.desktop .homeTop .profileChip{margin-right:auto}
    /* the dock already shows level, XP and wallet - no duplicates on desktop */
    body.desktop .homeTop .curGroup{display:none}
    body.desktop .heroPane .levelBar{display:none}
    body.desktop .homeBody{min-height:calc(100vh - 116px)}

    /* ---------- v5.3 fixes from screenshot review ---------- */
    /* PROFILE: a real dashboard - identity | stats | records */
    body.desktop #profileScreen .card{display:grid;
      grid-template-columns:minmax(280px,340px) minmax(0,1fr) minmax(260px,320px);
      /* PHASE 5.2.3 FINAL: THE ACCOUNT IS PART OF THE PROFILE, NOT A FOOTNOTE.
         .profAccount had no area of its own, so the grid put it in an
         implicit row AFTER every named one - which meant the account section
         rendered BELOW the BACK button, half off the bottom of the page. That
         is literally the shape of "bolted on afterwards". It now sits under
         the identity it belongs to, and BACK ends the page as a terminal
         action should. */
      grid-template-areas:"title title title" "ident stats side" "acct stats side" "back back back";
      gap:clamp(14px,1.4vw,24px);align-content:start}
    body.desktop #profileScreen .screenTitle{grid-area:title}
    body.desktop .profIdentity{grid-area:ident;display:flex;flex-direction:column;gap:10px;
      border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel-1);padding:18px}
    body.desktop .profIdentity .small{margin:0;text-align:left}
    body.desktop .profIdentity input{width:100%}
    body.desktop #profileStats{grid-area:stats;margin:0;grid-template-columns:repeat(3,1fr);align-content:start}
    body.desktop .profSide{grid-area:side;display:flex;flex-direction:column;gap:12px}
    body.desktop #profileScreen .backBtn{grid-area:back;justify-self:start;width:auto;min-width:190px;margin-top:4px}
    body.desktop .profAccount{grid-area:acct;align-self:start;margin-top:0}
    .profSide{display:none}
    body.desktop .profSide{display:flex}
    body.desktop .profIdentity + #profileStats{margin-top:0}

    /* HUD: fullscreen sits with the top-right chips, never over PULSE */
    body.desktop #fsBtn{top:96px;bottom:auto;right:24px}
    body.desktop #hud{padding-bottom:34px}

    /* HEROES: denser, larger cards fill the width */
    body.desktop .charsLayout{grid-template-columns:minmax(300px,380px) 1fr}
    body.desktop #cdPreview{width:min(20vw,290px);height:min(20vw,290px)}
    body.desktop .charsLayout .skinGrid{
      grid-template-columns:repeat(auto-fill,minmax(clamp(158px,12vw,196px),1fr));gap:14px}
    body.desktop .skinCard canvas{width:104px;height:104px}

    /* SETTINGS: a dashboard reads better with several groups open */
    body.desktop #settingsList{grid-template-columns:repeat(auto-fit,minmax(clamp(330px,25vw,430px),1fr))}

    /* SHOP: featured hero reads art | story | price across the full width */
    body.desktop .fhInfo{display:grid;grid-template-columns:1fr auto;
      grid-template-rows:auto auto auto;align-items:center;gap:2px 24px;width:100%}
    body.desktop .fhInfo i{grid-column:1;grid-row:1}
    body.desktop .fhInfo b{grid-column:1;grid-row:2}
    body.desktop .fhInfo span:not(.priceTag){grid-column:1;grid-row:3}
    body.desktop .fhInfo .priceTag{grid-column:2;grid-row:1/span 3;justify-self:end;
      font-size:15px;padding:12px 22px;margin:0}

    /* A desktop app without navigation looks broken, so the sidebar always
       exists; progressive disclosure hides the ADVANCED destinations only. */
    body.desktop.onb0 #menuNav,body.desktop.onb1 #menuNav{display:flex}
    body.desktop.onb0 .navBtn[data-nav="challenges"],
    body.desktop.onb0 .navBtn[data-nav="profile"],
    body.desktop.onb1 .navBtn[data-nav="profile"]{display:none}
    body.desktop.onb0 .navGroup.deskOnly{border-top:0;margin-top:4px;padding-top:4px}

    /* ---------- v5.3 large-display tier: use the pixels, don't float in them ----------
       On 1440p+ the composition scales up as a whole (type, controls, panels,
       character stage) instead of leaving a small phone-sized island in the
       middle of a large monitor. */
    @media (min-width:1800px) and (min-height:1000px){
      body.desktop{--nav-w:250px;--shell-gutter:clamp(28px,2.4vw,60px);--content-max:1760px}
      body.desktop .navBrand b{font-size:23px}
      body.desktop .navBtn{min-height:50px;padding:11px 15px}
      body.desktop .navBtn i{font-size:18px;width:24px}
      body.desktop .navBtn span{font-size:13px}
      body.desktop .navMini{min-height:46px;font-size:11.5px}
      body.desktop .heroPane .logo{font-size:clamp(58px,4.2vw,86px)}
      body.desktop .heroPane .tag{font-size:15px;letter-spacing:.34em}
      body.desktop #heroStage{width:min(30vw,560px)}
      body.desktop #startBtn{font-size:clamp(30px,2.2vw,44px);padding:26px 34px;border-radius:24px}
      body.desktop .modeCard{padding:26px 28px}
      body.desktop .modeCard b{font-size:32px}
      body.desktop .modeCard .desc{font-size:15px}
      body.desktop .loadoutBtn{font-size:14px;padding:16px}
      body.desktop .dockCard{padding:18px 22px;gap:12px}
      body.desktop .dockCard h4{font-size:11px}
      body.desktop .dockRow{font-size:14px}
      body.desktop .dockRow b{font-size:18px}
      body.desktop .dockBig b{font-size:42px}
      body.desktop .ctrlCard{padding:16px 18px}
      body.desktop .ckRow{font-size:12.5px}
      body.desktop .ckRow kbd{font-size:11.5px;padding:4px 9px}
      body.desktop .homeLinks .miniLink{font-size:12.5px}
      body.desktop .screenTitle{font-size:clamp(42px,3vw,56px)}
      body.desktop .statBox b{font-size:32px}
      body.desktop .statBox span{font-size:10px}
      body.desktop .skinCard b{font-size:14px}
      body.desktop .skinCard span{font-size:10.5px}
      body.desktop .lbRow .lbName{font-size:17px}
      body.desktop .lbRow .lbScore{font-size:24px}
      body.desktop .setHead{font-size:13.5px;padding:15px 18px}
      body.desktop .setRow label{font-size:14px}
      body.desktop .setRow .sub{font-size:11px}
      body.desktop .upRow{padding:20px 22px}
      body.desktop .topbar .chip .value{font-size:26px}
    }
    /* keep the composition anchored rather than floating in the middle */
    body.desktop .homeBody{align-content:center;justify-items:stretch}

    /* ---------- v5.3-rc2: gameplay is edge to edge ----------
       The sidebar must vanish during a run. This rule comes last so it also
       beats the onboarding rules that force the sidebar visible on menus. */
    body.desktop #menuNav.hidden{display:none !important}

    /* =====================================================================
       v5.3-rc2 — targeted desktop layout fixes from the 1080p playtest
       ===================================================================== */

    /* ---------- RANKINGS: list + real support column ---------- */
    body.desktop #lbContent{max-width:none}
    body.desktop .lbLayout{display:grid;grid-template-columns:minmax(0,69fr) minmax(280px,31fr);
      gap:clamp(16px,1.6vw,30px);align-items:start}
    body.desktop .lbMain{min-width:0}
    body.desktop .lbSide{display:flex;flex-direction:column;gap:12px;position:sticky;top:0}
    .lbSide{display:none}
    body.desktop .lbSide .dockEmpty{margin-top:2px;line-height:1.5}
    body.desktop #lbTabs{max-width:none}

    /* ---------- MY STATS: grouped dashboard ---------- */
    .statGroups{display:flex;flex-direction:column;gap:14px}
    .statGroup{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel-1);
      padding:14px 16px 16px}
    .statGroup h4{margin:0 0 10px;font-size:9px;font-weight:1000;letter-spacing:.2em;color:var(--muted)}
    .statGroup .statList{margin:0}
    body.desktop .statGroups{display:grid;align-items:start;
      grid-template-columns:repeat(auto-fit,minmax(clamp(300px,23vw,380px),1fr));
      gap:clamp(14px,1.4vw,22px)}
    body.desktop .statGroup .statList{grid-template-columns:repeat(2,1fr);gap:10px}
    body.desktop .statGroup h4{font-size:10px}

    /* ---------- HOW TO PLAY: grouped cards, no monitor-wide lines ---------- */
    .howGroups{display:flex;flex-direction:column;gap:12px}
    .howGroup{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel-1);padding:14px 16px 16px}
    .howGroup h4{margin:0 0 4px;font-size:9.5px;font-weight:1000;letter-spacing:.2em;color:var(--cyan)}
    .howNote{margin:0 0 8px;font-size:10.5px;color:var(--muted);font-weight:700}
    .howItems{display:flex;flex-direction:column;gap:8px}
    body.desktop .howGroups{display:grid;align-items:start;
      grid-template-columns:repeat(auto-fit,minmax(clamp(330px,26vw,460px),1fr));
      gap:clamp(14px,1.4vw,24px)}
    body.desktop .howGroup.wide{grid-column:1/-1}
    body.desktop .howGroup.wide .howItems{display:grid;
      grid-template-columns:repeat(auto-fit,minmax(clamp(280px,21vw,360px),1fr));gap:10px}
    body.desktop .howRow{max-width:none}
    body.desktop .howRow span{font-size:11.5px;line-height:1.55}
    body.desktop #howScreen .card{width:min(100%,var(--content-max))}
    body.desktop #tutorialBtn{max-width:360px;margin-top:16px}

    /* ---------- UPGRADES: balanced 3x2, vertically centred ---------- */
    body.desktop #upgradesScreen .card{min-height:calc(100vh - 150px);display:flex;
      flex-direction:column;justify-content:flex-start}
    /* header stays at the top; the six cards sit centred in what is left */
    body.desktop #upgradeList{grid-template-columns:repeat(3,minmax(0,1fr));
      gap:clamp(14px,1.5vw,26px);max-width:1300px;width:100%;margin:auto auto}
    body.desktop .upRow{padding:20px 22px}
    body.desktop .upRow .upHead b{font-size:15px}
    body.desktop .upRow .upIc{width:38px;height:38px;font-size:17px}
    body.desktop .upRow .upStats{font-size:12px;line-height:1.6;margin-top:8px}
    body.desktop .upRow .upBuy{min-height:44px;font-size:12px}
    body.desktop .upRow:hover{border-color:rgba(120,248,255,.36);background:rgba(255,255,255,.06)}
    body.desktop .claimBtn.claimAll{max-width:1300px;margin:14px auto 0;width:100%}
    body.desktop #upgradesScreen .screenTitle,
    body.desktop #upgradesScreen .card > .small{flex:none}
    @media (max-width:1500px){
      body.desktop #upgradeList{grid-template-columns:repeat(2,minmax(0,1fr))}
    }

    /* ---------- GAME OVER: a little more presence, two columns ---------- */
    body.desktop #endScreen .card{width:min(100%,1020px)}
    body.desktop #endScreen .logo,body.desktop #endScreen .endTitle{font-size:clamp(46px,4vw,64px)}
    body.desktop #endScreen .endHero{transform:scale(1.06);transform-origin:center top}
    body.desktop .endBtnRow.two{gap:14px}
    body.desktop #againBtn{font-size:clamp(22px,1.7vw,30px);padding:20px;min-height:64px}
    body.desktop #endScreen .pauseKeys{margin-top:14px}

    /* ---------- CUSTOMIZE: slightly larger category chips ---------- */
    body.desktop #customScreen .catTab{padding:9px 15px;font-size:11px;letter-spacing:.09em}
    body.desktop #customScreen .filterRow .catTab,
    body.desktop #customScreen .filterRow button{padding:7px 13px;font-size:10.5px}
    body.desktop #customScreen .catTabs{gap:7px}
    body.desktop #customScreen .filterRow{gap:7px;margin-top:2px}

    /* ---------- SETTINGS: keyboard caps + balanced columns ---------- */
    .keyCaps{display:flex;gap:5px;flex-wrap:wrap;justify-content:flex-end}
    .keyCaps kbd{display:inline-block;min-width:26px;text-align:center;padding:5px 9px;border-radius:7px;
      border:1px solid var(--line);background:rgba(255,255,255,.07);color:#dfe6ff;
      font-family:inherit;font-size:10.5px;font-weight:900;letter-spacing:.04em}
    /* masonry-style packing: a tall panel (Keyboard & Mouse) must not leave a
       blank column beside it, and accordions still expand/collapse freely */
    /* explicit three columns with dense packing: short panels fill the gaps
       left by a taller neighbour, and accordions still open/close freely */
    /* three independent columns: a tall open panel cannot leave a hole under
       its short neighbours, and accordion behaviour is untouched */
    body.desktop #settingsList.setColumns{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
      gap:clamp(14px,1.3vw,22px);align-items:start}
    body.desktop .setCol{display:flex;flex-direction:column;gap:clamp(12px,1.1vw,16px);min-width:0}
    body.desktop .setSection{margin:0}
    @media (max-width:1500px){
      body.desktop #settingsList.setColumns{grid-template-columns:repeat(2,minmax(0,1fr))}
    }
    @media (max-width:1100px){
      body.desktop #settingsList.setColumns{grid-template-columns:1fr}
    }
    body.desktop #settingsScreen .card{width:min(100%,var(--content-max))}

    /* ---------- HEROES: consistent card heights + selected state ---------- */
    body.desktop .charsLayout .skinGrid{align-items:stretch}
    body.desktop .skinCard{display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
      height:100%}
    body.desktop .skinCard span{margin-top:auto}
    body.desktop .skinCard.selected{border-color:rgba(120,248,255,.7)}

    /* =====================================================================
       v5.3-rc3 — HUD hazard visibility
       While a lethal entity passes behind a HUD element the element drops its
       plate and blur so the danger is never hidden. Text and icons stay fully
       opaque and pick up a shadow, so readability survives. Nothing about
       collisions, movement or the world changes.
       ===================================================================== */
    #hud .chip,#loopMeter,#announce,#buffBar,#pauseBtn,#fsBtn,#pulseBtn{
      transition:background-color .16s ease,border-color .16s ease,
                 box-shadow .16s ease,backdrop-filter .16s ease}
    .hudClear{backdrop-filter:none !important;box-shadow:none !important}
    /* the plate nearly disappears AND the border turns to the danger colour,
       so the player reads "something lethal is behind this panel" instantly */
    #hud .chip.hudClear{background:rgba(5,8,28,.05);border-color:rgba(255,92,122,.75);
      box-shadow:0 0 12px rgba(255,92,122,.25) !important}
    #hud .chip.hudClear .label{color:#e8eeff;text-shadow:0 1px 4px #000,0 0 10px #000}
    #hud .chip.hudClear .value{text-shadow:0 1px 5px #000,0 0 12px #000}
    #announce.hudClear{background:rgba(4,6,20,.10);border-color:rgba(255,92,122,.6)}
    #announce.hudClear b,#announce.hudClear span{text-shadow:0 1px 5px #000,0 0 12px #000}
    #loopMeter.hudClear{background:rgba(0,0,0,.12)}
    #buffBar.hudClear .buffPill{background:rgba(4,6,20,.18);text-shadow:0 1px 4px #000}
    #pauseBtn.hudClear,#fsBtn.hudClear{background:rgba(7,10,32,.08);border-color:rgba(255,92,122,.7);
      text-shadow:0 1px 5px #000,0 0 12px #000}
    #pulseBtn.hudClear{background:radial-gradient(circle at 35% 25%,rgba(255,255,255,.16),
      rgba(120,248,255,.08) 30%,rgba(255,102,212,.05) 62%,rgba(5,7,25,.18));
      border-color:rgba(255,92,122,.6);text-shadow:0 1px 5px #000,0 0 12px #000}
    /* High Contrast keeps a readable plate: outline instead of transparency */
    body.hc #hud .chip.hudClear,body.hc #announce.hudClear,
    body.hc #pauseBtn.hudClear,body.hc #fsBtn.hudClear{
      background:rgba(0,0,0,.62);border-color:#fff;border-width:2px}
    /* Reduced Motion: no fade, just switch */
    body.rm #hud .chip,body.rm #announce,body.rm #loopMeter,body.rm #buffBar,
    body.rm #pauseBtn,body.rm #fsBtn,body.rm #pulseBtn{transition:none}

    /* v5.3-rc3: HUD typography scales on large monitors (readability only) */
    @media (min-width:1600px){
      body.desktop .topbar .chip{padding:10px 15px}
      body.desktop .topbar .chip .label{font-size:9.5px}
      body.desktop .topbar .chip .value{font-size:25px}
      body.desktop #loopText{font-size:9.5px;letter-spacing:.18em}
      body.desktop #announce b{font-size:15px}
      body.desktop #announce span{font-size:11px}
      body.desktop #ctrlHint{font-size:10.5px}
    }
    @media (min-width:2200px){
      body.desktop .topbar .chip .value{font-size:29px}
      body.desktop .topbar .chip .label{font-size:10.5px}
      body.desktop #announce b{font-size:17px}
    }

    /* ---------- v5.3-rc3: Challenges composition ---------- */
    .chSide{display:none}
    body.desktop .chSide{display:flex;flex-direction:column;gap:12px;grid-area:side}
    /* the summary column is absolutely placed so a tall panel can never
       stretch the rows of the challenge list beside it */
    body.desktop #challengesScreen .card{position:relative;
      padding-right:calc(330px + clamp(18px,1.8vw,34px))}
    body.desktop .chSide{position:absolute;top:0;right:0;width:330px;grid-area:auto}
    body.desktop #chSummary{max-width:none}
    body.desktop #chTabs{max-width:560px}
    body.desktop #chTimer{margin:2px 0 8px}
    body.desktop #claimAllBtn{max-width:560px}
    /* one wide, readable card per challenge instead of three cramped columns */
    body.desktop #chList{display:flex;flex-direction:column;gap:12px}
    /* taller, easier-to-scan challenge cards */
    body.desktop #chList .chRow{padding:15px 20px 14px}
    body.desktop #chList .chRow b{font-size:14.5px;letter-spacing:.02em}
    body.desktop #chList .chBar{height:7px;margin:9px 0 10px}
    /* progress + reward on the left, action pinned to the right edge */
    body.desktop #chList .chFoot{display:flex;align-items:center;justify-content:space-between;
      gap:18px;width:100%}
    body.desktop #chList .chFoot > span{font-size:11.5px;display:flex;align-items:center;gap:10px}
    body.desktop #chList .chFoot .rew{color:var(--gold);font-weight:900;
      padding:4px 11px;border-radius:99px;border:1px solid rgba(255,213,106,.35);
      background:rgba(255,213,106,.09)}
    body.desktop #chList .claimBtn{min-width:170px;min-height:44px;font-size:11.5px}
    body.desktop #chList .chRow.done{border-color:rgba(255,213,106,.4)}
    body.desktop #chList .chRow.quiet{opacity:.5}
    body.desktop .chSide .dcName{font-size:12px;font-weight:900;color:#dfe6ff}

    /* ---------- v5.3-rc3: Profile reads as one dashboard ---------- */
    body.desktop #profileScreen .card{align-items:start}
    body.desktop .profIdentity{align-self:start;gap:12px;align-items:stretch;padding:20px}
    body.desktop #profAvatar{width:100%;max-width:180px;aspect-ratio:1;margin:0 auto;
      border-radius:var(--r-md);background:rgba(4,6,20,.45);border:1px solid var(--line)}
    body.desktop .profWho{display:flex;flex-direction:column;gap:3px;text-align:center}
    body.desktop .profWho b{font-size:17px;letter-spacing:.06em}
    body.desktop .profWho .small{margin:0;text-align:center;font-size:10.5px}
    body.desktop .profLevel{display:flex;flex-direction:column;gap:5px;
      padding:12px 0 2px;border-top:1px solid rgba(255,255,255,.08)}
    body.desktop .profLevel .dockRow b{font-size:20px}
    body.desktop .profLevel .sub{text-align:right;font-size:10px;color:var(--muted);font-weight:800}
    body.desktop .profIdentity .small{margin:0}
    body.desktop .profIdentity input{width:100%;text-align:center}
    /* stat tiles and the records column share one rhythm */
    body.desktop #profileStats{gap:clamp(12px,1.1vw,18px);align-self:start}
    body.desktop #profileStats .statBox{display:flex;flex-direction:column;justify-content:center;
      min-height:104px}
    body.desktop .profSide{align-self:start}
    body.desktop .profSide .dockCard{padding:16px 18px}
    #profAvatar{display:none}
    body.desktop #profAvatar{display:block}
    .profWho,.profLevel{display:none}
    body.desktop .profWho{display:flex}
    body.desktop .profLevel{display:flex}

    /* v5.3-rc3: phone keeps the old gap; desktop aligns the three columns */
    #profileStats{margin-top:12px}
    body.desktop #profileStats{margin-top:0}

    /* ---------- v5.3-rc3: Run Details reads as a summary, not a poster ----------
       The route map is a 320x180 canvas; letting it stretch to the full card
       width blew it up to ~590px tall and left a large empty region. It now
       keeps its own scale and the details flow stays compact. */
    body.desktop #routeMapWrap{max-width:560px;margin:10px auto;padding:12px}
    body.desktop #routeMap{max-width:100%}
    body.desktop #runDetails .statList{grid-template-columns:repeat(4,1fr);gap:12px}
    body.desktop #runDetails .statBox{min-height:78px}
    body.desktop #runDetails{display:flex;flex-direction:column;gap:10px}
    body.desktop #runDetails .chRow{padding:12px 16px}
    body.desktop .mapLegend{font-size:10px;opacity:.75}

    /* ---------- v5.3-rc3: Shop stays composed with small rotations ----------
       On very wide monitors a full-width featured bar left a long empty middle
       and the item cards looked lost. The shop keeps a sensible content width
       and grows its cards instead. No items were added. */
    @media (min-width:1800px){
      /* PHASE 5.2.3: the width pin is GONE. It made the Shop card 1520px while
         every other screen used --content-max, so moving between tabs shifted
         the content edge by 29px and the whole product appeared to change
         scale - measured across all ten menu tabs, Shop was the only one that
         did not start at x=296. The problem it was solving (a long empty
         middle in the featured bar) was solved properly by giving that bar a
         real height and a 240px preview; it does not need the card narrowed
         as well. The card grows its cards instead, which is what the rest of
         this block already does. */
      body.desktop #shopScreen .skinGrid{
        grid-template-columns:repeat(auto-fill,minmax(clamp(180px,12vw,238px),1fr));gap:16px}
      body.desktop #shopScreen .skinCard canvas{width:118px;height:118px}
      body.desktop #shopScreen .featHero{padding:24px 28px}
    }
    /* the featured hero keeps its art, story and price visually connected */
    body.desktop .featHero{max-width:1520px}
    body.desktop .fhInfo{grid-template-columns:minmax(0,1fr) auto;max-width:1100px}

    /* =====================================================================
       PHASE 5.2.3 FINAL — DESKTOP SCALE AND NOTIFICATION PLACEMENT

       The audit measured the same defect on eight screens: at 1920x1080 the
       content stopped between 55% and 75% of the way down and the rest of the
       viewport was empty. Home was the exception - it had been given a real
       desktop composition and the others had not.

       Nothing below invents content to fill space. Where a page has room, the
       things already on it get bigger and reach the edge the header already
       reaches. Where it does not, the page keeps a deliberate bottom gutter so
       it ends rather than trails off.
       ===================================================================== */

    /* ---------- WHERE A NOTIFICATION LANDS ----------
       The toast was fixed at top-centre with no desktop rule at all. At 1366
       that put it straight through the Shop tab strip and across the Power
       subtitle - covering CONTROLS, which is the one thing a transient overlay
       must never do. It moves to the foot of the content column, clear of
       every header, in the band this stylesheet now reserves. */
    body.desktop #toast{
      top:calc(14px + var(--safe-top));
      left:calc(var(--nav-w) + var(--shell-gutter));right:var(--shell-gutter);
      transform:translateY(-10px);
      width:max-content;max-width:min(560px,46vw);margin:0 auto;
      font-size:12.5px;padding:11px 20px}
    body.desktop #toast.show{transform:translateY(0)}
    /* In a run there is no sidebar and no content column - the arena is the
       whole window - so the toast re-centres on it and drops below the loop
       meter and the announcement rail rather than landing on top of them. */
    body.desktop.inRun #toast{left:0;right:0;top:calc(96px + var(--safe-top))}

    /* ---------- POWER: content instead of a void ----------
       #upgradeList carried "margin:auto auto" inside a full-height flex
       column, so the only content on the page was vertically centred between a
       ~180px gap under the header and a ~285px gap beneath it. The cards now
       start where the header ends and grow to fill the height they were
       floating in, and the grid shares the header right edge instead of
       stopping 210px short of it. */
    body.desktop #upgradesScreen .card{justify-content:flex-start;gap:14px}
    body.desktop #upgradeList{max-width:none;margin:0;flex:1;
      grid-auto-rows:minmax(190px,1fr);align-content:stretch;align-items:stretch}
    body.desktop .claimBtn.claimAll{max-width:none;margin:6px 0 0;flex:none}
    body.desktop .upRow{display:flex;flex-direction:column;justify-content:space-between}
    body.desktop .upRow .upBuy{margin-top:auto}
    @media (min-width:1600px){
      body.desktop #upgradeList{gap:clamp(18px,1.6vw,30px)}
      body.desktop .upRow{padding:26px 28px}
      body.desktop .upRow .upHead b{font-size:17px}
      body.desktop .upRow .upIc{width:46px;height:46px;font-size:20px}
      body.desktop .upRow .upStats{font-size:13px;line-height:1.75;margin-top:12px}
      body.desktop .upRow .upBuy{min-height:52px;font-size:13px}
    }
    /* the bulk action is a different KIND of action from the six beside it,
       and was drawn as a seventh identical yellow slab */
    body.desktop .claimBtn.claimAll{background:transparent;color:var(--gold);
      border:1px solid rgba(255,213,106,.5);box-shadow:none;font-size:12px;min-height:46px}
    body.desktop .claimBtn.claimAll:active{background:rgba(255,213,106,.12)}
    @media (hover:hover){
      body.desktop .claimBtn.claimAll:hover{background:rgba(255,213,106,.1);
        border-color:rgba(255,213,106,.8)}
    }

    /* ---------- SHOP: one right edge, not two ----------
       .skinGrid is auto-fill, which lays out SEVEN tracks in the 1510px
       content area. The featured rotation always has exactly five items, so
       two tracks stayed empty and the row ended at 5/7 of the width while the
       banner above it spanned the whole of it. The rotation gets its own
       column count; every other tab keeps auto-fill, because those tabs have
       enough items to fill it. */
    body.desktop #shopScreen .skinGrid.rotation{
      grid-template-columns:repeat(5,minmax(0,1fr))}
    @media (min-width:1600px){
      body.desktop #shopScreen .skinGrid.rotation canvas{width:150px;height:150px}
      body.desktop #shopScreen .skinGrid.rotation b{font-size:14px}
      body.desktop #shopScreen .featHero canvas{width:190px;height:190px}
      body.desktop #shopScreen .featHero{padding:28px 32px}
    }
    body.desktop #shopContent > p.small{margin:2px 0 12px}

    /* ---------- HEROES / STYLE: the grid uses the height it has ---------- */
    @media (min-width:1600px){
      body.desktop #charsScreen .skinGrid,body.desktop #customScreen .skinGrid{
        grid-template-columns:repeat(auto-fill,minmax(clamp(170px,12vw,215px),1fr));gap:16px}
      body.desktop #charsScreen .skinCard canvas,
      body.desktop #customScreen .skinCard canvas{width:128px;height:128px}
      body.desktop #charsScreen .skinCard b,
      body.desktop #customScreen .skinCard b{font-size:13.5px}
    }

    /* ---------- CHALLENGES: a row is a card, not a rule across the page ----
       Rows were 1210px wide holding about 40px of content, with the title at
       the far left and the status 1100px away at the far right. The measure is
       capped so a challenge reads as one object. */
    body.desktop #chList{max-width:1180px}
    body.desktop #chList .chRow{padding:18px 22px 16px}
    /* P2 (5.3.1): the ROW measure is capped above, deliberately, so a
       challenge reads as one object. The track had a SECOND, narrower cap
       of its own, so inside that card the bar stopped 374px short of the
       content edge at 1920 and 151px at 1600 - the card read unfinished
       while the numbers under it were correct. The row cap stays; the bar
       now spans the content it belongs to. Fill width is untouched: it is
       set inline from real progress. */
    body.desktop #chList .chBar{max-width:none}

    /* ---------- PROFILE ----------
       SAVE NAME was the brightest thing on the page - a full-width
       cyan-to-gold gradient - for a secondary action, out-competing the
       player level, every stat tile and both record cards. */
    /* TRIED AND REJECTED: making the six stat tiles fill the height of the
       identity card and the records rail beside them. Two columns of three
       stretched tiles DID end the three columns level with each other, and it
       looked worse - every tile became a 185px box with one number in the
       corner of it. A page that ends early is better than one padded with
       empty boxes, so the tiles keep their proportions and the column keeps
       its natural length. Recorded in PHASE-5.2.3-REMAINING-GAPS.md (D). */
    body.desktop #profileStats{align-self:start;align-content:start}
    body.desktop #profileSave{background:transparent;color:var(--cyan);
      border:1px solid rgba(120,248,255,.4);box-shadow:none;
      font-size:11.5px;min-height:42px;padding:10px 16px}
    body.desktop #profileSave:active{background:rgba(120,248,255,.12)}
    @media (hover:hover){
      body.desktop #profileSave:hover{background:rgba(120,248,255,.1);
        border-color:rgba(120,248,255,.75)}
    }

    /* ---------- BACK reads as the page exit, on both screens that have one
       On Profile it sat mid-page with ~340px of empty space around it; on
       Settings it was left-aligned under the first column and read as part of
       the GRAPHICS card above it. */
    body.desktop #settingsScreen [data-back],body.desktop #profileScreen [data-back]{
      margin:18px auto 0;min-width:220px;max-width:320px;display:block}

    /* ---------- SETTINGS: three columns that finish near each other ----------
       Column 3 was five collapsed headers and about 90% empty while column 1
       ran 750px tall. Dense packing lets a short panel fill the gap beside a
       tall one instead of leaving a hole. */
    @media (min-width:1500px){
      body.desktop #settingsList.setColumns{grid-auto-flow:row dense}
    }

    /* ---------- RESULT: the unlock strip keeps one baseline ----------
       BEYOND MEMORY wraps to two lines and pushed its reward below the other
       five. The name gets exactly two lines of room so every reward lands on
       the same line whether the name wraps or not. */
    .unlockCard{display:grid;grid-template-rows:46px 2.3em auto;
      align-items:center;justify-items:center;gap:2px;width:78px}
    .unlockCard b{align-self:center;line-height:1.15}
    /* six identical typed trophies said nothing about what was earned, on a
       screen where every other icon in the game is drawn. This is the badge
       shape Power already uses for an upgrade. */
    .uAch{display:flex;align-items:center;justify-content:center;
      width:44px;height:44px;border-radius:50%;font-size:19px;font-weight:900;
      color:var(--gold);border:1px solid rgba(255,213,106,.55);
      background:rgba(255,213,106,.1);box-shadow:0 0 14px rgba(255,213,106,.2)}
    @media (min-width:1600px){
      body.desktop #routeMapWrap{max-width:720px}
    }

    /* ---------- RANKS: an empty board looks like a board ---------- */
    /* .lbEmpty centres its children, which left the ten places as a narrow
       column in the middle of a wide panel. They are rows of a board, so they
       take the board's width. */
    .lbSlots{display:flex;flex-direction:column;gap:6px;margin-top:16px;
      width:100%;max-width:640px;text-align:left}
    .lbSlot{display:flex;align-items:center;gap:12px;padding:11px 16px;
      border-radius:12px;border:1px dashed rgba(255,255,255,.1);
      background:rgba(255,255,255,.016)}
    .lbSlot i{font-style:normal;width:26px;flex:none;text-align:center;
      font-size:11px;font-weight:900;color:rgba(174,184,216,.45)}
    .lbSlot span{flex:1;font-size:11px;font-weight:800;color:rgba(174,184,216,.4);
      letter-spacing:.06em}
    body.desktop .lbSlots{max-width:760px}

    /* ================================================================
       PHASE 5.2.3 — FINAL PROFESSIONAL POLISH PASS
       ================================================================

       THE ONE PROBLEM THE AUDIT KEPT FINDING. Photographed at 1600x900,
       HOME and POWER fill the shell and every other screen stops between
       200 and 400 pixels short of the bottom - HEROES, RANKS, CHALLENGES,
       SHOP and PROFILE all top-anchor their content and leave the rest of
       the viewport empty. It is the same defect five times, and it is why
       those screens read as weaker than HOME rather than as different.

       The cause is one rule: body.desktop .screen uses align-items:flex-start
       and .card has no height, so a short screen simply sits at the top.
       POWER does not have the problem because #upgradesScreen .card already
       sets min-height:calc(100vh - 150px) and lays out as a flex column.

       So this extends THAT pattern rather than inventing another one. The
       card fills the shell, the header stays at the top, and the region that
       should absorb the leftover height is named per screen. Nothing is
       stretched for the sake of it - what grows is the content region that
       has somewhere to put the space. */

    /* align-items:stretch rather than a magic min-height: the card then fills
       exactly the padded area the shell gives it, at any viewport, instead of
       guessing at 150px of chrome and leaving a hundred of them over. */
    body.desktop #charsScreen,
    body.desktop #lbScreen,
    body.desktop #challengesScreen,
    body.desktop #shopScreen,
    body.desktop #profileScreen{align-items:stretch}
    body.desktop #charsScreen .card,
    body.desktop #lbScreen .card,
    body.desktop #challengesScreen .card,
    body.desktop #shopScreen .card{display:flex;flex-direction:column}
    body.desktop #charsScreen .screenTitle,
    body.desktop #lbScreen .screenTitle,
    body.desktop #challengesScreen .screenTitle,
    body.desktop #shopScreen .screenTitle,
    body.desktop #charsScreen .card > .small,
    body.desktop #lbScreen .card > .small,
    body.desktop #shopScreen .card > .small,
    body.desktop #challengesScreen .card > .small,
    body.desktop #lbScreen .tabs,
    body.desktop #challengesScreen .tabs,
    body.desktop #shopScreen .tabs,
    body.desktop #chSummary,body.desktop #chTimer{flex:none}

    /* HEROES: the preview column is short and the grid is long, so the grid
       takes the height and the preview stays with it rather than stranding a
       third of the screen under a 400px panel. */
    body.desktop .charsLayout{flex:1;min-height:0;align-items:stretch}
    /* THE PREVIEW TAKES THE COLUMN IT IS IN. It was a 400px panel at the top
       of a 900px column, which is where a third of this screen's dead space
       came from - and the hero being previewed is the reason to be on the
       page, so the space is better spent on it than left under it. */
    body.desktop #charDetail{position:sticky;top:0;align-self:stretch;
      display:flex;flex-direction:column;justify-content:center;gap:14px}
    body.desktop #charDetail canvas{width:100%;max-width:300px;height:auto;
      aspect-ratio:1;margin:0 auto}
    body.desktop .charsLayout .skinGrid{align-content:start}

    /* RANKS: the board is the page, so it takes the room. */
    body.desktop #lbContent{flex:1;display:flex;flex-direction:column;min-height:0}
    body.desktop #lbContent > .lbWrap,
    body.desktop #lbContent > .lbLayout{flex:1;min-height:0}

    /* CHALLENGES: three cards in a 900px column were sitting in the top
       third. They get room to breathe instead of being stretched. */
    body.desktop #chList{flex:1;justify-content:flex-start;gap:clamp(12px,1.6vh,22px)}
    /* The rows take the room rather than the container holding it empty
       beneath them - capped, because a challenge is a title, a bar and a
       reward and there is a height past which that is just a tall box. */
    body.desktop #chList .chRow{padding:18px 22px 17px;
      /* The cap is viewport-relative, not a fixed 152px. A flat cap is right
         at 900px tall and wrong at 1080, where it handed back three hundred
         pixels of the gap this rule exists to close - the second QA pass
         caught it at 1920x1080. */
      flex:1 1 auto;max-height:clamp(140px,18vh,208px);
      display:flex;flex-direction:column;justify-content:center}
    /* AND THE RAIL RUNS THE FULL HEIGHT. Three cards cannot fill a nine
       hundred pixel column without being inflated into tall empty boxes, so
       the page is squared off by the column that CAN carry it: the summary
       rail becomes a real second column with its panels distributed down it,
       and the slack that is left under the list reads as breathing room below
       a finished list rather than as an unfinished page. */
    body.desktop .chSide{position:absolute;top:0;right:0;bottom:0;width:330px;
      display:flex;flex-direction:column;gap:clamp(12px,1.6vh,22px)}
    body.desktop .chSide > *{flex:0 0 auto}
    body.desktop .chSide > *:last-child{margin-bottom:0}

    /* SHOP: the rotation grid absorbs the height under the feature banner. */
    body.desktop #shopContent{flex:1;display:flex;flex-direction:column;min-height:0}
    body.desktop #shopContent .skinGrid{align-content:start}

    /* ---------- PROFILE: the hole in the middle ----------
       The grid was align-content:start with a full-width BACK row at the
       bottom, so on a tall viewport the stats column ended a third of the way
       down and BACK floated alone in the empty half beneath it. The rows now
       take the height they need and BACK ends the identity column, where it
       reads as the end of a page rather than as a button adrift in space. */
    /* TWO COLUMNS, NOT THREE. The three-column version put six stat tiles in
       the middle and two panels on the right, and neither had enough in it to
       reach the bottom - so the widest part of the page was a hole with a
       BACK button floating in it. Identity, account and BACK now own the left
       column and read as one block; the right column carries the numbers,
       with the two record panels side by side underneath the tiles so it
       fills rather than trails off. */
    body.desktop #profileScreen .card{
      grid-template-columns:minmax(300px,360px) minmax(0,1fr);
      /* IDENTITY SPANS. Sharing one row track with the stat tiles made that
         track as tall as the identity card - 530px - so the tiles sat at the
         top of it and left a band of nothing underneath before the record
         panels could begin. Spanning lets the right column stack
         continuously instead of inheriting the left column's height. */
      grid-template-areas:"title title" "ident stats" "ident side" "acct  side" "back  side";
      grid-template-rows:auto auto auto auto 1fr;align-content:stretch}
    body.desktop #profileScreen .backBtn{grid-area:back;align-self:start;
      justify-self:stretch;width:100%;min-width:0;margin-top:2px}
    body.desktop #profileStats{align-content:start}
    body.desktop .profSide{grid-area:side;display:grid;align-content:start;
      grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(12px,1.2vw,20px)}
    body.desktop .profSide > *{margin:0}

    /* ---------- states that were reading as the wrong thing ---------- */

    /* A PROGRESS BAR AT 0% IS STILL A PROGRESS BAR. The track was faint
       enough that an untouched challenge looked like a hairline rule rather
       than something with a value in it, which is the difference between "not
       started" and "not implemented". */
    .chBar{background:rgba(255,255,255,.13);
      box-shadow:inset 0 0 0 1px rgba(255,255,255,.05)}

    /* "IN PROGRESS" IS A STATUS, NOT A BUTTON. It shared the claim button's
       shape, so an unfinished challenge looked like a disabled control the
       player had failed to earn the right to press. It is a chip now, and
       CLAIM keeps the button shape to itself. */
    body.desktop #chList .claimBtn:disabled,
    #chList .claimBtn:disabled{
      border-style:solid;border-color:rgba(255,255,255,.10);
      background:rgba(255,255,255,.035);color:rgba(174,184,216,.72);
      letter-spacing:.14em;font-weight:800;opacity:1;cursor:default}

    /* NEW, WITH RESTRAINT. The badge already clears when the screen is left,
       but on the visit that shows it a whole grid of solid gold pills shouts
       over the OWNED state beside them. Outlined rather than filled: still the
       first thing the eye finds, no longer the loudest thing on the card. */
    .skinCard .isNew,.charCard .isNew,.newPill{
      background:rgba(255,213,106,.10);color:var(--gold);
      border:1px solid rgba(255,213,106,.42);box-shadow:none;font-weight:850}

    /* AN EMPTY LADDER SHOULD LOOK DESIGNED. Ten identical filled rows read as
       ten broken components; a quiet, receding ladder reads as ten places to
       put something, and lets the message above it be the focal point. */
    .lbSlots .lbSlot{background:transparent;border-style:dashed;
      border-color:rgba(255,255,255,.055)}
    .lbSlots .lbSlot:nth-child(n+4){opacity:.72}
    .lbSlots .lbSlot:nth-child(n+7){opacity:.5}
    .lbSlots .lbSlot:nth-child(n+9){opacity:.34}

    /* ---------- PAUSE: leaving the run should not look like a setting ----------
       KEEP GOING, RESTART, SETTINGS, FULLSCREEN and MAIN MENU were five
       identical pills in a stack. Two of them throw the run away and three of
       them do not, and nothing on screen said which was which. The two that
       cost you something are set apart - a rule above them and a warmer edge -
       without turning either into a red alarm the player has to read twice.
       Behaviour is untouched; this is which button looks like what. */
    #quitFromPause{margin-top:14px;position:relative}
    #quitFromPause::before{content:"";position:absolute;left:12%;right:12%;top:-8px;
      height:1px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.14),transparent)}
    #restartFromPause,#quitFromPause{border-color:rgba(255,168,120,.26)}
    #restartFromPause:hover,#quitFromPause:hover{
      border-color:rgba(255,168,120,.55);color:#ffd9c2;
      background:rgba(255,146,92,.10)}

    /* ---------- HOW TO PLAY: four groups, three columns ----------
       Adding BATTLE ROYALE made a fourth group, and a plain grid puts the
       fourth in a new row - one column of content beside two columns of
       nothing. Multi-column flows them into balanced columns instead, so the
       page stays square whether there are three groups or five. The wide
       POWER-UPS band is still a grid item and keeps its own full-width row
       below, which is why it is lifted out of the column flow. */
    @media (min-width:1200px){
      body.desktop .howGroups{display:block;
        column-count:3;column-gap:clamp(14px,1.4vw,24px)}
      body.desktop .howGroup{break-inside:avoid;margin:0 0 clamp(14px,1.4vw,24px)}
      body.desktop .howGroup.wide{column-span:all;margin-top:0}
    }

    /* ---------- SHOP: the rotation row is the storefront ----------
       #shopContent grew to fill the card but its grid kept align-content:start,
       so the products stayed in a band at the top with a fifth of the page
       empty underneath. On the FEATURED tab there is exactly one row, so it
       takes the height and the previews get to be the size of the thing you
       are being sold. Scoped to .rotation on purpose: the category tabs have
       many rows and must keep flowing from the top. */
    body.desktop #shopContent .skinGrid.rotation{align-content:stretch}
    body.desktop #shopContent .skinGrid.rotation .skinCard{height:100%;
      justify-content:center;gap:10px}
    body.desktop #shopContent .skinGrid.rotation .skinCard canvas{
      width:clamp(150px,11vw,196px);height:clamp(150px,11vw,196px)}

    /* THE PRICE IS THE BUTTON. A card whose only affordance is a small pill
       does not read as buyable, so the pill is given the size and the states
       of the control it actually is. No price, no economy and no purchase
       behaviour is touched - this is what the existing control looks like. */
    body.desktop #shopScreen .skinCard .price{
      font-size:12px;padding:8px 18px;border-radius:99px;
      border:1px solid rgba(255,213,106,.42);background:rgba(255,213,106,.10);
      transition:border-color var(--t-fast),background var(--t-fast),transform var(--t-fast)}
    body.desktop #shopScreen .skinCard:hover .price{
      border-color:rgba(255,213,106,.85);background:rgba(255,213,106,.20);
      transform:translateY(-1px)}

    /* ---------- SETTINGS: BACK is not adrift ----------

       And BACK was centred under all of it in that dead space, which is the
       same orphaned-button shape the Profile audit found. It ends the first
       column, where the eye already is.

       The three columns themselves are LEFT ALONE. They are a deliberate
       grouping - sound and picture, then input, then the system sections - and
       the third looks short only because its four sections are collapsed,
       which is what a settings page should look like before you open one. A
       masonry flow would re-order them by height and break that grouping to
       chase a balance the page does not actually need. */
    body.desktop #settingsScreen .backBtn{align-self:flex-start;
      justify-self:start;margin-left:0}

    /* ================================================================
       PHASE 5.2.3 — FINAL COMPOSITION PASS
       ================================================================
       The owner reviewed a recording of the whole game and reported that at
       1920x1080 the content still reads slightly undersized and several
       screens are still top-heavy. The previous pass fixed screens that
       STOPPED short of the bottom; this one is about SCALE and vertical
       distribution on a large monitor.

       Nothing here fills space for its own sake. The rules are a scale step
       that only applies on genuinely large screens, and vertical distribution
       for the panels that were bunching at the top. */

    /* ---------- a scale step for large desktops ----------
       Every size in the shell is authored for a 1440-1600px window. On a 1920
       monitor the same numbers leave the composition looking small in the
       middle of the screen. This is one modest step - roughly a tenth - gated
       on the viewport being large in BOTH directions, so a short wide window
       does not get taller content it has no room for. */
    @media (min-width:1700px) and (min-height:950px){
      :root{--content-max:1660px;--nav-w:236px}
      body.desktop .screenTitle{font-size:clamp(34px,2.8vw,50px)}
      body.desktop .navBtn{font-size:13px;padding:13px 16px}
      body.desktop .card > .small{font-size:13px}
      /* The stat/records panels that carry the numbers on Home and Profile. */
      body.desktop .statPane .panel,body.desktop .profSide > *{padding:18px 20px}
    }

    /* ---------- HOME: presence, not a redesign ----------
       The three-column composition is kept exactly. What changes is that the
       mode carousel stops donating a fixed column of empty space to each
       arrow, and the primary action carries the weight its position implies. */
    @media (min-width:1700px) and (min-height:950px){
      /* PHASE 5.2.3. The composition is the accepted one and is not being
         changed - these are the same rules with more confident numbers,
         because measured at 1920x1080 the three panes were 734px of a 994px
         body with 150px of nothing above them and 110px below. The content
         starts higher and each of the three primary elements - the mode card,
         PLAY, and the wordmark - takes a step up. Nothing new, nothing wider,
         no pane stretched to meet an edge. */
      body.desktop .homeBody{min-height:calc(100vh - 120px);padding-top:14px}
      body.desktop .playPane .mainBtn{min-height:124px;font-size:34px}
      body.desktop .modeCard{min-height:248px}
      body.desktop .modeCard .modeName{font-size:clamp(30px,2.4vw,42px)}
      body.desktop .heroPane .logo{font-size:clamp(54px,4.8vw,88px)}
      body.desktop .statPane .panel{padding:20px 22px}
    }

    /* ---------- SHOP / RANKS / PROFILE: distribute, do not stretch ----------
       These fill their shell already; the content inside was still sitting in
       the top half. The lists and grids space their rows through the height
       they have instead of stacking from the top and stopping. */
    @media (min-height:950px){
      body.desktop #shopContent .skinGrid.rotation .skinCard canvas{
        width:clamp(150px,12vw,232px);height:clamp(150px,12vw,232px)}
      body.desktop .lbSlots{display:flex;flex-direction:column;justify-content:space-between;
        flex:1;min-height:0}
      body.desktop #profileStats .statTile{min-height:clamp(96px,11vh,132px)}
      body.desktop .profSide > *{min-height:0}
    }

    /* ---------- THE SHARED PAUSE MENU ----------
       Classic, Hardcore and Daily all use this card. At 1920x1080 a 660px
       panel of small buttons in the middle of a full-screen arena reads as a
       utility dialog rather than as the game pausing. About a fifth larger,
       with the title carrying the moment and the controls sized to match -
       still nowhere near covering the arena, which is the other half of the
       requirement. */
    @media (min-width:1500px) and (min-height:860px){
      body.desktop #pauseScreen .card{width:min(100%,800px)}
      body.desktop #pauseScreen .screenTitle{font-size:clamp(46px,4vw,68px)}
      /* The title is centred and the two lines under it were not, so at the
         new size they read as a caption stuck to the left edge of a centred
         card rather than as part of it. One composition. */
      body.desktop #pauseScreen #pauseModeTag,
      body.desktop #pauseScreen #pauseModeTag + p,
      body.desktop #pauseScreen .screenTitle + .small{text-align:center}
      body.desktop #pauseScreen #pauseModeTag{font-size:13px;letter-spacing:.22em}
      body.desktop #pauseScreen .mainBtn{min-height:74px;font-size:24px}
      body.desktop #pauseScreen .secondaryBtn{min-height:56px;font-size:15px}
      body.desktop #pauseScreen .pauseKeys{margin-top:18px;font-size:12px}
    }

    /* ---------- SETTINGS: a page, not a row of panels at the top ----------
       The owner's review singled this one out. Everything useful was in the
       upper half, the third column held four collapsed headers, and BACK sat
       under all of it detached from the structure.

       The grouping is kept exactly - sound and picture, input, system - because
       it is the right grouping and re-flowing it by height would destroy it.
       What changes is that the columns now OWN the height they are given: an
       OPEN section grows into the space and lets its rows breathe, a COLLAPSED
       one stays exactly as compact as it should be, and BACK ends the page
       instead of floating under it.

       This is why the sections carry an .open class rather than being <details>
       - it is the one state a stylesheet needs to tell "has content to show"
       from "is a header waiting to be pressed". */
    body.desktop #settingsScreen{align-items:stretch}
    body.desktop #settingsScreen .card{display:flex;flex-direction:column}
    body.desktop #settingsScreen .screenTitle{flex:none}
    body.desktop #settingsList.setColumns{flex:1;min-height:0;align-items:stretch;
      /* The columns are allowed to be taller than the window - at 1366x768
         three columns of open sections simply are - so the region scrolls
         rather than growing past the card and putting BACK on top of the
         GRAPHICS panel, which is what it did at 1600x900. */
      overflow-y:auto;overflow-x:hidden}
    body.desktop .setCol{height:100%}
    body.desktop .setSection{flex:0 0 auto}
    /* PHASE 5.2.3. THE SECTIONS NO LONGER STRETCH.
       Two passes were spent making an open section grow into its column, and
       both produced the fault they were trying to remove: KEYBOARD & MOUSE
       held five rows in a 729px column, so either the gaps blew apart
       (space-evenly, 160px above the first control) or the rows inflated to a
       104px cap and the panel still finished 180px short of its own bottom
       edge. A settings panel is the size of the settings in it.

       Natural heights, and the three columns land within about two hundred
       pixels of each other on their own - AUDIO plus GRAPHICS against
       KEYBOARD plus two collapsed against ACCESSIBILITY plus three - which is
       what a balanced settings page actually looks like. The slack is at the
       foot of the columns, in one place, where BACK already is. */
    body.desktop .setSection.open{display:flex;flex-direction:column}
    body.desktop .setSection.open .setBody{display:flex;flex-direction:column}
    /* THE SPACE GOES INTO THE ROWS, NOT BETWEEN THEM. The first attempt at
       this used justify-content:space-evenly, and a section with five rows in
       a 730px column put a hundred and sixty pixels of nothing above the
       first control - which reads as a rendering fault, not as breathing
       room. The rows themselves grow instead, and they stop growing at a
       height a row of controls can justify; whatever is left over stays at
       the bottom of the section as padding, where it looks like padding. */
    body.desktop .setSection.open .setRow{flex:0 0 auto}
    body.desktop #settingsScreen .backBtn{flex:none;margin-top:16px}


    /* ---------- HEROES: the hero is the reason to be on this page ----------
       The previous pass gave the preview the whole column, which was right,
       and then the column looked emptier than before - a 300px hero at the
       top of a 900px panel with six hundred pixels of nothing under it. Two
       things were wrong.

       FIRST, the centring never ran. #charDetail is justify-content:center,
       but .cdInfo carries flex:1 from the narrow layout, so it grew to 544px
       for 150px of content and there was no free space left to centre. The
       info block is content-sized here, and the parent centres what is left.

       SECOND, the hero was small. It is the subject of the screen and it was
       being rendered at the size of a grid thumbnail; the tile it sits in is
       380px wide. It scales with the space now - by width AND by height, so a
       short window does not get a hero it has to scroll past. */
    @media (min-width: 1100px) {
      /* A STICKY RAIL HAS TO BE AS TALL AS THE WINDOW, NOT AS TALL AS THE
         COLUMN. With thirteen heroes the grid beside this is longer than the
         viewport, so centring inside the column put the middle of the preview
         at the middle of a 1100px column - which at 1366x768 left the EQUIPPED
         button below the fold. Bounded to what can actually be seen, the
         centring happens where the player is looking. */
      body.desktop #charDetail { max-height: calc(100vh - 128px); }
      body.desktop #charDetail > .cdInfo { flex: 0 0 auto; }
      body.desktop #cdPreview,
      body.desktop #charDetail canvas {
        width: min(24vw, 40vh, 420px);
        height: min(24vw, 40vh, 420px);
        max-width: none;
      }
      body.desktop .cdInfo b { font-size: clamp(20px, 1.5vw, 26px); }
    }

    /* ---------- SHOP: top-heavy, and it was the spacing ----------
       The owner's note was that Shop is "currently too top-heavy". It fills
       its shell - the previous pass saw to that - but the three blocks inside
       it stack from the top with fixed gaps and stop 237px short, so the page
       reads as a poster pinned to the ceiling of an empty room.

       Nothing is stretched to fix it. The featured item gets to be the size of
       a featured item, the gaps take a share of the viewport instead of being
       constants tuned for a 900px window, and the rotation cards let their
       previews grow - which is the thing being sold. */
    @media (min-width: 1100px) and (min-height: 860px) {
      body.desktop #shopContent { gap: clamp(20px, 3.2vh, 46px); }
      body.desktop #shopContent .featHero {
        min-height: clamp(248px, 29vh, 330px);
        padding: clamp(20px, 2.4vh, 34px) clamp(24px, 2vw, 40px);
      }
      body.desktop #shopContent .featHero canvas {
        width: clamp(180px, 20vh, 240px);
        height: clamp(180px, 20vh, 240px);
      }
      body.desktop #shopContent .featHero .fhInfo b { font-size: clamp(26px, 2vw, 36px); }
      body.desktop #shopContent .skinGrid.rotation { align-content: stretch; }
    }

    /* ---------- RANKS: the rail has to look deliberate when it is short ----
       The board takes the page and does that well. The rail beside it holds
       two summary panels totalling 467px against an 842px board, so a third
       of the right-hand side was empty - and the brief is explicit that this
       screen has to feel deliberate with no runs, one run or many, and that
       no leaderboard entry may be invented to fill it.

       So nothing is added. The two panels that are already there share the
       height and let their own rows breathe, which is what the space is for.
       The caps matter: a row grows to about a line and a half and then stops,
       so a board with one entry gets a composed rail rather than six enormous
       bars pretending to be content. */
    @media (min-width: 1100px) and (min-height: 860px) {
      body.desktop .lbLayout { align-items: stretch; }
      body.desktop .lbSide { align-self: stretch; }
      body.desktop .lbSide > .dockCard {
        display: flex; flex-direction: column; flex: 1 1 auto;
      }
      /* The cap. The height goes INTO the rows and they stop growing at a
         size a row of one label and one number can justify; anything left
         over stays at the foot of the panel, where it reads as padding
         rather than as a gap somebody forgot to fill. */
      body.desktop .lbSide .dockRow { flex: 1 1 auto; max-height: 62px; }
    }

    /* ---------- PROFILE: the last three hundred pixels ----------
       The previous pass recomposed this screen into two columns and killed
       the hole that had BACK floating in it. The owner's note is that it is
       STILL top-loaded, and it is: the card reserves a 1fr row for the record
       panels, and then .profSide carries align-self:start from the narrow
       layout, so it refuses the row it was given and the page stops 300px
       above the bottom.

       The tiles are the headline numbers and take a little more height; the
       record panels take what is left and let their own rows breathe, with
       the same 52px cap Ranks uses so that a profile with few records spaces
       out rather than inflating. Nothing new is displayed, and in particular
       nothing here implies that any of it leaves the device. */
    @media (min-width: 1100px) and (min-height: 860px) {
      body.desktop #profileStats { align-self: stretch; align-content: stretch; }
      body.desktop #profileStats .statBox { min-height: clamp(104px, 12.5vh, 148px); }
      body.desktop .profSide { align-self: stretch; align-content: stretch; }
      /* THE SPACE GOES INTO THE ROWS. space-evenly put a hundred and seventy
         pixels between each of COLLECTION's three lines, which is the same
         fault the Settings columns had: a gap that large stops reading as
         spacing and starts reading as something missing. The rows grow to a
         height a row can justify and then stop.

         RECORDS has eight lines and takes the column; COLLECTION has three and
         does not pretend otherwise. Two panels of visibly different heights,
         because they hold visibly different amounts. */
      body.desktop .profSide { align-items: start; }
      body.desktop .profSide > .dockCard { display: flex; flex-direction: column; }
      /* RECORDS WAS 609px TALL FOR EIGHT LINES. Stretching it to the foot of
         the column removed the dead space arithmetically and produced an
         oversized nearly-empty panel instead, which is what the owner is
         looking at. A panel is the size of what is in it. The balance goes
         OUTSIDE the panels, as page margin, not inside them as padding. */
      body.desktop .profSide > .dockCard { align-self: start; }
      body.desktop .profSide .dockRow { flex: 0 0 auto; max-height: none; }
    }

    /* ---------- CHALLENGES: three cards are three cards ----------
       The previous pass told #chList to take the height, and with three items
       in it the rows simply grew - 194px of card around 90px of content, each
       one top-loaded, which is precisely the "giant cards" §29 warns against.
       The space was being absorbed by the wrong thing.

       A challenge row is capped at a size a challenge row can justify, its
       content sits in the middle of it, and the group is centred in the
       column instead of stacking from the top. The summary rail beside it
       takes the height the way the Ranks rail does - it has three real panels
       to put there, and none of it is invented. */
    @media (min-width: 1100px) and (min-height: 860px) {
      /* NOT CENTRED. The pass before this one capped the rows and then centred
         the group, and centring is what made it read as an island: a hundred
         and thirty pixels of nothing above the first challenge, the same below
         the last, and no relationship to the tabs the list belongs to. A list
         starts where its heading ends. The rows keep their cap - they were 194px
         of card around 90px of content before that - and the slack goes to the
         foot of the page, where a bottom margin is what it looks like. */
      body.desktop #chList { justify-content: flex-start; gap: clamp(16px, 3.4vh, 44px); }
      body.desktop #chList .chRow {
        /* THREE ROWS OF EIGHTY PIXELS OF CONTENT DO NOT FILL A 1080px SCREEN,
           and every way of pretending otherwise was tried and looks worse.
           Stretched to fill the column they were 194px of card around 90px of
           content. Grown to a 178px cap they were the same fault in a smaller
           size. Centred as a group they read as an island with no relationship
           to the tabs above them.

           So: the row is the height a row of this content should be, the list
           starts where its heading ends, the gaps take a share of the viewport,
           and what is left is a bottom margin. A page with a margin is a normal
           thing. An inflated card is the thing §29 names. */
        flex: 0 1 auto;
        max-height: clamp(132px, 15vh, 172px);
        justify-content: center;
        padding-block: clamp(14px, 1.8vh, 24px);
      }
      /* THREE PANELS, NOT TWO. The Ranks rail has two panels holding four and
         six facts, so growing their rows fills it honestly. This rail has
         three holding four, two and two - grow those and you get three panels
         each with its own void in it. They keep their natural heights and the
         rail spreads them instead: top, middle, bottom, which is a shape a
         sidebar is allowed to have. */
      /* CENTRED, NOT SPREAD. space-between was tried first and it put two
         hundred and fifty pixels between panels that belong together - three
         summaries pinned to the corners of a rail read as disconnected, not
         as composed. The list beside them is centred, so the rail is centred
         too and the two columns pair. */
      /* The rail follows the list: both start at the top of the content area,
         so the two columns share one baseline instead of floating at two
         different heights in the middle of the screen. */
      body.desktop .chSide { justify-content: flex-start; gap: clamp(14px, 2vh, 26px); }
      body.desktop .chSide > .dockCard { flex: 0 0 auto; }
    }

    /* ================================================================
       PHASE 5.2.3 — SURGICAL CORRECTIONS
       ================================================================ */

    /* ---------- STYLE: THE SAVE SLOTS WERE MOVING 633 PIXELS ----------
       Reported as a layout jump when switching category, and it is exactly
       that. Measured at 1920x1080: SAVE SLOT 1 sits at y=1189 on COLOURS and
       y=556 on TRAILS. The card itself goes from 1312px to 679px.

       The cause is the area map. #custGrid occupies the "grid" area, which
       spans rows four to seven, and the left column's ACTIONS, SAVE SLOTS and
       note sit in rows five, six and seven. A spanning item's height is
       distributed across the tracks it spans, so the item grid - 1088px of
       swatches on COLOURS, 452px on TRAILS - was setting the height of the
       rows that the persistent controls live in. Change category, change the
       swatch count, move the Save Slots.

       Two things fix it together, and neither is a redesign:

         the card takes the shell's height, so the row that should absorb the
         slack has a definite one to absorb;

         the item grid scrolls. overflow-y:auto makes its automatic minimum
         size zero, so it stops contributing anything to the tracks it spans
         and fills them instead of setting them.

       The slack then goes to the LAST row, where COLLECTION BOOK already sat
       at align-self:end - so it anchors to the foot of the column the way
       BACK does on Settings and Profile, and everything above it stays put. */
    @media (min-width: 1100px) {
      /* CONTENT-AWARE, NOT FULL-HEIGHT. Stretching the screen gave the item
         grid a definite height to fill, which is what stopped it dictating the
         rows the Save Slots live in - but it also made the card as tall as the
         shell, 1024px at 1920x1080, for a left column of about seven hundred.
         The owner is right that it reads as a long empty rectangle.

         The grid gets a bounded height of its own instead. That keeps the
         property the Save Slot fix depends on - overflow-y:auto means the grid
         contributes nothing to the tracks it spans, so a category with 40
         swatches and one with 12 produce the same row heights - while letting
         the CARD be as tall as its content and no taller. The whitespace moves
         outside the card, which is where §20 says it belongs. */
      body.desktop #customScreen #custGrid {
        max-height: clamp(360px, 52vh, 560px);
        /* A SCROLL EDGE THAT LOOKS LIKE ONE. Cutting a row of swatches in half
           at a hard boundary reads as clipping, not as "there is more below";
           the last 30px fade so the boundary announces itself. When the list is
           short enough not to scroll, the fade lands on empty padding and is
           invisible. */
        padding-bottom: 10px;
        -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent);
        mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent);
      }
      body.desktop #customScreen .card {
        grid-template-rows: auto auto auto auto auto auto auto minmax(0, 1fr);
        grid-template-areas:
          "title   title"
          "preview tabs"
          "preview filters"
          "preview grid"
          "actions grid"
          "presets grid"
          "note    grid"
          "link    grid";
        min-height: 0;
      }
      /* COLLECTION BOOK had been sharing the note's cell and reaching the
         foot of it with align-self:end - which was invisible while the row
         was as tall as the swatch grid, and became a 350px hole the moment
         the grid stopped setting that height. Its own row, directly under
         the text it follows. */
      body.desktop #customScreen #collectionLink {
        grid-area: link; align-self: start; margin-top: 10px;
      }
      body.desktop #customScreen #custGrid {
        align-self: stretch;
        min-height: 0;
        overflow-y: auto;
        overflow-x: hidden;
      }
      body.desktop #customScreen .card > .small { align-self: start; }
    }

    /* ---------- THE PAUSE MENU: AN ALIGNMENT BUG, NOT A TASTE CALL ----------
       SETTINGS and FULLSCREEN were left-aligned while KEEP GOING, RESTART and
       MAIN MENU were centred, and it was accidental in the plainest way:

         body.desktop .deskOnly { display: flex }

       was written for the sidebar's nav GROUPS, and these two buttons carry
       .deskOnly because they are meaningless on a phone. A button that becomes
       a flex container stops obeying text-align - the label becomes a flex item
       and lands at flex-start - so two of five controls quietly moved to the
       left edge. Measured: display:flex on those two, inline-block on the other
       three, identical text-align on all five.

       The rule keeps doing its job for the nav; a BUTTON that it turns into a
       flex box now centres what is in it. */
    body.desktop button.deskOnly { justify-content: center; align-items: center; }

    /* THE MODE TAG AND THE BLURB WERE CENTRED INSIDE A NARROWER BOX than the
       card, so their centre sat 94px left of the card's. Centred text is only
       centred if the box is. */
    body.desktop #pauseScreen .small { max-width: none; width: 100%; }

    /* THE EXIT PAIR. Both of these end the run; nothing else on the card does.
       A hairline and a little air is the whole distinction - no red, no
       shouting, just a line that says the two below it are not like the three
       above. */
    body.desktop #pauseScreen .pauseExit:first-of-type {
      margin-top: 22px;
      box-shadow: 0 -12px 0 -11px rgba(255, 255, 255, .16);
    }

    /* ---------- THE PAUSE BACKPLATE ----------
       The card had background: transparent and no border - the controls were
       sitting directly on the arena, which is why the menu never read as a
       surface. A dark translucent plate with one restrained cyan edge, sized
       to the card that is already there. It covers about a third of the width
       and does not blur the game behind it beyond a light pass, so the arena
       stays legible underneath - the point is separation, not concealment. */
    body.desktop #pauseScreen .card {
      background:
        linear-gradient(165deg, rgba(120, 248, 255, .06), rgba(10, 14, 34, .90) 42%);
      border: 1px solid rgba(120, 248, 255, .20);
      border-radius: 22px;
      padding: clamp(26px, 3vh, 40px) clamp(28px, 2.4vw, 44px);
      box-shadow: 0 28px 90px rgba(0, 0, 0, .62),
                  0 0 0 1px rgba(0, 0, 0, .35),
                  0 0 46px rgba(120, 248, 255, .07);
      backdrop-filter: blur(7px);
      -webkit-backdrop-filter: blur(7px);
    }

    /* ---------- HEROES: THE PREVIEW WAS OVERFLOWING ITS OWN COLUMN ----------
       The last pass sized the hero at min(24vw, 40vh, 420px) and never checked
       it against the width of the thing it sits in. At 1920x1080 that resolves
       to 420px inside a 380px panel: measured, #cdPreview runs from x=319 to
       x=739 while its panel ends at x=678, so the preview was bleeding 41px
       out of the card and under the first row of the hero grid. That is what
       "messed up" was looking at.

       Two more faults came with it. #charDetail is a centred flex column, so
       .cdInfo shrank to its content - 147px of name, mastery and EQUIPPED
       under a 420px picture. And the panel is 900px tall for about 620px of
       content, with the whole block floating in the middle of it.

       Recomposed as one hero card rather than a picture with captions:

         the column is wider, so a large hero fits INSIDE it instead of
         escaping it - and the preview is bounded by the column first, so this
         cannot come back whatever the viewport does;

         the information spans the column - a real name, a real mastery bar and
         an EQUIP control the width of the card it belongs to;

         the block is anchored to the top of the panel with the slack below it,
         because a hero card reads from the top down and centring it was what
         made the panel feel like it had nothing in it. */
    @media (min-width: 1100px) {
      body.desktop .charsLayout {
        grid-template-columns: minmax(320px, 430px) minmax(0, 1fr);
      }
      body.desktop #charDetail {
        /* The panel is sized to PAIR WITH THE GRID rather than to fill the
           column. Stretched it was 900px tall for 595px of hero card and the
           bottom third was empty; hugging its content it was a short box at
           the top of a tall column, which is the fault the pass before last
           was fixing. A card that ends within fifty pixels of where the hero
           grid ends reads as the other half of the same screen. */
        align-self: start;
        min-height: min(74vh, 800px);
        max-height: calc(100vh - 128px);
        justify-content: center;
        gap: clamp(12px, 1.6vh, 20px);
        padding: clamp(18px, 2vh, 26px);
      }
      /* min() against 100% FIRST: the column is the ceiling, always. */
      body.desktop #cdPreview,
      body.desktop #charDetail canvas {
        width: min(100%, 38vh, 390px);
        height: auto;
        aspect-ratio: 1;
        max-width: 100%;
      }
      body.desktop #charDetail > .cdInfo {
        flex: 0 0 auto;
        align-self: stretch;
        width: 100%;
        gap: 8px;
      }
      body.desktop .cdInfo b { font-size: clamp(21px, 1.6vw, 30px); }
      body.desktop #charDetail .cdEquipBtn { width: 100%; }
      body.desktop #charDetail .cdMastery,
      body.desktop #charDetail .cdBar { width: 100%; }
    }

    /* ---------- PROFILE: A DASHBOARD, NOT A FULL-HEIGHT RECTANGLE ----------
       The card was stretched to the shell - 1578x1024 at 1920x1080 - while its
       content ended at about 772. That is 280px of empty card, which is what
       the owner is looking at when they call the lower-right area unused, and
       it is why every previous attempt to "fix" it reached for something to
       stretch INTO the gap. RECORDS was made 609px tall for eight lines once
       already and correctly rejected.

       The gap is not inside the card. The card is simply too tall. Content-
       aware height puts the whitespace outside it, where §20 asks for it, and
       nothing has to be inflated to earn its place. The panels keep the natural
       heights the last pass gave them: RECORDS is the size of eight records and
       COLLECTION is the size of three, and they are allowed to differ. */
    @media (min-width: 1100px) {
      body.desktop #profileScreen { align-items: flex-start; }
      body.desktop #profileScreen .card {
        grid-template-rows: auto auto auto auto auto;
        align-content: start;
      }
      body.desktop #profileScreen #profileStats { align-self: start; }
      body.desktop #profileScreen .profSide { align-self: start; }
      /* The identity column and the numbers column now end near each other
         instead of one being padded to the shell. */
      body.desktop #profileScreen .backBtn { margin-top: 14px; }
    }

    /* ---------- CHALLENGES: the same fault Profile had ----------
       The list, the gaps and the rail are all where the last pass left them and
       are not being moved again. What was still wrong is the CARD: stretched to
       the shell at 1024px with its content finishing at about 696, so three
       daily challenges sat above three hundred pixels of empty card.

       Three dailies do not fill a 1080px screen and nothing should pretend they
       do - that was tried and rejected twice. The card ends where its content
       ends and the remaining height is page margin. */
    @media (min-width: 1100px) {
      body.desktop #challengesScreen { align-items: flex-start; }
      body.desktop #challengesScreen .card { min-height: 0; }
      body.desktop #chList { flex: 0 0 auto; }
    }

/* ==================== ACCOUNT SYNC. PHASE 5.2.4J ====================
   Two one-time decisions - bring your progress in, or choose which progress
   this device shows. Both are rare, both are irreversible in one direction,
   and both deserve a real dialog rather than a toast. Styled from the same
   tokens as the rest of the game so nothing here looks bolted on. */
.syncModalWrap {
  position: fixed; inset: 0; z-index: var(--z-dialog);
  display: flex; align-items: center; justify-content: center;
  padding: 20px; background: rgba(4, 6, 16, 0.78);
  backdrop-filter: blur(6px);
  animation: syncFade 180ms ease-out;
}
@keyframes syncFade { from { opacity: 0; } to { opacity: 1; } }
.syncModal {
  width: min(440px, 100%);
  background: var(--panel, #141a2e);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 16px;
  padding: 22px 20px 18px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.55);
}
.syncModal h2 {
  margin: 0 0 12px; font-size: 19px; letter-spacing: 0.06em;
  color: var(--ink, #eaf0ff);
}
.syncModalBody p { margin: 0 0 10px; font-size: 14px; line-height: 1.5; color: var(--muted, #aeb8d8); }
.syncModalBody strong { color: var(--ink, #eaf0ff); }
.syncModalNote { font-size: 13px !important; opacity: 0.82; }
.syncModalRow { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; }
/* The dialog reuses .mainBtn / .secondaryBtn so the two actions carry the
   game's own contrast, hover and focus treatments. Only the layout differs:
   side by side and equal width, rather than stacked full-width. */
.syncModalRow .mainBtn,
.syncModalRow .secondaryBtn { flex: 1 1 160px; width: auto; margin-top: 0; }
.syncModalRow .secondaryBtn { background: rgba(255, 255, 255, 0.08); }
@media (hover: hover) { .syncModalRow .secondaryBtn:hover { background: rgba(255, 255, 255, 0.16); } }
@media (max-width: 420px) {
  .syncModalRow .mainBtn,
  .syncModalRow .secondaryBtn { flex-basis: 100%; }
}

/* Previous-period rewards, only drawn during the 24-hour grace. Quiet by
   design: it is a small second chance, not a second challenge list. */
.chPrevHead { margin: 14px 0 6px; opacity: 0.75; letter-spacing: 0.08em; text-transform: uppercase; }
.chRow.chPrev { opacity: 0.92; border-style: dashed; }

/* ACCOUNT PROGRESS STATUS. PHASE 5.2.4J.
   A guest, and every production build, never sees this: it stays .hidden
   unless progress is genuinely being synced to an account. Four states, in
   the player's language - no queue depths, no operation names, no codes. */
.syncChip{display:inline-flex;align-items:center;gap:6px;padding:5px 10px;border-radius:999px;
  font-size:11px;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;
  background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);color:var(--dim,#9aa4b2)}
.syncChip.hidden{display:none}
.syncChip .sdot{width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}
.syncChip.ok{color:#7fd6a6}
.syncChip.busy{color:#8fb8ff}
.syncChip.busy .sdot{animation:syncPulse 1.1s ease-in-out infinite}
.syncChip.wait{color:#e0b96a}
.syncChip.bad{color:#f08a8a;border-color:rgba(240,138,138,.35)}
@keyframes syncPulse{0%,100%{opacity:1}50%{opacity:.25}}
@media (max-width:560px){.syncChip .slabel{display:none}.syncChip{padding:5px 7px}}

/* A layer that something else has opened on top of. `inert` already blocks
   focus and pointer events where it is supported; this is the fallback, and
   it also says visually which dialog is answering the keyboard. */
[data-ll-layer][inert] { pointer-events: none; filter: saturate(0.7) brightness(0.75); }

/* Typed confirmation inside the confirm dialog. Only ever shown for a
   destructive action that asks the player to type something or prove who
   they are. */
#confirmInput{display:block;width:100%;margin:10px 0 2px;padding:11px 12px;border-radius:12px;
  border:1px solid var(--line);background:rgba(255,255,255,.06);color:#fff;
  font-size:14px;font-weight:800;letter-spacing:.06em;text-align:center}
#confirmInput.hidden{display:none}
#confirmInput::placeholder{color:var(--muted);font-weight:700;letter-spacing:.04em}
.modalBtns button[disabled]{opacity:.45;cursor:not-allowed}
