import SwiftUI struct CardLayout { let availableSize: CGSize let variant: GameVariant let deepestColumn: (faceDown: Int, faceUp: Int) var columnCount: Int { variant.tableauCount } var isLandscape: Bool { availableSize.width > availableSize.height } /// Card size — constrained by the tighter of width or height. var cardWidth: CGFloat { let computed = min(widthConstrainedCardWidth, heightConstrainedCardWidth) #if os(macOS) // Cap card width on macOS so cards don't get absurdly large on big displays return min(computed, 120) #else return computed #endif } var cardHeight: CGFloat { cardWidth * 1.4 } /// Padding between cards. In landscape, this expands to fill the available width /// so cards spread evenly across the screen. var horizontalPadding: CGFloat { let basePadding = max(2, availableSize.width * 0.008) // If height-constrained (landscape), distribute extra width as padding if heightConstrainedCardWidth < widthConstrainedCardWidth { let totalCardWidth = CGFloat(columnCount) * cardWidth let availableForPadding = availableSize.width - totalCardWidth let gaps = CGFloat(columnCount + 1) return max(basePadding, availableForPadding / gaps) } return basePadding } var verticalOverlapFaceDown: CGFloat { cardHeight * 0.15 } var verticalOverlapFaceUp: CGFloat { cardHeight * 0.25 } var cornerRadius: CGFloat { cardWidth * 0.08 } func cardSize() -> CGSize { CGSize(width: cardWidth, height: cardHeight) } var touchTargetPadding: CGFloat { max(0, (44 - cardWidth) / 2) } // MARK: - Private private var basePadding: CGFloat { max(2, availableSize.width * 0.008) } private var widthConstrainedCardWidth: CGFloat { let totalPadding = basePadding * CGFloat(columnCount + 1) return (availableSize.width - totalPadding) / CGFloat(columnCount) } private var heightConstrainedCardWidth: CGFloat { let d = CGFloat(deepestColumn.faceDown) let u = CGFloat(max(0, deepestColumn.faceUp - 1)) let totalFactor = 1.0 + 0.1 + (d * 0.15 + u * 0.25 + 1.0) // Use 92% of available height to leave room for VStack spacing, // bottom padding, and any unmeasured platform chrome let usableHeight = availableSize.height * 0.92 let base = max(30, usableHeight / (1.4 * totalFactor)) #if os(iOS) return isLandscape ? base * 1.3 : base #else return base #endif } }