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:
| Element | Source field | Style |
|---|---|---|
| Faction label | posting.faction | Uppercase secondary text, letter-spaced |
| Difficulty pill | posting.difficulty | Bordered pill colored by DIFFICULTY_VAR |
| Location line | posting.planetId → PLANETS[id].name | LOCATION: <name> muted label, white name |
| Objective headline | posting.objectiveLabel | Uppercased, 20px, cyan --med-accent-deep |
| Flavor blurb | posting.objectiveBlurb | Left-border quote block, secondary text |
| Payout line | posting.payoutLine | Amber, semi-bold, letter-spaced |
| Posted-by attribution | posting.postedBy | — Posted by <name>, muted, 11px |
| Accept button | — | Full-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):
| Difficulty | CSS 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.
Related
mission-deploy— what happens after Accept lands in/play.mission-objectives— the objective-type taxonomyobjectiveLabeldraws from.assemble-run— how theRunDefis built from(shipId, planetId).hub-illumination-map— hub-screen sibling surfaces.