{ "_comment": "COMMAND DECK — the cyberpunk bar across the bottom of the screen (rendered by js/ui/ActionBar.js). Six evenly spaced slots: `buttons` is the ORDERED list — give a slot an `id` (the action it will fire) + `label` + `accent`, or leave both `null` for a reserved slot. Buttons currently fire `onAction` (see GameScene); the behavior behind them comes next. Everything visual is procedural (no image assets) and tunable here: layout in `height`/`margin`/`padding`/`button`, palette in `colors`, CRT scanlines in `scanline`, motion in `animation` (boot flicker, the rail comet, the glitch bursts, the RGB pull-apart).", "enabled": true, "height": 56, "margin": { "left": 20, "right": 20, "bottom": 12 }, "padding": 16, "button": { "widthFactor": 0.72, "maxWidth": 190, "notch": 10, "fontSize": 14, "letterSpacing": 2.5 }, "buttons": [ { "id": "research", "label": "Research", "accent": "#00e5ff" }, { "id": "scan", "label": "Scan", "accent": "#ffc94d" }, { "id": "ship", "label": "Ship", "accent": "#7ce8a4" }, { "id": "quests", "label": "Quests", "accent": "#ffc94d" }, { "id": "map", "label": "Map", "accent": "#c084fc" }, { "id": "menu", "label": "Menu", "accent": "#ff2d6f" } ], "surface": { "_comment": "Deck layout for the surface (SurfaceScene): `research` is replaced by `shop` (toggles the world's shop clip — while the clip is up the slot relabels to `labelActive`, and pressing it swaps back), `explore` folds up the world's service drawer (see `explore` below), and `takeoff` sits to the left of `menu`. SurfaceScene passes this list to ActionBar via its `buttons` option; everything else (height, colors, animation) is shared with the flight deck.", "buttons": [ { "id": "shop", "label": "Shop", "labelActive": "Leave Shop", "accent": "#00e5ff" }, { "id": "build", "label": "Build", "accent": "#ffc94d" }, { "id": "explore", "label": "Explore", "accent": "#7ce8a4" }, { "id": "quests", "label": "Quests", "accent": "#ffc94d" }, { "id": "takeoff", "label": "Take Off", "accent": "#7ce8a4" }, { "id": "menu", "label": "Menu", "accent": "#ff2d6f" } ], "explore": { "_comment": "The EXPLORE drawer — the full-width secondary bar (the deck's own left→right span, resting on its top rail — the shop tab bar's recipe) that folds up from the surface deck's Explore slot. SurfaceScene renders it with js/ui/MenuSubBar.js in span mode; the item LOGIC is js/ui/ExploreBar.js (worldContext / matchesWhere / resolveItems). `items` is the ordered button list: `id` (fired via onAction — the behavior lands later), `label`, `labelActive` (the label while the service is up — BAR relabels to LEAVE BAR while its clip loops, and is the only live button on the surface until pressed), `accent`, and an optional `where` clause — EVERY key it names must match the world the player is standing on for the button to appear. Keys: `world` ('planet' | 'station'); `planetClass` (the planet's class — 'terran' | 'rocky' | 'gas' | 'ice' | 'lava' | 'arid' | 'capitalNeutral' | 'capitalApex' | 'capitalMindweave' | 'capitalZenith', per data/planets.json → frames; 'none' on stations); `faction` (the faction controlling this system — 'apex' | 'mindweave' | 'zenith', or 'neutral' = wild space; per data/factions.json); `isHomeSystem` (boolean — true in the player's home / starting system; `{ isHomeSystem: false }` = every system EXCEPT the home). A value may be a single entry or a list (any-match). No `where` = every planet and station; the Job Board carries `{ isHomeSystem: false }` — it's on every planet and station EXCEPT the player's home system. DESIGN NOTE: the home system is safe space — enemies should NEVER be placed there, so there is no work to post on its board. JOB BOARD is a nearly full-screen CONSOLE (js/ui/JobBoardWindow.js — the build screen's structure, depth 80 over the deck/drawer/upper-left HUD): pressing it opens the board (left: the feed — `video` below, a muted 2:3 loop of assets/videos/jobboard.mp4; right: the JOB BOARD title, an AVAILABLE JOBS section — the board's postings — and a JOB DETAILS section — the selected job's contract — both placeholders until the postings land); the console's ✕ is the way-out, and the JOB BOARD slot's own press stays live as the second door (the deck + drawer stand down meanwhile — SurfaceScene.setServiceLocked). The bar's clip is data/landing.json → videos[frame].bar (the neutral capital's capital-bar-neutral.mp4).", "video": { "file": "assets/videos/jobboard.mp4", "aspect": [2, 3] }, "items": [ { "id": "jobBoard", "label": "Job Board", "accent": "#ffc94d", "where": { "isHomeSystem": false } }, { "id": "bar", "label": "Bar", "labelActive": "Leave Bar", "accent": "#00e5ff", "where": { "planetClass": "capitalNeutral" } } ] } }, "colors": { "panelTop": "#101c36", "panelBottom": "#04070e", "slotBg": "#0b1322", "slotBorder": "#22405f", "rail": "#00e5ff", "railBottom": "#ff2d6f", "text": "#eaf6ff", "reserved": "#3d4c74", "hatch": "#78b4ff" }, "scanline": { "_comment": "CRT scanlines over the bar + buttons — the menu's recipe (one dark row per pitch rows). Defaults follow theme.overlay.scanline; override here for the deck only.", "pitch": 3, "alpha": 0.14 }, "animation": { "bootStagger": 70, "rgb": { "_comment": "RGB pull-apart on labels/panel (the menu title's GlitchText technique): idleOffset px of constant chromatic fringe, burstOffset extra px at full burst, idleAlpha/burstAlpha ghost strength.", "idleOffset": 1.8, "burstOffset": 6, "idleAlpha": 0.4, "burstAlpha": 0.55 }, "comet": { "enabled": true, "everyMs": [3600, 7800], "durationMs": 950 }, "glitch": { "enabled": true, "intervalMs": [3500, 9000], "durationMs": [240, 480], "slices": [3, 6] }, "shimmer": { "_comment": "The RESEARCH slot's READY cue (GameScene._applyResearchCue arms it while a tech can start — one cue on screen at a time; the console's category tabs wear the same recipe, data/research.json → fx.tabShimmer): a yellow band sweeps the face, then a comet runs the cut-corner edge (js/ui/ShimmerCue.js). color is the comm pulse's yellow family (data/comms.json → box.pulse); periodMs = faceMs + gapMs + edgeMs + the silent rest.", "enabled": true, "color": "#ffd84d", "periodMs": 4200, "faceMs": 700, "gapMs": 140, "edgeMs": 1500, "dashFrac": 0.3 } } }