# Brutalist App-Wide Implementation Plan

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Extend the Brutalist design style from home-screen-only to work app-wide (TripDetailView, ScheduleListView, SavedTripsListView, SettingsView).

**Architecture:** Adaptive view routers that switch between Classic and Brutalist variants based on `DesignStyleManager.shared.currentStyle`. StyleProvider protocol provides shared styling constants. Brutalist uses Theme colors but with monospace fonts, sharp corners, and borders.

**Tech Stack:** SwiftUI, @Observable pattern, Theme system, DesignStyleManager

---

## Task 1: Create StyleProvider Protocol

**Files:**
- Create: `SportsTime/Core/Design/StyleProvider.swift`

**Step 1: Create the StyleProvider file**

```swift
//
//  StyleProvider.swift
//  SportsTime
//
//  Provides style-specific constants for shared components.
//

import SwiftUI

/// Protocol for style-specific visual properties
protocol StyleProvider {
    // Shape
    var cornerRadius: CGFloat { get }
    var borderWidth: CGFloat { get }

    // Typography
    var fontDesign: Font.Design { get }
    var usesUppercase: Bool { get }
    var headerTracking: CGFloat { get }

    // Effects
    var usesGradientBackgrounds: Bool { get }
    var usesSoftShadows: Bool { get }

    // Helpers
    func flatBackground(_ colorScheme: ColorScheme) -> Color
}

// MARK: - Classic Style

struct ClassicStyle: StyleProvider {
    let cornerRadius: CGFloat = 12
    let borderWidth: CGFloat = 0
    let fontDesign: Font.Design = .default
    let usesUppercase = false
    let headerTracking: CGFloat = 0
    let usesGradientBackgrounds = true
    let usesSoftShadows = true

    func flatBackground(_ colorScheme: ColorScheme) -> Color {
        Theme.cardBackground(colorScheme)
    }
}

// MARK: - Brutalist Style

struct BrutalistStyle: StyleProvider {
    let cornerRadius: CGFloat = 0
    let borderWidth: CGFloat = 1
    let fontDesign: Font.Design = .monospaced
    let usesUppercase = true
    let headerTracking: CGFloat = 2
    let usesGradientBackgrounds = false
    let usesSoftShadows = false

    func flatBackground(_ colorScheme: ColorScheme) -> Color {
        colorScheme == .dark ? .black : Color(white: 0.95)
    }
}

// MARK: - UIDesignStyle Extension

extension UIDesignStyle {
    /// Returns the appropriate StyleProvider for this design style
    var styleProvider: StyleProvider {
        switch self {
        case .brutalist:
            return BrutalistStyle()
        default:
            return ClassicStyle()
        }
    }
}
```

**Step 2: Verify the file compiles**

Run: `xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' build 2>&1 | tail -20`
Expected: BUILD SUCCEEDED

**Step 3: Commit**

```bash
git add SportsTime/Core/Design/StyleProvider.swift
git commit -m "$(cat <<'EOF'
feat(design): add StyleProvider protocol for style-specific constants

Introduces ClassicStyle and BrutalistStyle implementations for shared
component styling across the app.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
EOF
)"
```

---

## Task 2: Create TripDetailView Brutalist Variant

**Files:**
- Create: `SportsTime/Features/Trip/Views/Variants/Brutalist/TripDetailView_Brutalist.swift`
- Create: `SportsTime/Features/Trip/Views/Variants/Classic/TripDetailView_Classic.swift`
- Create: `SportsTime/Features/Trip/Views/AdaptiveTripDetailView.swift`
- Modify: `SportsTime/Features/Trip/Views/TripDetailView.swift` (extract to Classic)

### Step 1: Create directory structure

```bash
mkdir -p SportsTime/Features/Trip/Views/Variants/Classic
mkdir -p SportsTime/Features/Trip/Views/Variants/Brutalist
```

### Step 2: Create AdaptiveTripDetailView router

Create `SportsTime/Features/Trip/Views/AdaptiveTripDetailView.swift`:

