Layout

PURPOSE

Metagame shell that wraps every React screen. Provides the fullscreen flex column, the fixed FX overlay stack used by the reward pipeline, and route-aware overflow handling. Every screen renders fullscreen — no legacy global PLAY / SHOP / COLLECTION bar. Screens that want chrome render it themselves (e.g. V32Shell for Hub/Shop, per-screen bottom bars for Ships).

OWNS

  • The outer div shell — height: 100%, flex column, overflow: hidden.
  • The <main> element that hosts childrenflex: 1, with route-conditional overflow.
  • The FxLayers component — five stacked fixed-position overlays for the reward pipeline:
    • #fx-dim (zIndex 9998) — dimmer / vignette.
    • #fx-flash (zIndex 9999) — full-screen tinted flash.
    • #fx-particles (zIndex 10000) — canvas for burst / inflow / sparkle particles.
    • #fx-floating (zIndex 10001) — cloned cards / icons in flight.
    • #fx-chips (zIndex 10002) — milestone chips and small labels.
  • Mounting of DevKeys, RewardOrchestrator, and RewardPopup once at shell level.

READS FROM

  • react-router-dom useLocation() — reads location.pathname to decide overflow mode.
  • ReactNode children prop — the routed screen content rendered inside <main>.

PUSHES TO

  • The DOM. The five FX layer elements are created here so the RewardOrchestrator and downstream effect code can target them by id.
  • Nothing in stores; the Layout is presentational shell only.

DOES NOT

  • Render any navigation chrome, header, footer, or global bar.
  • Apply any post-FX CSS filter to <main>. The cartoony post-FX moved out of a global SVG filter: url() into a GLSL post-process pass inside the nebula shader (FS1/FS2) because CSS filters don’t reliably apply to WebGL-textured layers.
  • Manage safe-area insets via CSS env() padding. The shell is height: 100% only — safe-area handling is delegated to screens / chrome components rather than applied at the Layout level.
  • Handle pointer events on FX layers. All FX layers are pointer-events: none by default; the orchestrator enables pointer events on specific layers when active.
  • Scroll. The outer container is overflow: hidden; only <main> scrolls, and only on dev routes.

Signals

  • Route prefix /dev — when location.pathname.startsWith('/dev'), <main> switches to overflow: auto so dev dashboards can scroll. All other routes keep overflow: hidden to lock the game / metagame to a fixed fullscreen viewport.

Entry points

  • Layout({ children }) — default export pattern used by the React Router tree to wrap every routed screen.
  • FxLayers() — internal component, not exported. Renders the five overlay layers as a fragment.
  • DOM IDs fx-dim, fx-flash, fx-particles, fx-floating, fx-chips — public-by-convention selectors that RewardOrchestrator and effect helpers query.

Pattern notes

  • The fullscreen-no-bar policy is intentional. Earlier shells rendered a global tab bar; the current model lets each screen own its own chrome so transitions can be coordinated per screen.
  • FX layer z-indexes are sequential from 9998 upward, leaving headroom below for any modal layer and above for native browser UI.
  • The route-conditional overflow is a single boolean (isDevRoute) rather than a route-table lookup — kept inline because dev routes are the only exception.
  • Post-FX history is preserved as an inline comment so future contributors don’t re-attempt the SVG filter: url() approach on <main>. The shader-tail path in engine/rendering/nebula-engine.ts (FS1/FS2) is the canonical home for cartoony post-FX.
  • DevKeys, RewardOrchestrator, and RewardPopup mount once at shell level so they survive route changes and remain available across every screen.