diff --git a/.planning/phases/02-calendar-display/02-05-SUMMARY.md b/.planning/phases/02-calendar-display/02-05-SUMMARY.md
new file mode 100644
index 0000000..db4568b
--- /dev/null
+++ b/.planning/phases/02-calendar-display/02-05-SUMMARY.md
@@ -0,0 +1,186 @@
+---
+phase: 02-calendar-display
+plan: "05"
+subsystem: pwa-calendar-ux
+tags: [event-popover, color-legend, app-nav, view-toolbar, skeleton, empty-state, xss-guard, tdd]
+dependency_graph:
+ requires: ["02-04"]
+ provides: [EventDetailPopover, ColorLegend, AppNav, ViewToolbar, SkeletonCalendar, EmptyState, CalendarShell-chrome]
+ affects: ["phase-03"]
+tech_stack:
+ added: []
+ patterns:
+ - EventDetailPopover dual-mode — Zustand openEventId (standalone) + customComponents.eventModal (Schedule-X)
+ - queryClient.getQueriesData for cross-query cache lookup by event id
+ - ViewToolbar accesses Schedule-X internal calendarApp.$app.calendarState for navigation
+ - SkeletonCalendar shimmer via CSS animation from tokens.css @keyframes shimmer
+ - TDD RED commit (test only) → GREEN commit (feat + test) per plan task 1 gate
+ - "@testing-library/jest-dom" imported in test-setup.ts for toHaveTextContent matcher
+key_files:
+ created:
+ - apps/pwa/src/components/EventDetailPopover.tsx
+ - apps/pwa/src/components/EventDetailPopover.test.tsx
+ - apps/pwa/src/components/ColorLegend.tsx
+ - apps/pwa/src/components/AppNav.tsx
+ - apps/pwa/src/components/ViewToolbar.tsx
+ - apps/pwa/src/components/SkeletonCalendar.tsx
+ - apps/pwa/src/components/EmptyState.tsx
+ modified:
+ - apps/pwa/src/components/CalendarShell.tsx
+ - apps/pwa/src/components/CalendarShell.test.tsx
+ - apps/pwa/src/test-setup.ts
+ - apps/pwa/src/api/client.ts
+ deleted:
+ - apps/pwa/src/components/EventProof.tsx
+decisions:
+ - "EventDetailPopover dual-mode: standalone (Zustand openEventId + TanStack Query cache) AND Schedule-X customComponents.eventModal"
+ - "ViewToolbar navigation via calendarApp.$app.calendarState — Schedule-X internal API; typed as any, runtime-guarded"
+ - "Phase 3 footer action area reserved in EventDetailPopover with code comment (D-08)"
+ - "Legacy fetchEventsLegacy / CalendarEvent types removed from client.ts along with EventProof deletion"
+metrics:
+ duration: "~30m"
+ completed: "2026-06-05"
+ tasks_completed: 2
+ tasks_pending: 1
+ files_created: 7
+ files_modified: 4
+ files_deleted: 1
+---
+
+# Phase 02 Plan 05: Calendar UX — Popover, Chrome, States Summary
+
+Read-only EventDetailPopover (XSS-safe, accessible, Phase-3-reusable), always-visible ColorLegend, AppNav/ViewToolbar chrome, and skeleton/empty/error states wired into CalendarShell; EventProof retired.
+
+## What Was Built
+
+### Task 1: EventDetailPopover + CalendarShell wiring (TDD)
+
+**`apps/pwa/src/components/EventDetailPopover.tsx`** (259 lines):
+- Resolves open event by `openEventId` (Zustand) from TanStack Query `['events']` cache using `queryClient.getQueriesData`
+- Dual-mode: standalone (primary, driven by Zustand) + `customComponents.eventModal` prop from Schedule-X
+- Renders title (heading), date/time, location (with MapPin icon when present), description (max-4-lines scroll), calendar name + color swatch footer
+- T-02e-01 XSS guard: all event fields as plain-text JSX children — no raw HTML injection
+- Focus trap on open, Escape to close (document keydown listener), backdrop-click to close
+- Close "×" button with `aria-label="Close"` and 44px minimum touch target
+- Phone: bottom-sheet layout (fixed bottom, rounded top corners); tablet/desktop: centered popover (max-width 360px)
+- Phase 3 footer action area reserved with comment — Phase 3 adds edit/delete actions there (D-08)
+- Wired in CalendarShell: `customComponents={{ eventModal: EventDetailPopover }}` AND rendered standalone after the layout
+
+**`apps/pwa/src/components/EventDetailPopover.test.tsx`** (192 lines, TDD RED → GREEN):
+- TDD RED commit: tests written first, failing because file missing
+- GREEN commit: implementation makes all 12 tests pass
+- Tests: title/location/description/calendarName render as text; heading role; Escape/close-button/backdrop all call `setOpenEventId(null)`; null openEventId renders nothing
+- XSS guard: `` in title → `heading.innerHTML` does NOT contain `` in title → `heading.innerHTML` does NOT contain `