read-only /roadmap page mirroring docs/backlog.md’s Next/Ideas/Done sections, plus a per-item detail page and search. MarketingController#roadmap parses the file at request time (no new database model – docs/backlog.md stays the single source of truth); RoadmapItem struct (id/title/detail/status). GET roadmap/:id -> #roadmap_item renders the full untruncated detail through Kramdown (GFM) for inline-code readability, 404s on unknown IDs (case-insensitive match). Search reuses the existing inventory-filter Stimulus controller (client-side, no new JS) – filters cards by title+detail, hides empty sections. Linked from both the logged-out marketing nav (desktop/mobile/footer) and the logged-in app’s account dropdown (sidebar + mobile top bar), next to Handbook/Keepers/About – it doesn’t belong in the sidebar’s primary entity nav. Shipped section paginated (20/page, manual each_slice – the pagy Gemfile dependency exists but isn’t wired up anywhere in the app, judged not worth blind-adopting for one array), controls rendered top and bottom via a new reusable application/components/_pagination primitive (label-divider style flourish arrow – real .svg asset at app/assets/images/roadmap/pagination-arrow.svg, read+inlined for currentColor theming, scaleX(-1)-mirrored for “previous”; tavern-badge-muted page-count pill; documented in .claude/rules/design-system.md). This is the smallest slice of BL-039 (feature voting) – submissions and voting deferred, need their own model + ADR. ADR-0080 (+ 3 refinement notes, 2026-07-06). Verified in-browser via Playwright (search filters correctly, detail page renders Kramdown-formatted code spans, pagination clamps/links correctly top and bottom).
BL-100
Shipped