@titan-design/react-ui 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/dist/bodymap.js +241 -189
  2. package/dist/bodymap.js.map +1 -1
  3. package/dist/bodymap.mjs +241 -189
  4. package/dist/bodymap.mjs.map +1 -1
  5. package/dist/index.d.mts +1017 -130
  6. package/dist/index.d.ts +1017 -130
  7. package/dist/index.js +3058 -1090
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +2992 -1091
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/{pages-MO0JCySL.d.mts → pages-8u_4WbL-.d.mts} +280 -57
  12. package/dist/{pages-D7Jlssvp.d.ts → pages-DaWiBOGa.d.ts} +280 -57
  13. package/dist/pages.d.mts +1 -1
  14. package/dist/pages.d.ts +1 -1
  15. package/dist/pages.js +1797 -1292
  16. package/dist/pages.js.map +1 -1
  17. package/dist/pages.mjs +1797 -1292
  18. package/dist/pages.mjs.map +1 -1
  19. package/dist/theme/index.d.mts +464 -268
  20. package/dist/theme/index.d.ts +464 -268
  21. package/dist/theme/index.js +303 -191
  22. package/dist/theme/index.js.map +1 -1
  23. package/dist/theme/index.mjs +295 -192
  24. package/dist/theme/index.mjs.map +1 -1
  25. package/dist/theme/tokens-css.js +160 -143
  26. package/dist/theme/tokens-css.js.map +1 -1
  27. package/dist/theme/tokens-css.mjs +160 -143
  28. package/dist/theme/tokens-css.mjs.map +1 -1
  29. package/package.json +3 -1
  30. package/src/arch/Architecture.stories.tsx +870 -0
  31. package/src/arch/annotations.json +48 -0
  32. package/src/arch/arch-graph.json +4301 -0
  33. package/src/components/custom/DateTime/DateTime.stories.tsx +83 -7
  34. package/src/components/custom/DateTime/DateTime.test.tsx +29 -3
  35. package/src/components/custom/DateTime/DateTime.tsx +55 -15
  36. package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
  37. package/src/components/custom/Gauge/Gauge.stories.tsx +1 -1
  38. package/src/components/custom/Gauge/Gauge.test.tsx +2 -2
  39. package/src/components/custom/Gauge/Gauge.tsx +6 -3
  40. package/src/components/custom/Metric/Metric.stories.tsx +1 -1
  41. package/src/components/custom/Scatter/Scatter.stories.tsx +1 -1
  42. package/src/components/custom/Scatter/Scatter.tsx +3 -5
  43. package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
  44. package/src/components/custom/Table/Table.stories.tsx +1 -1
  45. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +62 -0
  46. package/src/components/custom/TimerReadout/TimerReadout.test.tsx +72 -0
  47. package/src/components/custom/TimerReadout/TimerReadout.tsx +75 -0
  48. package/src/components/custom/TimerReadout/index.ts +1 -0
  49. package/src/components/custom/Treemap/Treemap.stories.tsx +1 -1
  50. package/src/components/custom/Treemap/Treemap.tsx +3 -5
  51. package/src/components/custom/Typography/Typography.stories.tsx +46 -28
  52. package/src/components/custom/Typography/Typography.test.tsx +13 -3
  53. package/src/components/custom/Typography/Typography.tsx +5 -0
  54. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +21 -13
  55. package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +16 -13
  56. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +11 -16
  57. package/src/components/custom/Workout/BaseBadge.stories.tsx +1 -1
  58. package/src/components/custom/Workout/BodyMap.stories.tsx +1 -1
  59. package/src/components/custom/Workout/BodyMap.tsx +6 -3
  60. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +1 -1
  61. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +6 -3
  62. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +1 -1
  63. package/src/components/custom/Workout/CapacityBandChart.test.tsx +2 -2
  64. package/src/components/custom/Workout/CapacityBandChart.tsx +9 -6
  65. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  66. package/src/components/custom/Workout/DeviationBar.test.tsx +1 -1
  67. package/src/components/custom/Workout/DeviationBar.tsx +7 -4
  68. package/src/components/custom/Workout/ExerciseCard.stories.tsx +51 -66
  69. package/src/components/custom/Workout/ExerciseCard.test.tsx +146 -143
  70. package/src/components/custom/Workout/ExerciseCard.tsx +176 -198
  71. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +99 -0
  72. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +72 -0
  73. package/src/components/custom/Workout/ExerciseCardHeading.tsx +43 -0
  74. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +151 -0
  75. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +5 -3
  76. package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +4 -4
  77. package/src/components/custom/Workout/ExerciseDetailPage.tsx +6 -6
  78. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +81 -0
  79. package/src/components/custom/Workout/ExerciseHeading.test.tsx +77 -0
  80. package/src/components/custom/Workout/ExerciseHeading.tsx +85 -0
  81. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +110 -0
  82. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +93 -0
  83. package/src/components/custom/Workout/ExerciseIndicator.tsx +120 -0
  84. package/src/components/custom/Workout/FatigueMeter.stories.tsx +51 -0
  85. package/src/components/custom/Workout/FatigueMeter.test.tsx +73 -0
  86. package/src/components/custom/Workout/FatigueMeter.tsx +82 -0
  87. package/src/components/custom/Workout/InputBar.stories.tsx +1 -1
  88. package/src/components/custom/Workout/InputBar.tsx +2 -1
  89. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  90. package/src/components/custom/Workout/IntensityBar.test.tsx +8 -8
  91. package/src/components/custom/Workout/IntensityBar.tsx +10 -6
  92. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +153 -0
  93. package/src/components/custom/Workout/LiveAuraFrame.test.tsx +74 -0
  94. package/src/components/custom/Workout/LiveAuraFrame.tsx +105 -0
  95. package/src/components/custom/Workout/MesoCard.stories.tsx +1 -1
  96. package/src/components/custom/Workout/MesoCard.tsx +8 -3
  97. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  98. package/src/components/custom/Workout/MesoProgressBar.test.tsx +1 -1
  99. package/src/components/custom/Workout/MesoProgressBar.tsx +3 -2
  100. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +1 -1
  101. package/src/components/custom/Workout/MesoStatusCard.tsx +20 -13
  102. package/src/components/custom/Workout/MetricTiles.stories.tsx +60 -0
  103. package/src/components/custom/Workout/MetricTiles.test.tsx +58 -0
  104. package/src/components/custom/Workout/MetricTiles.tsx +48 -0
  105. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +1 -1
  106. package/src/components/custom/Workout/MuscleGroupChip.tsx +7 -4
  107. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
  108. package/src/components/custom/Workout/PrBadge.stories.tsx +1 -1
  109. package/src/components/custom/Workout/PrBadge.tsx +5 -2
  110. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +1 -1
  111. package/src/components/custom/Workout/PrHistoryModal.tsx +4 -3
  112. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +4 -16
  113. package/src/components/custom/Workout/ProgramPlanningPage.tsx +10 -7
  114. package/src/components/custom/Workout/README.md +146 -4
  115. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
  116. package/src/components/custom/Workout/ReadinessCheck.tsx +7 -4
  117. package/src/components/custom/Workout/RestTimer.stories.tsx +1 -1
  118. package/src/components/custom/Workout/RestTimer.tsx +17 -11
  119. package/src/components/custom/Workout/RowInventory.stories.tsx +405 -0
  120. package/src/components/custom/Workout/S3FullRail.stories.tsx +34 -0
  121. package/src/components/custom/Workout/S3SessionPace.stories.tsx +95 -0
  122. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +61 -0
  123. package/src/components/custom/Workout/S3SetTypes.stories.tsx +176 -0
  124. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +240 -0
  125. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +40 -0
  126. package/src/components/custom/Workout/ScheduleTiles.test.tsx +34 -0
  127. package/src/components/custom/Workout/ScheduleTiles.tsx +70 -0
  128. package/src/components/custom/Workout/SegmentedBar.stories.tsx +99 -0
  129. package/src/components/custom/Workout/SegmentedBar.test.tsx +77 -0
  130. package/src/components/custom/Workout/SegmentedBar.tsx +172 -0
  131. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +50 -0
  132. package/src/components/custom/Workout/SegmentedProgressBar.test.tsx +80 -0
  133. package/src/components/custom/Workout/SegmentedProgressBar.tsx +67 -0
  134. package/src/components/custom/Workout/SessionHeader.stories.tsx +121 -0
  135. package/src/components/custom/Workout/SessionHeader.test.tsx +122 -0
  136. package/src/components/custom/Workout/SessionHeader.tsx +152 -0
  137. package/src/components/custom/Workout/SessionRail.stories.tsx +153 -0
  138. package/src/components/custom/Workout/SessionRail.test.tsx +86 -0
  139. package/src/components/custom/Workout/SessionRail.tsx +126 -0
  140. package/src/components/custom/Workout/SetBar.stories.tsx +124 -0
  141. package/src/components/custom/Workout/SetBar.test.tsx +123 -0
  142. package/src/components/custom/Workout/SetBar.tsx +171 -0
  143. package/src/components/custom/Workout/SetRow.stories.tsx +50 -100
  144. package/src/components/custom/Workout/SetRow.test.tsx +94 -166
  145. package/src/components/custom/Workout/SetRow.tsx +170 -232
  146. package/src/components/custom/Workout/SetStrip.stories.tsx +154 -0
  147. package/src/components/custom/Workout/SetStrip.test.tsx +109 -0
  148. package/src/components/custom/Workout/SetStrip.tsx +71 -0
  149. package/src/components/custom/Workout/SetTableHeader.stories.tsx +66 -0
  150. package/src/components/custom/Workout/SetTableHeader.test.tsx +47 -0
  151. package/src/components/custom/Workout/SetTableHeader.tsx +88 -0
  152. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +47 -0
  153. package/src/components/custom/Workout/SetsRepsLoad.test.tsx +44 -0
  154. package/src/components/custom/Workout/SetsRepsLoad.tsx +52 -0
  155. package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
  156. package/src/components/custom/Workout/Sparkline.tsx +2 -1
  157. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  158. package/src/components/custom/Workout/StatusDot.test.tsx +2 -2
  159. package/src/components/custom/Workout/StatusDot.tsx +12 -9
  160. package/src/components/custom/Workout/StatusPill.stories.tsx +59 -0
  161. package/src/components/custom/Workout/StatusPill.test.tsx +63 -0
  162. package/src/components/custom/Workout/StatusPill.tsx +88 -0
  163. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +1 -1
  164. package/src/components/custom/Workout/StrengthTrendChart.tsx +9 -6
  165. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +1 -1
  166. package/src/components/custom/Workout/SupersetWrapper.tsx +2 -1
  167. package/src/components/custom/Workout/TempoBar.stories.tsx +1 -1
  168. package/src/components/custom/Workout/TempoDisplay.stories.tsx +12 -1
  169. package/src/components/custom/Workout/TempoDisplay.tsx +29 -36
  170. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +1 -1
  171. package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +254 -0
  172. package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +145 -0
  173. package/src/components/custom/Workout/VelocityStrip.stories.tsx +129 -66
  174. package/src/components/custom/Workout/VelocityStrip.test.tsx +379 -144
  175. package/src/components/custom/Workout/VelocityStrip.tsx +508 -120
  176. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +73 -0
  177. package/src/components/custom/Workout/VolumeLandmarkBar.test.tsx +119 -0
  178. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +154 -0
  179. package/src/components/custom/Workout/WeekRow.stories.tsx +1 -1
  180. package/src/components/custom/Workout/WeekRow.tsx +3 -2
  181. package/src/components/custom/Workout/WeightBadge.stories.tsx +1 -1
  182. package/src/components/custom/Workout/WeightBadge.tsx +6 -2
  183. package/src/components/custom/Workout/WorkoutCard.stories.tsx +2 -6
  184. package/src/components/custom/Workout/WorkoutCard.test.tsx +13 -46
  185. package/src/components/custom/Workout/WorkoutCard.tsx +5 -2
  186. package/src/components/custom/Workout/WorkoutPill.stories.tsx +1 -1
  187. package/src/components/custom/Workout/WorkoutPill.tsx +12 -8
  188. package/src/components/custom/Workout/ZoneTrack.stories.tsx +140 -0
  189. package/src/components/custom/Workout/ZoneTrack.test.tsx +200 -0
  190. package/src/components/custom/Workout/ZoneTrack.tsx +315 -0
  191. package/src/components/custom/Workout/icons.tsx +6 -67
  192. package/src/components/custom/Workout/index.ts +55 -2
  193. package/src/components/custom/Workout/metricText.stories.tsx +69 -0
  194. package/src/components/custom/Workout/metricText.tsx +34 -0
  195. package/src/components/custom/Workout/paceTone.test.ts +29 -0
  196. package/src/components/custom/Workout/paceTone.ts +27 -0
  197. package/src/components/custom/Workout/setHeadingKit.tsx +177 -0
  198. package/src/components/custom/index.ts +1 -0
  199. package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
  200. package/src/components/icons/SvgIcon.tsx +50 -0
  201. package/src/components/icons/icons.stories.tsx +122 -0
  202. package/src/components/icons/icons.test.tsx +73 -0
  203. package/src/components/icons/icons.tsx +153 -0
  204. package/src/components/icons/index.ts +4 -0
  205. package/src/components/index.ts +6 -0
  206. package/src/components/shell/BrandLockup.stories.tsx +27 -0
  207. package/src/components/shell/BrandLockup.test.tsx +23 -0
  208. package/src/components/shell/BrandLockup.tsx +47 -0
  209. package/src/components/shell/DashboardShell.stories.tsx +65 -0
  210. package/src/components/shell/DashboardShell.test.tsx +42 -0
  211. package/src/components/shell/DashboardShell.tsx +74 -0
  212. package/src/components/shell/DeviceIndicator.stories.tsx +28 -0
  213. package/src/components/shell/DeviceIndicator.test.tsx +26 -0
  214. package/src/components/shell/DeviceIndicator.tsx +52 -0
  215. package/src/components/shell/DeviceMenu.stories.tsx +47 -0
  216. package/src/components/shell/DeviceMenu.test.tsx +40 -0
  217. package/src/components/shell/DeviceMenu.tsx +65 -0
  218. package/src/components/shell/DeviceRow.stories.tsx +38 -0
  219. package/src/components/shell/DeviceRow.test.tsx +37 -0
  220. package/src/components/shell/DeviceRow.tsx +58 -0
  221. package/src/components/shell/NavItem.stories.tsx +52 -0
  222. package/src/components/shell/NavItem.test.tsx +35 -0
  223. package/src/components/shell/NavItem.tsx +75 -0
  224. package/src/components/shell/README.md +116 -0
  225. package/src/components/shell/SessionRailSpecimen.stories.tsx +753 -0
  226. package/src/components/shell/SessionStatePill.stories.tsx +27 -0
  227. package/src/components/shell/SessionStatePill.test.tsx +25 -0
  228. package/src/components/shell/SessionStatePill.tsx +46 -0
  229. package/src/components/shell/SideNav.stories.tsx +81 -0
  230. package/src/components/shell/SideNav.test.tsx +51 -0
  231. package/src/components/shell/SideNav.tsx +74 -0
  232. package/src/components/shell/TopBar.stories.tsx +49 -0
  233. package/src/components/shell/TopBar.test.tsx +36 -0
  234. package/src/components/shell/TopBar.tsx +89 -0
  235. package/src/components/shell/index.ts +13 -0
  236. package/src/components/ui/alert/Alert.stories.tsx +1 -1
  237. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
  238. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  239. package/src/components/ui/badge/Badge.stories.tsx +1 -1
  240. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  241. package/src/components/ui/button/Button.stories.tsx +1 -1
  242. package/src/components/ui/card/Card.stories.tsx +1 -1
  243. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  244. package/src/components/ui/chip/Chip.stories.tsx +1 -1
  245. package/src/components/ui/collapse/Collapse.stories.tsx +1 -1
  246. package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
  247. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  248. package/src/components/ui/drawer/Drawer.stories.tsx +1 -1
  249. package/src/components/ui/form-field/FormField.stories.tsx +1 -1
  250. package/src/components/ui/help-tip/HelpTip.stories.tsx +1 -1
  251. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  252. package/src/components/ui/index.ts +1 -0
  253. package/src/components/ui/indicator/Indicator.stories.tsx +38 -2
  254. package/src/components/ui/indicator/Indicator.test.tsx +20 -1
  255. package/src/components/ui/indicator/Indicator.tsx +50 -6
  256. package/src/components/ui/indicator/index.ts +1 -1
  257. package/src/components/ui/input/Input.stories.tsx +1 -1
  258. package/src/components/ui/link/Link.stories.tsx +1 -1
  259. package/src/components/ui/list-item/ListItem.stories.tsx +1 -1
  260. package/src/components/ui/menu/Menu.stories.tsx +1 -1
  261. package/src/components/ui/modal/Modal.stories.tsx +1 -1
  262. package/src/components/ui/pill/Pill.stories.tsx +1 -1
  263. package/src/components/ui/popover/Popover.stories.tsx +1 -1
  264. package/src/components/ui/progress/Progress.stories.tsx +1 -1
  265. package/src/components/ui/radio/Radio.stories.tsx +1 -1
  266. package/src/components/ui/section/Section.stories.tsx +1 -1
  267. package/src/components/ui/select/Select.stories.tsx +1 -1
  268. package/src/components/ui/skeleton/Skeleton.stories.tsx +1 -1
  269. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  270. package/src/components/ui/spinner/Spinner.tsx +3 -2
  271. package/src/components/ui/stack/Stack.stories.tsx +1 -1
  272. package/src/components/ui/surface/Surface.stories.tsx +1 -1
  273. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  274. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  275. package/src/components/ui/tile/Tile.stories.tsx +79 -0
  276. package/src/components/ui/tile/Tile.test.tsx +48 -0
  277. package/src/components/ui/tile/Tile.tsx +64 -0
  278. package/src/components/ui/tile/index.ts +2 -0
  279. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  280. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
  281. package/src/components/ui/toolbar-button/ToolbarButton.tsx +2 -1
  282. package/src/components/ui/tooltip/Tooltip.stories.tsx +1 -1
  283. package/src/examples/react-hook-form/ReactHookForm.stories.tsx +1 -1
  284. package/src/hooks/index.ts +2 -0
  285. package/src/hooks/useTimer.test.ts +150 -0
  286. package/src/hooks/useTimer.ts +77 -0
  287. package/src/index.ts +3 -0
  288. package/src/pages.ts +1 -1
  289. package/src/stories/PresetShowcase.stories.tsx +1 -1
  290. package/src/stories/ThemePresets.stories.tsx +1 -1
  291. package/src/theme/ColorSystem.stories.tsx +323 -0
  292. package/src/theme/Colors.stories.tsx +74 -9
  293. package/src/theme/barrel.ts +1 -0
  294. package/src/theme/config.ts +20 -3
  295. package/src/theme/elevation.stories.tsx +1 -1
  296. package/src/theme/extracted-colors-dataviz.ts +21 -0
  297. package/src/theme/extracted-colors-ui.ts +20 -0
  298. package/src/theme/global.css +69 -53
  299. package/src/theme/gradients.test.ts +25 -0
  300. package/src/theme/gradients.ts +36 -0
  301. package/src/theme/shadows.stories.tsx +1 -1
  302. package/src/theme/tokens/primitives.test.ts +232 -0
  303. package/src/theme/tokens/primitives.ts +238 -87
  304. package/src/theme/tokens/semantic.ts +101 -81
  305. package/src/theme/workout-tokens.ts +26 -21
  306. package/src/utils/avatar-color.ts +3 -3
  307. package/tailwind.config.js +15 -3
package/dist/pages.js CHANGED
@@ -8,22 +8,160 @@ var tailwindMerge = require('tailwind-merge');
8
8
 
9
9
  // src/components/custom/Workout/ActiveWorkoutPage.tsx
10
10
 
11
+ // src/theme/tokens/primitives.ts
12
+ var primitiveColors = {
13
+ // Blue scale
14
+ blue: {
15
+ 400: "#60A5FA",
16
+ 600: "#5048E5",
17
+ 700: "#3832A0"},
18
+ // Red scale
19
+ red: {
20
+ 500: "#EF4444",
21
+ 600: "#D14343"},
22
+ // Vivid red scale
23
+ redVivid: {
24
+ 400: "#FF6070",
25
+ 500: "#FF4757",
26
+ 700: "#C42539"},
27
+ // Neutral/Gray scale
28
+ neutral: {
29
+ 50: "#FAFAFA",
30
+ 100: "#F3F4F6",
31
+ 300: "#D1D5DB",
32
+ 400: "#9CA3AF",
33
+ 500: "#6B7280",
34
+ 600: "#4B5563",
35
+ 900: "#111827"},
36
+ // Charcoal scale (dark backgrounds)
37
+ charcoal: {
38
+ 200: "#3C3C3C",
39
+ 300: "#2C2C2C",
40
+ 400: "#1F1F1F",
41
+ 500: "#1C1C1C",
42
+ 600: "#191919",
43
+ 700: "#161616",
44
+ 900: "#101010"
45
+ },
46
+ // Pure colors
47
+ white: "#FFFFFF"};
48
+ var primitiveRamps = {
49
+ red: {
50
+ 50: "#FFF4F4",
51
+ 500: "#E05254",
52
+ 600: "#D14343",
53
+ // pin
54
+ 700: "#A4221C",
55
+ 800: "#7E1002"},
56
+ orange: {
57
+ 200: "#FFC7A2",
58
+ 300: "#FFA063",
59
+ 400: "#FF7900",
60
+ // pin
61
+ 500: "#DA5F00",
62
+ 600: "#B94A00"},
63
+ amber: {
64
+ 50: "#FFF7DD",
65
+ 200: "#FFD352",
66
+ 300: "#F9B415",
67
+ // pin
68
+ 400: "#E08C00",
69
+ 500: "#C27400"},
70
+ green: {
71
+ 50: "#E3FFEE",
72
+ 200: "#58F69E",
73
+ 300: "#2ED573",
74
+ // pin
75
+ 400: "#21C05D",
76
+ 500: "#22A444",
77
+ 600: "#298732"},
78
+ cyan: {
79
+ // pin
80
+ 400: "#01B5D1",
81
+ 500: "#2697B7",
82
+ 600: "#307B9B",
83
+ 700: "#2A617F",
84
+ 800: "#22465F",
85
+ 900: "#0B3149"},
86
+ blue: {
87
+ 50: "#EFF8FF",
88
+ 300: "#78C2FF",
89
+ 500: "#2196F3",
90
+ // pin
91
+ 600: "#1072CB"},
92
+ magenta: {
93
+ 600: "#BA2996"}
94
+ };
95
+ var sequentialEffort = [
96
+ primitiveRamps.green[300],
97
+ primitiveRamps.amber[200],
98
+ primitiveRamps.amber[300],
99
+ primitiveRamps.orange[400],
100
+ primitiveRamps.red[600],
101
+ primitiveRamps.red[700]
102
+ ];
103
+ var discreteRainbow = [
104
+ "#E8ECFB",
105
+ "#D9CCE3",
106
+ "#D1BBD7",
107
+ "#CAACCB",
108
+ "#BA8DB4",
109
+ "#AE76A3",
110
+ "#AA6F9E",
111
+ "#994F88",
112
+ "#882E72",
113
+ "#1965B0",
114
+ "#437DBF",
115
+ "#5289C7",
116
+ "#6195CF",
117
+ "#7BAFDE",
118
+ "#4EB265",
119
+ "#90C987",
120
+ "#CAE0AB",
121
+ "#F7F056",
122
+ "#F7CB45",
123
+ "#F6C141",
124
+ "#F4A736",
125
+ "#F1932D",
126
+ "#EE8026",
127
+ "#E8601C",
128
+ "#E65518",
129
+ "#DC050C",
130
+ "#A5170E",
131
+ "#72190E",
132
+ "#42150A"
133
+ ];
134
+
11
135
  // src/theme/workout-tokens.ts