```swift
//
//  AdaptiveTripDetailView.swift
//  SportsTime
//
//  Routes to the appropriate trip detail variant based on the selected design style.
//

import SwiftUI

struct AdaptiveTripDetailView: View {
    let trip: Trip
    let games: [String: RichGame]?

    /// Initialize with trip and games dictionary
    init(trip: Trip, games: [String: RichGame]) {
        self.trip = trip
        self.games = games
    }

    /// Initialize with just trip - games will be loaded from AppDataProvider
    init(trip: Trip) {
        self.trip = trip
        self.games = nil
    }

    var body: some View {
        switch DesignStyleManager.shared.currentStyle {
        case .brutalist:
            if let games = games {
                TripDetailView_Brutalist(trip: trip, games: games)
            } else {
                TripDetailView_Brutalist(trip: trip)
            }
        default:
            if let games = games {
                TripDetailView_Classic(trip: trip, games: games)
            } else {
                TripDetailView_Classic(trip: trip)
            }
        }
    }
}
```

### Step 3: Copy existing TripDetailView to Classic variant

Copy `TripDetailView.swift` to `Variants/Classic/TripDetailView_Classic.swift` and rename the struct to `TripDetailView_Classic`.

Key changes:
- Rename `struct TripDetailView` → `struct TripDetailView_Classic`
- Rename `struct DaySection` → `struct DaySection_Classic`
- Rename `struct GameRow` → `struct GameRow_Classic`
- Rename `struct TravelSection` → `struct TravelSection_Classic`
- Rename `struct EVChargerRow` → `struct EVChargerRow_Classic`
- Keep `struct ShareSheet` as-is (shared utility)
- Keep `enum ItinerarySection` as-is (shared model)
- Remove the #Preview at the bottom

### Step 4: Create TripDetailView_Brutalist

Create `SportsTime/Features/Trip/Views/Variants/Brutalist/TripDetailView_Brutalist.swift`:

