diff --git a/ZeroSpace/ZeroSpace.Web/SiteMap.cs b/ZeroSpace/ZeroSpace.Web/SiteMap.cs
new file mode 100644
index 0000000..fd79545
--- /dev/null
+++ b/ZeroSpace/ZeroSpace.Web/SiteMap.cs
@@ -0,0 +1,51 @@
+using Microsoft.AspNetCore.Components.Routing;
+
+namespace ZeroSpace.Web;
+
+///
+/// Single source of truth for which pages exist, which are released,
+/// and which appear in navigation. New pages are hidden until explicitly
+/// released here (fail-closed): anything not listed, or listed with
+/// Released=false, redirects to the galaxy map via .
+///
+public static class SiteMap
+{
+ public sealed record Entry(
+ Type Page,
+ string Title,
+ string Href,
+ string IconCss,
+ string? Badge,
+ NavLinkMatch Match,
+ bool Released,
+ bool ShowInNav);
+
+ public static readonly IReadOnlyList Entries =
+ [
+ // The releasable surface.
+ new(typeof(Pages.Galaxy), "Galaxy Map", "galaxy", "bi bi-galaxy-nav-menu", "MAP",
+ NavLinkMatch.Prefix, Released: true, ShowInNav: true),
+
+ // Detail dossiers: part of the galaxy experience (linked from the map),
+ // but not top-level navigation destinations.
+ new(typeof(Pages.StarSystemDetail), "System Dossier", "StarSystem", "bi bi-star-systems-nav-menu", null,
+ NavLinkMatch.Prefix, Released: true, ShowInNav: false),
+ new(typeof(Pages.SystemMasteryDetail), "Mastery Dossier", "SystemMastery", "bi bi-masteries-nav-menu", null,
+ NavLinkMatch.Prefix, Released: true, ShowInNav: false),
+
+ // In-development pages: hidden from nav and unreachable by URL for now.
+ new(typeof(Pages.Home), "Home", "", "bi bi-house-door-fill-nav-menu", null,
+ NavLinkMatch.All, Released: false, ShowInNav: false),
+ new(typeof(Pages.StarSystemIndex), "Star Systems", "StarSystem", "bi bi-star-systems-nav-menu", null,
+ NavLinkMatch.Prefix, Released: false, ShowInNav: false),
+ new(typeof(Pages.SystemMasteryIndex), "Masteries", "SystemMastery", "bi bi-masteries-nav-menu", null,
+ NavLinkMatch.Prefix, Released: false, ShowInNav: false),
+ new(typeof(Pages.Units), "Units", "units", "bi bi-units-nav-menu", null,
+ NavLinkMatch.Prefix, Released: false, ShowInNav: false),
+ ];
+
+ public static IEnumerable NavEntries => Entries.Where(e => e.Released && e.ShowInNav);
+
+ public static bool IsReleased(Type pageType) =>
+ Entries.FirstOrDefault(e => e.Page == pageType)?.Released ?? false;
+}