## Reusable HapticManager

- Use case: One shared object that prepares generators once and exposes every standard haptic as a single call.
- Feel: N/A — infrastructure. Guarantees minimal latency by keeping the Taptic Engine prepared.
- APIs: UIImpactFeedbackGenerator, UISelectionFeedbackGenerator, UINotificationFeedbackGenerator
- Minimum OS: iOS 13+

### Swift (UIKit + SwiftUI compatible)

```swift
import UIKit

/// Central access point for standard haptics.
/// Call `HapticManager.shared.prepare()` shortly before an interaction
/// (e.g. when a screen appears) so the Taptic Engine is spun up.
final class HapticManager {
    static let shared = HapticManager()

    private let lightImpact  = UIImpactFeedbackGenerator(style: .light)
    private let mediumImpact = UIImpactFeedbackGenerator(style: .medium)
    private let heavyImpact  = UIImpactFeedbackGenerator(style: .heavy)
    private let softImpact   = UIImpactFeedbackGenerator(style: .soft)
    private let rigidImpact  = UIImpactFeedbackGenerator(style: .rigid)
    private let selection    = UISelectionFeedbackGenerator()
    private let notification = UINotificationFeedbackGenerator()

    private init() {}

    /// Wakes the Taptic Engine to remove first-hit latency.
    func prepare() {
        lightImpact.prepare()
        selection.prepare()
        notification.prepare()
    }

    // MARK: Impacts (physical collisions: taps, snaps, drops)
    func tapLight(intensity: CGFloat = 1.0)  { lightImpact.impactOccurred(intensity: intensity) }
    func tapMedium(intensity: CGFloat = 1.0) { mediumImpact.impactOccurred(intensity: intensity) }
    func tapHeavy(intensity: CGFloat = 1.0)  { heavyImpact.impactOccurred(intensity: intensity) }
    func tapSoft()  { softImpact.impactOccurred() }   // rounded, muffled
    func tapRigid() { rigidImpact.impactOccurred() }  // sharp, precise

    // MARK: Selection (ticks while values change)
    func selectionChanged() { selection.selectionChanged() }

    // MARK: Notifications (outcomes)
    func success() { notification.notificationOccurred(.success) }
    func warning() { notification.notificationOccurred(.warning) }
    func error()   { notification.notificationOccurred(.error) }
}

// Usage anywhere in the app:
// HapticManager.shared.tapLight()
// HapticManager.shared.success()
```

### Notes
- Generators are cheap to keep alive; keeping one instance per style avoids re-allocations.
- prepare() keeps the engine ready for ~1–2 seconds. Call it on touch-down or on screen appear, then fire on touch-up.
- Haptics are silently ignored on devices without a Taptic Engine (iPads, old iPhones) — no capability check needed for UIFeedbackGenerator.