```swift
//
//  TripDetailView_Brutalist.swift
//  SportsTime
//
//  BRUTALIST: Raw, unpolished, anti-design trip details.
//  Monospace typography, harsh borders, ticket stub aesthetics.
//

import SwiftUI
import SwiftData
import MapKit

struct TripDetailView_Brutalist: View {
    @Environment(\.modelContext) private var modelContext
    @Environment(\.colorScheme) private var colorScheme

    let trip: Trip
    private let providedGames: [String: RichGame]?

    @Query private var savedTrips: [SavedTrip]
    @State private var showProPaywall = false
    @State private var showExportSheet = false
    @State private var exportURL: URL?
    @State private var isExporting = false
    @State private var exportProgress: PDFAssetPrefetcher.PrefetchProgress?
    @State private var mapCameraPosition: MapCameraPosition = .automatic
    @State private var isSaved = false
    @State private var routePolylines: [MKPolyline] = []
    @State private var isLoadingRoutes = false
    @State private var loadedGames: [String: RichGame] = [:]
    @State private var isLoadingGames = false

    private let exportService = ExportService()
    private let dataProvider = AppDataProvider.shared

    private var games: [String: RichGame] {
        providedGames ?? loadedGames
    }

    private var bgColor: Color {
        colorScheme == .dark ? .black : Color(white: 0.95)
    }

    private var textColor: Color {
        colorScheme == .dark ? .white : .black
    }

    init(trip: Trip, games: [String: RichGame]) {
        self.trip = trip
        self.providedGames = games
    }

    init(trip: Trip) {
        self.trip = trip
        self.providedGames = nil
    }

    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 0) {
                // HERO MAP - Sharp edges
                heroMapSection
                    .frame(height: 280)

                // HEADER
                tripHeader
                    .padding(.horizontal, 16)
                    .padding(.top, 24)

                perforatedDivider
                    .padding(.vertical, 24)

                // STATS ROW
                statsRow
                    .padding(.horizontal, 16)

                perforatedDivider
                    .padding(.vertical, 24)

                // SCORE CARD
                if let score = trip.score {
                    scoreCard(score)
                        .padding(.horizontal, 16)

                    perforatedDivider
                        .padding(.vertical, 24)
                }

                // ITINERARY
                itinerarySection
                    .padding(.horizontal, 16)
                    .padding(.bottom, 32)
            }
        }
        .background(bgColor)
        .toolbar {
            ToolbarItemGroup(placement: .primaryAction) {
                ShareButton(trip: trip, style: .icon)
                    .foregroundStyle(Theme.warmOrange)

                Button {
                    if StoreManager.shared.isPro {
                        Task { await exportPDF() }
                    } else {
                        showProPaywall = true
                    }
                } label: {
                    HStack(spacing: 2) {
                        Image(systemName: "doc.fill")
                        if !StoreManager.shared.isPro {
                            ProBadge()
                        }
                    }
                    .foregroundStyle(Theme.warmOrange)
                }
            }
        }
        .sheet(isPresented: $showExportSheet) {
            if let url = exportURL {
                ShareSheet(items: [url])
            }
        }
        .sheet(isPresented: $showProPaywall) {
            PaywallView()
        }
        .onAppear { checkIfSaved() }
        .task { await loadGamesIfNeeded() }
        .overlay {
            if isExporting {
                exportProgressOverlay
            }
        }
    }

    // MARK: - Perforated Divider

    private var perforatedDivider: some View {
        HStack(spacing: 8) {
            ForEach(0..<25, id: \.self) { _ in
                Circle()
                    .fill(textColor.opacity(0.3))
                    .frame(width: 6, height: 6)
            }
        }
        .frame(maxWidth: .infinity)
        .padding(.horizontal, 16)
    }

    // MARK: - Hero Map Section

    private var heroMapSection: some View {
        ZStack(alignment: .topTrailing) {
            Map(position: $mapCameraPosition, interactionModes: []) {
                ForEach(stopCoordinates.indices, id: \.self) { index in
                    let stop = stopCoordinates[index]
                    Annotation(stop.name, coordinate: stop.coordinate) {
                        Rectangle()
                            .fill(index == 0 ? Theme.warmOrange : Theme.routeGold)
                            .frame(width: 12, height: 12)
                    }
                }

                ForEach(routePolylines.indices, id: \.self) { index in
                    MapPolyline(routePolylines[index])
                        .stroke(Theme.routeGold, lineWidth: 3)
                }
            }
            .mapStyle(.standard(elevation: .flat))

            // Save button - sharp rectangle
            Button { toggleSaved() } label: {
                Image(systemName: isSaved ? "heart.fill" : "heart")
                    .font(.title3)
                    .foregroundStyle(isSaved ? .red : textColor)
                    .padding(12)
                    .background(bgColor)
                    .border(textColor.opacity(0.3), width: 1)
            }
            .padding(12)
        }
        .task {
            updateMapRegion()
            await fetchDrivingRoutes()
        }
    }

    // MARK: - Header

    private var tripHeader: some View {
        VStack(alignment: .leading, spacing: 8) {
            // Date range
            Text(trip.formattedDateRange.uppercased())
                .font(.system(.caption, design: .monospaced))
                .foregroundStyle(textColor.opacity(0.5))
                .tracking(2)

            // Route as arrow chain
            Text(trip.stops.map { $0.city.uppercased() }.joined(separator: " → "))
                .font(.system(.title3, design: .monospaced).bold())
                .foregroundStyle(textColor)
                .lineLimit(2)

            // Sport badges - bordered rectangles
            HStack(spacing: 8) {
                ForEach(Array(trip.uniqueSports), id: \.self) { sport in
                    HStack(spacing: 4) {
                        Image(systemName: sport.iconName)
                            .font(.caption2)
                        Text(sport.rawValue.uppercased())
                            .font(.system(.caption2, design: .monospaced))
                    }
                    .padding(.horizontal, 10)
                    .padding(.vertical, 6)
                    .foregroundStyle(sport.themeColor)
                    .border(sport.themeColor.opacity(0.5), width: 1)
                }
            }
        }
    }

    // MARK: - Stats Row

    private var statsRow: some View {
        HStack(spacing: 12) {
            brutalistStat(value: "\(trip.tripDuration)", label: "DAYS")
            brutalistStat(value: "\(trip.stops.count)", label: "CITIES")
            brutalistStat(value: "\(trip.totalGames)", label: "GAMES")
        }
    }

    private func brutalistStat(value: String, label: String) -> some View {
        VStack(spacing: 4) {
            Text(value)
                .font(.system(.title, design: .monospaced).bold())
                .foregroundStyle(textColor)
            Text("[ \(label) ]")
                .font(.system(.caption2, design: .monospaced))
                .foregroundStyle(textColor.opacity(0.5))
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 16)
        .border(textColor.opacity(0.2), width: 1)
    }

    // MARK: - Score Card

    private func scoreCard(_ score: TripScore) -> some View {
        VStack(spacing: 16) {
            HStack {
                Text("[ TRIP SCORE ]")
                    .font(.system(.caption, design: .monospaced))
                    .foregroundStyle(textColor.opacity(0.5))
                Spacer()
                Text(score.scoreGrade)
                    .font(.system(size: 48, weight: .black, design: .monospaced))
                    .foregroundStyle(Theme.warmOrange)
            }

            HStack(spacing: 8) {
                scoreItem(label: "GAMES", value: score.gameQualityScore)
                scoreItem(label: "ROUTE", value: score.routeEfficiencyScore)
                scoreItem(label: "BALANCE", value: score.leisureBalanceScore)
                scoreItem(label: "PREFS", value: score.preferenceAlignmentScore)
            }
        }
        .padding(16)
        .border(textColor.opacity(0.2), width: 2)
    }

    private func scoreItem(label: String, value: Double) -> some View {
        VStack(spacing: 4) {
            Text(String(format: "%.0f", value))
                .font(.system(.headline, design: .monospaced).bold())
                .foregroundStyle(textColor)
            Text(label)
                .font(.system(.caption2, design: .monospaced))
                .foregroundStyle(textColor.opacity(0.4))
        }
        .frame(maxWidth: .infinity)
    }

    // MARK: - Itinerary

    private var itinerarySection: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text("[ ITINERARY ]")
                .font(.system(.caption, design: .monospaced))
                .foregroundStyle(textColor.opacity(0.5))

            if isLoadingGames {
                Text("LOADING...")
                    .font(.system(.body, design: .monospaced))
                    .foregroundStyle(textColor.opacity(0.5))
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 32)
            } else {
                ForEach(Array(itinerarySections.enumerated()), id: \.offset) { index, section in
                    switch section {
                    case .day(let dayNumber, let date, let gamesOnDay):
                        DaySection_Brutalist(
                            dayNumber: dayNumber,
                            date: date,
                            games: gamesOnDay
                        )
                    case .travel(let segment):
                        TravelSection_Brutalist(segment: segment)
                    }
                }
            }
        }
    }

    // MARK: - Export Progress Overlay

    private var exportProgressOverlay: some View {
        ZStack {
            Color.black.opacity(0.8)
                .ignoresSafeArea()

            VStack(spacing: 16) {
                Text("CREATING PDF")
                    .font(.system(.headline, design: .monospaced))
                    .foregroundStyle(.white)

                Text(exportProgress?.currentStep.uppercased() ?? "PREPARING...")
                    .font(.system(.caption, design: .monospaced))
                    .foregroundStyle(.white.opacity(0.7))

                if let progress = exportProgress {
                    Text("\(Int(progress.percentComplete * 100))%")
                        .font(.system(.title, design: .monospaced).bold())
                        .foregroundStyle(Theme.warmOrange)
                }
            }
            .padding(32)
            .border(Color.white.opacity(0.3), width: 2)
        }
    }

    // MARK: - Computed Properties (same as Classic)

    private var stopCoordinates: [(name: String, coordinate: CLLocationCoordinate2D)] {
        trip.stops.compactMap { stop -> (String, CLLocationCoordinate2D)? in
            if let coord = stop.coordinate { return (stop.city, coord) }
            if let stadiumId = stop.stadium,
               let stadium = dataProvider.stadium(for: stadiumId) {
                return (stadium.name, stadium.coordinate)
            }
            return nil
        }
    }

    private var itinerarySections: [ItinerarySection] {
        // Same logic as Classic variant
        var sections: [ItinerarySection] = []
        var dayCitySections: [(dayNumber: Int, date: Date, city: String, games: [RichGame])] = []
        let days = tripDays

        for (index, dayDate) in days.enumerated() {
            let dayNum = index + 1
            let gamesOnDay = gamesOn(date: dayDate)
            guard !gamesOnDay.isEmpty else { continue }

            var gamesByCity: [(city: String, games: [RichGame])] = []
            for game in gamesOnDay {
                let city = game.stadium.city
                if let lastIndex = gamesByCity.indices.last, gamesByCity[lastIndex].city == city {
                    gamesByCity[lastIndex].games.append(game)
                } else {
                    gamesByCity.append((city, [game]))
                }
            }

            for cityGroup in gamesByCity {
                dayCitySections.append((dayNum, dayDate, cityGroup.city, cityGroup.games))
            }
        }

        for (index, section) in dayCitySections.enumerated() {
            if index > 0 {
                let prevCity = dayCitySections[index - 1].city
                let currentCity = section.city
                if !prevCity.isEmpty && !currentCity.isEmpty && prevCity != currentCity {
                    if let travelSegment = findTravelSegment(from: prevCity, to: currentCity) {
                        sections.append(.travel(travelSegment))
                    }
                }
            }
            sections.append(.day(dayNumber: section.dayNumber, date: section.date, games: section.games))
        }

        return sections
    }

    private var tripDays: [Date] {
        let calendar = Calendar.current
        guard let startDate = trip.stops.first?.arrivalDate,
              let endDate = trip.stops.last?.departureDate else { return [] }

        var days: [Date] = []
        var current = calendar.startOfDay(for: startDate)
        let end = calendar.startOfDay(for: endDate)

        while current <= end {
            days.append(current)
            current = calendar.date(byAdding: .day, value: 1, to: current)!
        }
        return days
    }

    private func gamesOn(date: Date) -> [RichGame] {
        let calendar = Calendar.current
        let dayStart = calendar.startOfDay(for: date)
        let allGameIds = trip.stops.flatMap { $0.games }
        let foundGames = allGameIds.compactMap { games[$0] }

        return foundGames.filter { richGame in
            calendar.startOfDay(for: richGame.game.dateTime) == dayStart
        }.sorted { $0.game.dateTime < $1.game.dateTime }
    }

    private func findTravelSegment(from fromCity: String, to toCity: String) -> TravelSegment? {
        let fromLower = fromCity.lowercased().trimmingCharacters(in: .whitespaces)
        let toLower = toCity.lowercased().trimmingCharacters(in: .whitespaces)

        return trip.travelSegments.first { segment in
            let segmentFrom = segment.fromLocation.name.lowercased().trimmingCharacters(in: .whitespaces)
            let segmentTo = segment.toLocation.name.lowercased().trimmingCharacters(in: .whitespaces)
            return segmentFrom == fromLower && segmentTo == toLower
        }
    }

    // MARK: - Actions (same as Classic)

    private func loadGamesIfNeeded() async {
        guard providedGames == nil else { return }
        let gameIds = trip.stops.flatMap { $0.games }
        guard !gameIds.isEmpty else { return }

        isLoadingGames = true
        var loaded: [String: RichGame] = [:]
        for gameId in gameIds {
            if let game = try? await dataProvider.fetchGame(by: gameId),
               let richGame = dataProvider.richGame(from: game) {
                loaded[gameId] = richGame
            }
        }
        loadedGames = loaded
        isLoadingGames = false
    }

    private func exportPDF() async {
        isExporting = true
        exportProgress = nil

        do {
            let url = try await exportService.exportToPDF(trip: trip, games: games) { progress in
                await MainActor.run { self.exportProgress = progress }
            }
            exportURL = url
            showExportSheet = true
        } catch { }

        isExporting = false
    }

    private func toggleSaved() {
        if isSaved { unsaveTrip() } else { saveTrip() }
    }

    private func saveTrip() {
        if !StoreManager.shared.isPro && savedTrips.count >= StoreManager.freeTripLimit {
            showProPaywall = true
            return
        }

        guard let savedTrip = SavedTrip.from(trip, games: games, status: .planned) else { return }
        modelContext.insert(savedTrip)

        if let _ = try? modelContext.save() {
            withAnimation(.spring(response: 0.3, dampingFraction: 0.6)) {
                isSaved = true
            }
        }
    }

    private func unsaveTrip() {
        let tripId = trip.id
        let descriptor = FetchDescriptor<SavedTrip>(predicate: #Predicate { $0.id == tripId })

        if let savedTrips = try? modelContext.fetch(descriptor) {
            for savedTrip in savedTrips { modelContext.delete(savedTrip) }
            try? modelContext.save()
            withAnimation(.spring(response: 0.3, dampingFraction: 0.6)) {
                isSaved = false
            }
        }
    }

    private func checkIfSaved() {
        let tripId = trip.id
        let descriptor = FetchDescriptor<SavedTrip>(predicate: #Predicate { $0.id == tripId })
        if let count = try? modelContext.fetchCount(descriptor), count > 0 {
            isSaved = true
        }
    }

    private func updateMapRegion() {
        guard !stopCoordinates.isEmpty else { return }

        let coordinates = stopCoordinates.map(\.coordinate)
        let lats = coordinates.map(\.latitude)
        let lons = coordinates.map(\.longitude)

        guard let minLat = lats.min(), let maxLat = lats.max(),
              let minLon = lons.min(), let maxLon = lons.max() else { return }

        let center = CLLocationCoordinate2D(
            latitude: (minLat + maxLat) / 2,
            longitude: (minLon + maxLon) / 2
        )

        let latSpan = (maxLat - minLat) * 1.3 + 0.5
        let lonSpan = (maxLon - minLon) * 1.3 + 0.5

        mapCameraPosition = .region(MKCoordinateRegion(
            center: center,
            span: MKCoordinateSpan(latitudeDelta: max(latSpan, 1), longitudeDelta: max(lonSpan, 1))
        ))
    }

    private func fetchDrivingRoutes() async {
        let stops = stopCoordinates
        guard stops.count >= 2 else { return }

        isLoadingRoutes = true
        var polylines: [MKPolyline] = []

        for i in 0..<(stops.count - 1) {
            let source = stops[i]
            let destination = stops[i + 1]

            let request = MKDirections.Request()
            let sourceLocation = CLLocation(latitude: source.coordinate.latitude, longitude: source.coordinate.longitude)
            let destLocation = CLLocation(latitude: destination.coordinate.latitude, longitude: destination.coordinate.longitude)
            request.source = MKMapItem(location: sourceLocation, address: nil)
            request.destination = MKMapItem(location: destLocation, address: nil)
            request.transportType = .automobile

            let directions = MKDirections(request: request)

            do {
                let response = try await directions.calculate()
                if let route = response.routes.first {
                    polylines.append(route.polyline)
                }
            } catch {
                let straightLine = MKPolyline(coordinates: [source.coordinate, destination.coordinate], count: 2)
                polylines.append(straightLine)
            }
        }

        routePolylines = polylines
        isLoadingRoutes = false
    }
}

// MARK: - Day Section (Brutalist)

struct DaySection_Brutalist: View {
    let dayNumber: Int
    let date: Date
    let games: [RichGame]
    @Environment(\.colorScheme) private var colorScheme

    private var textColor: Color {
        colorScheme == .dark ? .white : .black
    }

    private var formattedDate: String {
        date.formatted(.dateTime.weekday(.wide).month().day()).uppercased()
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            // Day header
            HStack {
                Text("DAY \(dayNumber)")
                    .font(.system(.title2, design: .monospaced).bold())
                    .foregroundStyle(textColor)

                Spacer()

                Text("\(games.count) GAME\(games.count > 1 ? "S" : "")")
                    .font(.system(.caption, design: .monospaced))
                    .foregroundStyle(Theme.warmOrange)
            }

            Text(formattedDate)
                .font(.system(.caption, design: .monospaced))
                .foregroundStyle(textColor.opacity(0.5))
                .tracking(1)

            // City
            if let city = games.first?.stadium.city {
                HStack(spacing: 4) {
                    Image(systemName: "mappin")
                        .font(.caption2)
                    Text(city.uppercased())
                        .font(.system(.caption, design: .monospaced))
                }
                .foregroundStyle(textColor.opacity(0.6))
            }

            // Games
            ForEach(games, id: \.game.id) { richGame in
                GameRow_Brutalist(game: richGame)
            }
        }
        .padding(16)
        .border(textColor.opacity(0.2), width: 1)
    }
}

// MARK: - Game Row (Brutalist)

struct GameRow_Brutalist: View {
    let game: RichGame
    @Environment(\.colorScheme) private var colorScheme

    private var textColor: Color {
        colorScheme == .dark ? .white : .black
    }

    var body: some View {
        HStack(spacing: 12) {
            // Sport indicator - vertical bar
            Rectangle()
                .fill(game.game.sport.themeColor)
                .frame(width: 4)

            VStack(alignment: .leading, spacing: 4) {
                // Matchup
                HStack(spacing: 8) {
                    Text(game.awayTeam.abbreviation)
                        .font(.system(.body, design: .monospaced).bold())
                    Text("@")
                        .font(.system(.caption, design: .monospaced))
                        .foregroundStyle(textColor.opacity(0.5))
                    Text(game.homeTeam.abbreviation)
                        .font(.system(.body, design: .monospaced).bold())
                }
                .foregroundStyle(textColor)

                // Stadium
                Text(game.stadium.name.uppercased())
                    .font(.system(.caption2, design: .monospaced))
                    .foregroundStyle(textColor.opacity(0.5))
            }

            Spacer()

            // Time
            Text(game.localGameTimeShort)
                .font(.system(.subheadline, design: .monospaced).bold())
                .foregroundStyle(Theme.warmOrange)
        }
        .padding(12)
        .background(textColor.opacity(0.03))
        .border(textColor.opacity(0.1), width: 1)
    }
}

// MARK: - Travel Section (Brutalist)

struct TravelSection_Brutalist: View {
    let segment: TravelSegment
    @Environment(\.colorScheme) private var colorScheme

    private var textColor: Color {
        colorScheme == .dark ? .white : .black
    }

    var body: some View {
        VStack(spacing: 0) {
            // Connector
            Rectangle()
                .fill(Theme.routeGold)
                .frame(width: 2, height: 20)

            // Travel card
            HStack(spacing: 12) {
                Image(systemName: "car.fill")
                    .foregroundStyle(Theme.routeGold)

                VStack(alignment: .leading, spacing: 2) {
                    Text("[ TRAVEL ]")
                        .font(.system(.caption2, design: .monospaced))
                        .foregroundStyle(textColor.opacity(0.4))

                    Text("\(segment.fromLocation.name.uppercased()) → \(segment.toLocation.name.uppercased())")
                        .font(.system(.caption, design: .monospaced))
                        .foregroundStyle(textColor)
                }

                Spacer()

                VStack(alignment: .trailing, spacing: 2) {
                    Text(segment.formattedDistance.uppercased())
                        .font(.system(.caption, design: .monospaced).bold())
                    Text(segment.formattedDuration.uppercased())
                        .font(.system(.caption2, design: .monospaced))
                        .foregroundStyle(textColor.opacity(0.5))
                }
                .foregroundStyle(textColor)
            }
            .padding(12)
            .border(Theme.routeGold.opacity(0.3), width: 1)

            // Connector
            Rectangle()
                .fill(Theme.routeGold)
                .frame(width: 2, height: 20)
        }
    }
}
```