12
136
  var WORKOUT_TOKENS = {
13
137
  // Canonical 4-band performance scale — the single source for BOTH the
14
138
  // VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
15
139
  // is intentionally inverted between the two consumers: for velocity, green =
16
- // fastest/best; for RPE, green = easiest. Each component maps its own
17
- // thresholds onto these four colors. Kept as vivid data-viz values (distinct
18
- // from the muted status tokens) and theme-independent, so like the heatmap
19
- // scale below they are consumed inline as a JS import rather than CSS vars.
20
- // A JS import also resolves on native RN, unlike var(--…) strings.
140
+ // fastest/best; for RPE, green = easiest. This is the [0,2,3,4] subsample of
141
+ // the canonical `sequentialEffort` primitive: an EVEN hue walk across the ramp
142
+ // (green gold orange → red). The earlier [0,1,2,4] spent two of its four
143
+ // bands on adjacent yellows (amber-200 + amber-300) and skipped orange, reading
144
+ // yellow-heavy with an abrupt jump to red; sampling amber-300 + orange-400
145
+ // instead gives four cleanly separable bands that hold at 3px strip scale.
146
+ // Consumed inline (RN).
21
147
  scale: {
22
- green: "#2ed573",
23
- yellow: "#ffd43b",
24
- orange: "#ffa502",
25
- red: "#ff4757"
148
+ green: sequentialEffort[0],
149
+ // green-300
150
+ yellow: sequentialEffort[2],
151
+ // amber-300 (gold)
152
+ orange: sequentialEffort[3],
153
+ // orange-400 (true orange)
154
+ red: sequentialEffort[4]
155
+ // red-600
26
156
  },
157
+ // Surface and border colors were removed here: those flip with the theme, so
158
+ // components use the CSS custom properties (var(--color-surface-*/border-*))
159
+ // directly. Only theme-independent data-viz values remain below.
160
+ // Intensity bar specific — over-target tiers deepen along the red ramp.
161
+ intensity: {
162
+ // matches status-error
163
+ over2: primitiveRamps.red[700],
164
+ over3: primitiveRamps.red[800]},
27
165
  // Placeholder strip
28
166
  placeholder: {
29
167
  fill: "#3A3A3A"
@@ -33,12 +171,6 @@ var WORKOUT_TOKENS = {
33
171
  function roundRpe(rpe) {
34
172
  return Math.round(rpe * 2) / 2;
35
173
  }
36
- function rpeColor(rpe) {
37
- if (rpe >= 9.5) return WORKOUT_TOKENS.scale.red;
38
- if (rpe >= 8.5) return WORKOUT_TOKENS.scale.orange;
39
- if (rpe >= 7) return WORKOUT_TOKENS.scale.yellow;
40
- return WORKOUT_TOKENS.scale.green;
41
- }
42
174
  function roundWeight(weight) {
43
175
  return Math.round(weight);
44
176
  }
@@ -66,14 +198,258 @@ function wrapJsx(fn) {
66
198
  var jsx = wrapJsx(jsxRuntime.jsx);
67
199
  var jsxs = wrapJsx(jsxRuntime.jsxs);
68
200
 
201
+ // src/components/custom/Workout/SegmentedBar.tsx
202
+ var PULSE_HALF_MS = 950;
203
+ var PULSE_MIN_OPACITY = 0.45;
204
+ var SEGMENTED_BAR_GAP = 5;
205
+ function usePulse(active) {
206
+ const [value] = react.useState(() => new reactNative.Animated.Value(0));
207
+ react.useEffect(() => {
208
+ if (!active) return;
209
+ const loop = reactNative.Animated.loop(
210
+ reactNative.Animated.sequence([
211
+ reactNative.Animated.timing(value, {
212
+ toValue: 1,
213
+ duration: PULSE_HALF_MS,
214
+ easing: reactNative.Easing.inOut(reactNative.Easing.ease),
215
+ useNativeDriver: false
216
+ }),
217
+ reactNative.Animated.timing(value, {
218
+ toValue: 0,
219
+ duration: PULSE_HALF_MS,
220
+ easing: reactNative.Easing.inOut(reactNative.Easing.ease),
221
+ useNativeDriver: false
222
+ })
223
+ ])
224
+ );
225
+ loop.start();
226
+ return () => loop.stop();
227
+ }, [active, value]);
228
+ return value;
229
+ }
230
+ function SegmentedBar({
231
+ segments,
232
+ height = 8,
233
+ gap = SEGMENTED_BAR_GAP,
234
+ radius = 2,
235
+ marker = null,
236
+ segmentTestID,
237
+ style,
238
+ ...props
239
+ }) {
240
+ const pulse = usePulse(segments.some((s) => s.pulse));
241
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: [{ flexDirection: "row", height, gap, position: "relative" }, style], ...props, children: [
242
+ segments.map((seg, i) => {
243
+ const fillStyle = {
244
+ width: `${(seg.fill ?? 1) * 100}%`,
245
+ height: "100%",
246
+ backgroundColor: seg.color,
247
+ opacity: seg.opacity
248
+ };
249
+ const testID = segmentTestID?.(seg, i) ?? "segmented-bar-segment";
250
+ return /* @__PURE__ */ jsx(
251
+ reactNative.View,
252
+ {
253
+ style: {
254
+ flex: seg.weight ?? 1,
255
+ minWidth: 0,
256
+ height: "100%",
257
+ marginLeft: seg.leadingGap,
258
+ borderRadius: radius,
259
+ overflow: "hidden"
260
+ },
261
+ children: seg.pulse ? /* @__PURE__ */ jsx(
262
+ reactNative.Animated.View,
263
+ {
264
+ style: {
265
+ ...fillStyle,
266
+ ...seg.pulseColor ? {
267
+ backgroundColor: pulse.interpolate({
268
+ inputRange: [0, 1],
269
+ outputRange: seg.pulseColor
270
+ })
271
+ } : {
272
+ opacity: pulse.interpolate({
273
+ inputRange: [0, 1],
274
+ outputRange: [PULSE_MIN_OPACITY, 1]
275
+ })
276
+ }
277
+ },
278
+ accessibilityElementsHidden: true,
279
+ testID
280
+ }
281
+ ) : /* @__PURE__ */ jsx(reactNative.View, { style: fillStyle, accessibilityElementsHidden: true, testID })
282
+ },
283
+ i
284
+ );
285
+ }),
286
+ marker ? /* @__PURE__ */ jsx(
287
+ reactNative.View,
288
+ {
289
+ style: {
290
+ position: "absolute",
291
+ left: `${marker.position * 100}%`,
292
+ top: 0,
293
+ bottom: 0,
294
+ width: 2,
295
+ backgroundColor: marker.color
296
+ },
297
+ accessibilityElementsHidden: true,
298
+ testID: "segmented-bar-marker"
299
+ }
300
+ ) : null
301
+ ] });
302
+ }
303
+
304
+ // src/components/custom/Workout/SetBar.tsx
305
+ var SET_STRIP_ZONES = {
306
+ slow: primitiveRamps.red[600],
307
+ moderate: primitiveRamps.orange[400],
308
+ fast: primitiveRamps.amber[300],
309
+ fastest: primitiveRamps.green[300]
310
+ };
311
+ var TODO_COLOR = primitiveColors.charcoal[300];
312
+ var SET_STRIP_VARIABLE_COLOR = primitiveRamps.cyan[900];
313
+ var NOTCH_GAP = 2;
314
+ var CLUSTER_GAP = 3;
315
+ var MYO_UPCOMING_CLUSTERS = 3;
316
+ var MYO_UPCOMING_CLUSTER_LEN = 5;
317
+ var MYO_UPCOMING_FADE = 0.6;
318
+ var PULSE_RANGE = {
319
+ [SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
320
+ [SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
321
+ [SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
322
+ [SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
323
+ };
324
+ function velocityZoneColor(v) {
325
+ if (v < 0.5) return SET_STRIP_ZONES.slow;
326
+ if (v < 0.75) return SET_STRIP_ZONES.moderate;
327
+ if (v < 1) return SET_STRIP_ZONES.fast;
328
+ return SET_STRIP_ZONES.fastest;
329
+ }
330
+ function chunkedSegments(chunks, gap) {
331
+ return chunks.flatMap(
332
+ (vels, ci) => vels.map((v, ri) => ({
333
+ color: velocityZoneColor(v),
334
+ ...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
335
+ }))
336
+ );
337
+ }
338
+ function rangeSegments(floor, max, doneVels) {
339
+ return Array.from({ length: max }, (_, i) => {
340
+ if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
341
+ return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
342
+ });
343
+ }
344
+ function myoUpcomingSegments(activationLen, clusterCount) {
345
+ const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
346
+ const trail = Array.from(
347
+ { length: clusterCount },
348
+ (_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
349
+ color: SET_STRIP_VARIABLE_COLOR,
350
+ opacity: MYO_UPCOMING_FADE ** ci,
351
+ ...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
352
+ }))
353
+ ).flat();
354
+ return [...activation, ...trail];
355
+ }
356
+ function setSegments(set) {
357
+ if (set.status === "todo") {
358
+ return [{ color: TODO_COLOR }];
359
+ }
360
+ if (set.status === "done") {
361
+ return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
362
+ }
363
+ if (set.status === "range") {
364
+ return rangeSegments(set.floor, set.max, set.doneVels);
365
+ }
366
+ if (set.status === "drop") {
367
+ return chunkedSegments(set.subloads, NOTCH_GAP);
368
+ }
369
+ if (set.status === "myo") {
370
+ return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
371
+ }
372
+ if (set.status === "myo-upcoming") {
373
+ return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
374
+ }
375
+ const performed = set.velocities.map((v) => {
376
+ const color = velocityZoneColor(v);
377
+ return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
378
+ });
379
+ const remaining = Math.max(0, set.planned - set.velocities.length);
380
+ return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
381
+ }
382
+ function setBarSegmentTestID(seg) {
383
+ if (seg.pulse) return "set-strip-pulse";
384
+ if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
385
+ return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
386
+ }
387
+ function SetBar({ set, height = 8 }) {
388
+ return /* @__PURE__ */ jsx(
389
+ SegmentedBar,
390
+ {
391
+ segments: setSegments(set),
392
+ height,
393
+ gap: 0,
394
+ radius: 0,
395
+ segmentTestID: setBarSegmentTestID,
396
+ style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
397
+ testID: "set-strip-set"
398
+ }
399
+ );
400
+ }
401
+
402
+ // src/components/custom/Workout/SetStrip.tsx
403
+ var SET_STRIP_GAP = 5;
404
+ function setCategory(set) {
405
+ switch (set.status) {
406
+ case "done":
407
+ case "drop":
408
+ case "myo":
409
+ return "done";
410
+ case "active":
411
+ return "active";
412
+ case "range":
413
+ return set.doneVels.length > 0 ? "active" : "upcoming";
414
+ case "todo":
415
+ case "myo-upcoming":
416
+ return "upcoming";
417
+ }
418
+ }
419
+ function describeSets(sets) {
420
+ const done = sets.filter((s) => setCategory(s) === "done").length;
421
+ const active = sets.filter((s) => setCategory(s) === "active").length;
422
+ const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
423
+ return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
424
+ }
425
+ function SetStrip({ sets, height = 8, className, ...props }) {
426
+ return /* @__PURE__ */ jsx(
427
+ reactNative.View,
428
+ {
429
+ className,
430
+ style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
431
+ accessibilityRole: "image",
432
+ accessibilityLabel: describeSets(sets),
433
+ testID: "set-strip",
434
+ ...props,
435
+ children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
436
+ }
437
+ );
438
+ }
439
+
69
440
  // src/components/custom/Workout/VelocityStrip.tsx
70
441
  var VEL_COLORS = WORKOUT_TOKENS.scale;
71
- var zoneIdToScaleToken = {
72
- speed: "green",
73
- power: "yellow",
74
- strengthSpeed: "orange",
75
- maximalStrength: "red",
76
- grinding: "red"
442
+ var bandIdToEffortColor = {
443
+ speed: sequentialEffort[0],
444
+ // green-300
445
+ power: sequentialEffort[2],
446
+ // amber-300 (gold)
447
+ strengthSpeed: sequentialEffort[3],
448
+ // orange-400
449
+ maximalStrength: sequentialEffort[4],
450
+ // red-600
451
+ grinding: sequentialEffort[5]
452
+ // red-700 — the distinct 5th band
77
453
  };
78
454
  function getVelocityZoneColor(velocity) {
79
455
  if (velocity >= 1) return "vel-green";
@@ -112,14 +488,132 @@ function classifyBand(velocity, bands) {
112
488
  return bands[bands.length - 1];
113
489
  }
114
490
  function bandColor(band) {
115
- const token = zoneIdToScaleToken[band.id];
116
- return token ? VEL_COLORS[token] : VEL_COLORS.green;
491
+ return bandIdToEffortColor[band.id] ?? VEL_COLORS.green;
117
492
  }
118
493
  function getLossStyle(loss) {
119
494
  if (loss > 25) return { color: VEL_COLORS.red };
120
495
  if (loss > 20) return { color: VEL_COLORS.orange };
121
496
  return null;
122
497
  }
498
+ var REP_GAP = 2;
499
+ var WIDE_GAP = 8;
500
+ var TODO_COLOR2 = primitiveColors.charcoal[300];
501
+ var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
502
+ function deriveDoneVelocities(set) {
503
+ switch (set.type) {
504
+ case "drop":
505
+ return set.subloads.flat();
506
+ case "myo":
507
+ return [...set.activation, ...set.clusters.flat()];
508
+ default:
509
+ return set.velocities;
510
+ }
511
+ }
512
+ function chunkedRepSlots(chunks) {
513
+ const slots = [];
514
+ chunks.forEach((chunk) => {
515
+ chunk.forEach((velocity, ri) => {
516
+ const first = slots.length === 0;
517
+ slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP : REP_GAP });
518
+ });
519
+ });
520
+ return slots;
521
+ }
522
+ function buildSlots(set) {
523
+ switch (set.type) {
524
+ case "straight": {
525
+ const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
526
+ return Array.from({ length: total }, (_, i) => {
527
+ const done = i < set.velocities.length;
528
+ return {
529
+ kind: done ? "rep" : "todo",
530
+ velocity: done ? set.velocities[i] : void 0,
531
+ leadingGap: i === 0 ? 0 : REP_GAP
532
+ };
533
+ });
534
+ }
535
+ case "range": {
536
+ const total = Math.max(set.max, set.velocities.length);
537
+ return Array.from({ length: total }, (_, i) => {
538
+ const kind = i < set.velocities.length ? "rep" : i < set.floor ? "todo" : "variable";
539
+ return {
540
+ kind,
541
+ velocity: kind === "rep" ? set.velocities[i] : void 0,
542
+ leadingGap: i === 0 ? 0 : REP_GAP
543
+ };
544
+ });
545
+ }
546
+ case "amrap": {
547
+ const reps = set.velocities.map((velocity, i) => ({
548
+ kind: "rep",
549
+ velocity,
550
+ leadingGap: i === 0 ? 0 : REP_GAP
551
+ }));
552
+ reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 : REP_GAP });
553
+ return reps;
554
+ }
555
+ case "drop":
556
+ return chunkedRepSlots(set.subloads);
557
+ case "myo": {
558
+ const slots = chunkedRepSlots([set.activation, ...set.clusters]);
559
+ if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 : REP_GAP });
560
+ return slots;
561
+ }
562
+ case "cluster": {
563
+ const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
564
+ return Array.from({ length: total }, (_, i) => {
565
+ const done = i < set.velocities.length;
566
+ const boundary = i > 0 && i % set.groupSize === 0;
567
+ return {
568
+ kind: done ? "rep" : "todo",
569
+ velocity: done ? set.velocities[i] : void 0,
570
+ leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP : REP_GAP
571
+ };
572
+ });
573
+ }
574
+ }
575
+ }
576
+ function setAccessibilityLabel(set, repCount) {
577
+ switch (set.type) {
578
+ case "straight":
579
+ return `Velocity strip, straight set, ${repCount} reps`;
580
+ case "range":
581
+ return `Velocity strip, rep-range set, floor ${set.floor} to ${set.max}, ${repCount} reps done`;
582
+ case "amrap":
583
+ return `Velocity strip, AMRAP set, ${repCount} reps and counting`;
584
+ case "drop":
585
+ return `Velocity strip, drop set, ${set.subloads.length} loads, ${repCount} reps`;
586
+ case "myo":
587
+ return `Velocity strip, myo-reps set, ${set.clusters.length} clusters${set.open ? ", open" : ""}, ${repCount} reps`;
588
+ case "cluster":
589
+ return `Velocity strip, cluster set, groups of ${set.groupSize}, ${repCount} reps`;
590
+ }
591
+ }
592
+ function slotAccessibilityLabel(slot, index) {
593
+ switch (slot.kind) {
594
+ case "rep":
595
+ return `Rep ${index + 1}: ${formatVelocity(slot.velocity ?? 0)} meters per second`;
596
+ case "todo":
597
+ return `Rep ${index + 1}: planned`;
598
+ case "variable":
599
+ return `Rep ${index + 1}: variable`;
600
+ case "continue":
601
+ return "Keep going";
602
+ }
603
+ }
604
+ var EXPANDED_TODO_STUB_PCT = 16;
605
+ var EXPANDED_ENCODED_PCT = 45;
606
+ var EXPANDED_HEIGHT = 60;
607
+ var FIXED_MAX_VELOCITY = 1.15;
608
+ var BARE_STUB_HEIGHT = 3;
609
+ function scaleDenominator(scale, maxVelocity) {
610
+ return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
611
+ }
612
+ function bareSlotHeight(slot, height, denom) {
613
+ if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
614
+ const ratio = Math.min(1, (slot.velocity ?? 0) / denom);
615
+ return Math.max(BARE_STUB_HEIGHT, ratio * height);
616
+ }
123
617
  function getReducedMotionPreference() {
124
618
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
125
619
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
@@ -141,31 +635,48 @@ var ANIMATION_EASING = reactNative.Easing.bezier(0.22, 1, 0.36, 1);
141
635
  var POP_EASING = reactNative.Easing.bezier(0.34, 1.56, 0.64, 1);
142
636
  function VelocityStrip({
143
637
  velocities,
638
+ set,
144
639
  zones,
145
640
  liveRepIndex,
146
- expanded = false,
641
+ expanded = true,
147
642
  onToggle,
148
643
  onRepPress,
149
- variant = "full",
644
+ variant = "expanded",
645
+ height = EXPANDED_HEIGHT,
646
+ scale = "peak",
647
+ showNumbers = true,
150
648
  showInfo = true,
151
649
  className,
152
650
  ...props
153
651
  }) {
154
- const maxVelocity = Math.max(...velocities, 0);
155
- const meanVelocity = calculateMeanVelocity(velocities);
156
- const loss = calculateVelocityLoss(velocities);
652
+ const doneVelocities = set ? deriveDoneVelocities(set) : velocities ?? [];
653
+ const slots = set ? buildSlots(set) : doneVelocities.map((v, i) => ({
654
+ kind: "rep",
655
+ velocity: v,
656
+ leadingGap: i === 0 ? 0 : REP_GAP
657
+ }));
658
+ const maxVelocity = Math.max(...doneVelocities, 0);
659
+ const meanVelocity = calculateMeanVelocity(doneVelocities);
660
+ const loss = calculateVelocityLoss(doneVelocities);
661
+ const framed = showNumbers || showInfo;
662
+ const scaleDenom = scaleDenominator(scale, maxVelocity);
157
663
  const hasZones = zones != null && zones.length > 0;
158
664
  const barColorFor = (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
665
+ const slotColor = (slot) => {
666
+ if (slot.kind === "rep") return barColorFor(slot.velocity ?? 0);
667
+ if (slot.kind === "todo") return TODO_COLOR2;
668
+ return SET_STRIP_VARIABLE_COLOR;
669
+ };
159
670
  const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
160
671
  const prefersReducedMotion = usePrefersReducedMotion();
161
- const [heightAnim] = react.useState(() => new reactNative.Animated.Value(expanded ? 60 : 3));
672
+ const [heightAnim] = react.useState(() => new reactNative.Animated.Value(expanded ? height : 3));
162
673
  const [labelOpacity] = react.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
163
674
  const [infoOpacity] = react.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
164
675
  const [liveScale] = react.useState(() => new reactNative.Animated.Value(1));
165
- const liveVelocity = liveRepIndex != null ? velocities[liveRepIndex] : void 0;
676
+ const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
166
677
  const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
167
678
  react.useEffect(() => {
168
- if (variant === "mini" || liveRepIndex == null || liveVelocity == null) return;
679
+ if (variant !== "expanded" || !framed || liveRepIndex == null || liveVelocity == null) return;
169
680
  if (prefersReducedMotion) {
170
681
  liveScale.setValue(1);
171
682
  return;
@@ -195,12 +706,12 @@ function VelocityStrip({
195
706
  useNativeDriver: true
196
707
  }).start();
197
708
  }
198
- }, [variant, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
709
+ }, [variant, framed, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
199
710
  react.useEffect(() => {
200
- if (variant === "mini") return;
711
+ if (variant !== "expanded" || !framed) return;
201
712
  reactNative.Animated.parallel([
202
713
  reactNative.Animated.timing(heightAnim, {
203
- toValue: expanded ? 60 : 3,
714
+ toValue: expanded ? height : 3,
204
715
  duration: ANIMATION_DURATION,
205
716
  easing: ANIMATION_EASING,
206
717
  useNativeDriver: false
@@ -218,39 +729,118 @@ function VelocityStrip({
218
729
  useNativeDriver: false
219
730
  })
220
731
  ]).start();
221
- }, [expanded, variant, heightAnim, labelOpacity, infoOpacity]);
732
+ }, [expanded, variant, framed, height, heightAnim, labelOpacity, infoOpacity]);
733
+ if (set == null && velocities == null) return null;
734
+ const repCount = doneVelocities.length;
735
+ const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
222
736
  if (variant === "mini") {
223
737
  const { style: externalStyle, ...restProps } = props;
224
738
  return /* @__PURE__ */ jsx(
225
739
  reactNative.View,
226
740
  {
227
741
  className,
228
- style: [{ flexDirection: "row", height: 3, gap: 2, borderRadius: 2, overflow: "hidden" }, externalStyle],
742
+ style: [
743
+ { flexDirection: "row", height: 3, gap: REP_GAP, borderRadius: 2, overflow: "hidden" },
744
+ externalStyle
745
+ ],
229
746
  accessibilityRole: "image",
230
- accessibilityLabel: `Velocity strip, ${velocities.length} reps`,
747
+ accessibilityLabel: miniLabel,
231
748
  testID: "velocity-strip-mini",
232
749
  ...restProps,
233
- children: velocities.map((v, i) => /* @__PURE__ */ jsx(
750
+ children: slots.map((slot, i) => /* @__PURE__ */ jsx(
234
751
  reactNative.View,
235
752
  {
236
753
  style: {
237
754
  flex: 1,
238
- backgroundColor: barColorFor(v),
755
+ backgroundColor: slotColor(slot),
239
756
  borderRadius: 1,
240
757
  minWidth: 4,
241
- height: "100%"
758
+ height: "100%",
759
+ // The container's uniform 2px gap covers rep spacing; a wide slot adds
760
+ // only the EXTRA (WIDE_GAP − REP_GAP) so the notch totals WIDE_GAP.
761
+ marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
762
+ ...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
763
+ },
764
+ accessibilityElementsHidden: true,
765
+ testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
766
+ },
767
+ i
768
+ ))
769
+ }
770
+ );
771
+ }
772
+ if (!framed) {
773
+ const { style: externalStyle, ...restProps } = props;
774
+ return /* @__PURE__ */ jsx(
775
+ reactNative.View,
776
+ {
777
+ className,
778
+ style: [
779
+ { flexDirection: "row", height, gap: REP_GAP, alignItems: "flex-end" },
780
+ externalStyle
781
+ ],
782
+ accessibilityRole: "image",
783
+ accessibilityLabel: miniLabel,
784
+ testID: "velocity-strip-compact",
785
+ ...restProps,
786
+ children: slots.map((slot, i) => /* @__PURE__ */ jsx(
787
+ reactNative.View,
788
+ {
789
+ style: {
790
+ flex: 1,
791
+ minWidth: 4,
792
+ height: bareSlotHeight(slot, height, scaleDenom),
793
+ // Top-rounded to match the framed chart's bars (the "rounded tops from
794
+ // the numbers one"); square bottoms since bars sit on the baseline.
795
+ borderTopLeftRadius: 2,
796
+ borderTopRightRadius: 2,
797
+ backgroundColor: slotColor(slot),
798
+ marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
799
+ ...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
242
800
  },
243
801
  accessibilityElementsHidden: true,
244
- testID: `velocity-bar-${i}`
802
+ testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
245
803
  },
246
804
  i
247
805
  ))
248
806
  }
249
807
  );
250
808
  }
251
- const stripLabel = `Velocity chart for set, ${velocities.length} reps, tap to ${expanded ? "collapse" : "expand"}`;
809
+ const stripLabel = set ? `${setAccessibilityLabel(set, repCount)}, tap to ${expanded ? "collapse" : "expand"}` : `Velocity chart for set, ${repCount} reps, tap to ${expanded ? "collapse" : "expand"}`;
252
810
  const hasInteractiveContainer = onToggle != null;
253
811
  const hasInteractiveReps = onRepPress != null && expanded;
812
+ const setBars = set != null && slots.map((slot, i) => {
813
+ const isStraightRep = set.type === "straight" && slot.kind === "rep";
814
+ const heightPct = isStraightRep ? scaleDenom > 0 ? Math.round((slot.velocity ?? 0) / scaleDenom * 100) : 0 : set.type === "straight" ? EXPANDED_TODO_STUB_PCT : EXPANDED_ENCODED_PCT;
815
+ return /* @__PURE__ */ jsx(
816
+ reactNative.View,
817
+ {
818
+ style: {
819
+ flex: 1,
820
+ height: "100%",
821
+ justifyContent: "flex-end",
822
+ marginLeft: slot.leadingGap
823
+ },
824
+ children: /* @__PURE__ */ jsx(
825
+ reactNative.View,
826
+ {
827
+ style: {
828
+ height: `${heightPct}%`,
829
+ minHeight: 2,
830
+ borderTopLeftRadius: 2,
831
+ borderTopRightRadius: 2,
832
+ backgroundColor: slotColor(slot),
833
+ ...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
834
+ },
835
+ testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`,
836
+ accessibilityRole: "image",
837
+ accessibilityLabel: slotAccessibilityLabel(slot, i)
838
+ }
839
+ )
840
+ },
841
+ i
842
+ );
843
+ });
254
844
  const stripContent = /* @__PURE__ */ jsxs(
255
845
  reactNative.Animated.View,
256
846
  {
@@ -263,7 +853,9 @@ function VelocityStrip({
263
853
  borderRadius: expanded ? 6 : 2,
264
854
  paddingTop: expanded ? 16 : 0,
265
855
  paddingBottom: expanded ? 24 : 0,
266
- paddingHorizontal: expanded ? 6 : 0,
856
+ // No horizontal inset the framed chart's bars span full width, matching
857
+ // the bare spotlight and mini. Numbers/info sit in the vertical padding.
858
+ paddingHorizontal: 0,
267
859
  paddingVertical: expanded ? void 0 : 8,
268
860
  overflow: expanded ? "visible" : "hidden"
269
861
  }
@@ -273,46 +865,65 @@ function VelocityStrip({
273
865
  testID: "velocity-strip",
274
866
  ...props,
275
867
  children: [
276
- /* @__PURE__ */ jsx(
277
- reactNative.View,
278
- {
279
- style: { flexDirection: "row", flex: 1, gap: 2, alignItems: "flex-end" },
280
- children: velocities.map((v, i) => {
281
- const barBackground = barColorFor(v);
282
- const barHeightPct = maxVelocity > 0 ? Math.round(v / (maxVelocity * 1.15) * 100) : 0;
283
- const isLive = liveRepIndex === i;
284
- const liveLabelSuffix = isLive ? isNewPeak ? ", latest rep, new set peak" : ", latest rep" : "";
285
- const barStyle = expanded ? { height: `${barHeightPct}%`, minHeight: 2, borderTopLeftRadius: 2, borderTopRightRadius: 2, backgroundColor: barBackground } : {
286
- minHeight: "100%",
287
- borderTopLeftRadius: 2,
288
- borderTopRightRadius: 2,
289
- borderBottomLeftRadius: 0,
290
- borderBottomRightRadius: 0,
291
- backgroundColor: barBackground
292
- };
293
- const barInner = isLive ? /* @__PURE__ */ jsx(
294
- reactNative.Animated.View,
295
- {
296
- style: [barStyle, { transform: [{ scale: liveScale }] }],
297
- testID: `velocity-bar-${i}`,
298
- accessibilityRole: "image",
299
- accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
300
- }
301
- ) : /* @__PURE__ */ jsx(
302
- reactNative.View,
303
- {
304
- style: barStyle,
305
- testID: `velocity-bar-${i}`,
306
- accessibilityRole: "image",
307
- accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
308
- }
309
- );
310
- const bar = /* @__PURE__ */ jsxs(
311
- reactNative.View,
312
- {
313
- style: { flex: 1, height: "100%", justifyContent: "flex-end", position: "relative" },
314
- children: [
315
- expanded && /* @__PURE__ */ jsx(reactNative.Animated.View, { style: { opacity: labelOpacity, alignItems: "center", position: "absolute", top: -13, left: 0, right: 0 }, accessibilityElementsHidden: true, children: /* @__PURE__ */ jsx(
868
+ /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", flex: 1, gap: set ? 0 : 2, alignItems: "flex-end" }, children: set ? setBars : doneVelocities.map((v, i) => {
869
+ const barBackground = barColorFor(v);
870
+ const barHeightPct = scaleDenom > 0 ? Math.round(v / scaleDenom * 100) : 0;
871
+ const isLive = liveRepIndex === i;
872
+ const liveLabelSuffix = isLive ? isNewPeak ? ", latest rep, new set peak" : ", latest rep" : "";
873
+ const barStyle = expanded ? {
874
+ height: `${barHeightPct}%`,
875
+ minHeight: 2,
876
+ borderTopLeftRadius: 2,
877
+ borderTopRightRadius: 2,
878
+ backgroundColor: barBackground
879
+ } : {
880
+ minHeight: "100%",
881
+ borderTopLeftRadius: 2,
882
+ borderTopRightRadius: 2,
883
+ borderBottomLeftRadius: 0,
884
+ borderBottomRightRadius: 0,
885
+ backgroundColor: barBackground
886
+ };
887
+ const barInner = isLive ? /* @__PURE__ */ jsx(
888
+ reactNative.Animated.View,
889
+ {
890
+ style: [barStyle, { transform: [{ scale: liveScale }] }],
891
+ testID: `velocity-bar-${i}`,
892
+ accessibilityRole: "image",
893
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
894
+ }
895
+ ) : /* @__PURE__ */ jsx(
896
+ reactNative.View,
897
+ {
898
+ style: barStyle,
899
+ testID: `velocity-bar-${i}`,
900
+ accessibilityRole: "image",
901
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
902
+ }
903
+ );
904
+ const bar = /* @__PURE__ */ jsxs(
905
+ reactNative.View,
906
+ {
907
+ style: {
908
+ flex: 1,
909
+ height: "100%",
910
+ justifyContent: "flex-end",
911
+ position: "relative"
912
+ },
913
+ children: [
914
+ expanded && showNumbers && /* @__PURE__ */ jsx(
915
+ reactNative.Animated.View,
916
+ {
917
+ style: {
918
+ opacity: labelOpacity,
919
+ alignItems: "center",
920
+ position: "absolute",
921
+ top: -13,
922
+ left: 0,
923
+ right: 0
924
+ },
925
+ accessibilityElementsHidden: true,
926
+ children: /* @__PURE__ */ jsx(
316
927
  reactNative.Text,
317
928
  {
318
929
  className: "text-text-secondary",
@@ -320,30 +931,30 @@ function VelocityStrip({
320
931
  testID: `velocity-label-${i}`,
321
932
  children: formatVelocity(v)
322
933
  }
323
- ) }),
324
- barInner
325
- ]
326
- },
327
- i
328
- );
329
- if (onRepPress && expanded) {
330
- return /* @__PURE__ */ jsx(
331
- reactNative.Pressable,
332
- {
333
- style: { flex: 1, height: "100%" },
334
- onPress: () => onRepPress(i, v),
335
- accessibilityRole: "button",
336
- accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
337
- testID: `velocity-bar-pressable-${i}`,
338
- children: bar
339
- },
340
- i
341
- );
342
- }
343
- return bar;
344
- })
934
+ )
935
+ }
936
+ ),
937
+ barInner
938
+ ]
939
+ },
940
+ i
941
+ );
942
+ if (onRepPress && expanded) {
943
+ return /* @__PURE__ */ jsx(
944
+ reactNative.Pressable,
945
+ {
946
+ style: { flex: 1, height: "100%" },
947
+ onPress: () => onRepPress(i, v),
948
+ accessibilityRole: "button",
949
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
950
+ testID: `velocity-bar-pressable-${i}`,
951
+ children: bar
952
+ },
953
+ i
954
+ );
345
955
  }
346
- ),
956
+ return bar;
957
+ }) }),
347
958
  expanded && showInfo && /* @__PURE__ */ jsxs(
348
959
  reactNative.Animated.View,
349
960
  {
@@ -489,12 +1100,14 @@ function PlaceholderStrip({
489
1100
  return /* @__PURE__ */ jsx(SingleStrip, { ...props });
490
1101
  }
491
1102
 
492
- // src/components/custom/Workout/icons.tsx
493
- function DumbbellIcon({
1103
+ // src/components/icons/SvgIcon.tsx
1104
+ function SvgIcon({
494
1105
  size = 24,
495
1106
  color = "currentColor",
1107
+ strokeWidth = 2,
496
1108
  fill = "none",
497
- strokeWidth = 2
1109
+ title,
1110
+ children
498
1111
  }) {
499
1112
  return /* @__PURE__ */ jsxs(
500
1113
  "svg",
@@ -507,36 +1120,59 @@ function DumbbellIcon({
507
1120
  strokeWidth,
508
1121
  strokeLinecap: "round",
509
1122
  strokeLinejoin: "round",
1123
+ ...title ? { role: "img", "aria-label": title } : { "aria-hidden": true },
510
1124
  children: [
511
- /* @__PURE__ */ jsx("path", { d: "M14.4 14.4 9.6 9.6" }),
512
- /* @__PURE__ */ jsx("path", { d: "M18.657 21.485a2 2 0 1 1-2.829-2.828l-1.767 1.768a2 2 0 1 1-2.829-2.829l6.364-6.364a2 2 0 1 1 2.829 2.829l-1.768 1.767a2 2 0 1 1 2.828 2.829z" }),
513
- /* @__PURE__ */ jsx("path", { d: "m21.5 21.5-1.4-1.4" }),
514
- /* @__PURE__ */ jsx("path", { d: "M3.9 3.9 2.5 2.5" }),
515
- /* @__PURE__ */ jsx("path", { d: "M6.404 12.768a2 2 0 1 1-2.829-2.829l1.768-1.767a2 2 0 1 1-2.828-2.829l2.828-2.828a2 2 0 1 1 2.829 2.828l1.767-1.768a2 2 0 1 1 2.829 2.829z" })
1125
+ title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
1126
+ children
516
1127
  ]
517
1128
  }
518
1129
  );
519
1130
  }
520
- function StarIcon({
521
- size = 24,
522
- color = "currentColor",
523
- fill = "none",
524
- strokeWidth = 2
525
- }) {
526
- return /* @__PURE__ */ jsx(
527
- "svg",
528
- {
529
- width: size,
530
- height: size,
531
- viewBox: "0 0 24 24",
532
- fill,
533
- stroke: color,
534
- strokeWidth,
535
- strokeLinecap: "round",
536
- strokeLinejoin: "round",
537
- children: /* @__PURE__ */ jsx("path", { d: "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" })
538
- }
539
- );
1131
+
1132
+ // src/components/icons/icons.tsx
1133
+ function StarIcon(props) {
1134
+ return /* @__PURE__ */ jsx(SvgIcon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" }) });
1135
+ }
1136
+ function ScaleIcon(props) {
1137
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1138
+ /* @__PURE__ */ jsx("path", { d: "m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
1139
+ /* @__PURE__ */ jsx("path", { d: "m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
1140
+ /* @__PURE__ */ jsx("path", { d: "M7 21h10" }),
1141
+ /* @__PURE__ */ jsx("path", { d: "M12 3v18" }),
1142
+ /* @__PURE__ */ jsx("path", { d: "M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2" })
1143
+ ] });
1144
+ }
1145
+ function AlertTriangleIcon(props) {
1146
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1147
+ /* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" }),
1148
+ /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
1149
+ /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
1150
+ ] });
1151
+ }
1152
+ function TrendingDownIcon(props) {
1153
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1154
+ /* @__PURE__ */ jsx("path", { d: "M16 17h6v-6" }),
1155
+ /* @__PURE__ */ jsx("path", { d: "m22 17-8.5-8.5-5 5L2 7" })
1156
+ ] });
1157
+ }
1158
+ function CircleSlashIcon(props) {
1159
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1160
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
1161
+ /* @__PURE__ */ jsx("path", { d: "m9 15 6-6" })
1162
+ ] });
1163
+ }
1164
+ function AwardIcon(props) {
1165
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1166
+ /* @__PURE__ */ jsx("path", { d: "m15.477 12.89 1.515 8.526a.5.5 0 0 1-.81.47l-3.58-2.687a1 1 0 0 0-1.197 0l-3.586 2.686a.5.5 0 0 1-.81-.469l1.514-8.526" }),
1167
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "6" })
1168
+ ] });
1169
+ }
1170
+ function InfoIcon(props) {
1171
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1172
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
1173
+ /* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
1174
+ /* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
1175
+ ] });
540
1176
  }
541
1177
  var baseBadgeSizeConfig = {
542
1178
  sm: { fontSize: 9, paddingH: 6, paddingV: 2, iconSize: 10 },
@@ -616,105 +1252,286 @@ function BaseBadge({
616
1252
  return badge;
617
1253
  }
618
1254
 
619
- // src/components/custom/Workout/WeightBadge.tsx
620
- function WeightBadge({
621
- value,
622
- unit = "lbs",
623
- reps,
624
- delta,
625
- isPr,
626
- showIcon = true,
627
- size = "md",
628
- onPress,
629
- className,
630
- ...props
631
- }) {
632
- const config = baseBadgeSizeConfig[size];
633
- const textColor = isPr ? "#FF7900" : "#9CA3AF";
634
- const iconColor = textColor;
635
- const repMaxClause = reps != null ? `${reps} rep max` : "Estimated one rep max";
636
- const deltaLabel = delta != null ? `, ${delta >= 0 ? "+" : ""}${delta}% change` : "";
637
- const fullLabel = `${repMaxClause}: ${value} ${unit}${deltaLabel}`;
638
- return /* @__PURE__ */ jsxs(
639
- BaseBadge,
640
- {
641
- variant: isPr ? "pr" : "plain",
642
- size,
643
- onPress,
644
- className,
645
- icon: showIcon ? /* @__PURE__ */ jsx(DumbbellIcon, { size: config.iconSize, color: iconColor, strokeWidth: 2 }) : void 0,
646
- accessibilityLabel: fullLabel,
647
- testID: "weight-badge",
648
- ...props,
649
- children: [
650
- /* @__PURE__ */ jsxs(
651
- reactNative.Text,
652
- {
653
- style: {
654
- fontFamily: '"Space Grotesk", sans-serif',
655
- fontWeight: "600",
656
- fontSize: config.fontSize,
657
- color: textColor
658
- },
659
- children: [
660
- isPr ? "\u2733 " : "",
661
- value,
662
- " ",
663
- unit
664
- ]
665
- }
666
- ),
667
- reps != null && /* @__PURE__ */ jsxs(
668
- reactNative.Text,
669
- {
670
- style: {
671
- fontFamily: '"Space Grotesk", sans-serif',
672
- fontWeight: "600",
673
- fontSize: config.fontSize,
674
- marginLeft: 2,
675
- color: textColor,
676
- opacity: 0.7
677
- },
678
- testID: "weight-badge-repmax",
679
- children: [
680
- reps,
681
- "RM"
682
- ]
683
- }
684
- ),
685
- delta != null && /* @__PURE__ */ jsxs(
686
- reactNative.Text,
687
- {
688
- style: {
689
- fontFamily: '"Space Grotesk", sans-serif',
690
- fontSize: config.fontSize,
691
- marginLeft: 4,
692
- color: delta >= 0 ? "#4caf50" : "#ef5350"
693
- },
694
- testID: "weight-badge-delta",
695
- children: [
696
- delta >= 0 ? "+" : "",
697
- delta,
698
- "%"
699
- ]
700
- }
701
- )
702
- ]
703
- }
704
- );
705
- }
706
- var typeLabels = {
707
- e1rm: "PR e1RM",
708
- weight: "PR Weight",
709
- reps: "PR Reps",
710
- volume: "PR Volume",
711
- velocity: "PR Velocity"
712
- };
713
- function PrBadge({
714
- type = "e1rm",
715
- label: labelProp,
716
- compact = false,
717
- animate = true,
1255
+ // src/theme/tokens/semantic.ts
1256
+ var semanticColorsLight = {
1257
+ // Brand colors (brand-*)
1258
+ "brand-primary": primitiveRamps.orange[400],
1259
+ "brand-primary-light": primitiveRamps.orange[300],
1260
+ "brand-primary-dark": primitiveRamps.orange[500],
1261
+ "brand-primary-subtle": "rgba(255, 121, 0, 0.08)",
1262
+ "brand-primary-hover": primitiveRamps.orange[500],
1263
+ "brand-primary-active": primitiveRamps.orange[600],
1264
+ "brand-secondary": primitiveRamps.cyan[600],
1265
+ "brand-secondary-light": primitiveRamps.cyan[500],
1266
+ "brand-secondary-dark": primitiveRamps.cyan[700],
1267
+ "brand-secondary-subtle": "rgba(48, 123, 155, 0.08)",
1268
+ "brand-secondary-hover": primitiveRamps.cyan[700],
1269
+ "brand-secondary-active": primitiveRamps.cyan[800],
1270
+ // Text on brand backgrounds (on-*)
1271
+ "on-brand-primary": primitiveColors.white,
1272
+ "on-brand-secondary": primitiveColors.white,
1273
+ // Status colors (status-*)
1274
+ "status-success": primitiveRamps.green[300],
1275
+ "status-success-light": primitiveRamps.green[200],
1276
+ "status-success-dark": primitiveRamps.green[600],
1277
+ "status-success-subtle": primitiveRamps.green[50],
1278
+ // Live-session accent — its OWN role, decoupled from success so the two can diverge
1279
+ "status-live": primitiveRamps.green[300],
1280
+ "status-live-muted": primitiveRamps.green[500],
1281
+ "status-error": primitiveRamps.red[600],
1282
+ "status-error-light": primitiveRamps.red[500],
1283
+ "status-error-dark": primitiveRamps.red[700],
1284
+ "status-error-subtle": primitiveRamps.red[50],
1285
+ "status-error-vivid": primitiveColors.redVivid[500],
1286
+ // vivid alert red
1287
+ "status-error-vivid-light": primitiveColors.redVivid[400],
1288
+ "status-error-vivid-dark": primitiveColors.redVivid[700],
1289
+ "status-error-vivid-subtle": "rgba(255, 71, 87, 0.12)",
1290
+ "status-warning": primitiveRamps.amber[300],
1291
+ "status-warning-light": primitiveRamps.amber[200],
1292
+ "status-warning-dark": primitiveRamps.amber[500],
1293
+ "status-warning-subtle": primitiveRamps.amber[50],
1294
+ "status-info": primitiveRamps.blue[500],
1295
+ "status-info-light": primitiveRamps.blue[300],
1296
+ "status-info-dark": primitiveRamps.blue[600],
1297
+ "status-info-subtle": primitiveRamps.blue[50],
1298
+ // Text on status backgrounds (on-status-*)
1299
+ "on-status-success": primitiveColors.white,
1300
+ "on-status-error": primitiveColors.white,
1301
+ "on-status-warning": primitiveColors.white,
1302
+ "on-status-info": primitiveColors.white,
1303
+ // Result/outcome indicators (result-*)
1304
+ "result-improve": "#4caf50",
1305
+ // Green - positive outcome
1306
+ "result-improve-light": "rgba(76, 175, 80, 0.12)",
1307
+ "result-improve-dark": "#248a24",
1308
+ "result-degrade": "#ef5350",
1309
+ // Red - negative outcome
1310
+ "result-degrade-light": "rgba(239, 83, 80, 0.12)",
1311
+ "result-degrade-dark": "#b30000",
1312
+ "result-inconclusive": "#9E9A97",
1313
+ // Gray - no clear result
1314
+ "result-inconclusive-light": "rgba(158, 154, 151, 0.12)",
1315
+ "result-neutral": primitiveColors.neutral[500],
1316
+ // Neutral baseline
1317
+ // Text on result backgrounds (on-result-*)
1318
+ "on-result-improve": primitiveColors.white,
1319
+ "on-result-degrade": primitiveColors.white,
1320
+ "on-result-inconclusive": primitiveColors.white,
1321
+ // Data visualization colors (data-*)
1322
+ // First 10 colors from discrete rainbow optimized for charts
1323
+ "data-1": discreteRainbow[9],
1324
+ // Blue
1325
+ "data-2": discreteRainbow[14],
1326
+ // Green
1327
+ "data-3": discreteRainbow[17],
1328
+ // Yellow
1329
+ "data-4": discreteRainbow[25],
1330
+ // Red
1331
+ "data-5": discreteRainbow[8],
1332
+ // Purple
1333
+ "data-6": discreteRainbow[20],
1334
+ // Orange
1335
+ "data-7": discreteRainbow[13],
1336
+ // Light Blue
1337
+ "data-8": discreteRainbow[15],
1338
+ // Light Green
1339
+ "data-9": discreteRainbow[3],
1340
+ // Lavender
1341
+ "data-10": discreteRainbow[22],
1342
+ // Dark Orange
1343
+ // Text colors (text-*)
1344
+ "text-primary": "#121828",
1345
+ "text-secondary": "#65748B",
1346
+ "text-tertiary": primitiveColors.neutral[400],
1347
+ "text-disabled": "rgba(55, 65, 81, 0.48)",
1348
+ "text-inverse": primitiveColors.white,
1349
+ "text-link": primitiveColors.blue[600],
1350
+ "text-link-hover": primitiveColors.blue[700],
1351
+ // Surface colors (surface-*) - for elevated containers
1352
+ "surface-base": primitiveColors.white,
1353
+ "surface-elevated": primitiveColors.neutral[50],
1354
+ // slightly off-white for elevated cards
1355
+ "surface-raised": primitiveColors.neutral[100],
1356
+ // light gray for raised cards
1357
+ "surface-overlay": primitiveColors.white,
1358
+ "surface-input": primitiveColors.neutral[50],
1359
+ // Input field background (filled variant)
1360
+ // Background colors (background-*)
1361
+ "background-base": "#EBEBEB",
1362
+ "background-default": primitiveColors.white,
1363
+ "background-subtle": primitiveColors.neutral[50],
1364
+ // Border colors (border-*)
1365
+ "border-default": "#E8E9EB",
1366
+ "border-subtle": primitiveColors.neutral[100],
1367
+ "border-strong": primitiveColors.neutral[300],
1368
+ "border-prominent": primitiveColors.neutral[400],
1369
+ // high-visibility divider
1370
+ "border-focus": primitiveColors.blue[600],
1371
+ "border-input": primitiveColors.neutral[300],
1372
+ // Input field border
1373
+ "border-input-hover": primitiveColors.neutral[400],
1374
+ // Input field border on hover
1375
+ "border-input-focus": primitiveColors.blue[600],
1376
+ // Input field border on focus
1377
+ "border-input-error": primitiveColors.red[600],
1378
+ // Input field border on error
1379
+ // Interactive states (interactive-*)
1380
+ "interactive-hover": "rgba(55, 65, 81, 0.04)",
1381
+ "interactive-focus": "rgba(55, 65, 81, 0.12)",
1382
+ "interactive-active": "rgba(55, 65, 81, 0.16)",
1383
+ "interactive-selected": "rgba(55, 65, 81, 0.08)",
1384
+ "interactive-disabled": "rgba(55, 65, 81, 0.12)",
1385
+ "interactive-disabled-text": "rgba(55, 65, 81, 0.26)",
1386
+ // Divider
1387
+ "divider": "#E8E9EB",
1388
+ // Avatar default
1389
+ "avatar-background": primitiveColors.neutral[500],
1390
+ "avatar-text": primitiveColors.white
1391
+ };
1392
+ var semanticColorsDark = {
1393
+ // Brand colors stay the same in dark mode
1394
+ "brand-primary": primitiveRamps.orange[400],
1395
+ "brand-primary-light": primitiveRamps.orange[300],
1396
+ "brand-primary-dark": primitiveRamps.orange[500],
1397
+ "brand-primary-subtle": "rgba(255, 121, 0, 0.12)",
1398
+ "brand-primary-hover": primitiveRamps.orange[300],
1399
+ "brand-primary-active": primitiveRamps.orange[200],
1400
+ "brand-secondary": primitiveRamps.cyan[600],
1401
+ "brand-secondary-light": primitiveRamps.cyan[500],
1402
+ "brand-secondary-dark": primitiveRamps.cyan[700],
1403
+ "brand-secondary-subtle": "rgba(48, 123, 155, 0.12)",
1404
+ "brand-secondary-hover": primitiveRamps.cyan[500],
1405
+ "brand-secondary-active": primitiveRamps.cyan[400],
1406
+ // Text on brand backgrounds
1407
+ "on-brand-primary": primitiveColors.white,
1408
+ "on-brand-secondary": primitiveColors.white,
1409
+ // Status colors
1410
+ "status-success": primitiveRamps.green[300],
1411
+ "status-success-light": primitiveRamps.green[200],
1412
+ "status-success-dark": primitiveRamps.green[600],
1413
+ "status-success-subtle": "rgba(46, 213, 115, 0.12)",
1414
+ // Live-session accent — its OWN role, decoupled from success so the two can diverge
1415
+ "status-live": primitiveRamps.green[300],
1416
+ "status-live-muted": primitiveRamps.green[500],
1417
+ "status-error": primitiveRamps.red[600],
1418
+ "status-error-light": primitiveRamps.red[500],
1419
+ "status-error-dark": primitiveRamps.red[700],
1420
+ "status-error-subtle": "rgba(209, 67, 67, 0.12)",
1421
+ "status-error-vivid": primitiveColors.redVivid[500],
1422
+ "status-error-vivid-light": primitiveColors.redVivid[400],
1423
+ "status-error-vivid-dark": primitiveColors.redVivid[700],
1424
+ "status-error-vivid-subtle": "rgba(255, 71, 87, 0.12)",
1425
+ "status-warning": primitiveRamps.amber[300],
1426
+ "status-warning-light": primitiveRamps.amber[200],
1427
+ "status-warning-dark": primitiveRamps.amber[500],
1428
+ "status-warning-subtle": "rgba(249, 180, 21, 0.12)",
1429
+ "status-info": primitiveRamps.blue[500],
1430
+ "status-info-light": primitiveRamps.blue[300],
1431
+ "status-info-dark": primitiveRamps.blue[600],
1432
+ "status-info-subtle": "rgba(33, 150, 243, 0.12)",
1433
+ // Text on status backgrounds
1434
+ "on-status-success": primitiveColors.white,
1435
+ "on-status-error": primitiveColors.white,
1436
+ "on-status-warning": primitiveColors.white,
1437
+ "on-status-info": primitiveColors.white,
1438
+ // Result/outcome indicators (result-*)
1439
+ "result-improve": "#4caf50",
1440
+ "result-improve-light": "rgba(76, 175, 80, 0.16)",
1441
+ "result-improve-dark": "#248a24",
1442
+ "result-degrade": "#ef5350",
1443
+ "result-degrade-light": "rgba(239, 83, 80, 0.16)",
1444
+ "result-degrade-dark": "#b30000",
1445
+ "result-inconclusive": "#9E9A97",
1446
+ "result-inconclusive-light": "rgba(158, 154, 151, 0.16)",
1447
+ "result-neutral": primitiveColors.neutral[400],
1448
+ // Text on result backgrounds
1449
+ "on-result-improve": primitiveColors.white,
1450
+ "on-result-degrade": primitiveColors.white,
1451
+ "on-result-inconclusive": primitiveColors.white,
1452
+ // Data visualization colors (same in dark mode for consistency)
1453
+ "data-1": discreteRainbow[9],
1454
+ "data-2": discreteRainbow[14],
1455
+ "data-3": discreteRainbow[17],
1456
+ "data-4": discreteRainbow[25],
1457
+ "data-5": discreteRainbow[8],
1458
+ "data-6": discreteRainbow[20],
1459
+ "data-7": discreteRainbow[13],
1460
+ "data-8": discreteRainbow[15],
1461
+ "data-9": discreteRainbow[3],
1462
+ "data-10": discreteRainbow[22],
1463
+ // Text colors - inverted for dark mode
1464
+ "text-primary": primitiveColors.neutral[100],
1465
+ "text-secondary": primitiveColors.neutral[400],
1466
+ "text-tertiary": primitiveColors.neutral[500],
1467
+ "text-disabled": "rgba(255, 255, 255, 0.38)",
1468
+ "text-inverse": primitiveColors.neutral[900],
1469
+ "text-link": "#828DF8",
1470
+ "text-link-hover": primitiveColors.blue[400],
1471
+ // Surface colors - dark backgrounds
1472
+ "surface-base": primitiveColors.charcoal[700],
1473
+ // main surface
1474
+ "surface-elevated": primitiveColors.charcoal[600],
1475
+ // elevated surface
1476
+ "surface-raised": primitiveColors.charcoal[500],
1477
+ // raised surface
1478
+ "surface-overlay": primitiveColors.charcoal[600],
1479
+ // overlay surface
1480
+ "surface-input": primitiveColors.charcoal[600],
1481
+ // input surface
1482
+ // Background colors
1483
+ "background-base": primitiveColors.charcoal[900],
1484
+ // darkest charcoal
1485
+ "background-default": primitiveColors.charcoal[700],
1486
+ // main background
1487
+ "background-subtle": primitiveColors.charcoal[500],
1488
+ // subtle background
1489
+ // Border colors
1490
+ "border-default": primitiveColors.charcoal[400],
1491
+ // default border
1492
+ "border-subtle": primitiveColors.charcoal[500],
1493
+ // subtle border
1494
+ "border-strong": primitiveColors.charcoal[300],
1495
+ // strong border
1496
+ "border-prominent": primitiveColors.charcoal[200],
1497
+ // high-visibility divider
1498
+ "border-focus": "#828DF8",
1499
+ "border-input": primitiveColors.neutral[600],
1500
+ "border-input-hover": primitiveColors.neutral[500],
1501
+ "border-input-focus": "#828DF8",
1502
+ "border-input-error": primitiveColors.red[500],
1503
+ // Interactive states
1504
+ "interactive-hover": "rgba(255, 255, 255, 0.04)",
1505
+ "interactive-focus": "rgba(255, 255, 255, 0.12)",
1506
+ "interactive-active": "rgba(255, 255, 255, 0.16)",
1507
+ "interactive-selected": "rgba(255, 255, 255, 0.08)",
1508
+ "interactive-disabled": "rgba(255, 255, 255, 0.12)",
1509
+ "interactive-disabled-text": "rgba(255, 255, 255, 0.26)",
1510
+ // Divider
1511
+ "divider": primitiveColors.charcoal[400],
1512
+ // divider color
1513
+ // Avatar default
1514
+ "avatar-background": primitiveColors.neutral[600],
1515
+ "avatar-text": primitiveColors.white
1516
+ };
1517
+ function getSemanticColors(mode) {
1518
+ return mode === "dark" ? semanticColorsDark : semanticColorsLight;
1519
+ }
1520
+
1521
+ // src/components/custom/Workout/PrBadge.tsx
1522
+ var BRAND_PRIMARY = getSemanticColors("dark")["brand-primary"];
1523
+ var typeLabels = {
1524
+ e1rm: "PR e1RM",
1525
+ weight: "PR Weight",
1526
+ reps: "PR Reps",
1527
+ volume: "PR Volume",
1528
+ velocity: "PR Velocity"
1529
+ };
1530
+ function PrBadge({
1531
+ type = "e1rm",
1532
+ label: labelProp,
1533
+ compact = false,
1534
+ animate = true,
718
1535
  className,
719
1536
  ...props
720
1537
  }) {
@@ -746,7 +1563,7 @@ function PrBadge({
746
1563
  accessibilityLabel: `Personal record: ${resolvedLabel}`,
747
1564
  testID: "pr-badge-star",
748
1565
  ...props,
749
- children: /* @__PURE__ */ jsx(StarIcon, { size: 14, color: "#FF7900", fill: "#FF7900", strokeWidth: 2 })
1566
+ children: /* @__PURE__ */ jsx(StarIcon, { size: 14, color: BRAND_PRIMARY, fill: BRAND_PRIMARY, strokeWidth: 2 })
750
1567
  }
751
1568
  ) : /* @__PURE__ */ jsx(
752
1569
  BaseBadge,
@@ -760,7 +1577,7 @@ function PrBadge({
760
1577
  {
761
1578
  style: {
762
1579
  fontSize: 12,
763
- color: "#FF7900",
1580
+ color: BRAND_PRIMARY,
764
1581
  fontWeight: "700",
765
1582
  fontFamily: "Inter, sans-serif"
766
1583
  },
@@ -785,6 +1602,204 @@ function PrBadge({
785
1602
  }
786
1603
  return badge;
787
1604
  }
1605
+ var DARK = getSemanticColors("dark");
1606
+ var TEXT_ACTIVE = DARK["text-primary"];
1607
+ var TEXT_MUTED = DARK["text-secondary"];
1608
+ var COL = { set: 36, reps: 44, load: 56, rpe: 36 };
1609
+ var cellText = { fontFamily: "Inter, sans-serif", fontSize: 13, fontWeight: "600" };
1610
+ var typeBadge = {
1611
+ fontSize: 10,
1612
+ fontWeight: "700",
1613
+ fontFamily: "Inter, sans-serif",
1614
+ color: WORKOUT_TOKENS.scale.orange,
1615
+ backgroundColor: "rgba(255,165,2,0.12)",
1616
+ paddingVertical: 1,
1617
+ paddingHorizontal: 5,
1618
+ borderRadius: 3,
1619
+ overflow: "hidden"
1620
+ };
1621
+ function displayReps(set) {
1622
+ return set.state === "done" ? set.reps : set.target.reps;
1623
+ }
1624
+ function displayWeight(set) {
1625
+ return set.state === "done" ? set.weight : set.target.weight;
1626
+ }
1627
+ function plannedReps(set) {
1628
+ if (set.state === "done") return set.velocities.length;
1629
+ if (set.state === "live") return set.target.reps;
1630
+ return set.planned ?? set.target.reps;
1631
+ }
1632
+ function accessibilityLabel(set) {
1633
+ const base = `Set ${set.setNumber}: ${displayReps(set)} reps at ${roundWeight(displayWeight(set))} ${set.unit}`;
1634
+ if (set.state === "live") return `${base}, in progress`;
1635
+ if (set.state === "todo") return `${base}, upcoming`;
1636
+ return base;
1637
+ }
1638
+ function Cell({
1639
+ width,
1640
+ children,
1641
+ testID
1642
+ }) {
1643
+ return /* @__PURE__ */ jsx(reactNative.View, { style: { width, alignItems: "center", justifyContent: "center" }, testID, children });
1644
+ }
1645
+ function RowStrip({ set }) {
1646
+ const zones = set.velocityZones;
1647
+ if (set.state === "live") {
1648
+ return /* @__PURE__ */ jsx(
1649
+ VelocityStrip,
1650
+ {
1651
+ variant: "expanded",
1652
+ showNumbers: false,
1653
+ showInfo: false,
1654
+ height: 24,
1655
+ scale: "fixed",
1656
+ set: { type: "straight", velocities: set.velocities, planned: set.target.reps },
1657
+ zones
1658
+ }
1659
+ );
1660
+ }
1661
+ const velocities = set.state === "done" ? set.velocities : [];
1662
+ return /* @__PURE__ */ jsx(
1663
+ VelocityStrip,
1664
+ {
1665
+ variant: "mini",
1666
+ set: { type: "straight", velocities, planned: plannedReps(set) },
1667
+ zones
1668
+ }
1669
+ );
1670
+ }
1671
+ function SetRow(set) {
1672
+ const live = set.state === "live";
1673
+ const valueColor = live ? TEXT_ACTIVE : TEXT_MUTED;
1674
+ const rpe = set.state === "todo" ? null : set.rpe;
1675
+ return /* @__PURE__ */ jsxs(
1676
+ reactNative.View,
1677
+ {
1678
+ style: { paddingVertical: 6, paddingHorizontal: 8 },
1679
+ accessibilityLabel: accessibilityLabel(set),
1680
+ testID: "set-row",
1681
+ children: [
1682
+ /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { justifyContent: "space-between" }, children: [
1683
+ /* @__PURE__ */ jsx(Cell, { width: COL.set, testID: "set-row-set-number", children: set.setType ? /* @__PURE__ */ jsx(reactNative.Text, { style: typeBadge, testID: "set-row-type-badge", children: set.setType }) : /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor, fontWeight: live ? "700" : "600" }, children: set.setNumber }) }),
1684
+ /* @__PURE__ */ jsx(Cell, { width: COL.reps, testID: "set-row-reps", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor }, children: displayReps(set) }) }),
1685
+ /* @__PURE__ */ jsx(Cell, { width: COL.load, testID: "set-row-weight", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor }, children: roundWeight(displayWeight(set)) }) }),
1686
+ /* @__PURE__ */ jsx(Cell, { width: COL.rpe, testID: "set-row-rpe", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: TEXT_MUTED }, children: rpe != null ? roundRpe(rpe) : "\u2014" }) })
1687
+ ] }),
1688
+ /* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: live ? 6 : 4 }, testID: "set-row-strip", children: /* @__PURE__ */ jsx(RowStrip, { set }) })
1689
+ ]
1690
+ }
1691
+ );
1692
+ }
1693
+ var PREV_COLUMN = { label: "PREV", width: void 0 };
1694
+ function buildColumns(unit, showPrevious) {
1695
+ return [
1696
+ { label: "SET", width: 36 },
1697
+ ...showPrevious ? [PREV_COLUMN] : [],
1698
+ { label: "REPS", width: 44 },
1699
+ { label: unit.toUpperCase(), width: 56 },
1700
+ { label: "RPE", width: 36 }
1701
+ ];
1702
+ }
1703
+ function SetTableHeader({
1704
+ unit = "lbs",
1705
+ showPrevious = true,
1706
+ testID = "table-header"
1707
+ }) {
1708
+ const columns = buildColumns(unit, showPrevious);
1709
+ return /* @__PURE__ */ jsx(
1710
+ reactNative.View,
1711
+ {
1712
+ className: "flex-row",
1713
+ style: {
1714
+ paddingVertical: 8,
1715
+ paddingHorizontal: 8,
1716
+ paddingBottom: 4,
1717
+ ...showPrevious ? {} : { justifyContent: "space-between" }
1718
+ },
1719
+ testID,
1720
+ children: columns.map(({ label, width }) => {
1721
+ const isFlex = width === void 0;
1722
+ return /* @__PURE__ */ jsx(
1723
+ reactNative.View,
1724
+ {
1725
+ style: {
1726
+ ...isFlex ? { minWidth: 44 } : { width, flexShrink: 1 },
1727
+ alignItems: "center",
1728
+ justifyContent: "center",
1729
+ gap: 8
1730
+ },
1731
+ ...isFlex ? { className: "flex-1" } : {},
1732
+ children: /* @__PURE__ */ jsx(
1733
+ reactNative.Text,
1734
+ {
1735
+ className: "text-text-tertiary",
1736
+ style: {
1737
+ fontSize: 10,
1738
+ fontWeight: "600",
1739
+ fontFamily: "Inter, sans-serif",
1740
+ textTransform: "uppercase",
1741
+ letterSpacing: 0.8
1742
+ },
1743
+ children: label
1744
+ }
1745
+ )
1746
+ },
1747
+ label
1748
+ );
1749
+ })
1750
+ }
1751
+ );
1752
+ }
1753
+ var METRIC_FONT = "Inter, sans-serif";
1754
+ function MetricCell({ children, color, fontSize = 11 }) {
1755
+ return /* @__PURE__ */ jsx(
1756
+ reactNative.Text,
1757
+ {
1758
+ style: {
1759
+ fontFamily: METRIC_FONT,
1760
+ fontSize,
1761
+ color,
1762
+ fontWeight: "600",
1763
+ letterSpacing: 1
1764
+ },
1765
+ children
1766
+ }
1767
+ );
1768
+ }
1769
+
1770
+ // src/components/custom/Workout/SetsRepsLoad.tsx
1771
+ var t = getSemanticColors("dark");
1772
+ var TIMES = "\xD7";
1773
+ var AT = "@";
1774
+ function SetsRepsLoad({
1775
+ sets,
1776
+ reps,
1777
+ load,
1778
+ unit = "lb",
1779
+ className,
1780
+ ...props
1781
+ }) {
1782
+ const value = t["text-primary"];
1783
+ const sep = t["text-tertiary"];
1784
+ return /* @__PURE__ */ jsxs(
1785
+ reactNative.View,
1786
+ {
1787
+ className,
1788
+ style: { flexDirection: "row", alignItems: "baseline" },
1789
+ accessibilityLabel: `${sets} sets of ${reps} reps at ${load} ${unit}`,
1790
+ testID: "sets-reps-load",
1791
+ ...props,
1792
+ children: [
1793
+ /* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
1794
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
1795
+ /* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
1796
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
1797
+ /* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
1798
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
1799
+ ]
1800
+ }
1801
+ );
1802
+ }
788
1803
  function hexToRgb(hex) {
789
1804
  const cleaned = hex.replace("#", "");
790
1805
  let r, g, b;
@@ -1094,452 +2109,54 @@ function alpha(color, opacity) {
1094
2109
  }
1095
2110
  return color;
1096
2111
  }
2112
+ var t2 = getSemanticColors("dark");
2113
+ var TEMPO_PACING = {
2114
+ behindThresholdPct: 0.15,
2115
+ minPhaseDurationMs: 500
2116
+ };
2117
+ function getTempoPacingState(elapsedMs, targetMs) {
2118
+ if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
2119
+ const ratio = elapsedMs / targetMs;
2120
+ if (ratio > 1 + TEMPO_PACING.behindThresholdPct) return "behind";
2121
+ return "on-pace";
2122
+ }
2123
+ function getTempoFillPct(elapsedMs, targetMs) {
2124
+ if (!targetMs) return 100;
2125
+ return Math.min(100, elapsedMs / targetMs * 100);
2126
+ }
2127
+ ({
2128
+ concentric: { color: t2["status-success"]},
2129
+ hold: { color: t2["brand-primary"]},
2130
+ eccentric: { color: t2["status-warning"]}
2131
+ });
1097
2132
 
1098
- // src/theme/tokens/primitives.ts
1099
- var primitiveColors = {
1100
- // Blue scale (primary brand)
1101
- blue: {
1102
- 400: "#60A5FA",
1103
- 600: "#5048E5",
1104
- // Primary main
1105
- 700: "#3832A0"},
1106
- // Teal scale (success)
1107
- teal: {
1108
- 50: "#F0FDFA",
1109
- 400: "#43C6B7",
1110
- // Success light
1111
- 500: "#14B8A6",
1112
- 700: "#0E8074"},
1113
- // Red scale (error)
1114
- red: {
1115
- 50: "#FEF2F2",
1116
- 400: "#DA6868",
1117
- // Error light
1118
- 500: "#EF4444",
1119
- 600: "#D14343",
1120
- // Error main
1121
- 700: "#922E2E"},
1122
- // Amber scale (warning)
1123
- amber: {
1124
- 50: "#FFFBEB",
1125
- 400: "#FFBF4C",
1126
- // Warning light
1127
- 500: "#FFB020",
1128
- // Warning main
1129
- 600: "#B27B16"},
1130
- // Sky/Blue scale (info)
1131
- sky: {
1132
- 50: "#F0F9FF",
1133
- 400: "#64B6F7",
1134
- // Info light
1135
- 500: "#2196F3",
1136
- 700: "#0B79D0"},
1137
- // Neutral/Gray scale
1138
- neutral: {
1139
- 50: "#FAFAFA",
1140
- 100: "#F3F4F6",
1141
- 300: "#D1D5DB",
1142
- 400: "#9CA3AF",
1143
- 500: "#6B7280",
1144
- 600: "#4B5563",
1145
- 900: "#111827"},
1146
- // Orange scale (accent/brand color)
1147
- accent: {
1148
- 200: "#FF9630",
1149
- 300: "#FF8500",
1150
- 400: "#FF7900",
1151
- 500: "#F56C00",
1152
- 600: "#E06D10",
1153
- 700: "#D0620C"},
1154
- // Charcoal scale (dark backgrounds)
1155
- charcoal: {
1156
- 300: "#2C2C2C",
1157
- 400: "#1F1F1F",
1158
- 500: "#1C1C1C",
1159
- 600: "#191919",
1160
- 700: "#161616",
1161
- 900: "#101010"
1162
- },
1163
- // Steel scale (cool accent)
1164
- steel: {
1165
- 300: "#678498",
1166
- 400: "#557488",
1167
- 500: "#406D87",
1168
- 600: "#39617A",
1169
- 700: "#32556D"},
1170
- // Pure colors
1171
- white: "#FFFFFF"};
1172
- var discreteRainbow = [
1173
- "#E8ECFB",
1174
- "#D9CCE3",
1175
- "#D1BBD7",
1176
- "#CAACCB",
1177
- "#BA8DB4",
1178
- "#AE76A3",
1179
- "#AA6F9E",
1180
- "#994F88",
1181
- "#882E72",
1182
- "#1965B0",
1183
- "#437DBF",
1184
- "#5289C7",
1185
- "#6195CF",
1186
- "#7BAFDE",
1187
- "#4EB265",
1188
- "#90C987",
1189
- "#CAE0AB",
1190
- "#F7F056",
1191
- "#F7CB45",
1192
- "#F6C141",
1193
- "#F4A736",
1194
- "#F1932D",
1195
- "#EE8026",
1196
- "#E8601C",
1197
- "#E65518",
1198
- "#DC050C",
1199
- "#A5170E",
1200
- "#72190E",
1201
- "#42150A"
1202
- ];
1203
-
1204
- // src/theme/tokens/semantic.ts
1205
- var semanticColorsLight = {
1206
- // Brand colors (brand-*)
1207
- "brand-primary": primitiveColors.accent[400],
1208
- // #FF7900
1209
- "brand-primary-light": primitiveColors.accent[200],
1210
- // #FF9630
1211
- "brand-primary-dark": primitiveColors.accent[700],
1212
- // #D0620C
1213
- "brand-primary-subtle": "rgba(255, 121, 0, 0.08)",
1214
- "brand-primary-hover": primitiveColors.accent[500],
1215
- "brand-primary-active": primitiveColors.accent[600],
1216
- "brand-secondary": primitiveColors.steel[500],
1217
- // #406D87
1218
- "brand-secondary-light": primitiveColors.steel[300],
1219
- // #678498
1220
- "brand-secondary-dark": primitiveColors.steel[700],
1221
- // #32556D
1222
- "brand-secondary-subtle": "rgba(64, 109, 135, 0.08)",
1223
- "brand-secondary-hover": primitiveColors.steel[600],
1224
- "brand-secondary-active": primitiveColors.steel[700],
1225
- // Text on brand backgrounds (on-*)
1226
- "on-brand-primary": primitiveColors.white,
1227
- "on-brand-secondary": primitiveColors.white,
1228
- // Status colors (status-*)
1229
- "status-success": primitiveColors.teal[500],
1230
- // #14B8A6
1231
- "status-success-light": primitiveColors.teal[400],
1232
- // #43C6B7
1233
- "status-success-dark": primitiveColors.teal[700],
1234
- // #0E8074
1235
- "status-success-subtle": primitiveColors.teal[50],
1236
- "status-error": primitiveColors.red[600],
1237
- // #D14343
1238
- "status-error-light": primitiveColors.red[400],
1239
- // #DA6868
1240
- "status-error-dark": primitiveColors.red[700],
1241
- // #922E2E
1242
- "status-error-subtle": primitiveColors.red[50],
1243
- "status-warning": primitiveColors.amber[500],
1244
- // #FFB020
1245
- "status-warning-light": primitiveColors.amber[400],
1246
- // #FFBF4C
1247
- "status-warning-dark": primitiveColors.amber[600],
1248
- // #B27B16
1249
- "status-warning-subtle": primitiveColors.amber[50],
1250
- "status-info": primitiveColors.sky[500],
1251
- // #2196F3
1252
- "status-info-light": primitiveColors.sky[400],
1253
- // #64B6F7
1254
- "status-info-dark": primitiveColors.sky[700],
1255
- // #0B79D0
1256
- "status-info-subtle": primitiveColors.sky[50],
1257
- // Text on status backgrounds (on-status-*)
1258
- "on-status-success": primitiveColors.white,
1259
- "on-status-error": primitiveColors.white,
1260
- "on-status-warning": primitiveColors.white,
1261
- "on-status-info": primitiveColors.white,
1262
- // Result/outcome indicators (result-*)
1263
- "result-improve": "#4caf50",
1264
- // Green - positive outcome
1265
- "result-improve-light": "rgba(76, 175, 80, 0.12)",
1266
- "result-improve-dark": "#248a24",
1267
- "result-degrade": "#ef5350",
1268
- // Red - negative outcome
1269
- "result-degrade-light": "rgba(239, 83, 80, 0.12)",
1270
- "result-degrade-dark": "#b30000",
1271
- "result-inconclusive": "#9E9A97",
1272
- // Gray - no clear result
1273
- "result-inconclusive-light": "rgba(158, 154, 151, 0.12)",
1274
- "result-neutral": primitiveColors.neutral[500],
1275
- // Neutral baseline
1276
- // Text on result backgrounds (on-result-*)
1277
- "on-result-improve": primitiveColors.white,
1278
- "on-result-degrade": primitiveColors.white,
1279
- "on-result-inconclusive": primitiveColors.white,
1280
- // Data visualization colors (data-*)
1281
- // First 10 colors from discrete rainbow optimized for charts
1282
- "data-1": discreteRainbow[9],
1283
- // #1965B0 - Blue
1284
- "data-2": discreteRainbow[14],
1285
- // #4EB265 - Green
1286
- "data-3": discreteRainbow[17],
1287
- // #F7F056 - Yellow
1288
- "data-4": discreteRainbow[25],
1289
- // #DC050C - Red
1290
- "data-5": discreteRainbow[8],
1291
- // #882E72 - Purple
1292
- "data-6": discreteRainbow[20],
1293
- // #F4A736 - Orange
1294
- "data-7": discreteRainbow[13],
1295
- // #7BAFDE - Light Blue
1296
- "data-8": discreteRainbow[15],
1297
- // #90C987 - Light Green
1298
- "data-9": discreteRainbow[3],
1299
- // #CAACCB - Lavender
1300
- "data-10": discreteRainbow[22],
1301
- // #EE8026 - Dark Orange
1302
- // Text colors (text-*)
1303
- "text-primary": "#121828",
1304
- "text-secondary": "#65748B",
1305
- "text-tertiary": primitiveColors.neutral[400],
1306
- "text-disabled": "rgba(55, 65, 81, 0.48)",
1307
- "text-inverse": primitiveColors.white,
1308
- "text-link": primitiveColors.blue[600],
1309
- "text-link-hover": primitiveColors.blue[700],
1310
- // Surface colors (surface-*) - for elevated containers
1311
- "surface-base": primitiveColors.white,
1312
- "surface-elevated": primitiveColors.neutral[50],
1313
- // #FAFAFA - slightly off-white for elevated cards
1314
- "surface-raised": primitiveColors.neutral[100],
1315
- // #F3F4F6 - light gray for raised cards
1316
- "surface-overlay": primitiveColors.white,
1317
- "surface-input": primitiveColors.neutral[50],
1318
- // Input field background (filled variant)
1319
- // Background colors (background-*)
1320
- "background-base": "#EBEBEB",
1321
- "background-default": primitiveColors.white,
1322
- "background-subtle": primitiveColors.neutral[50],
1323
- // Border colors (border-*)
1324
- "border-default": "#E8E9EB",
1325
- "border-subtle": primitiveColors.neutral[100],
1326
- "border-strong": primitiveColors.neutral[300],
1327
- "border-focus": primitiveColors.blue[600],
1328
- "border-input": primitiveColors.neutral[300],
1329
- // Input field border
1330
- "border-input-hover": primitiveColors.neutral[400],
1331
- // Input field border on hover
1332
- "border-input-focus": primitiveColors.blue[600],
1333
- // Input field border on focus
1334
- "border-input-error": primitiveColors.red[600],
1335
- // Input field border on error
1336
- // Interactive states (interactive-*)
1337
- "interactive-hover": "rgba(55, 65, 81, 0.04)",
1338
- "interactive-focus": "rgba(55, 65, 81, 0.12)",
1339
- "interactive-active": "rgba(55, 65, 81, 0.16)",
1340
- "interactive-selected": "rgba(55, 65, 81, 0.08)",
1341
- "interactive-disabled": "rgba(55, 65, 81, 0.12)",
1342
- "interactive-disabled-text": "rgba(55, 65, 81, 0.26)",
1343
- // Divider
1344
- "divider": "#E8E9EB",
1345
- // Avatar default
1346
- "avatar-background": primitiveColors.neutral[500],
1347
- "avatar-text": primitiveColors.white
1348
- };
1349
- var semanticColorsDark = {
1350
- // Brand colors stay the same in dark mode
1351
- "brand-primary": primitiveColors.accent[400],
1352
- "brand-primary-light": primitiveColors.accent[200],
1353
- "brand-primary-dark": primitiveColors.accent[700],
1354
- "brand-primary-subtle": "rgba(255, 121, 0, 0.12)",
1355
- "brand-primary-hover": primitiveColors.accent[300],
1356
- "brand-primary-active": primitiveColors.accent[200],
1357
- "brand-secondary": primitiveColors.steel[500],
1358
- "brand-secondary-light": primitiveColors.steel[300],
1359
- "brand-secondary-dark": primitiveColors.steel[700],
1360
- "brand-secondary-subtle": "rgba(64, 109, 135, 0.12)",
1361
- "brand-secondary-hover": primitiveColors.steel[400],
1362
- "brand-secondary-active": primitiveColors.steel[300],
1363
- // Text on brand backgrounds
1364
- "on-brand-primary": primitiveColors.white,
1365
- "on-brand-secondary": primitiveColors.white,
1366
- // Status colors
1367
- "status-success": primitiveColors.teal[500],
1368
- "status-success-light": primitiveColors.teal[400],
1369
- "status-success-dark": primitiveColors.teal[700],
1370
- "status-success-subtle": "rgba(20, 184, 166, 0.12)",
1371
- "status-error": primitiveColors.red[600],
1372
- "status-error-light": primitiveColors.red[400],
1373
- "status-error-dark": primitiveColors.red[700],
1374
- "status-error-subtle": "rgba(209, 67, 67, 0.12)",
1375
- "status-warning": primitiveColors.amber[500],
1376
- "status-warning-light": primitiveColors.amber[400],
1377
- "status-warning-dark": primitiveColors.amber[600],
1378
- "status-warning-subtle": "rgba(255, 176, 32, 0.12)",
1379
- "status-info": primitiveColors.sky[500],
1380
- "status-info-light": primitiveColors.sky[400],
1381
- "status-info-dark": primitiveColors.sky[700],
1382
- "status-info-subtle": "rgba(33, 150, 243, 0.12)",
1383
- // Text on status backgrounds
1384
- "on-status-success": primitiveColors.white,
1385
- "on-status-error": primitiveColors.white,
1386
- "on-status-warning": primitiveColors.white,
1387
- "on-status-info": primitiveColors.white,
1388
- // Result/outcome indicators (result-*)
1389
- "result-improve": "#4caf50",
1390
- "result-improve-light": "rgba(76, 175, 80, 0.16)",
1391
- "result-improve-dark": "#248a24",
1392
- "result-degrade": "#ef5350",
1393
- "result-degrade-light": "rgba(239, 83, 80, 0.16)",
1394
- "result-degrade-dark": "#b30000",
1395
- "result-inconclusive": "#9E9A97",
1396
- "result-inconclusive-light": "rgba(158, 154, 151, 0.16)",
1397
- "result-neutral": primitiveColors.neutral[400],
1398
- // Text on result backgrounds
1399
- "on-result-improve": primitiveColors.white,
1400
- "on-result-degrade": primitiveColors.white,
1401
- "on-result-inconclusive": primitiveColors.white,
1402
- // Data visualization colors (same in dark mode for consistency)
1403
- "data-1": discreteRainbow[9],
1404
- "data-2": discreteRainbow[14],
1405
- "data-3": discreteRainbow[17],
1406
- "data-4": discreteRainbow[25],
1407
- "data-5": discreteRainbow[8],
1408
- "data-6": discreteRainbow[20],
1409
- "data-7": discreteRainbow[13],
1410
- "data-8": discreteRainbow[15],
1411
- "data-9": discreteRainbow[3],
1412
- "data-10": discreteRainbow[22],
1413
- // Text colors - inverted for dark mode
1414
- "text-primary": primitiveColors.neutral[100],
1415
- "text-secondary": primitiveColors.neutral[400],
1416
- "text-tertiary": primitiveColors.neutral[500],
1417
- "text-disabled": "rgba(255, 255, 255, 0.38)",
1418
- "text-inverse": primitiveColors.neutral[900],
1419
- "text-link": "#828DF8",
1420
- "text-link-hover": primitiveColors.blue[400],
1421
- // Surface colors - dark backgrounds
1422
- "surface-base": primitiveColors.charcoal[700],
1423
- // #161616 - main surface
1424
- "surface-elevated": primitiveColors.charcoal[600],
1425
- // #191919 - elevated surface
1426
- "surface-raised": primitiveColors.charcoal[500],
1427
- // #1C1C1C - raised surface
1428
- "surface-overlay": primitiveColors.charcoal[600],
1429
- // #191919 - overlay surface
1430
- "surface-input": primitiveColors.charcoal[600],
1431
- // #191919 - input surface
1432
- // Background colors
1433
- "background-base": primitiveColors.charcoal[900],
1434
- // #101010 - darkest charcoal
1435
- "background-default": primitiveColors.charcoal[700],
1436
- // #161616 - main background
1437
- "background-subtle": primitiveColors.charcoal[500],
1438
- // #1C1C1C - subtle background
1439
- // Border colors
1440
- "border-default": primitiveColors.charcoal[400],
1441
- // #1F1F1F - default border
1442
- "border-subtle": primitiveColors.charcoal[500],
1443
- // #1C1C1C - subtle border
1444
- "border-strong": primitiveColors.charcoal[300],
1445
- // #2C2C2C - strong border
1446
- "border-focus": "#828DF8",
1447
- "border-input": primitiveColors.neutral[600],
1448
- "border-input-hover": primitiveColors.neutral[500],
1449
- "border-input-focus": "#828DF8",
1450
- "border-input-error": primitiveColors.red[500],
1451
- // Interactive states
1452
- "interactive-hover": "rgba(255, 255, 255, 0.04)",
1453
- "interactive-focus": "rgba(255, 255, 255, 0.12)",
1454
- "interactive-active": "rgba(255, 255, 255, 0.16)",
1455
- "interactive-selected": "rgba(255, 255, 255, 0.08)",
1456
- "interactive-disabled": "rgba(255, 255, 255, 0.12)",
1457
- "interactive-disabled-text": "rgba(255, 255, 255, 0.26)",
1458
- // Divider
1459
- "divider": primitiveColors.charcoal[400],
1460
- // #1F1F1F - divider color
1461
- // Avatar default
1462
- "avatar-background": primitiveColors.neutral[600],
1463
- "avatar-text": primitiveColors.white
1464
- };
1465
- function getSemanticColors(mode) {
1466
- return mode === "dark" ? semanticColorsDark : semanticColorsLight;
1467
- }
1468
-
1469
- // src/components/custom/Workout/TempoBar.tsx
1470
- var t = getSemanticColors("dark");
1471
- var TEMPO_PACING = {
1472
- behindThresholdPct: 0.15,
1473
- minPhaseDurationMs: 500
1474
- };
1475
- function getTempoPacingState(elapsedMs, targetMs) {
1476
- if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
1477
- const ratio = elapsedMs / targetMs;
1478
- if (ratio > 1 + TEMPO_PACING.behindThresholdPct) return "behind";
1479
- return "on-pace";
1480
- }
1481
- function getTempoFillPct(elapsedMs, targetMs) {
1482
- if (!targetMs) return 100;
1483
- return Math.min(100, elapsedMs / targetMs * 100);
1484
- }
1485
- ({
1486
- concentric: { color: t["status-success"]},
1487
- hold: { color: t["brand-primary"]},
1488
- eccentric: { color: t["status-warning"]}
1489
- });
1490
-
1491
- // src/components/custom/Workout/TempoDisplay.tsx
1492
- var INTER = "Inter, sans-serif";
1493
- var TEXT_TERTIARY = "#6B7280";
1494
- var STATUS_ERROR = "#D14343";
1495
- var phaseColors = {
1496
- eccentric: "#FFB020",
1497
- // status-warning (amber)
1498
- pauseBottom: TEXT_TERTIARY,
1499
- concentric: "#14B8A6",
1500
- // status-success (teal)
1501
- pauseTop: TEXT_TERTIARY,
1502
- dash: TEXT_TERTIARY
1503
- };
1504
- function TempoValue({
1505
- value,
1506
- color,
1507
- fontSize
1508
- }) {
1509
- return /* @__PURE__ */ jsx(
1510
- reactNative.Text,
1511
- {
1512
- style: {
1513
- fontFamily: INTER,
1514
- fontSize,
1515
- color,
1516
- fontWeight: "600",
1517
- letterSpacing: 1
1518
- },
1519
- children: value
1520
- }
1521
- );
1522
- }
1523
- function TempoSeparator({ color, fontSize }) {
1524
- return /* @__PURE__ */ jsx(
1525
- reactNative.Text,
1526
- {
1527
- style: {
1528
- fontFamily: INTER,
1529
- fontSize,
1530
- color,
1531
- fontWeight: "600",
1532
- letterSpacing: 1
1533
- },
1534
- children: "-"
1535
- }
1536
- );
1537
- }
1538
- var LIVE_PHASES = [
1539
- { key: "eccentric", color: phaseColors.eccentric },
1540
- { key: "pauseBottom", color: phaseColors.pauseBottom },
1541
- { key: "concentric", color: phaseColors.concentric },
1542
- { key: "pauseTop", color: phaseColors.pauseTop }
2133
+ // src/components/custom/Workout/TempoDisplay.tsx
2134
+ var t3 = getSemanticColors("dark");
2135
+ var INTER = "Inter, sans-serif";
2136
+ var TEXT_TERTIARY = "#6B7280";
2137
+ var STATUS_ERROR = t3["status-error"];
2138
+ var phaseColors = {
2139
+ eccentric: t3["status-warning"],
2140
+ pauseBottom: TEXT_TERTIARY,
2141
+ concentric: t3["status-success"],
2142
+ pauseTop: TEXT_TERTIARY,
2143
+ dash: TEXT_TERTIARY
2144
+ };
2145
+ function TempoValue({
2146
+ value,
2147
+ color,
2148
+ fontSize
2149
+ }) {
2150
+ return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: value });
2151
+ }
2152
+ function TempoSeparator({ color, fontSize }) {
2153
+ return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: "-" });
2154
+ }
2155
+ var LIVE_PHASES = [
2156
+ { key: "eccentric", color: phaseColors.eccentric },
2157
+ { key: "pauseBottom", color: phaseColors.pauseBottom },
2158
+ { key: "concentric", color: phaseColors.concentric },
2159
+ { key: "pauseTop", color: phaseColors.pauseTop }
1543
2160
  ];
1544
2161
  function LiveTempoCell({
1545
2162
  value,
@@ -1595,6 +2212,7 @@ function TempoDisplay({
1595
2212
  tempo,
1596
2213
  size = "md",
1597
2214
  colored = true,
2215
+ showLabel = true,
1598
2216
  showInfo = true,
1599
2217
  live,
1600
2218
  onPress,
@@ -1628,7 +2246,7 @@ function TempoDisplay({
1628
2246
  },
1629
2247
  ...props,
1630
2248
  children: [
1631
- /* @__PURE__ */ jsx(
2249
+ showLabel && /* @__PURE__ */ jsx(
1632
2250
  reactNative.Text,
1633
2251
  {
1634
2252
  style: {
@@ -1706,301 +2324,177 @@ function TempoDisplay({
1706
2324
  testID: "tempo-tooltip",
1707
2325
  children: [
1708
2326
  /* @__PURE__ */ jsxs(
1709
- reactNative.View,
1710
- {
1711
- style: {
1712
- backgroundColor: "#191919",
1713
- borderRadius: 6,
1714
- paddingVertical: 8,
1715
- paddingHorizontal: 12,
1716
- borderWidth: 1,
1717
- borderColor: "#2C2C2C"
1718
- },
1719
- children: [
1720
- /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
1721
- "Eccentric: ",
1722
- eccentric,
1723
- "s"
1724
- ] }),
1725
- /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
1726
- "Pause (bottom): ",
1727
- pauseBottom,
1728
- "s"
1729
- ] }),
1730
- /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
1731
- "Concentric: ",
1732
- concentric,
1733
- "s"
1734
- ] }),
1735
- /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
1736
- "Pause (top): ",
1737
- pauseTop,
1738
- "s"
1739
- ] })
1740
- ]
1741
- }
1742
- ),
1743
- /* @__PURE__ */ jsx(
1744
- reactNative.View,
1745
- {
1746
- style: {
1747
- width: 0,
1748
- height: 0,
1749
- borderLeftWidth: 5,
1750
- borderRightWidth: 5,
1751
- borderTopWidth: 5,
1752
- borderLeftColor: "transparent",
1753
- borderRightColor: "transparent",
1754
- borderTopColor: "#2C2C2C"
1755
- }
1756
- }
1757
- )
1758
- ]
1759
- }
1760
- )
1761
- ]
1762
- }
1763
- );
1764
- }
1765
- function formatAccessibilityLabel(setNumber, reps, weight, unit) {
1766
- const repsText = reps != null ? `${reps} reps` : "no reps";
1767
- const weightText = weight != null ? `at ${roundWeight(weight)} ${unit}` : "";
1768
- return `Set ${setNumber}: ${repsText}${weightText ? ` ${weightText}` : ""}`;
1769
- }
1770
- function SetRow({
1771
- mode,
1772
- setNumber,
1773
- previous,
1774
- reps,
1775
- weight,
1776
- rpe,
1777
- unit,
1778
- velocities,
1779
- velocityZones,
1780
- velocityLiveRepIndex,
1781
- velocityExpanded,
1782
- onVelocityToggle,
1783
- setType,
1784
- prBadges,
1785
- isNextSet,
1786
- targets
1787
- }) {
1788
- const isActive = mode === "active";
1789
- const isCompleted = mode === "completed";
1790
- const rowStyle = {
1791
- flexDirection: "row",
1792
- alignItems: "center",
1793
- padding: 7,
1794
- paddingHorizontal: 8,
1795
- gap: 8,
1796
- borderRadius: 8
1797
- };
1798
- if (isActive && isNextSet) {
1799
- rowStyle.backgroundColor = "rgba(255,121,0,0.06)";
1800
- rowStyle.borderWidth = 1;
1801
- rowStyle.borderColor = "rgba(255,121,0,0.15)";
1802
- }
1803
- if (isCompleted && !isNextSet) {
1804
- rowStyle.opacity = 0.55;
1805
- }
1806
- return /* @__PURE__ */ jsxs(
1807
- reactNative.View,
1808
- {
1809
- style: rowStyle,
1810
- accessibilityLabel: formatAccessibilityLabel(
1811
- setNumber,
1812
- reps,
1813
- weight,
1814
- unit
1815
- ),
1816
- testID: "set-row",
1817
- children: [
1818
- /* @__PURE__ */ jsx(
1819
- reactNative.View,
1820
- {
1821
- style: { width: 36, alignItems: "center", justifyContent: "center" },
1822
- testID: "set-row-set-number",
1823
- children: setType ? /* @__PURE__ */ jsx(
1824
- reactNative.Text,
1825
- {
1826
- style: {
1827
- fontSize: 10,
1828
- fontWeight: "700",
1829
- fontFamily: "Inter, sans-serif",
1830
- color: "#ffa502",
1831
- backgroundColor: "rgba(255,165,2,0.12)",
1832
- paddingVertical: 1,
1833
- paddingHorizontal: 5,
1834
- borderRadius: 3,
1835
- overflow: "hidden"
1836
- },
1837
- testID: "set-row-type-badge",
1838
- children: setType
1839
- }
1840
- ) : /* @__PURE__ */ jsx(
1841
- reactNative.Text,
1842
- {
1843
- className: "text-text-secondary",
1844
- style: {
1845
- fontSize: 13,
1846
- fontWeight: "600",
1847
- fontFamily: "Inter, sans-serif"
1848
- },
1849
- children: setNumber
1850
- }
1851
- )
1852
- }
1853
- ),
1854
- /* @__PURE__ */ jsx(
1855
- reactNative.View,
1856
- {
1857
- className: "flex-1",
1858
- testID: "set-row-previous",
1859
- children: /* @__PURE__ */ jsx(
1860
- reactNative.Text,
1861
- {
1862
- className: "text-text-secondary",
1863
- style: {
1864
- fontSize: 12,
1865
- fontFamily: "Inter, sans-serif"
1866
- },
1867
- children: previous ? `${previous.reps} x ${roundWeight(previous.weight)}` : "\u2014"
1868
- }
1869
- )
1870
- }
1871
- ),
1872
- /* @__PURE__ */ jsx(
1873
- reactNative.View,
1874
- {
1875
- style: { width: 44, alignItems: "center", justifyContent: "center" },
1876
- testID: "set-row-reps",
1877
- children: isActive && reps == null && targets ? /* @__PURE__ */ jsx(
1878
- reactNative.Text,
1879
- {
1880
- className: "text-text-tertiary",
1881
- style: {
1882
- fontSize: 13,
1883
- fontStyle: "italic",
1884
- fontFamily: "Inter, sans-serif"
1885
- },
1886
- testID: "set-row-target-reps",
1887
- children: targets.reps
1888
- }
1889
- ) : /* @__PURE__ */ jsx(
1890
- reactNative.Text,
1891
- {
1892
- className: reps != null ? "text-text-primary" : "text-text-tertiary",
1893
- style: {
1894
- fontSize: 14,
1895
- fontWeight: "600",
1896
- fontFamily: "Inter, sans-serif"
1897
- },
1898
- children: reps != null ? reps : "\u2014"
1899
- }
1900
- )
1901
- }
1902
- ),
1903
- /* @__PURE__ */ jsx(
1904
- reactNative.View,
1905
- {
1906
- style: { width: 56, alignItems: "center", justifyContent: "center" },
1907
- testID: "set-row-weight",
1908
- children: isActive && weight == null && targets ? /* @__PURE__ */ jsx(
1909
- reactNative.Text,
1910
- {
1911
- className: "text-text-tertiary",
1912
- style: {
1913
- fontSize: 13,
1914
- fontStyle: "italic",
1915
- fontFamily: "Inter, sans-serif"
1916
- },
1917
- testID: "set-row-target-weight",
1918
- children: roundWeight(targets.weight)
1919
- }
1920
- ) : /* @__PURE__ */ jsx(
1921
- reactNative.Text,
1922
- {
1923
- className: weight != null ? "text-text-primary" : "text-text-tertiary",
1924
- style: {
1925
- fontSize: 14,
1926
- fontWeight: "600",
1927
- fontFamily: "Inter, sans-serif"
1928
- },
1929
- children: weight != null ? roundWeight(weight) : "\u2014"
1930
- }
1931
- )
1932
- }
1933
- ),
1934
- /* @__PURE__ */ jsx(
1935
- reactNative.View,
1936
- {
1937
- style: { width: 36, alignItems: "center", justifyContent: "center" },
1938
- testID: "set-row-rpe",
1939
- children: /* @__PURE__ */ jsx(
1940
- reactNative.Text,
1941
- {
1942
- className: rpe != null ? void 0 : "text-text-tertiary",
1943
- style: {
1944
- fontSize: 13,
1945
- fontWeight: "600",
1946
- fontFamily: "Inter, sans-serif",
1947
- // rpeColor returns a concrete hex (native-safe); only the tertiary
1948
- // fallback needs the className token.
1949
- color: rpe != null ? rpeColor(rpe) : void 0
1950
- },
1951
- children: rpe != null ? roundRpe(rpe) : "\u2014"
1952
- }
1953
- )
1954
- }
1955
- ),
1956
- prBadges && prBadges.length > 0 && /* @__PURE__ */ jsx(
1957
- reactNative.View,
1958
- {
1959
- className: "flex-row items-center",
1960
- style: { gap: 4 },
1961
- testID: "set-row-pr-badges",
1962
- children: prBadges.map((badge, i) => /* @__PURE__ */ jsx(
1963
- PrBadge,
1964
- {
1965
- type: badge.type,
1966
- label: badge.label,
1967
- animate: false,
1968
- compact: true
1969
- },
1970
- i
1971
- ))
1972
- }
1973
- ),
1974
- velocities && velocities.length > 0 && /* @__PURE__ */ jsx(
1975
- reactNative.View,
1976
- {
1977
- style: { position: "absolute", bottom: 0, left: 8, right: 8 },
1978
- testID: "set-row-velocity-strip",
1979
- children: /* @__PURE__ */ jsx(
1980
- VelocityStrip,
1981
- {
1982
- velocities,
1983
- zones: velocityZones,
1984
- liveRepIndex: velocityLiveRepIndex,
1985
- expanded: velocityExpanded,
1986
- onToggle: onVelocityToggle,
1987
- variant: onVelocityToggle ? "full" : "mini"
1988
- }
1989
- )
2327
+ reactNative.View,
2328
+ {
2329
+ style: {
2330
+ backgroundColor: "#191919",
2331
+ borderRadius: 6,
2332
+ paddingVertical: 8,
2333
+ paddingHorizontal: 12,
2334
+ borderWidth: 1,
2335
+ borderColor: "#2C2C2C"
2336
+ },
2337
+ children: [
2338
+ /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
2339
+ "Eccentric: ",
2340
+ eccentric,
2341
+ "s"
2342
+ ] }),
2343
+ /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
2344
+ "Pause (bottom): ",
2345
+ pauseBottom,
2346
+ "s"
2347
+ ] }),
2348
+ /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
2349
+ "Concentric: ",
2350
+ concentric,
2351
+ "s"
2352
+ ] }),
2353
+ /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 10, lineHeight: 16, color: "#9CA3AF", fontFamily: INTER }, children: [
2354
+ "Pause (top): ",
2355
+ pauseTop,
2356
+ "s"
2357
+ ] })
2358
+ ]
2359
+ }
2360
+ ),
2361
+ /* @__PURE__ */ jsx(
2362
+ reactNative.View,
2363
+ {
2364
+ style: {
2365
+ width: 0,
2366
+ height: 0,
2367
+ borderLeftWidth: 5,
2368
+ borderRightWidth: 5,
2369
+ borderTopWidth: 5,
2370
+ borderLeftColor: "transparent",
2371
+ borderRightColor: "transparent",
2372
+ borderTopColor: "#2C2C2C"
2373
+ }
2374
+ }
2375
+ )
2376
+ ]
1990
2377
  }
1991
2378
  )
1992
2379
  ]
1993
2380
  }
1994
2381
  );
1995
2382
  }
1996
- function resolveColor(token, mode = "dark") {
1997
- return reactNative.Platform.OS === "web" ? `var(--color-${token})` : getSemanticColors(mode)[token];
2383
+ var t4 = getSemanticColors("dark");
2384
+ var INDICATOR_CONFIG = {
2385
+ imbalance: { Icon: ScaleIcon, color: t4["status-error"], label: "Left/right imbalance" },
2386
+ overshoot: { Icon: AlertTriangleIcon, color: t4["status-error"], label: "Load overshoot" },
2387
+ "velocity-loss": { Icon: TrendingDownIcon, color: t4["status-warning"], label: "Velocity loss" },
2388
+ "missed-reps": { Icon: CircleSlashIcon, color: t4["status-warning"], label: "Missed reps" },
2389
+ pr: { Icon: AwardIcon, color: t4["status-success"], label: "Personal record" },
2390
+ info: { Icon: InfoIcon, color: t4["status-info"], label: "More info" }
2391
+ };
2392
+ function ExerciseIndicator({ kind, onPress, className, ...props }) {
2393
+ const { Icon, color, label } = INDICATOR_CONFIG[kind];
2394
+ const chip = /* @__PURE__ */ jsx(
2395
+ reactNative.View,
2396
+ {
2397
+ className,
2398
+ style: {
2399
+ width: 16,
2400
+ height: 16,
2401
+ borderRadius: 8,
2402
+ borderWidth: 1,
2403
+ borderColor: color,
2404
+ alignItems: "center",
2405
+ justifyContent: "center"
2406
+ },
2407
+ accessibilityRole: "image",
2408
+ accessibilityLabel: label,
2409
+ testID: "exercise-indicator",
2410
+ ...props,
2411
+ children: /* @__PURE__ */ jsx(Icon, { size: 11, color })
2412
+ }
2413
+ );
2414
+ if (onPress) {
2415
+ return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, children: chip });
2416
+ }
2417
+ return chip;
1998
2418
  }
1999
2419
 
2000
- // src/components/custom/Workout/ExerciseCard.tsx
2001
- function buildColumnHeaders(unit) {
2002
- return ["SET", "PREV", "REPS", unit.toUpperCase(), "RPE"];
2420
+ // src/components/custom/Workout/ExerciseHeading.tsx
2421
+ function headingLabel(name, sets, reps, load, unit) {
2422
+ return `${name}, ${sets}\xD7${reps} @ ${roundWeight(load)} ${unit}`;
2423
+ }
2424
+ function ExerciseHeading({
2425
+ name,
2426
+ sets,
2427
+ reps,
2428
+ load,
2429
+ unit = "lbs",
2430
+ tempo,
2431
+ indicator,
2432
+ dimmed,
2433
+ onPress
2434
+ }) {
2435
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { opacity: dimmed ? 0.55 : 1 }, testID: "exercise-heading", children: [
2436
+ /* @__PURE__ */ jsxs(
2437
+ reactNative.Pressable,
2438
+ {
2439
+ onPress,
2440
+ accessibilityRole: "button",
2441
+ accessibilityLabel: headingLabel(name, sets, reps, load, unit),
2442
+ className: "flex-row items-center",
2443
+ style: { gap: 8 },
2444
+ testID: "exercise-card-header",
2445
+ children: [
2446
+ /* @__PURE__ */ jsx(
2447
+ reactNative.Text,
2448
+ {
2449
+ className: "text-text-primary",
2450
+ style: { fontSize: 14, fontFamily: '"Space Grotesk", sans-serif', fontWeight: "700" },
2451
+ testID: "exercise-card-name",
2452
+ children: name
2453
+ }
2454
+ ),
2455
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
2456
+ indicator && /* @__PURE__ */ jsx(ExerciseIndicator, { kind: indicator })
2457
+ ]
2458
+ }
2459
+ ),
2460
+ /* @__PURE__ */ jsxs(
2461
+ reactNative.View,
2462
+ {
2463
+ className: "flex-row items-center",
2464
+ style: { gap: 8, marginTop: 1 },
2465
+ testID: "exercise-card-summary",
2466
+ children: [
2467
+ /* @__PURE__ */ jsx(SetsRepsLoad, { sets, reps, load, unit }),
2468
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
2469
+ tempo && /* @__PURE__ */ jsx(TempoDisplay, { tempo, size: "sm", showLabel: false, showInfo: false })
2470
+ ]
2471
+ }
2472
+ )
2473
+ ] });
2474
+ }
2475
+
2476
+ // src/components/custom/Workout/ExerciseCardHeading.tsx
2477
+ function ExerciseCardHeading({
2478
+ setStates,
2479
+ stripHeight = 8,
2480
+ dimmed,
2481
+ testID = "exercise-card",
2482
+ ...heading
2483
+ }) {
2484
+ return /* @__PURE__ */ jsxs(
2485
+ reactNative.View,
2486
+ {
2487
+ style: { paddingVertical: 9, paddingHorizontal: 12, opacity: dimmed ? 0.55 : 1 },
2488
+ testID,
2489
+ children: [
2490
+ /* @__PURE__ */ jsx(ExerciseHeading, { ...heading }),
2491
+ setStates.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: 7 }, testID: "exercise-card-strip", children: /* @__PURE__ */ jsx(SetStrip, { sets: setStates, height: stripHeight }) })
2492
+ ]
2493
+ }
2494
+ );
2003
2495
  }
2496
+
2497
+ // src/components/custom/Workout/ExerciseCard.tsx
2004
2498
  function getSupersetBorderRadius(position) {
2005
2499
  switch (position) {
2006
2500
  case "first":
@@ -2043,7 +2537,7 @@ function formatSummary(summary) {
2043
2537
  if (!summary) return "";
2044
2538
  return `${summary.sets}\xD7${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`;
2045
2539
  }
2046
- function formatAccessibilityLabel2(name, summary, prescription) {
2540
+ function formatAccessibilityLabel(name, summary, prescription) {
2047
2541
  if (summary) return `${name}, ${formatSummary(summary)}`;
2048
2542
  if (prescription) return `${name}, ${prescription}`;
2049
2543
  return name;
@@ -2052,7 +2546,6 @@ function CollapsedCard({
2052
2546
  name,
2053
2547
  onToggle,
2054
2548
  summary,
2055
- e1rm,
2056
2549
  isPR,
2057
2550
  setVelocities,
2058
2551
  velocityZones,
@@ -2071,7 +2564,7 @@ function CollapsedCard({
2071
2564
  onPressIn: () => setPressed(true),
2072
2565
  onPressOut: () => setPressed(false),
2073
2566
  accessibilityRole: "button",
2074
- accessibilityLabel: formatAccessibilityLabel2(name, summary, void 0),
2567
+ accessibilityLabel: formatAccessibilityLabel(name, summary, void 0),
2075
2568
  testID: "exercise-card",
2076
2569
  children: /* @__PURE__ */ jsxs(
2077
2570
  reactNative.View,
@@ -2085,100 +2578,93 @@ function CollapsedCard({
2085
2578
  ...supersetMargin
2086
2579
  },
2087
2580
  children: [
2088
- /* @__PURE__ */ jsxs(
2089
- reactNative.View,
2090
- {
2091
- className: "flex-row items-center",
2092
- testID: "exercise-card-header",
2093
- children: [
2094
- /* @__PURE__ */ jsx(
2095
- reactNative.Text,
2096
- {
2097
- className: "text-text-primary",
2098
- style: {
2099
- fontSize: 14,
2100
- fontFamily: '"Space Grotesk", sans-serif',
2101
- fontWeight: "700"
2102
- },
2103
- testID: "exercise-card-name",
2104
- children: name
2105
- }
2106
- ),
2107
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
2108
- summary && /* @__PURE__ */ jsx(
2109
- reactNative.Text,
2110
- {
2111
- className: "text-text-secondary",
2112
- style: {
2113
- fontSize: 12,
2114
- fontFamily: "Inter, sans-serif",
2115
- marginRight: 8
2116
- },
2117
- testID: "exercise-card-summary",
2118
- children: formatSummary(summary)
2119
- }
2120
- ),
2121
- e1rm && /* @__PURE__ */ jsx(
2122
- WeightBadge,
2123
- {
2124
- value: roundWeight(e1rm.value),
2125
- unit: e1rm.unit,
2126
- size: "sm",
2127
- showIcon: false,
2128
- testID: "exercise-card-e1rm"
2129
- }
2130
- ),
2131
- isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
2132
- ]
2133
- }
2134
- ),
2135
- (completedSets > 0 || remaining > 0) && /* @__PURE__ */ jsxs(
2136
- reactNative.View,
2137
- {
2138
- className: "flex-row",
2139
- style: { marginTop: 6, gap: 4 },
2140
- testID: "exercise-card-strips",
2141
- children: [
2142
- setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
2143
- VelocityStrip,
2144
- {
2145
- velocities,
2146
- zones: velocityZones,
2147
- variant: "mini",
2148
- testID: `exercise-card-velocity-strip-${i}`
2149
- },
2150
- i
2151
- )),
2152
- Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
2153
- PlaceholderStrip,
2154
- {
2155
- testID: `exercise-card-placeholder-${i}`
2156
- },
2157
- `placeholder-${i}`
2158
- ))
2159
- ]
2160
- }
2161
- )
2581
+ /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", testID: "exercise-card-header", children: [
2582
+ /* @__PURE__ */ jsx(
2583
+ reactNative.Text,
2584
+ {
2585
+ className: "text-text-primary",
2586
+ style: {
2587
+ fontSize: 14,
2588
+ fontFamily: '"Space Grotesk", sans-serif',
2589
+ fontWeight: "700"
2590
+ },
2591
+ testID: "exercise-card-name",
2592
+ children: name
2593
+ }
2594
+ ),
2595
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
2596
+ summary && /* @__PURE__ */ jsx(
2597
+ reactNative.Text,
2598
+ {
2599
+ className: "text-text-secondary",
2600
+ style: {
2601
+ fontSize: 12,
2602
+ fontFamily: "Inter, sans-serif",
2603
+ marginRight: 8
2604
+ },
2605
+ testID: "exercise-card-summary",
2606
+ children: formatSummary(summary)
2607
+ }
2608
+ ),
2609
+ isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
2610
+ ] }),
2611
+ (completedSets > 0 || remaining > 0) && /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row", style: { marginTop: 6, gap: 4 }, testID: "exercise-card-strips", children: [
2612
+ setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
2613
+ VelocityStrip,
2614
+ {
2615
+ velocities,
2616
+ zones: velocityZones,
2617
+ variant: "mini",
2618
+ testID: `exercise-card-velocity-strip-${i}`
2619
+ },
2620
+ i
2621
+ )),
2622
+ Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
2623
+ PlaceholderStrip,
2624
+ {
2625
+ testID: `exercise-card-placeholder-${i}`
2626
+ },
2627
+ `placeholder-${i}`
2628
+ ))
2629
+ ] })
2162
2630
  ]
2163
2631
  }
2164
2632
  )
2165
2633
  }
2166
2634
  );
2167
2635
  }
2636
+ var DARK2 = getSemanticColors("dark");
2637
+ var BODY_DIVIDER = DARK2["border-subtle"];
2638
+ function deriveHeaderSetStates(sets) {
2639
+ return sets.map((set) => {
2640
+ switch (set.state) {
2641
+ case "live":
2642
+ return { status: "active", velocities: set.velocities, planned: set.target.reps };
2643
+ case "done":
2644
+ return { status: "done", velocities: set.velocities };
2645
+ case "todo":
2646
+ return { status: "todo", planned: set.planned ?? set.target.reps };
2647
+ }
2648
+ });
2649
+ }
2168
2650
  function ExpandedCard({
2169
2651
  name,
2170
2652
  onToggle,
2171
2653
  summary,
2172
- e1rm,
2173
2654
  isPR,
2174
2655
  sets,
2175
2656
  tempo,
2657
+ indicator,
2658
+ setStates,
2659
+ stripHeight = 8,
2660
+ velocityZones,
2176
2661
  supersetPosition
2177
2662
  }) {
2178
2663
  const borderRadius = getSupersetBorderRadius(supersetPosition);
2179
2664
  const supersetMargin = getSupersetMargin(supersetPosition);
2180
2665
  const unit = summary?.unit ?? sets?.[0]?.unit ?? "lbs";
2181
- const columnHeaders = buildColumnHeaders(unit);
2666
+ const headerStates = setStates ?? (sets ? deriveHeaderSetStates(sets) : []);
2667
+ const headerIndicator = indicator ?? (isPR ? "pr" : void 0);
2182
2668
  return /* @__PURE__ */ jsxs(
2183
2669
  reactNative.View,
2184
2670
  {
@@ -2191,114 +2677,32 @@ function ExpandedCard({
2191
2677
  testID: "exercise-card",
2192
2678
  children: [
2193
2679
  /* @__PURE__ */ jsx(
2194
- reactNative.Pressable,
2680
+ ExerciseCardHeading,
2195
2681
  {
2682
+ name,
2683
+ sets: summary?.sets ?? sets?.length ?? 0,
2684
+ reps: summary?.reps ?? 0,
2685
+ load: summary?.weight ?? 0,
2686
+ unit,
2687
+ tempo,
2688
+ indicator: headerIndicator,
2689
+ setStates: headerStates,
2690
+ stripHeight,
2196
2691
  onPress: onToggle,
2197
- accessibilityRole: "button",
2198
- accessibilityLabel: formatAccessibilityLabel2(name, summary, void 0),
2199
- "aria-expanded": true,
2200
- testID: "exercise-card-header",
2201
- children: /* @__PURE__ */ jsxs(
2202
- reactNative.View,
2203
- {
2204
- className: "flex-row items-center",
2205
- style: {
2206
- padding: 12,
2207
- paddingHorizontal: 14,
2208
- paddingBottom: 10,
2209
- borderBottomWidth: 1,
2210
- borderBottomColor: resolveColor("border-default")
2211
- },
2212
- children: [
2213
- /* @__PURE__ */ jsx(
2214
- reactNative.Text,
2215
- {
2216
- className: "text-text-primary",
2217
- style: {
2218
- fontSize: 14,
2219
- fontFamily: '"Space Grotesk", sans-serif',
2220
- fontWeight: "700"
2221
- },
2222
- testID: "exercise-card-name",
2223
- children: name
2224
- }
2225
- ),
2226
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
2227
- summary && /* @__PURE__ */ jsx(
2228
- reactNative.Text,
2229
- {
2230
- className: "text-text-secondary",
2231
- style: {
2232
- fontSize: 12,
2233
- fontFamily: "Inter, sans-serif",
2234
- marginRight: 8
2235
- },
2236
- testID: "exercise-card-summary",
2237
- children: formatSummary(summary)
2238
- }
2239
- ),
2240
- e1rm && /* @__PURE__ */ jsx(
2241
- WeightBadge,
2242
- {
2243
- value: roundWeight(e1rm.value),
2244
- unit: e1rm.unit,
2245
- size: "sm",
2246
- showIcon: false,
2247
- testID: "exercise-card-e1rm"
2248
- }
2249
- ),
2250
- isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
2251
- ]
2252
- }
2253
- )
2692
+ testID: "exercise-card-heading"
2254
2693
  }
2255
2694
  ),
2256
- tempo && /* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: 8, paddingHorizontal: 14 }, testID: "exercise-card-tempo", children: /* @__PURE__ */ jsx(TempoDisplay, { tempo, size: "sm" }) }),
2257
- /* @__PURE__ */ jsx(
2695
+ /* @__PURE__ */ jsxs(
2258
2696
  reactNative.View,
2259
2697
  {
2260
- className: "flex-row",
2261
- style: {
2262
- paddingVertical: 8,
2263
- paddingHorizontal: 8,
2264
- paddingBottom: 4
2265
- },
2266
- testID: "exercise-card-column-headers",
2267
- children: columnHeaders.map((header, i) => {
2268
- const widths = [36, void 0, 44, 56, 36];
2269
- const width = widths[i];
2270
- const isFlex = width === void 0;
2271
- return /* @__PURE__ */ jsx(
2272
- reactNative.View,
2273
- {
2274
- style: {
2275
- ...isFlex ? {} : { width },
2276
- alignItems: "center",
2277
- justifyContent: "center",
2278
- gap: 8
2279
- },
2280
- ...isFlex ? { className: "flex-1" } : {},
2281
- children: /* @__PURE__ */ jsx(
2282
- reactNative.Text,
2283
- {
2284
- className: "text-text-tertiary",
2285
- style: {
2286
- fontSize: 10,
2287
- fontWeight: "600",
2288
- fontFamily: "Inter, sans-serif",
2289
- textTransform: "uppercase",
2290
- letterSpacing: 0.8
2291
- },
2292
- children: header
2293
- }
2294
- )
2295
- },
2296
- header
2297
- );
2298
- })
2698
+ style: { borderTopWidth: 1, borderTopColor: BODY_DIVIDER, paddingBottom: 6 },
2699
+ testID: "exercise-card-body",
2700
+ children: [
2701
+ /* @__PURE__ */ jsx(SetTableHeader, { unit, showPrevious: false, testID: "exercise-card-column-headers" }),
2702
+ sets && /* @__PURE__ */ jsx(reactNative.View, { testID: "exercise-card-sets", children: sets.map((set, i) => /* @__PURE__ */ jsx(SetRow, { ...set, velocityZones: set.velocityZones ?? velocityZones }, i)) })
2703
+ ]
2299
2704
  }
2300
- ),
2301
- sets && /* @__PURE__ */ jsx(reactNative.View, { testID: "exercise-card-sets", children: sets.map((setProps, i) => /* @__PURE__ */ jsx(SetRow, { ...setProps }, i)) })
2705
+ )
2302
2706
  ]
2303
2707
  }
2304
2708
  );
@@ -2307,11 +2711,12 @@ function UpcomingCard({
2307
2711
  name,
2308
2712
  prescription,
2309
2713
  previousBest,
2310
- supersetPosition
2714
+ supersetPosition,
2715
+ onToggle
2311
2716
  }) {
2312
2717
  const borderRadius = getSupersetBorderRadius(supersetPosition);
2313
2718
  const supersetMargin = getSupersetMargin(supersetPosition);
2314
- return /* @__PURE__ */ jsx(
2719
+ return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress: onToggle, accessibilityRole: "button", children: /* @__PURE__ */ jsx(
2315
2720
  reactNative.View,
2316
2721
  {
2317
2722
  style: {
@@ -2321,7 +2726,7 @@ function UpcomingCard({
2321
2726
  ...borderRadius,
2322
2727
  ...supersetMargin
2323
2728
  },
2324
- accessibilityLabel: formatAccessibilityLabel2(name, void 0, prescription),
2729
+ accessibilityLabel: formatAccessibilityLabel(name, void 0, prescription),
2325
2730
  testID: "exercise-card",
2326
2731
  children: /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", children: [
2327
2732
  /* @__PURE__ */ jsx(
@@ -2329,6 +2734,7 @@ function UpcomingCard({
2329
2734
  {
2330
2735
  className: "text-text-primary",
2331
2736
  style: {
2737
+ flexShrink: 0,
2332
2738
  fontSize: 14,
2333
2739
  fontFamily: '"Space Grotesk", sans-serif',
2334
2740
  fontWeight: "700"
@@ -2341,7 +2747,9 @@ function UpcomingCard({
2341
2747
  reactNative.Text,
2342
2748
  {
2343
2749
  className: "text-text-secondary",
2750
+ numberOfLines: 1,
2344
2751
  style: {
2752
+ flexShrink: 1,
2345
2753
  fontSize: 12,
2346
2754
  fontFamily: "Inter, sans-serif",
2347
2755
  marginLeft: 8
@@ -2350,12 +2758,14 @@ function UpcomingCard({
2350
2758
  children: prescription
2351
2759
  }
2352
2760
  ),
2353
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
2761
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", style: { minWidth: 8 } }),
2354
2762
  previousBest && /* @__PURE__ */ jsx(
2355
2763
  reactNative.Text,
2356
2764
  {
2357
2765
  className: "text-text-tertiary",
2766
+ numberOfLines: 1,
2358
2767
  style: {
2768
+ flexShrink: 1,
2359
2769
  fontSize: 11,
2360
2770
  fontFamily: "Inter, sans-serif"
2361
2771
  },
@@ -2365,19 +2775,32 @@ function UpcomingCard({
2365
2775
  )
2366
2776
  ] })
2367
2777
  }
2368
- );
2778
+ ) });
2369
2779
  }
2370
- function ExerciseCard(props) {
2371
- switch (props.state) {
2372
- case "collapsed":
2373
- return /* @__PURE__ */ jsx(CollapsedCard, { ...props });
2374
- case "expanded":
2375
- return /* @__PURE__ */ jsx(ExpandedCard, { ...props });
2376
- case "upcoming":
2377
- return /* @__PURE__ */ jsx(UpcomingCard, { ...props });
2378
- }
2780
+ function ExerciseCard({
2781
+ upcoming,
2782
+ expanded,
2783
+ defaultExpanded,
2784
+ onExpandedChange,
2785
+ ...rest
2786
+ }) {
2787
+ const [internalExpanded, setInternalExpanded] = react.useState(defaultExpanded ?? false);
2788
+ const isControlled = expanded !== void 0;
2789
+ const isExpanded = isControlled ? expanded : internalExpanded;
2790
+ const onToggle = () => {
2791
+ const next = !isExpanded;
2792
+ onExpandedChange?.(next);
2793
+ if (!isControlled) setInternalExpanded(next);
2794
+ };
2795
+ if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
2796
+ return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
2379
2797
  }
2380
- var BRAND_PRIMARY = "#FF7900";
2798
+ function resolveColor(token, mode = "dark") {
2799
+ return reactNative.Platform.OS === "web" ? `var(--color-${token})` : getSemanticColors(mode)[token];
2800
+ }
2801
+
2802
+ // src/components/custom/Workout/InputBar.tsx
2803
+ var BRAND_PRIMARY2 = getSemanticColors("dark")["brand-primary"];
2381
2804
  var INPUT_CLASSNAME = "bg-surface-raised border-border-strong text-text-primary";
2382
2805
  var inputStyle = {
2383
2806
  fontSize: 14,
@@ -2506,7 +2929,7 @@ function InputBar({
2506
2929
  onPress: onRecord,
2507
2930
  disabled: !canRecord,
2508
2931
  style: ({ pressed }) => ({
2509
- backgroundColor: BRAND_PRIMARY,
2932
+ backgroundColor: BRAND_PRIMARY2,
2510
2933
  borderRadius: 8,
2511
2934
  paddingVertical: 10,
2512
2935
  paddingHorizontal: 16,
@@ -2533,7 +2956,46 @@ function InputBar({
2533
2956
  }
2534
2957
  );
2535
2958
  }
2536
- var BRAND_PRIMARY2 = "#FF7900";
2959
+ function formatDuration(ms) {
2960
+ const totalSeconds = Math.max(0, Math.ceil(ms / 1e3));
2961
+ const minutes = Math.floor(totalSeconds / 60);
2962
+ const seconds = totalSeconds % 60;
2963
+ return `${minutes}:${String(seconds).padStart(2, "0")}`;
2964
+ }
2965
+ function useTimer(options = {}) {
2966
+ const {
2967
+ mode = "up",
2968
+ durationMs,
2969
+ elapsedMs: controlledElapsedMs,
2970
+ running = false,
2971
+ autoTick = false,
2972
+ tickMs = 1e3
2973
+ } = options;
2974
+ const isControlled = controlledElapsedMs != null;
2975
+ const [internalElapsedMs, setInternalElapsedMs] = react.useState(0);
2976
+ react.useEffect(() => {
2977
+ if (isControlled || !autoTick || !running) return;
2978
+ const id = setInterval(() => {
2979
+ setInternalElapsedMs((prev) => prev + tickMs);
2980
+ }, tickMs);
2981
+ return () => clearInterval(id);
2982
+ }, [isControlled, autoTick, running, tickMs]);
2983
+ const elapsedMs = isControlled ? controlledElapsedMs : internalElapsedMs;
2984
+ const hasDuration = durationMs != null && durationMs > 0;
2985
+ const remainingMs = durationMs != null ? Math.max(0, durationMs - elapsedMs) : 0;
2986
+ let progress;
2987
+ if (hasDuration) {
2988
+ progress = Math.min(1, Math.max(0, elapsedMs / durationMs));
2989
+ } else {
2990
+ progress = mode === "down" ? 1 : 0;
2991
+ }
2992
+ const label = formatDuration(mode === "down" ? remainingMs : elapsedMs);
2993
+ const done = durationMs != null && elapsedMs >= durationMs;
2994
+ return { elapsedMs, remainingMs, progress, label, done };
2995
+ }
2996
+
2997
+ // src/components/custom/Workout/RestTimer.tsx
2998
+ var BRAND_PRIMARY3 = getSemanticColors("dark")["brand-primary"];
2537
2999
  function RestTimer({
2538
3000
  totalSeconds,
2539
3001
  elapsedMs,
@@ -2543,14 +3005,18 @@ function RestTimer({
2543
3005
  visible,
2544
3006
  displayOnly = false
2545
3007
  }) {
2546
- if (!visible) return null;
2547
- const remainingMs = Math.max(0, totalSeconds * 1e3 - elapsedMs);
3008
+ const {
3009
+ remainingMs,
3010
+ progress,
3011
+ label: timeDisplay
3012
+ } = useTimer({
3013
+ mode: "down",
3014
+ durationMs: totalSeconds * 1e3,
3015
+ elapsedMs
3016
+ });
2548
3017
  const remainingSec = Math.ceil(remainingMs / 1e3);
2549
- const minutes = Math.floor(remainingSec / 60);
2550
- const seconds = remainingSec % 60;
2551
- const timeDisplay = `${minutes}:${String(seconds).padStart(2, "0")}`;
2552
- const totalMs = totalSeconds * 1e3;
2553
- const progressPct = totalMs > 0 ? Math.min(100, elapsedMs / totalMs * 100) : 100;
3018
+ const progressPct = progress * 100;
3019
+ if (!visible) return null;
2554
3020
  return /* @__PURE__ */ jsxs(
2555
3021
  reactNative.View,
2556
3022
  {
@@ -2639,7 +3105,7 @@ function RestTimer({
2639
3105
  {
2640
3106
  style: {
2641
3107
  height: "100%",
2642
- backgroundColor: BRAND_PRIMARY2,
3108
+ backgroundColor: BRAND_PRIMARY3,
2643
3109
  borderRadius: 2,
2644
3110
  width: `${progressPct}%`
2645
3111
  },
@@ -2696,7 +3162,7 @@ function RestTimer({
2696
3162
  fontSize: 11,
2697
3163
  fontFamily: "Inter, sans-serif",
2698
3164
  fontWeight: "600",
2699
- color: BRAND_PRIMARY2
3165
+ color: BRAND_PRIMARY3
2700
3166
  },
2701
3167
  children: "Skip"
2702
3168
  }
@@ -2710,7 +3176,7 @@ function RestTimer({
2710
3176
  }
2711
3177
  var DEFAULTS = {
2712
3178
  label: "SS",
2713
- color: "#FF7900",
3179
+ color: getSemanticColors("dark")["brand-primary"],
2714
3180
  bgBase: "#101010"
2715
3181
  };
2716
3182
  function SupersetWrapper({
@@ -2763,7 +3229,7 @@ function SupersetWrapper({
2763
3229
  }
2764
3230
 
2765
3231
  // src/components/custom/Workout/ActiveWorkoutPage.tsx
2766
- var BRAND_PRIMARY3 = "#FF7900";
3232
+ var BRAND_PRIMARY4 = getSemanticColors("dark")["brand-primary"];
2767
3233
  function countCompletedSets(exercise) {
2768
3234
  if (exercise.setVelocities != null) return exercise.setVelocities.length;
2769
3235
  if (exercise.status === "completed") return exercise.totalPlannedSets;
@@ -2789,23 +3255,22 @@ function deriveWorkoutProgress(exercises) {
2789
3255
  function findActiveExercise(exercises) {
2790
3256
  return exercises.find((exercise) => exercise.status === "active") ?? null;
2791
3257
  }
2792
- function statusToCardState(status, focused) {
2793
- if (status === "upcoming") return "upcoming";
2794
- return focused ? "expanded" : "collapsed";
3258
+ function isUpcomingExercise(status) {
3259
+ return status === "upcoming";
2795
3260
  }
2796
3261
  function toActiveCardProps(exercise, focused, onToggle, supersetPosition = null, supersetColor) {
2797
- const state = statusToCardState(exercise.status, focused);
3262
+ const upcoming = isUpcomingExercise(exercise.status);
2798
3263
  return {
2799
3264
  name: exercise.name,
2800
- state,
2801
- onToggle,
3265
+ upcoming,
3266
+ expanded: focused,
3267
+ onExpandedChange: onToggle,
2802
3268
  summary: exercise.summary,
2803
- e1rm: exercise.e1rm,
2804
3269
  isPR: exercise.isPR,
2805
3270
  tempo: exercise.tempo,
2806
3271
  setVelocities: exercise.setVelocities,
2807
3272
  totalPlannedSets: exercise.totalPlannedSets,
2808
- sets: state === "expanded" ? exercise.sets : void 0,
3273
+ sets: focused ? exercise.sets : void 0,
2809
3274
  prescription: exercise.prescription,
2810
3275
  previousBest: exercise.previousBest,
2811
3276
  supersetPosition,
@@ -2878,7 +3343,7 @@ function WorkoutHeader({
2878
3343
  fontSize: 15,
2879
3344
  fontFamily: '"Space Grotesk", sans-serif',
2880
3345
  fontWeight: "700",
2881
- color: BRAND_PRIMARY3,
3346
+ color: BRAND_PRIMARY4,
2882
3347
  fontVariant: ["tabular-nums"]
2883
3348
  },
2884
3349
  children: `${progress.completedExercises}/${progress.totalExercises}`
@@ -2912,7 +3377,7 @@ function WorkoutHeader({
2912
3377
  style: {
2913
3378
  height: "100%",
2914
3379
  width: `${Math.round(progress.fraction * 100)}%`,
2915
- backgroundColor: BRAND_PRIMARY3,
3380
+ backgroundColor: BRAND_PRIMARY4,
2916
3381
  borderRadius: 2
2917
3382
  },
2918
3383
  testID: "active-workout-page-progress-fill"
@@ -3494,17 +3959,18 @@ function CardContent({ children, className }) {
3494
3959
  function CardFooter({ children, className }) {
3495
3960
  return /* @__PURE__ */ jsx(reactNative.View, { className: cn("px-6 py-4 flex-row items-center gap-2", className), children });
3496
3961
  }
3962
+ var t5 = getSemanticColors("dark");
3497
3963
  var solidVariantColors = {
3498
- success: "#14B8A6",
3499
- warning: "#FFB020",
3500
- error: "#D14343",
3964
+ success: t5["status-success"],
3965
+ warning: t5["status-warning"],
3966
+ error: t5["status-error"],
3501
3967
  neutral: "#6B7280"
3502
3968
  };
3503
3969
  var ringVariantStyles = {
3504
3970
  "on-track": {
3505
- backgroundColor: "rgba(20,184,166,0.15)",
3971
+ backgroundColor: "rgba(46,213,115,0.15)",
3506
3972
  borderWidth: 1,
3507
- borderColor: "rgba(20,184,166,0.3)"
3973
+ borderColor: "rgba(46,213,115,0.3)"
3508
3974
  },
3509
3975
  deviation: {
3510
3976
  backgroundColor: "rgba(245,158,11,0.15)",
@@ -3519,11 +3985,11 @@ var ringVariantStyles = {
3519
3985
  }
3520
3986
  };
3521
3987
  var glowStyles = {
3522
- success: { boxShadow: "0 0 4px rgba(20,184,166,0.4)" },
3988
+ success: { boxShadow: "0 0 4px rgba(46,213,115,0.4)" },
3523
3989
  warning: { boxShadow: "0 0 4px rgba(245,158,11,0.4)" },
3524
3990
  error: { boxShadow: "0 0 4px rgba(239,68,68,0.4)" },
3525
3991
  neutral: { boxShadow: "0 0 4px rgba(107,114,128,0.4)" },
3526
- "on-track": { boxShadow: "0 0 4px rgba(20,184,166,0.4)" },
3992
+ "on-track": { boxShadow: "0 0 4px rgba(46,213,115,0.4)" },
3527
3993
  deviation: { boxShadow: "0 0 4px rgba(245,158,11,0.4)" },
3528
3994
  future: { boxShadow: "0 0 4px rgba(107,114,128,0.4)" }
3529
3995
  };
@@ -3537,8 +4003,8 @@ var iconColors = {
3537
4003
  warning: "#6B4000",
3538
4004
  error: "#5C1A1A",
3539
4005
  neutral: "#D1D5DB",
3540
- "on-track": "#14B8A6",
3541
- deviation: "#FFB020",
4006
+ "on-track": t5["status-success"],
4007
+ deviation: t5["status-warning"],
3542
4008
  future: "#6B7280"
3543
4009
  };
3544
4010
  var sizeDimensions = {
@@ -3619,20 +4085,24 @@ function StatusDot({
3619
4085
  }
3620
4086
  return /* @__PURE__ */ jsx(reactNative.View, { className, ...props, children: dot });
3621
4087
  }
4088
+ var MESO_ACCENT_GRADIENT_DARK = primitiveRamps.orange[500];
4089
+ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
4090
+ var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
3622
4091
 
3623
4092
  // src/components/custom/Workout/MesoStatusCard.tsx
3624
- var BRAND_PRIMARY4 = "#FF7900";
3625
- var BRAND_PRIMARY_DARK = "#C45E00";
3626
- var BRAND_PRIMARY_LIGHT = "#FFB066";
3627
- var SUCCESS = "#14B8A6";
3628
- var WARNING = "#FFB020";
3629
- var ERROR = "#D14343";
3630
- var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY4, BRAND_PRIMARY_LIGHT];
4093
+ var t6 = getSemanticColors("dark");
4094
+ var BRAND_PRIMARY5 = t6["brand-primary"];
4095
+ var BRAND_PRIMARY_DARK = MESO_ACCENT_GRADIENT_DARK;
4096
+ var BRAND_PRIMARY_LIGHT = MESO_ACCENT_GRADIENT_LIGHT;
4097
+ var SUCCESS = t6["status-success"];
4098
+ var WARNING = t6["status-warning"];
4099
+ var ERROR = t6["status-error"];
4100
+ var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY5, BRAND_PRIMARY_LIGHT];
3631
4101
  var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
3632
- var GAUGE_GRADIENT = "linear-gradient(90deg, rgba(20,184,166,0.25) 0%, rgba(255,176,32,0.25) 50%, rgba(209,67,67,0.25) 100%)";
4102
+ var GAUGE_GRADIENT = "linear-gradient(90deg, rgba(46,213,115,0.25) 0%, rgba(249,180,21,0.25) 50%, rgba(209,67,67,0.25) 100%)";
3633
4103
  var STATUS_VARIANTS = {
3634
- success: { bg: "rgba(20,184,166,0.15)", border: "rgba(20,184,166,0.3)", text: SUCCESS },
3635
- warning: { bg: "rgba(255,176,32,0.15)", border: "rgba(255,176,32,0.3)", text: WARNING },
4104
+ success: { bg: "rgba(46,213,115,0.15)", border: "rgba(46,213,115,0.3)", text: SUCCESS },
4105
+ warning: { bg: "rgba(249,180,21,0.15)", border: "rgba(249,180,21,0.3)", text: WARNING },
3636
4106
  error: { bg: "rgba(209,67,67,0.15)", border: "rgba(209,67,67,0.25)", text: ERROR }
3637
4107
  };
3638
4108
  function clamp01(value) {
@@ -3698,7 +4168,7 @@ function StatusPill({ badge }) {
3698
4168
  }
3699
4169
  );
3700
4170
  }
3701
- function MetricCell({ metric }) {
4171
+ function MetricCell2({ metric }) {
3702
4172
  return /* @__PURE__ */ jsxs(reactNative.View, { style: { width: "48%" }, testID: "meso-status-card-metric", children: [
3703
4173
  /* @__PURE__ */ jsx(
3704
4174
  reactNative.Text,
@@ -3916,7 +4386,7 @@ function MesoStatusCard({
3916
4386
  className: "flex-row flex-wrap",
3917
4387
  style: { gap: 8 },
3918
4388
  testID: "meso-status-card-metrics",
3919
- children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell, { metric }, `${metric.label}-${index}`))
4389
+ children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell2, { metric }, `${metric.label}-${index}`))
3920
4390
  }
3921
4391
  ),
3922
4392
  gauges.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 12 }, testID: "meso-status-card-gauges", children: gauges.map((gauge, index) => /* @__PURE__ */ jsx(Gauge, { gauge }, `${gauge.label}-${index}`)) }),
@@ -3924,9 +4394,9 @@ function MesoStatusCard({
3924
4394
  reactNative.View,
3925
4395
  {
3926
4396
  style: {
3927
- backgroundColor: "rgba(255,176,32,0.06)",
4397
+ backgroundColor: "rgba(249,180,21,0.06)",
3928
4398
  borderWidth: 1,
3929
- borderColor: "rgba(255,176,32,0.15)",
4399
+ borderColor: "rgba(249,180,21,0.15)",
3930
4400
  borderRadius: 8,
3931
4401
  paddingVertical: 10,
3932
4402
  paddingHorizontal: 12
@@ -3952,9 +4422,9 @@ function MesoStatusCard({
3952
4422
  className: "flex-row items-center",
3953
4423
  style: {
3954
4424
  gap: 8,
3955
- backgroundColor: "rgba(20,184,166,0.06)",
4425
+ backgroundColor: "rgba(46,213,115,0.06)",
3956
4426
  borderWidth: 1,
3957
- borderColor: "rgba(20,184,166,0.2)",
4427
+ borderColor: "rgba(46,213,115,0.2)",
3958
4428
  borderRadius: 8,
3959
4429
  paddingVertical: 10,
3960
4430
  paddingHorizontal: 12
@@ -3983,21 +4453,22 @@ function MesoStatusCard({
3983
4453
  }
3984
4454
  );
3985
4455
  }
3986
- var BRAND_PRIMARY5 = "#FF7900";
4456
+ var sem = getSemanticColors("dark");
4457
+ var BRAND_PRIMARY6 = sem["brand-primary"];
3987
4458
  var PROJECTION_LINE_COLOR = resolveColor("text-tertiary");
3988
- var STATUS_SUCCESS = "#14B8A6";
3989
- var STATUS_ERROR2 = "#D14343";
3990
- var STATUS_WARNING = "#FFB020";
4459
+ var STATUS_SUCCESS = sem["status-success"];
4460
+ var STATUS_ERROR2 = sem["status-error"];
4461
+ var STATUS_WARNING = sem["status-warning"];
3991
4462
  var GRID_LINE = "rgba(255,255,255,0.06)";
3992
- var SUCCESS_PILL_BG = "rgba(20,184,166,0.10)";
3993
- var SUCCESS_PILL_BORDER = "rgba(20,184,166,0.20)";
4463
+ var SUCCESS_PILL_BG = "rgba(46,213,115,0.10)";
4464
+ var SUCCESS_PILL_BORDER = "rgba(46,213,115,0.20)";
3994
4465
  var ERROR_PILL_BG = "rgba(209,67,67,0.10)";
3995
4466
  var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
3996
4467
  var PLOT_LEFT = 26;
3997
4468
  var TOOLTIP_WIDTH = 124;
3998
4469
  function timeOf(dateStr) {
3999
- const t2 = Date.parse(dateStr);
4000
- return Number.isNaN(t2) ? 0 : t2;
4470
+ const t12 = Date.parse(dateStr);
4471
+ return Number.isNaN(t12) ? 0 : t12;
4001
4472
  }
4002
4473
  function formatValue(value) {
4003
4474
  return `${Math.round(value)}`;
@@ -4274,7 +4745,7 @@ function StrengthTrendChart({
4274
4745
  ChartLine,
4275
4746
  {
4276
4747
  coords: geometry.actual,
4277
- color: BRAND_PRIMARY5,
4748
+ color: BRAND_PRIMARY6,
4278
4749
  strokeWidth: 2.5,
4279
4750
  testID: "strength-trend-chart-actual-segment"
4280
4751
  }
@@ -4327,7 +4798,7 @@ function StrengthTrendChart({
4327
4798
  width: 8,
4328
4799
  height: 8,
4329
4800
  borderRadius: 1,
4330
- backgroundColor: BRAND_PRIMARY5
4801
+ backgroundColor: BRAND_PRIMARY6
4331
4802
  }
4332
4803
  },
4333
4804
  `point-${c.index}`
@@ -4457,7 +4928,7 @@ function StrengthTrendChart({
4457
4928
  testID: "strength-trend-chart-legend",
4458
4929
  children: [
4459
4930
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 5 }, children: [
4460
- /* @__PURE__ */ jsx(reactNative.View, { style: { width: 14, height: 2.5, borderRadius: 1.5, backgroundColor: BRAND_PRIMARY5 } }),
4931
+ /* @__PURE__ */ jsx(reactNative.View, { style: { width: 14, height: 2.5, borderRadius: 1.5, backgroundColor: BRAND_PRIMARY6 } }),
4461
4932
  /* @__PURE__ */ jsx(reactNative.Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "Actual" })
4462
4933
  ] }),
4463
4934
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 5 }, children: [
@@ -4486,13 +4957,14 @@ function StrengthTrendChart({
4486
4957
  }
4487
4958
  );
4488
4959
  }
4489
- var STATUS_SUCCESS2 = "#14B8A6";
4490
- var STATUS_WARNING2 = "#FFB020";
4491
- var STATUS_INFO = "#2196F3";
4960
+ var t7 = getSemanticColors("dark");
4961
+ var STATUS_SUCCESS2 = t7["status-success"];
4962
+ var STATUS_WARNING2 = t7["status-warning"];
4963
+ var STATUS_INFO = t7["status-info"];
4492
4964
  var DOT_BORDER = "#F3F4F6";
4493
- var BAND_FILL = "rgba(20,184,166,0.1)";
4494
- var BAND_EDGE = "rgba(20,184,166,0.45)";
4495
- var PROJECTION_FILL = "rgba(20,184,166,0.05)";
4965
+ var BAND_FILL = "rgba(46,213,115,0.1)";
4966
+ var BAND_EDGE = "rgba(46,213,115,0.45)";
4967
+ var PROJECTION_FILL = "rgba(46,213,115,0.05)";
4496
4968
  var PADDING_LEFT = 28;
4497
4969
  var PADDING_RIGHT = 10;
4498
4970
  var PADDING_TOP = 8;
@@ -4531,13 +5003,13 @@ function interpEdge(pixels, x, key) {
4531
5003
  const a = pixels[i];
4532
5004
  const b = pixels[i + 1];
4533
5005
  if (x >= a.x && x <= b.x) {
4534
- const t2 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
4535
- return a[key] + t2 * (b[key] - a[key]);
5006
+ const t12 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
5007
+ return a[key] + t12 * (b[key] - a[key]);
4536
5008
  }
4537
5009
  }
4538
5010
  return pixels[pixels.length - 1][key];
4539
5011
  }
4540
- function buildColumns(pixels, step) {
5012
+ function buildColumns2(pixels, step) {
4541
5013
  if (pixels.length < 2) return [];
4542
5014
  const start = pixels[0].x;
4543
5015
  const end = pixels[pixels.length - 1].x;
@@ -4649,7 +5121,7 @@ function CapacityBandChart({
4649
5121
  }
4650
5122
  const { toX, toY } = scale;
4651
5123
  const bandPixels = toPixels(band, toX, toY);
4652
- const columns = buildColumns(bandPixels, COLUMN_STEP);
5124
+ const columns = buildColumns2(bandPixels, COLUMN_STEP);
4653
5125
  const topEdge = buildEdges(bandPixels, "yHigh");
4654
5126
  const bottomEdge = buildEdges(bandPixels, "yLow");
4655
5127
  const lastBandPoint = band[band.length - 1];
@@ -4724,8 +5196,8 @@ function CapacityBandChart({
4724
5196
  testID: "capacity-band-chart-reveal",
4725
5197
  children: /* @__PURE__ */ jsxs(reactNative.View, { style: { width, height, position: "relative" }, children: [
4726
5198
  projection && /* @__PURE__ */ jsxs(reactNative.View, { testID: "capacity-band-chart-projection", children: [
4727
- renderColumns(buildColumns(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
4728
- renderColumns(buildColumns(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
5199
+ renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
5200
+ renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
4729
5201
  renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS2, true, "capacity-band-chart-projection-training"),
4730
5202
  renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS2, true, "capacity-band-chart-projection-training"),
4731
5203
  renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
@@ -4864,7 +5336,7 @@ function CapacityBandChart({
4864
5336
  }
4865
5337
 
4866
5338
  // src/components/custom/Workout/ExerciseDetailPage.tsx
4867
- var BRAND_PRIMARY6 = "#FF7900";
5339
+ var BRAND_PRIMARY7 = getSemanticColors("dark")["brand-primary"];
4868
5340
  var CHART_WIDTH = 326;
4869
5341
  var CHART_HEIGHT = 150;
4870
5342
  var EXERCISE_DETAIL_TABS = [
@@ -4886,10 +5358,9 @@ function deriveExerciseStats(entries) {
4886
5358
  function toExerciseCardProps(entry, expanded, onToggle) {
4887
5359
  return {
4888
5360
  name: entry.title,
4889
- state: expanded ? "expanded" : "collapsed",
4890
- onToggle,
5361
+ expanded,
5362
+ onExpandedChange: onToggle,
4891
5363
  summary: entry.summary,
4892
- e1rm: entry.e1rm,
4893
5364
  isPR: entry.isPR,
4894
5365
  tempo: entry.tempo,
4895
5366
  sets: expanded ? entry.sets : void 0
@@ -4928,7 +5399,7 @@ function TabBar({ active, onSelect }) {
4928
5399
  alignItems: "center",
4929
5400
  paddingVertical: 10,
4930
5401
  borderBottomWidth: 2,
4931
- borderBottomColor: isActive ? BRAND_PRIMARY6 : "transparent"
5402
+ borderBottomColor: isActive ? BRAND_PRIMARY7 : "transparent"
4932
5403
  },
4933
5404
  testID: `exercise-detail-page-tab-${key}`,
4934
5405
  children: /* @__PURE__ */ jsx(
@@ -5250,7 +5721,7 @@ function ExerciseDetailPage({
5250
5721
  fontSize: 15,
5251
5722
  fontFamily: '"Space Grotesk", sans-serif',
5252
5723
  fontWeight: "700",
5253
- color: BRAND_PRIMARY6
5724
+ color: BRAND_PRIMARY7
5254
5725
  },
5255
5726
  children: `${exercise.currentE1rm} ${exercise.unit}`
5256
5727
  }
@@ -5316,9 +5787,9 @@ function ExerciseDetailPage({
5316
5787
  }
5317
5788
  );
5318
5789
  }
5319
- var BRAND_PRIMARY7 = "#FF7900";
5790
+ var BRAND_PRIMARY8 = getSemanticColors("dark")["brand-primary"];
5320
5791
  var segmentBgColors = {
5321
- completed: "rgba(20,184,166,0.3)",
5792
+ completed: "rgba(46,213,115,0.3)",
5322
5793
  current: "rgba(255,121,0,0.5)",
5323
5794
  upcoming: "rgba(255,255,255,0.06)"
5324
5795
  };
@@ -5368,7 +5839,7 @@ function MesoSegment({ meso, isActive, onPress }) {
5368
5839
  },
5369
5840
  isActive ? {
5370
5841
  borderWidth: 2,
5371
- borderColor: BRAND_PRIMARY7,
5842
+ borderColor: BRAND_PRIMARY8,
5372
5843
  transform: [{ scale: 1.02 }, { scale: pressScale }]
5373
5844
  } : void 0
5374
5845
  ],
@@ -5382,7 +5853,7 @@ function MesoSegment({ meso, isActive, onPress }) {
5382
5853
  top: 0,
5383
5854
  bottom: 0,
5384
5855
  width: `${progress * 100}%`,
5385
- backgroundColor: BRAND_PRIMARY7
5856
+ backgroundColor: BRAND_PRIMARY8
5386
5857
  },
5387
5858
  testID: `meso-segment-fill-${meso.id}`
5388
5859
  }
@@ -5433,9 +5904,11 @@ var colorStyles = {
5433
5904
  default: "bg-text-tertiary",
5434
5905
  primary: "bg-brand-primary",
5435
5906
  success: "bg-status-success",
5907
+ live: "bg-status-live",
5436
5908
  error: "bg-status-error",
5437
5909
  warning: "bg-status-warning",
5438
- info: "bg-status-info"
5910
+ info: "bg-status-info",
5911
+ "error-vivid": "bg-status-error-vivid"
5439
5912
  };
5440
5913
  function Indicator({
5441
5914
  size = "sm",
@@ -5443,14 +5916,40 @@ function Indicator({
5443
5916
  customColor,
5444
5917
  glow = false,
5445
5918
  ring = false,
5919
+ pulse = false,
5446
5920
  className,
5447
5921
  style,
5448
5922
  ...props
5449
5923
  }) {
5924
+ const pulseMode = pulse === true ? "opacity" : pulse || false;
5925
+ const colorClass = !customColor ? colorStyles[color] : void 0;
5926
+ const colorStyle = customColor ? { backgroundColor: customColor } : void 0;
5927
+ if (pulseMode === "ping") {
5928
+ return /* @__PURE__ */ jsxs(reactNative.View, { className: cn("relative", sizeStyles[size], className), style, ...props, children: [
5929
+ /* @__PURE__ */ jsx(
5930
+ reactNative.View,
5931
+ {
5932
+ className: cn("absolute inset-0 rounded-full animate-ping", colorClass),
5933
+ style: colorStyle
5934
+ }
5935
+ ),
5936
+ /* @__PURE__ */ jsx(
5937
+ reactNative.View,
5938
+ {
5939
+ className: cn(
5940
+ "relative rounded-full",
5941
+ sizeStyles[size],
5942
+ colorClass,
5943
+ ring && "border-2 border-background-base"
5944
+ ),
5945
+ style: colorStyle
5946
+ }
5947
+ )
5948
+ ] });
5949
+ }
5450
5950
  const dynamicStyle = {
5451
5951
  ...typeof style === "object" ? style : {},
5452
- ...customColor ? { backgroundColor: customColor } : {},
5453
- ...glow && !customColor ? {} : {},
5952
+ ...colorStyle ?? {},
5454
5953
  ...glow && customColor ? { boxShadow: `0 0 6px ${customColor}` } : {}
5455
5954
  };
5456
5955
  return /* @__PURE__ */ jsx(
@@ -5459,12 +5958,14 @@ function Indicator({
5459
5958
  className: cn(
5460
5959
  "rounded-full shrink-0",
5461
5960
  sizeStyles[size],
5462
- !customColor && colorStyles[color],
5961
+ colorClass,
5962
+ pulseMode === "opacity" && "animate-pulse",
5463
5963
  ring && "border-2 border-background-base",
5464
- glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(20,184,166,0.6)]",
5964
+ glow && !customColor && (color === "success" || color === "live") && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
5465
5965
  glow && !customColor && color === "error" && "shadow-[0_0_6px_rgba(239,68,68,0.6)]",
5466
5966
  glow && !customColor && color === "warning" && "shadow-[0_0_6px_rgba(245,158,11,0.6)]",
5467
5967
  glow && !customColor && color === "primary" && "shadow-[0_0_6px_rgba(255,121,0,0.6)]",
5968
+ glow && !customColor && color === "error-vivid" && "shadow-[0_0_6px_rgba(255,71,87,0.6)]",
5468
5969
  className
5469
5970
  ),
5470
5971
  style: Object.keys(dynamicStyle).length > 0 ? dynamicStyle : style,
@@ -5542,31 +6043,32 @@ function Badge({
5542
6043
  }
5543
6044
  );
5544
6045
  }
6046
+ var t8 = getSemanticColors("dark");
5545
6047
  var statusBgColors = {
5546
- completed: "rgba(20,184,166,0.15)",
6048
+ completed: "rgba(46,213,115,0.15)",
5547
6049
  current: "rgba(255,121,0,0.15)",
5548
6050
  next: "transparent",
5549
6051
  upcoming: "#1C1C1C",
5550
6052
  missed: "rgba(209,67,67,0.1)",
5551
- deload: "rgba(147,51,234,0.15)"
6053
+ deload: "rgba(186,41,150,0.15)"
5552
6054
  };
5553
6055
  var statusBorderStyles = {
5554
- completed: { borderWidth: 1, borderColor: "rgba(20,184,166,0.3)" },
6056
+ completed: { borderWidth: 1, borderColor: "rgba(46,213,115,0.3)" },
5555
6057
  current: { borderWidth: 1, borderColor: "rgba(255,121,0,0.3)" },
5556
6058
  next: { borderWidth: 1, borderColor: "rgba(255,121,0,0.4)" },
5557
6059
  upcoming: { borderWidth: 1, borderColor: "#1F1F1F" },
5558
6060
  missed: { borderWidth: 1, borderColor: "rgba(209,67,67,0.25)" },
5559
- deload: { borderWidth: 1, borderColor: "rgba(147,51,234,0.3)" }
6061
+ deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
5560
6062
  };
5561
6063
  var textColors = {
5562
- completed: "#14B8A6",
5563
- current: "#FF7900",
5564
- next: "#FF7900",
6064
+ completed: t8["status-success"],
6065
+ current: t8["brand-primary"],
6066
+ next: t8["brand-primary"],
5565
6067
  upcoming: "#6B7280",
5566
6068
  missed: "rgba(209,67,67,0.7)",
5567
- deload: "#9333ea"
6069
+ deload: WORKOUT_PILL_DELOAD
5568
6070
  };
5569
- function usePulse(enabled) {
6071
+ function usePulse2(enabled) {
5570
6072
  const [opacity] = react.useState(() => new reactNative.Animated.Value(1));
5571
6073
  react.useEffect(() => {
5572
6074
  if (!enabled) {
@@ -5604,7 +6106,7 @@ function WorkoutPill({
5604
6106
  ...props
5605
6107
  }) {
5606
6108
  const shouldPulse = pulse ?? status === "current";
5607
- const pulseOpacity = usePulse(shouldPulse);
6109
+ const pulseOpacity = usePulse2(shouldPulse);
5608
6110
  const isCompleted = status === "completed";
5609
6111
  const isMissed = status === "missed";
5610
6112
  const pill = /* @__PURE__ */ jsx(
@@ -5694,23 +6196,24 @@ function WorkoutPill({
5694
6196
  }
5695
6197
  return pill;
5696
6198
  }
6199
+ var t9 = getSemanticColors("dark");
5697
6200
  var TRACK_BG = "#333333";
5698
6201
  var LABEL_COLOR = "#6B7280";
5699
6202
  var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
5700
6203
  var AT_TARGET_GLOW = "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)";
5701
6204
  var ZONE = {
5702
- building: "#14B8A6",
5703
- // teal — below target ramp-up
5704
- approaching: "#FFB020",
5705
- // amber — nearing target
5706
- target: "#FF7900",
5707
- // brand — exactly at target
5708
- over1: "#D14343",
5709
- // red — mild over-target
5710
- over2: "#A62626",
5711
- // deep red — moderate over-target
5712
- over3: "#7A1C1C"
5713
- // darkest red — severe over-target
6205
+ building: t9["status-success"],
6206
+ // below target ramp-up
6207
+ approaching: t9["status-warning"],
6208
+ // nearing target
6209
+ target: t9["brand-primary"],
6210
+ // exactly at target
6211
+ over1: t9["status-error"],
6212
+ // mild over-target
6213
+ over2: WORKOUT_TOKENS.intensity.over2,
6214
+ // moderate over-target
6215
+ over3: WORKOUT_TOKENS.intensity.over3
6216
+ // severe over-target
5714
6217
  };
5715
6218
  var BULGE_SIZE = { 1: 8, 2: 10, 3: 11 };
5716
6219
  function zoneForPct(pct) {
@@ -5868,7 +6371,7 @@ function IntensityBar({
5868
6371
  }
5869
6372
 
5870
6373
  // src/components/custom/Workout/WeekRow.tsx
5871
- var BRAND_PRIMARY8 = "#FF7900";
6374
+ var BRAND_PRIMARY9 = getSemanticColors("dark")["brand-primary"];
5872
6375
  function WeekRow({
5873
6376
  weekNumber,
5874
6377
  totalWeeks,
@@ -5888,12 +6391,12 @@ function WeekRow({
5888
6391
  gap: 12
5889
6392
  };
5890
6393
  if (isDeload) {
5891
- rowStyle.backgroundColor = "rgba(147,51,234,0.06)";
6394
+ rowStyle.backgroundColor = "rgba(186,41,150,0.06)";
5892
6395
  }
5893
6396
  if (isCurrent) {
5894
6397
  rowStyle.backgroundColor = "rgba(255,121,0,0.04)";
5895
6398
  rowStyle.borderLeftWidth = 2;
5896
- rowStyle.borderLeftColor = BRAND_PRIMARY8;
6399
+ rowStyle.borderLeftColor = BRAND_PRIMARY9;
5897
6400
  }
5898
6401
  return /* @__PURE__ */ jsxs(
5899
6402
  reactNative.View,
@@ -5917,7 +6420,7 @@ function WeekRow({
5917
6420
  width: 6,
5918
6421
  height: 6,
5919
6422
  borderRadius: 3,
5920
- backgroundColor: BRAND_PRIMARY8
6423
+ backgroundColor: BRAND_PRIMARY9
5921
6424
  },
5922
6425
  accessibilityElementsHidden: true,
5923
6426
  testID: "week-row-current-dot"
@@ -5931,7 +6434,7 @@ function WeekRow({
5931
6434
  fontSize: 13,
5932
6435
  fontWeight: "700",
5933
6436
  fontFamily: "Inter, sans-serif",
5934
- color: isCurrent ? BRAND_PRIMARY8 : void 0
6437
+ color: isCurrent ? BRAND_PRIMARY9 : void 0
5935
6438
  },
5936
6439
  accessibilityElementsHidden: true,
5937
6440
  children: `W${weekNumber}`
@@ -5972,11 +6475,11 @@ function WeekRow({
5972
6475
  }
5973
6476
 
5974
6477
  // src/components/custom/Workout/MesoCard.tsx
5975
- var BRAND_PRIMARY9 = "#FF7900";
5976
- var BRAND_PRIMARY_DARK2 = "#C45E00";
5977
- var BRAND_PRIMARY_LIGHT2 = "#FFB066";
6478
+ var BRAND_PRIMARY10 = getSemanticColors("dark")["brand-primary"];
6479
+ var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
6480
+ var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
5978
6481
  var BORDER_DEFAULT = "#1F1F1F";
5979
- var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY9, BRAND_PRIMARY_LIGHT2];
6482
+ var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
5980
6483
  function heatmapColor(percentage) {
5981
6484
  const clamped = Math.max(0, Math.min(100, percentage));
5982
6485
  const opacity = 0.12 + clamped / 100 * 0.78;
@@ -6011,7 +6514,7 @@ function MesoCard({
6011
6514
  }, [highlighted, highlight]);
6012
6515
  const borderColor = highlight.interpolate({
6013
6516
  inputRange: [0, 1],
6014
- outputRange: [BORDER_DEFAULT, BRAND_PRIMARY9]
6517
+ outputRange: [BORDER_DEFAULT, BRAND_PRIMARY10]
6015
6518
  });
6016
6519
  const header = /* @__PURE__ */ jsxs(reactNative.View, { style: { paddingHorizontal: 14, paddingTop: 12, paddingBottom: 10 }, testID: "meso-card-body", children: [
6017
6520
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 8 }, testID: "meso-card-header", children: [
@@ -6071,7 +6574,7 @@ function MesoCard({
6071
6574
  {
6072
6575
  variant: "outline",
6073
6576
  elevation: 2,
6074
- borderColor: highlighted ? BRAND_PRIMARY9 : BORDER_DEFAULT,
6577
+ borderColor: highlighted ? BRAND_PRIMARY10 : BORDER_DEFAULT,
6075
6578
  className,
6076
6579
  style: [
6077
6580
  { borderColor },
@@ -6128,12 +6631,13 @@ function MesoCard({
6128
6631
  }
6129
6632
  ) });
6130
6633
  }
6634
+ var t10 = getSemanticColors("dark");
6131
6635
  var dotColorMap = {
6132
6636
  untrained: "#2C2C2C",
6133
- behind: "#406D87",
6134
- ontrack: "#14B8A6",
6135
- target: "#FF7900",
6136
- over: "#D14343"
6637
+ behind: t10["brand-secondary"],
6638
+ ontrack: t10["status-success"],
6639
+ target: t10["brand-primary"],
6640
+ over: t10["status-error"]
6137
6641
  };
6138
6642
  function MuscleGroupChip({
6139
6643
  name,
@@ -6207,9 +6711,10 @@ function MuscleGroupChip({
6207
6711
  }
6208
6712
 
6209
6713
  // src/components/custom/Workout/WorkoutCard.tsx
6714
+ var t11 = getSemanticColors("dark");
6210
6715
  var COLORS = {
6211
- statusSuccess: "#14B8A6",
6212
- brandPrimary: "#FF7900",
6716
+ statusSuccess: t11["status-success"],
6717
+ brandPrimary: t11["brand-primary"],
6213
6718
  borderDefault: "#1F1F1F",
6214
6719
  brandPrimarySubtle: "rgba(255,121,0,0.06)"
6215
6720
  };
@@ -6353,7 +6858,7 @@ function WorkoutCard({
6353
6858
  }
6354
6859
 
6355
6860
  // src/components/custom/Workout/ProgramPlanningPage.tsx
6356
- var BRAND_PRIMARY10 = "#FF7900";
6861
+ var BRAND_PRIMARY11 = getSemanticColors("dark")["brand-primary"];
6357
6862
  var WORKOUT_PILL_STATUS = {
6358
6863
  completed: "completed",
6359
6864
  today: "current",
@@ -6421,7 +6926,7 @@ function Breadcrumbs({ crumbs, onNavigate }) {
6421
6926
  fontSize: 12,
6422
6927
  fontFamily: "Inter, sans-serif",
6423
6928
  fontWeight: isLast ? "700" : "500",
6424
- color: isLast ? void 0 : BRAND_PRIMARY10
6929
+ color: isLast ? void 0 : BRAND_PRIMARY11
6425
6930
  },
6426
6931
  children: crumb.label
6427
6932
  }
@@ -6493,8 +6998,8 @@ function WeekLevel({ week, onSelectWorkout }) {
6493
6998
  function WorkoutLevel({ workout, expandedExercise, onToggleExercise }) {
6494
6999
  const exercises = workout.exercises.map((exercise, index) => ({
6495
7000
  ...exercise,
6496
- state: exercise.state === "upcoming" ? exercise.state : index === expandedExercise ? "expanded" : "collapsed",
6497
- onToggle: () => onToggleExercise(index)
7001
+ expanded: index === expandedExercise,
7002
+ onExpandedChange: () => onToggleExercise(index)
6498
7003
  }));
6499
7004
  return /* @__PURE__ */ jsx(reactNative.View, { testID: "program-planning-page-workout-level", children: /* @__PURE__ */ jsx(
6500
7005
  WorkoutCard,
@@ -6637,7 +7142,7 @@ exports.findMeso = findMeso;
6637
7142
  exports.findWeek = findWeek;
6638
7143
  exports.findWorkout = findWorkout;
6639
7144
  exports.groupExercises = groupExercises;
6640
- exports.statusToCardState = statusToCardState;
7145
+ exports.isUpcomingExercise = isUpcomingExercise;
6641
7146
  exports.summarizeVbt = summarizeVbt;
6642
7147
  exports.toActiveCardProps = toActiveCardProps;
6643
7148
  exports.toExerciseCardProps = toExerciseCardProps;