Mission Board Screen

The hub’s job-board surface. Players land here from / (HOME) and pick one of two posted mercenary contracts; accepting one assembles a RunDef and routes to /games/starship-survivors/play. Implemented in src/metagame/screens/MissionBoardScreen.tsx.

Surface framing

Diegetically the screen is “CYGNUS NET v3.2” — a mercenary exchange terminal. The header reads JOB BOARD // CYGNUS NET v3.2 with a stable mount-time UTC timestamp on the right and a ← HOME ghost button on the left. Below the header a single-line preamble in cyan caps reads INCOMING POSTINGS — 2 OF MANY. SELECT ASSIGNMENT.. A footer locks the fiction: CYGNUS NET MERCENARY EXCHANGE — UNAUTHORIZED ACCESS PROHIBITED — ALL CONTRACTS BINDING UNDER OUTER-RIM COMPACT §7.

Visually the screen runs on the unified medical-UI tokens (<MedPanel>, --med-* CSS vars) — white panels on a recessed grey background, cyan accent for headlines, amber for the payout line. The earlier green-on-black terminal aesthetic was removed in Tick 77 (PR 4/5) per decisions/20260513-001.

Postings

Exactly two postings are drawn at mount time via pickTwoPostings() from src/starship-survivors/data/mission-postings.ts, stored once in useState so they remain stable for the lifetime of the screen mount. The preamble copy (“2 OF MANY”) hints at a larger pool, but only two are surfaced per visit.

Each MissionPosting renders as a <MedPanel variant="card"> and shows, top-to-bottom:

ElementSource fieldStyle
Faction labelposting.factionUppercase secondary text, letter-spaced
Difficulty pillposting.difficultyBordered pill colored by DIFFICULTY_VAR
Location lineposting.planetIdPLANETS[id].nameLOCATION: <name> muted label, white name
Objective headlineposting.objectiveLabelUppercased, 20px, cyan --med-accent-deep
Flavor blurbposting.objectiveBlurbLeft-border quote block, secondary text
Payout lineposting.payoutLineAmber, semi-bold, letter-spaced
Posted-by attributionposting.postedBy— Posted by <name>, muted, 11px
Accept buttonFull-width primary medical button, label ACCEPT

The Accept button carries an aria-label of the form Accept mission: <objectiveLabel> on <planetName>.

Difficulty → color mapping

Five tiers map to medical-UI status tokens (rising severity):

DifficultyCSS var
Routine--med-status-good
Standard--med-accent
Hazardous--med-amber
Critical--med-status-warn
Black Flag--med-status-bad

Unknown difficulties fall back to --med-text-muted.

Accept flow

handleAccept(posting) reads selectedShipId from useSessionStore, calls assembleRunDef with { shipId, planetIndex: posting.planetId }, writes the result to the session via setRunDef, then navigates to /games/starship-survivors/play using react-router-dom’s useNavigate. The two postings on screen don’t carry per-mission overrides into assembleRunDef beyond the planet ID — the rest of the run shape (modifiers, biome, boss profile) is derived downstream from the planet config.

What is and isn’t on this screen

The current implementation surfaces only what’s listed above. Notably absent from MissionBoardScreen.tsx at the verified commit:

  • No extraction-timer display. The run’s own extraction timer is set by the run-assembly pipeline, not previewed on the card.
  • No weapon-box count line. Crate counts come from biome/run config; the card doesn’t summarize them.
  • No refresh/reroll affordance. The two postings are drawn once on mount; there’s no in-screen way to redraw them — leaving and re-entering is the only refresh.
  • No skip-cost ramp. There is no skip action and no gem economy hooked into this screen.

If those affordances are intended, they’re not yet wired here — track them in TODO.md rather than treating them as concept-page facts.