### Step 5: Update original TripDetailView to be a typealias

Replace `TripDetailView.swift` with a simple redirect:

```swift
//
//  TripDetailView.swift
//  SportsTime
//
//  Redirects to AdaptiveTripDetailView for backward compatibility.
//

import SwiftUI

typealias TripDetailView = AdaptiveTripDetailView
```

### Step 6: Verify the build

Run: `xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' build 2>&1 | tail -30`
Expected: BUILD SUCCEEDED

### Step 7: Commit

```bash
git add SportsTime/Features/Trip/Views/
git commit -m "$(cat <<'EOF'
feat(trip): add brutalist variant for TripDetailView

- Create AdaptiveTripDetailView router
- Add TripDetailView_Classic (extracted from original)
- Add TripDetailView_Brutalist with monospace fonts, borders, perforated dividers
- Update TripDetailView as typealias for backward compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
EOF
)"
```

---

## Task 3: Create SavedTripsListView Variants

**Files:**
- Create: `SportsTime/Features/Home/Views/Variants/Classic/SavedTripsListView_Classic.swift`
- Create: `SportsTime/Features/Home/Views/Variants/Brutalist/SavedTripsListView_Brutalist.swift`
- Create: `SportsTime/Features/Home/Views/AdaptiveSavedTripsListView.swift`
- Modify: `SportsTime/Features/Home/Views/HomeView.swift` (extract SavedTripsListView, update to use Adaptive)

