import SwiftUI // MARK: - Палитра /// Фирменные цвета Rapira из дизайн-макета. enum RapiraPalette { // Рост static let accent = Color(red: 0.251, green: 0.835, blue: 0.216) // #40D537 static let accentEdge = Color(red: 0.051, green: 0.157, blue: 0.082) // #0D2815 static let accentHalo = Color(red: 0.075, green: 0.214, blue: 0.106) // #13371B static let accentText = Color(red: 0.277, green: 0.895, blue: 0.239) // #47E43D // Падение — зеркальная красная гамма. static let negative = Color(red: 0.949, green: 0.267, blue: 0.267) // #F24444 static let negativeEdge = Color(red: 0.161, green: 0.051, blue: 0.051) static let negativeHalo = Color(red: 0.220, green: 0.075, blue: 0.075) static let negativeText = Color(red: 0.965, green: 0.310, blue: 0.290) // Фон и разделители static let backgroundTint = Color(red: 0.157, green: 0.161, blue: 0.169) // #28292B static let divider = Color.white.opacity(0.06) // MARK: Выбор цвета по направлению движения static func line(_ positive: Bool) -> Color { positive ? accent : negative } static func lineEdge(_ positive: Bool) -> Color { positive ? accentEdge : negativeEdge } static func halo(_ positive: Bool) -> Color { positive ? accentHalo : negativeHalo } static func stat(_ positive: Bool) -> Color { positive ? accentText : negativeText } } // MARK: - Фон /// Радиальный фон из макета: широкая подсветка от верхней кромки, уходящая /// в чёрный на 61%. В исходнике это эллипс с радиусами 995×512 — отсюда /// растяжение по горизонтали. struct RapiraBackground: View { var body: some View { GeometryReader { geo in RadialGradient( gradient: Gradient(stops: [ .init(color: RapiraPalette.backgroundTint, location: 0), .init(color: .black, location: 0.612), ]), center: UnitPoint(x: 0.5, y: 0), startRadius: 0, endRadius: geo.size.height ) .frame(width: geo.size.width, height: geo.size.height) .scaleEffect(x: 1.94, y: 1, anchor: .top) .clipped() } } } // MARK: - Логотип /// Знак Rapira — две «ленты», кривые сняты с фирменного SVG /// (34.7 × 32 в исходных координатах). struct RapiraMark: View { var size: CGFloat = 22 var body: some View { ZStack { MarkShape(part: .upper).fill(.white) MarkShape(part: .lower).fill(.white.opacity(0.6)) } .frame(width: size * (34.736 / 32.0), height: size) } private struct MarkShape: Shape { enum Part { case upper, lower } let part: Part func path(in rect: CGRect) -> Path { let sx = rect.width / 34.736 let sy = rect.height / 32.0 func p(_ x: CGFloat, _ y: CGFloat) -> CGPoint { CGPoint(x: rect.minX + x * sx, y: rect.minY + y * sy) } var path = Path() switch part { case .upper: path.move(to: p(15.545, 0)) path.addCurve(to: p(7.406, 6.217), control1: p(12.037, 0), control2: p(8.393, 2.783)) path.addLine(to: p(0, 31.996)) path.addLine(to: p(6.942, 31.996)) path.addLine(to: p(12.094, 14.062)) path.addCurve(to: p(20.233, 7.845), control1: p(13.081, 10.629), control2: p(16.725, 7.845)) path.addLine(to: p(32.482, 7.845)) path.addLine(to: p(34.736, 0)) case .lower: path.move(to: p(31.083, 12.803)) path.addLine(to: p(27.464, 25.713)) path.addCurve(to: p(19.432, 32.0), control1: p(26.490, 29.186), control2: p(22.895, 32.0)) path.addLine(to: p(11.886, 32.0)) path.addLine(to: p(15.506, 19.090)) path.addCurve(to: p(23.537, 12.803), control1: p(16.479, 15.618), control2: p(20.075, 12.803)) path.addLine(to: p(31.083, 12.803)) } path.closeSubpath() return path } } } // MARK: - Цвета монет /// Фолбэк-цвета для иконок монет, когда SVG с CDN недоступен. enum CoinPalette { static func color(for currency: String) -> Color { switch currency { case "BTC": .orange case "ETH": Color(red: 0.39, green: 0.49, blue: 0.94) case "SOL": Color(red: 0.56, green: 0.25, blue: 0.95) case "BNB": Color(red: 0.96, green: 0.72, blue: 0.04) case "XRP": Color(red: 0.14, green: 0.14, blue: 0.14) case "TON": Color(red: 0.04, green: 0.65, blue: 0.93) case "USDT": Color(red: 0.149, green: 0.631, blue: 0.482) default: Color(red: 0.55, green: 0.55, blue: 0.60) } } }