  * { margin: 0; padding: 0; box-sizing: border-box; }
  html, body { width: 100%; height: 100%; overflow: hidden; background: #05060d; }
  canvas { display: block; }
  #hud {
    position: fixed; top: 0; left: 0; right: 0;
    padding: 14px 18px;
    font-family: "Segoe UI", system-ui, sans-serif;
    color: #cfe6ff; pointer-events: none;
    display: flex; justify-content: space-between; align-items: flex-start;
    text-shadow: 0 0 6px rgba(0,0,0,.8);
  }
  #hud .right { text-align: right; }
  .bigval { font-size: 14px; font-weight: 600; }
  .hud-earned { color: #77ee77; } /* credits earned this run — green, matches the "+xx" kill popups */
  .label { font-size: 12px; letter-spacing: 1px; opacity: .6; text-transform: uppercase; }
  #shieldbar { width: 220px; height: 10px; border: 1px solid #3a6ea5; border-bottom: 0;
               border-radius: 7px 7px 0 0; overflow: hidden; margin-top: 4px; background: rgba(0,0,0,.4); }
  #shieldfill { height: 100%; width: 100%; background: linear-gradient(90deg,#36d1dc,#5b86e5); transition: width .15s; } /* blue = active */
  #shieldbar.recharging #shieldfill { background: linear-gradient(90deg,#7b2ff7,#b06bff); } /* purple = recharging */
  #hpbar { width: 220px; height: 14px; border: 1px solid #3a6ea5; border-radius: 7px; overflow: hidden; margin-top: 4px; background: rgba(0,0,0,.4); }
  #hpbar.with-shield { border-radius: 0 0 7px 7px; border-top: 0; margin-top: 0; } /* touch the shield bar above */
  #hpfill { height: 100%; width: 100%; background: linear-gradient(90deg,#ff5a5a,#c81e1e); transition: width .15s; } /* health = red */
  #hppct { margin-top: 4px; font: 700 14px system-ui, sans-serif; color: #cfe6ff; }
  #help {
    position: fixed; bottom: 14px; left: 18px;
    font-family: system-ui, sans-serif; font-size: 13px; color: #7f93b5;
    pointer-events: none; line-height: 1.6;
  }
  #help b { color: #cfe6ff; }
  #overlay {
    position: fixed; inset: 0; display: none;
    align-items: center; justify-content: center; flex-direction: column;
    background: rgba(5,6,13,.82); color: #e8f1ff;
    font-family: system-ui, sans-serif; text-align: center;
  }
  #overlay h1 { font-size: 44px; margin-bottom: 8px; }
  #overlay p { opacity: .75; margin-bottom: 24px; }
  #overlay button {
    pointer-events: auto; cursor: pointer;
    padding: 12px 30px; font-size: 16px; border: 0; border-radius: 8px;
    background: linear-gradient(90deg,#36d1dc,#5b86e5); color: #02101f; font-weight: 700;
  }
  /* secondary overlay button (e.g. "Back to Hangar" beside Restart) — muted outline, sits below */
  #overlay button.secondary {
    margin-top: 10px; background: rgba(255,255,255,.08); color: #dce8ff;
    border: 1px solid rgba(160,190,255,.35);
  }
  /* Welcome / start screen: a fixed grid — scrollable greeting/intro on top, pinned Take-off footer at the
     bottom (only the text scrolls; Take off is always on-screen, like the Main Window). */
  #welcome {
    position: fixed; inset: 0; z-index: 12; display: none;
    grid-template-columns: 100%;
    grid-template-rows: 1fr auto;
    box-sizing: border-box;
    background: radial-gradient(120% 90% at 50% 20%, rgba(20,32,60,.72), rgba(5,6,13,.92));
    color: #e8f1ff; font-family: system-ui, sans-serif; text-align: center; padding: 24px;
  }
  /* Scroll region: centers the greeting+intro when they fit, and (via auto margins, NOT
     justify-content:center — which would clip the top in a scroll container) top-aligns + scrolls fully
     into reach when the text overflows a short viewport. */
  #welcome-scroll {
    overflow-y: auto; min-height: 0;
    display: flex; flex-direction: column; align-items: center;
  }
  #welcome-scroll > :first-child { margin-top: auto; }
  #welcome-scroll > :last-child  { margin-bottom: auto; }
  /* On PC (non-phone) the start screen pins the greeting/intro AND the Take-off footer to the TOP (button
     still directly under the text), instead of the mobile vertical-center. Both rows size to content and
     align to the top; the scroll cell's centering auto-margins are dropped so the text starts at the top. */
  body:not(.dev-phone) #welcome { grid-template-rows: auto auto; align-content: start; }
  body:not(.dev-phone) #welcome-scroll > :first-child { margin-top: 0; }
  body:not(.dev-phone) #welcome-scroll > :last-child  { margin-bottom: 0; }
  /* Pinned footer: Take off + community link, always visible. */
  #welcome-footer {
    display: flex; flex-direction: column; align-items: center; gap: 14px;
    padding-top: 16px;
  }
  #welcome-footer .community-link { margin-top: 0; } /* footer gap handles spacing (override the default 20px) */
  #lang-switch { position: absolute; top: 18px; right: 18px; display: flex; gap: 6px; }
  .lang-switch button {
    font: inherit; font-size: 13px; letter-spacing: 1px; color: #cfe0ff; cursor: pointer;
    background: rgba(255,255,255,.06); border: 1px solid rgba(160,190,255,.28);
    border-radius: 7px; padding: 5px 11px;
  }
  .lang-switch button.active { background: #4a7dff; border-color: #4a7dff; color: #fff; }
  #welcome h1 { font-size: 40px; letter-spacing: 1px; margin-bottom: 10px; }
  #welcome .intro { font-size: 26px; opacity: .82; max-width: 560px; line-height: 1.5; margin-bottom: 26px; }
  /* Staged L1 welcome briefing: Take-off is hidden while `.intro` types out, then fades in +0.5s after
     typing completes. Same visibility technique as the Main Window (visibility, not display → no reflow). */
  #welcome #takeoff { transition: opacity .25s ease; }
  #welcome.welcome-hide-go #takeoff { opacity: 0; visibility: hidden; }
  /* ---- Main Window (the redesigned between-battles / landing screen; was the "Hangar") ----
     A fixed landscape layout: the top bar (settings gear + auth + Vega Sentinels wordmark + inactive
     Ships) lives in its own fixed elements; below it a grid — left menu | work zone | a PER-VIEW right
     column (the mission list on Missions, the Loadout context panel on Loadout, absent on
     Character/Map/Craft, where the grid drops to two columns). Only the work zone (mission description /
     shop) and the mission list scroll; the frame stays put.
     One layout for mobile & desktop (designed against the forced mobile-landscape orientation). */
  #mainwin {
    position: fixed; inset: 0; z-index: 12; display: none;
    grid-template-columns: minmax(160px, 18%) 1fr;   /* base: 2 columns — Character / Map / Craft */
    grid-template-rows: 100%;
    gap: 14px; padding: 52px 14px 14px; box-sizing: border-box;
    background: radial-gradient(120% 90% at 50% 20%, rgba(20,32,60,.72), rgba(5,6,13,.92));
    color: #e8f1ff; font-family: system-ui, sans-serif; text-align: left;
  }
  #mainwin.on { display: grid; }
  /* The top-left #account-bar floats OVER the left column (it's fixed-position, outside the grid). On
     non-phone forms give that column a wider min so the bar — which grows with a longer localized
     "guest / log in" string (RU is ~1.5× EN) — is always fully contained by it and never spills right
     into the work zone to clip the mission title. Phone keeps the narrow 160px min (screen too small to
     spare it). Pairs with #account-bar's own max-width cap below. */
  body:not(.dev-phone) #mainwin { grid-template-columns: minmax(240px, 18%) 1fr; }
  /* Missions adds the right column (the mission list, 25%); Loadout adds the context panel (30%). */
  #mainwin.missions-open { grid-template-columns: minmax(160px, 18%) 1fr 25%; }
  #mainwin.bay-open      { grid-template-columns: minmax(160px, 18%) 1fr 30%; }
  body:not(.dev-phone) #mainwin.missions-open { grid-template-columns: minmax(240px, 18%) 1fr 25%; }
  body:not(.dev-phone) #mainwin.bay-open      { grid-template-columns: minmax(240px, 18%) 1fr 30%; }
  /* left menu */
  /* margin-top (NOT padding) gives the menu its OWN scroll section starting BELOW the top-left auth
     (nickname / login-signup) block: the overflow viewport begins below it, so scrolled menu items are
     CLIPPED there instead of sliding up behind the auth block. The opaque #account-bar is a backstop
     for taller auth states. */
  #mw-menu { display: flex; flex-direction: column; gap: 8px; align-items: stretch; min-height: 0; overflow-y: auto; margin-top: 34px; }
  #mw-menu .mw-row { display: flex; align-items: center; gap: 6px; }
  .mw-collapse {
    pointer-events: auto; cursor: pointer; flex: none; width: 30px; height: 38px; line-height: 1;
    font-size: 14px; color: #cfe0ff; background: rgba(255,255,255,.05);
    border: 1px solid rgba(160,190,255,.25); border-radius: 8px;
  }
  .mw-item {
    pointer-events: auto; cursor: pointer; flex: 1 1 auto; text-align: left;
    font: inherit; font-size: 20px; letter-spacing: .5px; color: #cfe0ff;
    background: rgba(255,255,255,.05); border: 1px solid rgba(160,190,255,.25);
    border-radius: 8px; padding: 11px 16px;
  }
  .mw-item:hover { background: rgba(40,60,100,.5); }
  .mw-item.active { background: #4a7dff; border-color: #4a7dff; color: #fff; }
  .mw-item.mw-hidden { display: none; }            /* Loadout/Stash/Shop hidden until the shop is unlocked */
  /* Loadout/Stash/Shop: centered label + ~30% shorter than the Missions item. */
  .mw-shop-item { text-align: center; padding-top: 5px; padding-bottom: 5px; line-height: 1.1; }
  .mw-sublist { display: flex; flex-direction: column; gap: 6px; margin: 2px 0 4px 36px; }
  #mw-missions-group[data-collapsed="1"] .mw-sublist { display: none; }
  .mw-sub {
    pointer-events: auto; cursor: pointer; text-align: center; font: inherit; font-size: 15px; color: #bcd0ee;
    background: rgba(20,30,55,.5); border: 1px solid rgba(140,175,255,.25); border-radius: 7px; padding: 8px 12px;
  }
  .mw-sub:hover { background: rgba(40,60,100,.6); }
  .mw-sub.active { background: rgba(74,125,255,.35); border-color: #4a7dff; color: #fff; }
  /* Base hub menu: five sections, natural height + top-aligned (don't stretch to fill the column). */
  #mw-menu .mw-item { flex: 0 0 auto; }
  /* in-bay tabs (Ship / Stash / Shop) — replace the former left-menu Loadout/Stash/Shop items */
  .bay-tabs { display: flex; gap: 6px; margin-bottom: 8px; flex: none; }
  .bay-tab {
    pointer-events: auto; cursor: pointer; font: inherit; font-size: 14px; color: #bcd0ee;
    background: rgba(20,30,55,.5); border: 1px solid rgba(140,175,255,.25); border-radius: 7px; padding: 6px 14px;
  }
  .bay-tab:hover { background: rgba(40,60,100,.6); }
  .bay-tab.active { background: rgba(74,125,255,.35); border-color: #4a7dff; color: #fff; }
  /* before the shop unlocks: only the Ship tab, and a hint (loadout stays read-only) */
  .bay-tabs.locked .bay-tab[data-bay="stash"], .bay-tabs.locked .bay-tab[data-bay="shop"] { display: none; }
  #mw-loadout-locked { display: none; color: #9fb3d6; font-size: 14px; margin: 0 0 8px; flex: none; }
  /* mission list — the right column on the Missions view (campaign + side-mission cards, scrolls) */
  #mw-mission-board { display: none; }
  #mainwin.missions-open #mw-mission-board {
    display: flex; flex-direction: column; gap: 6px; flex: 1 1 auto; min-height: 0; overflow-y: auto;
  }
  .mission-card {
    display: grid; grid-template-columns: 1fr auto; align-items: start; gap: 3px 8px;
    padding: 8px 10px; cursor: pointer; flex: none;
    border: 1px solid rgba(140,175,255,.22); border-radius: 8px; background: rgba(20,30,55,.5);
  }
  .mission-card:hover { background: rgba(40,60,100,.5); }
  .mission-card.selected { border-color: #4a7dff; }
  .mission-card.active { border-color: #ffcf5a; background: rgba(90,72,24,.32); } /* active mission = gold accent */
  .mission-card .mc-main { grid-column: 1; grid-row: 1; min-width: 0; }
  /* the title WRAPS in the narrow column (no ellipsis) — side-mission titles are two words too long */
  .mission-card .mc-title { font-size: 15px; line-height: 1.25; color: #dfe9ff; overflow-wrap: anywhere; }
  .mission-card .mc-sub { font-size: 12px; color: #9fb3d6; margin-top: 2px; }
  .mission-card .mc-badge {
    grid-column: 2; grid-row: 1; align-self: start; flex: none;
    font-size: 11px; letter-spacing: .5px; padding: 2px 8px; border-radius: 10px; white-space: nowrap;
  }
  .mission-card .mc-badge.active { background: #ffcf5a; color: #1a1a1a; }
  .mission-card .mc-badge.taken { background: rgba(120,160,255,.28); color: #cfe0ff; }
  .mission-card .mc-badge.cleared { background: rgba(89,224,160,.28); color: #b8f0d0; } /* cleared = green, distinct from gold "active" / blue "taken" */
  .mission-card .mc-actions {
    grid-column: 1 / -1; grid-row: 2; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px;
  }
  .mission-card .mc-actions:empty { display: none; }  /* active campaign card has no buttons → no empty row */
  .mission-card .mc-actions button {
    pointer-events: auto; cursor: pointer; font: inherit; font-size: 12px; color: #cfe0ff;
    background: rgba(255,255,255,.06); border: 1px solid rgba(160,190,255,.3); border-radius: 6px; padding: 4px 10px;
  }
  .mission-card .mc-actions button:hover { background: rgba(60,90,150,.5); }
  .mission-card .mc-actions button.primary { background: #4a7dff; border-color: #4a7dff; color: #fff; }
  /* Character screen (progression, docs/plans/2026-08-09-character-progression.md): level + XP bar header,
     then a responsive grid of the five skill cards each with a "+" to spend an unspent point. */
  #mw-character { display: flex; flex-direction: column; gap: 14px; padding: 6px 4px; overflow-y: auto; }
  #ch-head { display: flex; flex-direction: column; gap: 8px; flex: none; }
  .ch-level { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
  .ch-level-num { font-size: 20px; color: #dfe9ff; font-weight: 700; }
  .ch-points { font-size: 13px; color: #9fb3d6; }
  .ch-points.has { color: #ffcf5a; font-weight: 700; }               /* gold when the player has points to spend */
  .ch-xpbar { position: relative; height: 18px; border-radius: 9px; overflow: hidden;
    background: rgba(20,30,55,.6); border: 1px solid rgba(140,175,255,.22); }
  .ch-xpfill { position: absolute; left: 0; top: 0; bottom: 0; background: #4a7dff; transition: width .3s ease; }
  .ch-xptext { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 11px; color: #eaf1ff; text-shadow: 0 0 3px rgba(0,0,0,.8); }
  #ch-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; }
  .skill-card { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px;
    border: 1px solid rgba(140,175,255,.22); border-radius: 8px; background: rgba(20,30,55,.5); }
  .skill-card .sc-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
  .skill-card .sc-name { font-size: 15px; color: #dfe9ff; font-weight: 700; }
  .skill-card .sc-pts { font-size: 14px; color: #ffcf5a; font-weight: 700; }
  .skill-card .sc-desc { font-size: 12px; color: #9fb3d6; flex: 1; }
  .skill-card .sc-foot { display: flex; justify-content: flex-end; }
  .skill-card .ch-plus {
    pointer-events: auto; cursor: pointer; font: inherit; font-size: 16px; font-weight: 700; line-height: 1;
    width: 30px; height: 30px; border-radius: 6px; color: #fff; background: #4a7dff; border: 1px solid #4a7dff;
  }
  .skill-card .ch-plus:hover { background: #5c8bff; }
  .skill-card .ch-plus:disabled { background: rgba(255,255,255,.06); border-color: rgba(160,190,255,.2); color: #6a7d9c; cursor: default; }
  /* Loadout screen (Slice C): centered ship + slot chips around it (center work zone) + a right context
     panel in the SAME right column the Missions view uses for the mission list (no box). Loadout widens
     that column from 25% to 30% for the shop's 3D-model card; the column's contents swap per view
     (mission list → ship-stats strip + panel). */
  #loadout-panel { display: none; }                    /* shown only while Loadout is open */
  #mainwin.bay-open #loadout-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; }
  #loadout-center { position: relative; flex: 1; min-width: 0; min-height: 0; }
  #loadout-ship { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
  #loadout-slots { position: absolute; inset: 0; pointer-events: none; }
  .slot-chip {
    position: absolute; transform: translate(-50%, -50%); pointer-events: auto; cursor: pointer;
    min-width: 260px; max-width: 320px; display: flex; flex-direction: column; gap: 6px; text-align: left;
    padding: 18px 24px; border-radius: 14px; color: #cfe0ff;
    background: rgba(16,24,44,.82); border: 1px solid rgba(140,175,255,.3);
  }
  .slot-chip:hover { background: rgba(40,60,100,.85); }
  .slot-chip.selected { border-color: #4a7dff; box-shadow: 0 0 0 2px #4a7dff; }
  .slot-chip.empty { border-style: dashed; color: #8fa3c6; }
  .slot-chip.empty.required { border-color: #ff6b6b; color: #ffb3b3; }
  .slot-chip .sc-type { font-size: 16px; letter-spacing: .5px; text-transform: uppercase; opacity: .75; }
  .slot-chip .sc-name { font-size: 30px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .lp-scroll { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; }
  .lp-hint { color: #9fb3d6; font-size: 21px; }
  .lp-hint.sub { font-size: 18px; }
  .lp-sub { color: #8fa3c6; font-size: 16px; text-transform: uppercase; letter-spacing: .5px; margin-top: 4px; }
  .lp-shop-item { background: rgba(20,30,55,.5); border: 1px solid rgba(140,175,255,.2); border-radius: 8px; padding: 8px 10px; }
  .lp-item { padding: 0; }  /* selected-item detail (name/stats/model/Remove) floats in the panel — no boxed frame */
  .lp-shop-item { cursor: pointer; }
  .lp-shop-item:hover { background: rgba(40,60,100,.55); }
  /* gold "new item" frame: an unlocked row the player has never clicked (client/src/shop-markers.js) */
  .lp-shop-item.new { border-color: #ffcf5a; box-shadow: 0 0 0 1px rgba(255,207,90,.35); }
  /* shop detail card: stats → 3D model → Buy (spec) */
  .lp-detail { display: flex; flex-direction: column; gap: 10px; }
  .lp-model { width: 100%; height: 220px; display: block; } /* transparent — the alpha WebGL canvas lets the starfield show through, like the centered ship */
  .lp-model-empty { display: flex; align-items: center; justify-content: center; color: #6f82a6; font-size: 13px; }
  .lp-name { font-size: 22px; color: #dfe9ff; }
  .lp-stats { font-size: 18px; color: #9fb3d6; margin-top: 3px; }
  .lp-acts { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
  .lp-acts .price { color: #ffcf5a; font-size: 20px; margin-right: auto; }
  .lp-acts button[data-act="sell"] { margin-left: auto; } /* Sell sits at the right of the action row */
  .lp-list { display: flex; flex-direction: column; gap: 5px; }
  .lp-row {
    pointer-events: auto; cursor: pointer; text-align: left; font: inherit; font-size: 20px; color: #cfe0ff;
    background: rgba(20,30,55,.5); border: 1px solid rgba(140,175,255,.22); border-radius: 6px; padding: 9px 14px;
  }
  .lp-row:hover { background: rgba(40,60,100,.55); }
  .lp-row.selected { border-color: #4a7dff; }
  .lp-types { display: flex; flex-wrap: wrap; gap: 5px; }
  .lp-type {
    pointer-events: auto; cursor: pointer; font: inherit; font-size: 12px; color: #bcd0ee;
    background: rgba(20,30,55,.5); border: 1px solid rgba(140,175,255,.25); border-radius: 6px; padding: 4px 9px;
  }
  .lp-type.active { background: rgba(74,125,255,.35); border-color: #4a7dff; color: #fff; }
  /* a section that still holds an unclicked new item — gold instead of the usual blue, active or not */
  .lp-type.new, .lp-type.active.new { border-color: #ffcf5a; color: #ffe1a0; box-shadow: 0 0 0 1px rgba(255,207,90,.35); }
  .lp-foot { flex: none; padding-top: 8px; display: flex; justify-content: flex-end; }
  .lp-item .lp-acts button, .lp-shop-item .lp-acts button, .lp-detail .lp-acts button, .lp-foot button {
    pointer-events: auto; cursor: pointer; font: inherit; font-size: 20px; color: #cfe0ff;
    background: rgba(255,255,255,.06); border: 1px solid rgba(160,190,255,.3); border-radius: 6px; padding: 8px 18px;
  }
  .lp-item .lp-acts button.primary, .lp-shop-item .lp-acts button.primary, .lp-detail .lp-acts button.primary, .lp-foot button.primary { background: #4a7dff; border-color: #4a7dff; color: #fff; }
  /* stub sections (Character / Map / Craft) */
  #mw-view-stub { padding: 6px 4px; }
  #mw-stub-title { margin: 0 0 10px; }
  #mw-stub-text { color: #bcd0ee; font-size: 16px; line-height: 1.5; max-width: 52ch; }
  /* Phone (longest edge < 900px): halve the base-menu chrome (menu, mission board, slot chips, loadout
     panel) so it fits a phone — the 3D model viewers (#loadout-ship, .lp-model) are deliberately left full
     size. See docs/plans/2026-08-08-base-menu-redesign.md. */
  body.dev-phone #mw-menu .mw-item { font-size: 12px; padding: 6px 10px; }
  /* row/column gaps match the desktop card grid (title+badge / sub / actions), just tighter */
  body.dev-phone .mission-card { padding: 5px 8px; gap: 2px 6px; }
  body.dev-phone .mission-card .mc-title { font-size: 12px; }
  body.dev-phone .mission-card .mc-sub { font-size: 10px; }
  body.dev-phone .mission-card .mc-badge { font-size: 9px; padding: 1px 6px; }
  body.dev-phone .mission-card .mc-actions button { font-size: 10px; padding: 3px 8px; }
  body.dev-phone .slot-chip { min-width: 130px; max-width: 165px; padding: 8px 12px; gap: 3px; border-radius: 9px; }
  body.dev-phone .slot-chip .sc-type { font-size: 10px; }
  body.dev-phone .slot-chip .sc-name { font-size: 15px; }
  body.dev-phone .lp-name { font-size: 13px; }
  body.dev-phone .lp-stats { font-size: 11px; }
  body.dev-phone .lp-sub { font-size: 10px; }
  body.dev-phone .lp-hint { font-size: 12px; }
  body.dev-phone .lp-hint.sub { font-size: 10px; }
  body.dev-phone .lp-row { font-size: 12px; padding: 5px 9px; }
  body.dev-phone .lp-acts .price { font-size: 12px; }
  body.dev-phone .lp-item .lp-acts button,
  body.dev-phone .lp-shop-item .lp-acts button,
  body.dev-phone .lp-detail .lp-acts button,
  body.dev-phone .lp-foot button { font-size: 12px; padding: 5px 10px; }
  body.dev-phone .lp-type { font-size: 11px; padding: 3px 8px; }
  body.dev-phone .lp-model { height: 73px; } /* phone: short box for the weapon 3D model */
  body.dev-phone #mw-stub-title { font-size: 18px; }
  body.dev-phone #mw-stub-text { font-size: 12px; }
  /* work zone (center): only the active view shows; the mission description is the only scroller.
     Column layout so the global launch bar (#mw-launch) can sit pinned under whichever view is open. */
  #mw-work { min-width: 0; min-height: 0; display: flex; flex-direction: column; }
  .mw-view { display: none; flex: 1; min-width: 0; min-height: 0; }
  .mw-view.active { display: flex; flex-direction: column; }
  #mw-view-mission { gap: 10px; --gun-h: clamp(150px, 36%, 280px); }
  #mw-mission-title { font-size: 22px; margin: 0; }
  #mw-mission-desc { flex: 1 1 auto; min-height: 3.5em; overflow-y: auto; font-size: 18px; line-height: 1.45; opacity: .92; padding-right: 8px; }
  #mw-mission-reward { color: #ffd86b; font-weight: 600; }
  /* Briefing item showcase pinned to the BOTTOM-RIGHT corner of the mission text, with the text wrapping
     around it. A 0-width strut floats right and reserves the TOP of the right column (so the text flows
     full-width past it), then the gun canvas `clear: right` drops below the strut into the bottom-right
     corner — the text then wraps full-width above it and down its left side. (Floats can't anchor to the
     bottom on their own; strut height = container − gun height is the classic CSS bottom-float trick.)
     Both stay display:none until a showcase briefing adds `.show-item` to the description. */
  /* strut height also subtracts the gun's 8px of vertical margin (2px top + 6px bottom) so the floated
     strut+gun stack totals exactly 100% — otherwise it overflows 8px and the description always scrolls. */
  #mw-item-strut { display: none; float: right; width: 0; height: calc(100% - var(--gun-h) - 8px); }
  #mw-item { display: none; float: right; clear: right; width: 46%; height: var(--gun-h); margin: 2px 0 6px 14px; }
  #mw-mission-desc.show-item #mw-item-strut, #mw-mission-desc.show-item #mw-item { display: block; }
  #mw-mission-actions { display: flex; gap: 12px; justify-content: center; align-items: center; flex-wrap: wrap; }
  #mw-go-note { font-size: 13px; color: #ff9a8a; min-height: 16px; text-align: center; }
  /* Global launch bar — "Take off" on every stage. Hidden on Map, which carries its own Take off next to
     "Autopilot to destination" inside the navigation component's action row. */
  #mw-launch { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 2px; padding-top: 8px; }
  #mainwin.map-open #mw-launch { display: none; }
  #mw-takeoff-note { font-size: 13px; color: #ff9a8a; min-height: 16px; text-align: center; }
  /* ---- Shared star-system navigation component (systemmap-ui.mountSystemNav) ---- */
  /* Map pinned LEFT (adjacent to the base's left nav), object list to its RIGHT. */
  .sysnav { display: flex; gap: 14px; flex: 1; min-height: 0; min-width: 0; }
  /* Base-menu Map: the launch bar is hidden here, so the view (map canvas on the left, object list +
     Take off / Autopilot row on the right — they share this bottom edge) would run under #xp-bar, which
     floats over the base at z-index 13. Clear it by 10px: the bar's top sits 20px off the viewport bottom
     (6px offset + 14px border-box height) and #mainwin already pads 14px → 20 + 10 − 14 = 16px.
     The in-flight overlay map (#systemmap-overlay) keeps its own 18px padding. */
  #mw-view-map { padding-bottom: 16px; }
  .sysnav-mapwrap { position: relative; flex: 1 1 auto; min-width: 0; min-height: 0; }
  .sysnav-canvas {
    width: 100%; height: 100%; display: block; border: 1px solid rgba(120,150,200,0.3);
    border-radius: 10px; cursor: crosshair; touch-action: none; /* we own pan/zoom gestures */
  }
  .sysnav-zoom { position: absolute; right: 8px; bottom: 8px; display: flex; flex-direction: column; gap: 6px; }
  .sysnav-zoom button {
    width: 30px; height: 30px; cursor: pointer; font: 700 17px/1 system-ui, sans-serif;
    color: #cfe6ff; background: rgba(10,16,26,0.82); border: 1px solid rgba(120,150,200,0.4); border-radius: 8px;
  }
  .sysnav-side { flex: 0 0 34%; max-width: 340px; display: flex; flex-direction: column; gap: 8px; min-height: 0; }
  .sysnav-title { color: #cfe6ff; font: 700 14px/1 system-ui, sans-serif; letter-spacing: .5px; }
  .sysnav-list { flex: 1 1 auto; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; min-height: 0; }
  .sysnav-row {
    display: grid; grid-template-columns: 12px 1fr auto; align-items: center; gap: 8px; width: 100%;
    padding: 7px 10px; cursor: pointer; text-align: left; border-radius: 8px;
    color: #cfe6ff; font: 600 13px/1.2 system-ui, sans-serif;
    background: rgba(120,150,200,0.10); border: 1px solid transparent;
  }
  .sysnav-row:hover { background: rgba(120,150,200,0.20); }
  /* "Your current mission is here" — a DASHED gold frame (the map draws the matching dashed ring). Kept
     distinct from the SOLID gold selection frame below, which wins while the row is selected. */
  .sysnav-row.mission-active { border-color: #ffd24a; border-style: dashed; }
  .sysnav-row.sel { border-color: #ffd24a; border-style: solid; background: rgba(255,210,74,0.14); }
  .sysnav-row.locked { opacity: .55; }
  .sysnav-dot { width: 10px; height: 10px; border-radius: 50%; }
  .sysnav-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .sysnav-kind, .sysnav-mission { font-size: 11px; font-weight: 500; opacity: .72; white-space: nowrap; }
  .sysnav-mission { color: #7fff9a; }
  .sysnav-row.locked .sysnav-mission { color: #c9cdd6; }
  .sysnav-hint { color: #9fb6d0; font: 500 12px/1.35 system-ui, sans-serif; min-height: 32px; }
  .sysnav-actions { display: flex; flex-wrap: wrap; gap: 8px; }
  .sysnav-actions button {
    cursor: pointer; font: 600 13px system-ui, sans-serif; padding: 9px 16px; border-radius: 8px;
    color: #cfe6ff; background: rgba(120,150,200,0.18); border: 1px solid rgba(120,150,200,0.4);
  }
  .sysnav-actions button.primary { color: #0b0f14; background: #6fd0ff; border-color: transparent; }
  /* Take off keeps the game's launch colour wherever it appears, so it reads as the same action as the
     global bar's button — just sized to sit beside "Autopilot to destination". */
  .sysnav-actions button.takeoff {
    color: #1a0c02; background: linear-gradient(90deg,#ffb35a,#ff7a3c); border-color: transparent;
    font-weight: 700; letter-spacing: .5px;
  }
  .sysnav-actions button:disabled { opacity: .45; cursor: not-allowed; }
  /* phone: the SAME side-by-side shape as the desktop — map filling the central area down to the bottom
     edge, object list as a column on the right. Stacking them (what shipped first) split a 390px-tall
     landscape phone into two strips: a 149px map above a 153px list, and the map is the whole point of
     this screen. The small left inset keeps the canvas clear of the fixed top-left account bar, which
     floats over the work zone on a phone. */
  body.dev-phone .sysnav { flex-direction: row; gap: 8px; padding-left: 12px; }
  body.dev-phone .sysnav-mapwrap { flex: 1 1 auto; min-height: 0; }
  body.dev-phone .sysnav-side { flex: 0 0 38%; max-width: 260px; gap: 5px; }
  body.dev-phone .sysnav-title { display: none; }   /* the rows are self-evident; the space is not */
  body.dev-phone .sysnav-list { flex: 1 1 auto; max-height: none; }
  body.dev-phone .sysnav-row { padding: 5px 8px; font-size: 12px; }
  body.dev-phone .sysnav-hint { min-height: 15px; font-size: 11px; }
  /* the right column is too narrow for two buttons side by side — stack them full-width instead of
     letting flex-wrap drop one to a ragged second line */
  body.dev-phone .sysnav-actions { flex-direction: column; }
  /* In the BASE MENU the bottom button now ends in the same corner as the floating ⛶ fullscreen button
     (46px, fixed bottom-right, z-60), which draws over it and steals the taps that land there — so reserve
     its footprint, and take the space back on the two states where ⛶ is hidden (already fullscreen, or a
     browser with no fullscreen API). The in-flight overlay needs none of this: it is z-9000, so it covers
     the ⛶ instead of fighting it. */
  body.dev-phone #mw-view-map .sysnav-actions { padding-right: 52px; }
  body.dev-phone.fs #mw-view-map .sysnav-actions,
  body.dev-phone.no-fs-api #mw-view-map .sysnav-actions { padding-right: 0; }
  body.dev-phone .sysnav-actions button { padding: 7px 12px; font-size: 12px; }
  /* In-flight navigation overlay: the SAME component, full-screen over the frozen game (G.mapOpen).
     z-index 9000 clears every fixed HUD element, and it covers the viewport, so it cannot overlap the
     bottom-center bar / corner controls / zoom pair the way a floating panel would. */
  #systemmap-overlay {
    position: fixed; inset: 0; z-index: 9000; display: none; flex-direction: column; gap: 12px;
    background: rgba(4,7,12,0.88); padding: 16px 18px 18px;
  }
  #systemmap-overlay .sysnav-head { color: #cfe6ff; font: 700 18px/1 system-ui, sans-serif; text-align: center; }
  #systemmap-host { flex: 1; min-height: 0; display: flex; }
  #systemmap-host .sysnav { flex: 1; }
  #mw-view-bay { overflow-y: auto; }
  #mw-bay-note { font-size: 13px; color: #ff9a8a; min-height: 16px; text-align: center; }
  /* right column — hidden unless the open view has content for it (Missions list / Loadout panel) */
  #mw-ship-col { display: none; flex-direction: column; min-width: 0; }
  #mainwin.missions-open #mw-ship-col, #mainwin.bay-open #mw-ship-col { display: flex; }
  /* Staged briefing reveal (L2/L3 campaign briefing, docs/plans/2026-07-05-1641-briefing-staged-reveal.md):
     Take-off is hidden while the briefing text types out, then fades in 0.5s after typing completes (the
     mission list stays visible throughout). visibility (not display) keeps its layout box so the mission
     view doesn't reflow when it appears. */
  #mw-go, #mw-launch { transition: opacity .25s ease; }
  #mainwin.briefing-hide-go #mw-go, #mainwin.briefing-hide-go #mw-launch { opacity: 0; visibility: hidden; }
  #takeoff, #mw-go, #mw-takeoff {
    pointer-events: auto; cursor: pointer;
    padding: 14px 44px; font-size: 18px; font-weight: 700; border: 0; border-radius: 10px;
    background: linear-gradient(90deg,#ffb35a,#ff7a3c); color: #1a0c02; letter-spacing: 1px;
  }
  /* the mission LAUNCH is the secondary of the pair now — "Take off" is the primary way out of the base */
  #mw-go { padding: 11px 28px; font-size: 15px; background: linear-gradient(90deg,#8fa6c8,#6f86ab); color: #0b0f14; }
  #mw-mission-nav {
    pointer-events: auto; cursor: pointer; padding: 11px 22px; font: 600 14px system-ui, sans-serif;
    color: #cfe6ff; background: rgba(120,150,200,0.18); border: 1px solid rgba(120,150,200,0.4); border-radius: 8px;
  }
  #takeoff:hover, #mw-go:hover, #mw-takeoff:hover, #mw-mission-nav:hover { filter: brightness(1.08); }
  #mw-go:disabled, #mw-takeoff:disabled, #mw-mission-nav:disabled { filter: grayscale(.7) brightness(.7); cursor: not-allowed; }
  /* Touch-only UI gate (e.g. the floating Full-screen button). */
  .touch-only { display: none; }
  body.touch .touch-only { display: inline-flex; }
  /* Mobile landscape: the web platform can't widen the viewport past the physical screen, and
     screen.orientation.lock is unsupported on iOS Safari — so when a touch phone is held in PORTRAIT
     we rotate the whole <body> 90° and run the game in the swapped (landscape) dimensions. The class is
     toggled by applyOrientation(); gameW/gameH + toGame() keep the renderer and touch input in this
     rotated space. transform makes position:fixed children relative to body, so all HUD/buttons follow. */
  body.rot {
    width: 100vh; height: 100vw;
    transform-origin: top left;
    transform: translateX(100vw) rotate(90deg);
  }
  /* Floating full-screen button — touch-only, bottom-right corner, hidden when already fullscreen.
     Only shown on menus (body.menu): during a live fight the bottom-right corner is the rocket
     button, so gating to menus avoids the overlap. */
  #fullscreen-btn {
    position: fixed; right: 14px; bottom: 14px; z-index: 60;
    width: 46px; height: 46px; padding: 0; border-radius: 12px;
    display: none; align-items: center; justify-content: center;
    font-size: 22px; line-height: 1; cursor: pointer; pointer-events: auto;
    color: #eaf2ff;                                   /* brighter than the old #cfe0ff */
    background: rgba(40,60,110,.55);                   /* brighter/more solid than the old .06 */
    border: 1px solid rgba(170,200,255,.55);
    box-shadow: 0 2px 10px rgba(0,0,0,.35);
  }
  /* Show on touch whenever the HUD/menu is up — menus AND in-game (active + paused). The body.fs and
     body.no-fs-api rules below still hide it (they win via !important). See DECISIONS §44. */
  body.touch #fullscreen-btn { display: flex; }
  /* In-game (HUD up, i.e. not a menu): move it just LEFT of the bottom-right rocket button and raise it
     clear of the phone's bottom browser bar. Rocket = right:28 bottom:40 84×84 (left edge ~112px from
     right); ⛶ near-edge sits at ~124px from right (real gap), center vertically aligned with the rocket.
     Verified on the touch harness (see DECISIONS §44). */
  body.touch:not(.menu) #fullscreen-btn { right: 124px; bottom: 58px; }
  body.fs #fullscreen-btn { display: none !important; } /* … but hide once fullscreen */
  #fullscreen-btn:active { background: rgba(60,85,150,.7); }
  /* iPhone Safari (no Fullscreen API): the ⛶ button can't work, so hide it and show an "Add to Home
     Screen" hint instead — that standalone PWA is the only true full screen on iPhone. Both are gated to
     touch menus and vanish once already launched standalone (body.standalone). Non-interactive pill. */
  body.no-fs-api #fullscreen-btn { display: none !important; }
  #a2hs-hint {
    position: fixed; right: 14px; bottom: 14px; z-index: 60;
    max-width: 230px; padding: 9px 12px; border-radius: 12px;
    display: none; align-items: center; gap: 7px;
    font-size: 12px; line-height: 1.3; text-align: left; color: #eaf2ff;
    background: rgba(40,60,110,.55);
    border: 1px solid rgba(170,200,255,.55);
    box-shadow: 0 2px 10px rgba(0,0,0,.35);
    pointer-events: none;                                /* never intercepts menu taps */
  }
  #a2hs-hint .a2hs-glyph { font-size: 18px; line-height: 1; flex: none; }
  /* iPhone Safari has no Fullscreen API, so "not in fullscreen" ≈ "not launched standalone" — show the
     Add-to-Home-Screen hint whenever NOT standalone, on menus AND in-game (active + paused). Non-
     interactive (pointer-events:none), so it never blocks controls. See DECISIONS §44. */
  body.touch.no-fs-api:not(.standalone) #a2hs-hint { display: flex; }
  body.standalone #a2hs-hint { display: none !important; }
  /* In-game the wide pill (max-width 230) can't sit where the 46px ⛶ goes; tuck it under the top-left
     settings gear, clear of the rocket (bottom-right), pause button (top-center) and zoom (mid-right).
     Verified on the harness it doesn't overlap the gear/minimap (see DECISIONS §44). */
  body.touch:not(.menu).no-fs-api #a2hs-hint { right: auto; bottom: auto; left: 14px; top: 56px; }
  /* Shop bay (docs/plans/hangar-shop.md). Now the "bay" work-zone view in the Main Window — shown when a
     Loadout/Stash/Shop menu item is picked (the items are gated to the shop being unlocked). */
  /* zoom 0.67 = everything in the bay (Loadout/Stash/Shop) — fonts, buttons, paddings — ~1.5× smaller. */
  #mw-view-bay { pointer-events: auto; text-align: left; zoom: 0.67; }
  #mw-view-bay .bay-credits { text-align: center; font-size: 20px; margin-bottom: 14px; }
  #mw-view-bay .bay-credits b { color: #ffd86b; }
  /* live ship-stats bar with deltas vs the previous config */
  /* Ship characteristics — a compact one-line strip at the TOP of the right column (no boxes), half the
     previous fonts, so HP / Accel / Turn / Weight fit on a single row under the "Ships" label. Loadout
     only: every other view either hides the column or fills it with the mission list. */
  #ship-stats { display: none; flex-wrap: nowrap; gap: 8px; justify-content: center; margin: 0 0 6px; }
  #mainwin.bay-open #ship-stats { display: flex; }   /* ship characteristics live in Loadout only */
  #ship-stats .stat { display: flex; align-items: baseline; gap: 2px; background: none; border: 0; padding: 0; min-width: 0; }
  #ship-stats .stat .k { font-size: 8px; letter-spacing: .5px; text-transform: uppercase; opacity: .6; }
  #ship-stats .stat .v { font-size: 10px; font-weight: 700; }
  #ship-stats .stat .v.crit { color: #ff5b5b; } /* e.g. HP 0 — a required slot is empty */
  #ship-stats .stat .d { font-size: 6px; margin-left: 2px; }
  #ship-stats .stat .d.up { color: #6ee29a; } #ship-stats .stat .d.down { color: #ff8a7a; }
  /* Loadout / Stash / Shop are separate screens, switched by the left menu (not a nav strip). The
     work zone bounds the height + scrolls, so the inner view needs no max-height of its own. */
  .bay-view { display: none; background: rgba(12,18,34,.55); border: 1px solid rgba(120,150,210,.18);
    border-radius: 12px; padding: 12px; }
  .bay-view.active { display: block; }
  /* Shop screen = two panes: a type list on the left, the items of the selected type on the right. */
  .shop-pane { display: grid; grid-template-columns: 168px 1fr; gap: 14px; }
  .shop-types { display: flex; flex-direction: column; gap: 6px; }
  .shop-types button {
    font: inherit; font-size: 13px; cursor: pointer; pointer-events: auto; color: #cfe0ff; text-align: left;
    background: rgba(255,255,255,.05); border: 1px solid rgba(160,190,255,.25); border-radius: 7px; padding: 7px 11px;
  }
  .shop-types button.active { background: #4a7dff; border-color: #4a7dff; color: #fff; }
  @media (max-width: 760px) {
    .shop-pane { grid-template-columns: 1fr; }
    .shop-types { flex-direction: row; flex-wrap: wrap; }
    #mw-mission-desc { font-size: 14px; }
    #welcome .intro { font-size: 16px; }
  }
  /* ---- Desktop (mouse / large-screen) Main Window polish (device-profiles iteration 1) ----
     Scoped to the desktop + desktop-lg FORM classes only; phone/tablet + the mobile @media override are
     untouched. See docs/plans/2026-07-01-1933-device-profiles-desktop-polish.md. */

  /* B1 — larger briefing title + body text on desktop */
  body.dev-desktop #mw-mission-title, body.dev-desktop-lg #mw-mission-title { font-size: 32px; }
  body.dev-desktop #mw-mission-desc,  body.dev-desktop-lg #mw-mission-desc  { font-size: 26px; }

  /* B2 — Loadout/Stash/Shop buttons: don't stretch to fill the column; fixed height slightly > Missions row.
     Cause: the shared .mw-item base has `flex: 1 1 auto`, which grows in the #mw-menu column flex. */
  body.dev-desktop .mw-shop-item, body.dev-desktop-lg .mw-shop-item { flex: 0 0 auto; height: 56px; }

  /* B3 — granted-item icon centered directly BELOW the mission text (drop the bottom-right float + strut).
     #mw-mission-desc becomes a flex column; the text keeps order 1, the item canvas takes order 2 and
     centers; the strut is hidden. (Take-off, a sibling AFTER #mw-mission-desc, then sits under the item.)
     B5 also folds `flex: 0 1 auto` into this rule so Take-off follows the content instead of pinning down. */
  body.dev-desktop #mw-mission-desc, body.dev-desktop-lg #mw-mission-desc {
    display: flex; flex-direction: column; flex: 0 1 auto;
  }
  body.dev-desktop #mw-mission-text, body.dev-desktop-lg #mw-mission-text { order: 1; }
  body.dev-desktop #mw-mission-desc.show-item #mw-item,
  body.dev-desktop-lg #mw-mission-desc.show-item #mw-item {
    order: 2; float: none; clear: none; align-self: center;
    width: 55%; height: var(--gun-h); margin: 12px 0 6px;
  }
  body.dev-desktop #mw-mission-desc.show-item #mw-item-strut,
  body.dev-desktop-lg #mw-mission-desc.show-item #mw-item-strut { display: none; }

  /* B4 — ship characteristics (#ship-stats: P/Accel/Turn/Weight) fonts ×2 on desktop. Verified under
     Playwright at 1440×900 that the four k/v pairs still fit on ONE line (no overflow), so the borderless
     2×2 grid fallback stays unused. */
  body.dev-desktop #ship-stats .stat .k, body.dev-desktop-lg #ship-stats .stat .k { font-size: 16px; }
  body.dev-desktop #ship-stats .stat .v, body.dev-desktop-lg #ship-stats .stat .v { font-size: 20px; }
  body.dev-desktop #ship-stats .stat .d, body.dev-desktop-lg #ship-stats .stat .d { font-size: 12px; }
  /* one item card (loadout slot / stash item / shop item): text-in-rectangle, no art */
  .bay-item {
    background: rgba(26,36,60,.7); border: 1px solid rgba(120,150,210,.22);
    border-radius: 9px; padding: 9px 10px; margin-bottom: 8px;
    /* Desktop: head + foot share ONE line (name left, price/buttons right), stats wraps full-width below.
       Touch phones stack into two rows instead — see `body.touch .bay-item` below. */
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px;
  }
  .bay-item.empty { opacity: .55; border-style: dashed; }
  .bay-item.required-empty { border-color: rgba(255,120,100,.6); color: #ffb3a8; opacity: .9; }
  /* header row: name (ellipsizes) · meta (slot tag / price) · (i) — each its own cell, never overlap */
  .bay-item .head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex: 1 1 auto; min-width: 0; }
  .bay-item .head .info-btn { margin-left: -2px; } /* hug the title */
  /* head row: [name][i] on the left, the slot tag (loadout) pushed to the right end. */
  .bay-item .name-text { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; font-size: 14px; }
  .bay-item .meta { flex: none; margin-left: auto; } /* margin-left:auto pushes the slot tag to the right end */
  /* price + action button(s). Desktop: sits inline at the right of the head. Touch: its own centered row. */
  .bay-item .foot { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 10px; flex: 0 0 auto; }
  .bay-item .foot .meta { margin-left: 0; } /* in the centered foot the price must not be pushed aside */
  .bay-item .slot-tag { font-size: 10px; letter-spacing: 1px; text-transform: uppercase; opacity: .5; }
  .bay-item .price { color: #ffd86b; font-size: 13px; white-space: nowrap; }
  .bay-item .owned-badge {
    flex: none; font-size: 16px; padding: 2px 9px; border-radius: 11px; white-space: nowrap;
    background: rgba(110,226,154,.15); border: 1px solid rgba(110,226,154,.4); color: #9ff0b8;
  }
  .bay-item .stats { font-size: 12px; opacity: .72; margin-top: 3px; line-height: 1.45; flex-basis: 100%; } /* desktop: own full-width line below head+foot */
  .bay-item .stats.hidden { display: none; }
  /* Touch phones: only the SHOP stacks a card into TWO rows — name on top, price + Buy centered below (a
     long item name + price + Buy don't fit one line on a phone). Loadout/Stash stay on a SINGLE row
     everywhere (simpler cards — the maintainer wants them one-line). Desktop keeps everything on one line. */
  body.touch #view-shop .bay-item { flex-direction: column; align-items: stretch; }
  body.touch #view-shop .bay-item .head, body.touch #view-shop .bay-item .foot, body.touch #view-shop .bay-item .stats { flex: 0 0 auto; }
  /* characteristics show only when the (i) button is tapped (it toggles .hidden) — no hover reveal */
  .bay-item .actions { display: inline-flex; gap: 6px; flex: none; }
  .bay-item .actions button, .bay-item .info-btn {
    font: inherit; font-size: 12px; cursor: pointer; pointer-events: auto;
    background: rgba(255,255,255,.07); border: 1px solid rgba(160,190,255,.3);
    color: #dce8ff; border-radius: 6px; padding: 4px 10px;
  }
  .bay-item .actions button:hover, .bay-item .info-btn:hover { background: rgba(255,255,255,.14); }
  .bay-item .actions button.sell { border-color: rgba(255,160,120,.4); color: #ffcaa8; }
  .bay-item .actions button.primary { border-color: #4a7dff; background: rgba(74,125,255,.25); }
  .bay-item .info-btn { flex: none; align-self: center; padding: 1px 8px; line-height: 1.4; }
  .bay-empty-note { font-size: 13px; opacity: .55; padding: 6px 2px; }
  /* Per-screen sizing: Loadout/Stash/Shop item text (incl. the hover characteristics) 2×; buttons 1.5×
     (Loadout) / 2× (Stash + Shop); the Shop type column 2×. The whole bay is then scaled DOWN ~1.5× by
     `#mw-view-bay { zoom }` (so these are the pre-zoom sizes). The ship-characteristics strip is separate
     now — it lives at the top of the right column, Loadout only, styled on #ship-stats above. */
  #view-loadout .name-text, #view-stash .name-text, #view-shop .name-text { font-size: 28px; }
  #view-loadout .slot-tag { font-size: 20px; }
  #view-stash .price, #view-shop .price { font-size: 26px; }
  #view-loadout .price { font-size: 22px; font-weight: 600; } /* resale value shown beside the slot's actions */
  #view-loadout .stats, #view-stash .stats, #view-shop .stats { font-size: 24px; line-height: 1.4; }
  #view-loadout .actions button, #view-loadout .info-btn { font-size: 18px; padding: 7px 15px; } /* 1.5× */
  #view-stash .actions button, #view-stash .info-btn,
  #view-shop  .actions button, #view-shop  .info-btn { font-size: 24px; padding: 8px 20px; } /* 2× */
  #view-shop .shop-types button { font-size: 26px; padding: 14px 22px; } /* 2× type list */
  /* Telegram community / feedback link (welcome screen + result overlay) */
  .community-link {
    pointer-events: auto; display: inline-block; margin-top: 20px;
    font-family: system-ui, sans-serif; font-size: 14px; color: #9fd0ff; text-decoration: none;
    opacity: .85; border-bottom: 1px dashed rgba(159,208,255,.45); padding-bottom: 1px;
  }
  .community-link:hover { opacity: 1; color: #cfe7ff; }
  #overlay .community-link { margin-top: 18px; }
  /* hide the in-game HUD while the welcome screen is up */
  body.menu #hud, body.menu #help, body.menu #perf, body.menu #rocket-btn, body.menu #event-log, body.menu #markers, body.menu #oob-warn, body.menu #return-hint, body.menu #return-btn, body.menu #roam-nav, body.menu #banner, body.menu #minimap, body.menu #pause-btn, body.menu #pause-overlay, body.menu #zoom { display: none; }
  /* …and the touch flight controls (stick + FIRE). #rocket-btn/#zoom were hidden individually above but the
     #touch layer itself was not, so on a phone the FIRE button stayed live ON TOP of the base menu — it
     overlapped the Map view's object list and could be pressed while browsing the hangar. */
  body.menu #touch { display: none; }
  /* Account dialog (login / register / post-level-1 name prompt) */
  #account {
    position: fixed; inset: 0; z-index: 14; display: none;
    align-items: center; justify-content: center;
    background: rgba(5,6,13,.86); color: #e8f1ff;
    font-family: system-ui, sans-serif; text-align: center; padding: 24px;
  }
  #account .account-box {
    width: 380px; max-width: 92vw; background: rgba(18,26,46,.94);
    border: 1px solid rgba(120,150,210,.3); border-radius: 14px; padding: 26px 24px;
    box-shadow: 0 12px 40px rgba(0,0,0,.5);
  }
  #account h1 { font-size: 26px; margin-bottom: 10px; }
  #account .intro { opacity: .82; font-size: 14px; line-height: 1.5; margin-bottom: 16px; }
  #account input {
    width: 100%; box-sizing: border-box; margin: 7px 0; padding: 11px 13px;
    font: inherit; font-size: 15px; color: #eaf2ff;
    background: rgba(8,12,22,.85); border: 1px solid rgba(120,150,210,.35); border-radius: 8px;
  }
  #account input:focus { outline: none; border-color: #5b86e5; }
  #account .err { color: #ff9a8a; font-size: 13px; min-height: 17px; margin: 4px 0 0; }
  #account .account-actions { display: flex; flex-direction: column; gap: 9px; margin-top: 14px; }
  #account button {
    pointer-events: auto; cursor: pointer; width: 100%;
    padding: 12px 18px; font: inherit; font-size: 15px; font-weight: 700; border: 0; border-radius: 9px;
    background: linear-gradient(90deg,#36d1dc,#5b86e5); color: #02101f;
  }
  #account button.ghost {
    background: transparent; color: #9fb6e6; font-weight: 600; border: 1px solid rgba(120,150,210,.3);
  }
  #account button:hover { filter: brightness(1.06); }
  /* Account status bar (top-left, on menu screens only) */
  #account-bar {
    position: fixed; top: 11px; left: 56px; z-index: 13; display: none; /* top-aligned with the settings gear; shifted right to clear it (top-left corner) */
    font-family: system-ui, sans-serif; font-size: 13px; color: #cfe0ff;
    background: rgb(11,17,32); border: 1px solid rgba(120,150,210,.25); /* opaque: scrolled left-menu items must not show through it */
    border-radius: 9px; padding: 8px 12px; max-width: 200px; text-align: left; line-height: 1.5;
    /* Capped so the bar stays within its (≥240px) left column and never overlaps the work zone; a longer
       localized string wraps to another line here instead of spilling right over the mission title. */
  }
  body.menu #account-bar { display: block; }
  /* Top-right of the Main Window: the credit balance + the "Ships" label, on one line (menus only).
     Ships is inactive for now (future ship-buying) — muted + non-interactive. (The side-mission board
     moved into the left menu's Missions list.) */
  #mw-topright {
    position: fixed; top: 14px; right: 18px; z-index: 13; display: none;
    align-items: baseline; gap: 14px; pointer-events: none;
    font-family: system-ui, sans-serif; font-size: 18px; letter-spacing: 1px;
  }
  body.menu #mw-topright { display: flex; }
  /* Money — the one live number up here, so it reads at full opacity in credit gold. A notch smaller
     than "Ships" (relative, so it follows the narrow-viewport step-down below). */
  #mw-credits { color: #ffd27a; font-weight: 600; font-size: .82em; white-space: nowrap; }
  #mw-ships { color: #cfe0ff; opacity: .4; }
  /* Narrow (phone-landscape) viewports: the enlarged centered wordmark grows with 4.5vw and would run
     into a two-item row, so stack the pair into a right-aligned column instead. */
  @media (max-width: 780px) {
    #mw-topright { flex-direction: column; align-items: flex-end; gap: 4px; font-size: 15px; }
  }
  #account-bar button {
    pointer-events: auto; cursor: pointer; font: inherit; font-size: 12px; color: #cfe0ff;
    background: rgba(255,255,255,.06); border: 1px solid rgba(160,190,255,.28);
    border-radius: 7px; padding: 4px 9px; margin: 5px 6px 0 0;
  }
  #account-bar .verify-nudge { color: #ffd27a; display: block; margin-top: 4px; }
  #account-bar .verified-ok { color: #9fe8b0; display: block; margin-top: 4px; }
  /* Perf overlay (load) */
  /* Game title (wordmark), centered at the very top */
  #gametitle {
    position: fixed; top: 9px; left: 50%; transform: translateX(-50%);
    font-family: "Segoe UI", system-ui, sans-serif; font-weight: 700;
    font-size: 16px; letter-spacing: 4px; text-transform: uppercase;
    color: #cfe0ff; opacity: .92; pointer-events: none; white-space: nowrap;
    text-shadow: 0 0 10px rgba(90,140,230,.6), 0 0 2px rgba(0,0,0,.9);
  }
  /* On the menus the wordmark is the centerpiece (the "Hangar" title is gone), so it's much larger. */
  body.menu #gametitle { top: 6px; font-size: clamp(22px, 4.5vw, 40px); letter-spacing: 6px; opacity: .96; }
  /* Pause/resume toggle — top bar, between the title and the Credits HUD. Freezes the fight (skips the
     sim update; rendering continues). Hidden on menus via body.menu; the result overlay (z-index 10) sits
     above it. */
  #pause-btn {
    position: fixed; top: 3px; left: 50%; transform: translateX(108px); z-index: 6;
    pointer-events: auto; cursor: pointer; width: 34px; height: 30px; line-height: 1;
    display: flex; align-items: center; justify-content: center; font-size: 15px; color: #cfe0ff;
    background: rgba(20,30,55,.6); border: 1px solid rgba(140,175,255,.35); border-radius: 7px;
    text-shadow: 0 0 6px rgba(0,0,0,.8);
  }
  #pause-btn:hover { background: rgba(40,60,100,.7); }
  /* Centered "Paused" label + Play button (shown while paused). pointer-events pass through except the
     button, so the frozen battlefield stays visible/uninteractive behind it. */
  #pause-overlay {
    position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 8;
    display: none; flex-direction: column; align-items: center; gap: 18px; pointer-events: none; text-align: center;
  }
  #pause-overlay.on { display: flex; }
  #pause-overlay .pause-title {
    font-family: system-ui, sans-serif; font-weight: 700; font-size: 52px; letter-spacing: 3px;
    color: #e8f1ff; text-transform: uppercase; text-shadow: 0 0 18px rgba(0,0,0,.85), 0 0 4px rgba(0,0,0,.95);
  }
  #pause-play {
    pointer-events: auto; cursor: pointer;
    padding: 12px 40px; font-size: 18px; font-weight: 700; border: 0; border-radius: 10px;
    background: linear-gradient(90deg,#36d1dc,#5b86e5); color: #02101f; letter-spacing: 1px;
  }
  #pause-play:hover { filter: brightness(1.08); }
  /* Settings gear — always available (top-left corner), above every overlay. Opening it also pauses a
     live fight (see openSettings), so it doubles as a one-click "pause + menu". */
  #settings-btn {
    position: fixed; top: 11px; left: 12px; z-index: 13; display: flex;
    width: 36px; height: 36px; line-height: 1; align-items: center; justify-content: center;
    font-size: 19px; color: #cfe0ff; cursor: pointer; pointer-events: auto;
    background: rgba(20,30,55,.6); border: 1px solid rgba(140,175,255,.35); border-radius: 8px;
  }
  #settings-btn:hover { background: rgba(40,60,100,.7); }
  /* Audio settings modal */
  #settings-overlay {
    position: fixed; inset: 0; z-index: 20; display: none; align-items: center; justify-content: center;
    background: rgba(5,6,13,.66); color: #e8f1ff; font-family: system-ui, sans-serif; padding: 20px;
  }
  #settings-overlay.on { display: flex; }
  #settings-overlay .settings-box {
    width: min(560px, 94vw); max-height: 98vh; overflow-y: auto; box-sizing: border-box;
    background: rgba(16,24,44,.96); border: 1px solid rgba(140,175,255,.28);
    border-radius: 16px; padding: 12px clamp(16px, 5vw, 32px); box-shadow: 0 18px 60px rgba(0,0,0,.55);
  }
  #settings-overlay h1 { font-size: 20px; letter-spacing: 1px; margin: 0 0 6px; }
  #settings-overlay .set-row { display: flex; align-items: center; gap: 12px; margin: 5px 0; }
  /* label shrinks (min-width:0) so it never forces the slider off the edge */
  #settings-overlay .set-row label { flex: 1 1 auto; min-width: 0; font-size: 15px; opacity: .92; }
  /* slider is shrinkable (flex-shrink 1) + capped — never overflows a narrow modal */
  #settings-overlay input[type=range] { flex: 1 1 90px; min-width: 0; max-width: 180px; height: 16px; accent-color: #5b86e5; cursor: pointer; }
  #settings-overlay .set-toggle {
    flex: 0 0 56px; font-size: 13px; font-weight: 700; letter-spacing: .5px; cursor: pointer;
    padding: 6px 0; border-radius: 8px; border: 1px solid rgba(140,175,255,.4);
    background: rgba(54,209,220,.18); color: #bff0f4;
  }
  #settings-overlay .set-toggle.off { background: rgba(120,130,150,.18); color: #9aa6bd; border-color: rgba(150,160,180,.35); }
  /* Graphics-quality segmented control (3 mutually-exclusive tiers) */
  /* the quality row stacks: label on its own line, the three buttons on the row below */
  #settings-overlay .set-row.stack { flex-direction: column; align-items: stretch; gap: 7px; }
  #settings-overlay .seg { display: flex; gap: 8px; }
  /* flex-basis:auto -> each button sizes to its text (so "Performance" gets the width it needs, no clip),
     then grows to share the row */
  #settings-overlay .seg-btn {
    flex: 1 1 auto; min-width: 0; padding: 6px 9px; border-radius: 8px; cursor: pointer; font: inherit;
    font-size: 12px; text-align: center; white-space: nowrap;
    background: rgba(120,130,150,.18); color: #9aa6bd; border: 1px solid rgba(150,160,180,.35);
  }
  #settings-overlay .seg-btn.on { background: rgba(91,134,229,.35); color: #eaf1ff; border-color: #5b86e5; }
  #settings-overlay .set-note { font-size: 11px; opacity: .6; margin: -3px 0 3px; }
  /* Danger zone: "Reset my progress" — a slide-to-confirm control. Separated by a faint divider. */
  #settings-overlay .set-row.danger { gap: 6px; margin: 8px 0 2px; padding-top: 8px; border-top: 1px solid rgba(255,120,120,.18); }
  #settings-overlay .set-row.danger > label { font-size: 13px; color: #ffb3b3; opacity: 1; }
  /* the slide track: a knob the user drags fully to the right; fill grows behind it */
  #reset-slide {
    position: relative; height: 32px; border-radius: 16px; overflow: hidden; cursor: grab; user-select: none;
    background: rgba(120,40,50,.28); border: 1px solid rgba(255,120,120,.38); touch-action: none;
  }
  #reset-slide.dragging { cursor: grabbing; }
  #reset-slide .slide-fill {
    position: absolute; left: 0; top: 0; bottom: 0; width: 30px; border-radius: 16px;
    background: linear-gradient(90deg, rgba(229,80,80,.55), rgba(229,57,57,.8));
  }
  #reset-slide .slide-text {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 12px; font-weight: 600; letter-spacing: .4px; color: #ffd7d7; pointer-events: none;
  }
  #reset-slide .slide-knob {
    position: absolute; left: 2px; top: 2px; width: 28px; height: 28px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center; font-size: 18px; font-weight: 800; line-height: 1;
    color: #2a0c0c; background: #ff8a8a; box-shadow: 0 2px 8px rgba(0,0,0,.4);
  }
  #reset-slide.armed .slide-knob { background: #ff5a5a; }
  #settings-close {
    margin-top: 7px; width: 100%; cursor: pointer; pointer-events: auto; font: inherit;
    padding: 9px 18px; font-size: 15px; font-weight: 600; border-radius: 10px;
    background: transparent; color: #9fb6e6; border: 1px solid rgba(120,150,210,.3);
  }
  #settings-close:hover { background: rgba(120,150,210,.12); }
  /* Confirm step (sits above the settings box, within the same overlay) */
  #reset-confirm { position: absolute; inset: 0; z-index: 1; display: none; align-items: center; justify-content: center; padding: 20px; }
  #reset-confirm.on { display: flex; }
  #reset-confirm .reset-confirm-box {
    width: min(420px, 92vw); box-sizing: border-box; text-align: center;
    background: rgba(28,16,20,.98); border: 1px solid rgba(255,120,120,.4);
    border-radius: 14px; padding: 24px 22px; box-shadow: 0 18px 60px rgba(0,0,0,.6);
  }
  #reset-confirm h2 { margin: 0 0 12px; font-size: 20px; color: #ffb3b3; }
  #reset-confirm p { margin: 0 0 20px; font-size: 14px; line-height: 1.5; opacity: .9; }
  #reset-confirm .reset-confirm-actions { display: flex; gap: 12px; }
  #reset-confirm .reset-confirm-actions button { flex: 1 1 0; padding: 11px 14px; font: inherit; font-size: 15px; font-weight: 600; border-radius: 10px; cursor: pointer; }
  #reset-confirm .ghost { background: transparent; color: #9fb6e6; border: 1px solid rgba(120,150,210,.35); }
  #reset-confirm .danger-btn { background: linear-gradient(90deg,#e53935,#b71c1c); color: #fff; border: 0; }
  #reset-confirm .danger-btn:disabled { opacity: .6; cursor: default; }
  /* Sell confirmation modal (docs/plans/2026-08-09-sell-confirm-quantity.md): sale price + optional quantity
     (slider + number when the stash holds more than one). Mirrors the reset-confirm box, blue accent. */
  #sell-overlay { position: fixed; inset: 0; z-index: 30; display: none; align-items: center; justify-content: center; padding: 20px; background: rgba(4,8,18,.6); }
  #sell-overlay.on { display: flex; }
  #sell-overlay .sell-box {
    width: min(420px, 92vw); box-sizing: border-box; text-align: center;
    background: rgba(16,22,40,.98); border: 1px solid rgba(140,175,255,.4);
    border-radius: 14px; padding: 24px 22px; box-shadow: 0 18px 60px rgba(0,0,0,.6);
  }
  #sell-overlay h2 { margin: 0 0 12px; font-size: 20px; color: #dfe9ff; }
  #sell-overlay .sell-item { font-size: 16px; color: #cfe0ff; margin: 0 0 16px; }
  #sell-overlay .sell-qty { display: flex; flex-direction: column; gap: 8px; margin: 0 0 16px; }
  #sell-overlay .sell-qty-head { display: flex; justify-content: space-between; font-size: 13px; color: #9fb3d6; }
  #sell-overlay .sell-qty input[type=range] { width: 100%; accent-color: #4a7dff; height: 18px; cursor: pointer; }
  #sell-overlay .sell-qty input[type=number] { width: 90px; align-self: center; font: inherit; font-size: 15px; text-align: center;
    background: rgba(20,30,55,.6); color: #eaf1ff; border: 1px solid rgba(140,175,255,.3); border-radius: 6px; padding: 5px 8px; }
  #sell-overlay .sell-total { font-size: 16px; color: #cfe0ff; margin: 0 0 20px; }
  #sell-overlay .sell-total b { color: #ffcf5a; font-size: 20px; }
  #sell-overlay .sell-actions { display: flex; gap: 12px; }
  #sell-overlay .sell-actions button { flex: 1 1 0; padding: 11px 14px; font: inherit; font-size: 15px; font-weight: 600; border-radius: 10px; cursor: pointer; }
  #sell-overlay .ghost { background: transparent; color: #9fb6e6; border: 1px solid rgba(120,150,210,.35); }
  #sell-overlay .primary-btn { background: #4a7dff; color: #fff; border: 0; }
  /* Credits & attributions overlay (opened from settings; z-index 21 so it sits above the settings modal).
     A scrollable list of third-party 3D models (full CC-BY attribution) + music/sound (courtesy). */
  #credits-overlay {
    position: fixed; inset: 0; z-index: 21; display: none; align-items: center; justify-content: center;
    background: rgba(5,6,13,.66); color: #e8f1ff; font-family: system-ui, sans-serif; padding: 20px;
  }
  #credits-overlay.on { display: flex; }
  #credits-overlay .credits-box {
    width: min(560px, 94vw); max-height: 92vh; overflow-y: auto; box-sizing: border-box;
    background: rgba(16,24,44,.96); border: 1px solid rgba(140,175,255,.28);
    border-radius: 16px; padding: 12px clamp(16px, 5vw, 32px); box-shadow: 0 18px 60px rgba(0,0,0,.55);
  }
  #credits-overlay h1 { font-size: 20px; letter-spacing: 1px; margin: 0 0 6px; }
  #credits-overlay .credits-intro { font-size: 12px; opacity: .6; margin: 0 0 8px; }
  #credits-overlay .credits-note { font-size: 12px; opacity: .78; margin: 0 0 10px; line-height: 1.4; }
  #credits-overlay .credit-section-title {
    font-size: 15px; font-weight: 700; letter-spacing: .5px; margin: 14px 0 6px;
    padding-bottom: 3px; border-bottom: 1px solid rgba(140,175,255,.18);
  }
  #credits-overlay .credit-item { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; margin: 7px 0; }
  #credits-overlay .credit-name { font-weight: 600; font-size: 14px; }
  #credits-overlay .credit-by { font-size: 13px; opacity: .72; }
  #credits-overlay .credit-meta { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 10px; font-size: 12px; }
  #credits-overlay .credit-meta a { color: #8fb4ff; text-decoration: none; }
  #credits-overlay .credit-meta a:hover { text-decoration: underline; }
  #credits-overlay .credit-license { opacity: .8; }
  #credits-overlay .credit-modified {
    font-size: 11px; font-weight: 700; letter-spacing: .4px; padding: 2px 8px; border-radius: 8px;
    background: rgba(54,209,220,.18); color: #bff0f4; border: 1px solid rgba(140,175,255,.4);
  }
  #credits-close {
    margin-top: 12px; width: 100%; cursor: pointer; pointer-events: auto; font: inherit;
    padding: 9px 18px; font-size: 15px; font-weight: 600; border-radius: 10px;
    background: transparent; color: #9fb6e6; border: 1px solid rgba(120,150,210,.3);
  }
  #credits-close:hover { background: rgba(120,150,210,.12); }
  /* Perf/diagnostics overlay: dev-only, gated by the sticky ?dev flag (body.devmode, see client/src/dev.js).
     Hidden for normal players. body.devmode:not(.menu) beats the menu-hide so it never shows on menus. */
  #perf {
    display: none;
    position: fixed; top: 34px; left: 50%; transform: translateX(-50%);
    font-family: ui-monospace, Menlo, monospace; font-size: 12px;
    color: #9fe8b0; background: rgba(0,0,0,.45); padding: 4px 10px; border-radius: 6px;
    pointer-events: none; white-space: nowrap; letter-spacing: .3px;
  }
  body.devmode:not(.menu) #perf { display: block; }
  /* Touch controls (touch devices only) */
  #touch { position: fixed; inset: 0; display: none; pointer-events: none; touch-action: none; z-index: 5; }
  #touch.on { display: block; }
  /* Full play area: tap-vs-drag lives here (a <10px tap = object pick, a drag = steering). Fire/rocket/zoom
     are layered above it (later #touch children in the same z-5 context, or z-6). See DECISIONS §42. */
  #stick-zone { position: absolute; inset: 0; pointer-events: auto; }
  #overlay { z-index: 10; } /* "Game over"/"Restart" screen - above touch controls */
  #stick-base {
    position: absolute; width: 120px; height: 120px; margin: -60px 0 0 -60px;
    border: 2px solid rgba(120,170,255,.5); border-radius: 50%;
    background: rgba(40,70,120,.18); display: none;
  }
  #stick-knob {
    position: absolute; width: 56px; height: 56px; margin: -28px 0 0 -28px;
    border-radius: 50%; background: rgba(120,170,255,.55); display: none;
  }
  #fire-btn {
    /* sit to the LEFT of the rocket button so the two don't overlap on touch */
    position: absolute; right: 134px; bottom: 34px; width: 96px; height: 96px;
    margin: 0; border-radius: 50%; pointer-events: auto;
    background: radial-gradient(circle at 50% 40%, rgba(255,140,90,.85), rgba(200,60,40,.7));
    border: 2px solid rgba(255,180,140,.8);
    display: flex; align-items: center; justify-content: center;
    color: #fff; font: 700 16px system-ui, sans-serif; user-select: none;
  }
  #fire-btn:active { filter: brightness(1.3); }
  /* Rocket button = reload indicator (circle fills as it reloads). Shown on PC and mobile. */
  #rocket-btn {
    position: fixed; right: 28px; bottom: 40px; width: 84px; height: 84px;
    border-radius: 50%; pointer-events: auto; user-select: none; overflow: hidden;
    border: 2px solid rgba(255,180,120,.55); background: rgba(20,16,12,.5);
    display: flex; align-items: center; justify-content: center;
    z-index: 6; /* above the now full-screen #stick-zone (z-5) so it stays tappable during flight */
  }
  #rocket-fill { position: absolute; inset: 0; border-radius: 50%; }
  #rocket-btn .rb-icon { position: relative; z-index: 1; font-size: 34px; text-shadow: 0 0 5px #000; }
  #rocket-btn.ready { border-color: #77ee77; box-shadow: 0 0 12px rgba(110,238,110,.5); }
  #rocket-btn:active { filter: brightness(1.25); }
  /* Event log: last 4 lines above the rocket button (both desktop + touch), each fading over 5s. */
  #event-log {
    position: fixed; right: 22px; bottom: 132px; z-index: 6;
    width: 240px; pointer-events: none;
    display: flex; flex-direction: column; align-items: flex-end; /* newest appended at the bottom */
    gap: 2px; text-align: right;
  }
  .event-line {
    color: #dfe8ff; font: 600 14px system-ui, sans-serif;
    text-shadow: 0 0 4px #000, 0 1px 2px #000;
    animation: eventfade 5s linear forwards;
  }
  @keyframes eventfade { 0%,60% { opacity: 1; } 100% { opacity: 0; } }
  /* Zoom controls (+/-): PC + mobile. Hidden on menus like the rest of the HUD. */
  #zoom {
    position: fixed; right: 16px; top: 50%; transform: translateY(-50%); z-index: 6;
    display: flex; flex-direction: column; gap: 8px; pointer-events: none;
  }
  #zoom button {
    pointer-events: auto; cursor: pointer; touch-action: manipulation; user-select: none;
    width: 44px; height: 44px; line-height: 1; font-size: 24px; font-weight: 700; color: #cfe0ff;
    background: rgba(20,30,55,.6); border: 1px solid rgba(140,175,255,.35); border-radius: 9px;
    display: flex; align-items: center; justify-content: center; text-shadow: 0 0 6px rgba(0,0,0,.8);
  }
  #zoom button:hover { background: rgba(40,60,100,.7); }
  #zoom button:active { filter: brightness(1.3); }
  /* Touch: place the zoom pair at the TOP-RIGHT, directly under the "Destroyed X/Y" HUD counter
     (the #hud .right column). Kept out of the bottom-center so it never collides with the
     Return-to-base button (#return-btn, also bottom-center on the return-to-base phase). Vertical
     "＋ / −" column, inheriting the base flex-direction:column. */
  body.touch #zoom {
    right: 16px; left: auto; top: 96px; bottom: auto;
    transform: none;
    gap: 10px;
  }
  /* Off-screen enemy indicators: arrows on the screen edge pointing toward off-screen enemies */
  #markers { position: fixed; inset: 0; pointer-events: none; z-index: 4; overflow: hidden; }
  .marker {
    /* Positioned by hud.js via a single `transform` (translate + centring + rotation) — pinned at the
       container origin here so JS never has to touch left/top, which would cost a layout per frame. */
    position: absolute; left: 0; top: 0; width: 0; height: 0; will-change: transform;
    border-top: 8px solid transparent; border-bottom: 8px solid transparent;
    border-left: 15px solid #fff; /* points +x by default; color set per enemy; rotated to aim */
    filter: drop-shadow(0 0 3px rgba(0,0,0,.85)); display: none;
  }
  /* Off-screen loot-drop arrows: same arrow shape as .marker, a fixed green so chests read as loot, not enemies */
  .drop-marker { border-left-color: #59e0a0; }
  /* Off-screen mission pointer (roam): same arrow shape, gold — matches the mission theme + the "Autopilot to
     Mission" button, and a gold glow (not the plain black drop-shadow) so it reads as "your objective" */
  .mission-marker { border-left-color: #ffcf5a; filter: drop-shadow(0 0 4px rgba(255,207,90,.85)); }
  /* Reward (L1/L2 last-kill) loot arrow: brighter green + a pulsing green glow so it stands out from the
     plain green loot arrows (which are already #59e0a0 — the pulse, not the hue, is the distinction) */
  .drop-marker.special { border-left-color: #7dffbf; animation: dropMarkerGlow 1.1s ease-in-out infinite; }
  @keyframes dropMarkerGlow {
    0%,100% { filter: drop-shadow(0 0 3px rgba(0,0,0,.85)) drop-shadow(0 0 4px rgba(89,224,160,.6)); }
    50%     { filter: drop-shadow(0 0 3px rgba(0,0,0,.85)) drop-shadow(0 0 10px rgba(89,224,160,1)); }
  }
  /* Kill credit popup: gold "+xx" text floating up from a destroyed enemy, fading over ~1s (in #markers) */
  .credit-popup {
    position: absolute; left: 0; top: 0; will-change: transform, opacity; /* transform set by hud.js (incl. the -50%/-50% centring) */
    color: #77ee77; font-family: system-ui, sans-serif; font-weight: 700; font-size: 16px;
    letter-spacing: .3px; text-shadow: 0 0 4px rgba(0,0,0,.9); white-space: nowrap;
    pointer-events: none; display: none;
  }
  .credit-popup.evade { color: #7fdfff; font-size: 15px; letter-spacing: 1px; } /* "EVADE" on a dodged shot (cyan) */
  /* Character progression HUD (docs/plans/2026-08-09-character-progression.md):
     free-skill-points badge on the Character menu item, the always-on bottom XP bar, and the level-up toast. */
  #mw-menu .mw-item { position: relative; }
  /* shared pill for the menu-item counters: free skill points (Character) and offered missions (Missions) */
  .mw-badge { display: none; }
  .mw-badge.show {
    display: inline-flex; align-items: center; justify-content: center;
    position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
    min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px;
    background: #ffcf5a; color: #1a1a1a; font-size: 12px; font-weight: 700; line-height: 1;
  }
  /* "(new)" on the Loadout menu item: newly unlocked shop items are waiting. Same gold as the badges
     above, but plain inline text — it says "look here", and a count would be noise. Cleared when the player
     OPENS THE SHOP (mainwindow.js updateLoadoutNew → shop.js markShopItemsSeen); the trail then continues
     inside the shop as the gold .lp-type.new tab + .lp-shop-item.new row. */
  .mw-new { display: none; }
  .mw-new.show { display: inline; margin-left: 6px; color: #ffcf5a; font-size: 12px; font-weight: 700; }
  #xp-bar {
    position: fixed; left: 50%; bottom: 6px; transform: translateX(-50%);
    width: 80%; height: 14px; z-index: 13; pointer-events: none; display: none; overflow: hidden; /* above #mainwin (12) so it shows on the base too */
    background: rgba(20,22,14,.5); border: 1px solid rgba(255,207,90,.35); border-radius: 8px;
  }
  #xp-bar-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0%;
    background: linear-gradient(90deg,#ffcf5a,#ffb020); transition: width .25s ease; }
  #xp-bar-text { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font: 700 10px system-ui, sans-serif; color: #fff; letter-spacing: .3px; text-shadow: 0 1px 2px rgba(0,0,0,.75); }
  #levelup-toast {
    position: fixed; left: 50%; top: 42%; transform: translate(-50%,-50%); z-index: 40; pointer-events: none;
    opacity: 0; white-space: nowrap; font: 800 48px system-ui, sans-serif; color: #fff; letter-spacing: 2px;
    text-shadow: 0 2px 16px rgba(0,0,0,.7);
  }
  #levelup-toast.show { animation: levelup-fade 2s ease-out forwards; }
  @keyframes levelup-fade { 0% { opacity: 1; } 100% { opacity: 0; } }
  /* Enemy health bar: a small translucent red bar that sits just above a damaged enemy (bottom edge pinned
     to the projected anchor; in #markers, shown once hp < max) */
  .enemy-hp {
    position: absolute; left: 0; top: 0; will-change: transform; /* transform set by hud.js (incl. the -50% / -100%-4px anchor) */
    width: 40px; height: 5px; border-radius: 3px; overflow: hidden;
    background: rgba(120, 20, 20, .28); box-shadow: 0 0 3px rgba(0, 0, 0, .6);
    pointer-events: none; display: none;
  }
  .enemy-hp > i { display: block; height: 100%; width: 100%; background: rgba(229, 57, 53, .72); }
  /* Shield strip stacked above the red enemy health bar (same 40px width, 1px gap). Blue = active,
     purple = broken & refilling (mirrors the player's #shieldbar). Anchored at the SAME projected point
     as .enemy-hp, just lifted by (4px gap + 5px bar + 1px gap) = 10px. */
  .enemy-shield {
    position: absolute; left: 0; top: 0; will-change: transform; /* transform set by hud.js (incl. the -50% / -100%-10px anchor) */
    width: 40px; height: 3px; border-radius: 2px; overflow: hidden;
    background: rgba(20, 50, 110, .28); box-shadow: 0 0 3px rgba(0, 0, 0, .6);
    pointer-events: none; display: none;
  }
  .enemy-shield > i { display: block; height: 100%; width: 100%; background: linear-gradient(90deg, #36d1dc, #5b86e5); }
  .enemy-shield.recharging > i { background: linear-gradient(90deg, #7b2ff7, #b06bff); }
  /* "Left the battlefield" warning + countdown (soft boundary). Centered near the top, non-interactive. */
  #oob-warn {
    position: fixed; top: 70px; left: 50%; transform: translateX(-50%); z-index: 5;
    display: none; text-align: center; pointer-events: none; max-width: 90vw;
    font-family: system-ui, sans-serif; text-shadow: 0 0 8px rgba(0,0,0,.9);
  }
  #oob-warn .oob-title {
    color: #ff8a6a; font-weight: 700; font-size: 17px; letter-spacing: .5px;
  }
  #oob-warn .oob-count { color: #ffd27a; font-size: 14px; margin-top: 3px; }
  /* "Sector cleared — return to base" hint (return-to-base). Centered near the top, non-interactive. */
  #return-hint {
    position: fixed; top: 70px; left: 50%; transform: translateX(-50%); z-index: 5;
    display: none; text-align: center; pointer-events: none; max-width: 90vw;
    font-family: system-ui, sans-serif; text-shadow: 0 0 8px rgba(0,0,0,.9);
    color: #7ec8ff; font-weight: 700; font-size: 17px; letter-spacing: .5px;
  }
  /* Bottom-center "Return to base" tap button (return-to-base). Explicit dock target, complements the
     top #return-hint. Interactive (pointer-events auto) and layered above the full-screen touch
     #stick-zone (z-5); horizontally centered, clear of the bottom-right #rocket-btn (and, on touch, of
     the zoom pair which now lives top-right). Styled to match the Take-off button (orange gradient,
     dark text) so it reads as the primary "go" action. Shown/hidden by updateReturnHint (display none
     by default). */
  #return-btn {
    position: fixed; left: 50%; bottom: 34px; transform: translateX(-50%); z-index: 6;
    display: none; pointer-events: auto; cursor: pointer;
    font-family: system-ui, sans-serif; font-weight: 700; font-size: 16px; letter-spacing: 1px;
    color: #1a0c02; background: linear-gradient(90deg,#ffb35a,#ff7a3c); border: 0;
    border-radius: 22px; padding: 12px 24px;
    box-shadow: 0 0 16px rgba(255,140,60,.4);
    -webkit-tap-highlight-color: transparent;
  }
  #return-btn:hover { filter: brightness(1.08); }
  #return-btn:active { filter: brightness(1.15); }
  /* Roam bottom-center nav: two buttons side by side (wrap on very narrow screens), same slot as #return-btn
     (they never show together — #return-btn is return-to-base, this is roam). "Return to Base" keeps the
     orange "go home" gradient; "Autopilot to Mission" is gold to match the off-screen mission pointer.
     Shown/hidden by updateRoamNav (display:none by default). */
  #roam-nav {
    position: fixed; left: 50%; bottom: 34px; transform: translateX(-50%); z-index: 6;
    display: none; gap: 12px; flex-wrap: wrap; justify-content: center; max-width: 92vw;
  }
  #roam-nav button {
    pointer-events: auto; cursor: pointer;
    font-family: system-ui, sans-serif; font-weight: 700; font-size: 15px; letter-spacing: .8px;
    border: 0; border-radius: 22px; padding: 11px 20px; -webkit-tap-highlight-color: transparent;
  }
  #roam-return { color: #1a0c02; background: linear-gradient(90deg,#ffb35a,#ff7a3c); box-shadow: 0 0 16px rgba(255,140,60,.4); }
  #roam-autopilot { color: #17130a; background: linear-gradient(90deg,#ffe08a,#ffcf5a); box-shadow: 0 0 16px rgba(255,207,90,.4); }
  #roam-nav button:hover { filter: brightness(1.08); }
  #roam-nav button:active { filter: brightness(1.15); }
  /* The destination you are currently flying to (switch/cancel): outlined + brightened so it reads as "engaged,
     tap again to cancel". */
  #roam-nav button.engaged { outline: 2px solid rgba(255,255,255,.9); outline-offset: 2px; filter: brightness(1.12); }
  /* Transient milestone banner ("10 enemies left", "Final Stage"): big centered line in the upper third
     so it clears the ship. Non-interactive; opacity is set inline by updateBanner (fades to 0 over 3s). */
  /* Level-load veil (see index.html): covers the canvas while the built scene is compiled/uploaded. It is
     plain DOM, so it paints even while the WebGL main thread is blocked — provided it is shown a frame
     BEFORE the blocking work starts, which main.js arranges. */
  #levelwarm {
    position: fixed; inset: 0; z-index: 9; display: flex;
    background: radial-gradient(ellipse at center, rgba(10,16,28,.86), rgba(4,7,14,.97));
    align-items: center; justify-content: center; pointer-events: none;
    /* Hidden by opacity rather than `display`, with a delay before it fades in: a fast machine finishes the
       warm in a frame or two and the veil never becomes visible at all, while a weak phone (~3 s of blocked
       main thread) sees it well before the freeze would read as a crash. */
    visibility: hidden; opacity: 0; transition: opacity .18s ease .09s, visibility 0s linear .27s;
  }
  #levelwarm.on { visibility: visible; opacity: 1; transition: opacity .18s ease .09s, visibility 0s; }
  #levelwarm-text {
    font-family: system-ui, sans-serif; font-weight: 700; font-size: clamp(18px, 3.2vw, 30px);
    letter-spacing: 1px; color: #cfe3ff; text-shadow: 0 0 18px rgba(90,170,255,.5);
    animation: levelwarmPulse 1.1s ease-in-out infinite;
  }
  @keyframes levelwarmPulse { 0%,100% { opacity: .55; } 50% { opacity: 1; } }
  #banner {
    position: fixed; top: 26%; left: 50%; transform: translate(-50%, -50%); z-index: 6;
    display: none; text-align: center; pointer-events: none; max-width: 90vw; white-space: nowrap;
    font-family: system-ui, sans-serif; font-weight: 800; font-size: 40px; letter-spacing: 1px;
    color: #eaf4ff; text-shadow: 0 0 18px rgba(90,170,255,.65), 0 2px 10px rgba(0,0,0,.9);
  }
  /* "Dock here" cursor: hovering the clickable base station shows a first-party landing/dock glyph
     (return-to-base, mouse only; the class is toggled on the WebGL canvas by main.js). A raster PNG is
     used deliberately — Safari does not support SVG data-URI cursors — with `pointer` as the fallback. */
  canvas.dock-cursor { cursor: url(assets/ui/dock-cursor.png) 16 16, pointer; }
  /* "Grab this chest" cursor: hovering a clickable loot drop shows the OS grab hand (mouse only; the
     class is toggled on the WebGL canvas by main.js). Built-in cursor — no asset needed. */
  canvas.grab-cursor { cursor: grab; }
  /* Corner radar: arena bounds + player + type-colored enemy dots. Non-interactive (touches pass through).
     Sits in the top-left cluster directly UNDER the shield/health bars and their % readout, left-aligned
     with them: #hud's 18px padding + the 44px inset that clears the settings gear = 62px; the bars stack
     to y≈63 (14 padding + 4 + 10 shield + 14 health + 4 + ~16 percent), so 72px leaves a small gap. */
  #minimap {
    position: fixed; left: 62px; top: 72px; z-index: 4;
    width: 132px; height: 132px; pointer-events: none; opacity: .82;
    background: rgba(8,12,24,.26); border: 1px solid rgba(120,150,210,.2); border-radius: 8px;
  }
  /* Out-of-combat Map button — occupies the (now hidden) mini-map corner while roaming; toggled by main.js.
     z-6 like #rocket-btn / #return-btn: the full-screen #stick-zone lives inside #touch at z-5 and comes
     LATER in the document, so at an equal z-index it won the hit test and swallowed every tap on this
     button — the map simply would not open on a phone (it worked on desktop only because #touch is hidden
     there). See DECISIONS §42. */
  #map-btn {
    position: fixed; left: 62px; top: 72px; z-index: 6; display: none;
    width: 132px; height: 40px; cursor: pointer;
    font: 700 15px/1 system-ui, sans-serif; color: #0b0f14;
    background: linear-gradient(#8fe0ff, #4bb6ff); border: 0; border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0,0,0,.35);
  }
  body.menu #map-btn { display: none !important; }