### Step 1: Create directory structure

```bash
mkdir -p SportsTime/Features/Home/Views/Variants/Classic
mkdir -p SportsTime/Features/Home/Views/Variants/Brutalist
```

### Step 2: Extract SavedTripsListView to Classic variant

Move `SavedTripsListView` (lines 406-581 from HomeView.swift) to `Variants/Classic/SavedTripsListView_Classic.swift`, renaming to `SavedTripsListView_Classic`.

### Step 3: Create SavedTripsListView_Brutalist

Create `SportsTime/Features/Home/Views/Variants/Brutalist/SavedTripsListView_Brutalist.swift` with brutalist styling.

### Step 4: Create AdaptiveSavedTripsListView router

### Step 5: Update HomeView.swift

Replace inline `SavedTripsListView` with `AdaptiveSavedTripsListView`.

### Step 6: Verify build and commit

---

## Task 4: Create ScheduleListView Variants

**Files:**
- Create: `SportsTime/Features/Schedule/Views/Variants/Classic/ScheduleListView_Classic.swift`
- Create: `SportsTime/Features/Schedule/Views/Variants/Brutalist/ScheduleListView_Brutalist.swift`
- Create: `SportsTime/Features/Schedule/Views/AdaptiveScheduleListView.swift`
- Modify: `SportsTime/Features/Schedule/Views/ScheduleListView.swift` (typealias)
- Modify: `SportsTime/Features/Home/Views/HomeView.swift` (update Schedule tab)

