import SwiftUI struct GameCardView: View { let game: Game let onSelect: () -> Void @Environment(GamesViewModel.self) private var viewModel private var inMultiView: Bool { game.broadcasts.contains(where: { bc in viewModel.activeStreams.contains(where: { $0.id == bc.id }) }) } private var awayColor: Color { TeamAssets.color(for: game.awayTeam.code) } private var homeColor: Color { TeamAssets.color(for: game.homeTeam.code) } var body: some View { Button(action: onSelect) { VStack(spacing: 0) { // Team color accent bar HStack(spacing: 0) { Rectangle().fill(awayColor) Rectangle().fill(homeColor) } .frame(height: 4) VStack(spacing: rowGap) { teamRow(team: game.awayTeam, isWinning: isWinning(away: true)) teamRow(team: game.homeTeam, isWinning: isWinning(away: false)) } .padding(.horizontal, cardPadH) .padding(.top, cardPadV) Spacer(minLength: 6) // Footer: status + linescore HStack { statusPill Spacer() if let linescore = game.linescore, !game.status.isScheduled { MiniLinescoreView( linescore: linescore, awayCode: game.awayTeam.code, homeCode: game.homeTeam.code ) } } .padding(.horizontal, cardPadH) .padding(.bottom, cardPadV) } .frame(maxWidth: .infinity, minHeight: cardHeight, alignment: .topLeading) .background(DS.Colors.panelFill) .clipShape(RoundedRectangle(cornerRadius: cardRadius, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: cardRadius, style: .continuous) .strokeBorder(borderColor, lineWidth: borderWidth) ) .shadow(color: DS.Shadows.card, radius: DS.Shadows.cardRadius, y: DS.Shadows.cardY) } .platformCardStyle() } @ViewBuilder private func teamRow(team: TeamInfo, isWinning: Bool) -> some View { HStack(spacing: teamSpacing) { TeamLogoView(team: team, size: logoSize) Text(team.code) .font(codeFont) .foregroundStyle(DS.Colors.textPrimary) .frame(width: codeWidth, alignment: .leading) Text(team.displayName) .font(nameFont) .foregroundStyle(DS.Colors.textSecondary) .lineLimit(1) Spacer(minLength: 4) if let record = team.record { Text(record) .font(recordFont) .foregroundStyle(DS.Colors.textTertiary) } if let streak = team.streak { Text(streak) .font(recordFont) .foregroundStyle(streak.hasPrefix("W") ? DS.Colors.positive : DS.Colors.live) } if !game.status.isScheduled, let score = team.score { Text("\(score)") .font(scoreFont) .foregroundStyle(isWinning ? DS.Colors.textPrimary : DS.Colors.textTertiary) .frame(width: scoreWidth, alignment: .trailing) } } } @ViewBuilder private var statusPill: some View { switch game.status { case .live(let inning): HStack(spacing: 6) { Circle().fill(DS.Colors.live).frame(width: 8, height: 8) Text(inning ?? "LIVE") .font(statusFont) .foregroundStyle(DS.Colors.live) } case .scheduled(let time): Text(time) .font(statusFont) .foregroundStyle(DS.Colors.textSecondary) case .final_: Text("FINAL") .font(statusFont) .foregroundStyle(DS.Colors.textTertiary) case .unknown: EmptyView() } } private func isWinning(away: Bool) -> Bool { guard let a = game.awayTeam.score, let h = game.homeTeam.score else { return false } return away ? a > h : h > a } private var borderColor: Color { if inMultiView { return DS.Colors.positive.opacity(0.5) } if game.isLive { return DS.Colors.live.opacity(0.3) } return DS.Colors.panelStroke } private var borderWidth: CGFloat { inMultiView || game.isLive ? 2 : 0.5 } #if os(tvOS) private var cardHeight: CGFloat { 200 } private var cardRadius: CGFloat { 22 } private var cardPadH: CGFloat { 22 } private var cardPadV: CGFloat { 16 } private var rowGap: CGFloat { 10 } private var logoSize: CGFloat { 44 } private var teamSpacing: CGFloat { 14 } private var codeWidth: CGFloat { 60 } private var scoreWidth: CGFloat { 40 } private var codeFont: Font { .system(size: 26, weight: .black, design: .rounded) } private var nameFont: Font { .system(size: 22, weight: .semibold) } private var scoreFont: Font { .system(size: 30, weight: .black, design: .rounded).monospacedDigit() } private var recordFont: Font { .system(size: 20, weight: .bold, design: .monospaced) } private var statusFont: Font { .system(size: 22, weight: .bold, design: .rounded) } #else private var cardHeight: CGFloat { 150 } private var cardRadius: CGFloat { 18 } private var cardPadH: CGFloat { 16 } private var cardPadV: CGFloat { 12 } private var rowGap: CGFloat { 8 } private var logoSize: CGFloat { 32 } private var teamSpacing: CGFloat { 10 } private var codeWidth: CGFloat { 44 } private var scoreWidth: CGFloat { 30 } private var codeFont: Font { .system(size: 18, weight: .black, design: .rounded) } private var nameFont: Font { .system(size: 14, weight: .semibold) } private var scoreFont: Font { .system(size: 22, weight: .black, design: .rounded).monospacedDigit() } private var recordFont: Font { .system(size: 13, weight: .bold, design: .monospaced) } private var statusFont: Font { .system(size: 14, weight: .bold, design: .rounded) } #endif }