diff --git a/DECISIONS.md b/DECISIONS.md
index 2c9618e..4a6917d 100644
--- a/DECISIONS.md
+++ b/DECISIONS.md
@@ -441,3 +441,9 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
## Step 9.8
- **Market lists every content ship model** with its stats straight from `@deadhead/content`; no API call is needed until Buy. After a purchase the player-state refresh adds the ship to Fleet, and a status line says where it was delivered.
+
+## Step 9.9
+
+- **Distance from Earth is live** (sim at the synced clock), so it changes as the planets move; Earth itself shows "Home".
+- **Landing requirement "vacuum" is shown as "Any ship"**, since every ship can land at a vacuum-rated location.
+- **Lease buttons are disabled when unaffordable** and absent where a bay exists or the location is undiscovered.
diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx
index fe9182a..9db703d 100644
--- a/apps/web/src/App.tsx
+++ b/apps/web/src/App.tsx
@@ -9,6 +9,7 @@ import { SCREENS } from "./routes";
import { Contracts } from "./screens/Contracts";
import { Fleet } from "./screens/Fleet";
import { HQ } from "./screens/HQ";
+import { Locations } from "./screens/Locations";
import { Market } from "./screens/Market";
import { Pilots } from "./screens/Pilots";
import { Placeholder } from "./screens/Placeholder";
@@ -22,6 +23,7 @@ const SCREEN_COMPONENTS: Partial,
"/pilots": ,
"/market": ,
+ "/locations": ,
};
/** Syncs the clock with the server once, then ticks it every second. */
diff --git a/apps/web/src/screens/Locations.tsx b/apps/web/src/screens/Locations.tsx
new file mode 100644
index 0000000..8d5ea8f
--- /dev/null
+++ b/apps/web/src/screens/Locations.tsx
@@ -0,0 +1,81 @@
+import { content, type Location } from "@deadhead/content";
+import { useClock } from "../clock/clock";
+import { cargoName, formatCredits, formatMillionKm } from "../format";
+import { distanceAt } from "../game";
+import { errorMessage, useGameAction, useLoadedPlayer } from "../state/actions";
+
+const TYPE_LABELS: Record = {
+ "atmospheric-world": "Atmospheric world",
+ "airless-body": "Airless body",
+ "cloud-platform": "Cloud platform",
+ "belt-station": "Belt station",
+};
+const LANDING_LABELS = { vacuum: "Any ship", atmospheric: "Atmospheric", hardened: "Hardened" };
+
+/** Every location; details and bay leasing only once discovered (Step 9.9). */
+export function Locations() {
+ const state = useLoadedPlayer();
+ const now = useClock((s) => s.now);
+ const lease = useGameAction("leaseBay");
+
+ return (
+
+