diff --git a/frontend/src/pages/Display.tsx b/frontend/src/pages/Display.tsx
index b49f122..dbd9ad1 100644
--- a/frontend/src/pages/Display.tsx
+++ b/frontend/src/pages/Display.tsx
@@ -12,6 +12,21 @@ const POLL_MS = 5 * 60 * 1000 // client polls more often than the server's hourl
// refresh just so a stale tab catches up quickly —
// the actual new data only shows up on the hour.
+// Today's hourly row needs to fit in one line without scrolling on a 10" kiosk
+// tablet, where each card is already at its minimum readable width (~11 fit).
+// Past hours aren't useful on a live display, so we only show now-onward; when
+// that's still more than fit, the near term stays at full 1-hour resolution
+// (most relevant) and the rest of the day coarsens to 2-hour steps. Only right
+// after midnight (24 hours still remaining) does this land on 12 instead of
+// 11 — a window nobody's realistically viewing the lobby display in.
+const TODAY_MAX_CARDS = 11
+function selectTodayCards(hourly: ForecastPoint[], nowHour: number): ForecastPoint[] {
+ const remaining = hourly.filter(p => hourOf(p.ts) >= nowHour)
+ if (remaining.length <= TODAY_MAX_CARDS) return remaining
+ const fineWindow = Math.max(0, 2 * TODAY_MAX_CARDS - remaining.length)
+ return remaining.filter((_, i) => i < fineWindow || (i - fineWindow) % 2 === 0)
+}
+
interface SelectedDay {
dateKey: string
dayLabel: string
@@ -132,7 +147,7 @@ export default function Display() {
{todayKey &&