Move .sheet(isPresented: $showPaywall) from subscription section to
top-level body so StoreManager state changes don't dismiss the sheet.
Reposition heart (16pt top/trailing) and map (16pt bottom/trailing,
above gradient) buttons on trip detail map.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the generic paywall header with a branded "SportsTime Pro / Your All-Access Pass"
hero section, 4 uniform feature cards (GeometryReader-sized squares), and a dashed ticket
perforation separator. Add "Show Paywall" debug button in Settings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gate Icon Generator section behind #if DEBUG and group it with other debug
sections at the bottom of Settings. Remove auto-focus on description field,
dismiss keyboard on return key and on scroll.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical:
- ProgressViewModel: use single stored ModelContext instead of creating
new ones per operation (deleteVisit silently no-op'd)
- ProgressViewModel: convert expensive computed properties to stored
with explicit recompute after mutations (3x recomputation per render)
Memory:
- AnimatedSportsIcon: replace recursive GCD asyncAfter with Task loop,
cancelled in onDisappear (19 unkillable timer chains)
- ItineraryItemService: remove [weak self] from actor Task (semantically
wrong, silently drops flushPendingUpdates)
- VisitPhotoService: remove [weak self] from @MainActor Task closures
Concurrency:
- StoreManager: replace nested MainActor.run{Task{}} with direct await
in listenForTransactions (fire-and-forget race)
- VisitPhotoService: move JPEG encoding/file writing off MainActor via
nonisolated static helper + Task.detached
- SportsIconImageGenerator: replace GCD dispatch with Task.detached for
structured concurrency compliance
Performance:
- Game/RichGame: cache DateFormatters as static lets instead of
allocating per-call (hundreds of allocations in schedule view)
- TripDetailView: wrap ~10 routeWaypoints print() in #if DEBUG, remove
2 let _ = print() from TripMapView.body (fires every render)
Accessibility:
- GameRow: add combined VoiceOver label (was reading abbreviations
letter-by-letter)
- Sport badges: add accessibilityLabel to prevent SF symbol name readout
- SportsTimeApp: post UIAccessibility.screenChanged after bootstrap
completes so VoiceOver users know app is ready
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add a11y label to ProgressMapView reset button and progress bar values
- Fix CADisplayLink retain cycle in ItineraryTableViewController via deinit
- Add [weak self] to PhotoGalleryViewModel Task closure
- Add @MainActor to TripWizardViewModel, remove manual MainActor.run hop
- Fix O(n²) rank lookup in PollDetailView/DebugPollPreviewView with enumerated()
- Cache itinerarySections via ItinerarySectionBuilder static extraction + @State
- Convert CanonicalSyncService/BootstrapService from actor to @MainActor final class
- Add .accessibilityHidden(true) to RegionMapSelector Map to prevent duplicate VoiceOver
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 22 new UI tests across 8 test files covering Home, Schedule, Progress,
Settings, TabNavigation, TripSaving, and TripOptions. Add accessibility
identifiers to 11 view files for test element discovery. Fix sport chip
assertion logic (all sports start selected, tap deselects), scroll container
issues on iOS 26 nested ScrollViews, toggle interaction, and delete trip flow.
Update QA coverage map from 32 to 54 automated test cases.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add comprehensive UI test infrastructure with Page Object pattern,
accessibility identifiers, UI test mode (--ui-testing, --reset-state,
--disable-animations), and 10 passing tests covering app launch, tab
navigation, trip wizard, trip saving, settings, schedule, and
accessibility at XXXL Dynamic Type. Also adds a 229-case QA test plan
Excel workbook for manual QA handoff.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add debug-only Marketing Video Mode toggle that enables hands-free
screen recording across the app: auto-scrolling Featured Trips carousel,
auto-filling trip wizard, smooth trip detail scrolling via CADisplayLink,
and trip options auto-sort with scroll.
Add Remotion marketing video project with 6 scene compositions using
image sequences extracted from screen recordings, varied phone entrance
animations, and deduped frames for smooth playback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all system colors (.secondary, Color(.secondarySystemBackground),
etc.) with Theme.textPrimary/textSecondary/textMuted/cardBackground/
surfaceGlow across 13 views. Remove PostHog debug logging. Add debug
settings for sample trips and hardcoded group poll preview.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add VoiceOver labels, hints, and element grouping across all 60+ views
- Add Reduce Motion support (Theme.Animation.prefersReducedMotion) to all animations
- Replace fixed font sizes with semantic Dynamic Type styles
- Hide decorative elements from VoiceOver with .accessibilityHidden(true)
- Add .minimumHitTarget() modifier ensuring 44pt touch targets
- Add AccessibilityAnnouncer utility for VoiceOver announcements
- Improve color contrast values in Theme.swift for WCAG AA compliance
- Extract CloudKitContainerConfig for explicit container identity
- Remove PostHog debug console log from AnalyticsManager
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create privacy.html and eula.html matching landing page style
- Update Settings links to sportstime.88oakapps.com/privacy.html and /eula.html
- Rename "Terms of Service" to "EULA" in Settings
- Replace emoji logo with real app icon across all landing pages
- Update footer links in index.html
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redesign trip, progress, and achievement share cards with premium
sports-media aesthetic. Remove unused milestone/context achievement card
types (only used in debug exporter). Fix gold text unreadable in light
mode. Fix sport selector to only show stroke on selected sport.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds debug-only DebugShareExporter that bulk-exports ~298 shareable
image variations (achievement cards, progress cards, trip cards, icons)
to Documents/DebugExport/ for visual QA. Also adds a button to populate
all stadium visits for testing the fully-unlocked app state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove Scripts/ directory (scraper no longer needed)
- Add themed background documentation to CLAUDE.md
- Add .gitignore for marketing-videos to prevent node_modules tracking
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix startDate to use Calendar.startOfDay instead of Date() to include
games earlier in the current day
- Add SyncLogger for file-based sync logging viewable in Settings
- Add "View Sync Logs" button in Settings debug section
- Add diagnostics and NBA game logging to ScheduleViewModel
- Add dropped game logging to DataProvider.filterRichGames
- Use SyncLogger in SportsTimeApp and CloudKitService for sync operations
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add debug-only sync status monitoring to help diagnose CloudKit sync issues.
Shows last sync time, success/failure, and record counts for each entity type.
Includes manual sync trigger and re-enable button when sync is paused.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AppearanceMode enum with system, light, and dark options
- Add AppearanceManager singleton to persist user preference
- Add appearance section in SettingsView with icon and description
- Apply preferredColorScheme at app root for immediate effect
- Include appearance mode in reset to defaults
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add debug toggle in Settings to override Pro subscription status (DEBUG builds only, defaults to true)
- Auto-validate wizard step flags on appear so button enables without explicit user interaction:
- DatesStep: calls updateHasSetDates() on appear
- RoutePreferenceStep: sets hasSetRoutePreference on appear
- RepeatCitiesStep: sets hasSetRepeatCities on appear
Previously, canPlanTrip required all flags to be explicitly set by user interaction, even when valid defaults were showing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add themed backgrounds for each onboarding feature page:
- Unlimited Trips: animated route map with dotted paths and traveling car
- Export & Share: floating documents with radiating share lines
- Track Your Journey: stadium map with pins and achievement badges
- Add sports-themed pricing page background with random glow effects
- Display introductory offer pricing in subscription rows
- Add feature bullets to each onboarding page for better value prop
- Add crown icon header and feature pills to pricing page
- Add debug button in Settings to preview onboarding flow
- Create StoreKit configuration file for testing IAP
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add design style system with 23 unique home screen aesthetics:
- Classic (original SportsTime design, now default)
- 12 experimental styles (Brutalist, Luxury Editorial, etc.)
- 10 polished app-inspired styles (Flighty, SeatGeek, Apple Maps,
Things 3, Airbnb, Spotify, Nike Run Club, Fantastical, Strava,
Carrot Weather)
Includes settings picker to switch between styles and persists
selection via UserDefaults.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement freemium model with StoreKit 2:
- StoreManager singleton for purchase/restore/entitlements
- ProFeature enum defining gated features
- PaywallView and OnboardingPaywallView for upsell UI
- ProGate view modifier and ProBadge component
Feature gating:
- Trip saving: 1 free trip, then requires Pro
- PDF export: Pro only with badge indicator
- Progress tab: Shows ProLockedView for free users
- Settings: Subscription management section
Also fixes pre-existing test issues with StadiumVisit
and ItineraryOption model signature changes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add scenePhase observer to sync when app returns to foreground
- Remove misleading "Sync Schedules" button from Settings (it only
reloaded local data, didn't actually sync from CloudKit)
- Fix GamesHistoryView to refresh list after deleting a visit
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add HTML files for hosting at 88oakapps.com/privacy and /terms
- Update SettingsView links to use 88oakapps.com domain
- Plain English, minimal legal documents for freemium app
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add RegionMapSelector UI for geographic trip filtering (East/Central/West)
- Add RouteFilters module for allowRepeatCities preference
- Improve GameDAGRouter to preserve route length diversity
- Routes now grouped by city count before scoring
- Ensures 2-city trips appear alongside longer trips
- Increased beam width and max options for better coverage
- Add TripOptionsView filters (max cities slider, pace filter)
- Remove TravelStyle section from trip creation (replaced by region selector)
- Clean up debug logging from DataProvider and ScenarioAPlanner
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Stadium Progress & Achievements:
- Add StadiumVisit and Achievement SwiftData models
- Create Progress tab with interactive map view
- Implement photo-based visit import with GPS/date matching
- Add achievement badges (count-based, regional, journey)
- Create shareable progress cards for social media
- Add canonical data infrastructure (stadium identities, team aliases)
- Implement score resolution from free APIs (MLB, NBA, NHL stats)
UI Improvements:
- Add ThemedSpinner and ThemedSpinnerCompact components
- Replace all ProgressView() with themed spinners throughout app
- Fix sport selection state not persisting when navigating away
Bug Fixes:
- Fix Coast to Coast trips showing only 1 city (validation issue)
- Fix stadium progress showing 0/0 (filtering issue)
- Remove "Stadium Quest" title from progress view
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove all print statements from planning engine, data providers, and PDF generation
- Fix deprecated CLGeocoder usage with MKLocalSearch for iOS 26
- Fix Swift 6 actor isolation by converting PDFGenerator/ExportService to @MainActor
- Add @retroactive to CLLocationCoordinate2D protocol conformances
- Fix unused variable warnings in GameDAGRouter and scenario planners
- Remove unreachable catch blocks in SettingsViewModel
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add themed background to all tab views (Schedule, Settings, My Trips)
- Remove navigation titles from all screens (tab bar provides context)
- Add empty state to My Trips view
- Remove max driving hours slider from trip planner (available in Settings)
- Replace save menu with heart button overlay on trip detail map
- Add ability to unsave trips by tapping heart again
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Teal: Cool cyan and teal tones (default)
- Orbit: Bold navy and orange
- Retro: Classic columbia blue
- Clutch: Championship red and gold
- Monochrome: Clean grayscale aesthetic
- Sunset: Warm oranges and purples
- Midnight: Deep blues and gold
Features:
- Theme selection persisted via UserDefaults
- ThemeManager singleton for app-wide theme state
- All Theme colors now dynamically switch based on selection
- Settings UI shows color preview circles for each theme
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
TripOptionCard improvements:
- Replace horizontal route with vertical layout (start → end with arrow)
- Remove rank badges (1, 2, 3, etc.)
- Split stats into two rows: cities/miles and sports with game counts
- Clear selection when navigating back from detail view
Settings cleanup:
- Remove unused settings (preferred game time, playoff games, notifications)
- Convert remaining settings to sliders
Planning fixes:
- Fix multi-day driving calculation in canTransition
- Remove over-restrictive trip rejection in TravelEstimator
- Clear games cache when sport selection changes
UI polish:
- RoutePreviewStrip shows all cities (abbreviated)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Three-scenario planning engine (A: date range, B: selected games, C: directional routes)
- GeographicRouteExplorer with anchor game support for route exploration
- Shared ItineraryBuilder for travel segment calculation
- TravelEstimator for driving time/distance estimation
- SwiftUI views for trip creation and detail display
- CloudKit integration for schedule data
- Python scraping scripts for sports schedules
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>