diff --git a/src/client/App.tsx b/src/client/App.tsx
index 9983d96..220012e 100644
--- a/src/client/App.tsx
+++ b/src/client/App.tsx
@@ -5,6 +5,7 @@ import { EventDetail } from "./components/EventDetail.tsx";
import { EventRow, type RowEvent } from "./components/EventRow.tsx";
import { NextUp } from "./components/NextUp.tsx";
import { Timeline } from "./components/Timeline.tsx";
+import { Welcome } from "./components/Welcome.tsx";
import { useCompletions } from "./state/useCompletions.ts";
import { usePrefs } from "./state/usePrefs.ts";
import { clockFor, DAY, endingSoonestFirst, formatRemaining } from "../shared/time.ts";
@@ -91,6 +92,25 @@ export function App() {
);
}
+ // First run: ask which games before showing a calendar full of ones they
+ // don't play. Stored as hiddenGames (the inverse) so a game added later shows
+ // up by default rather than staying invisible.
+ if (!prefs.onboarded) {
+ return (
+
+
+ update({
+ onboarded: true,
+ hiddenGames: games.filter((g) => !chosen.includes(g)),
+ })
+ }
+ />
+
+ );
+ }
+
const staleSources = state.feed.sources.filter(
(s) => s.lastSuccessAt === null || now - Date.parse(s.lastSuccessAt) > 2 * DAY,
);
diff --git a/src/client/components/Welcome.tsx b/src/client/components/Welcome.tsx
new file mode 100644
index 0000000..7b9a17d
--- /dev/null
+++ b/src/client/components/Welcome.tsx
@@ -0,0 +1,118 @@
+import { useState } from "react";
+import { gameMeta } from "../../shared/games.ts";
+import type { GameId } from "../../shared/schema.ts";
+
+/**
+ * First run: pick your games.
+ *
+ * Asked once, before any events are shown, because a calendar full of games you
+ * don't play is worse than an empty one — it buries the thing you came for.
+ *
+ * Nothing is preselected. An empty state with a disabled button is clearer than
+ * guessing on the reader's behalf and hoping they notice.
+ */
+export function Welcome({
+ available,
+ onConfirm,
+}: {
+ available: GameId[];
+ onConfirm: (chosen: GameId[]) => void;
+}) {
+ const [chosen, setChosen] = useState([]);
+
+ const toggle = (id: GameId) =>
+ setChosen((prev) =>
+ prev.includes(id) ? prev.filter((g) => g !== id) : [...prev, id],
+ );
+
+ return (
+
+
+ EVENTCLOCK
+
+
+
+ Which games do you play?
+
+
+ You'll only see events for these. Change it any time — nothing is saved
+ anywhere but this browser.
+