/* ---------- bundled fonts ----------
   Self-hosted rather than loaded from the Google Fonts CDN, which until now was the project's only
   external dependency. Two reasons, both of which matter for shipping:
     1. Offline. A packaged build (Electron/Steam) has no guarantee of a network, and these two faces
        ARE the game's visual identity — falling back to Courier New doesn't look like a stylistic
        variation, it looks broken.
     2. Privacy/law. CDN font loading transmits every player's IP address to Google, which is a GDPR
        problem for an EU release (German courts have already ruled on exactly this).
   Both faces are SIL Open Font License 1.1, so redistribution is explicitly permitted; the full
   license text for each ships in fonts/ as OFL requires, and neither Reserved Font Name has been
   altered. See fonts/OFL-Press-Start-2P.txt and fonts/OFL-VT323.txt.

   These are Google's own subset files with Google's own unicode-range values, copied verbatim, so
   glyph coverage is identical to what the CDN was serving — no rendering changes. Only the latin and
   latin-ext subsets are bundled (the game is English); cyrillic, greek and vietnamese are dropped
   because unlike a CDN, a packaged build ships every file whether or not it is ever needed. Total
   payload 56KB. */
@font-face{
  font-family:'Press Start 2P';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('fonts/press-start-2p-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Press Start 2P';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('fonts/press-start-2p-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face{
  font-family:'VT323';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('fonts/vt323-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'VT323';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('fonts/vt323-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root{
  --bg:#0e131c;
  --panel:#1a2230;
  --panel-line:#324058;
  --ink:#eef1f6;
  --ink-soft:#c1cbdc;
  --gold:#e3b94c;
  --gold-dim:#8a6a1c;
  --frost:#6fb8dd;
  --purple:#8a6fc4;
  --red:#c9394f;
  --good:#4fae62;
  --lexicon:#5f9e6e;   /* the Glossary drawer only — ivy, not the green that means "success" */
  --tower-accent:#6fb3c4;
  --ascension-accent:#9a7fd0;
  --changer-accent:#e0704f;
  --orders-accent:#5fb6a6;
  --keep-accent:#8fa3c4;
  --astral-accent:#e4dcf5;
  --ghost:#6b76a0;
  --font-display:'Press Start 2P', 'Courier New', monospace;
  --font-body:'VT323', 'Courier New', monospace;
  --bevel-lt:rgba(255,255,255,0.16);
  --bevel-dk:rgba(0,0,0,0.4);
  --pixel-shadow:4px 4px 0 0 rgba(0,0,0,0.55);
}

*{box-sizing:border-box;}

/* The game renders into a fixed internal resolution (W/H in game.js = 1120x630). Presentation size
   is decoupled from it: layoutStage() recomputes these two variables from the viewport on load,
   resize and fullscreen change, and every box that must track the canvas reads them instead of
   hardcoding 1120px. Defaults are the native size, so the layout is correct before the first
   layoutStage() call and stays correct if JS never runs. */
:root{
  --stage-w:1120px;
  --stage-h:630px;
  --ui-stage-scale:1;  /* the stage's own scale: in-run DOM overlays follow the stage down (layoutStage) */
  --ui-user-scale:1;   /* the player's Text and panel size from Options (applySettings) */
  --ui-scale:calc(var(--ui-stage-scale) * var(--ui-user-scale));
  --stage-scale:1; /* the canvas's true display scale, uncapped — the Barracks tray tracks it */
  /* How wide the three edge-drawer handles are. One number, because the handle's width and the
     distance the drawer parks itself off-screen are the same measurement seen from two sides, and
     they were two hardcoded 44s that had to agree. 39.6 is 44 less a tenth: the rail takes less of
     the Skill Tree's right-hand edge without the handles losing any of their height. */
  --tab-w:39.6px;
}

/* Text and panel size (2026-10-11). Almost every size in this stylesheet is in rem, and the
   display face bottoms out around 0.36rem, so the one honest way to make the game bigger is to move
   the root. Panels that are capped in px scroll or wrap rather than clip. */
/* The root size, and the one rule that sets it (2026-10-12).

   It used to be two rules: a calc() carrying the player's Text and panel size, and then
   html,body{font-size:17px} immediately below it — same specificity, later in the sheet, so the
   flat 17px won every time. The slider moved the in-game hover cards and the toast, because those
   multiply by --ui-scale by hand, and nothing else. The menus, the panels and every paragraph in
   the game are laid out in rem and never heard about it.

   Fixing the root is one line. The reason it was not a one-line fix is the division of labour
   below, which has to be got right or the player's scale is counted twice:

     REM lengths already carry the player's scale, because that is what moving the root does.
       Anything in rem that also wants the STAGE's scale multiplies by --ui-stage-scale.
     PX lengths carry neither, so anything in px that wants both multiplies by --ui-scale,
       which is still stage x user.

   So: --ui-scale for px, --ui-stage-scale for rem. */
html{font-size:calc(17px * var(--ui-user-scale, 1));}
html,body{
  margin:0;
  height:100%;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--font-body);
  overflow:hidden;
  user-select:none; -webkit-user-select:none;
}
body{font-size:1rem;}
input, textarea{user-select:text; -webkit-user-select:text;}

/* ---------- pixel UI system: hard corners, stepped bevels, no blur/ease ---------- */
.big-btn, .st-tab, .node-buy, .danger-btn, #btn-start-wave, #btn-speed, #btn-pause, #btn-mercantile,
.profile-stats, .overlay-content, .skilltree-panel, #tower-info, .stats-tile, .ach-row,
.level-card, .lb-row, .setup-map-card, .setup-tower-card, .profile-row, #hud, #canvas-wrap,
.shop-item, #toast, #pb-banner, #sg-reveal-toast, .profile-new-row input,
.stats-kill-row, .sg-info, .sg-card, .sg-card-badge, .node-pip,
#mechanic-tip, #wave-tip, #ability-tip, .confirm-panel, #tut-spot, #tut-bubble, .howto-row, .rail-btn, .rail-label{
  border-radius:0 !important;
}
.big-btn, .st-tab, .node-buy, .danger-btn, #btn-start-wave, #btn-speed, #btn-pause, #btn-mercantile, .profile-stats, .rail-btn{
  box-shadow:inset -3px -3px 0 0 var(--bevel-dk), inset 3px 3px 0 0 var(--bevel-lt);
  transition:none;
  font-family:var(--font-display);
  letter-spacing:0.02em;
}
.big-btn:active, .st-tab:active, .node-buy:active, .danger-btn:active, .rail-btn:active,
#btn-start-wave:active, #btn-speed:active, #btn-pause:active, #btn-mercantile:active, .profile-stats:active{
  box-shadow:inset 3px 3px 0 0 var(--bevel-dk), inset -3px -3px 0 0 var(--bevel-lt);
}

#game-root{
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  height:100%;
  gap:10px;
  padding:10px;
}

/* ---------- HUD ---------- */
#hud{
  width:var(--stage-w);
  max-width:100%;
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:4px 10px;
  background:var(--panel);
  border:2px solid var(--panel-line);
  border-radius:8px;
  padding:3px 12px; /* 2026-09-21: the bar took too much of the screen — a third shorter */
}
.hud-item{
  display:flex;
  align-items:center;
  gap:5px;
  font-weight:600;
  font-size:0.95rem;
  font-variant-numeric:tabular-nums;
}
.hud-item.hud-xp{color:var(--purple);}
.hud-icon{
  width:22px; height:22px;
  image-rendering:pixelated;
}
#hud-spacer{flex:1;}
#wave-action{display:flex; align-items:center; gap:8px;}
#wave-countdown{color:var(--ink-soft); font-size:0.85rem; min-width:2.2em;}

button{
  font-family:inherit;
  cursor:pointer;
}
#btn-start-wave, #btn-speed, #btn-pause, #btn-mercantile{
  background:#25314a;
  border:3px solid var(--panel-line);
  color:var(--ink);
  padding:4px 10px;
  font-size:0.56rem;
  font-weight:600;
}
#btn-start-wave:hover, #btn-speed:hover, #btn-pause:hover, #btn-mercantile:hover{background:#324164;}
#btn-start-wave:active, #btn-speed:active, #btn-pause:active, #btn-mercantile:active{transform:translateY(2px);}
#btn-mercantile{color:var(--gold); border-color:var(--gold-dim);}
/* Start Wave's label is dynamic and sometimes long ("Start Wave 30 — Final Boss!", "...(Overlap)") —
   Press Start 2P is too wide per character for that, and was pushing Pause/End Run onto a second
   HUD row. VT323 (the body font) is far more compact while staying on-theme. */
#btn-start-wave{font-family:var(--font-body); font-size:1rem; font-weight:400; letter-spacing:0;}
/* "Mercantile" is the one HUD label long enough in Press Start 2P to help crowd the row — same
   compactness fix as Start Wave, keeping the same border/bevel/gold color so it still matches. */
#btn-mercantile{font-family:var(--font-body); font-size:1rem; font-weight:400; letter-spacing:0;}
#btn-start-wave:disabled{opacity:0.4; cursor:default;}
#btn-start-wave:disabled:hover{background:#25314a;}
#btn-start-wave.ready{
  background:var(--gold-dim);
  border-color:var(--gold);
}
#btn-start-wave.ready:hover{background:#a67c22;}
#tower-info .ti-bonus{font-size:0.7rem; color:var(--frost); margin-top:5px; line-height:1.45;}
#tower-info .ti-bonus b{color:var(--gold); font-weight:600;}
#tower-info .ti-bonus.vet{color:var(--ink-soft);}
.danger-btn{
  background:#3a1f26;
  border:3px solid #5c2a34;
  color:#f2c9d0;
  padding:8px 14px;
  font-size:0.62rem;
  font-weight:600;
}
.danger-btn:hover{background:#4d2530;}
.danger-btn:active{transform:translateY(2px);}

/* ---------- canvas ---------- */
#canvas-wrap{
  position:relative;
  width:var(--stage-w);
  height:var(--stage-h);
  max-width:100%;
  border:2px solid var(--panel-line);
  border-radius:8px;
  overflow:hidden;
  box-shadow:var(--pixel-shadow);
}
#game-canvas{
  display:block;
  width:100%;
  height:100%;
  image-rendering:pixelated;
  cursor:crosshair;
}

#tower-info{
  position:absolute;
  background:rgba(18,24,34,0.97);
  border:3px solid var(--panel-line);
  padding:12px 14px;
  width:300px; /* fixed (2026-09-16): a growing digit count or the Ascended chip used to move the Upgrade button */
  font-size:0.95rem;
  font-variant-numeric:tabular-nums;
  pointer-events:auto;
  box-shadow:var(--pixel-shadow);
  z-index:5;
}
#tower-info h3{margin:0 0 8px; font-family:var(--font-display); font-size:0.6rem; line-height:1.6; height:0.96rem; color:var(--gold); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}

/* ---------- kingdom-rule badge tooltip (hover / click on the on-canvas badge) ---------- */
#mechanic-tip{
  position:absolute;
  width:min(360px, 45%);
  background:rgba(18,24,34,0.97);
  border:3px solid var(--gold);
  padding:12px 14px;
  box-shadow:var(--pixel-shadow);
  z-index:6;
  pointer-events:none; /* hover-driven: the tip must never steal the pointer from the badge under it */
}
#mechanic-tip .mt-name{font-family:var(--font-display); font-size:0.58rem; line-height:1.6; color:var(--gold); margin-bottom:6px;}
#mechanic-tip .mt-dormant{font-family:var(--font-body); font-size:0.8rem; color:var(--ink-soft); letter-spacing:0; margin-left:6px;}
#mechanic-tip .mt-body{font-size:0.92rem; line-height:1.4; color:var(--ink);}
#mechanic-tip .mt-sub{margin-top:8px; padding-top:8px; border-top:1px solid var(--panel-line); color:var(--ink-soft);}
#mechanic-tip .mt-hint{margin-top:8px; font-size:0.78rem; color:var(--ghost);}
/* Only set when refreshHudCards() has had to cap the height — a card taller than the battlefield
   (35 Mutations will do it) scrolls rather than running off the bottom of the screen. */
#mechanic-tip.hud-tip-scroll, #wave-tip.hud-tip-scroll{overflow-y:auto; overflow-x:hidden;}
/* Next-wave enemy card: same family as the rule tip, frost-bordered because it's intel, not a rule. */
#ability-tip{
  position:absolute; width:250px;
  background:rgba(18,24,34,0.97); border:3px solid var(--frost); padding:10px 12px;
  box-shadow:var(--pixel-shadow); z-index:6; pointer-events:none;
}
#ability-tip .wt-name{font-family:var(--font-display); font-size:0.58rem; line-height:1.6; color:var(--frost); margin-bottom:6px;}
#ability-tip .wt-role{font-family:var(--font-body); font-size:0.8rem; color:var(--ink-soft); margin-left:8px;}
#ability-tip .at-body{font-size:0.9rem; color:var(--ink); line-height:1.35;}
#ability-tip .wt-sub{margin-top:8px; padding-top:8px; border-top:1px solid var(--panel-line); font-size:0.82rem; color:var(--gold);}
#wave-tip{
  position:absolute; width:250px;
  background:rgba(18,24,34,0.97); border:3px solid var(--frost); padding:10px 12px;
  box-shadow:var(--pixel-shadow); z-index:6; pointer-events:none;
}
#wave-tip .wt-name{font-family:var(--font-display); font-size:0.58rem; line-height:1.6; color:var(--frost); margin-bottom:6px;}
#wave-tip .wt-role{font-family:var(--font-body); font-size:0.8rem; color:var(--ink-soft); margin-left:8px;}
#wave-tip .wt-grid{display:grid; grid-template-columns:auto 1fr; gap:2px 12px; font-size:0.88rem; color:var(--ink-soft);}
#wave-tip .wt-grid b{color:var(--ink); font-variant-numeric:tabular-nums; text-align:right;}
#wave-tip .wt-sub{margin-top:8px; padding-top:8px; border-top:1px solid var(--panel-line); font-size:0.82rem; color:var(--ink-soft); line-height:1.35;}
#wave-tip .wt-sub b{color:var(--gold);}
/* Barracks label on the tray, and the barricade slot once it is standing */
#shop-overlay::before{
  /* The words come from game.js (applyBarracksLabel), because a ::before is out of reach of
     the translation pass that rewrites the rest of the static text. The fallback is the English,
     so the tab still reads correctly if the property is never set. */
  content: var(--barracks-label, 'BARRACKS · CLICK OR KEY'); position:absolute; top:-9px; left:10px; line-height:1;
  font-family:var(--font-display); font-size:0.3rem; letter-spacing:0.02em; color:#c1cbdc;
  background:rgba(16,20,30,0.95); padding:3px 6px; border:2px solid var(--panel-line);
}
.shop-item.built{opacity:0.55; cursor:default;}
.shop-item.built .shop-cost{color:var(--good);}

/* ---------- are-you-sure modal (sell / move) ---------- */
/* Sits above tower-info (5) and the tip (6) but below the screen overlays (10): it is a question
   about the battlefield, so the battlefield stays visible, dimmed by the paused-state render. */
#confirm-modal{
  position:absolute; inset:0;
  display:flex; align-items:center; justify-content:center;
  background:rgba(6,8,14,0.6);
  z-index:130; /* above every overlay and the banner; under the load screen */
}
.confirm-panel{
  background:var(--sheet-solid, rgba(14,12,26,0.98));
  border:3px solid var(--frame-line, #4a3a7a);
  box-shadow:0 0 0 1px #0b0814, 0 0 0 3px var(--frame-gold, rgba(201,171,88,0.55)), 0 0 28px var(--frame-glow, rgba(138,111,196,0.28)), var(--pixel-shadow);
  padding:18px 20px 16px;
  width:min(380px, 88%);
}
.confirm-panel h3{margin:0 0 10px; font-family:var(--font-display); font-size:0.62rem; line-height:1.6; color:var(--gold);}
.confirm-panel p{margin:0 0 12px; font-size:0.98rem; line-height:1.4; color:var(--ink);}
.confirm-remember{display:flex; align-items:center; gap:8px; font-size:0.85rem; color:var(--ink-soft); margin-bottom:14px; cursor:pointer;}
.confirm-remember input{width:16px; height:16px; margin:0; accent-color:var(--gold); cursor:pointer;}
.confirm-actions{display:flex; gap:8px;}
.confirm-actions .node-buy{flex:1; margin-top:0;}
.confirm-actions .node-buy.ghost{background:rgba(255,255,255,0.05);}
#confirm-modal.hidden{display:none;}
#tower-info .stat-row{display:flex; justify-content:space-between; color:var(--ink); margin:2px 0;}
#tower-info .stat-row span:first-child{color:var(--ink-soft);}
#tower-info .ti-matchup{font-size:0.72rem; color:var(--frost); margin-top:6px; font-style:italic;}
/* Why this tower is underperforming its own listed stats right now. Gold to match the on-canvas
   mechanic badge, so the two read as the same system. */
#tower-info .ti-mechanic{font-size:0.7rem; color:var(--gold); margin-top:5px; line-height:1.45;}
#tower-info .ti-actions{display:flex; flex-wrap:wrap; gap:8px; margin-top:10px;}
#tower-info button{
  flex:1 1 auto;
  min-width:80px;
  border:3px solid var(--panel-line);
  border-radius:0;
  background:#25314a;
  color:var(--ink);
  padding:6px 8px;
  font-family:var(--font-display);
  font-size:0.42rem;
  line-height:1.5;
  box-shadow:inset -3px -3px 0 0 var(--bevel-dk), inset 3px 3px 0 0 var(--bevel-lt);
}
#tower-info button:active{box-shadow:inset 3px 3px 0 0 var(--bevel-dk), inset -3px -3px 0 0 var(--bevel-lt); transform:translateY(2px);}
#tower-info button:hover{background:#324164;}
#tower-info button.sell{background:#3a1f26; border-color:#5c2a34;}
#tower-info button.sell:hover{background:#4d2530;}
#tower-info button:disabled{opacity:0.35; cursor:default;}

