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) { HStack(spacing: 0) { // Team color accent bar HStack(spacing: 0) { Rectangle().fill(awayColor) Rectangle().fill(homeColor) } .frame(width: 4) .clipShape(RoundedRectangle(cornerRadius: 2)) .padding(.vertical, 10) HStack(spacing: teamBlockSpacing) { // Away team teamBlock(team: game.awayTeam, isWinning: isWinning(away: true)) // Status / Score center VStack(spacing: 4) { if !game.status.isScheduled, let away = game.awayTeam.score, let home = game.homeTeam.score { Text("\(away) - \(home)") .font(scoreFont) .foregroundStyle(.white) .monospacedDigit() .contentTransition(.numericText()) } statusPill } .frame(width: scoreCenterWidth) // Home team teamBlock(team: game.homeTeam, isWinning: isWinning(away: false), trailing: true) } .padding(.horizontal, blockPadH) // Mini linescore (if available) if let linescore = game.linescore, !game.status.isScheduled { Divider() .frame(height: dividerHeight) .background(.white.opacity(0.08)) MiniLinescoreView( linescore: linescore, awayCode: game.awayTeam.code, homeCode: game.homeTeam.code ) .padding(.horizontal, miniLSPad) } } .frame(maxWidth: .infinity, minHeight: cardHeight, alignment: .leading) .padding(.vertical, cardPadV) .background(cardBackground) .clipShape(RoundedRectangle(cornerRadius: cardRadius, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: cardRadius, style: .continuous) .strokeBorder(borderColor, lineWidth: borderWidth) ) } .platformCardStyle() } // MARK: - Team Block @ViewBuilder private func teamBlock(team: TeamInfo, isWinning: Bool, trailing: Bool = false) -> some View { HStack(spacing: teamInnerSpacing) { if trailing { Spacer(minLength: 0) } TeamLogoView(team: team, size: logoSize) VStack(alignment: trailing ? .trailing : .leading, spacing: 2) { Text(team.code) .font(codeFont) .foregroundStyle(.white) if let record = team.record { HStack(spacing: 4) { Text(record) .font(recordFont) .foregroundStyle(.white.opacity(0.5)) if let streak = team.streak { Text(streak) .font(recordFont) .foregroundStyle(streak.hasPrefix("W") ? DS.Colors.positive : DS.Colors.live) } } } } if !trailing { Spacer(minLength: 0) } } .frame(maxWidth: .infinity) } // MARK: - Status @ViewBuilder private var statusPill: some View { switch game.status { case .live(let inning): HStack(spacing: 5) { Circle().fill(DS.Colors.live).frame(width: 6, height: 6) Text(inning ?? "LIVE") .font(statusFont) .foregroundStyle(.white) } .padding(.horizontal, 10) .padding(.vertical, 5) .background(DS.Colors.live.opacity(0.18)) .clipShape(Capsule()) case .scheduled(let time): Text(time) .font(statusFont) .foregroundStyle(.white.opacity(0.8)) case .final_: Text("FINAL") .font(statusFont) .foregroundStyle(.white.opacity(0.7)) case .unknown: EmptyView() } } // MARK: - Helpers 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 cardBackground: some ShapeStyle { Color(red: 0.06, green: 0.07, blue: 0.10) } private var borderColor: Color { if inMultiView { return .green.opacity(0.35) } if game.isLive { return DS.Colors.live.opacity(0.3) } return .white.opacity(0.06) } private var borderWidth: CGFloat { inMultiView || game.isLive ? 2 : 1 } // MARK: - Platform Sizing #if os(tvOS) private var cardHeight: CGFloat { 120 } private var cardPadV: CGFloat { 8 } private var cardRadius: CGFloat { 20 } private var logoSize: CGFloat { 40 } private var teamBlockSpacing: CGFloat { 12 } private var teamInnerSpacing: CGFloat { 12 } private var blockPadH: CGFloat { 18 } private var scoreCenterWidth: CGFloat { 140 } private var dividerHeight: CGFloat { 60 } private var miniLSPad: CGFloat { 16 } private var scoreFont: Font { .system(size: 30, weight: .black, design: .rounded) } private var codeFont: Font { .system(size: 24, weight: .black, design: .rounded) } private var recordFont: Font { .system(size: 18, weight: .bold, design: .monospaced) } private var statusFont: Font { .system(size: 18, weight: .bold, design: .rounded) } #else private var cardHeight: CGFloat { 90 } private var cardPadV: CGFloat { 6 } private var cardRadius: CGFloat { 16 } private var logoSize: CGFloat { 30 } private var teamBlockSpacing: CGFloat { 8 } private var teamInnerSpacing: CGFloat { 8 } private var blockPadH: CGFloat { 12 } private var scoreCenterWidth: CGFloat { 100 } private var dividerHeight: CGFloat { 44 } private var miniLSPad: CGFloat { 10 } private var scoreFont: Font { .system(size: 22, weight: .black, design: .rounded) } private var codeFont: Font { .system(size: 17, weight: .black, design: .rounded) } private var recordFont: Font { .system(size: 12, weight: .bold, design: .monospaced) } private var statusFont: Font { .system(size: 13, weight: .bold, design: .rounded) } #endif }