import SwiftUI struct StatCard: View { let title: String let value: Int let color: Color var body: some View { VStack(alignment: .leading, spacing: 4) { Text(title).font(.caption).foregroundStyle(Sage.muted) Text("\(value)").font(.title.weight(.bold)).foregroundStyle(color) } .frame(maxWidth: .infinity, alignment: .leading) .padding(14) .background(Sage.card, in: RoundedRectangle(cornerRadius: 12)) .overlay(RoundedRectangle(cornerRadius: 12).stroke(Sage.line)) } } struct DomainBar: View { let name: String let count: Int let max: Int var body: some View { HStack(spacing: 10) { Text(name).font(.caption).foregroundStyle(Sage.ink) .frame(width: 120, alignment: .leading).lineLimit(1) GeometryReader { geo in ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: 4).fill(Sage.surface) RoundedRectangle(cornerRadius: 4) .fill(Sage.domainColor(name)) .frame(width: geo.size.width * fraction) } } .frame(height: 10) Text("\(count)").font(.caption.monospacedDigit()).foregroundStyle(Sage.muted) .frame(width: 44, alignment: .trailing) } } private var fraction: CGFloat { max > 0 ? CGFloat(count) / CGFloat(max) : 0 } } struct ScoreBar: View { let score: Double var body: some View { HStack(spacing: 6) { GeometryReader { geo in ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: 3).fill(Sage.surface) RoundedRectangle(cornerRadius: 3).fill(Sage.brand) .frame(width: geo.size.width * CGFloat(min(max(score, 0), 1))) } } .frame(height: 6) Text(String(format: "%.2f", score)).font(.caption2.monospacedDigit()).foregroundStyle(Sage.muted) } } } struct ErrorBanner: View { let text: String var body: some View { Text(text) .font(.callout) .foregroundStyle(Sage.danger) .frame(maxWidth: .infinity, alignment: .leading) .padding(12) .background(Sage.danger.opacity(0.08), in: RoundedRectangle(cornerRadius: 10)) .overlay(RoundedRectangle(cornerRadius: 10).stroke(Sage.danger.opacity(0.3))) } }