PURPOSE

Shared metagame chrome for the React shell. Wraps a child stage with a fixed 70px top bar (account pill + build-version stamp + live currency display) and an 80px bottom tab nav, on a medical-UI panel-recessed background. Every metagame screen renders fullscreen inside this shell; the shell provides the 70/80px chrome and a scrollable main stage flexed between them.

OWNS

  • V32Shell — named-export functional component rendering the shell layout.
  • V32ShellProps — internal props interface (children: ReactNode, optional hideChrome: boolean).
  • The WARP_SVG_PATH constant — inline path data for the warp-crystal currency icon.
  • The two inline SVG icon strings injected into the currency pills (warp crystal with var(--warp-core) fill plus #fdf4ff stroke; credits coin as a layered yellow disc with #fbbf24 outer ring, #78350f stroke, #fde68a inner disc).
  • The top-bar layout: account pill on the left, build-version stamp centered, currency pills on the right.
  • The main-stage container with vh-clamped padding/gap (clamp(8px, 1.8vh, 15px) / clamp(6px, 1.8vh, 15px)), vertical scrolling, hidden horizontal overflow, and momentum scrolling on iOS via WebkitOverflowScrolling: 'touch'.
  • The chrome-hide behavior: top bar fades to opacity: 0 with pointer events disabled; bottom nav is unmounted entirely.
  • The wallet display-override resolution (frozen before-values during reward presentation take precedence over live store values).
  • Anchor registration of the currency pills for fly-to animations.

READS FROM

  • reactReactNode type for children.
  • react-router-domuseNavigate for the account-pill route push.
  • @starship-survivors/engine/core/configBUILD_VERSION for the centered version stamp.
  • @starship-survivors/stores/walletStore — live gems and credits selectors.
  • @starship-survivors/stores/displayOverrideStorewalletOverrides map selector (keyed by 'gems' / 'credits').
  • @starship-survivors/services/anchor-registryuseAnchor for the wallet-gems and wallet-credits anchor refs.
  • ./BottomNav — the default BottomNav export for the bottom tabs.
  • ../screens/shop/shop.css — imported for side-effect to apply the .sig-shop scoped styles, medical-UI overrides, top-bar styling, account-pill styling, currency-pill styling.
  • CSS custom properties consumed by the imported stylesheet: --med-panel-raised, --med-panel-recessed, --med-border, --warp-core.

PUSHES TO

  • React Router via navigate('/profile') when the account pill is clicked.
  • The anchor registry, which receives DOM refs for the credits and gems currency pills under the keys wallet-credits and wallet-gems.
  • The DOM via dangerouslySetInnerHTML — the currency pills are injected with a concatenation of the inline SVG icon string and the localized currency value.

DOES NOT

  • Manage any component-local state — all dynamic values come from store selectors and prop input.
  • Persist anything to stores, Supabase, or localStorage.
  • Compute or mutate wallet values; only reads walletStore and the display-override layer.
  • Define the bottom tabs or their active-state logic — that belongs to BottomNav.
  • Apply a separate fade animation to the bottom nav; the hide is a hard unmount of BottomNav rather than an opacity transition.
  • Render the onboarding chrome-free layout — OnboardingShell is a separate component.
  • Re-emit window events, signals, or telemetry.
  • Re-implement the canvas/HUD or any in-game UI — purely React metagame chrome.
  • Polyfill or guard against SVG injection beyond the trusted constant strings it constructs.

Signals

  • Wallet selector reads cause re-renders on every walletStore gems/credits change.
  • Display-override selector reads cause re-renders when walletOverrides is set or cleared (used by reward presentation to freeze the before-value during the fly-to animation).
  • Anchor registry mount/unmount cycle registers and unregisters wallet-gems and wallet-credits automatically with the useAnchor hook.
  • No window events, post-FX subscriptions, or signal-bus traffic.

Entry points

  • Named export V32Shell is consumed by the main metagame screens (hub, shop, ships, profile, collection, etc.) to wrap their content with the standard chrome.
  • The hideChrome prop is used by callers mounting fullscreen overlays that need the chrome out of the way without unmounting the shell.

Pattern notes

  • The CSS scope class is still named .sig-shop even though the component is shared across all metagame screens; the name is preserved per the file header to keep the scope key stable for ported V32 child markup.
  • The main stage is a <div className="v32-main"> rather than a <main> element, deliberately avoiding nested <main> elements when the shell is mounted inside Layout.tsx.
  • The display-override layer takes precedence with walletOverrides?.get('gems') ?? liveGems so reward-presentation flows can freeze the pre-reward value on screen while the gain animates.
  • Styling tokens follow ADR 20260513-001 (medical UI): top bar uses var(--med-panel-raised) with a hairline border-bottom; currency pills use var(--med-panel-recessed) plus var(--med-border).
  • The build-version stamp is rendered inline-styled (not in shop.css) at 10px, 40% white opacity, 1px letter-spacing, centered between the account pill and the currencies.
  • Currency icons are inlined via dangerouslySetInnerHTML rather than imported as React SVG components so the icon and the formatted number share the same flow inside the pill without extra wrapper nodes.
  • hideChrome is asymmetric: the top bar stays mounted but invisible (so the anchor refs and CSS layout do not shift), while the bottom nav is fully unmounted.
  • The shell relies on its parent flex container providing height; the main stage uses flex: 1 with minHeight: 0 to make the inner overflow scroll correctly under a Flexbox parent.