# Loading System Redesign

## Overview

A complete overhaul of all loading views, progress indicators, and spinners to achieve visual consistency, a fresh premium aesthetic, and Apple-level polish.

## Design Decisions

| Decision | Choice |
|----------|--------|
| Visual direction | Minimal + premium (Apple-level polish, no gimmicks) |
| Animation style | Apple-style indeterminate (smooth arcs, gentle opacity) |
| Loading text | Context-aware only (static labels, no clever messages) |
| Colors | Theme-aware using existing `Theme.warmOrange` |
| Skeleton animation | Gentle opacity pulse (no directional shimmer) |

## Component Hierarchy

| Tier | Component | Use Case |
|------|-----------|----------|
| **Inline** | `LoadingSpinner` | Buttons, small areas, inline waits |
| **Content** | `LoadingPlaceholder` | Skeleton shapes replacing content |
| **Blocking** | `LoadingSheet` | Full-screen modal operations |

## Animation Principles

- 1-second cycle duration (spinners), 1.2-second (skeletons)
- Ease-in-out timing curves
- Opacity range: 0.3 → 0.6 (subtle, never harsh)
- No bouncing, no scaling, no playfulness
- All shapes in a skeleton group pulse together (synchronized)

## Text Rules

- Static, context-specific labels only
- Format: "Loading [noun]..." (e.g., "Loading games...", "Loading schedule...")
- No rotating messages, no AI-generated text
- Text optional for spinners under 2 seconds

---

## Component: LoadingSpinner

Replaces `ThemedSpinner`, `ThemedSpinnerCompact`, and all native `ProgressView()` usages.

### API

```swift
LoadingSpinner(size: .medium)  // .small, .medium, .large
LoadingSpinner(size: .small, label: "Loading...")
```

### Sizes

| Size | Diameter | Stroke | Use Case |
|------|----------|--------|----------|
| `.small` | 16pt | 2pt | Inline with text, buttons |
| `.medium` | 24pt | 3pt | Default, standalone |
| `.large` | 40pt | 4pt | Prominent loading states |

### Visual Design

- Single arc (270°) rotating smoothly
- Background track at 15% opacity of accent color
- Foreground arc at 100% accent color (`Theme.warmOrange`)
- 1-second full rotation, linear timing
- Round line caps

### Optional Label

- Appears to the right of spinner (horizontal layout)
- Uses `Theme.textSecondary`
- Font: `.subheadline` for small/medium, `.body` for large

### Replacements

| File | Line | New Code |
|------|------|----------|
| `SportsStep.swift` | 32 | `LoadingSpinner(size: .medium)` |
| `ReviewStep.swift` | 50 | `LoadingSpinner(size: .medium)` |
| `StadiumVisitHistoryView.swift` | 17 | `LoadingSpinner(size: .medium)` |
| `GamesHistoryView.swift` | 17 | `LoadingSpinner(size: .medium, label: "Loading games...")` |
| `StadiumVisitSheet.swift` | 235 | `LoadingSpinner(size: .small)` |

---

## Component: LoadingPlaceholder

Replaces `PlaceholderCard` and shimmer rectangles in `LoadingTripsView`.

### API

```swift
// Basic shapes
LoadingPlaceholder.rectangle(width: 120, height: 16)
LoadingPlaceholder.circle(diameter: 40)
LoadingPlaceholder.capsule(width: 80, height: 24)

// Pre-built composites
LoadingPlaceholder.card       // Trip card skeleton
LoadingPlaceholder.listRow    // Single list row skeleton
```

### Animation

- Gentle opacity pulse: 0.3 → 0.5 → 0.3
- 1.2-second cycle, ease-in-out
- All shapes in a group pulse together (synchronized)
- No directional movement, no shimmer sweep

### Colors

- Light mode: `Theme.textMuted.opacity(0.2)` base
- Dark mode: `Theme.cardBackgroundElevated` base
- Pulse brightens by ~0.15 opacity at peak

### Card Skeleton Layout

```
┌─────────────────────────┐
│ ▓▓▓▓▓▓    ▓▓▓          │  <- header row
│                         │
│ ▓▓▓▓▓▓▓▓▓▓             │  <- title
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓          │  <- subtitle
│                         │
│ ▓▓▓▓▓  ▓▓▓▓▓           │  <- stats row
└─────────────────────────┘
```

---

## Component: LoadingSheet

Replaces `LoadingOverlay` and `PlanningProgressView`.

### API

```swift
LoadingSheet(label: "Planning trip")
LoadingSheet(label: "Exporting PDF", detail: "Generating maps...")
```

### Visual Design

```
┌─────────────────────────────┐
│                             │
│      ╭─────────────────╮    │
│      │                 │    │
│      │    (spinner)    │    │
│      │                 │    │
│      │  Planning trip  │    │
│      │                 │    │
│      ╰─────────────────╯    │
│                             │
└─────────────────────────────┘
       ↑ dimmed background
```

### Specifications

- Background: `Color.black.opacity(0.5)`
- Card: `Theme.cardBackground` with `Theme.CornerRadius.large`
- Spinner: `LoadingSpinner(size: .large)` centered
- Label: `.headline` weight, `Theme.textPrimary`
- Detail (optional): `.subheadline`, `Theme.textSecondary`
- Card padding: `Theme.Spacing.xl` all sides
- Shadow: `radius: 16, y: 8`

### Static Labels by Context

| Context | Label |
|---------|-------|
| Trip planning | "Planning trip" |
| PDF export | "Exporting PDF" |
| Data sync | "Syncing data" |
| Generic | "Loading" |

---

## Migration Plan

### Components to Deprecate

| Current | Action | Replacement |
|---------|--------|-------------|
| `ThemedSpinner` | Remove | `LoadingSpinner` |
| `ThemedSpinnerCompact` | Remove | `LoadingSpinner(size: .small)` |
| `LoadingDots` | Remove | Not replaced |
| `PlaceholderCard` | Remove | `LoadingPlaceholder.card` |
| `PlanningProgressView` | Remove | `LoadingSheet` |
| `LoadingOverlay` | Remove | `LoadingSheet` |
| `LoadingTripsView` | Refactor | Use new skeletons |
| `LoadingTextGenerator` | Remove | Static labels only |

### Components to Keep

| Component | Reason |
|-----------|--------|
| `AnimatedRouteGraphic` | Used elsewhere, not a loading state |
| `PulsingDot` | Used in maps, not loading |
| `RoutePreviewStrip` | Display component, not loading |
| `StatPill` | Display component, not loading |
| `EmptyStateView` | Empty state, not loading |

### New File Structure

```
SportsTime/Core/Theme/
├── Theme.swift                    (existing - no changes)
├── AnimatedComponents.swift       (keep non-loading components)
└── Loading/
    ├── LoadingSpinner.swift       (new)
    ├── LoadingPlaceholder.swift   (new)
    └── LoadingSheet.swift         (new)
```

### Summary

- 3 new files created
- 6 components deprecated/removed
- 5 native `ProgressView()` usages replaced
- `LoadingTextGenerator.swift` deleted
- `LoadingTripsView.swift` refactored to use new components
