- "parks open" → "parks open this week" for clarity
- Week calendar cells: stack hours above tz abbreviation (smaller,
dimmer) instead of inline to avoid overflow in tight 130px columns
- Mobile park cards: tz abbreviation inline but smaller/dimmer (60% opacity)
- Month calendar: same two-line stacking in compact day cells
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- isWithinOperatingWindow now accepts an IANA timezone and reads the
current time in the park's local timezone via Intl.DateTimeFormat,
fixing false positives when the server runs in UTC but parks store
hours in local time (e.g. Pacific parks showing open at 6:50 AM EDT)
- Remove the 1-hour pre-open buffer so parks are not marked open before
their doors actually open; retain the 1-hour post-close grace period
- Add getTimezoneAbbr() helper to derive the short tz label (EDT, PDT…)
- All hours labels now display with the local timezone abbreviation
(e.g. "10am – 6pm PDT") in WeekCalendar, ParkCard, and ParkMonthCalendar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 🎢 Coasters button in nav bar (URL-driven: ?coasters=1)
- When active, swaps ride counts for coaster counts per park
- Label switches between "X rides operating" / "X coasters operating"
- Arrow key navigation preserves coaster filter state
- Only shown when coaster data exists in park-meta
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- "X rides open" → "X rides operating" (desktop + mobile)
- Green glowing dot next to park name when actively operating
- Hours text in calendar cells: larger (0.78rem) and bolder (600)
- Parks open badge: green tint when parks are open, larger text
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract isWithinOperatingWindow() to lib/env.ts (shared)
- Park detail page: always fetch Queue-Times, but force all rides
closed when outside the ±1h operating window
- LiveRidePanel: always show closed ride count badge (not just when
some rides are also open); label reads "X rides total" when none
are open vs "X closed / down" when some are
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fetch Queue-Times ride counts for parks open today (5min cache)
- Only shown within 1h before open to 1h after scheduled close
- Count displayed on the right of the park name/location cell (desktop)
and below the open badge (mobile)
- Whole park cell is now a clickable link
- Hover warms the park cell background; no row-wide highlight
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove today background/border from data row cells so the yellow
highlight only appears on the day label, not the entire column.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
new Date().toISOString() returns UTC, causing the calendar to advance
to the next day at 8pm EDT / 7pm EST. getTodayLocal() reads local
wall-clock time and rolls back one day before 3am so the calendar
stays on the current day through the night.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WeekNav: add keydown listener for ArrowLeft/ArrowRight week nav
- EmptyState: replace dev-facing "No data scraped yet" + npm commands
with customer-friendly "Schedule not available yet" message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restores fixed-height ride rows with ellipsis truncation.
Adds title attribute so hovering shows the full name in a native tooltip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Matching fixes:
- normalize() now strips all non-word/non-space chars via [^\w\s] instead of
a hand-rolled list, catching !, curly apostrophe (U+2019), and any future edge cases
- Add isCoaster() helper with prefix matching (min 5 chars) to handle subtitle
mismatches in either direction (e.g. "Apocalypse" vs "Apocalypse the Ride",
"The New Revolution - Classic" vs "New Revolution")
- Fix top-level rides loop which still used coasterNames.has(normalize()) instead
of isCoaster() — this was the recurring bug causing top-level rides to miss
UI:
- Dark neutral base (#111) replacing cold navy and muddy purple
- Neon accent palette: hot pink, electric green, vivid yellow, cyan
- Park page max-width 960→1280px, calendar cells 72→96px tall
- Scrollbar accent matches theme
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Queue-Times groups rides into lands (e.g. "Coasters", "Family", "Kids").
Capture that categorisation in LiveRide.isCoaster and surface it as a
toggle in the new LiveRidePanel client component.
- lib/scrapers/queuetimes.ts: add isCoaster: boolean to LiveRide,
derived from land.name.toLowerCase().includes("coaster")
- components/LiveRidePanel.tsx: client component replacing the old
inline LiveRideList; adds a "🎢 Coasters only" pill toggle that
filters the grid; toggle only appears when the park has coaster-
categorised rides; amber when active, muted when inactive
- app/park/[id]/page.tsx: swap LiveRideList for LiveRidePanel,
remove now-dead LiveRideList/LiveRideRow functions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the cramped 7-column day grid with a clean open-days list.
Each card now shows:
- Park name + "Open today" / "Closed today" badge in the header
- One row per open day (Today, Monday, Friday...) with full hours
- Today row highlighted in amber; passholder days labeled inline
- Whole card is a tap target linking to the park detail page
Also:
- Hide the legend below sm breakpoint (not needed on phones)
- Reduce horizontal padding to 16px on mobile (was 24px)
- Tighten MobileCardList vertical spacing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove unused formatDate() from park page (replaced by formatShortDate)
- Remove unused date prop from DayCell component
- Change let to const for cellBorderRadius in ParkCard
- Change let to const for bg in ParkMonthCalendar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Visual overhaul:
- Warmer color system with amber accent for Today, better text hierarchy
- Row hover highlighting, sticky column shadow on horizontal scroll
- Closed cells replaced with dot (·) instead of "Closed" text
- Regional grouping (Northeast/Southeast/Midwest/Texas & South/West)
- Two-row header with park count badge and WeekNav on separate lines
- Amber "Today" button in WeekNav when off current week
- Mobile card layout (< 1024px) with 7-day grid per park; table on desktop
- Skeleton loading state via app/loading.tsx
Park detail pages (/park/[id]):
- Month calendar view with ← → navigation via ?month= param
- Live ride status fetched from Six Flags API (cached 1h)
- Ride hours only shown when they differ from park operating hours
- Fallback to nearest upcoming open day when today is dropped by API,
including cross-month fallback for end-of-month edge case
Data layer:
- Park type gains region field; parks.ts exports groupByRegion()
- db.ts gains getParkMonthData() for single-park month queries
- sixflags.ts gains scrapeRidesForDay() returning RidesFetchResult
with rides, dataDate, isExact, and parkHoursLabel
Removed: CalendarGrid.tsx, MonthNav.tsx (dead code)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Buyout days are now treated as closed unless they carry a Passholder
Preview event, in which case they surface as a distinct purple cell
in the UI showing "Passholder" + hours
- DB gains a special_type column (auto-migrated on next startup)
- scrape.ts threads specialType through to upsertDay
- debug.ts now shows events, isBuyout, isPassholderPreview, and
specialType in the parsed result section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Next.js 15 + Tailwind CSS v4 week calendar showing Six Flags park hours.
Scrapes the internal CloudFront API, stores results in SQLite.
Includes Dockerfile (Debian/Playwright-compatible), docker-compose, and
Gitea Actions pipeline that builds and pushes to the container registry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>