/* Hasher Main Screen Pass 7 — Typography Implementation. Self-hosted
   webfonts (no runtime fonts.googleapis.com dependency), served from the
   same repo-root assets/ tree every other resolver in assetPaths.ts
   points at (docs/ASSET_BIBLE.md Section 21's own /fonts entry). Rye is
   a single static weight (the family only ships one); Inter is the
   variable file, declared once with a font-weight RANGE so 400/600/700
   all resolve from the one file instead of bundling three static
   instances — see Section 3/6 of the Pass 7 brief ("if variable Inter
   is legitimately available and simpler, that is acceptable"). Both
   files were downloaded directly from Google Fonts' own CDN
   (fonts.gstatic.com), which serves the same OFL-licensed binaries the
   fonts.googleapis.com stylesheet would have linked to at runtime — this
   only changes WHERE the bytes are fetched from (bundled vs. runtime),
   not their license or origin. */
@font-face {
  font-family: "Rye";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/rye_regular.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/fonts/inter_variable.woff2") format("woff2");
}

/* Pass 9 — Typography Final Hybrid (Option D). Oswald joins Rye/Inter as a
   THIRD, additive role: machine/navigation controls, distinct from Rye's
   world-identity role and Inter's functional/data role — never a
   replacement for either. Self-hosted the same way Rye/Inter already are:
   downloaded directly from Google Fonts' own CDN (fonts.gstatic.com),
   the same OFL-1.1-licensed binary the fonts.googleapis.com stylesheet
   would have linked to at runtime — this only changes WHERE the bytes are
   fetched from, not their license or origin. One variable-font file
   covering exactly the 600-700 weight range this hierarchy actually uses
   (600 for secondary nav controls, 700 for the primary Start Day
   control) — the same "one variable file over several static instances"
   choice already made for Inter, and the narrowest weight range Google
   Fonts serves that still covers both. */
@font-face {
  font-family: "Oswald";
  font-style: normal;
  font-weight: 600 700;
  font-display: swap;
  src: url("/assets/fonts/oswald_variable.woff2") format("woff2");
}

:root {
  /* World/identity layer (major game-world headings, Hasher names) vs.
     machine/navigation-control layer (Pass 9) vs. functional/data layer
     (labels, numbers) — the restrained Pass 7 hierarchy, now with Oswald
     added as its own additive role rather than folded into either
     existing variable. Each has its own themed fallback chain so an
     offline/blocked-font load still reads as the right FAMILY of face,
     never a generic sans standing in for a themed one. */
  --font-frontier: "Rye", Rockwell, "Roboto Slab", Georgia, "Times New Roman", serif;
  --font-interface: "Inter", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --font-control: "Oswald", "Arial Narrow", Arial, sans-serif;

  --coal: #0e1013;
  --panel: #171a1f;
  --panel-border: #2a2f37;
  --text: #e8e6e1;
  --text-dim: #9aa0a8;
  --rich: #b5652f;
  --rich-dim: #6e3f1f;
  --poor: #3c6e8f;
  --poor-dim: #274a5f;
  --cooled-glow: #7fd4ff;
  --amber: #f0a94e;
  --danger: #d15b5b;
  --ok: #6fbf73;
  /* Epic rarity — "Purple" is locked (docs/ASSET_BIBLE.md Section 8's rarity colour table); this exact hex isn't separately locked, chosen to match the rest of this palette's muted saturation/lightness. */
  --epic: #8a6bb0;
  /* Mythic is explicitly a provisional placeholder, not a confirmed design decision (Mythic is FUTURE/DISABLED — ACTIVE_GEAR_RARITIES never produces it) — deliberately desaturated rather than picking a "real" jewel tone for a tier with no approved visual design yet. */
  --mythic: #6b6f78;
  --radius: 8px;

  /* ---------- DIGITAL GOLD RUSH milestone — Crew / Hasher Management screen tokens ----------
     Additive only: every existing token above is untouched. These extend the
     same dark, muted, low-saturation palette (never a competing visual
     language) toward the milestone's own "weathered timber, blackened
     steel, aged brass" frontier-mining direction; --danger/--ok already ARE
     Red & Green's own accent colours, reused here rather than duplicated. */
  --gr-timber: #241a13;
  --gr-timber-light: #3a2a1d;
  --gr-steel: #2c3036;
  --gr-steel-dark: #1b1e22;
  --gr-brass: #c9974f;
  --gr-brass-dim: #8a6a3a;
  --gr-rivet: #14110c;
  /* Pass 9 Option D — a third, brighter brass step approved specifically
     for BTC Balance: plain --gr-brass measured too subdued next to
     Cash's green during the typography audition (computed luminance
     ~157.7 vs this value's ~188.4, a real ~20% increase, confirmed
     before approval) — still the same aged-metal family, not a new hue. */
  --gr-brass-bright: #e3b968;
}

/* Screen-reader-only text (icon-only stat values still need a real text
   alternative — Phase 17's own accessibility requirement) — visually
   hidden without `display:none`, which would remove it from the
   accessibility tree too. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  background: var(--coal);
  color: var(--text);
  font-family: var(--font-interface);
}

/* Pass 7 — form controls don't inherit the body font-family by default
   (a standard browser UA-stylesheet quirk, not something introduced by
   this pass) — without this, every button/input/select/textarea falls
   back to the platform's native control font regardless of what `body`
   declares. `inherit` (not a repeated font stack, per rule 6) is enough
   to pull in `var(--font-interface)` from body, which is what rules
   11/12 require for nav buttons, action buttons, and form fields. */
button,
input,
select,
textarea {
  font-family: inherit;
}

#root {
  min-height: 100vh;
}

.app-shell {
  /* Corrective Pass 3 — narrowed from 1880px. The wider shell (Pass 1/2)
     left almost no visible margin at 1920px for the underground
     environment behind the facility to actually read (Phase 7's own
     "the current almost-black treatment does not establish setting"
     finding) — this opens a real, if restrained, strip of mine wall on
     both sides at desktop width without giving up meaningful board room
     (the board itself is sized independently, see .board-iso). */
  max-width: 1720px;
  margin: 0 auto;
  /* Pass 5B — top/bottom trimmed 16px -> 12px as one of several small
     savings toward fitting the full board + legend in one viewport
     (see .board-iso's tile-size ceiling and .board-outer's padding for
     the larger contributors). Side padding unchanged — the visible mine
     margin from Corrective Pass 3 stays intact. */
  padding: 12px 16px;
  position: relative;
}

.app-header h1 {
  margin: 0 0 4px 0;
  font-size: 22px;
}

.app-session-gate {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  min-height: 60vh;
  text-align: center;
}

.app-subtitle {
  margin: 0 0 16px 0;
  color: var(--text-dim);
  font-size: 13px;
}

.app-subtitle code {
  background: var(--panel);
  padding: 1px 6px;
  border-radius: 4px;
}

.error-banner {
  background: #3a1f1f;
  border: 1px solid var(--danger);
  color: #ffb3b3;
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-bottom: 12px;
  font-size: 14px;
}

/* Gear Durability milestone — a concise, routine-gameplay notification (never an error banner's styling/role) for a qualifying action's fatigue roll (Durability Notification Queue milestone: durabilityLoss moved to GearDurabilityNotifications below). */
.durability-toast {
  background: #2a2113;
  border: 1px solid var(--amber, #f0a94e);
  color: #f0d9a8;
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-bottom: 12px;
  font-size: 14px;
}

/* Durability visibility milestone (Goal 2) — the prominent, transient
   GEAR BROKEN alert. Fixed-position, non-blocking (pointer-events: none —
   never intercepts a click, never requires dismissal), auto-fading via the
   ONE keyframe animation below, whose duration must match
   GearBrokenAlert.tsx's ALERT_DURATION_MS exactly (4200ms) so the node
   unmounts right as the fade finishes. */
.gear-broken-alert {
  position: fixed;
  top: 72px;
  left: 50%;
  transform: translateX(-50%);
  /* Above every modal/overlay in the app (the highest pre-existing
     z-index is 100, for .modal-backdrop) — a break can happen while the
     player has the Shop, Equipment, or Crew panel open, and the alert
     must still be seen, not silently swallowed behind it. */
  z-index: 300;
  width: min(380px, calc(100vw - 32px));
  pointer-events: none;
  text-align: center;
  padding: 16px 20px 14px;
  border-radius: 6px;
  background: linear-gradient(180deg, var(--gr-timber-light), var(--gr-timber));
  border: 2px solid var(--danger);
  box-shadow: 0 0 0 1px var(--gr-rivet) inset, 0 0 22px rgba(209, 91, 91, 0.55), 0 10px 28px rgba(0, 0, 0, 0.5);
  overflow: hidden;
  animation: gear-broken-alert-enter 4200ms ease-out forwards;
}

/* Western-industrial crack/impact treatment: a few jagged diagonal shards
   flashing across the panel on entry, suggesting a cracked steel plate
   rather than a generic modern toast. */
.gear-broken-alert-crack {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(105deg, transparent 42%, rgba(209, 91, 91, 0.65) 43%, rgba(209, 91, 91, 0.65) 44%, transparent 45%),
    linear-gradient(72deg, transparent 58%, rgba(255, 179, 179, 0.5) 59%, rgba(255, 179, 179, 0.5) 60%, transparent 61%),
    linear-gradient(130deg, transparent 20%, rgba(209, 91, 91, 0.4) 21%, rgba(209, 91, 91, 0.4) 21.6%, transparent 23%);
  opacity: 0;
  animation: gear-broken-alert-crack-flash 4200ms ease-out forwards;
}

.gear-broken-alert-head {
  font-family: var(--font-frontier);
  font-size: 22px;
  letter-spacing: 0.08em;
  color: #ffd3d3;
  text-shadow: 0 0 10px rgba(209, 91, 91, 0.9), 0 2px 1px rgba(0, 0, 0, 0.6);
  margin-bottom: 6px;
}

.gear-broken-alert-item {
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.03em;
  color: var(--gr-brass-bright);
  margin-bottom: 4px;
}

.gear-broken-alert-line {
  font-family: var(--font-control);
  font-size: 12.5px;
  color: var(--text);
  line-height: 1.4;
}

.gear-broken-alert-durability {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--danger);
  margin-top: 2px;
}

.gear-broken-alert-note {
  margin-top: 6px;
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #ffb3b3;
}

.gear-broken-alert-note-secondary {
  margin-top: 3px;
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.03em;
  color: var(--text-dim);
}

@keyframes gear-broken-alert-enter {
  0% {
    opacity: 0;
    transform: translateX(-50%) scale(0.9);
  }
  4% {
    opacity: 1;
    transform: translateX(calc(-50% - 7px)) scale(1.02);
  }
  8% {
    transform: translateX(calc(-50% + 6px)) scale(1);
  }
  12% {
    transform: translateX(calc(-50% - 4px)) scale(1);
  }
  16% {
    transform: translateX(calc(-50% + 3px)) scale(1);
  }
  20% {
    transform: translateX(-50%) scale(1);
  }
  88% {
    opacity: 1;
    transform: translateX(-50%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translateX(-50%) scale(1);
  }
}

@keyframes gear-broken-alert-crack-flash {
  0%,
  4% {
    opacity: 0;
  }
  6% {
    opacity: 1;
  }
  22% {
    opacity: 0.25;
  }
  100% {
    opacity: 0.25;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gear-broken-alert {
    animation: gear-broken-alert-enter-reduced 4200ms ease-out forwards;
  }

  .gear-broken-alert-crack {
    animation: none;
    opacity: 0.2;
  }
}

@keyframes gear-broken-alert-enter-reduced {
  0% {
    opacity: 0;
  }
  4% {
    opacity: 1;
  }
  88% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

/* Durability Notification Queue milestone — the "GEAR DAMAGED" alert: the
   same fixed, above-every-modal anchor point as GEAR BROKEN (the player
   learns one place to glance, never two), but deliberately one tier
   quieter everywhere it can be — amber instead of red, a narrower panel,
   smaller type, a single gentle settle instead of a jagged multi-step
   shake, and a shorter 3600ms lifetime (GearDamagedAlert.tsx's own
   GEAR_DAMAGED_ALERT_DURATION_MS, which this animation's duration must
   keep matching exactly). */
.gear-damaged-alert {
  position: fixed;
  top: 72px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 300;
  width: min(320px, calc(100vw - 32px));
  pointer-events: none;
  text-align: center;
  padding: 12px 16px 11px;
  border-radius: 6px;
  background: linear-gradient(180deg, var(--gr-timber-light), var(--gr-timber));
  border: 2px solid var(--amber, #f0a94e);
  box-shadow: 0 0 0 1px var(--gr-rivet) inset, 0 0 16px rgba(240, 169, 78, 0.4), 0 8px 20px rgba(0, 0, 0, 0.4);
  overflow: hidden;
  animation: gear-damaged-alert-enter 3600ms ease-out forwards;
}

.gear-damaged-alert-crack {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(105deg, transparent 46%, rgba(240, 169, 78, 0.45) 47%, rgba(240, 169, 78, 0.45) 48%, transparent 49%),
    linear-gradient(74deg, transparent 62%, rgba(255, 214, 158, 0.35) 63%, rgba(255, 214, 158, 0.35) 64%, transparent 65%);
  opacity: 0;
  animation: gear-damaged-alert-crack-flash 3600ms ease-out forwards;
}

.gear-damaged-alert-head {
  font-family: var(--font-frontier);
  font-size: 16px;
  letter-spacing: 0.07em;
  color: #ffe4b8;
  text-shadow: 0 0 8px rgba(240, 169, 78, 0.75), 0 2px 1px rgba(0, 0, 0, 0.55);
  margin-bottom: 5px;
}

.gear-damaged-alert-item {
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.03em;
  color: var(--gr-brass-bright);
  margin-bottom: 3px;
}

.gear-damaged-alert-line {
  font-family: var(--font-control);
  font-size: 11.5px;
  color: var(--text);
  line-height: 1.4;
}

.gear-damaged-alert-delta {
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--amber, #f0a94e);
  margin-top: 2px;
}

.gear-damaged-alert-durability {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  margin-top: 1px;
}

.gear-damaged-alert-note {
  margin-top: 5px;
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #ffd9a0;
}

@keyframes gear-damaged-alert-enter {
  0% {
    opacity: 0;
    transform: translateX(-50%) scale(0.94);
  }
  8% {
    opacity: 1;
    transform: translateX(-50%) scale(1.01);
  }
  16% {
    transform: translateX(-50%) scale(1);
  }
  85% {
    opacity: 1;
    transform: translateX(-50%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translateX(-50%) scale(1);
  }
}

@keyframes gear-damaged-alert-crack-flash {
  0%,
  6% {
    opacity: 0;
  }
  10% {
    opacity: 0.8;
  }
  24% {
    opacity: 0.18;
  }
  100% {
    opacity: 0.18;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gear-damaged-alert {
    animation: gear-damaged-alert-enter-reduced 3600ms ease-out forwards;
  }

  .gear-damaged-alert-crack {
    animation: none;
    opacity: 0.15;
  }
}

@keyframes gear-damaged-alert-enter-reduced {
  0% {
    opacity: 0;
  }
  8% {
    opacity: 1;
  }
  85% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

.panel {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 14px;
}

.panel h2 {
  margin: 0 0 10px 0;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}

/* ---------- Stats header ---------- */

.stats-header {
  display: flex;
  flex-wrap: wrap;
  gap: 16px 28px;
}

.stats-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.stats-group-heading {
  margin: 0;
  font-size: 11px;
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.stats-group-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 10px 16px;
}

.stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.stat-label {
  font-size: 11px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.stat-value {
  font-size: 16px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.stat-bonus {
  color: var(--cooled-glow);
}

/* SPD BONUS COLOUR milestone — SPD Bonus shared .stat-bonus's blue with
   Cooling Bonus, making two different bonus types look like the same
   thing. This modifier overrides just the color, applied in addition to
   (never replacing) .stat-bonus, reusing the existing warm brass/gold
   token already used for emphasis elsewhere (--gr-brass-bright) rather
   than a new isolated colour — distinct from both Cooling's blue
   (--cooled-glow) and Total's --amber (a different warm tone, so SPD
   never reads as "the total" either). Presentation only: no change to
   spdBonusProductionPerMinuteDisplay, its formula, or settlement. */
.stat-bonus-spd {
  color: var(--gr-brass-bright);
}

.stat-total {
  color: var(--amber);
}

/* ---------- Layout ---------- */

/* Action bar: Start Day / Pause Day / Shop / Crew / Equipment / Trading /
   Board / Stats / Settings / Help — sits directly under the stats header,
   above the (now board-dominant) main area. Wraps on narrow widths rather
   than scrolling horizontally. */
.action-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.action-bar-buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.action-bar-divider {
  width: 1px;
  align-self: stretch;
  background: var(--panel-border);
  margin: 0 2px;
}

/* LIQUIDATION STATUS GROUP milestone — the dedicated right-side group
   holding the Liquidation Block and the Daily Operating Time widget
   together, replacing the old arrangement where the Liquidation Block
   badge sat wedged into `.action-bar-buttons` between Trading and
   Marketplace. `align-items: stretch` gives both panels the same height
   automatically regardless of their own content length — no fixed
   height needed, so neither clips if a value's text ever runs longer. */
.action-bar-status-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 10px;
  flex: 0 0 auto;
}

/* Shared "deliberate facility status panel" treatment — the exact same
   warm timber/brass gradient `.facility-crew-panel`/`.facility-operations-
   console` already established (token reuse, never a separately-guessed
   brown), restrained brass border, static only — no pulse, no glow, no
   animation in any state (TOOLBAR CORRECTION PASS: the Liquidation
   Block's own unclaimed-reward pulse was removed entirely, not just
   recoloured — prominence now comes from the larger `$` value and the
   panel treatment itself, never motion). Applied to BOTH panels so they
   read as one deliberate pair, not navigation buttons. */
.facility-status-panel {
  display: flex;
  padding: 6px 14px;
  background: radial-gradient(
    ellipse 140% 90% at 50% -10%,
    color-mix(in srgb, var(--gr-timber-light) 90%, transparent) 0%,
    color-mix(in srgb, var(--gr-timber) 92%, transparent) 55%,
    color-mix(in srgb, var(--gr-rivet) 94%, transparent) 100%
  );
  border: 1px solid rgba(201, 151, 79, 0.35);
  border-radius: 6px;
}

/* LIQUIDATION STATUS GROUP milestone — restored, and TOOLBAR CORRECTION
   PASS — now the ONE live DailyOperatingTimeWidget on the facility
   screen. This rule used to be `display: none` (Digital Gold Rush Hasher
   Main Screen milestone, Pass 2): the widget was originally added purely
   to fill the action bar's own empty horizontal space, then hidden once
   the SAME DailyOperatingTimeWidget, same data, started rendering a
   second time in `.facility-top-strip` instead (App.tsx). The Liquidation
   Status Group milestone first restored this copy while that second one
   was still mounted — a real, if temporary, duplicate; the correction
   pass removed the `.facility-top-strip` render entirely (App.tsx no
   longer mounts a second instance there at all, not merely CSS-hidden),
   so this is now the single live render on this screen. The Crew popup's
   own separate instance (a different screen/context) is untouched. Never
   a second implementation of the countdown or a second timer — see
   DailyOperatingTimeWidget.tsx's own comment: it reads the one shared
   heartbeat, no interval of its own. */
.action-bar-time {
  display: flex;
}

.action-bar-time .daily-operating-widget {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  padding: 0;
  margin-bottom: 0;
  background: none;
  border: none;
  white-space: nowrap;
}

.action-bar-time .daily-operating-label {
  margin-bottom: 0;
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.action-bar-time .daily-operating-value {
  margin-bottom: 0;
}

.action-bar-time .daily-operating-countdown {
  font-size: 20px;
  font-weight: 700;
  color: var(--gr-brass-bright);
  margin-bottom: 0;
  font-variant-numeric: tabular-nums;
}

.action-bar-time .daily-operating-bar-track {
  display: none;
}

.action-bar-time .daily-operating-budget-note,
.action-bar-time .daily-operating-reset-note {
  margin-bottom: 0;
  font-size: 10px;
  color: var(--text-dim);
}

.action-bar-time .daily-operating-exhausted {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Main play area: the Mining Facility is the game. Digital Gold Rush
   Hasher Main Screen milestone, corrected in Pass 3 — Crew (left) and
   Operations (right) are deliberately COMPACT columns, not stretched to
   match the board's height (Pass 2's mistake: forcing Crew to fill the
   board's full height is what produced the oversized vertical cards).
   The board keeps the most real estate, but its own share is pulled back
   slightly from Pass 2's 2.6fr so the side columns read as genuine
   framing rather than starved slivers, and so there's room left for the
   environmental treatment around the board (see .board-panel). */
.app-main {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) minmax(0, 2.15fr) minmax(260px, 1.2fr);
  gap: 18px;
  align-items: start;
  /* Pass 6B — the mine-environment artwork now bleeds out from the board
     column across this whole three-column region (see
     .board-mine-environment below) instead of being clipped tightly to
     the board's own column. overflow:hidden here is the new, broader
     clip boundary — it replaces .board-panel's old overflow:hidden, so
     the artwork can extend behind Crew/Operations without ever causing
     horizontal PAGE overflow: this is still just the central gameplay
     row, not the whole app-shell. */
  overflow: hidden;
  /* Pass 6C — Background Void Fix. Audited via computed styles: nothing
     between the mine artwork (.board-mine-environment /
     .board-mine-environment-backdrop, both transparent outside their own
     rock content) and `body` sets an opaque background across this whole
     row — .board-outer/.board-panel/.app-board-column/.app-main were all
     `background: none` (.board-panel has only its own small vignette,
     which doesn't fill the row). So the transparent gaps in the mine PNG
     were revealing `body`'s own background directly: solid `--coal`
     (#0e1013, a cool near-black) plus a faint 115deg diagonal rock-
     striation texture and a few warm lantern-glow pools fixed at
     viewport-corner positions that don't reach this central area — read
     together as a flat cold void, not cave darkness. This adds ONE new
     background layer on .app-main itself (the same box the mine artwork
     is already clipped to) — a warm charcoal-brown radial gradient,
     centered on the board, ending at the exact `--coal` value by 80% so
     it blends seamlessly into the page beyond this row with no visible
     edge. Paints behind every child regardless of z-index (an element's
     own background always paints before its descendants, negative
     z-index included), so both mine layers, .board-platform, and
     Crew/Operations remain completely unaffected — this only changes
     what was previously bare transparency. */
  /* Board Scale Test — Brown Cave Infill: the 3-stop gradient above read
     as a cold void at its own outer edge (#0e1013, `--coal` — R<B, an
     actually-blue-leaning near-black) rather than cave darkness, exactly
     where the smallest remaining gaps around the now-larger board still
     show it through. Replaced with a single flat color rather than
     another gradient: sampled every fully-opaque pixel across the whole
     approved mine PNG with luminance < 55 (~72k samples) and averaged
     them to find the artwork's own real darkest tone (~#20140e, warm
     brown) — closest of the three example swatches from spec by color
     distance is #1b1814 (dist. ~8.8, vs ~10.6 and ~18.1 for the other
     two), so that's the one used. Flat, not graded, per "choose the ONE
     colour" — the mine artwork's own soft mask edges already do the
     blending; this backdrop no longer needs to. */
  background: #1b1814;
}

/* Corrective Pass 3 — neither side column stretches to match the board
   anymore. Crew is now a compact head roster (see .facility-crew-panel/
   .facility-crew-portraits) that should visibly end well above the
   board's own bottom edge; Operations similarly ends at its own natural
   content height. Both simply sit at the top of the row, framed by the
   board beside them — never artificially inflated to fill unused space.
   Pass 6B — all three columns now get an explicit stacking context
   (position:relative + z-index) so the board column's own internal
   z-index:1 (.board-outer, carrying the now-much-larger environment
   artwork) can never leak out and paint over Crew/Operations: Crew and
   Operations sit at z-index:1, the board column at z-index:0, so any
   visual overlap always resolves in Crew/Operations' favor — the
   artwork may extend visually behind them, never on top (each panel's
   own opaque --panel background then fully hides whatever is behind
   it). This doesn't change either panel's own internal layout at all. */
.app-crew-column {
  min-width: 0;
  position: relative;
  z-index: 1;
}

.app-board-column {
  min-width: 0;
  position: relative;
  z-index: 0;
}

.app-side-column {
  min-width: 0;
  display: flex;
  flex-direction: column;
  position: relative;
  z-index: 1;
}

.trading-summary-values {
  margin: 4px 0 0 0;
}

@media (max-width: 1100px) {
  .app-main {
    grid-template-columns: minmax(0, 1fr) minmax(240px, 1.2fr);
  }
  .app-crew-column {
    grid-column: 1 / -1;
    order: 3;
  }
  .app-board-column {
    order: 1;
  }
  .app-side-column {
    order: 2;
  }
}

@media (max-width: 860px) {
  .app-main {
    grid-template-columns: 1fr;
  }
  .app-crew-column {
    grid-column: auto;
  }
}

/* ---------- Board (fixed isometric) ---------- */

.board-panel {
  /* New Board Integration — board sizing. `--board-u` is px per unit of the
     board model's own geometry (see three/boardModelGrid.ts): the plot grid,
     the drawn board and the equipment all scale from this one number, so the
     whole board reflows together. It replaces the old --board-tile-size
     clamp (a uniform tile size) because the tile grid is now built from the
     model's measured cells.

     The COMPLETE painted frame (rim, corner fittings and all — not just the
     tile grid) has to fit between the Crew and Operations panels, so the
     size is derived from the board column's own width. Measured live (real
     GL output, clipping/occluders removed, --board-u 240..360): the
     frame's painted width = 2.66·u − 80.8px (±3px) and it is centred on
     the column, so half of it = 1.33·u − 40.4px; the column's half-width
     plus the 18px gutter to each panel is the room available. Setting
     u = 0.3759·columnWidth + 30px makes the frame ≈ the column's own width,
     i.e. ~18px clear of both panels (also absorbing a classic scrollbar's
     ~8px of vw error).

     --board-col-w restates .app-main's column split (1fr / 2.15fr / 1.2fr,
     18px gaps, .app-shell max-width 1720px less its 16px side padding):
     (min(100vw,1720px) − 32px − 36px) × 2.15/4.35. Verified against the
     live column at 1920 (816.5px) and 1366 (641.5px).

     --board-u-fit-h (used only ≥1500px wide, where the header doesn't wrap
     and .app-main starts at ~310px) keeps board + legend inside the
     viewport: legend bottom = 331px + 2.008·u at this rule's paddings, so
     u ≤ (100vh − 343px) / 2.008. Below 1500px the header wraps taller than
     any viewport-height fit could absorb and the page scrolls by design, so
     width alone decides. */
  --board-col-w: calc((min(100vw, 1720px) - 68px) * 0.49425);
  --board-u-fit-w: calc(var(--board-col-w) * 0.3759 + 30px);
  --board-u-fit-h: calc((100vh - 343px) * 0.498);
  --board-u: clamp(150px, var(--board-u-fit-w), 400px);
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Pass 5B — tightened 12px -> 6px (gap to the legend below), one of
     several small savings toward fitting the full board + legend within
     one viewport. */
  gap: 6px;
}

/* Height fit only where .app-main starts at its usual ~310px (see the
   --board-u derivation above). */
@media (min-width: 1500px) {
  .board-panel {
    --board-u: clamp(150px, min(var(--board-u-fit-w), var(--board-u-fit-h)), 400px);
  }
}

/* The board column's share of .app-main changes at the existing responsive
   breakpoints (2 columns at <=1100px, 1 column at <=860px) — same
   derivation, different column width. */
@media (max-width: 1100px) {
  .board-panel {
    --board-col-w: calc((100vw - 50px) * 0.4545);
  }
}

@media (max-width: 860px) {
  .board-panel {
    --board-col-w: calc(100vw - 32px);
  }
}

.board-outer {
  width: 100%;
  display: flex;
  justify-content: center;
  /* Corrective Pass 3 reopened this from Pass 2's 4px/16px so the
     environmental framing had room to show; Pass 4 trimmed the top to
     6px. Pass 5B trims the BOTTOM too (18px -> 7px) — the Pass 5 rock
     clusters redesigned this pass are more compact and no longer need as
     much room, and the full board+legend now needs to fit one viewport
     (see final report for the measured before/after). */
  padding: 6px 0 7px 0;
  perspective: 1400px;
  overflow: visible;
  /* Sits above the decorative environmental framing added to .board-panel
     this pass (rock wall / timber beams) — those are purely behind-the-
     board dressing and must never visually cover any tile. */
  position: relative;
  z-index: 1;
}

/* Presentational-only 3D equipment layer (Production Hasher Board 3D
   Integration milestone) — sits OUTSIDE .board-iso's own 3D transform
   context on purpose (it does its own screen-space positioning from
   getBoundingClientRect(), see BoardEquipmentSceneManager), covers the
   full viewport so plot screen coordinates need no extra offset math,
   and stays well under .modal-backdrop's z-index so dialogs always draw
   over it. pointer-events: none is load-bearing — the DOM board remains
   the sole click/selection authority. */
.board-equipment-3d-canvas {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 5;
}

/* New Board Integration — the approved board/platform GLB, a SEPARATE
   canvas from the equipment layer above. It has to live INSIDE
   .board-outer (which is z-index:1 and therefore its own stacking
   context, containing both the mine artwork and .board-platform with its
   tiles): a canvas outside that context can only sit wholly above or
   wholly below the entire board, never between "artwork behind" and
   "tiles in front". Inside it, z-index:0 + DOM order (rendered right
   before .board-platform, after the artwork) puts it above the artwork
   and below the platform (z-index:1) and its tiles.
   position:absolute, not fixed: .board-outer's `perspective` makes it the
   containing block either way, so absolute is the honest declaration.
   The generous negative inset gives the mesh room to extend past the
   platform's layout box (it is drawn larger/tilted, exactly like the
   platform's own CSS transform overflows its box) — .app-main's
   overflow:hidden remains the real clip boundary. pointer-events: none
   for the same reason as the equipment canvas — the DOM board stays the
   sole click/selection authority. */
.board-platform-3d-canvas {
  position: absolute;
  inset: -220px -160px -220px -160px;
  width: calc(100% + 320px);
  height: calc(100% + 440px);
  pointer-events: none;
  z-index: 0;
}

/* Physical platform/base the board rests on — approved board presentation
   correction: the board must read as a physical object with a chunky
   dark perimeter, not a bare floating grid. Carries the tilt itself (see
   below) so the platform and the tile grid it frames tilt together as
   one cohesive object, rather than a flat 2D frame around a 3D grid. */
.board-platform {
  padding: 16px;
  /* New Board Integration — the CSS-drawn platform chrome (dark gradient
     fill, near-black border, drop shadow) is retired: the approved 6×6
     board GLB (BoardPlatform3D, drawn behind this element) is now the
     platform visual, and an opaque fill here would simply hide it. The
     box itself is unchanged — same padding, and the border stays 4px but
     transparent — so the layout box, the tile grid inside it, and the CSS
     transform below that every tile position and the equipment layer are
     measured against all stay exactly where they were. */
  background: none;
  border: 4px solid transparent;
  border-radius: 10px;
  box-shadow: none;
  /* Approved board correction: mild perspective only — a physical board
     viewed at a slight downward angle, never the previous heavy
     isometric tilt (was rotateX(52deg) rotateZ(45deg), which also
     rotated the square grid 45° into a diamond). Zero Z-rotation keeps
     the 6×6 structure reading as rows/columns, not a tactical diamond. */
  /* Board Scale Test — +20% presentation scale, appended after rotateX
     (not before): CSS composes `transform: A B` as A applied to the
     result of B, so `scale` here runs FIRST, in the flat pre-rotation
     plane, and rotateX(28deg) — itself completely unchanged — then tilts
     the already-enlarged flat board. That order scales every resulting
     axis (including the depth rotateX introduces) by the same 1.2x,
     giving a uniformly bigger board with the exact same proportions and
     projection angle. Reversing the order would scale X/Y only after the
     tilt, leaving the perspective-derived depth unscaled and visibly
     distorting the tilt. Pure paint-time transform — doesn't change this
     element's LAYOUT box, so .board-outer/.app-main's own sizing, and
     therefore Crew/Operations/header position, are completely
     unaffected; .board-outer's existing `overflow: visible` (plus
     .app-main's overflow:hidden as the real clip boundary, same
     technique the mine artwork already relies on) lets the larger
     painted board extend past its own layout box without disturbing
     anything else. Default `transform-origin: 50% 50%` (unset, already
     center) grows the board from its own current visual center, per
     spec. The 3D Miner (BoardEquipment3D.tsx, protected/untouched) reads
     each tile's live `getBoundingClientRect()` for both position and
     scale, so it tracks this automatically with no code change.

     Board Position Polish — translateY(-25px) added as the OUTERMOST
     (leftmost) function, not appended at the end. `transform: A B C`
     applies C first, then B, then A, to the local vertex — rotateX and
     scale here are UNCHANGED, both value and relative order (scale still
     runs first against the flat plane, rotateX still tilts the result,
     exactly as above), so this is purely T x (Rx x S), the standard
     "translate the already-rotated-and-scaled rigid shape" composition —
     a pure post-shift of the finished tilted board, not a shift of its
     flat pre-transform geometry. Putting translateY at the END instead
     (`rotateX(...) scale(...) translateY(...)`) would shift the FLAT
     pre-rotation vertex first, so the following scale(1.15) would also
     scale that shift (~-28.75px) before the tilt bent it further off
     -Y — wrong, and exactly the distortion this ordering avoids.
     Pixel units (not %) keep the shift's magnitude independent of the
     element's own (now-larger) box. Moves only .board-platform's own
     descendants (frame + 6x6 tile grid) — .board-mine-environment /
     -backdrop / -infill-top / -infill-right are siblings inside
     .board-outer, not descendants of .board-platform, so the mine
     artwork's position is completely unaffected by this. */
  transform: translateY(-25px) rotateX(28deg) scale(1.15);
  /* New Board Integration — was `preserve-3d`, now `flat`. Its only
     consumers were the .tile-label counter-rotation and the two brass-bolt
     pseudo-elements, both retired, so nothing on the board lives in 3D any
     more; the platform's own matrix and .board-outer's perspective (which
     is what the new board mesh and the equipment layer both reproduce) are
     unaffected. Under `preserve-3d` Chromium's point hit-test dropped every
     tile on the near-viewer half of the tilted plane (rows 3-5): measured
     at unmodified HEAD, elementFromPoint / real mouse presses at those 18
     tile centres returned .board-iso instead of the tile button. `flat`
     restores 36/36. */
  transform-style: flat;
}

.board-iso {
  /* Tile size is the smaller of a width-based and a height-based
     preferred value, so it only shrinks below the 108px ceiling on
     screens that are short, wide, or both — see chat (height-responsive
     board sizing pass). The height term is a linear fit of this grid's
     own rendered (post rotateX) bounding-box height as a function of
     tile size, re-measured directly in-browser after the mild-perspective
     correction above (previously calibrated against the old 52°/45°
     tilt, which compressed height far more aggressively). */
  /* Corrective Pass 3 pulled the ceiling 126px -> 110px (rotateX(28deg)
     compounds tile size into vertical footprint fast). Pass 5B trims it
     once more, 110px -> 98px — measured live: the board+legend extended
     below the viewport at 1920x1080/1600x900 once the top chrome was
     accounted for, and this is the single largest contributor to
     reclaiming that space (see final report for the full before/after
     measurement). Still a modest reduction (~11%), not the aggressive
     108->126 reversal Pass 3 already rejected. The vw term is also
     trimmed 6.6vw -> 5vw: at 1920px that's still above the 98px ceiling
     (unaffected — 1920's board size is governed by the ceiling either
     way), but at 1600px wide it's what actually determines tile size,
     and 1600×900 needed the extra help the flat ceiling alone didn't
     provide — re-measured live at each step (5.9vw wasn't enough on its
     own; settled on 5vw once the Day-timer widget compression below was
     also in place, see final report for the full measurement chain). */
  /* New Board Integration — the grid's tracks are no longer a uniform
     `repeat(6, tile-size)` with a 5px gap: Board.tsx sets them inline from
     the board model's own measured per-column/per-row cell extents and
     grooves (three/boardModelGrid.ts), each `calc(var(--board-u) * <model
     units>)`, so the plots line up with the drawn cells. The size clamp
     that used to live here as --board-tile-size is now --board-u on
     .board-panel (px per model unit). The grooves are real grid tracks
     now, hence no `gap`. */
  display: grid;
  gap: 0;
}

.tile {
  position: relative;
  border: 1px solid rgba(0, 0, 0, 0.4);
  border-radius: 3px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 15px; /* scaled up to match the ~2x larger tiles */
  font-weight: 700;
  color: rgba(0, 0, 0, 0.65);
  transition: filter 0.1s ease, box-shadow 0.15s ease;
}

/* Approved terrain textures — background-color is the graceful fallback
   if the image ever fails to load (same layered-fallback contract as
   every other art resolver in this codebase), never removed just
   because the image is now present. */
.tile-rich {
  background-color: var(--rich);
  background-image: url("/assets/tiles/tile_rich.png");
  background-size: cover;
  background-position: center;
}

.tile-poor {
  background-color: var(--poor);
  background-image: url("/assets/tiles/tile_poor.png");
  background-size: cover;
  background-position: center;
}

.tile:hover {
  filter: brightness(1.15);
}

.tile-has-miner,
.tile-has-cooling {
  color: #12100c;
}

.tile-starting-miner {
  outline: 2px solid var(--amber);
  outline-offset: -2px;
}

.tile-cooled-glow {
  box-shadow: 0 0 0 2px var(--cooled-glow), 0 0 14px 4px var(--cooled-glow);
}

.tile-selected-source {
  box-shadow: 0 0 0 3px var(--amber);
}

.board-legend {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--text-dim);
  position: relative;
  z-index: 1;
  /* New Board Integration — the legend sits BELOW the complete physical
     frame with a clear gap, not under its near rim. The frame's near edge
     (plus the slab's visible thickness) paints below .board-outer's layout
     box by 0.391·u − 80.5px (measured live, real GL output, u=240..360,
     ±3.5px); this margin lifts the legend to that edge + 14px clear, less
     the panel's own 6px flex gap. max(0) keeps it sane at very small u. */
  margin-top: max(0px, calc(var(--board-u) * 0.391 - 72.5px));
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
}

.legend-swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  display: inline-block;
}

.legend-rich {
  background: var(--rich);
}

.legend-poor {
  background: var(--poor);
}

.legend-cooled {
  background: var(--panel);
  box-shadow: 0 0 0 2px var(--cooled-glow), 0 0 8px 2px var(--cooled-glow);
}

/* ---------- Shop ---------- */

.shop-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 8px;
  margin-bottom: 10px;
}

.shop-item {
  background: #1e232a;
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  padding: 8px;
  text-align: left;
  color: var(--text);
  cursor: pointer;
  font-size: 12px;
}

.shop-item:hover {
  border-color: var(--amber);
}

.shop-item-unaffordable {
  opacity: 0.55;
}

.shop-item-title {
  font-weight: 700;
  margin-bottom: 2px;
}

.shop-item-desc {
  color: var(--text-dim);
  font-size: 11px;
  margin-bottom: 4px;
}

.shop-item-cost {
  color: var(--amber);
  font-weight: 600;
}

/* ---------- Shop group / unlock panel (Shop Unlock Economy — framework milestone) ----------
   Generalized from the old Shop-2-specific .shop2-* classes: every shop
   beyond Shop 1 renders this same panel, driven entirely by
   ShopGroupViewModel.unlock — nothing here is Shop-2-specific. */

.shop-group {
  margin-bottom: 16px;
}

.shop-group:last-child {
  margin-bottom: 0;
}

.shop-unlock-panel {
  margin-bottom: 10px;
}

.shop-unlock-status-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  font-size: 12px;
  color: var(--text-dim);
}

.shop-unlock-badge {
  font-size: 10px;
  padding: 2px 8px;
  border-radius: 999px;
  text-transform: none;
  letter-spacing: 0;
}

.shop-unlock-locked {
  background: #3a1f1f;
  color: #ffb3b3;
}

.shop-unlock-eligible {
  background: #3a3320;
  color: var(--amber);
}

.shop-unlock-unlocked {
  background: #1f3a24;
  color: var(--ok);
}

.shop-unlock-progress {
  margin-bottom: 10px;
}

.shop-unlock-progress-track {
  background: #101317;
  border-radius: 999px;
  height: 8px;
  overflow: hidden;
}

.shop-unlock-progress-fill {
  background: var(--amber);
  height: 100%;
}

.shop-unlock-progress-text {
  font-size: 11px;
  color: var(--text-dim);
  margin-top: 4px;
}

.shop-unlock-cost-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  font-size: 12px;
  color: var(--text-dim);
}

.shop-unlock-cost {
  color: var(--amber);
  font-weight: 600;
}

.shop-unlock-hint {
  font-size: 12px;
  color: var(--text-dim);
  margin-top: 6px;
}

/* An "advanced" tier level whose shop is unlocked but which hasn't had its
   own unlock paid yet — deliberately styled distinctly from a normal
   .shop-item so it never reads as a (disabled) buy button. */
.shop-item-locked {
  cursor: default;
  border-style: dashed;
  opacity: 0.85;
}

.shop-item-locked-badge {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--danger);
  margin-bottom: 4px;
}

.shop-item-unlock-cost {
  color: var(--amber);
  font-weight: 600;
  font-size: 11px;
  margin-bottom: 6px;
}

.cancel-selection {
  background: none;
  border: 1px solid var(--panel-border);
  color: var(--text-dim);
  border-radius: 6px;
  padding: 6px 10px;
  cursor: pointer;
  font-size: 12px;
}

/* ---------- Inventory ---------- */

.inventory-tag {
  margin-left: auto;
  font-size: 10px;
  background: #101317;
  border-radius: 999px;
  padding: 2px 8px;
  color: var(--text-dim);
}

.inventory-tag-cooled {
  color: var(--cooled-glow);
}

.inventory-hint {
  font-size: 11px;
  color: var(--text-dim);
}

/* ---------- Owned Equipment grouped summary (UI/UX polish: replaces the old long scrolling per-plot list) ---------- */

.equipment-groups-list {
  list-style: none;
  margin: 0 0 10px 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.equipment-group-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: linear-gradient(180deg, #1f242b 0%, #191d23 100%);
  border: 1px solid var(--panel-border);
  border-left: 3px solid var(--card-accent, var(--panel-border));
  border-radius: 8px;
  padding: 10px 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.equipment-group-row:hover {
  border-color: var(--card-accent, var(--amber));
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  transform: translateY(-1px);
}

/* Same --card-accent hook the equipment browser cards use below — kind-based
   for now, ready to be overridden per-item once equipment has a rarity tier. */
.equipment-group-row-miner {
  --card-accent: var(--rich);
}

.equipment-group-row-cooling {
  --card-accent: var(--poor);
}

.equipment-group-label {
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.01em;
  margin-bottom: 3px;
}

.equipment-group-stat {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.equipment-group-stat-total {
  color: var(--amber);
  font-weight: 600;
}

/* ---------- Equipment browser modal (filter/sort/sell every owned item, placed or not) ---------- */

/* Two classes deliberately, not one — .modal-dialog's own base max-width
   rule (defined later in this file, in the Sale confirmation modal section)
   has equal specificity, and equal-specificity ties resolve by source
   order, not by which rule "sounds more specific." A single-class
   .modal-dialog-wide selector here would silently lose that tie no matter
   which order the two class names appear in the JSX. */
.modal-dialog.modal-dialog-wide {
  max-width: 660px;
}

.equipment-browser-filters {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.equipment-browser-filters .btn {
  border-radius: 999px;
  padding: 7px 16px;
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.02em;
  transition: border-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
}

.equipment-browser-filters .btn:hover:not(:disabled) {
  transform: translateY(-1px);
}

.equipment-browser-pin {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  color: var(--text-dim);
  margin: 0 0 16px 0;
  padding: 6px 8px 6px 14px;
  background: #1a1e24;
  border: 1px solid var(--panel-border);
  border-radius: 999px;
}

.equipment-browser-list {
  list-style: none;
  margin: 0 0 14px 0;
  padding: 2px 6px 6px 2px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-height: 480px;
  overflow-y: auto;
}

.equipment-browser-item {
  display: flex;
  align-items: center;
  gap: 18px;
  background: linear-gradient(165deg, #21262d 0%, #191d23 65%, #16191e 100%);
  border: 1px solid var(--panel-border);
  border-left: 4px solid var(--card-accent, var(--panel-border));
  border-radius: 12px;
  padding: 16px 18px;
  font-size: 13px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}

.equipment-browser-item:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.4);
  border-color: var(--card-accent, var(--amber));
}

/* The single hook a future per-item rarity tier would take over instead of
   these kind-based defaults — see the matching comment in
   EquipmentBrowserModal.tsx. Set once here, read by the border, the level
   badge, and the hover border-color above via var(--card-accent, ...). */
.equipment-browser-item-miner {
  --card-accent: var(--rich);
}

.equipment-browser-item-cooling {
  --card-accent: var(--poor);
}

.equipment-browser-art-wrap {
  flex: 0 0 auto;
  padding: 2px;
}

/* Fixed-size placeholder — reserved for a future 3D render of the actual
   item, not a real asset yet. Deliberately generic (not miner/cooling-
   specific artwork) so it reads as "space reserved," not as a finished
   icon. */
.equipment-browser-art {
  width: 68px;
  height: 68px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed var(--panel-border);
  border-radius: 12px;
  background: radial-gradient(circle at 50% 38%, #262b32 0%, #171a1f 100%);
  color: var(--text-dim);
  font-size: 28px;
  opacity: 0.7;
}

.equipment-browser-item-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.equipment-browser-item-header {
  display: flex;
  align-items: center;
  gap: 9px;
}

.equipment-browser-item-name {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.equipment-browser-level-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 22px;
  padding: 0 8px;
  border-radius: 999px;
  background: var(--card-accent, var(--panel-border));
  color: #14110c;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.03em;
}

.equipment-browser-item-tags {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.equipment-browser-item-tags .inventory-tag {
  margin-left: 0;
  font-size: 10.5px;
  padding: 3px 9px;
}

.equipment-browser-item-prices {
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
}

.equipment-browser-item-actions {
  flex: 0 0 auto;
  align-self: center;
}

.equipment-browser-sell-btn {
  min-width: 128px;
  font-weight: 600;
  white-space: nowrap;
}

/* ---------- Equipment inventory / placement / management (FTUE milestone; miner + cooling share one "placeable asset" UI) ---------- */

/* GROUP THE BOARD PLACEMENT PICKER BY LEVEL milestone — one visual card per
   owned level, matching the Supply Depot's own product-card presentation
   (reuses .depot-card-art-image/-layers/-glyph via the shared ProductArt
   component in EquipmentArt.tsx — same approved art, same rarity colour). */
.placement-picker-dialog {
  max-width: min(94vw, 820px);
}

.placement-cards {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  max-height: min(60vh, 520px);
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(150px, 15vw, 220px), 1fr));
  gap: clamp(8px, 1vw, 16px);
}

.placement-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.1), 0 6px 16px rgba(0, 0, 0, 0.35);
}

.placement-card-art {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(80px, 14vh, 140px);
  background:
    radial-gradient(ellipse 70% 65% at 50% 55%, rgba(240, 169, 78, 0.16) 0%, transparent 72%),
    linear-gradient(180deg, #2a2118 0%, #15110d 100%);
  border-bottom: 1px solid rgba(201, 151, 79, 0.25);
}

.placement-card[data-kind="cooling"] .placement-card-art {
  background:
    radial-gradient(ellipse 70% 65% at 50% 55%, rgba(127, 212, 255, 0.16) 0%, transparent 72%),
    linear-gradient(180deg, #1c2329 0%, #10151a 100%);
}

.placement-card-level {
  position: absolute;
  top: 6px;
  left: 6px;
  padding: 1px 10px;
  font-family: var(--font-control);
  font-size: clamp(12px, 1vw, 16px);
  letter-spacing: 0.06em;
  color: #1c1206;
  background: linear-gradient(180deg, var(--gr-brass-bright), var(--gr-brass));
  border-radius: 999px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}

.placement-card-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 10px 10px;
}

.placement-card-title {
  margin: 0;
  font-family: var(--font-control);
  font-size: clamp(13px, 1vw, 16px);
  letter-spacing: 0.04em;
  color: var(--gr-brass-bright);
}

.placement-card-detail {
  margin: 0;
  font-size: 11px;
  color: var(--text-dim);
}

.placement-card-count {
  font-family: var(--font-control);
  font-size: clamp(12px, 0.95vw, 15px);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: color-mix(in srgb, var(--ok) 70%, var(--gr-brass-bright));
  font-variant-numeric: tabular-nums;
}

.placement-card-place {
  margin-top: 2px;
  width: 100%;
  font-size: 12px;
}

.equipment-management-actions {
  flex-wrap: wrap;
}

.purchase-confirm-detail-line {
  color: var(--text-dim);
  text-align: right;
}

.empty-hint {
  color: var(--text-dim);
  font-size: 12px;
}

/* ---------- Buttons (shared) ---------- */

.btn {
  background: #1e232a;
  border: 1px solid var(--panel-border);
  color: var(--text);
  border-radius: 6px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 13px;
}

.btn:hover:not(:disabled) {
  border-color: var(--amber);
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-primary {
  background: var(--amber);
  border-color: var(--amber);
  color: #201400;
  font-weight: 700;
}

.btn-danger {
  border-color: var(--danger);
  color: #ffb3b3;
}

.btn-speed-active {
  border-color: var(--amber);
  color: var(--amber);
}

/* ---------- Sell / Sim / Market panels ---------- */

.sell-controls-row,
.sim-controls-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 8px;
}

.sell-controls-row input {
  flex: 1;
  min-width: 0;
  background: #101317;
  border: 1px solid var(--panel-border);
  color: var(--text);
  border-radius: 6px;
  padding: 8px 10px;
  font-size: 13px;
}

.sim-speed-label {
  font-size: 12px;
  color: var(--text-dim);
}

.sim-speed-legend {
  list-style: none;
  margin: 0 0 8px 0;
  padding: 0;
  font-size: 12px;
  color: var(--text-dim);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.error-text {
  color: #ffb3b3;
  font-size: 12px;
  margin: 4px 0 0 0;
}

.sparkline {
  width: 100%;
  height: 40px;
  color: var(--amber);
  margin-bottom: 8px;
}

.market-bounds {
  font-size: 11px;
  color: var(--text-dim);
  margin-bottom: 8px;
}

/* ---------- Final score ---------- */

.final-score-locked {
  border-color: var(--amber);
}

.final-score-value {
  font-size: 28px;
  font-weight: 800;
  color: var(--amber);
  margin: 0 0 4px 0;
}

.final-score-tier {
  margin: 0 0 8px 0;
}

.final-score-detail {
  font-size: 12px;
  color: var(--text-dim);
  margin: 0 0 8px 0;
}

.final-score-note {
  font-size: 11px;
  color: var(--text-dim);
  margin: 0;
}

/* ---------- Hasher inventory / Crew builder / Dev controls ---------- */

.btn-small {
  padding: 4px 8px;
  font-size: 12px;
}

.hasher-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  /* Explicit, not just relying on the "auto" default: each row's height
     must come from its content (the tallest card in that row), never a
     fixed value, so a card with a crew-position badge or wrapped button
     text never gets clipped by its own grid row. */
  grid-auto-rows: auto;
  align-items: start;
  gap: 10px;
}

.hasher-card {
  /* No height/max-height here, deliberately — the card must grow to fit
     all six gear slots plus rarity/base/effective stats/crew-position
     buttons, on every card, at every width. align-items: start on the
     grid above (rather than the grid default of stretch) means a card is
     always exactly as tall as its own content, never stretched to match
     a taller sibling in the same row and never clipped either way. */
  background: var(--coal);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 8px 10px;
  font-size: 12px;
}

.hasher-card-in-crew {
  border-color: var(--ok);
}

.hasher-card-title {
  font-weight: 600;
  text-transform: capitalize;
  margin-bottom: 4px;
}

.hasher-card-stats {
  color: var(--text-dim);
  margin-bottom: 4px;
}

.hasher-card-effective-stats {
  color: var(--ok);
}

.hasher-card-badge {
  display: inline-block;
  font-size: 11px;
  color: var(--amber);
  margin-bottom: 4px;
}

.hasher-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 6px;
}

/* ---------- Card frame (CardFrame.tsx — the reusable Frame/Portrait/
   Overlay layering docs/ASSET_BIBLE.md Section 12 specifies) ----------
   Every percentage below is docs/ASSET_BIBLE.md Section 12.4's own
   measured-against-the-real-files table (that section's own stated
   confidence level: "read by visual inspection... a strong first pass"
   — not independently re-verified here, since no approved frame PNG
   exists in this repo yet to re-measure against; see the final report
   for what remains untested as a result). Deliberately no fixed
   max-width: unlike GearCard (which owns its own small fixed-size
   presentation contexts), every existing CardFrame caller
   (.hasher-card-grid's auto-fill grid cells, .hasher-reveal-card-front's
   own sized wrapper) already reserves its own correctly-proportioned slot
   — Section 22.4's "scale by width, the layout reserves a
   correctly-proportioned slot" rule, so CardFrame fills whatever width
   its caller gives it and derives height from the locked 2:3 ratio. */

.card-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 1024 / 1536;
  border-radius: 8px;
  overflow: hidden;
  background: var(--panel);
}

/* Anchored near the top (roughly the header-medallion band, 4-14% per
   Section 12.4) rather than dead-centered on the full card — the
   portrait window (13-75%) shows its OWN "Portrait pending" fallback
   independently and simultaneously whenever both are missing (today's
   actual state, with neither asset present), and centering both
   pieces of text on the same full-card box put them on top of each
   other at smaller card sizes (confirmed in-browser at the
   .hasher-card-grid's own ~230px cell width). */
.card-frame-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding: 6% 8% 0;
  gap: 4px;
  text-align: center;
  background: linear-gradient(165deg, #21262d 0%, #14171b 100%);
  border: 1px solid var(--panel-border);
}

.card-frame-placeholder-label {
  font-weight: 700;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text);
}

.card-frame-placeholder-note {
  font-size: 10px;
  color: var(--text-dim);
}

/* Bottom-most once loaded, per Section 12.3's own explicit "the frame
   is the bottom-most visual layer" — matches this component's actual
   Frame -> Portrait -> Overlay DOM order (Section 12.2) with no z-index
   override needed; natural paint order already gives the right stack. */
.card-frame-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Portrait safe area — 9-91% width x 13-75% height (Section 12.4 / 22.3). */
.card-frame-portrait-window {
  position: absolute;
  left: 9%;
  right: 9%;
  top: 13%;
  height: 62%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* `contain`, not `cover` — the window's own aspect ratio (~0.88, per the
   9-91%/13-75% safe area) is wider than the source portraits' native
   ~0.667 (2:3) canvas, so `cover` was forced to scale up until the
   window's width was filled and then crop the resulting ~30% height
   overflow evenly off the top AND bottom — cutting into headroom and
   feet/props on any portrait whose subject already fills most of its
   own canvas (confirmed on Whale). `contain` shows the whole image at
   the largest scale that fits with zero cropping, filling one full
   window dimension; `object-position: 50% top` anchors that scaled
   image to the window's top edge (near the frame's own ornamentation)
   so any leftover space collects at the bottom, letting feet/props
   extend naturally toward the stats footer rather than the crop being
   split (and centered) top and bottom. */
.card-frame-portrait {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: 50% top;
}

.card-frame-portrait-empty {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 4px;
}

.card-frame-portrait-empty-label {
  font-size: 11px;
  color: var(--text-dim);
}

/* Overlay safe area's footer panel — 15-85% width x 77-91% height
   (Section 12.4 / 22.3). Title/stats/traits/actions stack in normal
   document flow inside this one region rather than four separately
   positioned sub-bands — Section 22.3's own title/stats split (top ~25%
   vs remaining ~75% of the footer panel) is a placement RECOMMENDATION,
   not a frame-art-demarcated boundary, and this component's actual JSX
   already renders them as one combined block in exactly that order. */
/* Red & Green approved Hasher card (see WebsiteCardFace.tsx / assets/websiteCardArt.ts). The card prints CLASS · RARITY and the headings
   HP / INT / SPD on an empty nameplate; the Hasher's saved name sits in the blank strip above the printed label and each value to the
   right of its heading. Positions are the ones the website measured for these exact files; sizes use cqw so they scale with the card. */
.website-card-face {
  position: relative;
  width: 100%;
  height: 100%;
  aspect-ratio: 2 / 3;
  container-type: inline-size;
  border-radius: 5.2% / 3.5%;
  overflow: hidden;
}
.website-card-face-art {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.website-card-face-data {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.website-card-face-name {
  position: absolute;
  top: 46.9%;
  left: 15%;
  right: 15%;
  height: 2.9%;
  display: block;
  overflow: hidden;
  color: var(--card-name-color, #2a160c);
  font: 900 3.7cqw/4.35cqw Georgia, serif;
  letter-spacing: 0;
  text-align: center;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}
.website-card-face-stats {
  position: absolute;
  inset: 0;
  font: 900 3.8cqw/1 Georgia, serif;
  font-variant-numeric: lining-nums;
}
.website-card-face-stats span {
  position: absolute;
  top: 53.4%;
  height: 3.3%;
  display: flex;
  align-items: center;
  overflow: hidden;
  color: #2a160c;
  white-space: nowrap;
}
.website-card-face-stats .stat-hp { left: 32.6%; width: 10.4%; }
.website-card-face-stats .stat-int { left: 51.2%; width: 9.4%; }
.website-card-face-stats .stat-spd { left: 68.6%; width: 11%; }
.website-card-face[data-layout="lowered"] .website-card-face-stats span { top: 55.3%; }
/* Three Gear cards print the cartouche higher (website CARD_LAYOUT_BY_ART). Their printed label also fills the strip a name would use,
   so no name is drawn on the card; the surrounding screen shows it. */
.website-card-face[data-layout="raised-gloves"] .website-card-face-stats span { top: 51.6%; }
.website-card-face[data-layout="raised-pants"] .website-card-face-stats span { top: 52%; margin-left: 1%; }
.website-card-face[data-layout^="raised"] .website-card-face-name { display: none; }
/* Gear: the approved card replaces the game's frame + master art composition entirely. */
.gear-card.gear-card-approved {
  overflow: visible;
  background: transparent;
  box-shadow: none;
  border-radius: 0;
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.45));
}
.gear-card-approved .website-card-face {
  position: absolute;
  inset: 0;
}
/* Gear icon as the approved card shrunk to the icon's box: the card carries its own coloured border, so the icon's border, glow and
   panel are dropped. */
.gear-icon.gear-icon-approved[data-rarity] {
  overflow: visible;
  border: 0;
  background: transparent;
  box-shadow: none;
}
.gear-icon-approved .gear-icon-card-art {
  width: auto;
  height: 100%;
  max-width: 100%;
  aspect-ratio: 2 / 3;
  object-fit: contain;
  border-radius: 12% / 8%;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
}
.card-frame[data-art="website"] {
  overflow: hidden;
  background: transparent;
}
.card-frame[data-art="website"] .card-frame-overlay {
  background: rgba(10, 8, 6, 0.78);
  border-radius: 4px;
  padding: 2px 4px;
}

.card-frame-overlay {
  position: absolute;
  left: 15%;
  right: 15%;
  top: 77%;
  height: 14%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 2px;
  overflow: hidden;
  text-align: center;
  color: var(--text);
}

.card-frame-title {
  font-weight: 700;
  font-size: 12px;
  text-transform: capitalize;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.card-frame-stats {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2px 8px;
  font-size: 10px;
}

.card-frame-stat-label {
  color: var(--text-dim);
}

.card-frame-stat-value {
  color: var(--ok);
  font-weight: 600;
}

.card-frame-traits {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 3px;
}

.card-frame-trait {
  font-size: 9px;
  padding: 1px 5px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-dim);
}

.card-frame-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
  margin-top: 2px;
}

.prototype-disclaimer {
  font-size: 11px;
  color: var(--amber);
  border: 1px dashed var(--amber);
  border-radius: var(--radius);
  padding: 4px 8px;
  margin-bottom: 10px;
}

.gear-slot-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: auto;
  gap: 6px;
  margin-top: 8px;
}

.gear-slot {
  /* min-height, not height: every slot should be a consistent size
     (clicking one must never reflow the layout — that reflow was the
     leading suspect for an earlier "stats don't update" report turning
     out to be misclicks on a shifted button) but a fixed `height` can
     clip its own content under larger fonts/zoom/OS text scaling.
     min-height gets the consistency without the clipping risk. All slot
     detail lives in the GearSlotPicker modal, not grown inline here. */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: 52px;
  width: 100%;
  background: var(--coal);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 6px;
  font: inherit;
  font-size: 11px;
  text-align: center;
  cursor: pointer;
  appearance: none;
}

.gear-slot:hover {
  border-color: var(--text-dim);
}

.gear-slot-filled {
  border-color: var(--ok);
}

.gear-slot-name {
  font-weight: 600;
  color: var(--text-dim);
  white-space: nowrap;
}

.gear-slot-item {
  color: var(--ok);
  text-transform: capitalize;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.gear-slot-empty .gear-slot-item {
  color: var(--text-dim);
}

.gear-picker-current,
.gear-picker-list-title {
  font-size: 13px;
  margin: 8px 0;
}

.gear-picker-list {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  max-height: 320px;
  overflow-y: auto;
  overflow-x: hidden;
}

.gear-picker-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  padding: 8px;
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  margin-bottom: 6px;
  font-size: 12px;
}

.gear-picker-item-summary {
  flex: 1;
  /* Forces the icon+text summary onto its own full-width line whenever the
     row wraps (narrow modal + a multi-stat compare string, e.g. a
     Legendary item with three stat deltas) — without this, `flex: 1` lets
     the summary shrink to share a line with the compare text/button
     instead, which produced a cramped, overlapping-looking row at 390px. */
  flex-basis: 100%;
  /* Flex items default to `min-width: auto`, which refuses to shrink
     below intrinsic content size — the actual cause of the horizontal
     scrollbar was `.gear-icon-art` having no size at all (fixed above),
     but this keeps a long item-summary string honest too, consistent
     with "no horizontal scrollbar, ever" for this list. */
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  text-transform: capitalize;
}

.gear-picker-item-status {
  font-size: 11px;
  color: var(--text-dim);
}

.gear-picker-item-compare {
  font-size: 11px;
  color: var(--ok);
  /* Equip Modal Fix pass: was `white-space: nowrap`, which forced a
     multi-stat compare string (e.g. a Legendary item's three "+N STAT
     (empty slot)" fragments joined by " · ") onto one unbreakable line —
     harmless at the modal's normal 380px width, but at the 320px/288px
     narrow-viewport modal width the line genuinely didn't fit and, since
     the list's `overflow-x: hidden` clips rather than scrolls, part of
     the text was silently invisible rather than merely off-screen. Normal
     wrapping lets it break at a word/space boundary instead. */
  white-space: normal;
}

.crew-position-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 14px;
}

.crew-position-slot {
  background: var(--coal);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 8px 10px;
  font-size: 12px;
  text-align: center;
}

.crew-position-title {
  font-weight: 600;
  margin-bottom: 4px;
}

.crew-position-empty {
  color: var(--text-dim);
}

.crew-position-hasher-name {
  text-transform: capitalize;
  color: var(--text-dim);
  margin-bottom: 6px;
}

.crew-position-breakdown {
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: left;
}

.crew-position-stat-breakdown-label {
  font-weight: 600;
  font-size: 11px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.crew-position-stat-breakdown-values {
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-size: 11px;
}

.crew-position-stat-breakdown-values strong {
  color: var(--ok);
  font-weight: 600;
}

.crew-stats-live {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 12px;
}

.crew-stat-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 8px;
  align-items: baseline;
  font-size: 13px;
}

.crew-stat-override-tag {
  color: var(--amber);
  font-size: 11px;
}

.crew-stat-value {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: color 0.2s ease;
}

.stat-increase {
  color: var(--ok);
}

.stat-decrease {
  color: var(--danger);
}

.crew-stat-effect {
  color: var(--text-dim);
  font-size: 11px;
  text-align: right;
}

.crew-locked-note {
  color: var(--ok);
  font-size: 12px;
}

.crew-setup-phase {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.dev-controls-panel h3 {
  font-size: 13px;
  margin: 10px 0 6px 0;
  color: var(--text-dim);
}

.dev-daily-status {
  font-size: 12px;
  margin: 0 0 8px 0;
}

.dev-override-row {
  display: grid;
  grid-template-columns: 60px 90px auto auto;
  gap: 6px;
  align-items: center;
  margin-bottom: 6px;
}

.dev-override-row input {
  width: 100%;
  background: var(--coal);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  color: var(--text);
  padding: 4px 6px;
}

/* ---------- Daily Operating Time widget ---------- */

.daily-operating-widget {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin-bottom: 14px;
}

.daily-operating-label {
  font-size: 12px;
  color: var(--text-dim);
  margin-bottom: 4px;
}

.daily-operating-value {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 14px;
  margin-bottom: 6px;
}

.daily-operating-countdown {
  font-size: 26px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  margin-bottom: 6px;
  transition: color 0.3s ease;
}

.daily-operating-green .daily-operating-countdown {
  color: var(--ok);
}

.daily-operating-yellow .daily-operating-countdown {
  color: var(--amber);
}

.daily-operating-red .daily-operating-countdown {
  color: var(--danger);
}

.daily-operating-bar-track {
  width: 100%;
  height: 10px;
  background: var(--coal);
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: 6px;
}

.daily-operating-bar-fill {
  height: 100%;
  border-radius: 999px;
  transition:
    width 0.3s linear,
    background-color 0.3s ease;
}

.daily-operating-bar-green {
  background: var(--ok);
}

.daily-operating-bar-yellow {
  background: var(--amber);
}

.daily-operating-bar-red {
  background: var(--danger);
}

.daily-operating-budget-note {
  font-size: 11px;
  color: var(--text-dim);
}

.daily-operating-reset-note {
  font-size: 11px;
  color: var(--text-dim);
  margin-top: 4px;
}

.daily-operating-exhausted {
  margin-bottom: 6px;
}

.daily-operating-exhausted-title {
  font-size: 18px;
  font-weight: 700;
  color: var(--danger);
}

.daily-operating-exhausted-subtitle {
  font-size: 12px;
  color: var(--text-dim);
  margin-top: 2px;
}

.daily-operating-delta {
  font-size: 13px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 999px;
  animation: daily-operating-delta-pop 2.5s ease-out forwards;
}

.daily-operating-delta-up {
  color: var(--ok);
  background: rgba(111, 191, 115, 0.15);
}

.daily-operating-delta-down {
  color: var(--danger);
  background: rgba(209, 91, 91, 0.15);
}

@keyframes daily-operating-delta-pop {
  0% {
    opacity: 0;
    transform: translateY(-4px) scale(1.15);
  }
  15% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  75% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

/* ---------- Mining indicator (informational — not spendable) ---------- */

.mining-indicator {
  background: var(--coal);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 8px 10px;
  margin-bottom: 10px;
}

.mining-indicator-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  font-size: 13px;
  padding: 2px 0;
}

.mining-indicator-label {
  color: var(--text-dim);
}

.mining-indicator-value {
  font-variant-numeric: tabular-nums;
  color: var(--amber);
}

.mining-indicator-note {
  margin: 6px 0 0 0;
  font-size: 11px;
  color: var(--text-dim);
}

/* ---------- Sell BTC preview ---------- */

.sell-preview {
  margin: 4px 0 8px 0;
}

.sell-preview-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 3px 0;
  font-size: 13px;
  border-bottom: 1px solid var(--panel-border);
}

.sell-preview-row:last-child {
  border-bottom: none;
}

.sell-preview-row dt {
  color: var(--text-dim);
}

.sell-preview-row dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.sell-preview-final dd {
  font-size: 16px;
  font-weight: 700;
  color: var(--ok);
}

/* LIVE FIVE-SECOND AVAILABLE-BTC SETTLEMENT DISPLAY milestone: a brief
   green/gold flash on the "Available BTC" figure exactly when it changes
   (a real settlement boundary, not merely a re-render — see
   SellControls.tsx's btcFlash effect). prefers-reduced-motion disables the
   animation entirely below, matching this codebase's existing convention
   (the value itself still updates instantly either way — only the
   animated transition is motion). */
.value-flash {
  animation: value-flash-pop 500ms ease-out;
}

@keyframes value-flash-pop {
  0% {
    color: #c9ffd6;
    text-shadow: 0 0 10px rgba(130, 255, 170, 0.85), 0 0 2px rgba(240, 200, 120, 0.6);
  }
  100% {
    color: inherit;
    text-shadow: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .value-flash {
    animation: none;
  }
}

/* Pass 2 — "You'd receive" is the one figure a player scans for before
   selling; scoped to .sell-panel only so the shared .sell-preview-final
   rule above (still used by other contexts) is untouched. */
.sell-panel .sell-preview-final {
  border-top: 1px solid var(--gr-brass-dim);
  padding-top: 6px;
  margin-top: 2px;
}
.sell-panel .sell-preview-final dt {
  color: var(--gr-brass);
  font-weight: 700;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.05em;
}
.sell-panel .sell-preview-final dd {
  font-size: 22px;
  color: var(--gr-brass);
  /* Pass 4 — a touch more presence on the one figure a player scans for. */
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
}

/* ---------- Trading Account (Design Bible Section 46) ---------- */

.trading-account-panel {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.trading-account-values,
.trading-summary-values {
  margin-top: 4px;
}

.trading-account-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 3px 0;
  font-size: 13px;
  border-bottom: 1px solid var(--panel-border);
}

.trading-account-row:last-child {
  border-bottom: none;
}

.trading-account-row dt {
  color: var(--text-dim);
}

.trading-account-row dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.trading-account-row-final dd {
  font-size: 16px;
  font-weight: 700;
  color: var(--ok);
}

.trading-profit-negative {
  color: var(--danger);
}

.trading-account-locked-note {
  margin: 8px 0 0 0;
  font-size: 12px;
  color: var(--text-dim);
}

/* ---------- Trading Terminal (HASHER MODE LEVERAGED BTC TRADING MILESTONE 1) ----------
   Reuses --danger/--ok verbatim for SHORT/LONG (already Red & Green's own
   confirmed accent colours — never a new neon/cyberpunk hue) and the same
   aged-brass/dark-timber/steel tokens the rest of Digital Gold Rush uses,
   so the terminal reads as an exchange console that still belongs to this
   world, not a generic broker clone. */

.trading-profit-positive {
  color: var(--ok);
}

.trading-terminal-panel {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.trading-terminal-mark-price {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 8px 10px;
  background: var(--gr-rivet);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
}

.trading-terminal-mark-price-label {
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.trading-terminal-mark-price-value {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--gr-brass-bright);
}

.trading-terminal-mark-price-unavailable {
  font-size: 12px;
  color: var(--danger);
}

.trading-terminal-entry {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
}

.trading-direction-selector {
  display: flex;
  gap: 8px;
}

.trading-direction-btn {
  flex: 1;
  padding: 8px;
  font-family: var(--font-control);
  font-size: 13px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  background: var(--gr-steel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  color: var(--text-dim);
  cursor: pointer;
}

.trading-direction-long.selected {
  border-color: var(--ok);
  color: var(--ok);
  background: rgba(111, 191, 115, 0.12);
}

.trading-direction-short.selected {
  border-color: var(--danger);
  color: var(--danger);
  background: rgba(209, 91, 91, 0.12);
}

.trading-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--text-dim);
}

.trading-field input[type="number"] {
  padding: 6px 8px;
  background: var(--gr-steel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.trading-leverage-field span strong {
  color: var(--gr-brass-bright);
}

.trading-leverage-field input[type="range"] {
  width: 100%;
  accent-color: var(--gr-brass);
}

.trading-preview {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 4px 0;
  padding: 8px 10px;
  background: var(--gr-rivet);
  border-radius: var(--radius);
}

.trading-preview-row {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
}

.trading-preview-row dt {
  color: var(--text-dim);
}

.trading-preview-row dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

/* FINAL POLISH ITEM 1 — these are the most consequential controls in the
   Order Entry panel, so they get deeper/more saturated tones than the
   shared `--ok`/`--danger` variables used elsewhere (P&L text, position
   borders, etc.) — a dedicated, slightly stronger shade for these two
   buttons specifically, rather than retuning `--ok`/`--danger` globally
   and risking every other use of those colors in the terminal. Still
   drawn from the same restrained Red & Green family — no neon, no
   Binance yellow, no glow. */
.trading-open-btn {
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 700;
  border-width: 2px;
}

.trading-open-btn-long {
  background: #4a9450;
  border-color: #396f3d;
  color: #eafbe9;
}

.trading-open-btn-long:hover:not(:disabled) {
  background: #56a75c;
  border-color: #4a9450;
}

.trading-open-btn-short {
  background: #b23f3f;
  border-color: #8a3030;
  color: #fdeaea;
}

.trading-open-btn-short:hover:not(:disabled) {
  background: #c24949;
  border-color: #b23f3f;
}

.trading-open-positions,
.trading-history {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.trading-open-positions h3,
.trading-history h3 {
  margin: 0;
  font-size: 13px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.trading-position-list,
.trading-history-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.trading-position-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--panel-border);
  border-left-width: 3px;
  border-radius: var(--radius);
}

.trading-position-long {
  border-left-color: var(--ok);
}

.trading-position-short {
  border-left-color: var(--danger);
}

.trading-position-header {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* FINAL POLISH ITEM 5 — a compact, restrained pill so LONG/SHORT reads
   instantly while scanning a dense table, without growing the row height
   or widening the Side column beyond its current content width. */
.trading-position-direction {
  display: inline-block;
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 1px 8px;
  border-radius: 999px;
  line-height: 1.5;
}

.trading-position-direction-long {
  color: var(--ok);
  background: rgba(111, 191, 115, 0.14);
  border: 1px solid rgba(111, 191, 115, 0.35);
}

.trading-position-direction-short {
  color: var(--danger);
  background: rgba(209, 91, 91, 0.14);
  border: 1px solid rgba(209, 91, 91, 0.35);
}

.trading-position-leverage {
  color: var(--gr-brass);
  font-size: 12px;
}

.trading-position-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.trading-position-pnl {
  display: flex;
  gap: 12px;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.trading-close-btn {
  align-self: flex-start;
}

.trading-history-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.trading-history-reason-badge {
  margin-left: auto;
  padding: 2px 6px;
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.05em;
  border-radius: 4px;
  color: var(--text-dim);
  background: var(--gr-steel);
}

.trading-history-reason-liquidation {
  color: var(--danger);
  background: rgba(209, 91, 91, 0.15);
}

.trading-history-reason-week_end {
  color: var(--gr-brass);
  background: rgba(201, 151, 79, 0.15);
}

/* ---------- Trading Terminal PROFESSIONAL PERPETUAL-FUTURES LAYOUT
   (MILESTONE 1C) ----------
   Supersedes Milestone 1B's 960px card/form layout with a near-fullscreen
   terminal destination (market strip / compact margin strip / chart-
   dominant main workspace / tabbed Positions-History activity section),
   following the same `.modal-dialog-fullscreen` size-variant machinery
   Marketplace already established — see Modal.tsx's own doc comment and
   the `[data-testid="marketplace-modal"].modal-dialog-fullscreen` rule
   below for the precedent this mirrors. No trading mathematics live in
   this file; every value below is formatted, never computed. */

/* `.modal-dialog[data-testid=...].modal-dialog-fullscreen` (class + attribute
   + class, specificity (0,3,0)) rather than the two-selector form Milestone
   1B's own trading-modal-width rule used — that rule silently lost a
   specificity TIE against the later-declared base `.modal-dialog.modal-
   dialog-fullscreen` rule below it in this same file (both (0,2,0), source
   order decides ties), a bug caught by live-measuring `getComputedStyle`
   in the browser during this milestone's own QA. Bumping to three
   selectors wins outright regardless of declaration order. */
.modal-dialog[data-testid="trading-modal"].modal-dialog-fullscreen {
  max-width: 1700px;
  width: 94vw;
  height: 90vh;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

[data-testid="trading-modal"] > .modal-content-header {
  margin-bottom: 6px;
}

[data-testid="trading-modal"] > .modal-content-body {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  display: flex;
}

.trading-account-panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  min-height: 0;
  overflow: hidden;
}

/* ---------- Top market strip ---------- */

.trading-market-strip {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
  padding: 8px 14px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass-dim);
  border-radius: var(--radius);
  font-variant-numeric: tabular-nums;
}

.trading-market-strip-symbol {
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 15px;
  color: var(--text);
}

.trading-market-strip-tag {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  border: 1px solid var(--gr-steel);
  border-radius: 4px;
  padding: 1px 6px;
}

.trading-market-strip-divider {
  width: 1px;
  align-self: stretch;
  background: var(--panel-border);
}

.trading-market-strip-label {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}

.trading-market-strip-price {
  font-size: 17px;
  font-weight: 700;
  color: var(--gr-brass-bright);
}

.trading-market-strip-spot {
  font-size: 13px;
  color: var(--text-dim);
}

.trading-market-strip-fill {
  flex: 1;
}

.trading-market-strip-badge {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  border: 1px solid var(--gr-steel);
  border-radius: 999px;
  padding: 2px 9px;
}

/* ---------- Compact account/margin strip ---------- */

.trading-margin-strip {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  flex-wrap: wrap;
  gap: 6px 20px;
  padding: 9px 14px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
}

.trading-margin-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.trading-margin-stat-label {
  font-family: var(--font-control);
  font-size: 9px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* FINAL POLISH ITEM 2 — a modest, deliberate size bump (13px -> 14px) for
   readability, matching what the Withdrawable Profit figure already used;
   the strip's own padding/gap grew by only 1-2px so its total height
   stays essentially unchanged — a professional data strip, not seven
   large cards. */
.trading-margin-stat-value {
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.trading-margin-stat-withdrawable .trading-margin-stat-value {
  color: var(--ok);
  font-size: 15px;
}

.trading-margin-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

.trading-withdraw-btn {
  padding: 6px 14px;
  font-size: 12px;
}

.trading-account-locked-note {
  margin: 0;
  font-size: 12px;
  color: var(--text-dim);
  flex-basis: 100%;
}

.trading-terminal-error {
  flex: 0 0 auto;
}

/* ---------- Liquidation Block (HASHER TRADING — LIQUIDATION BLOCK REWARDS milestone) ---------- */

.liquidation-block-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--gr-brass-dim);
  border-radius: var(--radius);
}

.liquidation-block-panel-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.liquidation-block-panel-title {
  font-family: var(--font-frontier);
  font-size: 17px;
  letter-spacing: 0.03em;
  color: var(--gr-brass-bright);
}

.liquidation-block-panel-countdown {
  font-family: var(--font-control);
  font-size: 12px;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.liquidation-block-panel-body {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
}

.liquidation-block-pool {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.liquidation-block-pool-label {
  font-family: var(--font-control);
  font-size: 9px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.liquidation-block-pool-value {
  font-size: 22px;
  font-weight: 700;
  color: var(--gr-brass-bright);
  font-variant-numeric: tabular-nums;
}

/* A TIME-cycle indicator only — how far the current 12-hour block has
   elapsed. Deliberately never framed as progress toward a dollar
   threshold: the block always resolves on the clock, not when this bar
   happens to fill. */
.liquidation-block-cycle-bar {
  flex: 1 1 160px;
  min-width: 120px;
  height: 6px;
  background: var(--panel-border);
  border-radius: 3px;
  overflow: hidden;
}

.liquidation-block-cycle-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--gr-brass-dim), var(--gr-brass-bright));
  border-radius: 3px 0 0 3px;
  transition: width 1s linear;
}

.liquidation-block-collect {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

.liquidation-block-unclaimed {
  font-family: var(--font-control);
  font-size: 12px;
  font-weight: 600;
  color: var(--ok);
  white-space: nowrap;
}

.liquidation-block-collect-btn {
  padding: 7px 16px;
  font-size: 12px;
  white-space: nowrap;
}

.liquidation-block-explainer {
  margin: 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--text-dim);
}

/* Fires ONLY while this player has an unclaimed reward — restrained,
   premium brass pulse on both the panel border and the Collect button,
   never a flashing/strobing casino treatment. */
.liquidation-block-panel-pulse {
  border-color: var(--gr-brass);
  animation: liquidation-block-panel-pulse 2.4s ease-in-out infinite;
}

.liquidation-block-collect-btn-pulse {
  animation: liquidation-block-collect-pulse 1.6s ease-in-out infinite;
}

@keyframes liquidation-block-panel-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 1px var(--gr-brass);
  }
  50% {
    box-shadow: 0 0 0 3px rgba(201, 151, 79, 0.25);
  }
}

@keyframes liquidation-block-collect-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 2px var(--gr-brass);
  }
  50% {
    box-shadow: 0 0 0 5px transparent;
  }
}

@media (prefers-reduced-motion: reduce) {
  .liquidation-block-panel-pulse,
  .liquidation-block-collect-btn-pulse {
    animation: none;
    box-shadow: 0 0 0 2px var(--gr-brass);
  }
}

.trading-info {
  position: relative;
}

.trading-info-toggle {
  background: transparent;
  border: 1px solid var(--gr-steel);
  color: var(--text-dim);
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: 999px;
  padding: 4px 10px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.trading-info-toggle::before {
  content: "i";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 1px solid currentColor;
  font-size: 9px;
  font-style: italic;
  font-weight: 700;
}

.trading-info-toggle:hover {
  border-color: var(--gr-brass);
  color: var(--gr-brass-bright);
}

.trading-info-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 5;
  width: 320px;
  margin: 0;
  padding: 12px 14px 12px 28px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-dim);
}

.trading-info-panel li {
  margin: 0 0 6px 0;
}

.trading-info-panel li:last-child {
  margin-bottom: 0;
}

.trading-withdraw-note {
  margin: 0;
  font-size: 12px;
  color: var(--text-dim);
  flex-basis: 100%;
}

.trading-terminal-mark-price-status {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 2px 7px;
  border-radius: 999px;
}

.trading-terminal-mark-price-status-live {
  color: var(--ok);
  background: rgba(111, 191, 115, 0.15);
}

/* ---------- Main workspace: chart (dominant) | order entry (compact) ---------- */

.trading-main-workspace {
  display: flex;
  flex: 1;
  min-height: 0;
  gap: 10px;
}

.trading-chart-panel {
  flex: 3;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 8px;
}

.trading-chart-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 0 0 auto;
}

.trading-chart-panel-title {
  font-family: var(--font-control);
  font-size: 12px;
  letter-spacing: 0.05em;
  color: var(--text-dim);
}

.trading-chart-timeframes {
  display: flex;
  gap: 4px;
}

.trading-chart-timeframe-btn {
  background: var(--gr-steel);
  border: 1px solid var(--panel-border);
  color: var(--text-dim);
  font-family: var(--font-control);
  font-size: 11px;
  padding: 3px 9px;
  border-radius: 4px;
  cursor: pointer;
}

.trading-chart-timeframe-btn.selected {
  border-color: var(--gr-brass);
  color: var(--gr-brass-bright);
  background: rgba(201, 151, 79, 0.14);
}

.trading-chart-disclaimer {
  flex: 0 0 auto;
  margin: 0;
  font-size: 10px;
  color: var(--text-dim);
  opacity: 0.75;
}

.trading-order-entry-panel {
  flex: 1;
  min-width: 220px;
  max-width: 340px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  overflow-y: auto;
  background: var(--gr-steel-dark);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
}

.trading-order-entry-panel h3 {
  margin: 0 0 2px 0;
  font-size: 12px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.trading-execute-row {
  display: flex;
  gap: 8px;
}

.trading-execute-row .trading-open-btn {
  flex: 1;
  padding: 10px 8px;
  font-size: 13px;
}

/* ---------- Activity section: Positions / Trade History tabs ---------- */

.trading-activity-section {
  flex: 0 1 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 120px;
  max-height: 34vh;
}

.trading-activity-tabs {
  display: flex;
  gap: 4px;
  flex: 0 0 auto;
}

.trading-activity-tab {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-dim);
  font-family: var(--font-control);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 6px 4px;
  cursor: pointer;
}

.trading-activity-tab.selected {
  color: var(--gr-brass-bright);
  border-bottom-color: var(--gr-brass);
}

.trading-activity-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* ---------- Desktop dense tables ---------- */

.trading-positions-table,
.trading-history-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.trading-positions-table th,
.trading-history-table th {
  position: sticky;
  top: 0;
  background: var(--gr-steel-dark);
  text-align: left;
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 5px 8px;
  border-bottom: 1px solid var(--panel-border);
}

.trading-positions-table td,
.trading-history-table td {
  padding: 5px 8px;
  border-bottom: 1px solid var(--panel-border);
  white-space: nowrap;
}

.trading-position-table-row {
  border-left: 3px solid transparent;
}

.trading-history-row td:first-child,
.trading-position-table-row td:first-child {
  border-left: inherit;
}

.trading-open-positions-empty,
.trading-history-table-wrap.empty-hint {
  padding: 16px 4px;
}

/* NEW UX FEATURE — a deliberate, restrained empty state instead of a bare
   sentence in blank space. No illustrations, no layout change — the
   activity section's own bounds are unchanged either way. */
.trading-positions-empty-state {
  padding: 20px 4px;
  text-align: center;
}

.trading-positions-empty-title {
  margin: 0 0 4px 0;
  font-family: var(--font-control);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.trading-positions-empty-hint {
  margin: 0;
  font-size: 12px;
  color: var(--text-dim);
  opacity: 0.8;
}

/* ---------- Responsive: dense tables are the DESKTOP treatment; below
   ~900px the activity section falls back to Milestone 1B's own card
   layout instead (CSS-only toggle, no JS breakpoint logic — same
   discipline the Marketplace browse grid already established). ---------- */
.trading-position-cards,
.trading-history-cards {
  display: none;
}

@media (max-width: 900px) {
  /* Stacking the chart above the order entry turns `.trading-main-
     workspace` from a side-by-side row into a column, but it still
     inherited the desktop rule's `flex: 1; min-height: 0` (a container
     that's forced to fit inside whatever space is left over, shrinking
     below its content if needed). With `.trading-account-panel` itself
     clipped to `overflow: hidden`, that squeezed the order-entry panel
     down to a couple of visible pixels behind its own internal scrollbar
     — found by measuring its rendered height live at 700px width. Pinning
     the stacked pieces to their natural content height (`flex: 0 0 auto`)
     and letting the whole account panel scroll instead fixes that without
     touching the desktop side-by-side layout at all. */
  .trading-account-panel {
    overflow-y: auto;
  }
  .trading-main-workspace {
    flex-direction: column;
    flex: 0 0 auto;
  }
  .trading-chart-panel {
    min-height: 260px;
    flex: 0 0 auto;
  }
  .trading-order-entry-panel {
    max-width: none;
    flex: 0 0 auto;
  }
  .trading-positions-table-wrap,
  .trading-history-table-wrap {
    display: none;
  }
  .trading-position-cards,
  .trading-history-cards {
    display: block;
  }
  .trading-margin-strip {
    gap: 8px 14px;
  }
}

/* NEW UX FEATURE — margin shortcuts (10%/25%/50%/MAX). Deliberately small
   and secondary — a convenience row directly under the margin input, kept
   visually quiet so it never competes with OPEN LONG/OPEN SHORT below. */
.trading-margin-shortcuts {
  display: flex;
  gap: 6px;
}

.trading-margin-shortcut-btn {
  flex: 1;
  background: var(--gr-steel);
  border: 1px solid var(--panel-border);
  color: var(--text-dim);
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 4px 0;
  border-radius: 4px;
  cursor: pointer;
}

.trading-margin-shortcut-btn:hover {
  border-color: var(--gr-brass);
  color: var(--gr-brass-bright);
}

.trading-margin-shortcut-max {
  color: var(--gr-brass-bright);
  border-color: var(--gr-brass-dim);
}

/* TRADING ACCOUNT PLAYABILITY PASS — LEVERAGE CONTROL, revised under the
   RESPONSIVE ORDER ENTRY milestone's own tick-overlap finding: 1x/2x/5x/10x
   used to be positioned proportionally along the slider (left:X% from each
   value's own position in the 1-100 range), which put all four within the
   first ~9% of the track — closer together in pixels than their own label
   text is wide, so they visually collided at EVERY viewport size tested,
   including 1920x1080. These seven are now evenly spaced QUICK-SELECT
   REFERENCE BUTTONS (a UI affordance, not a calibrated axis) — clicking
   one still sets the real slider to that exact leverage value via the same
   setLeverage() the slider itself uses, and the slider thumb's own
   position remains the true, unchanged linear 1-100x mapping; only the
   BUTTON ROW'S layout is no longer tied to the track's geometry. The
   subtle chip/border treatment (vs. the old bare hairline label) is
   deliberate, so the row reads as presets rather than tick marks. */
.trading-leverage-ticks {
  display: flex;
  justify-content: space-between;
  gap: 4px;
  margin-top: 4px;
}

.trading-leverage-tick {
  flex: 1;
  background: var(--gr-steel);
  border: 1px solid var(--panel-border);
  border-radius: 3px;
  padding: 0;
  margin: 0;
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
}

.trading-leverage-tick:hover {
  border-color: var(--gr-brass-dim);
  color: var(--gr-brass-bright);
}

.trading-leverage-tick.selected {
  background: rgba(201, 151, 79, 0.14);
  border-color: var(--gr-brass);
  color: var(--gr-brass-bright);
  font-weight: 700;
}

.trading-notional-formula {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 10px;
  margin: 2px 0 4px 0;
  background: var(--gr-rivet);
  border-radius: var(--radius);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.trading-notional-formula-op {
  color: var(--text-dim);
}

/* FINAL POLISH ITEM 3 — "$X MARGIN x Nx -> $Y POSITION" reads clearly as
   cause-and-effect rather than a bare equation; the arrow gets a touch
   more visual weight than the plain "x" operator to carry that reading. */
.trading-notional-formula-arrow {
  color: var(--gr-brass);
  font-weight: 700;
  font-size: 15px;
}

.trading-notional-formula-result {
  color: var(--gr-brass-bright);
  font-weight: 700;
  margin-left: auto;
}

.trading-position-pnl-headline {
  margin-left: auto;
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.trading-position-roi {
  font-size: 11px;
  font-weight: 600;
  opacity: 0.85;
}

.trading-position-liquidation {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--text-dim);
  padding-top: 4px;
  border-top: 1px dashed var(--panel-border);
}

/* FINAL POLISH ITEM 4 — SAFE gets a restrained brass tint instead of
   inheriting the base's plain grey, so the three tiers each read as a
   deliberate, distinct color family (brass / amber / red) — see this
   block's own audit note in viewModel.ts's `liquidationRisk` for why a
   4th, CONTINUOUS signal (the `--risk-proximity` border-width rule below)
   was also needed: 1.05%/1.72%/3.24% all land in this same "warning"
   bucket, so no amount of retinting ONE flat color can tell them apart
   from each other — only a within-tier gradient can, without touching
   the <1%/<5% thresholds themselves. */
.trading-position-liquidation-safe {
  color: var(--gr-brass);
}

.trading-position-liquidation-warning {
  color: var(--amber);
  font-weight: 600;
}

.trading-position-liquidation-danger {
  color: var(--danger);
  font-weight: 700;
}

.trading-position-liquidation-distance {
  font-variant-numeric: tabular-nums;
}

.trading-position-risk-warning {
  border-left-color: var(--amber);
}

.trading-position-risk-danger {
  border-left-color: var(--danger);
  box-shadow: inset 0 0 0 1px rgba(209, 91, 91, 0.25);
}

/* Continuous within-tier severity: 0 at the 5% warning/safe boundary, 100
   at the liquidation price itself (see liquidationRisk's own doc comment
   in viewModel.ts) — scales ONLY the existing risk-tier border's width
   (2px..6px), never its color, so two "warning" positions at very
   different distances (e.g. 1.05% vs 3.24%) are now visually distinct
   without changing the discrete safe/warning/danger classification or
   its thresholds. A position with no proximity value (mark price
   unavailable) simply falls back to the flat 3px every row already had. */
.trading-position-table-row,
.trading-position-row {
  border-left-width: calc(2px + (var(--risk-proximity, 25) / 100) * 4px);
}

/* RESPONSIVE ORDER ENTRY MILESTONE — at 1920x1080 the Trading modal's 90vh
   cap leaves just enough room for every fixed-height chrome piece (market
   strip, compact account strip, Liquidation Block, activity tabs) plus
   Order Entry's own natural content height, with nothing to spare. Below
   that, 90vh shrinks while Order Entry's content does not, so the pre-
   existing `.trading-order-entry-panel { overflow-y: auto }` (needed for
   the much narrower/shorter widths in the max-width:900px block above)
   started silently scrolling the primary Open Long/Open Short buttons out
   of view at ordinary 1536x864/1366x768 laptop heights — a HEIGHT
   constraint, not a width one, so this is a separate max-height query and
   1920x1080 (never under 900px tall) is untouched. Trims padding/gaps on
   the same fixed-height chrome this milestone's own measurements found
   (Liquidation Block panel, account strip, activity section's min-height,
   Order Entry's own row spacing) — never by deleting a field, a preview
   row, a leverage tick, or restricting the 1-100x leverage range. Placed
   here, after every base `.trading-*`/`.liquidation-block-*` rule in this
   file, so these overrides always win the cascade on source order — they
   were originally placed right after the max-width:900px block much
   earlier in this section, which put several of them (anything whose base
   rule appears below that point, e.g. `.trading-notional-formula`) BEHIND
   their own base rule and silently defeated, caught by live-measuring
   `getComputedStyle` during this milestone's own QA. */
@media (max-height: 900px) {
  .trading-market-strip {
    padding: 5px 12px;
  }
  .trading-margin-strip {
    padding: 5px 12px;
    gap: 3px 14px;
  }
  .liquidation-block-panel {
    padding: 6px 10px;
    gap: 4px;
  }
  .trading-activity-section {
    min-height: 80px;
  }
  .trading-account-panel {
    gap: 4px;
  }
  .trading-order-entry-panel {
    padding: 6px 8px;
    gap: 4px;
  }
  .trading-order-entry-panel h3 {
    margin-bottom: 0;
  }
  .trading-field {
    gap: 1px;
  }
  /* Order Entry's own "Available Capital" readout duplicates the account
     strip's own stat directly above (always visible, never scrolled) —
     collapsing its label+value onto one row here only saves the vertical
     space of a redundant second line, never the information itself. */
  .trading-order-entry-panel label:has(> [data-testid="trading-available-capital"]) {
    flex-direction: row;
    justify-content: space-between;
    align-items: baseline;
  }
  .trading-field input[type="number"] {
    padding: 4px 8px;
  }
  .trading-margin-shortcuts {
    gap: 4px;
  }
  .trading-margin-shortcut-btn {
    padding: 2px 0;
  }
  .trading-leverage-ticks {
    margin-top: 3px;
  }
  .trading-notional-formula {
    padding: 4px 6px;
    margin: 0;
  }
  .trading-preview {
    padding: 4px 6px;
    margin: 0;
    gap: 1px;
  }
}

/* A second, deeper tier for 1366x768 — the max-height:900px trims above
   already close the gap at 1536x864, but 768 still needs more. Layered on
   top of (not a replacement for) the tier above; both apply together here.
   Same rule: tighten spacing only, never remove a field or a preview row. */
@media (max-height: 800px) {
  .liquidation-block-panel {
    padding: 5px 10px;
    gap: 3px;
  }
  .liquidation-block-explainer {
    font-size: 10px;
    line-height: 1.25;
  }
  .trading-activity-section {
    min-height: 64px;
  }
  .trading-order-entry-panel {
    padding: 5px 6px;
    gap: 2px;
  }
  .trading-order-entry-panel h3 {
    font-size: 11px;
  }
  .trading-field input[type="number"] {
    padding: 3px 6px;
  }
  .trading-preview {
    padding: 3px 5px;
  }
  /* At this width+height combination Order Entry's own rendered width
     (measured ~316px) is narrow enough that the formula's 5 inline
     segments no longer fit on one line at the tier-above's 13px/6px-gap
     sizing, so it silently wrapped to 2 lines — costing ~22px of height
     for a pure width side-effect, not a deliberate content change. Tighter
     font/gap/padding here keeps it on one line at this size, verified live. */
  .trading-notional-formula {
    font-size: 11px;
    gap: 4px;
    padding: 3px 5px;
  }
  /* Same "restrained empty state" copy (see its own doc comment above),
     just less padding around it — the two-line message and the tab strip
     above it were the largest single contributor to the activity
     section's real (content-driven, not min-height-floor-driven) height
     at this size. */
  .trading-positions-empty-state {
    padding: 8px 4px;
  }
  .trading-activity-tab {
    padding: 4px 4px;
  }
}

.sell-success-toast,
.dev-advance-success-toast {
  margin: 8px 0 0 0;
  padding: 8px 10px;
  border-radius: 6px;
  background: rgba(111, 191, 115, 0.15);
  border: 1px solid var(--ok);
  color: var(--ok);
  font-size: 13px;
}

/* ---------- Sale confirmation modal ---------- */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(5, 6, 8, 0.65);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 16px;
}

.modal-dialog {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 20px 22px;
  width: 100%;
  max-width: 380px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}

.modal-dialog h2 {
  margin: 0 0 14px 0;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}

/* ---------- Generic Modal (Shop/Crew/Equipment/placeholder popups) ---------- */

/* These popups wrap existing full-size panels (ShopPanel, CrewBuilderPanel,
   etc.), which can be much taller than the small confirmation dialogs
   .modal-dialog was originally sized for — scoped to .modal-dialog-content
   only, so the existing narrow confirmation modals are unaffected. */
.modal-dialog.modal-dialog-content {
  max-height: 85vh;
  overflow-y: auto;
}

.modal-content-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.modal-content-header h2 {
  margin: 0;
}

/* Pass 7 — world/identity layer, scoped to only the specific modals whose
   title matches the approved Rye list (Crew Management, Equipment,
   Marketplace) via each modal's own data-testid — deliberately NOT a
   blanket `.modal-content-header h2` rule, since that shared header is
   also used by Shop, Trading, Results, BTC Chart, Review & Lock Crew,
   and Volatility, none of which are on the approved list (rule 13: "Do
   NOT sweep every modal automatically. Audit before touching."). */
[data-testid="crew-management-screen"] .modal-content-header h2,
[data-testid="equipment-modal"] .modal-content-header h2,
[data-testid="marketplace-modal"] .modal-content-header h2 {
  font-family: var(--font-frontier);
  font-weight: 400;
}

/* Pass 7 — Equipment Browser modal (opened from an empty compatible board
   tile), a second, differently-built "Equipment" title outside the
   shared Modal component — matches the approved list the same as the
   Equipment modal/console sub-heading above. */
#equipment-browser-title {
  font-family: var(--font-frontier);
  font-weight: 400;
}

.modal-content-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Flattens the wrapped panel's own border/background so it doesn't read as
   a box nested inside another box — the panels themselves are unmodified
   (per "only change how it is accessed"), this is purely how they look
   once relocated into a dialog that already provides that chrome. */
.modal-content-body .panel {
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  margin-bottom: 0;
}

.modal-detail-list {
  margin: 0 0 12px 0;
}

.modal-detail-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 4px 0;
  font-size: 13px;
  border-bottom: 1px solid var(--panel-border);
}

.modal-detail-row:last-child {
  border-bottom: none;
}

.modal-detail-row dt {
  color: var(--text-dim);
}

.modal-detail-row dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.modal-detail-final dd {
  font-size: 17px;
  font-weight: 700;
  color: var(--ok);
}

/* Supply Depot Bulk Purchases milestone — the quantity row's dd holds a
   whole control cluster (stepper + input + MAX), not a plain text value,
   so it needs its own centered alignment rather than .modal-detail-row's
   shared baseline rule. */
.modal-detail-quantity {
  align-items: center;
}

.quantity-stepper {
  display: flex;
  align-items: center;
  gap: 6px;
}

.btn-icon {
  width: 28px;
  height: 28px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  line-height: 1;
}

.quantity-input {
  width: 52px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  background: var(--gr-steel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  color: var(--text);
  padding: 6px 2px;
}

.quantity-input[aria-invalid="true"] {
  border-color: #ffb3b3;
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 4px;
}

/* ---------- Mobile ---------- */

@media (max-width: 480px) {
  .app-shell {
    padding: 10px;
  }
  .board-panel {
    --board-u: clamp(110px, 52vw, 160px);
  }
  .stats-group-stats {
    grid-template-columns: repeat(2, 1fr);
  }
  .crew-position-grid {
    grid-template-columns: 1fr;
  }
  .dev-override-row {
    grid-template-columns: 1fr 1fr;
  }
  .onboarding-card {
    padding: 20px;
  }
  .welcome-title {
    font-size: 22px;
  }
}

/* ---------- Onboarding (Welcome / Hasher reveal / Gear opening / Facility transition) ---------- */

.onboarding-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.onboarding-card {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 32px;
  max-width: 440px;
  width: 100%;
  text-align: center;
  animation: onboarding-fade-in 0.3s ease-out;
}

@keyframes onboarding-fade-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---------- Hasher Mode Entry Screen (approved full-scene artwork) ---------- */

/* Scoped to .welcome-screen specifically (not the shared .onboarding-screen
   base above, which every other onboarding step also uses) so the scene
   artwork/positioning context never reaches the Hasher reveal, gear
   opening, or facility-transition screens. */
.welcome-screen {
  position: relative;
  overflow: hidden;
}

/* Full-bleed cinematic cover: fills the viewport, preserves the artwork's
   own aspect ratio (no stretching), crops rather than letterboxes. At
   every desktop resolution this milestone targets (1920x1080/1536x864/
   1366x768 are all exactly 16:9), the source art (CLEAN ARTWORK PASS:
   hasher_mode_entry_scene_clean.png, 1762x893, ~1.97:1 — close to but not
   identical to the previous artwork's 1740x904, still wider than 16:9)
   crops a small, symmetric sliver off the LEFT and RIGHT edges only — the
   full vertical composition (mountains down to the mine floor) always
   stays in frame. object-position keeps the mountain/character
   composition centered as that crop happens; re-verified live at all
   three target resolutions against the new artwork — unchanged, still
   correct. */
.welcome-scene-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 44%;
  z-index: 0;
  pointer-events: none;
  transform: scale(1);
  animation: welcome-scene-drift 42s ease-in-out infinite alternate;
}

/* Extremely slow, small Ken Burns drift — "tiny atmospheric drift," never
   a slideshow effect. At 42s for a 3.5% scale change, no single frame
   shows perceptible motion; it only reads as "alive" over time. */
@keyframes welcome-scene-drift {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(1.035);
  }
}

/* A restrained warm pulse loosely over the artwork's own lantern clusters
   (left sign structure, right elevated platform) — "lantern glow
   breathing," achieved as a CSS overlay rather than by touching the
   supplied artwork itself (never regenerated/recomposed, per this
   screen's own doc comment). */
.welcome-scene-glow {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(circle at 9% 30%, rgba(240, 169, 78, 0.16) 0%, transparent 30%),
    radial-gradient(circle at 91% 34%, rgba(240, 169, 78, 0.14) 0%, transparent 28%);
  animation: welcome-glow-pulse 7s ease-in-out infinite alternate;
}

@keyframes welcome-glow-pulse {
  from {
    opacity: 0.65;
  }
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .welcome-scene-bg,
  .welcome-scene-glow {
    animation: none;
  }
}

/* The real, live onboarding panel — deliberately opaque (inherits
   .onboarding-card's own --panel background, no alpha) and positioned
   centrally exactly where the supplied artwork's own baked-in concept
   preview of this same panel sits, so it fully occludes that preview
   rather than stacking live text on top of visible baked text. A brass
   perimeter + grounded shadow gives it a "mounted plaque sitting on the
   scene" feel consistent with the rest of the game's riveted-panel
   language, rather than a generic flat modal.
   Panel-size refinement pass — sized up ~25% (560->700 max-width, and
   proportionally taller via padding/spacing below, never via larger
   type) so the live panel more completely covers the artwork's own
   baked-in panel area behind it. A full `border` shorthand (2px solid,
   not just border-color) replaces the earlier 1px override — same
   restrained --gr-brass (warm aged gold, not the brighter --amber/
   --gr-brass-bright used elsewhere for hover/CTA states) so it reads as
   a premium plaque perimeter, not a glow. The old inset brass ring in
   the box-shadow is dropped now that a real border serves that role —
   doubling both looked heavier than intended. */
.welcome-card {
  position: relative;
  z-index: 2;
  max-width: 700px;
  padding: 54px 56px;
  border: 2px solid var(--gr-brass);
  box-shadow: 0 24px 60px -12px rgba(0, 0, 0, 0.85);
  /* HASHER MODE ENTRY PANEL POSITION fix: .onboarding-screen's flex
     centering lands this panel's true vertical center lower than looks
     balanced against the artwork's own mountain composition, leaving too
     much empty gap above it. The artwork is a fixed 16:9 cover crop with
     no vertical cropping at any of this screen's target sizes (see
     .welcome-scene-bg's own comment), so mixing vh and px in one calc()
     is what makes a single rule land this card at the same balanced
     height relative to the mountains at every target size, rather than a
     flat px offset that would overshoot badly once the viewport shrinks.
     Scoped to .welcome-card only — every other onboarding screen sharing
     .onboarding-card's flex centering is unaffected.
     CLEAN ARTWORK PASS: re-verified live at 1920x1080/1536x864/1366x768
     against hasher_mode_entry_scene_clean.png (no baked-in title or
     ghost-panel mockup to align to anymore) — this exact offset still
     lands the panel in the clean central space with a balanced gap above
     and below, so it carries over unchanged. No masking pseudo-element is
     needed any more (the old .welcome-card::after existed only to hide
     the previous artwork's baked-in ghost CTA, which this artwork never
     had) — removed. */
  transform: translateY(calc(188px - 24.074vh));
}

.welcome-title {
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: 32px;
  letter-spacing: 0.02em;
  color: var(--gr-brass-bright);
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
  margin: 0 0 22px;
}

.welcome-description {
  color: var(--text-dim);
  line-height: 1.5;
  margin-bottom: 36px;
}

/* Same "controls mounted into mining equipment" family START DAY/SHOP/
   CREW/etc. already use (.action-bar .btn, styles.css) — Oswald,
   uppercase, letter-spaced — but sized and colored as this screen's own
   single primary CTA rather than a small nav control. Scoped under
   .welcome-card (not the bare .btn-primary two classes below it are
   layered on) so it wins outright regardless of file order. */
.welcome-card .welcome-cta {
  font-family: var(--font-control);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 700;
  background: linear-gradient(180deg, var(--gr-brass-bright) 0%, var(--amber) 100%);
  border: 1px solid var(--gr-brass-dim);
  color: #201400;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.25) inset,
    0 2px 6px rgba(0, 0, 0, 0.5);
}

.welcome-card .welcome-cta:hover:not(:disabled) {
  filter: brightness(1.06);
  border-color: var(--gr-brass-bright);
}

.welcome-card .welcome-cta:active:not(:disabled) {
  filter: brightness(0.92);
  transform: translateY(1px);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6) inset;
}

.btn-large {
  padding: 14px 28px;
  font-size: 16px;
  font-weight: 600;
}

/* ---------- Weekly Summary (Weekly Completion Framework) ---------- */

.weekly-summary-card {
  max-width: 560px;
  text-align: left;
}

.weekly-summary-title {
  font-size: 26px;
  margin: 0 0 4px;
  text-align: center;
}

.weekly-summary-day {
  color: var(--text-dim);
  text-align: center;
  margin: 0 0 20px;
}

.weekly-summary-card .final-score-value,
.weekly-summary-card .final-score-tier {
  text-align: center;
}

.weekly-summary-reward-note {
  text-align: center;
  color: var(--text-dim);
  font-size: 13px;
  margin: 0 0 20px;
}

.weekly-summary-stats {
  margin: 0 0 16px;
}

.weekly-summary-stat-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 6px 0;
  font-size: 13px;
  border-bottom: 1px solid var(--panel-border);
}

.weekly-summary-stat-row:last-child {
  border-bottom: none;
}

.weekly-summary-stat-row dt {
  color: var(--text-dim);
}

.weekly-summary-stat-row dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.weekly-summary-equipment {
  margin: 0 0 24px;
}

.weekly-summary-equipment h2 {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  margin: 0 0 8px;
}

.weekly-summary-equipment ul {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 13px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 4px 16px;
}

.weekly-summary-card .btn-large {
  display: block;
  width: 100%;
}

.reveal-progress {
  color: var(--text-dim);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 16px;
}

/* ---------- CINEMATIC STARTING HASHER REVEAL ----------
   Replaces the old small-modal-on-plain-background presentation (still
   used verbatim by GearOpeningSequence/.reveal-progress above, untouched)
   with a full-bleed environment + a much larger hero card. Scoped
   entirely under "hasher-reveal-" class names — nothing here is shared
   with, or changes, any other onboarding screen. */

.hasher-reveal-screen {
  position: relative;
  overflow: hidden;
}

.hasher-reveal-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  z-index: 0;
  pointer-events: none;
}

/* Darkens the artwork's own edges/corners for text/card readability
   without a second rectangular panel — the environment stays visible
   through the clear center it was supplied with. */
.hasher-reveal-vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 70% 65% at 50% 46%, transparent 40%, rgba(8, 6, 4, 0.55) 100%),
    radial-gradient(circle at 9% 20%, rgba(240, 169, 78, 0.14) 0%, transparent 26%),
    radial-gradient(circle at 91% 24%, rgba(240, 169, 78, 0.12) 0%, transparent 24%);
  animation: hasher-reveal-lantern-pulse 6s ease-in-out infinite alternate;
}

@keyframes hasher-reveal-lantern-pulse {
  from {
    opacity: 0.85;
  }
  to {
    opacity: 1;
  }
}

.hasher-reveal-dust {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
}

.hasher-reveal-dust-mote {
  position: absolute;
  left: calc(4% + (var(--hasher-dust-index) * 9.5%));
  bottom: -4%;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: rgba(240, 200, 140, 0.55);
  opacity: 0;
  animation: hasher-reveal-dust-drift 9s linear infinite;
  animation-delay: calc(var(--hasher-dust-index) * -0.9s);
}

@keyframes hasher-reveal-dust-drift {
  0% {
    opacity: 0;
    transform: translateY(0) translateX(0);
  }
  12% {
    opacity: 0.7;
  }
  88% {
    opacity: 0.25;
  }
  100% {
    opacity: 0;
    transform: translateY(-92vh) translateX(14px);
  }
}

.hasher-reveal-stage,
.hasher-reveal-crew-ready {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
}

.hasher-reveal-heading,
.gear-opening-heading {
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: 26px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.75);
  margin: 0;
}

.hasher-reveal-progress {
  font-family: var(--font-control);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--text-dim);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.85);
  margin: 0 0 6px;
}

/* HASHER REVEAL FINAL VISUAL POLISH milestone — canonical rarity -> color
   mapping, reused verbatim from .crew-mgmt-portrait-wrap[data-rarity=...]'s
   own already-established Hasher glow convention (a few hundred lines
   above) — the SAME source both the individual reveal's aura/flash/
   particles AND the final crew screen's own per-card aura key off, via
   this one custom property. Only the four rarities Hashers can actually
   roll (LaunchCardRarity has no legendary — see toLaunchRarity) have an
   entry; anything else falls back to the existing warm-gold default via
   var(--hasher-reveal-rarity-color, var(--gr-brass-bright)) everywhere
   below. */
.hasher-reveal-card-rise[data-rarity="common"],
.hasher-reveal-crew-card-wrap[data-rarity="common"] {
  --hasher-reveal-rarity-color: var(--rich-dim);
}
.hasher-reveal-card-rise[data-rarity="uncommon"],
.hasher-reveal-crew-card-wrap[data-rarity="uncommon"] {
  --hasher-reveal-rarity-color: var(--ok);
}
.hasher-reveal-card-rise[data-rarity="rare"],
.hasher-reveal-crew-card-wrap[data-rarity="rare"] {
  --hasher-reveal-rarity-color: var(--poor);
}
.hasher-reveal-card-rise[data-rarity="epic"],
.hasher-reveal-crew-card-wrap[data-rarity="epic"] {
  --hasher-reveal-rarity-color: var(--epic);
}

/* Soft rarity-coloured aura behind the card — environment stays warm
   frontier gold (untouched); this is the "Hasher reveal energy" the
   brief asks for. Individual reveal: fades in only once phase="revealed"
   (never during rise/flip, per "subtle aura AFTER reveal"). Final crew
   screen: `.hasher-reveal-crew-aura` modifier below gives it its own
   entrance instead (no data-phase there — those three are already
   revealed). Purely a background layer (z-index:0, inset, no layout
   impact) — never changes the card's own size or position.
   HASHER REVEAL FINAL CREW SCREEN VISUAL FIX: a large, heavily blurred,
   generously inset radial gradient — "soft light + haze", never a
   coloured rectangle. The blur is what makes this read as diffuse glow
   rather than a shape with an edge; the -40% inset gives the blur room
   to fully dissipate well before this element's own (invisible) box
   boundary, so no rectangular silhouette is ever visible regardless of
   card size. Shared verbatim by both the individual reveal and the final
   crew screen (`.hasher-reveal-crew-aura` below only adds crew-specific
   entrance/breathing timing, never a second colour or shape). */
.hasher-reveal-aura {
  position: absolute;
  inset: -40%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(
    circle at 50% 48%,
    var(--hasher-reveal-rarity-color, var(--gr-brass-bright)) 0%,
    var(--hasher-reveal-rarity-color, var(--gr-brass-bright)) 10%,
    transparent 56%
  );
  filter: blur(18px);
  opacity: 0;
  transition: opacity 0.7s ease;
}

.hasher-reveal-card-rise[data-phase="revealed"] .hasher-reveal-aura {
  opacity: 0.38;
}

/* Card arrival — physical weight via a short translateY+scale ease, never
   a bounce (no overshoot in the curve) or a spin. `perspective` here (not
   on the flip element itself) is what gives the child's rotateY below
   real 3D depth instead of a flat horizontal squash. */
.hasher-reveal-card-rise {
  position: relative;
  height: clamp(320px, 47vh, 520px);
  aspect-ratio: 1024 / 1536;
  transform: translateY(22px) scale(0.86);
  opacity: 0;
  perspective: 1200px;
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.5s ease;
  margin-bottom: 6px;
}

.hasher-reveal-card-rise-up {
  transform: translateY(0) scale(1);
  opacity: 1;
}

.hasher-reveal-card-flip {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.8s cubic-bezier(0.45, 0.05, 0.15, 1);
}

.hasher-reveal-card-flip-flipped {
  transform: rotateY(180deg);
}

.hasher-reveal-card-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.65);
}

/* HTML/CSS card back (no card-back asset exists for Hashers — the Loot
   Chest reveal's own resolveLootCardBackAsset() is a Gear-scoped file,
   not reused here) — dark timber/aged-metal surface, brass border,
   crossed pickaxes, matching this project's own established Red & Green
   frontier language rather than a new visual identity. */
.hasher-reveal-card-back {
  background: linear-gradient(160deg, #2b2116 0%, #1a140c 55%, #100b07 100%);
  border: 3px solid var(--gr-brass);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.3), inset 0 0 44px rgba(0, 0, 0, 0.55), 0 22px 55px rgba(0, 0, 0, 0.65);
}

.hasher-reveal-card-back-emblem {
  font-size: 17%;
  line-height: 1;
  filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6));
  opacity: 0.88;
}

/* box-shadow:none — the shared .hasher-reveal-card-face rectangular drop
   shadow suits the OPAQUE card back, but on the front face it paints a
   dark rectangle-shaped halo just outside the ornate (non-rectangular)
   frame silhouette. */
.hasher-reveal-card-front {
  transform: rotateY(180deg);
  box-shadow: none;
}

.hasher-reveal-card-front .card-frame {
  width: 100%;
  height: 100%;
  /* INDIVIDUAL REVEAL GREY BACKING FIX — the same two-layer correction
     .hasher-reveal-crew-card-wrap already applies (see below): the
     approved frame_<rarity>.png has genuine transparent margin around its
     ornate silhouette, but CardFrame's own `background: var(--panel)` and
     its always-present .card-frame-placeholder gradient/border underneath
     are both opaque dark-grey, painting a plain rectangle that pokes out
     past the frame on all four edges/corners (found by toggling each
     layer off in the browser: neither layer alone removes it, both must
     be transparent). Scoped to the individual reveal's front face only —
     never CardFrame.tsx itself or any other caller. */
  background: transparent;
}

.hasher-reveal-card-front .card-frame-placeholder {
  background: transparent;
  border-color: transparent;
}

.hasher-reveal-card-front .card-frame-placeholder-label,
.hasher-reveal-card-front .card-frame-placeholder-note {
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9);
}

/* Flash at the flip's own midpoint — timed via the phase machine's own
   data-phase attribute (the same technique
   .loot-reveal[data-phase="opening"] already uses), delayed to land as
   the card visually turns face-up rather than at the very start of the
   flip. HASHER REVEAL FINAL VISUAL POLISH milestone: now rarity-tinted
   (var(--hasher-reveal-rarity-color), same mapping as the aura above)
   rather than always gold — the white core keeps every rarity's flash
   reading as a bright flash rather than a flat colour swatch. */
.hasher-reveal-flash {
  position: absolute;
  inset: -18%;
  z-index: 2;
  border-radius: 50%;
  background: radial-gradient(circle, #fff6da 0%, var(--hasher-reveal-rarity-color, var(--gr-brass-bright)) 42%, transparent 72%);
  opacity: 0;
  pointer-events: none;
}

.hasher-reveal-card-rise[data-phase="card-flipping"] .hasher-reveal-flash {
  animation: hasher-reveal-flash 0.6s ease-out 0.32s;
}

@keyframes hasher-reveal-flash {
  0% {
    opacity: 0;
    transform: scale(0.5);
  }
  35% {
    opacity: 0.8;
  }
  100% {
    opacity: 0;
    transform: scale(1.5);
  }
}

/* A brief, restrained rarity-coloured particle burst right as the card
   settles into "revealed" — mounted once (React only adds these spans
   when revealed becomes true, see HasherRevealSequence.tsx), animates
   once (no `infinite`), never loops — "restrained... never excessive
   particles" per this milestone's own brief. */
.hasher-reveal-card-particles {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  overflow: visible;
}

.hasher-reveal-card-particle {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--hasher-reveal-rarity-color, var(--gr-brass-bright));
  opacity: 0;
  --hasher-particle-angle: calc(var(--hasher-card-particle-index) * 45deg);
  animation: hasher-reveal-card-particle-burst 1s ease-out calc(var(--hasher-card-particle-index) * 40ms) forwards;
}

@keyframes hasher-reveal-card-particle-burst {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) rotate(var(--hasher-particle-angle)) translateY(0) scale(0.6);
  }
  18% {
    opacity: 0.9;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) rotate(var(--hasher-particle-angle)) translateY(-150px) scale(1);
  }
}

/* HP/SPD/INT now render INSIDE each card's own footer (CardFrame's `stats`
   prop — see HasherRevealSequence.tsx's `hasherCardStats`), not as a
   separate row beneath it. CardFrame's own footer type is fixed-px, sized
   for the small inventory/catalogue cards (.card-frame-title/-stats in
   styles.css, ~1685) — these two starting-crew screens use much larger
   cards, so the same footer scales with the card's own height here
   instead, exactly the precedent `.workshop-station-card .card-frame-*`
   below already established for the Workshop's own larger cards. Scoped to
   each screen's own card wrapper only — never CardFrame.tsx itself or any
   other caller. */
.hasher-reveal-card-front .card-frame-title {
  font-size: clamp(12px, 2.6vh, 18px);
  /* Never let the stats row's own nowrap-safety squeeze this flex sibling
     down to zero height (the exact 1366x768 bug: .card-frame-stats used to
     inherit base's flex-wrap:wrap, wrapping to a second line inside this
     fixed-height (14% of card) column and leaving no room for the title,
     which the flex algorithm then collapsed to 0px — invisible, not
     merely small). flex-shrink:0 plus the guaranteed-single-line stats
     row below are both required; either alone still regresses at small
     card sizes. */
  flex-shrink: 0;
}

.hasher-reveal-card-front .card-frame-stats {
  gap: 2px clamp(4px, 1vh, 10px);
  font-size: clamp(10px, 2.2vh, 15px);
  /* MUST stay one line — base .card-frame-stats's flex-wrap:wrap is safe
     only at the small fixed-10px base card size; at this reveal's much
     larger clamp()'d font, wrapping to a second line overflows the 14%-tall
     overlay box and collapses the title (see comment above). */
  flex-wrap: nowrap;
  flex-shrink: 0;
}

/* Staggered fade-in for the footer stats, same cadence (HP, then SPD,
   then INT, 0.15s apart) the removed external row used — CardFrame renders
   `stats` in array order as sibling `.card-frame-stat` spans, so nth-child
   reproduces the same per-stat delay without the old dedicated classes.
   Only mounted (and so only animated) once `revealed` — see
   HasherRevealSequence.tsx — never replays on re-render. */
.hasher-reveal-card-front .card-frame-stat {
  opacity: 0;
  transform: translateY(8px) scale(0.92);
  animation: hasher-reveal-stat-in 0.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
.hasher-reveal-card-front .card-frame-stat:nth-child(1) {
  animation-delay: 0s;
}
.hasher-reveal-card-front .card-frame-stat:nth-child(2) {
  animation-delay: 0.15s;
}
.hasher-reveal-card-front .card-frame-stat:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes hasher-reveal-stat-in {
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* The final "Your Crew Is Ready" summary's three cards — smaller than the
   individual reveal's hero card (height clamp ~240-500px vs ~320-520px),
   so a slightly smaller footer clamp; all three stats are already known
   (no staggered reveal here, matching this screen's own non-staggered
   per-card entrance) so no animation is needed. */
.hasher-reveal-crew-card-wrap .card-frame-title {
  font-size: clamp(10px, 2.1vh, 14px);
  /* Same title-collapse defense as .hasher-reveal-card-front above. */
  flex-shrink: 0;
}

.hasher-reveal-crew-card-wrap .card-frame-stats {
  gap: 2px clamp(3px, 0.8vh, 8px);
  font-size: clamp(9px, 1.7vh, 12px);
  /* Same nowrap-safety as .hasher-reveal-card-front above — these cards are
     smaller still, so this matters even more here. */
  flex-wrap: nowrap;
  flex-shrink: 0;
}

.hasher-reveal-next {
  min-width: 200px;
}

/* Specialist caption — external to the card (HasherRevealSequence.tsx no
   longer passes a `traits` prop to either CardFrame call), deliberately
   larger/bolder than CardFrame's own tiny footer trait text (9-10px) ever
   was, since this is now a standalone caption, not a cramped footer line.
   Same frontier typography/colour treatment the rest of this screen's text
   uses (brass/gold on dark, drop-shadowed for legibility over the mine
   background), not a new palette. */
.hasher-reveal-specialist-caption {
  font-family: var(--font-control);
  font-size: clamp(14px, 1.6vw, 19px);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.85);
  margin: 0;
  text-align: center;
  white-space: nowrap;
}

/* Individual reveal: a normal flex child of .hasher-reveal-stage, sitting
   between the card and the Next-Hasher/View-Crew button — the stage's own
   `gap: 10px` (declared on .hasher-reveal-stage/.hasher-reveal-crew-ready
   above) already gives it clean spacing on both sides, no extra margin
   needed. Mounts (and so only animates) once `revealed`, reusing the exact
   fade/rise the footer stats already use so it reads as part of the same
   reveal beat, not a separate afterthought. */
.hasher-reveal-stage > .hasher-reveal-specialist-caption {
  opacity: 0;
  transform: translateY(8px) scale(0.92);
  animation: hasher-reveal-stat-in 0.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

/* Final "Your Crew Is Ready" summary: lives inside each card's own
   .hasher-reveal-crew-card-wrap (after its CardFrame in source order) so it
   stays correctly associated with that specific Hasher even with three
   cards side by side, but the wrapper's own height is a fixed clamp() sized
   exactly to the card — positioned absolutely just below the wrapper's own
   box rather than relying on normal flow pushing it out from under a
   fixed-height ancestor. Fades in with the same per-card stagger delay the
   card's own entrance already uses (--hasher-crew-card-index), landing
   just after the card settles. */
.hasher-reveal-crew-specialist-caption {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 8px;
  font-size: clamp(11px, 1.1vw, 15px);
  opacity: 0;
  animation: onboarding-fade-in 0.4s ease-out forwards;
  animation-delay: calc(var(--hasher-crew-card-index) * 0.15s + 0.5s);
}

/* HASHER REVEAL FINAL VISUAL POLISH milestone — the payoff screen after
   all three Hashers are revealed. Was a small, clustered row of 150px
   thumbnails; now three HERO cards spread across the full width, each
   sized independently via its own wrapper (never forcing CardFrame's
   own width:100% to fight a flex-basis) — mirrors the individual
   reveal's own height-driven `.hasher-reveal-card-rise` sizing exactly,
   just via clamp()'s own smaller ceiling (500px, vs. the individual
   card's 520px) so heading + three cards + Continue still comfortably
   fit above 1080px tall without scrolling. `justify-content: center`
   plus a generous, viewport-width-aware `gap` (not fixed percentages)
   is what naturally spreads the three cards toward roughly the
   25%/50%/75% composition the brief describes, without hardcoding
   those fractions — the actual spread follows from real card width plus
   this gap at whatever viewport width is current. */
.hasher-reveal-crew-grid {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: clamp(28px, 6vw, 100px);
  /* Bottom margin widened from 22px — each card's own specialist caption
     (.hasher-reveal-crew-specialist-caption) is positioned absolutely just
     below it, so it doesn't contribute to this flex row's own layout
     height; without the extra room here the Continue button would land
     hard against, or overlapping, those captions. */
  margin: 14px 0 46px;
  width: 100%;
}

.hasher-reveal-crew-card-wrap {
  position: relative;
  height: clamp(240px, 46vh, 500px);
  aspect-ratio: 1024 / 1536;
  opacity: 0;
  transform: translateY(24px) scale(0.9);
  animation: hasher-reveal-crew-card-in 0.6s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: calc(var(--hasher-crew-card-index) * 0.15s);
}

@keyframes hasher-reveal-crew-card-in {
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.hasher-reveal-crew-card-wrap .card-frame {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  /* HASHER REVEAL FINAL CREW SCREEN VISUAL FIX — the approved
     frame_<rarity>.png art has genuine transparent margin around its own
     ornate (non-rectangular) silhouette, by design, so the portrait/
     background can show through it. CardFrame's own `background:
     var(--panel)` and its always-present `.card-frame-placeholder`
     fallback layer underneath are both flat dark colours, invisible at
     the card's normal ~230px catalogue size but a visible dark
     rectangle at this screen's hero scale. Scoped to just this screen's
     cards (never CardFrame.tsx itself, never any other caller) — makes
     both dark layers transparent so the mine environment/aura show
     through the frame's own margin instead, "the mine itself is the
     stage." The placeholder's "artwork pending" fallback text keeps its
     own text-shadow (below) for legibility if a frame ever genuinely
     fails to load here. */
  background: transparent;
}

.hasher-reveal-crew-card-wrap .card-frame-placeholder {
  background: transparent;
  border-color: transparent;
}

.hasher-reveal-crew-card-wrap .card-frame-placeholder-label,
.hasher-reveal-crew-card-wrap .card-frame-placeholder-note {
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9);
}

/* Each card's own rarity aura gets its OWN entrance (no data-phase to key
   off here — these three are already revealed) — lands just after that
   card's own rise-in settles, then breathes very gently (never a flash
   or pulse) so the payoff screen feels alive without drawing attention
   away from the cards themselves. */
.hasher-reveal-crew-aura {
  opacity: 0;
  animation:
    hasher-reveal-crew-aura-in 0.8s ease forwards calc(var(--hasher-crew-card-index) * 0.15s + 0.2s),
    hasher-reveal-crew-aura-breathe 5s ease-in-out infinite calc(var(--hasher-crew-card-index) * 0.15s + 1s);
}

@keyframes hasher-reveal-crew-aura-in {
  to {
    opacity: 0.4;
  }
}

/* Small opacity swing only (0.4 -> 0.5 -> 0.4) — "very small
   intensity variation", never a flash. Starting keyframe matches
   -aura-in's own end value so there is no visible jump at the handoff. */
@keyframes hasher-reveal-crew-aura-breathe {
  0%,
  100% {
    opacity: 0.4;
  }
  50% {
    opacity: 0.5;
  }
}

/* Slow, continuous, LOCAL rarity-coloured dust — distinct from the
   individual reveal's one-shot particle burst (.hasher-reveal-card-
   particle, untouched): here it drifts gently near each card for as
   long as this screen is showing, "coloured dust particles near the
   edges" rather than a celebratory burst. */
.hasher-reveal-crew-card-dust {
  position: absolute;
  inset: -22%;
  z-index: 0;
  pointer-events: none;
  overflow: visible;
}

.hasher-reveal-crew-card-dust-mote {
  position: absolute;
  left: calc(12% + (var(--hasher-crew-dust-index) * 15%));
  bottom: -4%;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--hasher-reveal-rarity-color, var(--gr-brass-bright));
  opacity: 0;
  animation: hasher-reveal-crew-dust-drift 7s ease-in-out infinite;
  animation-delay: calc((var(--hasher-crew-card-index) * 0.15s) + (var(--hasher-crew-dust-index) * -1.4s));
}

@keyframes hasher-reveal-crew-dust-drift {
  0% {
    opacity: 0;
    transform: translateY(0);
  }
  15% {
    opacity: 0.6;
  }
  85% {
    opacity: 0.2;
  }
  100% {
    opacity: 0;
    transform: translateY(-65%);
  }
}

/* Continue lands once the third (rightmost) card has settled — never
   available before the player has actually seen all three. */
.hasher-reveal-crew-continue {
  opacity: 0;
  animation: onboarding-fade-in 0.4s ease-out 0.85s forwards;
}

@media (max-width: 900px) {
  .hasher-reveal-card-rise {
    height: clamp(240px, 44vh, 380px);
  }
  .hasher-reveal-heading {
    font-size: 20px;
  }
  .hasher-reveal-crew-grid {
    gap: clamp(14px, 4vw, 32px);
  }
  .hasher-reveal-crew-card-wrap {
    height: clamp(180px, 42vh, 320px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hasher-reveal-vignette,
  .hasher-reveal-dust-mote,
  .hasher-reveal-card-front .card-frame-stat,
  .hasher-reveal-specialist-caption,
  .hasher-reveal-card-particle,
  .hasher-reveal-crew-card-wrap,
  .hasher-reveal-crew-continue {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .hasher-reveal-card-rise,
  .hasher-reveal-card-flip,
  .hasher-reveal-flash,
  .hasher-reveal-aura {
    transition-duration: 0.01ms;
    animation-duration: 0.01ms;
  }
  /* Ambient drifting dust reads as motion by definition — hidden
     entirely in reduced motion, same treatment loot-reveal-particle's
     own base (unanimated) opacity:0 already gets. */
  .hasher-reveal-crew-card-dust-mote {
    animation: none;
    opacity: 0;
  }
  .hasher-reveal-crew-aura {
    animation: none;
    opacity: 0.4;
  }
}

@keyframes onboarding-flip-in {
  from {
    opacity: 0;
    transform: scale(0.85);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.gear-box-bonus {
  color: var(--ok);
  font-weight: 600;
  font-size: 16px;
  margin: 4px 0;
}

.gear-box-description {
  color: var(--text-dim);
  font-size: 11px;
  padding: 0 12px;
}

.facility-transition-card p {
  color: var(--text-dim);
  margin-bottom: 24px;
}

/* ---------- Gear card (full portrait 2:3 card — GearCard.tsx) ----------
   Two composited layers per docs/ASSET_BIBLE.md Section 15.2.1: the
   rarity frame is a fully OPAQUE 1024x1536 card background (unlike
   Hasher's transparent-cutout frame), with the equipment master art
   composed on top inside the frame's own measured art-window region
   (~9-91% width, 14-63% height of the card canvas). Both layers stay
   real <img> elements, never flattened. */

.gear-card {
  position: relative;
  width: 100%;
  max-width: 220px;
  aspect-ratio: 1024 / 1536;
  margin: 0 auto;
  border-radius: 10px;
  overflow: hidden;
  background: var(--panel);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}

.gear-card-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: linear-gradient(165deg, #21262d 0%, #14171b 100%);
  border: 1px solid var(--panel-border);
  text-align: center;
  padding: 12px;
}

.gear-card-placeholder-label {
  font-weight: 700;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text);
}

.gear-card-placeholder-note {
  font-size: 11px;
  color: var(--text-dim);
}

.gear-card-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.gear-card-art-window {
  position: absolute;
  left: 9%;
  right: 9%;
  top: 14%;
  height: 49%; /* 14%–63% of the card canvas */
  display: flex;
  align-items: center;
  justify-content: center;
}

.gear-card-art {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.gear-card-art-placeholder {
  font-size: 44px;
  line-height: 1;
}

.gear-card-info {
  position: absolute;
  left: 12%;
  right: 12%;
  top: 66%;
  bottom: 5%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-align: center;
  color: var(--text);
}

.gear-card-slot-label {
  font-weight: 700;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.gear-card-stats {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 3px 10px;
  font-size: 11px;
}

.gear-card-stat-label {
  color: var(--text-dim);
}

.gear-card-stat-value {
  color: var(--ok);
  font-weight: 600;
}

/* ---------- GearIcon (compact swatch) ----------
   Equip Modal Fix pass: this component (GearIcon.tsx) and its
   `.gear-icon`/`.gear-icon-art` classes existed with zero CSS anywhere in
   this file — the doc comment in GearIcon.tsx even names
   `.gear-icon[data-rarity=...]` as if it were already here. With no size
   constraint at all, `.gear-icon-art` rendered its master-art PNG at full
   native resolution (1536x1024) inside `GearSlotPicker.tsx`'s
   `.gear-picker-list` (which only constrains its OWN height, via
   `overflow-y: auto`) — a ~1536px-wide flex item forced both a horizontal
   AND vertical scrollbar and pushed the list item to over 1000px tall.
   Fixed the same way `.gear-card-art` already is: a fixed-size square box
   with `overflow: hidden`, and the art `max-width/max-height: 100%` +
   `object-fit: contain` (never `cover` — `GearCard.tsx`'s own doc comment
   already established the six master arts have deliberately different
   native aspect ratios that must never be cropped to force a fit). */
.gear-icon {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--panel-border);
  background: var(--panel);
}

.gear-icon-small {
  width: 32px;
  height: 32px;
}

.gear-icon-medium {
  width: 48px;
  height: 48px;
}

.gear-icon-large {
  width: 96px;
  height: 96px;
}

.gear-icon-art {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.gear-icon-placeholder {
  line-height: 1;
}

.gear-icon-small .gear-icon-placeholder {
  font-size: 14px;
}

.gear-icon-medium .gear-icon-placeholder {
  font-size: 22px;
}

.gear-icon-large .gear-icon-placeholder {
  font-size: 40px;
}

/* Rarity border/glow (Gear Rarity + Card Selector visual pass) — this
   component's own doc comment already named `.gear-icon[data-rarity=...]`
   as the intended hook; it just never had CSS until now. Same canonical
   tokens `lootRevealPresentation.ts` uses for chest-opening, so a Gear
   icon reads the same rarity everywhere it appears (equipped Crew slots,
   this file's Gear selector, the existing inventory grid) — common gets
   no special treatment (the default neutral --panel-border already reads
   as "subdued"), and mythic is intentionally omitted (future/disabled,
   never produced by live generation). Border + a soft glow only, never a
   recolored fill — the master art stays fully readable. */
.gear-icon[data-rarity="uncommon"] {
  border-color: var(--ok);
  box-shadow: 0 0 8px -2px var(--ok);
}
.gear-icon[data-rarity="rare"] {
  border-color: var(--poor);
  box-shadow: 0 0 8px -2px var(--poor);
}
.gear-icon[data-rarity="epic"] {
  border-color: var(--epic);
  box-shadow: 0 0 8px -2px var(--epic);
}
.gear-icon[data-rarity="legendary"] {
  border-color: var(--amber);
  box-shadow: 0 0 10px -2px var(--amber);
}

/* ---------- Gear Box Opening screen (GearOpeningSequence.tsx) ----------
   GEAR BOX OPENING — CINEMATIC REDESIGN milestone: the same full-bleed
   cinematic-environment treatment HasherRevealSequence.tsx already
   established (.hasher-reveal-bg/-vignette/-dust), applied to the mine-
   tunnel background so the player stays in one continuous environment
   across all 3 Gear Boxes. Reuses the exact hasher-reveal-dust-drift
   keyframes (pure motion, not screen-specific) rather than duplicating
   them under a new name. */

.gear-opening-screen {
  position: relative;
  overflow: hidden;
}

.gear-opening-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  z-index: 0;
  pointer-events: none;
}

.gear-opening-vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 68% 62% at 50% 48%, transparent 38%, rgba(8, 6, 4, 0.58) 100%),
    radial-gradient(circle at 10% 22%, rgba(240, 169, 78, 0.14) 0%, transparent 26%),
    radial-gradient(circle at 90% 22%, rgba(240, 169, 78, 0.12) 0%, transparent 24%);
  animation: hasher-reveal-lantern-pulse 6s ease-in-out infinite alternate;
}

.gear-opening-dust {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
}

.gear-opening-dust-mote {
  position: absolute;
  left: calc(4% + (var(--hasher-dust-index) * 9.5%));
  bottom: -4%;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: rgba(240, 200, 140, 0.55);
  opacity: 0;
  animation: hasher-reveal-dust-drift 9s linear infinite;
  animation-delay: calc(var(--hasher-dust-index) * -0.9s);
}

.gear-opening-stage {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .gear-opening-vignette,
  .gear-opening-dust-mote {
    animation: none;
  }
}

/* ---------- Loot chest opening / Gear reveal (LootChestReveal.tsx) ----------
   Phase-driven presentation only — see lootRevealPhaseMachine.ts for the
   real state machine this CSS visualizes (closed -> anticipation ->
   opening -> card-rising -> card-floating -> card-flipping -> revealed).
   Every transition duration below is deliberately kept close to (never
   required to exactly match) LOOT_REVEAL_TIMING in
   lootRevealPresentation.ts — this is a visual crossfade/motion cue, not
   something the phase machine reads back.

   `--loot-reveal-color` (set inline on `.loot-reveal-stage`, resolved per
   gear.rarity by resolveLootRarityPresentation — Legendary/amber included)
   is now scoped to the REVEALED ITEM ONLY (.loot-reveal-card-aura,
   .loot-reveal-rarity-label) — GEAR BOX OPENING — CINEMATIC REDESIGN
   milestone's own "the GOLDEN crate light remains environmental... the
   rarity colour belongs specifically to the revealed gear." Every other
   element below (ambient glow, particles, chest flash, burst rays) uses
   the fixed warm-gold --gr-brass-bright token instead, same as
   HasherRevealSequence.tsx's own default un-rarity-tinted glow. */

.loot-reveal {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

.loot-reveal-stage {
  position: relative;
  width: 100%;
  max-width: 320px;
  height: clamp(220px, 62vw, 320px);
  margin: 0 auto;
}

/* GEAR BOX OPENING — CINEMATIC REDESIGN milestone: the crate is the hero
   object in the real onboarding flow — scoped to .gear-opening-screen
   only, so the dev-only Art QA Gallery's 5-wide compact preview row
   (ArtGalleryPanel.tsx, using this exact same .loot-reveal-stage class)
   keeps its existing small size untouched. */
/* Sized from HEIGHT + aspect-ratio (same technique .hasher-reveal-card-rise
   already uses), not width:100%/max-width — .onboarding-screen (combined
   on the same element as .gear-opening-screen) is itself a centering flex
   row, so any ancestor here only ever shrink-wraps to its content; a
   width percentage would have nothing definite to resolve against. Every
   child position inside (.loot-reveal-chest/.loot-reveal-card-rise) is
   already expressed as a % of THIS box, so they scale together with it
   regardless of its absolute pixel size. */
.gear-opening-screen .loot-reveal-stage {
  width: auto;
  max-width: none;
  height: clamp(360px, 58vh, 620px);
  aspect-ratio: 1 / 1;
  /* The revealed card rises above this box's own top edge by design
     (.loot-reveal-card-rise-up's translateY(-46%) of the CARD's own
     height) — that overshoot is absolutely positioned, so normal flow
     (and the outer .onboarding-screen's vertical centering) never
     accounts for it on its own. Reserve real margin for it so the
     heading above never collides with the revealed card, especially at
     shorter viewport heights (1366x768). */
  margin-top: 100px;
}

/* Environmental crate glow — fixed warm gold, never rarity-tinted (see
   this section's own header comment). Repositioned to bloom specifically
   around .loot-reveal-chest's own footprint rather than the whole stage,
   so it still reads as "light from the crate" now that the stage is much
   taller in the cinematic layout (room above for the rising card). */
.loot-reveal-ambient-glow {
  position: absolute;
  left: 50%;
  bottom: 2%;
  width: 100%;
  aspect-ratio: 1536 / 1024;
  transform: translateX(-50%);
  background: radial-gradient(circle, var(--gr-brass-bright) 0%, transparent 68%);
  opacity: 0.22;
  pointer-events: none;
  animation: loot-reveal-glow-pulse 2.4s ease-in-out infinite;
}

@keyframes loot-reveal-glow-pulse {
  0%,
  100% {
    opacity: 0.16;
  }
  50% {
    opacity: 0.3;
  }
}

/* Restrained idle dust off the crate — visible in every phase (including
   before the player has clicked "Open Gear Box"), unlike the one-shot
   opening dust burst (.loot-reveal-dust, below). Sparse (4) and slow, so it
   never reads as "loading" or a particle-system stress test. */
.loot-reveal-idle-dust {
  position: absolute;
  inset: 0;
  overflow: visible;
  pointer-events: none;
}

.loot-reveal-idle-dust-mote {
  position: absolute;
  left: calc(38% + (var(--loot-idle-dust-index) * 8%));
  bottom: 12%;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: rgba(255, 210, 140, 0.65);
  opacity: 0;
  animation: loot-reveal-idle-dust-drift 6s ease-in infinite;
  animation-delay: calc(var(--loot-idle-dust-index) * -1.5s);
}

@keyframes loot-reveal-idle-dust-drift {
  0% {
    opacity: 0;
    transform: translateY(0);
  }
  15% {
    opacity: 0.7;
  }
  85% {
    opacity: 0.15;
  }
  100% {
    opacity: 0;
    transform: translateY(-70px);
  }
}

/* Centred with left:11% ((100% - 78%) / 2), NOT left:50% + translateX(-50%)
   — the rumble/jolt below animate the individual `translate`/`rotate`
   properties on this element, and rotating an element that also carries a
   translateX(-50%) transform would pivot it around a point half a crate
   away from where it visually sits (an unwanted ~4px/deg vertical swing).
   transform-origin sits low so any rotation reads as weight settling on
   the ground, not floating. */
.loot-reveal-chest {
  position: absolute;
  left: 11%;
  bottom: 8%;
  width: 78%;
  aspect-ratio: 1536 / 1024;
  transform-origin: 50% 92%;
}

/* GEAR BOX OPENING — RUMBLE + DUST BURST milestone. Crate rumble/jolt:
   only the physical crate (this element and its layers) moves; every
   surrounding UI element stays put. Driven purely by the existing
   [data-phase] attribute — no timers, no changes to the phase machine.

   ANTICIPATION (900ms; Legendary 1300ms — LOOT_REVEAL_TIMING): a heavy,
   irregular shudder that starts almost imperceptibly and builds to about
   ±3.6px horizontal / ±1.8px vertical / ±1deg toward the end (never a
   bounce, squash, spin or large travel), returning to rest at 100%.
   Legendary's extra 400ms is a gentler ±0.6px lead-in, then the same build
   — the rumble simply builds for longer. `forwards` (not `both`) on the
   build so its 0% frame never overrides the lead-in during the delay.

   OPENING: one stronger jolt (~±4.5px / ±1.4deg, 300ms) as the open asset
   snaps in, then the OPEN crate is at rest — the rule stops matching in
   card-rising, so nothing keeps shaking while the gear rises. */
.loot-reveal[data-phase="anticipation"] .loot-reveal-chest {
  animation: loot-reveal-crate-rumble 0.9s linear forwards;
}

.loot-reveal[data-phase="anticipation"][data-rarity="legendary"] .loot-reveal-chest {
  animation:
    loot-reveal-crate-rumble-lead 0.4s linear,
    loot-reveal-crate-rumble 0.9s linear 0.4s forwards;
}

.loot-reveal[data-phase="opening"] .loot-reveal-chest {
  animation: loot-reveal-crate-jolt 0.3s linear forwards;
}

@keyframes loot-reveal-crate-rumble-lead {
  0% { translate: 0 0; rotate: 0deg; }
  10% { translate: 0.4px -0.2px; rotate: 0.08deg; }
  20% { translate: -0.5px 0.2px; rotate: -0.1deg; }
  30% { translate: 0.6px -0.3px; rotate: 0.12deg; }
  40% { translate: -0.3px 0.3px; rotate: -0.08deg; }
  50% { translate: -0.6px 0.1px; rotate: -0.12deg; }
  60% { translate: 0.5px -0.2px; rotate: 0.1deg; }
  70% { translate: 0.2px 0.2px; rotate: 0.05deg; }
  80% { translate: -0.5px -0.1px; rotate: -0.1deg; }
  90% { translate: 0.4px 0.2px; rotate: 0.08deg; }
  100% { translate: 0 0; rotate: 0deg; }
}

@keyframes loot-reveal-crate-rumble {
  0% { translate: 0 0; rotate: 0deg; }
  4% { translate: 0.6px -0.3px; rotate: 0.1deg; }
  8% { translate: -0.8px 0.4px; rotate: -0.15deg; }
  12% { translate: 0.9px -0.5px; rotate: 0.2deg; }
  16% { translate: 1px 0.3px; rotate: 0.18deg; }
  20% { translate: -1.2px -0.6px; rotate: -0.25deg; }
  24% { translate: 0.9px 0.5px; rotate: 0.3deg; }
  28% { translate: -1.4px 0.4px; rotate: -0.3deg; }
  32% { translate: -1.6px -0.7px; rotate: -0.35deg; }
  36% { translate: 1.3px 0.8px; rotate: 0.4deg; }
  40% { translate: -1.8px -0.6px; rotate: -0.4deg; }
  44% { translate: 2px 0.9px; rotate: 0.5deg; }
  48% { translate: 1.7px -0.8px; rotate: 0.45deg; }
  52% { translate: -2.2px 1px; rotate: -0.5deg; }
  56% { translate: -2.4px -0.9px; rotate: -0.55deg; }
  60% { translate: 2px 1.1px; rotate: 0.6deg; }
  64% { translate: -2.6px -1px; rotate: -0.6deg; }
  68% { translate: 2.8px 1.2px; rotate: 0.7deg; }
  72% { translate: 3px -1.1px; rotate: 0.75deg; }
  76% { translate: -2.6px 1.4px; rotate: -0.8deg; }
  80% { translate: -3.4px -1.2px; rotate: -0.85deg; }
  84% { translate: 3.2px 1.5px; rotate: 0.9deg; }
  88% { translate: -3.6px -1.6px; rotate: -0.95deg; }
  92% { translate: 3.4px 1.8px; rotate: 1deg; }
  96% { translate: -1.8px -0.8px; rotate: -0.5deg; }
  100% { translate: 0 0; rotate: 0deg; }
}

@keyframes loot-reveal-crate-jolt {
  0% { translate: 0 0; rotate: 0deg; }
  14% { translate: 4px -2.5px; rotate: 1.3deg; }
  34% { translate: -4.5px 1.2px; rotate: -1.4deg; }
  54% { translate: 2.8px -1px; rotate: 0.7deg; }
  74% { translate: -1.2px 0.5px; rotate: -0.3deg; }
  100% { translate: 0 0; rotate: 0deg; }
}

/* Warm light leaking around the closed crate's lid seam — restrained
   idle pulse, intensifying (via the [data-phase="anticipation"] override
   below) once the player commits to opening it. Positioned over the
   lid-line area of the mining-crate artwork's own 1536x1024 canvas. */
.loot-reveal-chest-seam-glow {
  position: absolute;
  left: 18%;
  top: 34%;
  width: 64%;
  height: 10%;
  background: radial-gradient(ellipse, rgba(255, 205, 120, 0.6) 0%, transparent 75%);
  opacity: 0.22;
  pointer-events: none;
  filter: blur(3px);
  animation: loot-reveal-seam-pulse 3.2s ease-in-out infinite;
}

@keyframes loot-reveal-seam-pulse {
  0%,
  100% {
    opacity: 0.18;
  }
  50% {
    opacity: 0.42;
  }
}

.loot-reveal[data-phase="anticipation"] .loot-reveal-chest-seam-glow {
  animation: loot-reveal-seam-pulse-intense 0.85s ease-in-out infinite;
}

@keyframes loot-reveal-seam-pulse-intense {
  0%,
  100% {
    opacity: 0.55;
  }
  50% {
    opacity: 0.95;
  }
}

.loot-reveal[data-phase="opening"] .loot-reveal-chest-seam-glow,
.loot-reveal[data-phase="card-rising"] .loot-reveal-chest-seam-glow,
.loot-reveal[data-phase="card-floating"] .loot-reveal-chest-seam-glow,
.loot-reveal[data-phase="card-flipping"] .loot-reveal-chest-seam-glow,
.loot-reveal[data-phase="revealed"] .loot-reveal-chest-seam-glow {
  animation: none;
  opacity: 0;
}

/* Extremely subtle "physical anticipation" breathing on the closed crate
   itself — never a bounce/shake, just enough scale+brightness drift to
   read as alive rather than a static image. */
.loot-reveal[data-phase="closed"] .loot-reveal-chest-closed {
  animation: loot-reveal-chest-idle-breathe 4s ease-in-out infinite;
}

@keyframes loot-reveal-chest-idle-breathe {
  0%,
  100% {
    transform: scale(1);
    filter: brightness(1);
  }
  50% {
    transform: scale(1.012);
    filter: brightness(1.04);
  }
}

.loot-reveal-chest-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* A lid snapping open is a cut, not a slow dissolve: the open asset fades
     in over 80ms (masked by the jolt + dust puff), then the closed asset
     drops out (see the :has() rule below) so the two never leave a
     see-through dip between them. */
  transition: opacity 0.08s linear;
}

.loot-reveal-chest-closed {
  opacity: 1;
}

.loot-reveal-chest-open {
  opacity: 0;
}

.loot-reveal-chest-open-visible {
  opacity: 1;
}

/* .loot-reveal-chest-open paints over .loot-reveal-chest-closed once
   visible (later in DOM order, same absolute-positioned footprint), but
   both source PNGs have real transparency — explicitly fade the closed
   image out too, so nothing shows through the open chest's lid gap. */
.loot-reveal-chest:has(.loot-reveal-chest-open-visible) .loot-reveal-chest-closed {
  opacity: 0;
  transition-delay: 0.08s;
  transition-duration: 0.02s;
}

.loot-reveal-chest-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 56px;
  line-height: 1;
}

/* GEAR BOX OPENING — RUMBLE + DUST BURST milestone: a compact, LOCAL mine-
   dust puff at the moment the lid opens — replaces the previous radial
   flash + conic "sunburst" rays (no star/spokes/magic rays/screen flash
   remain; the warm glow baked into loot_chest_open.png is the only light).
   Warm brown/tan dust, never rarity-tinted. Lives inside .loot-reveal-chest
   (origin as % of the crate's own box) and is unclipped — no ancestor sets
   overflow other than the full-screen .gear-opening-screen.

   Timing (all within the existing opening 500ms + card-rising 550ms, no
   added delay): puffs expand fast (~250ms = 26% of 950ms), then disperse
   softly over the remaining ~700ms; debris is thrown for ~850ms. Both rules
   also match card-rising AND card-floating so the animation isn't cancelled
   when the phase advances mid-flight (it ends on its own well before
   card-floating finishes, with slack for slow frame scheduling); a skip()
   straight to "revealed" simply drops it. */
.loot-reveal-dust {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
}

.loot-reveal-dust-puff {
  position: absolute;
  left: var(--dust-x);
  top: var(--dust-y);
  width: var(--dust-size);
  aspect-ratio: 1 / 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(206, 170, 120, 0.85) 0%,
    rgba(168, 128, 84, 0.55) 38%,
    rgba(120, 88, 56, 0.22) 62%,
    transparent 74%
  );
  filter: blur(5px);
  opacity: 0;
}

.loot-reveal[data-phase="opening"] .loot-reveal-dust-puff,
.loot-reveal[data-phase="card-rising"] .loot-reveal-dust-puff,
.loot-reveal[data-phase="card-floating"] .loot-reveal-dust-puff {
  animation: loot-reveal-dust-puff 0.95s ease-out var(--dust-delay, 0ms);
}

@keyframes loot-reveal-dust-puff {
  0% {
    opacity: 0;
    transform: translate(0, 0) scale(0.3);
  }
  26% {
    opacity: 0.92;
    transform: translate(calc(var(--dust-dx) * 0.55), calc(var(--dust-dy) * 0.55)) scale(0.95);
  }
  100% {
    opacity: 0;
    transform: translate(var(--dust-dx), var(--dust-dy)) scale(1.55);
  }
}

/* Small irregular debris/dust flecks thrown up from the lid seam — earthy,
   soft-edged, semi-transparent; a couple of tiny brighter "glint" flecks
   because the crate itself holds warm light. Fixed hand-placed set (see
   DUST_DEBRIS in LootChestReveal.tsx), thrown up then dropped by gravity. */
.loot-reveal-particle {
  position: absolute;
  left: var(--dust-x);
  top: var(--dust-y);
  width: var(--dust-size);
  height: var(--dust-size);
  translate: -50% -50%;
  border-radius: 46% 54% 50% 50% / 52% 48% 55% 45%;
  filter: blur(0.6px);
  opacity: 0;
}

.loot-reveal-particle-dark {
  background: rgba(96, 68, 42, 0.85);
}

.loot-reveal-particle-mid {
  background: rgba(158, 122, 80, 0.85);
}

.loot-reveal-particle-light {
  background: rgba(214, 182, 136, 0.8);
}

.loot-reveal-particle-glint {
  background: rgba(255, 222, 160, 0.95);
}

.loot-reveal[data-phase="opening"] .loot-reveal-particle,
.loot-reveal[data-phase="card-rising"] .loot-reveal-particle,
.loot-reveal[data-phase="card-floating"] .loot-reveal-particle {
  animation: loot-reveal-particle-burst 0.85s cubic-bezier(0.15, 0.7, 0.35, 1) var(--dust-delay, 0ms);
}

@keyframes loot-reveal-particle-burst {
  0% {
    opacity: 0;
    transform: translate(0, 0) scale(0.5);
  }
  10% {
    opacity: 0.95;
  }
  52% {
    opacity: 0.8;
    transform: translate(calc(var(--dust-dx) * 0.9), var(--dust-dy)) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(var(--dust-dx), calc(var(--dust-dy) + 30px)) scale(0.7);
  }
}

/* Reduced motion's replacement for the dust puff: a soft in-place fade, no
   travel/scale (see the reduced-motion block below). */
@keyframes loot-reveal-dust-fade {
  0% {
    opacity: 0;
  }
  30% {
    opacity: 0.4;
  }
  100% {
    opacity: 0;
  }
}

.loot-reveal-card-rise {
  position: absolute;
  left: 50%;
  bottom: 14%;
  width: 46%;
  max-width: 190px;
  aspect-ratio: 1024 / 1536;
  transform: translate(-50%, 0) scale(0.7);
  opacity: 0;
  perspective: 900px;
  transition: transform 0.55s cubic-bezier(0.34, 1.4, 0.4, 1), opacity 0.4s ease;
}

/* Scales the revealed item up to match the hero-sized crate in the real
   cinematic onboarding flow — same "scoped to .gear-opening-screen so the
   Art QA Gallery's compact preview is untouched" approach as the crate's
   own .gear-opening-screen .loot-reveal-stage override above. */
.gear-opening-screen .loot-reveal-card-rise {
  max-width: 320px;
}

.loot-reveal-card-rise-up {
  transform: translate(-50%, -46%) scale(1);
  opacity: 1;
  animation: loot-reveal-card-float 3s ease-in-out 0.6s infinite;
}

@keyframes loot-reveal-card-float {
  0%,
  100% {
    margin-top: 0;
  }
  50% {
    margin-top: -8px;
  }
}

/* Rarity-coloured aura behind the revealed item only — GEAR BOX OPENING
   — CINEMATIC REDESIGN milestone, same technique as .hasher-reveal-aura.
   var(--loot-reveal-color) is set inline on .loot-reveal-stage (an
   ancestor) by LootChestReveal.tsx, resolved per gear.rarity including
   Legendary/amber — never a second/new colour system. */
.loot-reveal-card-aura {
  position: absolute;
  inset: -45%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(circle at 50% 45%, var(--loot-reveal-color) 0%, var(--loot-reveal-color) 10%, transparent 58%);
  filter: blur(16px);
  opacity: 0;
  transition: opacity 0.7s ease;
}

.loot-reveal[data-phase="revealed"] .loot-reveal-card-aura {
  opacity: 0.4;
}

.loot-reveal-card-flip {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.7s cubic-bezier(0.45, 0.05, 0.15, 1);
}

.loot-reveal-card-flip-flipped {
  transform: rotateY(180deg);
}

.loot-reveal-card-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
}

.loot-reveal-card-back {
  background: var(--panel);
}

.loot-reveal-card-back img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.loot-reveal-card-front {
  transform: rotateY(180deg);
}

.loot-reveal-card-front .gear-card {
  max-width: none;
  width: 100%;
  height: 100%;
  box-shadow: none;
}

.loot-reveal-info {
  text-align: center;
  animation: onboarding-fade-in 0.3s ease-out;
}

.loot-reveal-slot {
  font-size: 13px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.loot-reveal-rarity-label {
  font-size: 20px;
  font-weight: 700;
  color: var(--loot-reveal-color);
}

@media (prefers-reduced-motion: reduce) {
  .loot-reveal-ambient-glow,
  .loot-reveal-card-rise-up,
  .loot-reveal-idle-dust-mote {
    animation: none;
  }
  /* These decorative loops/shakes are each triggered by a
     [data-phase="..."] rule more specific than a plain class selector —
     matched here (not just the bare class) so `animation: none` actually
     wins the cascade instead of losing to the more specific rule that
     started them. Reduced motion gets NO rumble and NO jolt: just the
     crate swap plus a soft in-place dust fade (below). */
  .loot-reveal-chest-seam-glow,
  .loot-reveal[data-phase="anticipation"] .loot-reveal-chest-seam-glow,
  .loot-reveal[data-phase="closed"] .loot-reveal-chest-closed,
  .loot-reveal[data-phase="anticipation"] .loot-reveal-chest,
  .loot-reveal[data-phase="anticipation"][data-rarity="legendary"] .loot-reveal-chest,
  .loot-reveal[data-phase="opening"] .loot-reveal-chest {
    animation: none;
  }
  .loot-reveal-chest-seam-glow {
    opacity: 0.3;
  }
  /* Debris flecks are travel-based — dropped entirely; the puff becomes a
     stationary fade (loot-reveal-dust-fade) instead of expanding/drifting. */
  .loot-reveal-particle {
    display: none;
  }
  .loot-reveal[data-phase="opening"] .loot-reveal-dust-puff,
  .loot-reveal[data-phase="card-rising"] .loot-reveal-dust-puff,
  .loot-reveal[data-phase="card-floating"] .loot-reveal-dust-puff {
    animation: loot-reveal-dust-fade 0.7s ease-out var(--dust-delay, 0ms);
  }
  .loot-reveal-chest:has(.loot-reveal-chest-open-visible) .loot-reveal-chest-closed {
    transition-duration: 0.01ms;
    transition-delay: 0s;
  }
  .loot-reveal-chest-img,
  .loot-reveal-card-rise,
  .loot-reveal-card-flip,
  .loot-reveal-card-aura {
    transition-duration: 0.01ms;
    animation-duration: 0.01ms;
  }
}

/* ---------- Shop 2 unlock popup (FTUE milestone) ---------- */

.shop2-unlock-dialog {
  text-align: center;
  animation: onboarding-flip-in 0.3s ease-out;
}

.shop2-unlock-title {
  color: var(--amber);
  font-size: 24px;
}

/* ==========================================================================
   DIGITAL GOLD RUSH milestone — Crew / Hasher Management screen
   (CrewManagementScreen.tsx). A frontier-mining-station equipment layout:
   weathered timber, blackened riveted steel, aged brass, warm restrained
   lantern light — never the app's own existing generic .panel look, and
   never WoW's own frames/fonts/ornamentation (structural reference only).
   ========================================================================== */

/* A fourth Modal size (see Modal.tsx's own doc comment on `chart`) — the
   central Hasher needs real room to "dominate the screen" on desktop
   without the existing .modal-dialog-wide's 660px cap. Every other Modal
   caller is completely unaffected — this class only ever applies when
   `fullscreen` is explicitly passed. */
.modal-dialog.modal-dialog-fullscreen {
  max-width: 1400px;
  width: 96vw;
  max-height: 92vh;
}

.crew-mgmt {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 4px 2px 2px;
  background: radial-gradient(ellipse 140% 90% at 50% -10%, var(--gr-timber-light) 0%, var(--gr-timber) 55%, var(--gr-rivet) 100%);
  border: 1px solid var(--gr-steel);
  border-radius: 10px;
}

/* ---------- Header: name, identity line, role controls ---------- */

.crew-mgmt-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 14px 16px 10px;
  text-align: center;
  border-bottom: 1px solid rgba(201, 151, 79, 0.25);
}

.crew-mgmt-name {
  /* Pass 7 — Hasher identity layer (Rye). font-weight:400, the only real
     weight Rye ships. */
  font-family: var(--font-frontier);
  font-size: 26px;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--gr-brass);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
}

.crew-mgmt-identity {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--text-dim);
}

.crew-mgmt-role-active {
  color: var(--ok);
}

.crew-mgmt-role-reserve {
  color: var(--text-dim);
}

.crew-mgmt-role-controls {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 4px;
}

.crew-mgmt-role-locked {
  align-items: center;
}

.crew-mgmt-role-tag {
  display: inline-flex;
  align-items: center;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--gr-brass-dim);
  color: var(--gr-brass);
}

.crew-mgmt-role-tag-editable {
  border-color: var(--ok);
  color: var(--ok);
}

/* ---------- Body: gear columns flank the large central stage ---------- */

/* VISUAL PASS 2 — bigger, closer equipment slots. The middle (stage)
   column used to be `1fr`, so it stretched to fill every pixel of
   whatever width the fullscreen modal happened to have, while the
   portrait inside it stayed capped at 360px — the flanking slot columns
   sat pinned near the grid's own outer edges, far from the character,
   with dead space on both sides of the portrait. Bounding the stage
   column and adding `justify-content: center` makes the whole three-
   column group size to its own content and center as one unit, so the
   slot columns end up hugging the portrait at every desktop width
   instead of being pushed apart by an elastic middle column — a layout
   fix, not a hardcoded margin that would only hold at one resolution. */
.crew-mgmt-body {
  display: grid;
  grid-template-columns: minmax(210px, 260px) minmax(340px, 420px) minmax(210px, 260px);
  justify-content: center;
  gap: 26px;
  align-items: center;
  padding: 4px 16px;
}

.crew-mgmt-slots {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.crew-mgmt-slot {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 96px;
  padding: 10px 14px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
  transition: border-color 0.15s ease, transform 0.1s ease;
}

.crew-mgmt-slot:hover {
  border-color: var(--gr-brass);
  transform: translateY(-1px);
}

/* Damaged-Gear Repair Controls milestone — the slot is no longer itself a
   button (a REPAIR control now sits beside it as a sibling, and two
   interactive elements can never nest); `.crew-mgmt-slot-open` is the
   inner button carrying the exact same reset/cursor/focus treatment the
   whole card used to carry. */
.crew-mgmt-slot-open {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1 1 auto;
  min-width: 0;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  appearance: none;
  text-align: left;
}

.crew-mgmt-slot-open:focus-visible {
  outline: 2px solid var(--gr-brass);
  outline-offset: 2px;
}

/* Rarity layered onto the frontier frame (Gear Rarity + Card Selector
   visual pass) — never a full recolor: the steel/brass gradient stays as
   the base `background` layer, a low-opacity rarity-tinted radial sits
   UNDER it as a second background (so the plate/rivet character shows
   through), and the border + a soft outer glow carry the rest of the
   signal. Only ever applied when `data-rarity` is actually set — the
   component never sets it on an empty slot, so empty slots stay neutral
   by construction, not by a separate override rule here. Common gets no
   accent (its own canonical color, --rich-dim, IS this frame's existing
   brass/steel neutral, so painting it again would be redundant); mythic
   is intentionally absent — future/disabled Gear is never equippable. */
.crew-mgmt-slot[data-rarity] {
  --slot-rarity: var(--rich-dim);
}
.crew-mgmt-slot[data-rarity="uncommon"] {
  --slot-rarity: var(--ok);
}
.crew-mgmt-slot[data-rarity="rare"] {
  --slot-rarity: var(--poor);
}
.crew-mgmt-slot[data-rarity="epic"] {
  --slot-rarity: var(--epic);
}
.crew-mgmt-slot[data-rarity="legendary"] {
  --slot-rarity: var(--amber);
}
.crew-mgmt-slot[data-rarity="uncommon"],
.crew-mgmt-slot[data-rarity="rare"],
.crew-mgmt-slot[data-rarity="epic"],
.crew-mgmt-slot[data-rarity="legendary"] {
  border-color: color-mix(in srgb, var(--slot-rarity) 55%, var(--gr-brass-dim));
  background:
    radial-gradient(ellipse 90% 75% at 30% 15%, color-mix(in srgb, var(--slot-rarity) 18%, transparent) 0%, transparent 72%),
    linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--slot-rarity) 32%, transparent), 0 0 16px -7px var(--slot-rarity);
}
.crew-mgmt-slot[data-rarity="uncommon"]:hover,
.crew-mgmt-slot[data-rarity="rare"]:hover,
.crew-mgmt-slot[data-rarity="epic"]:hover,
.crew-mgmt-slot[data-rarity="legendary"]:hover {
  border-color: var(--slot-rarity);
}

/* VISUAL PASS 4 — Rare read as noticeably more subtle than Uncommon at
   the shared intensity above (browser-inspection finding), so Rare alone
   gets a ~15% stronger border/glow: a richer border mix and a wider,
   less-clipped glow. Uncommon/Epic/Legendary are deliberately untouched
   — this is not a global intensity change. */
.crew-mgmt-slot[data-rarity="rare"] {
  border-color: color-mix(in srgb, var(--slot-rarity) 65%, var(--gr-brass-dim));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--slot-rarity) 38%, transparent), 0 0 18px -6px var(--slot-rarity);
}

/* Damaged-Gear Repair Controls milestone — REPAIR sits beside the card on
   the OUTER edge (away from the central portrait) for both columns, a
   true mirror: the left column's whole row is reversed (REPAIR, then the
   open-button) while the right column's open-button alone is reversed
   (art/text swap, unchanged from before) and REPAIR simply follows it in
   DOM order, landing on the right column's own outer (right) edge. */
.crew-mgmt-slot-left {
  flex-direction: row-reverse;
}

.crew-mgmt-slot-right .crew-mgmt-slot-open {
  flex-direction: row-reverse;
  text-align: right;
}

/* The shared `.gear-repair-controls` block defaults to vertical-stack
   spacing (margin-top, full-width button) for the Equipment tab's card
   column — here it's a horizontal row sibling instead, so it needs
   neither. */
.crew-mgmt-slot > .gear-repair-controls {
  margin-top: 0;
  flex: 0 0 auto;
}

.crew-mgmt-slot-art {
  flex: 0 0 auto;
  display: flex;
}

/* Empty-slot "mounting plate" — an engraved brass/steel plate rather than a
   generic dashed SaaS dropzone (Phase 8's own explicit instruction). The
   slot glyph is drawn with plain CSS (a Unicode symbol via `content`), not
   a new image asset. */
.crew-mgmt-slot-plate {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  height: 84px;
  border-radius: 8px;
  background: radial-gradient(circle at 40% 32%, #3a3f46 0%, var(--gr-steel-dark) 75%);
  border: 1px solid var(--gr-rivet);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.18);
  font-size: 34px;
  color: var(--gr-brass-dim);
}

.crew-mgmt-slot-plate::before {
  content: "⛏";
}
.crew-mgmt-slot-plate[data-slot="helmet"]::before {
  content: "⛑";
}
.crew-mgmt-slot-plate[data-slot="chest"]::before {
  content: "🛡";
}
.crew-mgmt-slot-plate[data-slot="gloves"]::before {
  content: "🧤";
}
.crew-mgmt-slot-plate[data-slot="pants"]::before {
  content: "👖";
}
.crew-mgmt-slot-plate[data-slot="boots"]::before {
  content: "🥾";
}
.crew-mgmt-slot-plate[data-slot="accessory"]::before {
  content: "💍";
}

.crew-mgmt-slot-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.crew-mgmt-slot-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--gr-brass);
}

.crew-mgmt-slot-summary {
  font-size: 13px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Multi-stat Gear stacks vertically — one bonus per line (Gear Rarity +
   Card Selector visual pass's own explicit request) rather than a single
   joined "+5 SPD +4 INT" string, which got cramped once slots grew in
   visual pass 2. Font size deliberately NOT shrunk to force a fit. */
.crew-mgmt-slot-stats {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.crew-mgmt-slot-stat-line {
  font-size: 13px;
  font-weight: 600;
  color: var(--ok);
  white-space: nowrap;
}

/* ---------- Center stage: portrait, minimal stats, navigation ---------- */

.crew-mgmt-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.crew-mgmt-portrait-wrap {
  position: relative;
  width: 100%;
  max-width: 360px;
  aspect-ratio: 3 / 4;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation: crew-mgmt-portrait-in 0.28s ease-out;
}

@keyframes crew-mgmt-portrait-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Restrained rarity treatment BEHIND the character — never a border/glow
   drawn over the artwork itself (Phase 4's "do NOT let the rarity glow
   overpower the artwork"). Gear Rarity + Card Selector visual pass:
   colors now come from the SAME canonical rarity tokens the Loot Chest
   Reveal already uses (`lootRevealPresentation.ts`'s own
   `--rich-dim`/`--ok`/`--poor`/`--epic`/`--amber` mapping,
   docs/ASSET_BIBLE.md Section 8's locked "everywhere, always" rule) —
   replacing this file's own earlier ad-hoc `--gr-rarity-*` tokens, which
   duplicated that palette with slightly different hues instead of
   reusing it. */
.crew-mgmt-portrait-glow {
  position: absolute;
  inset: 6% 4% 0;
  border-radius: 50% 50% 20% 20% / 60% 60% 20% 20%;
  background: radial-gradient(ellipse 70% 60% at 50% 30%, var(--rarity-glow, var(--rich-dim)) 0%, transparent 70%);
  opacity: 0.22;
  pointer-events: none;
}

.crew-mgmt-portrait-wrap[data-rarity="common"] {
  --rarity-glow: var(--rich-dim);
}
.crew-mgmt-portrait-wrap[data-rarity="uncommon"] {
  --rarity-glow: var(--ok);
}
.crew-mgmt-portrait-wrap[data-rarity="rare"] {
  --rarity-glow: var(--poor);
}
.crew-mgmt-portrait-wrap[data-rarity="epic"] {
  --rarity-glow: var(--epic);
}

.crew-mgmt-portrait-wrap[data-rarity="legendary"] {
  --rarity-glow: var(--gr-brass-bright);
}

.crew-mgmt-portrait {
  position: relative;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 14px 18px rgba(0, 0, 0, 0.55));
}

/* The website's approved Hasher card: shown whole, never cropped or stretched, with the saved name and stats on its nameplate. */
.crew-mgmt-portrait.crew-mgmt-portrait-website {
  width: auto;
  height: 100%;
  aspect-ratio: 2 / 3;
  max-width: 100%;
  border-radius: 10px;
  filter: drop-shadow(0 14px 18px rgba(0, 0, 0, 0.55));
}

.crew-mgmt-portrait-empty {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  color: var(--text-dim);
  font-size: 13px;
  border: 1px dashed var(--gr-brass-dim);
  border-radius: 12px;
}

.crew-mgmt-stats {
  display: flex;
  flex-wrap: wrap; /* a narrow phone wraps the third stat rather than overflowing */
  justify-content: center;
  gap: 4px 18px;
  font-size: 17px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.crew-mgmt-stat {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--text);
}

.crew-mgmt-stat-value {
  color: var(--gr-brass);
}

/* The active equipped-Gear total for the stat, in green next to the gold base value (omitted when it is zero). */
.crew-mgmt-stat-bonus {
  color: var(--ok);
  font-weight: 800;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.7);
}

/* On a phone the three stats (base + gear) still fit on one line: a touch smaller, a touch closer; the wrap above is only the fallback for the narrowest screens. */
@media (max-width: 480px) {
  .crew-mgmt-stats {
    font-size: 15px;
    gap: 4px 10px;
  }
  .crew-mgmt-stat {
    gap: 3px;
  }
}

.crew-mgmt-nav {
  display: flex;
  align-items: center;
  gap: 12px;
}

.crew-mgmt-nav-counter {
  font-size: 12px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  min-width: 42px;
  text-align: center;
}

/* ---------- Footer: crew overview medallions + weekly stats ---------- */

.crew-mgmt-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  padding: 10px 16px 14px;
  border-top: 1px solid rgba(201, 151, 79, 0.25);
}

.crew-mgmt-overview {
  display: flex;
  gap: 8px;
}

.crew-mgmt-medallion {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 4px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 999px;
  cursor: pointer;
  appearance: none;
  transition: border-color 0.15s ease, transform 0.1s ease;
}

.crew-mgmt-medallion:hover:not(:disabled) {
  border-color: var(--gr-brass);
  transform: translateY(-1px);
}

.crew-mgmt-medallion:disabled {
  cursor: default;
  opacity: 0.55;
}

.crew-mgmt-medallion-selected {
  border-color: var(--ok);
  box-shadow: 0 0 0 2px rgba(111, 191, 115, 0.35);
}

.hasher-portrait-icon {
  display: inline-flex;
  overflow: hidden;
  border-radius: 50%;
  background: var(--gr-steel-dark);
}

.hasher-portrait-icon-small {
  width: 30px;
  height: 30px;
}

.hasher-portrait-icon-medium {
  width: 48px;
  height: 48px;
}

.hasher-portrait-icon-art {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 15%;
}

/* Website card face cropped to its portrait window (about 9-91% wide, 13-75% tall of the card) so a full card reads as a round portrait. */
.hasher-portrait-icon-art.hasher-portrait-icon-website-art {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 36%;
  transform: scale(1.7);
  transform-origin: 50% 36%;
}
/* The large circle (Marketplace) is big enough to show the card's printed nameplate at the bottom of the 1.7x crop, cut through its
   CLASS / HP INT SPD lettering; this tighter crop keeps to the artwork above it (about 12-45% of the card's height). */
.hasher-portrait-icon-large .hasher-portrait-icon-art.hasher-portrait-icon-website-art {
  object-position: 50% 0%;
  transform: scale(2);
  transform-origin: 50% 42.7%;
}

.hasher-portrait-icon-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-size: 14px;
}

.crew-mgmt-medallion-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  color: var(--text-dim);
  font-size: 12px;
}

.crew-mgmt-medallion-label {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--text-dim);
}

.crew-mgmt-weekly {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  padding: 6px 14px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 999px;
}

.crew-mgmt-weekly-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  text-transform: uppercase;
}

.crew-mgmt-weekly-badge {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--gr-rivet);
  background: var(--gr-brass);
  padding: 2px 8px;
  border-radius: 999px;
}

.crew-mgmt-weekly-preview {
  border-style: dashed;
}

.crew-mgmt-empty {
  padding: 24px;
}

/* ---------- Responsive: compress before shrinking the character (Phase 15) ---------- */

@media (max-width: 1360px) {
  .crew-mgmt-body {
    grid-template-columns: minmax(170px, 210px) minmax(300px, 360px) minmax(170px, 210px);
    gap: 18px;
  }
  .crew-mgmt-slot {
    min-height: 82px;
    padding: 8px 10px;
    gap: 10px;
  }
  .crew-mgmt-slot-plate {
    width: 68px;
    height: 68px;
    font-size: 28px;
  }
}

@media (max-width: 900px) {
  .crew-mgmt-body {
    grid-template-columns: 1fr;
  }
  .crew-mgmt-slots {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
  }
  .crew-mgmt-slot {
    flex: 1 1 45%;
    min-height: 76px;
  }
  .crew-mgmt-slot-left {
    flex-direction: row;
  }
  .crew-mgmt-slot-right .crew-mgmt-slot-open {
    flex-direction: row;
    text-align: left;
  }
  .crew-mgmt-footer {
    justify-content: center;
    text-align: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  .crew-mgmt-portrait-wrap {
    animation: none;
  }
}

/* ==========================================================================
   GEAR RARITY + CARD SELECTOR visual pass — the large, card-based Gear
   selector opened from a Crew Management equipment slot
   (GearCardSelector.tsx). Same Digital Gold Rush frontier-industrial
   language as `.crew-mgmt` (weathered timber, blackened steel, aged
   brass), never a plain grey settings-dialog look. Reuses `.modal-backdrop`
   as its base (proven z-index/centering, `GearSlotPicker`'s own working
   pattern) with a warmer, slightly lighter tint of its own so the dimmed
   Crew Management screen stays perceptible behind it, per this pass's own
   "keep it embedded" requirement — never a second, near-opaque backdrop
   stacked on top of the outer Modal's own.
   ========================================================================== */

.gear-selector-backdrop {
  background: rgba(12, 9, 6, 0.58);
}

.gear-selector-panel {
  display: flex;
  flex-direction: column;
  width: 74vw;
  max-width: 1180px;
  min-width: 340px;
  max-height: 86vh;
  padding: 20px 24px;
  gap: 12px;
  background: radial-gradient(ellipse 140% 90% at 50% -10%, var(--gr-timber-light) 0%, var(--gr-timber) 55%, var(--gr-rivet) 100%);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55);
}

.gear-selector-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid rgba(201, 151, 79, 0.25);
}

.gear-selector-header h2 {
  /* Pass 7 — "Select {Slot}" is a functional dialog instruction, not
     game-world signage (unlike "Equipment"/"Gear Inventory" itself), so
     it stays in the functional Inter layer rather than Rye. */
  margin: 0;
  font-family: var(--font-interface);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--gr-brass);
}

.gear-selector-section-label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 6px;
}

.gear-selector-current {
  flex: 0 0 auto;
}

.gear-selector-current-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 8px;
}

.gear-selector-current-row > .gear-repair-controls {
  margin-top: 0;
  flex: 0 0 auto;
}

.gear-selector-current-summary {
  font-size: 13px;
  color: var(--text);
  text-transform: capitalize;
}

/* The scrolling region — only the grid scrolls (Phase 10's own "do not
   make the entire Crew Management screen scroll awkwardly"); header,
   current-item row, and the action row below all stay pinned.

   VISUAL PASS 4 — `auto-fit` (not `auto-fill`) + a firm pixel max (not
   `1fr`) + `justify-content: center`: with the old `auto-fill`/`1fr`
   combination, a 1-3 item collection still reserved every column track
   the full row COULD hold and stretched real cards to fill them, so a
   couple of cards ended up pinned at the left edge of a mostly-empty
   panel. `auto-fit` collapses the unused trailing tracks instead of
   keeping them reserved, so the group of real cards — each capped at its
   own natural max width, never stretched — becomes exactly as wide as
   its content, and `justify-content: center` then centers that group
   within the panel. A full row still wraps and fills the width exactly
   as before; this only changes what happens when there's not enough
   content to fill one. */
.gear-selector-grid {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 260px));
  justify-content: center;
  gap: 18px;
  padding: 4px 2px 8px;
}

.gear-selector-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
}

/* ~18% larger than GearCard's own default 220px cap — a scoped override
   so GearCard.tsx and its other callers (Loot Chest Reveal, the Art QA
   Gallery) keep their existing size untouched; only this selector's own
   presentation of the same, unmodified card component grows. */
.gear-selector-card .gear-card {
  max-width: 260px;
}

/* Rarity reads through border + soft glow, same canonical tokens and same
   layered technique as the equipped Crew slots — "a Rare card should
   instantly look Rare" without ever recoloring the card's own frame art. */
.gear-selector-card[data-rarity="uncommon"] {
  border-color: var(--ok);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--ok) 35%, transparent), 0 0 18px -8px var(--ok);
}
.gear-selector-card[data-rarity="rare"] {
  border-color: var(--poor);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--poor) 35%, transparent), 0 0 18px -8px var(--poor);
}
.gear-selector-card[data-rarity="epic"] {
  border-color: var(--epic);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--epic) 35%, transparent), 0 0 18px -8px var(--epic);
}
.gear-selector-card[data-rarity="legendary"] {
  border-color: var(--amber);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--amber) 35%, transparent), 0 0 20px -8px var(--amber);
}

/* Multi-stat Gear stacks vertically here too — a scoped override of
   GearCard's own default flex-wrap row, so GearCard.tsx itself (and its
   OTHER callers — Loot Chest Reveal, the Art QA Gallery) stay completely
   unmodified and keep their existing horizontal layout. */
.gear-selector-card .gear-card-stats {
  flex-direction: column;
  align-items: center;
  gap: 3px;
}

.gear-selector-card-status {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  font-size: 11px;
  text-align: center;
}

.gear-selector-card-status-label {
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 10px;
}

.gear-selector-card-status-value {
  color: var(--gr-brass);
  font-weight: 600;
}

.gear-selector-card-listed-badge {
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--danger);
  text-transform: uppercase;
}

/* Listed Gear must visually read unavailable — desaturated and dimmed,
   never hidden or silently delisted; the Equip/Move Here action is simply
   omitted for these (Phase 14's own "Do NOT allow Equip"). */
.gear-selector-card-listed {
  filter: grayscale(0.6);
  opacity: 0.6;
}

.gear-selector-card-compare {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.gear-selector-compare-positive {
  color: var(--ok);
}

.gear-selector-compare-negative {
  color: var(--danger);
}

.gear-selector-compare-neutral {
  color: var(--text-dim);
}

.gear-selector-card-action {
  width: 100%;
}

.gear-selector-card > .gear-repair-controls {
  margin-top: 0;
}

.gear-selector-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid rgba(201, 151, 79, 0.25);
}

@media (max-width: 900px) {
  .gear-selector-panel {
    width: 94vw;
    max-height: 90vh;
  }
  .gear-selector-grid {
    grid-template-columns: repeat(auto-fit, minmax(150px, 200px));
  }
}

/* ==========================================================================
   DIGITAL GOLD RUSH — HASHER MAIN SCREEN milestone. Same frontier-
   industrial language the Crew Management milestone established (weathered
   timber, blackened steel, aged brass, warm lantern light) applied to the
   main gameplay screen: a deep-mine facility the board sits inside, rather
   than a dashboard the board happens to be embedded in. Scoped to classes
   only this screen uses (.facility-*, .app-shell, .app-header, .action-bar,
   .sell-panel, .trading-placeholder-panel, .hasher-portrait-icon) — the
   shared .panel/.btn bases every other screen (Shop, Marketplace, Trading
   modal, ...) still relies on are deliberately untouched.
   ========================================================================== */

/* Underground mine environment — Corrective Pass 3. Pass 2's version of
   this was audited and rejected: at low opacity, a thin repeating
   diagonal stripe plus a generic corner vignette reads as "dark webpage,"
   not "underground." This keeps the same constraint (Phase 7's own
   "environment remains around/behind gameplay... do not allocate half
   the screen to scenery" — no new image asset, no dedicated scenic
   section, restrained to the margins) but makes each element genuinely
   legible: distinct warm lantern pools (not a uniform glow), mottled
   rock-wall blotches (not a flat vignette), and real rock striation —
   all still fixed/behind everything, zero layout cost. Most of it only
   becomes visible in .app-shell's own margin (see that rule's Pass 3
   note) and behind panel gaps; the board gets its own dedicated framing
   below (.board-panel). */
body {
  background:
    /* warm lantern pools — three fixed glows suggesting hung lamps, not a single uniform wash */
    radial-gradient(circle 320px at 6% 8%, rgba(240, 169, 78, 0.16) 0%, transparent 70%),
    radial-gradient(circle 360px at 95% 14%, rgba(240, 169, 78, 0.13) 0%, transparent 70%),
    radial-gradient(circle 420px at 50% 104%, rgba(240, 169, 78, 0.11) 0%, transparent 65%),
    /* timber support beams — two distinct vertical bands near the outer
       edges (roughly where .app-shell's own margin sits at desktop
       width), not a repeating pattern, so they read as structure */
    linear-gradient(
      90deg,
      transparent 0%,
      transparent 2.4%,
      rgba(18, 13, 9, 0.6) 2.4%,
      rgba(58, 42, 29, 0.4) 3.1%,
      rgba(18, 13, 9, 0.6) 3.8%,
      transparent 4.2%,
      transparent 100%
    ),
    linear-gradient(
      90deg,
      transparent 0%,
      transparent 95.8%,
      rgba(18, 13, 9, 0.6) 96.2%,
      rgba(58, 42, 29, 0.4) 96.9%,
      rgba(18, 13, 9, 0.6) 97.6%,
      transparent 97.6%,
      transparent 100%
    ),
    /* mottled rock-wall blotches — irregular, genuinely visible patches rather than one flat gradient */
    radial-gradient(ellipse 46% 38% at 14% 26%, rgba(72, 56, 43, 0.32) 0%, transparent 72%),
    radial-gradient(ellipse 50% 42% at 86% 68%, rgba(64, 50, 39, 0.3) 0%, transparent 72%),
    radial-gradient(ellipse 60% 40% at 50% 2%, rgba(58, 42, 29, 0.4) 0%, transparent 62%),
    radial-gradient(ellipse 55% 45% at 8% 92%, rgba(50, 40, 32, 0.32) 0%, transparent 68%),
    radial-gradient(ellipse 55% 45% at 92% 96%, rgba(50, 40, 32, 0.32) 0%, transparent 68%),
    /* rock striation, kept but strengthened slightly */
    repeating-linear-gradient(
      115deg,
      rgba(255, 255, 255, 0.02) 0px,
      rgba(255, 255, 255, 0.02) 2px,
      transparent 2px,
      transparent 24px
    ),
    /* base vignette, darkest at the corners */
    radial-gradient(ellipse 120% 90% at 50% 100%, rgba(0, 0, 0, 0.6) 0%, transparent 60%),
    radial-gradient(ellipse 140% 100% at 0% 50%, rgba(0, 0, 0, 0.5) 0%, transparent 42%),
    radial-gradient(ellipse 140% 100% at 100% 50%, rgba(0, 0, 0, 0.5) 0%, transparent 42%),
    var(--coal);
  background-attachment: fixed;
}

/* ---------- Header + nav: one continuous industrial signage band ---------- */

.facility-header {
  padding: 8px 18px;
  display: flex;
  align-items: center;
  gap: 14px;
  background: linear-gradient(180deg, var(--gr-timber-light) 0%, var(--gr-timber) 100%);
  border: 1px solid var(--gr-brass-dim);
  border-bottom: none;
  border-radius: 10px 10px 0 0;
  box-shadow: 0 1px 0 rgba(201, 151, 79, 0.25) inset;
  position: relative;
}

/* The user's supplied Option 4 logo artwork, used exactly as delivered
   (resolveLogoAsset) — never regenerated/redesigned. Pass 4 — bumped
   46px -> 54px (~17%) for real branding presence; the header's own
   padding stays unchanged so it grows only as much as the taller logo
   itself requires, not dramatically. The image's own transparent
   background lets the header's timber gradient show around it. */
.facility-logo {
  height: 54px;
  width: auto;
  flex-shrink: 0;
  display: block;
}

.facility-header-text {
  min-width: 0;
}

.facility-header-env {
  color: var(--text-dim);
  font-weight: 400;
  font-size: 14px;
}

.facility-header::before,
.facility-header::after {
  content: "";
  position: absolute;
  top: 8px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #d9b56c 0%, var(--gr-rivet) 75%);
  opacity: 0.85;
}
.facility-header::before {
  left: 8px;
}
.facility-header::after {
  right: 8px;
}

.facility-header h1 {
  /* Pass 7 — world/identity layer. Rye ships one real weight; explicit
     font-weight:400 avoids the browser synthetically bolding it (h1's
     UA-default bold), which reads muddy on a decorative face. */
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: 20px;
  letter-spacing: 0.02em;
  color: var(--gr-brass);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
}

.facility-header .app-subtitle {
  margin-bottom: 0;
}

/* The action bar immediately follows the header in the DOM — sharing its
   brass border and losing the top radius makes the two read as one
   continuous mounted signage/control band (Phase 5's own "visually
   integrate them into the industrial header", without moving ActionBar's
   own conditional render tree). */
.action-bar {
  background: linear-gradient(180deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border: 1px solid var(--gr-brass-dim);
  border-top: 1px solid rgba(201, 151, 79, 0.2);
  border-radius: 0 0 10px 10px;
  padding: 8px 16px;
  /* Pass 4 tightened 10px -> 7px; Pass 5B tightens once more, 7px -> 4px,
     as one of several small contributors toward fitting the full board +
     legend within one viewport (see the final report for the full list
     and the measured before/after). */
  margin-bottom: 4px;
}

/* Pass 9 Option D — approved final nav construction. Scoped to
   `.action-bar` specifically (never the bare `.btn`/`.btn-primary`
   classes, which are shared by ~40 unrelated buttons across the app —
   modals, Sell BTC, etc.) so only the main facility nav row gets the
   Oswald/machine-control treatment; every other button in the app keeps
   its existing plain `.btn` styling untouched. Oswald (machine-control
   layer) replaces Inter here only for the button LABEL text — every
   number/value anywhere else on screen stays Inter, per the approved
   hierarchy. Charcoal/blackened-steel face + thin brass perimeter +
   inset highlight/shadow for physical depth, matching the audition's
   "controls mounted into mining equipment, not browser buttons" brief;
   `.action-bar`'s own steel gradient (above) is the row's shared
   backing, this is each individual control's own face on top of it. */
.action-bar .btn {
  font-family: var(--font-control);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.035em;
  font-size: 13px;
  background: var(--gr-steel-dark);
  border-color: var(--gr-brass-dim);
  border-radius: 4px;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.05) inset,
    0 1px 2px rgba(0, 0, 0, 0.4);
}

.action-bar .btn:not(:disabled):hover {
  /* Overrides the generic `.btn:hover:not(:disabled)` amber border
     (line ~1176) with the same brass perimeter the rest of this control
     family uses — "brass perimeter slightly brighter," not a color-
     family switch to amber. */
  border-color: var(--gr-brass);
  filter: brightness(1.08);
}

.action-bar .btn:not(:disabled):active {
  filter: brightness(0.9);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6) inset;
  transform: translateY(1px);
}

.action-bar .btn:disabled {
  background: var(--gr-steel-dark);
  border-color: #4a4034;
  color: #6b6f78;
  /* Overrides the generic `.btn:disabled` 0.45 opacity — full opacity
     here so the muted steel/brass/text colors above (not transparency)
     are what read as "disabled," matching the approved treatment. */
  opacity: 1;
}

.action-bar .btn-primary {
  /* Scoped override of the generic amber `.btn-primary` (line ~1185,
     shared by every other primary button in the app) — Start Day gets
     its own aged-brass/muted-gold face instead of flat amber, still
     visually "the primary control" but in this row's own material
     language. */
  background: linear-gradient(160deg, #dcae63 0%, var(--gr-brass) 55%, var(--gr-brass-dim) 100%);
  border-color: #6b4f28;
  color: #201a12;
  font-weight: 700;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.25) inset,
    0 2px 3px rgba(0, 0, 0, 0.5);
}

/* HASHER TRADING — LIQUIDATION BLOCK REWARDS milestone: an always-visible
   readout, now a deliberate facility status panel (`.facility-status-panel`
   supplies the background/border/padding/radius) rather than a nav-button
   look-alike — restrained and informational by default, not a slot-machine
   callout. Still a real <button> (click opens Trading), just no longer
   skinned like one. */
.liquidation-block-badge {
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 2px;
  cursor: pointer;
  font-family: var(--font-control);
  line-height: 1.25;
  text-align: left;
}

.liquidation-block-badge:hover {
  border-color: var(--gr-brass);
  filter: brightness(1.08);
}

.liquidation-block-badge-label {
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* The primary value — deliberately the single largest, boldest element in
   either status panel so it reads as the dominant figure at a glance. */
.liquidation-block-badge-pool {
  font-size: 22px;
  font-weight: 700;
  color: var(--gr-brass-bright);
  font-variant-numeric: tabular-nums;
}

.liquidation-block-badge-countdown {
  font-size: 12px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* ---------- Top instrument strip: BTC / Cash / Production + Day timer ---------- */

/* Corrective Pass 3 — Section 9's own "stop making everything a
   rectangle" instruction: Pass 1/2 had this as two visually separate
   boxes (the stats strip, then a second bordered box for the Day timer)
   sitting side by side. This is now ONE physical instrument panel — a
   single shared background/border/rivets on the wrapper — with the Day
   timer as a trailing segment inside it, divided by a thin internal seam
   rather than its own frame. */
.facility-top-strip {
  display: flex;
  align-items: stretch;
  gap: 0;
  /* Pass 5B — tightened 10px -> 4px, part of fitting the full board +
     legend within one viewport. */
  margin-bottom: 4px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border: 1px solid var(--gr-brass-dim);
  border-radius: var(--radius);
  overflow: hidden;
}

/* TOOLBAR CORRECTION PASS — StatsHeader now has this strip entirely to
   itself (the Daily Operating Time widget that used to render a second
   column here was removed, not just hidden; its one live instance now
   lives in ActionBar's own `.action-bar-status-group`). `flex: 1 1 auto`
   already let it fill whatever width was free, so it now simply spans
   the full strip with no further layout change needed — confirmed by
   live measurement at both 1920x1080 and 1366x768. */
.facility-top-strip .stats-header {
  flex: 1 1 auto;
  margin-bottom: 0;
  background: none;
  border: none;
  box-shadow: none;
}

.facility-status-strip {
  display: flex;
  align-items: stretch;
  gap: 0;
}

/* Pass 4 — the divider between metric groups is now a two-tone groove
   (a dark seam with a faint highlight immediately after it) instead of a
   single flat line, so it reads as a seam between machined plates rather
   than a spreadsheet column rule. */
.facility-status-block {
  padding: 10px 18px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border-right: 1px solid rgba(0, 0, 0, 0.5);
  box-shadow: 1px 0 0 rgba(255, 255, 255, 0.05);
}

.facility-status-block:last-child {
  border-right: none;
  box-shadow: none;
}

/* Pass 4 — a tiny physical indicator beside each group label (Wallet /
   Production), the kind of small engraved marker a real instrument panel
   would carry, reinforcing "machine" over "metrics row". Purely
   decorative, matches the existing rivet/brass palette. */
.facility-top-strip .stats-group-heading {
  display: flex;
  align-items: center;
  gap: 5px;
}
.facility-top-strip .stats-group-heading::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #e8c789 0%, var(--gr-brass-dim) 75%);
  flex-shrink: 0;
}

.facility-status-btc {
  flex: 1.4 1 0;
}

.facility-production-block {
  flex: 1 1 0;
}

/* Cash — deliberately its own small, plain block (Phase 13's own explicit
   "no duplicated financial metrics" instruction): a label and one large
   brass-emphasised number, nothing else. */
.facility-cash-block {
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 130px;
}

.facility-cash-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}

.facility-cash-value {
  font-size: 26px;
  font-weight: 700;
  /* Pass 9 Option D — Cash is the "positive/available" value in the
     approved semantic hierarchy; brass now belongs to BTC Balance
     (see [data-testid="stat-btc-balance"] below) instead. */
  color: var(--ok);
  font-variant-numeric: tabular-nums;
}

/* Pass 9 Option D — BTC Balance reads as the valuable-asset/gold value;
   --gr-brass-bright (not plain --gr-brass) per the approved audition's
   "modestly increase brightness/contrast" note. Every other stat value
   (BTC Price, Total BTC Mined, Week Time Remaining, Base production)
   stays the plain neutral .stat-value color — deliberately untouched,
   including BTC Price, which stays neutral because the current market-
   data architecture has no authoritative current-UTC-day opening price
   to truthfully compare against (see btcMarketClient.ts /
   BtcMarketService's own getAuthoritativeCandle doc comment). */
[data-testid="stat-btc-balance"] {
  color: var(--gr-brass-bright);
  font-weight: 700;
}

/* ---------- Left column: Crew status ---------- */

/* Corrective Pass 3 — no longer stretched to the board's height (that
   forced height is exactly what Pass 2's giant vertical member cards
   grew to fill). This panel now sizes itself from its own compact
   content and stops well above the board's bottom edge, as a genuine
   roster panel rather than a full-height dashboard column. */
/* Pass 4 — softened from a flat opaque card: the timber gradient now lets
   a little of the underlying mine environment bleed through
   (color-mix'd toward transparent, same tokens, never new colors), the
   border drops to a thin translucent brass line instead of a solid one,
   and an inset vignette (below, in the shared mounting rule) gives the
   panel a recessed, mounted-into-the-wall feel instead of a raised card
   sitting on top of the page. */
.facility-crew-panel {
  background: radial-gradient(
    ellipse 140% 90% at 50% -10%,
    color-mix(in srgb, var(--gr-timber-light) 90%, transparent) 0%,
    color-mix(in srgb, var(--gr-timber) 92%, transparent) 55%,
    color-mix(in srgb, var(--gr-rivet) 94%, transparent) 100%
  );
  border: 1px solid rgba(201, 151, 79, 0.3);
  padding: 12px;
}

/* ---------- Shared physical-mounting treatment ("push the material
   treatment further... functional panels mounted into a mining machine")
   — a warm inset top edge (like light catching a beveled physical panel)
   plus corner rivets. Corrective Pass 3: now applied to the OUTER
   mounted units only (Crew roster, the top instrument strip, and the one
   Operations console) — .sell-panel/.trading-placeholder-panel dropped
   from this group since they're no longer independently mounted panels,
   just sections inside the console (see the Operations rules below), so
   they no longer carry their own rivets/shadow. ---------- */
.facility-crew-panel,
.facility-operations-console,
.facility-top-strip {
  position: relative;
  box-shadow: 0 1px 0 rgba(201, 151, 79, 0.2) inset, 0 4px 10px rgba(0, 0, 0, 0.35);
}

/* Pass 4 — Crew and Operations read as MOUNTED (recessed into the
   facility wall) rather than raised dashboard cards: a soft inward
   vignette on top of the shared top-edge highlight above. */
.facility-crew-panel,
.facility-operations-console {
  box-shadow:
    0 1px 0 rgba(201, 151, 79, 0.2) inset,
    0 0 26px rgba(0, 0, 0, 0.4) inset,
    0 4px 10px rgba(0, 0, 0, 0.35);
}

/* The top instrument strip wants MORE physical presence instead — a real
   inset bevel, like a recessed panel, rather than the softening above. */
.facility-top-strip {
  box-shadow:
    0 1px 0 rgba(201, 151, 79, 0.2) inset,
    inset 0 2px 5px rgba(0, 0, 0, 0.45),
    0 4px 10px rgba(0, 0, 0, 0.35);
}

.facility-crew-panel::before,
.facility-crew-panel::after,
.facility-operations-console::before,
.facility-operations-console::after,
.facility-top-strip::before,
.facility-top-strip::after {
  content: "";
  position: absolute;
  top: 8px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #d9b56c 0%, var(--gr-rivet) 75%);
  opacity: 0.85;
  pointer-events: none;
}
.facility-crew-panel::before,
.facility-operations-console::before,
.facility-top-strip::before {
  left: 8px;
}
.facility-crew-panel::after,
.facility-operations-console::after,
.facility-top-strip::after {
  right: 8px;
}

.facility-crew-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.facility-crew-panel-header h2 {
  /* Pass 7 — world/identity layer. Same specificity as the generic
     `.panel h2` (line ~171) but later in source order, so this wins for
     the properties it declares; text-transform/letter-spacing reset per
     rule 15's "current semantic casing preferred" for this heading set.
     Pass 7B — color explicitly matched to Operations' brass/gold
     treatment (flagged as undecided in the Pass 7 report; user
     confirmed brass is correct). */
  margin: 0;
  font-family: var(--font-frontier);
  font-weight: 400;
  color: var(--gr-brass);
  font-size: 17px;
  text-transform: none;
  letter-spacing: 0;
}

.facility-crew-lock-badge {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--gr-steel-dark);
  border: 1px solid var(--gr-brass-dim);
  color: var(--text-dim);
}

.facility-crew-lock-badge-locked {
  border-color: var(--ok);
  color: var(--ok);
}

/* Pass 5 — changed from a horizontal row of three heads to a VERTICAL
   roster (portrait left, name/type/role/stats stacked right) per this
   pass's own explicit direction, to make better use of the column's
   height. Still explicitly NOT Pass 2's rejected direction: there is no
   background/border/padding box around each row below — only a hairline
   divider between rows (.facility-crew-member + .facility-crew-member)
   stands in for separation, and the whole roster lives inside the one
   .facility-crew-panel frame, never a card-within-a-card. */
.facility-crew-portraits {
  display: flex;
  flex-direction: column;
  margin-bottom: 12px;
}

.facility-crew-member {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 14px;
  /* Pass 5B — a little more breathing room per row (10px -> 14px) per
     review: "three clearly separated characters rather than one
     compressed list." Still just padding + a hairline divider, no card
     box, and the panel doesn't grow dramatically (3 rows × 4px extra ≈
     12px total). */
  padding: 14px 0;
}

.facility-crew-member + .facility-crew-member {
  border-top: 1px solid rgba(201, 151, 79, 0.15);
}

.facility-crew-member-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}

.facility-crew-member-role {
  align-self: flex-start;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gr-rivet);
  background: var(--gr-brass);
  padding: 2px 9px;
  border-radius: 999px;
  margin-top: 2px;
}

/* Pass 4 caught truncated names ("Boomtown B...") at 13px/nowrap/
   ellipsis in the old 96px-wide horizontal layout; fixed there by
   dropping to 11px and allowing wrap. The vertical roster's text column
   is wider (the row's remaining width after the portrait, not a fixed
   96px), so there's more natural room, but the same small size + wrap
   safety net is kept rather than assuming the extra width is always
   enough — re-verified against the full 49-name pool this pass too (see
   final report). */
.facility-crew-member-name {
  /* Pass 7 — Hasher identity layer (Rye). font-weight:400, the only real
     weight Rye ships, to avoid synthetic bold at this small size. */
  font-family: var(--font-frontier);
  font-size: 14px;
  font-weight: 400;
  color: var(--text);
  line-height: 1.2;
  white-space: normal;
  overflow-wrap: break-word;
  max-width: 100%;
}

.facility-crew-member-type {
  font-size: 10.5px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Individual current Hasher stats (Pass 5) — deliberately smaller/dimmer
   than the Weekly Crew summary below (.facility-crew-weekly-stats), so
   the two never read as the same kind of number at a glance. */
.facility-crew-member-stats {
  display: flex;
  gap: 10px;
  font-size: 11.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  margin-top: 2px;
}

.facility-crew-member-empty {
  width: 84px;
  height: 84px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--gr-rivet);
  color: var(--text-dim);
  flex-shrink: 0;
}

/* Weekly frozen/live crew stats — condensed from the old inline paragraph
   explanation (Pass 2's own explicit "too visually heavy for the main
   game screen" finding) to one short label + three icon stats. The full
   original sentence is preserved verbatim as this element's own `title`
   tooltip (CrewSummaryPanel.tsx) rather than deleted. */
.facility-crew-weekly {
  background: var(--gr-steel-dark);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 8px;
  padding: 8px 10px;
  margin-bottom: 10px;
  cursor: help;
}

.facility-crew-weekly .crew-stats-scope-note {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gr-brass);
  margin-bottom: 4px;
}

.facility-crew-weekly-stats {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.facility-manage-crew-btn {
  width: 100%;
}

/* Portrait rarity rim — same canonical tokens the Crew Management screen
   already uses (--ok/--poor/--epic), a restrained ring + soft glow rather
   than a recolored portrait. Hasher rarity only has four live tiers
   (common/uncommon/rare/epic — see Phase 8's own explicit "do NOT
   introduce Legendary or Mythic Hashers"); common gets no ring at all.
   Pass 5 — 96px -> 84px (within this pass's own 80-96px range) to help
   three full roster rows fit the column height without growing it
   past the board. */
.hasher-portrait-icon-large {
  width: 84px;
  height: 84px;
  flex-shrink: 0;
  border: 3px solid var(--gr-brass-dim);
}

.hasher-portrait-icon[data-rarity="uncommon"] {
  border-color: var(--ok);
  box-shadow: 0 0 0 2px rgba(111, 191, 115, 0.25), 0 0 10px -3px var(--ok);
}
.hasher-portrait-icon[data-rarity="rare"] {
  border-color: var(--poor);
  box-shadow: 0 0 0 2px rgba(60, 110, 143, 0.32), 0 0 12px -3px var(--poor);
}
.hasher-portrait-icon[data-rarity="epic"] {
  border-color: var(--epic);
  box-shadow: 0 0 0 2px rgba(138, 107, 176, 0.3), 0 0 12px -3px var(--epic);
}

/* ---------- Right column: ONE Operations console ---------- */

/* Corrective Pass 3, Section 6/9 — Sell BTC and Trading Account used to be
   two independently boxed panels, which (a) read as more dashboard
   rectangles and (b) left a large empty region below them once their
   combined natural height fell short of the board's. Both now live
   inside ONE physical console (this wrapper carries the background/
   border/rivets — see the shared mounting-treatment rule above), sized
   to its own content and never stretched to match the board. */
.facility-operations-console {
  background: radial-gradient(
    ellipse 140% 90% at 50% -10%,
    color-mix(in srgb, var(--gr-timber-light) 90%, transparent) 0%,
    color-mix(in srgb, var(--gr-timber) 92%, transparent) 55%,
    color-mix(in srgb, var(--gr-rivet) 94%, transparent) 100%
  );
  border: 1px solid rgba(201, 151, 79, 0.3);
  /* Pass 4 — tightened from 14px to match the smaller section gaps below. */
  padding: 12px;
}

h2.facility-operations-heading {
  /* Pass 7 — world/identity layer. Rule 15's "current semantic casing
     preferred" for this heading set means dropping the forced uppercase
     now that it's set in a display face rather than a functional label.
     Pass 7B fix: this element's own parent section carries class
     "panel" (`className="panel facility-operations-console"`), so the
     generic `.panel h2` (line 185) matches it too — a bare class
     selector here (specificity 0,1,0) was LOSING to `.panel h2`'s
     descendant selector (0,1,0 + 0,0,1 = 0,1,1) for color/text-transform
     regardless of source order, silently keeping this heading text-dim/
     uppercase instead of the intended brass/sentence-case. Adding the
     `h2` type selector brings this rule to (0,1,1), tying `.panel h2`
     and winning on source order like `.facility-crew-panel-header h2`
     already correctly does. */
  font-family: var(--font-frontier);
  font-weight: 400;
  margin: 0 0 8px 0;
  font-size: 17px;
  text-transform: none;
  letter-spacing: 0;
  color: var(--gr-brass);
}

/* The nested Sell BTC / Trading Account sections drop their own
   independent panel chrome (background/border/radius/shadow/margin) now
   that they're sections of the one console above — a thin top divider
   between sections stands in for the separate boxes, and each section's
   own h2 becomes a smaller sub-heading rather than a competing panel
   title. */
.facility-operations-console .panel {
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
  margin: 0;
}

/* Pass 4 — section gaps tightened 14px -> 10px (the console read as
   "tall and sparse" per review); still a real, deliberate seam between
   sections, just a tighter one. */
.facility-operations-console > .panel + .panel,
.facility-operations-console > .facility-operations-equipment {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid rgba(201, 151, 79, 0.2);
}

.facility-operations-console .panel h2 {
  color: var(--gr-brass);
  font-size: 13px;
  margin-bottom: 6px;
}

/* Pass 7B — reverted from Pass 7's Rye treatment: at this small size Rye
   read as "fiddly" on live review. EQUIPMENT belongs with SELL BTC and
   TRADING ACCOUNT as the three clean functional sub-sections inside
   OPERATIONS (which stays Rye as the panel's own identity heading) — so
   this now matches Sell BTC's own family exactly: brass/13px/uppercase
   from this rule, Inter + bold inherited from body/h2 defaults (the same
   two-rule cascade Sell BTC itself relies on, no dedicated font-family
   override needed since Inter is already the base font). */
.facility-operations-equipment h2 {
  color: var(--gr-brass);
  font-size: 13px;
  margin: 0 0 6px 0;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Pass 7B — Trading Account heading, also reverted from Pass 7's Rye
   treatment to the same Inter functional family as Sell BTC/Equipment
   above. Two separate components both render this literal text:
   TradingAccountPanel (`.trading-account-summary`, the standalone
   Trading modal's full detail view — NOT nested inside
   `.facility-operations-console`, so it needs its own explicit color/
   uppercase rather than inheriting them) and TradingAccountSummaryPanel
   (`.trading-placeholder-panel`, the Operations console's sidebar
   version, which already inherits brass/13px from
   `.facility-operations-console .panel h2` above and uppercase from the
   generic `.panel h2` — restated explicitly here too so both instances
   are guaranteed to match regardless of nesting context). */
.panel.trading-account-summary h2,
.panel.trading-placeholder-panel h2 {
  font-family: var(--font-interface);
  font-weight: 700;
  color: var(--gr-brass);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.facility-operations-equipment-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
}

.facility-operations-equipment-list li {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  color: var(--text-dim);
}

.facility-operations-equipment-list li span:last-child {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* Keyboard focus for the Sell BTC panel's interactive controls — same
   gold/amber outline language .workshop-slot:focus-visible already
   established, not the removed persistent green pulse. */
.sell-panel input:focus-visible,
.sell-panel button:focus-visible {
  outline: 2px solid var(--gr-brass-bright);
  outline-offset: 2px;
}

/* The Sell BTC submit control — "a substantial physical control" (Pass 2's
   own instruction), restrained brass emphasis, never touching the shared
   .btn-primary other screens (Shop, modals, ...) still use unmodified. */
.sell-panel [data-testid="sell-open-confirm"] {
  width: 100%;
  padding: 12px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: linear-gradient(180deg, #dcae64 0%, var(--gr-brass) 55%, var(--gr-brass-dim) 100%);
  border: 1px solid #6e5326;
  color: var(--gr-rivet);
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.25) inset;
}
/* Pass 4 — a restrained warm glow (never neon) when the action is actually
   available, so the button reads as "ready" rather than just decorative;
   :disabled below stays flat/muted so the unavailable state is unambiguous. */
.sell-panel [data-testid="sell-open-confirm"]:not(:disabled) {
  box-shadow:
    0 2px 0 rgba(0, 0, 0, 0.3),
    0 1px 0 rgba(255, 255, 255, 0.25) inset,
    0 0 14px -4px rgba(240, 169, 78, 0.55);
}

.sell-panel [data-testid="sell-open-confirm"]:disabled {
  background: var(--gr-steel-dark);
  border-color: var(--gr-brass-dim);
  color: var(--text-dim);
  box-shadow: none;
}

/* ---------- Developer Tools disclosure (Pass 2) — visually separated
   from the player-facing game, collapsed by default. Muted/utility
   styling deliberately distinct from the warm facility chrome above, so
   scrolling past it reads as "leaving the game", not "more game". ---------- */
.facility-dev-tools {
  margin-top: 18px;
  border-top: 1px dashed var(--panel-border);
  padding-top: 10px;
}

.facility-dev-tools summary {
  cursor: pointer;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  padding: 4px 0;
}

.facility-dev-tools summary:hover {
  color: var(--text);
}

.facility-dev-tools[open] summary {
  margin-bottom: 8px;
}

/* BigInt Crash Fix + Dev Test Controls milestone — CrewWorkshop.tsx itself
   stays deliberately free of developer markup (see CrewWorkshopDevTools.tsx's
   own doc comment), so this renders as a small fixed-position overlay
   ABOVE the full-viewport Workshop rather than inline within its layout.
   Same muted/collapsed-by-default styling as .facility-dev-tools above,
   just repositioned since the Workshop has no header flow to sit inside. */
.crew-workshop-dev-tools {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 50;
  max-width: 260px;
  padding: 8px 10px;
  background: rgba(20, 16, 12, 0.85);
  border: 1px dashed var(--panel-border);
  border-radius: 6px;
}

.crew-workshop-dev-tools summary {
  cursor: pointer;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}

.crew-workshop-dev-tools summary:hover {
  color: var(--text);
}

.crew-workshop-dev-tools[open] summary {
  margin-bottom: 8px;
}

/* ---------- Board frame: a mounted plate around the facility's own perimeter ----------
   The board itself — .board-platform's transform/border/background/shadow,
   the tile grid, terrain art — is completely untouched (Phase 3/13's own
   explicit protection). This only ADDS aged-brass corner bolts as
   decorative pseudo-elements on top of the existing platform, which
   inherit its own rotateX tilt for free (they're real children of the
   platform), never replacing any of its existing declarations.
   (New Board Integration: the bolts themselves are now retired below, and
   the platform is `transform-style: flat` — see .board-platform above.) */
/* Pass 6 — audited and reduced. Corrective Pass 3's rock-wall-blotch and
   lantern-glow gradients, and Pass 4's "hollowed cavity" gradient, were
   CSS stand-ins for a real environment; now that the approved mine-
   environment artwork (.board-mine-environment, below) provides real
   rocks/timber/lanterns at far higher fidelity, those stand-in layers
   would only fight the real artwork for the same visual job. Reduced to
   a single soft dark vignette — just enough to seat the artwork's own
   edges into this panel's background (Phase 12's blending requirement),
   not a competing "second environment." The two CSS timber-beam
   pseudo-elements this rule used to carry are removed outright — the
   artwork has its own, far more detailed timber posts. */
.board-panel {
  position: relative;
  background: radial-gradient(ellipse 70% 60% at 50% 50%, rgba(4, 3, 2, 0.4) 0%, transparent 70%);
  border-radius: 12px;
  /* Pass 4 trimmed top padding 10px -> 6px; Pass 5B trims once more,
     6px -> 3px, toward fitting the full board + legend within one
     viewport (see final report). */
  /* New Board Integration — top padding 3px -> 20px: reserves room ABOVE the
     board for the physical frame's top rim, which paints above
     .board-outer's layout box (transforms don't take layout space) and was
     being clipped by .app-main's overflow:hidden / the toolbar above.
     Measured live (real GL output): the rim reaches 12px above .board-outer
     at u=240 and ~4px at u=400 (-0.048px per px of u, ±2px); 20px = the
     measured worst case at the smallest supported size + the 8px clear
     margin wanted under the toolbar, less this rule's old 3px. */
  padding: 20px 26px 4px;
  /* Pass 6B — the overflow:hidden Pass 6 added here (to stop the
     oversized artwork from painting over Crew/Operations) is REMOVED:
     the user explicitly asked for more of the artwork to stay visible
     around the board, not clipped tightly to this one column. The clip
     boundary has moved out to .app-main (the whole Crew+Board+Operations
     row) and the Crew/Operations-safety guarantee is now handled by
     z-index layering (.app-crew-column/.app-board-column/.app-side-column,
     see .app-main above) instead of a tight clip here. No overflow
     declared here at all now — same as this rule's pre-Pass-6 state. */
}

.board-platform {
  position: relative;
  /* Pass 5 — real z-index (was implicit/auto) so the platform reliably
     paints above the environment layer added as its sibling in
     .board-outer (Pass 6: the approved mine artwork; previously the
     removed CSS rock experiments), regardless of DOM order. Additive
     only: transform/border/background/box-shadow above remain
     completely untouched. */
  z-index: 1;
}

.board-platform::before,
.board-platform::after {
  /* New Board Integration — these two CSS brass bolts belonged to the
     retired CSS platform chrome; the new board GLB carries its own corner
     fittings, so drawing these too would double them up (and they'd read
     as added symbols on a board that must carry none). */
  display: none;
  content: "";
  position: absolute;
  top: 8px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #e8c789 0%, var(--gr-brass) 45%, #5c431f 100%);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
  z-index: 1;
}
.board-platform::before {
  left: 8px;
}
.board-platform::after {
  right: 8px;
}

/* ---------- Mine-environment background infill — "Close the Remaining
   Gaps" pass ----------
   Two large rectangles sitting BEHIND the approved Layer 1/Layer 2
   artwork below (both left completely untouched by this pass). Measured
   live at 1920x1080 via getBoundingClientRect + a canvas readback of the
   PNG's own alpha channel: neither art layer has opaque content directly
   above the board or in a narrow strip right of the board, so those
   patches were showing .app-main's plain backdrop gradient through
   instead of mine material. Each rectangle uses the SAME approved PNG as
   a CSS background-image (same file precedent as .tile-rich/.tile-poor
   above), scaled up (background-size, one axis auto so aspect ratio
   can't distort) and positioned (background-position) so a LARGE,
   fully-opaque, generically dark/textured corner of the source art fills
   it — verified pixel-by-pixel fully opaque (no transparent speckling)
   before picking these two crops, so neither can show a stray hole.
   Both rectangles are sized past the measured void on every side, so
   their own edges land underneath Layer 1/Layer 2's existing opaque rock
   (or behind .board-platform itself where they dip below the board's own
   top edge) — the crop boundary is hidden by the art already on top of
   it, not by a fade. z-index -2 keeps both behind Layer 1 (0), Layer 2
   (-1) and the platform (1); .app-main's overflow:hidden still bounds
   them exactly as it already bounds Layer 2. pointer-events:none plus
   aria-hidden (Board.tsx) means neither can intercept a tile click or
   reach the accessibility tree. */
.board-mine-environment-infill-top {
  position: absolute;
  left: 11.8%;
  top: -6.42%;
  width: 86.33%;
  height: 26%;
  background-image: url("/assets/environment/hasher_mine_environment.png");
  background-repeat: no-repeat;
  background-size: 285% auto;
  background-position: 0px -1232px;
  z-index: -2;
  pointer-events: none;
}

.board-mine-environment-infill-right {
  position: absolute;
  left: 112.52%;
  top: 7.34%;
  width: 26.16%;
  height: 93.27%;
  background-image: url("/assets/environment/hasher_mine_environment.png");
  background-repeat: no-repeat;
  background-size: 724% auto;
  background-position: -1240px -460px;
  z-index: -2;
  pointer-events: none;
}

/* ---------- Approved mine-environment artwork — Pass 6 ----------
   Replaces the Pass 5 (six large circular blobs) and Pass 5B (fourteen
   angular chunks in five clusters) CSS rock experiments — both rejected
   on live review. This is real production art (Board.tsx,
   resolveHasherMineEnvironmentAsset), used exactly as supplied: no
   recolor, no recrop, no filter — only CSS scale/position/mask.
   Positioned relative to .board-outer (same technique the removed CSS
   rocks used) so it tracks the platform's actual rendered bounds at any
   viewport/tile-size. `height: auto` with only `width` set means the
   browser scales the 1448x1086 source at its own native aspect ratio —
   it is structurally impossible for this rule to stretch/distort it.
   z-index:0 keeps it behind .board-platform (z-index:1 above);
   pointer-events:none plus aria-hidden/alt="" (Board.tsx) means it can
   never intercept a tile click or reach the accessibility tree. The
   mask-image feathers the artwork's own rectangular edge into
   transparency (Phase 12's blending requirement) — the source PNG pixels
   are never touched, only their on-screen alpha falls off near the
   boundary, so the rock/timber/lantern detail itself stays fully
   visible well inside that edge. */
.board-mine-environment {
  position: absolute;
  left: 50%;
  top: 50%;
  /* Pass 6C — reverted to the original Pass 6 145%: pixel-analysis of the
     PNG's own alpha channel (decoded this pass, see the doc comment in
     Board.tsx) found the source art's real rock/timber content is a
     relatively thin ring hugging the transparent opening — scaling this
     whole unit up (Pass 6B/8's 280%) grew the opening right along with
     the ring, so the "extra space" it exposed was mostly more of the
     SAME hole (showing plain page background), not new rock. 145% is the
     scale where this layer's opening actually matches the board, giving
     the tight, correctly-fitted board frame the user already approved —
     reaching farther out is now .board-mine-environment-backdrop's job
     (below), a second, larger instance of the same file. */
  width: 145%;
  height: auto;
  /* The artwork's transparent opening is not perfectly centered in the
     source image (measured from the real PNG alpha data: opening center
     sits at ~50.9% width / ~46.5% height, not 50/50 — there's more
     rubble/floor below than headroom above). translate() percentages
     are relative to the ELEMENT's own box, so translating by the
     opening's own offset (instead of the usual -50%/-50%) aligns the
     opening's actual center with .board-outer's center, not the image's
     geometric center. Unchanged since Pass 6 — this alignment math is
     independent of the element's own width, so both this layer and the
     backdrop layer below share the exact same centering and stay
     concentric with each other and with the board. */
  transform: translate(-50.9%, -46.5%);
  z-index: 0;
  pointer-events: none;
  /* Pass 6C — at 145% this layer's own rock content already reaches
     almost exactly to its own canvas edge in every direction (measured
     from the real alpha data: e.g. the right-side rock extends to within
     1px of the image's own boundary) — there is no meaningful "outer
     margin" left to fade through, so this is now just a light corner/
     edge feather rather than the large falloff earlier passes needed to
     survive a tight external CSS clip. */
  -webkit-mask-image: radial-gradient(ellipse 68% 68% at 50% 50%, #000 82%, transparent 100%);
  mask-image: radial-gradient(ellipse 68% 68% at 50% 50%, #000 82%, transparent 100%);
}

/* Pass 6C — the second, larger instance of the SAME mine-environment PNG
   (Board.tsx renders it as a sibling <img>, same src/resolver, same
   centering transform). Deliberately a "donut": transparent through its
   own oversized opening (that whole middle zone is .board-mine-
   environment's job above, and the real board/platform's job on top of
   that), opaque through its own ring (picking up roughly where the layer
   above's rock ends), transparent again past the ring's own natural end.
   z-index:-1 (not 0) — explicitly BELOW .board-mine-environment despite
   coming after it in DOM order, so the tight board-hugging frame always
   paints on top of this coarser, larger-scaled backdrop rather than the
   reverse. Width/position unchanged from Pass 6C (250%, same centering
   transform as Layer 1 — Layer 1 stays locked, and this layer's own
   opening alignment doesn't need pixel precision since the donut mask
   already hides its whole inner region regardless). */
.board-mine-environment-backdrop {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 250%;
  height: auto;
  transform: translate(-50.9%, -46.5%);
  z-index: -1;
  pointer-events: none;
  /* Pass 6C-final — the ONLY change from the approved Pass 6C composition:
     the last two stops moved from 95%/108% to 90%/115%, a small widening
     of the final transparency transition (13 points -> 25 points) so the
     very outermost boundary softens a little more gradually into the
     page background. The first two stops (55%/72%, covering the whole
     inner/mid ring the user approved) are untouched, and the ellipse's
     own size/position (50% 50% at 50% 50%) is untouched — this is a
     values-only tweak to the existing mask, not a reshape. */
  -webkit-mask-image: radial-gradient(ellipse 50% 50% at 50% 50%, transparent 55%, #000 72%, #000 90%, transparent 115%);
  mask-image: radial-gradient(ellipse 50% 50% at 50% 50%, transparent 55%, #000 72%, #000 90%, transparent 115%);
}

/* Pass 5B — this breakpoint used to read "max-width: 1600px," which
   includes the 1600×900 target viewport itself: stacking the Day-timer
   widget below the stats strip there roughly TRIPLED the top strip's
   height (measured live: ~90px in a row vs ~260-270px stacked), which
   was the dominant reason the board+legend didn't fit the viewport at
   1600×900. Live-verified that the row layout still fits cleanly at
   1600px wide with no horizontal overflow and no internal wrapping
   before lowering this to 1400px, so 1600×900 now keeps the compact row
   strip; only genuinely narrower widths still get the stacked fallback. */
@media (max-width: 1400px) {
  .facility-cash-value {
    font-size: 22px;
  }
}

@media (max-width: 1100px) {
  .facility-status-strip {
    flex-wrap: wrap;
  }
  .facility-status-block {
    border-right: none;
    border-bottom: 1px solid rgba(201, 151, 79, 0.2);
  }
}

/* ==========================================================================
   Shared status-Badge primitive (Badge.tsx). Previously unstyled anywhere
   in this file — every existing "coloured pill" instead grew its own
   bespoke class (.shop-unlock-badge, .hasher-card-badge, etc.). The
   VOLATILITY LEAGUE TRADING TERMINAL milestone leans on the shared
   component heavily (tournament status, RED/GREEN candle results, League
   outcomes), so it needs real styling now — additive only, touches no
   existing bespoke badge class or any other screen's layout.
   ========================================================================== */

.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.6;
  border: 1px solid transparent;
}

.badge-neutral {
  background: rgba(154, 160, 168, 0.16);
  color: var(--text-dim);
  border-color: rgba(154, 160, 168, 0.3);
}

.badge-success {
  background: rgba(111, 191, 115, 0.16);
  color: var(--ok);
  border-color: rgba(111, 191, 115, 0.4);
}

.badge-danger {
  background: rgba(209, 91, 91, 0.16);
  color: var(--danger);
  border-color: rgba(209, 91, 91, 0.4);
}

.badge-warning {
  background: rgba(201, 151, 79, 0.18);
  color: var(--gr-brass-bright);
  border-color: rgba(201, 151, 79, 0.45);
}

.badge-info {
  background: rgba(127, 212, 255, 0.14);
  color: var(--cooled-glow);
  border-color: rgba(127, 212, 255, 0.35);
}

/* ==========================================================================
   VOLATILITY LEAGUE TRADING TERMINAL milestone (VolatilityModal.tsx /
   VolatilityLeagueSection.tsx / BtcTerminalChart.tsx). Digital Gold Rush
   visual language reused verbatim from the Crew / Hasher Management
   screen's own confirmed tokens (--gr-timber/--gr-steel/--gr-brass/
   --gr-rivet, --font-frontier/--font-control/--font-interface,
   --danger/--ok as the market RED/GREEN accents) — additive only, no new
   palette, no new font import. Scoped entirely under "vt-" (Volatility
   Terminal) class names; nothing here touches any other screen.
   ========================================================================== */

/* VOLATILITY EXCHANGE mock-up milestone, item 1 — a genuine in-world
   POP-OUT, not a full-screen takeover: ~94vw × ~90vh (the approved
   92-95vw / 88-92vh target), leaving a real visible margin of the
   Hasher facility around every edge (the shared `.modal-backdrop`
   already sits at 65% black beneath this — restrained darkening, no
   opaque replacement — see the backdrop-blur rule just below). Scoped
   by data-testid, not a change to Modal.tsx's own `fullscreen` size for
   every other caller (rule: "do not sweep every modal automatically"). */
[data-testid="volatility-modal"].modal-dialog-fullscreen {
  max-width: 1760px;
  width: 94vw;
  max-height: 90vh;
}

/* A very light backdrop blur specifically behind Volatility — the
   facility stays clearly recognizable (never opaque, never heavily
   blurred), it just reads as "behind glass" rather than a flat dark
   fill, reinforcing the pop-out-terminal feel. Every other modal's
   backdrop is untouched. */
[data-testid="volatility-modal-backdrop"] {
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

/* VISUAL PASS 3 — WORLD BUILDING milestone. The Volatility interface is
   meant to read as ONE physical exchange terminal mounted into the Red &
   Green world, not a stack of independently floating cards on a plain
   brown fill. This outer housing — scoped to the volatility modal's own
   `.modal-content-body` only, every other modal is untouched — is the
   "console" the mode switches and the terminal screen are mounted into:
   a blackened-steel outer edge, an inset brass seam, and a soft inner
   shadow for real bevel depth. CSS gradients/shadows only — no external
   texture asset, no raster artwork. */
[data-testid="volatility-modal"] > .modal-content-header {
  justify-content: flex-end;
  margin-bottom: 6px;
}

/* Item 1 — the generic modal-chrome "Volatility" title is redundant once
   VOLATILITY LEAGUE is the terminal's own primary heading below. Kept in
   the DOM (not display:none) purely so the dialog's aria-labelledby
   accessible name still resolves to real text for assistive tech — only
   visually removed, via the same clip technique this file's own
   `.visually-hidden` utility already uses elsewhere. */
[data-testid="volatility-modal"] > .modal-content-header h2 {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Item 2 — Close restyled as a small brass/industrial control belonging
   to the terminal, not generic browser chrome. Text content ("Close")
   is untouched (shared across every Modal caller) — the "×" is purely a
   CSS-generated addition, scoped to this one modal. */
/* APPROVED MOCK-UP milestone — the reference's CLOSE control is a dark
   instrument box with a brass seam and gold text, matching the header
   stat cells, not a solid brass-filled pill. */
[data-testid="volatility-modal"] .modal-close-button {
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass);
  color: var(--gr-brass-bright);
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  padding: 7px 16px;
  border-radius: 5px;
  box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.5);
}

[data-testid="volatility-modal"] .modal-close-button:hover {
  background: rgba(201, 151, 79, 0.16);
}

[data-testid="volatility-modal"] .modal-close-button::after {
  content: " \00d7";
  font-weight: 900;
}

[data-testid="volatility-modal"] > .modal-content-body {
  background: linear-gradient(155deg, #241d17 0%, #14100c 55%, #0a0807 100%);
  border: 2px solid var(--gr-brass-dim);
  border-radius: 14px;
  padding: 8px 8px 6px;
  box-shadow:
    inset 0 0 0 1px rgba(201, 151, 79, 0.28),
    inset 0 0 0 5px var(--gr-rivet),
    inset 0 0 0 6px rgba(201, 151, 79, 0.14),
    inset 0 2px 8px rgba(0, 0, 0, 0.65),
    0 8px 28px rgba(0, 0, 0, 0.55),
    0 1px 0 rgba(255, 255, 255, 0.03);
}

/* Corner brackets — four small brass "L" fixtures reinforcing the
   physical-fixture read, purely CSS (no raster asset): two short
   gradient bars per corner. */
[data-testid="volatility-modal"] > .modal-content-body::before,
[data-testid="volatility-modal"] > .modal-content-body::after {
  content: "";
  position: absolute;
  width: 22px;
  height: 22px;
  pointer-events: none;
  background-image: linear-gradient(var(--gr-brass-bright), var(--gr-brass-bright)), linear-gradient(var(--gr-brass-bright), var(--gr-brass-bright));
  background-repeat: no-repeat;
}

[data-testid="volatility-modal"] > .modal-content-body {
  position: relative;
}

[data-testid="volatility-modal"] > .modal-content-body::before {
  top: 6px;
  left: 6px;
  background-position: top left, top left;
  background-size: 3px 22px, 22px 3px;
}

[data-testid="volatility-modal"] > .modal-content-body::after {
  bottom: 6px;
  right: 6px;
  background-position: bottom right, bottom right;
  background-size: 3px 22px, 22px 3px;
}

/* The two modes as physical switches mounted on the console housing —
   item 21. A recessed rail (inset shadow) holding two buttons; the
   active one lights up brass/illuminated, the inactive one stays a dark
   recessed steel tab. Not generic top-nav tabs. */
.vt-tabs {
  display: flex;
  gap: 6px;
  padding: 4px;
  margin-bottom: 6px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-steel);
  border-radius: 8px;
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.55);
}

.vt-tab {
  flex: 1;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border: 1px solid var(--gr-steel);
  color: var(--text-dim);
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 8px 14px;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

.vt-tab:hover {
  color: var(--text);
}

.vt-tab-active {
  background: linear-gradient(165deg, var(--gr-brass) 0%, var(--gr-brass-dim) 100%);
  border-color: var(--gr-brass);
  color: #1a1204;
  box-shadow: 0 0 10px rgba(201, 151, 79, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

.vt-practice-tab {
  padding-top: 6px;
}

/* The terminal "screen" itself, mounted inside the outer housing above —
   a frontier-timber/blackened-steel surface with real depth: a subtle
   wood-grain hatch UNDER the existing radial gradient (never replacing
   it), plus four small brass rivets at the corners. Every layer is a CSS
   gradient — no image asset. */
.vt-terminal {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 4px 2px 2px;
  position: relative;
  background-color: var(--gr-timber);
  background-image:
    radial-gradient(circle 2.5px at 13px 13px, var(--gr-brass-dim) 92%, transparent 100%),
    radial-gradient(circle 2.5px at calc(100% - 13px) 13px, var(--gr-brass-dim) 92%, transparent 100%),
    radial-gradient(circle 2.5px at 13px calc(100% - 13px), var(--gr-brass-dim) 92%, transparent 100%),
    radial-gradient(circle 2.5px at calc(100% - 13px) calc(100% - 13px), var(--gr-brass-dim) 92%, transparent 100%),
    repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.07) 0px, rgba(0, 0, 0, 0.07) 1px, transparent 1px, transparent 5px),
    radial-gradient(ellipse 140% 90% at 50% -10%, var(--gr-timber-light) 0%, var(--gr-timber) 55%, var(--gr-rivet) 100%);
  border: 1px solid var(--gr-steel);
  border-radius: 10px;
  box-shadow: inset 0 0 24px rgba(0, 0, 0, 0.35);
}

/* ---------- Header — item 5: a destination, not a form. VOLATILITY
   LEAGUE is the primary heading (large, frontier serif); DIGITAL GOLD
   RUSH stays a small world-identity eyebrow above it. The instrument row
   below reads like exchange-board dials, each separated by a thin brass
   seam, with the countdown singled out into its own illuminated ticker
   (item 5's "visually significant" requirement) — item 6: still one
   compact block, no reintroduced empty space. ---------- */

.vt-header {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 16px 5px;
  border-bottom: 1px solid rgba(201, 151, 79, 0.25);
}

.vt-header-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.vt-header-brand {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.vt-eyebrow {
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gr-brass-dim);
}

/* Specificity note: this h2 sits inside `.modal-dialog`, which already
   has its own generic `.modal-dialog h2` rule (font-size/color/margin/
   letter-spacing) at (0,1,1) specificity — higher than a bare `.vt-title`
   (0,1,0) would be, so it would silently win despite coming first in the
   file. Qualifying with `.vt-header` (0,2,0) is the deliberate fix. */
.vt-header .vt-title {
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: 34px;
  letter-spacing: 0.02em;
  color: var(--gr-brass);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
  margin: 0;
  line-height: 1.1;
  text-transform: none;
}

/* APPROVED MOCK-UP milestone — the reference pairs the big title with a
   small italic tagline underneath. Static marketing copy, not canonical
   game data, so it carries no testid and nothing reads it back. */
.vt-header-tagline {
  font-family: var(--font-interface);
  font-size: 11px;
  font-style: italic;
  color: var(--text-dim);
  letter-spacing: 0.01em;
}

/* WAGER MANAGEMENT PASS milestone, item 12 — Current Wagers fills the
   header's own unused width to the right of the stat cells, on the SAME
   row, so the header's overall height never grows. */
.vt-header-stats-row {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}

.vt-header-stats {
  display: flex;
  align-items: stretch;
  gap: 6px;
  flex-wrap: wrap;
}

/* Item 12/16 — a dedicated instrument strip, own bordered face like the
   stat cells beside it. `min-width: 0` lets it shrink in the flex row
   instead of forcing the header to wrap; the card strip itself scrolls
   horizontally (never grows the header) once more cards exist than fit. */
.vt-current-wagers {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  min-width: 0;
  flex: 1 1 220px;
  max-width: 480px;
}

.vt-current-wagers-strip {
  display: flex;
  align-items: stretch;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--gr-brass-dim) var(--gr-rivet);
  padding-bottom: 1px;
}

.vt-current-wagers-empty {
  display: flex;
  align-items: center;
  font-size: 12px;
  color: var(--text-dim);
  font-style: italic;
  padding: 4px 2px;
}

.vt-current-wager-card {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  padding: 3px 10px;
  min-width: 64px;
  background: var(--gr-rivet);
  border: 1px solid rgba(201, 151, 79, 0.25);
  border-radius: 5px;
}

.vt-current-wager-timeframe {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--gr-brass-dim);
  text-transform: uppercase;
}

.vt-current-wager-direction {
  font-family: var(--font-control);
  font-size: 13px;
  font-weight: 700;
}

.vt-current-wager-red .vt-current-wager-direction {
  color: #ffc7c7;
}

.vt-current-wager-green .vt-current-wager-direction {
  color: #b8f0bc;
}

.vt-current-wager-red {
  border-left: 3px solid var(--danger);
}

.vt-current-wager-green {
  border-left: 3px solid var(--ok);
}

.vt-current-wager-status {
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  white-space: nowrap;
}

/* APPROVED MOCK-UP milestone — each stat reads as its own small
   instrument box (dark face, brass seam) rather than a continuous row
   with thin dividers, matching the reference's OPEN/CLOSES IN/BUY-IN/
   PRIZE POOL/ENTRIES cell treatment. */
.vt-header-stat {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  padding: 3px 14px;
  background: var(--gr-rivet);
  border: 1px solid rgba(201, 151, 79, 0.22);
  border-radius: 5px;
}

.vt-header-label {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.vt-header-stat strong {
  font-size: 18px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* item 4 — an OPEN sign, not a generic status pill: a lit, glowing plate
   when the window is genuinely OPEN, a dark unlit plate otherwise
   (CLOSED/SETTLING/SETTLED etc.) — still real canonical text
   (`tournamentStatusLabel`), never a fabricated "OPEN" independent of
   the real status. */
.vt-open-sign {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 4px;
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--gr-rivet);
  border: 1px solid var(--text-dim);
  color: var(--text-dim);
}

.vt-open-sign-lit {
  background: rgba(111, 191, 115, 0.16);
  border-color: var(--ok);
  color: var(--ok);
  text-shadow: 0 0 8px rgba(111, 191, 115, 0.6);
  box-shadow: 0 0 10px rgba(111, 191, 115, 0.35), inset 0 0 6px rgba(111, 191, 115, 0.2);
}

/* The countdown is Volatility's own clock face — the single most
   time-sensitive number on screen, so it gets a lit instrument housing
   of its own instead of sitting flush with the other dials. */
.vt-header-stat-countdown {
  padding: 3px 18px;
  border-color: var(--gr-brass-dim);
  box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
}

.vt-header-stats .vt-countdown {
  font-family: var(--font-control);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--gr-brass-bright);
  text-shadow: 0 0 8px rgba(227, 185, 104, 0.45);
}

.vt-how-it-works {
  color: var(--text-dim);
  font-size: 12px;
  flex-shrink: 0;
}

.vt-how-it-works summary {
  cursor: pointer;
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gr-brass-dim);
  padding: 6px 12px;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 4px;
  list-style: none;
}

.vt-how-it-works summary::-webkit-details-marker {
  display: none;
}

.vt-how-it-works summary:hover {
  color: var(--gr-brass);
  border-color: var(--gr-brass);
}

.vt-how-it-works[open] summary {
  color: var(--gr-brass-bright);
  border-color: var(--gr-brass);
}

.vt-help-list {
  margin: 8px 0 0 0;
  padding-left: 18px;
  max-width: 420px;
  line-height: 1.5;
  font-size: 12px;
}

.vt-state-banner {
  margin: 0 18px;
  padding: 6px 12px;
  background: rgba(201, 151, 79, 0.12);
  border: 1px solid rgba(201, 151, 79, 0.35);
  border-radius: 6px;
  font-family: var(--font-control);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  width: fit-content;
}

.vt-inline-error {
  margin: 0 18px;
  color: #ffb3b3;
  font-size: 13px;
}

/* ---------- Shared panel card ---------- */

.vt-panel {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 16px;
  /* Item 25 — richer, less uniformly brown: a near-black/blackened-steel
     card body (charcoal, not timber), with the warm brass border doing
     the "aged metal fixture" work instead of the fill itself. */
  background: linear-gradient(165deg, #23262b 0%, #15171b 100%);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
  min-width: 0;
  position: relative;
}

/* APPROVED MOCK-UP milestone — every panel (chart, wager, entry, recent
   candles, leaderboard) gets its own small brass corner brackets, not
   just the outer terminal frame, matching the reference's "each panel
   is its own riveted plate" treatment. One pseudo-element, four corners,
   via multiple background layers — no extra markup, no raster asset. */
.vt-panel::before {
  content: "";
  position: absolute;
  inset: 4px;
  pointer-events: none;
  background-image: linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)),
    linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)),
    linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim));
  background-repeat: no-repeat;
  background-position:
    top left,
    top left,
    top right,
    top right,
    bottom left,
    bottom left,
    bottom right,
    bottom right;
  background-size:
    1.5px 10px,
    10px 1.5px,
    1.5px 10px,
    10px 1.5px,
    1.5px 10px,
    10px 1.5px,
    1.5px 10px,
    10px 1.5px;
  opacity: 0.7;
}

.vt-panel-title {
  font-family: var(--font-control);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gr-brass);
}

.vt-panel-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
}

.vt-status-line {
  color: var(--text-dim);
  font-size: 13px;
  margin: 0;
}

/* APPROVED MOCK-UP milestone, items 18-19 — "Entry #N — current run" on
   the left, CURRENT SCORE as its own right-aligned block on the right,
   exactly the reference's YOUR ENTRY header composition. */
.vt-entry-header-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

.vt-entry-score-block {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}

.vt-entry-score-block .vt-scoreboard-value {
  font-size: 22px;
}

/* A real, derived count (candles already closed but not yet reflected
   in `appliedWagers`) — never implies Current Score has moved yet. */
.vt-settling-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass-dim);
  color: var(--gr-brass-dim);
  font-family: var(--font-control);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.vt-status-list {
  list-style: none;
  margin: 4px 0 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--text-dim);
}

.vt-subsection-title {
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 4px 0 0 0;
}

.vt-empty-note {
  color: var(--text-dim);
  font-size: 13px;
  font-style: italic;
  margin: 0;
}

.vt-outcome-correct {
  color: var(--ok);
}

.vt-outcome-incorrect {
  color: var(--danger);
}

.vt-outcome-void {
  color: var(--text-dim);
}

/* ---------- Main grid: chart + wager ---------- */

.vt-main-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.9fr) minmax(300px, 1fr);
  gap: 14px;
  padding: 0 18px;
}

/* ---------- Chart panel ---------- */

.vt-chart-panel-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}

.vt-chart-price-block {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

/* PRIMARY CHART milestone — the ONLY thing said here about the embedded
   widget's own live price: a small, unobtrusive tag, never a second
   number that could visibly disagree with the game's own authoritative
   price shown elsewhere in the terminal (Recent Candles, wager targets).
   Deliberately no paragraph-length disclaimer — the distinction stays
   this compact. */
.vt-chart-live-label {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gr-brass-dim);
  padding: 2px 8px;
  border: 1px solid rgba(201, 151, 79, 0.3);
  border-radius: 4px;
}

.vt-timeframe-buttons {
  display: flex;
  gap: 4px;
}

.vt-timeframe-btn {
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass-dim);
  color: var(--text-dim);
  border-radius: 5px;
  padding: 5px 10px;
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.vt-timeframe-btn:hover {
  border-color: var(--gr-brass);
  color: var(--text);
}

.vt-timeframe-btn-active {
  background: var(--gr-brass-dim);
  border-color: var(--gr-brass);
  color: #1a1204;
}

/* PRIMARY CHART milestone — the embedded TradingView widget fills the
   panel's own remaining height (flex:1 inside `.vt-panel`'s existing
   flex-column layout), inset with the same "near-black instrument
   screen" treatment the old SVG chart used, so it still reads as the
   terminal's own display glass rather than a bare iframe dropped into
   the page. */
.btc-chart-workspace {
  position: relative;
  flex: 1;
  min-height: 360px;
  display: flex;
  border: 1px solid var(--gr-steel);
  border-radius: 6px;
  overflow: hidden;
  background: #0c0d0f;
}

.btc-chart-workspace-widget,
.tradingview-widget-container {
  flex: 1;
  min-height: 0;
}

/* PRIMARY CHART milestone, item 11 — an honest, contained loading/error
   state, never a blank/broken-looking panel while the external widget
   script loads (or a silent dead panel if it fails). */
.btc-chart-workspace-status {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  text-align: center;
  font-size: 13px;
  color: var(--text-dim);
  background: #0c0d0f;
}

.vt-market-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.vt-market-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* APPROVED MOCK-UP milestone — a small lit dot ahead of the market
   status text, matching the reference's green indicator. */
.vt-market-status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.vt-market-status-live {
  color: var(--ok);
}

.vt-market-status-live .vt-market-status-dot {
  box-shadow: 0 0 6px rgba(111, 191, 115, 0.7);
}

.vt-market-status-delayed,
.vt-market-status-reconnecting {
  color: var(--gr-brass-bright);
}

.vt-market-status-unavailable {
  color: var(--danger);
}

/* VOLATILITY CANDLE COUNTDOWN milestone — the chart's own candle-close
   countdown, presentation-only (see VolatilityLeagueSection.tsx's own
   chartCandleCloseAtMs doc comment). Deliberately styled distinctly from
   .vt-market-status (a different concept — a countdown, not a connection
   health indicator) but at the same size/weight so the two sit evenly in
   .vt-market-row's shared flex row. */
.vt-chart-candle-countdown {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* ---------- Wager panel ---------- */

/* VOLATILITY EXCHANGE mock-up milestone, item 31 — before entering, the
   Wager panel is never left mostly blank: an intentional admission
   state with the same buy-in/time-bank/window-close facts a player
   needs to decide, styled as its own lit plate rather than bare text. */
.vt-admission-panel {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px 4px 4px;
}

.vt-admission-title {
  font-family: var(--font-frontier);
  font-size: 20px;
  letter-spacing: 0.03em;
  color: var(--gr-brass-bright);
  text-align: center;
}

.vt-admission-stats {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 6px;
  padding: 10px 14px;
  box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
}

.vt-admission-stat {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.vt-admission-stat strong {
  font-family: var(--font-control);
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.vt-enter-btn {
  width: 100%;
}

.vt-final-score {
  font-size: 14px;
  color: var(--text);
  margin: 0;
}

.vt-new-best-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(111, 191, 115, 0.18);
  border: 1px solid var(--ok);
  color: var(--ok);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
}

.vt-wager-step {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.vt-wager-step-label {
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gr-brass-dim);
}

.vt-wager-timeframe-row {
  display: flex;
  gap: 8px;
}

/* Item 14 — "substantial controls," not thin form chips: real button
   mass (taller, roomier), a beveled steel face, and a distinctly lit
   engaged state when selected (item 13's "mechanically engaged /
   illuminated" language, reused here for the same physical-switch feel
   the mode tabs use). */
.vt-wager-timeframe-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 6px;
  padding: 7px 6px;
  color: var(--text);
  cursor: pointer;
  transition: background-color 0.15s ease, box-shadow 0.15s ease;
}

.vt-wager-timeframe-btn:hover:not(:disabled) {
  border-color: var(--gr-brass);
}

.vt-wager-timeframe-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.vt-wager-timeframe-btn-active {
  background: linear-gradient(165deg, var(--gr-brass) 0%, var(--gr-brass-dim) 100%);
  border-color: var(--gr-brass);
  box-shadow: 0 0 10px rgba(201, 151, 79, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

.vt-wager-timeframe-btn-active .vt-wager-timeframe-label,
.vt-wager-timeframe-btn-active .vt-wager-timeframe-hint {
  color: #1a1204;
}

.vt-wager-timeframe-label {
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 17px;
}

.vt-wager-timeframe-hint {
  font-size: 10px;
  color: var(--text-dim);
  text-align: center;
  min-height: 12px;
}

/* WAGER MANAGEMENT PASS milestone, item 11 — a distinct, legible state
   for "this exact candle is already wagered," never relying on color
   alone (the direction word is already part of the text itself). */
.vt-wager-timeframe-hint-placed {
  color: var(--gr-brass-bright);
  font-weight: 700;
}

/* APPROVED MOCK-UP milestone — the reference shows two lit instrument
   boxes side by side directly above CONFIRM WAGER: target-candle timing
   on the left, wager summary on the right. Echoes the header clock's
   own `.vt-header-stat-countdown`/`.vt-countdown` treatment so these
   read as the same "instrument" family. */
.vt-wager-info-row {
  display: flex;
  gap: 10px;
}

.vt-wager-info-box {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 6px;
  padding: 8px 12px;
  box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
}

.vt-wager-info-box-title {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gr-brass-dim);
}

.vt-wager-info-stat {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.vt-wager-info-stat-row {
  flex-direction: row;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.vt-wager-info-stat-row strong {
  font-family: var(--font-control);
  font-size: 13px;
  color: var(--text);
  text-align: right;
}

.vt-wager-info-stat strong.vt-wager-summary-empty {
  color: var(--text-dim);
  font-weight: 400;
}

.vt-target-candle-value {
  font-family: var(--font-control);
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--gr-brass-bright);
  text-shadow: 0 0 8px rgba(227, 185, 104, 0.45);
}

.vt-target-candle-value-locked {
  color: var(--danger);
  text-shadow: none;
}

.vt-wager-direction-hint {
  margin: 0;
  font-size: 12px;
  color: var(--text-dim);
}

.vt-wager-direction-hint strong {
  color: var(--text);
}

.vt-wager-direction-row {
  display: flex;
  gap: 10px;
}

.vt-wager-direction-row .btn {
  flex: 1;
  padding: 9px 10px;
  font-size: 15px;
  letter-spacing: 0.03em;
}

/* Item 14 — RED/GREEN read as strong, physical bet levers, not tinted
   text buttons: a solid tinted face at rest (already unmistakably
   colored — never neutral-until-selected), a firmer lit fill once
   chosen (item 18's "not color alone" is carried by the direction word
   + arrow glyph already in the JSX, kept unchanged here). */
.btn-volatility-red {
  background: linear-gradient(165deg, rgba(209, 91, 91, 0.28) 0%, rgba(209, 91, 91, 0.12) 100%);
  border: 1px solid var(--danger);
  color: #ffc7c7;
  font-weight: 700;
}

.btn-volatility-red:hover:not(:disabled) {
  background: linear-gradient(165deg, rgba(209, 91, 91, 0.4) 0%, rgba(209, 91, 91, 0.2) 100%);
}

.btn-volatility-green {
  background: linear-gradient(165deg, rgba(111, 191, 115, 0.28) 0%, rgba(111, 191, 115, 0.12) 100%);
  border: 1px solid var(--ok);
  color: #b8f0bc;
  font-weight: 700;
}

.btn-volatility-green:hover:not(:disabled) {
  background: linear-gradient(165deg, rgba(111, 191, 115, 0.4) 0%, rgba(111, 191, 115, 0.2) 100%);
}

.vt-direction-btn-active.btn-volatility-red {
  background: var(--danger);
  color: #2a0d0d;
  box-shadow: 0 0 10px rgba(209, 91, 91, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

.vt-direction-btn-active.btn-volatility-green {
  background: var(--ok);
  color: #0d2a10;
  box-shadow: 0 0 10px rgba(111, 191, 115, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

.vt-confirm-wager-btn {
  width: 100%;
  padding: 8px 14px;
  font-size: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

/* APPROVED MOCK-UP milestone — CONFIRM WAGER carries a small trailing
   chevron in the reference, echoed here as a purely decorative glyph
   (no new affordance — the whole button is already the single click
   target). */
.vt-confirm-wager-chevron {
  font-size: 18px;
  line-height: 1;
  font-weight: 700;
}

/* ---------- Bottom grid: entry / recent candles / leaderboard ---------- */

.vt-bottom-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 14px;
  padding: 0 18px 16px;
}

/* Items 22-24 — ONE shared, fixed-height instrument row. Every card gets
   the SAME explicit height (not just a cap), so Your Entry / Recent
   Candles / Leaderboard visually form a single bottom row even when
   their real content is short — and each card scrolls its OWN overflow
   internally (mainly Recent Candles' table; occasionally Leaderboard's
   rows or a long wager history) instead of ever growing the row itself,
   which is what produced the extra "bottom-row" scrollbar in the
   reviewed screenshot. There is no scroll container around the row —
   only ever inside one card. */
.vt-bottom-grid {
  align-items: stretch;
}

.vt-bottom-grid .vt-panel {
  height: 224px;
  overflow: hidden;
}
/* Compact-viewport override lives in the consolidated `@media (max-height:
   960px)` block near the end of this Volatility section — kept as one
   block, not two, after Visual Pass 2 accidentally left a second,
   conflicting copy of this same query lying around. */

/* VOLATILITY EXCHANGE mock-up milestone, items 21/27 — the fixed-height
   bottom-row cards (above) no longer scroll as a whole: each one's
   title/scoreboard/fixed furniture stays put, and ONLY this inner
   wrapper — Active Wagers + Recent Wagers + entry history in Your
   Entry, the table body in Recent Candles and Leaderboard — scrolls.
   `min-height: 0` is required on a flex child for `overflow-y: auto` to
   actually clamp instead of growing the flex item (and therefore the
   fixed-height card) to fit its content. */
.vt-panel-scroll-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.vt-scoreboard {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.vt-scoreboard-row {
  display: flex;
  gap: 18px;
}

.vt-scoreboard-cell {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.vt-scoreboard-label {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* Item 16 — Time is a core Volatility currency, so it gets the same
   scoreboard weight as Points: the largest numerals in this panel. */
.vt-scoreboard-value {
  font-family: var(--font-control);
  font-size: 30px;
  font-weight: 700;
  color: var(--gr-brass-bright);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.vt-scoreboard-footnote {
  font-size: 10px;
  color: var(--text-dim);
  font-style: italic;
}

.vt-scoreboard-row-secondary {
  gap: 22px;
  padding-top: 8px;
  border-top: 1px solid rgba(201, 151, 79, 0.15);
}

.vt-scoreboard-value-sm {
  font-family: var(--font-control);
  font-size: 17px;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.vt-scoreboard-multiplier {
  font-size: 13px;
  font-weight: 600;
  color: var(--gr-brass-dim);
}

/* Item 17 — Active Wager gets its own inset instrument card, direction
   never carried by color alone (arrow glyph + the word RED/GREEN, both
   already in the JSX). */
.vt-active-wager-list {
  list-style: none;
  margin: 4px 0 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.vt-active-wager-card {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 8px 12px;
  border-radius: 6px;
  background: var(--gr-rivet);
  border: 1px solid rgba(201, 151, 79, 0.2);
  font-size: 12px;
}

.vt-active-wager-red {
  border-left: 3px solid var(--danger);
}

.vt-active-wager-green {
  border-left: 3px solid var(--ok);
}

.vt-active-wager-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.vt-active-wager-direction {
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 13px;
}

.vt-active-wager-red .vt-active-wager-direction {
  color: #ffc7c7;
}

.vt-active-wager-green .vt-active-wager-direction {
  color: #b8f0bc;
}

.vt-active-wager-countdown {
  font-variant-numeric: tabular-nums;
  color: var(--gr-brass-bright);
  font-weight: 600;
  white-space: nowrap;
}

/* APPROVED MOCK-UP milestone — a purely decorative affordance chevron
   (the whole card has no click handler of its own to open, so this is
   atmosphere, not a promise of a detail view) and a real progress fill
   toward the candle's own close (see the `progressPct` derivation at
   the call site — real data, clamped, never fabricated). */
.vt-active-wager-chevron {
  color: var(--text-dim);
  font-size: 16px;
  line-height: 1;
}

.vt-active-wager-progress {
  height: 3px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.4);
  overflow: hidden;
}

.vt-active-wager-progress-fill {
  height: 100%;
  border-radius: 2px;
  background: var(--gr-brass-bright);
}

.vt-active-wager-red .vt-active-wager-progress-fill {
  background: var(--danger);
}

.vt-active-wager-green .vt-active-wager-progress-fill {
  background: var(--ok);
}

.vt-entry-fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 14px;
  margin: 0;
}

.vt-entry-fields dt {
  font-family: var(--font-control);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.vt-entry-fields dd {
  margin: 2px 0 0 0;
  font-size: 14px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* Item 18 — readable header, calmer row spacing, and its own internal
   scrollbar restyled to belong to the terminal rather than the OS
   default (safe/standard scrollbar-color/width properties only). */
.vt-candles-table,
.vt-leaderboard-table,
.vt-wager-history-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.vt-candles-table th,
.vt-leaderboard-table th,
.vt-wager-history-table th {
  text-align: left;
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gr-brass-dim);
  padding: 0 8px 8px 0;
  border-bottom: 1px solid rgba(201, 151, 79, 0.25);
}

.vt-candles-table td,
.vt-leaderboard-table td,
.vt-wager-history-table td {
  padding: 6px 8px 6px 0;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

/* WAGER MANAGEMENT PASS milestone, items 20-28 */
.vt-wager-history-open {
  align-self: flex-start;
  margin-top: 4px;
  padding: 5px 12px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 5px;
  color: var(--gr-brass-bright);
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}

.vt-wager-history-open:hover {
  background: rgba(201, 151, 79, 0.16);
}

[data-testid="wager-history-modal"].modal-dialog-wide {
  max-width: 880px;
  width: 92vw;
  max-height: 82vh;
  display: flex;
  flex-direction: column;
  background: linear-gradient(165deg, #23262b 0%, #15171b 100%);
  border: 1px solid var(--gr-brass-dim);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.18), 0 12px 40px rgba(0, 0, 0, 0.6);
  position: relative;
}

/* Item 22 — the same restrained corner-bracket language every other
   Volatility panel already uses (see `.vt-panel::before`), not a
   generic browser dialog. */
[data-testid="wager-history-modal"].modal-dialog-wide::before {
  content: "";
  position: absolute;
  inset: 6px;
  pointer-events: none;
  background-image: linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)),
    linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)),
    linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim)), linear-gradient(var(--gr-brass-dim), var(--gr-brass-dim));
  background-repeat: no-repeat;
  background-position:
    top left,
    top left,
    top right,
    top right,
    bottom left,
    bottom left,
    bottom right,
    bottom right;
  background-size:
    1.5px 14px,
    14px 1.5px,
    1.5px 14px,
    14px 1.5px,
    1.5px 14px,
    14px 1.5px,
    1.5px 14px,
    14px 1.5px;
  opacity: 0.8;
}

[data-testid="wager-history-modal"] h2 {
  font-family: var(--font-control);
  color: var(--gr-brass);
}

[data-testid="wager-history-modal"] .modal-content-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  gap: 8px;
}

[data-testid="wager-history-modal"] .modal-close-button {
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass);
  color: var(--gr-brass-bright);
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  padding: 6px 14px;
  border-radius: 5px;
  box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.5);
}

[data-testid="wager-history-modal"] .modal-close-button:hover {
  background: rgba(201, 151, 79, 0.16);
}

[data-testid="wager-history-modal"] .modal-close-button::after {
  content: " \00d7";
  font-weight: 900;
}

.vt-wager-history-subtitle {
  margin: -4px 0 0 0;
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gr-brass-dim);
}

.vt-wager-history-summary {
  margin: 0;
}

.vt-wager-history-table-wrap {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--gr-brass-dim) var(--gr-rivet);
}

.vt-wager-history-table thead th {
  position: sticky;
  top: 0;
  background: var(--panel);
}

.vt-wager-history-red {
  color: #ffc7c7;
  font-weight: 700;
}

.vt-wager-history-green {
  color: #b8f0bc;
  font-weight: 700;
}

.vt-panel-scroll-body {
  scrollbar-width: thin;
  scrollbar-color: var(--gr-brass-dim) var(--gr-rivet);
}

.vt-panel-scroll-body::-webkit-scrollbar {
  width: 8px;
}

.vt-panel-scroll-body::-webkit-scrollbar-track {
  background: var(--gr-rivet);
}

.vt-panel-scroll-body::-webkit-scrollbar-thumb {
  background: var(--gr-brass-dim);
  border-radius: 4px;
}

/* Items 19-20 — an honest, deliberate empty state (no fabricated rows),
   and stronger hierarchy once real rows exist: a numbered medallion for
   the top 3 ranks, a heavier score column. */
.vt-leaderboard-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  text-align: center;
  flex: 1;
  padding: 10px;
}

/* APPROVED MOCK-UP milestone — a muted trophy glyph anchors the empty
   leaderboard, matching the reference. CSS-drawn inline SVG, no raster
   asset. */
.vt-leaderboard-trophy {
  width: 40px;
  height: 40px;
  color: var(--gr-brass-dim);
  opacity: 0.55;
  margin-bottom: 2px;
}

.vt-leaderboard-empty-title {
  font-family: var(--font-control);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gr-brass-dim);
  margin: 0;
}

.vt-leaderboard-rank {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  color: var(--text-dim);
  font-weight: 600;
}

.vt-leaderboard-rank-1,
.vt-leaderboard-rank-2,
.vt-leaderboard-rank-3 {
  width: 22px;
  height: 22px;
  border-radius: 999px;
  font-weight: 700;
  color: #1a1204;
}

.vt-leaderboard-rank-1 {
  background: linear-gradient(165deg, var(--gr-brass-bright) 0%, var(--gr-brass) 100%);
}

.vt-leaderboard-rank-2 {
  background: linear-gradient(165deg, #d7dbe0 0%, #a6acb5 100%);
}

.vt-leaderboard-rank-3 {
  background: linear-gradient(165deg, #c98a52 0%, #93602f 100%);
}

.vt-leaderboard-score {
  font-weight: 700;
  color: var(--gr-brass-bright);
}

.vt-leaderboard-me {
  background: rgba(201, 151, 79, 0.1);
}

.vt-pool-note {
  font-size: 11px;
  color: var(--text-dim);
  margin: 6px 0 0 0;
  line-height: 1.4;
}

.vt-results-panel {
  margin: 0 18px 16px;
}

.vt-results-podium {
  margin: 4px 0 0 0;
  padding-left: 18px;
  color: var(--text);
}

@media (max-width: 1500px) {
  .vt-main-grid {
    grid-template-columns: 1fr;
  }
  .vt-bottom-grid {
    grid-template-columns: 1fr;
  }
}

/* 1600×900 target: the modal's own 92vh cap leaves noticeably less room
   than a 1920×1080 desktop, so the chart/header/panels shed padding and
   the now-larger Visual Pass 3 numerals step back down here — same
   content, same layout shape, just denser. Consolidated into ONE block
   (a second, conflicting `@media (max-height: 960px)` used to exist
   with a stale `max-height` left over from the Pass 2 layout — removed,
   since `.vt-bottom-grid .vt-panel`'s fixed `height` above already
   handles the compact case via its own nested override). */
@media (max-height: 960px) {
  .btc-chart-workspace {
    min-height: 260px;
  }
  .vt-header {
    padding: 5px 16px 4px;
    gap: 2px;
  }
  .vt-header .vt-title {
    font-size: 24px;
  }
  .vt-header-stats .vt-countdown {
    font-size: 18px;
  }
  .vt-header-stat strong {
    font-size: 15px;
  }
  .vt-header-stat {
    padding: 1px 14px;
  }
  .vt-panel {
    padding: 7px 14px;
    gap: 4px;
  }
  .vt-terminal {
    gap: 6px;
    padding-top: 2px;
  }
  .vt-tabs {
    margin-bottom: 4px;
    padding: 3px;
  }
  .vt-tab {
    padding: 6px 12px;
  }
  .vt-scoreboard-value {
    font-size: 20px;
  }
  .vt-scoreboard-row-secondary {
    padding-top: 4px;
  }
  .vt-wager-step {
    gap: 2px;
  }
  .vt-wager-timeframe-btn {
    padding: 5px 6px;
    gap: 1px;
  }
  .vt-wager-timeframe-label {
    font-size: 14px;
  }
  .vt-wager-direction-row .btn {
    padding: 7px 8px;
    font-size: 13px;
  }
  .vt-wager-info-box {
    padding: 6px 10px;
  }
  .vt-confirm-wager-btn {
    padding: 6px;
    font-size: 13px;
  }
  .vt-bottom-grid .vt-panel {
    height: 160px;
  }
  [data-testid="volatility-modal"] > .modal-content-body {
    padding: 6px 6px 4px;
  }
}

/* ==========================================================================
   MARKETPLACE MILESTONE 1 / 1B — FULL-SCREEN MARKETPLACE + GEAR BROWSE /
   FILTER / SELL EXPERIENCE (MarketplacePanel.tsx and its Browse/Sell/
   MyMarket/Sales sub-components). Digital Gold Rush visual language reused
   verbatim from the Volatility League Terminal's own confirmed shell
   pattern just above (--gr-timber/--gr-steel/--gr-brass/--gr-rivet,
   --font-frontier/--font-control/--font-interface, the
   .gear-icon[data-rarity=...] rarity palette) — additive only, no new
   palette, no new font import. Scoped entirely under "mk-" (Marketplace)
   class names; nothing here touches any other screen. UX PATTERNS ONLY
   were borrowed from the two reference marketplaces named in the
   milestone brief — every color, texture, and asset below is this
   repository's own.

   MILESTONE 1B VISUAL REFINEMENT — the project owner reviewed Milestone 1
   and found it functionally right but visually too close to a developer
   panel: cards too small, art too small, native browser checkboxes, a
   flat content background, no destination identity, an oversized
   permanent settlement paragraph. This revision keeps every class name
   and every piece of working logic (nothing below changes what
   MarketplacePanel/BrowseTab/SellTab/MyMarketTab/SalesTab/FilterRail
   actually DO) and re-earns the "frontier trading post / digital gold
   rush exchange" identity the brief keeps asking for: a "display case"
   inset surface for the listing grid, a brass-plated header, real custom
   checkbox controls, substantially larger cards (~2-2.5x footprint) with
   dominant artwork, and a compact settlement-info disclosure instead of a
   permanent paragraph.
   ========================================================================== */

/* Same "genuine in-world pop-out, real facility margin visible" sizing as
   the Volatility terminal's own scoped override, just above — a
   Marketplace trading real Hashers/Gear deserves the same visual weight.
   Scoped by data-testid, not a change to Modal.tsx's shared `fullscreen`
   size for every other caller (Crew Management is untouched). */
/* MARKETPLACE MILESTONE 1C — FINAL BROWSE POLISH / VIEWPORT BEHAVIOR.
   Root cause of the reported outer page/document scrollbar (see this
   milestone's own final report item 1 for the exact live-measured
   numbers): this rule previously set only `max-height`, never a real
   `height`. `max-height` alone caps growth but establishes no definite
   length for a percentage-height DESCENDANT to resolve against — so
   `.mk-shell`'s own `height: 100%` fell back to `auto` and the whole
   Marketplace shell simply grew to its natural content size (over 1400px
   tall with 7 real listings), overflowing past this dialog's own
   90vh-capped visual box. The dialog's own `overflow-y: auto` from the
   shared `.modal-dialog-content` rule clipped MOST of that, but not all
   of it — enough leaked through to push `document.documentElement`
   taller than the viewport, producing the outer scrollbar. Setting a
   real `height` (not just `max-height`) here — plus `display:flex` and
   `overflow:hidden` so the flex chain below can now properly size AND
   clip itself — closes that gap at the source, scoped to this one
   Marketplace-only selector; every other modal (Crew Management, plain
   `fullscreen` callers) is untouched. */
[data-testid="marketplace-modal"].modal-dialog-fullscreen, [data-testid="equipment-modal"].modal-dialog-fullscreen {
  max-width: 1760px;
  width: 94vw;
  height: 90vh;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

[data-testid="marketplace-modal-backdrop"], [data-testid="equipment-modal-backdrop"] {
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

[data-testid="marketplace-modal"] > .modal-content-header, [data-testid="equipment-modal"] > .modal-content-header {
  justify-content: flex-end;
  margin-bottom: 6px;
}

/* Generic modal-chrome title hidden (kept in the DOM for aria-labelledby) —
   same clip technique as the Volatility terminal and this file's own
   .visually-hidden utility, once the Marketplace shell renders its own tab
   strip as the primary heading. */
[data-testid="marketplace-modal"] > .modal-content-header h2, [data-testid="equipment-modal"] > .modal-content-header h2 {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

[data-testid="marketplace-modal"] .modal-close-button, [data-testid="equipment-modal"] .modal-close-button {
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass);
  color: var(--gr-brass-bright);
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  padding: 7px 16px;
  border-radius: 5px;
  box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.5);
}

[data-testid="marketplace-modal"] .modal-close-button:hover, [data-testid="equipment-modal"] .modal-close-button:hover {
  background: rgba(201, 151, 79, 0.16);
}

[data-testid="marketplace-modal"] .modal-close-button::after, [data-testid="equipment-modal"] .modal-close-button::after {
  content: " \00d7";
  font-weight: 900;
}

[data-testid="marketplace-modal"] > .modal-content-body, [data-testid="equipment-modal"] > .modal-content-body {
  background: linear-gradient(155deg, #241d17 0%, #14100c 55%, #0a0807 100%);
  border: 2px solid var(--gr-brass-dim);
  border-radius: 14px;
  padding: 10px 12px;
  position: relative;
  /* Completes the height chain the dialog rule above now provides a real
     length for: this flex item takes exactly the dialog's remaining
     height (the header sits above it) and clips its own overflow, so
     `.mk-shell`'s `height:100%` finally resolves to a real, bounded
     pixel value instead of `auto`. */
  flex: 1;
  min-height: 0;
  overflow: hidden;
  box-shadow:
    inset 0 0 0 1px rgba(201, 151, 79, 0.28),
    inset 0 0 0 5px var(--gr-rivet),
    inset 0 0 0 6px rgba(201, 151, 79, 0.14),
    inset 0 2px 8px rgba(0, 0, 0, 0.65),
    0 8px 28px rgba(0, 0, 0, 0.55),
    0 1px 0 rgba(255, 255, 255, 0.03);
}

[data-testid="marketplace-modal"] > .modal-content-body::before,
[data-testid="marketplace-modal"] > .modal-content-body::after,
[data-testid="equipment-modal"] > .modal-content-body::before,
[data-testid="equipment-modal"] > .modal-content-body::after {
  content: "";
  position: absolute;
  width: 22px;
  height: 22px;
  pointer-events: none;
  background-image: linear-gradient(var(--gr-brass-bright), var(--gr-brass-bright)), linear-gradient(var(--gr-brass-bright), var(--gr-brass-bright));
  background-repeat: no-repeat;
}

[data-testid="marketplace-modal"] > .modal-content-body::before, [data-testid="equipment-modal"] > .modal-content-body::before {
  top: 6px;
  left: 6px;
  background-position: top left, top left;
  background-size: 3px 22px, 22px 3px;
}

[data-testid="marketplace-modal"] > .modal-content-body::after, [data-testid="equipment-modal"] > .modal-content-body::after {
  bottom: 6px;
  right: 6px;
  background-position: bottom right, bottom right;
  background-size: 3px 22px, 22px 3px;
}

.mk-shell {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  height: 100%;
  min-height: 0;
}

/* MARKETPLACE MILESTONE 1C item 3 — Toast.tsx's own generic, reusable
   feedback system, styled here for its first real usage anywhere in the
   app (an audit confirmed it was built and unit-tested but never wired
   into a live screen — see MarketplacePanel.tsx's own doc comment).
   Deliberately GENERIC class names (`.toast-stack`/`.toast-*`), not
   `mk-`-prefixed, so this same styling is ready to serve `App.tsx`'s own
   still-pending `.error-banner` cutover later without duplication — but
   positioned here specifically for the Marketplace modal (absolute
   within `.mk-shell`, which is now `position:relative`), not fixed to
   the whole viewport, since only Marketplace uses it today. Each variant
   gets a left accent bar in the same semantic colors already used
   elsewhere in this game (--ok/--amber/--danger), never a full neon
   fill — restrained, matching the frontier console's own restraint
   rule. */
.toast-stack {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 320px;
  max-width: calc(100% - 16px);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: linear-gradient(165deg, var(--gr-timber-light) 0%, var(--gr-timber) 100%);
  border: 1px solid var(--gr-steel);
  border-left: 3px solid var(--gr-brass);
  border-radius: 6px;
  padding: 10px 12px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45);
  pointer-events: auto;
  animation: mk-toast-in 0.15s ease-out;
}

@keyframes mk-toast-in {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.toast-success {
  border-left-color: var(--ok);
}
.toast-warning {
  border-left-color: var(--amber);
}
.toast-error {
  border-left-color: var(--danger);
}
.toast-info {
  border-left-color: var(--gr-brass);
}

/* Visually hidden, not display:none — the toast's own role (alert/status)
   plus this label give assistive tech "Error: <message>"/"Success:
   <message>", clearer than the message text alone; sighted players read
   the same meaning from the left accent color, so it's hidden on screen
   only. Same clip technique as this file's own .visually-hidden utility
   and the Marketplace modal's own hidden title. */
.toast-variant-label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.toast-message {
  flex: 1;
  font-family: var(--font-interface);
  font-size: 13px;
  color: var(--text);
  line-height: 1.4;
}

.toast-dismiss {
  flex-shrink: 0;
  background: transparent;
  border: none;
  color: var(--text-dim);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
}

.toast-dismiss:hover {
  color: var(--text);
}

/* MILESTONE 1B item 10 — a restrained brass-plated destination title, not
   a giant banner: the same eyebrow-over-title pattern the Volatility
   League Terminal's own header already established (.vt-eyebrow/.vt-title)
   — reused for visual consistency across Red & Green destinations, sized
   down from Volatility's own 34px so it costs almost no listing space. */
.mk-header {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 2px 4px 0;
}

.mk-header-eyebrow {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gr-brass-dim);
}

.mk-header-title {
  margin: 0;
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: 22px;
  letter-spacing: 0.02em;
  color: var(--gr-brass);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
  line-height: 1.15;
}

/* Primary nav — BROWSE / MY MARKET / SELL / SALES — the same "physical
   switches on the console housing" treatment as the Volatility mode
   switch (.vt-tabs), four positions instead of two. */
.mk-tabs {
  display: flex;
  gap: 6px;
  padding: 4px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-steel);
  border-radius: 8px;
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.55);
}

.mk-tab {
  flex: 1;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border: 1px solid var(--gr-steel);
  color: var(--text-dim);
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 10px 14px;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

.mk-tab:hover {
  color: var(--text);
}

.mk-tab-active {
  background: linear-gradient(165deg, var(--gr-brass) 0%, var(--gr-brass-dim) 100%);
  border-color: var(--gr-brass);
  color: #1a1204;
  box-shadow: 0 0 10px rgba(201, 151, 79, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

.mk-tab-content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 2px 2px 8px;
}

/* MARKETPLACE MILESTONE 1C — Browse fills the tab pane's own bounded
   height (now real, per the dialog/content-body fix above) and gives the
   listing/results region the primary internal scroll (task's own item 2:
   "prefer the listing/results region to own the primary scrolling
   behavior while preserving useful access to filters/search/navigation")
   — toolbar and filter rail both stay flex-shrink:0 so they're never
   compressed or scrolled away; only `.mk-browse-results` below actually
   scrolls. `.mk-tab-content`'s own `overflow-y:auto` (used as-is by the
   simpler Sell/My Market/Sales tabs) stays as a harmless safety net here
   — it only ever engages if Browse's own internal scrolling somehow
   didn't absorb the overflow, which it always does. */
.mk-browse {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

/* ---------- Browse toolbar: category pills, search, sort ---------- */

.mk-browse-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
  flex-shrink: 0;
}

.mk-category-pills {
  display: flex;
  gap: 4px;
  background: var(--gr-rivet);
  padding: 3px;
  border-radius: 999px;
  border: 1px solid var(--gr-steel);
  box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.5);
}

.mk-category-pill {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-dim);
  font-family: var(--font-control);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 7px 16px;
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.mk-category-pill:hover {
  color: var(--text);
}

.mk-category-pill-active {
  background: linear-gradient(165deg, var(--gr-brass) 0%, var(--gr-brass-dim) 100%);
  border-color: var(--gr-brass);
  color: #1a1204;
  box-shadow: 0 0 8px rgba(201, 151, 79, 0.4);
}

/* Milestone 1B item 9 — search/sort read as instruments mounted on the
   same console, not generic HTML form controls: a recessed dark well, a
   brass caret on the select, a warm focus glow matching the rest of the
   frontier control language. */
.mk-search {
  flex: 1;
  min-width: 220px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-steel);
  border-radius: 6px;
  color: var(--text);
  font-family: var(--font-interface);
  font-size: 14px;
  padding: 10px 14px;
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.mk-search::placeholder {
  color: var(--text-dim);
}

.mk-search:focus,
.mk-sort:focus {
  outline: none;
  border-color: var(--gr-brass);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(201, 151, 79, 0.25);
}

.mk-sort {
  background: var(--gr-rivet);
  border: 1px solid var(--gr-steel);
  border-radius: 6px;
  color: var(--text);
  font-family: var(--font-control);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 10px 30px 10px 14px;
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--gr-brass-bright) 50%), linear-gradient(135deg, var(--gr-brass-bright) 50%, transparent 50%);
  background-position: calc(100% - 16px) center, calc(100% - 11px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}

/* ---------- Browse layout: filter rail + result grid ---------- */

.mk-browse-layout {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 16px;
  align-items: stretch;
}

/* Milestone 1B item 7/8 — the rail as a small timber-framed instrument
   panel bolted to the console: a warm wood-toned surface, a brass top
   seam, and a soft inner shadow for real depth, rather than a flat
   translucent rectangle.
   MILESTONE 1C — `position: sticky` removed: it never actually did
   anything (sticky only matters within a scrolling ancestor, and this
   rail's own ancestor didn't scroll — `.mk-browse-results` did, per the
   task's own item 2 layout). Now a plain flex sibling of the results
   region within a `height`-bounded row (`align-items: stretch` above),
   so it simply stays in place next to the results while they scroll —
   the task's own preferred "no unnecessary sticky-position complexity"
   outcome. `overflow-y: auto` kept as a defensive fallback only, for the
   unlikely case the rail's own content (many filter options at a narrow
   width) exceeds the row's height. */
.mk-filter-rail {
  flex: 0 0 216px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  background: linear-gradient(180deg, rgba(58, 42, 29, 0.55) 0%, rgba(20, 16, 12, 0.6) 100%);
  border: 1px solid var(--gr-steel);
  border-top: 2px solid var(--gr-brass-dim);
  border-radius: 8px;
  padding: 14px;
  overflow-y: auto;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), inset 0 -12px 20px -12px rgba(0, 0, 0, 0.6);
}

.mk-filter-group + .mk-filter-group {
  border-top: 1px solid rgba(201, 151, 79, 0.16);
  padding-top: 14px;
}

.mk-filter-heading {
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  margin: 0 0 8px;
}

/* Milestone 1B item 8 — real semantic checkboxes, visually restyled: the
   native input stays for keyboard/AT behavior (focus-visible below), a
   sibling `<span>` renders the dark recessed box and its brass checked
   state — never a fake non-semantic control. */
.mk-check {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-dim);
  font-family: var(--font-interface);
  font-size: 13px;
  padding: 3px 0;
  cursor: pointer;
}

.mk-check:hover {
  color: var(--text);
}

.mk-check input {
  position: absolute;
  inset: 0;
  width: 18px;
  height: 18px;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.mk-check-box {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  border-radius: 3px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-steel);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.6);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}

.mk-check-box::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 1px;
  background: var(--gr-brass-bright);
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.12s ease, transform 0.12s ease;
}

.mk-check input:checked + .mk-check-box {
  background: rgba(201, 151, 79, 0.16);
  border-color: var(--gr-brass);
  box-shadow: 0 0 6px rgba(201, 151, 79, 0.35);
}

.mk-check input:checked + .mk-check-box::after {
  opacity: 1;
  transform: scale(1);
}

.mk-check input:focus-visible + .mk-check-box {
  outline: 2px solid var(--gr-brass-bright);
  outline-offset: 2px;
}

.mk-check-label {
  line-height: 1.1;
}

.mk-filter-price-row {
  display: flex;
  gap: 8px;
}

.mk-price-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
}

.mk-price-field-label {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.mk-price-field input {
  width: 100%;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-steel);
  border-radius: 5px;
  color: var(--text);
  padding: 7px 8px;
  font-family: var(--font-interface);
  font-size: 13px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.5);
}

.mk-price-field input:focus {
  outline: none;
  border-color: var(--gr-brass);
}

.mk-filter-clear {
  align-self: stretch;
  background: var(--gr-steel-dark);
  border: 1px solid var(--gr-steel);
  color: var(--text-dim);
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 8px 10px;
  border-radius: 5px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.mk-filter-clear:not(:disabled):hover {
  border-color: var(--gr-brass);
  color: var(--gr-brass-bright);
}

.mk-filter-clear:disabled {
  opacity: 0.45;
  cursor: default;
}

/* Milestone 1B item 7 — the listing area reads as a lit display case set
   into the console: a slightly recessed dark surface with a soft warm
   glow from the top, separated from the rail by a thin brass seam.
   MILESTONE 1C item 2/7 — this is now the ONE element that actually
   scrolls in Browse (`min-height:0` + `overflow-y:auto`, bounded by
   `.mk-browse-layout`'s own real height) — the listing/results region
   "owning the primary scrolling behavior" the task asks for. Also makes
   better use of the now-fixed available height (item 7: "make better use
   of the available vertical space") since it's no longer at the mercy of
   an unbounded, overflowing ancestor. */
.mk-browse-results {
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  background: radial-gradient(ellipse 100% 60% at 50% 0%, rgba(201, 151, 79, 0.05) 0%, transparent 60%), rgba(10, 8, 7, 0.35);
  border: 1px solid rgba(201, 151, 79, 0.14);
  border-radius: 8px;
  padding: 16px;
  box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.4);
}

.mk-result-count {
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--text-dim);
  margin: 0 0 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(201, 151, 79, 0.14);
}

/* ---------- Card grid + card ----------
   MILESTONE 1B item 1 — cards substantially larger than Milestone 1
   (~2-2.5x visual footprint: a 140px-wide, ~170px-tall card with 96px art
   grows to a ~240px+-wide, ~300px+-tall card with 148px art), while still
   comfortably fitting several across a normal desktop-width Marketplace.
   See this milestone's own final report item 6/7 for the exact numbers. */

.mk-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 18px;
}

/* MILESTONE 1C item 6 — root changed from a real <button> to a
   role="button" div (a <button> can't legally contain the new explicit
   Buy <button> below), so the pointer/focus affordances a <button>
   normally gives for free are declared explicitly here. */
.mk-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  background: linear-gradient(165deg, var(--gr-timber-light) 0%, var(--gr-timber) 100%);
  border: 1px solid var(--panel-border);
  border-radius: 10px;
  padding: 14px 14px 16px;
  cursor: pointer;
  text-align: center;
  font-family: var(--font-interface);
  color: var(--text);
  transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
}

.mk-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45);
}

.mk-card:focus-visible {
  outline: 2px solid var(--gr-brass-bright);
  outline-offset: 2px;
}

/* Same rarity -> color convention as .gear-icon[data-rarity=...] — common
   gets no special treatment, mythic is intentionally omitted (future/
   disabled, never produced by live generation). Restrained: a border +
   soft glow + a small tinted rarity plate, never a full neon fill. */
.mk-card[data-rarity="uncommon"] {
  border-color: var(--ok);
  box-shadow: 0 0 10px -3px var(--ok);
}
.mk-card[data-rarity="rare"] {
  border-color: var(--poor);
  box-shadow: 0 0 10px -3px var(--poor);
}
.mk-card[data-rarity="epic"] {
  border-color: var(--epic);
  box-shadow: 0 0 10px -3px var(--epic);
}
.mk-card[data-rarity="legendary"] {
  border-color: var(--amber);
  box-shadow: 0 0 12px -2px var(--amber);
}

.mk-card-rarity-plate {
  align-self: flex-start;
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid var(--panel-border);
  border-radius: 4px;
  padding: 2px 7px;
}

.mk-card[data-rarity="uncommon"] .mk-card-rarity-plate {
  color: var(--ok);
  border-color: var(--ok);
}
.mk-card[data-rarity="rare"] .mk-card-rarity-plate {
  color: var(--poor);
  border-color: var(--poor);
}
.mk-card[data-rarity="epic"] .mk-card-rarity-plate {
  color: var(--epic);
  border-color: var(--epic);
}
.mk-card[data-rarity="legendary"] .mk-card-rarity-plate {
  color: var(--amber);
  border-color: var(--amber);
}

/* Art dominates the upper portion of the card (item 1's own explicit
   instruction) — a "display window" surface behind it, and the shared
   GearIcon/HasherPortraitIcon "large" size scaled up further, scoped to
   this card only (their own default sizes elsewhere in the app, e.g. the
   Equip picker, are untouched). */
.mk-card-art {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 14px 0;
  margin: 2px 0 4px;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.25) 0%, transparent 72%);
  border-radius: 8px;
}

.mk-card-art .gear-icon-large {
  width: 148px;
  height: 148px;
}

.mk-card-art .hasher-portrait-icon-large {
  width: 132px;
  height: 132px;
}

.mk-card-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
}

.mk-card-name {
  font-family: var(--font-frontier);
  font-size: 17px;
  line-height: 1.15;
}

.mk-card-slot {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* Compact stat badges (Milestone 1B item 2/3 — real public statBonuses) —
   understated, HP/SPD/INT colour-neutral so they never compete with the
   rarity accent for attention. */
.mk-card-stats {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 5px;
  margin: 2px 0;
}

.mk-card-stat-badge {
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  color: var(--cooled-glow);
  background: rgba(127, 212, 255, 0.08);
  border: 1px solid rgba(127, 212, 255, 0.3);
  border-radius: 4px;
  padding: 2px 7px;
}

/* Price substantially more prominent than any other card metadata (item
   1's own explicit instruction) — large brass numerals. MILESTONE 1C
   item 4: seller removed from the compact card entirely (was a small dim
   line here in 1B) — it remains in the listing detail, where it's useful
   contextual information rather than clutter. */
.mk-card-price {
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 22px;
  color: var(--gr-brass-bright);
  margin-top: 4px;
}

/* MILESTONE 1C item 5/6 — the one obvious action affordance per card.
   "Buy" is a real nested <button> (stops propagation so it doesn't
   double-fire the card's own onClick) that opens the exact same listing
   detail / quote-confirmation flow as clicking anywhere else on the card
   — it never purchases directly, matching the task's own explicit "do
   NOT bypass the listing detail or quote/confirmation flow" instruction.
   "Your Listing" is plain text, not a button — there is no purchase
   action to offer on the viewer's own listing. */
.mk-card-action {
  margin-top: 6px;
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 6px 18px;
  border-radius: 5px;
}

.mk-card-action-buy {
  background: linear-gradient(165deg, var(--gr-brass) 0%, var(--gr-brass-dim) 100%);
  border: 1px solid var(--gr-brass);
  color: #1a1204;
  cursor: pointer;
  transition: box-shadow 0.12s ease;
}

.mk-card-action-buy:hover {
  box-shadow: 0 0 10px rgba(201, 151, 79, 0.45);
}

.mk-card-action-own {
  background: transparent;
  border: 1px solid var(--panel-border);
  color: var(--text-dim);
}

/* ---------- Listing detail ---------- */

.mk-detail {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.mk-detail-back {
  align-self: flex-start;
}

.mk-detail-body {
  display: flex;
  gap: 28px;
  flex-wrap: wrap;
}

/* Milestone 1B item 13 — "feel like inspecting a valuable item": a larger
   display-case window matching the enlarged card art. */
.mk-detail-art {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 220px;
  height: 220px;
  padding: 16px;
  background: radial-gradient(ellipse at center, rgba(201, 151, 79, 0.06) 0%, rgba(20, 16, 12, 0.6) 75%);
  border: 1px solid var(--panel-border);
  border-radius: 10px;
  box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.45);
}
.mk-detail-art .gear-icon-large {
  width: 180px;
  height: 180px;
}
.mk-detail-art .hasher-portrait-icon-large {
  width: 160px;
  height: 160px;
}
.mk-detail-art[data-rarity="uncommon"] {
  border-color: var(--ok);
}
.mk-detail-art[data-rarity="rare"] {
  border-color: var(--poor);
}
.mk-detail-art[data-rarity="epic"] {
  border-color: var(--epic);
}
.mk-detail-art[data-rarity="legendary"] {
  border-color: var(--amber);
}

.mk-detail-info {
  flex: 1;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.mk-detail-rarity-badge {
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
}

.mk-detail-name {
  margin: 0;
  font-family: var(--font-frontier);
  font-size: 22px;
}

.mk-detail-slot {
  margin: 0;
  color: var(--text-dim);
}

.mk-detail-stats {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin: 4px 0;
}

.mk-detail-stat {
  background: rgba(111, 191, 115, 0.12);
  border: 1px solid var(--ok);
  border-radius: 4px;
  padding: 3px 8px;
  font-family: var(--font-control);
  font-size: 12px;
  font-weight: 700;
  color: var(--ok);
}

.mk-detail-price-row,
.mk-detail-seller-row,
.mk-detail-status-row {
  display: flex;
  justify-content: space-between;
  border-top: 1px solid var(--panel-border);
  padding: 6px 0;
  font-size: 13px;
}

.mk-detail-own-listing-note {
  color: var(--text-dim);
  font-style: italic;
}

/* ---------- Sell tab ----------
   MILESTONE 1B item 14 — visually related to Browse: same grid class
   (`.mk-card-grid`, inherits the same enlarged column sizing), same
   rarity language (GearCard's own frame art + this file's rarity
   palette), same control typography. GearCard is already a substantial
   ~220px full-portrait card (see GearCard.tsx/`.gear-card`) — sits
   comfortably inside a 240px+ grid column without further scaling. */

.mk-sell-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.mk-sell-card .btn {
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.mk-create-listing {
  max-width: 340px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.mk-create-listing-price {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  width: 100%;
}

.mk-create-listing-price input {
  background: var(--gr-rivet);
  border: 1px solid var(--gr-steel);
  border-radius: 5px;
  color: var(--text);
  padding: 8px;
  font-family: var(--font-interface);
  font-size: 14px;
}

.mk-create-listing-fee-row,
.mk-create-listing-proceeds-row {
  display: flex;
  justify-content: space-between;
  width: 100%;
  font-size: 12px;
  color: var(--text-dim);
}

.mk-create-listing-actions {
  display: flex;
  gap: 8px;
  width: 100%;
}

.mk-create-listing-actions .btn {
  flex: 1;
}

/* ---------- My Market tab ----------
   MILESTONE 1B item 15 — refined to the same visual language (rarity
   accent, brass price, frontier typography) while keeping ACTIVE/SOLD/
   CANCELLED easy to tell apart at a glance (a status pill's own color,
   not just its text). */

.mk-bucket-tabs {
  display: flex;
  gap: 6px;
  margin-bottom: 12px;
}

.mk-bucket-tab {
  background: var(--gr-steel-dark);
  border: 1px solid var(--gr-steel);
  color: var(--text-dim);
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 7px 16px;
  border-radius: 5px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.mk-bucket-tab:hover {
  color: var(--text);
}

.mk-bucket-tab-active {
  background: linear-gradient(165deg, var(--gr-brass) 0%, var(--gr-brass-dim) 100%);
  border-color: var(--gr-brass);
  color: #1a1204;
}

.mk-my-market-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.mk-my-market-row {
  display: flex;
  align-items: center;
  gap: 12px;
  background: linear-gradient(165deg, var(--gr-timber-light) 0%, var(--gr-timber) 100%);
  border: 1px solid var(--panel-border);
  border-radius: 8px;
  padding: 10px 14px;
}

.mk-my-market-row[data-rarity="uncommon"] {
  border-left: 3px solid var(--ok);
}
.mk-my-market-row[data-rarity="rare"] {
  border-left: 3px solid var(--poor);
}
.mk-my-market-row[data-rarity="epic"] {
  border-left: 3px solid var(--epic);
}
.mk-my-market-row[data-rarity="legendary"] {
  border-left: 3px solid var(--amber);
}

.mk-my-market-generic-icon {
  font-size: 20px;
  width: 32px;
  text-align: center;
}

.mk-my-market-name {
  flex: 1;
  font-family: var(--font-frontier);
  font-size: 14px;
}

.mk-my-market-price {
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 16px;
  color: var(--gr-brass-bright);
}

.mk-my-market-status {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  text-transform: uppercase;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 4px;
  padding: 3px 8px;
}

.mk-my-market-date {
  font-size: 11px;
  color: var(--text-dim);
}

/* ---------- Sales tab ---------- */

.mk-sales-scope-note {
  color: var(--text-dim);
  font-size: 12px;
  margin-top: 0;
}

.mk-sales-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  background: rgba(20, 16, 12, 0.35);
  border-radius: 8px;
  overflow: hidden;
}

.mk-sales-table th {
  text-align: left;
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  padding: 8px 10px;
  background: var(--gr-rivet);
  border-bottom: 1px solid var(--gr-steel);
}

.mk-sales-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--panel-border);
}

.mk-sales-table tbody tr:hover {
  background: rgba(201, 151, 79, 0.06);
}

/* Milestone 1B item 11 — the permanent settlement paragraph is replaced
   by a compact secondary disclosure: a small "Market Info" toggle that
   expands the same, unchanged information on demand, rather than
   spending a full-width line on every screen forever. Payment
   architecture itself is untouched — this is presentation only. */
.mk-footer {
  border-top: 1px solid var(--gr-steel);
  padding-top: 8px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.mk-market-info {
  position: relative;
}

.mk-market-info-toggle {
  background: transparent;
  border: 1px solid var(--gr-steel);
  color: var(--text-dim);
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: 999px;
  padding: 4px 10px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.mk-market-info-toggle:hover {
  border-color: var(--gr-brass);
  color: var(--gr-brass-bright);
}

.mk-market-info-toggle::before {
  content: "i";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 1px solid currentColor;
  font-size: 9px;
  font-style: italic;
  font-weight: 700;
}

.mk-market-info-panel {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  width: 300px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 8px;
  padding: 12px 14px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-dim);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  z-index: 1;
}

/* ---------- Responsive (Milestone 1B item 16): filter rail stacks above
   results at narrow widths, and the card grid's own minmax() already
   reduces column count gracefully as width shrinks — no JS breakpoint
   logic needed for that part. No horizontal overflow at any width. ---------- */
@media (max-width: 820px) {
  .mk-browse-layout {
    flex-direction: column;
  }
  .mk-filter-rail {
    /* Milestone 1C: natural height when stacked above results, not a
       competing flex:1 — the results region below should get whatever
       space remains and do the scrolling, matching desktop behavior. */
    flex: 0 0 auto;
    width: 100%;
  }
  .mk-detail-body {
    flex-direction: column;
  }
  .mk-card-grid {
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 12px;
  }
}

/* ==========================================================================
   CREW WORKSHOP — the cinematic weekly Crew Setup screen (CrewWorkshop.tsx)
   ==========================================================================
   One fixed-viewport screen (no page scroll): a timber / forged-steel /
   aged-brass staging shed built ENTIRELY from the existing --gr-* tokens over
   the reused mine environment plate (see CrewWorkshopBackdrop.tsx — the plate
   is chosen in one resolver, so a dedicated workshop plate can replace it
   without touching this layout). Every rule is scoped under .crew-workshop or
   a .workshop-* class; nothing here alters any existing component's styling.
   Real Hasher/gear artwork only — no new art, no decorative copy. */

.crew-workshop {
  --ws-pad: clamp(16px, 2.2vw, 44px);
  --ws-gap: clamp(12px, 1.5vw, 28px);
  --ws-slot-w: clamp(64px, 5.4vw, 108px);
  --ws-slot-icon: clamp(36px, 3.2vw, 58px);
  --ws-inner-gap: clamp(6px, 0.6vw, 12px);
  --ws-station-pad: clamp(10px, 0.9vw, 18px);
  /* The narrowest station's usable width (Lead takes 1.08fr of the row, the flanks 1fr each), less both slot columns — the card fills what's left, capped by height so it never crowds the output bar. */
  --ws-col-w: calc((100vw - 2 * var(--ws-pad) - 2 * var(--ws-gap)) / 3.08);
  --ws-card-w: min(calc(var(--ws-col-w) - 2 * var(--ws-slot-w) - 2 * var(--ws-inner-gap) - 2 * var(--ws-station-pad) - 2px), 33vh);

  position: relative;
  /* One viewport tall, but never SHORTER than its content: on a short window (a laptop browser is often only ~650px tall) or with many
     repair notes the screen grows and the page scrolls, instead of the stations running under the output bar or a slot's text spilling out. */
  min-height: 100vh;
  min-height: 100dvh;
  overflow: hidden;
  isolation: isolate;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: clamp(8px, 1.4vh, 18px);
  padding: clamp(10px, 1.8vh, 24px) var(--ws-pad) clamp(12px, 2vh, 28px);
  background: var(--coal);
  color: var(--text);
  font-family: var(--font-interface);
}

/* ---------- Environment (isolated, replaceable) ---------- */

.workshop-bg-layer {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.workshop-bg {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 55%;
  /* The darker workshop treatment over the reused mine plate. */
  filter: brightness(0.4) saturate(0.85) blur(1.5px);
  transform: scale(1.04);
}

.workshop-bg-vignette {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 62% 52% at 50% 44%, rgba(240, 169, 78, 0.12) 0%, transparent 62%),
    radial-gradient(ellipse 95% 85% at 50% 50%, transparent 38%, rgba(8, 5, 3, 0.88) 100%),
    linear-gradient(180deg, rgba(8, 5, 3, 0.6) 0%, transparent 24%, transparent 68%, rgba(8, 5, 3, 0.78) 100%);
}

.workshop-header,
.workshop-stations,
.workshop-output {
  position: relative;
  z-index: 1;
}

/* ---------- Header ---------- */

.workshop-header {
  display: flex;
  align-items: center;
  gap: clamp(12px, 1.4vw, 24px);
  min-height: 0;
}

.workshop-logo {
  height: clamp(34px, 5vh, 56px);
  width: auto;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.6));
}

.workshop-title {
  margin: 0;
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: clamp(22px, 2.6vw, 44px);
  line-height: 1;
  color: var(--gr-brass-bright);
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.85);
}

.workshop-progress {
  margin-left: auto;
  font-family: var(--font-control);
  font-size: clamp(12px, 1vw, 16px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 6px 14px;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 999px;
  background: rgba(14, 12, 9, 0.7);
}

.workshop-error {
  position: absolute;
  z-index: 5;
  top: clamp(64px, 10vh, 110px);
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: 8px 16px;
  border: 1px solid var(--danger);
  border-radius: 8px;
  background: rgba(40, 14, 14, 0.94);
  color: #ffc9c9;
  font-size: 14px;
}

/* ---------- Stations ---------- */

.workshop-stations {
  display: grid;
  grid-template-columns: 1fr 1.08fr 1fr;
  gap: var(--ws-gap);
  align-items: center;
  min-height: 0;
}

.workshop-station {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(6px, 1vh, 12px);
  min-width: 0;
  padding: var(--ws-station-pad);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 12px;
  /* Corner rivets over a timber-to-blackened-steel panel. */
  background:
    radial-gradient(circle at 12px 12px, var(--gr-brass-dim) 0 2px, var(--gr-rivet) 3px, transparent 4px),
    radial-gradient(circle at calc(100% - 12px) 12px, var(--gr-brass-dim) 0 2px, var(--gr-rivet) 3px, transparent 4px),
    radial-gradient(circle at 12px calc(100% - 12px), var(--gr-brass-dim) 0 2px, var(--gr-rivet) 3px, transparent 4px),
    radial-gradient(circle at calc(100% - 12px) calc(100% - 12px), var(--gr-brass-dim) 0 2px, var(--gr-rivet) 3px, transparent 4px),
    linear-gradient(180deg, rgba(58, 42, 29, 0.86) 0%, rgba(30, 22, 15, 0.92) 46%, rgba(18, 14, 10, 0.95) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(201, 151, 79, 0.14),
    inset 0 1px 0 rgba(255, 220, 160, 0.08),
    0 18px 40px rgba(0, 0, 0, 0.55);
}

/* Lead: slightly stronger emphasis — brighter brass and warmer glow, a touch larger card — but the same structure as its flanks. */
.workshop-station-lead {
  border-color: var(--gr-brass);
  box-shadow:
    inset 0 0 0 1px rgba(227, 185, 104, 0.28),
    inset 0 1px 0 rgba(255, 220, 160, 0.14),
    0 22px 48px rgba(0, 0, 0, 0.6),
    0 0 64px -18px rgba(240, 169, 78, 0.34);
}

.workshop-station-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: var(--font-control);
}

.workshop-station-role {
  font-size: clamp(14px, 1.3vw, 22px);
  letter-spacing: 0.22em;
  color: var(--gr-brass-bright);
}

.workshop-station-weight {
  font-size: clamp(12px, 1vw, 17px);
  letter-spacing: 0.08em;
  color: #1c1206;
  background: linear-gradient(180deg, var(--gr-brass-bright), var(--gr-brass));
  padding: 2px 10px;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

.workshop-station-body {
  display: grid;
  grid-template-columns: var(--ws-slot-w) auto var(--ws-slot-w);
  gap: var(--ws-inner-gap);
  align-items: stretch;
  justify-content: center;
  min-height: 0;
}

.workshop-station-card {
  width: var(--ws-card-w);
  aspect-ratio: 1024 / 1536;
  filter: drop-shadow(0 10px 18px rgba(0, 0, 0, 0.6));
}

.workshop-station-lead .workshop-station-card {
  width: calc(var(--ws-card-w) * 1.06);
}

/* The approved frames are true RGBA (see the individual-reveal backing fix) — CardFrame's own opaque panel + placeholder layers would paint a grey rectangle behind the ornate silhouette, so they are made transparent here only, scoped to this screen. */
.crew-workshop .card-frame {
  background: transparent;
}

.crew-workshop .card-frame-placeholder {
  background: transparent;
  border-color: transparent;
}

.crew-workshop .card-frame-placeholder-label,
.crew-workshop .card-frame-placeholder-note {
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9);
}

.workshop-station-empty {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  text-align: center;
  border: 2px solid var(--gr-brass-dim);
  border-radius: 14px;
  background:
    radial-gradient(ellipse 80% 55% at 50% 30%, rgba(201, 151, 79, 0.1), transparent 70%),
    linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.16);
}

.workshop-station-empty-role {
  font-family: var(--font-control);
  font-size: clamp(14px, 1.3vw, 22px);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gr-brass);
}

.workshop-station-empty-note {
  font-size: clamp(11px, 0.9vw, 14px);
  color: var(--text-dim);
}

.workshop-station-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(6px, 0.9vh, 10px);
  text-align: center;
  width: 100%;
}

.workshop-station-who {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  max-width: 100%;
}

.workshop-station-name {
  font-family: var(--font-frontier);
  font-size: clamp(14px, 1.3vw, 22px);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.workshop-station-identity {
  font-size: clamp(11px, 0.85vw, 14px);
  letter-spacing: 0.04em;
  color: var(--text-dim);
}

.workshop-change {
  font-family: var(--font-control);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: clamp(12px, 0.95vw, 15px);
  padding: 8px 20px;
  background: linear-gradient(180deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border-color: var(--gr-brass-dim);
  color: var(--gr-brass-bright);
}

.workshop-change:hover:not(:disabled) {
  border-color: var(--gr-brass-bright);
}

/* ---------- Gear slots (attached to a station) ---------- */

.workshop-slot-col {
  display: grid;
  grid-template-rows: repeat(3, 1fr);
  gap: clamp(6px, 0.9vh, 12px);
  min-width: 0;
}

.workshop-slot {
  --slot-rarity: var(--gr-brass-dim);
  position: relative;
  min-width: 0;
  /* Every slot, filled or empty, is at least tall enough for the label, three bonus lines, the durability and a Repair control, so a
     damaged item's Repair button never makes one slot (or one station) taller than its neighbours. */
  min-height: 124px;
  overflow: hidden;
  container: ws-slot / inline-size;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 4px 3px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
  transition: border-color 0.15s ease, transform 0.1s ease;
}

/* Damaged-Gear Repair Controls milestone — the slot is no longer itself a
   button (a REPAIR control now sits beside it as a sibling, and two
   interactive elements can never nest); `.workshop-slot-open` is the inner
   button, and these `:has()` rules carry the frame's own
   hover/disabled treatment the whole card used to carry directly. */
.workshop-slot:has(.workshop-slot-open:hover:not(:disabled)) {
  border-color: var(--gr-brass-bright);
  transform: translateY(-1px);
}

.workshop-slot:has(.workshop-slot-open:disabled) {
  opacity: 0.5;
}

.workshop-slot-open {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font: inherit;
  text-align: center;
  cursor: pointer;
  appearance: none;
  color: var(--text);
  background: none;
  border: none;
  padding: 0;
  margin: 0;
}

.workshop-slot-open:focus-visible,
.workshop-hasher-tile:focus-visible,
.workshop-gear-tile:focus-visible {
  outline: 2px solid var(--gr-brass-bright);
  outline-offset: 2px;
}

.workshop-slot-open:disabled {
  cursor: default;
}

.workshop-slot > .gear-repair-controls {
  margin-top: 0;
  flex: 0 0 auto;
  width: 100%;
  justify-content: center;
  text-align: center; /* the Repair button, or the "TRDR repair required" / unavailable note, centred like the rest of the slot */
}

.workshop-slot[data-rarity="uncommon"] {
  --slot-rarity: var(--ok);
}
.workshop-slot[data-rarity="rare"] {
  --slot-rarity: var(--poor);
}
.workshop-slot[data-rarity="epic"] {
  --slot-rarity: var(--epic);
}
.workshop-slot[data-rarity="legendary"] {
  --slot-rarity: var(--amber);
}
.workshop-slot[data-rarity="common"] {
  --slot-rarity: var(--rich-dim);
}

/* Rarity only ever layers onto a FILLED slot (data-rarity is never set on an empty one), as a border + soft glow over the steel base — never a recolor. */
.workshop-slot[data-rarity] {
  border: 2px solid var(--slot-rarity);
  padding: 3px 2px 4px; /* the 2px outline replaces 1px of the old border: the inside stays the same size */
  background:
    radial-gradient(ellipse 90% 75% at 30% 15%, color-mix(in srgb, var(--slot-rarity) 18%, transparent) 0%, transparent 72%),
    linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--slot-rarity) 28%, transparent),
    0 0 12px -1px color-mix(in srgb, var(--slot-rarity) 55%, transparent),
    inset 0 0 12px -5px color-mix(in srgb, var(--slot-rarity) 70%, transparent);
}

.workshop-slot-art {
  display: flex;
}

.crew-workshop .workshop-slot .gear-icon {
  width: var(--ws-slot-icon);
  height: var(--ws-slot-icon);
}

/* An empty slot shows the slot's own real master art, dimmed. */
.workshop-slot-empty .gear-icon {
  opacity: 0.38;
  filter: grayscale(1) brightness(0.8);
  border-color: var(--gr-brass-dim);
  box-shadow: none;
}

.workshop-slot-label {
  font-family: var(--font-control);
  font-size: clamp(9px, 0.7vw, 12px);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gr-brass);
}

.workshop-slot-empty-label {
  font-size: clamp(10px, 0.75vw, 12px);
  color: var(--text-dim);
}

.workshop-slot-bonus {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
}

.workshop-slot-bonus-line {
  font-size: clamp(10px, 0.8vw, 13px);
  font-weight: 700;
  line-height: 1.25;
  color: var(--ok);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.workshop-slot[data-broken="true"] {
  border-color: var(--danger);
}

.workshop-slot[data-broken="true"] .gear-icon {
  filter: grayscale(0.85);
}

.workshop-slot-broken,
.workshop-broken-flag {
  font-size: clamp(9px, 0.7vw, 11px);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #ffb3b3;
}

/* ---------- Filled gear slot: the item's live information over its dimmed approved card ----------
   Everything lives INSIDE the slot's outline: the approved card (whole, 2:3, never stretched) is a dimmed backdrop; the slot label, the
   bonuses (top) and the durability (pinned to the bottom) are plain, sharp text on top of it. Only the image is ever dimmed. */
.workshop-slot-filled .workshop-slot-open {
  position: relative;
  isolation: isolate;
  justify-content: flex-start;
  gap: 2px;
  min-height: 84px; /* label + three bonus lines + durability: the most a slot ever shows */
  padding: 2px 0 0;
}

.workshop-slot-filled .workshop-slot-art {
  position: absolute;
  inset: 0;
  z-index: -1;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

/* A soft scrim over the artwork keeps the text legible whatever the card looks like. */
.workshop-slot-filled .workshop-slot-art::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(12, 9, 6, 0.34) 0%, rgba(12, 9, 6, 0.16) 45%, rgba(12, 9, 6, 0.4) 100%);
}

.crew-workshop .workshop-slot-filled .gear-icon {
  width: 100%;
  height: 100%;
  border: 0;
  background: transparent;
  box-shadow: none;
  overflow: hidden;
}

/* The card is secondary: dimmed, with the faintest softening on the IMAGE only (the engraving would otherwise compete with the numbers on top
   of it). The outline, label, bonuses and durability are never filtered. */
.crew-workshop .workshop-slot-filled .gear-icon-art {
  opacity: 0.92;
  filter: brightness(0.66) saturate(0.9) blur(0.5px);
}

.workshop-slot-filled .workshop-slot-label {
  text-align: center;
  color: var(--gr-brass-bright);
  white-space: nowrap;
  text-shadow: 0 1px 0 #000, 0 0 4px rgba(0, 0, 0, 0.9);
}

/* The bonus lines and durability become direct rows of the button so the durability can sit at the bottom. */
.workshop-slot-filled .workshop-slot-bonus {
  display: contents;
}

.workshop-slot-filled .workshop-slot-bonus-line {
  text-align: center;
  font-weight: 800;
  line-height: 1.2;
  color: color-mix(in srgb, var(--ok) 80%, #fff);
  text-shadow: 0 1px 0 #000, 0 0 3px rgba(0, 0, 0, 0.95);
}

.workshop-slot-filled .workshop-slot-broken {
  text-shadow: 0 1px 0 #000, 0 0 3px rgba(0, 0, 0, 0.95);
}

.workshop-slot-filled .durability-display {
  margin-top: auto;
  gap: 2px;
  padding: 0 2px 1px;
}

.workshop-slot-filled .durability-display .durability-display-head {
  flex-direction: row;
  align-items: baseline;
  justify-content: center;
  gap: 4px;
}

.workshop-slot-filled .durability-display-label {
  display: none;
  color: var(--text);
  text-shadow: 0 1px 0 #000;
}

.workshop-slot-filled .durability-display-value {
  text-align: center;
  color: #fff;
  font-size: clamp(10px, 0.85vw, 13px);
  font-weight: 800;
  text-shadow: 0 1px 0 #000, 0 0 3px rgba(0, 0, 0, 0.95);
}

.workshop-slot-filled .durability-display-track {
  height: 6px;
  border-color: rgba(255, 255, 255, 0.22);
  background: rgba(0, 0, 0, 0.7);
}

/* The Broken flag is the slot's own red BROKEN line above; the shared flag + note row would not fit. */
.workshop-slot-filled .durability-display-broken-row {
  display: none;
}

/* A wide slot has room for the caption and the percent as well. */
@container ws-slot (min-width: 92px) {
  .workshop-slot-filled .durability-display .durability-display-head {
    flex-direction: column;
    align-items: center;
    gap: 0;
  }
  .workshop-slot-filled .durability-display-label {
    display: block;
    font-size: 10px;
  }
  .workshop-slot-filled .durability-display-value {
    font-size: clamp(10px, 11cqw, 13px);
  }
  .workshop-slot-filled .durability-display-value::after {
    content: " (" attr(data-percent) ")";
  }
}

/* Hover / broken keep the item's own outline (hover brightens, broken goes red). */
.workshop-slot-filled:has(.workshop-slot-open:hover:not(:disabled)) {
  border-color: var(--slot-rarity);
  filter: brightness(1.12);
}
.workshop-slot-filled[data-broken="true"] {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px rgba(209, 91, 91, 0.3), 0 0 12px -1px rgba(209, 91, 91, 0.5), inset 0 0 12px -5px rgba(209, 91, 91, 0.6);
}

/* ---------- Combined crew output (bottom bar) ---------- */

.workshop-output {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: clamp(12px, 1.6vw, 28px);
  padding: clamp(8px, 1.4vh, 16px) clamp(14px, 1.6vw, 28px);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 12px;
  background: linear-gradient(180deg, rgba(48, 34, 24, 0.9) 0%, rgba(16, 12, 9, 0.95) 100%);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.14), 0 -8px 30px rgba(0, 0, 0, 0.45);
}

.workshop-output-cells {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  min-width: 0;
}

.workshop-output-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 0 clamp(8px, 1vw, 20px);
  text-align: center;
}

.workshop-output-cell + .workshop-output-cell {
  border-left: 1px solid rgba(201, 151, 79, 0.3);
}

.workshop-output-label {
  font-family: var(--font-control);
  font-size: clamp(11px, 0.9vw, 15px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gr-brass);
}

.workshop-output-value {
  font-family: var(--font-control);
  font-size: clamp(26px, 2.8vw, 50px);
  line-height: 1.05;
  color: var(--gr-brass-bright);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.7);
}

.workshop-output-effect {
  font-size: clamp(12px, 1vw, 17px);
  font-weight: 600;
  color: var(--text);
}

.workshop-output-caption {
  font-size: clamp(10px, 0.75vw, 12px);
  letter-spacing: 0.06em;
  color: var(--text-dim);
}

.workshop-output-actions {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  align-content: center;
  gap: 10px 12px;
  max-width: 30%;
}

.workshop-output-actions .btn,
.workshop-overlay .btn {
  font-family: var(--font-control);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.workshop-output-actions .btn:not(.btn-primary) {
  padding: 10px 18px;
  font-size: clamp(12px, 0.95vw, 15px);
  background: linear-gradient(180deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border-color: var(--gr-brass-dim);
  color: var(--gr-brass-bright);
}

.workshop-review,
.workshop-action-primary {
  padding: 12px 28px;
  font-size: clamp(14px, 1.15vw, 19px);
  background: linear-gradient(180deg, var(--gr-brass-bright) 0%, var(--gr-brass) 100%);
  border-color: var(--gr-brass-bright);
  color: #1c1206;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 240, 200, 0.4);
}

.workshop-review-hint {
  flex: 1 1 100%;
  text-align: right;
  font-size: clamp(11px, 0.8vw, 13px);
  color: var(--text-dim);
}

/* ---------- Overlays ---------- */

.workshop-overlay-backdrop {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3vh 3vw;
  background: rgba(6, 4, 2, 0.74);
  backdrop-filter: blur(3px);
  animation: workshop-overlay-in 0.16s ease-out;
}

@keyframes workshop-overlay-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.workshop-overlay {
  width: min(1180px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--gr-brass);
  border-radius: 14px;
  background: linear-gradient(180deg, #2b2016 0%, #18130e 62%, #120e0a 100%);
  box-shadow: inset 0 0 0 1px rgba(227, 185, 104, 0.16), 0 30px 80px rgba(0, 0, 0, 0.75);
  color: var(--text);
}

.workshop-overlay-review {
  width: min(960px, 100%);
}

.workshop-overlay-breakdown {
  width: min(1560px, 100%);
}

.workshop-overlay-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 22px;
  border-bottom: 1px solid rgba(201, 151, 79, 0.32);
}

.workshop-overlay-header h2 {
  margin: 0;
  font-family: var(--font-control);
  font-weight: 500;
  font-size: clamp(16px, 1.4vw, 22px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  white-space: nowrap;
}

/* Hasher Picker — Rarity Filter + Default Sort milestone. Sits in the
   header's own middle flex slot (title / filters / Close), centred in
   whatever space remains once the title and Close button take theirs —
   never widening or heightening the overlay to fit. Wraps onto a second
   line at narrow widths (1366x768) rather than overlapping the title or
   Close button or forcing the header taller in a way that clips. */
.workshop-overlay-header-extra {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  justify-content: center;
}

.workshop-rarity-filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 6px;
  row-gap: 4px;
}

/* Compact timber/brass toggle — rarity-coloured border/text, never a
   bright modern "pill." Unselected reads as recessed/inert; selected
   fills faintly with the rarity colour and brightens its border/text,
   a clearly distinct state without changing size/shape. `--toggle-rarity`
   is set inline per-button from the same canonical rarity colour map
   every other rarity-bearing visual in the game already uses (gear icons,
   crew slots, loot reveal) — falls back to a neutral brass tone for the
   "All" control, which has no single rarity. */
.workshop-rarity-toggle {
  appearance: none;
  border: 1px solid var(--toggle-rarity, var(--gr-brass));
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.28);
  color: var(--toggle-rarity, var(--gr-brass));
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1;
  padding: 5px 9px;
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.workshop-rarity-toggle:hover {
  border-color: var(--toggle-rarity, var(--gr-brass-bright));
  color: var(--toggle-rarity, var(--gr-brass-bright));
}

.workshop-rarity-toggle[aria-pressed="true"] {
  background: color-mix(in srgb, var(--toggle-rarity, var(--gr-brass)) 28%, rgba(0, 0, 0, 0.28));
  color: var(--text);
  border-color: var(--toggle-rarity, var(--gr-brass-bright));
}

.workshop-rarity-toggle[aria-pressed="false"] {
  opacity: 0.6;
}

.workshop-rarity-toggle-all {
  --toggle-rarity: var(--gr-brass);
  margin-left: 2px;
  border-style: dashed;
}

.workshop-rarity-toggle:focus-visible {
  outline: 2px solid var(--gr-brass-bright);
  outline-offset: 2px;
  opacity: 1;
}

.workshop-overlay-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 390px);
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

.workshop-empty {
  margin: 0;
  padding: 24px;
  color: var(--text-dim);
}

.workshop-section-label {
  font-family: var(--font-control);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gr-brass);
}

/* Shared "selected item" detail column (Hasher + gear pickers). */
.workshop-hasher-detail,
.workshop-gear-detail {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  overflow-y: auto;
  padding: 20px;
  border-left: 1px solid rgba(201, 151, 79, 0.28);
  background: rgba(0, 0, 0, 0.24);
}

.workshop-detail-name {
  font-family: var(--font-frontier);
  font-size: clamp(18px, 1.6vw, 26px);
  color: var(--text);
}

.workshop-detail-identity {
  font-size: 14px;
  letter-spacing: 0.04em;
  color: var(--text);
}

.workshop-detail-specialist {
  align-self: flex-start;
  font-family: var(--font-control);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  padding: 2px 10px;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 999px;
}

.workshop-detail-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.workshop-detail-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 8px 4px;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 8px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
}

.workshop-detail-stat-label {
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--gr-brass);
}

.workshop-detail-stat-value {
  font-family: var(--font-control);
  font-size: 28px;
  line-height: 1.1;
  color: var(--gr-brass-bright);
  font-variant-numeric: tabular-nums;
}

.workshop-detail-stat-base {
  font-size: 11px;
  color: var(--text-dim);
}

.workshop-detail-status {
  font-size: 13px;
  color: var(--text-dim);
}

.workshop-detail-hint {
  margin: 0;
  font-size: 13px;
  color: var(--text-dim);
}

.workshop-overlay-actions {
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding-top: 10px;
}

.workshop-overlay-actions .workshop-action-primary {
  flex: 1 1 100%;
}

.workshop-overlay-actions .btn:not(.workshop-action-primary) {
  flex: 1 1 auto;
}

/* The shared `.gear-repair-controls` block defaults to vertical-stack
   spacing (margin-top) for the Equipment tab's card column — here it's a
   row sibling of Equip/Unequip/Cancel instead, so it needs none, and its
   own button should size like the other secondary actions in this row. */
.workshop-overlay-actions > .gear-repair-controls {
  margin-top: 0;
  flex: 1 1 auto;
}

.workshop-role-tag {
  align-self: center;
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--gr-brass-dim);
  background: rgba(0, 0, 0, 0.5);
  color: var(--gr-brass-bright);
  white-space: nowrap;
}

.workshop-role-tag-current {
  border-color: var(--amber);
  color: var(--amber);
}

.workshop-role-tag-assigned-elsewhere {
  border-color: var(--text-dim);
  color: var(--text);
}

.workshop-role-tag-broken {
  border-color: var(--danger);
  color: #ffb3b3;
}

/* ---------- Select / Change Hasher ---------- */

.workshop-hasher-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(128px, 11vw, 168px), 1fr));
  gap: 12px;
  align-content: start;
  padding: 18px;
  min-height: 0;
  overflow-y: auto;
}

.workshop-hasher-tile {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 8px;
  font: inherit;
  color: inherit;
  text-align: center;
  cursor: pointer;
  appearance: none;
  background: rgba(0, 0, 0, 0.22);
  border: 2px solid transparent;
  border-radius: 12px;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.workshop-hasher-tile:hover {
  border-color: rgba(201, 151, 79, 0.5);
}

.workshop-hasher-tile-selected {
  border-color: var(--gr-brass-bright);
  background: rgba(201, 151, 79, 0.12);
  box-shadow: 0 0 0 1px rgba(227, 185, 104, 0.4), 0 0 20px -8px var(--gr-brass-bright);
}

.workshop-hasher-tile[data-state="assigned-elsewhere"] .workshop-hasher-tile-art {
  opacity: 0.45;
  filter: saturate(0.5);
}

.workshop-hasher-tile-art {
  display: block;
  width: 100%;
}

.workshop-hasher-tile-name {
  font-family: var(--font-frontier);
  font-size: 13px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- Select / Equip Gear ---------- */

.workshop-gear-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  overflow-y: auto;
  padding: 18px;
}

.workshop-gear-current {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.26);
}

.workshop-gear-current-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
}

.workshop-gear-current-none {
  font-size: 14px;
  color: var(--text-dim);
}

.workshop-gear-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(120px, 10vw, 150px), 1fr));
  gap: 12px;
  align-content: start;
}

.workshop-gear-tile {
  --tile-rarity: var(--gr-brass-dim);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 10px 8px;
  font: inherit;
  color: inherit;
  text-align: center;
  cursor: pointer;
  appearance: none;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border: 2px solid color-mix(in srgb, var(--tile-rarity) 55%, var(--gr-brass-dim));
  border-radius: 12px;
}

.workshop-gear-tile[data-rarity="uncommon"] {
  --tile-rarity: var(--ok);
}
.workshop-gear-tile[data-rarity="rare"] {
  --tile-rarity: var(--poor);
}
.workshop-gear-tile[data-rarity="epic"] {
  --tile-rarity: var(--epic);
}
.workshop-gear-tile[data-rarity="legendary"] {
  --tile-rarity: var(--amber);
}

.workshop-gear-tile:hover {
  border-color: var(--tile-rarity);
}

.workshop-gear-tile-selected {
  border-color: var(--gr-brass-bright);
  box-shadow: 0 0 0 1px rgba(227, 185, 104, 0.45), 0 0 20px -8px var(--gr-brass-bright);
}

.workshop-gear-tile[data-broken="true"] {
  border-color: var(--danger);
}

.workshop-gear-tile[data-broken="true"] .gear-icon {
  filter: grayscale(0.85);
}

.workshop-gear-tile-rarity {
  font-family: var(--font-control);
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text);
  max-width: 100%;
  line-height: 1.2;
  overflow-wrap: anywhere;
}

/* Rented Hasher teams (Bible Section 66): a borrowed or rented-out Hasher's card carries a small label. */
.workshop-hasher-rental {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
}
.workshop-hasher-rental-badge {
  position: absolute;
  top: 6px;
  left: 50%;
  transform: translateX(-50%);
  max-width: calc(100% - 12px);
  padding: 2px 8px;
  border-radius: 4px;
  background: rgba(20, 14, 6, 0.86);
  border: 1px solid rgba(214, 170, 92, 0.75);
  color: #f3dfb4;
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

/* An imported item's full name under a card shown on its own (the nameplate holds about 24 characters; a few cards have no room for one). */
.gear-selector-card-name,
.mk-sell-card-name {
  max-width: 100%;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
  color: var(--text);
  overflow-wrap: anywhere;
}

/* The approved card inside a tile: a clear thumbnail, with the name and bonuses repeated as text below it. */
.workshop-gear-tile .gear-card {
  max-width: 112px;
}

.workshop-gear-tile-bonus {
  font-size: 13px;
  font-weight: 600;
  color: var(--ok);
}

.workshop-gear-tile-durability {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.workshop-gear-detail-card {
  width: min(180px, 46%);
  align-self: center;
}

.workshop-gear-detail-facts {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: center;
}

.workshop-gear-detail-line {
  font-size: 14px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.workshop-compare {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.workshop-compare th,
.workshop-compare td {
  padding: 4px 8px;
  text-align: center;
  border-bottom: 1px solid rgba(201, 151, 79, 0.2);
}

.workshop-compare thead th {
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gr-brass);
  font-weight: 500;
}

.workshop-compare tbody th {
  color: var(--gr-brass-bright);
  font-family: var(--font-control);
  letter-spacing: 0.1em;
}

.workshop-delta-up {
  color: var(--ok);
  font-weight: 700;
}

.workshop-delta-down {
  color: var(--danger);
  font-weight: 700;
}

/* ---------- Review & Lock / Breakdown ---------- */

.workshop-review-body {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 20px 24px 22px;
  overflow-y: auto;
}

.workshop-review-crew {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

.workshop-review-member {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 12px 10px;
  text-align: center;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
}

.workshop-review-member[data-position="lead"] {
  border-color: var(--gr-brass);
}

.workshop-review-role {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-family: var(--font-control);
  font-size: 13px;
  letter-spacing: 0.2em;
  color: var(--gr-brass-bright);
}

.workshop-review-weight {
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--gr-brass);
  font-variant-numeric: tabular-nums;
}

.workshop-review-name {
  font-family: var(--font-frontier);
  font-size: 18px;
}

.workshop-review-identity {
  font-size: 13px;
  color: var(--text-dim);
}

.workshop-review-stats {
  display: flex;
  gap: 10px;
  font-family: var(--font-control);
  font-size: 15px;
  letter-spacing: 0.06em;
  color: var(--gr-brass-bright);
  font-variant-numeric: tabular-nums;
}

.workshop-review-gear {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 5px;
  margin-top: 4px;
}

.workshop-review-gear-empty .gear-icon {
  opacity: 0.3;
  filter: grayscale(1);
}

.workshop-review-output {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  padding: 10px 0;
  border-top: 1px solid rgba(201, 151, 79, 0.3);
  border-bottom: 1px solid rgba(201, 151, 79, 0.3);
}

.workshop-review-output .workshop-output-cell + .workshop-output-cell {
  border-left: 1px solid rgba(201, 151, 79, 0.3);
}

.workshop-review-copy {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 15px;
}

.workshop-review-copy p {
  margin: 0;
  color: var(--text);
  font-weight: 600;
}

.workshop-review-copy .workshop-review-copy-note {
  color: var(--text-dim);
  font-weight: 400;
  font-size: 14px;
}

.workshop-review-actions .btn:not(.workshop-action-primary) {
  flex: 0 0 auto;
}

.workshop-review-actions {
  justify-content: flex-end;
}

.workshop-review-actions .workshop-action-primary {
  flex: 0 0 auto;
}

/* View Breakdown — three side-by-side role panels (Secondary · Lead · Tertiary, the Workshop's own order), one compact table per panel. Sized to fit without internal scrolling at the approved desktop viewports; type scales with the viewport rather than shrinking to fit. */

.workshop-breakdown-body {
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 1.6vh, 18px);
  padding: clamp(14px, 2vh, 22px) clamp(14px, 1.6vw, 26px);
  overflow-y: auto;
  min-height: 0;
}

.workshop-breakdown-grid {
  display: grid;
  grid-template-columns: 1fr 1.06fr 1fr;
  gap: clamp(10px, 1.1vw, 20px);
  align-items: start;
}

.workshop-breakdown-panel {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: clamp(12px, 1.6vh, 18px) clamp(10px, 1vw, 18px) clamp(8px, 1.2vh, 14px);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 12px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.12);
}

/* Lead carries the same subtle emphasis as its Workshop station — brighter brass and a soft glow, not a bigger panel. */
.workshop-breakdown-panel[data-position="lead"] {
  border-color: var(--gr-brass);
  box-shadow: inset 0 0 0 1px rgba(227, 185, 104, 0.26), 0 0 32px -14px rgba(240, 169, 78, 0.4);
}

.workshop-breakdown-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: var(--font-control);
}

.workshop-breakdown-role {
  font-size: clamp(14px, 1.2vw, 20px);
  letter-spacing: 0.2em;
  color: var(--gr-brass-bright);
}

.workshop-breakdown-weight {
  font-size: clamp(12px, 0.95vw, 16px);
  letter-spacing: 0.06em;
  color: #1c1206;
  background: linear-gradient(180deg, var(--gr-brass-bright), var(--gr-brass));
  padding: 1px 10px;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

.workshop-breakdown-name {
  font-family: var(--font-frontier);
  font-size: clamp(16px, 1.3vw, 22px);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.workshop-breakdown-identity {
  font-size: clamp(12px, 0.9vw, 15px);
  letter-spacing: 0.04em;
  color: var(--text-dim);
}

.workshop-breakdown-empty {
  margin: 8px 0 0;
  font-size: clamp(13px, 1vw, 16px);
  color: var(--text-dim);
}

.workshop-breakdown-table {
  width: 100%;
  margin-top: 6px;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

.workshop-breakdown-table th,
.workshop-breakdown-table td {
  padding: clamp(5px, 0.9vh, 9px) clamp(3px, 0.4vw, 8px);
  text-align: right;
  border-bottom: 1px solid rgba(201, 151, 79, 0.2);
  white-space: nowrap;
}

.workshop-breakdown-table tbody tr:last-child th,
.workshop-breakdown-table tbody tr:last-child td {
  border-bottom: 0;
}

.workshop-breakdown-table thead th {
  font-family: var(--font-control);
  font-weight: 500;
  font-size: clamp(11px, 0.8vw, 13px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gr-brass);
}

.workshop-breakdown-table tbody th {
  text-align: left;
  font-family: var(--font-control);
  font-weight: 500;
  font-size: clamp(13px, 1vw, 16px);
  letter-spacing: 0.1em;
  color: var(--gr-brass-bright);
}

.workshop-breakdown-table thead th:first-child {
  text-align: left;
}

.workshop-breakdown-table td {
  font-size: clamp(15px, 1.15vw, 20px);
  color: var(--text);
}

.workshop-breakdown-table td.workshop-breakdown-effective {
  font-weight: 700;
  color: var(--gr-brass-bright);
}

/* Gear bonus: positive is green, zero recedes, and a negative (effective below base) is flagged rather than hidden. */
.workshop-breakdown-table td.workshop-breakdown-gear-positive {
  color: var(--ok);
  font-weight: 700;
}

.workshop-breakdown-table td.workshop-breakdown-gear-zero {
  color: var(--text-dim);
  opacity: 0.55;
}

.workshop-breakdown-table td.workshop-breakdown-gear-negative {
  color: var(--danger);
  font-weight: 700;
}

.workshop-overlay-actions.workshop-breakdown-actions {
  margin-top: 0;
  padding-top: 0;
  justify-content: flex-end;
}

.workshop-overlay-actions.workshop-breakdown-actions .btn:not(.workshop-action-primary) {
  flex: 0 0 auto;
  min-width: 140px;
}

/* Below the approved desktop widths the three panels stack rather than shrink their type. */
@media (max-width: 1000px) {
  .workshop-breakdown-grid {
    grid-template-columns: 1fr;
  }
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .workshop-overlay-backdrop {
    animation: none;
  }

  .workshop-slot,
  .workshop-hasher-tile {
    transition: none;
  }

  .workshop-slot:has(.workshop-slot-open:hover:not(:disabled)) {
    transform: none;
  }
}

/* Station card footer text — CardFrame's footer type is fixed-px (sized for the small inventory cards); the Workshop's cards are much larger, so the same footer scales with the viewport here (scoped to the stations only; picker tiles keep the default). */
.workshop-station-card .card-frame-title {
  font-size: clamp(11px, 0.85vw, 17px);
}

.workshop-station-card .card-frame-stats {
  font-size: clamp(10px, 0.72vw, 14px);
}


/* A primary action that can't be used right now never wears the gold "available" emphasis: the disabled state drops to blackened steel with a dashed border, hatching and muted text (and no glow), so it can't be mistaken for an actionable button. Native `disabled` still applies, so it is also skipped by Tab and inert to click/Enter/Space. Scoped to the Workshop's two primary-action classes only. */
.workshop-review:disabled,
.workshop-action-primary:disabled {
  opacity: 1;
  cursor: not-allowed;
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.035) 0 6px, transparent 6px 12px),
    linear-gradient(180deg, #2a2d32 0%, #1c1f23 100%);
  border: 1px dashed #5a5f66;
  color: #858b93;
  box-shadow: none;
  text-shadow: none;
}

/* ==========================================================================
   MINING SUPPLY DEPOT — Phase 2 shell (SupplyDepot.tsx)
   ==========================================================================
   Full-viewport frontier depot in the Crew Workshop's visual language: timber,
   forged steel and aged brass from the existing --gr-* tokens over a dark
   treatment of the reused mine plate (chosen in ONE resolver,
   resolveSupplyDepotBackgroundAsset, so a dedicated Depot plate can replace it
   without touching this layout). Every rule is scoped to a .depot* class;
   nothing here alters any existing component. The selected shop's product
   area is reserved empty for the later product-card phase. */

.depot-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100; /* same layer as .modal-backdrop, so a dialog opened later in the DOM (a popup, a confirmation) still stacks above */
  display: flex;
  padding: clamp(8px, 2vh, 26px) clamp(8px, 1.6vw, 30px);
  background: rgba(6, 4, 2, 0.8);
  animation: depot-fade-in 0.16s ease-out;
}

@keyframes depot-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.depot {
  --depot-pad: clamp(14px, 1.6vw, 30px);
  --depot-gap: clamp(10px, 1.6vh, 20px);
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr);
  gap: var(--depot-gap);
  padding: clamp(12px, 2vh, 24px) var(--depot-pad) var(--depot-pad);
  overflow: hidden;
  isolation: isolate;
  border: 1px solid var(--gr-brass);
  border-radius: 14px;
  background: var(--coal);
  box-shadow: inset 0 0 0 1px rgba(227, 185, 104, 0.16), 0 30px 80px rgba(0, 0, 0, 0.75);
  color: var(--text);
  font-family: var(--font-interface);
}

/* ---------- Environment (isolated, replaceable) ---------- */

.depot-bg-layer {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.depot-bg {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 60%;
  filter: brightness(0.4) saturate(0.85) blur(1.5px);
  transform: scale(1.04);
}

.depot-bg-vignette {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 60% 50% at 50% 40%, rgba(240, 169, 78, 0.12) 0%, transparent 62%),
    radial-gradient(ellipse 95% 85% at 50% 50%, transparent 36%, rgba(8, 5, 3, 0.9) 100%),
    linear-gradient(180deg, rgba(8, 5, 3, 0.6) 0%, transparent 26%, transparent 70%, rgba(8, 5, 3, 0.8) 100%);
}

.depot-header,
.depot-tabs,
.depot-panel {
  position: relative;
  z-index: 1;
}

/* ---------- Header: title, resource strip, Close ---------- */

.depot-header {
  display: flex;
  align-items: center;
  gap: clamp(12px, 1.4vw, 26px);
  min-width: 0;
}

.depot-logo {
  height: clamp(32px, 4.6vh, 52px);
  width: auto;
  flex: 0 0 auto;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.6));
}

.depot-title {
  margin: 0;
  flex: 0 1 auto;
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: clamp(20px, 2.3vw, 40px);
  line-height: 1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.85);
  white-space: nowrap;
}

/* GOAL 2 — prominent cash badge: sits in the header (never inside a tab panel, so it survives every Shop-tab switch) and is visually distinct from the compact resource strip beside it. */
.depot-cash-badge {
  flex: 0 0 auto;
  font-family: var(--font-control);
  font-weight: 700;
  font-size: clamp(14px, 1.35vw, 24px);
  letter-spacing: 0.06em;
  white-space: nowrap;
  color: #eafff0;
  background: linear-gradient(180deg, color-mix(in srgb, var(--ok) 38%, var(--gr-steel)) 0%, color-mix(in srgb, var(--ok) 22%, var(--gr-steel-dark)) 100%);
  border: 1px solid color-mix(in srgb, var(--ok) 60%, var(--gr-brass-dim));
  border-radius: 10px;
  padding: clamp(6px, 0.9vh, 10px) clamp(12px, 1.2vw, 20px);
  font-variant-numeric: tabular-nums;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 4px 14px rgba(0, 0, 0, 0.4);
}

.depot-resources {
  margin: 0 0 0 auto;
  display: flex;
  min-width: 0;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
  background: rgba(14, 12, 9, 0.72);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.1);
}

.depot-resource {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: clamp(5px, 0.8vh, 9px) clamp(10px, 1.1vw, 20px);
}

.depot-resource + .depot-resource {
  border-left: 1px solid rgba(201, 151, 79, 0.3);
}

.depot-resource dt {
  font-family: var(--font-control);
  font-size: clamp(11px, 0.8vw, 13px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gr-brass);
  white-space: nowrap;
}

.depot-resource dd {
  margin: 0;
  font-family: var(--font-control);
  font-size: clamp(15px, 1.25vw, 22px);
  line-height: 1.15;
  color: var(--gr-brass-bright);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.depot-close {
  flex: 0 0 auto;
  font-family: var(--font-control);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: clamp(12px, 0.95vw, 15px);
  padding: 8px 20px;
  background: linear-gradient(180deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  border-color: var(--gr-brass-dim);
  color: var(--gr-brass-bright);
}

.depot-close:hover:not(:disabled) {
  border-color: var(--gr-brass-bright);
}

.depot-close:focus-visible,
.depot-tab:focus-visible {
  outline: 2px solid var(--gr-brass-bright);
  outline-offset: 2px;
}

/* ---------- Horizontal shop tabs ---------- */

.depot-tabs {
  display: grid;
  grid-template-columns: repeat(var(--depot-tab-count, 6), minmax(0, 1fr));
  gap: clamp(6px, 0.8vw, 14px);
}

.depot-tab {
  --tab-accent: var(--text-dim);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-width: 0;
  padding: clamp(8px, 1.3vh, 15px) 8px;
  font: inherit;
  color: var(--text);
  cursor: pointer;
  appearance: none;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
  background: linear-gradient(180deg, rgba(58, 42, 29, 0.86) 0%, rgba(24, 18, 12, 0.92) 100%);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.1);
  transition: border-color 0.15s ease, background 0.15s ease;
}

.depot-tab:hover {
  border-color: var(--gr-brass);
}

.depot-tab-name {
  font-family: var(--font-control);
  font-size: clamp(15px, 1.4vw, 24px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  white-space: nowrap;
}

/* The state chip is always TEXT (Unlocked / Eligible / Locked), never colour alone. */
.depot-tab-state {
  font-family: var(--font-control);
  font-size: clamp(11px, 0.85vw, 14px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 1px 10px;
  border: 1px solid var(--tab-accent);
  border-radius: 999px;
  color: var(--tab-accent);
  background: rgba(0, 0, 0, 0.35);
}

.depot-tab[data-state="unlocked"] {
  --tab-accent: var(--ok);
}

.depot-tab[data-state="eligible"] {
  --tab-accent: var(--amber);
}

/* Locked tabs stay fully selectable (read-only inspection) — only quieter. */
.depot-tab[data-state="locked"] {
  background: linear-gradient(180deg, rgba(34, 27, 20, 0.86) 0%, rgba(16, 13, 10, 0.92) 100%);
}

.depot-tab[data-state="locked"] .depot-tab-name {
  color: var(--gr-brass);
}

.depot-tab[data-active="true"] {
  border-color: var(--gr-brass-bright);
  background: linear-gradient(180deg, rgba(122, 86, 42, 0.88) 0%, rgba(58, 42, 29, 0.96) 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 220, 160, 0.3), 0 0 26px -8px var(--amber);
}

.depot-tab[data-active="true"] .depot-tab-name {
  color: #fff1d0;
}

/* ---------- Selected-shop panel (content region reserved for the product cards) ---------- */

.depot-panel {
  display: flex;
  flex-direction: column;
  gap: var(--depot-gap);
  min-height: 0;
  padding: clamp(12px, 1.8vh, 22px) clamp(12px, 1.4vw, 26px);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 12px;
  background: linear-gradient(180deg, rgba(43, 32, 22, 0.86) 0%, rgba(18, 14, 10, 0.92) 100%);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.1);
}

.depot-shop-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px clamp(10px, 1vw, 18px);
}

.depot-shop-title {
  margin: 0;
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: clamp(18px, 1.8vw, 30px);
  color: var(--text);
}

.depot-shop-state {
  font-family: var(--font-control);
  font-size: clamp(12px, 0.95vw, 15px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 2px 12px;
  border: 1px solid var(--text-dim);
  border-radius: 999px;
  color: var(--text-dim);
  background: rgba(0, 0, 0, 0.35);
}

.depot-shop-state[data-state="unlocked"] {
  border-color: var(--ok);
  color: var(--ok);
}

.depot-shop-state[data-state="eligible"] {
  border-color: var(--amber);
  color: var(--amber);
}

.depot-shop-note {
  flex: 1 1 100%;
  margin: 0;
  font-size: clamp(12px, 0.95vw, 15px);
  color: var(--text-dim);
}

.depot-products {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* Phase 4 finding: the shop-unlock panel's own fixed height means this
     region now needs to scroll far more often at the approved desktop
     widths than it did in Phase 3 (which never had to share the panel's
     budget). The default scrollbar was invisible against this dark
     background, so an action control could sit just past the fold with no
     visible cue that more was there — same styled-scrollbar treatment
     already used for the gear/terminal scroll panels elsewhere. */
  scrollbar-width: thin;
  scrollbar-color: var(--gr-brass-dim) var(--gr-rivet);
}

.depot-products::-webkit-scrollbar {
  width: 10px;
}

.depot-products::-webkit-scrollbar-track {
  background: var(--gr-rivet);
}

.depot-products::-webkit-scrollbar-thumb {
  background: var(--gr-brass-dim);
  border-radius: 999px;
}

/* Below the approved desktop widths the tabs wrap into rows instead of shrinking their type. */
@media (max-width: 1000px) {
  .depot-tabs {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .depot-header {
    flex-wrap: wrap;
  }
}

@media (prefers-reduced-motion: reduce) {
  .depot-backdrop {
    animation: none;
  }

  .depot-tab {
    transition: none;
  }
}

/* ---------- Phase 3: product sections and cards ---------- */

.depot-products:focus-visible {
  outline: 2px solid var(--gr-brass-bright);
  outline-offset: 2px;
}

/* Mining Rigs and Cooling sit side by side when there is room (each with its own card grid), and stack below that. */
.depot-sections {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
  gap: var(--depot-gap) clamp(14px, 1.6vw, 30px);
  align-items: start;
}

.depot-section {
  min-width: 0;
}

.depot-section-title {
  margin: 0 0 clamp(8px, 1.2vh, 14px);
  padding-bottom: 6px;
  font-family: var(--font-control);
  font-weight: 500;
  font-size: clamp(14px, 1.2vw, 20px);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
  border-bottom: 1px solid rgba(201, 151, 79, 0.32);
}

.depot-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(220px, 17vw, 340px), 1fr));
  gap: clamp(10px, 1.1vw, 20px);
}

.depot-card {
  --card-accent: var(--gr-brass-dim);
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--card-accent);
  border-radius: 12px;
  background: linear-gradient(165deg, var(--gr-steel) 0%, var(--gr-steel-dark) 100%);
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.1), 0 10px 24px rgba(0, 0, 0, 0.4);
}

.depot-card[data-status="available"] {
  --card-accent: color-mix(in srgb, var(--ok) 55%, var(--gr-brass-dim));
}

.depot-card[data-tier="advanced"] {
  box-shadow: inset 0 0 0 1px rgba(227, 185, 104, 0.22), 0 10px 24px rgba(0, 0, 0, 0.4);
}

/* Artwork plate — the same art window for every product; the level badge sits on it. */
.depot-card-art {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(110px, 19vh, 210px);
  background:
    radial-gradient(ellipse 70% 65% at 50% 55%, rgba(240, 169, 78, 0.16) 0%, transparent 72%),
    linear-gradient(180deg, #2a2118 0%, #15110d 100%);
  border-bottom: 1px solid rgba(201, 151, 79, 0.25);
}

.depot-card-art-image {
  max-width: 82%;
  max-height: 92%;
  object-fit: contain;
  filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.6));
}

/* Cooling's base+accent decal pair, registered on top of each other (Shop Presentation Phase). Both PNGs share one canvas and are pixel-aligned (coolingDecals.ts), so stacking them absolutely inside a box sized like a single .depot-card-art-image reproduces the board's own resting (unpulsed) decal exactly.
   PLAYTHROUGH BLOCKER fix — this span previously had only max-width/max-height (caps, not sizes) and both its <img> children are position:absolute (removed from flow, so they never contribute to the parent's own size) — with no intrinsic size of its own, a bare <span> collapses to 0x0, silently rendering nothing despite both images loading successfully (confirmed: naturalWidth/naturalHeight populated, complete:true, opacity/visibility normal — only computed width/height were 0). aspect-ratio (all six decal PNGs share this exact 1030x864 canvas — verified from their real PNG headers) plus an explicit height gives the browser the same "how big should this actually be" information object-fit:contain gets for free from a single <img>'s own intrinsic size; max-width remains as the same second constraint a single image already had. */
.depot-card-art-layers {
  position: relative;
  aspect-ratio: 1030 / 864;
  height: 92%;
  max-width: 82%;
}

.depot-card-art-layers .depot-card-art-image {
  position: absolute;
  inset: 0;
  max-width: 100%;
  max-height: 100%;
  width: 100%;
  height: 100%;
}

/* The existing fallback glyph treatment (⚙ / ❄) — the defensive case for a level neither resolver has approved art for. */
.depot-card-art-glyph {
  font-size: clamp(52px, 9vh, 104px);
  line-height: 1;
  color: var(--gr-brass);
}

.depot-card[data-kind="cooling"] .depot-card-art {
  background:
    radial-gradient(ellipse 70% 65% at 50% 55%, rgba(127, 212, 255, 0.16) 0%, transparent 72%),
    linear-gradient(180deg, #1c2329 0%, #10151a 100%);
}

.depot-card[data-kind="cooling"] .depot-card-art-glyph {
  color: var(--cooled-glow);
  text-shadow: 0 0 22px rgba(127, 212, 255, 0.45);
}

.depot-card-level {
  position: absolute;
  top: 8px;
  left: 8px;
  padding: 2px 12px;
  font-family: var(--font-control);
  font-size: clamp(15px, 1.25vw, 22px);
  letter-spacing: 0.08em;
  color: #1c1206;
  background: linear-gradient(180deg, var(--gr-brass-bright), var(--gr-brass));
  border-radius: 999px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}

.depot-card-body {
  display: flex;
  flex-direction: column;
  gap: clamp(5px, 0.8vh, 9px);
  padding: clamp(9px, 1.2vh, 14px) clamp(10px, 0.9vw, 16px) clamp(10px, 1.3vh, 16px);
}

.depot-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.depot-card-title {
  margin: 0;
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: clamp(16px, 1.3vw, 22px);
  color: var(--text);
  white-space: nowrap;
}

.depot-card-tier {
  flex: 0 0 auto;
  font-family: var(--font-control);
  font-size: clamp(11px, 0.85vw, 14px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 1px 9px;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 999px;
  color: var(--gr-brass);
  background: rgba(0, 0, 0, 0.3);
}

.depot-card-tier[data-tier="advanced"] {
  border-color: var(--gr-brass-bright);
  color: var(--gr-brass-bright);
}

.depot-card-facts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: clamp(12px, 0.95vw, 15px);
  color: var(--text-dim);
}

.depot-card-facts strong {
  color: var(--cooled-glow);
  font-family: var(--font-control);
  font-size: 1.15em;
  letter-spacing: 0.04em;
}

.depot-card-price {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding-top: 4px;
  border-top: 1px solid rgba(201, 151, 79, 0.2);
}

.depot-card-price-label {
  font-family: var(--font-control);
  font-size: clamp(11px, 0.8vw, 13px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gr-brass);
}

.depot-card-price-value {
  font-family: var(--font-control);
  font-size: clamp(18px, 1.5vw, 26px);
  line-height: 1.1;
  color: var(--gr-brass-bright);
  font-variant-numeric: tabular-nums;
}

.depot-card[data-status="unaffordable"] .depot-card-price-value {
  color: var(--danger);
}

.depot-card-unlock {
  font-size: clamp(12px, 0.9vw, 14px);
  color: var(--text-dim);
}

.depot-card-unlock strong {
  color: var(--gr-brass-bright);
  font-family: var(--font-control);
  letter-spacing: 0.04em;
}

/* Availability state — always TEXT, never colour alone. It is a non-interactive plate; the purchase / unlock control arrives in a later phase. */
.depot-card-status {
  margin-top: 2px;
  padding: clamp(5px, 0.8vh, 9px) 8px;
  text-align: center;
  font-family: var(--font-control);
  font-size: clamp(12px, 0.95vw, 15px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  border-radius: 8px;
  border: 1px solid var(--gr-brass-dim);
  color: var(--text-dim);
  background: rgba(0, 0, 0, 0.3);
}

.depot-card-status[data-status="available"] {
  color: var(--ok);
  border-color: var(--ok);
  background: rgba(111, 191, 115, 0.1);
}

/* An unaffordable product reads unmistakably disabled: steel, dashed and hatched, in the same treatment the Crew Workshop uses. */
.depot-card-status[data-status="unaffordable"] {
  color: #d98a8a;
  border: 1px dashed #7a4a4a;
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.035) 0 6px, transparent 6px 12px),
    linear-gradient(180deg, #2a2d32 0%, #1c1f23 100%);
}

.depot-card-status[data-status="advanced-locked"] {
  color: var(--amber);
  border: 1px dashed var(--gr-brass-dim);
}

.depot-card-status[data-status="shop-locked"],
.depot-card-status[data-status="week-ended"] {
  border: 1px dashed #5a5f66;
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.035) 0 6px, transparent 6px 12px),
    linear-gradient(180deg, #2a2d32 0%, #1c1f23 100%);
}

/* VISUAL CORRECTION — SHOPS 2 AND 3 RARITY COLOURS: "shop-locked" cards
   used to share the "week-ended" treatment below (grayscale(0.85)
   brightness(0.75)) — a broad filter across the whole card, exactly the
   kind this milestone's own instructions rule out. Measured on real
   pixels: it drove Miner L3/L4/L5/L6 and Cooling L2/L3's approved accent
   hues (blue/purple/gold/crimson) down to single-digit HSV saturation,
   reading as silver/lilac/grey against the approved board appearance for
   the same level — while Shop 1 (never lockable) never hit this rule at
   all, which is exactly why only Shops 2/3 read wrong. A Supply Depot
   preview's whole purpose is to show what a shop actually contains, so
   the "quiet the artwork" idea itself doesn't hold for a locked-but-
   previewable shop — the existing "SHOP LOCKED" action label and
   "Read-only preview" banner already communicate lock state without
   touching the art. `week-ended` (the week is genuinely over, nothing
   anywhere is purchasable) is a real "everything is closed" state, kept
   exactly as before. */
.depot-card[data-status="week-ended"] .depot-card-art-image,
.depot-card[data-status="week-ended"] .depot-card-art-glyph {
  filter: grayscale(0.85) brightness(0.75);
}

@media (max-width: 1000px) {
  .depot-sections {
    grid-template-columns: 1fr;
  }
}

/* ---------- Phase 4: product actions, feedback, shop-unlock panel ---------- */

/* The card's action control is the same plate as before, now a real
   <button> (data-status keeps every colour treatment above unchanged). Only
   the actionable variants (BUY / UNLOCK LEVEL) need their own cursor/hover/
   focus — the disabled ones already read as disabled via the existing
   dashed/hatched treatment and the native :disabled cursor. */
.depot-card-status {
  appearance: none;
  width: 100%;
  font: inherit;
  cursor: not-allowed;
}

.depot-card-status.depot-card-action-enabled {
  cursor: pointer;
}

.depot-card-status.depot-card-action-enabled:hover {
  filter: brightness(1.12);
}

.depot-card-status.depot-card-action-enabled:focus-visible {
  outline: 2px solid var(--gr-brass-bright);
  outline-offset: 2px;
}

/* An affordable level unlock reads as a call to action — solid brass border, distinct from the still-dashed "advanced locked, insufficient cash" plate above. */
.depot-card-status[data-status="advanced-locked"].depot-card-action-enabled {
  color: var(--gr-brass-bright);
  border: 1px solid var(--gr-brass-bright);
  background: rgba(227, 185, 104, 0.16);
}

/* ---------- Depot-scoped feedback ---------- */

.depot-feedback {
  padding: clamp(6px, 0.9vh, 10px) 12px;
  font-family: var(--font-control);
  font-size: clamp(12px, 0.95vw, 14px);
  letter-spacing: 0.03em;
  border-radius: 8px;
  border: 1px solid var(--ok);
  color: var(--ok);
  background: rgba(111, 191, 115, 0.12);
}

.depot-feedback-error {
  border-color: var(--danger);
  color: #f2b8b8;
  background: rgba(209, 91, 91, 0.14);
}

/* ---------- Shop-unlock / progression panel ---------- */

.depot-unlock-panel {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: clamp(10px, 1.4vh, 16px) clamp(12px, 1.2vw, 18px);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.22);
}

.depot-unlock-panel[data-state="eligible"] {
  border-color: var(--gr-brass-bright);
}

.depot-unlock-hint {
  margin: 0;
  font-size: clamp(11px, 0.85vw, 13px);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.depot-unlock-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.depot-unlock-label {
  font-family: var(--font-frontier);
  font-size: clamp(15px, 1.15vw, 19px);
  color: var(--text);
}

.depot-unlock-state {
  font-family: var(--font-control);
  font-size: clamp(11px, 0.85vw, 13px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.depot-unlock-state[data-state="eligible"] {
  color: var(--gr-brass-bright);
}

.depot-unlock-progress-track {
  background: #101317;
  border-radius: 999px;
  height: 8px;
  overflow: hidden;
}

.depot-unlock-progress-fill {
  background: var(--gr-brass-bright);
  height: 100%;
}

.depot-unlock-progress-text {
  margin-top: 3px;
  font-size: clamp(11px, 0.85vw, 13px);
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.depot-unlock-cost-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  font-size: clamp(12px, 0.9vw, 14px);
  color: var(--text-dim);
}

.depot-unlock-cost {
  font-family: var(--font-control);
  font-size: clamp(15px, 1.2vw, 19px);
  color: var(--gr-brass-bright);
  font-variant-numeric: tabular-nums;
}

.depot-unlock-action {
  align-self: flex-start;
}

/* ==========================================================================
   Equipment modal — Gear tab (owned-gear inventory) + equip dialog.

   The modal reuses the Marketplace's console housing (the
   `[data-testid="equipment-modal"]` selectors sit beside the Marketplace's
   own in that block above — nothing else uses that test id) and its `.mk-*`
   browse vocabulary (shell, header, tabs, search, filter rail, checkboxes,
   result count, card grid, card). Only what differs in meaning or layout gets
   an `.eq-` class here: the informational note, per-option counts, radio
   variant, durability, flags, equipped-by line, empty state, and the equip
   dialog. Colour tokens are the existing --gr-* / semantic ones; no new
   palette and no severity thresholds (durability is green, or red only when
   the server's own `broken` flag is set).
   ========================================================================== */

.eq-note {
  flex-shrink: 0;
  margin: 0 0 10px;
  padding: 7px 12px;
  font-family: var(--font-interface);
  font-size: 12px;
  line-height: 1.4;
  color: var(--text-dim);
  background: rgba(127, 212, 255, 0.06);
  border: 1px solid rgba(127, 212, 255, 0.22);
  border-radius: 6px;
}

/* The crew-lock note rides in the result-count row (count left, note right) so the card grid keeps its height at short viewports. */
.eq-results-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 6px 16px;
  margin: 0 0 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(201, 151, 79, 0.14);
}

.eq-results-head .mk-result-count {
  margin: 0;
  padding: 0;
  border: 0;
}

.eq-note-inline {
  margin: 0;
  padding: 3px 12px;
  border-radius: 999px;
}

/* Two tabs sit as a compact left-aligned pair rather than stretching across the whole console the way the Marketplace's four do. */
.eq-shell .mk-tabs {
  align-self: flex-start;
}

.eq-shell .mk-tab {
  flex: 0 0 auto;
  min-width: 190px;
}

.eq-check-count {
  margin-left: auto;
  font-family: var(--font-control);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}

.eq-radio .mk-check-box,
.eq-radio .mk-check-box::after {
  border-radius: 50%;
}

.eq-feedback {
  margin: 0 0 12px;
  padding: 8px 12px;
  border-radius: 6px;
  font-family: var(--font-interface);
  font-size: 13px;
  border: 1px solid var(--panel-border);
}

.eq-feedback-success {
  color: var(--ok);
  border-color: var(--ok);
  background: rgba(111, 191, 115, 0.08);
}

.eq-feedback-error {
  color: #ffb3b3;
  border-color: var(--danger);
  background: rgba(209, 91, 91, 0.1);
}

.eq-browse .empty-state {
  text-align: center;
  padding: 40px 12px;
}

.eq-browse .empty-state-title {
  margin: 0 0 6px;
  font-family: var(--font-frontier);
  font-size: 18px;
  color: var(--gr-brass-bright);
}

.eq-browse .empty-hint {
  margin: 0;
  font-size: 13px;
}

.eq-empty-clear {
  margin-top: 14px;
  padding: 8px 18px;
}

/* ---------- Card (a .mk-card that is not itself clickable) ---------- */

.mk-card.eq-card {
  cursor: default;
}

.mk-card.eq-card:hover {
  transform: none;
}

.mk-card.eq-card .mk-card-name {
  text-wrap: balance;
}

/* Short viewports (e.g. 1366x768): a slightly tighter card so a whole row of cards, EQUIP included, fits in the results pane. */
@media (max-height: 800px) {
  .mk-card.eq-card {
    padding: 10px 12px 12px;
  }

  .eq-card .mk-card-art {
    padding: 8px 0;
    margin: 0;
  }

  .eq-card .mk-card-art .gear-icon-large {
    width: 108px;
    height: 108px;
  }
}

/* Durability visibility milestone — the ONE shared durability bar every
   Gear-showing surface renders (Crew Management's equipped-slot cards, all
   6 Gear slot pickers, and this Equipment modal) — see
   DurabilityDisplay.tsx/durabilityPresentation.ts. Replaces the old
   Equipment-modal-only .eq-durability* rules (same visual footprint, now
   shared instead of duplicated). */
.durability-display {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 100%;
  margin-top: 2px;
}

.durability-display-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.06em;
}

.durability-display-label {
  text-transform: uppercase;
  color: var(--text-dim);
}

.durability-display-value {
  color: var(--text);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.durability-display-track {
  height: 5px;
  overflow: hidden;
  border-radius: 3px;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-steel);
}

.durability-display-fill {
  display: block;
  height: 100%;
  background: var(--ok);
  transition: width 200ms ease-out;
}

.durability-display[data-state="warning"] .durability-display-fill {
  background: var(--amber);
}

.durability-display[data-state="warning"] .durability-display-value {
  color: var(--amber);
}

.durability-display[data-state="danger"] .durability-display-fill {
  background: var(--danger);
}

.durability-display[data-state="danger"] .durability-display-value {
  color: var(--danger);
  font-weight: 800;
}

.durability-display[data-state="broken"] .durability-display-fill {
  background: var(--danger);
  opacity: 0.35;
}

.durability-display[data-state="broken"] .durability-display-value {
  color: var(--danger);
  font-weight: 800;
}

.durability-display-broken-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.durability-display-broken-flag {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.08em;
  padding: 2px 7px;
  border-radius: 4px;
  border: 1px solid var(--danger);
  background: rgba(209, 91, 91, 0.14);
  color: #ffb3b3;
}

.durability-display-broken-note {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--danger);
}

.durability-display-compact .durability-display-head {
  /* Narrow slot buttons (e.g. CrewWorkshop's ~104px-wide gear slots) can't
     fit "Durability" and "N/M (P%)" side by side — stacking them keeps
     every bit of required text inside the card's own bounds instead of
     overflowing it (found live at 1920x1080: the row layout overflowed a
     104px-wide slot by ~23px). */
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  font-size: 10px;
}

.durability-display-compact .durability-display-track {
  height: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .durability-display-fill {
    transition: none;
  }
}

/* Damaged-Gear Repair Controls milestone — the ONE shared repair-control
   block every Gear-showing surface renders (Equipment tab, Crew Workshop,
   Manage Crew, all Gear pickers) — see GearRepairControls.tsx. */
.gear-repair-controls {
  display: flex;
  margin-top: 4px;
}

.gear-repair-action {
  width: 100%;
}

.gear-repair-controls-unavailable,
.gear-repair-controls-trdr {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  padding: 4px 2px;
}

.gear-repair-controls-trdr {
  color: var(--danger);
  font-weight: 700;
  text-transform: uppercase;
}

.gear-repair-controls-compact .gear-repair-action {
  font-size: 11px;
  padding: 4px 8px;
}

.gear-repair-controls-compact.gear-repair-controls-unavailable,
.gear-repair-controls-compact.gear-repair-controls-trdr {
  font-size: 9px;
}

.eq-flags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
}

.eq-flag {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 2px 7px;
  border-radius: 4px;
  border: 1px solid var(--panel-border);
}

.eq-flag-broken {
  color: #ffb3b3;
  border-color: var(--danger);
  background: rgba(209, 91, 91, 0.12);
}

.eq-flag-listed {
  color: var(--amber);
  border-color: var(--amber);
  background: rgba(240, 169, 78, 0.1);
}

.eq-card-equip {
  cursor: pointer;
}

.eq-card-equip:disabled {
  opacity: 0.55;
  cursor: default;
}

.eq-card-equip-blocked {
  background: transparent;
  border: 1px solid var(--panel-border);
  color: var(--text-dim);
  cursor: not-allowed;
}

.eq-equipped-by {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.eq-card-action-equipped {
  color: var(--ok);
  border: 1px solid var(--ok);
  background: rgba(111, 191, 115, 0.12);
}

.eq-holder {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 2px 8px;
  font-size: 13px;
}

.eq-holder-name {
  color: var(--text);
  font-weight: 600;
}

.eq-holder-position {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
}

/* ---------- Equip dialog (nested in the Equipment modal) ---------- */

.eq-dialog-backdrop {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3vh 3vw;
  background: rgba(6, 4, 2, 0.74);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.eq-dialog {
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
  max-width: 640px;
  max-height: 90vh;
  overflow-y: auto;
  padding: 18px 20px;
  color: var(--text);
  font-family: var(--font-interface);
  background: linear-gradient(155deg, #241d17 0%, #14100c 55%, #0a0807 100%);
  border: 2px solid var(--gr-brass-dim);
  border-radius: 12px;
  box-shadow: inset 0 0 0 1px rgba(201, 151, 79, 0.28), 0 12px 40px rgba(0, 0, 0, 0.6);
}

.eq-dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* Two classes: the dialog is a DOM descendant of the Equipment `.modal-dialog`, whose generic `.modal-dialog h2` rule (small, uppercase, dim, 14px bottom margin) would otherwise win on specificity. */
.eq-dialog .eq-dialog-title {
  margin: 0;
  font-family: var(--font-frontier);
  font-weight: 400;
  font-size: 20px;
  line-height: 1.2;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--gr-brass);
}

.eq-dialog-close {
  flex-shrink: 0;
  background: var(--gr-rivet);
  border: 1px solid var(--gr-brass);
  color: var(--gr-brass-bright);
  font-family: var(--font-control);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 7px 16px;
  border-radius: 5px;
  cursor: pointer;
  box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.5);
}

.eq-dialog-close::after {
  content: " \00d7";
  font-weight: 900;
}

.eq-dialog-close:hover:not(:disabled) {
  background: rgba(201, 151, 79, 0.16);
}

.eq-dialog-close:disabled {
  opacity: 0.5;
  cursor: default;
}

.eq-dialog .btn-primary {
  background: linear-gradient(165deg, var(--gr-brass) 0%, var(--gr-brass-dim) 100%);
  border-color: var(--gr-brass);
  color: #1a1204;
}

.eq-dialog .btn-primary:hover:not(:disabled) {
  border-color: var(--gr-brass-bright);
  box-shadow: 0 0 10px rgba(201, 151, 79, 0.45);
}

.eq-dialog-lead,
.eq-dialog-hint {
  margin: 0;
  font-size: 13px;
  color: var(--text-dim);
}

.eq-dialog-hint {
  font-size: 12px;
}

.eq-summary {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-width: 0;
  padding: 10px 12px;
  background: rgba(0, 0, 0, 0.28);
  border: 1px solid var(--panel-border);
  border-radius: 8px;
}

.eq-summary[data-rarity="uncommon"] {
  border-color: var(--ok);
}
.eq-summary[data-rarity="rare"] {
  border-color: var(--poor);
}
.eq-summary[data-rarity="epic"] {
  border-color: var(--epic);
}
.eq-summary[data-rarity="legendary"] {
  border-color: var(--amber);
}

.eq-summary-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.eq-summary-name {
  font-family: var(--font-frontier);
  font-size: 16px;
  line-height: 1.2;
}

.eq-summary-meta,
.eq-summary-durability {
  font-family: var(--font-control);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.eq-summary-stats {
  justify-content: flex-start;
  margin: 2px 0;
}

.eq-summary[data-broken="true"] .eq-summary-durability {
  color: var(--danger);
}

.eq-target-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.eq-target {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "who action"
    "slot action";
  align-items: center;
  gap: 3px 14px;
  padding: 10px 12px;
  background: linear-gradient(165deg, var(--gr-timber-light) 0%, var(--gr-timber) 100%);
  border: 1px solid var(--panel-border);
  border-radius: 8px;
}

.eq-target-who {
  grid-area: who;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 10px;
}

.eq-target-position {
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
}

.eq-target-name {
  font-family: var(--font-frontier);
  font-size: 16px;
}

.eq-target-slot {
  grid-area: slot;
  font-size: 12px;
  color: var(--text-dim);
}

.eq-target-slot strong {
  color: var(--text);
  font-weight: 600;
}

.eq-target-action {
  grid-area: action;
  white-space: nowrap;
}

.eq-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 28px;
  margin: 0;
}

.eq-fact dt {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.eq-fact dd {
  margin: 2px 0 0;
  font-size: 14px;
}

.eq-swap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: stretch;
  gap: 10px;
}

.eq-swap-side {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.eq-swap-side .eq-summary {
  flex: 1;
}

.eq-swap-heading {
  margin: 0;
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
}

.eq-swap-arrow {
  align-self: center;
  font-size: 22px;
  color: var(--gr-brass);
}

.eq-compare {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.eq-compare-caption {
  padding-bottom: 6px;
  text-align: left;
  font-family: var(--font-control);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gr-brass-bright);
}

.eq-compare th,
.eq-compare td {
  padding: 5px 8px;
  text-align: right;
  border-bottom: 1px solid rgba(201, 151, 79, 0.16);
}

.eq-compare th:first-child {
  text-align: left;
}

.eq-compare thead th {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.eq-compare tbody th {
  font-weight: 700;
}

.eq-delta-up {
  color: var(--ok);
}

.eq-delta-down {
  color: var(--danger);
}

.eq-delta-flat {
  color: var(--text-dim);
}

.eq-dialog-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}

@media (max-width: 560px) {
  .eq-swap {
    grid-template-columns: minmax(0, 1fr);
  }

  .eq-swap-arrow {
    justify-self: center;
    transform: rotate(90deg);
  }

  .eq-target {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "who"
      "slot"
      "action";
  }
}

/* ==========================================================================
   Mining Equipment tab — owned miner / cooling cards.

   Everything structural is the Gear tab's own vocabulary and is NOT restated
   here: .mk-browse / .mk-filter-rail / .mk-check / .eq-radio / .eq-check-count /
   .mk-search / .eq-results-head / .mk-result-count / .eq-note(-inline) /
   .mk-card-grid / .mk-card.eq-card (and its short-viewport tightening above).
   Only what a mining card means differently lives below: a kind accent instead
   of a rarity colour, the artwork window, the three counts and the availability
   pill, plus the summary row's action.
   ========================================================================== */

.eq-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
}

.eq-card-top .mk-card-rarity-plate {
  align-self: auto;
}

.eq-mining-card[data-kind="miner"] {
  border-color: var(--rich);
  box-shadow: 0 0 10px -3px var(--rich);
}

.eq-mining-card[data-kind="cooling"] {
  border-color: var(--poor);
  box-shadow: 0 0 10px -3px var(--poor);
}

.eq-mining-card[data-kind="miner"] .mk-card-rarity-plate {
  color: var(--rich);
  border-color: var(--rich);
}

.eq-mining-card[data-kind="cooling"] .mk-card-rarity-plate {
  color: var(--cooled-glow);
  border-color: var(--poor);
}

.eq-status-pill {
  font-family: var(--font-control);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 4px;
  border: 1px solid var(--panel-border);
  color: var(--text-dim);
}

.eq-status-pill[data-availability="available"] {
  color: var(--ok);
  border-color: var(--ok);
  background: rgba(111, 191, 115, 0.1);
}

/* Same square art window as the Gear card's icon (148px; 108px on short viewports below). */
.eq-mining-art {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 148px;
  height: 148px;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--panel-border);
  background: var(--panel);
}

.eq-mining-card[data-kind="miner"] .eq-mining-art {
  border-color: var(--rich);
}

.eq-mining-card[data-kind="cooling"] .eq-mining-art {
  border-color: var(--poor);
}

.eq-mining-art-image {
  box-sizing: border-box;
  max-width: 100%;
  max-height: 100%;
  padding: 6px;
  object-fit: contain;
}

.eq-mining-art[data-kind="cooling"] .eq-mining-art-image {
  padding: 16px;
}

/* The existing fallback icon (⚙ / ❄), coloured as the Supply Depot colours it. */
.eq-mining-art-glyph {
  font-size: 64px;
  line-height: 1;
  color: var(--gr-brass);
}

.eq-mining-art[data-kind="cooling"] .eq-mining-art-glyph {
  color: var(--cooled-glow);
  text-shadow: 0 0 18px rgba(127, 212, 255, 0.45);
}

.eq-counts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
  width: 100%;
  margin: 4px 0 2px;
}

.eq-counts > div {
  padding: 5px 4px;
  text-align: center;
  background: rgba(0, 0, 0, 0.28);
  border: 1px solid var(--panel-border);
  border-radius: 6px;
}

.eq-counts dt {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.eq-counts dd {
  margin: 2px 0 0;
  font-family: var(--font-control);
  font-size: 18px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.eq-level-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 10px;
}

.eq-results-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
}

.eq-head-action {
  margin: 0;
}

.eq-note-attention {
  color: var(--gr-brass-bright);
  border-color: rgba(201, 151, 79, 0.5);
  background: rgba(201, 151, 79, 0.08);
}

.eq-browse .inventory-hint {
  margin: 14px 0 0;
  font-size: 12px;
}

@media (max-height: 800px) {
  .eq-mining-art {
    width: 108px;
    height: 108px;
  }

  .eq-mining-art-glyph {
    font-size: 48px;
  }
}

/* Equipment browser art containment (the modal View / Open Inventory open). `.equipment-icon-art` had no size rule anywhere, so the approved
   1312x1199 Miner L1 PNG rendered at full size inside the browser's 68px art box and spilled over the list. Scoped to that box only. */
.equipment-browser-art {
  overflow: hidden;
}

.equipment-browser-art .equipment-icon-art {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* ---------- Red & Green website integration ---------- */
/* "RETURN TO RED & GREEN" is anchored to the top-right of the browser viewport at every width (position: fixed, so it stays put while the page
   scrolls). It is mounted as a direct child of #root, outside the app's own containers, so no parent transform / filter / contain can turn
   it into a containing block for fixed positioning. The padding keeps it clear of notches (safe-area insets). z-index 90 keeps it under every
   dialog layer (.modal-backdrop is 100), so it never floats over an open dialog.
   Space is reserved for it: while it is mounted, the page gets a top strip as tall as the button plus its gaps (class set on <html> by the
   component), so it never covers the facility header, the toolbar or the Crew Workshop's counter. */
.return-to-website {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 12px);
  right: calc(env(safe-area-inset-right, 0px) + 12px);
  z-index: 90;
  padding: 8px 12px;
  border: 1px solid var(--amber);
  border-radius: 6px;
  background: rgba(16, 20, 15, 0.92);
  color: #f3e7c8;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-decoration: none;
  white-space: nowrap;
}

html.has-return-link body {
  padding-top: calc(env(safe-area-inset-top, 0px) + 58px);
}

.return-to-website:hover,
.return-to-website:focus-visible {
  background: var(--amber);
  color: #201400;
  outline: none;
}

/* Local launcher only (RG_LOCAL_TEST_DATA): top-left, opposite the return link, in the same reserved strip and under every dialog. */
.local-test-data-badge {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 12px);
  left: calc(env(safe-area-inset-left, 0px) + 12px);
  z-index: 90;
  margin: 0;
  padding: 8px 10px;
  border: 1px solid #f2a74b;
  border-radius: 6px;
  background: #5a1f12;
  color: #fff1d5;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

/* Phone width: compact enough to keep a clear gap from the return link. */
@media (max-width: 420px) {
  .local-test-data-badge {
    padding: 6px 8px;
    font-size: 11px;
    letter-spacing: 0.03em;
  }
}

/* Phone width only (desktop untouched): the facility toolbar's status group is `white-space: nowrap`, which made it ~480px wide and
   stretched the whole page (and every dialog centred in it) beyond the screen. Let it wrap and shrink to the screen instead. */
@media (max-width: 700px) {
  .action-bar-status-group {
    flex: 1 1 100%;
    min-width: 0;
    max-width: 100%;
  }
  .facility-status-panel.action-bar-time {
    flex: 1 1 100%;
    min-width: 0;
  }
  .action-bar-time .daily-operating-widget {
    white-space: normal;
    min-width: 0;
  }
  /* The Wallet / Cash / Production strip is three side-by-side blocks that cannot fit a phone (Cash printed over BTC Balance and
     Production ran off the edge): stack them, with the seam turned to run between rows. */
  .facility-status-strip {
    flex-direction: column;
  }
  .facility-status-block {
    min-width: 0;
    border-right: none;
    border-bottom: 1px solid rgba(0, 0, 0, 0.5);
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05);
  }
  .facility-status-block:last-child {
    border-bottom: none;
    box-shadow: none;
  }
  .facility-cash-block {
    min-width: 0;
  }
}

/* Crew Workshop on a phone. The screen is a fixed-viewport, three-station desktop design with no narrow layout, so at phone width
   three stations plus slot columns were squeezed into ~100px each and printed over one another. Phone only (desktop untouched):
   the stations stack in one column (Lead first), the page scrolls, and the card/slot sizes are re-derived from the one column. */
@media (max-width: 760px) {
  .crew-workshop {
    --ws-pad: 12px;
    --ws-gap: 12px;
    --ws-slot-w: 64px;
    --ws-slot-icon: 40px;
    --ws-inner-gap: 6px;
    --ws-station-pad: 10px;
    --ws-col-w: calc(100vw - 2 * var(--ws-pad));
    --ws-card-w: min(calc(var(--ws-col-w) - 2 * var(--ws-slot-w) - 2 * var(--ws-inner-gap) - 2 * var(--ws-station-pad) - 2px), 38vh);
    height: auto;
    min-height: 100dvh;
    overflow: visible;
    grid-template-rows: auto auto auto;
  }
  .workshop-header {
    flex-wrap: wrap;
  }
  .workshop-stations {
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
  }
  .workshop-station-lead {
    order: -1;
  }
  .workshop-station-lead .workshop-station-card {
    width: var(--ws-card-w);
  }
  .workshop-output {
    flex-direction: column;
  }
  .workshop-output-actions {
    max-width: 100%;
    justify-content: stretch;
  }
  .workshop-output-actions .btn {
    flex: 1 1 140px;
  }
  .workshop-output-cell {
    padding: 0 6px;
  }

  /* The Hasher / Gear pickers are a two-column dialog (item grid + detail column, 300px minimum) that cannot fit a phone: the
     grid collapsed to a sliver and the rarity chips printed over the title. Stack them inside the dialog and let it scroll. */
  .workshop-overlay-backdrop {
    padding: 10px;
  }
  .workshop-overlay-header {
    flex-wrap: wrap;
    padding: 10px 14px;
  }
  .workshop-overlay-header h2 {
    white-space: normal;
    font-size: 15px;
  }
  .workshop-overlay-header-extra {
    order: 3;
    flex: 1 1 100%;
  }
  .workshop-overlay-body {
    display: block;
    overflow-y: auto;
  }
  .workshop-hasher-grid {
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
    gap: 8px;
    padding: 12px;
    overflow: visible;
  }
  .workshop-gear-list {
    padding: 12px;
    overflow: visible;
  }
  .workshop-hasher-detail,
  .workshop-gear-detail {
    overflow: visible;
    border-left: none;
    border-top: 1px solid rgba(201, 151, 79, 0.28);
    padding: 14px;
  }
  /* Review & Lock: the three crew cards stack (names such as "Claim Jumper Montgomery" spilled into the next column). It keeps
     its own column flow, which the generic body rule above would otherwise flatten. */
  .workshop-review-body {
    display: flex;
    padding: 14px;
  }
  .workshop-review-crew {
    grid-template-columns: minmax(0, 1fr);
  }
}

.website-import-rejected {
  max-width: 560px;
  margin: 0;
  padding-left: 18px;
  text-align: left;
  font-size: 13px;
  color: var(--text-dim);
}

/* ===== Weekly preparation and the automatic season-open lock (Game Design Bible Section 5 / 54) ===== */
.next-week-prep {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px 16px;
  flex-wrap: wrap;
  margin: 10px 0 4px;
  padding: 10px 14px;
  border: 1px solid var(--gr-brass-dim);
  border-radius: 8px;
  background: rgba(14, 12, 9, 0.72);
  text-align: left;
}
.next-week-prep-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 260px;
}
.next-week-prep-title {
  color: var(--gr-brass-bright);
  font-size: 14px;
  letter-spacing: 0.04em;
}
.next-week-prep-detail {
  color: var(--text-dim);
  font-size: 13px;
  line-height: 1.4;
}
.next-week-prep-status {
  color: var(--text);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.crew-mgmt-prep {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 12px 16px 0;
  text-align: center;
}
.crew-mgmt-modes {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: center;
}
.crew-mgmt-prep-text {
  margin: 0;
  max-width: 62ch;
  color: var(--text-dim);
  font-size: 13px;
  line-height: 1.45;
}
.crew-mgmt-prep-text strong {
  color: var(--gr-brass-bright);
}
.crew-mgmt-prepared-badge {
  white-space: nowrap;
}
.crew-mgmt-notice,
.workshop-notice {
  margin: 0 16px;
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px solid var(--gr-brass-dim);
  background: rgba(14, 12, 9, 0.78);
  color: var(--text);
  font-size: 13px;
  line-height: 1.45;
  text-align: left;
}
.crew-mgmt-notice-pending,
.workshop-notice-pending {
  border-color: var(--gr-brass);
}
.crew-mgmt-notice-success,
.workshop-notice-success {
  border-color: #4f9d62;
}
.crew-mgmt-notice-attention,
.workshop-notice-attention {
  border-color: var(--danger);
}
.workshop-header-banners {
  flex-wrap: wrap;
}
.workshop-banners {
  flex: 1 1 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.workshop-banners .workshop-notice {
  margin: 0;
}
.workshop-banners .next-week-prep {
  margin: 0;
}
.weekly-summary-card .next-week-prep {
  margin: 14px 0 6px;
}
@media (max-width: 760px) {
  .next-week-prep {
    flex-direction: column;
    align-items: stretch;
  }
  .next-week-prep-text {
    flex: 0 0 auto;
  }
  .next-week-prep .btn {
    width: 100%;
  }
  .crew-mgmt-prep,
  .crew-mgmt-notice {
    padding-left: 10px;
    padding-right: 10px;
    margin-left: 10px;
    margin-right: 10px;
  }
  .crew-mgmt-prep {
    margin: 0;
    padding-top: 10px;
  }
  .crew-mgmt-modes .btn {
    flex: 1 1 140px;
  }
}
/* =====================================================================================================================
   WEEKLY RESULTS LEDGER (ResultsScreen.tsx / WeeklyLedgerSheet.tsx)
   =====================================================================================================================
   A paper ledger sheet inside the Results modal. The game's UI has no parchment texture of its own (its chrome is the dark
   timber / steel / brass palette), so the paper is taken from the one paper the game does ship: the approved playing cards.
   --ledger-paper is the median of those cards' light pixels, --ledger-ink the ink their nameplates are printed in (#2a160c, also
   used by .website-card-face), --ledger-green the approved Uncommon card's name colour. Type is the game's own Rye / Oswald /
   Inter; rules and edging reuse --gr-brass-dim. No new image, no new face. */

/* The shared Modal has no height limit; the ledger is long, so on a short or narrow window the BODY scrolls and the header
   (with Close) stays put. Scoped to this modal only. */
.modal-dialog[data-testid="results-modal"] {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  overflow: hidden;
}

.modal-dialog[data-testid="results-modal"] > .modal-content-header {
  flex: 0 0 auto;
}

.modal-dialog[data-testid="results-modal"] > .modal-content-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

[data-testid="results-modal"] .modal-content-header h2 {
  font-family: var(--font-frontier);
  font-weight: 400;
  letter-spacing: 0.04em;
}

.ledger-sheet {
  --ledger-paper: #f5e0c8;
  --ledger-ink: #2a160c;
  --ledger-ink-soft: color-mix(in srgb, var(--ledger-ink) 74%, var(--ledger-paper));
  --ledger-green: #245329;
  --ledger-red: color-mix(in srgb, var(--danger) 55%, var(--ledger-ink));
  --ledger-rule: color-mix(in srgb, var(--gr-brass-dim) 75%, var(--ledger-paper));
  position: relative;
  flex: 0 0 auto;
  padding: clamp(14px, 3.4vw, 24px);
  color: var(--ledger-ink);
  font-family: var(--font-interface);
  border: 1px solid var(--gr-brass-dim);
  border-radius: 6px;
  background:
    radial-gradient(ellipse 80% 55% at 18% 6%, rgba(255, 255, 255, 0.4), transparent 62%),
    radial-gradient(ellipse 70% 50% at 88% 98%, color-mix(in srgb, var(--gr-brass-dim) 22%, transparent), transparent 66%),
    var(--ledger-paper);
  box-shadow:
    inset 0 0 0 4px var(--ledger-paper),
    inset 0 0 0 5px var(--ledger-rule),
    0 10px 28px rgba(0, 0, 0, 0.55);
}

.ledger-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 18px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--ledger-rule);
  font-family: var(--font-control);
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ledger-ink-soft);
}

.ledger-head strong {
  color: var(--ledger-ink);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.ledger-status {
  padding: 2px 11px;
  border: 1.5px solid currentColor;
  border-radius: 999px;
  font-weight: 700;
  color: var(--ledger-ink);
}

.ledger-status-active {
  color: var(--ledger-green);
}

.ledger-section-title {
  margin: 0 0 6px;
  font-family: var(--font-frontier);
  font-size: 20px;
  font-weight: 400;
  letter-spacing: 0.03em;
  color: var(--ledger-ink);
}

/* The estimated (or final) total: the focus of the sheet. */
.ledger-total {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  margin: 14px 0 12px;
  padding: 14px 8px 12px;
  text-align: center;
  border-top: 3px double var(--ledger-rule);
  border-bottom: 3px double var(--ledger-rule);
}

.ledger-total-label {
  font-family: var(--font-control);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ledger-ink-soft);
}

.ledger-total-value {
  font-family: var(--font-frontier);
  font-size: clamp(34px, 9vw, 56px);
  line-height: 1.08;
  color: var(--ledger-ink);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
}

.ledger-total-note,
.ledger-tier {
  font-size: 13px;
  line-height: 1.4;
  color: var(--ledger-ink-soft);
}

.ledger-tier strong {
  color: var(--ledger-ink);
}

.ledger-lines {
  display: flex;
  flex-direction: column;
}

.ledger-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: 14px;
  padding: 8px 0;
  border-bottom: 1px dotted var(--ledger-rule);
}

.ledger-label {
  font-size: 15px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.ledger-value {
  text-align: right;
  white-space: nowrap;
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.ledger-note {
  grid-column: 1 / -1;
  margin-top: 3px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ledger-ink-soft);
}

/* Meaning is carried by the sign and the words; colour only reinforces it. */
.ledger-row-pos .ledger-value {
  color: var(--ledger-green);
}

.ledger-row-neg .ledger-value,
.ledger-row-neg .ledger-label {
  color: var(--ledger-red);
}

.ledger-row-zero .ledger-value {
  color: var(--ledger-ink-soft);
}

.ledger-row-sub {
  padding: 5px 0 5px 18px;
}

.ledger-row-sub .ledger-label {
  font-size: 14px;
  font-weight: 500;
}

.ledger-row-sub .ledger-value {
  font-size: 14px;
}

.ledger-row-strong {
  border-bottom: 0;
  border-top: 1px solid var(--ledger-ink);
  margin-top: 4px;
  padding-top: 9px;
}

.ledger-row-strong .ledger-label {
  font-weight: 700;
}

.ledger-row-strong .ledger-value {
  font-size: 18px;
}

.ledger-divider {
  margin: 10px 0 4px;
  border-top: 3px double var(--ledger-rule);
}

.ledger-group-title {
  margin: 8px 0 0;
  font-family: var(--font-control);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ledger-ink-soft);
}

.ledger-warn {
  color: var(--ledger-red);
  font-weight: 700;
}

.ledger-incomplete {
  display: block;
  margin: 4px 0 2px;
  font-size: 13px;
  line-height: 1.4;
}

.ledger-sheet + .ledger-sheet {
  margin-top: 4px;
}

@media (max-width: 480px) {
  .ledger-label {
    font-size: 14px;
  }
  .ledger-value {
    font-size: 15px;
  }
  .ledger-row {
    column-gap: 10px;
  }
  .ledger-row-sub {
    padding-left: 12px;
  }
  .ledger-head {
    font-size: 12px;
    gap: 4px 12px;
  }
}

/* ---------- TRADING: WITHDRAW WITH OPEN POSITIONS + OPTIONAL STOP-LOSS ----------
   Presentation only: every figure is formatted from the shared trading.ts calculations (Withdrawable Profit, stop validation, stop estimate).
   Profit is green and loss red, but every figure also carries an explicit +/- sign and a words label, so colour is never the only cue. */
.trading-margin-stat-reserved .trading-margin-stat-value {
  color: var(--text-dim);
  font-size: 13px;
}

.trading-withdraw-control {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px;
}

.trading-withdraw-amount {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.trading-withdraw-amount-label {
  font-family: var(--font-control);
  font-size: 9px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.trading-withdraw-amount-row {
  display: flex;
  gap: 6px;
}

.trading-withdraw-amount-row input {
  width: 110px;
  min-width: 0;
  padding: 5px 8px;
  font-variant-numeric: tabular-nums;
}

.trading-withdraw-max-btn {
  padding: 5px 10px;
  font-size: 11px;
}

.trading-withdraw-input-error {
  margin: 0;
  flex-basis: 100%;
  font-size: 12px;
  color: var(--danger);
}

.trading-stop-ticket {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 10px;
}

.trading-stop-field input {
  width: 100%;
  min-width: 0;
  font-variant-numeric: tabular-nums;
}

.trading-stop-side,
.trading-stop-note {
  margin: 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--text-dim);
}

.trading-stop-error {
  margin: 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--danger);
}

.trading-stop-estimate {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  padding: 6px 8px;
  border-radius: var(--radius);
  border: 1px solid var(--panel-border);
  background: var(--gr-steel-dark);
  font-variant-numeric: tabular-nums;
}

.trading-stop-estimate-profit {
  color: var(--ok);
  border-color: var(--ok);
}

.trading-stop-estimate-loss {
  color: var(--danger);
  border-color: var(--danger);
}

.trading-stop-estimate-label {
  font-size: 12px;
  font-weight: 600;
}

.trading-stop-estimate-value {
  font-size: 14px;
}

.trading-stop-estimate-detail {
  flex-basis: 100%;
  font-size: 11px;
  color: var(--text-dim);
}

.trading-stop-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  padding: 4px 0;
}

.trading-stop-line-label {
  font-family: var(--font-control);
  font-size: 10px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.trading-stop-price {
  font-variant-numeric: tabular-nums;
}

.trading-stop-none {
  color: var(--text-dim);
}

.trading-stop-line-actions,
.trading-stop-actions {
  display: flex;
  gap: 6px;
}

.trading-stop-line-actions {
  margin-left: auto;
}

.trading-stop-editor {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 460px;
  padding: 6px 0;
}

.trading-stop-editor-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 12px;
}

.trading-stop-editor-field input {
  width: 100%;
  min-width: 0;
  font-variant-numeric: tabular-nums;
}

.trading-position-stop-row td {
  padding-top: 0;
  border-top: 0;
  background: var(--gr-steel-dark);
}

.trading-position-cards .trading-stop-line {
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px dashed var(--panel-border);
}

.trading-history-reason-stop_loss {
  color: var(--gr-brass);
  background: rgba(201, 151, 79, 0.15);
}

@media (max-width: 640px) {
  .trading-withdraw-control {
    width: 100%;
  }

  .trading-withdraw-control .trading-withdraw-btn {
    flex: 1 1 100%;
  }

  .trading-stop-line-actions {
    margin-left: 0;
    flex-basis: 100%;
  }

  .trading-stop-line-actions .btn,
  .trading-stop-actions .btn {
    flex: 1 1 0;
  }
}

/* Desktop: the stop-loss lines and the withdraw controls make the terminal taller. Rather than squeezing the chart below its own minimum
   (its widget would then spill over the Positions table), the workspace keeps a floor and the account panel scrolls when the window is short. */
@media (min-width: 901px) {
  .trading-account-panel {
    overflow-y: auto;
  }

  .trading-main-workspace {
    flex: 1 0 auto;
    min-height: 420px;
  }

  .trading-activity-section {
    flex-shrink: 0;
  }
}

/* The positions table cells are nowrap; the stop line's explanation and estimate must wrap inside the full-width row instead of running off it. */
.trading-position-stop-row td,
.trading-position-cards .trading-stop-line {
  white-space: normal;
}

/* Stacked (narrow) layout: the whole terminal scrolls, so the Positions list is shown at its natural height instead of in a short inner scroller,
   which would leave a position with its stop-loss line only a few lines of window. */
@media (max-width: 900px) {
  .trading-activity-section {
    flex-shrink: 0;
    max-height: none;
  }

  .trading-activity-body {
    overflow-y: visible;
  }
}

/* Phone width: the dev-only Crew Workshop tools toggle (fixed, top-left) would run under the fixed return button (top-right). Keep the collapsed toggle
   short, and drop the opened panel into the reserved strip's bottom edge so neither covers the other. */
@media (max-width: 480px) {
  html.has-return-link .crew-workshop-dev-tools:not([open]) {
    max-width: 138px;
  }

  html.has-return-link .crew-workshop-dev-tools:not([open]) summary {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  html.has-return-link .crew-workshop-dev-tools[open] {
    top: calc(env(safe-area-inset-top, 0px) + 58px);
  }
}
