// ── Countdown Ring ───────────────────────────────────────────── // Visual countdown ring (like Time Timer) — neurodivergent-friendly // Color transitions: green → yellow → orange → red as time decreases import SwiftUI struct CountdownRing: View { let progress: Double // 0.0 (full) → 1.0 (empty) let urgency: UrgencyLevel let remainingSeconds: TimeInterval let totalSeconds: TimeInterval var size: CGFloat = 220 var lineWidth: CGFloat = 12 private var ringColor: Color { // Color transition based on remaining time ratio let ratio = 1.0 - progress // 1.0 = full time, 0.0 = no time if ratio > 0.5 { return CMColors.gentle // green } else if ratio > 0.25 { return CMColors.standard // yellow } else if ratio > 0.1 { return CMColors.important // orange } else { return CMColors.critical // red } } private var glowColor: Color { ringColor.opacity(0.3) } var body: some View { ZStack { // Background track Circle() .stroke(CMColors.border, lineWidth: lineWidth) .frame(width: size, height: size) // Progress arc (fills clockwise from 12 o'clock) Circle() .trim(from: 0, to: 1.0 - progress) .stroke( ringColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round) ) .frame(width: size, height: size) .rotationEffect(.degrees(-90)) .shadow(color: glowColor, radius: 8) .animation(.easeInOut(duration: 0.3), value: progress) // Center content VStack(spacing: CMSpacing.xs) { Text(formatDuration(remainingSeconds)) .font(CMFonts.mono(size: size * 0.18, weight: .bold)) .foregroundStyle(CMColors.text) .contentTransition(.numericText()) if totalSeconds > 0 { Text(formatDurationCompact(totalSeconds)) .font(CMFonts.body(size: size * 0.06)) .foregroundStyle(CMColors.textMuted) } } } } } // MARK: - Mini Countdown Ring (for cards) struct MiniCountdownRing: View { let progress: Double let urgency: UrgencyLevel var size: CGFloat = 32 var lineWidth: CGFloat = 3 private var ringColor: Color { CMColors.urgencyColor(urgency) } var body: some View { ZStack { Circle() .stroke(CMColors.border, lineWidth: lineWidth) .frame(width: size, height: size) Circle() .trim(from: 0, to: 1.0 - progress) .stroke( ringColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round) ) .frame(width: size, height: size) .rotationEffect(.degrees(-90)) .animation(.easeInOut(duration: 0.3), value: progress) } } }