### Step 1: Create directory structure

```bash
mkdir -p SportsTime/Features/Schedule/Views/Variants/Classic
mkdir -p SportsTime/Features/Schedule/Views/Variants/Brutalist
```

### Step 2-6: Same pattern as TripDetailView

---

## Task 5: Create SettingsView Variants

**Files:**
- Create: `SportsTime/Features/Settings/Views/Variants/Classic/SettingsView_Classic.swift`
- Create: `SportsTime/Features/Settings/Views/Variants/Brutalist/SettingsView_Brutalist.swift`
- Create: `SportsTime/Features/Settings/Views/AdaptiveSettingsView.swift`
- Modify: `SportsTime/Features/Settings/Views/SettingsView.swift` (typealias)
- Modify: `SportsTime/Features/Home/Views/HomeView.swift` (update Settings tab)

### Step 1: Create directory structure

```bash
mkdir -p SportsTime/Features/Settings/Views/Variants/Classic
mkdir -p SportsTime/Features/Settings/Views/Variants/Brutalist
```

### Step 2-6: Same pattern as TripDetailView

---

## Task 6: Run Tests and Fix Issues

**Step 1: Run full test suite**

Run: `xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' test`
Expected: All tests pass

**Step 2: Fix any failing tests**

Update test files that reference view types directly.

**Step 3: Commit fixes**

---

## Task 7: Final Verification and Documentation Update

**Step 1: Test all design styles in simulator**

1. Build and run app
2. Go to Settings → Home Screen Style
3. Select "Classic" - verify all screens look correct
4. Select "Brutalist" - verify all screens have brutalist styling

**Step 2: Update CLAUDE.md if needed**

Add note about adaptive view pattern if helpful for future development.

**Step 3: Final commit**

```bash
git add -A
git commit -m "$(cat <<'EOF'
docs: complete brutalist app-wide implementation

All main screens now support Classic and Brutalist design variants:
- TripDetailView
- SavedTripsListView
- ScheduleListView
- SettingsView

Other 22 design styles fall back to Classic for non-home screens.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
EOF
)"
```
