diff --git a/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconFull.imageset/Contents.json b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconFull.imageset/Contents.json
new file mode 100644
index 0000000..79e959b
--- /dev/null
+++ b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconFull.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "full.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconFull.imageset/full.svg b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconFull.imageset/full.svg
new file mode 100644
index 0000000..1b46017
--- /dev/null
+++ b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconFull.imageset/full.svg
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconLower.imageset/Contents.json b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconLower.imageset/Contents.json
new file mode 100644
index 0000000..05952b2
--- /dev/null
+++ b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconLower.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "lower.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconLower.imageset/lower.svg b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconLower.imageset/lower.svg
new file mode 100644
index 0000000..8a9c816
--- /dev/null
+++ b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconLower.imageset/lower.svg
@@ -0,0 +1,33 @@
+
\ No newline at end of file
diff --git a/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconUpper.imageset/Contents.json b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconUpper.imageset/Contents.json
new file mode 100644
index 0000000..97c600c
--- /dev/null
+++ b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconUpper.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "upper.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true
+ }
+}
diff --git a/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconUpper.imageset/upper.svg b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconUpper.imageset/upper.svg
new file mode 100644
index 0000000..4d37eee
--- /dev/null
+++ b/tabatago-swift/TabataGo/Resources/Assets.xcassets/ZoneIconUpper.imageset/upper.svg
@@ -0,0 +1,35 @@
+
\ No newline at end of file
diff --git a/tabatago-swift/TabataGo/Views/Components/ZoneHighlightIcon.swift b/tabatago-swift/TabataGo/Views/Components/ZoneHighlightIcon.swift
index cec9253..0ef701f 100644
--- a/tabatago-swift/TabataGo/Views/Components/ZoneHighlightIcon.swift
+++ b/tabatago-swift/TabataGo/Views/Components/ZoneHighlightIcon.swift
@@ -1,131 +1,35 @@
import SwiftUI
-struct BodySilhouetteShape: Shape {
- func path(in rect: CGRect) -> Path {
- let w = rect.width
- let h = rect.height
- let mx = w * 0.5
- var p = Path()
-
- let headCY = h * 0.09
- let headR = w * 0.12
- p.addEllipse(in: CGRect(
- x: mx - headR, y: headCY - headR,
- width: headR * 2, height: headR * 2
- ))
-
- let neckW = w * 0.06
- let neckTop = headCY + headR
- let shoulderY = h * 0.20
- p.addRect(CGRect(
- x: mx - neckW, y: neckTop,
- width: neckW * 2, height: shoulderY - neckTop
- ))
-
- let shoulderHW = w * 0.42
- let armOuterBotY = h * 0.44
- let armInnerBotY = h * 0.44
- let armOuterX = w * 0.06
- let armInnerX = w * 0.12
-
- p.move(to: CGPoint(x: mx - neckW, y: shoulderY))
- p.addLine(to: CGPoint(x: mx - shoulderHW, y: shoulderY))
- p.addLine(to: CGPoint(x: mx - armOuterX, y: armOuterBotY))
- p.addLine(to: CGPoint(x: mx - armInnerX, y: armInnerBotY))
- p.closeSubpath()
-
- p.move(to: CGPoint(x: mx + neckW, y: shoulderY))
- p.addLine(to: CGPoint(x: mx + shoulderHW, y: shoulderY))
- p.addLine(to: CGPoint(x: mx + armOuterX, y: armOuterBotY))
- p.addLine(to: CGPoint(x: mx + armInnerX, y: armInnerBotY))
- p.closeSubpath()
-
- let torsoHW = w * 0.18
- let waistY = h * 0.46
- let waistHW = w * 0.15
- p.move(to: CGPoint(x: mx - torsoHW, y: shoulderY))
- p.addLine(to: CGPoint(x: mx + torsoHW, y: shoulderY))
- p.addLine(to: CGPoint(x: mx + waistHW, y: waistY))
- p.addLine(to: CGPoint(x: mx - waistHW, y: waistY))
- p.closeSubpath()
-
- let hipHW = w * 0.22
- let hipY = h * 0.52
- p.move(to: CGPoint(x: mx - waistHW, y: waistY))
- p.addLine(to: CGPoint(x: mx + waistHW, y: waistY))
- p.addLine(to: CGPoint(x: mx + hipHW, y: hipY))
- p.addLine(to: CGPoint(x: mx - hipHW, y: hipY))
- p.closeSubpath()
-
- let gap = w * 0.02
- let legBotY = h * 0.92
- let footH = h * 0.05
- let footExtra = w * 0.04
-
- p.move(to: CGPoint(x: mx - hipHW, y: hipY))
- p.addLine(to: CGPoint(x: mx - gap, y: hipY))
- p.addLine(to: CGPoint(x: mx - gap, y: legBotY))
- p.addLine(to: CGPoint(x: mx - hipHW - footExtra, y: legBotY + footH))
- p.addLine(to: CGPoint(x: mx - hipHW, y: legBotY))
- p.closeSubpath()
-
- p.move(to: CGPoint(x: mx + gap, y: hipY))
- p.addLine(to: CGPoint(x: mx + hipHW, y: hipY))
- p.addLine(to: CGPoint(x: mx + hipHW, y: legBotY))
- p.addLine(to: CGPoint(x: mx + hipHW + footExtra, y: legBotY + footH))
- p.addLine(to: CGPoint(x: mx + gap, y: legBotY))
- p.closeSubpath()
-
- return p
- }
-}
-
+/// Body-zone highlight icon backed by vector SVG assets in `Assets.xcassets`.
+///
+/// Each zone renders its own artwork:
+/// - `upper-body` → `ZoneIconUpper`
+/// - `lower-body` → `ZoneIconLower`
+/// - `full-body` → `ZoneIconFull`
+///
+/// The SVGs ship with their own baked-in colors, so the icon is **not**
+/// tinted by `Theme.zoneGradient` — it shows the artwork as authored.
+/// To make icons auto-recolor per zone instead, switch the imagesets to
+/// single-color SVGs and set `"template-rendering-intent": "template"`
+/// in each `Contents.json`, then apply `.foregroundStyle(...)` here.
+///
+/// Drawn on a 56×80 frame with `scaledToFit()` so the SVG keeps its
+/// native aspect ratio inside the card.
struct ZoneHighlightIcon: View {
let zone: String
- private var waistFraction: CGFloat { 0.50 }
-
var body: some View {
- let shape = BodySilhouetteShape()
- ZStack {
- shape
- .fill(.white.opacity(0.12))
-
- shape
- .fill(zoneGradient)
- .mask(zoneMask)
- }
- .frame(width: 56, height: 80)
+ Image(decorative: imageName, bundle: .main)
+ .resizable()
+ .scaledToFit()
+ .frame(width: 56, height: 80)
}
- private var zoneGradient: LinearGradient {
- switch zone {
- case "upper-body":
- LinearGradient(colors: [.orange, .red.opacity(0.8)], startPoint: .top, endPoint: .bottom)
- case "lower-body":
- LinearGradient(colors: [.blue, .purple.opacity(0.8)], startPoint: .top, endPoint: .bottom)
- case "full-body":
- LinearGradient(colors: [Theme.brand, .purple], startPoint: .top, endPoint: .bottom)
- default:
- LinearGradient(colors: [.gray, .secondary], startPoint: .top, endPoint: .bottom)
- }
- }
-
- @ViewBuilder
- private var zoneMask: some View {
- switch zone {
- case "upper-body":
- Rectangle()
- .frame(height: 80 * waistFraction)
- .frame(maxHeight: .infinity, alignment: .top)
- case "lower-body":
- Rectangle()
- .frame(height: 80 * waistFraction)
- .frame(maxHeight: .infinity, alignment: .bottom)
- case "full-body":
- Rectangle()
- default:
- Rectangle()
+ private var imageName: String {
+ switch zone.lowercased() {
+ case "upper-body", "upper": return "ZoneIconUpper"
+ case "lower-body", "lower": return "ZoneIconLower"
+ default: return "ZoneIconFull"
}
}
}