import SwiftUI struct ApprovalCardModifier: ViewModifier { var highlighted = false func body(content: Content) -> some View { content .padding(14) .background( RoundedRectangle(cornerRadius: IdP.cardRadius, style: .continuous) .fill(Color.idpCard) ) .overlay( RoundedRectangle(cornerRadius: IdP.cardRadius, style: .continuous) .stroke(highlighted ? IdP.tint : Color.idpBorder, lineWidth: 1) ) .background( highlighted ? RoundedRectangle(cornerRadius: IdP.cardRadius + 3, style: .continuous) .fill(IdP.tint.opacity(0.10)) .padding(-3) : nil ) } } extension View { func approvalCard(highlighted: Bool = false) -> some View { modifier(ApprovalCardModifier(highlighted: highlighted)) } func deviceRowStyle() -> some View { modifier(DeviceRowStyle()) } } struct RequestHeroCard: View { let request: ApprovalRequest let handle: String var body: some View { VStack(spacing: 12) { MonogramAvatar( title: request.source, size: 52, tint: BrandTint.color(for: request.source), filled: true ) VStack(spacing: 4) { Text("\(request.source) wants to sign in") .font(.title3.weight(.bold)) .multilineTextAlignment(.center) .fixedSize(horizontal: false, vertical: true) HStack(spacing: 4) { Text("requesting") Text(handle) .foregroundStyle(IdP.tint) .fontWeight(.semibold) Text("ยท") Text(request.createdAt, style: .relative) } .font(.footnote) .foregroundStyle(Color.idpMutedForeground) } } .frame(maxWidth: .infinity) .padding(.vertical, 6) .approvalCard(highlighted: false) } } struct MonogramAvatar: View { let title: String var size: CGFloat = 40 var tint: Color = IdP.tint /// Shadcn-style filled tile (brand color bg, white text). /// When false, renders the legacy tinted-pastel avatar. var filled: Bool = true private var monogram: String { let letters = title .replacingOccurrences(of: "auth.", with: "") .split { !$0.isLetter && !$0.isNumber } .prefix(2) .compactMap { $0.first } let glyph = String(letters.map(Character.init)) return glyph.isEmpty ? "I" : glyph.uppercased() } var body: some View { ZStack { RoundedRectangle(cornerRadius: 8, style: .continuous) .fill(filled ? tint : tint.opacity(0.14)) Text(monogram) .font(.system(size: size * (monogram.count > 1 ? 0.36 : 0.44), weight: .bold, design: .default)) .foregroundStyle(filled ? .white : tint) .tracking(-0.3) } .frame(width: size, height: size) .accessibilityHidden(true) } } enum BrandTint { static func color(for host: String) -> Color { let key = host.lowercased() if key.contains("github") { return Color(red: 0.14, green: 0.16, blue: 0.18) } if key.contains("lufthansa") { return Color(red: 0.02, green: 0.09, blue: 0.30) } if key.contains("hetzner") { return Color(red: 0.84, green: 0.05, blue: 0.18) } if key.contains("notion") { return Color(red: 0.12, green: 0.12, blue: 0.12) } if key.contains("apple") { return Color(red: 0.18, green: 0.18, blue: 0.22) } if key.contains("reddit") { return Color(red: 1.00, green: 0.27, blue: 0.00) } if key.contains("cli") { return Color(red: 0.24, green: 0.26, blue: 0.32) } if key.contains("workspace") { return Color(red: 0.26, green: 0.38, blue: 0.88) } if key.contains("foss") || key.contains("berlin-mbp") { return Color(red: 0.12, green: 0.45, blue: 0.70) } return IdP.tint } } struct DeviceRowStyle: ViewModifier { func body(content: Content) -> some View { content .padding(.vertical, 2) } }