#pb-banner{
  position:absolute; top:34%; left:50%; transform:translate(-50%, -50%) scale(0.7);
  font-size:1.7rem; font-weight:800; letter-spacing:0.02em; text-align:center;
  padding:14px 34px; border-radius:14px; pointer-events:none; opacity:0; z-index:7;
  text-shadow:0 2px 8px rgba(0,0,0,0.5); white-space:nowrap;
}
#pb-banner.pb{background:rgba(24,32,48,0.85); border:2px solid var(--frost); color:#eaf6ff;}
#pb-banner.high{background:rgba(48,32,10,0.88); border:2px solid var(--gold); color:#fff6de;}
#pb-banner.rival{background:rgba(24,32,48,0.85); border:2px solid var(--purple); color:#f1e8ff; font-size:1.3rem;}
#pb-banner.show{animation:pb-pop 2.6s ease-out forwards;}
@keyframes pb-pop{
  0%{opacity:0; transform:translate(-50%, -50%) scale(0.7);}
  12%{opacity:1; transform:translate(-50%, -50%) scale(1.08);}
  20%{transform:translate(-50%, -50%) scale(1);}
  80%{opacity:1; transform:translate(-50%, -50%) scale(1);}
  100%{opacity:0; transform:translate(-50%, -55%) scale(1);}
}
@media (prefers-reduced-motion: reduce){
  #pb-banner.show{animation:none; opacity:1; transform:translate(-50%, -50%) scale(1); transition:opacity 2s ease-out 0.6s;}
}
body.reduce-motion #pb-banner.show{animation:none; opacity:1; transform:translate(-50%, -50%) scale(1); transition:opacity 2s ease-out 0.6s;}
.menu-who{
  margin:-2px 0 10px; text-align:center;
  font-size:0.62rem; letter-spacing:0.08em; text-transform:uppercase; color:#8f9ab4;
}
.menu-who strong{color:#c9b6ef; font-weight:700;}

/* ---- the music drawer (2026-10-11) ----
   It slides in from the right edge on hover, as one piece: a tab joined to the panel, not an icon
   floating on its own. Parked, only the tab shows.

   The parked and the open transform MUST sit at the same specificity. The first version of this put
   the parked state on #music-tab (an id) and the open state on .edge-tab.open (a class), so the id
   won both times and the drawer could never move — hover did nothing, clicking the tab did nothing.
   A custom property keeps the geometry per-tab while both transforms stay on the class. */
.edge-tab{
  position:absolute; top:116px; right:0; z-index:118;
  display:flex; align-items:stretch;
  transform:translateX(var(--parked, 0px));
  transition:transform 0.28s ease-out;
}
.edge-tab:hover, .edge-tab:focus-within, .edge-tab.open{transform:translateX(0);}
/* ---- the rail: three drawers, one geometry (2026-10-11) ----
   Sized and placed against the MAIN MENU PANEL, which is 434px tall and vertically centred in the
   790px frame, so it runs 178 to 612. The rail fills exactly that span:

     music     178 .. 314
     log       327 .. 463
     glossary  476 .. 612

   Three identical 136px tabs with 13px of clear air between them. The music tab's top sits on the
   panel's top and the glossary tab's bottom sits on the panel's bottom, to the pixel.

   They were previously content-sized, which is what made them look like three unrelated things:
   an empty Log gave a 75px tab, the music player 135px and the glossary 194px, all at different
   tops. A set of three has to be a set. Height is now fixed and identical, the handle stretches to
   it, and each panel scrolls inside it. */
/* The three tops and the one height come from --rail-top / --tab-h, which layoutEdgeRail() measures
   off the real menu panel every time the layout settles. They are NOT hardcoded, because the panel
   grows a row when there is a run to continue: it was 434px tall with one and 425px without, so a
   fixed 178/612 lined up on one screen and was five pixels out on the other. The defaults below are
   only what the first frame uses before the first measurement lands. */
/* The HANDLE is the standardised part: all three are exactly --tab-h, which is what makes the
   parked rail read as one set. The PANEL sizes to its own content from the same top edge.

   Forcing the panel to the handle's height too was a step too far: the music player needs 195px
   for a name, a seek bar, a transport and a list toggle, and 130px of it clipped the toggle — the
   main reason to open the drawer at all. A panel that is taller than its handle still meets it at
   the top, which was the actual complaint (an empty Log left the handle standing proud with a gap
   where the borders should have joined). Only one drawer is ever open, so a tall panel overlapping
   the parked handles below it costs nothing: moving the pointer off it parks it again. */
/* Parked, the tab is exactly its handle, so the three hit areas are the three handles and nothing
   else. Letting it size to the panel even while parked made each tab's hover zone as tall as its
   OPEN panel — 192px against a 136px handle — so the music tab's invisible box reached down over
   the top of the Log's handle and swallowed the hover meant for it. Open or hovered it grows to
   the panel, which is what makes the panel hoverable. */
/* Parked, all three handles are exactly --tab-h, which is what makes the rail read as one set.
   OPEN, the handle is whatever the panel is — and that is the fix for the last two complaints at
   once. Forcing the panel to the handle clipped the music player's list toggle; leaving the handle
   at --tab-h left an empty Log's handle standing proud below a short panel, with a gap where the
   two borders should have met. Neither is a question of which one is right: they simply have to be
   the same box, and the panel is the one with the content, so the handle follows it.

   --panel-max is measured per tab by layoutEdgeRail(): the distance from that tab's top to the
   bottom of the frame. Without it the Glossary, which is the lowest of the three, opened a 320px
   panel from a top that left under 310px of screen and ran its last letters off the bottom. */
.edge-tab{height:var(--tab-h, 136px); align-items:flex-start;}
.edge-tab:hover, .edge-tab:focus-within, .edge-tab.open{height:auto; align-items:stretch;}
.edge-tab > button{height:var(--tab-h, 136px); flex:0 0 var(--tab-w, 39.6px);}
.edge-tab:hover > button, .edge-tab:focus-within > button, .edge-tab.open > button{height:auto;}
.edge-tab > div{
  min-height:var(--tab-h, 136px); max-height:var(--panel-max, 320px);
  box-sizing:border-box; overflow-y:auto; overflow-x:hidden;
}
#music-tab{--parked:calc(100% - var(--tab-w, 39.6px)); top:var(--rail-top, 178px);}
#log-tab{--parked:calc(100% - var(--tab-w, 39.6px)); top:calc(var(--rail-top, 178px) + var(--tab-h, 136px) + var(--rail-gap, 13px));}
#glossary-tab{--parked:calc(100% - var(--tab-w, 39.6px)); top:calc(var(--rail-top, 178px) + 2 * (var(--tab-h, 136px) + var(--rail-gap, 13px)));}
/* The tab: tall, flush to the edge, open on the side that meets the panel. */
#music-tab-handle{
  flex:0 0 auto; width:var(--tab-w, 39.6px);
  display:flex; align-items:center; justify-content:center;
  background:#171d2a; border:3px solid var(--purple); border-right:none; color:var(--gold);
  padding:0; cursor:pointer;
  box-shadow:var(--pixel-shadow), inset 3px 3px 0 0 var(--bevel-lt);
}
#music-tab-handle:hover, #music-tab-handle:focus-visible{background:var(--frame-glow, rgba(138,111,196,0.25));}
#music-tab-panel{
  width:290px;
  background:rgba(24,28,40,0.97); border:3px solid var(--purple); padding:8px 10px 10px;
  box-shadow:var(--pixel-shadow);
}
#music-tab-panel h4, #log-tab-panel h4, #glossary-tab-panel h4{
  margin:0 0 6px; font-family:var(--font-display); font-size:0.42rem; letter-spacing:0.1em;
  text-transform:uppercase;
}
#music-tab-panel h4{color:var(--gold);}
/* ---- the player (2026-10-12) ----
   Was four stacked boxes of equal weight, which read as a form rather than a transport. Now there
   is an order to it: the name is the loudest thing, the seek bar is a real bar with a position on
   it, the three transport buttons are the only large targets, and the repeat mode has moved up to
   the heading line because it is a setting rather than a control you reach for mid-track. */
#mt-head{display:flex; align-items:center; gap:4px; margin:0 0 5px;}
/* Semi-auto lit, auto unlit: one glance says whether the game is still choosing for you. */
#mt-source.on{border-color:var(--gold); color:var(--gold); background:rgba(240,207,107,0.16);}
#mt-head h4{margin:0 !important; flex:1;}
#mt-now{margin:0 0 6px; min-height:2.1em;}
#mt-now-name{
  font-size:0.86rem; font-weight:700; color:var(--gold); line-height:1.15;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
#mt-now-sub{
  font-size:0.56rem; color:#8f9ab4; letter-spacing:0.04em; text-transform:uppercase;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
#mt-seek{margin:0 0 7px;}
/* Taller, with a knob, so the position is readable at a glance and the bar looks grabbable. */
#mt-bar{
  position:relative; height:8px; background:#0d111a; border:2px solid #2d3650; cursor:pointer;
}
#mt-bar:hover{border-color:var(--purple);}
#mt-bar:focus-visible{border-color:var(--gold); outline:none;}
#mt-bar-fill{position:absolute; inset:0 auto 0 0; width:0; background:var(--purple);}
#mt-bar-knob{
  position:absolute; top:-3px; left:0; width:4px; height:10px; margin-left:-2px;
  background:var(--gold); box-shadow:0 0 0 1px #0d111a;
}
#mt-times{
  display:flex; justify-content:space-between; font-size:0.54rem; color:#6f7a93;
  margin-top:3px; font-variant-numeric:tabular-nums;
}
#mt-controls{display:flex; gap:5px; justify-content:center; margin:0 0 6px;}
#mt-controls button{
  flex:0 0 auto; padding:2px 9px; line-height:0;
  background:none; border:none; color:var(--ink-soft); cursor:pointer; box-shadow:none;
}
/* The glyph never takes the click. The button is the control; the drawing
   inside it is scenery, and letting it be an event target is what made the
   centre of the play button feel dead while its edges worked. */
#mt-controls button svg{display:block; image-rendering:pixelated; pointer-events:none;}
#mt-controls button:hover, #mt-controls button:focus-visible{color:var(--gold); background:none;}
#mt-controls button:active{transform:translateY(1px);}
/* The play/pause button is the one you press most, so it is the one that stands out — by being
   gold and a size larger, not by being in a box. */
#mt-play{color:var(--gold) !important; padding:0 11px !important;}
/* The repeat mode: a small square on the heading line, not a fourth transport button. */
#mt-mode, #mt-source{
  flex:0 0 auto; height:22px; padding:0 4px; line-height:1;
  background:#171d2a; border:2px solid #2d3650; color:var(--gold); font-size:0.78rem; cursor:pointer;
}
#mt-mode{width:26px;}
/* AUTO / SEMI / MAN reads at a glance where a third glyph would have to be learned. */
#mt-source{
  font-family:var(--font-display); font-size:0.38rem; letter-spacing:0.06em; min-width:34px;
}
#mt-source[data-mode="auto"]{color:var(--ink-soft); border-color:#2d3650;}
#mt-source[data-mode="semi"]{color:var(--frost); border-color:var(--frost);}
#mt-source[data-mode="manual"]{color:var(--gold); border-color:var(--gold);}
#mt-mode:hover, #mt-mode:focus-visible,
#mt-source:hover, #mt-source:focus-visible{border-color:var(--gold); background:rgba(240,207,107,0.18);}
#mt-list-toggle{
  display:block; width:100%; text-align:left; padding:5px 8px; margin:0 0 6px;
  background:rgba(16,20,30,0.85); border:2px solid #2d3650; color:#c1cbdc;
  font-size:0.62rem; letter-spacing:0.06em; text-transform:uppercase; cursor:pointer;
}
#mt-list-toggle:hover, #mt-list-toggle:focus-visible{border-color:var(--purple); color:var(--gold);}
#mt-list-caret{display:inline-block; transition:transform 0.18s ease-out;}
#mt-list-toggle[aria-expanded="true"] #mt-list-caret{transform:rotate(90deg);}
#music-tab-list.collapsed{display:none;}
.mt-item{
  display:block; width:100%; text-align:left; margin:0 0 4px; padding:6px 8px;
  background:rgba(16,20,30,0.85); border:2px solid #2d3650; color:var(--ink);
  font-size:0.72rem; line-height:1.25; cursor:pointer;
}
.mt-item:hover:not(.locked), .mt-item:focus-visible:not(.locked){background:var(--frame-glow, rgba(138,111,196,0.22)); border-color:var(--purple);}
.mt-item.on{border-color:var(--gold); background:rgba(240,207,107,0.16);}
.mt-item.locked{opacity:0.45; cursor:default; font-style:italic;}
.mt-name{display:block; font-weight:700;}
.mt-sub{display:block; font-size:0.6rem; color:#a9b2c8;}
.mt-item.on .mt-sub{color:#e8d9a8;}
.mt-head{margin:8px 0 5px; font-size:0.56rem; letter-spacing:0.08em; text-transform:uppercase; color:#8f9ab4;}
#music-tab-foot{margin:6px 0 0; font-size:0.58rem; color:#6f7a93; line-height:1.3;}
@media (prefers-reduced-motion: reduce){ .edge-tab{transition:none;} #mt-list-caret{transition:none;} }
/* ---- 7: "drawers slide" off ----
   The parked transform stays (that is the geometry, not the animation); only the tween goes. */
body.no-slide .edge-tab{transition:none;}
body.no-slide #mt-list-caret{transition:none;}
body.reduce-motion .edge-tab{transition:none;}

/* ---- the Log drawer (2026-10-11) ----
   Deliberately the music tab's geometry with a different colour: one 44px handle, the panel the
   same width, parked the same way, so the right edge of the screen reads as two of the same thing
   rather than two unrelated things of different sizes. */

#log-tab-handle{
  flex:0 0 auto; width:var(--tab-w, 39.6px);
  display:flex; align-items:center; justify-content:center;
  background:#1d1a12; border:3px solid var(--gold); border-right:none; color:var(--gold);
  padding:0; cursor:pointer;
  box-shadow:var(--pixel-shadow), inset 3px 3px 0 0 var(--bevel-lt);
}
#log-tab-handle:hover, #log-tab-handle:focus-visible{background:rgba(240,207,107,0.22);}
#log-tab-handle.fresh{background:rgba(240,207,107,0.3);}
#log-tab-panel{
  width:290px;
  background:rgba(26,24,18,0.97); border:3px solid var(--gold); padding:8px 10px 10px;
  box-shadow:var(--pixel-shadow);
}
#log-tab-panel h4{
  margin:0 0 8px; font-family:var(--font-display); font-size:0.46rem; letter-spacing:0.1em;
  color:var(--gold); text-transform:uppercase;
}
/* The newest notice, in the drawer rather than flying across the screen. */
#log-list{display:flex; flex-direction:column; gap:4px;}
.log-row{
  padding:5px 8px; background:rgba(16,14,10,0.8); border-left:3px solid #3d3522;
  font-size:0.74rem; line-height:1.3; color:var(--ink-soft);
}
.log-row.ach{border-left-color:var(--gold); color:#f3e6c2;}
/* An achievement row goes somewhere, so it has to look like it does. */
/* A grid, not a flex row. As a flex row with margin-left:auto on the caret, a notice long enough
   to wrap in a 290px drawer pushed the caret onto its own line underneath — which reads as a
   second, separate button below the entry rather than as one row you can click. */
button.log-row{display:grid; grid-template-columns:1fr auto; align-items:start; gap:6px; width:100%; text-align:left; cursor:pointer; font:inherit;}
button.log-row:hover, button.log-row:focus-visible{background:rgba(240,207,107,0.16); border-left-color:#fff8e6;}
.log-go{color:var(--gold); align-self:center;}
.log-row.deny{border-left-color:#8a3f4c;}
.log-when{display:block; font-size:0.58rem; color:#8f8666; letter-spacing:0.04em;}
.log-empty{font-size:0.74rem; color:#8f8666; font-style:italic;}
/* A toast now lasts twice as long, stays put while the pointer is on it, and opens the Log when
   clicked — so it had to stop being click-through. It is display:none while hidden, so it cannot
   be in the way of the battlefield except in the seconds it is actually saying something. */
/* The Glossary drawer: the same block as the other two, in the realm green. */
#glossary-tab-handle{
  flex:0 0 auto; width:var(--tab-w, 39.6px);
  display:flex; align-items:center; justify-content:center;
  background:#111d16; border:3px solid var(--lexicon); border-right:none; color:var(--lexicon);
  padding:0; cursor:pointer;
  box-shadow:var(--pixel-shadow), inset 3px 3px 0 0 var(--bevel-lt);
}
#glossary-tab-handle:hover, #glossary-tab-handle:focus-visible{background:rgba(127,191,127,0.22);}
#glossary-tab-panel{
  width:290px;
  background:rgba(18,26,21,0.97); border:3px solid var(--lexicon); padding:8px 10px 10px;
  box-shadow:var(--pixel-shadow);
}
#glossary-tab-panel h4{
  margin:0 0 8px; font-family:var(--font-display); font-size:0.46rem; letter-spacing:0.1em;
  color:var(--lexicon); text-transform:uppercase;
}
/* A glossary is a reference, so nothing is open until it is asked for: a letter, then a word. */
.gl-letter{margin:0 0 4px;}
.gl-letter-head{
  display:flex; align-items:center; gap:8px; width:100%; text-align:left; padding:5px 8px;
  background:rgba(14,20,16,0.85); border:2px solid #2a3a30; color:var(--ink);
  font-size:0.74rem; cursor:pointer;
}
.gl-letter-head:hover, .gl-letter-head:focus-visible{border-color:var(--lexicon); color:#dff5e4;}
.gl-letter-head b{font-family:var(--font-display); font-size:0.52rem; color:var(--lexicon); font-weight:400; min-width:1.2em;}
.gl-letter-head span{font-size:0.66rem; color:#8fa89a;}
.gl-caret{margin-left:auto;}
.gl-words{padding:4px 0 4px 10px; display:flex; flex-direction:column; gap:3px;}
.gl-word-head{
  display:flex; align-items:center; gap:8px; width:100%; text-align:left; padding:4px 8px;
  background:rgba(14,20,16,0.7); border:2px solid #243029; color:var(--ink-soft);
  font-size:0.72rem; cursor:pointer;
}
.gl-word-head:hover, .gl-word-head:focus-visible{border-color:var(--lexicon); color:var(--ink);}
.gl-word.open .gl-word-head{color:#dff5e4; border-color:var(--lexicon);}
.gl-def{margin:3px 0 6px 8px; font-size:0.74rem; line-height:1.35; color:#b6c6bb; border-left:2px solid #2a3a30; padding-left:8px;}
.gl-note{margin:8px 0 0; font-size:0.66rem; color:#8fa89a; font-style:italic;}
.gl-empty{font-size:0.74rem; color:#8fa89a; font-style:italic;}
/* The same list as a chapter of the Encyclopaedia, on a page rather than in a drawer. */
.gl-book{max-width:560px;}
#toast{
  position:absolute;
  top:14px; left:50%;
  transform:translateX(-50%);
  background:rgba(18,24,34,0.92);
  border:2px solid var(--panel-line);
  color:var(--ink);
  padding:8px 16px;
  border-radius:20px;
  font-size:0.85rem;
  pointer-events:auto;
  cursor:pointer;
  transition:opacity .3s ease;
  z-index:6;
}
#toast:hover{border-color:var(--gold-dim); background:rgba(26,32,44,0.96);}
/* ---- 29: keyboard navigation (2026-10-11) ----
   Every control in the game is a real <button>, <input> or <select>, so Tab order already worked;
   what was missing was being able to SEE where the keyboard was. One ring, gold, on everything,
   and only for :focus-visible so a mouse click never draws it. */
:focus-visible{outline:3px solid var(--gold); outline-offset:2px;}
button:focus-visible, .mt-item:focus-visible, .opt-keybind:focus-visible,
.shop-item:focus-visible, .node-buy:focus-visible, .sg-jump-btn:focus-visible{outline:3px solid var(--gold); outline-offset:2px;}
.hidden{display:none !important;}

/* ---------- shop overlay (floats on the battlefield) ---------- */
#shop-overlay{
  position:absolute;
  left:50%;
  bottom:6px;
  transform:translateX(-50%);
  display:flex;
  gap:8px;
  background:rgba(14,19,28,0.62);
  border:2px solid var(--panel-line);
  border-radius:0;
  padding:6px;
  backdrop-filter:blur(2px);
  z-index:4;
}
.shop-item{
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:2px;
  background:var(--ground-85, rgba(20,27,39,0.85));
  border:2px solid var(--panel-line);
  border-radius:6px;
  position:relative; /* the hotkey badge sits in the corner */
  padding:1px 0 2px; width:55px; height:44px; box-sizing:border-box; justify-content:flex-end; /* 2026-09-22: 55 wide (easy to hit), 44 tall — the same footprint as a keep ability */
  color:var(--ink);
}
.shop-item canvas{width:26px; height:28px; image-rendering:pixelated;}
/* The hotkey, tucked into the corner rather than given a line of its own — the slot is 55x44 and
   every pixel of it was already spoken for. Ghost-coloured so it reads as a label on the button
   and not as part of the price. */
.shop-key{
  position:absolute; top:1px; right:3px; pointer-events:none;
  font-family:var(--font-display); font-size:0.34rem; line-height:1;
  color:var(--ghost); letter-spacing:0;
}
.shop-item.selected .shop-key{color:var(--gold);}
.shop-item .shop-cost{font-size:0.68rem; line-height:1; color:var(--gold); font-variant-numeric:tabular-nums; font-weight:700;}
.shop-item:hover{border-color:var(--frost);}
.shop-item.selected{border-color:var(--gold); background:rgba(36,29,15,0.9);}
.shop-item:disabled, .shop-item.unaffordable{opacity:0.4;}

/* ---------- the opening: the attract shot, the profile gate, the boot fade (2026-10-12) ----------
   Three states live on #title-screen: .attract (the wide room and one line), .gate (the camera has
   arrived, ask whose save) and neither (the menu). Everything here is driven off those. */
#boot-veil{
  position:absolute; inset:0; background:#05060b; z-index:400; pointer-events:none;
  opacity:1; transition:opacity 1.3s ease-out;
}
#boot-veil.gone{opacity:0;}
/* The whole opening layer fades as one. The title is on it rather than on the menu panel, so
   pressing a key both starts the camera and takes the title card away with it — the menu arrives
   into a window you have already been told the name of. */
#attract-layer{
  position:absolute; inset:0; z-index:12; pointer-events:none;
  opacity:0; visibility:hidden;
  transition:opacity 0.55s ease, visibility 0s linear 0.55s;
}
#title-screen.attract #attract-layer{
  opacity:1; visibility:visible;
  transition:opacity 0.45s ease, visibility 0s;
}
/* Across the wall above the window. The wide shot leaves roughly the top fifth of the frame as
   bare stone at every aspect the game runs at, which is what this sits in. */
#attract-head{position:absolute; left:0; right:0; top:3.5%; text-align:center;}
#attract-title{
  margin:0 0 0.5rem; font-family:var(--font-display);
  font-size:1.95rem; line-height:1.2; letter-spacing:0.015em; color:#fdf4de;
  text-shadow:0 0 30px rgba(255,186,110,0.5), 0 0 10px rgba(255,170,90,0.35), 4px 4px 0 rgba(0,0,0,0.8);
}
#attract-motto{
  font-family:var(--font-display); font-size:0.46rem; letter-spacing:0.26em;
  color:var(--gold); line-height:1.6;
  text-shadow:0 0 14px rgba(255,186,110,0.4), 2px 2px 0 rgba(0,0,0,0.85);
}
#attract-prompt{
  position:absolute; left:0; right:0; bottom:10%;
  text-align:center;
  font-family:var(--font-display); font-size:0.52rem; letter-spacing:0.18em;
  color:#ffdca8; text-shadow:0 0 12px rgba(255,170,90,0.45), 3px 3px 0 rgba(0,0,0,0.7);
}
/* The pulse is on the span, not the prompt, so it multiplies with the layer's fade instead of
   fighting it. */
#attract-prompt span{display:inline-block; animation:attract-pulse 2.4s ease-in-out infinite;}
@keyframes attract-pulse{0%,100%{opacity:0.34;} 50%{opacity:1;}}
#menu-gate{
  position:relative; z-index:12; text-align:center;
  background:rgba(10,14,20,0.82); border:4px solid var(--panel-line);
  box-shadow:var(--pixel-shadow); padding:20px 30px; max-width:420px;
}
#menu-gate .gate-line{font-size:1rem; color:#e3dcc9; margin:0 0 2px;}
#menu-gate .gate-line strong{color:var(--gold); font-weight:700;}
#menu-gate .gate-q{
  font-family:var(--font-display); font-size:0.44rem; letter-spacing:0.08em;
  color:var(--frost); line-height:1.6; margin:0 0 14px;
}
#menu-gate .gate-actions{display:flex; gap:10px; justify-content:center;}
#menu-gate .gate-actions .big-btn{min-width:104px;}
/* The gate is the only one that is display-switched; the opening layer fades. */
#menu-gate{display:none;}
#title-screen.gate #menu-gate{display:block;}
#title-screen.attract .overlay-content,
#title-screen.zooming .overlay-content,
#title-screen.gate .overlay-content{display:none;}
/* The right-hand rail belongs to the menu, not to the room: it would otherwise sit over the wide
   shot pointing at a panel that is not there yet. */
