import SwiftUI /// Wiederverwendbare Karten-Hülle in drei Größen — entspricht den /// Web-`CardSurface.svelte`-Varianten. Konsistenter Border-Radius (14pt), /// gleicher Border-Stil, gleiche Shadow-Behandlung über alle Größen, /// optional ein linker Color-Accent-Streifen. /// /// Spec aus `cards/apps/web/src/lib/components/CardSurface.svelte`: /// - Alle Größen Border-Radius 0.875rem (14pt) /// - Border 1px hsl(--color-border) /// - Background hsl(--color-surface) /// - Aspect-Ratio 5/7 für `.md` und `.hero`, fix für `.lg` struct CardSurface: View { enum Size: Sendable { case md // Deck-Tile in der Liste (max-width 18rem) case lg // Fan-Detail (12rem x 16.8rem) case hero // Study-Lernkarte (max-width 24rem) } enum Elevation: Sendable { case flat // Subtle shadow case standard // Default Karten-Shadow case raised // Study-Hero } let size: Size let elevation: Elevation let colorAccentHex: String? let content: () -> Content init( size: Size = .md, elevation: Elevation = .standard, colorAccentHex: String? = nil, @ViewBuilder content: @escaping () -> Content ) { self.size = size self.elevation = elevation self.colorAccentHex = colorAccentHex self.content = content } var body: some View { ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: 14, style: .continuous) .fill(CardsTheme.surface) .overlay( RoundedRectangle(cornerRadius: 14, style: .continuous) .stroke(CardsTheme.border, lineWidth: 1) ) if let colorAccentHex { Color.swatchFromHex(colorAccentHex) .frame(width: 6) .clipShape( UnevenRoundedRectangle( topLeadingRadius: 14, bottomLeadingRadius: 14, bottomTrailingRadius: 0, topTrailingRadius: 0, style: .continuous ) ) } content() .padding(EdgeInsets(top: 16, leading: 22, bottom: 18, trailing: 16)) } .frame(maxWidth: maxWidth) .aspectRatio(aspectRatio, contentMode: .fit) .shadow(color: shadowColor, radius: shadowRadius, x: 0, y: shadowY) } private var maxWidth: CGFloat? { switch size { case .md: 288 // 18rem case .lg: 192 // 12rem case .hero: 384 // 24rem } } private var aspectRatio: CGFloat? { switch size { case .md, .hero: 5.0 / 7.0 case .lg: 12.0 / 16.8 } } private var shadowColor: Color { CardsTheme.foreground.opacity(elevation == .raised ? 0.18 : 0.08) } private var shadowRadius: CGFloat { switch elevation { case .flat: 3 case .standard: 8 case .raised: 18 } } private var shadowY: CGFloat { switch elevation { case .flat: 1 case .standard: 4 case .raised: 12 } } }