body.attract-mode .edge-tab{display:none;}
@media (prefers-reduced-motion: reduce){ #attract-prompt span{animation:none; opacity:0.95;} #boot-veil{transition:none;} #attract-layer{transition:opacity 0s, visibility 0s;} }
body.reduce-motion #attract-prompt span{animation:none; opacity:0.95;}

/* ---------- overlays ---------- */
.overlay{
  position:absolute; inset:0;
  display:flex;
  align-items:center;
  justify-content:center;
  z-index:10;
}
.overlay-bg{
  position:absolute; inset:0;
  width:100%; height:100%;
  image-rendering:pixelated;
}
.overlay-content{
  position:relative;
  text-align:center;
  background:rgba(10,14,20,0.72);
  padding:18px 30px;
  border:4px solid var(--panel-line);
  box-shadow:var(--pixel-shadow);
  max-width:520px;
}
.overlay .eyebrow{
  font-family:var(--font-display);
  font-size:0.42rem;
  letter-spacing:0.1em;
  color:var(--gold);
  margin-bottom:10px;
  line-height:1.5;
}
.overlay h1{
  font-family:var(--font-display);
  font-size:1.05rem;
  line-height:1.3;
  margin:0 0 10px;
  color:#fbf7ec;
  text-shadow:3px 3px 0 rgba(0,0,0,0.6);
  text-wrap:balance;
}
.overlay p{color:#e3dcc9; margin:0 0 10px; line-height:1.25; font-size:0.95rem;}
.overlay .howto{font-size:0.78rem; color:#c7bfa9;}
.overlay .xp-line{color:var(--purple); font-weight:600;}
.profile-line{font-size:0.8rem; margin-bottom:4px !important;}
.profile-line strong{color:var(--frost);}
.link-btn{
  background:none;
  border:none;
  color:var(--frost);
  text-decoration:underline;
  font-size:0.95rem;
  font-family:var(--font-body);
  font-weight:600;
  padding:0;
}
.link-btn:hover{color:#8fcbe8;}
.title-actions{display:flex; flex-direction:column; gap:6px; width:min(280px, 78vw); margin:0 auto 12px;}
.big-btn{
  background:var(--gold-dim);
  border:3px solid var(--gold);
  color:#fff8e6;
  font-size:0.52rem;
  line-height:1.5;
  padding:9px 18px;
}
.big-btn:hover{background:#a67c22;}
.big-btn:active{transform:translateY(2px);}
/* ---- 6: "buttons move when pressed" off (2026-10-11) ----
   Every button in the game sinks 2px on :active. It is a nice bit of weight and it is also the
   whole interface physically jumping under the pointer. Off, the press is said in colour instead,
   so the feedback is not lost — it just stops moving. */
body.no-press-move .big-btn:active,
body.no-press-move .danger-btn:active,
body.no-press-move .rail-btn:active,
body.no-press-move #tower-info button:active,
body.no-press-move #btn-start-wave:active,
body.no-press-move #btn-speed:active,
body.no-press-move #btn-pause:active,
body.no-press-move #btn-mercantile:active,
body.no-press-move .profile-stats:active,
body.no-press-move .node-buy:active,
body.no-press-move .shop-item:active,
body.no-press-move .opt-choice-btn:active{transform:none;}
body.no-press-move .big-btn:active,
body.no-press-move .node-buy:active,
body.no-press-move .opt-choice-btn:active{filter:brightness(1.35);}
.big-btn.ghost-btn{
  background:rgba(255,255,255,0.05);
  border-color:var(--purple);
  color:#e4dcf5;
}
.big-btn.ghost-btn:hover{background:var(--frame-glow, rgba(138,111,196,0.25));}
.title-actions .big-btn{position:relative; text-align:left; padding-left:36px;}
.title-actions .big-btn::before{
  content:'\25B8'; position:absolute; left:14px; top:50%; transform:translateY(-50%);
  opacity:0; color:var(--gold);
}
.title-actions .big-btn:hover::before, .title-actions .big-btn:focus-visible::before{opacity:1;}
.title-actions .big-btn.ghost-btn::before{color:var(--purple);}
/* Continue Run only exists when there's a saved run, so when it's there it IS the thing the player
   came back for. Its own green accent rather than gold (start something new) or purple (menus), and
   its marker stays lit instead of waiting for hover. */
.big-btn.continue-btn{
  background:rgba(48,104,64,0.55);
  border-color:#6fbf84;
  color:#eaffef;
}
.big-btn.continue-btn:hover{background:rgba(68,146,88,0.72);}
.title-actions .big-btn.continue-btn::before{color:#8fe0a6; opacity:1;}
/* End Run sits in the same column as the other pause buttons, so it takes their size, alignment
   and hover marker — only the colour says "careful". */
.big-btn.danger-btn{background:#3a1f26; border-color:#5c2a34; color:#f2c9d0; font-size:0.52rem; font-weight:400;}
.big-btn.danger-btn:hover{background:#4d2530;}
.title-actions .big-btn.danger-btn::before{color:#e08a9a;}
/* Achievements + Leaderboards: a pair of pixel icons pinned to the panel's top-right corner, out of
   the main list (that list is for choosing what to play; these are for looking back). The word
   appears under the icon on hover/focus so nobody has to guess a glyph. */
.title-rail{display:flex; justify-content:flex-end; gap:6px; margin:-8px -8px 8px 0;} /* in the flow (2026-09-20): it used to float over the eyebrow */
.rail-btn{
  position:relative; width:44px; height:44px; display:flex; align-items:center; justify-content:center;
  background:#171d2a; border:3px solid var(--purple); color:var(--gold); padding:0;
  box-shadow:var(--pixel-shadow), inset -3px -3px 0 0 var(--bevel-dk), inset 3px 3px 0 0 var(--bevel-lt);
}
.rail-btn:hover, .rail-btn:focus-visible{background:var(--frame-glow, rgba(138,111,196,0.25));}
.rail-btn:active{transform:translateY(2px);}
/* Exit to Desktop, in the corner of the screen rather than on the panel — out on the stone,
   clear of everything the menu is for. The id beats .rail-btn's display:flex, which is what lets
   the three opening states hide it; .hidden still wins when the host cannot quit at all. */
/* --frame-in-x/y is the visible frame's offset from the overlay, measured in layoutEdgeRail().
   The overlays are absolutely positioned but #game-root is not positioned, so .overlay{inset:0}
   resolves against the viewport, not against the frame — the overlay is BIGGER than the picture,
   by however much letterboxing the window happens to need. Anchoring a corner button to the
   overlay's corner therefore hangs it over the frame's border, which is exactly what it did. */
#btn-exit-game{
  position:absolute; z-index:13; border-color:#8a3f4c; color:#e3909c;
  left:calc(var(--frame-in-x, 0px) + 14px);
  top:calc(var(--frame-in-y, 0px) + 14px);
}
#btn-exit-game:hover, #btn-exit-game:focus-visible{background:rgba(190,80,100,0.22);}
#btn-exit-game .rail-label{left:0; transform:none; border-color:#8a3f4c;}
#title-screen.attract #btn-exit-game,
#title-screen.zooming #btn-exit-game,
#title-screen.gate #btn-exit-game{display:none;}
/* The player's name takes the panel's top-left, and carries the auto margin that pushes the three
   record buttons to the right. */
.title-rail .menu-who{
  /* margin:0 first — the standalone .menu-who rule carries a 10px bottom margin from when this
     was a sentence under the title, and a flex item is centred on its MARGIN box, so that alone
     sat the name six pixels above the icons it is meant to line up with. */
  margin:0 auto 0 0; align-self:center; padding:0 2px;
  font-family:var(--font-display); font-size:0.44rem; line-height:1.6;
  letter-spacing:0.04em; max-width:52%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* A box round the name, sunk into the panel. It is the one thing on this
   screen that is about the person playing rather than about the game, and
   as bare text on the panel it read as a caption. */
.title-rail .menu-who{
  background:var(--panel-sunk, rgba(0,0,0,0.34));
  border:2px solid var(--panel-line);
  padding:3px 8px; border-radius:0;
  box-shadow:inset 2px 2px 0 0 rgba(0,0,0,0.35);
}
.title-rail .menu-who strong{color:#c9b6ef; font-weight:400;}
#btn-open-leaderboard.rail-btn{color:#e8c25a;}
#btn-open-achievements.rail-btn{color:#ffd34d;}
.rail-label{
  position:absolute; top:100%; left:50%; transform:translateX(-50%); margin-top:6px;
  font-family:var(--font-display); font-size:0.38rem; letter-spacing:0.06em; white-space:nowrap;
  color:#e4dcf5; background:rgba(10,14,20,0.92); border:2px solid var(--purple); padding:4px 6px;
  opacity:0; pointer-events:none;
}
.rail-btn:hover .rail-label, .rail-btn:focus-visible .rail-label{opacity:1;}
/* The label is centred under its button, which means half of it hangs outside the rail at either
   end. It is absolutely positioned and invisible until hovered, so it looked free — but a laid-out
   box past the right edge still counts towards the panel's scrollWidth, and once Text and panel
   size actually worked the menu panel started reporting 362px of content in a 340px box and
   scrolling sideways. The outermost labels tuck in instead of hanging over. */
.title-rail > .rail-btn:last-child .rail-label{left:auto; right:0; transform:none;}
.title-rail > .rail-btn:first-of-type .rail-label{left:0; transform:none;}
@media (max-width:640px){ .title-rail{justify-content:center; margin:0 0 10px;} }
/* Second line on the Continue and Save & Quit buttons: says exactly which run / which wave, so
   neither button is a leap of faith. */
/* Continue lives on each mode's landing page (2026-09-15), in the header beside the title. */
.st-header .mode-continue{padding:8px 16px; text-align:left; min-width:220px;}
.st-header:has(.mode-continue:not(.hidden)){align-items:center;}
#continue-story-detail, #continue-infinite-detail, #save-quit-detail{
  display:block;
  font-family:var(--font-body);
  font-size:0.78rem;
  letter-spacing:0;
  color:#a9d9b8;
  margin-top:3px;
}

/* ---------- skill tree ---------- */
.skilltree-panel{
  position:relative;
  width:min(760px, 92vw);
  max-height:88vh;
  display:flex;
  flex-direction:column;
  background:var(--sheet, rgba(12,16,24,0.92));
  border:4px solid var(--panel-line);
  box-shadow:var(--pixel-shadow);
  padding:22px 26px;
  gap:14px;
}
.st-header{display:flex; align-items:baseline; justify-content:space-between;}
.st-header h1{font-family:var(--font-display); font-size:1rem; line-height:1.6; margin:0; color:#fbf7ec;}
.st-profile{font-size:0.85rem; color:var(--ink-soft); margin-top:4px;}
.st-profile span{color:var(--frost); font-weight:600;}
.st-xp{color:var(--purple); font-weight:700; font-variant-numeric:tabular-nums; font-size:0.9rem;}
.st-xp-earned{color:var(--ink-soft); font-weight:400; font-size:0.82rem;}
.st-footer{display:flex; gap:12px; align-items:center;}
.st-footer .big-btn{flex:1;}
.st-footer .danger-btn{white-space:nowrap;}
.st-tabs{display:flex; gap:6px; align-items:stretch;}
.st-tab{
  flex:1;
  background:var(--panel);
  border:3px solid var(--panel-line);
  color:var(--ink-soft);
  padding:9px 4px;
  font-size:0.46rem;
  line-height:1.5;
}
.st-tab.active{background:var(--gold-dim); border-color:var(--gold); color:#fff8e6;}
/* ---- the League's own tab ----
   Pride of place means it does not look like its neighbours: a shield, a gold rim, its name in the
   display face, and a rule in the gap after it so the eye reads one hall and then five ledgers.
   It sizes to its content rather than taking a sixth of the row; the five ledgers keep flex:1 and
   stay equal to each other (measured 152px against 5 x 115px at 1400 wide). */
.st-tab.lg-tab{
  position:relative; flex:0 0 auto;
  display:flex; align-items:center; justify-content:center; gap:7px;
  padding:9px 15px; margin-right:9px;
  background:var(--panel); border-color:var(--gold-dim); color:var(--gold);
  font-family:var(--font-display); letter-spacing:0.07em;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,0.45);
}
.st-tab.lg-tab svg{display:block; width:13px; height:13px; flex:0 0 auto; image-rendering:pixelated;}
.st-tab.lg-tab:hover, .st-tab.lg-tab:focus-visible{border-color:var(--gold); color:#fff8e6;}
.st-tab.lg-tab.active{background:var(--gold-dim); border-color:var(--gold); color:#fff8e6; box-shadow:inset 0 0 0 1px rgba(0,0,0,0.3);}
.st-tab.lg-tab::after{content:''; position:absolute; right:-9px; top:1px; bottom:1px; width:2px; background:var(--panel-line);}
#skilltree-screen .skilltree-panel{width:min(1300px, 97vw); height:min(820px, 94vh); max-height:94vh;}
#sg-reveal-toast{
  position:absolute; top:10px; left:50%; transform:translate(-50%,-14px); opacity:0;
  background:rgba(24,28,40,0.94); border:2px solid var(--gold); border-radius:10px;
  padding:8px 20px; font-weight:700; color:var(--gold); font-size:0.9rem;
  pointer-events:none; z-index:6; transition:opacity 0.3s, transform 0.3s; white-space:nowrap;
}
#sg-reveal-toast.show{opacity:1; transform:translate(-50%,0);}
@media (prefers-reduced-motion: reduce){
  #sg-reveal-toast{transition:opacity 0.3s;}
}
body.reduce-motion #sg-reveal-toast{transition:opacity 0.3s;}

/* ---------- skill tree: tiered flow ---------- */
/* 2026-09-15: replaced the radial "sphere grid" entirely — it packed an ever-growing number of
   chains (Game Changers alone reached 10) around a fixed-size ring, so labels overlapped no matter
   how far you zoomed in. This layout instead gives every skill LINE its own horizontal row
   (Tier 1 -> Tier 2 -> Tier 3, left to right); rows just stack vertically and the list scrolls, so
   crowding is structurally impossible regardless of how many chains a tree ever grows to. */
/* ---------- skill tree: one page ----------
   2026-09-15 overhaul. One scrolling page, a section per tree, a jump bar above. Colour is scarce
   on purpose: cards are neutral; each tree's accent appears only in its header, its trunk and the
   badge of a learned node; gold means maxed, or "you can buy this now"; grey means locked. Nothing
   else gets a colour. Buying is the + on the card. */
.sg-jump{display:flex; flex-wrap:wrap; gap:6px;}
.sg-jump-btn{background:var(--panel); border:2px solid var(--panel-line); color:var(--ink-soft); font-family:var(--font-display); font-size:0.38rem; letter-spacing:0.04em; padding:7px 9px; cursor:pointer;}
.sg-jump-btn:hover{color:var(--ink); border-color:var(--ink-soft);}
.sg-board{display:grid; grid-template-columns:1fr 280px; gap:16px; flex:1; min-height:0;}
@media (max-width:820px){ .sg-board{grid-template-columns:1fr;} }
.sg-flow-wrap{display:flex; flex-direction:column; min-height:0; background:var(--panel-sunk, #141b27); border:3px solid var(--panel-line); box-shadow:var(--pixel-shadow);}
.sg-flow{flex:1; min-height:0; overflow:auto; padding:6px 14px 18px; display:flex; flex-direction:column; gap:22px;}
.sg-tree{--tree-accent:var(--gold); position:relative; padding-left:26px;
  background:linear-gradient(90deg, transparent 8px, var(--tree-accent) 8px, var(--tree-accent) 11px, transparent 11px) no-repeat 0 36px / 100% calc(100% - 36px);}
.sg-tree[data-tree="tower"]{--tree-accent:var(--tower-accent);}
.sg-tree[data-tree="enemy"]{--tree-accent:var(--ascension-accent);}
.sg-tree[data-tree="changer"]{--tree-accent:var(--changer-accent);}
.sg-tree[data-tree="orders"]{--tree-accent:var(--orders-accent);}
.sg-tree[data-tree="keep"]{--tree-accent:var(--keep-accent);}
.sg-tree[data-tree="favour"]{--tree-accent:var(--gold);}
.sg-tree-head{display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 12px; margin:0 0 10px 0; padding:8px 0 6px; border-bottom:2px solid var(--panel-line); position:sticky; top:0; background:var(--panel-sunk, #141b27); z-index:1;}
.sg-tree-head h3{margin:0; font-family:var(--font-display); font-size:0.66rem; color:var(--tree-accent);}
.sg-tree-pct{font-size:0.8rem; color:var(--ink-soft); font-variant-numeric:tabular-nums;}
.sg-tree-note{flex-basis:100%; font-size:0.8rem; color:var(--ink-soft);}
.sg-tree > .sg-tree-head{margin-left:-26px;} /* only inside a bough (which pads 26px for its rail); the Favour page has no rail, and the header was off-screen left (2026-09-21) */
.sg-tree > .sg-tree-body{margin-left:-14px;}
.sg-row{display:flex; align-items:center; position:relative; margin-bottom:10px;}
.sg-row::before{content:''; flex:0 0 auto; width:14px; height:3px; background:var(--tree-accent); opacity:0.7;}
.sg-connector{width:28px; height:3px; flex:0 0 auto; background:repeating-linear-gradient(90deg, #3a4658 0 5px, transparent 5px 9px);}
.sg-connector.charged{background:repeating-linear-gradient(90deg, var(--tree-accent) 0 5px, transparent 5px 9px);}
.sg-connector.maxed{background:repeating-linear-gradient(90deg, var(--gold) 0 5px, transparent 5px 9px);}
.sg-card{
  position:relative; display:flex; align-items:center; gap:8px; text-align:left;
  width:236px; min-width:236px; padding:7px 8px 7px 10px;
  background:#1c2434; border:2px solid #3a4658; color:var(--ink); cursor:pointer; flex-shrink:0;
}
.sg-card.tier3{width:250px; min-width:250px;}
.sg-card:hover{border-color:var(--ink-soft);}
.sg-card.selected{border-color:#fff8e6;}
.sg-card.maxed{border-color:var(--gold);}
.sg-card-badge{width:34px; height:34px; flex:0 0 auto; display:flex; align-items:center; justify-content:center;
  background:var(--panel-sunk, #141b27); border:2px solid #3a4658; font-family:var(--font-display); font-size:0.56rem; color:var(--ink-soft);}
.sg-card.learned .sg-card-badge{background:var(--tree-accent); border-color:var(--tree-accent); color:var(--panel-deep, #0f141d);}
.sg-card.maxed .sg-card-badge{background:var(--gold); border-color:var(--gold); color:var(--panel-deep, #0f141d);}
.sg-card-body{min-width:0; flex:1;}
.sg-card-name{font-size:0.72rem; color:var(--ink); font-weight:700; line-height:1.25; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.sg-card-sub{font-size:0.6rem; color:var(--ink-soft); margin-top:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.sg-card-sub .sg-mastery-badge{color:var(--gold);}
/* the buy control lives on the card: + with the cost under it; gold when affordable */
.sg-buy{flex:0 0 auto; width:48px; height:38px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:0;
  background:var(--panel-sunk, #141b27); border:2px solid #3a4658; color:var(--ink-soft); font-family:var(--font-display); font-size:0.7rem; line-height:1; cursor:pointer; padding:0;}
.sg-buy small{font-family:var(--font-body); font-size:0.66rem; letter-spacing:0; margin-top:1px;}
.sg-card.affordable .sg-buy{background:var(--gold-dim); border-color:var(--gold); color:#fff8e6;}
.sg-card.affordable .sg-buy:hover{background:#a67c22;}
.sg-buy:disabled{cursor:default; opacity:0.55;}
.sg-buy.max{font-size:0.4rem; color:var(--gold); border-color:var(--gold); background:transparent; cursor:default;}
.sg-card.ghost{background:transparent; border:2px dashed var(--ghost); cursor:pointer;}
.sg-card.ghost .sg-card-badge{background:transparent; border-color:var(--ghost); color:var(--ghost);}
.sg-card.ghost .sg-card-name{color:var(--ghost); font-style:italic;}
.sg-card.ghost .sg-card-sub{color:var(--ghost);}
.sg-info{--tree-accent:var(--gold); background:var(--panel-sunk, #141b27); border:4px solid var(--panel-line); box-shadow:var(--pixel-shadow); padding:16px;
  display:flex; flex-direction:column; gap:10px; align-self:start; overflow-y:auto; max-height:480px;}
.sg-info-tier{font-size:0.66rem; letter-spacing:0.08em; text-transform:uppercase; color:var(--tree-accent);}
.sg-info h4{margin:0; font-size:1rem; color:var(--ink);}
.sg-info p{margin:0; font-size:0.82rem; color:var(--ink-soft); line-height:1.45;}
.sg-info-cost{margin-top:auto; padding-top:10px; border-top:1px solid var(--panel-line); display:flex; align-items:center; justify-content:space-between; gap:8px;}
.sg-info-cost .cost-amt{font-size:0.76rem; color:var(--ink-soft);}
.sg-info-cost .node-buy{margin-top:0;}
.sg-info-empty{color:var(--ink-soft); font-size:0.85rem; font-style:italic; margin:auto 0;}
.sg-lock-badge{align-self:flex-start; font-size:0.62rem; letter-spacing:0.08em; text-transform:uppercase; color:var(--ghost); border:2px solid var(--ghost); border-radius:0; padding:2px 7px;}

.node-pips{display:flex; gap:3px; flex-wrap:wrap;}
.node-pip{width:11px; height:11px; border-radius:50%; background:#2a3444; border:1px solid var(--panel-line);}
.node-pip.filled{background:var(--gold); border-color:var(--gold);}
.node-buy{
  margin-top:auto;
  background:#25314a;
  border:3px solid var(--panel-line);
  color:var(--ink);
  padding:8px 10px;
  font-size:0.55rem;
  font-weight:700;
}
.node-buy:hover{background:#324164;}
.node-buy:disabled{opacity:0.4; cursor:default;}
.node-buy.maxed{background:var(--good); border-color:var(--good); color:#0c1a10; opacity:1;}

/* ---------- profile switcher ---------- */
.profile-list{
  display:flex;
  flex-direction:column;
  gap:8px;
  overflow-y:auto;
  max-height:50vh;
}
.profile-row{
  display:flex;
  align-items:center;
  gap:10px;
  background:var(--panel-sunk, #141b27);
  border:2px solid var(--panel-line);
  border-radius:8px;
  padding:4px 8px 4px 4px;
}
.profile-row.active{border-color:var(--gold);}
.profile-select{
  flex:1;
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  gap:2px;
  background:none;
  border:none;
  color:var(--ink);
  padding:8px 10px;
  text-align:left;
}
.profile-select span{font-size:0.76rem; color:var(--ink-soft); font-variant-numeric:tabular-nums;}
.profile-del{
  background:none;
  border:none;
  color:var(--ink-soft);
  font-size:1.2rem;
  line-height:1;
  padding:6px 10px;
  border-radius:6px;
}
.profile-stats{
  background:none;
  border:2px solid var(--panel-line);
  color:var(--frost);
  font-size:0.55rem;
  font-weight:400;
  padding:7px 12px;
  border-radius:6px;
  white-space:nowrap;
}
.profile-stats:hover{background:rgba(111,184,221,0.12);}

/* ---------- statistics ---------- */
.stats-summary{display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:10px;}
.stats-tile{
  background:var(--panel-sunk, #141b27); border:1px solid var(--panel-line); border-radius:8px;
  padding:14px; display:flex; flex-direction:column; gap:4px;
}
.stats-num{font-size:1.4rem; font-weight:700; color:var(--gold); font-variant-numeric:tabular-nums;}
.stats-label{font-size:0.74rem; color:var(--ink-soft);}
.stats-tile.headline{grid-column:span 2; border-color:var(--gold-dim); background:#1c1808;}
.stats-tile.headline .stats-num{font-size:2rem; color:var(--gold);}
.stats-tile.headline .stats-label{color:var(--gold); text-transform:uppercase; letter-spacing:0.04em;}
@media (max-width:480px){ .stats-tile.headline{grid-column:span 1;} }
.stats-kills-header{font-size:0.9rem; font-weight:700; color:var(--ink); margin-top:6px;}
.stats-kills{
  display:flex; flex-direction:column; gap:6px;
  overflow-y:auto; max-height:min(360px, 40vh); padding-right:4px;
}
.stats-kill-row{
  display:flex; justify-content:space-between; background:var(--panel-sunk, #141b27);
  border:1px solid var(--panel-line); border-radius:6px; padding:8px 12px;
  font-size:0.85rem; font-variant-numeric:tabular-nums;
}
.stats-kill-row span:last-child{color:var(--gold); font-weight:700;}
.stats-kill-row.stats-kill-empty{color:var(--ink-soft); font-style:italic; justify-content:center; font-variant-numeric:normal;}

/* ---------- achievements ---------- */
.ach-boosts{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:8px;
}
.ach-boost-tile{
  background:var(--panel-sunk, #141b27); border:2px solid var(--panel-line); border-radius:0;
  padding:8px 12px; display:flex; flex-direction:column; gap:2px;
}
.ach-boost-label{font-size:0.68rem; color:var(--ink-soft);}
.ach-boost-val{font-size:1rem; font-weight:700; color:var(--gold); font-variant-numeric:tabular-nums;}
.ach-boost-count{font-size:0.66rem; color:var(--ghost);}
.ach-list{
  display:flex; flex-direction:column; gap:8px;
  overflow-y:auto; padding-right:4px;
}
.ach-row{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  background:var(--panel-sunk, #141b27); border:1px solid var(--panel-line); border-radius:8px; padding:12px 14px;
  opacity:0.55;
}
.ach-row.unlocked{opacity:1; border-color:var(--gold);}
.ach-row.highlight{box-shadow:0 0 0 2px var(--gold);}
/* Arriving from the Log. A gold outline alone is easy to miss in a list of ninety, so the row the
   player was sent to pulses for a couple of seconds after it is scrolled to. */
.ach-row.just-landed{animation:ach-landed 2.2s ease-out 1;}
@keyframes ach-landed{
  0%{box-shadow:0 0 0 3px var(--gold), 0 0 26px 6px rgba(240,207,107,0.55); background:rgba(240,207,107,0.2);}
  60%{box-shadow:0 0 0 3px var(--gold), 0 0 14px 2px rgba(240,207,107,0.28);}
  100%{box-shadow:0 0 0 2px var(--gold); background:none;}
}
@media (prefers-reduced-motion: reduce){ .ach-row.just-landed{animation:none; box-shadow:0 0 0 3px var(--gold);} }
body.reduce-motion .ach-row.just-landed{animation:none; box-shadow:0 0 0 3px var(--gold);}
.ach-row.filtered-out{display:none;}
.ach-row-main h4{margin:0 0 4px; font-family:var(--font-display); font-size:0.52rem; line-height:1.6; color:var(--ink); letter-spacing:0.03em; font-weight:400; display:flex; align-items:center; gap:7px; flex-wrap:wrap;}
.ach-row.unlocked .ach-row-main h4{color:var(--gold);}
.ach-row-main p{margin:0; font-size:0.78rem; color:var(--ink-soft); line-height:1.35;}
.ach-row-progress{margin:4px 0 0; font-size:0.72rem; color:var(--frost); font-variant-numeric:tabular-nums;}
.ach-cat-badge{
  font-family:var(--font-display); font-size:0.42rem; letter-spacing:0.03em; text-transform:uppercase;
  padding:3px 5px; border:1px solid var(--panel-line); color:var(--ink-soft); white-space:nowrap;
}
.ach-cat-badge.cat-hero{color:var(--gold); border-color:var(--gold-dim);}
.ach-cat-badge.cat-tower{color:var(--tower-accent); border-color:var(--tower-accent);}
.ach-cat-badge.cat-enemy{color:var(--ascension-accent); border-color:var(--ascension-accent);}
.ach-cat-badge.cat-keep{color:var(--keep-accent); border-color:var(--keep-accent);}
.ach-row-status{font-size:0.76rem; color:var(--ink-soft); white-space:nowrap; font-variant-numeric:tabular-nums; text-align:right;}
.ach-row.unlocked .ach-row-status{color:var(--gold); font-weight:700;}
.ach-row-boost{display:block; font-size:0.68rem; color:var(--ink-soft); font-weight:400; margin-top:2px;}

/* ---------- pause screen (in-run) ---------- */
#pause-screen{background:rgba(6,9,14,0.6);}
.pause-panel{width:min(340px, 90vw);}
.pause-panel .st-header{justify-content:center;}
.pause-panel .title-actions{margin-bottom:0;}

/* ---------- the mercantile (in-run store) ---------- */
#mercantile-screen{background:rgba(6,9,14,0.6);}
#mercantile-screen .skilltree-panel{width:min(640px, 92vw);}
.mercantile-list{
  display:flex; flex-direction:column; gap:8px;
  overflow-y:auto; padding-right:4px;
}
.mc-row{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  background:var(--panel-sunk, #141b27); border:2px solid var(--panel-line); padding:12px 14px;
}
.mc-row.active{border-color:var(--good);}
.mc-row.locked{opacity:0.5;}
.mc-row-main h4{margin:0 0 4px; font-size:0.92rem; color:var(--ink);}
.mc-row.active .mc-row-main h4{color:var(--good);}
.mc-row-main p{margin:0; font-size:0.82rem; color:var(--ink-soft); line-height:1.35;}
.mc-row-action{flex-shrink:0;}
.mc-row-action .node-buy{white-space:nowrap;}
.mc-status-active{font-size:0.5rem; font-family:var(--font-display); letter-spacing:0.04em; color:var(--good); white-space:nowrap;}
.mc-status-locked{font-size:0.72rem; color:var(--ink-soft); font-style:italic; white-space:nowrap;}
/* Sort/filter bars: labelled dropdowns, one row, styled to the pixel system. Used by the Mercantile,
   the leaderboards and the achievements screen. */
.filter-bar{display:flex; flex-wrap:wrap; gap:8px 18px; margin-bottom:8px; align-items:center;}
.filter-field{display:inline-flex; align-items:center; gap:8px;}
.filter-field span{font-family:var(--font-display); font-size:0.4rem; color:var(--ink-soft); letter-spacing:0.06em;}
.pixel-select{
  appearance:none; -webkit-appearance:none;
  background:var(--panel) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath fill='%23e3b94c' d='M0 0h10L5 6z'/%3E%3C/svg%3E") no-repeat right 10px center;
  border:3px solid var(--panel-line); color:var(--ink); font-family:var(--font-body); font-size:1rem;
  padding:5px 30px 5px 10px; border-radius:0; cursor:pointer; min-width:170px;
  box-shadow:inset -3px -3px 0 0 var(--bevel-dk), inset 3px 3px 0 0 var(--bevel-lt);
}
.pixel-select:hover{background-color:#25314a;}
.pixel-select:focus-visible{outline:2px solid var(--gold); outline-offset:1px;}
.pixel-select option{background:var(--panel); color:var(--ink);}
.mc-cat{display:flex; align-items:baseline; gap:10px; margin-top:6px; padding:0 2px;}
.mc-cat:first-child{margin-top:0;}
.mc-cat h3{margin:0; font-family:var(--font-display); font-size:0.5rem; color:var(--gold); letter-spacing:0.04em;}
.mc-cat span{font-size:0.8rem; color:var(--ink-soft);}
.mc-tag{font-family:var(--font-body); font-weight:400; font-size:0.72rem; color:var(--frost); border:1px solid var(--panel-line); padding:1px 6px; margin-left:8px; vertical-align:middle;}
.mc-empty{color:var(--ink-soft); font-size:0.85rem; text-align:center; padding:30px 10px;}

/* ---------- options screen ---------- */
#options-screen .skilltree-panel{width:min(560px, 92vw);}
/* The panel is capped at 88vh; the list is the part that gives (min-height:0 lets a flex child
   shrink below its content) and scrolls — on a short window the last rows were simply cut off. */
/* overflow-x is pinned shut on purpose. Setting only overflow-y makes the OTHER axis compute to
   'auto', so the list could be scrolled sideways — and a sideways-scrolled list clips the first
   letter of every heading on the page, which looks like a font bug and is not one. */
.options-list{display:flex; flex-direction:column; gap:8px; overflow-y:auto; overflow-x:hidden; min-height:0; padding-right:4px;}
/* ---- option rows (2026-10-11) ----
   Groups under a plain heading, and every row the same shape: name, one sentence, control on the
   right. The grouping is the point — eleven settings in four arbitrary tabs was the thing the
   sensory review objected to most, ahead of any individual setting. */
.opt-group{display:flex; flex-direction:column; gap:6px; margin:0 0 14px;}
.opt-group-head{
  margin:2px 0 2px; font-family:var(--font-display); font-size:0.44rem; letter-spacing:0.12em;
  text-transform:uppercase; color:#8f9ab4; font-weight:400;
}
.opt-row{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  background:var(--panel-sunk, #141b27); border:2px solid var(--panel-line); padding:12px 14px;
}
/* Calm mode's own row reads as the switch above the rest, not as one more row in the list. */
.opt-row-big{background:var(--panel-big, #1b2133); border-color:var(--gold-dim);}
/* A row Calm mode is holding: dimmed, with the reason, rather than a control that does nothing. */
.opt-row.opt-held{opacity:0.5;}
.opt-note{margin:6px 0 0 !important; font-size:0.72rem !important; color:var(--gold-dim) !important; font-style:italic;}
/* A choice shows every state at once. A <select> shows one and hides the rest, which is exactly
   the wrong way round for somebody deciding between them. */
.opt-choice{display:flex; gap:4px; flex-shrink:0; flex-wrap:wrap; justify-content:flex-end; max-width:210px;}
.opt-choice-btn{
  padding:6px 9px; font-size:0.72rem; line-height:1.1; cursor:pointer;
  background:var(--panel-raise, #1d2638); border:2px solid var(--panel-line); color:var(--ink-soft);
}
.opt-choice-btn:hover:not(:disabled), .opt-choice-btn:focus-visible:not(:disabled){border-color:var(--gold-dim); color:var(--ink);}
.opt-choice-btn.on{background:var(--gold-dim); border-color:var(--gold); color:#fff8e6; font-weight:700;}
.opt-choice-btn:disabled{cursor:default;}
.opt-slider{display:flex; align-items:center; gap:8px; flex-shrink:0;}
.opt-slider input[type="range"]{width:120px; accent-color:var(--gold);}
.opt-slider-val{
  min-width:2.1em; text-align:right; font-size:0.74rem; color:var(--gold);
  font-variant-numeric:tabular-nums;
}
.opt-keybinds{display:grid; grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); gap:5px; margin:8px 0 0;}
.opt-keybind{
  display:flex; align-items:center; justify-content:space-between; gap:8px; padding:5px 8px;
  background:var(--panel-raise, #1d2638); border:2px solid var(--panel-line); color:var(--ink-soft);
  font-size:0.74rem; cursor:pointer; text-align:left;
}
.opt-keybind:hover, .opt-keybind:focus-visible{border-color:var(--gold-dim); color:var(--ink);}
.opt-keybind b{color:var(--gold); font-family:var(--font-display); font-size:0.46rem; font-weight:400;}
.opt-keybind.listening{border-color:var(--gold); background:var(--gold-dim); color:#fff8e6;}
/* A flex item will not shrink below its content unless it is told it may, so a long description
   pushed the row 27px wider than the list that holds it. */
.opt-row-main{min-width:0; flex:1 1 auto;}
.opt-row-main h4{margin:0 0 5px; font-family:var(--font-display); font-size:0.52rem; line-height:1.6; color:var(--gold); letter-spacing:0.03em; font-weight:400;}
.opt-row-main p{margin:0; font-size:0.8rem; color:var(--ink-soft); line-height:1.35;}
.opt-toggle{position:relative; display:inline-block; width:46px; height:24px; flex-shrink:0; cursor:pointer;}
.opt-toggle input{position:absolute; opacity:0; width:0; height:0;}
.opt-toggle-track{position:absolute; inset:0; background:#25314a; border:3px solid var(--panel-line); transition:none;}
.opt-toggle-track::before{content:''; position:absolute; top:1px; left:1px; width:14px; height:14px; background:var(--ink-soft);}
.opt-toggle input:checked + .opt-toggle-track{background:var(--gold-dim); border-color:var(--gold);}
.opt-toggle input:checked + .opt-toggle-track::before{left:auto; right:1px; background:#fff8e6;}
.profile-del:hover{background:#3a1f26; color:#f2c9d0;}
.profile-new-row{display:flex; gap:8px;}
.profile-new-row input{
  flex:1;
  background:var(--panel-sunk, #141b27);
  border:3px solid var(--panel-line);
  color:var(--ink);
  padding:10px 12px;
  border-radius:6px;
  font-size:0.9rem;
  font-family:inherit;
}
.profile-new-row input:focus{outline:none; border-color:var(--frost);}
.profile-new-row .big-btn{padding:10px 20px;}

/* ---------- lore text (level select, level cards, victory epilogue) ---------- */
.lore-text{color:var(--frost); font-style:italic; font-size:0.92rem; line-height:1.35; margin:0 0 8px;}
.lore-arc{margin:0 0 4px; max-width:640px;}
#victory-lore{color:var(--frost);}
#victory-lore:empty{display:none;}

/* ---------- story mode: level select ---------- */
/* ---------- Story Mode realm map ---------- */
#levelselect-screen .skilltree-panel{width:min(1040px, 94vw);}
.realm-map{position:relative; height:min(330px, 40vh); min-height:170px; flex-shrink:1;
  background:radial-gradient(ellipse at 50% 60%, rgba(111,184,221,0.08), transparent 70%), var(--panel-deep, #0f141d);
  border:3px solid var(--panel-line); overflow:hidden;}
.realm-road{position:absolute; inset:0; width:100%; height:100%;}
.realm-road .road{fill:none; stroke-width:5; stroke-linecap:round; vector-effect:non-scaling-stroke;}
.realm-road .road.cleared{stroke:var(--gold);}
.realm-road .road.ahead{stroke:#4a5570; stroke-dasharray:6 8;}
.realm-road .road.seal{stroke:var(--purple); stroke-dasharray:2 8;}
.realm-stop{position:absolute; transform:translate(-50%,-50%); background:none; border:none; padding:0;
  display:flex; flex-direction:column; align-items:center; gap:4px; color:var(--ink); width:120px;}
.realm-stop canvas{width:84px; height:84px; border-radius:50%; border:4px solid #4a5570; image-rendering:pixelated; background:var(--panel-sunk, #141b27); box-shadow:0 0 0 3px var(--panel-deep, #0f141d), var(--pixel-shadow);}
.realm-stop.open canvas{border-color:var(--frost);}
.realm-stop.cleared canvas, .realm-stop.ngplus canvas{border-color:var(--gold);}
.realm-stop.beyond canvas{border-color:var(--purple);}
.realm-stop.locked{opacity:0.55; cursor:default;}
.realm-stop.locked canvas{border-style:dashed;}
.realm-stop.selected canvas{box-shadow:0 0 0 3px var(--panel-deep, #0f141d), 0 0 0 6px var(--gold), var(--pixel-shadow);}
.realm-stop:hover:not(.locked) canvas{filter:brightness(1.15);}
.realm-num{font-family:var(--font-display); font-size:0.42rem; color:var(--ink-soft); letter-spacing:0.06em;}
.realm-stop.beyond .realm-num{color:var(--purple);}
.realm-name{font-size:0.9rem; font-weight:700; color:var(--ink); text-shadow:2px 2px 0 rgba(0,0,0,0.7); text-align:center; line-height:1.1;}
.realm-mark{position:absolute; top:10px; right:14px; color:var(--gold); font-size:0.95rem; text-shadow:2px 2px 0 rgba(0,0,0,0.7);}
.realm-detail{display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap; flex-shrink:0;
  background:var(--panel-sunk, #141b27); border:3px solid var(--panel-line); padding:12px 16px; min-height:96px;}
.rd-main{flex:1 1 300px;}
.rd-where{font-family:var(--font-display); font-size:0.4rem; letter-spacing:0.06em; color:var(--frost); margin-bottom:6px;}
.rd-main h3{margin:0 0 4px; font-family:var(--font-display); font-size:0.7rem; color:var(--gold);}
.rd-main p{margin:0 0 6px; font-size:0.88rem; color:var(--ink-soft);}
.rd-main .level-mechanic{margin-top:6px;}
.rd-actions{display:flex; gap:8px; flex:0 0 auto;}
.rd-actions .node-buy{margin-top:0; min-width:120px;}
/* Daily challenge button: the detail line names today's kingdom and modifier. */
.big-btn.daily-btn{border-color:var(--gold); color:#fff1c9; background:rgba(227,185,76,0.10);}
.big-btn.daily-btn.done{opacity:0.6;}
#daily-detail{display:block; font-family:var(--font-body); font-size:0.82rem; letter-spacing:0; color:var(--ink-soft); margin-top:2px; font-weight:400;}
/* Endless mutators */
#mutator-modal{background:rgba(6,9,14,0.72);}
/* A grid, not a wrap. Flex-wrap put a fourth card on a row of its own under three — the Astral's
   Fourth Card turned the offer into an L. The column count follows how many are being offered, so
   three sit in a row and four sit two-by-two. */
.mutator-list{display:grid; gap:10px; width:100%; grid-template-columns:repeat(3, 1fr);}
.mutator-list.n1{grid-template-columns:1fr;}
.mutator-list.n2, .mutator-list.n4{grid-template-columns:repeat(2, 1fr);}
.mutator-card{min-width:0; text-align:left; background:var(--panel-sunk, #141b27); border:3px solid var(--panel-line); padding:12px; color:var(--ink); cursor:pointer;
  box-shadow:inset -3px -3px 0 0 var(--bevel-dk), inset 3px 3px 0 0 var(--bevel-lt);}
.mutator-card:hover{border-color:var(--ascension-accent); background:#1a2234;}
.mutator-card h4{margin:0 0 6px; font-family:var(--font-display); font-size:0.5rem; color:var(--ascension-accent); line-height:1.6;}
.mutator-card p{margin:0; font-size:0.86rem; color:var(--ink-soft); line-height:1.35;}
/* Run summary on the end screens */
/* auto-fit, not a fixed four: the run’s XP joined the row and a hard
   four-column grid put it alone on a line of its own. */
.run-summary{display:grid; grid-template-columns:repeat(auto-fit, minmax(104px, 1fr)); gap:6px; margin:4px 0 12px; text-align:left;}
/* the one everybody looks for first */
.run-summary .rs-xp{border-color:var(--purple);}
.run-summary .rs-xp b{color:#d6c6ff;}
.run-summary div{background:var(--ground-80, rgba(20,27,39,0.8)); border:2px solid var(--panel-line); padding:6px 8px;}
.run-summary div span{display:block; font-family:var(--font-display); font-size:0.36rem; color:var(--ink-soft); letter-spacing:0.06em; margin-bottom:3px;}
.run-summary div b{font-size:0.95rem; color:var(--gold); font-variant-numeric:tabular-nums;}
.run-summary .rs-wide{grid-column:1 / -1;}
.run-summary .rs-ach{max-height:150px; overflow-y:auto;}
.run-summary .rs-ach-row{display:flex; gap:10px; align-items:baseline; padding:3px 6px 3px 0; border:0; border-top:1px solid rgba(74,85,112,0.4); background:none; box-shadow:none; width:100%; text-align:left; cursor:pointer; color:inherit; font-family:inherit; letter-spacing:0;}
.run-summary .rs-ach-row:hover{background:rgba(240,207,107,0.10);}
.run-summary .rs-ach-row:hover b{color:var(--gold);}
.run-summary .rs-ach-go{margin-left:auto; color:var(--gold); font-size:0.9rem;}
.run-summary .rs-ach-row b{font-size:0.9rem; white-space:nowrap;}
.run-summary .rs-ach-row em{font-style:normal; font-size:0.82rem; color:var(--ink-soft);}
/* The ending */
#ending-screen .ending-panel{width:min(640px, 92vw); align-items:center; text-align:center;}
.ending-panel h1{font-family:var(--font-display); font-size:0.95rem; color:#fbf7ec; margin:0; text-shadow:3px 3px 0 rgba(0,0,0,0.6);}
.ending-scroll{max-height:min(420px, 52vh); overflow-y:auto; width:100%; padding:4px 10px; min-height:0;}
.ending-body p{font-size:1.02rem; line-height:1.45; color:var(--ink); margin:0 0 12px;}
.ending-body .ending-defender{color:var(--frost);}
.ending-body .ending-defender b{color:var(--gold);}
.ending-body .ending-hint{color:var(--purple); font-style:italic;}
.ending-rulers{display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:6px; margin:6px 0 14px;}
.ending-rulers div{background:var(--ground-80, rgba(20,27,39,0.8)); border:2px solid var(--gold-dim); padding:8px;}
.ending-rulers b{display:block; color:var(--gold); font-size:0.95rem;}
.ending-rulers span{font-size:0.78rem; color:var(--ink-soft);}
/* Story intro (in-run popup) */
#story-intro{background:rgba(6,9,14,0.72);}
.intro-panel{width:min(560px, 92vw); text-align:center; align-items:center;}
.intro-eyebrow{font-family:var(--font-display); font-size:0.42rem; letter-spacing:0.1em; color:var(--frost);}
.intro-panel h1{font-family:var(--font-display); font-size:1rem; color:#fbf7ec; margin:0; text-shadow:3px 3px 0 rgba(0,0,0,0.6);}
.intro-ruler{font-size:0.95rem; color:var(--gold);}
.intro-lore{font-size:1rem; line-height:1.4; max-width:52ch; margin:0;}
.intro-panel .level-mechanic{text-align:left; width:100%;}
.intro-panel .st-footer{width:100%;}
.level-status{font-size:0.78rem; font-weight:700; color:var(--frost);}
.level-actions{display:flex; gap:8px; margin-top:auto;}
.level-actions .node-buy{flex:1; margin-top:0;}



/* The kingdom's rule, on the card you pick the kingdom from. Its own accent (not the frost blue
   used for status/lore) because it's a rules statement, not flavour or progress. */
.level-mechanic{
  font-size:0.8rem;
  line-height:1.4;
  color:#e8dcc0;
  background:rgba(227,185,76,0.09);
  border-left:3px solid var(--gold);
  padding:7px 10px;
}
.level-mechanic strong{
  display:block;
  font-family:var(--font-display);
  font-size:0.5rem;
  letter-spacing:0.04em;
  color:var(--gold);
  margin-bottom:5px;
}
.setup-map-mechanic{
  margin-top:5px;
  font-size:0.7rem;
  font-weight:700;
  color:var(--gold);
  letter-spacing:0.02em;
}
.setup-map-card.locked .setup-map-mechanic{color:var(--ink-soft);}

/* ---------- infinite mode setup (map + tower picker) ---------- */
#infinite-setup-screen .skilltree-panel{width:min(1000px, 96vw); overflow-y:auto; overflow-x:hidden;}
/* Nothing in here gives way. Every child of the panel is a flex item in a
   column box with a max height, so without this they all shrink together
   when the content grows - and .setup-towers-section clips rather than
   scrolls, which is how Choose Your Towers came to be 70px of a 225px
   section. The panel scrolls instead. */
#infinite-setup-screen .skilltree-panel > *{flex:0 0 auto;}
.setup-section{display:flex; flex-direction:column; gap:8px;}
.setup-section h3{margin:0; font-family:var(--font-display); font-size:0.66rem; line-height:1.6; color:var(--gold); font-weight:400;}
.setup-maps{display:grid; grid-template-columns:repeat(auto-fit, minmax(180px, 1fr)); gap:10px;}
/* Infinite map choice as a row of Story-map crests (2026-09-16). */
.setup-section{position:relative;}
.setup-realms{display:flex; justify-content:space-between; align-items:flex-start; gap:6px; padding:4px 12px 2px;}
.realm-stop.setup-realm{position:static; transform:none; flex:1 1 0; display:flex; flex-direction:column; align-items:center; gap:3px; min-width:0;}
.realm-stop.setup-realm canvas{width:72px; height:72px;}
.realm-stop.setup-realm .realm-name{max-width:120px; text-align:center;}
.setup-realm-rule{font-size:0.72rem; color:var(--gold); line-height:1.2; text-align:center;}
.realm-stop.setup-realm.locked .setup-realm-rule{color:var(--ink-soft);}
/* Open, but only below the difficulty currently chosen. Not locked - clicking it
   brings the difficulty down to meet it - so it reads as available, with the
   caveat in the line underneath rather than in a greyed-out card. */
.realm-stop.setup-realm.easier canvas{opacity:0.72;}
.realm-stop.setup-realm.easier .realm-name{color:var(--ink-soft);}
.realm-stop.setup-realm.easier .setup-realm-rule{color:var(--purple, #9a86d6);}
.realm-stop.setup-realm.easier.selected canvas{opacity:1;}
.setup-tower-sig{font-size:0.72rem; color:var(--ink-soft); line-height:1.25; margin-top:2px;}
.setup-map-card{
  background:var(--panel-sunk, #141b27); border:2px solid var(--panel-line); border-radius:8px;
  padding:12px 14px; cursor:pointer;
}
.setup-map-card h4{margin:0 0 4px; font-size:0.95rem; color:var(--ink);}
.setup-map-card.selected{border-color:var(--gold);}
.setup-map-card.selected h4{color:var(--gold);}
.setup-map-card.locked{opacity:0.45; cursor:default;}
.setup-map-card .level-status{font-size:0.72rem;}
.setup-towers-section{overflow:hidden;}
.setup-towers{display:grid; grid-template-columns:1fr 1fr 1fr; gap:12px;}
.setup-tower-col h4{margin:0 0 6px; font-family:var(--font-display); font-size:0.5rem; line-height:1.6; color:var(--frost); text-transform:uppercase; letter-spacing:0.04em; font-weight:400;}
.setup-tower-list{display:flex; flex-direction:column; gap:8px; max-height:220px; overflow-y:auto; padding-right:4px;}
.setup-tower-card{
  display:flex; align-items:center; gap:10px;
  background:var(--panel-sunk, #141b27); border:2px solid var(--panel-line); border-radius:8px;
  padding:8px 10px; cursor:pointer;
}
.setup-tower-card canvas{image-rendering:pixelated; flex-shrink:0;}
.setup-tower-card.selected{border-color:var(--gold);}
.setup-tower-name{font-size:0.8rem; font-weight:700; color:var(--ink);}
.setup-tower-kingdom b{color:var(--gold); font-weight:600;}
.setup-tower-kingdom{font-size:0.68rem; color:var(--ink-soft);}
#btn-start-infinite:disabled{opacity:0.4; cursor:default;}

/* ---------- leaderboards ---------- */
.lb-list{
  min-height:0;
  display:flex;
  flex-direction:column;
  gap:6px;
  overflow-y:auto;
  min-height:200px;
  padding-right:4px;
}
.lb-row{
  display:flex;
  align-items:center;
  gap:12px;
  background:var(--panel-sunk, #141b27);
  border:2px solid var(--panel-line);
  border-radius:8px;
  padding:10px 14px;
}
.lb-row.me{border-color:var(--gold);}
.lb-rank{width:1.8em; font-weight:700; color:var(--ink-soft); font-variant-numeric:tabular-nums;}
.lb-row:nth-child(1) .lb-rank{color:var(--gold);}
.lb-row:nth-child(2) .lb-rank{color:#cfd6e2;}
.lb-row:nth-child(3) .lb-rank{color:#c98a4b;}
.lb-name{flex:1; font-weight:700;}
.lb-name .lb-date{font-weight:400; color:var(--ink-soft); font-size:0.76rem; margin-left:8px;}
.lb-tree{font-size:0.68rem; color:var(--ink-soft); border:1px solid var(--panel-line); padding:2px 6px; white-space:nowrap; font-variant-numeric:tabular-nums;}
.lb-tree.maxed{color:var(--good); border-color:var(--good);}
.lb-level{font-size:0.68rem; color:var(--frost); border:1px solid var(--panel-line); padding:2px 6px; white-space:nowrap;}
.lb-mode{font-size:0.68rem; color:#cfc2ec; border:1px solid #453a63; padding:2px 6px; white-space:nowrap;}
.lb-mode.daily{color:var(--gold); border-color:#6b5a2a;}
.lb-mode.story{color:#8fe0a6; border-color:#2f5a3a;}
.lb-value{font-variant-numeric:tabular-nums; font-weight:700; color:var(--gold);}
.lb-empty{color:var(--ink-soft); font-size:0.85rem; text-align:center; padding:30px 0;}

/* ---------- How to Play (title screen reference) ---------- */
.howto-list{display:flex; flex-direction:column; gap:8px; overflow-y:auto; min-height:0;}
.howto-row{background:var(--panel-sunk, #141b27); border:2px solid var(--panel-line); padding:12px 14px;}
.howto-row h4{margin:0 0 5px; font-family:var(--font-display); font-size:0.52rem; line-height:1.6; color:var(--gold); letter-spacing:0.03em;}
.howto-row p{margin:0; font-size:0.95rem; line-height:1.45; color:var(--ink);}
.howto-row strong{color:#fff8e6;}

/* ---------- first-play tutorial ----------
   A spotlight (the box-shadow trick: a transparent box over the target with a huge dark shadow
   around it) and a speech bubble. The spot itself never takes the pointer, so the player can click
   the thing being pointed at — the tutorial guides, it doesn't lock. Only the bubble is clickable. */
#tutorial-layer{position:fixed; inset:0; z-index:100; pointer-events:none;}
/* Back exists so no step is one-way: a bubble can always be read again. It is hidden on the first
   step rather than disabled, because a button that is there and does nothing is a worse answer. */
#tut-back.hidden{display:none !important;}
/* What the step is waiting for, said plainly under the text rather than implied by the Next button
   quietly disappearing. */
#tut-wait{
  margin-top:8px; font-size:0.78rem; color:var(--frost); line-height:1.35;
}
#tut-wait::before{content:'\2192  ';}
#tut-spot{
  position:absolute;
  border:3px solid var(--gold);
  box-shadow:0 0 0 9999px rgba(4,6,12,0.62);
  pointer-events:none;
  transition:left 0.18s, top 0.18s, width 0.18s, height 0.18s;
}
#tut-bubble{
  position:absolute;
  width:min(360px, 92vw);
  background:rgba(18,24,34,0.98);
  border:4px solid var(--gold);
  box-shadow:var(--pixel-shadow);
  padding:14px 16px 12px;
  pointer-events:auto;
}
#tut-step{font-family:var(--font-display); font-size:0.46rem; letter-spacing:0.06em; color:var(--gold); margin-bottom:8px;}
#tut-text{font-size:1.02rem; line-height:1.45; color:var(--ink);}
#tut-text b{color:#fff8e6;}
/* A tutorial card that names a colour shows that colour. This is the same violet the enchant
   buttons are drawn in (#c0abe8, see #tower-info .ti-ench) and it is deliberately a literal rather
   than var(--purple): the card says "the purple buttons", so the word and the buttons have to stay
   the same colour in all four themes or the card starts lying in three of them. */
#tut-text b.tut-ench{color:#c0abe8;}
.tut-actions{display:flex; align-items:center; justify-content:space-between; gap:10px; margin-top:12px;}
.tut-actions .node-buy{margin-top:0; padding:8px 16px;}
.tut-actions .link-btn{font-size:0.85rem;}
#tutorial-layer.waiting .tut-actions .node-buy{display:none;}
/* A gentle pulse on the spotlight while a step waits on the player. Off under the Ambient
   Animations setting, like every other decorative motion. */
#tutorial-layer.waiting #tut-spot{animation:tut-pulse 1.4s ease-in-out infinite;}
body.reduce-motion #tutorial-layer.waiting #tut-spot{animation:none;}
@keyframes tut-pulse{0%,100%{border-color:var(--gold);}50%{border-color:#fff8e6;}}

/* ---------- controller virtual cursor ---------- */
#pad-cursor{
  position:fixed; z-index:200; pointer-events:none; width:22px; height:22px; margin:-3px 0 0 -3px;
  background:
    linear-gradient(#fff8e6, #fff8e6) 0 0 / 3px 14px no-repeat,
    linear-gradient(#fff8e6, #fff8e6) 0 0 / 14px 3px no-repeat,
    linear-gradient(#fff8e6, #fff8e6) 3px 3px / 3px 3px no-repeat,
    linear-gradient(#fff8e6, #fff8e6) 6px 6px / 3px 3px no-repeat,
    linear-gradient(#fff8e6, #fff8e6) 9px 9px / 3px 3px no-repeat;
  filter:drop-shadow(1px 1px 0 var(--panel-deep, #0f141d)) drop-shadow(-1px -1px 0 var(--panel-deep, #0f141d));
}

/* ---------- Elysia's Favour (Skill Tree tab: the Skill Point sink) ---------- */
.favour-grid{display:grid; grid-template-columns:repeat(auto-fit, minmax(230px, 1fr)); gap:12px;}
.favour-card{background:#1c2434; border:3px solid var(--panel-line); padding:12px 14px; display:flex; flex-direction:column; gap:6px;
  box-shadow:inset -3px -3px 0 0 var(--bevel-dk), inset 3px 3px 0 0 var(--bevel-lt);}
.favour-card.affordable{border-color:var(--gold);}
.favour-card.capped{border-color:var(--good); opacity:0.85;}
.favour-card.chest{grid-column:1 / -1; border-color:var(--frost);}
.favour-card h4{margin:0; font-family:var(--font-display); font-size:0.5rem; line-height:1.6; color:var(--gold);}
.favour-card.chest h4{color:var(--frost);}
.favour-card p{margin:0; font-size:0.84rem; color:var(--ink-soft); line-height:1.35;}
.favour-rank{font-size:0.88rem; color:var(--ink); font-variant-numeric:tabular-nums;}
.favour-rank b{color:var(--gold);}
.favour-capped{font-family:var(--font-display); font-size:0.42rem; color:var(--good); letter-spacing:0.04em;}
.favour-card .node-buy{margin-top:auto;}
.favour-chest-buttons{display:flex; gap:8px; flex-wrap:wrap;}
.favour-chest-buttons .node-buy{flex:1 1 120px; display:flex; flex-direction:column; align-items:center; gap:2px;}
.favour-chest-buttons .node-buy span{font-family:var(--font-body); font-size:0.78rem; letter-spacing:0; opacity:0.8;}

/* Cataclysm: the one red control in the game, on purpose */
.rd-actions .node-buy.rd-cata{background:#3a1f26; border-color:#c9394f; color:#ffd9de;}
.rd-actions .node-buy.rd-cata:hover{background:#5a2a36;}
.rd-actions .node-buy.rd-cata:disabled{opacity:0.45; cursor:not-allowed; background:#2a1a1e;}
.rd-actions .node-buy.rd-cata:disabled:hover{background:#2a1a1e;}
.rd-actions{flex-direction:column; align-items:stretch; gap:6px; max-width:420px;}
.rd-btns{display:flex; gap:8px; justify-content:flex-end;}
.rd-notes{display:flex; flex-direction:column; gap:2px;}
.rd-note{font-size:0.72rem; line-height:1.25; color:#f2b3bd; text-align:right;}
.rd-note b{color:inherit;}
.rd-actions{display:flex; flex-wrap:wrap; gap:8px; justify-content:flex-end; align-items:flex-start;}

/* ---------- Mercantile voucher (Bulk Order) — a gold ticket above the shelves ---------- */
.mc-voucher{display:flex; align-items:stretch; margin-bottom:10px; border:3px solid var(--gold); background:linear-gradient(90deg, rgba(227,185,76,0.16), rgba(227,185,76,0.05));
  box-shadow:inset -3px -3px 0 0 var(--bevel-dk), inset 3px 3px 0 0 var(--bevel-lt), var(--pixel-shadow);}
.mc-voucher.locked{border-color:var(--panel-line); background:var(--panel-sunk, #141b27); opacity:0.6; box-shadow:none;}
.mc-voucher.redeemed{border-color:var(--good); background:rgba(79,174,98,0.08);}
.mc-voucher.ready{animation:voucher-glint 1.4s steps(2,end) infinite;}
@keyframes voucher-glint{0%,100%{box-shadow:inset -3px -3px 0 0 var(--bevel-dk), inset 3px 3px 0 0 var(--bevel-lt), var(--pixel-shadow);}50%{box-shadow:inset -3px -3px 0 0 var(--bevel-dk), inset 3px 3px 0 0 var(--bevel-lt), 0 0 0 3px rgba(227,185,76,0.5), var(--pixel-shadow);}}
@media (prefers-reduced-motion:reduce){.mc-voucher.ready{animation:none;}}
.mcv-stub{display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px; padding:10px 12px; border-right:3px dashed var(--gold); min-width:78px;}
.mc-voucher.locked .mcv-stub{border-right-color:var(--panel-line);}
.mc-voucher.redeemed .mcv-stub{border-right-color:var(--good);}
.mcv-punch{width:12px; height:12px; border:3px solid var(--gold); background:var(--panel-deep, #0f141d);}
.mc-voucher.locked .mcv-punch{border-color:var(--panel-line);}
.mc-voucher.redeemed .mcv-punch{border-color:var(--good); background:var(--good);}
.mcv-label{font-family:var(--font-display); font-size:0.4rem; letter-spacing:0.12em; color:var(--gold);}
.mc-voucher.locked .mcv-label{color:var(--ink-soft);}
.mc-voucher.redeemed .mcv-label{color:var(--good);}
.mcv-body{flex:1; padding:10px 14px;}
.mcv-body h4{margin:0 0 4px; font-family:var(--font-display); font-size:0.5rem; line-height:1.6; color:var(--gold);}
.mc-voucher.redeemed .mcv-body h4{color:var(--good);}
.mcv-body p{margin:0; font-size:0.84rem; color:var(--ink-soft); line-height:1.35;}
.mcv-action{display:flex; align-items:center; padding:10px 14px; flex-shrink:0;}
.mcv-action .node-buy{margin-top:0; white-space:nowrap;}
.mcv-stamp{font-family:var(--font-display); font-size:0.5rem; color:var(--good); border:3px solid var(--good); padding:8px 10px; transform:rotate(-6deg); letter-spacing:0.06em;}

/* 2026-09-16: enchants on the tower panel, Mercantile services, prestige/signature cards, leaderboard crest */
#tower-info .ti-enchants{display:flex; gap:4px; margin-top:6px; flex-wrap:wrap;}
/* Enchant violet is NOT themed, on purpose — the same reasoning as --good and --red. The tutorial
   card calls these "the purple buttons", so they are purple in Emberhold and Veilfrost too. */
#tower-info .ti-ench{font-family:var(--font-body); font-size:0.78rem; padding:2px 6px; border:2px solid #453a63; background:#191230; color:#cfc2ec; letter-spacing:0; box-shadow:none;}
#tower-info .ti-ench small{display:block; font-size:0.7rem; color:var(--ink-soft);}
#tower-info .ti-ench.on{border-color:#c0abe8; background:#281e45; color:#fff; padding:4px 8px;}
#tower-info button.ti-ench:hover{background:#281e45;}
.favour-card.prestige{border-color:#f0cf6b;}
.favour-card.signature{border-color:#8fe3ff;}
/* Marks an option that had to be earned — it is simply absent until then. */
.opt-earned{font-family:var(--font-display); font-size:0.36rem; letter-spacing:0.08em; color:var(--gold);
  border:2px solid var(--gold); padding:3px 6px; margin-left:8px; vertical-align:middle;
  box-shadow:0 0 10px rgba(240,207,107,0.3);}

/* The archive: a shelf of saved board states, opened from the filter bar. */
.lb-arch-toggle{margin-left:auto; font-family:var(--font-display); font-size:0.44rem; letter-spacing:0.04em; color:var(--ink-soft);
  background:#25314a; border:2px solid var(--panel-line); padding:7px 12px; cursor:pointer;}
.lb-arch-toggle:hover{background:#324164; color:var(--ink);}
.lb-arch-toggle.on{border-color:var(--gold); color:var(--gold);}
.lb-archive{background:#131a28; border:2px solid var(--panel-line); padding:12px 14px; margin-bottom:10px;}
.lb-arch-new{display:flex; align-items:center; gap:8px; flex-wrap:wrap;}
.lb-arch-name{flex:1 1 180px; min-width:0; background:#0e131c; border:2px solid var(--panel-line); color:var(--ink);
  font-family:var(--font-body); font-size:0.9rem; padding:7px 9px;}
.lb-arch-name:focus{outline:none; border-color:var(--gold);}
.lb-arch-btn{font-family:var(--font-display); font-size:0.42rem; letter-spacing:0.04em; color:var(--ink);
  background:#25314a; border:2px solid var(--panel-line); padding:7px 11px; cursor:pointer; flex:0 0 auto;}
.lb-arch-btn:hover:not(:disabled){background:#324164;}
.lb-arch-btn:disabled{opacity:0.35; cursor:default;}
.lb-arch-btn.danger{background:#3a1f26; border-color:#5c2a34;}
.lb-arch-btn.danger:hover{background:#4d2530;}
.lb-arch-hint{font-size:0.78rem; color:var(--ink-soft); margin:8px 0 4px;}
.lb-arch-empty{font-size:0.82rem; color:var(--ink-soft); padding:6px 0 2px;}
.lb-arch-row{display:flex; align-items:center; gap:10px; padding:7px 0; border-top:1px solid var(--panel-line);}
.lb-arch-title{font-weight:700; color:var(--ink); flex:0 0 auto;}
.lb-arch-meta{flex:1 1 auto; min-width:0; font-size:0.78rem; color:var(--ink-soft); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}

/* The League: the standings across every board, one row per profile, medals and points. */
.lb-row.league{flex-wrap:wrap;}
.lg-medals{display:flex; align-items:center; gap:10px; margin:0 12px;}
.lg-medal{display:flex; align-items:center; gap:3px;}
.lg-medal img{width:16px; height:19px; image-rendering:pixelated; display:block;}
.lg-medal b{font-family:var(--font-display); font-size:0.42rem; color:var(--ink-soft); letter-spacing:0;}
.lg-boards{flex:1 1 100%; font-size:0.76rem; color:var(--ink-soft); margin-top:4px; padding-left:2px;}

/* A drawn crown beside the name, not a glyph before it: the old star-plus-count read as "+1". */
.lb-crest{display:inline-block; width:22px; height:19px; margin-left:7px; vertical-align:-5px; image-rendering:pixelated;}
.sig-field{margin-bottom:6px; font-size:0.78rem;}
.sig-field select{font-size:0.78rem;}

/* 2026-09-16: Next Wave / streak hover cards */
#wave-tip{width:auto; max-width:420px; min-width:260px;}
#wave-tip .wt-head{font-family:var(--font-display); font-size:0.5rem; color:var(--gold); line-height:1.6; margin-bottom:6px;}
#wave-tip .wt-row{display:flex; gap:10px; align-items:flex-start; padding:6px 0; border-top:1px solid var(--panel-line);}
#wave-tip .wt-row.boss .wt-name{color:var(--gold);}
#wave-tip .wt-spr{flex:0 0 30px; image-rendering:pixelated;}
#wave-tip .wt-main{flex:1; min-width:0;}
#wave-tip .wt-stats{font-size:0.86rem; color:var(--ink-soft); line-height:1.35;}
#wave-tip .wt-stats b{color:var(--ink); font-variant-numeric:tabular-nums;}
#wave-tip .wt-row .wt-sub{margin-top:2px; padding-top:0; border-top:none;}
#wave-tip .mt-hint{margin-top:8px; font-size:0.78rem; color:var(--ghost);}

/* 2026-09-16: rulers — portraits, dispatches, the Chronicle */
.ruler-portrait{image-rendering:pixelated; display:block; margin:4px auto 0;}
#ruler-say{position:absolute; right:14px; bottom:88px; width:320px; display:flex; gap:10px; align-items:flex-end; background:rgba(18,24,34,0.95); border:3px solid var(--gold); padding:8px 10px; box-shadow:var(--pixel-shadow); z-index:5; pointer-events:none;}
#ruler-say canvas{flex:0 0 auto; image-rendering:pixelated;}
#ruler-say .rs-name{font-family:var(--font-display); font-size:0.45rem; color:var(--gold); line-height:1.6; margin-bottom:3px;}
#ruler-say .rs-text{font-size:0.9rem; line-height:1.3; color:var(--ink);}
.rd-portrait{flex:0 0 auto; image-rendering:pixelated; align-self:center;}
.ending-rulers canvas{display:block; margin:0 auto 4px; image-rendering:pixelated;}
.chronicle-list{overflow-y:auto; display:flex; flex-direction:column; gap:14px; padding-right:6px;}
.chron-kingdom{background:var(--panel-sunk, #141b27); border:2px solid var(--panel-line); padding:12px 14px;}
.chron-kingdom.locked{opacity:0.55;}
.chron-head{display:flex; gap:12px; align-items:center; margin-bottom:8px;}
.chron-head canvas{image-rendering:pixelated;}
.chron-head h3{margin:0; font-family:var(--font-display); font-size:0.62rem; color:var(--gold); line-height:1.6;}
.chron-ruler{font-size:0.85rem; color:var(--ink-soft);}
/* One column, top to bottom. It was a 2x2 grid, which gave five entries that happen in a
   definite order no reading order at all. */
.chron-pages{display:flex; flex-direction:column; gap:0;}
.chron-page{
  background:var(--ground-80, rgba(20,27,39,0.8)); border-left:3px solid var(--panel-line);
  padding:8px 12px; margin:0 0 2px;
}
.chron-page.locked{color:var(--ghost); border-left-color:#2a3348;}
.chron-page h4{margin:0 0 4px; font-family:var(--font-display); font-size:0.4rem; letter-spacing:0.08em; color:var(--frost); line-height:1.6; text-transform:uppercase;}
.chron-page p{margin:0; font-size:0.86rem; line-height:1.4; color:var(--ink);}
.chron-page.locked p{color:var(--ghost); font-style:italic;}
.chron-page .chron-need{font-style:normal; color:#8e9ab6; font-size:0.8rem;}
.chron-page .chron-need::before{content:'\1F512\00a0\00a0'; font-size:0.74rem; opacity:0.75;}
/* A quotation looks like one: the line, then who said it. Two of the five entries are dispatches
   and printing them as body text with a dash in front was most of why the page did not read. */
.chron-quote{border-left-color:var(--gold-dim);}
.chron-page blockquote{
  margin:0; font-size:0.92rem; line-height:1.45; color:#f3e6c2; font-style:italic;
}
.chron-page blockquote::before{content:'\201C';}
.chron-page blockquote::after{content:'\201D';}
.chron-page cite{
  display:block; margin-top:3px; font-style:normal; font-size:0.72rem; color:var(--ink-soft);
}
.chron-page cite::before{content:'\2014\00a0';}
/* The centenary line is a footnote on the realm, not a chapter of it. */
.chron-note{border-left-color:#2a3348;}
.chron-note p{font-size:0.78rem; color:var(--ink-soft);}
.chron-tally{
  margin-left:auto; font-family:var(--font-display); font-size:0.42rem; color:var(--ink-soft);
  letter-spacing:0.04em;
}
.chron-what{
  margin:0 0 10px; font-size:0.85rem; line-height:1.4; color:var(--ink-soft); max-width:62ch;
}
#levelselect-screen .st-footer .big-btn{flex:1;}

/* Load screen (2026-09-16): sits over everything, including the story intro. */
#load-screen{position:fixed; inset:0; z-index:150; background:#0a0e16; display:flex; align-items:center; justify-content:center; opacity:1; transition:opacity 0.35s;}
#load-screen.fading{opacity:0; pointer-events:none;}
#load-screen .ls-panel{display:flex; flex-direction:column; align-items:center; gap:10px; text-align:center; width:min(440px, 90%);}
#load-screen .ls-eyebrow{font-family:var(--font-display); font-size:0.42rem; letter-spacing:0.1em; color:var(--frost);}
#load-screen canvas{image-rendering:pixelated;}
#load-screen .ls-title{font-family:var(--font-display); font-size:1rem; color:#fbf7ec; text-shadow:3px 3px 0 rgba(0,0,0,0.6); line-height:1.6;}
#load-screen .ls-ruler{font-size:1rem; color:var(--gold);}
#load-screen .ls-bar{width:100%; height:12px; border:2px solid var(--panel-line); background:var(--panel-sunk, #141b27); margin-top:6px; box-shadow:var(--pixel-shadow);}
#load-screen .ls-bar-fill{height:100%; width:0; background:var(--gold);}
#load-screen .ls-tip{font-size:1rem; color:#c7bfa9; line-height:1.3; max-width:44ch; min-height:2.6em;}

/* Enchant hover card (2026-09-16): same look as the ability card. */
#ench-tip{position:absolute; width:250px; background:rgba(18,24,34,0.97); border:3px solid var(--gold); padding:10px 12px; box-shadow:var(--pixel-shadow); z-index:7; pointer-events:none;}
#ench-tip .wt-name{font-family:var(--font-display); font-size:0.58rem; line-height:1.6; color:var(--gold); margin-bottom:6px;}
#ench-tip .wt-role{font-family:var(--font-body); font-size:0.8rem; color:var(--ink-soft); margin-left:8px;}
#ench-tip .at-body{font-size:0.9rem; color:var(--ink); line-height:1.35;}
#ench-tip .wt-sub{margin-top:8px; padding-top:8px; border-top:1px solid var(--panel-line); font-size:0.82rem; color:var(--ink-soft);}
#ench-tip .ench-short{color:#e07a7a;}

/* NG+ note under its button, quieter than the Cataclysm one (2026-09-16). */
.rd-note.rd-ngp-note{color:var(--ink-soft);}

/* ---------- consistency pass (2026-09-16) ----------
   The display face is a bitmap font: a synthesized bold only smears it. Every element set in it
   sits at weight 400. Toasts share one 2px border; chips share 1px. */
h1, .overlay h1, .skilltree-panel h3, .realm-detail h3, .sg-tree-head h3, .favour-card h4, #tower-info h3, .confirm-panel h3,
.node-buy, .danger-btn, .sg-buy, #btn-start-wave, #btn-speed, #btn-pause, #btn-mercantile, .big-btn, .st-tab, .rail-btn,
#mechanic-tip .mt-name, #wave-tip .wt-name, #ability-tip .wt-name, #ench-tip .wt-name, #ruler-say .rs-name,
.rd-where, .intro-eyebrow, .eyebrow, .ls-eyebrow, .ls-title, .ach-cat-badge, .sg-jump-btn{font-weight:400;}
#ruler-say{pointer-events:auto; cursor:pointer;}
#ruler-say:hover{border-color:#f0cf6b;}
#ruler-say .rs-close{position:absolute; top:4px; right:7px; font-family:var(--font-display); font-size:0.42rem; color:var(--ink-soft);}
#ruler-say:hover .rs-close{color:var(--gold);}

/* Realm map hover card (2026-09-16): the requirements for a sealed kingdom, with progress. */
#realm-tip, .realm-tip{position:absolute; width:270px; z-index:5; pointer-events:none; background:rgba(18,24,34,0.97); border:3px solid var(--gold); padding:10px 12px; box-shadow:var(--pixel-shadow);}
.realm-tip .rt-name, #realm-tip .rt-name{font-family:var(--font-display); font-size:0.58rem; line-height:1.6; color:var(--gold); margin-bottom:4px;}
.realm-tip .rt-status, #realm-tip .rt-status{font-family:var(--font-body); font-size:0.8rem; color:var(--ink-soft); margin-left:8px;}
.realm-tip .rt-sub, #realm-tip .rt-sub{font-size:0.88rem; color:var(--ink); line-height:1.3; margin-bottom:4px;}
.realm-tip .rt-req, #realm-tip .rt-req{display:flex; align-items:baseline; gap:6px; font-size:0.88rem; color:var(--ink); line-height:1.35;}
.realm-tip .rt-req.ok, #realm-tip .rt-req.ok{color:var(--ink-soft);}
.realm-tip .rt-tick, #realm-tip .rt-tick{color:#e07a7a; flex:0 0 auto;}
.realm-tip .rt-req.ok .rt-tick, #realm-tip .rt-req.ok .rt-tick{color:var(--good);}
.realm-tip .rt-prog, #realm-tip .rt-prog{margin-left:auto; color:var(--frost); font-variant-numeric:tabular-nums; white-space:nowrap; padding-left:8px;}

#realm-tip .rt-sig, .realm-tip .rt-sig{font-size:0.82rem; color:var(--ink-soft); line-height:1.3; margin-top:4px; padding-top:4px; border-top:1px solid var(--panel-line);}
#realm-tip .rt-sig b, .realm-tip .rt-sig b{color:var(--frost);}
/* Barracks hover card (2026-09-16), same look as the ability card. */
#shop-tip{position:absolute; width:270px; background:rgba(18,24,34,0.97); border:3px solid var(--frost); padding:10px 12px; box-shadow:var(--pixel-shadow); z-index:7; pointer-events:none;}
#shop-tip .wt-name{font-family:var(--font-display); font-size:0.58rem; line-height:1.6; color:var(--frost); margin-bottom:6px;}
#shop-tip .wt-role{font-family:var(--font-body); font-size:0.8rem; color:var(--ink-soft); margin-left:8px;}
#shop-tip .at-body{font-size:0.9rem; color:var(--ink); line-height:1.35;}
#shop-tip .wt-stats{font-size:0.82rem; color:var(--ink-soft); margin-top:4px;}
#shop-tip .wt-sub{margin-top:8px; padding-top:8px; border-top:1px solid var(--panel-line); font-size:0.85rem; color:var(--gold); line-height:1.3;}

#load-screen .ls-go{margin-top:10px; min-width:220px;}
#load-screen .ls-panel{min-height:400px;} /* the button appearing must not shift the tip */

#wave-tip .wt-total{margin-top:6px; padding-top:6px; border-top:1px solid var(--panel-line); color:#cfc2ec;}

/* Each hover card in the badge's own colour (2026-09-17). */
#wave-tip[data-kind="wave"]{border-color:#9aa4bf;} #wave-tip[data-kind="wave"] .wt-head{color:#9aa4bf;}
#wave-tip[data-kind="boss"]{border-color:var(--gold);} #wave-tip[data-kind="boss"] .wt-head{color:var(--gold);}
#wave-tip[data-kind="streak"]{border-color:#7fb8cc;} #wave-tip[data-kind="streak"] .wt-head{color:#bfefff;}
#wave-tip[data-kind="fury"]{border-color:#ff9a5c;} #wave-tip[data-kind="fury"] .wt-head{color:#ffb27a;}
#wave-tip[data-kind="mutations"]{border-color:#9a7fd0;} #wave-tip[data-kind="mutations"] .wt-head{color:#cfc2ec;}
#wave-tip[data-kind="daily"]{border-color:var(--gold);} #wave-tip[data-kind="daily"] .wt-head{color:var(--gold);}

/* How to Play diagrams (2026-09-20). */
.howto-diagram{display:block; width:100%; max-width:480px; height:auto; image-rendering:pixelated; margin:6px auto 10px; background:var(--panel-deep, #0f141d); border:2px solid var(--panel-line);}
#tower-info .ti-ench.aura{border-color:#7fb8cc; color:#bfefff;}
#tower-info .ti-ench.on.aura{color:#bfefff;}

/* The Skill Tree canvas (2026-09-20). */
.sg-tools{display:flex; justify-content:space-between; align-items:flex-start; gap:10px; flex-wrap:wrap;}
.sg-viewbar{display:flex; gap:6px; flex-wrap:wrap;}
.sg-jump-btn{--tree-accent:var(--ink-soft);}
/* A jump button is a toggle (2026-10-11): pressed, it holds the other six boughs right down so one
   branch can be read on its own. Pressed again, or Fit, gives the whole tree back. */
.sg-jump-btn[data-tree]{border-color:var(--tree-accent); color:var(--tree-accent);}
.sg-jump-btn[data-tree]:hover{background:rgba(255,255,255,0.06); color:var(--ink);}
/* After the [data-tree] rules and at a higher specificity than them, or the per-tree colour wins
   and the focused button paints its own label in its own background. */
.sg-jump-btn[data-tree].on{background:var(--tree-accent); border-color:#fff8e6; color:#0e1018;}
.sg-jump-btn[data-tree].on::after{content:' ✕'; opacity:0.8;}
.sg-jump-btn[data-tree].on:hover{background:var(--tree-accent); color:#0e1018;}
.sg-favour-btn{border-color:var(--gold); color:var(--gold);}
.sg-favour-btn.active{background:#3a2f16;}
#sg-canvas{display:block; width:100%; height:100%; flex:1; min-height:0; cursor:grab; image-rendering:pixelated;}
#sg-canvas:active{cursor:grabbing;}
.sg-info-empty{color:var(--ink-soft); line-height:1.4;}

/* Weekly challenge button, boss gallery, skin preview (2026-09-20). */
#weekly-detail{display:block; font-family:var(--font-body); font-size:0.82rem; letter-spacing:0; color:var(--ink-soft); margin-top:2px; font-weight:400;}
.big-btn.weekly-btn{border-color:var(--purple); color:#e4dcf5; background:rgba(138,111,196,0.10);}
.boss-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(300px, 1fr)); gap:10px; margin-top:8px;}
.boss-card{display:flex; gap:10px; align-items:flex-end; background:var(--panel-deep, #0f141d); border:2px solid var(--panel-line); padding:8px 10px;}
.boss-card.locked{opacity:0.7;}
.boss-spr{flex:0 0 64px; image-rendering:pixelated;}
.boss-main h4{margin:0 0 3px; font-family:var(--font-display); font-size:0.5rem; line-height:1.6; color:var(--gold); font-weight:400;}
.boss-card.locked .boss-main h4{color:var(--ink-soft);}
.boss-sub{font-size:0.8rem; color:var(--ink-soft); line-height:1.3;}
.boss-sub b{color:var(--frost);}
.boss-stat{font-size:0.82rem; color:var(--ink); margin-top:3px;}
.boss-stat b{color:var(--gold);}
/* ---- Tower dress (2026-10-15) ----
   This row stacks rather than sitting side by side, and these three rules are why it has to. The
   skin <select> is sized to its longest skin name and carries min-width:170px, while .opt-row-main
   is allowed to shrink to nothing — side by side, the title and description measured 26px against
   the control's 107, and the 128px preview canvas overflowed its own column and ran underneath
   the dropdown. Stacked, there is nothing to divide. */
#opt-skin-preview{display:block; image-rendering:pixelated; margin-top:6px; height:40px; max-width:100%;}
.opt-row-stack > .filter-field{align-self:flex-start; width:100%; max-width:280px; margin-top:9px;}
.opt-row-stack > .filter-field .pixel-select{width:100%; min-width:0;}

/* Challenge Mode (2026-09-20). */
.big-btn.challenge-btn{border-color:var(--good); color:#dff5e4; background:rgba(79,174,98,0.14);}
.big-btn.challenge-btn:hover{background:rgba(79,174,98,0.26);}
#challenge-screen .skilltree-panel{width:min(820px, 94vw);}
.challenge-cards{display:grid; grid-template-columns:1fr 1fr; gap:14px;}
.challenge-card{background:var(--panel-sunk, #141b27); border:3px solid var(--panel-line); padding:16px; display:flex; flex-direction:column; gap:8px;}
.challenge-card.daily{border-color:var(--gold);} .challenge-card.weekly{border-color:var(--purple);}
.challenge-eyebrow{font-family:var(--font-display); font-size:0.4rem; letter-spacing:0.1em; color:var(--frost);}
.challenge-card h3{margin:0; font-family:var(--font-display); font-size:0.7rem; color:#fbf7ec; line-height:1.6;}
.challenge-card p{margin:0; font-size:0.95rem; color:var(--ink-soft); line-height:1.35; flex:1;}
.challenge-card .big-btn{width:100%;}
#challenge-screen .st-footer{display:flex; gap:10px;} #challenge-screen .st-footer .big-btn{flex:1;}
@media (max-width:700px){ .challenge-cards{grid-template-columns:1fr;} }
/* Options tabs. */
.opt-tabs{display:flex; gap:6px; flex-wrap:wrap; margin-bottom:4px;}
.opt-page{display:flex; flex-direction:column; gap:8px;}
.opt-row-stack{flex-direction:column; align-items:stretch;}
.opt-row-stack .howto-diagram{margin:8px 0;}
/* Fixed layout: the frame is 1280×790 and every panel is sized in px inside it, so nothing reflows. */
body.fixed-layout{display:flex; align-items:center; justify-content:center; overflow:hidden; background:#06090e;}
body.fixed-layout #game-root{flex:0 0 auto; border:2px solid var(--panel-line);}
body.fixed-layout .skilltree-panel{width:760px; max-height:640px;}
body.fixed-layout #skilltree-screen .skilltree-panel{width:1200px; height:680px; max-height:680px;}
body.fixed-layout #levelselect-screen .skilltree-panel{width:1040px;}
body.fixed-layout #infinite-setup-screen .skilltree-panel{width:1000px;}
body.fixed-layout #mercantile-screen .skilltree-panel{width:640px;}
body.fixed-layout #options-screen .skilltree-panel{width:560px;}
body.fixed-layout #challenge-screen .skilltree-panel{width:820px;}
body.fixed-layout .pause-panel{width:340px;}
body.fixed-layout .intro-panel{width:560px;}
body.fixed-layout #ending-screen .ending-panel{width:640px;}
body.fixed-layout .ending-scroll{max-height:400px;}
body.fixed-layout .realm-map{height:300px;}
body.fixed-layout .title-actions{width:280px;}
body.fixed-layout #tut-bubble{width:360px;}
body.fixed-layout .sg-board{grid-template-columns:1fr 280px;}
body.fixed-layout .title-rail{justify-content:flex-end; margin:-8px -8px 6px 0;}
body.fixed-layout .challenge-cards{grid-template-columns:1fr 1fr;}
/* Skill tree side column and the plaque. */
.sg-side{display:flex; flex-direction:column; gap:12px; min-height:0;}
.sg-side .sg-info{flex:1; min-height:0; overflow-y:auto;}
.sg-plaque{flex:0 0 auto; display:flex; align-items:center; gap:14px; padding:14px 16px; cursor:pointer; text-align:left; color:var(--gold);
  background:linear-gradient(180deg, #1c1810 0%, #0f0c08 100%); border:4px double var(--gold);
  box-shadow:inset 0 0 0 2px #2a2214, inset 0 0 24px rgba(240,207,107,0.12), var(--pixel-shadow), 0 0 18px rgba(138,111,196,0.25);}
.sg-plaque:hover{box-shadow:inset 0 0 0 2px #2a2214, inset 0 0 30px rgba(240,207,107,0.22), var(--pixel-shadow), 0 0 26px rgba(138,111,196,0.45); border-color:#f0cf6b;}
.sg-plaque.active{background:linear-gradient(180deg, #2a1f3a 0%, #120c1a 100%); border-color:var(--purple); color:#e4dcf5;}
.sg-plaque canvas{flex:0 0 64px; image-rendering:pixelated; filter:drop-shadow(0 0 6px rgba(240,207,107,0.5));}
.sg-plaque-title{font-family:var(--font-display); font-size:0.62rem; line-height:1.6; letter-spacing:0.06em; text-shadow:0 0 8px rgba(240,207,107,0.5);}
.sg-plaque-sub{font-size:0.82rem; color:var(--ink-soft); margin-top:3px; letter-spacing:0.04em;}
@keyframes plaqueGlow{0%,100%{box-shadow:inset 0 0 0 2px #2a2214, inset 0 0 24px rgba(240,207,107,0.12), var(--pixel-shadow), 0 0 14px rgba(138,111,196,0.2);} 50%{box-shadow:inset 0 0 0 2px #2a2214, inset 0 0 30px rgba(240,207,107,0.2), var(--pixel-shadow), 0 0 26px rgba(138,111,196,0.45);}}
.sg-plaque:not(:hover):not(.active){animation:plaqueGlow 3.2s ease-in-out infinite;}
body.reduce-motion .sg-plaque{animation:none;}

/* The Encyclopaedia (2026-09-20). */
#howto-screen .skilltree-panel.enc-panel{width:min(1200px, 97vw); height:min(800px, 94vh); max-height:94vh;}
body.fixed-layout #howto-screen .skilltree-panel.enc-panel{width:1200px; height:680px; max-height:680px;}
.enc-board{display:grid; grid-template-columns:200px 1fr; gap:14px; flex:1; min-height:0;}
.enc-nav{display:flex; flex-direction:column; gap:6px; overflow-y:auto; min-height:0;}
.enc-nav-btn{text-align:left; background:var(--panel-sunk, #141b27); border:2px solid var(--panel-line); color:var(--ink-soft); font-family:var(--font-display); font-size:0.42rem; line-height:1.6; letter-spacing:0.03em; padding:10px 12px; cursor:pointer;}
.enc-nav-btn:hover{color:var(--ink); border-color:var(--ink-soft);}
.enc-nav-btn.active{color:var(--gold); border-color:var(--gold); background:#1c1810;}
.enc-page{background:var(--panel-sunk, #141b27); border:3px solid var(--panel-line); box-shadow:var(--pixel-shadow); padding:14px 18px; overflow-y:auto; min-height:0; font-size:0.98rem; line-height:1.4; color:var(--ink);}
.enc-title{margin:0 0 10px; font-family:var(--font-display); font-size:0.7rem; line-height:1.6; color:var(--gold); font-weight:400;}
.enc-page h4{margin:16px 0 6px; font-family:var(--font-display); font-size:0.5rem; line-height:1.6; color:var(--frost); letter-spacing:0.04em; font-weight:400;}
.enc-page h4:first-child{margin-top:0;}
.enc-page p{margin:0 0 8px; color:var(--ink); max-width:78ch;}
.enc-steps{margin:0 0 8px; padding-left:22px; max-width:78ch;} .enc-steps li{margin:3px 0;}
.enc-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(280px, 1fr)); gap:8px; margin:4px 0 8px;}
.enc-grid-3{grid-template-columns:repeat(auto-fill, minmax(240px, 1fr));}
.enc-grid-4{grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));}
.enc-card{background:var(--panel-deep, #0f141d); border:2px solid var(--panel-line); padding:10px 12px; font-size:0.92rem; line-height:1.35; color:var(--ink);}
.enc-card b{color:#fbf7ec;}
.enc-unit{display:flex; gap:10px; align-items:flex-start;}
.enc-unit canvas{flex:0 0 48px; image-rendering:pixelated;}
.enc-kingdom{display:flex; gap:10px; align-items:flex-start;} .enc-kingdom.locked{opacity:0.7;}
.enc-kingdom canvas{flex:0 0 48px; image-rendering:pixelated;}
.enc-sub{color:var(--ink-soft); font-size:0.85rem;}
.enc-stats{color:var(--ink-soft); font-size:0.82rem; margin-top:2px; font-variant-numeric:tabular-nums;}
.enc-sig{margin-top:4px; font-size:0.85rem; color:var(--gold);} .enc-sig b{color:var(--gold);}
.enc-note{background:#1c1810; border:2px solid var(--gold-dim); padding:8px 12px; color:#fff1c9; font-size:0.9rem; margin:6px 0 10px; max-width:78ch;}
@media (max-width:760px){ .enc-board{grid-template-columns:1fr;} .enc-nav{flex-direction:row; flex-wrap:wrap;} }
body.fixed-layout .enc-board{grid-template-columns:200px 1fr;}

/* The Tree of Knowing (2026-09-20). */
.sg-motto{font-family:var(--font-display); font-size:0.42rem; letter-spacing:0.22em; color:var(--gold); text-shadow:0 0 8px rgba(240,207,107,0.45); margin-bottom:6px;}
.sg-lore{max-width:none; line-height:1.3; color:#cfc6e6; font-size:0.88rem;}
.sg-lore-dim{color:var(--ink-soft); display:inline; margin-left:8px;}
#skilltree-screen .skilltree-panel{background:linear-gradient(180deg, var(--ground-85, rgba(20,18,40,0.94)), var(--sheet-solid, rgba(12,16,24,0.94))); border-color:var(--frame-line, #5a4a7a);}
#skilltree-screen .sg-flow-wrap{border-color:#8a6a1c; box-shadow:var(--pixel-shadow), inset 0 0 0 1px rgba(240,207,107,0.25);}
#skilltree-screen .sg-info{border-color:var(--frame-line, #5a4a7a);}
#sg-info .cost-amt small{display:block; color:var(--ink-soft); font-size:0.72rem; margin-top:2px;}

/* The Mastery bar in the Skill Tree header. The count sits INSIDE the bar —
   a second line of text beside it is the crowding this is avoiding. */
.st-mastery-bar{
  position:relative; margin:4px 0 0; height:14px;
  background:var(--panel-deep, #0f141d); border:2px solid var(--panel-line);
  overflow:hidden;
}
.st-mastery-bar[hidden]{display:none;}
.st-mb-fill{
  position:absolute; inset:0 auto 0 0; width:0;
  background:linear-gradient(90deg, #4b3f70 0%, #9a86d6 65%, #e4dcf5 100%);
}
.st-mb-text{
  position:relative; display:block; text-align:center; line-height:10px;
  font-family:var(--font-display); font-size:0.40rem; letter-spacing:0.06em;
  color:#f2ecff; text-shadow:1px 1px 0 rgba(0,0,0,0.8);
}
.sg-info-mastery{margin:4px 0 2px; font-size:0.74rem; color:#d6c6ff; letter-spacing:0.02em;}
.sg-info-mastery b{color:#f2ecff;}
.sg-undo-btn{border-color:#c9394f; color:#ffd9de;}
.sg-undo-btn:disabled{opacity:0.4; cursor:default; border-color:var(--panel-line); color:var(--ink-soft);}

/* Playtest pass (2026-09-21): in-run DOM overlays scale with the stage. At a full-size stage the
   scale is 1 and nothing changes; in a small window the ruler strip, cards, toasts, Barracks and
   tower panel shrink with the battlefield instead of eating it. */
#ruler-say{width:calc(320px * var(--ui-scale)); bottom:calc(88px * var(--ui-scale)); right:calc(14px * var(--ui-scale)); padding:calc(8px * var(--ui-scale)) calc(10px * var(--ui-scale)); gap:calc(10px * var(--ui-scale)); border-width:calc(3px * var(--ui-scale));}
#ruler-say .rs-text{font-size:calc(0.9rem * var(--ui-stage-scale));}
#ruler-say .rs-name{font-size:calc(0.45rem * var(--ui-stage-scale));}
#ruler-say canvas{height:calc(60px * var(--ui-scale)) !important; width:auto !important;}
#toast{font-size:calc(0.85rem * var(--ui-stage-scale)); padding:calc(8px * var(--ui-scale)) calc(16px * var(--ui-scale)); max-width:calc(60% + 20px);}
#mechanic-tip{width:calc(min(360px, 45%) * var(--ui-scale)); padding:calc(12px * var(--ui-scale)) calc(14px * var(--ui-scale));}
#mechanic-tip .mt-body{font-size:calc(0.92rem * var(--ui-stage-scale));}
#mechanic-tip .mt-name{font-size:calc(0.58rem * var(--ui-stage-scale));}
#wave-tip{max-width:calc(420px * var(--ui-scale)); min-width:calc(260px * var(--ui-scale)); padding:calc(10px * var(--ui-scale)) calc(12px * var(--ui-scale));}
#wave-tip .wt-stats, #wave-tip .wt-sub{font-size:calc(0.86rem * var(--ui-stage-scale));}
#wave-tip .wt-head, #wave-tip .wt-name{font-size:calc(0.5rem * var(--ui-stage-scale));}
#ability-tip, #ench-tip, #shop-tip{width:calc(250px * var(--ui-scale)); padding:calc(10px * var(--ui-scale)) calc(12px * var(--ui-scale));}
#ability-tip .at-body, #ench-tip .at-body, #shop-tip .at-body{font-size:calc(0.9rem * var(--ui-stage-scale));}
#ability-tip .wt-sub, #ench-tip .wt-sub, #shop-tip .wt-sub, #shop-tip .wt-stats{font-size:calc(0.84rem * var(--ui-stage-scale));}
#ability-tip .wt-name, #ench-tip .wt-name, #shop-tip .wt-name{font-size:calc(0.58rem * var(--ui-stage-scale));}
.shop-item{width:calc(55px * var(--stage-scale)); height:calc(44px * var(--stage-scale));}
.shop-item canvas{width:calc(26px * var(--stage-scale)); height:calc(28px * var(--stage-scale));}
.shop-item .shop-cost{font-size:calc(0.68rem * var(--stage-scale));}
#shop-overlay{bottom:calc(6px * var(--stage-scale)); gap:calc(6px * var(--stage-scale)); padding:calc(6px * var(--stage-scale));}
#shop-overlay::before{font-size:calc(0.3rem * var(--stage-scale)); padding:calc(3px * var(--stage-scale)) calc(6px * var(--stage-scale)); top:calc(-9px * var(--stage-scale)); left:calc(10px * var(--stage-scale));}
#tower-info{width:calc(300px * var(--ui-scale)); font-size:calc(0.95rem * var(--ui-stage-scale)); padding:calc(12px * var(--ui-scale)) calc(14px * var(--ui-scale));}
#tower-info h3{font-size:calc(0.6rem * var(--ui-stage-scale)); height:calc(0.96rem * var(--ui-stage-scale));}
/* Story map at small sizes: spheres and names scale with the panel; the panel scrolls rather than clips. */
.realm-stop canvas{width:min(84px, 9vw); height:min(84px, 9vw);}
.realm-stop .realm-name{font-size:min(0.78rem, 1.5vw + 0.3rem);}
#levelselect-screen .skilltree-panel{overflow-y:auto;}
/* Same rule, same reason: this panel already scrolled, but its children
   still shrank to fit before it ever did. */
#levelselect-screen .skilltree-panel > *{flex:0 0 auto;}

/* The Wardrobe (Achievements screen, 2026-09-21): every tower skin as a card, worn with one click. */
.wardrobe{margin:0 0 8px; border:2px solid var(--panel-line); background:var(--ground-60, rgba(14,18,28,0.6)); padding:0;}
.wardrobe-head{display:flex; align-items:center; gap:12px; width:100%; background:none; border:0; box-shadow:none; padding:6px 10px; cursor:pointer; text-align:left; color:inherit; letter-spacing:0;}
.wardrobe-head:hover{background:rgba(240,207,107,0.08);}
.wardrobe-head h3{margin:0; font-family:var(--font-display); font-size:0.52rem; color:var(--gold); line-height:1.4;}
.wardrobe-head span{font-size:0.8rem; color:var(--ink-soft);}
.wardrobe-caret{margin-left:auto; color:var(--gold); font-size:0.9rem;}
.wardrobe.open .wardrobe-grid{padding:0 10px 8px;}
.wardrobe-grid{display:flex; flex-wrap:nowrap; gap:6px; overflow-x:auto; padding-bottom:4px;} /* one strip, scrolls sideways: the achievements list keeps its height */
.wardrobe-card{flex:0 0 104px; width:104px; background:var(--panel-sunk, #141b27); border:2px solid var(--panel-line); color:var(--ink); padding:4px 4px 5px; cursor:pointer; text-align:center; display:flex; flex-direction:column; align-items:center; gap:2px;}
.wardrobe-card canvas{image-rendering:pixelated; width:96px; height:40px;}
.wardrobe-card b{font-family:var(--font-display); font-size:0.42rem; line-height:1.5;}
.wardrobe-card small{font-size:0.66rem; color:var(--ink-soft); line-height:1.25;}
.wardrobe-card:hover:not(:disabled){border-color:var(--gold);}
.wardrobe-card.worn{border-color:var(--gold); box-shadow:0 0 0 1px var(--gold) inset;}
.wardrobe-card.worn small{color:var(--gold);}
.wardrobe-card.locked{opacity:0.75; cursor:default;}
/* The rail on the two setup screens. The cards above are shared with The
   Wardrobe deliberately — one place to change how a skin reads — so all that
   is needed here is the line that says what is worn and a little air. */
.setup-skins-section h3{margin-bottom:6px;}
.setup-skins .wardrobe-grid{padding:0 0 4px;}
.skinrail-head{display:flex; align-items:baseline; gap:10px; margin:0 0 6px;}
.skinrail-head b{font-family:var(--font-display); font-size:0.42rem; line-height:1.6; color:var(--gold);}
.skinrail-head span{font-size:0.72rem; color:var(--ink-soft);}
/* The Story screen is the tighter of the two — a road, a detail card and a
   footer are already in it — so its rail is a shorter strip. */
.story-skins-section{margin-top:8px;}
.story-skins-section .skinrail-head{margin-bottom:4px;}
.story-skins-section .wardrobe-card{flex-basis:92px; width:92px;}
.story-skins-section .wardrobe-card canvas{width:84px; height:34px;}

/* The Centenary (2026-09-21): the Crown Boon cards in gold; a crown per century on the boards. */
.crown-panel{border-color:var(--gold);}
.crown-panel h1{color:var(--gold);}
.mutator-card.crown{border-color:var(--gold-dim);}
.mutator-card.crown h4{color:var(--gold);}
.mutator-card.crown:hover{border-color:var(--gold); background:#1f1a12;}
.lb-crown{font-size:0.8rem; color:var(--gold); margin-right:8px; letter-spacing:0;}

/* Named towers and the best-wave tick (2026-09-21). */
.ti-name-row{margin:-4px 0 8px;}
.ti-name{width:100%; box-sizing:border-box; background:var(--panel-deep, #0f141d); border:2px solid var(--panel-line); color:var(--ink); font-family:var(--font-body, inherit); font-size:0.85rem; padding:3px 6px;}
.ti-name:focus{outline:none; border-color:var(--gold);}
.ti-name::placeholder{color:var(--ghost);}
/* ---- how much the HUD shows (2026-10-11) ----
   'standard' is the default and drops the two pieces of between-runs reference from the strip;
   'compact' drops those and the speed button too. See hudShows() in game.js for the badges on the
   battlefield itself. */
body[data-hud="standard"] .hud-best,
body[data-hud="standard"] .hud-item.hud-xp,
body[data-hud="compact"] .hud-best,
body[data-hud="compact"] .hud-item.hud-xp{display:none;}
.hud-best{font-family:var(--font-display); font-size:0.42rem; color:var(--ghost); margin-left:8px; letter-spacing:0; white-space:nowrap;}
.hud-best.beat{color:var(--gold);}

/* The pencil (2026-09-21): the name field is behind a small edit mark in the panel title. */
#tower-info h3{display:flex; align-items:center; gap:6px;}
#tower-info h3 .ti-title{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
/* The level never shrinks and never ellipses — .ti-title is the only flexible part of the line. */
#tower-info h3 .ti-lv{flex:0 0 auto; color:var(--gold); white-space:nowrap;}
#tower-info h3 .ti-lv::before{content:'· '; color:var(--ink-soft);}
/* Ascended is the level chip wearing the frost colours, not a second pill: two of them ate 145px of
   a 238px line and left the tower's own name with 53. */
#tower-info h3 .ti-lv.asc{color:#8fe3ff;}
#tower-info h3 .ti-lv.asc::before{color:#2a5468;}
#tower-info .ti-rename{flex:0 0 22px; width:22px; min-width:0; box-sizing:border-box; font-family:var(--font-body); font-size:0.95rem; line-height:1; padding:0; height:18px; display:flex; align-items:center; justify-content:center; background:#191230; border:2px solid #453a63; color:#cfc2ec; letter-spacing:0; box-shadow:none; cursor:pointer;}
#tower-info .ti-rename:hover{border-color:#c0abe8; background:#281e45; color:#fff;}
.ti-name-row.hidden{display:none;}
/* The menu frame (2026-09-21): a darker violet wrapper with a thin gold inner line, a soft violet
   glow, faint runic ticks along the edge and a star at two corners — less "purple box". */
.skilltree-panel{
  border:3px solid var(--frame-line, #4a3a7a);
  box-shadow:0 0 0 1px #0b0814, 0 0 0 3px var(--frame-gold, rgba(201,171,88,0.55)), 0 0 28px var(--frame-glow, rgba(138,111,196,0.28)), var(--pixel-shadow);
  background:
    linear-gradient(90deg, var(--frame-gold-soft, rgba(201,171,88,0.18)) 0 2px, transparent 2px 14px) top/100% 3px repeat-x,
    linear-gradient(90deg, var(--frame-gold-soft, rgba(201,171,88,0.18)) 0 2px, transparent 2px 14px) bottom/100% 3px repeat-x,
    var(--sheet, rgba(12,16,24,0.92));
}
.skilltree-panel::before, .skilltree-panel::after{
  content:'✦'; position:absolute; color:var(--gold, #c9ab58); font-size:14px; line-height:1; pointer-events:none;
  text-shadow:0 0 6px var(--frame-gold, rgba(201,171,88,0.6));
}
.skilltree-panel::before{top:2px; left:3px;}
.skilltree-panel::after{bottom:2px; right:3px;}
#levelselect-screen .skilltree-panel{overflow-x:hidden;}

/* The opening (2026-09-21): the skip button rides the battlefield's bottom-right corner. */
#cine-skip{position:absolute; right:calc(14px * var(--ui-scale)); bottom:calc(14px * var(--ui-scale)); z-index:6; font-family:var(--font-display); font-size:calc(0.5rem * var(--ui-stage-scale)); padding:calc(8px * var(--ui-scale)) calc(12px * var(--ui-scale)); background:rgba(18,24,34,0.9); border:2px solid var(--gold-dim); color:var(--gold); cursor:pointer; box-shadow:none;}
#cine-skip:hover{border-color:var(--gold); background:rgba(36,29,15,0.95);}
#cine-skip.hidden{display:none;}
body.cine-on #shop-overlay, body.cine-on #ruler-say, body.cine-on #tower-info, body.cine-on #wave-tip, body.cine-on #mechanic-tip{display:none !important;}
/* The HUD bar dims with the field during the opening — it sits outside the canvas, so the
   cinematic's own darkness never reached it (2026-09-22). */
body.cine-on #hud{opacity:0.3; pointer-events:none; filter:saturate(0.4);}
#hud{transition:opacity 0.5s ease-out, filter 0.5s ease-out;}
.lb-march{font-size:0.7rem; color:var(--ink-soft); margin-right:8px; letter-spacing:0;}

/* The Crest, in the Skill Tree header (2026-09-25). Given what it costs — the entire tree — it gets
   the only gold-on-gold plaque on the screen, and a soft glow so the eye lands on it first. */
/* The Crest sits in the side column directly above Elysia's Favour and is built as its twin — same
   double border, same plate, same 64px emblem — so the two honours read as a pair rather than as a
   badge someone squeezed into the header. */
.st-crest{flex:0 0 auto; display:flex; align-items:center; gap:14px; padding:14px 16px; cursor:help; text-align:left; color:var(--gold);
  background:linear-gradient(180deg, #1c1810 0%, #0f0c08 100%); border:4px double var(--gold);
  box-shadow:inset 0 0 0 2px #2a2214, inset 0 0 24px rgba(240,207,107,0.12), var(--pixel-shadow), 0 0 18px rgba(240,207,107,0.22);}
.st-crest canvas{flex:0 0 64px; width:64px; height:74px; image-rendering:pixelated; filter:drop-shadow(0 0 6px rgba(240,207,107,0.5));}
/* Before it is earned the plate is still there, just unlit — so the honour is visible as a goal. */
.st-crest.locked{border-color:#3f4560; color:var(--ink-soft); background:linear-gradient(180deg, #141a27 0%, #0c0f18 100%);
  box-shadow:inset 0 0 0 2px #1b2130, var(--pixel-shadow);}
.st-crest.locked canvas{filter:none; opacity:0.85;}
.st-crest.locked .st-crest-label b{color:#8d96b4; text-shadow:none;}
.st-crest-label{display:flex; flex-direction:column; gap:3px;}
.st-crest-label b{font-family:var(--font-display); font-size:0.62rem; line-height:1.6; letter-spacing:0.06em; color:var(--gold); text-shadow:0 0 8px rgba(240,207,107,0.5);}
.st-crest-label span{font-size:0.82rem; color:var(--ink-soft); letter-spacing:0.04em;}

/* The naming, on the victory screen (2026-09-24): quieter than the epilogue above it, and the one
   thing on the screen that is not a number. */
.victory-naming{margin:2px 0 10px; font-size:0.95rem; line-height:1.5; color:var(--ink-soft); font-style:italic;}
.victory-naming b{font-style:normal; font-family:var(--font-display); font-size:0.62rem; color:var(--gold); letter-spacing:0.02em;}

/* Overall game progress on the Achievements page (2026-09-23): one centred line under the header —
   a label, a thin bar and the number. Deliberately small: it is a headline, not a second scoreboard. */
.ach-progress{display:flex; align-items:center; justify-content:center; gap:10px; margin:-2px 0 8px; cursor:help;}
.ach-progress .ap-label{font-family:var(--font-display); font-size:0.4rem; letter-spacing:0.04em; color:var(--gold); white-space:nowrap;}
.ach-progress .ap-bar{width:210px; height:10px; background:rgba(10,14,22,0.75); border:2px solid var(--panel-line); border-radius:0; overflow:hidden;}
.ach-progress .ap-bar i{display:block; height:100%; background:linear-gradient(90deg,#8a6a1c,#e3b94c); box-shadow:inset 0 2px 0 0 rgba(255,241,201,0.25);}
.ach-progress .ap-num{font-size:0.9rem; font-variant-numeric:tabular-nums; color:var(--ink); min-width:2.8em;}

/* Challenge cards spell out the run (2026-09-21). */
.challenge-spec{display:flex; flex-direction:column; gap:5px; background:rgba(10,14,22,0.6); border:2px solid var(--panel-line); padding:8px 10px; font-size:0.86rem; line-height:1.35;}
.ch-row{display:flex; gap:10px;}
.ch-k{flex:0 0 58px; font-family:var(--font-display); font-size:0.42rem; line-height:1.9; color:var(--gold); letter-spacing:0;}
.ch-v{color:var(--ink);} .ch-v b{color:#fff1c9;} .ch-v i{color:#c0abe8; font-style:normal;}
.ch-status .ch-v{color:var(--ink-soft);}
/* Scrollbars in the game's own dress (2026-09-21): a dark rail and a bevelled gold thumb, square
   corners, no arrows. WebKit only — setting the standard scrollbar-color/width would switch these off. */
::-webkit-scrollbar{width:12px; height:12px;}
::-webkit-scrollbar-track{background:#0d1119; border-left:2px solid #232c3d;}
::-webkit-scrollbar-track:horizontal{border-left:0; border-top:2px solid #232c3d;}
::-webkit-scrollbar-thumb{background:#3a2f16; border:2px solid #8a6a1c; box-shadow:inset 2px 2px 0 0 rgba(255,241,201,0.18), inset -2px -2px 0 0 rgba(0,0,0,0.45);}
::-webkit-scrollbar-thumb:hover{background:#5a4a1c; border-color:#c9ab58;}
::-webkit-scrollbar-corner{background:#0d1119;}
::-webkit-scrollbar-button{display:none; width:0; height:0;}

/* The tower panel never reflows (2026-09-21): a fixed height, a scrolling body, the buttons pinned
   at the foot in a fixed grid (Upgrade on its own row, Move/Sell below), and the enchant buttons
   in a 2×2 grid of equal cells whether they are bought or not. */
#tower-info{display:flex; flex-direction:column; height:calc(392px * var(--ui-scale)); box-sizing:border-box;}
#tower-info h3{flex:0 0 auto;}
#tower-info .ti-body{flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden; padding-right:2px;}
#tower-info .ti-actions{flex:0 0 auto; display:grid; grid-template-columns:1fr 1fr; gap:6px 8px; margin-top:8px;}
#tower-info .ti-actions button{min-width:0; width:100%; height:calc(34px * var(--ui-scale)); padding:0 6px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
#tower-info .ti-actions #ti-upgrade{grid-column:1 / -1;}
#tower-info .ti-actions.two #ti-sell{grid-column:1 / -1;}
#tower-info .ti-enchants{display:grid; grid-template-columns:1fr 1fr; grid-auto-rows:calc(36px * var(--ui-scale)); gap:4px; margin-top:6px;}
#tower-info .ti-ench, #tower-info .ti-ench.on{height:100%; box-sizing:border-box; padding:2px 6px; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; line-height:1.15;}
#tower-info .ti-ench small{line-height:1.1;}

/* The Barracks fades with the keep abilities while a wave runs and the pointer is up-field. */
#shop-overlay{transition:opacity 0.25s ease-out;}
body.trays-idle #shop-overlay{opacity:0.42;}
body.trays-idle #shop-overlay:hover{opacity:1;}

/* The Council's last word (2026-09-22). */
.ending-council{font-style:italic; color:#e4dcf5; border-left:3px solid var(--gold-dim); padding:2px 0 2px 14px; margin:16px 0 2px; text-align:left; line-height:1.5;}
#ending-body .ending-council-who{font-family:var(--font-display); font-size:0.44rem !important; line-height:2.2; color:var(--gold); letter-spacing:0; margin:8px 0 18px; padding-left:14px; text-align:left; text-transform:none; overflow:visible;}

/* The realm's rule, on its own line under its name rather than running on from it (2026-10-11).
   The flavour belongs to the lore block above; this block is the rules. */
.level-mechanic .lm-rule{display:block; margin-top:4px;}
#wave-tip[data-kind="cata"]{border-color:#8fe0f2;} #wave-tip[data-kind="cata"] .wt-head{color:#bff0ff;}

/* The transport buttons show where they end (2026-10-17). A pixel glyph with
   no border around it gives no sense of its hit area; you aim at the symbol,
   land on the padding, and conclude the button is dead. The border IS the
   clickable edge - it is drawn on the element that takes the click. */
#mt-controls button{
  border:1px solid rgba(154,164,191,0.38);
  border-radius:2px;
  background:rgba(12,16,24,0.5);
}
#mt-controls button:hover,
#mt-controls button:focus-visible{ border-color:var(--gold, #e3b94c); background:rgba(32,40,58,0.8); }
#mt-controls button:active{ background:rgba(48,60,84,0.9); }

/* ---------------------------------------------------------------------------
   Profile sign-in (2026-10-17)
   ------------------------------------------------------------------------ */
#profile-auth{ display:flex; align-items:center; justify-content:center; }
#profile-auth.hidden{ display:none; }
.auth-panel{
  width:min(440px, 92%); padding:26px 28px 22px;
  background:rgba(14,19,28,0.97); border:3px solid var(--panel-line);
  display:flex; flex-direction:column; gap:14px;
}
.auth-panel h2{ margin:0; font-size:0.86rem; color:var(--gold); }
.auth-sub{ margin:0; font-size:0.82rem; line-height:1.5; color:var(--ink-soft); }
.auth-body{ display:flex; flex-direction:column; gap:10px; }
.auth-body label{ display:flex; flex-direction:column; gap:5px; font-size:0.74rem; color:var(--ink-soft); }
.auth-body input, .auth-body select{
  padding:10px 11px; font:inherit; font-size:0.8rem;
  background:#0b0e15; color:var(--ink); border:2px solid var(--panel-line);
}
.auth-body input:focus, .auth-body select:focus{ outline:none; border-color:var(--gold); }
.auth-err{ margin:0; font-size:0.78rem; color:#ff9b7a; }
.auth-actions{ display:flex; gap:10px; }
.auth-actions .big-btn{ flex:1; }
.auth-link{
  background:none; border:0; padding:4px 0 0; cursor:pointer;
  font:inherit; font-size:0.74rem; color:var(--ink-soft); text-decoration:underline;
}
.auth-link:hover{ color:var(--gold); }
/* A locked profile says so in the list, so "why is it asking me" never comes up. */
.profile-row .profile-lock{ margin-left:6px; color:var(--gold); }
.profile-setpw{ font-size:0.68rem; padding:6px 9px; }

/* The live match check is not always a complaint. */
.auth-err.auth-ok{ color:#8fe0a0; }

/* ---------------------------------------------------------------------------
   The edge rail's clipping box (2026-10-18)
   ---------------------------------------------------------------------------
   The three drawers park by translating themselves off the right-hand edge,
   so the parked half of each one really is laid out beyond the viewport.
   overflow-x:hidden on <body> only hides that from the document's scrollbar;
   a mobile browser's visual viewport pans independently and shows it anyway.
   Clipping it with an element instead settles it for good: what is outside an
   overflow:hidden box is not painted, so there is nothing to find.

   inset:0 with #game-root unpositioned resolves to the viewport, which is
   exactly where the drawers already were — so this changes where they are
   CLIPPED and not where they sit. layoutEdgeRail() re-derives the rail's top
   by measurement, so the new containing block needs no arithmetic anywhere.
   ------------------------------------------------------------------------ */
#edge-rail-clip{
  position:absolute; inset:0;
  overflow:hidden;
  pointer-events:none;          /* never eat a click meant for the game */
  z-index:118;
}
#edge-rail-clip > .edge-tab{ pointer-events:auto; }
/* A shut door with a date on it, where the Aura Orb buy will be (2026-10-18).
   Styled as the disabled buy rather than as an absence: the player should see
   the shape of the thing they do not have yet. */
.ti-ench.aura.locked{ opacity:0.45; cursor:default; border-style:dashed; }

/* The Log's unread count, on the parked handle (2026-10-19). Small, gold and
   on the outer edge so it is readable without opening the drawer. */
#log-tab-handle{ position:relative; }
#log-tab-handle .log-count{
  position:absolute; top:5px; left:50%; transform:translateX(-50%);
  min-width:15px; padding:1px 3px; border-radius:8px;
  background:var(--gold, #e3b94c); color:#1a1508;
  font-size:0.42rem; line-height:1.5; text-align:center; font-weight:700;
  pointer-events:none;
}
#log-tab-handle .log-count[hidden]{ display:none; }

/* ---------------------------------------------------------------------------
   Infinite Mode's difficulty row (2026-10-19)
   ---------------------------------------------------------------------------
   Cut from the same cloth as the realm stops under it - same panel, same
   border, same selected gold - because the two are one decision taken in
   order rather than two widgets that happen to share a screen.
   ------------------------------------------------------------------------ */
.setup-difficulty{ display:flex; gap:10px; }
.setup-diff{
  flex:1 1 0; min-width:0; padding:10px 12px; text-align:left; cursor:pointer;
  display:flex; flex-direction:column; gap:3px;
  background:var(--panel, #161c28); border:2px solid var(--panel-line, #324058);
  color:var(--ink, #e8ecf6); font:inherit;
}
.setup-diff .sd-name{ font-size:0.52rem; letter-spacing:0.04em; }
.setup-diff .sd-blurb{ font-size:0.72em; line-height:1.4; color:var(--ink-soft, #9aa4bf); }
.setup-diff.open:hover, .setup-diff.open:focus-visible{ border-color:var(--gold, #e3b94c); }
.setup-diff.selected{ border-color:var(--gold, #e3b94c); background:rgba(227,185,76,0.12); }
.setup-diff.selected .sd-name{ color:var(--gold, #e3b94c); }
.setup-diff.locked{ opacity:0.45; cursor:default; border-style:dashed; }
/* The Cataclysm names itself in the colour it uses everywhere else. */
.setup-diff:last-child.selected{ border-color:#c9394f; background:rgba(201,57,79,0.12); }
.setup-diff:last-child.selected .sd-name{ color:#ff9aa8; }
@media (max-width: 760px){ .setup-difficulty{ flex-direction:column; } }

/* ---------- the credits ----------
   The ending scroll's shape, with the roll softened off at top and bottom so it reads as passing
   through the panel rather than starting and stopping inside it. */
/* Darker than the other panels on purpose. Every one of them is a sheet you read for a few
   seconds over a lit room; this is two and a half minutes of small type, and the throne room's
   moon sits directly behind the middle of it. */
#credits-screen .credits-panel{width:min(640px, 92vw); align-items:center; text-align:center; background:rgba(8,11,17,0.95);}
.credits-panel h1{font-family:var(--font-display); font-size:0.95rem; color:#fbf7ec; margin:0; text-shadow:3px 3px 0 rgba(0,0,0,0.6);}
.credits-scroll{
  max-height:min(430px, 54vh); overflow-y:auto; width:100%; min-height:0; padding:0 10px;
  mask-image:linear-gradient(to bottom, transparent 0, #000 46px, #000 calc(100% - 46px), transparent 100%);
  -webkit-mask-image:linear-gradient(to bottom, transparent 0, #000 46px, #000 calc(100% - 46px), transparent 100%);
}
.credits-scroll:focus-visible{outline:2px solid var(--gold); outline-offset:3px;}
/* The roll starts and ends off the panel, which is what the mask above is for. */
.cr-gap{height:54px;}
.cr-head{font-family:var(--font-display); font-size:0.46rem; letter-spacing:0.1em; color:var(--gold); margin:30px 0 14px; line-height:1.6;}
.cr-note{font-size:0.95rem; line-height:1.5; color:var(--ink-soft); max-width:44ch; margin:0 auto;}
.cr-open{font-size:1.05rem; color:var(--ink); margin-bottom:28px;}
.cr-roles{display:flex; flex-direction:column; gap:16px; margin:0;}
.cr-roles dt{font-family:var(--font-display); font-size:0.36rem; letter-spacing:0.06em; color:var(--ink-soft); line-height:1.7;}
.cr-roles dd{margin:5px 0 0; font-size:1.05rem; color:var(--gold);}
.cr-score{list-style:none; counter-reset:cr; margin:0; padding:0; display:flex; flex-direction:column; gap:2px;}
.cr-score li{counter-increment:cr; font-size:0.98rem; color:var(--ink);}
.cr-score li::before{content:counter(cr, decimal-leading-zero) "\00a0\00a0"; color:var(--frost); font-variant-numeric:tabular-nums;}
.cr-you{font-size:0.98rem; line-height:1.5; color:var(--frost); max-width:44ch; margin:0 auto;}
.cr-you b{color:var(--gold);}
.cr-thanks{font-size:1.12rem; color:var(--ink); margin:34px 0 0;}
.cr-motto{font-family:var(--font-display); font-size:0.52rem; letter-spacing:0.08em; color:var(--gold); margin:28px 0 0; line-height:1.9;}
.cr-motto span{display:block; font-family:var(--font-body); font-size:1rem; letter-spacing:0; font-style:italic; color:var(--ink-soft); margin-top:10px;}
.cr-fine{font-size:0.82rem; color:var(--ink-soft); margin:24px 0 0;}

/* ---------- the Steam Cloud screen ----------
   It borrows the profile list wholesale: same rows, same type, same spacing, because it is
   the same thing seen from somewhere else. Only three things differ, and all three are here. */
/* The name in a cloud row is not a button that goes anywhere - the row's action is the one on
   the right - so it must not read as a disabled control. Full ink, normal cursor. */
#cloud-list .profile-select[disabled]{ opacity:1; cursor:default; }
/* Armed by a first click, and now saying what the second one will do. Gold rather than red:
   this replaces a save, it does not delete one, and the cloud copy survives either way. */
#cloud-list .cloud-armed{
  border-color:var(--gold); color:var(--gold);
  background:rgba(227,185,76,0.12); white-space:normal; line-height:1.35;
}
.cloud-empty{ margin:10px 2px; color:var(--ink-soft); font-size:0.8rem; }

/* ---------- the two places a save can live ----------
   Tabs on the profile screen, not a second screen behind a button. Underline rather than
   boxes: these sit directly above a list of rows, and a boxed tab on top of bordered rows
   reads as two competing frames. */
.profile-tabs{ display:flex; gap:22px; margin:0 2px 14px; border-bottom:1px solid rgba(255,255,255,0.09); }
.pt-tab{
  background:none; border:0; border-bottom:2px solid transparent;
  margin-bottom:-1px; padding:6px 2px 9px;
  font-family:inherit; font-size:0.78rem; letter-spacing:0.04em;
  color:var(--ink-soft); cursor:pointer;
}
.pt-tab:hover{ color:var(--ink); }
.pt-tab.active{ color:var(--gold); border-bottom-color:var(--gold); }
.pt-tab[hidden]{ display:none; }
#pane-cloud #cloud-status{ margin:0 2px 12px; }
#pane-cloud #btn-cloud-save{ margin-top:14px; }
